Oasis3 4.0.2
mpp_io_mod_oa.F90
Go to the documentation of this file.
00001 #ifndef key_noIO
00002 #ifndef __PARNETCDF
00003 !-----------------------------------------------------------------------
00004 !                 Parallel I/O for message-passing codes
00005 !
00006 ! AUTHOR: V. Balaji (vbalaji@noaa.gov)
00007 !         Princeton University/GFDL
00008 !
00009 ! MODIFICATIONS: Reiner Vogelsang (reiner@sgi.com)
00010 !                Reiner Vogelsang, Rene Redler: added pnetcdf
00011 !                Rene Redler: Included Modification from MPI Met
00012 !                (Luis Kornblueh, Stephanie Legutke)
00013 !                - Replaced -huge(1.0_4) by -huge(1.0_ip_single_p)
00014 !                - Initialisation of pointer
00015 !                - changed x to 1x in format descriptor
00016 !                Sophie Valcke: replaced ip_single_p with ip_single_mpp
00017 !
00018 !
00019 ! This program is free software; The author agrees that you can
00020 ! redistribute and/or modify this version of the program under the
00021 ! terms of the Lesser GNU General Public License as published
00022 ! by the Free Software Foundation.
00023 !
00024 ! This program is distributed in the hope that it will be useful,
00025 ! but WITHOUT ANY WARRANTY; without even the implied warranty of
00026 ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00027 ! Lesser GNU General Public License for more details
00028 ! (http://www.gnu.org/copyleft/lesser.html).
00029 !-----------------------------------------------------------------------
00030 !#include <os.h>
00031 
00032 module mpp_io_mod_oa
00033   use mod_kinds_mpp
00034   use mpp_mod_oa
00035   use mpp_domains_mod_oa
00036   implicit none
00037 #include <os.h>
00038   private
00039 
00040   character(len=128), private :: version= 
00041        '$Id: mpp_io_mod_oa.F90 3541 2012-08-09 09:58:25Z coquart $'
00042   character(len=128), private :: tagname= 
00043        '$Name$'
00044 
00045   integer, private :: pe, npes
00046 
00047   type, public :: axistype
00048      private
00049      character(len=128) :: name
00050      character(len=128) :: units
00051      character(len=256) :: longname
00052      character(len=8) :: cartesian
00053      integer :: sense, len           !+/-1, depth or height?
00054      type(domain1D) :: domain !if pointer is associated, it is a distributed data axis
00055      real, pointer :: data(:)   !axis values (not used if time axis)
00056      character(len=64), pointer :: cdata(:)  !RV,bundles
00057      integer :: clenid                       !RV,bundles
00058      integer :: id, did, type, natt         !id is the "variable ID", did is the "dimension ID": netCDF requires 2 IDs for axes
00059      type(atttype), pointer :: Att(:)
00060   end type axistype
00061 
00062   type, public :: atttype
00063      integer :: type, len
00064      character(len=128) :: name
00065      character(len=256) :: catt
00066 ! just use type conversion for integers
00067      real, pointer :: fatt(:)
00068   end type atttype
00069 
00070   type, public :: fieldtype
00071      private
00072      character(len=128) :: name
00073      character(len=128) :: units
00074      character(len=256) :: longname
00075      real :: min, max, missing, fill, scale, add
00076      integer :: pack
00077      type(axistype), pointer :: axes(:) !axes associated with field
00078 !size, time_axis_index redundantly hold info already contained in axes
00079 !it's clunky and inelegant, but required so that axes can be shared among multiple files
00080      integer, pointer :: size(:)
00081      integer :: time_axis_index
00082      integer :: id, type, natt, ndim
00083      type(atttype), pointer :: Att(:)
00084   end type fieldtype
00085 
00086   type, private :: filetype
00087      character(len=256) :: name
00088      integer :: action, format, access, threading, fileset, record, ncid
00089      logical :: opened, initialized, nohdrs
00090      integer :: time_level
00091      real(DOUBLE_KIND) :: time
00092      integer :: id             !variable ID of time axis associated with file (only one time axis per file)
00093      integer :: recdimid             !dim ID of time axis associated with file (only one time axis per file)
00094 !
00095 ! time axis values are stored here instead of axis%data since mpp_write
00096 ! assumes these values are not time values. Not used in mpp_write
00097 !
00098      real(DOUBLE_KIND), pointer :: time_values(:)
00099 
00100 ! additional elements of filetype for mpp_read (ignored for mpp_write)
00101      integer :: ndim, nvar, natt  ! number of dimensions, non-dimension variables and global attributes
00102 ! redundant axis types stored here and in associated fieldtype
00103 ! some axes are not used by any fields, i.e. "edges"
00104      type(axistype), pointer  :: axis(:)
00105      type(fieldtype), pointer :: var(:)
00106      type(atttype), pointer   :: att(:)
00107   end type filetype
00108 
00109   type(axistype), public  :: default_axis !provided to users with default components
00110   type(fieldtype), public :: default_field !provided to users with default components
00111   type(atttype), public   :: default_att !provided to users with default components
00112 !action on open
00113   integer, parameter, public :: MPP_WRONLY=100, MPP_RDONLY=101, MPP_APPEND=102, MPP_OVERWR=103
00114 !format
00115   integer, parameter, public :: MPP_ASCII=200,  MPP_IEEE32=201, MPP_NATIVE=202, MPP_NETCDF=203
00116 !access
00117   integer, parameter, public :: MPP_SEQUENTIAL=300, MPP_DIRECT=301
00118 !threading, fileset
00119   integer, parameter, public :: MPP_SINGLE=400, MPP_MULTI=401, MPP_PARALLEL=401
00120 !action on close
00121   integer, parameter, public :: MPP_DELETE=501, MPP_COLLECT=502
00122 
00123   type(filetype), private, allocatable :: mpp_file(:)
00124   integer, private :: records_per_pe
00125   integer, private :: maxunits, unit_begin, unit_end
00126   integer, private :: varnum=0
00127   integer, private :: error
00128   character(len=256) :: text
00129 !null unit: returned by PEs not participating in IO after a collective call
00130   integer, parameter, private :: NULLUNIT=-1
00131   real(DOUBLE_KIND), parameter, private :: NULLTIME=-1.
00132 #ifdef DEBUG
00133   logical, private :: verbose=.FALSE., debug=.TRUE., module_is_initialized=.FALSE.
00134 #else
00135   logical, private :: verbose=.FALSE., debug=.FALSE., module_is_initialized=.FALSE.
00136 #endif
00137 
00138   real(DOUBLE_KIND), private, allocatable :: mpp_io_stack(:)
00139   integer, private :: mpp_io_stack_size=0, mpp_io_stack_hwm=0
00140   
00141   interface mpp_write_meta
00142      module procedure mpp_write_meta_var
00143      module procedure mpp_write_meta_scalar_r
00144      module procedure mpp_write_meta_scalar_i
00145      module procedure mpp_write_meta_axis
00146      module procedure mpp_write_meta_field
00147      module procedure mpp_write_meta_global
00148      module procedure mpp_write_meta_global_scalar_r
00149      module procedure mpp_write_meta_global_scalar_i
00150   end interface
00151 
00152   interface mpp_copy_meta
00153      module procedure mpp_copy_meta_axis
00154      module procedure mpp_copy_meta_field
00155      module procedure mpp_copy_meta_global
00156   end interface
00157      
00158   interface mpp_write
00159      module procedure mpp_write_2ddecomp_r1d
00160      module procedure mpp_write_2ddecomp_r2d
00161      module procedure mpp_write_2ddecomp_r3d
00162      module procedure mpp_write_2ddecomp_r4d
00163      module procedure mpp_write_r0D
00164      module procedure mpp_write_r1D
00165      module procedure mpp_write_r2D
00166      module procedure mpp_write_r3D
00167      module procedure mpp_write_r4D
00168      module procedure mpp_write_axis
00169   end interface
00170 
00171   interface mpp_read
00172      module procedure mpp_read_2ddecomp_r1d
00173      module procedure mpp_read_2ddecomp_r2d
00174      module procedure mpp_read_2ddecomp_r3d
00175      module procedure mpp_read_2ddecomp_r4d
00176      module procedure mpp_read_r0D
00177      module procedure mpp_read_r1D
00178      module procedure mpp_read_r2D
00179      module procedure mpp_read_r3D
00180      module procedure mpp_read_r4D
00181   end interface
00182 
00183   interface mpp_get_id
00184      module procedure mpp_get_axis_id
00185      module procedure mpp_get_field_id
00186   end interface
00187 
00188   interface mpp_get_atts
00189      module procedure mpp_get_global_atts
00190      module procedure mpp_get_field_atts
00191      module procedure mpp_get_axis_atts
00192   end interface
00193 
00194   interface mpp_modify_meta
00195 !     module procedure mpp_modify_att_meta
00196      module procedure mpp_modify_field_meta
00197      module procedure mpp_modify_axis_meta
00198   end interface
00199 
00200   public :: mpp_close, mpp_flush, mpp_get_iospec, mpp_get_id, mpp_get_ncid, mpp_get_unit_range, mpp_io_init, mpp_io_exit, &
00201             mpp_open, mpp_set_unit_range, mpp_write, mpp_write_meta, mpp_read, mpp_get_info, mpp_get_atts, &
00202             mpp_get_fields, mpp_get_times, mpp_get_axes, mpp_copy_meta, mpp_get_recdimid, mpp_get_axis_data, mpp_modify_meta, &
00203             mpp_io_set_stack_size, mpp_get_field_index, mpp_nullify_axistype, mpp_nullify_axistype_array
00204 
00205   private :: read_record, mpp_read_meta, lowercase
00206 
00207 #ifdef use_netCDF
00208 #include <netcdf.inc>
00209 #endif
00210 
00211   contains
00212 
00213 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00214 !                                                                      !
00215 !               mpp_io_init: initialize parallel I/O                   !
00216 !                                                                      !
00217 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00218     subroutine mpp_io_init( flags, maxunit,maxresunit )
00219       integer, intent(in), optional :: flags, maxunit ,maxresunit
00220 !rv
00221 !rv I introduced the variable to indentify that the top max_reserved_units 
00222 !rv of maxunits are reserved for OASIS coupler specific things like the trace
00223 !rv files. This variable is active only if one specifies explicitely  the
00224 !rv argument maxunit.
00225       integer::max_reserved_units
00226 !rv
00227 !initialize IO package: initialize mpp_file array, set valid range of units for fortran IO
00228 
00229       if( module_is_initialized )return
00230       call mpp_init(flags)           !if mpp_init has been called, this call will merely return
00231       pe = mpp_pe()
00232       npes = mpp_npes()
00233       call mpp_domains_init(flags)
00234 
00235       maxunits = 64
00236       if( PRESENT(maxunit) )maxunits = maxunit
00237 
00238       max_reserved_units=5
00239       if( PRESENT(maxresunit) )max_reserved_units = maxresunit
00240 
00241       if( PRESENT(flags) )then
00242           debug   = flags.EQ.MPP_DEBUG
00243           verbose = flags.EQ.MPP_VERBOSE .OR. debug
00244       end if
00245 !initialize default_field
00246       default_field%name = 'noname'
00247       default_field%units = 'nounits'
00248       default_field%longname = 'noname'
00249       default_field%id = -1
00250       default_field%type = -1
00251       default_field%natt = -1
00252       default_field%ndim = -1
00253 !largest possible 4-byte reals
00254       default_field%min = -huge(1._ip_single_mpp)
00255       default_field%max =  huge(1._ip_single_mpp)
00256       default_field%missing = -1e36
00257       default_field%fill = -1e36
00258       default_field%scale = 0.
00259       default_field%add = huge(1._ip_single_mpp)
00260       default_field%pack = 1
00261       default_field%time_axis_index = -1 !this value will never match any index
00262       Nullify(default_field%axes)
00263       Nullify(default_field%size)
00264       Nullify(default_field%att)
00265 ! Initialize default axis
00266       default_axis%name = 'noname'
00267       default_axis%units = 'nounits'
00268       default_axis%longname = 'noname'
00269       default_axis%cartesian = 'none'
00270       default_axis%sense = 0
00271       default_axis%len = -1
00272       default_axis%id = -1
00273       default_axis%did = -1
00274       default_axis%type = -1
00275       default_axis%natt = -1
00276       Nullify(default_axis%data)
00277 ! Initialize default attribute
00278       default_att%name = 'noname'
00279       default_att%type = -1
00280       default_att%len = -1
00281       default_att%catt = 'none'
00282       Nullify(default_att%fatt)
00283    
00284 !up to MAXUNITS fortran units and MAXUNITS netCDF units are supported
00285 !file attributes (opened, format, access, threading, fileset) are saved against the unit number
00286 !external handles to netCDF units are saved from maxunits+1:2*maxunits
00287       allocate( mpp_file(NULLUNIT:2*maxunits) ) !starts at NULLUNIT=-1, used by non-participant PEs in single-threaded I/O
00288       mpp_file(:)%name   = ' '
00289       mpp_file(:)%action    = -1
00290       mpp_file(:)%format    = -1
00291       mpp_file(:)%threading = -1
00292       mpp_file(:)%fileset   = -1
00293       mpp_file(:)%record    = -1
00294       mpp_file(:)%ncid      = -1
00295       mpp_file(:)%opened = .FALSE.
00296       mpp_file(:)%initialized = .FALSE.
00297       mpp_file(:)%time_level = 0
00298       mpp_file(:)%time = NULLTIME
00299       mpp_file(:)%id = -1
00300 !
00301       mpp_file(:)%ndim = -1
00302       mpp_file(:)%nvar = -1
00303 !NULLUNIT "file" is always single-threaded, open and initialized (to pass checks in mpp_write)
00304       mpp_file(NULLUNIT)%threading = MPP_SINGLE
00305       mpp_file(NULLUNIT)%opened = .TRUE.
00306       mpp_file(NULLUNIT)%initialized = .TRUE.
00307 !declare the stdunits to be open
00308       mpp_file(stdin ())%opened = .TRUE.
00309       mpp_file(stdout())%opened = .TRUE.
00310       mpp_file(stderr())%opened = .TRUE.
00311       mpp_file(stdout())%opened = .TRUE.
00312 !set range of allowed fortran unit numbers: could be compiler-dependent (should not overlap stdin/out/err)
00313 !
00314 !rv For OASIS 3 I consider the top max_reserved_units to be excluded from
00315 !rv the list of files ito closed during mpp_io_exit.
00316 !rv      call mpp_set_unit_range( 7, maxunits )
00317       if(present(maxunit)) then
00318         call mpp_set_unit_range( 7, maxunits-max_reserved_units )
00319       else
00320         call mpp_set_unit_range( 7, maxunits )
00321       endif
00322 !rv
00323 
00324       if( pe.EQ.mpp_root_pe() )then
00325           write( stdout(),'(/a)' )'MPP_IO module '//trim(version)
00326 #ifdef use_netCDF
00327           text = NF_INQ_LIBVERS()
00328           write( stdout(),'(a)' )'Using netCDF library version '//trim(text)
00329 #endif
00330       endif
00331 
00332 #ifdef CRAYPVP
00333 !we require every file to be assigned threadwise: PVPs default to global, and are reset here
00334       call ASSIGN( 'assign -P thread p:%', error )
00335 #endif
00336 
00337       call mpp_io_set_stack_size(131072) ! default initial value
00338       call mpp_sync()
00339       module_is_initialized = .TRUE.
00340       return
00341     end subroutine mpp_io_init
00342 
00343     subroutine mpp_io_exit()
00344       integer :: unit
00345 
00346       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_IO_EXIT: must first call mpp_io_init.' )
00347 !close all open fortran units
00348       do unit = unit_begin,unit_end
00349          if( mpp_file(unit)%opened )call mpp_flush(unit)
00350       end do
00351       call mpp_sync()
00352       do unit = unit_begin,unit_end
00353          if( mpp_file(unit)%opened )close(unit)
00354       end do
00355 #ifdef use_netCDF
00356 !close all open netCDF units
00357       do unit = maxunits+1,2*maxunits
00358          if( mpp_file(unit)%opened )error = NF_CLOSE(mpp_file(unit)%ncid)
00359       end do
00360 #endif
00361 
00362       call mpp_max(mpp_io_stack_hwm)
00363       
00364       if( pe.EQ.mpp_root_pe() )then
00365 !          write( stdout,'(/a)' )'Exiting MPP_IO module...'
00366 !          write( stdout,* )'MPP_IO_STACK high water mark=', mpp_io_stack_hwm
00367       end if
00368       deallocate(mpp_file)
00369       module_is_initialized = .FALSE.
00370       return
00371     end subroutine mpp_io_exit
00372 
00373     subroutine mpp_io_set_stack_size(n)
00374 !set the mpp_io_stack variable to be at least n LONG words long
00375       integer, intent(in) :: n
00376       character(len=8) :: text
00377 
00378       if( n.GT.mpp_io_stack_size .AND. allocated(mpp_io_stack) )deallocate(mpp_io_stack)
00379       if( .NOT.allocated(mpp_io_stack) )then
00380           allocate( mpp_io_stack(n) )
00381           mpp_io_stack_size = n
00382           write( text,'(i8)' )n 
00383           if( pe.EQ.mpp_root_pe() ) &
00384           call mpp_error( NOTE, 'MPP_IO_SET_STACK_SIZE: stack size set to '//text//'.' )    
00385       end if
00386 
00387       return
00388     end subroutine mpp_io_set_stack_size
00389     
00390 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00391 !                                                                            !
00392 !           OPENING AND CLOSING FILES: mpp_open() and mpp_close()            !
00393 !                                                                            !
00394 ! mpp_open( unit, file, action, form, access, threading, &                   !
00395 !           fileset, iospec, nohdrs, recl, pelist )                          !
00396 !      integer, intent(out) :: unit                                          !
00397 !      character(len=*), intent(in) :: file                                  !
00398 !      integer, intent(in), optional :: action, form, access, threading,     !
00399 !                                       fileset, recl                        !
00400 !      character(len=*), intent(in), optional :: iospec                      !
00401 !      logical, intent(in), optional :: nohdrs                               !
00402 !      integer, optional, intent(in) :: pelist(:) !default ALL               !
00403 !                                                                            !
00404 !  unit is intent(OUT): always _returned_by_ mpp_open()                      !
00405 !  file is the filename: REQUIRED                                            !
00406 !    we append .nc to filename if it is a netCDF file                        !
00407 !    we append .<pppp> to filename if fileset is private (pppp is PE number) !
00408 !  iospec is a system hint for I/O organization                              !
00409 !         e.g assign(1) on SGI/Cray systems.                                 !
00410 !  if nohdrs is .TRUE. headers are not written on non-netCDF writes.         !
00411 !  nohdrs has no effect when action=MPP_RDONLY|MPP_APPEND                    !
00412 !                    or when form=MPP_NETCDF                                 !
00413 ! FLAGS:                                                                     !
00414 !    action is one of MPP_RDONLY, MPP_APPEND or MPP_WRONLY                   !
00415 !    form is one of MPP_ASCII:  formatted read/write                         !
00416 !                   MPP_NATIVE: unformatted read/write, no conversion        !
00417 !                   MPP_IEEE32: unformatted read/write, conversion to IEEE32 !
00418 !                   MPP_NETCDF: unformatted read/write, conversion to netCDF !
00419 !    access is one of MPP_SEQUENTIAL or MPP_DIRECT (ignored for netCDF)      !
00420 !      RECL argument is REQUIRED for direct access IO                        !
00421 !    threading is one of MPP_SINGLE or MPP_MULTI                             !
00422 !      single-threaded IO in a multi-PE run is done by PE0                   !
00423 !    fileset is one of MPP_MULTI and MPP_SINGLE                              !
00424 !      fileset is only used for multi-threaded I/O                           !
00425 !      if all I/O PEs in <pelist> use a single fileset,                      !
00426 !              they write to the same file                                   !
00427 !      if all I/O PEs in <pelist> use a multi  fileset,                      !
00428 !              they each write an independent file                           !
00429 !  recl is the record length in bytes                                        !
00430 !  pelist is the list of I/O PEs (currently ALL)                             !
00431 !                                                                            !
00432 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00433     subroutine mpp_open( unit, file, action, form, access, threading, &
00434                                      fileset, iospec, nohdrs, recl, pelist )
00435       integer, intent(out) :: unit
00436       character(len=*), intent(in) :: file
00437       integer, intent(in), optional :: action, form, access, threading, 
00438            fileset, recl
00439       character(len=*), intent(in), optional :: iospec
00440       logical, intent(in), optional :: nohdrs
00441       integer, intent(in), optional :: pelist(:) !default ALL
00442 
00443       character(len=16) :: act, acc, for, pos
00444       integer :: action_flag, form_flag, access_flag, threading_flag, fileset_flag, length
00445       logical :: exists
00446       character(len=64) :: filespec
00447       type(axistype) :: unlim    !used by netCDF with mpp_append
00448 
00449       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_OPEN: must first call mpp_io_init.' )
00450 !set flags
00451       action_flag = MPP_WRONLY        !default
00452       if( PRESENT(action) )action_flag = action
00453       form_flag = MPP_ASCII
00454       if( PRESENT(form) )form_flag = form
00455 #ifndef use_netCDF
00456       if( form_flag.EQ.MPP_NETCDF ) &
00457            call mpp_error( FATAL, 'MPP_OPEN: To open a file with form=MPP_NETCDF, you must compile mpp_io with -Duse_netCDF.' )
00458 #endif
00459       access_flag = MPP_SEQUENTIAL
00460       if( PRESENT(access) )access_flag = access
00461       threading_flag = MPP_SINGLE
00462       if( npes.GT.1 .AND. PRESENT(threading) )threading_flag = threading
00463       fileset_flag = MPP_MULTI
00464       if( PRESENT(fileset) )fileset_flag = fileset
00465       if( threading_flag.EQ.MPP_SINGLE )fileset_flag = MPP_SINGLE
00466 
00467 !get a unit number
00468       if( threading_flag.EQ.MPP_SINGLE )then
00469           if( pe.NE.mpp_root_pe() .AND. action_flag.NE.MPP_RDONLY )then
00470               unit = NULLUNIT           !PEs not participating in IO from this mpp_open() will return this value for unit
00471               return
00472           end if
00473       end if
00474       if( form_flag.EQ.MPP_NETCDF )then
00475           do unit = maxunits+1,2*maxunits
00476              if( .NOT.mpp_file(unit)%opened )exit
00477           end do
00478           if( unit.GT.2*maxunits )call mpp_error( FATAL, 'MPP_OPEN: too many open netCDF files.' )
00479       else
00480           do unit = unit_begin, unit_end
00481              inquire( unit,OPENED=mpp_file(unit)%opened )
00482              if( .NOT.mpp_file(unit)%opened )exit
00483           end do
00484           if( unit.GT.unit_end )call mpp_error( FATAL, 'MPP_OPEN: no available units.' )
00485       end if
00486 
00487 !get a filename
00488       text = file
00489       length = len(file)
00490 
00491 !RV   I dropped the automatic file name extension. PSMILE will always
00492 !RV   provide netcdf file names with an extension .nc or names containing .nc.
00493 !RV      if( form_flag.EQ.MPP_NETCDF.AND. file(length-2:length) /= '.nc' ) &
00494 !RV         text = trim(file)//'.nc'
00495          
00496       if( fileset_flag.EQ.MPP_MULTI )write( text,'(a,i4.4)' )trim(text)//'.', pe
00497       mpp_file(unit)%name = text
00498       if( verbose ) write (stdout(), '(a,2i3,1x,a,5i5)') &
00499            'MPP_OPEN: PE, unit, filename, action, format, access, threading, fileset=', &
00500            pe, unit, trim(mpp_file(unit)%name), action_flag, form_flag, access_flag, threading_flag, fileset_flag
00501 
00502 !action: read, write, overwrite, append: act and pos are ignored by netCDF
00503       if( action_flag.EQ.MPP_RDONLY )then
00504           act = 'READ'
00505           pos = 'REWIND'
00506 !          if( form_flag.EQ.MPP_NETCDF )call mpp_error( FATAL, 'MPP_OPEN: only writes are currently supported with netCDF.' )
00507       else if( action_flag.EQ.MPP_WRONLY .OR. action_flag.EQ.MPP_OVERWR )then
00508           act = 'WRITE'
00509           pos = 'REWIND'
00510       else if( action_flag.EQ.MPP_APPEND )then
00511           act = 'WRITE'
00512           pos = 'APPEND'
00513       else
00514           call mpp_error( FATAL, 'MPP_OPEN: action must be one of MPP_WRONLY, MPP_APPEND or MPP_RDONLY.' )
00515       end if
00516 
00517 !access: sequential or direct: ignored by netCDF
00518       if( form_flag.NE.MPP_NETCDF )then
00519           if( access_flag.EQ.MPP_SEQUENTIAL )then
00520               acc = 'SEQUENTIAL'
00521           else if( access_flag.EQ.MPP_DIRECT )then
00522               acc = 'DIRECT'
00523               if( form_flag.EQ.MPP_ASCII )call mpp_error( FATAL, 'MPP_OPEN: formatted direct access I/O is prohibited.' )
00524               if( .NOT.PRESENT(recl) ) &
00525                    call mpp_error( FATAL, 'MPP_OPEN: recl (record length in bytes) must be specified with access=MPP_DIRECT.' )
00526               mpp_file(unit)%record = 1
00527               records_per_pe = 1 !each PE writes 1 record per mpp_write
00528           else
00529               call mpp_error( FATAL, 'MPP_OPEN: access must be one of MPP_SEQUENTIAL or MPP_DIRECT.' )
00530           end if
00531       end if
00532 
00533 !threading: SINGLE or MULTI
00534       if( threading_flag.EQ.MPP_MULTI )then
00535 !fileset: MULTI or SINGLE (only for multi-threaded I/O
00536           if( fileset_flag.EQ.MPP_SINGLE )then
00537               if( form_flag.EQ.MPP_NETCDF .AND. act.EQ.'WRITE' ) &
00538                    call mpp_error( FATAL, 'MPP_OPEN: netCDF currently does not support single-file multi-threaded output.' )
00539 
00540 #ifdef _CRAYT3E
00541               call ASSIGN( 'assign -I -F global.privpos f:'//trim(mpp_file(unit)%name), error )
00542 #endif
00543           else if( fileset_flag.NE.MPP_MULTI )then
00544               call mpp_error( FATAL, 'MPP_OPEN: fileset must be one of MPP_MULTI or MPP_SINGLE.' )
00545           end if
00546       else if( threading_flag.NE.MPP_SINGLE )then
00547           call mpp_error( FATAL, 'MPP_OPEN: threading must be one of MPP_SINGLE or MPP_MULTI.' )
00548       end if
00549 
00550 !apply I/O specs before opening the file
00551 !note that -P refers to the scope of a fortran unit, which is always thread-private even if file is shared
00552 #ifdef CRAYPVP
00553       call ASSIGN( 'assign -I -P thread  f:'//trim(mpp_file(unit)%name), error )
00554 #endif
00555 #ifdef _CRAYT3E
00556       call ASSIGN( 'assign -I -P private f:'//trim(mpp_file(unit)%name), error )
00557 #endif
00558       if( PRESENT(iospec) )then
00559 !iospec provides hints to the system on how to organize I/O
00560 !on Cray systems this is done through 'assign', see assign(1) and assign(3F)
00561 !on other systems this will be expanded as needed
00562 !no error checks here on whether the supplied iospec is valid
00563 #ifdef SGICRAY
00564           call ASSIGN( 'assign -I '//trim(iospec)//' f:'//trim(mpp_file(unit)%name), error )
00565           if( form_flag.EQ.MPP_NETCDF )then
00566 !for netCDF on SGI/Cray systems we pass it to the environment variable NETCDF_XFFIOSPEC
00567 !ideally we should parse iospec, pass the argument of -F to NETCDF_FFIOSPEC, and the rest to NETCDF_XFFIOSPEC
00568 !maybe I'll get around to it someday
00569 !PXFSETENV is a POSIX-standard routine for setting environment variables from fortran
00570               call PXFSETENV( 'NETCDF_XFFIOSPEC', 0, trim(iospec), 0, 1, error )
00571           end if
00572 #endif
00573       end if
00574 
00575 !open the file as specified above for various formats
00576       if( form_flag.EQ.MPP_NETCDF )then
00577 #ifdef use_netCDF
00578           if( action_flag.EQ.MPP_WRONLY )then
00579               error = NF_CREATE( trim(mpp_file(unit)%name), NF_NOCLOBBER, mpp_file(unit)%ncid ); call netcdf_err(error)
00580               if( verbose ) write (stdout(), '(a,i3,i16)') 'MPP_OPEN: new netCDF file: pe, ncid=', pe, mpp_file(unit)%ncid
00581           else if( action_flag.EQ.MPP_OVERWR )then
00582               error = NF_CREATE( trim(mpp_file(unit)%name), NF_CLOBBER,   mpp_file(unit)%ncid ); call netcdf_err(error)
00583               action_flag = MPP_WRONLY !after setting clobber, there is no further distinction btwn MPP_WRONLY and MPP_OVERWR
00584               if( verbose ) write (stdout(), '(a,i3,i16)') 'MPP_OPEN: overwrite netCDF file: pe, ncid=', pe, mpp_file(unit)%ncid
00585           else if( action_flag.EQ.MPP_APPEND )then
00586               error = NF_OPEN( trim(mpp_file(unit)%name), NF_WRITE, mpp_file(unit)%ncid ); call netcdf_err(error)
00587 !get the current time level of the file: writes to this file will be at next time level
00588               error = NF_INQ_UNLIMDIM( mpp_file(unit)%ncid, unlim%did )
00589               if( error.EQ.NF_NOERR )then
00590                   error = NF_INQ_DIM( mpp_file(unit)%ncid, unlim%did, unlim%name, mpp_file(unit)%time_level )
00591                   call netcdf_err(error)
00592                   error = NF_INQ_VARID( mpp_file(unit)%ncid, unlim%name, mpp_file(unit)%id ); call netcdf_err(error)
00593               end if
00594               if( verbose ) write (stdout(), '(a,i3,i16,i4)') 'MPP_OPEN: append to existing netCDF file: pe, ncid, time_axis_id=',&
00595                    pe, mpp_file(unit)%ncid, mpp_file(unit)%id
00596       else if( action_flag.EQ.MPP_RDONLY )then
00597           error = NF_OPEN( trim(mpp_file(unit)%name), NF_NOWRITE, mpp_file(unit)%ncid ); call netcdf_err(error)
00598           if( verbose ) write (stdout(), '(a,i3,i16,i4)') 'MPP_OPEN: opening existing netCDF file: pe, ncid, time_axis_id=',&
00599                    pe, mpp_file(unit)%ncid, mpp_file(unit)%id
00600               mpp_file(unit)%format=form_flag ! need this for mpp_read
00601               call mpp_read_meta(unit)
00602           end if
00603           mpp_file(unit)%opened = .TRUE.
00604 #endif
00605       else
00606 !format: ascii, native, or IEEE 32 bit
00607           if( form_flag.EQ.MPP_ASCII )then
00608               for = 'FORMATTED'
00609           else if( form_flag.EQ.MPP_IEEE32 )then
00610               for = 'UNFORMATTED'
00611 !assign -N is currently unsupported on SGI
00612 #ifdef _CRAY
00613               call ASSIGN( 'assign -I -N ieee_32 f:'//trim(mpp_file(unit)%name), error )
00614 #endif
00615           else if( form_flag.EQ.MPP_NATIVE )then
00616               for = 'UNFORMATTED'
00617           else
00618               call mpp_error( FATAL, 'MPP_OPEN: form must be one of MPP_ASCII, MPP_NATIVE, MPP_IEEE32 or MPP_NETCDF.' )
00619           end if
00620           inquire( file=trim(mpp_file(unit)%name), EXIST=exists )
00621           if( exists .AND. action_flag.EQ.MPP_WRONLY ) &
00622                call mpp_error( WARNING, 'MPP_OPEN: File '//trim(mpp_file(unit)%name)//' opened WRONLY already exists!' )
00623           if( action_flag.EQ.MPP_OVERWR )action_flag = MPP_WRONLY
00624 !perform the OPEN here
00625           if( PRESENT(recl) )then
00626               if( verbose ) write (stdout(), '(2(1x,a,i3),5(1x,a),a,i8)') 'MPP_OPEN: PE=', pe, &
00627                    'unit=', unit, trim(mpp_file(unit)%name), 'attributes=', trim(acc), trim(for), trim(act), ' RECL=', recl
00628               open( unit, file=trim(mpp_file(unit)%name), access=acc, form=for, action=act, recl=recl )
00629           else
00630               if( verbose ) write (stdout(), '(2(1x,a,i3),6(1x,a))')      'MPP_OPEN: PE=', pe, &
00631                    'unit=', unit, trim(mpp_file(unit)%name), 'attributes=', trim(acc), trim(for), trim(pos), trim(act)
00632               open( unit, file=trim(mpp_file(unit)%name), access=acc, form=for, action=act, position=pos )
00633           end if
00634 !check if OPEN worked
00635           inquire( unit,OPENED=mpp_file(unit)%opened )
00636           if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_OPEN: error in OPEN() statement.' )
00637       end if
00638       mpp_file(unit)%action = action_flag
00639       mpp_file(unit)%format = form_flag
00640       mpp_file(unit)%access = access_flag
00641       mpp_file(unit)%threading = threading_flag
00642       mpp_file(unit)%fileset = fileset_flag
00643       if( PRESENT(nohdrs) )mpp_file(unit)%nohdrs = nohdrs
00644 
00645       if( action_flag.EQ.MPP_WRONLY )then
00646           if( form_flag.NE.MPP_NETCDF .AND. access_flag.EQ.MPP_DIRECT )call mpp_write_meta( unit, 'record_length', ival=recl )
00647 !actual file name
00648           call mpp_write_meta( unit, 'filename', cval=mpp_file(unit)%name )
00649 !MPP_IO package version
00650           call mpp_write_meta( unit, 'MPP_IO_VERSION', cval=trim(version) )
00651 !filecount for multifileset
00652           if( threading_flag.EQ.MPP_MULTI .AND. fileset_flag.EQ.MPP_MULTI ) &
00653                call mpp_write_meta( unit, 'NumFilesInSet', ival=npes )
00654       end if
00655 
00656       return
00657     end subroutine mpp_open
00658 
00659     subroutine mpp_close( unit, action )
00660       integer, intent(in) :: unit
00661       integer, intent(in), optional :: action
00662       character(len=8) :: status
00663       logical :: collect
00664 
00665       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_CLOSE: must first call mpp_io_init.' )
00666       if( unit.EQ.NULLUNIT )return !nothing was actually opened on this unit
00667 
00668 !action on close
00669       status = 'KEEP'
00670 !collect is supposed to launch the post-processing collector tool for multi-fileset
00671       collect = .FALSE.
00672       if( PRESENT(action) )then
00673           if( action.EQ.MPP_DELETE )then
00674               status = 'DELETE'
00675           else if( action.EQ.MPP_COLLECT )then
00676               collect = .FALSE.         !should be TRUE but this is not yet ready
00677               call mpp_error( WARNING, 'MPP_CLOSE: the COLLECT operation is not yet implemented.' )
00678           else
00679               call mpp_error( FATAL, 'MPP_CLOSE: action must be one of MPP_DELETE or MPP_COLLECT.' )
00680           end if
00681       end if
00682       if( mpp_file(unit)%fileset.NE.MPP_MULTI )collect = .FALSE.
00683       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
00684 #ifdef use_netCDF
00685           error = NF_CLOSE(mpp_file(unit)%ncid); call netcdf_err(error)
00686 #endif
00687       else
00688           close(unit,status=status)
00689       end if
00690 #ifdef SGICRAY
00691 !this line deleted: since the FILENV is a shared file, this might cause a problem in
00692 ! multi-threaded I/O if one PE does assign -R before another one has opened it.
00693 !      call ASSIGN( 'assign -R f:'//trim(mpp_file(unit)%name), error )
00694 #endif
00695       mpp_file(unit)%name = ' '
00696       mpp_file(unit)%action    = -1
00697       mpp_file(unit)%format    = -1
00698       mpp_file(unit)%access    = -1
00699       mpp_file(unit)%threading = -1
00700       mpp_file(unit)%fileset   = -1
00701       mpp_file(unit)%record    = -1
00702       mpp_file(unit)%ncid      = -1
00703       mpp_file(unit)%opened = .FALSE.
00704       mpp_file(unit)%initialized = .FALSE.
00705       mpp_file(unit)%id = -1
00706       mpp_file(unit)%time_level = 0
00707       mpp_file(unit)%time = NULLTIME
00708       return
00709     end subroutine mpp_close
00710 
00711 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00712 !                                                                            !
00713 !                             MPP_WRITE_META                                 !
00714 !                                                                            !
00715 !This series of routines is used to describe the contents of the file        !
00716 !being written on <unit>. Each file can contain any number of fields,        !
00717 !which can be functions of 0-3 spatial axes and 0-1 time axes. Axis          !
00718 !descriptors are stored in the <axistype> structure and field                !
00719 !descriptors in the <fieldtype> structure.                                   !
00720 !                                                                            !
00721 !  type, public :: axistype                                                  !
00722 !     sequence                                                               !
00723 !     character(len=128) :: name                                             !
00724 !     character(len=128) :: units                                            !
00725 !     character(len=256) :: longname                                         !
00726 !     integer :: sense           !+/-1, depth or height?                     !
00727 !     type(domain1D) :: domain                                      !
00728 !     real, pointer :: data(:) !axis values (not used if time axis)          !
00729 !     integer :: id                                                          !
00730 !  end type axistype                                                         !
00731 !                                                                            !
00732 !  type, public :: fieldtype                                                 !
00733 !     sequence                                                               !
00734 !     character(len=128) :: name                                             !
00735 !     character(len=128) :: units                                            !
00736 !     character(len=256) :: longname                                         !
00737 !     real :: min, max, missing, fill, scale, add                            !
00738 !     type(axistype), pointer :: axis(:)                                     !
00739 !     integer :: id                                                          !
00740 !  end type fieldtype                                                        !
00741 !                                                                            !
00742 !The metadata contained in the type is always written for each axis and      !
00743 !field. Any other metadata one wishes to attach to an axis or field          !
00744 !can subsequently be passed to mpp_write_meta using the ID, as shown below.  !
00745 !                                                                            !
00746 !mpp_write_meta can take several forms:                                      !
00747 !                                                                            !
00748 !  mpp_write_meta( unit, name, rval=rval, pack=pack )                        !
00749 !  mpp_write_meta( unit, name, ival=ival )                                   !
00750 !  mpp_write_meta( unit, name, cval=cval )                                   !
00751 !      integer, intent(in) :: unit                                           !
00752 !      character(len=*), intent(in) :: name                                  !
00753 !      real, intent(in), optional :: rval(:)                                 !
00754 !      integer, intent(in), optional :: ival(:)                              !
00755 !      character(len=*), intent(in), optional :: cval                        !
00756 !                                                                            !
00757 !    This form defines global metadata associated with the file as a         !
00758 !    whole. The attribute is named <name> and can take on a real, integer    !
00759 !    or character value. <rval> and <ival> can be scalar or 1D arrays.       !
00760 !                                                                            !
00761 !  mpp_write_meta( unit, id, name, rval=rval, pack=pack )                    !
00762 !  mpp_write_meta( unit, id, name, ival=ival )                               !
00763 !  mpp_write_meta( unit, id, name, cval=cval )                               !
00764 !      integer, intent(in) :: unit, id                                       !
00765 !      character(len=*), intent(in) :: name                                  !
00766 !      real, intent(in), optional :: rval(:)                                 !
00767 !      integer, intent(in), optional :: ival(:)                              !
00768 !      character(len=*), intent(in), optional :: cval                        !
00769 !                                                                            !
00770 !    This form defines metadata associated with a previously defined         !
00771 !    axis or field, identified to mpp_write_meta by its unique ID <id>.      !
00772 !    The attribute is named <name> and can take on a real, integer           !
00773 !    or character value. <rval> and <ival> can be scalar or 1D arrays.       !
00774 !    This need not be called for attributes already contained in             !
00775 !    the type.                                                               !
00776 !                                                                            !
00777 !    PACK can take values 1,2,4,8. This only has meaning when writing        !
00778 !    floating point numbers. The value of PACK defines the number of words   !
00779 !    written into 8 bytes. For pack=4 and pack=8, an integer value is        !
00780 !    written: rval is assumed to have been scaled to the appropriate dynamic !
00781 !    range.                                                                  !
00782 !    PACK currently only works for netCDF files, and is ignored otherwise.   !
00783 !                                                                            !
00784 !   subroutine mpp_write_meta_axis( unit, axis, name, units, longname, &     !
00785 !        cartesian, sense, domain, data )                                    !
00786 !     integer, intent(in) :: unit                                            !
00787 !     type(axistype), intent(inout) :: axis                                  !
00788 !     character(len=*), intent(in) :: name, units, longname                  !
00789 !     character(len=*), intent(in), optional :: cartesian                    !
00790 !     integer, intent(in), optional :: sense                                 !
00791 !     type(domain1D), intent(in), optional :: domain                 !
00792 !     real, intent(in), optional :: data(:)                                  !
00793 !                                                                            !
00794 !    This form defines a time or space axis. Metadata corresponding to the   !
00795 !    type above are written to the file on <unit>. A unique ID for subsequent!
00796 !    references to this axis is returned in axis%id. If the <domain>         !
00797 !    element is present, this is recognized as a distributed data axis       !
00798 !    and domain decomposition information is also written if required (the   !
00799 !    domain decomposition info is required for multi-fileset multi-threaded  !
00800 !    I/O). If the <data> element is allocated, it is considered to be a space!
00801 !    axis, otherwise it is a time axis with an unlimited dimension. Only one !
00802 !    time axis is allowed per file.                                          !
00803 !                                                                            !
00804 !   subroutine mpp_write_meta_field( unit, field, axes, name, units, longname!
00805 !        min, max, missing, fill, scale, add, pack )                         !
00806 !     integer, intent(in) :: unit                                            !
00807 !     type(fieldtype), intent(out) :: field                                  !
00808 !     type(axistype), intent(in) :: axes(:)                                  !
00809 !     character(len=*), intent(in) :: name, units, longname                  !
00810 !     real, intent(in), optional :: min, max, missing, fill, scale, add      !
00811 !     integer, intent(in), optional :: pack                                  !
00812 !                                                                            !
00813 !    This form defines a field. Metadata corresponding to the type           !
00814 !    above are written to the file on <unit>. A unique ID for subsequent     !
00815 !    references to this field is returned in field%id. At least one axis     !
00816 !    must be associated, 0D variables are not considered. mpp_write_meta     !
00817 !    must previously have been called on all axes associated with this       !
00818 !    field.                                                                  !
00819 !                                                                            !
00820 ! The mpp_write_meta package also includes subroutines write_attribute and   !
00821 ! write_attribute_netcdf, that are private to this module.                   !
00822 !                                                                            !
00823 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00824     subroutine mpp_write_meta_global( unit, name, rval, ival, cval, pack )
00825 !writes a global metadata attribute to unit <unit>
00826 !attribute <name> can be an real, integer or character
00827 !one and only one of rval, ival, and cval should be present
00828 !the first found will be used
00829 !for a non-netCDF file, it is encoded into a string "GLOBAL <name> <val>"
00830       integer, intent(in) :: unit
00831       character(len=*), intent(in) :: name
00832       real,             intent(in), optional :: rval(:)
00833       integer,          intent(in), optional :: ival(:)
00834       character(len=*), intent(in), optional :: cval
00835       integer, intent(in), optional :: pack
00836 
00837       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
00838       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
00839       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
00840       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
00841       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
00842       if( mpp_file(unit)%initialized ) &
00843            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
00844 
00845       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
00846 #ifdef use_netCDF
00847           call write_attribute_netcdf( unit, NF_GLOBAL, name, rval, ival, cval, pack )
00848 #endif
00849       else
00850           call write_attribute( unit, 'GLOBAL '//trim(name), rval, ival, cval, pack )
00851       end if
00852 
00853       return
00854     end subroutine mpp_write_meta_global
00855 
00856 !versions of above to support <rval> and <ival> as scalars (because of f90 strict rank matching)
00857     subroutine mpp_write_meta_global_scalar_r( unit, name, rval, pack )
00858       integer, intent(in) :: unit
00859       character(len=*), intent(in) :: name
00860       real, intent(in) :: rval
00861       integer, intent(in), optional :: pack
00862 
00863       call mpp_write_meta_global( unit, name, rval=(/rval/), pack=pack )
00864       return
00865     end subroutine mpp_write_meta_global_scalar_r
00866 
00867     subroutine mpp_write_meta_global_scalar_i( unit, name, ival )
00868       integer, intent(in) :: unit
00869       character(len=*), intent(in) :: name
00870       integer, intent(in) :: ival
00871 
00872       call mpp_write_meta_global( unit, name, ival=(/ival/) )
00873       return
00874     end subroutine mpp_write_meta_global_scalar_i
00875 
00876     subroutine mpp_write_meta_var( unit, id, name, rval, ival, cval, pack )
00877 !writes a metadata attribute for variable <id> to unit <unit>
00878 !attribute <name> can be an real, integer or character
00879 !one and only one of rval, ival, and cval should be present
00880 !the first found will be used
00881 !for a non-netCDF file, it is encoded into a string "<id> <name> <val>"
00882       integer, intent(in) :: unit, id
00883       character(len=*), intent(in) :: name
00884       real,             intent(in), optional :: rval(:)
00885       integer,          intent(in), optional :: ival(:)
00886       character(len=*), intent(in), optional :: cval
00887       integer, intent(in), optional :: pack
00888 
00889       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
00890       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
00891       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
00892       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
00893       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
00894       if( mpp_file(unit)%initialized ) &
00895            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
00896 
00897       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
00898           call write_attribute_netcdf( unit, id, name, rval, ival, cval, pack )
00899       else
00900           write( text, '(a,i4,a)' )'VARIABLE ', id, ' '//name
00901           call write_attribute( unit, trim(text), rval, ival, cval, pack )
00902       end if
00903 
00904       return
00905     end subroutine mpp_write_meta_var
00906 
00907 !versions of above to support <rval> and <ival> as scalar (because of f90 strict rank matching)
00908     subroutine mpp_write_meta_scalar_r( unit, id, name, rval, pack )
00909       integer, intent(in) :: unit, id
00910       character(len=*), intent(in) :: name
00911       real, intent(in) :: rval
00912       integer, intent(in), optional :: pack
00913 
00914       call mpp_write_meta( unit, id, name, rval=(/rval/), pack=pack )
00915       return
00916     end subroutine mpp_write_meta_scalar_r
00917 
00918     subroutine mpp_write_meta_scalar_i( unit, id, name, ival )
00919       integer, intent(in) :: unit, id
00920       character(len=*), intent(in) :: name
00921       integer, intent(in) :: ival
00922 
00923       call mpp_write_meta( unit, id, name, ival=(/ival/) )
00924       return
00925     end subroutine mpp_write_meta_scalar_i
00926 
00927     subroutine mpp_write_meta_axis( unit, axis, name, units, longname, cartesian, sense, domain, data, cdata) !RV,bundles
00928 !load the values in an axistype (still need to call mpp_write)
00929 !write metadata attributes for axis
00930 !it is declared intent(inout) so you can nullify pointers in the incoming object if needed
00931 !the f90 standard doesn't guarantee that intent(out) on a type guarantees that its pointer components will be unassociated
00932       integer, intent(in) :: unit
00933       type(axistype), intent(inout) :: axis
00934       character(len=*), intent(in) :: name, units, longname
00935       character(len=*), intent(in), optional :: cartesian
00936       integer, intent(in), optional :: sense
00937       type(domain1D), intent(in), optional :: domain
00938       real, intent(in), optional :: data(:)
00939       character(len=*), intent(in), optional :: cdata(:) !RV,bundles
00940       integer :: is, ie, isg, ieg
00941 
00942       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
00943       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
00944       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
00945       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
00946       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
00947       if( mpp_file(unit)%initialized ) &
00948            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
00949 
00950 !pre-existing pointers need to be nullified
00951       if( ASSOCIATED(axis%data) )NULLIFY(axis%data)
00952       if( ASSOCIATED(axis%cdata) )NULLIFY(axis%cdata) !RV,bundles
00953 !load axistype
00954       axis%name     = name
00955       axis%units    = units
00956       axis%longname = longname
00957       if( PRESENT(cartesian) )axis%cartesian = cartesian
00958       if( PRESENT(sense)     )axis%sense     = sense
00959       if( PRESENT(domain)    )then
00960           axis%domain = domain
00961           call mpp_get_global_domain( domain, isg, ieg )
00962           call mpp_get_compute_domain( domain, is, ie )
00963       else
00964           axis%domain = NULL_DOMAIN1D
00965           if( PRESENT(data) )then
00966              isg=1; ieg=size(data); is=isg; ie=ieg
00967           endif
00968           if( PRESENT(cdata) )then !!RV,bundles
00969              isg=1; ieg=size(cdata); is=isg; ie=ieg !!RV,bundles
00970           endif !!RV,bundles
00971       end if
00972       if( PRESENT(data) )then
00973           if( PRESENT(domain) )then
00974               if( size(data).NE.ieg-isg+1 ) &
00975                    call mpp_error( FATAL, 'MPP_WRITE_META_AXIS: size(data).NE.domain%global%size.' )
00976               allocate( axis%data(isg:ieg) )
00977           else
00978               allocate( axis%data(size(data)) )
00979           end if
00980           axis%data = data
00981       end if
00982       if( PRESENT(cdata) )then !RV,bundles
00983           if( PRESENT(domain) )then !RV,bundles
00984               if( size(cdata).NE.ieg-isg+1 ) & !RV,bundles
00985                    call mpp_error( FATAL, 'MPP_WRITE_META_AXIS: size(cdata).NE.domain%global%size.' ) !RV,bundles
00986               allocate( axis%cdata(isg:ieg) ) !RV,bundles
00987               allocate( axis%data(isg:ieg) ) !RV,bundles
00988           else !RV,bundles
00989               allocate( axis%cdata(size(cdata)) ) !RV,bundles
00990               allocate( axis%data(size(cdata)) ) !RV,bundles
00991           end if !RV,bundles
00992           axis%cdata = cdata !RV,bundles
00993       end if !RV,bundles
00994 
00995 
00996 !write metadata
00997       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
00998 #ifdef use_netCDF
00999 !write axis def
01000 !space axes are always floats, time axis is always double
01001           if( ASSOCIATED(axis%data).or. ASSOCIATED(axis%cdata) )then !space axisRV,bundles 
01002               if( mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
01003                   error = NF_DEF_DIM( mpp_file(unit)%ncid, axis%name, ie-is+1,         axis%did )
01004               else
01005                   if(  ASSOCIATED(axis%data).and.(.not.present(cdata)))then !!RV,bundles
01006                   error = NF_DEF_DIM( mpp_file(unit)%ncid, axis%name, size(axis%data), axis%did )
01007                   else !!RV,bundles
01008                   error = NF_DEF_DIM( mpp_file(unit)%ncid, 'MAX_STRLEN', len(axis%cdata), axis%clenid )!!RV,bundles
01009                   error = NF_DEF_DIM( mpp_file(unit)%ncid, axis%name, size(axis%cdata), axis%did )!!RV,bundles
01010                   endif !!RV,bundles
01011               end if
01012               call netcdf_err(error)
01013               if(present(cdata)) then !!RV, bundles
01014               error = NF_DEF_VAR( mpp_file(unit)%ncid, axis%name, NF_CHAR, 2,(/axis%clenid, axis%did/), axis%id )
01015               call netcdf_err(error) !!Bundles
01016               else !!Bundles
01017               error = NF_DEF_VAR( mpp_file(unit)%ncid, axis%name, NF_FLOAT, 1, axis%did, axis%id ); call netcdf_err(error) !!Bundles
01018               endif !!Bundles
01019 
01020           else                            !time axis
01021               if( mpp_file(unit)%id.NE.-1 ) &
01022                    call mpp_error( FATAL, 'MPP_WRITE_META_AXIS: There is already a time axis for this file.' )
01023               error = NF_DEF_DIM( mpp_file(unit)%ncid, axis%name, NF_UNLIMITED, axis%did ); call netcdf_err(error)
01024               error = NF_DEF_VAR( mpp_file(unit)%ncid, axis%name, NF_DOUBLE, 1, axis%did, axis%id ); call netcdf_err(error)
01025               mpp_file(unit)%id = axis%id !file ID is the same as time axis varID
01026           end if
01027 #endif
01028       else
01029           varnum = varnum + 1
01030           axis%id = varnum
01031           axis%did = varnum
01032 !write axis def
01033           write( text, '(a,i4,a)' )'AXIS ', axis%id, ' name'
01034           call write_attribute( unit, trim(text), cval=axis%name )
01035           write( text, '(a,i4,a)' )'AXIS ', axis%id, ' size'
01036           if( ASSOCIATED(axis%data) )then !space axis
01037               if( mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
01038                   call write_attribute( unit, trim(text), ival=(/ie-is+1/) )
01039               else
01040                   if(ASSOCIATED(axis%data).and.(.not.present(cdata))) then !!RV,bundles
01041 
01042                   call write_attribute( unit, trim(text), ival=(/size(axis%data)/) )
01043                   else !!RV,bundles
01044                   call write_attribute( unit, trim(text), ival=(/size(axis%cdata)/) ) !!RV, bundles
01045                   endif !!RV, bundles
01046               end if
01047           else                            !time axis
01048               if( mpp_file(unit)%id.NE.-1 ) &
01049                    call mpp_error( FATAL, 'MPP_WRITE_META_AXIS: There is already a time axis for this file.' )
01050               call write_attribute( unit, trim(text), ival=(/0/) ) !a size of 0 indicates time axis
01051               mpp_file(unit)%id = axis%id
01052           end if
01053       end if
01054 !write axis attributes
01055       call mpp_write_meta( unit, axis%id, 'long_name', cval=axis%longname )
01056       call mpp_write_meta( unit, axis%id, 'units',     cval=axis%units    )
01057       if( PRESENT(cartesian) )call mpp_write_meta( unit, axis%id, 'cartesian_axis', cval=axis%cartesian )
01058       if( PRESENT(sense) )then
01059           if( sense.EQ.-1 )then
01060               call mpp_write_meta( unit, axis%id, 'positive', cval='down' )
01061           else if( sense.EQ.1 )then
01062               call mpp_write_meta( unit, axis%id, 'positive', cval='up' )
01063           end if
01064 !silently ignore values of sense other than +/-1.
01065       end if
01066       if( mpp_file(unit)%threading.EQ.MPP_MULTI .AND. mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
01067           call mpp_write_meta( unit, axis%id, 'domain_decomposition', ival=(/isg,ieg,is,ie/) )
01068       end if
01069       if( verbose ) write (stdout(), '(a,2i3,1x,a,2i3)') &
01070            'MPP_WRITE_META: Wrote axis metadata, pe, unit, axis%name, axis%id, axis%did=', &
01071            pe, unit, trim(axis%name), axis%id, axis%did 
01072 
01073       return
01074     end subroutine mpp_write_meta_axis
01075 
01076     subroutine mpp_write_meta_field( unit, field, axes, name, units, longname, min, max, missing, fill, scale, add, pack )
01077 !define field: must have already called mpp_write_meta(axis) for each axis
01078       integer, intent(in) :: unit
01079       type(fieldtype), intent(out) :: field
01080       type(axistype), intent(in) :: axes(:)
01081       character(len=*), intent(in) :: name, units, longname
01082       real, intent(in), optional :: min, max, missing, fill, scale, add
01083       integer, intent(in), optional :: pack
01084 !this array is required because of f77 binding on netCDF interface
01085       integer, allocatable :: axis_id(:)
01086       real :: a, b
01087       integer :: i
01088 
01089       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
01090       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
01091       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
01092       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
01093       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
01094       if( mpp_file(unit)%initialized ) &
01095            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
01096 !
01097 ! 2005-07-20, Luis Kornblueh, MPIMet
01098 !     if( ASSOCIATED(field%axes) )NULLIFY(field%axes)
01099 !fill in field metadata
01100       field%name = name
01101       field%units = units
01102       field%longname = longname
01103       allocate( field%axes(size(axes)) )
01104       field%axes = axes
01105       field%time_axis_index = -1 !this value will never match any axis index
01106 !size is buffer area for the corresponding axis info: it is required to buffer this info in the fieldtype
01107 !because axis might be reused in different files
01108       allocate( field%size(size(axes)) )
01109       do i = 1,size(axes)
01110          if( ASSOCIATED(axes(i)%data) )then !space axis
01111              field%size(i) = size(axes(i)%data)
01112          else               !time
01113              field%size(i) = 1
01114              field%time_axis_index = i
01115          end if
01116       end do
01117 !attributes
01118       if( PRESENT(min) )field%min = min
01119       if( PRESENT(max) )field%max = max
01120       if( PRESENT(missing) )field%missing = missing
01121       if( PRESENT(fill) )field%fill = fill
01122       if( PRESENT(scale) )field%scale = scale
01123       if( PRESENT(add) )field%add = add
01124       
01125 !pack is currently used only for netCDF
01126       field%pack = 2        !default write 32-bit floats
01127       if( PRESENT(pack) )field%pack = pack
01128       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
01129 #ifdef use_netCDF
01130           if (.not. allocated(axis_id)) allocate( axis_id(size(field%axes)) )
01131           do i = 1,size(field%axes)
01132              axis_id(i) = field%axes(i)%did
01133           end do
01134 !write field def
01135           select case (field%pack)
01136               case(1)
01137                   error = NF_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_DOUBLE, size(field%axes), axis_id, field%id )
01138               case(2)
01139                   error = NF_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_FLOAT,  size(field%axes), axis_id, field%id )
01140               case(4)
01141                   if( .NOT.PRESENT(scale) .OR. .NOT.PRESENT(add) ) &
01142                        call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: scale and add must be supplied when pack=4.' )
01143                   error = NF_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_SHORT,  size(field%axes), axis_id, field%id )
01144               case(8)
01145                   if( .NOT.PRESENT(scale) .OR. .NOT.PRESENT(add) ) &
01146                        call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: scale and add must be supplied when pack=8.' )
01147                   error = NF_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_BYTE,   size(field%axes), axis_id, field%id )
01148               case default
01149                   call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: only legal packing values are 1,2,4,8.' )
01150           end select
01151           call netcdf_err(error)
01152 #endif
01153       else
01154           varnum = varnum + 1
01155           field%id = varnum
01156           if( PRESENT(pack) )call mpp_error( WARNING, 'MPP_WRITE_META: Packing is currently available only on netCDF files.' )
01157 !write field def
01158           write( text, '(a,i4,a)' )'FIELD ', field%id, ' name'
01159           call write_attribute( unit, trim(text), cval=field%name )
01160           write( text, '(a,i4,a)' )'FIELD ', field%id, ' axes'
01161           call write_attribute( unit, trim(text), ival=field%axes(:)%did )
01162       end if
01163 !write field attributes: these names follow netCDF conventions
01164       call mpp_write_meta( unit, field%id, 'long_name', cval=field%longname )
01165       call mpp_write_meta( unit, field%id, 'units',     cval=field%units    )
01166 !all real attributes must be written as packed
01167       if( PRESENT(min) .AND. PRESENT(max) )then
01168           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
01169               call mpp_write_meta( unit, field%id, 'valid_range', rval=(/min,max/), pack=pack )
01170           else
01171               a = nint((min-add)/scale)
01172               b = nint((max-add)/scale)
01173               call mpp_write_meta( unit, field%id, 'valid_range', rval=(/a,  b  /), pack=pack )
01174           end if
01175       else if( PRESENT(min) )then
01176           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
01177               call mpp_write_meta( unit, field%id, 'valid_min', rval=field%min, pack=pack )
01178           else
01179               a = nint((min-add)/scale)
01180               call mpp_write_meta( unit, field%id, 'valid_min', rval=a, pack=pack )
01181           end if
01182       else if( PRESENT(max) )then
01183           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
01184               call mpp_write_meta( unit, field%id, 'valid_max', rval=field%max, pack=pack )
01185           else
01186               a = nint((max-add)/scale)
01187               call mpp_write_meta( unit, field%id, 'valid_max', rval=a, pack=pack )
01188           end if
01189       end if
01190       if( PRESENT(missing) )then
01191           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
01192               call mpp_write_meta( unit, field%id, 'missing_value', rval=field%missing, pack=pack )
01193           else
01194               a = nint((missing-add)/scale)
01195               call mpp_write_meta( unit, field%id, 'missing_value', rval=a, pack=pack )
01196           end if
01197       end if
01198       if( PRESENT(fill) )then
01199           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
01200               call mpp_write_meta( unit, field%id, '_FillValue', rval=field%fill, pack=pack )
01201           else
01202               a = nint((fill-add)/scale)
01203               call mpp_write_meta( unit, field%id, '_FillValue', rval=a, pack=pack )
01204           end if
01205       end if
01206       if( field%pack.NE.1 .AND. field%pack.NE.2 )then
01207           call mpp_write_meta( unit, field%id, 'packing', ival=field%pack )
01208           if( PRESENT(scale) )call mpp_write_meta( unit, field%id, 'scale_factor',  rval=field%scale )
01209           if( PRESENT(add)   )call mpp_write_meta( unit, field%id, 'add_offset',    rval=field%add   )
01210       end if
01211       if( verbose ) write (stdout(), '(a,2i3,1x,a,i3)') 'MPP_WRITE_META: Wrote field metadata: pe, unit, field%name, field%id=', &
01212            pe, unit, trim(field%name), field%id 
01213 
01214       return
01215     end subroutine mpp_write_meta_field
01216 
01217     subroutine write_attribute( unit, name, rval, ival, cval, pack )
01218 !called to write metadata for non-netCDF I/O
01219       integer, intent(in) :: unit
01220       character(len=*), intent(in) :: name
01221       real, intent(in), optional :: rval(:)
01222       integer, intent(in), optional :: ival(:)
01223       character(len=*), intent(in), optional :: cval
01224 !pack is currently ignored in this routine: only used by netCDF I/O
01225       integer, intent(in), optional :: pack
01226 
01227       if( mpp_file(unit)%nohdrs )return
01228 !encode text string
01229       if( PRESENT(rval) )then
01230           write( text,* )trim(name)//'=', rval
01231       else if( PRESENT(ival) )then
01232           write( text,* )trim(name)//'=', ival
01233       else if( PRESENT(cval) )then
01234           text = ' '//trim(name)//'='//trim(cval)
01235       else
01236           call mpp_error( FATAL, 'WRITE_ATTRIBUTE: one of rval, ival, cval must be present.' )
01237       end if
01238       if( mpp_file(unit)%format.EQ.MPP_ASCII )then
01239 !implies sequential access
01240           write( unit,fmt='(a)' )trim(text)//char(10)
01241       else                      !MPP_IEEE32 or MPP_NATIVE
01242           if( mpp_file(unit)%access.EQ.MPP_SEQUENTIAL )then
01243               write(unit)trim(text)//char(10)
01244           else                  !MPP_DIRECT
01245               write( unit,rec=mpp_file(unit)%record )trim(text)//char(10)
01246               if( verbose ) write (stdout(), '(a,i3,a,i3)') 'WRITE_ATTRIBUTE: PE=', pe, ' wrote record ', mpp_file(unit)%record
01247               mpp_file(unit)%record = mpp_file(unit)%record + 1
01248           end if
01249       end if
01250       return
01251     end subroutine write_attribute
01252 
01253     subroutine write_attribute_netcdf( unit, id, name, rval, ival, cval, pack )
01254 !called to write metadata for netCDF I/O
01255       integer, intent(in) :: unit
01256       integer, intent(in) :: id
01257       character(len=*), intent(in) :: name
01258       real,             intent(in), optional :: rval(:)
01259       integer,          intent(in), optional :: ival(:)
01260       character(len=*), intent(in), optional :: cval
01261       integer, intent(in), optional :: pack
01262       integer :: lenc
01263       integer, allocatable :: rval_i(:)
01264 #ifdef use_netCDF
01265       integer :: ii, il_bytesize, il_iosize
01266       integer :: il_int_iosize, il_rbyt
01267 !
01268       if( PRESENT(rval) )then
01269 !
01270       il_bytesize = BIT_SIZE(ii)/8
01271       INQUIRE (iolength=il_iosize) ii
01272       il_int_iosize = il_iosize
01273       INQUIRE (iolength=il_iosize) rval(1)
01274       il_rbyt = il_iosize/il_int_iosize*il_bytesize
01275 !pack is only meaningful for FP numbers
01276           if( PRESENT(pack) )then
01277               if( pack.EQ.1 )then
01278                   if( il_rbyt.EQ.DOUBLE_KIND )then
01279                       error = NF_PUT_ATT_DOUBLE( mpp_file(unit)%ncid, id, name, NF_DOUBLE, size(rval), rval )
01280                   else if( il_rbyt.EQ.FLOAT_KIND )then
01281                       call mpp_error( WARNING, &
01282                            'WRITE_ATTRIBUTE_NETCDF: attempting to write internal 32-bit real as external 64-bit.' )
01283                       error = NF_PUT_ATT_REAL  ( mpp_file(unit)%ncid, id, name, NF_DOUBLE, size(rval), rval )
01284                   end if
01285                   call netcdf_err(error)
01286               else if( pack.EQ.2 )then
01287                   if( il_rbyt.EQ.DOUBLE_KIND )then
01288                       error = NF_PUT_ATT_DOUBLE( mpp_file(unit)%ncid, id, name, NF_FLOAT,  size(rval), rval )
01289                   else if( il_rbyt.EQ.FLOAT_KIND )then
01290                       error = NF_PUT_ATT_REAL  ( mpp_file(unit)%ncid, id, name, NF_FLOAT,  size(rval), rval )
01291                   end if
01292                   call netcdf_err(error)
01293               else if( pack.EQ.4 )then
01294                   allocate( rval_i(size(rval)) )
01295                   rval_i = rval
01296                   call mpp_flush(6)
01297                   if( il_rbyt.EQ.DOUBLE_KIND )then
01298                       error = NF_PUT_ATT_DOUBLE( mpp_file(unit)%ncid, id, name, NF_SHORT,  size(rval_i), rval )
01299                   else if( il_rbyt.EQ.FLOAT_KIND )then
01300                       error = NF_PUT_ATT_REAL  ( mpp_file(unit)%ncid, id, name, NF_SHORT,  size(rval_i), rval )
01301                   end if
01302                   call netcdf_err(error)
01303                   deallocate(rval_i)
01304               else if( pack.EQ.8 )then
01305                   allocate( rval_i(size(rval)) )
01306                   rval_i = rval
01307                   if( il_rbyt.EQ.DOUBLE_KIND )then
01308                       error = NF_PUT_ATT_DOUBLE( mpp_file(unit)%ncid, id, name, NF_BYTE,   size(rval_i), rval )
01309                   else if( il_rbyt.EQ.FLOAT_KIND )then
01310                       error = NF_PUT_ATT_REAL  ( mpp_file(unit)%ncid, id, name, NF_BYTE,   size(rval_i), rval )
01311                   end if
01312                   call netcdf_err(error)
01313                   deallocate(rval_i)
01314               else
01315                   call mpp_error( FATAL, 'WRITE_ATTRIBUTE_NETCDF: only legal packing values are 1,2,4,8.' )
01316               end if
01317           else
01318 !default is to write FLOATs (32-bit)
01319               if( il_rbyt.EQ.DOUBLE_KIND )then
01320                   error = NF_PUT_ATT_DOUBLE( mpp_file(unit)%ncid, id, name, NF_FLOAT,  size(rval), rval )
01321               else if( il_rbyt.EQ.FLOAT_KIND )then
01322                   error = NF_PUT_ATT_REAL  ( mpp_file(unit)%ncid, id, name, NF_FLOAT,  size(rval), rval )
01323               end if
01324               call netcdf_err(error)
01325           end if
01326       else if( PRESENT(ival) )then
01327           error = NF_PUT_ATT_INT ( mpp_file(unit)%ncid, id, name, NF_INT, size(ival), ival ); call netcdf_err(error)
01328       else if( present(cval) )then
01329           error = NF_PUT_ATT_TEXT( mpp_file(unit)%ncid, id, name, len_trim(cval), cval ); call netcdf_err(error)
01330       else
01331           call mpp_error( FATAL, 'WRITE_ATTRIBUTE_NETCDF: one of rval, ival, cval must be present.' )
01332       end if
01333 #endif /* use_netCDF */ 
01334       return
01335     end subroutine write_attribute_netcdf
01336 
01337 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
01338 !                                                                      !
01339 !                             MPP_WRITE                                !
01340 !                                                                      !
01341 ! mpp_write is used to write data to the file on <unit> using the      !
01342 ! file parameters supplied by mpp_open(). Axis and field definitions   !
01343 ! must have previously been written to the file using mpp_write_meta.  !
01344 !                                                                      !
01345 ! mpp_write can take 2 forms, one for distributed data and one for     !
01346 ! non-distributed data. Distributed data refer to arrays whose two     !
01347 ! fastest-varying indices are domain-decomposed. Distributed data      !
01348 ! must be 2D or 3D (in space). Non-distributed data can be 0-3D.       !
01349 !                                                                      !
01350 ! In all calls to mpp_write, tstamp is an optional argument. It is to  !
01351 ! be omitted if the field was defined not to be a function of time.    !
01352 ! Results are unpredictable if the argument is supplied for a time-    !
01353 ! independent field, or omitted for a time-dependent field. Repeated   !
01354 ! writes of a time-independent field are also not recommended. One     !
01355 ! time level of one field is written per call.                         !
01356 !                                                                      !
01357 !                                                                      !
01358 ! For non-distributed data, use                                        !
01359 !                                                                      !
01360 !  mpp_write( unit, field, data, tstamp )                              !
01361 !     integer, intent(in) :: unit                                      !
01362 !     type(fieldtype), intent(in) :: field                             !
01363 !     real, optional :: tstamp                                         !
01364 !     data is real and can be scalar or of rank 1-3.                   !
01365 !                                                                      !
01366 ! For distributed data, use                                            !
01367 !                                                                      !
01368 !  mpp_write( unit, field, domain, data, tstamp )                      !
01369 !     integer, intent(in) :: unit                                      !
01370 !     type(fieldtype), intent(in) :: field                             !
01371 !     type(domain2D), intent(in) :: domain                             !
01372 !     real, optional :: tstamp                                         !
01373 !     data is real and can be of rank 2 or 3.                          !
01374 !                                                                      !
01375 !  mpp_write( unit, axis )                                             !
01376 !     integer, intent(in) :: unit                                      !
01377 !     type(axistype), intent(in) :: axis                               !
01378 !                                                                      !
01379 ! This call writes the actual co-ordinate values along each space      !
01380 ! axis. It must be called once for each space axis after all other     !
01381 ! metadata has been written.                                           !
01382 !                                                                      !
01383 ! The mpp_write package also includes the routine write_record which   !
01384 ! performs the actual write. This routine is private to this module.   !
01385 !                                                                      !
01386 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
01387 #define MPP_WRITE_2DDECOMP_1D_ mpp_write_2ddecomp_r1d
01388 #define MPP_WRITE_2DDECOMP_2D_ mpp_write_2ddecomp_r2d
01389 #define MPP_WRITE_2DDECOMP_3D_ mpp_write_2ddecomp_r3d
01390 #define MPP_WRITE_2DDECOMP_4D_ mpp_write_2ddecomp_r4d
01391 #define MPP_TYPE_ real
01392 #include <mpp_write_2Ddecomp.h>
01393 
01394 #define MPP_WRITE_ mpp_write_r0D
01395 #define MPP_TYPE_ real
01396 #define MPP_RANK_ !
01397 #define MPP_WRITE_RECORD_ call write_record( unit, field, 1, (/data/), tstamp )
01398 #include <mpp_write.h>
01399 
01400 #define MPP_WRITE_ mpp_write_r1D
01401 #define MPP_TYPE_ real
01402 #define MPP_WRITE_RECORD_ call write_record( unit, field, size(data), data, tstamp )
01403 #define MPP_RANK_ (:)
01404 #include <mpp_write.h>
01405 
01406 #define MPP_WRITE_ mpp_write_r2D
01407 #define MPP_TYPE_ real
01408 #define MPP_WRITE_RECORD_ call write_record( unit, field, size(data), data, tstamp )
01409 #define MPP_RANK_ (:,:)
01410 #include <mpp_write.h>
01411 
01412 #define MPP_WRITE_ mpp_write_r3D
01413 #define MPP_TYPE_ real
01414 #define MPP_WRITE_RECORD_ call write_record( unit, field, size(data), data, tstamp )
01415 #define MPP_RANK_ (:,:,:)
01416 #include <mpp_write.h>
01417 
01418 #define MPP_WRITE_ mpp_write_r4D
01419 #define MPP_TYPE_ real
01420 #define MPP_WRITE_RECORD_ call write_record( unit, field, size(data), data, tstamp )
01421 #define MPP_RANK_ (:,:,:,:)
01422 #include <mpp_write.h>
01423 
01424     subroutine mpp_write_axis( unit, axis )
01425       integer, intent(in) :: unit
01426       type(axistype), intent(in) :: axis
01427       type(fieldtype) :: field
01428       integer :: is, ie
01429 
01430 
01431       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_WRITE: must first call mpp_io_init.' )
01432       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE: invalid unit number.' )
01433       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
01434       if( mpp_file(unit)%fileset  .EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
01435 !we convert axis to type(fieldtype) in order to call write_record
01436       field = default_field
01437       allocate( field%axes(1) )
01438       field%axes(1) = axis
01439       allocate( field%size(1) )
01440       field%id = axis%id
01441       if( mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
01442           call mpp_get_compute_domain( axis%domain, is, ie )
01443           field%size(1) = ie-is+1
01444 !!RV,bundles
01445           if(associated( axis%cdata)) then 
01446           call write_record_c( unit, field, field%size(1), axis%cdata(is:) ) 
01447           else 
01448           call write_record( unit, field, field%size(1), axis%data(is:) )
01449           endif 
01450 !!RV,bundles
01451       else
01452 !!RV,bundles
01453           if(associated( axis%cdata)) then 
01454           field%size(1) = size(axis%cdata)
01455           call write_record_c(unit,field, field%size(1), axis%cdata )
01456           else 
01457           field%size(1) = size(axis%data)
01458           call write_record( unit, field, field%size(1), axis%data )
01459           endif 
01460 !!RV,bundles
01461       end if
01462       return
01463     end subroutine mpp_write_axis
01464 
01465     subroutine write_record_c( unit, field, nwords, cdata, time_in, domain ) !!RV,bundles
01466 !routine that is finally called by all mpp_write routines to perform the write
01467 !a non-netCDF record contains:
01468 !      field ID
01469 !      a set of 4 coordinates (is:ie,js:je) giving the data subdomain
01470 !      a timelevel and a timestamp (=NULLTIME if field is static)
01471 !      3D real data (stored as 1D)
01472 !if you are using direct access I/O, the RECL argument to OPEN must be large enough for the above
01473 !in a global direct access file, record position on PE is given by %record.
01474 
01475 !Treatment of timestamp:
01476 !   We assume that static fields have been passed without a timestamp.
01477 !   Here that is converted into a timestamp of NULLTIME.
01478 !   For non-netCDF fields, field is treated no differently, but is written
01479 !   with a timestamp of NULLTIME. There is no check in the code to prevent
01480 !   the user from repeatedly writing a static field.
01481 
01482       integer, intent(in) :: unit, nwords
01483       type(fieldtype), intent(in) :: field
01484 !RV,bundles
01485       character(len=64), intent(in) :: cdata(nwords)
01486       real(DOUBLE_KIND), intent(in), optional :: time_in
01487       type(domain2D), intent(in), optional :: domain
01488 !RV      integer, dimension(size(field%axes)) :: start, axsiz
01489 
01490       integer,allocatable,dimension(:) :: start, axsiz
01491 !RV
01492       real :: time
01493       integer :: time_level
01494       logical :: newtime
01495       integer :: subdomain(4)
01496       integer :: packed_data(nwords)
01497       integer :: i, is, ie, js, je, isg, ieg, jsg, jeg, isizc, jsizc, isizg, jsizg
01498 #ifdef use_netCDF
01499       integer :: ii, il_bytesize, il_iosize
01500       integer :: il_int_iosize, il_rbyt
01501 #endif
01502 
01503 #ifdef use_CRI_pointers
01504       real(FLOAT_KIND) :: data_r4(nwords)
01505       pointer( ptr1, data_r4)
01506       pointer( ptr2, packed_data)
01507 
01508       if (mpp_io_stack_size < 2*nwords) call mpp_io_set_stack_size(2*nwords)
01509 
01510       ptr1 = LOC(mpp_io_stack(1))
01511       ptr2 = LOC(mpp_io_stack(nwords+1))
01512 #endif
01513 
01514       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_WRITE: must first call mpp_io_init.' )
01515       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE: invalid unit number.' )
01516       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
01517       if( mpp_file(unit)%fileset  .EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
01518 
01519 !RV
01520       allocate(start(size(field%axes)))
01521       allocate(axsiz(size(field%axes)))
01522 !RV
01523       if( .NOT.mpp_file(unit)%initialized )then
01524 !this is the first call to mpp_write
01525 !we now declare the file to be initialized
01526 !if this is netCDF we switch file from DEFINE mode to DATA mode
01527           if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
01528 #ifdef use_netCDF
01529 !NOFILL is probably required for parallel: any circumstances in which not advisable?
01530               error = NF_SET_FILL( mpp_file(unit)%ncid, NF_NOFILL, i ); call netcdf_err(error)
01531               if( mpp_file(unit)%action.EQ.MPP_WRONLY )error = NF_ENDDEF(mpp_file(unit)%ncid); call netcdf_err(error)
01532 #endif
01533           else
01534               call mpp_write_meta( unit, 'END', cval='metadata' )
01535           end if
01536           mpp_file(unit)%initialized = .TRUE.
01537           if( verbose ) write (stdout(), '(a,i3,a)') 'MPP_WRITE: PE=', pe, ' initialized file '//trim(mpp_file(unit)%name)//'.'
01538       end if
01539 
01540 !initialize time: by default assume NULLTIME
01541       time = NULLTIME
01542       time_level = -1
01543       newtime = .FALSE.
01544       if( PRESENT(time_in) )time = time_in
01545 !increment time level if new time
01546       if( time.GT.mpp_file(unit)%time+EPSILON(time) )then !new time
01547           mpp_file(unit)%time_level = mpp_file(unit)%time_level + 1
01548           mpp_file(unit)%time = time
01549           newtime = .TRUE.
01550       end if
01551       if( verbose ) write (stdout(), '(a,2i3,2i5,es13.5)') 'MPP_WRITE: PE, unit, %id, %time_level, %time=',&
01552            pe, unit, mpp_file(unit)%id, mpp_file(unit)%time_level, mpp_file(unit)%time
01553 
01554       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
01555 !define netCDF data block to be written:
01556 !  time axis: START = time level
01557 !             AXSIZ = 1
01558 !  space axis: if there is no domain info
01559 !              START = 1
01560 !              AXSIZ = field%size(axis)
01561 !          if there IS domain info:
01562 !              start of domain is compute%start_index for multi-file I/O
01563 !                                 global%start_index for all other cases
01564 !              this number must be converted to 1 for NF_PUT_VAR
01565 !                  (netCDF fortran calls are with reference to 1),
01566 !          So, START = compute%start_index - <start of domain> + 1
01567 !              AXSIZ = usually compute%size
01568 !          However, if compute%start_index-compute%end_index+1.NE.compute%size,
01569 !              we assume that the call is passing a subdomain.
01570 !              To pass a subdomain, you must pass a domain2D object that satisfies the following:
01571 !                  global%start_index must contain the <start of domain> as defined above;
01572 !                  the data domain and compute domain must refer to the subdomain being passed.
01573 !              In this case, START = compute%start_index - <start of domain> + 1
01574 !                            AXSIZ = compute%start_index - compute%end_index + 1! NOTE: passing of subdomains will fail for multi-PE single-threaded I/O,
01575 !       since that attempts to gather all data on PE 0.
01576           start = 1
01577           do i = 1,size(field%axes)
01578              axsiz(i) = field%size(i)
01579              if( i.EQ.field%time_axis_index )start(i) = mpp_file(unit)%time_level
01580              start(i) = max(start(i),1)
01581           end do
01582           if( PRESENT(domain) )then
01583               call mpp_get_compute_domain( domain, is,  ie,  js,  je,  xsize=isizc, ysize=jsizc )
01584               call mpp_get_global_domain ( domain, isg, ieg, jsg, jeg, xsize=isizg, ysize=jsizg )
01585               axsiz(1) = isizc
01586               axsiz(2) = jsizc
01587               if( npes.GT.1 .AND. mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
01588                   start(1) = is - isg + 1
01589                   start(2) = js - jsg + 1
01590               else
01591                   if( isizc.NE.ie-is+1 )then
01592                       start(1) = is - isg + 1
01593                       axsiz(1) = ie - is + 1
01594                   end if
01595                   if( jsizc.NE.je-js+1 )then
01596                       start(2) = js - jsg + 1
01597                       axsiz(2) = je - js + 1
01598                   end if
01599               end if
01600           end if
01601           if( debug ) &
01602            write (stdout(), '(a,2i3,12i4)') 'a WRITE_RECORD: PE, unit, start, axsiz=', pe, unit, start, axsiz
01603 #ifdef use_netCDF
01604 !write time information if new time
01605           if( newtime )then
01606               il_bytesize = BIT_SIZE(ii)/8
01607               INQUIRE (iolength=il_iosize) ii
01608               il_int_iosize = il_iosize
01609               INQUIRE (iolength=il_iosize) time
01610               il_rbyt = il_iosize/il_int_iosize*il_bytesize
01611               if( il_rbyt.EQ.DOUBLE_KIND )then
01612                   error = NF_PUT_VAR1_DOUBLE( mpp_file(unit)%ncid, mpp_file(unit)%id, mpp_file(unit)%time_level, time )
01613               else if( il_rbyt.EQ.FLOAT_KIND )then
01614                   error = NF_PUT_VAR1_REAL  ( mpp_file(unit)%ncid, mpp_file(unit)%id, mpp_file(unit)%time_level, time )
01615               end if
01616           end if
01617           if( field%pack.LE.2 )then
01618                  write(6,*) ' Iam here 6!'
01619                  call mpp_flush(6)
01620                error = NF_PUT_VARA_TEXT( mpp_file(unit)%ncid, field%id, (/1,start/), (/len(cdata),axsiz/), cdata ) !!RV, bundles
01621                  write(6,*) ' Iam here 7!'
01622                  call mpp_flush(6)
01623           else  !!RV, bundles
01624                  write(6,*) ' Iam here 8!'
01625                  call mpp_flush(6)
01626             call mpp_error( FATAL, 'MPP_WRITE_RECORD_C: pack on text !' )
01627           end if !!RV, bundles
01628                  write(6,*) ' Iam here 9!',error
01629                  call mpp_flush(6)
01630           call netcdf_err(error)
01631 #endif
01632       else                      !non-netCDF
01633 !subdomain contains (/is,ie,js,je/)
01634           if( PRESENT(domain) )then
01635               subdomain(:) = (/ is, ie, js, je /)
01636           else
01637               subdomain(:) = -1    ! -1 means use global value from axis metadata
01638           end if
01639           if( mpp_file(unit)%format.EQ.MPP_ASCII )then
01640 !implies sequential access
01641               write( unit,* )field%id, subdomain, time_level, time, cdata
01642           else                      !MPP_IEEE32 or MPP_NATIVE
01643               if( mpp_file(unit)%access.EQ.MPP_SEQUENTIAL )then
01644 #ifdef __sgi
01645                   if( mpp_file(unit)%format.EQ.MPP_IEEE32 )then
01646                       write(unit)field%id, subdomain, time_level, time, cdata
01647                   else
01648                       write(unit)field%id, subdomain, time_level, time, cdata
01649                   end if
01650 #else
01651                   write(unit)field%id, subdomain, time_level, time, cdata
01652 #endif
01653               else                  !MPP_DIRECT
01654 #ifdef __sgi
01655                   if( mpp_file(unit)%format.EQ.MPP_IEEE32 )then
01656                       write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, cdata
01657                   else
01658                       write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, cdata
01659                   end if
01660 #else
01661                   write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, cdata
01662 #endif
01663                   if( debug ) write (stdout(), '(a,i3,a,i3)') 'MPP_WRITE: PE=', pe, ' wrote record ', mpp_file(unit)%record
01664               end if
01665           end if
01666       end if
01667 
01668 !recompute current record for direct access I/O
01669       if( mpp_file(unit)%access.EQ.MPP_DIRECT )then
01670           if( mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
01671 !assumes all PEs participate in I/O: modify later
01672               mpp_file(unit)%record = mpp_file(unit)%record + records_per_pe*npes
01673           else
01674               mpp_file(unit)%record = mpp_file(unit)%record + records_per_pe
01675           end if
01676       end if
01677 !RV
01678       deallocate(start)
01679       deallocate(axsiz)
01680 !RV
01681       return
01682     end subroutine write_record_c
01683 
01684     subroutine write_record_b( unit, field, nwords, data, time_in, domain,block_id )
01685 !routine that is finally called by all mpp_write routines to perform the write
01686 !a non-netCDF record contains:
01687 !      field ID
01688 !      a set of 4 coordinates (is:ie,js:je) giving the data subdomain
01689 !      a timelevel and a timestamp (=NULLTIME if field is static)
01690 !      3D real data (stored as 1D)
01691 !if you are using direct access I/O, the RECL argument to OPEN must be large enough for the above
01692 !in a global direct access file, record position on PE is given by %record.
01693 
01694 !Treatment of timestamp:
01695 !   We assume that static fields have been passed without a timestamp.
01696 !   Here that is converted into a timestamp of NULLTIME.
01697 !   For non-netCDF fields, field is treated no differently, but is written
01698 !   with a timestamp of NULLTIME. There is no check in the code to prevent
01699 !   the user from repeatedly writing a static field.
01700 !RV,SGI:
01701 !   The routine write_record_b is a special clone of write_record.
01702 !   The assumption is here that the user has declared a data structure
01703 !   like a(:,:,:,1:no_of_blocks). For whatever reason that arrray is written
01704 !   is not written in a big chunk but on a per block basis for a certain time
01705 !   stamp: At t_i write a(:,:,:,block_id).After all block are written the data structure of the file
01706 !   should look like as if array a was written in one big chunk.
01707 !   Moreover, I assume that the time axis is always the last one and that the block axis
01708 !   comes befor the time axis, means the block axis is the last pseudo spatial axis.
01709 
01710       integer, intent(in) :: unit, nwords
01711       type(fieldtype), intent(in) :: field
01712       real, intent(in) :: data(nwords)
01713       real(DOUBLE_KIND), intent(in), optional :: time_in
01714       integer,intent(in),optional :: block_id
01715       type(domain2D), intent(in), optional :: domain
01716 !RV      integer, dimension(size(field%axes)) :: start, axsiz
01717 
01718       integer,allocatable,dimension(:) :: start, axsiz
01719 !RV
01720       real :: time
01721       integer :: time_level
01722       logical :: newtime
01723       integer :: subdomain(4)
01724       integer :: packed_data(nwords)
01725       integer :: i, is, ie, js, je, isg, ieg, jsg, jeg, isizc, jsizc, isizg, jsizg
01726 #ifdef use_netCDF
01727       integer :: ii, il_bytesize, il_iosize
01728       integer :: il_int_iosize, il_rbyt
01729 #endif
01730 
01731 #ifdef use_CRI_pointers
01732       real(FLOAT_KIND) :: data_r4(nwords)
01733       pointer( ptr1, data_r4)
01734       pointer( ptr2, packed_data)
01735 
01736       if (mpp_io_stack_size < 2*nwords) call mpp_io_set_stack_size(2*nwords)
01737 
01738       ptr1 = LOC(mpp_io_stack(1))
01739       ptr2 = LOC(mpp_io_stack(nwords+1))
01740 #endif
01741 
01742       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_WRITE: must first call mpp_io_init.' )
01743       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE: invalid unit number.' )
01744       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
01745       if( mpp_file(unit)%fileset  .EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
01746 
01747 !RV
01748       allocate(start(size(field%axes)))
01749       allocate(axsiz(size(field%axes)))
01750 !RV
01751       if( .NOT.mpp_file(unit)%initialized )then
01752 !this is the first call to mpp_write
01753 !we now declare the file to be initialized
01754 !if this is netCDF we switch file from DEFINE mode to DATA mode
01755           if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
01756 #ifdef use_netCDF
01757 !NOFILL is probably required for parallel: any circumstances in which not advisable?
01758               error = NF_SET_FILL( mpp_file(unit)%ncid, NF_NOFILL, i ); call netcdf_err(error)
01759               if( mpp_file(unit)%action.EQ.MPP_WRONLY )error = NF_ENDDEF(mpp_file(unit)%ncid); call netcdf_err(error)
01760 #endif
01761           else
01762               call mpp_write_meta( unit, 'END', cval='metadata' )
01763           end if
01764           mpp_file(unit)%initialized = .TRUE.
01765           if( verbose ) write (stdout(), '(a,i3,a)') 'MPP_WRITE: PE=', pe, ' initialized file '//trim(mpp_file(unit)%name)//'.'
01766       end if
01767 
01768 !initialize time: by default assume NULLTIME
01769       time = NULLTIME
01770       time_level = -1
01771       newtime = .FALSE.
01772       if( PRESENT(time_in) )time = time_in
01773 !increment time level if new time
01774       if( time.GT.mpp_file(unit)%time+EPSILON(time) )then !new time
01775           mpp_file(unit)%time_level = mpp_file(unit)%time_level + 1
01776           mpp_file(unit)%time = time
01777           newtime = .TRUE.
01778       end if
01779       if( verbose ) write (stdout(), '(a,2i3,2i5,es13.5)') 'MPP_WRITE: PE, unit, %id, %time_level, %time=',&
01780            pe, unit, mpp_file(unit)%id, mpp_file(unit)%time_level, mpp_file(unit)%time
01781 
01782       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
01783 !define netCDF data block to be written:
01784 !  time axis: START = time level
01785 !             AXSIZ = 1
01786 !  space axis: if there is no domain info
01787 !              START = 1
01788 !              AXSIZ = field%size(axis)
01789 !          if there IS domain info:
01790 !              start of domain is compute%start_index for multi-file I/O
01791 !                                 global%start_index for all other cases
01792 !              this number must be converted to 1 for NF_PUT_VAR
01793 !                  (netCDF fortran calls are with reference to 1),
01794 !          So, START = compute%start_index - <start of domain> + 1
01795 !              AXSIZ = usually compute%size
01796 !          However, if compute%start_index-compute%end_index+1.NE.compute%size,
01797 !              we assume that the call is passing a subdomain.
01798 !              To pass a subdomain, you must pass a domain2D object that satisfies the following:
01799 !                  global%start_index must contain the <start of domain> as defined above;
01800 !                  the data domain and compute domain must refer to the subdomain being passed.
01801 !              In this case, START = compute%start_index - <start of domain> + 1
01802 !                            AXSIZ = compute%start_index - compute%end_index + 1
01803 ! NOTE: passing of subdomains will fail for multi-PE single-threaded I/O,
01804 !       since that attempts to gather all data on PE 0.
01805           start = 1
01806           do i = 1,size(field%axes)
01807              axsiz(i) = field%size(i)
01808              if( i.EQ.field%time_axis_index )start(i) = mpp_file(unit)%time_level
01809          start(i) = max(start(i),1) 
01810           end do
01811           if( PRESENT(domain) )then
01812               call mpp_get_compute_domain( domain, is,  ie,  js,  je,  xsize=isizc, ysize=jsizc )
01813               call mpp_get_global_domain ( domain, isg, ieg, jsg, jeg, xsize=isizg, ysize=jsizg )
01814               axsiz(1) = isizc
01815               axsiz(2) = jsizc
01816               if( npes.GT.1 .AND. mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
01817                   start(1) = is - isg + 1
01818                   start(2) = js - jsg + 1
01819               else
01820                   if( isizc.NE.ie-is+1 )then
01821                       start(1) = is - isg + 1
01822                       axsiz(1) = ie - is + 1
01823                   end if
01824                   if( jsizc.NE.je-js+1 )then
01825                       start(2) = js - jsg + 1
01826                       axsiz(2) = je - js + 1
01827                   end if
01828               end if
01829           end if
01830 !RV,SGI
01831           if( PRESENT(block_id) )then
01832             if (block_id.le.0) then
01833                call mpp_error( FATAL, 'MPP_RECORD_B: block_id <= 0!' )
01834             endif
01835             if( PRESENT(time_in) )then
01836 
01837                if(block_id.gt. axsiz(size(field%axes)-1)) &
01838                  call mpp_error( FATAL, 'MPP_RECORD_B: block_id > axis range!' )
01839 
01840                start(size(field%axes)-1)=block_id
01841 
01842             else
01843 
01844                if(block_id.gt. axsiz(size(field%axes))) &
01845                  call mpp_error( FATAL, 'MPP_RECORD_B: block_id > axis range!' )
01846 
01847                start(size(field%axes))=block_id
01848 
01849             endif
01850           endif
01851 !RV,SGI
01852           if( debug ) &
01853            write (stdout(), '(a,2i3,12i4)') 'b WRITE_RECORD: PE, unit, start, axsiz=', pe, unit, start, axsiz
01854 #ifdef use_netCDF
01855 !write time information if new time
01856           il_bytesize = BIT_SIZE(ii)/8
01857           INQUIRE (iolength=il_iosize) ii
01858           il_int_iosize = il_iosize
01859           if( newtime )then
01860               INQUIRE (iolength=il_iosize) time
01861               il_rbyt = il_iosize/il_int_iosize*il_bytesize
01862               if( il_rbyt .EQ. DOUBLE_KIND )then
01863                   error = NF_PUT_VAR1_DOUBLE( mpp_file(unit)%ncid, mpp_file(unit)%id, mpp_file(unit)%time_level, time )
01864               else if( il_rbyt .EQ. FLOAT_KIND )then
01865                   error = NF_PUT_VAR1_REAL  ( mpp_file(unit)%ncid, mpp_file(unit)%id, mpp_file(unit)%time_level, time )
01866               end if
01867           end if
01868           if( field%pack.LE.2 )then
01869               INQUIRE (iolength=il_iosize) data(1)
01870               il_rbyt = il_iosize/il_int_iosize*il_bytesize
01871               if( il_rbyt .EQ. DOUBLE_KIND )then
01872 !                  write(stderr,*)data
01873                   error = NF_PUT_VARA_DOUBLE( mpp_file(unit)%ncid, field%id, start, axsiz, data )
01874               else if( il_rbyt .EQ. FLOAT_KIND )then                                           
01875                   error = NF_PUT_VARA_REAL  ( mpp_file(unit)%ncid, field%id, start, axsiz, data )
01876               end if
01877           else              !convert to integer using scale and add: no error check on packed data representation
01878               packed_data = nint((data-field%add)/field%scale)
01879               error = NF_PUT_VARA_INT   ( mpp_file(unit)%ncid, field%id, start, axsiz, packed_data )
01880           end if
01881           call netcdf_err(error)
01882 #endif
01883       else                      !non-netCDF
01884 !subdomain contains (/is,ie,js,je/)
01885           if( PRESENT(domain) )then
01886               subdomain(:) = (/ is, ie, js, je /)
01887           else
01888               subdomain(:) = -1    ! -1 means use global value from axis metadata
01889           end if
01890           if( mpp_file(unit)%format.EQ.MPP_ASCII )then
01891 !implies sequential access
01892               write( unit,* )field%id, subdomain, time_level, time, data
01893           else                      !MPP_IEEE32 or MPP_NATIVE
01894               if( mpp_file(unit)%access.EQ.MPP_SEQUENTIAL )then
01895 #ifdef __sgi
01896                   if( mpp_file(unit)%format.EQ.MPP_IEEE32 )then
01897                       data_r4 = data !IEEE conversion layer on SGI until assign -N ieee_32 is supported
01898                       write(unit)field%id, subdomain, time_level, time, data_r4
01899                   else
01900                       write(unit)field%id, subdomain, time_level, time, data
01901                   end if
01902 #else
01903                   write(unit)field%id, subdomain, time_level, time, data
01904 #endif
01905               else                  !MPP_DIRECT
01906 #ifdef __sgi
01907                   if( mpp_file(unit)%format.EQ.MPP_IEEE32 )then
01908                       data_r4 = data !IEEE conversion layer on SGI until assign -N ieee_32 is supported
01909                       write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, data_r4
01910                   else
01911                       write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, data
01912                   end if
01913 #else
01914                   write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, data
01915 #endif
01916                   if( debug ) write (stdout(), '(a,i3,a,i3)') 'MPP_WRITE: PE=', pe, ' wrote record ', mpp_file(unit)%record
01917               end if
01918           end if
01919       end if
01920 
01921 !recompute current record for direct access I/O
01922       if( mpp_file(unit)%access.EQ.MPP_DIRECT )then
01923           if( mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
01924 !assumes all PEs participate in I/O: modify later
01925               mpp_file(unit)%record = mpp_file(unit)%record + records_per_pe*npes
01926           else
01927               mpp_file(unit)%record = mpp_file(unit)%record + records_per_pe
01928           end if
01929       end if
01930 
01931 !RV
01932       deallocate(start)
01933       deallocate(axsiz)
01934 !RV
01935       return
01936     end subroutine write_record_b
01937 
01938     subroutine write_record( unit, field, nwords, data, time_in, domain )
01939 !routine that is finally called by all mpp_write routines to perform the write
01940 !a non-netCDF record contains:
01941 !      field ID
01942 !      a set of 4 coordinates (is:ie,js:je) giving the data subdomain
01943 !      a timelevel and a timestamp (=NULLTIME if field is static)
01944 !      3D real data (stored as 1D)
01945 !if you are using direct access I/O, the RECL argument to OPEN must be large enough for the above
01946 !in a global direct access file, record position on PE is given by %record.
01947 
01948 !Treatment of timestamp:
01949 !   We assume that static fields have been passed without a timestamp.
01950 !   Here that is converted into a timestamp of NULLTIME.
01951 !   For non-netCDF fields, field is treated no differently, but is written
01952 !   with a timestamp of NULLTIME. There is no check in the code to prevent
01953 !   the user from repeatedly writing a static field.
01954 
01955       integer, intent(in) :: unit, nwords
01956       type(fieldtype), intent(in) :: field
01957       real, intent(in) :: data(nwords)
01958       real(DOUBLE_KIND), intent(in), optional :: time_in
01959       type(domain2D), intent(in), optional :: domain
01960 !RV   Very unsafe!!!! One can not use size(field%axes) before it
01961 !RV   is clear that every thing has been initialized.
01962 !RV   The code crashes in a multi-PE run.
01963 !RV      integer, dimension(size(field%axes)) :: start, axsiz
01964       integer,allocatable,dimension(:) :: start, axsiz
01965 !RV
01966       real :: time
01967       integer :: time_level
01968       logical :: newtime
01969       integer :: subdomain(4)
01970       integer :: packed_data(nwords)
01971       integer :: i, is, ie, js, je, isg, ieg, jsg, jeg, isizc, jsizc, isizg, jsizg
01972 !rv,sgi<
01973       integer :: icount_domains
01974 !rv,sgi>
01975 #ifdef use_netCDF
01976       integer :: ii, il_bytesize, il_iosize
01977       integer :: il_int_iosize, il_rbyt
01978 #endif
01979 
01980 #ifdef use_CRI_pointers
01981       real(FLOAT_KIND) :: data_r4(nwords)
01982       pointer( ptr1, data_r4)
01983       pointer( ptr2, packed_data)
01984 
01985       if (mpp_io_stack_size < 2*nwords) call mpp_io_set_stack_size(2*nwords)
01986 
01987       ptr1 = LOC(mpp_io_stack(1))
01988       ptr2 = LOC(mpp_io_stack(nwords+1))
01989 #endif
01990 
01991       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_WRITE: must first call mpp_io_init.' )
01992       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE: invalid unit number.' )
01993       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
01994       if( mpp_file(unit)%fileset  .EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
01995 
01996 !RV
01997       allocate(start(size(field%axes)))
01998       allocate(axsiz(size(field%axes)))
01999 !RV
02000       if( .NOT.mpp_file(unit)%initialized )then
02001 !this is the first call to mpp_write
02002 !we now declare the file to be initialized
02003 !if this is netCDF we switch file from DEFINE mode to DATA mode
02004           if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
02005 #ifdef use_netCDF
02006 !NOFILL is probably required for parallel: any circumstances in which not advisable?
02007               error = NF_SET_FILL( mpp_file(unit)%ncid, NF_NOFILL, i ); call netcdf_err(error)
02008               if( mpp_file(unit)%action.EQ.MPP_WRONLY )error = NF_ENDDEF(mpp_file(unit)%ncid); call netcdf_err(error)
02009 #endif
02010           else
02011               call mpp_write_meta( unit, 'END', cval='metadata' )
02012           end if
02013           mpp_file(unit)%initialized = .TRUE.
02014           if( verbose ) write (stdout(), '(a,i3,a)') 'MPP_WRITE: PE=', pe, ' initialized file '//trim(mpp_file(unit)%name)//'.'
02015       end if
02016 
02017 !initialize time: by default assume NULLTIME
02018       time = NULLTIME
02019       time_level = -1
02020       newtime = .FALSE.
02021       if( PRESENT(time_in) )time = time_in
02022 !increment time level if new time
02023       if( time.GT.mpp_file(unit)%time+EPSILON(time) )then !new time
02024           mpp_file(unit)%time_level = mpp_file(unit)%time_level + 1
02025           mpp_file(unit)%time = time
02026           newtime = .TRUE.
02027       end if
02028       if( verbose ) write (stdout(), '(a,2i3,2i5,es13.5)') 'MPP_WRITE: PE, unit, %id, %time_level, %time=',&
02029            pe, unit, mpp_file(unit)%id, mpp_file(unit)%time_level, mpp_file(unit)%time
02030 
02031       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
02032 !define netCDF data block to be written:
02033 !  time axis: START = time level
02034 !             AXSIZ = 1
02035 !  space axis: if there is no domain info
02036 !              START = 1
02037 !              AXSIZ = field%size(axis)
02038 !          if there IS domain info:
02039 !              start of domain is compute%start_index for multi-file I/O
02040 !                                 global%start_index for all other cases
02041 !              this number must be converted to 1 for NF_PUT_VAR
02042 !                  (netCDF fortran calls are with reference to 1),
02043 !          So, START = compute%start_index - <start of domain> + 1
02044 !              AXSIZ = usually compute%size
02045 !          However, if compute%start_index-compute%end_index+1.NE.compute%size,
02046 !              we assume that the call is passing a subdomain.
02047 !              To pass a subdomain, you must pass a domain2D object that satisfies the following:
02048 !                  global%start_index must contain the <start of domain> as defined above;
02049 !                  the data domain and compute domain must refer to the subdomain being passed.
02050 !              In this case, START = compute%start_index - <start of domain> + 1
02051 !                            AXSIZ = compute%start_index - compute%end_index + 1
02052 ! NOTE: passing of subdomains will fail for multi-PE single-threaded I/O,
02053 !       since that attempts to gather all data on PE 0.
02054           start = 1
02055 !
02056 !rv,sgi<
02057 !Treatment of the case x(k,i,j) where k is a common, non-decompsoed axis of
02058 !all PEs and i,j are 2D decomposed .
02059 !the array x(k,i,j) is collapsed allong the two first axis. It is treated 2D.
02060 !A corresponding domain is defined as well which is used for stitching.
02061 !However, for writing to a file the decomposition information is taken
02062 !from the field axes rather then from the domain 'domain'.
02063 !If icount_domains is 2 we have exactly that case.
02064           icount_domains=0
02065 !rv,sgi<
02066           do i = 1,size(field%axes)
02067              axsiz(i) = field%size(i)
02068              if( i.EQ.field%time_axis_index )start(i) = mpp_file(unit)%time_level
02069          start(i) = max(start(i),1) 
02070 !rv,sgi<
02071              if((field%axes(i)%domain .ne.  NULL_DOMAIN1D) .and. &
02072                 (field%axes(1)%domain .eq.  NULL_DOMAIN1D)) &
02073                 icount_domains=icount_domains+1
02074 !rv,sgi>
02075           end do
02076           if( PRESENT(domain) )then
02077             if(icount_domains .ne. 2 ) then
02078               call mpp_get_compute_domain( domain, is,  ie,  js,  je &
02079                                          ,  xsize=isizc, ysize=jsizc )
02080               call mpp_get_global_domain ( domain, isg, ieg, jsg, jeg &
02081                                          , xsize=isizg, ysize=jsizg )
02082               axsiz(1) = isizc
02083               axsiz(2) = jsizc
02084               if( npes.GT.1 .AND. mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
02085                   start(1) = is - isg + 1
02086                   start(2) = js - jsg + 1
02087               else
02088                   if( isizc.NE.ie-is+1 )then
02089                       start(1) = is - isg + 1
02090                       axsiz(1) = ie - is + 1
02091                   end if
02092                   if( jsizc.NE.je-js+1 )then
02093                       start(2) = js - jsg + 1
02094                       axsiz(2) = je - js + 1
02095                   end if
02096               end if
02097 !rv,sgi<
02098             else
02099 
02100               call mpp_get_compute_domain( field%axes(2)%domain, is,  ie &
02101                                          ,  size=isizc)
02102               call mpp_get_global_domain ( field%axes(2)%domain, isg, ieg &
02103                                          , size=isizg )
02104               call mpp_get_compute_domain( field%axes(3)%domain, js,  je &
02105                                          ,  size=jsizc)
02106               call mpp_get_global_domain ( field%axes(3)%domain, jsg, jeg &
02107                                          , size=jsizg )
02108               axsiz(2) = isizc
02109               axsiz(3) = jsizc
02110               if( npes.GT.1 .AND. mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
02111                   start(2) = is - isg + 1
02112                   start(3) = js - jsg + 1
02113               else
02114                   if( isizc.NE.ie-is+1 )then
02115                       start(2) = is - isg + 1
02116                       axsiz(2) = ie - is + 1
02117                   end if
02118                   if( jsizc.NE.je-js+1 )then
02119                       start(3) = js - jsg + 1
02120                       axsiz(3) = je - js + 1
02121                   end if
02122               end if
02123 
02124             endif
02125 !rv,sgi>
02126           end if
02127           if( debug ) write (stdout(),'(a,I12,I12)') &
02128                       'c WRITE_RECORD: PE, unit, icount_domains, start, axsiz=' &
02129                     , pe, unit, icount_domains, start, axsiz
02130 #ifdef use_netCDF
02131 !write time information if new time
02132           il_bytesize = BIT_SIZE(ii)/8
02133           INQUIRE (iolength=il_iosize) ii
02134           il_int_iosize = il_iosize          
02135           if( newtime )then
02136               INQUIRE (iolength=il_iosize) time
02137               il_rbyt = il_iosize/il_int_iosize*il_bytesize
02138               if( il_rbyt.EQ.DOUBLE_KIND )then
02139                   error = NF_PUT_VAR1_DOUBLE( mpp_file(unit)%ncid, mpp_file(unit)%id, mpp_file(unit)%time_level, time )
02140               else if( il_rbyt.EQ.FLOAT_KIND )then
02141                   error = NF_PUT_VAR1_REAL  ( mpp_file(unit)%ncid, mpp_file(unit)%id, mpp_file(unit)%time_level, time )
02142               end if
02143           end if
02144           if( field%pack.LE.2 )then
02145               INQUIRE (iolength=il_iosize) data(1)
02146               il_rbyt = il_iosize/il_int_iosize*il_bytesize
02147               if( il_rbyt .EQ. DOUBLE_KIND )then
02148                   error = NF_PUT_VARA_DOUBLE( mpp_file(unit)%ncid, field%id, start, axsiz, data )
02149               else if( il_rbyt .EQ. FLOAT_KIND )then                                           
02150                   error = NF_PUT_VARA_REAL  ( mpp_file(unit)%ncid, field%id, start, axsiz, data )
02151               end if
02152           else              !convert to integer using scale and add: no error check on packed data representation
02153               packed_data = nint((data-field%add)/field%scale)
02154               error = NF_PUT_VARA_INT   ( mpp_file(unit)%ncid, field%id, start, axsiz, packed_data )
02155           end if
02156           call netcdf_err(error)
02157 #endif
02158       else                      !non-netCDF
02159 !subdomain contains (/is,ie,js,je/)
02160           if( PRESENT(domain) )then
02161               subdomain(:) = (/ is, ie, js, je /)
02162           else
02163               subdomain(:) = -1    ! -1 means use global value from axis metadata
02164           end if
02165           if( mpp_file(unit)%format.EQ.MPP_ASCII )then
02166 !implies sequential access
02167               write( unit,* )field%id, subdomain, time_level, time, data
02168           else                      !MPP_IEEE32 or MPP_NATIVE
02169               if( mpp_file(unit)%access.EQ.MPP_SEQUENTIAL )then
02170 #ifdef __sgi
02171                   if( mpp_file(unit)%format.EQ.MPP_IEEE32 )then
02172                       data_r4 = data !IEEE conversion layer on SGI until assign -N ieee_32 is supported
02173                       write(unit)field%id, subdomain, time_level, time, data_r4
02174                   else
02175                       write(unit)field%id, subdomain, time_level, time, data
02176                   end if
02177 #else
02178                   write(unit)field%id, subdomain, time_level, time, data
02179 #endif
02180               else                  !MPP_DIRECT
02181 #ifdef __sgi
02182                   if( mpp_file(unit)%format.EQ.MPP_IEEE32 )then
02183                       data_r4 = data !IEEE conversion layer on SGI until assign -N ieee_32 is supported
02184                       write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, data_r4
02185                   else
02186                       write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, data
02187                   end if
02188 #else
02189                   write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, data
02190 #endif
02191                   if( debug ) write (stdout(), '(a,i3,a,i3)') 'MPP_WRITE: PE=', pe, ' wrote record ', mpp_file(unit)%record
02192               end if
02193           end if
02194       end if
02195 
02196 !recompute current record for direct access I/O
02197       if( mpp_file(unit)%access.EQ.MPP_DIRECT )then
02198           if( mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
02199 !assumes all PEs participate in I/O: modify later
02200               mpp_file(unit)%record = mpp_file(unit)%record + records_per_pe*npes
02201           else
02202               mpp_file(unit)%record = mpp_file(unit)%record + records_per_pe
02203           end if
02204       end if
02205 !RV
02206       deallocate(start)
02207       deallocate(axsiz)
02208 !RV
02209 
02210       return
02211     end subroutine write_record
02212 
02213 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
02214 !                                                                      !
02215 !                          MPP_COPY_META                               !
02216 !                                                                      !
02217 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
02218     subroutine mpp_copy_meta_global( unit, gatt )
02219 !writes a global metadata attribute to unit <unit>
02220 !attribute <name> can be an real, integer or character
02221 !one and only one of rval, ival, and cval should be present
02222 !the first found will be used
02223 !for a non-netCDF file, it is encoded into a string "GLOBAL <name> <val>"
02224       integer, intent(in) :: unit
02225       type(atttype), intent(in) :: gatt
02226       integer :: len
02227 
02228       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
02229       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
02230       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
02231       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
02232       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
02233       if( mpp_file(unit)%initialized ) &
02234            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
02235 #ifdef use_netCDF
02236       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
02237          if( gatt%type.EQ.NF_CHAR )then
02238             len = gatt%len
02239             call write_attribute_netcdf( unit, NF_GLOBAL, gatt%name, cval=gatt%catt(1:len) )
02240          else
02241             call write_attribute_netcdf( unit, NF_GLOBAL, gatt%name, rval=gatt%fatt )
02242          endif
02243       else
02244          if( gatt%type.EQ.NF_CHAR )then
02245             len=gatt%len
02246             call write_attribute( unit, 'GLOBAL '//trim(gatt%name), cval=gatt%catt(1:len) )
02247          else
02248             call write_attribute( unit, 'GLOBAL '//trim(gatt%name), rval=gatt%fatt )
02249          endif
02250      end if
02251 #else
02252      call mpp_error( FATAL, 'MPP_READ currently requires use_netCDF option' )
02253 #endif
02254       return
02255     end subroutine mpp_copy_meta_global
02256 
02257     subroutine mpp_copy_meta_axis( unit, axis, domain )
02258 !load the values in an axistype (still need to call mpp_write)
02259 !write metadata attributes for axis.  axis is declared inout 
02260 !because the variable and dimension ids are altered
02261 
02262       integer, intent(in) :: unit
02263       type(axistype), intent(inout) :: axis
02264       type(domain1D), intent(in), optional :: domain
02265       character(len=512) :: text
02266       integer :: i, len, is, ie, isg, ieg
02267 
02268       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
02269       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
02270       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
02271       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
02272       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
02273       if( mpp_file(unit)%initialized ) &
02274            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
02275 
02276 ! redefine domain if present
02277       if( PRESENT(domain) )then
02278           axis%domain = domain
02279       else
02280           axis%domain = NULL_DOMAIN1D
02281       end if
02282 
02283 #ifdef use_netCDF      
02284 !write metadata
02285       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
02286 
02287 !write axis def
02288           if( ASSOCIATED(axis%data) )then !space axis
02289               if( mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
02290                   call mpp_get_compute_domain( axis%domain, is, ie )
02291                   call mpp_get_global_domain( axis%domain, isg, ieg )
02292                   error = NF_DEF_DIM( mpp_file(unit)%ncid, axis%name, ie-is+1, axis%did )
02293               else
02294                   error = NF_DEF_DIM( mpp_file(unit)%ncid, axis%name, size(axis%data),          axis%did )
02295               end if
02296               call netcdf_err(error)
02297               error = NF_DEF_VAR( mpp_file(unit)%ncid, axis%name, NF_FLOAT, 1, axis%did, axis%id ); call netcdf_err(error)
02298           else                            !time axis
02299               error = NF_DEF_DIM( mpp_file(unit)%ncid, axis%name, NF_UNLIMITED, axis%did ); call netcdf_err(error)
02300               error = NF_DEF_VAR( mpp_file(unit)%ncid, axis%name, NF_DOUBLE, 1, axis%did, axis%id ); call netcdf_err(error)
02301               mpp_file(unit)%id = axis%id !file ID is the same as time axis varID
02302               mpp_file(unit)%recdimid = axis%did ! record dimension id
02303           end if
02304       else
02305           varnum = varnum + 1
02306           axis%id = varnum
02307           axis%did = varnum
02308 !write axis def
02309           write( text, '(a,i4,a)' )'AXIS ', axis%id, ' name'
02310           call write_attribute( unit, trim(text), cval=axis%name )
02311           write( text, '(a,i4,a)' )'AXIS ', axis%id, ' size'
02312           if( ASSOCIATED(axis%data) )then !space axis
02313               if( mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
02314                   call write_attribute( unit, trim(text), ival=(/ie-is+1/) )
02315               else
02316                   call write_attribute( unit, trim(text), ival=(/size(axis%data)/) )
02317               end if
02318           else                            !time axis
02319               if( mpp_file(unit)%id.NE.-1 ) &
02320                    call mpp_error( FATAL, 'MPP_WRITE_META_AXIS: There is already a time axis for this file.' )
02321               call write_attribute( unit, trim(text), ival=(/0/) ) !a size of 0 indicates time axis
02322               mpp_file(unit)%id = axis%id
02323           end if
02324       end if
02325 !write axis attributes
02326 
02327       do i=1,axis%natt
02328          if( axis%Att(i)%name.NE.default_att%name )then
02329             if( axis%Att(i)%type.EQ.NF_CHAR )then
02330                len = axis%Att(i)%len
02331                call mpp_write_meta( unit, axis%id, axis%Att(i)%name, cval=axis%Att(i)%catt(1:len) )
02332             else
02333                call mpp_write_meta( unit, axis%id, axis%Att(i)%name, rval=axis%Att(i)%fatt)
02334             endif
02335          endif
02336       enddo
02337 
02338       if( mpp_file(unit)%threading.EQ.MPP_MULTI .AND. mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
02339           call mpp_write_meta( unit, axis%id, 'domain_decomposition', ival=(/isg,ieg,is,ie/) )
02340       end if
02341       if( verbose ) write (stdout(), '(a,2i3,1x,a,2i3)') &
02342            'MPP_WRITE_META: Wrote axis metadata, pe, unit, axis%name, axis%id, axis%did=', &
02343            pe, unit, trim(axis%name), axis%id, axis%did 
02344 #else
02345       call mpp_error( FATAL, 'MPP_READ currently requires use_netCDF option' )
02346 #endif      
02347       return
02348     end subroutine mpp_copy_meta_axis    
02349 
02350     subroutine mpp_copy_meta_field( unit, field, axes )
02351 !useful for copying field metadata from a previous call to mpp_read_meta
02352 !define field: must have already called mpp_write_meta(axis) for each axis
02353       integer, intent(in) :: unit
02354       type(fieldtype), intent(inout) :: field
02355       type(axistype), intent(in), optional :: axes(:)
02356 !this array is required because of f77 binding on netCDF interface
02357       integer, allocatable :: axis_id(:)
02358       real :: a, b
02359       integer :: i
02360 
02361       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
02362       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
02363       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
02364       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
02365       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
02366       if( mpp_file(unit)%initialized ) &
02367            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
02368 
02369        if( field%pack.NE.1 .AND. field%pack.NE.2 )then
02370             if( field%pack.NE.4 .AND. field%pack.NE.8 ) &
02371                call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: only legal packing values are 1,2,4,8.' )
02372       end if
02373 
02374       if (PRESENT(axes)) then
02375          deallocate(field%axes)
02376          deallocate(field%size)
02377          allocate(field%axes(size(axes)))
02378          allocate(field%size(size(axes)))
02379          field%axes = axes
02380          do i=1,size(axes)
02381             if (ASSOCIATED(axes(i)%data)) then
02382                field%size(i) = size(axes(i)%data)
02383             else
02384                field%size(i) = 1
02385                field%time_axis_index = i
02386             endif
02387          enddo
02388       endif
02389          
02390       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
02391 #ifdef use_netCDF
02392           if (.not. allocated(axis_id)) allocate( axis_id(size(field%axes)) )
02393           do i = 1,size(field%axes)
02394              axis_id(i) = field%axes(i)%did
02395           end do
02396 !write field def
02397           select case (field%pack)
02398               case(1)
02399                   error = NF_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_DOUBLE, size(field%axes), axis_id, field%id )
02400               case(2)
02401                   error = NF_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_FLOAT,  size(field%axes), axis_id, field%id )
02402               case(4)
02403                   if( field%scale.EQ.default_field%scale .OR. field%add.EQ.default_field%add ) &
02404                        call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: scale and add must be supplied when pack=4.' )
02405                   error = NF_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_SHORT,  size(field%axes), axis_id, field%id )
02406               case(8)
02407                   if( field%scale.EQ.default_field%scale .OR. field%add.EQ.default_field%add ) &
02408                        call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: scale and add must be supplied when pack=8.' )
02409                   error = NF_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_BYTE,   size(field%axes), axis_id, field%id )
02410               case default
02411                   call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: only legal packing values are 1,2,4,8.' )
02412           end select
02413 #endif
02414       else
02415           varnum = varnum + 1
02416           field%id = varnum
02417           if( field%pack.NE.default_field%pack ) &
02418            call mpp_error( WARNING, 'MPP_WRITE_META: Packing is currently available only on netCDF files.' )
02419 !write field def
02420           write( text, '(a,i4,a)' )'FIELD ', field%id, ' name'
02421           call write_attribute( unit, trim(text), cval=field%name )
02422           write( text, '(a,i4,a)' )'FIELD ', field%id, ' axes'
02423           call write_attribute( unit, trim(text), ival=field%axes(:)%did )
02424       end if
02425 !write field attributes: these names follow netCDF conventions
02426       call mpp_write_meta( unit, field%id, 'long_name', cval=field%longname )
02427       call mpp_write_meta( unit, field%id, 'units',     cval=field%units    )
02428 !all real attributes must be written as packed
02429       if( (field%min.NE.default_field%min) .AND. (field%max.NE.default_field%max) )then
02430           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
02431               call mpp_write_meta( unit, field%id, 'valid_range', rval=(/field%min,field%max/), pack=field%pack )
02432           else
02433               a = nint((field%min-field%add)/field%scale)
02434               b = nint((field%max-field%add)/field%scale)
02435               call mpp_write_meta( unit, field%id, 'valid_range', rval=(/a,  b  /), pack=field%pack )
02436           end if
02437       else if( field%min.NE.default_field%min )then
02438           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
02439               call mpp_write_meta( unit, field%id, 'valid_min', rval=field%min, pack=field%pack )
02440           else
02441               a = nint((field%min-field%add)/field%scale)
02442               call mpp_write_meta( unit, field%id, 'valid_min', rval=a, pack=field%pack )
02443           end if
02444       else if( field%max.NE.default_field%max )then
02445           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
02446               call mpp_write_meta( unit, field%id, 'valid_max', rval=field%max, pack=field%pack )
02447           else
02448               a = nint((field%max-field%add)/field%scale)
02449               call mpp_write_meta( unit, field%id, 'valid_max', rval=a, pack=field%pack )
02450           end if
02451       end if
02452       if( field%missing.NE.default_field%missing )then
02453           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
02454               call mpp_write_meta( unit, field%id, 'missing_value', rval=field%missing, pack=field%pack )
02455           else
02456               a = nint((field%missing-field%add)/field%scale)
02457               call mpp_write_meta( unit, field%id, 'missing_value', rval=a, pack=field%pack )
02458           end if
02459       end if
02460       if( field%fill.NE.default_field%fill )then
02461           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
02462               call mpp_write_meta( unit, field%id, '_FillValue', rval=field%fill, pack=field%pack )
02463           else
02464               a = nint((field%fill-field%add)/field%scale)
02465               call mpp_write_meta( unit, field%id, '_FillValue', rval=a, pack=field%pack )
02466           end if
02467       end if
02468       if( field%pack.NE.1 .AND. field%pack.NE.2 )then
02469           call mpp_write_meta( unit, field%id, 'packing', ival=field%pack )
02470           if( field%scale.NE.default_field%scale )call mpp_write_meta( unit, field%id, 'scale_factor',  rval=field%scale )
02471           if( field%add.NE.default_field%add   )call mpp_write_meta( unit, field%id, 'add_offset',    rval=field%add   )
02472       end if
02473       if( verbose ) write (stdout(), '(a,2i3,1x,a,i3)') 'MPP_WRITE_META: Wrote field metadata: pe, unit, field%name, field%id=', &
02474            pe, unit, trim(field%name), field%id 
02475 
02476       return
02477     end subroutine mpp_copy_meta_field
02478     
02479 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
02480 !                                                                      !
02481 !                               MPP_READ                               !
02482 !                                                                      !
02483 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
02484 
02485 #define MPP_READ_2DDECOMP_1D_ mpp_read_2ddecomp_r1d
02486 #define MPP_READ_2DDECOMP_2D_ mpp_read_2ddecomp_r2d
02487 #define MPP_READ_2DDECOMP_3D_ mpp_read_2ddecomp_r3d
02488 #define MPP_READ_2DDECOMP_4D_ mpp_read_2ddecomp_r4d
02489 #define MPP_TYPE_ real
02490 #include <mpp_read_2Ddecomp.h>
02491 
02492     subroutine read_record( unit, field, nwords, data, time_level, domain )
02493 !routine that is finally called by all mpp_read routines to perform the read
02494 !a non-netCDF record contains:
02495 !      field ID
02496 !      a set of 4 coordinates (is:ie,js:je) giving the data subdomain
02497 !      a timelevel and a timestamp (=NULLTIME if field is static)
02498 !      3D real data (stored as 1D)
02499 !if you are using direct access I/O, the RECL argument to OPEN must be large enough for the above
02500 !in a global direct access file, record position on PE is given by %record.
02501 
02502 !Treatment of timestamp:
02503 !   We assume that static fields have been passed without a timestamp.
02504 !   Here that is converted into a timestamp of NULLTIME.
02505 !   For non-netCDF fields, field is treated no differently, but is written
02506 !   with a timestamp of NULLTIME. There is no check in the code to prevent
02507 !   the user from repeatedly writing a static field.
02508 
02509       integer, intent(in) :: unit, nwords
02510       type(fieldtype), intent(in) :: field
02511       real, intent(inout) :: data(nwords)
02512       integer, intent(in), optional  :: time_level
02513       type(domain2D), intent(in), optional :: domain
02514       integer, dimension(size(field%axes)) :: start, axsiz
02515       real :: time
02516 
02517       logical :: newtime
02518       integer :: subdomain(4), tlevel
02519 
02520       integer(SHORT_KIND) :: i2vals(nwords)
02521 !#ifdef __sgi      
02522       integer(INT_KIND) :: ivals(nwords)
02523       real(FLOAT_KIND) :: rvals(nwords)
02524 !#else      
02525 !      integer :: ivals(nwords)
02526 !      real :: rvals(nwords)      
02527 !#endif      
02528 
02529       real(DOUBLE_KIND) :: r8vals(nwords)
02530 
02531       integer :: i, error, is, ie, js, je, isg, ieg, jsg, jeg
02532 
02533 #ifdef use_CRI_pointers
02534       pointer( ptr1, i2vals )
02535       pointer( ptr2, ivals )
02536       pointer( ptr3, rvals )
02537       pointer( ptr4, r8vals )
02538 
02539       if (mpp_io_stack_size < 4*nwords) call mpp_io_set_stack_size(4*nwords)
02540 
02541       ptr1 = LOC(mpp_io_stack(1))
02542       ptr2 = LOC(mpp_io_stack(nwords+1))
02543       ptr3 = LOC(mpp_io_stack(2*nwords+1))
02544       ptr4 = LOC(mpp_io_stack(3*nwords+1))
02545 #endif
02546       if (.not.PRESENT(time_level)) then
02547           tlevel = 0
02548       else
02549           tlevel = time_level
02550       endif
02551       
02552 #ifdef use_netCDF
02553       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'READ_RECORD: must first call mpp_io_init.' )
02554       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'READ_RECORD: invalid unit number.' )
02555       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
02556 !RV
02557       if( mpp_file(unit)%fileset.EQ.MPP_MULTI .and. present(domain)) &
02558         call mpp_error( FATAL, 'READ_RECORD: multiple filesets not supported for MPP_READ' )
02559 
02560       if( .NOT.mpp_file(unit)%initialized ) call mpp_error( FATAL, 'MPP_READ: must first call mpp_read_meta.' )
02561 
02562 
02563  
02564       if( verbose ) write (stdout(), '(a,2i3,2i5)') 'MPP_READ: PE, unit, %id, %time_level =',&
02565            pe, unit, mpp_file(unit)%id, tlevel
02566 
02567       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
02568 !define netCDF data block to be read:
02569 !  time axis: START = time level
02570 !             AXSIZ = 1
02571 !  space axis: if there is no domain info
02572 !              START = 1
02573 !              AXSIZ = field%size(axis)
02574 !          if there IS domain info:
02575 !              start of domain is compute%start_index for multi-file I/O
02576 !                                 global%start_index for all other cases
02577 !              this number must be converted to 1 for NF_GET_VAR
02578 !                  (netCDF fortran calls are with reference to 1),
02579 !          So, START = compute%start_index - <start of domain> + 1
02580 !              AXSIZ = usually compute%size
02581 !          However, if compute%start_index-compute%end_index+1.NE.compute%size,
02582 !              we assume that the call is passing a subdomain.
02583 !              To pass a subdomain, you must pass a domain2D object that satisfies the following:
02584 !                  global%start_index must contain the <start of domain> as defined above;
02585 !                  the data domain and compute domain must refer to the subdomain being passed.
02586 !              In this case, START = compute%start_index - <start of domain> + 1
02587 !                            AXSIZ = compute%start_index - compute%end_index + 1
02588 ! NOTE: passing of subdomains will fail for multi-PE single-threaded I/O,
02589 !       since that attempts to gather all data on PE 0.
02590           start = 1
02591           do i = 1,size(field%axes)
02592              axsiz(i) = field%size(i)
02593              if( field%axes(i)%did.EQ.field%time_axis_index )start(i) = tlevel
02594           end do
02595           if( PRESENT(domain) )then
02596               call mpp_get_compute_domain( domain, is,  ie,  js,  je  )
02597               call mpp_get_global_domain ( domain, isg, ieg, jsg, jeg )
02598               axsiz(1) = ie-is+1
02599               axsiz(2) = je-js+1
02600               if( npes.GT.1 .AND. mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
02601                   start(1) = is - isg + 1
02602                   start(2) = js - jsg + 1
02603               else
02604                   if( ie-is+1.NE.ie-is+1 )then
02605                       start(1) = is - isg + 1
02606                       axsiz(1) = ie - is + 1
02607                   end if
02608                   if( je-js+1.NE.je-js+1 )then
02609                       start(2) = js - jsg + 1
02610                       axsiz(2) = je - js + 1
02611                   end if
02612               end if
02613           end if
02614 
02615           if( verbose ) write (stdout(), '(a,2i3,i6,12i4)') 'READ_RECORD: PE, unit, nwords, start, axsiz=', &
02616                            pe, unit, nwords, start, axsiz
02617 
02618           select case (field%type)
02619              case(NF_BYTE)
02620 ! use type conversion 
02621                 call mpp_error( FATAL, 'MPP_READ: does not support NF_BYTE packing' )
02622              case(NF_SHORT)
02623                 error = NF_GET_VARA_INT2  ( mpp_file(unit)%ncid, field%id, start, axsiz, i2vals ); call netcdf_err(error)
02624                  data(:)=i2vals(:)*field%scale + field%add
02625              case(NF_INT)
02626                 error = NF_GET_VARA_INT   ( mpp_file(unit)%ncid, field%id, start, axsiz, ivals  ); call netcdf_err(error)
02627                 data(:)=ivals(:)
02628              case(NF_FLOAT)
02629                 error = NF_GET_VARA_REAL  ( mpp_file(unit)%ncid, field%id, start, axsiz, rvals  ); call netcdf_err(error)
02630                 data(:)=rvals(:)
02631              case(NF_DOUBLE)
02632                 error = NF_GET_VARA_DOUBLE( mpp_file(unit)%ncid, field%id, start, axsiz, r8vals ); call netcdf_err(error)
02633                 data(:)=r8vals(:)
02634              case default
02635                 call mpp_error( FATAL, 'MPP_READ: invalid pack value' )
02636           end select
02637       else                      !non-netCDF
02638 !subdomain contains (/is,ie,js,je/)
02639           call mpp_error( FATAL, 'Currently dont support non-NetCDF mpp read' )
02640 
02641       end if
02642 #else
02643       call mpp_error( FATAL, 'MPP_READ currently requires use_netCDF option' )
02644 #endif      
02645       return
02646     end subroutine read_record
02647     subroutine read_record_b(unit,field,nwords,data,time_level,domain,block_id)
02648 !routine that is finally called by all mpp_read routines to perform the read
02649 !a non-netCDF record contains:
02650 !      field ID
02651 !      a set of 4 coordinates (is:ie,js:je) giving the data subdomain
02652 !      a timelevel and a timestamp (=NULLTIME if field is static)
02653 !      3D real data (stored as 1D)
02654 !if you are using direct access I/O, the RECL argument to OPEN must be large enough for the above
02655 !in a global direct access file, record position on PE is given by %record.
02656 
02657 !Treatment of timestamp:
02658 !   We assume that static fields have been passed without a timestamp.
02659 !   Here that is converted into a timestamp of NULLTIME.
02660 !   For non-netCDF fields, field is treated no differently, but is written
02661 !   with a timestamp of NULLTIME. There is no check in the code to prevent
02662 !   the user from repeatedly writing a static field.
02663 
02664       integer, intent(in) :: unit, nwords
02665       type(fieldtype), intent(in) :: field
02666       real, intent(inout) :: data(nwords)
02667       integer, intent(in), optional  :: time_level
02668 !RV
02669       integer, intent(in), optional  ::  block_id
02670 !RV
02671       type(domain2D), intent(in), optional :: domain
02672       integer, dimension(size(field%axes)) :: start, axsiz
02673       real :: time
02674 
02675       logical :: newtime
02676       integer :: subdomain(4), tlevel
02677 
02678       integer(SHORT_KIND) :: i2vals(nwords)
02679 !#ifdef __sgi      
02680       integer(INT_KIND) :: ivals(nwords)
02681       real(FLOAT_KIND) :: rvals(nwords)
02682 !#else      
02683 !      integer :: ivals(nwords)
02684 !      real :: rvals(nwords)      
02685 !#endif      
02686 
02687       real(DOUBLE_KIND) :: r8vals(nwords)
02688 
02689       integer :: i, error, is, ie, js, je, isg, ieg, jsg, jeg
02690 
02691 #ifdef use_CRI_pointers
02692       pointer( ptr1, i2vals )
02693       pointer( ptr2, ivals )
02694       pointer( ptr3, rvals )
02695       pointer( ptr4, r8vals )
02696 
02697       if (mpp_io_stack_size < 4*nwords) call mpp_io_set_stack_size(4*nwords)
02698 
02699       ptr1 = LOC(mpp_io_stack(1))
02700       ptr2 = LOC(mpp_io_stack(nwords+1))
02701       ptr3 = LOC(mpp_io_stack(2*nwords+1))
02702       ptr4 = LOC(mpp_io_stack(3*nwords+1))
02703 #endif
02704       if (.not.PRESENT(time_level)) then
02705           tlevel = 0
02706       else
02707           tlevel = time_level
02708       endif
02709       
02710 #ifdef use_netCDF
02711       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'READ_RECORD: must first call mpp_io_init.' )
02712       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'READ_RECORD: invalid unit number.' )
02713       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
02714 !RV
02715       if( mpp_file(unit)%fileset.EQ.MPP_MULTI .and. present(domain)) &
02716         call mpp_error( FATAL, 'READ_RECORD: multiple filesets not supported for MPP_READ' )
02717 
02718       if( .NOT.mpp_file(unit)%initialized ) call mpp_error( FATAL, 'MPP_READ: must first call mpp_read_meta.' )
02719 
02720 
02721  
02722       if( verbose ) write (stdout(), '(a,2i3,2i5)') 'MPP_READ: PE, unit, %id, %time_level =',&
02723            pe, unit, mpp_file(unit)%id, tlevel
02724 
02725       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
02726 !define netCDF data block to be read:
02727 !  time axis: START = time level
02728 !             AXSIZ = 1
02729 !  space axis: if there is no domain info
02730 !              START = 1
02731 !              AXSIZ = field%size(axis)
02732 !          if there IS domain info:
02733 !              start of domain is compute%start_index for multi-file I/O
02734 !                                 global%start_index for all other cases
02735 !              this number must be converted to 1 for NF_GET_VAR
02736 !                  (netCDF fortran calls are with reference to 1),
02737 !          So, START = compute%start_index - <start of domain> + 1
02738 !              AXSIZ = usually compute%size
02739 !          However, if compute%start_index-compute%end_index+1.NE.compute%size,
02740 !              we assume that the call is passing a subdomain.
02741 !              To pass a subdomain, you must pass a domain2D object that satisfies the following:
02742 !                  global%start_index must contain the <start of domain> as defined above;
02743 !                  the data domain and compute domain must refer to the subdomain being passed.
02744 !              In this case, START = compute%start_index - <start of domain> + 1
02745 !                            AXSIZ = compute%start_index - compute%end_index + 1
02746 ! NOTE: passing of subdomains will fail for multi-PE single-threaded I/O,
02747 !       since that attempts to gather all data on PE 0.
02748           start = 1
02749           do i = 1,size(field%axes)
02750              axsiz(i) = field%size(i)
02751              if( field%axes(i)%did.EQ.field%time_axis_index )start(i) = tlevel
02752           end do
02753           if( PRESENT(domain) )then
02754               call mpp_get_compute_domain( domain, is,  ie,  js,  je  )
02755               call mpp_get_global_domain ( domain, isg, ieg, jsg, jeg )
02756               axsiz(1) = ie-is+1
02757               axsiz(2) = je-js+1
02758               if( npes.GT.1 .AND. mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
02759                   start(1) = is - isg + 1
02760                   start(2) = js - jsg + 1
02761               else
02762                   if( ie-is+1.NE.ie-is+1 )then
02763                       start(1) = is - isg + 1
02764                       axsiz(1) = ie - is + 1
02765                   end if
02766                   if( je-js+1.NE.je-js+1 )then
02767                       start(2) = js - jsg + 1
02768                       axsiz(2) = je - js + 1
02769                   end if
02770               end if
02771           end if
02772 !RV,SGI
02773           if( PRESENT(block_id) )then
02774             if (block_id.le.0) then
02775                call mpp_error( FATAL, 'READ_RECORD_B: block_id <= 0!' )
02776             endif
02777             if( PRESENT(time_level) )then
02778 
02779                if(block_id.gt. axsiz(size(field%axes)-1)) &
02780                  call mpp_error( FATAL, 'READ_RECORD_B: block_id > axis range!' )
02781                start(size(field%axes)-1)=block_id
02782 
02783             else
02784 
02785                if(block_id.gt. axsiz(size(field%axes))) &
02786                  call mpp_error( FATAL, 'READ_RECORD_B: block_id > axis range!' )
02787                start(size(field%axes))=block_id
02788 
02789             endif
02790           endif
02791 
02792 !RV,SGI
02793 
02794           if( verbose ) write (stdout(), '(a,2i3,i6,12i4)') 'READ_RECORD: PE, unit, nwords, start, axsiz=', &
02795                            pe, unit, nwords, start, axsiz
02796 
02797           select case (field%type)
02798              case(NF_BYTE)
02799 ! use type conversion 
02800                 call mpp_error( FATAL, 'MPP_READ: does not support NF_BYTE packing' )
02801              case(NF_SHORT)
02802                 error = NF_GET_VARA_INT2  ( mpp_file(unit)%ncid, field%id, start, axsiz, i2vals ); call netcdf_err(error)
02803                  data(:)=i2vals(:)*field%scale + field%add
02804              case(NF_INT)
02805                 error = NF_GET_VARA_INT   ( mpp_file(unit)%ncid, field%id, start, axsiz, ivals  ); call netcdf_err(error)
02806                 data(:)=ivals(:)
02807              case(NF_FLOAT)
02808                 error = NF_GET_VARA_REAL  ( mpp_file(unit)%ncid, field%id, start, axsiz, rvals  ); call netcdf_err(error)
02809                 data(:)=rvals(:)
02810              case(NF_DOUBLE)
02811                 error = NF_GET_VARA_DOUBLE( mpp_file(unit)%ncid, field%id, start, axsiz, r8vals ); call netcdf_err(error)
02812                 data(:)=r8vals(:)
02813              case default
02814                 call mpp_error( FATAL, 'MPP_READ: invalid pack value' )
02815           end select
02816       else                      !non-netCDF
02817 !subdomain contains (/is,ie,js,je/)
02818           call mpp_error( FATAL, 'Currently dont support non-NetCDF mpp read' )
02819 
02820       end if
02821 #else
02822       call mpp_error( FATAL, 'MPP_READ currently requires use_netCDF option' )
02823 #endif      
02824       return
02825     end subroutine read_record_b
02826 
02827     subroutine mpp_read_r4D( unit, field, data, tindex,blockid)
02828       integer, intent(in) :: unit
02829       type(fieldtype), intent(in) :: field
02830       real, intent(inout) :: data(:,:,:,:)
02831       integer, intent(in), optional :: tindex
02832       integer, intent(in), optional :: blockid
02833 
02834       if(present(blockid)) then
02835         call read_record_b(unit,field,size(data),data,tindex,block_id=blockid )
02836       else
02837         call read_record( unit, field, size(data), data, tindex )
02838       endif
02839     end subroutine mpp_read_r4D
02840 
02841     subroutine mpp_read_r3D( unit, field, data, tindex,blockid)
02842       integer, intent(in) :: unit
02843       type(fieldtype), intent(in) :: field
02844       real, intent(inout) :: data(:,:,:)
02845       integer, intent(in), optional :: tindex
02846       integer, intent(in), optional :: blockid
02847 
02848       if(present(blockid)) then
02849         call read_record_b(unit,field,size(data),data,tindex,block_id=blockid )
02850       else
02851         call read_record( unit, field, size(data), data, tindex )
02852       endif
02853     end subroutine mpp_read_r3D
02854 
02855     subroutine mpp_read_r2D( unit, field, data, tindex )
02856       integer, intent(in) :: unit
02857       type(fieldtype), intent(in) :: field
02858       real, intent(inout) :: data(:,:)
02859       integer, intent(in), optional :: tindex
02860 
02861       call read_record( unit, field, size(data), data, tindex )
02862     end subroutine mpp_read_r2D
02863 
02864     subroutine mpp_read_r1D( unit, field, data, tindex )
02865       integer, intent(in) :: unit
02866       type(fieldtype), intent(in) :: field
02867       real, intent(inout) :: data(:)
02868       integer, intent(in), optional :: tindex
02869 
02870       call read_record( unit, field, size(data), data, tindex )
02871     end subroutine mpp_read_r1D
02872 
02873     subroutine mpp_read_r0D( unit, field, data, tindex )
02874       integer, intent(in) :: unit
02875       type(fieldtype), intent(in) :: field
02876       real, intent(inout) :: data
02877       integer, intent(in), optional :: tindex
02878       real, dimension(1) :: data_tmp
02879 
02880       data_tmp(1)=data
02881       call read_record( unit, field, 1, data_tmp, tindex )
02882       data=data_tmp(1)
02883     end subroutine mpp_read_r0D
02884 
02885     subroutine mpp_read_meta(unit)
02886 !
02887 ! read file attributes including dimension and variable attributes
02888 ! and store in filetype structure.  All of the file information
02889 ! with the exception of the (variable) data is stored.  Attributes
02890 ! are supplied to the user by get_info,get_atts,get_axes and get_fields  
02891 !
02892 ! every PE is eligible to call mpp_read_meta
02893 !
02894       integer, parameter :: MAX_DIMVALS = 100000
02895       integer, intent(in) :: unit
02896 
02897       integer         :: ncid,ndim,nvar_total,natt,recdim,nv,nvar,len
02898       integer :: error,i,j 
02899       integer         :: type,nvdims,nvatts, dimid
02900       integer, allocatable, dimension(:) :: dimids
02901       type(axistype) , allocatable, dimension(:) :: Axis
02902       character(len=128) :: name, attname, unlimname, attval
02903       logical :: isdim
02904 
02905       integer(SHORT_KIND) :: i2vals(MAX_DIMVALS)
02906 !#ifdef __sgi
02907       integer(INT_KIND) :: ivals(MAX_DIMVALS)
02908       real(FLOAT_KIND)  :: rvals(MAX_DIMVALS)
02909 !#else      
02910 !      integer :: ivals(MAX_DIMVALS)
02911 !      real    :: rvals(MAX_DIMVALS)      
02912 !#endif      
02913       real(DOUBLE_KIND) :: r8vals(MAX_DIMVALS)
02914 
02915 #ifdef use_netCDF      
02916 
02917       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
02918         ncid = mpp_file(unit)%ncid
02919         error = NF_INQ(ncid,ndim, nvar_total,&
02920                       natt, recdim);call netcdf_err(error)
02921 
02922 
02923         mpp_file(unit)%ndim = ndim
02924         mpp_file(unit)%natt = natt
02925         mpp_file(unit)%recdimid = recdim
02926 !
02927 ! if no recdim exists, recdimid = -1
02928 ! variable id of unlimdim and length
02929 !
02930         if( recdim.NE.-1 )then 
02931            error = NF_INQ_DIM( ncid, recdim, unlimname, mpp_file(unit)%time_level );call netcdf_err(error)
02932            error = NF_INQ_VARID( ncid, unlimname, mpp_file(unit)%id ); call netcdf_err(error)
02933         else
02934            mpp_file(unit)%time_level = -1 ! set to zero so mpp_get_info returns ntime=0 if no time axis present
02935         endif
02936 
02937         if ( natt .gt. 0 ) allocate(mpp_file(unit)%Att(natt))
02938         allocate(Axis(ndim))
02939         allocate(dimids(ndim))
02940         allocate(mpp_file(unit)%Axis(ndim))          
02941 
02942 !
02943 ! initialize fieldtype and axis type
02944 !
02945 
02946 
02947         do i=1,ndim
02948            Axis(i) = default_axis
02949            mpp_file(unit)%Axis(i) = default_axis
02950         enddo
02951 
02952         do i=1,natt
02953            mpp_file(unit)%Att(i) = default_att
02954         enddo
02955         
02956 !
02957 ! assign global attributes
02958 !
02959         do i=1,natt
02960            error=NF_INQ_ATTNAME(ncid,NF_GLOBAL,i,name);call netcdf_err(error)
02961            error=NF_INQ_ATT(ncid,NF_GLOBAL,trim(name),type,len);call netcdf_err(error)
02962            mpp_file(unit)%Att(i)%name = name
02963            mpp_file(unit)%Att(i)%len = len
02964            mpp_file(unit)%Att(i)%type = type
02965 !
02966 !  allocate space for att data and assign 
02967 !
02968            select case (type)
02969               case (NF_CHAR)
02970                  if (len.gt.512) then
02971             call mpp_error(NOTE,'GLOBAL ATT too long - not reading this metadata') 
02972                     len=7
02973                     mpp_file(unit)%Att(i)%len=len
02974                     mpp_file(unit)%Att(i)%catt = 'unknown'
02975                  else
02976                      error=NF_GET_ATT_TEXT(ncid,NF_GLOBAL,name,mpp_file(unit)%Att(i)%catt);call netcdf_err(error)
02977                      if (verbose.and.pe == 0) write (stdout(),*)  'GLOBAL ATT ',trim(name),' ',mpp_file(unit)%Att(i)%catt(1:len)
02978                  endif
02979 !
02980 ! store integers in float arrays
02981 !
02982               case (NF_SHORT)
02983                  allocate(mpp_file(unit)%Att(i)%fatt(len))
02984                  error=NF_GET_ATT_INT2(ncid,NF_GLOBAL,name,i2vals);call netcdf_err(error)
02985                  if( verbose .and. pe == 0 )write (stdout(),*)  'GLOBAL ATT ',trim(name),' ',i2vals(1:len)
02986                  mpp_file(unit)%Att(i)%fatt(1:len)=i2vals(1:len)
02987               case (NF_INT)
02988                  allocate(mpp_file(unit)%Att(i)%fatt(len))
02989                  error=NF_GET_ATT_INT(ncid,NF_GLOBAL,name,ivals);call netcdf_err(error)
02990                  if( verbose .and. pe == 0 )write (stdout(),*)  'GLOBAL ATT ',trim(name),' ',ivals(1:len)
02991                  mpp_file(unit)%Att(i)%fatt(1:len)=ivals(1:len)
02992               case (NF_FLOAT)
02993                  allocate(mpp_file(unit)%Att(i)%fatt(len))
02994                  error=NF_GET_ATT_REAL(ncid,NF_GLOBAL,name,rvals);call netcdf_err(error)
02995                  mpp_file(unit)%Att(i)%fatt(1:len)=rvals(1:len)
02996                  if( verbose .and. pe == 0)write (stdout(),*)  'GLOBAL ATT ',trim(name),' ',mpp_file(unit)%Att(i)%fatt(1:len)
02997               case (NF_DOUBLE)
02998                  allocate(mpp_file(unit)%Att(i)%fatt(len))
02999                  error=NF_GET_ATT_DOUBLE(ncid,NF_GLOBAL,name,r8vals);call netcdf_err(error)
03000                  mpp_file(unit)%Att(i)%fatt(1:len)=r8vals(1:len)
03001                  if( verbose .and. pe == 0)write (stdout(),*)  'GLOBAL ATT ',trim(name),' ',mpp_file(unit)%Att(i)%fatt(1:len)
03002            end select
03003 
03004         enddo
03005 !
03006 ! assign dimension name and length
03007 !
03008         do i=1,ndim
03009            error = NF_INQ_DIM(ncid,i,name,len);call netcdf_err(error)
03010            Axis(i)%name = name
03011            Axis(i)%len = len
03012         enddo
03013 
03014         nvar=0
03015         do i=1, nvar_total
03016            error=NF_INQ_VAR(ncid,i,name,type,nvdims,dimids,nvatts);call netcdf_err(error)
03017            isdim=.false.
03018            do j=1,ndim
03019               if( trim(lowercase(name)).EQ.trim(lowercase(Axis(j)%name)) )isdim=.true.
03020            enddo
03021            if (.not.isdim) nvar=nvar+1
03022         enddo
03023         mpp_file(unit)%nvar = nvar
03024         allocate(mpp_file(unit)%Var(nvar))
03025 
03026         do i=1,nvar
03027            mpp_file(unit)%Var(i) = default_field
03028         enddo
03029         
03030 !
03031 ! assign dimension info
03032 !
03033         do i=1, nvar_total
03034            error=NF_INQ_VAR(ncid,i,name,type,nvdims,dimids,nvatts);call netcdf_err(error)
03035            isdim=.false.
03036            do j=1,ndim
03037               if( trim(lowercase(name)).EQ.trim(lowercase(Axis(j)%name)) )isdim=.true.
03038            enddo
03039 
03040            if( isdim )then
03041               error=NF_INQ_DIMID(ncid,name,dimid);call netcdf_err(error)
03042               Axis(dimid)%type = type
03043               Axis(dimid)%did = dimid
03044               Axis(dimid)%id = i
03045               Axis(dimid)%natt = nvatts
03046               ! get axis values
03047               if( i.NE.mpp_file(unit)%id )then   ! non-record dims
03048                  select case (type)
03049                  case (NF_INT)
03050                     len=Axis(dimid)%len
03051                     allocate(Axis(dimid)%data(len))
03052                     error = NF_GET_VAR_INT(ncid,i,ivals);call netcdf_err(error)
03053                     Axis(dimid)%data(1:len)=ivals(1:len)                     
03054                  case (NF_FLOAT)
03055                     len=Axis(dimid)%len
03056                     allocate(Axis(dimid)%data(len))
03057                     error = NF_GET_VAR_REAL(ncid,i,rvals);call netcdf_err(error)
03058                     Axis(dimid)%data(1:len)=rvals(1:len)
03059                  case (NF_DOUBLE)
03060                     len=Axis(dimid)%len
03061                     allocate(Axis(dimid)%data(len))
03062                     error = NF_GET_VAR_DOUBLE(ncid,i,r8vals);call netcdf_err(error)
03063                     Axis(dimid)%data(1:len) = r8vals(1:len)
03064                  case (NF_CHAR) !RV,bundle
03065                     len=Axis(dimid)%len !RV,bundle
03066                     allocate(Axis(dimid)%cdata(len)) !RV,bundle
03067                     error = NF_GET_VAR_TEXT(ncid,i,Axis(dimid)%cdata) !RV,bundle
03068                     print*,'cdata',Axis(dimid)%cdata !RV,bundle
03069                     call netcdf_err(error) !RV,bundle
03070                  case default
03071                     call mpp_error( FATAL, 'Invalid data type for dimension' )
03072                  end select
03073              else
03074                  len = mpp_file(unit)%time_level
03075                  allocate(mpp_file(unit)%time_values(len))
03076                  select case (type)
03077                  case (NF_FLOAT)
03078                     error = NF_GET_VAR_REAL(ncid,i,rvals);call netcdf_err(error)
03079                     mpp_file(unit)%time_values(1:len) = rvals(1:len)
03080                  case (NF_DOUBLE)
03081                     error = NF_GET_VAR_DOUBLE(ncid,i,r8vals);call netcdf_err(error)
03082                     mpp_file(unit)%time_values(1:len) = r8vals(1:len)
03083                  case default
03084                     call mpp_error( FATAL, 'Invalid data type for dimension' )
03085                  end select
03086               endif
03087               ! assign dimension atts
03088               if( nvatts.GT.0 )allocate(Axis(dimid)%Att(nvatts))
03089 
03090               do j=1,nvatts
03091                  Axis(dimid)%Att(j) = default_att
03092               enddo
03093 
03094               do j=1,nvatts
03095                  error=NF_INQ_ATTNAME(ncid,i,j,attname);call netcdf_err(error)
03096                  error=NF_INQ_ATT(ncid,i,trim(attname),type,len);call netcdf_err(error)
03097 
03098                  Axis(dimid)%Att(j)%name = trim(attname)
03099                  Axis(dimid)%Att(j)%type = type
03100                  Axis(dimid)%Att(j)%len = len
03101 
03102                  select case (type)
03103                  case (NF_CHAR)
03104                     if (len.gt.512) call mpp_error(FATAL,'DIM ATT too long') 
03105                     error=NF_GET_ATT_TEXT(ncid,i,trim(attname),Axis(dimid)%Att(j)%catt);call netcdf_err(error)
03106                     if( verbose .and. pe == 0 ) &
03107                          write (stdout(),*)  'AXIS ',trim(Axis(dimid)%name),' ATT ',trim(attname),' ',Axis(dimid)%Att(j)%catt(1:len)
03108                     ! store integers in float arrays
03109                     ! assume dimension data not packed 
03110                  case (NF_SHORT)
03111                     allocate(Axis(dimid)%Att(j)%fatt(len))
03112                     error=NF_GET_ATT_INT2(ncid,i,trim(attname),i2vals);call netcdf_err(error)
03113                     Axis(dimid)%Att(j)%fatt(1:len)=i2vals(1:len)
03114                     if( verbose .and. pe == 0  ) &
03115                          write (stdout(),*)  'AXIS ',trim(Axis(dimid)%name),' ATT ',trim(attname),' ',Axis(dimid)%Att(j)%fatt
03116                  case (NF_INT)
03117                     allocate(Axis(dimid)%Att(j)%fatt(len))
03118                     error=NF_GET_ATT_INT(ncid,i,trim(attname),ivals);call netcdf_err(error)
03119                     Axis(dimid)%Att(j)%fatt(1:len)=ivals(1:len)
03120                     if( verbose .and. pe == 0  ) &
03121                          write (stdout(),*)  'AXIS ',trim(Axis(dimid)%name),' ATT ',trim(attname),' ',Axis(dimid)%Att(j)%fatt
03122                  case (NF_FLOAT)
03123                     allocate(Axis(dimid)%Att(j)%fatt(len))
03124                     error=NF_GET_ATT_REAL(ncid,i,trim(attname),rvals);call netcdf_err(error)
03125                     Axis(dimid)%Att(j)%fatt(1:len)=rvals(1:len)
03126                     if( verbose  .and. pe == 0 ) &
03127                          write (stdout(),*)  'AXIS ',trim(Axis(dimid)%name),' ATT ',trim(attname),' ',Axis(dimid)%Att(j)%fatt
03128                  case (NF_DOUBLE)
03129                     allocate(Axis(dimid)%Att(j)%fatt(len))
03130                     error=NF_GET_ATT_DOUBLE(ncid,i,trim(attname),r8vals);call netcdf_err(error)
03131                     Axis(dimid)%Att(j)%fatt(1:len)=r8vals(1:len)
03132                     if( verbose  .and. pe == 0 ) &
03133                          write (stdout(),*)  'AXIS ',trim(Axis(dimid)%name),' ATT ',trim(attname),' ',Axis(dimid)%Att(j)%fatt
03134                  case default
03135                     call mpp_error( FATAL, 'Invalid data type for dimension at' )                      
03136                  end select
03137                  ! assign pre-defined axis attributes
03138                  select case(trim(attname))
03139                  case('long_name')
03140                     Axis(dimid)%longname=Axis(dimid)%Att(j)%catt(1:len)
03141                  case('units')
03142                     Axis(dimid)%units=Axis(dimid)%Att(j)%catt(1:len)
03143                  case('cartesian_axis')
03144                     Axis(dimid)%cartesian=Axis(dimid)%Att(j)%catt(1:len)
03145                  case('positive') 
03146                     attval = Axis(dimid)%Att(j)%catt(1:len)
03147                     if( attval.eq.'down' )then
03148                        Axis(dimid)%sense=-1
03149                     else if( attval.eq.'up' )then
03150                        Axis(dimid)%sense=1
03151                     endif
03152                  end select
03153 
03154               enddo
03155               ! store axis info in filetype
03156               mpp_file(unit)%Axis(dimid) = Axis(dimid)
03157            endif
03158         enddo
03159 ! assign variable info
03160         nv = 0
03161         do i=1, nvar_total
03162            error=NF_INQ_VAR(ncid,i,name,type,nvdims,dimids,nvatts);call netcdf_err(error)
03163 !
03164 ! is this a dimension variable?
03165 !          
03166            isdim=.false.
03167            do j=1,ndim
03168               if( trim(lowercase(name)).EQ.trim(lowercase(Axis(j)%name)) )isdim=.true.
03169            enddo
03170 
03171            if( .not.isdim )then
03172 ! for non-dimension variables
03173               nv=nv+1; if( nv.GT.mpp_file(unit)%nvar )call mpp_error( FATAL, 'variable index exceeds number of defined variables' )
03174               mpp_file(unit)%Var(nv)%type = type
03175               mpp_file(unit)%Var(nv)%id = i
03176               mpp_file(unit)%Var(nv)%name = name
03177               mpp_file(unit)%Var(nv)%natt = nvatts
03178 ! determine packing attribute based on NetCDF variable type
03179              select case (type)
03180              case(NF_SHORT)
03181                  mpp_file(unit)%Var(nv)%pack = 4
03182              case(NF_FLOAT)
03183                  mpp_file(unit)%Var(nv)%pack = 2
03184              case(NF_DOUBLE)
03185                  mpp_file(unit)%Var(nv)%pack = 1
03186              case (NF_INT)
03187                  mpp_file(unit)%Var(nv)%pack = 2
03188              case default
03189                    call mpp_error( FATAL, 'Invalid variable type in NetCDF file' )
03190              end select
03191 ! assign dimension ids
03192               mpp_file(unit)%Var(nv)%ndim = nvdims
03193               allocate(mpp_file(unit)%Var(nv)%axes(nvdims))
03194               do j=1,nvdims
03195                  mpp_file(unit)%Var(nv)%axes(j) = Axis(dimids(j))
03196               enddo
03197               allocate(mpp_file(unit)%Var(nv)%size(nvdims))
03198 
03199               do j=1,nvdims
03200                  if( dimids(j).eq.mpp_file(unit)%recdimid )then
03201                     mpp_file(unit)%Var(nv)%time_axis_index = dimids(j)
03202                     mpp_file(unit)%Var(nv)%size(j)=1    ! dimid length set to 1 here for consistency w/ mpp_write 
03203                  else
03204                     mpp_file(unit)%Var(nv)%size(j)=Axis(dimids(j))%len
03205                  endif
03206               enddo
03207 ! assign variable atts
03208               if( nvatts.GT.0 )allocate(mpp_file(unit)%Var(nv)%Att(nvatts))
03209 
03210               do j=1,nvatts
03211                  mpp_file(unit)%Var(nv)%Att(j) = default_att
03212               enddo
03213               
03214               do j=1,nvatts
03215                  error=NF_INQ_ATTNAME(ncid,i,j,attname);call netcdf_err(error)
03216                  error=NF_INQ_ATT(ncid,i,attname,type,len);call netcdf_err(error)
03217                  mpp_file(unit)%Var(nv)%Att(j)%name = trim(attname)
03218                  mpp_file(unit)%Var(nv)%Att(j)%type = type
03219                  mpp_file(unit)%Var(nv)%Att(j)%len = len
03220                  
03221                  select case (type)
03222                    case (NF_CHAR)
03223                      if (len.gt.512) call mpp_error(FATAL,'VAR ATT too long') 
03224                      error=NF_GET_ATT_TEXT(ncid,i,trim(attname),mpp_file(unit)%Var(nv)%Att(j)%catt(1:len));call netcdf_err(error)
03225                      if (verbose .and. pe == 0 )&
03226                            write (stdout(),*)  'Var ',nv,' ATT ',trim(attname),' ',mpp_file(unit)%Var(nv)%Att(j)%catt(1:len)
03227 ! store integers as float internally
03228                    case (NF_SHORT)
03229                      allocate(mpp_file(unit)%Var(nv)%Att(j)%fatt(len))
03230                      error=NF_GET_ATT_INT2(ncid,i,trim(attname),i2vals);call netcdf_err(error)
03231                      mpp_file(unit)%Var(nv)%Att(j)%fatt(1:len)= i2vals(1:len)
03232                      if( verbose  .and. pe == 0 )&
03233                           write (stdout(),*)  'Var ',nv,' ATT ',trim(attname),' ',mpp_file(unit)%Var(nv)%Att(j)%fatt
03234                    case (NF_INT)
03235                      allocate(mpp_file(unit)%Var(nv)%Att(j)%fatt(len))
03236                      error=NF_GET_ATT_INT(ncid,i,trim(attname),ivals);call netcdf_err(error)
03237                      mpp_file(unit)%Var(nv)%Att(j)%fatt(1:len)=ivals(1:len)
03238                      if( verbose .and. pe == 0  )&
03239                           write (stdout(),*)  'Var ',nv,' ATT ',trim(attname),' ',mpp_file(unit)%Var(nv)%Att(j)%fatt
03240                    case (NF_FLOAT)
03241                      allocate(mpp_file(unit)%Var(nv)%Att(j)%fatt(len))
03242                      error=NF_GET_ATT_REAL(ncid,i,trim(attname),rvals);call netcdf_err(error)
03243                      mpp_file(unit)%Var(nv)%Att(j)%fatt(1:len)=rvals(1:len)
03244                      if( verbose  .and. pe == 0 )&
03245                           write (stdout(),*)  'Var ',nv,' ATT ',trim(attname),' ',mpp_file(unit)%Var(nv)%Att(j)%fatt
03246                    case (NF_DOUBLE)
03247                      allocate(mpp_file(unit)%Var(nv)%Att(j)%fatt(len))
03248                      error=NF_GET_ATT_DOUBLE(ncid,i,trim(attname),r8vals);call netcdf_err(error)
03249                      mpp_file(unit)%Var(nv)%Att(j)%fatt(1:len)=r8vals(1:len)
03250                      if( verbose .and. pe == 0  ) &
03251                           write (stdout(),*)  'Var ',nv,' ATT ',trim(attname),' ',mpp_file(unit)%Var(nv)%Att(j)%fatt
03252                    case default
03253                         call mpp_error( FATAL, 'Invalid data type for variable att' )
03254                  end select
03255 ! assign pre-defined field attributes
03256                  select case (trim(attname))
03257                     case ('long_name')
03258                       mpp_file(unit)%Var(nv)%longname=mpp_file(unit)%Var(nv)%Att(j)%catt(1:len)
03259                     case('units')
03260                       mpp_file(unit)%Var(nv)%units=mpp_file(unit)%Var(nv)%Att(j)%catt(1:len)
03261                     case('scale_factor') 
03262                        mpp_file(unit)%Var(nv)%scale=mpp_file(unit)%Var(nv)%Att(j)%fatt(1)
03263                     case('missing') 
03264                        mpp_file(unit)%Var(nv)%missing=mpp_file(unit)%Var(nv)%Att(j)%fatt(1)
03265                     case('add_offset') 
03266                        mpp_file(unit)%Var(nv)%add=mpp_file(unit)%Var(nv)%Att(j)%fatt(1)              
03267                     case('valid_range') 
03268                        mpp_file(unit)%Var(nv)%min=mpp_file(unit)%Var(nv)%Att(j)%fatt(1)
03269                        mpp_file(unit)%Var(nv)%max=mpp_file(unit)%Var(nv)%Att(j)%fatt(2)
03270                  end select
03271               enddo
03272            endif
03273         enddo   ! end variable loop
03274       else
03275         call mpp_error( FATAL,  'MPP READ CURRENTLY DOES NOT SUPPORT NON-NETCDF' ) 
03276       endif
03277 
03278       mpp_file(unit)%initialized = .TRUE.
03279 #else
03280       call mpp_error( FATAL, 'MPP_READ currently requires use_netCDF option' )
03281 #endif      
03282       return
03283     end subroutine mpp_read_meta
03284 
03285 
03286     subroutine mpp_get_info( unit, ndim, nvar, natt, ntime )
03287 
03288       integer, intent(in) :: unit
03289       integer, intent(out) :: ndim, nvar, natt, ntime
03290 
03291 
03292       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_INFO: must first call mpp_io_init.' )
03293       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_INFO: invalid unit number.' )
03294 
03295       ndim = mpp_file(unit)%ndim
03296       nvar = mpp_file(unit)%nvar
03297       natt = mpp_file(unit)%natt
03298       ntime = mpp_file(unit)%time_level
03299 
03300       return
03301 
03302     end subroutine mpp_get_info
03303 
03304      
03305     subroutine mpp_get_global_atts( unit, global_atts )
03306 !
03307 !  copy global file attributes for use by user
03308 !
03309 !  global_atts is an attribute type which is allocated from the
03310 !  calling routine
03311 
03312       integer,       intent(in)    :: unit
03313       type(atttype), intent(inout) :: global_atts(:)
03314       integer :: natt,i
03315 
03316       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_INFO: must first call mpp_io_init.' )
03317       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_INFO: invalid unit number.' )
03318 
03319       if (size(global_atts).lt.mpp_file(unit)%natt) &
03320       call mpp_error(FATAL, 'MPP_GET_ATTS: atttype not dimensioned properly in calling routine')
03321 
03322       natt = mpp_file(unit)%natt
03323       global_atts = default_att
03324 
03325       do i=1,natt
03326          global_atts(i) = mpp_file(unit)%Att(i)
03327       enddo
03328 
03329       return
03330    end subroutine mpp_get_global_atts
03331 
03332    subroutine mpp_get_field_atts( field, name, units, longname, min, max, missing, ndim, siz, axes, atts )
03333 
03334      type(fieldtype), intent(in) :: field
03335      character(len=*), intent(out) , optional :: name, units
03336      character(len=*), intent(out), optional :: longname
03337      real,intent(out), optional :: min,max,missing
03338      integer, intent(out), optional :: ndim
03339      integer, intent(out), dimension(:), optional :: siz
03340 
03341      type(atttype), intent(out), optional, dimension(:) :: atts
03342      type(axistype), intent(out), optional, dimension(:) :: axes
03343 
03344      integer :: n,m
03345 
03346      if (PRESENT(name)) name = field%name
03347      if (PRESENT(units)) units = field%units
03348      if (PRESENT(longname)) longname = field%longname
03349      if (PRESENT(min)) min = field%min
03350      if (PRESENT(max)) max = field%max
03351      if (PRESENT(missing)) missing = field%missing
03352      if (PRESENT(ndim)) ndim = field%ndim
03353      if (PRESENT(atts)) then
03354         atts = default_att
03355         n = size(atts);m=size(field%Att)
03356         if (n.LT.m) call mpp_error(FATAL,'attribute array not large enough in mpp_get_field_atts')
03357         atts(1:m) = field%Att(1:m)
03358      end if
03359      if (PRESENT(axes)) then
03360         axes = default_axis
03361         n = size(axes);m=field%ndim
03362         if (n.LT.m) call mpp_error(FATAL,'axis array not large enough in mpp_get_field_atts')
03363         axes(1:m) = field%axes(1:m)
03364      end if
03365      if (PRESENT(siz)) then
03366         siz = -1
03367         n = size(siz);m=field%ndim
03368         if (n.LT.m) call mpp_error(FATAL,'size array not large enough in mpp_get_field_atts')
03369         siz(1:m) = field%size(1:m)
03370      end if     
03371      return
03372    end subroutine mpp_get_field_atts
03373 
03374    subroutine mpp_get_axis_atts( axis, name, units, longname, cartesian, sense, len, natts, atts )
03375 
03376      type(axistype), intent(in) :: axis
03377      character(len=*), intent(out) , optional :: name, units
03378      character(len=*), intent(out), optional :: longname, cartesian
03379      integer,intent(out), optional :: sense, len , natts
03380      type(atttype), intent(out), optional, dimension(:) :: atts
03381 
03382      integer :: n,m 
03383 
03384      if (PRESENT(name)) name = axis%name
03385      if (PRESENT(units)) units = axis%units
03386      if (PRESENT(longname)) longname = axis%longname
03387      if (PRESENT(cartesian)) cartesian = axis%cartesian
03388      if (PRESENT(sense)) sense = axis%sense
03389      if (PRESENT(len)) len = axis%len
03390      if (PRESENT(atts)) then
03391         atts = default_att
03392         n = size(atts);m=size(axis%Att)
03393         if (n.LT.m) call mpp_error(FATAL,'attribute array not large enough in mpp_get_field_atts')
03394         atts(1:m) = axis%Att(1:m)
03395      end if
03396      if (PRESENT(natts)) natts = size(axis%Att)
03397      
03398      return
03399    end subroutine mpp_get_axis_atts
03400 
03401 
03402     subroutine mpp_get_fields( unit, variables )
03403 !
03404 !  copy variable information from file (excluding data)
03405 !  global_atts is an attribute type which is allocated from the
03406 !  calling routine
03407 ! 
03408       integer,         intent(in)    :: unit
03409       type(fieldtype), intent(inout) :: variables(:)
03410 
03411       integer :: nvar,i
03412 
03413       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_FIELDS: must first call mpp_io_init.' )
03414       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_FIELDS: invalid unit number.' )
03415 
03416       if (size(variables).ne.mpp_file(unit)%nvar) &
03417       call mpp_error(FATAL, 'MPP_GET_FIELDS: fieldtype not dimensioned properly in calling routine')
03418 
03419       nvar = mpp_file(unit)%nvar
03420 
03421       do i=1,nvar
03422          variables(i) = mpp_file(unit)%Var(i)
03423       enddo
03424 
03425       return
03426    end subroutine mpp_get_fields
03427 
03428     subroutine mpp_get_axes( unit, axes, time_axis )
03429 !
03430 !  copy variable information from file (excluding data)
03431 !  global_atts is an attribute type which is allocated from the
03432 !  calling routine
03433 ! 
03434       integer, intent(in) :: unit
03435       type(axistype), intent(out) :: axes(:)
03436       type(axistype), intent(out), optional :: time_axis      
03437       character(len=128) :: name
03438       logical :: save
03439       integer :: ndim,i, nvar, j, num_dims, k
03440 
03441       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_AXES: must first call mpp_io_init.' )
03442       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_AXES: invalid unit number.' )
03443 
03444       if (size(axes).ne.mpp_file(unit)%ndim) &
03445       call mpp_error(FATAL, 'MPP_GET_AXES: axistype not dimensioned properly in calling routine')
03446 
03447       
03448       if (PRESENT(time_axis)) time_axis = default_axis
03449       ndim = mpp_file(unit)%ndim
03450       do i=1,ndim
03451         if (ASSOCIATED(mpp_file(unit)%Axis(i)%data)) then   
03452            axes(i)=mpp_file(unit)%Axis(i)
03453        else
03454            axes(i)=mpp_file(unit)%Axis(i)
03455            if (PRESENT(time_axis)) time_axis = mpp_file(unit)%Axis(i)
03456         endif
03457       enddo
03458 
03459       return
03460    end subroutine mpp_get_axes
03461 
03462    subroutine mpp_get_times( unit, time_values )
03463 !
03464 !  copy time information from file and convert to time_type
03465 ! 
03466       integer, intent(in) :: unit
03467       real(DOUBLE_KIND), intent(inout) :: time_values(:)
03468 
03469       integer :: ntime,i
03470 
03471       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_TIMES: must first call mpp_io_init.' )
03472       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_TIMES: invalid unit number.' )
03473 
03474       if (size(time_values).ne.mpp_file(unit)%time_level) &
03475       call mpp_error(FATAL, 'MPP_GET_TIMES: time_values not dimensioned properly in calling routine')
03476 
03477       ntime = mpp_file(unit)%time_level
03478 
03479       do i=1,ntime
03480          time_values(i) = mpp_file(unit)%time_values(i)
03481       enddo
03482 
03483 
03484 
03485       return
03486    end subroutine mpp_get_times
03487 
03488    function mpp_get_field_index(fields,fieldname)
03489 
03490      type(fieldtype), dimension(:) :: fields
03491      character(len=*) :: fieldname
03492      integer :: mpp_get_field_index
03493 
03494      integer :: n
03495 
03496      mpp_get_field_index = -1
03497 
03498      do n=1,size(fields)
03499         if (lowercase(fields(n)%name) == lowercase(fieldname)) then
03500            mpp_get_field_index = n
03501            exit
03502         endif
03503      enddo
03504 
03505      return
03506    end function mpp_get_field_index
03507 
03508    function mpp_get_field_size(field)
03509 
03510      type(fieldtype) :: field
03511      integer :: mpp_get_field_size(4)
03512 
03513      integer :: n
03514 
03515      mpp_get_field_size = -1
03516 
03517      mpp_get_field_size(1) = field%size(1)
03518      mpp_get_field_size(2) = field%size(2)
03519      mpp_get_field_size(3) = field%size(3)
03520      mpp_get_field_size(4) = field%size(4)
03521 
03522      return
03523    end function mpp_get_field_size
03524 
03525 
03526    subroutine mpp_get_axis_data( axis, data )
03527 
03528      type(axistype), intent(in) :: axis
03529      real, dimension(:), intent(out) :: data
03530 
03531 
03532      if (size(data).lt.axis%len) call mpp_error(FATAL,'MPP_GET_AXIS_DATA: data array not large enough')
03533      if (.NOT.ASSOCIATED(axis%data)) then
03534         call mpp_error(NOTE,'MPP_GET_AXIS_DATA: use mpp_get_times for record dims')
03535         data = 0.
03536      else
03537         data(1:axis%len) = axis%data
03538      endif
03539 
03540      return
03541    end subroutine mpp_get_axis_data
03542 
03543 
03544    function mpp_get_recdimid(unit)
03545 !
03546       integer, intent(in) :: unit
03547       integer  :: mpp_get_recdimid
03548 
03549 
03550       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_RECDIMID: must first call mpp_io_init.' )
03551       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_RECDIMID: invalid unit number.' )
03552 
03553       mpp_get_recdimid = mpp_file(unit)%recdimid
03554 
03555       return
03556    end function mpp_get_recdimid
03557 
03558 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
03559 !                                                                      !
03560 !         mpp_get_iospec, mpp_flush: OS-dependent calls                !
03561 !                                                                      !
03562 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
03563 
03564     subroutine mpp_flush(unit)
03565 !flush the output on a unit, syncing with disk
03566       integer, intent(in) :: unit
03567 
03568       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_FLUSH: must first call mpp_io_init.' )
03569       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_FLUSH: invalid unit number.' )
03570       if( .NOT.mpp_file(unit)%initialized )call mpp_error( FATAL, 'MPP_FLUSH: cannot flush a file during writing of metadata.' )
03571       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
03572 
03573       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
03574 #ifdef use_netCDF
03575           error = NF_SYNC(mpp_file(unit)%ncid); call netcdf_err(error)
03576 #endif
03577       else
03578           call FLUSH(unit)
03579       end if
03580       return
03581     end subroutine mpp_flush
03582 
03583     subroutine mpp_get_iospec( unit, iospec )
03584       integer, intent(in) :: unit
03585       character(len=*), intent(out) :: iospec
03586 
03587       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_IOSPEC: must first call mpp_io_init.' )
03588       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_IOSPEC: invalid unit number.' )
03589 #ifdef SGICRAY
03590 !currently will write to stdout: don't know how to trap and return as string to iospec
03591       call ASSIGN( 'assign -V f:'//trim(mpp_file(unit)%name), error )
03592 #endif
03593       return
03594     end subroutine mpp_get_iospec
03595 
03596 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
03597 !                                                                      !
03598 !         netCDF-specific routines: mpp_get_id, netcdf_error         !
03599 !                                                                      !
03600 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
03601 
03602     function mpp_get_ncid(unit)
03603       integer :: mpp_get_ncid
03604       integer, intent(in) :: unit
03605 
03606       mpp_get_ncid = mpp_file(unit)%ncid
03607       return
03608     end function mpp_get_ncid
03609 
03610     function mpp_get_axis_id(axis)
03611       integer mpp_get_axis_id
03612       type(axistype), intent(in) :: axis
03613       mpp_get_axis_id = axis%id
03614       return
03615     end function mpp_get_axis_id
03616 
03617     function mpp_get_field_id(field)
03618       integer mpp_get_field_id
03619       type(fieldtype), intent(in) :: field
03620       mpp_get_field_id = field%id
03621       return
03622     end function mpp_get_field_id
03623 
03624     subroutine netcdf_err(err)
03625       integer, intent(in) :: err
03626       character(len=80) :: errmsg
03627       integer :: unit
03628 
03629 #ifdef use_netCDF
03630       if( err.EQ.NF_NOERR )return
03631       errmsg = NF_STRERROR(err)
03632       call mpp_io_exit()        !make sure you close all open files
03633       call mpp_error( FATAL, 'NETCDF ERROR: '//trim(errmsg) )
03634 #endif
03635       return
03636     end subroutine netcdf_err
03637 
03638 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
03639 !                                                                      !
03640 !       minor routines: mpp_get_unit_range, mpp_set_unit_range         !
03641 !                                                                      !
03642 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
03643 
03644     subroutine mpp_get_unit_range( unit_begin_out, unit_end_out )
03645       integer, intent(out) ::      unit_begin_out, unit_end_out
03646 
03647       unit_begin_out = unit_begin; unit_end_out = unit_end
03648       return
03649     end subroutine mpp_get_unit_range
03650 
03651     subroutine mpp_set_unit_range( unit_begin_in, unit_end_in )
03652       integer, intent(in) ::       unit_begin_in, unit_end_in
03653 
03654       if( unit_begin_in.GT.unit_end_in )call mpp_error( FATAL, 'MPP_SET_UNIT_RANGE: unit_begin_in.GT.unit_end_in.' )
03655       if( unit_begin_in.LT.0           )call mpp_error( FATAL, 'MPP_SET_UNIT_RANGE: unit_begin_in.LT.0.' )
03656       if( unit_end_in  .GT.maxunits    )call mpp_error( FATAL, 'MPP_SET_UNIT_RANGE: unit_end_in.GT.maxunits.' )
03657       unit_begin = unit_begin_in; unit_end = unit_end_in
03658       return
03659     end subroutine mpp_set_unit_range
03660 
03661     subroutine mpp_modify_axis_meta( axis, name, units, longname, cartesian, data )
03662 
03663       type(axistype), intent(inout) :: axis
03664       character(len=*), intent(in), optional :: name, units, longname, cartesian
03665       real, dimension(:), intent(in), optional :: data
03666 
03667       if (PRESENT(name)) axis%name = trim(name)
03668       if (PRESENT(units)) axis%units = trim(units)
03669       if (PRESENT(longname)) axis%longname = trim(longname)
03670       if (PRESENT(cartesian)) axis%cartesian = trim(cartesian)
03671       if (PRESENT(data)) then
03672          axis%len = size(data)
03673          if (ASSOCIATED(axis%data)) deallocate(axis%data)
03674          allocate(axis%data(axis%len))
03675          axis%data = data
03676       endif
03677          
03678       return
03679     end subroutine mpp_modify_axis_meta
03680 
03681     subroutine mpp_modify_field_meta( field, name, units, longname, min, max, missing, axes )
03682 
03683       type(fieldtype), intent(inout) :: field
03684       character(len=*), intent(in), optional :: name, units, longname
03685       real, intent(in), optional :: min, max, missing
03686       type(axistype), dimension(:), intent(inout), optional :: axes
03687 
03688       if (PRESENT(name)) field%name = trim(name)
03689       if (PRESENT(units)) field%units = trim(units)
03690       if (PRESENT(longname)) field%longname = trim(longname)
03691       if (PRESENT(min)) field%min = min
03692       if (PRESENT(max)) field%max = max
03693       if (PRESENT(missing)) field%missing = missing
03694 !      if (PRESENT(axes)) then
03695 !         axis%len = size(data)
03696 !         deallocate(axis%data)
03697 !         allocate(axis%data(axis%len))
03698 !         axis%data = data
03699 !      endif
03700          
03701       return
03702     end subroutine mpp_modify_field_meta
03703 
03704     function lowercase (cs)
03705       implicit none
03706       character(len=*), intent(in) :: cs
03707       character(len=len(cs))       :: lowercase 
03708       
03709       integer, parameter :: co=iachar('a')-iachar('A') ! case offset
03710       integer :: i
03711       character :: ca
03712       
03713       lowercase = cs
03714       do i = 1, len(cs)
03715         ca = cs(i:i)
03716         if (ca >= "A" .and. ca <= "Z") then
03717           lowercase(i:i) =  achar(iachar(ca)+co)
03718         endif
03719       enddo
03720           
03721     end function lowercase
03722 
03723 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
03724 !                                                                      !
03725 !       minor routines: mpp_nullify_axistype,                          !
03726 !                       mpp_nullify_axistype_array                     !
03727 !                                                                      !
03728 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
03729 
03730     subroutine mpp_nullify_axistype(axis)
03731       type(axistype), intent(inout) :: axis
03732 
03733       Nullify(axis%data)
03734       Nullify(axis%cdata)
03735       Nullify(axis%Att)
03736     end subroutine mpp_nullify_axistype
03737 
03738     subroutine mpp_nullify_axistype_array(axis)
03739       type(axistype), intent(inout), dimension(:) :: axis
03740       integer :: i
03741 
03742       do i=1, size(axis)
03743         Nullify(axis(i)%data)
03744         Nullify(axis(i)%cdata)
03745         Nullify(axis(i)%Att)
03746       enddo
03747     end subroutine mpp_nullify_axistype_array
03748         
03749 end module mpp_io_mod_oa
03750 
03751 #else
03752 !
03753 !ParNetCDF
03754 !
03755 module mpp_io_mod_oa
03756   use mod_kinds_mpp
03757   use mpp_mod_oa
03758   use mpp_domains_mod_oa
03759   implicit none
03760 #include <os.h>
03761   private
03762 
03763   character(len=128), private :: version= 
03764        '$Id: mpp_io_mod_oa.F90 3541 2012-08-09 09:58:25Z coquart $'
03765   character(len=128), private :: tagname= 
03766        '$Name$'
03767 
03768   integer, private :: pe, npes
03769 
03770   type, public :: axistype
03771      private
03772      character(len=128) :: name
03773      character(len=128) :: units
03774      character(len=256) :: longname
03775      character(len=8) :: cartesian
03776      integer :: sense, len           !+/-1, depth or height?
03777      type(domain1D) :: domain !if pointer is associated, it is a distributed data axis
03778      real, pointer :: data(:)   !axis values (not used if time axis)
03779      character(len=64), pointer :: cdata(:)  !RV,bundles
03780      integer :: clenid                       !RV,bundles
03781      integer :: id, did, type, natt         !id is the "variable ID", did is the "dimension ID": netCDF requires 2 IDs for axes
03782      type(atttype), pointer :: Att(:)
03783   end type axistype
03784 
03785   type, public :: atttype
03786      integer :: type, len
03787      character(len=128) :: name
03788      character(len=256) :: catt
03789 ! just use type conversion for integers
03790      real, pointer :: fatt(:)
03791   end type atttype
03792 
03793   type, public :: fieldtype
03794      private
03795      character(len=128) :: name
03796      character(len=128) :: units
03797      character(len=256) :: longname
03798      real :: min, max, missing, fill, scale, add
03799      integer :: pack
03800      type(axistype), pointer :: axes(:) !axes associated with field
03801 !size, time_axis_index redundantly hold info already contained in axes
03802 !it's clunky and inelegant, but required so that axes can be shared among multiple files
03803      integer, pointer :: size(:)
03804      integer :: time_axis_index
03805      integer :: id, type, natt, ndim
03806      type(atttype), pointer :: Att(:)
03807   end type fieldtype
03808 
03809   type, private :: filetype
03810      character(len=256) :: name
03811      integer :: action, format, access, threading, fileset, record, ncid
03812      logical :: opened, initialized, nohdrs
03813      integer :: time_level
03814      real(DOUBLE_KIND) :: time
03815      integer :: id             !variable ID of time axis associated with file (only one time axis per file)
03816      integer :: recdimid             !dim ID of time axis associated with file (only one time axis per file)
03817 !
03818 ! time axis values are stored here instead of axis%data since mpp_write
03819 ! assumes these values are not time values. Not used in mpp_write
03820 !
03821      real(DOUBLE_KIND), pointer :: time_values(:)
03822 
03823 ! additional elements of filetype for mpp_read (ignored for mpp_write)
03824      integer :: ndim, nvar, natt  ! number of dimensions, non-dimension variables and global attributes
03825 ! redundant axis types stored here and in associated fieldtype
03826 ! some axes are not used by any fields, i.e. "edges"
03827      type(axistype), pointer  :: axis(:)
03828      type(fieldtype), pointer :: var(:)
03829      type(atttype), pointer   :: att(:)
03830   end type filetype
03831 
03832   type(axistype), public  :: default_axis !provided to users with default components
03833   type(fieldtype), public :: default_field !provided to users with default components
03834   type(atttype), public   :: default_att !provided to users with default components
03835 !action on open
03836   integer, parameter, public :: MPP_WRONLY=100, MPP_RDONLY=101, MPP_APPEND=102, MPP_OVERWR=103
03837 !format
03838   integer, parameter, public :: MPP_ASCII=200,  MPP_IEEE32=201, MPP_NATIVE=202, MPP_NETCDF=203
03839 !access
03840   integer, parameter, public :: MPP_SEQUENTIAL=300, MPP_DIRECT=301
03841 !threading, fileset
03842   integer, parameter, public :: MPP_SINGLE=400, MPP_MULTI=401, MPP_PARALLEL=402
03843 !action on close
03844   integer, parameter, public :: MPP_DELETE=501, MPP_COLLECT=502
03845 
03846   type(filetype), private, allocatable :: mpp_file(:)
03847   integer, private :: records_per_pe
03848   integer, private :: maxunits, unit_begin, unit_end
03849   integer, private :: varnum=0
03850   integer, private :: error
03851   character(len=256) :: text
03852 !null unit: returned by PEs not participating in IO after a collective call
03853   integer, parameter, private :: NULLUNIT=-1
03854   real(DOUBLE_KIND), parameter, private :: NULLTIME=-1.
03855 #ifdef DEBUG
03856   logical, private :: verbose=.FALSE., debug=.TRUE., module_is_initialized=.FALSE.
03857 #else
03858   logical, private :: verbose=.FALSE., debug=.FALSE., module_is_initialized=.FALSE.
03859 #endif
03860 
03861   real(DOUBLE_KIND), private, allocatable :: mpp_io_stack(:)
03862   integer, private :: mpp_io_stack_size=0, mpp_io_stack_hwm=0
03863   
03864   interface mpp_write_meta
03865      module procedure mpp_write_meta_var
03866      module procedure mpp_write_meta_scalar_r
03867      module procedure mpp_write_meta_scalar_i
03868      module procedure mpp_write_meta_axis
03869      module procedure mpp_write_meta_field
03870      module procedure mpp_write_meta_global
03871      module procedure mpp_write_meta_global_scalar_r
03872      module procedure mpp_write_meta_global_scalar_i
03873   end interface
03874 
03875   interface mpp_copy_meta
03876      module procedure mpp_copy_meta_axis
03877      module procedure mpp_copy_meta_field
03878      module procedure mpp_copy_meta_global
03879   end interface
03880      
03881   interface mpp_write
03882      module procedure mpp_write_2ddecomp_r1d
03883      module procedure mpp_write_2ddecomp_r2d
03884      module procedure mpp_write_2ddecomp_r3d
03885      module procedure mpp_write_2ddecomp_r4d
03886      module procedure mpp_write_r0D
03887      module procedure mpp_write_r1D
03888      module procedure mpp_write_r2D
03889      module procedure mpp_write_r3D
03890      module procedure mpp_write_r4D
03891      module procedure mpp_write_axis
03892   end interface
03893 
03894   interface mpp_read
03895      module procedure mpp_read_2ddecomp_r1d
03896      module procedure mpp_read_2ddecomp_r2d
03897      module procedure mpp_read_2ddecomp_r3d
03898      module procedure mpp_read_2ddecomp_r4d
03899      module procedure mpp_read_r0D
03900      module procedure mpp_read_r1D
03901      module procedure mpp_read_r2D
03902      module procedure mpp_read_r3D
03903      module procedure mpp_read_r4D
03904   end interface
03905 
03906   interface mpp_get_id
03907      module procedure mpp_get_axis_id
03908      module procedure mpp_get_field_id
03909   end interface
03910 
03911   interface mpp_get_atts
03912      module procedure mpp_get_global_atts
03913      module procedure mpp_get_field_atts
03914      module procedure mpp_get_axis_atts
03915   end interface
03916 
03917   interface mpp_modify_meta
03918 !     module procedure mpp_modify_att_meta
03919      module procedure mpp_modify_field_meta
03920      module procedure mpp_modify_axis_meta
03921   end interface
03922 
03923   public :: mpp_close, mpp_flush, mpp_get_iospec, mpp_get_id, mpp_get_ncid, mpp_get_unit_range, mpp_io_init, mpp_io_exit, &
03924             mpp_open, mpp_set_unit_range, mpp_write, mpp_write_meta, mpp_read, mpp_get_info, mpp_get_atts, &
03925             mpp_get_fields, mpp_get_times, mpp_get_axes, mpp_copy_meta, mpp_get_recdimid, mpp_get_axis_data, mpp_modify_meta, &
03926             mpp_io_set_stack_size, mpp_get_field_index, mpp_nullify_axistype, mpp_nullify_axistype_array
03927 
03928   private :: read_record, mpp_read_meta, lowercase
03929 
03930 #ifdef use_netCDF
03931 #include <pnetcdf.inc>
03932 !rr
03933 #ifdef NAG_COMPILER
03934     use mpi
03935 #else
03936 #include <mpif.h>
03937 !!include 'mpif.h'
03938 #endif
03939 
03940   integer(kind=MPI_OFFSET_KIND), private :: idim
03941 #endif
03942 
03943   contains
03944 
03945 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
03946 !                                                                      !
03947 !               mpp_io_init: initialize parallel I/O                   !
03948 !                                                                      !
03949 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
03950     subroutine mpp_io_init( flags, maxunit,maxresunit )
03951       integer, intent(in), optional :: flags, maxunit ,maxresunit
03952 !rv
03953 !rv I introduced the variable to indentify that the top max_reserved_units 
03954 !rv of maxunits are reserved for OASIS coupler specific things like the trace
03955 !rv files. This variable is active only if one specifies explicitely  the
03956 !rv argument maxunit.
03957       integer::max_reserved_units
03958 !rv
03959 !initialize IO package: initialize mpp_file array, set valid range of units for fortran IO
03960 
03961       if( module_is_initialized )return
03962       call mpp_init(flags)           !if mpp_init has been called, this call will merely return
03963       pe = mpp_pe()
03964       npes = mpp_npes()
03965       call mpp_domains_init(flags)
03966 
03967       maxunits = 64
03968       if( PRESENT(maxunit) )maxunits = maxunit
03969 
03970       max_reserved_units=5
03971       if( PRESENT(maxresunit) )max_reserved_units = maxresunit
03972 
03973       if( PRESENT(flags) )then
03974           debug   = flags.EQ.MPP_DEBUG
03975           verbose = flags.EQ.MPP_VERBOSE .OR. debug
03976       end if
03977 !initialize default_field
03978       default_field%name = 'noname'
03979       default_field%units = 'nounits'
03980       default_field%longname = 'noname'
03981       default_field%id = -1
03982       default_field%type = -1
03983       default_field%natt = -1
03984       default_field%ndim = -1
03985 !largest possible 4-byte reals
03986       default_field%min = -huge(1._ip_single_mpp)
03987       default_field%max =  huge(1._ip_single_mpp)
03988       default_field%missing = -1e36
03989       default_field%fill = -1e36
03990       default_field%scale = 0.
03991       default_field%add = huge(1._ip_single_mpp)
03992       default_field%pack = 1
03993       default_field%time_axis_index = -1 !this value will never match any index
03994       Nullify(default_field%axes)
03995       Nullify(default_field%size)
03996       Nullify(default_field%att)
03997 ! Initialize default axis
03998       default_axis%name = 'noname'
03999       default_axis%units = 'nounits'
04000       default_axis%longname = 'noname'
04001       default_axis%cartesian = 'none'
04002       default_axis%sense = 0
04003       default_axis%len = -1
04004       default_axis%id = -1
04005       default_axis%did = -1
04006       default_axis%type = -1
04007       default_axis%natt = -1
04008       Nullify(default_axis%data)
04009 ! Initialize default attribute
04010       default_att%name = 'noname'
04011       default_att%type = -1
04012       default_att%len = -1
04013       default_att%catt = 'none'
04014       Nullify(default_att%fatt)
04015       
04016 !up to MAXUNITS fortran units and MAXUNITS netCDF units are supported
04017 !file attributes (opened, format, access, threading, fileset) are saved against the unit number
04018 !external handles to netCDF units are saved from maxunits+1:2*maxunits
04019       allocate( mpp_file(NULLUNIT:2*maxunits) ) !starts at NULLUNIT=-1, used by non-participant PEs in single-threaded I/O
04020       mpp_file(:)%name   = ' '
04021       mpp_file(:)%action    = -1
04022       mpp_file(:)%format    = -1
04023       mpp_file(:)%threading = -1
04024       mpp_file(:)%fileset   = -1
04025       mpp_file(:)%record    = -1
04026       mpp_file(:)%ncid      = -1
04027       mpp_file(:)%opened = .FALSE.
04028       mpp_file(:)%initialized = .FALSE.
04029       mpp_file(:)%time_level = 0
04030       mpp_file(:)%time = NULLTIME
04031       mpp_file(:)%id = -1
04032 !
04033       mpp_file(:)%ndim = -1
04034       mpp_file(:)%nvar = -1
04035 !NULLUNIT "file" is always single-threaded, open and initialized (to pass checks in mpp_write)
04036       mpp_file(NULLUNIT)%threading = MPP_SINGLE
04037       mpp_file(NULLUNIT)%opened = .TRUE.
04038       mpp_file(NULLUNIT)%initialized = .TRUE.
04039 !declare the stdunits to be open
04040       mpp_file(stdin ())%opened = .TRUE.
04041       mpp_file(stdout())%opened = .TRUE.
04042       mpp_file(stderr())%opened = .TRUE.
04043       mpp_file(stdout())%opened = .TRUE.
04044 !set range of allowed fortran unit numbers: could be compiler-dependent (should not overlap stdin/out/err)
04045 !
04046 !rv For OASIS 3 I consider the top max_reserved_units to be excluded from
04047 !rv the list of files ito closed during mpp_io_exit.
04048 !rv      call mpp_set_unit_range( 7, maxunits )
04049       if(present(maxunit)) then
04050         call mpp_set_unit_range( 7, maxunits-max_reserved_units )
04051       else
04052         call mpp_set_unit_range( 7, maxunits )
04053       endif
04054 !rv
04055 
04056 !rr   if( pe.EQ.mpp_root_pe() )then
04057           write( stdout(),'(/a)' )'MPP_IO module '//trim(version)
04058 #ifdef use_netCDF
04059 !rr not yet supported
04060 !rr       text = NFMPI_INQ_LIBVERS()
04061 !rr       write( stdout(),'(a)' )'Using netCDF library version '//trim(text)
04062 #endif
04063 !rr   endif
04064 
04065 #ifdef CRAYPVP
04066 !we require every file to be assigned threadwise: PVPs default to global, and are reset here
04067       call ASSIGN( 'assign -P thread p:%', error )
04068 #endif
04069 
04070       call mpp_io_set_stack_size(131072) ! default initial value
04071       call mpp_sync()
04072       module_is_initialized = .TRUE.
04073       return
04074     end subroutine mpp_io_init
04075 
04076     subroutine mpp_io_exit()
04077       integer :: unit
04078 
04079       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_IO_EXIT: must first call mpp_io_init.' )
04080 !close all open fortran units
04081       do unit = unit_begin,unit_end
04082          if( mpp_file(unit)%opened )call FLUSH(unit)
04083       end do
04084       call mpp_sync()
04085       do unit = unit_begin,unit_end
04086          if( mpp_file(unit)%opened )close(unit)
04087       end do
04088 #ifdef use_netCDF
04089 !close all open netCDF units
04090       do unit = maxunits+1,2*maxunits
04091          if( mpp_file(unit)%opened )error = NFMPI_CLOSE(mpp_file(unit)%ncid)
04092       end do
04093 #endif
04094 
04095       call mpp_max(mpp_io_stack_hwm)
04096       
04097 !rr   if( pe.EQ.mpp_root_pe() )then
04098 !          write( stdout,'(/a)' )'Exiting MPP_IO module...'
04099 !          write( stdout,* )'MPP_IO_STACK high water mark=', mpp_io_stack_hwm
04100 !rr   end if
04101       deallocate(mpp_file)
04102       module_is_initialized = .FALSE.
04103       return
04104     end subroutine mpp_io_exit
04105 
04106     subroutine mpp_io_set_stack_size(n)
04107 !set the mpp_io_stack variable to be at least n LONG words long
04108       integer, intent(in) :: n
04109       character(len=8) :: text
04110 
04111       if( n.GT.mpp_io_stack_size .AND. allocated(mpp_io_stack) )deallocate(mpp_io_stack)
04112       if( .NOT.allocated(mpp_io_stack) )then
04113           allocate( mpp_io_stack(n) )
04114           mpp_io_stack_size = n
04115           write( text,'(i8)' )n 
04116 !rr       if( pe.EQ.mpp_root_pe() ) &
04117           call mpp_error( NOTE, 'MPP_IO_SET_STACK_SIZE: stack size set to '//text//'.' )    
04118       end if
04119 
04120       return
04121     end subroutine mpp_io_set_stack_size
04122     
04123 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
04124 !                                                                            !
04125 !           OPENING AND CLOSING FILES: mpp_open() and mpp_close()            !
04126 !                                                                            !
04127 ! mpp_open( unit, file, action, form, access, threading, &                   !
04128 !           fileset, iospec, nohdrs, recl, pelist )                          !
04129 !      integer, intent(out) :: unit                                          !
04130 !      character(len=*), intent(in) :: file                                  !
04131 !      integer, intent(in), optional :: action, form, access, threading,     !
04132 !                                       fileset, recl                        !
04133 !      character(len=*), intent(in), optional :: iospec                      !
04134 !      logical, intent(in), optional :: nohdrs                               !
04135 !      integer, optional, intent(in) :: pelist(:) !default ALL               !
04136 !                                                                            !
04137 !  unit is intent(OUT): always _returned_by_ mpp_open()                      !
04138 !  file is the filename: REQUIRED                                            !
04139 !    we append .nc to filename if it is a netCDF file                        !
04140 !    we append .<pppp> to filename if fileset is private (pppp is PE number) !
04141 !  iospec is a system hint for I/O organization                              !
04142 !         e.g assign(1) on SGI/Cray systems.                                 !
04143 !  if nohdrs is .TRUE. headers are not written on non-netCDF writes.         !
04144 !  nohdrs has no effect when action=MPP_RDONLY|MPP_APPEND                    !
04145 !                    or when form=MPP_NETCDF                                 !
04146 ! FLAGS:                                                                     !
04147 !    action is one of MPP_RDONLY, MPP_APPEND or MPP_WRONLY                   !
04148 !    form is one of MPP_ASCII:  formatted read/write                         !
04149 !                   MPP_NATIVE: unformatted read/write, no conversion        !
04150 !                   MPP_IEEE32: unformatted read/write, conversion to IEEE32 !
04151 !                   MPP_NETCDF: unformatted read/write, conversion to netCDF !
04152 !    access is one of MPP_SEQUENTIAL or MPP_DIRECT (ignored for netCDF)      !
04153 !      RECL argument is REQUIRED for direct access IO                        !
04154 !    threading is one of MPP_SINGLE or MPP_MULTI                             !
04155 !      single-threaded IO in a multi-PE run is done by PE0                   !
04156 !    fileset is one of MPP_MULTI and MPP_SINGLE                              !
04157 !      fileset is only used for multi-threaded I/O                           !
04158 !      if all I/O PEs in <pelist> use a single fileset,                      !
04159 !              they write to the same file                                   !
04160 !      if all I/O PEs in <pelist> use a multi  fileset,                      !
04161 !              they each write an independent file                           !
04162 !  recl is the record length in bytes                                        !
04163 !  pelist is the list of I/O PEs (currently ALL)                             !
04164 !                                                                            !
04165 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
04166     subroutine mpp_open( unit, file, action, mpp_comm, form, access, threading, &
04167                                      fileset, iospec, nohdrs, recl, pelist )
04168 
04169       integer, intent(out) :: unit
04170       character(len=*), intent(in) :: file
04171       integer, intent(in), optional :: action, form, access, threading, 
04172            fileset, recl, mpp_comm
04173       character(len=*), intent(in), optional :: iospec
04174       logical, intent(in), optional :: nohdrs
04175       integer, intent(in), optional :: pelist(:) !default ALL
04176 
04177       character(len=16) :: act, acc, for, pos
04178       integer :: action_flag, form_flag, access_flag, threading_flag, fileset_flag, length
04179       logical :: exists
04180       character(len=64) :: filespec
04181       type(axistype) :: unlim    !used by netCDF with mpp_append
04182 
04183       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_OPEN: must first call mpp_io_init.' )
04184 !set flags
04185       action_flag = MPP_WRONLY        !default
04186       if( PRESENT(action) )action_flag = action
04187       form_flag = MPP_ASCII
04188       if( PRESENT(form) )form_flag = form
04189 #ifndef use_netCDF
04190       if( form_flag.EQ.MPP_NETCDF ) &
04191            call mpp_error( FATAL, 'MPP_OPEN: To open a file with form=MPP_NETCDF, you must compile mpp_io with -Duse_netCDF.' )
04192 #endif
04193       access_flag = MPP_SEQUENTIAL
04194       if( PRESENT(access) )access_flag = access
04195       threading_flag = MPP_SINGLE
04196       if( npes.GT.1 .AND. PRESENT(threading) )threading_flag = threading
04197       fileset_flag = MPP_MULTI
04198       if( PRESENT(fileset) )fileset_flag = fileset
04199       if( threading_flag.EQ.MPP_SINGLE )fileset_flag = MPP_SINGLE
04200 !rr
04201       fileset_flag = MPP_PARALLEL
04202       threading_flag = MPP_PARALLEL
04203 
04204 !get a unit number
04205       if( threading_flag.EQ.MPP_SINGLE )then
04206           if( pe.NE.mpp_root_pe() .AND. action_flag.NE.MPP_RDONLY )then
04207               unit = NULLUNIT           !PEs not participating in IO from this mpp_open() will return this value for unit
04208               return
04209           end if
04210       end if
04211       if( form_flag.EQ.MPP_NETCDF )then
04212           do unit = maxunits+1,2*maxunits
04213              if( .NOT.mpp_file(unit)%opened )exit
04214           end do
04215           if( unit.GT.2*maxunits )call mpp_error( FATAL, 'MPP_OPEN: too many open netCDF files.' )
04216       else
04217           do unit = unit_begin, unit_end
04218              inquire( unit,OPENED=mpp_file(unit)%opened )
04219              if( .NOT.mpp_file(unit)%opened )exit
04220           end do
04221           if( unit.GT.unit_end )call mpp_error( FATAL, 'MPP_OPEN: no available units.' )
04222       end if
04223 
04224 !get a filename
04225       text = file
04226       length = len(file)
04227 
04228 !RV   I dropped the automatic file name extension. PSMILE will always
04229 !RV   provide netcdf file names with an extension .nc or names containing .nc.
04230 !RV      if( form_flag.EQ.MPP_NETCDF.AND. file(length-2:length) /= '.nc' ) &
04231 !RV         text = trim(file)//'.nc'
04232          
04233       if( fileset_flag.EQ.MPP_MULTI )write( text,'(a,i4.4)' )trim(text)//'.', pe
04234       mpp_file(unit)%name = text
04235       if( verbose ) write (stdout(), '(a,2i3,1x,a,5i5)') &
04236            'MPP_OPEN: PE, unit, filename, action, format, access, threading, fileset=', &
04237            pe, unit, trim(mpp_file(unit)%name), action_flag, form_flag, access_flag, threading_flag, fileset_flag
04238 
04239 !action: read, write, overwrite, append: act and pos are ignored by netCDF
04240       if( action_flag.EQ.MPP_RDONLY )then
04241           act = 'READ'
04242           pos = 'REWIND'
04243 !          if( form_flag.EQ.MPP_NETCDF )call mpp_error( FATAL, 'MPP_OPEN: only writes are currently supported with netCDF.' )
04244       else if( action_flag.EQ.MPP_WRONLY .OR. action_flag.EQ.MPP_OVERWR )then
04245           act = 'WRITE'
04246           pos = 'REWIND'
04247       else if( action_flag.EQ.MPP_APPEND )then
04248           act = 'WRITE'
04249           pos = 'APPEND'
04250       else
04251           call mpp_error( FATAL, 'MPP_OPEN: action must be one of MPP_WRONLY, MPP_APPEND or MPP_RDONLY.' )
04252       end if
04253 
04254 !access: sequential or direct: ignored by netCDF
04255       if( form_flag.NE.MPP_NETCDF )then
04256           if( access_flag.EQ.MPP_SEQUENTIAL )then
04257               acc = 'SEQUENTIAL'
04258           else if( access_flag.EQ.MPP_DIRECT )then
04259               acc = 'DIRECT'
04260               if( form_flag.EQ.MPP_ASCII )call mpp_error( FATAL, 'MPP_OPEN: formatted direct access I/O is prohibited.' )
04261               if( .NOT.PRESENT(recl) ) &
04262                    call mpp_error( FATAL, 'MPP_OPEN: recl (record length in bytes) must be specified with access=MPP_DIRECT.' )
04263               mpp_file(unit)%record = 1
04264               records_per_pe = 1 !each PE writes 1 record per mpp_write
04265           else
04266               call mpp_error( FATAL, 'MPP_OPEN: access must be one of MPP_SEQUENTIAL or MPP_DIRECT.' )
04267           end if
04268       end if
04269 
04270 !threading: SINGLE or MULTI
04271       if( threading_flag.EQ.MPP_MULTI )then
04272 !fileset: MULTI or SINGLE (only for multi-threaded I/O
04273           if( fileset_flag.EQ.MPP_SINGLE )then
04274               if( form_flag.EQ.MPP_NETCDF .AND. act.EQ.'WRITE' ) &
04275                    call mpp_error( FATAL, 'MPP_OPEN: netCDF currently does not support single-file multi-threaded output.' )
04276 
04277 #ifdef _CRAYT3E
04278               call ASSIGN( 'assign -I -F global.privpos f:'//trim(mpp_file(unit)%name), error )
04279 #endif
04280           else if( fileset_flag.NE.MPP_PARALLEL )then
04281               call mpp_error( FATAL, 'MPP_OPEN: fileset must be one of MPP_PARALLEL.' )
04282           end if
04283       else if( threading_flag.NE.MPP_PARALLEL )then
04284           call mpp_error( FATAL, 'MPP_OPEN: threading must be MPP_PARALLEL.' )
04285       end if
04286 
04287 !apply I/O specs before opening the file
04288 !note that -P refers to the scope of a fortran unit, which is always thread-private even if file is shared
04289 #ifdef CRAYPVP
04290       call ASSIGN( 'assign -I -P thread  f:'//trim(mpp_file(unit)%name), error )
04291 #endif
04292 #ifdef _CRAYT3E
04293       call ASSIGN( 'assign -I -P private f:'//trim(mpp_file(unit)%name), error )
04294 #endif
04295       if( PRESENT(iospec) )then
04296 !iospec provides hints to the system on how to organize I/O
04297 !on Cray systems this is done through 'assign', see assign(1) and assign(3F)
04298 !on other systems this will be expanded as needed
04299 !no error checks here on whether the supplied iospec is valid
04300 #ifdef SGICRAY
04301           call ASSIGN( 'assign -I '//trim(iospec)//' f:'//trim(mpp_file(unit)%name), error )
04302           if( form_flag.EQ.MPP_NETCDF )then
04303 !for netCDF on SGI/Cray systems we pass it to the environment variable NETCDF_XFFIOSPEC
04304 !ideally we should parse iospec, pass the argument of -F to NETCDF_FFIOSPEC, and the rest to NETCDF_XFFIOSPEC
04305 !maybe I'll get around to it someday
04306 !PXFSETENV is a POSIX-standard routine for setting environment variables from fortran
04307               call PXFSETENV( 'NETCDF_XFFIOSPEC', 0, trim(iospec), 0, 1, error )
04308           end if
04309 #endif
04310       end if
04311 
04312 !open the file as specified above for various formats
04313       if( form_flag.EQ.MPP_NETCDF )then
04314 #ifdef use_netCDF
04315           if( action_flag.EQ.MPP_WRONLY )then
04316               error = NFMPI_CREATE( mpp_comm, trim(mpp_file(unit)%name), NF_NOCLOBBER, MPI_INFO_NULL, mpp_file(unit)%ncid )
04317               call netcdf_err(error)
04318               if( verbose ) write (stdout(), '(a,i3,i16)') 'MPP_OPEN: new netCDF file: pe, ncid=', pe, mpp_file(unit)%ncid
04319           else if( action_flag.EQ.MPP_OVERWR )then
04320               error = NFMPI_CREATE( mpp_comm, trim(mpp_file(unit)%name), NF_CLOBBER, MPI_INFO_NULL, mpp_file(unit)%ncid )
04321               call netcdf_err(error)
04322               action_flag = MPP_WRONLY !after setting clobber, there is no further distinction btwn MPP_WRONLY and MPP_OVERWR
04323               if( verbose ) write (stdout(), '(a,i3,i16)') 'MPP_OPEN: overwrite netCDF file: pe, ncid=', pe, mpp_file(unit)%ncid
04324           else if( action_flag.EQ.MPP_APPEND )then
04325               error = NFMPI_OPEN( mpp_comm, trim(mpp_file(unit)%name), NF_WRITE, MPI_INFO_NULL, mpp_file(unit)%ncid )
04326               call netcdf_err(error)
04327 !get the current time level of the file: writes to this file will be at next time level
04328               error = NFMPI_INQ_UNLIMDIM( mpp_file(unit)%ncid, unlim%did )
04329               if( error.EQ.NF_NOERR )then
04330                   error = NFMPI_INQ_DIM( mpp_file(unit)%ncid, unlim%did, unlim%name, idim )
04331                   mpp_file(unit)%time_level = idim 
04332                   call netcdf_err(error)
04333                   error = NFMPI_INQ_VARID( mpp_file(unit)%ncid, unlim%name, mpp_file(unit)%id ); call netcdf_err(error)
04334               end if
04335               if( verbose ) write (stdout(), '(a,i3,i16,i4)') 'MPP_OPEN: append to existing netCDF file: pe, ncid, time_axis_id=',&
04336                    pe, mpp_file(unit)%ncid, mpp_file(unit)%id
04337       else if( action_flag.EQ.MPP_RDONLY )then
04338           error = NFMPI_OPEN( mpp_comm, trim(mpp_file(unit)%name), NF_NOWRITE, MPI_INFO_NULL, mpp_file(unit)%ncid )
04339               call netcdf_err(error)
04340           if( verbose ) write (stdout(), '(a,i3,i16,i4)') 'MPP_OPEN: opening existing netCDF file: pe, ncid, time_axis_id=',&
04341                    pe, mpp_file(unit)%ncid, mpp_file(unit)%id
04342               mpp_file(unit)%format=form_flag ! need this for mpp_read
04343               call mpp_read_meta(unit)
04344           end if
04345           mpp_file(unit)%opened = .TRUE.
04346 #endif
04347       else
04348 !format: ascii, native, or IEEE 32 bit
04349           if( form_flag.EQ.MPP_ASCII )then
04350               for = 'FORMATTED'
04351           else if( form_flag.EQ.MPP_IEEE32 )then
04352               for = 'UNFORMATTED'
04353 !assign -N is currently unsupported on SGI
04354 #ifdef _CRAY
04355               call ASSIGN( 'assign -I -N ieee_32 f:'//trim(mpp_file(unit)%name), error )
04356 #endif
04357           else if( form_flag.EQ.MPP_NATIVE )then
04358               for = 'UNFORMATTED'
04359           else
04360               call mpp_error( FATAL, 'MPP_OPEN: form must be one of MPP_ASCII, MPP_NATIVE, MPP_IEEE32 or MPP_NETCDF.' )
04361           end if
04362           inquire( file=trim(mpp_file(unit)%name), EXIST=exists )
04363           if( exists .AND. action_flag.EQ.MPP_WRONLY ) &
04364                call mpp_error( WARNING, 'MPP_OPEN: File '//trim(mpp_file(unit)%name)//' opened WRONLY already exists!' )
04365           if( action_flag.EQ.MPP_OVERWR )action_flag = MPP_WRONLY
04366 !perform the OPEN here
04367           if( PRESENT(recl) )then
04368               if( verbose ) write (stdout(), '(2(1x,a,i3),5(1x,a),a,i8)') 'MPP_OPEN: PE=', pe, &
04369                    'unit=', unit, trim(mpp_file(unit)%name), 'attributes=', trim(acc), trim(for), trim(act), ' RECL=', recl
04370               open( unit, file=trim(mpp_file(unit)%name), access=acc, form=for, action=act, recl=recl )
04371           else
04372               if( verbose ) write (stdout(), '(2(1x,a,i3),6(1x,a))')      'MPP_OPEN: PE=', pe, &
04373                    'unit=', unit, trim(mpp_file(unit)%name), 'attributes=', trim(acc), trim(for), trim(pos), trim(act)
04374               open( unit, file=trim(mpp_file(unit)%name), access=acc, form=for, action=act, position=pos )
04375           end if
04376 !check if OPEN worked
04377           inquire( unit,OPENED=mpp_file(unit)%opened )
04378           if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_OPEN: error in OPEN() statement.' )
04379       end if
04380       mpp_file(unit)%action = action_flag
04381       mpp_file(unit)%format = form_flag
04382       mpp_file(unit)%access = access_flag
04383       mpp_file(unit)%threading = threading_flag
04384       mpp_file(unit)%fileset = fileset_flag
04385       if( PRESENT(nohdrs) )mpp_file(unit)%nohdrs = nohdrs
04386 
04387       if( action_flag.EQ.MPP_WRONLY )then
04388           if( form_flag.NE.MPP_NETCDF .AND. access_flag.EQ.MPP_DIRECT )call mpp_write_meta( unit, 'record_length', ival=recl )
04389 !actual file name
04390           call mpp_write_meta( unit, 'filename', cval=mpp_file(unit)%name )
04391 !MPP_IO package version
04392           call mpp_write_meta( unit, 'MPP_IO_VERSION', cval=trim(version) )
04393 !filecount for multifileset
04394           if( threading_flag.EQ.MPP_MULTI .AND. fileset_flag.EQ.MPP_MULTI ) &
04395                call mpp_write_meta( unit, 'NumFilesInSet', ival=npes )
04396       end if
04397 
04398       return
04399     end subroutine mpp_open
04400 
04401     subroutine mpp_close( unit, action )
04402       integer, intent(in) :: unit
04403       integer, intent(in), optional :: action
04404       character(len=8) :: status
04405       logical :: collect
04406 
04407       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_CLOSE: must first call mpp_io_init.' )
04408       if( unit.EQ.NULLUNIT )return !nothing was actually opened on this unit
04409 
04410 !action on close
04411       status = 'KEEP'
04412 !collect is supposed to launch the post-processing collector tool for multi-fileset
04413       collect = .FALSE.
04414       if( PRESENT(action) )then
04415           if( action.EQ.MPP_DELETE )then
04416               status = 'DELETE'
04417           else if( action.EQ.MPP_COLLECT )then
04418               collect = .FALSE.         !should be TRUE but this is not yet ready
04419               call mpp_error( WARNING, 'MPP_CLOSE: the COLLECT operation is not yet implemented.' )
04420           else
04421               call mpp_error( FATAL, 'MPP_CLOSE: action must be one of MPP_DELETE or MPP_COLLECT.' )
04422           end if
04423       end if
04424       if( mpp_file(unit)%fileset.NE.MPP_MULTI )collect = .FALSE.
04425       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
04426 #ifdef use_netCDF
04427           error = NFMPI_CLOSE(mpp_file(unit)%ncid); call netcdf_err(error)
04428 #endif
04429       else
04430           close(unit,status=status)
04431       end if
04432 #ifdef SGICRAY
04433 !this line deleted: since the FILENV is a shared file, this might cause a problem in
04434 ! multi-threaded I/O if one PE does assign -R before another one has opened it.
04435 !      call ASSIGN( 'assign -R f:'//trim(mpp_file(unit)%name), error )
04436 #endif
04437       mpp_file(unit)%name = ' '
04438       mpp_file(unit)%action    = -1
04439       mpp_file(unit)%format    = -1
04440       mpp_file(unit)%access    = -1
04441       mpp_file(unit)%threading = -1
04442       mpp_file(unit)%fileset   = -1
04443       mpp_file(unit)%record    = -1
04444       mpp_file(unit)%ncid      = -1
04445       mpp_file(unit)%opened = .FALSE.
04446       mpp_file(unit)%initialized = .FALSE.
04447       mpp_file(unit)%id = -1
04448       mpp_file(unit)%time_level = 0
04449       mpp_file(unit)%time = NULLTIME
04450       return
04451     end subroutine mpp_close
04452 
04453 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
04454 !                                                                            !
04455 !                             MPP_WRITE_META                                 !
04456 !                                                                            !
04457 !This series of routines is used to describe the contents of the file        !
04458 !being written on <unit>. Each file can contain any number of fields,        !
04459 !which can be functions of 0-3 spatial axes and 0-1 time axes. Axis          !
04460 !descriptors are stored in the <axistype> structure and field                !
04461 !descriptors in the <fieldtype> structure.                                   !
04462 !                                                                            !
04463 !  type, public :: axistype                                                  !
04464 !     sequence                                                               !
04465 !     character(len=128) :: name                                             !
04466 !     character(len=128) :: units                                            !
04467 !     character(len=256) :: longname                                         !
04468 !     integer :: sense           !+/-1, depth or height?                     !
04469 !     type(domain1D) :: domain                                               !
04470 !     real, pointer :: data(:) !axis values (not used if time axis)          !
04471 !     integer :: id                                                          !
04472 !  end type axistype                                                         !
04473 !                                                                            !
04474 !  type, public :: fieldtype                                                 !
04475 !     sequence                                                               !
04476 !     character(len=128) :: name                                             !
04477 !     character(len=128) :: units                                            !
04478 !     character(len=256) :: longname                                         !
04479 !     real :: min, max, missing, fill, scale, add                            !
04480 !     type(axistype), pointer :: axis(:)                                     !
04481 !     integer :: id                                                          !
04482 !  end type fieldtype                                                        !
04483 !                                                                            !
04484 !The metadata contained in the type is always written for each axis and      !
04485 !field. Any other metadata one wishes to attach to an axis or field          !
04486 !can subsequently be passed to mpp_write_meta using the ID, as shown below.  !
04487 !                                                                            !
04488 !mpp_write_meta can take several forms:                                      !
04489 !                                                                            !
04490 !  mpp_write_meta( unit, name, rval=rval, pack=pack )                        !
04491 !  mpp_write_meta( unit, name, ival=ival )                                   !
04492 !  mpp_write_meta( unit, name, cval=cval )                                   !
04493 !      integer, intent(in) :: unit                                           !
04494 !      character(len=*), intent(in) :: name                                  !
04495 !      real, intent(in), optional :: rval(:)                                 !
04496 !      integer, intent(in), optional :: ival(:)                              !
04497 !      character(len=*), intent(in), optional :: cval                        !
04498 !                                                                            !
04499 !    This form defines global metadata associated with the file as a         !
04500 !    whole. The attribute is named <name> and can take on a real, integer    !
04501 !    or character value. <rval> and <ival> can be scalar or 1D arrays.       !
04502 !                                                                            !
04503 !  mpp_write_meta( unit, id, name, rval=rval, pack=pack )                    !
04504 !  mpp_write_meta( unit, id, name, ival=ival )                               !
04505 !  mpp_write_meta( unit, id, name, cval=cval )                               !
04506 !      integer, intent(in) :: unit, id                                       !
04507 !      character(len=*), intent(in) :: name                                  !
04508 !      real, intent(in), optional :: rval(:)                                 !
04509 !      integer, intent(in), optional :: ival(:)                              !
04510 !      character(len=*), intent(in), optional :: cval                        !
04511 !                                                                            !
04512 !    This form defines metadata associated with a previously defined         !
04513 !    axis or field, identified to mpp_write_meta by its unique ID <id>.      !
04514 !    The attribute is named <name> and can take on a real, integer           !
04515 !    or character value. <rval> and <ival> can be scalar or 1D arrays.       !
04516 !    This need not be called for attributes already contained in             !
04517 !    the type.                                                               !
04518 !                                                                            !
04519 !    PACK can take values 1,2,4,8. This only has meaning when writing        !
04520 !    floating point numbers. The value of PACK defines the number of words   !
04521 !    written into 8 bytes. For pack=4 and pack=8, an integer value is        !
04522 !    written: rval is assumed to have been scaled to the appropriate dynamic !
04523 !    range.                                                                  !
04524 !    PACK currently only works for netCDF files, and is ignored otherwise.   !
04525 !                                                                            !
04526 !   subroutine mpp_write_meta_axis( unit, axis, name, units, longname, &     !
04527 !        cartesian, sense, domain, data )                                    !
04528 !     integer, intent(in) :: unit                                            !
04529 !     type(axistype), intent(inout) :: axis                                  !
04530 !     character(len=*), intent(in) :: name, units, longname                  !
04531 !     character(len=*), intent(in), optional :: cartesian                    !
04532 !     integer, intent(in), optional :: sense                                 !
04533 !     type(domain1D), intent(in), optional :: domain                         !
04534 !     real, intent(in), optional :: data(:)                                  !
04535 !                                                                            !
04536 !    This form defines a time or space axis. Metadata corresponding to the   !
04537 !    type above are written to the file on <unit>. A unique ID for subsequent!
04538 !    references to this axis is returned in axis%id. If the <domain>         !
04539 !    element is present, this is recognized as a distributed data axis       !
04540 !    and domain decomposition information is also written if required (the   !
04541 !    domain decomposition info is required for multi-fileset multi-threaded  !
04542 !    I/O). If the <data> element is allocated, it is considered to be a space!
04543 !    axis, otherwise it is a time axis with an unlimited dimension. Only one !
04544 !    time axis is allowed per file.                                          !
04545 !                                                                            !
04546 !   subroutine mpp_write_meta_field( unit, field, axes, name, units, longname!
04547 !        min, max, missing, fill, scale, add, pack )                         !
04548 !     integer, intent(in) :: unit                                            !
04549 !     type(fieldtype), intent(out) :: field                                  !
04550 !     type(axistype), intent(in) :: axes(:)                                  !
04551 !     character(len=*), intent(in) :: name, units, longname                  !
04552 !     real, intent(in), optional :: min, max, missing, fill, scale, add      !
04553 !     integer, intent(in), optional :: pack                                  !
04554 !                                                                            !
04555 !    This form defines a field. Metadata corresponding to the type           !
04556 !    above are written to the file on <unit>. A unique ID for subsequent     !
04557 !    references to this field is returned in field%id. At least one axis     !
04558 !    must be associated, 0D variables are not considered. mpp_write_meta     !
04559 !    must previously have been called on all axes associated with this       !
04560 !    field.                                                                  !
04561 !                                                                            !
04562 ! The mpp_write_meta package also includes subroutines write_attribute and   !
04563 ! write_attribute_netcdf, that are private to this module.                   !
04564 !                                                                            !
04565 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
04566     subroutine mpp_write_meta_global( unit, name, rval, ival, cval, pack )
04567 !writes a global metadata attribute to unit <unit>
04568 !attribute <name> can be an real, integer or character
04569 !one and only one of rval, ival, and cval should be present
04570 !the first found will be used
04571 !for a non-netCDF file, it is encoded into a string "GLOBAL <name> <val>"
04572       integer, intent(in) :: unit
04573       character(len=*), intent(in) :: name
04574       real,             intent(in), optional :: rval(:)
04575       integer,          intent(in), optional :: ival(:)
04576       character(len=*), intent(in), optional :: cval
04577       integer, intent(in), optional :: pack
04578 
04579       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
04580       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
04581       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
04582       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
04583       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
04584       if( mpp_file(unit)%initialized ) &
04585            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
04586 
04587       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
04588 #ifdef use_netCDF
04589           call write_attribute_netcdf( unit, NF_GLOBAL, name, rval, ival, cval, pack )
04590 #endif
04591       else
04592           call write_attribute( unit, 'GLOBAL '//trim(name), rval, ival, cval, pack )
04593       end if
04594 
04595       return
04596     end subroutine mpp_write_meta_global
04597 
04598 !versions of above to support <rval> and <ival> as scalars (because of f90 strict rank matching)
04599     subroutine mpp_write_meta_global_scalar_r( unit, name, rval, pack )
04600       integer, intent(in) :: unit
04601       character(len=*), intent(in) :: name
04602       real, intent(in) :: rval
04603       integer, intent(in), optional :: pack
04604 
04605       call mpp_write_meta_global( unit, name, rval=(/rval/), pack=pack )
04606       return
04607     end subroutine mpp_write_meta_global_scalar_r
04608 
04609     subroutine mpp_write_meta_global_scalar_i( unit, name, ival )
04610       integer, intent(in) :: unit
04611       character(len=*), intent(in) :: name
04612       integer, intent(in) :: ival
04613 
04614       call mpp_write_meta_global( unit, name, ival=(/ival/) )
04615       return
04616     end subroutine mpp_write_meta_global_scalar_i
04617 
04618     subroutine mpp_write_meta_var( unit, id, name, rval, ival, cval, pack )
04619 !writes a metadata attribute for variable <id> to unit <unit>
04620 !attribute <name> can be an real, integer or character
04621 !one and only one of rval, ival, and cval should be present
04622 !the first found will be used
04623 !for a non-netCDF file, it is encoded into a string "<id> <name> <val>"
04624       integer, intent(in) :: unit, id
04625       character(len=*), intent(in) :: name
04626       real,             intent(in), optional :: rval(:)
04627       integer,          intent(in), optional :: ival(:)
04628       character(len=*), intent(in), optional :: cval
04629       integer, intent(in), optional :: pack
04630 
04631       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
04632       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
04633       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
04634       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
04635       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
04636       if( mpp_file(unit)%initialized ) &
04637            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
04638 
04639       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
04640           call write_attribute_netcdf( unit, id, name, rval, ival, cval, pack )
04641       else
04642           write( text, '(a,i4,a)' )'VARIABLE ', id, ' '//name
04643           call write_attribute( unit, trim(text), rval, ival, cval, pack )
04644       end if
04645 
04646       return
04647     end subroutine mpp_write_meta_var
04648 
04649 !versions of above to support <rval> and <ival> as scalar (because of f90 strict rank matching)
04650     subroutine mpp_write_meta_scalar_r( unit, id, name, rval, pack )
04651       integer, intent(in) :: unit, id
04652       character(len=*), intent(in) :: name
04653       real, intent(in) :: rval
04654       integer, intent(in), optional :: pack
04655 
04656       call mpp_write_meta( unit, id, name, rval=(/rval/), pack=pack )
04657       return
04658     end subroutine mpp_write_meta_scalar_r
04659 
04660     subroutine mpp_write_meta_scalar_i( unit, id, name, ival )
04661       integer, intent(in) :: unit, id
04662       character(len=*), intent(in) :: name
04663       integer, intent(in) :: ival
04664 
04665       call mpp_write_meta( unit, id, name, ival=(/ival/) )
04666       return
04667     end subroutine mpp_write_meta_scalar_i
04668 
04669     subroutine mpp_write_meta_axis( unit, axis, name, units, longname, cartesian, sense, domain, data, cdata) !RV,bundles
04670 !load the values in an axistype (still need to call mpp_write)
04671 !write metadata attributes for axis
04672 !it is declared intent(inout) so you can nullify pointers in the incoming object if needed
04673 !the f90 standard doesn't guarantee that intent(out) on a type guarantees that its pointer components will be unassociated
04674       integer, intent(in) :: unit
04675       type(axistype), intent(inout) :: axis
04676       character(len=*), intent(in) :: name, units, longname
04677       character(len=*), intent(in), optional :: cartesian
04678       integer, intent(in), optional :: sense
04679       type(domain1D), intent(in), optional :: domain
04680       real, intent(in), optional :: data(:)
04681       character(len=*), intent(in), optional :: cdata(:) !RV,bundles
04682       integer :: is, ie, isg, ieg
04683 
04684       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
04685       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
04686       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
04687       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
04688       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
04689       if( mpp_file(unit)%initialized ) &
04690            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
04691 
04692 !pre-existing pointers need to be nullified
04693       if( ASSOCIATED(axis%data) )NULLIFY(axis%data)
04694       if( ASSOCIATED(axis%cdata) )NULLIFY(axis%cdata) !RV,bundles
04695 !load axistype
04696       axis%name     = name
04697       axis%units    = units
04698       axis%longname = longname
04699       if( PRESENT(cartesian) )axis%cartesian = cartesian
04700       if( PRESENT(sense)     )axis%sense     = sense
04701       if( PRESENT(domain)    )then
04702           axis%domain = domain
04703           call mpp_get_global_domain( domain, isg, ieg )
04704           call mpp_get_compute_domain( domain, is, ie )
04705       else
04706           axis%domain = NULL_DOMAIN1D
04707           if( PRESENT(data) )then
04708              isg=1; ieg=size(data); is=isg; ie=ieg
04709           endif
04710           if( PRESENT(cdata) )then !!RV,bundles
04711              isg=1; ieg=size(cdata); is=isg; ie=ieg !!RV,bundles
04712           endif !!RV,bundles
04713       end if
04714       if( PRESENT(data) )then
04715           if( PRESENT(domain) )then
04716               if( size(data).NE.ieg-isg+1 ) &
04717                    call mpp_error( FATAL, 'MPP_WRITE_META_AXIS: size(data).NE.domain%global%size.' )
04718               allocate( axis%data(isg:ieg) )
04719           else
04720               allocate( axis%data(size(data)) )
04721           end if
04722           axis%data = data
04723       end if
04724       if( PRESENT(cdata) )then !RV,bundles
04725           if( PRESENT(domain) )then !RV,bundles
04726               if( size(cdata).NE.ieg-isg+1 ) & !RV,bundles
04727                    call mpp_error( FATAL, 'MPP_WRITE_META_AXIS: size(cdata).NE.domain%global%size.' ) !RV,bundles
04728               allocate( axis%cdata(isg:ieg) ) !RV,bundles
04729               allocate( axis%data(isg:ieg) ) !RV,bundles
04730           else !RV,bundles
04731               allocate( axis%cdata(size(cdata)) ) !RV,bundles
04732               allocate( axis%data(size(cdata)) ) !RV,bundles
04733           end if !RV,bundles
04734           axis%cdata = cdata !RV,bundles
04735       end if !RV,bundles
04736 
04737 
04738 !write metadata
04739       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
04740 #ifdef use_netCDF
04741 !write axis def
04742 !space axes are always floats, time axis is always double
04743           if( ASSOCIATED(axis%data).or. ASSOCIATED(axis%cdata) )then !space axisRV,bundles
04744               if( mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
04745                   idim = ie-is+1
04746                   error = NFMPI_DEF_DIM( mpp_file(unit)%ncid, axis%name, idim, axis%did )
04747               else
04748                   if(  ASSOCIATED(axis%data).and.(.not.present(cdata)))then !!RV,bundles
04749                     idim = size(axis%data)
04750                     error = NFMPI_DEF_DIM( mpp_file(unit)%ncid, axis%name, idim, axis%did )
04751                   else !!RV,bundles
04752                     idim = len(axis%cdata)
04753                     error = NFMPI_DEF_DIM( mpp_file(unit)%ncid, 'MAX_STRLEN', idim, axis%clenid )!!RV,bundles
04754                     call netcdf_err(error)
04755                     idim = size(axis%cdata)
04756                     error = NFMPI_DEF_DIM( mpp_file(unit)%ncid, axis%name, idim, axis%did )!!RV,bundles
04757                   endif !!RV,bundles
04758               end if
04759               call netcdf_err(error)
04760               if(present(cdata)) then !!RV, bundles
04761               error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, axis%name, NF_CHAR, 2,(/axis%clenid, axis%did/), axis%id )
04762               call netcdf_err(error) !!Bundles
04763               else !!Bundles
04764               error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, axis%name, NF_FLOAT, 1, axis%did, axis%id ); call netcdf_err(error) !!Bundles
04765               endif !!Bundles
04766 
04767           else                            !time axis
04768               if( mpp_file(unit)%id.NE.-1 ) &
04769                    call mpp_error( FATAL, 'MPP_WRITE_META_AXIS: There is already a time axis for this file.' )
04770               idim = NF_UNLIMITED
04771               error = NFMPI_DEF_DIM( mpp_file(unit)%ncid, axis%name, idim, axis%did ); call netcdf_err(error)
04772               error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, axis%name, NF_DOUBLE, 1, axis%did, axis%id ); call netcdf_err(error)
04773               mpp_file(unit)%id = axis%id !file ID is the same as time axis varID
04774           end if
04775 #endif
04776       else
04777           varnum = varnum + 1
04778           axis%id = varnum
04779           axis%did = varnum
04780 !write axis def
04781           write( text, '(a,i4,a)' )'AXIS ', axis%id, ' name'
04782           call write_attribute( unit, trim(text), cval=axis%name )
04783           write( text, '(a,i4,a)' )'AXIS ', axis%id, ' size'
04784           if( ASSOCIATED(axis%data) )then !space axis
04785               if( mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
04786                   call write_attribute( unit, trim(text), ival=(/ie-is+1/) )
04787               else
04788                   if(ASSOCIATED(axis%data).and.(.not.present(cdata))) then !!RV,bundles
04789 
04790                   call write_attribute( unit, trim(text), ival=(/size(axis%data)/) )
04791                   else !!RV,bundles
04792                   call write_attribute( unit, trim(text), ival=(/size(axis%cdata)/) ) !!RV, bundles
04793                   endif !!RV, bundles
04794               end if
04795           else                            !time axis
04796               if( mpp_file(unit)%id.NE.-1 ) &
04797                    call mpp_error( FATAL, 'MPP_WRITE_META_AXIS: There is already a time axis for this file.' )
04798               call write_attribute( unit, trim(text), ival=(/0/) ) !a size of 0 indicates time axis
04799               mpp_file(unit)%id = axis%id
04800           end if
04801       end if
04802 
04803 !write axis attributes
04804       call mpp_write_meta( unit, axis%id, 'long_name', cval=axis%longname )
04805       call mpp_write_meta( unit, axis%id, 'units',     cval=axis%units    )
04806       if( PRESENT(cartesian) )call mpp_write_meta( unit, axis%id, 'cartesian_axis', cval=axis%cartesian )
04807       if( PRESENT(sense) )then
04808           if( sense.EQ.-1 )then
04809               call mpp_write_meta( unit, axis%id, 'positive', cval='down' )
04810           else if( sense.EQ.1 )then
04811               call mpp_write_meta( unit, axis%id, 'positive', cval='up' )
04812           end if
04813 !silently ignore values of sense other than +/-1.
04814       end if
04815       if( mpp_file(unit)%threading.EQ.MPP_MULTI .AND. mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
04816           call mpp_write_meta( unit, axis%id, 'domain_decomposition', ival=(/isg,ieg,is,ie/) )
04817       end if
04818       if( verbose ) write (stdout(), '(a,2i3,1x,a,2i3)') &
04819            'MPP_WRITE_META: Wrote axis metadata, pe, unit, axis%name, axis%id, axis%did=', &
04820            pe, unit, trim(axis%name), axis%id, axis%did 
04821 
04822       return
04823     end subroutine mpp_write_meta_axis
04824 
04825     subroutine mpp_write_meta_field( unit, field, axes, name, units, longname, min, max, missing, fill, scale, add, pack )
04826 !define field: must have already called mpp_write_meta(axis) for each axis
04827       integer, intent(in) :: unit
04828       type(fieldtype), intent(out) :: field
04829       type(axistype), intent(in) :: axes(:)
04830       character(len=*), intent(in) :: name, units, longname
04831       real, intent(in), optional :: min, max, missing, fill, scale, add
04832       integer, intent(in), optional :: pack
04833 !this array is required because of f77 binding on netCDF interface
04834       integer, allocatable :: axis_id(:)
04835       real :: a, b
04836       integer :: i
04837 
04838       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
04839       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
04840       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
04841       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
04842       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
04843       if( mpp_file(unit)%initialized ) &
04844            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
04845 
04846 !pre-existing pointers need to be nullified
04847       if( ASSOCIATED(field%axes) )NULLIFY(field%axes)
04848 !fill in field metadata
04849       field%name = name
04850       field%units = units
04851       field%longname = longname
04852       allocate( field%axes(size(axes)) )
04853       field%axes = axes
04854       field%time_axis_index = -1 !this value will never match any axis index
04855 !size is buffer area for the corresponding axis info: it is required to buffer this info in the fieldtype
04856 !because axis might be reused in different files
04857       allocate( field%size(size(axes)) )
04858       do i = 1,size(axes)
04859          if( ASSOCIATED(axes(i)%data) )then !space axis
04860              field%size(i) = size(axes(i)%data)
04861          else               !time
04862              field%size(i) = 1
04863              field%time_axis_index = i
04864          end if
04865       end do
04866 !attributes
04867       if( PRESENT(min) )field%min = min
04868       if( PRESENT(max) )field%max = max
04869       if( PRESENT(missing) )field%missing = missing
04870       if( PRESENT(fill) )field%fill = fill
04871       if( PRESENT(scale) )field%scale = scale
04872       if( PRESENT(add) )field%add = add
04873       
04874 !pack is currently used only for netCDF
04875       field%pack = 2        !default write 32-bit floats
04876       if( PRESENT(pack) )field%pack = pack
04877       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
04878 #ifdef use_netCDF
04879           if (.not. allocated(axis_id)) allocate( axis_id(size(field%axes)) )
04880           do i = 1,size(field%axes)
04881              axis_id(i) = field%axes(i)%did
04882           end do
04883 !write field def
04884           select case (field%pack)
04885               case(1)
04886                   error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_DOUBLE, size(field%axes), axis_id, field%id )
04887               case(2)
04888                   error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_FLOAT,  size(field%axes), axis_id, field%id )
04889               case(4)
04890                   if( .NOT.PRESENT(scale) .OR. .NOT.PRESENT(add) ) &
04891                        call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: scale and add must be supplied when pack=4.' )
04892                   error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_SHORT,  size(field%axes), axis_id, field%id )
04893               case(8)
04894                   if( .NOT.PRESENT(scale) .OR. .NOT.PRESENT(add) ) &
04895                        call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: scale and add must be supplied when pack=8.' )
04896                   error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_BYTE,   size(field%axes), axis_id, field%id )
04897               case default
04898                   call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: only legal packing values are 1,2,4,8.' )
04899           end select
04900           call netcdf_err(error)
04901 #endif
04902       else
04903           varnum = varnum + 1
04904           field%id = varnum
04905           if( PRESENT(pack) )call mpp_error( WARNING, 'MPP_WRITE_META: Packing is currently available only on netCDF files.' )
04906 !write field def
04907           write( text, '(a,i4,a)' )'FIELD ', field%id, ' name'
04908           call write_attribute( unit, trim(text), cval=field%name )
04909           write( text, '(a,i4,a)' )'FIELD ', field%id, ' axes'
04910           call write_attribute( unit, trim(text), ival=field%axes(:)%did )
04911       end if
04912 !write field attributes: these names follow netCDF conventions
04913       call mpp_write_meta( unit, field%id, 'long_name', cval=field%longname )
04914       call mpp_write_meta( unit, field%id, 'units',     cval=field%units    )
04915 !all real attributes must be written as packed
04916       if( PRESENT(min) .AND. PRESENT(max) )then
04917           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
04918               call mpp_write_meta( unit, field%id, 'valid_range', rval=(/min,max/), pack=pack )
04919           else
04920               a = nint((min-add)/scale)
04921               b = nint((max-add)/scale)
04922               call mpp_write_meta( unit, field%id, 'valid_range', rval=(/a,  b  /), pack=pack )
04923           end if
04924       else if( PRESENT(min) )then
04925           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
04926               call mpp_write_meta( unit, field%id, 'valid_min', rval=field%min, pack=pack )
04927           else
04928               a = nint((min-add)/scale)
04929               call mpp_write_meta( unit, field%id, 'valid_min', rval=a, pack=pack )
04930           end if
04931       else if( PRESENT(max) )then
04932           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
04933               call mpp_write_meta( unit, field%id, 'valid_max', rval=field%max, pack=pack )
04934           else
04935               a = nint((max-add)/scale)
04936               call mpp_write_meta( unit, field%id, 'valid_max', rval=a, pack=pack )
04937           end if
04938       end if
04939       if( PRESENT(missing) )then
04940           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
04941               call mpp_write_meta( unit, field%id, 'missing_value', rval=field%missing, pack=pack )
04942           else
04943               a = nint((missing-add)/scale)
04944               call mpp_write_meta( unit, field%id, 'missing_value', rval=a, pack=pack )
04945           end if
04946       end if
04947       if( PRESENT(fill) )then
04948           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
04949               call mpp_write_meta( unit, field%id, '_FillValue', rval=field%fill, pack=pack )
04950           else
04951               a = nint((fill-add)/scale)
04952               call mpp_write_meta( unit, field%id, '_FillValue', rval=a, pack=pack )
04953           end if
04954       end if
04955       if( field%pack.NE.1 .AND. field%pack.NE.2 )then
04956           call mpp_write_meta( unit, field%id, 'packing', ival=field%pack )
04957           if( PRESENT(scale) )call mpp_write_meta( unit, field%id, 'scale_factor',  rval=field%scale )
04958           if( PRESENT(add)   )call mpp_write_meta( unit, field%id, 'add_offset',    rval=field%add   )
04959       end if
04960       if( verbose ) write (stdout(), '(a,2i3,1x,a,i3)') 'MPP_WRITE_META: Wrote field metadata: pe, unit, field%name, field%id=', &
04961            pe, unit, trim(field%name), field%id 
04962 
04963       return
04964     end subroutine mpp_write_meta_field
04965 
04966     subroutine write_attribute( unit, name, rval, ival, cval, pack )
04967 !called to write metadata for non-netCDF I/O
04968       integer, intent(in) :: unit
04969       character(len=*), intent(in) :: name
04970       real, intent(in), optional :: rval(:)
04971       integer, intent(in), optional :: ival(:)
04972       character(len=*), intent(in), optional :: cval
04973 !pack is currently ignored in this routine: only used by netCDF I/O
04974       integer, intent(in), optional :: pack
04975 
04976       if( mpp_file(unit)%nohdrs )return
04977 !encode text string
04978       if( PRESENT(rval) )then
04979           write( text,* )trim(name)//'=', rval
04980       else if( PRESENT(ival) )then
04981           write( text,* )trim(name)//'=', ival
04982       else if( PRESENT(cval) )then
04983           text = ' '//trim(name)//'='//trim(cval)
04984       else
04985           call mpp_error( FATAL, 'WRITE_ATTRIBUTE: one of rval, ival, cval must be present.' )
04986       end if
04987       if( mpp_file(unit)%format.EQ.MPP_ASCII )then
04988 !implies sequential access
04989           write( unit,fmt='(a)' )trim(text)//char(10)
04990       else                      !MPP_IEEE32 or MPP_NATIVE
04991           if( mpp_file(unit)%access.EQ.MPP_SEQUENTIAL )then
04992               write(unit)trim(text)//char(10)
04993           else                  !MPP_DIRECT
04994               write( unit,rec=mpp_file(unit)%record )trim(text)//char(10)
04995               if( verbose ) write (stdout(), '(a,i3,a,i3)') 'WRITE_ATTRIBUTE: PE=', pe, ' wrote record ', mpp_file(unit)%record
04996               mpp_file(unit)%record = mpp_file(unit)%record + 1
04997           end if
04998       end if
04999       return
05000     end subroutine write_attribute
05001 
05002     subroutine write_attribute_netcdf( unit, id, name, rval, ival, cval, pack )
05003 !called to write metadata for netCDF I/O
05004       integer, intent(in) :: unit
05005       integer, intent(in) :: id
05006       character(len=*), intent(in) :: name
05007       real,             intent(in), optional :: rval(:)
05008       integer,          intent(in), optional :: ival(:)
05009       character(len=*), intent(in), optional :: cval
05010       integer, intent(in), optional :: pack
05011       integer :: lenc
05012       integer, allocatable :: rval_i(:)
05013 #ifdef use_netCDF
05014       integer :: ii, il_bytesize, il_iosize
05015       integer :: il_int_iosize, il_rbyt
05016 !
05017       if( PRESENT(rval) )then
05018           il_bytesize = BIT_SIZE(ii)/8
05019           INQUIRE (iolength=il_iosize) ii
05020           il_int_iosize = il_iosize
05021           INQUIRE (iolength=il_iosize) rval(1)
05022           il_rbyt = il_iosize/il_int_iosize*il_bytesize
05023 !pack is only meaningful for FP numbers
05024           if( PRESENT(pack) )then
05025               if( pack.EQ.1 )then
05026                   idim = size(rval)
05027                   if( il_rbyt .EQ. DOUBLE_KIND )then
05028                       error = NFMPI_PUT_ATT_DOUBLE( mpp_file(unit)%ncid, id, name, NF_DOUBLE, idim, rval )
05029                   else if( il_rbyt .EQ. FLOAT_KIND )then
05030                       call mpp_error( WARNING, &
05031                            'WRITE_ATTRIBUTE_NETCDF: attempting to write internal 32-bit real as external 64-bit.' )
05032                       error = NFMPI_PUT_ATT_REAL  ( mpp_file(unit)%ncid, id, name, NF_DOUBLE, idim, rval )
05033                   end if
05034                   call netcdf_err(error)
05035               else if( pack.EQ.2 )then
05036                   idim =  size(rval)
05037                   if( il_rbyt.EQ.DOUBLE_KIND )then
05038                       error = NFMPI_PUT_ATT_DOUBLE( mpp_file(unit)%ncid, id, name, NF_FLOAT, idim, rval )
05039                   else if( il_rbyt.EQ.FLOAT_KIND )then
05040                       error = NFMPI_PUT_ATT_REAL  ( mpp_file(unit)%ncid, id, name, NF_FLOAT, idim, rval )
05041                   end if
05042                   call netcdf_err(error)
05043               else if( pack.EQ.4 )then
05044                   allocate( rval_i(size(rval)) )
05045                   rval_i = rval
05046                   idim = size(rval_i)
05047                   if( il_rbyt.EQ.DOUBLE_KIND )then
05048                       error = NFMPI_PUT_ATT_DOUBLE( mpp_file(unit)%ncid, id, name, NF_SHORT, idim, rval )
05049                   else if( il_rbyt.EQ.FLOAT_KIND )then
05050                       error = NFMPI_PUT_ATT_REAL  ( mpp_file(unit)%ncid, id, name, NF_SHORT, idim, rval )
05051                   end if
05052                   call netcdf_err(error)
05053                   deallocate(rval_i)
05054               else if( pack.EQ.8 )then
05055                   allocate( rval_i(size(rval)) )
05056                   rval_i = rval
05057                   idim = size(rval_i)
05058                   if( il_rbyt.EQ.DOUBLE_KIND )then
05059                       error = NFMPI_PUT_ATT_DOUBLE( mpp_file(unit)%ncid, id, name, NF_BYTE, idim, rval )
05060                   else if( il_rbyt.EQ.FLOAT_KIND )then
05061                       error = NFMPI_PUT_ATT_REAL  ( mpp_file(unit)%ncid, id, name, NF_BYTE, idim, rval )
05062                   end if
05063                   call netcdf_err(error)
05064                   deallocate(rval_i)
05065               else
05066                   call mpp_error( FATAL, 'WRITE_ATTRIBUTE_NETCDF: only legal packing values are 1,2,4,8.' )
05067               end if
05068           else
05069 !default is to write FLOATs (32-bit)
05070               idim = size(rval)
05071               if( il_rbyt.EQ.DOUBLE_KIND )then
05072                   error = NFMPI_PUT_ATT_DOUBLE( mpp_file(unit)%ncid, id, name, NF_FLOAT, idim, rval )
05073               else if( il_rbyt.EQ.FLOAT_KIND )then
05074                   error = NFMPI_PUT_ATT_REAL  ( mpp_file(unit)%ncid, id, name, NF_FLOAT, idim, rval )
05075               end if
05076               call netcdf_err(error)
05077           end if
05078       else if( PRESENT(ival) )then
05079           idim = size(ival)
05080           error = NFMPI_PUT_ATT_INT ( mpp_file(unit)%ncid, id, name, NF_INT, idim, ival ); call netcdf_err(error)
05081       else if( present(cval) )then
05082           idim = len_trim(cval)
05083           error = NFMPI_PUT_ATT_TEXT( mpp_file(unit)%ncid, id, name, idim, cval ); call netcdf_err(error)
05084       else
05085           call mpp_error( FATAL, 'WRITE_ATTRIBUTE_NETCDF: one of rval, ival, cval must be present.' )
05086       end if
05087 #endif /* use_netCDF */ 
05088       return
05089     end subroutine write_attribute_netcdf
05090 
05091 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
05092 !                                                                      !
05093 !                             MPP_WRITE                                !
05094 !                                                                      !
05095 ! mpp_write is used to write data to the file on <unit> using the      !
05096 ! file parameters supplied by mpp_open(). Axis and field definitions   !
05097 ! must have previously been written to the file using mpp_write_meta.  !
05098 !                                                                      !
05099 ! mpp_write can take 2 forms, one for distributed data and one for     !
05100 ! non-distributed data. Distributed data refer to arrays whose two     !
05101 ! fastest-varying indices are domain-decomposed. Distributed data      !
05102 ! must be 2D or 3D (in space). Non-distributed data can be 0-3D.       !
05103 !                                                                      !
05104 ! In all calls to mpp_write, tstamp is an optional argument. It is to  !
05105 ! be omitted if the field was defined not to be a function of time.    !
05106 ! Results are unpredictable if the argument is supplied for a time-    !
05107 ! independent field, or omitted for a time-dependent field. Repeated   !
05108 ! writes of a time-independent field are also not recommended. One     !
05109 ! time level of one field is written per call.                         !
05110 !                                                                      !
05111 !                                                                      !
05112 ! For non-distributed data, use                                        !
05113 !                                                                      !
05114 !  mpp_write( unit, field, data, tstamp )                              !
05115 !     integer, intent(in) :: unit                                      !
05116 !     type(fieldtype), intent(in) :: field                             !
05117 !     real, optional :: tstamp                                         !
05118 !     data is real and can be scalar or of rank 1-3.                   !
05119 !                                                                      !
05120 ! For distributed data, use                                            !
05121 !                                                                      !
05122 !  mpp_write( unit, field, domain, data, tstamp )                      !
05123 !     integer, intent(in) :: unit                                      !
05124 !     type(fieldtype), intent(in) :: field                             !
05125 !     type(domain2D), intent(in) :: domain                             !
05126 !     real, optional :: tstamp                                         !
05127 !     data is real and can be of rank 2 or 3.                          !
05128 !                                                                      !
05129 !  mpp_write( unit, axis )                                             !
05130 !     integer, intent(in) :: unit                                      !
05131 !     type(axistype), intent(in) :: axis                               !
05132 !                                                                      !
05133 ! This call writes the actual co-ordinate values along each space      !
05134 ! axis. It must be called once for each space axis after all other     !
05135 ! metadata has been written.                                           !
05136 !                                                                      !
05137 ! The mpp_write package also includes the routine write_record which   !
05138 ! performs the actual write. This routine is private to this module.   !
05139 !                                                                      !
05140 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
05141 #define MPP_WRITE_2DDECOMP_1D_ mpp_write_2ddecomp_r1d
05142 #define MPP_WRITE_2DDECOMP_2D_ mpp_write_2ddecomp_r2d
05143 #define MPP_WRITE_2DDECOMP_3D_ mpp_write_2ddecomp_r3d
05144 #define MPP_WRITE_2DDECOMP_4D_ mpp_write_2ddecomp_r4d
05145 #define MPP_TYPE_ real
05146 #include <mpp_write_2Ddecomp.h>
05147 
05148 #define MPP_WRITE_ mpp_write_r0D
05149 #define MPP_TYPE_ real
05150 #define MPP_RANK_ !
05151 #define MPP_WRITE_RECORD_ call write_record( unit, field, 1, (/data/), tstamp )
05152 #include <mpp_write.h>
05153 
05154 #define MPP_WRITE_ mpp_write_r1D
05155 #define MPP_TYPE_ real
05156 #define MPP_WRITE_RECORD_ call write_record( unit, field, size(data), data, tstamp )
05157 #define MPP_RANK_ (:)
05158 #include <mpp_write.h>
05159 
05160 #define MPP_WRITE_ mpp_write_r2D
05161 #define MPP_TYPE_ real
05162 #define MPP_WRITE_RECORD_ call write_record( unit, field, size(data), data, tstamp )
05163 #define MPP_RANK_ (:,:)
05164 #include <mpp_write.h>
05165 
05166 #define MPP_WRITE_ mpp_write_r3D
05167 #define MPP_TYPE_ real
05168 #define MPP_WRITE_RECORD_ call write_record( unit, field, size(data), data, tstamp )
05169 #define MPP_RANK_ (:,:,:)
05170 #include <mpp_write.h>
05171 
05172 #define MPP_WRITE_ mpp_write_r4D
05173 #define MPP_TYPE_ real
05174 #define MPP_WRITE_RECORD_ call write_record( unit, field, size(data), data, tstamp )
05175 #define MPP_RANK_ (:,:,:,:)
05176 #include <mpp_write.h>
05177 
05178     subroutine mpp_write_axis( unit, axis )
05179       integer, intent(in) :: unit
05180       type(axistype), intent(in) :: axis
05181       type(fieldtype) :: field
05182       integer :: is, ie
05183 
05184 
05185       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_WRITE: must first call mpp_io_init.' )
05186       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE: invalid unit number.' )
05187       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
05188       if( mpp_file(unit)%fileset  .EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
05189 !we convert axis to type(fieldtype) in order to call write_record
05190       field = default_field
05191       allocate( field%axes(1) )
05192       field%axes(1) = axis
05193       allocate( field%size(1) )
05194       field%id = axis%id
05195       if( mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
05196           call mpp_get_compute_domain( axis%domain, is, ie )
05197           field%size(1) = ie-is+1
05198 !!RV,bundles
05199           if(associated( axis%cdata)) then 
05200           call write_record_c( unit, field, field%size(1), axis%cdata(is:) ) 
05201           else 
05202           call write_record( unit, field, field%size(1), axis%data(is:) )
05203           endif 
05204 !!RV,bundles
05205       else
05206 !!RV,bundles
05207           if(associated( axis%cdata)) then 
05208           field%size(1) = size(axis%cdata)
05209           call write_record_c(unit,field, field%size(1), axis%cdata )
05210           else 
05211           field%size(1) = size(axis%data)
05212           call write_record( unit, field, field%size(1), axis%data )
05213           endif 
05214 !!RV,bundles
05215       end if
05216       return
05217     end subroutine mpp_write_axis
05218 
05219     subroutine write_record_c( unit, field, nwords, cdata, time_in, domain ) !!RV,bundles
05220 !routine that is finally called by all mpp_write routines to perform the write
05221 !a non-netCDF record contains:
05222 !      field ID
05223 !      a set of 4 coordinates (is:ie,js:je) giving the data subdomain
05224 !      a timelevel and a timestamp (=NULLTIME if field is static)
05225 !      3D real data (stored as 1D)
05226 !if you are using direct access I/O, the RECL argument to OPEN must be large enough for the above
05227 !in a global direct access file, record position on PE is given by %record.
05228 
05229 !Treatment of timestamp:
05230 !   We assume that static fields have been passed without a timestamp.
05231 !   Here that is converted into a timestamp of NULLTIME.
05232 !   For non-netCDF fields, field is treated no differently, but is written
05233 !   with a timestamp of NULLTIME. There is no check in the code to prevent
05234 !   the user from repeatedly writing a static field.
05235 
05236       integer, intent(in) :: unit, nwords
05237       type(fieldtype), intent(in) :: field
05238 !RV,bundles
05239       character(len=64), intent(in) :: cdata(nwords)
05240       real(DOUBLE_KIND), intent(in), optional :: time_in
05241       type(domain2D), intent(in), optional :: domain
05242 !RV      integer, dimension(size(field%axes)) :: start, axsiz
05243 
05244       integer,allocatable,dimension(:) :: start, axsiz
05245 !RV
05246       real :: time
05247       integer :: time_level
05248       logical :: newtime
05249       integer :: subdomain(4)
05250       integer :: packed_data(nwords)
05251       integer :: i, is, ie, js, je, isg, ieg, jsg, jeg, isizc, jsizc, isizg, jsizg
05252 #ifdef use_netCDF
05253       integer :: ii, il_bytesize, il_iosize
05254       integer :: il_int_iosize, il_rbyt
05255 #endif
05256 
05257 #ifdef use_CRI_pointers
05258       real(FLOAT_KIND) :: data_r4(nwords)
05259       pointer( ptr1, data_r4)
05260       pointer( ptr2, packed_data)
05261 
05262       if (mpp_io_stack_size < 2*nwords) call mpp_io_set_stack_size(2*nwords)
05263 
05264       ptr1 = LOC(mpp_io_stack(1))
05265       ptr2 = LOC(mpp_io_stack(nwords+1))
05266 #endif
05267 
05268       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_WRITE: must first call mpp_io_init.' )
05269       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE: invalid unit number.' )
05270       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
05271       if( mpp_file(unit)%fileset  .EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
05272 
05273 !RV
05274       allocate(start(size(field%axes)))
05275       allocate(axsiz(size(field%axes)))
05276 !RV
05277       if( .NOT.mpp_file(unit)%initialized )then
05278 !this is the first call to mpp_write
05279 !we now declare the file to be initialized
05280 !if this is netCDF we switch file from DEFINE mode to DATA mode
05281           if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
05282 #ifdef use_netCDF
05283 !NOFILL is probably required for parallel: any circumstances in which not advisable?
05284 !rr not yet supported
05285 !rr           error = NFMPI_SET_FILL( mpp_file(unit)%ncid, NF_NOFILL, i ); call netcdf_err(error)
05286               if( mpp_file(unit)%action.EQ.MPP_WRONLY )error = NFMPI_ENDDEF(mpp_file(unit)%ncid); call netcdf_err(error)
05287 #endif
05288           else
05289               call mpp_write_meta( unit, 'END', cval='metadata' )
05290           end if
05291           mpp_file(unit)%initialized = .TRUE.
05292           if( verbose ) write (stdout(), '(a,i3,a)') 'MPP_WRITE: PE=', pe, ' initialized file '//trim(mpp_file(unit)%name)//'.'
05293       end if
05294 
05295 !initialize time: by default assume NULLTIME
05296       time = NULLTIME
05297       time_level = -1
05298       newtime = .FALSE.
05299       if( PRESENT(time_in) )time = time_in
05300 !increment time level if new time
05301       if( time.GT.mpp_file(unit)%time+EPSILON(time) )then !new time
05302           mpp_file(unit)%time_level = mpp_file(unit)%time_level + 1
05303           mpp_file(unit)%time = time
05304           newtime = .TRUE.
05305       end if
05306       if( verbose ) write (stdout(), '(a,2i3,2i5,es13.5)') 'MPP_WRITE: PE, unit, %id, %time_level, %time=',&
05307            pe, unit, mpp_file(unit)%id, mpp_file(unit)%time_level, mpp_file(unit)%time
05308 
05309       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
05310 !define netCDF data block to be written:
05311 !  time axis: START = time level
05312 !             AXSIZ = 1
05313 !  space axis: if there is no domain info
05314 !              START = 1
05315 !              AXSIZ = field%size(axis)
05316 !          if there IS domain info:
05317 !              start of domain is compute%start_index for multi-file I/O
05318 !                                 global%start_index for all other cases
05319 !              this number must be converted to 1 for NFMPI_PUT_VAR
05320 !                  (netCDF fortran calls are with reference to 1),
05321 !          So, START = compute%start_index - <start of domain> + 1
05322 !              AXSIZ = usually compute%size
05323 !          However, if compute%start_index-compute%end_index+1.NE.compute%size,
05324 !              we assume that the call is passing a subdomain.
05325 !              To pass a subdomain, you must pass a domain2D object that satisfies the following:
05326 !                  global%start_index must contain the <start of domain> as defined above;
05327 !                  the data domain and compute domain must refer to the subdomain being passed.
05328 !              In this case, START = compute%start_index - <start of domain> + 1
05329 !                            AXSIZ = compute%start_index - compute%end_index + 1! NOTE: passing of subdomains will fail for multi-PE single-threaded I/O,
05330 !       since that attempts to gather all data on PE 0.
05331           start = 1
05332           do i = 1,size(field%axes)
05333              axsiz(i) = field%size(i)
05334              if( i.EQ.field%time_axis_index )start(i) = mpp_file(unit)%time_level
05335              start(i) = max(start(i),1)
05336           end do
05337           if( PRESENT(domain) )then
05338               call mpp_get_compute_domain( domain, is,  ie,  js,  je,  xsize=isizc, ysize=jsizc )
05339               call mpp_get_global_domain ( domain, isg, ieg, jsg, jeg, xsize=isizg, ysize=jsizg )
05340               axsiz(1) = isizc
05341               axsiz(2) = jsizc
05342               if( npes.GT.1 .AND. mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
05343                   start(1) = is - isg + 1
05344                   start(2) = js - jsg + 1
05345               else
05346                   if( isizc.NE.ie-is+1 )then
05347                       start(1) = is - isg + 1
05348                       axsiz(1) = ie - is + 1
05349                   end if
05350                   if( jsizc.NE.je-js+1 )then
05351                       start(2) = js - jsg + 1
05352                       axsiz(2) = je - js + 1
05353                   end if
05354               end if
05355           end if
05356           if( debug ) &
05357            write (stdout(), '(a,2i3,12i4)') 'd WRITE_RECORD: PE, unit, start, axsiz=', pe, unit, start, axsiz
05358 #ifdef use_netCDF
05359 !write time information if new time
05360           if( newtime )then
05361               il_bytesize = BIT_SIZE(ii)/8
05362               INQUIRE (iolength=il_iosize) ii
05363               il_int_iosize = il_iosize
05364               INQUIRE (iolength=il_iosize) time
05365               il_rbyt = il_iosize/il_int_iosize*il_bytesize
05366               if( il_rbyt.EQ.DOUBLE_KIND )then
05367                   idim = mpp_file(unit)%time_level
05368                   error = NFMPI_PUT_VAR1_DOUBLE( mpp_file(unit)%ncid, mpp_file(unit)%id, idim, time )
05369               else if( il_rbyt.EQ.FLOAT_KIND )then
05370                   idim = mpp_file(unit)%time_level
05371                   error = NFMPI_PUT_VAR1_REAL  ( mpp_file(unit)%ncid, mpp_file(unit)%id, idim , time )
05372               end if
05373           end if
05374           if( field%pack.LE.2 )then
05375                  write(6,*) ' Iam here 6!'
05376                  call mpp_flush(6)
05377                error = NFMPI_PUT_VARA_TEXT_ALL( mpp_file(unit)%ncid, field%id, (/1,start/), (/len(cdata),axsiz/), cdata ) !!RV, bundles
05378                  write(6,*) ' Iam here 7!'
05379                  call mpp_flush(6)
05380           else  !!RV, bundles
05381                  write(6,*) ' Iam here 8!'
05382                  call mpp_flush(6)
05383             call mpp_error( FATAL, 'MPP_WRITE_RECORD_C: pack on text !' )
05384           end if !!RV, bundles
05385                  write(6,*) ' Iam here 9!',error
05386                  call mpp_flush(6)
05387           call netcdf_err(error)
05388 #endif
05389       else                      !non-netCDF
05390 !subdomain contains (/is,ie,js,je/)
05391           if( PRESENT(domain) )then
05392               subdomain(:) = (/ is, ie, js, je /)
05393           else
05394               subdomain(:) = -1    ! -1 means use global value from axis metadata
05395           end if
05396           if( mpp_file(unit)%format.EQ.MPP_ASCII )then
05397 !implies sequential access
05398               write( unit,* )field%id, subdomain, time_level, time, cdata
05399           else                      !MPP_IEEE32 or MPP_NATIVE
05400               if( mpp_file(unit)%access.EQ.MPP_SEQUENTIAL )then
05401 #ifdef __sgi
05402                   if( mpp_file(unit)%format.EQ.MPP_IEEE32 )then
05403                       write(unit)field%id, subdomain, time_level, time, cdata
05404                   else
05405                       write(unit)field%id, subdomain, time_level, time, cdata
05406                   end if
05407 #else
05408                   write(unit)field%id, subdomain, time_level, time, cdata
05409 #endif
05410               else                  !MPP_DIRECT
05411 #ifdef __sgi
05412                   if( mpp_file(unit)%format.EQ.MPP_IEEE32 )then
05413                       write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, cdata
05414                   else
05415                       write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, cdata
05416                   end if
05417 #else
05418                   write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, cdata
05419 #endif
05420                   if( debug ) write (stdout(), '(a,i3,a,i3)') 'MPP_WRITE: PE=', pe, ' wrote record ', mpp_file(unit)%record
05421               end if
05422           end if
05423       end if
05424 
05425 !recompute current record for direct access I/O
05426       if( mpp_file(unit)%access.EQ.MPP_DIRECT )then
05427           if( mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
05428 !assumes all PEs participate in I/O: modify later
05429               mpp_file(unit)%record = mpp_file(unit)%record + records_per_pe*npes
05430           else
05431               mpp_file(unit)%record = mpp_file(unit)%record + records_per_pe
05432           end if
05433       end if
05434 !RV
05435       deallocate(start)
05436       deallocate(axsiz)
05437 !RV
05438       return
05439     end subroutine write_record_c
05440 
05441     subroutine write_record_b( unit, field, nwords, data, time_in, domain,block_id )
05442 !routine that is finally called by all mpp_write routines to perform the write
05443 !a non-netCDF record contains:
05444 !      field ID
05445 !      a set of 4 coordinates (is:ie,js:je) giving the data subdomain
05446 !      a timelevel and a timestamp (=NULLTIME if field is static)
05447 !      3D real data (stored as 1D)
05448 !if you are using direct access I/O, the RECL argument to OPEN must be large enough for the above
05449 !in a global direct access file, record position on PE is given by %record.
05450 
05451 !Treatment of timestamp:
05452 !   We assume that static fields have been passed without a timestamp.
05453 !   Here that is converted into a timestamp of NULLTIME.
05454 !   For non-netCDF fields, field is treated no differently, but is written
05455 !   with a timestamp of NULLTIME. There is no check in the code to prevent
05456 !   the user from repeatedly writing a static field.
05457 !RV,SGI:
05458 !   The routine write_record_b is a special clone of write_record.
05459 !   The assumption is here that the user has declared a data structure
05460 !   like a(:,:,:,1:no_of_blocks). For whatever reason that arrray is written
05461 !   is not written in a big chunk but on a per block basis for a certain time
05462 !   stamp: At t_i write a(:,:,:,block_id).After all block are written the data structure of the file
05463 !   should look like as if array a was written in one big chunk.
05464 !   Moreover, I assume that the time axis is always the last one and that the block axis
05465 !   comes befor the time axis, means the block axis is the last pseudo spatial axis.
05466 
05467       integer, intent(in) :: unit, nwords
05468       type(fieldtype), intent(in) :: field
05469       real, intent(in) :: data(nwords)
05470       real(DOUBLE_KIND), intent(in), optional :: time_in
05471       integer,intent(in),optional :: block_id
05472       type(domain2D), intent(in), optional :: domain
05473 !RV      integer, dimension(size(field%axes)) :: start, axsiz
05474 
05475       integer,allocatable,dimension(:) :: start, axsiz
05476 !RV
05477       real :: time
05478       integer :: time_level
05479       logical :: newtime
05480       integer :: subdomain(4)
05481       integer :: packed_data(nwords)
05482       integer :: i, is, ie, js, je, isg, ieg, jsg, jeg, isizc, jsizc, isizg, jsizg
05483 #ifdef use_netCDF
05484       integer :: ii, il_bytesize, il_iosize
05485       integer :: il_int_iosize, il_rbyt
05486 #endif
05487 
05488 #ifdef use_CRI_pointers
05489       real(FLOAT_KIND) :: data_r4(nwords)
05490       pointer( ptr1, data_r4)
05491       pointer( ptr2, packed_data)
05492 
05493       if (mpp_io_stack_size < 2*nwords) call mpp_io_set_stack_size(2*nwords)
05494 
05495       ptr1 = LOC(mpp_io_stack(1))
05496       ptr2 = LOC(mpp_io_stack(nwords+1))
05497 #endif
05498 
05499       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_WRITE: must first call mpp_io_init.' )
05500       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE: invalid unit number.' )
05501       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
05502       if( mpp_file(unit)%fileset  .EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
05503 
05504 !RV
05505       allocate(start(size(field%axes)))
05506       allocate(axsiz(size(field%axes)))
05507 !RV
05508       if( .NOT.mpp_file(unit)%initialized )then
05509 !this is the first call to mpp_write
05510 !we now declare the file to be initialized
05511 !if this is netCDF we switch file from DEFINE mode to DATA mode
05512           if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
05513 #ifdef use_netCDF
05514 !NOFILL is probably required for parallel: any circumstances in which not advisable?
05515 !rr not yet supported
05516 !rr           error = NFMPI_SET_FILL( mpp_file(unit)%ncid, NF_NOFILL, i ); call netcdf_err(error)
05517               if( mpp_file(unit)%action.EQ.MPP_WRONLY )error = NFMPI_ENDDEF(mpp_file(unit)%ncid); call netcdf_err(error)
05518 #endif
05519           else
05520               call mpp_write_meta( unit, 'END', cval='metadata' )
05521           end if
05522           mpp_file(unit)%initialized = .TRUE.
05523           if( verbose ) write (stdout(), '(a,i3,a)') 'MPP_WRITE: PE=', pe, ' initialized file '//trim(mpp_file(unit)%name)//'.'
05524       end if
05525 
05526 !initialize time: by default assume NULLTIME
05527       time = NULLTIME
05528       time_level = -1
05529       newtime = .FALSE.
05530       if( PRESENT(time_in) )time = time_in
05531 !increment time level if new time
05532       if( time.GT.mpp_file(unit)%time+EPSILON(time) )then !new time
05533           mpp_file(unit)%time_level = mpp_file(unit)%time_level + 1
05534           mpp_file(unit)%time = time
05535           newtime = .TRUE.
05536       end if
05537       if( verbose ) write (stdout(), '(a,2i3,2i5,es13.5)') 'MPP_WRITE: PE, unit, %id, %time_level, %time=',&
05538            pe, unit, mpp_file(unit)%id, mpp_file(unit)%time_level, mpp_file(unit)%time
05539 
05540       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
05541 !define netCDF data block to be written:
05542 !  time axis: START = time level
05543 !             AXSIZ = 1
05544 !  space axis: if there is no domain info
05545 !              START = 1
05546 !              AXSIZ = field%size(axis)
05547 !          if there IS domain info:
05548 !              start of domain is compute%start_index for multi-file I/O
05549 !                                 global%start_index for all other cases
05550 !              this number must be converted to 1 for NFMPI_PUT_VAR
05551 !                  (netCDF fortran calls are with reference to 1),
05552 !          So, START = compute%start_index - <start of domain> + 1
05553 !              AXSIZ = usually compute%size
05554 !          However, if compute%start_index-compute%end_index+1.NE.compute%size,
05555 !              we assume that the call is passing a subdomain.
05556 !              To pass a subdomain, you must pass a domain2D object that satisfies the following:
05557 !                  global%start_index must contain the <start of domain> as defined above;
05558 !                  the data domain and compute domain must refer to the subdomain being passed.
05559 !              In this case, START = compute%start_index - <start of domain> + 1
05560 !                            AXSIZ = compute%start_index - compute%end_index + 1
05561 ! NOTE: passing of subdomains will fail for multi-PE single-threaded I/O,
05562 !       since that attempts to gather all data on PE 0.
05563           start = 1
05564           do i = 1,size(field%axes)
05565              axsiz(i) = field%size(i)
05566              if( i.EQ.field%time_axis_index )start(i) = mpp_file(unit)%time_level
05567          start(i) = max(start(i),1) 
05568           end do
05569           if( PRESENT(domain) )then
05570               call mpp_get_compute_domain( domain, is,  ie,  js,  je,  xsize=isizc, ysize=jsizc )
05571               call mpp_get_global_domain ( domain, isg, ieg, jsg, jeg, xsize=isizg, ysize=jsizg )
05572               axsiz(1) = isizc
05573               axsiz(2) = jsizc
05574               if( npes.GT.1 .AND. mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
05575                   start(1) = is - isg + 1
05576                   start(2) = js - jsg + 1
05577               else
05578                   if( isizc.NE.ie-is+1 )then
05579                       start(1) = is - isg + 1
05580                       axsiz(1) = ie - is + 1
05581                   end if
05582                   if( jsizc.NE.je-js+1 )then
05583                       start(2) = js - jsg + 1
05584                       axsiz(2) = je - js + 1
05585                   end if
05586               end if
05587           end if
05588 !RV,SGI
05589           if( PRESENT(block_id) )then
05590             if (block_id.le.0) then
05591                call mpp_error( FATAL, 'MPP_RECORD_B: block_id <= 0!' )
05592             endif
05593             if( PRESENT(time_in) )then
05594 
05595                if(block_id.gt. axsiz(size(field%axes)-1)) &
05596                  call mpp_error( FATAL, 'MPP_RECORD_B: block_id > axis range!' )
05597 
05598                start(size(field%axes)-1)=block_id
05599 
05600             else
05601 
05602                if(block_id.gt. axsiz(size(field%axes))) &
05603                  call mpp_error( FATAL, 'MPP_RECORD_B: block_id > axis range!' )
05604 
05605                start(size(field%axes))=block_id
05606 
05607             endif
05608           endif
05609 !RV,SGI
05610           if( debug ) &
05611            write (stdout(), '(a,2i3,12i4)') 'e WRITE_RECORD: PE, unit, start, axsiz=', pe, unit, start, axsiz
05612 #ifdef use_netCDF
05613 !write time information if new time
05614           if( newtime )then
05615               il_bytesize = BIT_SIZE(ii)/8
05616               INQUIRE (iolength=il_iosize) ii
05617               il_int_iosize = il_iosize
05618               INQUIRE (iolength=il_iosize) time
05619               il_rbyt = il_iosize/il_int_iosize*il_bytesize
05620               if( il_rbyt .EQ. DOUBLE_KIND )then
05621                   idim = mpp_file(unit)%time_level
05622                   error = NFMPI_PUT_VAR1_DOUBLE( mpp_file(unit)%ncid, mpp_file(unit)%id, idim, time )
05623               else if( il_rbyt .EQ. FLOAT_KIND )then
05624                   idim = mpp_file(unit)%time_level
05625                   error = NFMPI_PUT_VAR1_REAL  ( mpp_file(unit)%ncid, mpp_file(unit)%id, idim, time )
05626               end if
05627           end if
05628           if( field%pack.LE.2 )then
05629              INQUIRE (iolength=il_iosize) data(1)
05630               il_rbyt = il_iosize/il_int_iosize*il_bytesize
05631               if( il_rbyt.EQ.DOUBLE_KIND )then
05632 !                  write(stderr,*)data
05633                   error = NFMPI_PUT_VARA_DOUBLE_ALL( mpp_file(unit)%ncid, field%id, start, axsiz, data )
05634               else if( il_rbyt.EQ.FLOAT_KIND )then                                           
05635                   error = NFMPI_PUT_VARA_REAL_ALL  ( mpp_file(unit)%ncid, field%id, start, axsiz, data )
05636               end if
05637           else              !convert to integer using scale and add: no error check on packed data representation
05638               packed_data = nint((data-field%add)/field%scale)
05639               error = NFMPI_PUT_VARA_INT_ALL   ( mpp_file(unit)%ncid, field%id, start, axsiz, packed_data )
05640           end if
05641           call netcdf_err(error)
05642 #endif
05643       else                      !non-netCDF
05644 !subdomain contains (/is,ie,js,je/)
05645           if( PRESENT(domain) )then
05646               subdomain(:) = (/ is, ie, js, je /)
05647           else
05648               subdomain(:) = -1    ! -1 means use global value from axis metadata
05649           end if
05650           if( mpp_file(unit)%format.EQ.MPP_ASCII )then
05651 !implies sequential access
05652               write( unit,* )field%id, subdomain, time_level, time, data
05653           else                      !MPP_IEEE32 or MPP_NATIVE
05654               if( mpp_file(unit)%access.EQ.MPP_SEQUENTIAL )then
05655 #ifdef __sgi
05656                   if( mpp_file(unit)%format.EQ.MPP_IEEE32 )then
05657                       data_r4 = data !IEEE conversion layer on SGI until assign -N ieee_32 is supported
05658                       write(unit)field%id, subdomain, time_level, time, data_r4
05659                   else
05660                       write(unit)field%id, subdomain, time_level, time, data
05661                   end if
05662 #else
05663                   write(unit)field%id, subdomain, time_level, time, data
05664 #endif
05665               else                  !MPP_DIRECT
05666 #ifdef __sgi
05667                   if( mpp_file(unit)%format.EQ.MPP_IEEE32 )then
05668                       data_r4 = data !IEEE conversion layer on SGI until assign -N ieee_32 is supported
05669                       write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, data_r4
05670                   else
05671                       write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, data
05672                   end if
05673 #else
05674                   write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, data
05675 #endif
05676                   if( debug ) write (stdout(), '(a,i3,a,i3)') 'MPP_WRITE: PE=', pe, ' wrote record ', mpp_file(unit)%record
05677               end if
05678           end if
05679       end if
05680 
05681 !recompute current record for direct access I/O
05682       if( mpp_file(unit)%access.EQ.MPP_DIRECT )then
05683           if( mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
05684 !assumes all PEs participate in I/O: modify later
05685               mpp_file(unit)%record = mpp_file(unit)%record + records_per_pe*npes
05686           else
05687               mpp_file(unit)%record = mpp_file(unit)%record + records_per_pe
05688           end if
05689       end if
05690 
05691 !RV
05692       deallocate(start)
05693       deallocate(axsiz)
05694 !RV
05695       return
05696     end subroutine write_record_b
05697 
05698     subroutine write_record( unit, field, nwords, data, time_in, domain )
05699 !routine that is finally called by all mpp_write routines to perform the write
05700 !a non-netCDF record contains:
05701 !      field ID
05702 !      a set of 4 coordinates (is:ie,js:je) giving the data subdomain
05703 !      a timelevel and a timestamp (=NULLTIME if field is static)
05704 !      3D real data (stored as 1D)
05705 !if you are using direct access I/O, the RECL argument to OPEN must be large enough for the above
05706 !in a global direct access file, record position on PE is given by %record.
05707 
05708 !Treatment of timestamp:
05709 !   We assume that static fields have been passed without a timestamp.
05710 !   Here that is converted into a timestamp of NULLTIME.
05711 !   For non-netCDF fields, field is treated no differently, but is written
05712 !   with a timestamp of NULLTIME. There is no check in the code to prevent
05713 !   the user from repeatedly writing a static field.
05714 
05715       integer, intent(in) :: unit, nwords
05716       type(fieldtype), intent(in) :: field
05717       real, intent(in) :: data(nwords)
05718       real(DOUBLE_KIND), intent(in), optional :: time_in
05719       type(domain2D), intent(in), optional :: domain
05720 !RV   Very unsafe!!!! One can not use size(field%axes) before it
05721 !RV   is clear that every thing has been initialized.
05722 !RV   The code crashes in a multi-PE run.
05723 !RV      integer, dimension(size(field%axes)) :: start, axsiz
05724 !rr
05725       integer(kind=MPI_OFFSET_KIND),allocatable,dimension(:) :: start, axsiz
05726 !RV
05727       real :: time
05728       integer :: time_level
05729       logical :: newtime
05730       integer :: subdomain(4)
05731       integer :: packed_data(nwords)
05732       integer :: i, is, ie, js, je, isg, ieg, jsg, jeg, isizc, jsizc, isizg, jsizg
05733 !rv,sgi<
05734       integer :: icount_domains
05735 !rv,sgi>
05736 #ifdef use_netCDF
05737       integer :: ii, il_bytesize, il_iosize
05738       integer :: il_int_iosize, il_rbyt
05739 #endif
05740 
05741 #ifdef use_CRI_pointers
05742       real(FLOAT_KIND) :: data_r4(nwords)
05743       pointer( ptr1, data_r4)
05744       pointer( ptr2, packed_data)
05745 
05746       if (mpp_io_stack_size < 2*nwords) call mpp_io_set_stack_size(2*nwords)
05747 
05748       ptr1 = LOC(mpp_io_stack(1))
05749       ptr2 = LOC(mpp_io_stack(nwords+1))
05750 #endif
05751 
05752       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_WRITE: must first call mpp_io_init.' )
05753       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE: invalid unit number.' )
05754       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
05755       if( mpp_file(unit)%fileset  .EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
05756 
05757 !RV
05758       allocate(start(size(field%axes)))
05759       allocate(axsiz(size(field%axes)))
05760 !RV
05761       if( .NOT.mpp_file(unit)%initialized )then
05762 !this is the first call to mpp_write
05763 !we now declare the file to be initialized
05764 !if this is netCDF we switch file from DEFINE mode to DATA mode
05765           if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
05766 #ifdef use_netCDF
05767 !NOFILL is probably required for parallel: any circumstances in which not advisable?
05768 !rr not yet supported
05769 !rr        error = NFMPI_SET_FILL( mpp_file(unit)%ncid, NF_NOFILL, i ); call netcdf_err(error)
05770            if( mpp_file(unit)%action.EQ.MPP_WRONLY )error = NFMPI_ENDDEF(mpp_file(unit)%ncid); call netcdf_err(error)
05771 #endif
05772           else
05773               call mpp_write_meta( unit, 'END', cval='metadata' )
05774           end if
05775           mpp_file(unit)%initialized = .TRUE.
05776           if( verbose ) write (stdout(), '(a,i3,a)') 'MPP_WRITE: PE=', pe, ' initialized file '//trim(mpp_file(unit)%name)//'.'
05777       end if
05778 
05779 !initialize time: by default assume NULLTIME
05780       time = NULLTIME
05781       time_level = -1
05782       newtime = .FALSE.
05783       if( PRESENT(time_in) )time = time_in
05784 !increment time level if new time
05785       if( time.GT.mpp_file(unit)%time+EPSILON(time) )then !new time
05786           mpp_file(unit)%time_level = mpp_file(unit)%time_level + 1
05787           mpp_file(unit)%time = time
05788           newtime = .TRUE.
05789       end if
05790       if( verbose ) write (stdout(), '(a,2i3,2i5,es13.5)') 'MPP_WRITE: PE, unit, %id, %time_level, %time=',&
05791            pe, unit, mpp_file(unit)%id, mpp_file(unit)%time_level, mpp_file(unit)%time
05792 
05793       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
05794 !define netCDF data block to be written:
05795 !  time axis: START = time level
05796 !             AXSIZ = 1
05797 !  space axis: if there is no domain info
05798 !              START = 1
05799 !              AXSIZ = field%size(axis)
05800 !          if there IS domain info:
05801 !              start of domain is compute%start_index for multi-file I/O
05802 !                                 global%start_index for all other cases
05803 !              this number must be converted to 1 for NFMPI_PUT_VAR
05804 !                  (netCDF fortran calls are with reference to 1),
05805 !          So, START = compute%start_index - <start of domain> + 1
05806 !              AXSIZ = usually compute%size
05807 !          However, if compute%start_index-compute%end_index+1.NE.compute%size,
05808 !              we assume that the call is passing a subdomain.
05809 !              To pass a subdomain, you must pass a domain2D object that satisfies the following:
05810 !                  global%start_index must contain the <start of domain> as defined above;
05811 !                  the data domain and compute domain must refer to the subdomain being passed.
05812 !              In this case, START = compute%start_index - <start of domain> + 1
05813 !                            AXSIZ = compute%start_index - compute%end_index + 1
05814 ! NOTE: passing of subdomains will fail for multi-PE single-threaded I/O,
05815 !       since that attempts to gather all data on PE 0.
05816           start = 1
05817 !
05818 !rv,sgi<
05819 !Treatment of the case x(k,i,j) where k is a common, non-decompsoed axis of
05820 !all PEs and i,j are 2D decomposed .
05821 !the array x(k,i,j) is collapsed allong the two first axis. It is treated 2D.
05822 !A corresponding domain is defined as well which is used for stitching.
05823 !However, for writing to a file the decomposition information is taken
05824 !from the field axes rather then from the domain 'domain'.
05825 !If icount_domains is 2 we have exactly that case.
05826           icount_domains=0
05827 !rv,sgi<
05828           do i = 1,size(field%axes)
05829              axsiz(i) = field%size(i)
05830              if( i.EQ.field%time_axis_index )start(i) = mpp_file(unit)%time_level
05831 !rr
05832          start(i) = max(start(i),1)
05833              if ( start(i) < 1 ) start(i) = 1
05834 !rv,sgi<
05835              if((field%axes(i)%domain .ne.  NULL_DOMAIN1D) .and. &
05836                 (field%axes(1)%domain .eq.  NULL_DOMAIN1D)) &
05837                 icount_domains=icount_domains+1
05838 !rv,sgi>
05839           end do
05840           if( PRESENT(domain) )then
05841             if(icount_domains .ne. 2 ) then
05842               call mpp_get_compute_domain( domain, is,  ie,  js,  je &
05843                                          ,  xsize=isizc, ysize=jsizc )
05844               call mpp_get_global_domain ( domain, isg, ieg, jsg, jeg &
05845                                          , xsize=isizg, ysize=jsizg )
05846 #ifdef __PARNETCDF
05847 !rr for longitudes, latitudes, and data
05848               start(1) = is - isg + 1
05849               start(2) = js - jsg + 1
05850 #endif
05851               axsiz(1) = isizc
05852               axsiz(2) = jsizc
05853               if( npes.GT.1 .AND. mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
05854                   start(1) = is - isg + 1
05855                   start(2) = js - jsg + 1
05856               else
05857                   if( isizc.NE.ie-is+1 )then
05858                       start(1) = is - isg + 1
05859                       axsiz(1) = ie - is + 1
05860                   end if
05861                   if( jsizc.NE.je-js+1 )then
05862                       start(2) = js - jsg + 1
05863                       axsiz(2) = je - js + 1
05864                   end if
05865               end if
05866 !rv,sgi<
05867             else
05868 
05869               call mpp_get_compute_domain( field%axes(2)%domain, is,  ie &
05870                                          ,  size=isizc)
05871               call mpp_get_global_domain ( field%axes(2)%domain, isg, ieg &
05872                                          , size=isizg )
05873               call mpp_get_compute_domain( field%axes(3)%domain, js,  je &
05874                                          ,  size=jsizc)
05875               call mpp_get_global_domain ( field%axes(3)%domain, jsg, jeg &
05876                                          , size=jsizg )
05877 
05878 
05879 #ifdef __PARNETCDF
05880 !rr for bounds
05881 
05882               start(2) = is - isg + 1
05883               start(3) = js - jsg + 1
05884 #endif
05885 
05886               axsiz(2) = isizc
05887               axsiz(3) = jsizc
05888               if( npes.GT.1 .AND. mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
05889                   start(2) = is - isg + 1
05890                   start(3) = js - jsg + 1
05891               else
05892                   if( isizc.NE.ie-is+1 )then
05893                       start(2) = is - isg + 1
05894                       axsiz(2) = ie - is + 1
05895                   end if
05896                   if( jsizc.NE.je-js+1 )then
05897                       start(3) = js - jsg + 1
05898                       axsiz(3) = je - js + 1
05899                   end if
05900               end if
05901 
05902             endif
05903 !rv,sgi>
05904           end if
05905           if( debug ) write (stdout(),'(a,3i5,12i4)') &
05906                       'f WRITE_RECORD: PE, unit, icount_domains, start, axsiz=' &
05907                     , pe, unit, icount_domains, start, axsiz
05908 #ifdef use_netCDF
05909 !write time information if new time
05910           il_bytesize = BIT_SIZE(ii)/8
05911           INQUIRE (iolength=il_iosize) ii
05912           il_int_iosize = il_iosize    
05913           if( newtime )then
05914               INQUIRE (iolength=il_iosize) time
05915               il_rbyt = il_iosize/il_int_iosize*il_bytesize
05916               if( il_rbyt .EQ. DOUBLE_KIND )then
05917                   idim = mpp_file(unit)%time_level
05918                   error = NFMPI_PUT_VAR1_DOUBLE( mpp_file(unit)%ncid, mpp_file(unit)%id, idim, time )
05919               else if( il_rbyt .EQ. FLOAT_KIND )then
05920                   idim = mpp_file(unit)%time_level
05921                   error = NFMPI_PUT_VAR1_REAL  ( mpp_file(unit)%ncid, mpp_file(unit)%id, idim, time )
05922               end if
05923           end if
05924           if( field%pack.LE.2 )then
05925               INQUIRE (iolength=il_iosize) data(1)
05926               il_rbyt = il_iosize/il_int_iosize*il_bytesize
05927               if( il_rbyt.EQ.DOUBLE_KIND )then
05928                   error = NFMPI_PUT_VARA_DOUBLE_ALL( mpp_file(unit)%ncid, field%id, start, axsiz, data )
05929               else if( il_rbyt.EQ.FLOAT_KIND )then                                           
05930                   error = NFMPI_PUT_VARA_REAL_ALL  ( mpp_file(unit)%ncid, field%id, start, axsiz, data )
05931               end if
05932           else              !convert to integer using scale and add: no error check on packed data representation
05933               packed_data = nint((data-field%add)/field%scale)
05934               error = NFMPI_PUT_VARA_INT_ALL  ( mpp_file(unit)%ncid, field%id, start, axsiz, packed_data )
05935           end if
05936           call netcdf_err(error)
05937 #endif
05938       else                      !non-netCDF
05939 !subdomain contains (/is,ie,js,je/)
05940           if( PRESENT(domain) )then
05941               subdomain(:) = (/ is, ie, js, je /)
05942           else
05943               subdomain(:) = -1    ! -1 means use global value from axis metadata
05944           end if
05945           if( mpp_file(unit)%format.EQ.MPP_ASCII )then
05946 !implies sequential access
05947               write( unit,* )field%id, subdomain, time_level, time, data
05948           else                      !MPP_IEEE32 or MPP_NATIVE
05949               if( mpp_file(unit)%access.EQ.MPP_SEQUENTIAL )then
05950 #ifdef __sgi
05951                   if( mpp_file(unit)%format.EQ.MPP_IEEE32 )then
05952                       data_r4 = data !IEEE conversion layer on SGI until assign -N ieee_32 is supported
05953                       write(unit)field%id, subdomain, time_level, time, data_r4
05954                   else
05955                       write(unit)field%id, subdomain, time_level, time, data
05956                   end if
05957 #else
05958                   write(unit)field%id, subdomain, time_level, time, data
05959 #endif
05960               else                  !MPP_DIRECT
05961 #ifdef __sgi
05962                   if( mpp_file(unit)%format.EQ.MPP_IEEE32 )then
05963                       data_r4 = data !IEEE conversion layer on SGI until assign -N ieee_32 is supported
05964                       write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, data_r4
05965                   else
05966                       write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, data
05967                   end if
05968 #else
05969                   write( unit, rec=mpp_file(unit)%record )field%id, subdomain, time_level, time, data
05970 #endif
05971                   if( debug ) write (stdout(), '(a,i3,a,i3)') 'MPP_WRITE: PE=', pe, ' wrote record ', mpp_file(unit)%record
05972               end if
05973           end if
05974       end if
05975 
05976 !recompute current record for direct access I/O
05977       if( mpp_file(unit)%access.EQ.MPP_DIRECT )then
05978           if( mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
05979 !assumes all PEs participate in I/O: modify later
05980               mpp_file(unit)%record = mpp_file(unit)%record + records_per_pe*npes
05981           else
05982               mpp_file(unit)%record = mpp_file(unit)%record + records_per_pe
05983           end if
05984       end if
05985 !RV
05986       deallocate(start)
05987       deallocate(axsiz)
05988 !RV
05989 
05990       return
05991     end subroutine write_record
05992 
05993 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
05994 !                                                                      !
05995 !                          MPP_COPY_META                               !
05996 !                                                                      !
05997 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
05998     subroutine mpp_copy_meta_global( unit, gatt )
05999 !writes a global metadata attribute to unit <unit>
06000 !attribute <name> can be an real, integer or character
06001 !one and only one of rval, ival, and cval should be present
06002 !the first found will be used
06003 !for a non-netCDF file, it is encoded into a string "GLOBAL <name> <val>"
06004       integer, intent(in) :: unit
06005       type(atttype), intent(in) :: gatt
06006       integer :: len
06007 
06008       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
06009       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
06010       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
06011       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
06012       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
06013       if( mpp_file(unit)%initialized ) &
06014            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
06015 #ifdef use_netCDF
06016       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
06017          if( gatt%type.EQ.NF_CHAR )then
06018             len = gatt%len
06019             call write_attribute_netcdf( unit, NF_GLOBAL, gatt%name, cval=gatt%catt(1:len) )
06020          else
06021             call write_attribute_netcdf( unit, NF_GLOBAL, gatt%name, rval=gatt%fatt )
06022          endif
06023       else
06024          if( gatt%type.EQ.NF_CHAR )then
06025             len=gatt%len
06026             call write_attribute( unit, 'GLOBAL '//trim(gatt%name), cval=gatt%catt(1:len) )
06027          else
06028             call write_attribute( unit, 'GLOBAL '//trim(gatt%name), rval=gatt%fatt )
06029          endif
06030      end if
06031 #else
06032      call mpp_error( FATAL, 'MPP_READ currently requires use_netCDF option' )
06033 #endif
06034       return
06035     end subroutine mpp_copy_meta_global
06036 
06037     subroutine mpp_copy_meta_axis( unit, axis, domain )
06038 !load the values in an axistype (still need to call mpp_write)
06039 !write metadata attributes for axis.  axis is declared inout 
06040 !because the variable and dimension ids are altered
06041 
06042       integer, intent(in) :: unit
06043       type(axistype), intent(inout) :: axis
06044       type(domain1D), intent(in), optional :: domain
06045       character(len=512) :: text
06046       integer :: i, len, is, ie, isg, ieg
06047 
06048       integer(kind=mpi_offset_kind) :: idim
06049 
06050       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
06051       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
06052       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
06053       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
06054       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
06055       if( mpp_file(unit)%initialized ) &
06056            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
06057 
06058 ! redefine domain if present
06059       if( PRESENT(domain) )then
06060           axis%domain = domain
06061       else
06062           axis%domain = NULL_DOMAIN1D
06063       end if
06064 
06065 #ifdef use_netCDF      
06066 !write metadata
06067       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
06068 
06069 !write axis def
06070           if( ASSOCIATED(axis%data) )then !space axis
06071               if( mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
06072                   call mpp_get_compute_domain( axis%domain, is, ie )
06073                   call mpp_get_global_domain( axis%domain, isg, ieg )
06074                   idim = ie-is+1
06075               else
06076                   idim =  size(axis%data)
06077               end if
06078               error = NFMPI_DEF_DIM( mpp_file(unit)%ncid, axis%name, idim, axis%did )
06079               call netcdf_err(error)
06080               error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, axis%name, NF_FLOAT, 1, axis%did, axis%id ); call netcdf_err(error)
06081           else                            !time axis
06082               idim = NF_UNLIMITED
06083               error = NFMPI_DEF_DIM( mpp_file(unit)%ncid, axis%name, idim, axis%did ); call netcdf_err(error)
06084               error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, axis%name, NF_DOUBLE, 1, axis%did, axis%id ); call netcdf_err(error)
06085               mpp_file(unit)%id = axis%id !file ID is the same as time axis varID
06086               mpp_file(unit)%recdimid = axis%did ! record dimension id
06087           end if
06088       else
06089           varnum = varnum + 1
06090           axis%id = varnum
06091           axis%did = varnum
06092 !write axis def
06093           write( text, '(a,i4,a)' )'AXIS ', axis%id, ' name'
06094           call write_attribute( unit, trim(text), cval=axis%name )
06095           write( text, '(a,i4,a)' )'AXIS ', axis%id, ' size'
06096           if( ASSOCIATED(axis%data) )then !space axis
06097               if( mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
06098                   call write_attribute( unit, trim(text), ival=(/ie-is+1/) )
06099               else
06100                   call write_attribute( unit, trim(text), ival=(/size(axis%data)/) )
06101               end if
06102           else                            !time axis
06103               if( mpp_file(unit)%id.NE.-1 ) &
06104                    call mpp_error( FATAL, 'MPP_WRITE_META_AXIS: There is already a time axis for this file.' )
06105               call write_attribute( unit, trim(text), ival=(/0/) ) !a size of 0 indicates time axis
06106               mpp_file(unit)%id = axis%id
06107           end if
06108       end if
06109 !write axis attributes
06110 
06111       do i=1,axis%natt
06112          if( axis%Att(i)%name.NE.default_att%name )then
06113             if( axis%Att(i)%type.EQ.NF_CHAR )then
06114                len = axis%Att(i)%len
06115                call mpp_write_meta( unit, axis%id, axis%Att(i)%name, cval=axis%Att(i)%catt(1:len) )
06116             else
06117                call mpp_write_meta( unit, axis%id, axis%Att(i)%name, rval=axis%Att(i)%fatt)
06118             endif
06119          endif
06120       enddo
06121 
06122       if( mpp_file(unit)%threading.EQ.MPP_MULTI .AND. mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
06123           call mpp_write_meta( unit, axis%id, 'domain_decomposition', ival=(/isg,ieg,is,ie/) )
06124       end if
06125       if( verbose ) write (stdout(), '(a,2i3,1x,a,2i3)') &
06126            'MPP_WRITE_META: Wrote axis metadata, pe, unit, axis%name, axis%id, axis%did=', &
06127            pe, unit, trim(axis%name), axis%id, axis%did 
06128 #else
06129       call mpp_error( FATAL, 'MPP_READ currently requires use_netCDF option' )
06130 #endif      
06131       return
06132     end subroutine mpp_copy_meta_axis    
06133 
06134     subroutine mpp_copy_meta_field( unit, field, axes )
06135 !useful for copying field metadata from a previous call to mpp_read_meta
06136 !define field: must have already called mpp_write_meta(axis) for each axis
06137       integer, intent(in) :: unit
06138       type(fieldtype), intent(inout) :: field
06139       type(axistype), intent(in), optional :: axes(:)
06140 !this array is required because of f77 binding on netCDF interface
06141       integer, allocatable :: axis_id(:)
06142       real :: a, b
06143       integer :: i
06144 
06145       if( .NOT.module_is_initialized    )call mpp_error( FATAL, 'MPP_WRITE_META: must first call mpp_io_init.' )
06146       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_WRITE_META: invalid unit number.' )
06147       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
06148       if( mpp_file(unit)%fileset.EQ.MPP_SINGLE   .AND. pe.NE.mpp_root_pe() )return
06149       if( mpp_file(unit)%action.NE.MPP_WRONLY )return !no writing metadata on APPEND
06150       if( mpp_file(unit)%initialized ) &
06151            call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
06152 
06153        if( field%pack.NE.1 .AND. field%pack.NE.2 )then
06154             if( field%pack.NE.4 .AND. field%pack.NE.8 ) &
06155                call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: only legal packing values are 1,2,4,8.' )
06156       end if
06157 
06158       if (PRESENT(axes)) then
06159          deallocate(field%axes)
06160          deallocate(field%size)
06161          allocate(field%axes(size(axes)))
06162          allocate(field%size(size(axes)))
06163          field%axes = axes
06164          do i=1,size(axes)
06165             if (ASSOCIATED(axes(i)%data)) then
06166                field%size(i) = size(axes(i)%data)
06167             else
06168                field%size(i) = 1
06169                field%time_axis_index = i
06170             endif
06171          enddo
06172       endif
06173          
06174       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
06175 #ifdef use_netCDF
06176           if (.not. allocated(axis_id)) allocate( axis_id(size(field%axes)) )
06177           do i = 1,size(field%axes)
06178              axis_id(i) = field%axes(i)%did
06179           end do
06180 !write field def
06181           select case (field%pack)
06182               case(1)
06183                   error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_DOUBLE, size(field%axes), axis_id, field%id )
06184               case(2)
06185                   error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_FLOAT,  size(field%axes), axis_id, field%id )
06186               case(4)
06187                   if( field%scale.EQ.default_field%scale .OR. field%add.EQ.default_field%add ) &
06188                        call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: scale and add must be supplied when pack=4.' )
06189                   error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_SHORT,  size(field%axes), axis_id, field%id )
06190               case(8)
06191                   if( field%scale.EQ.default_field%scale .OR. field%add.EQ.default_field%add ) &
06192                        call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: scale and add must be supplied when pack=8.' )
06193                   error = NFMPI_DEF_VAR( mpp_file(unit)%ncid, field%name, NF_BYTE,   size(field%axes), axis_id, field%id )
06194               case default
06195                   call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: only legal packing values are 1,2,4,8.' )
06196           end select
06197 #endif
06198       else
06199           varnum = varnum + 1
06200           field%id = varnum
06201           if( field%pack.NE.default_field%pack ) &
06202            call mpp_error( WARNING, 'MPP_WRITE_META: Packing is currently available only on netCDF files.' )
06203 !write field def
06204           write( text, '(a,i4,a)' )'FIELD ', field%id, ' name'
06205           call write_attribute( unit, trim(text), cval=field%name )
06206           write( text, '(a,i4,a)' )'FIELD ', field%id, ' axes'
06207           call write_attribute( unit, trim(text), ival=field%axes(:)%did )
06208       end if
06209 !write field attributes: these names follow netCDF conventions
06210       call mpp_write_meta( unit, field%id, 'long_name', cval=field%longname )
06211       call mpp_write_meta( unit, field%id, 'units',     cval=field%units    )
06212 !all real attributes must be written as packed
06213       if( (field%min.NE.default_field%min) .AND. (field%max.NE.default_field%max) )then
06214           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
06215               call mpp_write_meta( unit, field%id, 'valid_range', rval=(/field%min,field%max/), pack=field%pack )
06216           else
06217               a = nint((field%min-field%add)/field%scale)
06218               b = nint((field%max-field%add)/field%scale)
06219               call mpp_write_meta( unit, field%id, 'valid_range', rval=(/a,  b  /), pack=field%pack )
06220           end if
06221       else if( field%min.NE.default_field%min )then
06222           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
06223               call mpp_write_meta( unit, field%id, 'valid_min', rval=field%min, pack=field%pack )
06224           else
06225               a = nint((field%min-field%add)/field%scale)
06226               call mpp_write_meta( unit, field%id, 'valid_min', rval=a, pack=field%pack )
06227           end if
06228       else if( field%max.NE.default_field%max )then
06229           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
06230               call mpp_write_meta( unit, field%id, 'valid_max', rval=field%max, pack=field%pack )
06231           else
06232               a = nint((field%max-field%add)/field%scale)
06233               call mpp_write_meta( unit, field%id, 'valid_max', rval=a, pack=field%pack )
06234           end if
06235       end if
06236       if( field%missing.NE.default_field%missing )then
06237           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
06238               call mpp_write_meta( unit, field%id, 'missing_value', rval=field%missing, pack=field%pack )
06239           else
06240               a = nint((field%missing-field%add)/field%scale)
06241               call mpp_write_meta( unit, field%id, 'missing_value', rval=a, pack=field%pack )
06242           end if
06243       end if
06244       if( field%fill.NE.default_field%fill )then
06245           if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
06246               call mpp_write_meta( unit, field%id, '_FillValue', rval=field%fill, pack=field%pack )
06247           else
06248               a = nint((field%fill-field%add)/field%scale)
06249               call mpp_write_meta( unit, field%id, '_FillValue', rval=a, pack=field%pack )
06250           end if
06251       end if
06252       if( field%pack.NE.1 .AND. field%pack.NE.2 )then
06253           call mpp_write_meta( unit, field%id, 'packing', ival=field%pack )
06254           if( field%scale.NE.default_field%scale )call mpp_write_meta( unit, field%id, 'scale_factor',  rval=field%scale )
06255           if( field%add.NE.default_field%add   )call mpp_write_meta( unit, field%id, 'add_offset',    rval=field%add   )
06256       end if
06257       if( verbose ) write (stdout(), '(a,2i3,1x,a,i3)') 'MPP_WRITE_META: Wrote field metadata: pe, unit, field%name, field%id=', &
06258            pe, unit, trim(field%name), field%id 
06259 
06260       return
06261     end subroutine mpp_copy_meta_field
06262     
06263 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
06264 !                                                                      !
06265 !                               MPP_READ                               !
06266 !                                                                      !
06267 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
06268 
06269 #define MPP_READ_2DDECOMP_1D_ mpp_read_2ddecomp_r1d
06270 #define MPP_READ_2DDECOMP_2D_ mpp_read_2ddecomp_r2d
06271 #define MPP_READ_2DDECOMP_3D_ mpp_read_2ddecomp_r3d
06272 #define MPP_READ_2DDECOMP_4D_ mpp_read_2ddecomp_r4d
06273 #define MPP_TYPE_ real
06274 #include <mpp_read_2Ddecomp.h>
06275 
06276     subroutine read_record( unit, field, nwords, data, time_level, domain )
06277 !routine that is finally called by all mpp_read routines to perform the read
06278 !a non-netCDF record contains:
06279 !      field ID
06280 !      a set of 4 coordinates (is:ie,js:je) giving the data subdomain
06281 !      a timelevel and a timestamp (=NULLTIME if field is static)
06282 !      3D real data (stored as 1D)
06283 !if you are using direct access I/O, the RECL argument to OPEN must be large enough for the above
06284 !in a global direct access file, record position on PE is given by %record.
06285 
06286 !Treatment of timestamp:
06287 !   We assume that static fields have been passed without a timestamp.
06288 !   Here that is converted into a timestamp of NULLTIME.
06289 !   For non-netCDF fields, field is treated no differently, but is written
06290 !   with a timestamp of NULLTIME. There is no check in the code to prevent
06291 !   the user from repeatedly writing a static field.
06292 
06293       integer, intent(in) :: unit, nwords
06294       type(fieldtype), intent(in) :: field
06295       real, intent(inout) :: data(nwords)
06296       integer, intent(in), optional  :: time_level
06297       type(domain2D), intent(in), optional :: domain
06298       integer(kind=MPI_OFFSET_KIND), dimension(size(field%axes)) :: start, axsiz
06299       real :: time
06300 
06301       logical :: newtime
06302       integer :: subdomain(4), tlevel
06303 
06304       integer(SHORT_KIND) :: i2vals(nwords)
06305 !#ifdef __sgi      
06306       integer(INT_KIND) :: ivals(nwords)
06307       real(FLOAT_KIND) :: rvals(nwords)
06308 !#else      
06309 !      integer :: ivals(nwords)
06310 !      real :: rvals(nwords)      
06311 !#endif      
06312 
06313       real(DOUBLE_KIND) :: r8vals(nwords)
06314 
06315       integer :: i, error, is, ie, js, je, isg, ieg, jsg, jeg
06316 
06317 #ifdef use_CRI_pointers
06318       pointer( ptr1, i2vals )
06319       pointer( ptr2, ivals )
06320       pointer( ptr3, rvals )
06321       pointer( ptr4, r8vals )
06322 
06323       if (mpp_io_stack_size < 4*nwords) call mpp_io_set_stack_size(4*nwords)
06324 
06325       ptr1 = LOC(mpp_io_stack(1))
06326       ptr2 = LOC(mpp_io_stack(nwords+1))
06327       ptr3 = LOC(mpp_io_stack(2*nwords+1))
06328       ptr4 = LOC(mpp_io_stack(3*nwords+1))
06329 #endif
06330       if (.not.PRESENT(time_level)) then
06331           tlevel = 0
06332       else
06333           tlevel = time_level
06334       endif
06335       
06336 #ifdef use_netCDF
06337       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'READ_RECORD: must first call mpp_io_init.' )
06338       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'READ_RECORD: invalid unit number.' )
06339       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
06340 !RV
06341       if( mpp_file(unit)%fileset.EQ.MPP_MULTI .and. present(domain)) &
06342         call mpp_error( FATAL, 'READ_RECORD: multiple filesets not supported for MPP_READ' )
06343 
06344       if( .NOT.mpp_file(unit)%initialized ) call mpp_error( FATAL, 'MPP_READ: must first call mpp_read_meta.' )
06345 
06346 
06347  
06348       if( verbose ) write (stdout(), '(a,2i3,2i5)') 'MPP_READ: PE, unit, %id, %time_level =',&
06349            pe, unit, mpp_file(unit)%id, tlevel
06350 
06351       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
06352 !define netCDF data block to be read:
06353 !  time axis: START = time level
06354 !             AXSIZ = 1
06355 !  space axis: if there is no domain info
06356 !              START = 1
06357 !              AXSIZ = field%size(axis)
06358 !          if there IS domain info:
06359 !              start of domain is compute%start_index for multi-file I/O
06360 !                                 global%start_index for all other cases
06361 !              this number must be converted to 1 for NFMPI_GET_VAR
06362 !                  (netCDF fortran calls are with reference to 1),
06363 !          So, START = compute%start_index - <start of domain> + 1
06364 !              AXSIZ = usually compute%size
06365 !          However, if compute%start_index-compute%end_index+1.NE.compute%size,
06366 !              we assume that the call is passing a subdomain.
06367 !              To pass a subdomain, you must pass a domain2D object that satisfies the following:
06368 !                  global%start_index must contain the <start of domain> as defined above;
06369 !                  the data domain and compute domain must refer to the subdomain being passed.
06370 !              In this case, START = compute%start_index - <start of domain> + 1
06371 !                            AXSIZ = compute%start_index - compute%end_index + 1
06372 ! NOTE: passing of subdomains will fail for multi-PE single-threaded I/O,
06373 !       since that attempts to gather all data on PE 0.
06374           start = 1
06375           do i = 1,size(field%axes)
06376              axsiz(i) = field%size(i)
06377              if( field%axes(i)%did.EQ.field%time_axis_index )start(i) = tlevel
06378           end do
06379           if( PRESENT(domain) )then
06380               call mpp_get_compute_domain( domain, is,  ie,  js,  je  )
06381               call mpp_get_global_domain ( domain, isg, ieg, jsg, jeg )
06382               axsiz(1) = ie-is+1
06383               axsiz(2) = je-js+1
06384               if( npes.GT.1 .AND. mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
06385                   start(1) = is - isg + 1
06386                   start(2) = js - jsg + 1
06387               else
06388                   if( ie-is+1.NE.ie-is+1 )then
06389                       start(1) = is - isg + 1
06390                       axsiz(1) = ie - is + 1
06391                   end if
06392                   if( je-js+1.NE.je-js+1 )then
06393                       start(2) = js - jsg + 1
06394                       axsiz(2) = je - js + 1
06395                   end if
06396               end if
06397           end if
06398 
06399           if( verbose ) write (stdout(), '(a,2i3,i6,12i4)') 'READ_RECORD: PE, unit, nwords, start, axsiz=', &
06400                            pe, unit, nwords, start, axsiz
06401 
06402           select case (field%type)
06403              case(NF_BYTE)
06404 ! use type conversion 
06405                 call mpp_error( FATAL, 'MPP_READ: does not support NF_BYTE packing' )
06406              case(NF_SHORT)
06407                 error = NFMPI_GET_VARA_INT2_ALL  ( mpp_file(unit)%ncid, field%id, start, axsiz, i2vals ); call netcdf_err(error)
06408                  data(:)=i2vals(:)*field%scale + field%add
06409              case(NF_INT)
06410                 error = NFMPI_GET_VARA_INT_ALL   ( mpp_file(unit)%ncid, field%id, start, axsiz, ivals  ); call netcdf_err(error)
06411                 data(:)=ivals(:)
06412              case(NF_FLOAT)
06413                 error = NFMPI_GET_VARA_REAL_ALL  ( mpp_file(unit)%ncid, field%id, start, axsiz, rvals  ); call netcdf_err(error)
06414                 data(:)=rvals(:)
06415              case(NF_DOUBLE)
06416                 error = NFMPI_GET_VARA_DOUBLE_ALL( mpp_file(unit)%ncid, field%id, start, axsiz, r8vals ); call netcdf_err(error)
06417                 data(:)=r8vals(:)
06418              case default
06419                 call mpp_error( FATAL, 'MPP_READ: invalid pack value' )
06420           end select
06421       else                      !non-netCDF
06422 !subdomain contains (/is,ie,js,je/)
06423           call mpp_error( FATAL, 'Currently dont support non-NetCDF mpp read' )
06424 
06425       end if
06426 #else
06427       call mpp_error( FATAL, 'MPP_READ currently requires use_netCDF option' )
06428 #endif      
06429       return
06430     end subroutine read_record
06431     subroutine read_record_b(unit,field,nwords,data,time_level,domain,block_id)
06432 !routine that is finally called by all mpp_read routines to perform the read
06433 !a non-netCDF record contains:
06434 !      field ID
06435 !      a set of 4 coordinates (is:ie,js:je) giving the data subdomain
06436 !      a timelevel and a timestamp (=NULLTIME if field is static)
06437 !      3D real data (stored as 1D)
06438 !if you are using direct access I/O, the RECL argument to OPEN must be large enough for the above
06439 !in a global direct access file, record position on PE is given by %record.
06440 
06441 !Treatment of timestamp:
06442 !   We assume that static fields have been passed without a timestamp.
06443 !   Here that is converted into a timestamp of NULLTIME.
06444 !   For non-netCDF fields, field is treated no differently, but is written
06445 !   with a timestamp of NULLTIME. There is no check in the code to prevent
06446 !   the user from repeatedly writing a static field.
06447 
06448       integer, intent(in) :: unit, nwords
06449       type(fieldtype), intent(in) :: field
06450       real, intent(inout) :: data(nwords)
06451       integer, intent(in), optional  :: time_level
06452 !RV
06453       integer, intent(in), optional  ::  block_id
06454 !RV
06455       type(domain2D), intent(in), optional :: domain
06456       integer(kind=MPI_OFFSET_KIND), dimension(size(field%axes)) :: start, axsiz
06457       real :: time
06458 
06459       logical :: newtime
06460       integer :: subdomain(4), tlevel
06461 
06462       integer(SHORT_KIND) :: i2vals(nwords)
06463 !#ifdef __sgi      
06464       integer(INT_KIND) :: ivals(nwords)
06465       real(FLOAT_KIND) :: rvals(nwords)
06466 !#else      
06467 !      integer :: ivals(nwords)
06468 !      real :: rvals(nwords)      
06469 !#endif      
06470 
06471       real(DOUBLE_KIND) :: r8vals(nwords)
06472 
06473       integer :: i, error, is, ie, js, je, isg, ieg, jsg, jeg
06474 
06475 #ifdef use_CRI_pointers
06476       pointer( ptr1, i2vals )
06477       pointer( ptr2, ivals )
06478       pointer( ptr3, rvals )
06479       pointer( ptr4, r8vals )
06480 
06481       if (mpp_io_stack_size < 4*nwords) call mpp_io_set_stack_size(4*nwords)
06482 
06483       ptr1 = LOC(mpp_io_stack(1))
06484       ptr2 = LOC(mpp_io_stack(nwords+1))
06485       ptr3 = LOC(mpp_io_stack(2*nwords+1))
06486       ptr4 = LOC(mpp_io_stack(3*nwords+1))
06487 #endif
06488       if (.not.PRESENT(time_level)) then
06489           tlevel = 0
06490       else
06491           tlevel = time_level
06492       endif
06493       
06494 #ifdef use_netCDF
06495       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'READ_RECORD: must first call mpp_io_init.' )
06496       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'READ_RECORD: invalid unit number.' )
06497       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
06498 !RV
06499       if( mpp_file(unit)%fileset.EQ.MPP_MULTI .and. present(domain)) &
06500         call mpp_error( FATAL, 'READ_RECORD: multiple filesets not supported for MPP_READ' )
06501 
06502       if( .NOT.mpp_file(unit)%initialized ) call mpp_error( FATAL, 'MPP_READ: must first call mpp_read_meta.' )
06503 
06504 
06505  
06506       if( verbose ) write (stdout(), '(a,2i3,2i5)') 'MPP_READ: PE, unit, %id, %time_level =',&
06507            pe, unit, mpp_file(unit)%id, tlevel
06508 
06509       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
06510 !define netCDF data block to be read:
06511 !  time axis: START = time level
06512 !             AXSIZ = 1
06513 !  space axis: if there is no domain info
06514 !              START = 1
06515 !              AXSIZ = field%size(axis)
06516 !          if there IS domain info:
06517 !              start of domain is compute%start_index for multi-file I/O
06518 !                                 global%start_index for all other cases
06519 !              this number must be converted to 1 for NFMPI_GET_VAR
06520 !                  (netCDF fortran calls are with reference to 1),
06521 !          So, START = compute%start_index - <start of domain> + 1
06522 !              AXSIZ = usually compute%size
06523 !          However, if compute%start_index-compute%end_index+1.NE.compute%size,
06524 !              we assume that the call is passing a subdomain.
06525 !              To pass a subdomain, you must pass a domain2D object that satisfies the following:
06526 !                  global%start_index must contain the <start of domain> as defined above;
06527 !                  the data domain and compute domain must refer to the subdomain being passed.
06528 !              In this case, START = compute%start_index - <start of domain> + 1
06529 !                            AXSIZ = compute%start_index - compute%end_index + 1
06530 ! NOTE: passing of subdomains will fail for multi-PE single-threaded I/O,
06531 !       since that attempts to gather all data on PE 0.
06532           start = 1
06533           do i = 1,size(field%axes)
06534              axsiz(i) = field%size(i)
06535              if( field%axes(i)%did.EQ.field%time_axis_index )start(i) = tlevel
06536           end do
06537           if( PRESENT(domain) )then
06538               call mpp_get_compute_domain( domain, is,  ie,  js,  je  )
06539               call mpp_get_global_domain ( domain, isg, ieg, jsg, jeg )
06540               axsiz(1) = ie-is+1
06541               axsiz(2) = je-js+1
06542               if( npes.GT.1 .AND. mpp_file(unit)%fileset.EQ.MPP_SINGLE )then
06543                   start(1) = is - isg + 1
06544                   start(2) = js - jsg + 1
06545               else
06546                   if( ie-is+1.NE.ie-is+1 )then
06547                       start(1) = is - isg + 1
06548                       axsiz(1) = ie - is + 1
06549                   end if
06550                   if( je-js+1.NE.je-js+1 )then
06551                       start(2) = js - jsg + 1
06552                       axsiz(2) = je - js + 1
06553                   end if
06554               end if
06555           end if
06556 !RV,SGI
06557           if( PRESENT(block_id) )then
06558             if (block_id.le.0) then
06559                call mpp_error( FATAL, 'READ_RECORD_B: block_id <= 0!' )
06560             endif
06561             if( PRESENT(time_level) )then
06562 
06563                if(block_id.gt. axsiz(size(field%axes)-1)) &
06564                  call mpp_error( FATAL, 'READ_RECORD_B: block_id > axis range!' )
06565                start(size(field%axes)-1)=block_id
06566 
06567             else
06568 
06569                if(block_id.gt. axsiz(size(field%axes))) &
06570                  call mpp_error( FATAL, 'READ_RECORD_B: block_id > axis range!' )
06571                start(size(field%axes))=block_id
06572 
06573             endif
06574           endif
06575 
06576 !RV,SGI
06577 
06578           if( verbose ) write (stdout(), '(a,2i3,i6,12i4)') 'READ_RECORD: PE, unit, nwords, start, axsiz=', &
06579                           pe, unit, nwords, start, axsiz
06580 
06581           select case (field%type)
06582              case(NF_BYTE)
06583 ! use type conversion 
06584                 call mpp_error( FATAL, 'MPP_READ: does not support NF_BYTE packing' )
06585              case(NF_SHORT)
06586                 error = NFMPI_GET_VARA_INT2_ALL  ( mpp_file(unit)%ncid, field%id, start, axsiz, i2vals ); call netcdf_err(error)
06587                  data(:)=i2vals(:)*field%scale + field%add
06588              case(NF_INT)
06589                 error = NFMPI_GET_VARA_INT_ALL   ( mpp_file(unit)%ncid, field%id, start, axsiz, ivals  ); call netcdf_err(error)
06590                 data(:)=ivals(:)
06591              case(NF_FLOAT)
06592                 error = NFMPI_GET_VARA_REAL_ALL  ( mpp_file(unit)%ncid, field%id, start, axsiz, rvals  ); call netcdf_err(error)
06593                 data(:)=rvals(:)
06594              case(NF_DOUBLE)
06595                 error = NFMPI_GET_VARA_DOUBLE_ALL( mpp_file(unit)%ncid, field%id, start, axsiz, r8vals ); call netcdf_err(error)
06596                 data(:)=r8vals(:)
06597              case default
06598                 call mpp_error( FATAL, 'MPP_READ: invalid pack value' )
06599           end select
06600       else                      !non-netCDF
06601 !subdomain contains (/is,ie,js,je/)
06602           call mpp_error( FATAL, 'Currently dont support non-NetCDF mpp read' )
06603 
06604       end if
06605 #else
06606       call mpp_error( FATAL, 'MPP_READ currently requires use_netCDF option' )
06607 #endif      
06608       return
06609     end subroutine read_record_b
06610 
06611     subroutine mpp_read_r4D( unit, field, data, tindex,blockid)
06612       integer, intent(in) :: unit
06613       type(fieldtype), intent(in) :: field
06614       real, intent(inout) :: data(:,:,:,:)
06615       integer, intent(in), optional :: tindex
06616       integer, intent(in), optional :: blockid
06617 
06618       if(present(blockid)) then
06619         call read_record_b(unit,field,size(data),data,tindex,block_id=blockid )
06620       else
06621         call read_record( unit, field, size(data), data, tindex )
06622       endif
06623     end subroutine mpp_read_r4D
06624 
06625     subroutine mpp_read_r3D( unit, field, data, tindex,blockid)
06626       integer, intent(in) :: unit
06627       type(fieldtype), intent(in) :: field
06628       real, intent(inout) :: data(:,:,:)
06629       integer, intent(in), optional :: tindex
06630       integer, intent(in), optional :: blockid
06631 
06632       if(present(blockid)) then
06633         call read_record_b(unit,field,size(data),data,tindex,block_id=blockid )
06634       else
06635         call read_record( unit, field, size(data), data, tindex )
06636       endif
06637     end subroutine mpp_read_r3D
06638 
06639     subroutine mpp_read_r2D( unit, field, data, tindex )
06640       integer, intent(in) :: unit
06641       type(fieldtype), intent(in) :: field
06642       real, intent(inout) :: data(:,:)
06643       integer, intent(in), optional :: tindex
06644 
06645       call read_record( unit, field, size(data), data, tindex )
06646     end subroutine mpp_read_r2D
06647 
06648     subroutine mpp_read_r1D( unit, field, data, tindex )
06649       integer, intent(in) :: unit
06650       type(fieldtype), intent(in) :: field
06651       real, intent(inout) :: data(:)
06652       integer, intent(in), optional :: tindex
06653 
06654       call read_record( unit, field, size(data), data, tindex )
06655     end subroutine mpp_read_r1D
06656 
06657     subroutine mpp_read_r0D( unit, field, data, tindex )
06658       integer, intent(in) :: unit
06659       type(fieldtype), intent(in) :: field
06660       real, intent(inout) :: data
06661       integer, intent(in), optional :: tindex
06662       real, dimension(1) :: data_tmp
06663 
06664       data_tmp(1)=data
06665       call read_record( unit, field, 1, data_tmp, tindex )
06666       data=data_tmp(1)
06667     end subroutine mpp_read_r0D
06668 
06669     subroutine mpp_read_meta(unit)
06670 !
06671 ! read file attributes including dimension and variable attributes
06672 ! and store in filetype structure.  All of the file information
06673 ! with the exception of the (variable) data is stored.  Attributes
06674 ! are supplied to the user by get_info,get_atts,get_axes and get_fields  
06675 !
06676 ! every PE is eligible to call mpp_read_meta
06677 !
06678       integer, parameter :: MAX_DIMVALS = 100000
06679       integer, intent(in) :: unit
06680 
06681       integer         :: ncid,ndim,nvar_total,natt,recdim,nv,nvar,len
06682       integer :: error,i,j 
06683       integer         :: type,nvdims,nvatts, dimid
06684       integer, allocatable, dimension(:) :: dimids
06685       type(axistype) , allocatable, dimension(:) :: Axis
06686       character(len=128) :: name, attname, unlimname, attval
06687       logical :: isdim
06688 
06689       integer(SHORT_KIND) :: i2vals(MAX_DIMVALS)
06690 !#ifdef __sgi
06691       integer(INT_KIND) :: ivals(MAX_DIMVALS)
06692       real(FLOAT_KIND)  :: rvals(MAX_DIMVALS)
06693 !#else      
06694 !      integer :: ivals(MAX_DIMVALS)
06695 !      real    :: rvals(MAX_DIMVALS)      
06696 !#endif      
06697       real(DOUBLE_KIND) :: r8vals(MAX_DIMVALS)
06698 
06699 #ifdef use_netCDF      
06700 
06701       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
06702         ncid = mpp_file(unit)%ncid
06703         error = NFMPI_INQ(ncid,ndim, nvar_total,&
06704                       natt, recdim);call netcdf_err(error)
06705 
06706 
06707         mpp_file(unit)%ndim = ndim
06708         mpp_file(unit)%natt = natt
06709         mpp_file(unit)%recdimid = recdim
06710 !
06711 ! if no recdim exists, recdimid = -1
06712 ! variable id of unlimdim and length
06713 !
06714         if( recdim.NE.-1 )then 
06715            error = NFMPI_INQ_DIM( ncid, recdim, unlimname, idim );call netcdf_err(error)
06716            mpp_file(unit)%time_level = idim
06717            error = NFMPI_INQ_VARID( ncid, unlimname, mpp_file(unit)%id ); call netcdf_err(error)
06718         else
06719            mpp_file(unit)%time_level = -1 ! set to zero so mpp_get_info returns ntime=0 if no time axis present
06720         endif
06721 
06722         if ( natt .gt. 0 ) allocate(mpp_file(unit)%Att(natt))
06723         allocate(Axis(ndim))
06724         allocate(dimids(ndim))
06725         allocate(mpp_file(unit)%Axis(ndim))          
06726 
06727 !
06728 ! initialize fieldtype and axis type
06729 !
06730 
06731 
06732         do i=1,ndim
06733            Axis(i) = default_axis
06734            mpp_file(unit)%Axis(i) = default_axis
06735         enddo
06736 
06737         do i=1,natt
06738            mpp_file(unit)%Att(i) = default_att
06739         enddo
06740         
06741 !
06742 ! assign global attributes
06743 !
06744         do i=1,natt
06745            error=NFMPI_INQ_ATTNAME(ncid,NF_GLOBAL,i,name);call netcdf_err(error)
06746            error=NFMPI_INQ_ATT(ncid,NF_GLOBAL,trim(name),type,idim);call netcdf_err(error)
06747            len = idim
06748            mpp_file(unit)%Att(i)%name = name
06749            mpp_file(unit)%Att(i)%len = len
06750            mpp_file(unit)%Att(i)%type = type
06751 !
06752 !  allocate space for att data and assign 
06753 !
06754            select case (type)
06755 
06756               case (NF_CHAR)
06757                  if (len.gt.512) then
06758             call mpp_error(NOTE,'GLOBAL ATT too long - not reading this metadata') 
06759                     len=7
06760                     mpp_file(unit)%Att(i)%len=len
06761                     mpp_file(unit)%Att(i)%catt = 'unknown'
06762                  else
06763                      error=NFMPI_GET_ATT_TEXT(ncid,NF_GLOBAL,name,mpp_file(unit)%Att(i)%catt);call netcdf_err(error)
06764                      if (verbose.and.pe == 0) write (stdout(),*)  'GLOBAL ATT ',trim(name),' ',mpp_file(unit)%Att(i)%catt(1:len)
06765                  endif
06766 !
06767 ! store integers in float arrays
06768 !
06769               case (NF_SHORT)
06770                  allocate(mpp_file(unit)%Att(i)%fatt(len))
06771                  error=NFMPI_GET_ATT_INT2(ncid,NF_GLOBAL,name,i2vals);call netcdf_err(error)
06772                  if( verbose .and. pe == 0 )write (stdout(),*)  'GLOBAL ATT ',trim(name),' ',i2vals(1:len)
06773                  mpp_file(unit)%Att(i)%fatt(1:len)=i2vals(1:len)
06774               case (NF_INT)
06775                  allocate(mpp_file(unit)%Att(i)%fatt(len))
06776                  error=NFMPI_GET_ATT_INT(ncid,NF_GLOBAL,name,ivals);call netcdf_err(error)
06777                  if( verbose .and. pe == 0 )write (stdout(),*)  'GLOBAL ATT ',trim(name),' ',ivals(1:len)
06778                  mpp_file(unit)%Att(i)%fatt(1:len)=ivals(1:len)
06779               case (NF_FLOAT)
06780                  allocate(mpp_file(unit)%Att(i)%fatt(len))
06781                  error=NFMPI_GET_ATT_REAL(ncid,NF_GLOBAL,name,rvals);call netcdf_err(error)
06782                  mpp_file(unit)%Att(i)%fatt(1:len)=rvals(1:len)
06783                  if( verbose .and. pe == 0)write (stdout(),*)  'GLOBAL ATT ',trim(name),' ',mpp_file(unit)%Att(i)%fatt(1:len)
06784               case (NF_DOUBLE)
06785                  allocate(mpp_file(unit)%Att(i)%fatt(len))
06786                  error=NFMPI_GET_ATT_DOUBLE(ncid,NF_GLOBAL,name,r8vals);call netcdf_err(error)
06787                  mpp_file(unit)%Att(i)%fatt(1:len)=r8vals(1:len)
06788                  if( verbose .and. pe == 0)write (stdout(),*)  'GLOBAL ATT ',trim(name),' ',mpp_file(unit)%Att(i)%fatt(1:len)
06789            end select
06790 
06791         enddo
06792 !
06793 ! assign dimension name and length
06794 !
06795         do i=1,ndim
06796            error = NFMPI_INQ_DIM(ncid,i,name,idim);call netcdf_err(error)
06797            len = idim
06798            Axis(i)%name = name
06799            Axis(i)%len = len
06800         enddo
06801 
06802         nvar=0
06803         do i=1, nvar_total
06804            error=NFMPI_INQ_VAR(ncid,i,name,type,nvdims,dimids,nvatts);call netcdf_err(error)
06805            isdim=.false.
06806            do j=1,ndim
06807               if( trim(lowercase(name)).EQ.trim(lowercase(Axis(j)%name)) )isdim=.true.
06808            enddo
06809            if (.not.isdim) nvar=nvar+1
06810         enddo
06811         mpp_file(unit)%nvar = nvar
06812         allocate(mpp_file(unit)%Var(nvar))
06813 
06814         do i=1,nvar
06815            mpp_file(unit)%Var(i) = default_field
06816         enddo
06817         
06818 !
06819 ! assign dimension info
06820 !
06821         do i=1, nvar_total
06822            error=NFMPI_INQ_VAR(ncid,i,name,type,nvdims,dimids,nvatts);call netcdf_err(error)
06823            isdim=.false.
06824            do j=1,ndim
06825               if( trim(lowercase(name)).EQ.trim(lowercase(Axis(j)%name)) )isdim=.true.
06826            enddo
06827 
06828            if( isdim )then
06829               error=NFMPI_INQ_DIMID(ncid,name,dimid);call netcdf_err(error)
06830               Axis(dimid)%type = type
06831               Axis(dimid)%did = dimid
06832               Axis(dimid)%id = i
06833               Axis(dimid)%natt = nvatts
06834               ! get axis values
06835               if( i.NE.mpp_file(unit)%id )then   ! non-record dims
06836                  select case (type)
06837                  case (NF_INT)
06838                     len=Axis(dimid)%len
06839                     allocate(Axis(dimid)%data(len))
06840                     error = NFMPI_GET_VAR_INT_ALL(ncid,i,ivals);call netcdf_err(error)
06841                     Axis(dimid)%data(1:len)=ivals(1:len)                     
06842                  case (NF_FLOAT)
06843                     len=Axis(dimid)%len
06844                     allocate(Axis(dimid)%data(len))
06845                     error = NFMPI_GET_VAR_REAL_ALL(ncid,i,rvals);call netcdf_err(error)
06846                     Axis(dimid)%data(1:len)=rvals(1:len)
06847                  case (NF_DOUBLE)
06848                     len=Axis(dimid)%len
06849                     allocate(Axis(dimid)%data(len))
06850                     error = NFMPI_GET_VAR_DOUBLE_ALL(ncid,i,r8vals);call netcdf_err(error)
06851                     Axis(dimid)%data(1:len) = r8vals(1:len)
06852                  case (NF_CHAR) !RV,bundle
06853                     len=Axis(dimid)%len !RV,bundle
06854                     allocate(Axis(dimid)%cdata(len)) !RV,bundle
06855                     error = NFMPI_GET_VAR_TEXT_ALL(ncid,i,Axis(dimid)%cdata) !RV,bundle
06856                     print*,'cdata',Axis(dimid)%cdata !RV,bundle
06857                     call netcdf_err(error) !RV,bundle
06858                  case default
06859                     call mpp_error( FATAL, 'Invalid data type for dimension' )
06860                  end select
06861              else
06862                  len = mpp_file(unit)%time_level
06863                  allocate(mpp_file(unit)%time_values(len))
06864                  select case (type)
06865                  case (NF_FLOAT)
06866                     error = NFMPI_GET_VAR_REAL_ALL(ncid,i,rvals);call netcdf_err(error)
06867                     mpp_file(unit)%time_values(1:len) = rvals(1:len)
06868                  case (NF_DOUBLE)
06869                     error = NFMPI_GET_VAR_DOUBLE_ALL(ncid,i,r8vals);call netcdf_err(error)
06870                     mpp_file(unit)%time_values(1:len) = r8vals(1:len)
06871                  case default
06872                     call mpp_error( FATAL, 'Invalid data type for dimension' )
06873                  end select
06874               endif
06875               ! assign dimension atts
06876               if( nvatts.GT.0 )allocate(Axis(dimid)%Att(nvatts))
06877 
06878               do j=1,nvatts
06879                  Axis(dimid)%Att(j) = default_att
06880               enddo
06881 
06882               do j=1,nvatts
06883                  error=NFMPI_INQ_ATTNAME(ncid,i,j,attname);call netcdf_err(error)
06884                  error=NFMPI_INQ_ATT(ncid,i,trim(attname),type,idim);call netcdf_err(error)
06885                  len = idim
06886 
06887                  Axis(dimid)%Att(j)%name = trim(attname)
06888                  Axis(dimid)%Att(j)%type = type
06889                  Axis(dimid)%Att(j)%len = len
06890 
06891                  select case (type)
06892                  case (NF_CHAR)
06893                     if (len.gt.512) call mpp_error(FATAL,'DIM ATT too long') 
06894                     error=NFMPI_GET_ATT_TEXT(ncid,i,trim(attname),Axis(dimid)%Att(j)%catt);call netcdf_err(error)
06895                     if( verbose .and. pe == 0 ) &
06896                          write (stdout(),*)  'AXIS ',trim(Axis(dimid)%name),' ATT ',trim(attname),' ',Axis(dimid)%Att(j)%catt(1:len)
06897                     ! store integers in float arrays
06898                     ! assume dimension data not packed 
06899                  case (NF_SHORT)
06900                     allocate(Axis(dimid)%Att(j)%fatt(len))
06901                     error=NFMPI_GET_ATT_INT2(ncid,i,trim(attname),i2vals);call netcdf_err(error)
06902                     Axis(dimid)%Att(j)%fatt(1:len)=i2vals(1:len)
06903                     if( verbose .and. pe == 0  ) &
06904                          write (stdout(),*)  'AXIS ',trim(Axis(dimid)%name),' ATT ',trim(attname),' ',Axis(dimid)%Att(j)%fatt
06905                  case (NF_INT)
06906                     allocate(Axis(dimid)%Att(j)%fatt(len))
06907                     error=NFMPI_GET_ATT_INT(ncid,i,trim(attname),ivals);call netcdf_err(error)
06908                     Axis(dimid)%Att(j)%fatt(1:len)=ivals(1:len)
06909                     if( verbose .and. pe == 0  ) &
06910                          write (stdout(),*)  'AXIS ',trim(Axis(dimid)%name),' ATT ',trim(attname),' ',Axis(dimid)%Att(j)%fatt
06911                  case (NF_FLOAT)
06912                     allocate(Axis(dimid)%Att(j)%fatt(len))
06913                     error=NFMPI_GET_ATT_REAL(ncid,i,trim(attname),rvals);call netcdf_err(error)
06914                     Axis(dimid)%Att(j)%fatt(1:len)=rvals(1:len)
06915                     if( verbose  .and. pe == 0 ) &
06916                          write (stdout(),*)  'AXIS ',trim(Axis(dimid)%name),' ATT ',trim(attname),' ',Axis(dimid)%Att(j)%fatt
06917                  case (NF_DOUBLE)
06918                     allocate(Axis(dimid)%Att(j)%fatt(len))
06919                     error=NFMPI_GET_ATT_DOUBLE(ncid,i,trim(attname),r8vals);call netcdf_err(error)
06920                     Axis(dimid)%Att(j)%fatt(1:len)=r8vals(1:len)
06921                     if( verbose  .and. pe == 0 ) &
06922                          write (stdout(),*)  'AXIS ',trim(Axis(dimid)%name),' ATT ',trim(attname),' ',Axis(dimid)%Att(j)%fatt
06923                  case default
06924                     call mpp_error( FATAL, 'Invalid data type for dimension at' )                      
06925                  end select
06926                  ! assign pre-defined axis attributes
06927                  select case(trim(attname))
06928                  case('long_name')
06929                     Axis(dimid)%longname=Axis(dimid)%Att(j)%catt(1:len)
06930                  case('units')
06931                     Axis(dimid)%units=Axis(dimid)%Att(j)%catt(1:len)
06932                  case('cartesian_axis')
06933                     Axis(dimid)%cartesian=Axis(dimid)%Att(j)%catt(1:len)
06934                  case('positive') 
06935                     attval = Axis(dimid)%Att(j)%catt(1:len)
06936                     if( attval.eq.'down' )then
06937                        Axis(dimid)%sense=-1
06938                     else if( attval.eq.'up' )then
06939                        Axis(dimid)%sense=1
06940                     endif
06941                  end select
06942 
06943               enddo
06944               ! store axis info in filetype
06945               mpp_file(unit)%Axis(dimid) = Axis(dimid)
06946            endif
06947         enddo
06948 ! assign variable info
06949         nv = 0
06950         do i=1, nvar_total
06951            error=NFMPI_INQ_VAR(ncid,i,name,type,nvdims,dimids,nvatts);call netcdf_err(error)
06952 !
06953 ! is this a dimension variable?
06954 !          
06955            isdim=.false.
06956            do j=1,ndim
06957               if( trim(lowercase(name)).EQ.trim(lowercase(Axis(j)%name)) )isdim=.true.
06958            enddo
06959 
06960            if( .not.isdim )then
06961 ! for non-dimension variables
06962               nv=nv+1; if( nv.GT.mpp_file(unit)%nvar )call mpp_error( FATAL, 'variable index exceeds number of defined variables' )
06963               mpp_file(unit)%Var(nv)%type = type
06964               mpp_file(unit)%Var(nv)%id = i
06965               mpp_file(unit)%Var(nv)%name = name
06966               mpp_file(unit)%Var(nv)%natt = nvatts
06967 ! determine packing attribute based on NetCDF variable type
06968              select case (type)
06969              case(NF_SHORT)
06970                  mpp_file(unit)%Var(nv)%pack = 4
06971              case(NF_FLOAT)
06972                  mpp_file(unit)%Var(nv)%pack = 2
06973              case(NF_DOUBLE)
06974                  mpp_file(unit)%Var(nv)%pack = 1
06975              case (NF_INT)
06976                  mpp_file(unit)%Var(nv)%pack = 2
06977              case default
06978                    call mpp_error( FATAL, 'Invalid variable type in NetCDF file' )
06979              end select
06980 ! assign dimension ids
06981               mpp_file(unit)%Var(nv)%ndim = nvdims
06982               allocate(mpp_file(unit)%Var(nv)%axes(nvdims))
06983               do j=1,nvdims
06984                  mpp_file(unit)%Var(nv)%axes(j) = Axis(dimids(j))
06985               enddo
06986               allocate(mpp_file(unit)%Var(nv)%size(nvdims))
06987 
06988               do j=1,nvdims
06989                  if( dimids(j).eq.mpp_file(unit)%recdimid )then
06990                     mpp_file(unit)%Var(nv)%time_axis_index = dimids(j)
06991                     mpp_file(unit)%Var(nv)%size(j)=1    ! dimid length set to 1 here for consistency w/ mpp_write 
06992                  else
06993                     mpp_file(unit)%Var(nv)%size(j)=Axis(dimids(j))%len
06994                  endif
06995               enddo
06996 ! assign variable atts
06997               if( nvatts.GT.0 )allocate(mpp_file(unit)%Var(nv)%Att(nvatts))
06998 
06999               do j=1,nvatts
07000                  mpp_file(unit)%Var(nv)%Att(j) = default_att
07001               enddo
07002               
07003               do j=1,nvatts
07004                  error=NFMPI_INQ_ATTNAME(ncid,i,j,attname);call netcdf_err(error)
07005                  error=NFMPI_INQ_ATT(ncid,i,attname,type,idim);call netcdf_err(error)
07006                  len = idim
07007                  mpp_file(unit)%Var(nv)%Att(j)%name = trim(attname)
07008                  mpp_file(unit)%Var(nv)%Att(j)%type = type
07009                  mpp_file(unit)%Var(nv)%Att(j)%len = len
07010                  
07011                  select case (type)
07012                    case (NF_CHAR)
07013                      if (len.gt.512) call mpp_error(FATAL,'VAR ATT too long') 
07014                      error=NFMPI_GET_ATT_TEXT(ncid,i,trim(attname),mpp_file(unit)%Var(nv)%Att(j)%catt(1:len));call netcdf_err(error)
07015                      if (verbose .and. pe == 0 )&
07016                            write (stdout(),*)  'Var ',nv,' ATT ',trim(attname),' ',mpp_file(unit)%Var(nv)%Att(j)%catt(1:len)
07017 ! store integers as float internally
07018                    case (NF_SHORT)
07019                      allocate(mpp_file(unit)%Var(nv)%Att(j)%fatt(len))
07020                      error=NFMPI_GET_ATT_INT2(ncid,i,trim(attname),i2vals);call netcdf_err(error)
07021                      mpp_file(unit)%Var(nv)%Att(j)%fatt(1:len)= i2vals(1:len)
07022                      if( verbose  .and. pe == 0 )&
07023                           write (stdout(),*)  'Var ',nv,' ATT ',trim(attname),' ',mpp_file(unit)%Var(nv)%Att(j)%fatt
07024                    case (NF_INT)
07025                      allocate(mpp_file(unit)%Var(nv)%Att(j)%fatt(len))
07026                      error=NFMPI_GET_ATT_INT(ncid,i,trim(attname),ivals);call netcdf_err(error)
07027                      mpp_file(unit)%Var(nv)%Att(j)%fatt(1:len)=ivals(1:len)
07028                      if( verbose .and. pe == 0  )&
07029                           write (stdout(),*)  'Var ',nv,' ATT ',trim(attname),' ',mpp_file(unit)%Var(nv)%Att(j)%fatt
07030                    case (NF_FLOAT)
07031                      allocate(mpp_file(unit)%Var(nv)%Att(j)%fatt(len))
07032                      error=NFMPI_GET_ATT_REAL(ncid,i,trim(attname),rvals);call netcdf_err(error)
07033                      mpp_file(unit)%Var(nv)%Att(j)%fatt(1:len)=rvals(1:len)
07034                      if( verbose  .and. pe == 0 )&
07035                           write (stdout(),*)  'Var ',nv,' ATT ',trim(attname),' ',mpp_file(unit)%Var(nv)%Att(j)%fatt
07036                    case (NF_DOUBLE)
07037                      allocate(mpp_file(unit)%Var(nv)%Att(j)%fatt(len))
07038                      error=NFMPI_GET_ATT_DOUBLE(ncid,i,trim(attname),r8vals);call netcdf_err(error)
07039                      mpp_file(unit)%Var(nv)%Att(j)%fatt(1:len)=r8vals(1:len)
07040                      if( verbose .and. pe == 0  ) &
07041                           write (stdout(),*)  'Var ',nv,' ATT ',trim(attname),' ',mpp_file(unit)%Var(nv)%Att(j)%fatt
07042                    case default
07043                         call mpp_error( FATAL, 'Invalid data type for variable att' )
07044                  end select
07045 ! assign pre-defined field attributes
07046                  select case (trim(attname))
07047                     case ('long_name')
07048                       mpp_file(unit)%Var(nv)%longname=mpp_file(unit)%Var(nv)%Att(j)%catt(1:len)
07049                     case('units')
07050                       mpp_file(unit)%Var(nv)%units=mpp_file(unit)%Var(nv)%Att(j)%catt(1:len)
07051                     case('scale_factor') 
07052                        mpp_file(unit)%Var(nv)%scale=mpp_file(unit)%Var(nv)%Att(j)%fatt(1)
07053                     case('missing') 
07054                        mpp_file(unit)%Var(nv)%missing=mpp_file(unit)%Var(nv)%Att(j)%fatt(1)
07055                     case('add_offset') 
07056                        mpp_file(unit)%Var(nv)%add=mpp_file(unit)%Var(nv)%Att(j)%fatt(1)              
07057                     case('valid_range') 
07058                        mpp_file(unit)%Var(nv)%min=mpp_file(unit)%Var(nv)%Att(j)%fatt(1)
07059                        mpp_file(unit)%Var(nv)%max=mpp_file(unit)%Var(nv)%Att(j)%fatt(2)
07060                  end select
07061               enddo
07062            endif
07063         enddo   ! end variable loop
07064       else
07065         call mpp_error( FATAL,  'MPP READ CURRENTLY DOES NOT SUPPORT NON-NETCDF' ) 
07066       endif
07067 
07068       mpp_file(unit)%initialized = .TRUE.
07069 #else
07070       call mpp_error( FATAL, 'MPP_READ currently requires use_netCDF option' )
07071 #endif      
07072       return
07073     end subroutine mpp_read_meta
07074 
07075 
07076     subroutine mpp_get_info( unit, ndim, nvar, natt, ntime )
07077 
07078       integer, intent(in) :: unit
07079       integer, intent(out) :: ndim, nvar, natt, ntime
07080 
07081 
07082       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_INFO: must first call mpp_io_init.' )
07083       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_INFO: invalid unit number.' )
07084 
07085       ndim = mpp_file(unit)%ndim
07086       nvar = mpp_file(unit)%nvar
07087       natt = mpp_file(unit)%natt
07088       ntime = mpp_file(unit)%time_level
07089 
07090       return
07091 
07092     end subroutine mpp_get_info
07093 
07094      
07095     subroutine mpp_get_global_atts( unit, global_atts )
07096 !
07097 !  copy global file attributes for use by user
07098 !
07099 !  global_atts is an attribute type which is allocated from the
07100 !  calling routine
07101 
07102       integer,       intent(in)    :: unit
07103       type(atttype), intent(inout) :: global_atts(:)
07104       integer :: natt,i
07105 
07106       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_INFO: must first call mpp_io_init.' )
07107       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_INFO: invalid unit number.' )
07108 
07109       if (size(global_atts).lt.mpp_file(unit)%natt) &
07110       call mpp_error(FATAL, 'MPP_GET_ATTS: atttype not dimensioned properly in calling routine')
07111 
07112       natt = mpp_file(unit)%natt
07113       global_atts = default_att
07114 
07115       do i=1,natt
07116          global_atts(i) = mpp_file(unit)%Att(i)
07117       enddo
07118 
07119       return
07120    end subroutine mpp_get_global_atts
07121 
07122    subroutine mpp_get_field_atts( field, name, units, longname, min, max, missing, ndim, siz, axes, atts )
07123 
07124      type(fieldtype), intent(in) :: field
07125      character(len=*), intent(out) , optional :: name, units
07126      character(len=*), intent(out), optional :: longname
07127      real,intent(out), optional :: min,max,missing
07128      integer, intent(out), optional :: ndim
07129      integer, intent(out), dimension(:), optional :: siz
07130 
07131      type(atttype), intent(out), optional, dimension(:) :: atts
07132      type(axistype), intent(out), optional, dimension(:) :: axes
07133 
07134      integer :: n,m
07135 
07136      if (PRESENT(name)) name = field%name
07137      if (PRESENT(units)) units = field%units
07138      if (PRESENT(longname)) longname = field%longname
07139      if (PRESENT(min)) min = field%min
07140      if (PRESENT(max)) max = field%max
07141      if (PRESENT(missing)) missing = field%missing
07142      if (PRESENT(ndim)) ndim = field%ndim
07143      if (PRESENT(atts)) then
07144         atts = default_att
07145         n = size(atts);m=size(field%Att)
07146         if (n.LT.m) call mpp_error(FATAL,'attribute array not large enough in mpp_get_field_atts')
07147         atts(1:m) = field%Att(1:m)
07148      end if
07149      if (PRESENT(axes)) then
07150         axes = default_axis
07151         n = size(axes);m=field%ndim
07152         if (n.LT.m) call mpp_error(FATAL,'axis array not large enough in mpp_get_field_atts')
07153         axes(1:m) = field%axes(1:m)
07154      end if
07155      if (PRESENT(siz)) then
07156         siz = -1
07157         n = size(siz);m=field%ndim
07158         if (n.LT.m) call mpp_error(FATAL,'size array not large enough in mpp_get_field_atts')
07159         siz(1:m) = field%size(1:m)
07160      end if     
07161      return
07162    end subroutine mpp_get_field_atts
07163 
07164    subroutine mpp_get_axis_atts( axis, name, units, longname, cartesian, sense, len, natts, atts )
07165 
07166      type(axistype), intent(in) :: axis
07167      character(len=*), intent(out) , optional :: name, units
07168      character(len=*), intent(out), optional :: longname, cartesian
07169      integer,intent(out), optional :: sense, len , natts
07170      type(atttype), intent(out), optional, dimension(:) :: atts
07171 
07172      integer :: n,m 
07173 
07174      if (PRESENT(name)) name = axis%name
07175      if (PRESENT(units)) units = axis%units
07176      if (PRESENT(longname)) longname = axis%longname
07177      if (PRESENT(cartesian)) cartesian = axis%cartesian
07178      if (PRESENT(sense)) sense = axis%sense
07179      if (PRESENT(len)) len = axis%len
07180      if (PRESENT(atts)) then
07181         atts = default_att
07182         n = size(atts);m=size(axis%Att)
07183         if (n.LT.m) call mpp_error(FATAL,'attribute array not large enough in mpp_get_field_atts')
07184         atts(1:m) = axis%Att(1:m)
07185      end if
07186      if (PRESENT(natts)) natts = size(axis%Att)
07187      
07188      return
07189    end subroutine mpp_get_axis_atts
07190 
07191 
07192     subroutine mpp_get_fields( unit, variables )
07193 !
07194 !  copy variable information from file (excluding data)
07195 !  global_atts is an attribute type which is allocated from the
07196 !  calling routine
07197 ! 
07198       integer,         intent(in)    :: unit
07199       type(fieldtype), intent(inout) :: variables(:)
07200 
07201       integer :: nvar,i
07202 
07203       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_FIELDS: must first call mpp_io_init.' )
07204       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_FIELDS: invalid unit number.' )
07205 
07206       if (size(variables).ne.mpp_file(unit)%nvar) &
07207       call mpp_error(FATAL, 'MPP_GET_FIELDS: fieldtype not dimensioned properly in calling routine')
07208 
07209       nvar = mpp_file(unit)%nvar
07210 
07211       do i=1,nvar
07212          variables(i) = mpp_file(unit)%Var(i)
07213       enddo
07214 
07215       return
07216    end subroutine mpp_get_fields
07217 
07218     subroutine mpp_get_axes( unit, axes, time_axis )
07219 !
07220 !  copy variable information from file (excluding data)
07221 !  global_atts is an attribute type which is allocated from the
07222 !  calling routine
07223 ! 
07224       integer, intent(in) :: unit
07225       type(axistype), intent(out) :: axes(:)
07226       type(axistype), intent(out), optional :: time_axis      
07227       character(len=128) :: name
07228       logical :: save
07229       integer :: ndim,i, nvar, j, num_dims, k
07230 
07231       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_AXES: must first call mpp_io_init.' )
07232       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_AXES: invalid unit number.' )
07233 
07234       if (size(axes).ne.mpp_file(unit)%ndim) &
07235       call mpp_error(FATAL, 'MPP_GET_AXES: axistype not dimensioned properly in calling routine')
07236 
07237       
07238       if (PRESENT(time_axis)) time_axis = default_axis
07239       ndim = mpp_file(unit)%ndim
07240       do i=1,ndim
07241         if (ASSOCIATED(mpp_file(unit)%Axis(i)%data)) then   
07242            axes(i)=mpp_file(unit)%Axis(i)
07243        else
07244            axes(i)=mpp_file(unit)%Axis(i)
07245            if (PRESENT(time_axis)) time_axis = mpp_file(unit)%Axis(i)
07246         endif
07247       enddo
07248 
07249       return
07250    end subroutine mpp_get_axes
07251 
07252    subroutine mpp_get_times( unit, time_values )
07253 !
07254 !  copy time information from file and convert to time_type
07255 ! 
07256       integer, intent(in) :: unit
07257       real(DOUBLE_KIND), intent(inout) :: time_values(:)
07258 
07259       integer :: ntime,i
07260 
07261       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_TIMES: must first call mpp_io_init.' )
07262       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_TIMES: invalid unit number.' )
07263 
07264       if (size(time_values).ne.mpp_file(unit)%time_level) &
07265       call mpp_error(FATAL, 'MPP_GET_TIMES: time_values not dimensioned properly in calling routine')
07266 
07267       ntime = mpp_file(unit)%time_level
07268 
07269       do i=1,ntime
07270          time_values(i) = mpp_file(unit)%time_values(i)
07271       enddo
07272 
07273 
07274 
07275       return
07276    end subroutine mpp_get_times
07277 
07278    function mpp_get_field_index(fields,fieldname)
07279 
07280      type(fieldtype), dimension(:) :: fields
07281      character(len=*) :: fieldname
07282      integer :: mpp_get_field_index
07283 
07284      integer :: n
07285 
07286      mpp_get_field_index = -1
07287 
07288      do n=1,size(fields)
07289         if (lowercase(fields(n)%name) == lowercase(fieldname)) then
07290            mpp_get_field_index = n
07291            exit
07292         endif
07293      enddo
07294 
07295      return
07296    end function mpp_get_field_index
07297 
07298    function mpp_get_field_size(field)
07299 
07300      type(fieldtype) :: field
07301      integer :: mpp_get_field_size(4)
07302 
07303      integer :: n
07304 
07305      mpp_get_field_size = -1
07306 
07307      mpp_get_field_size(1) = field%size(1)
07308      mpp_get_field_size(2) = field%size(2)
07309      mpp_get_field_size(3) = field%size(3)
07310      mpp_get_field_size(4) = field%size(4)
07311 
07312      return
07313    end function mpp_get_field_size
07314 
07315 
07316    subroutine mpp_get_axis_data( axis, data )
07317 
07318      type(axistype), intent(in) :: axis
07319      real, dimension(:), intent(out) :: data
07320 
07321 
07322      if (size(data).lt.axis%len) call mpp_error(FATAL,'MPP_GET_AXIS_DATA: data array not large enough')
07323      if (.NOT.ASSOCIATED(axis%data)) then
07324         call mpp_error(NOTE,'MPP_GET_AXIS_DATA: use mpp_get_times for record dims')
07325         data = 0.
07326      else
07327         data(1:axis%len) = axis%data
07328      endif
07329 
07330      return
07331    end subroutine mpp_get_axis_data
07332 
07333 
07334    function mpp_get_recdimid(unit)
07335 !
07336       integer, intent(in) :: unit
07337       integer  :: mpp_get_recdimid
07338 
07339 
07340       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_RECDIMID: must first call mpp_io_init.' )
07341       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_RECDIMID: invalid unit number.' )
07342 
07343       mpp_get_recdimid = mpp_file(unit)%recdimid
07344 
07345       return
07346    end function mpp_get_recdimid
07347 
07348 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
07349 !                                                                      !
07350 !         mpp_get_iospec, mpp_flush: OS-dependent calls                !
07351 !                                                                      !
07352 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
07353 
07354     subroutine mpp_flush(unit)
07355 !flush the output on a unit, syncing with disk
07356       integer, intent(in) :: unit
07357 
07358       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_FLUSH: must first call mpp_io_init.' )
07359       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_FLUSH: invalid unit number.' )
07360       if( .NOT.mpp_file(unit)%initialized )call mpp_error( FATAL, 'MPP_FLUSH: cannot flush a file during writing of metadata.' )
07361       if( mpp_file(unit)%threading.EQ.MPP_SINGLE .AND. pe.NE.mpp_root_pe() )return
07362 
07363       if( mpp_file(unit)%format.EQ.MPP_NETCDF )then
07364 #ifdef use_netCDF
07365           error = NFMPI_SYNC(mpp_file(unit)%ncid); call netcdf_err(error)
07366 #endif
07367       else
07368           call FLUSH(unit)
07369       end if
07370       return
07371     end subroutine mpp_flush
07372 
07373     subroutine mpp_get_iospec( unit, iospec )
07374       integer, intent(in) :: unit
07375       character(len=*), intent(out) :: iospec
07376 
07377       if( .NOT.module_is_initialized )call mpp_error( FATAL, 'MPP_GET_IOSPEC: must first call mpp_io_init.' )
07378       if( .NOT.mpp_file(unit)%opened )call mpp_error( FATAL, 'MPP_GET_IOSPEC: invalid unit number.' )
07379 #ifdef SGICRAY
07380 !currently will write to stdout: don't know how to trap and return as string to iospec
07381       call ASSIGN( 'assign -V f:'//trim(mpp_file(unit)%name), error )
07382 #endif
07383       return
07384     end subroutine mpp_get_iospec
07385 
07386 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
07387 !                                                                      !
07388 !         netCDF-specific routines: mpp_get_id, netcdf_error         !
07389 !                                                                      !
07390 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
07391 
07392     function mpp_get_ncid(unit)
07393       integer :: mpp_get_ncid
07394       integer, intent(in) :: unit
07395 
07396       mpp_get_ncid = mpp_file(unit)%ncid
07397       return
07398     end function mpp_get_ncid
07399 
07400     function mpp_get_axis_id(axis)
07401       integer mpp_get_axis_id
07402       type(axistype), intent(in) :: axis
07403       mpp_get_axis_id = axis%id
07404       return
07405     end function mpp_get_axis_id
07406 
07407     function mpp_get_field_id(field)
07408       integer mpp_get_field_id
07409       type(fieldtype), intent(in) :: field
07410       mpp_get_field_id = field%id
07411       return
07412     end function mpp_get_field_id
07413 
07414     subroutine netcdf_err(err)
07415       integer, intent(in) :: err
07416       character(len=80) :: errmsg
07417       integer :: unit
07418 
07419 #ifdef use_netCDF
07420       if( err.EQ.NF_NOERR )return
07421       errmsg = NFMPI_STRERROR(err)
07422       call mpp_io_exit()        !make sure you close all open files
07423       call mpp_error( FATAL, 'NETCDF ERROR: '//trim(errmsg) )
07424 #endif
07425       return
07426     end subroutine netcdf_err
07427 
07428 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
07429 !                                                                      !
07430 !       minor routines: mpp_get_unit_range, mpp_set_unit_range         !
07431 !                                                                      !
07432 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
07433 
07434     subroutine mpp_get_unit_range( unit_begin_out, unit_end_out )
07435       integer, intent(out) ::      unit_begin_out, unit_end_out
07436 
07437       unit_begin_out = unit_begin; unit_end_out = unit_end
07438       return
07439     end subroutine mpp_get_unit_range
07440 
07441     subroutine mpp_set_unit_range( unit_begin_in, unit_end_in )
07442       integer, intent(in) ::       unit_begin_in, unit_end_in
07443 
07444       if( unit_begin_in.GT.unit_end_in )call mpp_error( FATAL, 'MPP_SET_UNIT_RANGE: unit_begin_in.GT.unit_end_in.' )
07445       if( unit_begin_in.LT.0           )call mpp_error( FATAL, 'MPP_SET_UNIT_RANGE: unit_begin_in.LT.0.' )
07446       if( unit_end_in  .GT.maxunits    )call mpp_error( FATAL, 'MPP_SET_UNIT_RANGE: unit_end_in.GT.maxunits.' )
07447       unit_begin = unit_begin_in; unit_end = unit_end_in
07448       return
07449     end subroutine mpp_set_unit_range
07450 
07451     subroutine mpp_modify_axis_meta( axis, name, units, longname, cartesian, data )
07452 
07453       type(axistype), intent(inout) :: axis
07454       character(len=*), intent(in), optional :: name, units, longname, cartesian
07455       real, dimension(:), intent(in), optional :: data
07456 
07457       if (PRESENT(name)) axis%name = trim(name)
07458       if (PRESENT(units)) axis%units = trim(units)
07459       if (PRESENT(longname)) axis%longname = trim(longname)
07460       if (PRESENT(cartesian)) axis%cartesian = trim(cartesian)
07461       if (PRESENT(data)) then
07462          axis%len = size(data)
07463          if (ASSOCIATED(axis%data)) deallocate(axis%data)
07464          allocate(axis%data(axis%len))
07465          axis%data = data
07466       endif
07467          
07468       return
07469     end subroutine mpp_modify_axis_meta
07470 
07471     subroutine mpp_modify_field_meta( field, name, units, longname, min, max, missing, axes )
07472 
07473       type(fieldtype), intent(inout) :: field
07474       character(len=*), intent(in), optional :: name, units, longname
07475       real, intent(in), optional :: min, max, missing
07476       type(axistype), dimension(:), intent(inout), optional :: axes
07477 
07478       if (PRESENT(name)) field%name = trim(name)
07479       if (PRESENT(units)) field%units = trim(units)
07480       if (PRESENT(longname)) field%longname = trim(longname)
07481       if (PRESENT(min)) field%min = min
07482       if (PRESENT(max)) field%max = max
07483       if (PRESENT(missing)) field%missing = missing
07484 !      if (PRESENT(axes)) then
07485 !         axis%len = size(data)
07486 !         deallocate(axis%data)
07487 !         allocate(axis%data(axis%len))
07488 !         axis%data = data
07489 !      endif
07490          
07491       return
07492     end subroutine mpp_modify_field_meta
07493 
07494     function lowercase (cs)
07495     character(len=*), intent(in) :: cs
07496 #ifdef __crayx1
07497     character(len=128)       :: lowercase
07498     integer :: ido, i
07499     integer, save :: 
07500 !
07501     & down_map_ascii(0:127)=(/ (ido, ido=0,64), (ido+32, ido=65,90), (ido, ido=91,127) /)
07502         do i = 1, len( cs )
07503             lowercase(i:i) = achar(down_map_ascii(iachar(cs(i:i))))
07504         end do
07505 #else 
07506       character(len=len(cs))       :: lowercase
07507       character :: ca(len(cs)) 
07508       
07509       integer, parameter :: co=iachar('a')-iachar('A') ! case offset
07510       
07511       ca = transfer(cs,"x",len(cs)) 
07512       where (ca >= "A" .and. ca <= "Z") ca = achar(iachar(ca)+co) 
07513           lowercase = transfer(ca,cs) 
07514 #endif          
07515     end function lowercase
07516 
07517 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
07518 !                                                                      !
07519 !       minor routines: mpp_nullify_axistype,                          !
07520 !                       mpp_nullify_axistype_array                     !
07521 !                                                                      !
07522 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
07523 
07524     subroutine mpp_nullify_axistype(axis)
07525       type(axistype), intent(inout) :: axis
07526 
07527       Nullify(axis%data)
07528       Nullify(axis%cdata)
07529       Nullify(axis%Att)
07530     end subroutine mpp_nullify_axistype
07531 
07532     subroutine mpp_nullify_axistype_array(axis)
07533       type(axistype), intent(inout), dimension(:) :: axis
07534       integer :: i
07535 
07536       do i=1, size(axis)
07537         Nullify(axis(i)%data)
07538         Nullify(axis(i)%cdata)
07539         Nullify(axis(i)%Att)
07540       enddo
07541     end subroutine mpp_nullify_axistype_array
07542         
07543 end module mpp_io_mod_oa
07544 
07545 
07546 #endif
07547 #endif
 All Data Structures Namespaces Files Functions Variables Defines