Oasis3 4.0.2
|
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