FMS  2024.03
Flexible Modeling System
mpp_io_write.inc
1 ! -*-f90-*-
2 
3 !***********************************************************************
4 !* GNU Lesser General Public License
5 !*
6 !* This file is part of the GFDL Flexible Modeling System (FMS).
7 !*
8 !* FMS is free software: you can redistribute it and/or modify it under
9 !* the terms of the GNU Lesser General Public License as published by
10 !* the Free Software Foundation, either version 3 of the License, or (at
11 !* your option) any later version.
12 !*
13 !* FMS is distributed in the hope that it will be useful, but WITHOUT
14 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 !* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 !* for more details.
17 !*
18 !* You should have received a copy of the GNU Lesser General Public
19 !* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
20 !***********************************************************************
21 
22 !> @file
23 !> @brief This series of routines is used to describe the contents of the file
24 !! being written on <unit>.
25 
26 !> @addtogroup mpp_io_mod
27 !> @{
28 
29 !> Writes a global metadata attribute to unit <unit>
30 !! attribute <name> can be an real, integer or character
31 !! one and only one of rval, ival, and cval should be present
32 !! the first found will be used
33 !! for a non-netCDF file, it is encoded into a string "GLOBAL <name> <val>"
34  subroutine mpp_write_meta_global( unit, name, rval, ival, cval, pack)
35  integer, intent(in) :: unit
36  character(len=*), intent(in) :: name
37  real, intent(in), optional :: rval(:)
38  integer, intent(in), optional :: ival(:)
39  character(len=*), intent(in), optional :: cval
40  integer, intent(in), optional :: pack
41 
42 ! call mpp_clock_begin(mpp_write_clock)
43  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_WRITE_META: must first call mpp_io_init.' )
44  if( .NOT. mpp_file(unit)%write_on_this_pe) then
45 ! call mpp_clock_end(mpp_write_clock)
46  return
47  endif
48  if( .NOT.mpp_file(unit)%opened )call mpp_error( fatal, 'MPP_WRITE_META: invalid unit number.' )
49  if( mpp_file(unit)%initialized ) &
50  call mpp_error( fatal, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
51 
52  if( mpp_file(unit)%format.EQ.mpp_netcdf )then
53 #ifdef use_netCDF
54  call write_attribute_netcdf( unit, nf_global, name, rval, ival, cval, pack )
55 #endif
56  else
57  call write_attribute( unit, 'GLOBAL '//trim(name), rval, ival, cval, pack )
58  end if
59 ! call mpp_clock_end(mpp_write_clock)
60 
61  return
62  end subroutine mpp_write_meta_global
63 
64 !versions of above to support <rval> and <ival> as scalars (because of f90 strict rank matching)
65  subroutine mpp_write_meta_global_scalar_r( unit, name, rval, pack )
66  integer, intent(in) :: unit
67  character(len=*), intent(in) :: name
68  real, intent(in) :: rval
69  integer, intent(in), optional :: pack
70 
71  call mpp_write_meta_global( unit, name, rval=(/rval/), pack=pack )
72  return
73  end subroutine mpp_write_meta_global_scalar_r
74 
75  subroutine mpp_write_meta_global_scalar_i( unit, name, ival, pack )
76  integer, intent(in) :: unit
77  character(len=*), intent(in) :: name
78  integer, intent(in) :: ival
79  integer, intent(in), optional :: pack
80 
81  call mpp_write_meta_global( unit, name, ival=(/ival/), pack=pack )
82  return
83  end subroutine mpp_write_meta_global_scalar_i
84 
85  subroutine mpp_write_meta_var( unit, id, name, rval, ival, cval, pack)
86 !writes a metadata attribute for variable <id> to unit <unit>
87 !attribute <name> can be an real, integer or character
88 !one and only one of rval, ival, and cval should be present
89 !the first found will be used
90 !for a non-netCDF file, it is encoded into a string "<id> <name> <val>"
91  integer, intent(in) :: unit, id
92  character(len=*), intent(in) :: name
93  real, intent(in), optional :: rval(:)
94  integer, intent(in), optional :: ival(:)
95  character(len=*), intent(in), optional :: cval
96  integer, intent(in), optional :: pack
97 
98  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_WRITE_META: must first call mpp_io_init.' )
99  if( .NOT. mpp_file(unit)%write_on_this_pe) then
100  return
101  endif
102  if( .NOT.mpp_file(unit)%opened )call mpp_error( fatal, 'MPP_WRITE_META: invalid unit number.' )
103  if( mpp_file(unit)%initialized ) &
104  call mpp_error( fatal, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
105 
106  if( mpp_file(unit)%format.EQ.mpp_netcdf )then
107  call write_attribute_netcdf( unit, id, name, rval, ival, cval, pack )
108  else
109  write( text, '(a,i4,a)' )'VARIABLE ', id, ' '//name
110  call write_attribute( unit, trim(text), rval, ival, cval, pack )
111  end if
112 
113  return
114  end subroutine mpp_write_meta_var
115 
116 !versions of above to support <rval> and <ival> as scalar (because of f90 strict rank matching)
117  subroutine mpp_write_meta_scalar_r( unit, id, name, rval, pack )
118  integer, intent(in) :: unit, id
119  character(len=*), intent(in) :: name
120  real, intent(in) :: rval
121  integer, intent(in), optional :: pack
122 
123  call mpp_write_meta( unit, id, name, rval=(/rval/), pack=pack )
124  return
125  end subroutine mpp_write_meta_scalar_r
126 
127  subroutine mpp_write_meta_scalar_i( unit, id, name, ival,pack )
128  integer, intent(in) :: unit, id
129  character(len=*), intent(in) :: name
130  integer, intent(in) :: ival
131  integer, intent(in), optional :: pack
132 
133  call mpp_write_meta( unit, id, name, ival=(/ival/),pack=pack )
134  return
135  end subroutine mpp_write_meta_scalar_i
136 
137 
138  subroutine mpp_write_axis_data (unit, axes )
139  integer, intent(in) :: unit
140  type(axistype), dimension(:), intent(in) :: axes
141 
142  integer :: naxis
143 
144  naxis = size (axes)
145  allocate (mpp_file(unit)%axis(naxis))
146  mpp_file(unit)%axis(1:naxis) = axes(1:naxis)
147 #ifdef use_netCDF
148  if( mpp_file(unit)%action.EQ.mpp_wronly )then
149  if(header_buffer_val>0) then
150  error = nf__enddef(mpp_file(unit)%ncid,header_buffer_val,4,0,4)
151  else
152  error = nf_enddef(mpp_file(unit)%ncid)
153  endif
154  endif
155 #endif
156  end subroutine mpp_write_axis_data
157 
158  subroutine mpp_def_dim_nodata(unit,name,size)
159  integer, intent(in) :: unit
160  character(len=*), intent(in) :: name
161  integer, intent(in) :: size
162  integer :: error,did
163 
164  ! This routine assumes the file is in define mode
165  if(.NOT. mpp_file(unit)%write_on_this_pe) return
166 #ifdef use_netCDF
167  error = nf_def_dim(mpp_file(unit)%ncid,name,size,did)
168  call netcdf_err(error, mpp_file(unit),string='Axis='//trim(name))
169 #endif
170  end subroutine mpp_def_dim_nodata
171 
172  subroutine mpp_def_dim_int(unit,name,dsize,longname,data)
173  integer, intent(in) :: unit
174  character(len=*), intent(in) :: name
175  integer, intent(in) :: dsize
176  character(len=*), intent(in) :: longname
177  integer, intent(in) :: data(:)
178  integer :: error,did,id
179 
180  ! This routine assumes the file is in define mode
181 #ifdef use_netCDF
182  if(.NOT. mpp_file(unit)%write_on_this_pe) return
183  error = nf_def_dim(mpp_file(unit)%ncid,name,dsize,did)
184  call netcdf_err(error, mpp_file(unit),string='Axis='//trim(name))
185 
186  ! Write dimension data.
187  error = nf_def_var( mpp_file(unit)%ncid, name, nf_int, 1, (/did/), id )
188  call netcdf_err( error, mpp_file(unit), string=' axis varable '//trim(name))
189 
190  error = nf_put_att_text( mpp_file(unit)%ncid, id, 'long_name', len_trim(longname), longname )
191  call netcdf_err( error, mpp_file(unit), string=' Attribute=long_name' )
192 
193  if( mpp_file(unit)%action.EQ.mpp_wronly )then
194  if(header_buffer_val>0) then
195  error = nf__enddef(mpp_file(unit)%ncid,header_buffer_val,4,0,4)
196  else
197  error = nf_enddef(mpp_file(unit)%ncid)
198  endif
199  endif
200  call netcdf_err( error, mpp_file(unit), string=' subroutine mpp_def_dim')
201  error = nf_put_vara_int( mpp_file(unit)%ncid, id, (/1/), (/size(data)/), data )
202  call netcdf_err( error, mpp_file(unit), string=' axis varable '//trim(name))
203  error = nf_redef(mpp_file(unit)%ncid)
204  call netcdf_err( error, mpp_file(unit), string=' subroutine mpp_def_dim')
205 #endif
206  return
207  end subroutine mpp_def_dim_int
208 
209  subroutine mpp_def_dim_real(unit,name,dsize,longname,data)
210  integer, intent(in) :: unit
211  character(len=*), intent(in) :: name
212  integer, intent(in) :: dsize
213  character(len=*), intent(in) :: longname
214  real, intent(in) :: data(:)
215  integer :: error,did,id
216 
217  ! This routine assumes the file is in define mode
218 #ifdef use_netCDF
219  if(.NOT. mpp_file(unit)%write_on_this_pe) return
220  error = nf_def_dim(mpp_file(unit)%ncid,name,dsize,did)
221  call netcdf_err(error, mpp_file(unit),string='Axis='//trim(name))
222 
223  ! Write dimension data.
224  error = nf_def_var( mpp_file(unit)%ncid, name, nf_int, 1, (/did/), id )
225  call netcdf_err( error, mpp_file(unit), string=' axis varable '//trim(name))
226 
227  error = nf_put_att_text( mpp_file(unit)%ncid, id, 'long_name', len_trim(longname), longname )
228  call netcdf_err( error, mpp_file(unit), string=' Attribute=long_name' )
229 
230  if( mpp_file(unit)%action.EQ.mpp_wronly )then
231  if(header_buffer_val>0) then
232  error = nf__enddef(mpp_file(unit)%ncid,header_buffer_val,4,0,4)
233  else
234  error = nf_enddef(mpp_file(unit)%ncid)
235  endif
236  endif
237  call netcdf_err( error, mpp_file(unit), string=' subroutine mpp_def_dim')
238  error = nf90_put_var( mpp_file(unit)%ncid, id, data, start=(/1/), count=(/size(data)/) )
239  call netcdf_err( error, mpp_file(unit), string=' axis varable '//trim(name))
240  error = nf_redef(mpp_file(unit)%ncid)
241  call netcdf_err( error, mpp_file(unit), string=' subroutine mpp_def_dim')
242 #endif
243  return
244  end subroutine mpp_def_dim_real
245 
246 
247 
248  subroutine mpp_write_meta_axis_r1d( unit, axis, name, units, longname, cartesian, sense, domain, data, min, &
249  & calendar)
250 !load the values in an axistype (still need to call mpp_write)
251 !write metadata attributes for axis
252 !it is declared intent(inout) so you can nullify pointers in the incoming object if needed
253 !the f90 standard doesn't guarantee that intent(out) on a type guarantees that its pointer
254 !components will be unassociated
255  integer, intent(in) :: unit
256  type(axistype), intent(inout) :: axis
257  character(len=*), intent(in) :: name, units, longname
258  character(len=*), intent(in), optional :: cartesian
259  integer, intent(in), optional :: sense
260  type(domain1d), intent(in), optional :: domain
261  real, intent(in), optional :: data(:)
262  real, intent(in), optional :: min
263  character(len=*), intent(in), optional :: calendar
264 
265  integer :: is, ie, isg, ieg
266  integer :: istat
267  logical :: domain_exist
268  type(domain2d), pointer :: io_domain => null()
269 
270 ! call mpp_clock_begin(mpp_write_clock)
271  !--- the shift and cartesian information is needed in mpp_write_meta_field from all the pe.
272  !--- we may revise this in the future.
273  axis%cartesian = 'N'
274  if( PRESENT(cartesian) )axis%cartesian = cartesian
275 
276  domain_exist = .false.
277 
278  if( PRESENT(domain) ) then
279  domain_exist = .true.
280  call mpp_get_global_domain( domain, isg, ieg )
281  if(mpp_file(unit)%io_domain_exist) then
282  io_domain => mpp_get_io_domain(mpp_file(unit)%domain)
283  if(axis%cartesian=='X') then
284  call mpp_get_global_domain( io_domain, xbegin=is, xend=ie)
285  else if(axis%cartesian=='Y') then
286  call mpp_get_global_domain( io_domain, ybegin=is, yend=ie)
287  endif
288  else
289  call mpp_get_compute_domain( domain, is, ie )
290  endif
291  else if( PRESENT(data) )then
292  isg=1; ieg=size(data(:)); is=isg; ie=ieg
293  endif
294 
295  axis%shift = 0
296  if( PRESENT(data) .AND. domain_exist ) then
297  if( size(data(:)) == ieg-isg+2 ) then
298  axis%shift = 1
299  ie = ie + 1
300  ieg = ieg + 1
301  endif
302  endif
303 
304  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_WRITE_META: must first call mpp_io_init.' )
305  if( .NOT. mpp_file(unit)%write_on_this_pe) then
306 ! call mpp_clock_end(mpp_write_clock)
307  return
308  endif
309  if( .NOT.mpp_file(unit)%opened )call mpp_error( fatal, 'MPP_WRITE_META: invalid unit number.' )
310  if( mpp_file(unit)%initialized ) &
311  call mpp_error( fatal, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
312 
313 !pre-existing pointers need to be nullified
314  if( ASSOCIATED(axis%data) ) then
315  DEALLOCATE(axis%data, stat=istat)
316  endif
317 !load axistype
318  axis%name = name
319  axis%units = units
320  axis%longname = longname
321  if( PRESENT(calendar) ) axis%calendar = calendar
322  if( PRESENT(sense) ) axis%sense = sense
323  if( PRESENT(data) )then
324  if( mpp_file(unit)%fileset.EQ.mpp_multi .AND. domain_exist ) then
325  axis%len = ie - is + 1
326  allocate(axis%data(axis%len))
327  axis%data = data(is-isg+1:ie-isg+1)
328  else
329  axis%len = size(data(:))
330  allocate(axis%data(axis%len))
331  axis%data = data
332  endif
333  endif
334 !write metadata
335  if( mpp_file(unit)%format.EQ.mpp_netcdf )then
336 #ifdef use_netCDF
337 !write axis def
338 !space axes are always floats, time axis is always double
339  if( ASSOCIATED(axis%data) )then !space axis
340  error = nf_def_dim( mpp_file(unit)%ncid, axis%name, axis%len, axis%did )
341  call netcdf_err( error, mpp_file(unit), axis )
342  if(pack_size == 1) then
343  error = nf_def_var( mpp_file(unit)%ncid, axis%name, nf_double, 1, (/axis%did/), axis%id )
344  else ! pack_size == 2
345  error = nf_def_var( mpp_file(unit)%ncid, axis%name, nf_float, 1, (/axis%did/), axis%id )
346  endif
347  call netcdf_err( error, mpp_file(unit), axis )
348  else !time axis
349  if( mpp_file(unit)%id.NE.-1 ) &
350  call mpp_error( fatal, 'MPP_WRITE_META_AXIS: There is already a time axis for this file.' )
351  error = nf_def_dim( mpp_file(unit)%ncid, axis%name, nf_unlimited, axis%did )
352  call netcdf_err( error, mpp_file(unit), axis )
353  if(pack_size == 1) then
354  error = nf_def_var( mpp_file(unit)%ncid, axis%name, nf_double, 1, (/axis%did/), axis%id )
355  else ! pack_size == 2
356  error = nf_def_var( mpp_file(unit)%ncid, axis%name, nf_float, 1, (/axis%did/), axis%id )
357  endif
358  call netcdf_err( error, mpp_file(unit), axis )
359  mpp_file(unit)%id = axis%id !file ID is the same as time axis varID
360  end if
361 #endif
362  else
363  varnum = varnum + 1
364  axis%id = varnum
365  axis%did = varnum
366 !write axis def
367  write( text, '(a,i4,a)' )'AXIS ', axis%id, ' name'
368  call write_attribute( unit, trim(text), cval=axis%name )
369  write( text, '(a,i4,a)' )'AXIS ', axis%id, ' size'
370  if( ASSOCIATED(axis%data) )then !space axis
371 ! if( mpp_file(unit)%fileset.EQ.MPP_MULTI .AND. axis%domain.NE.NULL_DOMAIN1D )then
372 ! call write_attribute( unit, trim(text), ival=(/ie-is+1/) )
373 ! else
374  call write_attribute( unit, trim(text), ival=(/size(axis%data(:))/) )
375 ! end if
376  else !time axis
377  if( mpp_file(unit)%id.NE.-1 ) &
378  call mpp_error( fatal, 'MPP_WRITE_META_AXIS: There is already a time axis for this file.' )
379  call write_attribute( unit, trim(text), ival=(/0/) ) !a size of 0 indicates time axis
380  mpp_file(unit)%id = axis%id
381  end if
382  end if
383 !write axis attributes
384  call mpp_write_meta( unit, axis%id, 'long_name', cval=axis%longname) ; axis%natt = axis%natt + 1
385  if (lowercase(trim(axis%units)).ne.'none' .OR. .NOT.cf_compliance) then
386  call mpp_write_meta( unit, axis%id, 'units', cval=axis%units) ; axis%natt = axis%natt + 1
387  endif
388  if( PRESENT(calendar) ) then
389  if (.NOT.cf_compliance) then
390  call mpp_write_meta( unit, axis%id, 'calendar', cval=axis%calendar)
391  else
392  call mpp_write_meta( unit, axis%id, 'calendar', cval=lowercase(axis%calendar))
393  endif
394  axis%natt = axis%natt + 1
395  endif
396  if( PRESENT(cartesian) ) then
397  if (.NOT.cf_compliance) then
398  call mpp_write_meta( unit, axis%id, 'cartesian_axis', cval=axis%cartesian)
399  axis%natt = axis%natt + 1
400  else
401  if (trim(axis%cartesian).ne.'N') then
402  call mpp_write_meta( unit, axis%id, 'axis', cval=axis%cartesian)
403  axis%natt = axis%natt + 1
404  endif
405  endif
406  endif
407  if( PRESENT(sense) )then
408  if( sense.EQ.-1 )then
409  call mpp_write_meta( unit, axis%id, 'positive', cval='down')
410  axis%natt = axis%natt + 1
411  else if( sense.EQ.1 )then
412  call mpp_write_meta( unit, axis%id, 'positive', cval='up')
413  axis%natt = axis%natt + 1
414  else
415  ! silently ignore values of sense other than +/-1.
416  end if
417  end if
418  if( PRESENT(min) ) then
419  call mpp_write_meta( unit, axis%id, 'valid_min', rval=min)
420  axis%natt = axis%natt + 1
421  endif
422  if( mpp_file(unit)%threading.EQ.mpp_multi .AND. mpp_file(unit)%fileset.EQ.mpp_multi .AND. domain_exist )then
423  call mpp_write_meta( unit, axis%id, 'domain_decomposition', ival=(/isg,ieg,is,ie/))
424  axis%natt = axis%natt + 1
425  end if
426  if( verbose )print '(a,2i6,x,a,2i3)', &
427  'MPP_WRITE_META: Wrote axis metadata, pe, unit, axis%name, axis%id, axis%did=', &
428  pe, unit, trim(axis%name), axis%id, axis%did
429 
430  mpp_file(unit)%ndim = max(1,mpp_file(unit)%ndim + 1)
431 
432 ! call mpp_clock_end(mpp_write_clock)
433  return
434  end subroutine mpp_write_meta_axis_r1d
435 
436  subroutine mpp_write_meta_axis_i1d(unit, axis, name, units, longname, data, min, compressed)
437 !load the values in an axistype (still need to call mpp_write)
438 !write metadata attributes for axis
439 !it is declared intent(inout) so you can nullify pointers in the incoming object if needed
440 !the f90 standard doesn't guarantee that intent(out) on a type guarantees that its pointer
441 !components will be unassociated
442  integer, intent(in) :: unit
443  type(axistype), intent(inout) :: axis
444  character(len=*), intent(in) :: name, units, longname
445  integer, intent(in) :: data(:)
446  integer, intent(in), optional :: min
447  character(len=*), intent(in), optional :: compressed
448 
449  integer :: istat
450  logical :: domain_exist
451  type(domain2d), pointer :: io_domain => null()
452 
453 ! call mpp_clock_begin(mpp_write_clock)
454  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_WRITE_META_I1D: must first call mpp_io_init.' )
455  if( .NOT. mpp_file(unit)%write_on_this_pe) then
456 ! call mpp_clock_end(mpp_write_clock)
457  return
458  endif
459  if( .NOT.mpp_file(unit)%opened )call mpp_error( fatal, 'MPP_WRITE_META: invalid unit number.' )
460  if( mpp_file(unit)%initialized ) &
461  call mpp_error( fatal, 'MPP_WRITE_META_I1D: cannot write metadata to file after an mpp_write.' )
462 
463 !pre-existing pointers need to be nullified
464  if( ASSOCIATED(axis%idata) ) then
465  DEALLOCATE(axis%idata, stat=istat)
466  endif
467 !load axistype
468  axis%name = name
469  axis%units = units
470  axis%longname = longname
471  if( PRESENT(compressed)) axis%compressed = trim(compressed)
472  axis%len = size(data(:))
473  allocate(axis%idata(axis%len))
474  axis%idata = data
475 !write metadata
476 #ifdef use_netCDF
477  if( mpp_file(unit)%format.EQ.mpp_netcdf )then
478  error = nf_def_dim( mpp_file(unit)%ncid, axis%name, axis%len, axis%did )
479  call netcdf_err( error, mpp_file(unit), axis )
480  error = nf_def_var( mpp_file(unit)%ncid, axis%name, nf_int, 1, (/axis%did/), axis%id )
481  call netcdf_err( error, mpp_file(unit), axis )
482  else
483  call mpp_error( fatal, 'MPP_WRITE_META_AXIS_I1D: Only netCDF format is currently supported.' )
484  end if
485 #endif
486 !write axis attributes
487  call mpp_write_meta( unit, axis%id, 'long_name', cval=axis%longname) ; axis%natt = axis%natt + 1
488  if (lowercase(trim(axis%units)).ne.'none' .OR. .NOT.cf_compliance) then
489  call mpp_write_meta( unit, axis%id, 'units', cval=axis%units) ; axis%natt = axis%natt + 1
490  endif
491  if( PRESENT(compressed) ) then
492  call mpp_write_meta( unit, axis%id, 'compress', cval=axis%compressed)
493  axis%natt = axis%natt + 1
494  endif
495  if( PRESENT(min) ) then
496  call mpp_write_meta( unit, axis%id, 'valid_min', ival=min)
497  axis%natt = axis%natt + 1
498  endif
499  if( verbose )print '(a,2i6,x,a,2i3)', &
500  'MPP_WRITE_META: Wrote axis metadata, pe, unit, axis%name, axis%id, axis%did=', &
501  pe, unit, trim(axis%name), axis%id, axis%did
502 
503  mpp_file(unit)%ndim = max(1,mpp_file(unit)%ndim + 1)
504 
505 ! call mpp_clock_end(mpp_write_clock)
506  return
507  end subroutine mpp_write_meta_axis_i1d
508 
509 
510  subroutine mpp_write_meta_axis_unlimited(unit, axis, name, data, unlimited, units, longname)
511 !load the values in an axistype (still need to call mpp_write)
512 !write metadata attributes for axis
513 !it is declared intent(inout) so you can nullify pointers in the incoming object if needed
514 !the f90 standard doesn't guarantee that intent(out) on a type guarantees that its pointer
515 !components will be unassociated
516  integer, intent(in) :: unit
517  type(axistype), intent(inout) :: axis
518  character(len=*), intent(in) :: name
519  integer, intent(in) :: data ! Number of elements to be written
520  logical, intent(in) :: unlimited ! Provides unique arg signature
521  character(len=*), intent(in), optional :: units, longname
522 
523  integer :: istat
524  logical :: domain_exist
525  type(domain2d), pointer :: io_domain => null()
526 
527 ! call mpp_clock_begin(mpp_write_clock)
528  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_WRITE_META_I1D: must first call mpp_io_init.' )
529  if( .NOT. mpp_file(unit)%write_on_this_pe) then
530 ! call mpp_clock_end(mpp_write_clock)
531  return
532  endif
533  if( .NOT.mpp_file(unit)%opened )call mpp_error( fatal, 'MPP_WRITE_META: invalid unit number.' )
534  if( mpp_file(unit)%initialized ) &
535  call mpp_error( fatal, 'MPP_WRITE_META_I1D: cannot write metadata to file after an mpp_write.' )
536 
537 !load axistype
538  axis%name = name
539  if(present(units)) axis%units = units
540  if(present(longname)) axis%longname = longname
541  axis%len = 1
542  allocate(axis%idata(1))
543  axis%idata = data
544 !write metadata
545 #ifdef use_netCDF
546  if( mpp_file(unit)%format.EQ.mpp_netcdf )then
547  error = nf_def_dim( mpp_file(unit)%ncid, axis%name, nf_unlimited, axis%did )
548  call netcdf_err( error, mpp_file(unit), axis )
549  error = nf_def_var( mpp_file(unit)%ncid, axis%name, nf_int, 0, (/axis%did/), axis%id )
550  call netcdf_err( error, mpp_file(unit), axis )
551  else
552  call mpp_error( fatal, 'MPP_WRITE_META_AXIS_UNLIMITED: Only netCDF format is currently supported.' )
553  end if
554 #endif
555 !write axis attributes
556  if(present(longname)) then
557  call mpp_write_meta(unit,axis%id,'long_name',cval=axis%longname); axis%natt=axis%natt+1
558  endif
559  if(present(units)) then
560  if (lowercase(trim(axis%units)).ne.'none' .OR. .NOT.cf_compliance) then
561  call mpp_write_meta(unit,axis%id,'units', cval=axis%units); axis%natt=axis%natt+1
562  endif
563  endif
564  if( verbose )print '(a,2i6,x,a,2i3)', &
565  'MPP_WRITE_META_UNLIMITED: Wrote axis metadata, pe, unit, axis%name, axis%id, axis%did=', &
566  pe, unit, trim(axis%name), axis%id, axis%did
567 
568  mpp_file(unit)%ndim = max(1,mpp_file(unit)%ndim + 1)
569 
570 ! call mpp_clock_end(mpp_write_clock)
571  return
572  end subroutine mpp_write_meta_axis_unlimited
573 
574 
575  subroutine mpp_write_meta_field( unit, field, axes, name, units, longname,&
576  min, max, missing, fill, scale, add, pack, time_method, standard_name, checksum)
577 !define field: must have already called mpp_write_meta(axis) for each axis
578  integer, intent(in) :: unit
579  type(fieldtype), intent(inout) :: field
580  type(axistype), intent(in) :: axes(:)
581  character(len=*), intent(in) :: name, units, longname
582  real, intent(in), optional :: min, max, missing, fill, scale, add
583  integer, intent(in), optional :: pack
584  character(len=*), intent(in), optional :: time_method
585  character(len=*), intent(in), optional :: standard_name
586  integer(i8_kind), dimension(:), intent(in), optional :: checksum
587 !this array is required because of f77 binding on netCDF interface
588  integer, allocatable :: axis_id(:)
589  real :: a, b
590  integer :: i, istat, ishift, jshift
591  character(len=64) :: checksum_char
592 
593 ! call mpp_clock_begin(mpp_write_clock)
594 
595  !--- figure out the location of data, this is needed in mpp_write.
596  !--- for NON-symmetry domain, the position is not an issue.
597  !--- we may need to rethink how to address the symmetric issue.
598  ishift = 0; jshift = 0
599  do i = 1, size(axes(:))
600  select case ( lowercase( axes(i)%cartesian ) )
601  case ( 'x' )
602  ishift = axes(i)%shift
603  case ( 'y' )
604  jshift = axes(i)%shift
605  end select
606  end do
607 
608  field%position = center
609  if(ishift == 1 .AND. jshift == 1) then
610  field%position = corner
611  else if(ishift == 1) then
612  field%position = east
613  else if(jshift == 1) then
614  field%position = north
615  endif
616 
617  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_WRITE_META: must first call mpp_io_init.' )
618 
619  if( .NOT.mpp_file(unit)%write_on_this_pe) then
620  if( .NOT. ASSOCIATED(field%axes) )allocate(field%axes(1)) !temporary fix
621 ! call mpp_clock_end(mpp_write_clock)
622  return
623  endif
624  if( .NOT.mpp_file(unit)%opened ) call mpp_error( fatal, 'MPP_WRITE_META: invalid unit number.' )
625  if( mpp_file(unit)%initialized ) then
626 ! File has already been written to and needs to be returned to define mode.
627 #ifdef use_netCDF
628  error = nf_redef(mpp_file(unit)%ncid)
629 #endif
630  mpp_file(unit)%initialized = .false.
631  endif
632 ! call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
633 
634 !pre-existing pointers need to be nullified
635  if( ASSOCIATED(field%axes) ) DEALLOCATE(field%axes, stat=istat)
636  if( ASSOCIATED(field%size) ) DEALLOCATE(field%size, stat=istat)
637 !fill in field metadata
638  field%name = name
639  field%units = units
640  field%longname = longname
641  allocate( field%axes(size(axes(:))) )
642  field%axes = axes
643  field%ndim = size(axes(:))
644  field%time_axis_index = -1 !this value will never match any axis index
645 !size is buffer area for the corresponding axis info: it is required to buffer this info in the fieldtype
646 !because axis might be reused in different files
647  allocate( field%size(size(axes(:))) )
648  do i = 1,size(axes(:))
649  if( ASSOCIATED(axes(i)%data) )then !space axis
650  field%size(i) = size(axes(i)%data(:))
651  else !time
652  field%size(i) = 1
653  field%time_axis_index = i
654  end if
655  end do
656 !attributes
657  if( PRESENT(min) ) field%min = min
658  if( PRESENT(max) ) field%max = max
659  if( PRESENT(scale) ) field%scale = scale
660  if( PRESENT(add) ) field%add = add
661  if( PRESENT(standard_name)) field%standard_name = standard_name
662  if( PRESENT(missing) ) field%missing = missing
663  if( PRESENT(fill) ) field%fill = fill
664  field%checksum = 0
665  if( PRESENT(checksum) ) field%checksum(1:size(checksum)) = checksum(:)
666 
667  ! Issue warning if fill and missing are different
668  if (present(fill).and.present(missing)) then
669  if (field%missing .ne. field%fill) then
670  call mpp_error(warning, 'MPP_WRITE_META: NetCDF attributes &
671  &_FillValue and missing_value should be equal.')
672  end if
673  end if
674 !pack is currently used only for netCDF
675  field%pack = 2 !default write 32-bit floats
676  if( PRESENT(pack) )field%pack = pack
677  if( mpp_file(unit)%format.EQ.mpp_netcdf )then
678 #ifdef use_netCDF
679  allocate( axis_id(size(field%axes(:))) )
680  do i = 1,size(field%axes(:))
681  axis_id(i) = field%axes(i)%did
682  end do
683 !write field def
684  select case (field%pack)
685  case(0)
686  error = nf_def_var( mpp_file(unit)%ncid, field%name, nf_int, size(field%axes(:)), axis_id, field%id )
687  case(1)
688  error = nf_def_var( mpp_file(unit)%ncid, field%name, nf_double, size(field%axes(:)),axis_id,field%id)
689  case(2)
690  error = nf_def_var( mpp_file(unit)%ncid, field%name, nf_float, size(field%axes(:)),axis_id,field%id)
691  case(4)
692  if( .NOT.PRESENT(scale) .OR. .NOT.PRESENT(add) ) &
693  call mpp_error( fatal, 'MPP_WRITE_META_FIELD: scale and add must be supplied when pack=4.' )
694  error = nf_def_var( mpp_file(unit)%ncid, field%name, nf_short, size(field%axes(:)),axis_id,field%id)
695  case(8)
696  if( .NOT.PRESENT(scale) .OR. .NOT.PRESENT(add) ) &
697  call mpp_error( fatal, 'MPP_WRITE_META_FIELD: scale and add must be supplied when pack=8.' )
698  error = nf_def_var( mpp_file(unit)%ncid, field%name, nf_byte, size(field%axes(:)),axis_id,field%id)
699  case default
700  call mpp_error( fatal, 'MPP_WRITE_META_FIELD: only legal packing values are 1,2,4,8.' )
701  end select
702  call netcdf_err( error, mpp_file(unit), field=field )
703  deallocate(axis_id)
704 #ifndef use_netCDF3
705  if(shuffle .NE. 0 .OR. deflate .NE. 0) then
706  error = nf_def_var_deflate(mpp_file(unit)%ncid, field%id, shuffle, deflate, deflate_level)
707  call netcdf_err( error, mpp_file(unit), field=field )
708  endif
709 #endif
710 #endif
711  else
712  varnum = varnum + 1
713  field%id = varnum
714  if( PRESENT(pack) )call mpp_error( warning, &
715  & 'MPP_WRITE_META: Packing is currently available only on netCDF files.' )
716 !write field def
717  write( text, '(a,i4,a)' )'FIELD ', field%id, ' name'
718  call write_attribute( unit, trim(text), cval=field%name )
719  write( text, '(a,i4,a)' )'FIELD ', field%id, ' axes'
720  call write_attribute( unit, trim(text), ival=field%axes(:)%did )
721  end if
722 !write field attributes: these names follow netCDF conventions
723  call mpp_write_meta( unit, field%id, 'long_name', cval=field%longname)
724  if (lowercase(trim(field%units)).ne.'none' .OR. .NOT.cf_compliance) then
725  call mpp_write_meta( unit, field%id, 'units', cval=field%units)
726  endif
727 !all real attributes must be written as packed
728  if( PRESENT(min) .AND. PRESENT(max) )then
729  if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
730  call mpp_write_meta( unit, field%id, 'valid_range', rval=(/min,max/), pack=pack )
731  else
732  a = nint((min-add)/scale)
733  b = nint((max-add)/scale)
734  call mpp_write_meta( unit, field%id, 'valid_range', rval=(/a, b /), pack=pack )
735  end if
736  else if( PRESENT(min) )then
737  if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
738  call mpp_write_meta( unit, field%id, 'valid_min', rval=field%min, pack=pack )
739  else
740  a = nint((min-add)/scale)
741  call mpp_write_meta( unit, field%id, 'valid_min', rval=a, pack=pack )
742  end if
743  else if( PRESENT(max) )then
744  if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
745  call mpp_write_meta( unit, field%id, 'valid_max', rval=field%max, pack=pack )
746  else
747  a = nint((max-add)/scale)
748  call mpp_write_meta( unit, field%id, 'valid_max', rval=a, pack=pack )
749  end if
750  end if
751 ! write missing_value
752  if ( present(missing) ) then
753  if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
754  call mpp_write_meta( unit, field%id, 'missing_value', rval=field%missing, pack=pack )
755  else
756  a = nint((missing-add)/scale)
757  call mpp_write_meta( unit, field%id, 'missing_value', rval=a, pack=pack )
758  end if
759  end if
760 ! write _FillValue
761  if ( present(fill) ) then
762  if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
763  call mpp_write_meta( unit, field%id, '_FillValue', rval=field%fill, pack=pack )
764  else if (field%pack==0) then ! some safety checks for integer fills
765  if ( present(scale).OR.present(add) ) then
766  call mpp_error(fatal,"add,scale not currently implimented for pack=0 int handling, try reals instead.")
767  else
768  ! Trust No One
769  call mpp_write_meta( unit, field%id, '_FillValue', ival=mpp_fill_int, pack=pack )
770  end if
771  else
772  a = nint((fill-add)/scale)
773  call mpp_write_meta( unit, field%id, '_FillValue', rval=a, pack=pack )
774  end if
775  end if
776 
777  if( field%pack.NE.1 .AND. field%pack.NE.2 )then
778  call mpp_write_meta( unit, field%id, 'packing', ival=field%pack )
779  if( PRESENT(scale) )call mpp_write_meta( unit, field%id, 'scale_factor', rval=field%scale )
780  if( PRESENT(add) )call mpp_write_meta( unit, field%id, 'add_offset', rval=field%add )
781  end if
782 
783  if( present(checksum) )then
784  write (checksum_char,'(Z16)') field%checksum(1)
785  do i = 2,size(checksum)
786  write (checksum_char,'(a,Z16)') trim(checksum_char)//",",checksum(i)
787  enddo
788  call mpp_write_meta( unit, field%id, 'checksum', cval=checksum_char )
789  end if
790 
791  if ( PRESENT(time_method) ) then
792  call mpp_write_meta(unit,field%id, 'cell_methods',cval='time: '//trim(time_method))
793  endif
794  if ( PRESENT(standard_name)) &
795  call mpp_write_meta(unit,field%id,'standard_name ', cval=field%standard_name)
796 
797  if( verbose )print '(a,2i6,x,a,i3)', 'MPP_WRITE_META: Wrote field metadata: pe, unit, field%name, field%id=', &
798  pe, unit, trim(field%name), field%id
799 
800 ! call mpp_clock_end(mpp_write_clock)
801  return
802  end subroutine mpp_write_meta_field
803 
804  subroutine write_attribute( unit, name, rval, ival, cval, pack )
805 !called to write metadata for non-netCDF I/O
806  integer, intent(in) :: unit
807  character(len=*), intent(in) :: name
808  real, intent(in), optional :: rval(:)
809  integer, intent(in), optional :: ival(:)
810  character(len=*), intent(in), optional :: cval
811 !pack is currently ignored in this routine: only used by netCDF I/O
812  integer, intent(in), optional :: pack
813 
814  if( mpp_file(unit)%nohdrs )return
815 !encode text string
816  if( PRESENT(rval) )then
817  write( text,* )trim(name)//'=', rval
818  else if( PRESENT(ival) )then
819  write( text,* )trim(name)//'=', ival
820  else if( PRESENT(cval) )then
821  text = ' '//trim(name)//'='//trim(cval)
822  else
823  call mpp_error( fatal, 'WRITE_ATTRIBUTE: one of rval, ival, cval must be present.' )
824  end if
825  if( mpp_file(unit)%format.EQ.mpp_ascii )then
826 !implies sequential access
827  write( unit,fmt='(a)' )trim(text)//char(10)
828  else !MPP_IEEE32 or MPP_NATIVE
829  if( mpp_file(unit)%access.EQ.mpp_sequential )then
830  write(unit)trim(text)//char(10)
831  else !MPP_DIRECT
832  write( unit,rec=mpp_file(unit)%record )trim(text)//char(10)
833  if( verbose )print '(a,i6,a,i3)', 'WRITE_ATTRIBUTE: PE=', pe, ' wrote record ', mpp_file(unit)%record
834  mpp_file(unit)%record = mpp_file(unit)%record + 1
835  end if
836  end if
837  return
838  end subroutine write_attribute
839 
840  subroutine write_attribute_netcdf( unit, id, name, rval, ival, cval, pack )
841 !called to write metadata for netCDF I/O
842  integer, intent(in) :: unit
843  integer, intent(in) :: id
844  character(len=*), intent(in) :: name
845  real, intent(in), optional :: rval(:)
846  integer, intent(in), optional :: ival(:)
847  character(len=*), intent(in), optional :: cval
848  integer, intent(in), optional :: pack
849  integer, allocatable :: rval_i(:)
850 #ifdef use_netCDF
851  if( PRESENT(rval) )then
852 !pack was only meaningful for FP numbers, but is now extended by the ival branch of this routine
853  if( PRESENT(pack) )then
854  if( pack== 0 ) then !! here be dragons, use ival branch!...
855  if( kind(rval).EQ.r8_kind )then
856  call mpp_error( fatal, &
857  'WRITE_ATTRIBUTE_NETCDF: attempting to write internal NF_INT, currently int32, as double.' )
858  else if( kind(rval).EQ.r4_kind )then
859  call mpp_error( fatal, &
860  'WRITE_ATTRIBUTE_NETCDF: attempting to write internal NF_INT, currently int32, as float.' )
861  end if
862  else if( pack.EQ.1 )then
863  if( kind(rval).EQ.r8_kind )then
864  error = nf_put_att_double( mpp_file(unit)%ncid, id, name, nf_double, &
865  size(rval(:)), real(rval, kind=r8_kind))
866  else if( kind(rval).EQ.r4_kind )then
867  call mpp_error( warning, &
868  'WRITE_ATTRIBUTE_NETCDF: attempting to write internal 32-bit real as external 64-bit.' )
869  error = nf_put_att_real( mpp_file(unit)%ncid, id, name, nf_double, &
870  size(rval(:)), real(rval, kind=r4_kind))
871  end if
872  call netcdf_err( error, mpp_file(unit), string=' Attribute='//name )
873  else if( pack.EQ.2 )then
874  if( kind(rval).EQ.r8_kind )then
875  error = nf_put_att_double( mpp_file(unit)%ncid, id, name, nf_float, &
876  size(rval(:)), real(rval, kind=r8_kind))
877  else if( kind(rval).EQ.r4_kind )then
878  error = nf_put_att_real( mpp_file(unit)%ncid, id, name, nf_float, &
879  size(rval(:)), real(rval, kind=r4_kind))
880  end if
881  call netcdf_err( error, mpp_file(unit), string=' Attribute='//name )
882  else if( pack.EQ.4 )then
883  allocate( rval_i(size(rval(:))) )
884  rval_i = rval
885  if( kind(rval).EQ.r8_kind )then
886  error = nf_put_att_double( mpp_file(unit)%ncid, id, name, nf_short, &
887  size(rval_i(:)), real(rval, kind=r8_kind))
888  else if( kind(rval).EQ.r4_kind )then
889  error = nf_put_att_real( mpp_file(unit)%ncid, id, name, nf_short, &
890  size(rval_i(:)), real(rval, kind=r4_kind))
891  end if
892  call netcdf_err( error, mpp_file(unit), string=' Attribute='//name )
893  deallocate(rval_i)
894  else if( pack.EQ.8 )then
895  allocate( rval_i(size(rval(:))) )
896  rval_i = rval
897  if( kind(rval).EQ.r8_kind )then
898  error = nf_put_att_double( mpp_file(unit)%ncid, id, name, nf_byte, &
899  size(rval_i(:)), real(rval, kind=r8_kind))
900  else if( kind(rval).EQ.r4_kind )then
901  error = nf_put_att_real( mpp_file(unit)%ncid, id, name, nf_byte, &
902  size(rval_i(:)), real(rval, kind=r4_kind))
903  end if
904  call netcdf_err( error, mpp_file(unit), string=' Attribute='//name )
905  deallocate(rval_i)
906  else
907  call mpp_error( fatal, 'WRITE_ATTRIBUTE_NETCDF: only legal packing values are 1,2,4,8.' )
908  end if
909  else
910 !default is to write FLOATs (32-bit)
911  if( kind(rval).EQ.r8_kind )then
912  error = nf_put_att_double( mpp_file(unit)%ncid, id, name, nf_float, &
913  size(rval(:)), real(rval, kind=r8_kind))
914  else if( kind(rval).EQ.r4_kind )then
915  error = nf_put_att_real( mpp_file(unit)%ncid, id, name, nf_float, &
916  size(rval(:)), real(rval, kind=r4_kind))
917  end if
918  call netcdf_err( error, mpp_file(unit), string=' Attribute='//name )
919  end if
920  else if( PRESENT(ival) )then
921  if( PRESENT(pack) ) then
922  if (pack ==0) then
923  if (kind(ival).EQ.i8_kind) then
924  call mpp_error(fatal,'only use NF_INTs with pack=0 for now')
925  end if
926  error = nf_put_att_int( mpp_file(unit)%ncid, id, name, &
927  nf_int, size(ival(:)), ival ) !!XXX int32_t
928  call netcdf_err( error, mpp_file(unit), string=' Attribute='//name )
929  else
930  call mpp_error( fatal, 'WRITE_ATTRIBUTE_NETCDF: only implimented ints when pack=0, else use reals.' )
931  endif
932  else
933  error = nf_put_att_int( mpp_file(unit)%ncid, id, name, nf_int, size(ival(:)), ival )
934  call netcdf_err( error, mpp_file(unit), string=' Attribute='//name )
935  end if
936  else if( present(cval) )then
937  if (.NOT.cf_compliance .or. trim(name).NE.'calendar') then
938  error = nf_put_att_text( mpp_file(unit)%ncid, id, name, len_trim(cval), cval )
939  else
940  error = nf_put_att_text( mpp_file(unit)%ncid, id, name, len_trim(cval), lowercase(cval) )
941  endif
942  call netcdf_err( error, mpp_file(unit), string=' Attribute='//name )
943  else
944  call mpp_error( fatal, 'WRITE_ATTRIBUTE_NETCDF: one of rval, ival, cval must be present.' )
945  end if
946 #endif /* use_netCDF */
947  return
948  end subroutine write_attribute_netcdf
949 
950 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
951 ! !
952 ! MPP_WRITE !
953 ! !
954 ! mpp_write is used to write data to the file on <unit> using the !
955 ! file parameters supplied by mpp_open(). Axis and field definitions !
956 ! must have previously been written to the file using mpp_write_meta. !
957 ! !
958 ! mpp_write can take 2 forms, one for distributed data and one for !
959 ! non-distributed data. Distributed data refer to arrays whose two !
960 ! fastest-varying indices are domain-decomposed. Distributed data !
961 ! must be 2D or 3D (in space). Non-distributed data can be 0-3D. !
962 ! !
963 ! In all calls to mpp_write, tstamp is an optional argument. It is to !
964 ! be omitted if the field was defined not to be a function of time. !
965 ! Results are unpredictable if the argument is supplied for a time- !
966 ! independent field, or omitted for a time-dependent field. Repeated !
967 ! writes of a time-independent field are also not recommended. One !
968 ! time level of one field is written per call. !
969 ! !
970 ! !
971 ! For non-distributed data, use !
972 ! !
973 ! mpp_write( unit, field, data, tstamp ) !
974 ! integer, intent(in) :: unit !
975 ! type(fieldtype), intent(in) :: field !
976 ! real(r8_kind), optional :: tstamp !
977 ! data is real and can be scalar or of rank 1-3. !
978 ! !
979 ! For distributed data, use !
980 ! !
981 ! mpp_write( unit, field, domain, data, tstamp ) !
982 ! integer, intent(in) :: unit !
983 ! type(fieldtype), intent(in) :: field !
984 ! type(domain2D), intent(in) :: domain !
985 ! real(r8_kind), optional :: tstamp !
986 ! data is real and can be of rank 2 or 3. !
987 ! !
988 ! mpp_write( unit, axis ) !
989 ! integer, intent(in) :: unit !
990 ! type(axistype), intent(in) :: axis !
991 ! !
992 ! This call writes the actual co-ordinate values along each space !
993 ! axis. It must be called once for each space axis after all other !
994 ! metadata has been written. !
995 ! !
996 ! The mpp_write package also includes the routine write_record which !
997 ! performs the actual write. This routine is private to this module. !
998 ! !
999 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1000 #undef WRITE_RECORD_
1001 #define WRITE_RECORD_ write_record_r8
1002 #undef MPP_WRITE_2DDECOMP_2D_
1003 #define MPP_WRITE_2DDECOMP_2D_ mpp_write_2ddecomp_r2d_r8
1004 #undef MPP_WRITE_2DDECOMP_3D_
1005 #define MPP_WRITE_2DDECOMP_3D_ mpp_write_2ddecomp_r3d_r8
1006 #undef MPP_WRITE_2DDECOMP_4D_
1007 #define MPP_WRITE_2DDECOMP_4D_ mpp_write_2ddecomp_r4d_r8
1008 #undef MPP_TYPE_
1009 #define MPP_TYPE_ real(KIND=r8_kind)
1010 #include <mpp_write_2Ddecomp.fh>
1011 
1012 #undef WRITE_RECORD_
1013 #define WRITE_RECORD_ write_record_r4
1014 #undef MPP_WRITE_2DDECOMP_2D_
1015 #define MPP_WRITE_2DDECOMP_2D_ mpp_write_2ddecomp_r2d_r4
1016 #undef MPP_WRITE_2DDECOMP_3D_
1017 #define MPP_WRITE_2DDECOMP_3D_ mpp_write_2ddecomp_r3d_r4
1018 #undef MPP_WRITE_2DDECOMP_4D_
1019 #define MPP_WRITE_2DDECOMP_4D_ mpp_write_2ddecomp_r4d_r4
1020 #undef MPP_TYPE_
1021 #define MPP_TYPE_ real(KIND=r4_kind)
1022 #include <mpp_write_2Ddecomp.fh>
1023 
1024 #undef MPP_WRITE_COMPRESSED_1D_
1025 #define MPP_WRITE_COMPRESSED_1D_ mpp_write_compressed_r1d_r8
1026 #undef MPP_WRITE_COMPRESSED_2D_
1027 #define MPP_WRITE_COMPRESSED_2D_ mpp_write_compressed_r2d_r8
1028 #undef MPP_WRITE_COMPRESSED_3D_
1029 #define MPP_WRITE_COMPRESSED_3D_ mpp_write_compressed_r3d_r8
1030 #undef WRITE_RECORD_
1031 #define WRITE_RECORD_ write_record_r8
1032 #undef MPP_TYPE_
1033 #define MPP_TYPE_ real(KIND=r8_kind)
1034 #include <mpp_write_compressed.fh>
1035 
1036 #undef MPP_WRITE_COMPRESSED_1D_
1037 #define MPP_WRITE_COMPRESSED_1D_ mpp_write_compressed_r1d_r4
1038 #undef MPP_WRITE_COMPRESSED_2D_
1039 #define MPP_WRITE_COMPRESSED_2D_ mpp_write_compressed_r2d_r4
1040 #undef MPP_WRITE_COMPRESSED_3D_
1041 #define MPP_WRITE_COMPRESSED_3D_ mpp_write_compressed_r3d_r4
1042 #undef WRITE_RECORD_
1043 #define WRITE_RECORD_ write_record_r4
1044 #undef MPP_TYPE_
1045 #define MPP_TYPE_ real(KIND=r4_kind)
1046 #include <mpp_write_compressed.fh>
1047 
1048 #undef MPP_WRITE_UNLIMITED_AXIS_1D_
1049 #define MPP_WRITE_UNLIMITED_AXIS_1D_ mpp_write_unlimited_axis_r1d
1050 #undef MPP_TYPE_
1051 #define MPP_TYPE_ real
1052 #include <mpp_write_unlimited_axis.fh>
1053 
1054 #undef MPP_WRITE_
1055 #define MPP_WRITE_ mpp_write_r0D_r8
1056 #undef MPP_TYPE_
1057 #define MPP_TYPE_ real(KIND=r8_kind)
1058 #undef MPP_RANK_
1059 #define MPP_RANK_ !
1060 #undef MPP_WRITE_RECORD_
1061 #define MPP_WRITE_RECORD_ write_record_r8( unit, field, 1, (/data/), tstamp)
1062 #include <mpp_write.fh>
1063 
1064 #undef MPP_WRITE_
1065 #define MPP_WRITE_ mpp_write_r1D_r8
1066 #undef MPP_TYPE_
1067 #define MPP_TYPE_ real(KIND=r8_kind)
1068 #undef MPP_WRITE_RECORD_
1069 #define MPP_WRITE_RECORD_ write_record_r8( unit, field, size(data(:)), data, tstamp)
1070 #undef MPP_RANK_
1071 #define MPP_RANK_ (:)
1072 #include <mpp_write.fh>
1073 
1074 #undef MPP_WRITE_
1075 #define MPP_WRITE_ mpp_write_r2D_r8
1076 #undef MPP_TYPE_
1077 #define MPP_TYPE_ real(KIND=r8_kind)
1078 #undef MPP_WRITE_RECORD_
1079 #define MPP_WRITE_RECORD_ write_record_r8( unit, field, size(data(:,:)), data, tstamp )
1080 #undef MPP_RANK_
1081 #define MPP_RANK_ (:,:)
1082 #include <mpp_write.fh>
1083 
1084 #undef MPP_WRITE_
1085 #define MPP_WRITE_ mpp_write_r3D_r8
1086 #undef MPP_TYPE_
1087 #define MPP_TYPE_ real(KIND=r8_kind)
1088 #undef MPP_WRITE_RECORD_
1089 #define MPP_WRITE_RECORD_ write_record_r8( unit, field, size(data(:,:,:)), data, tstamp)
1090 #undef MPP_RANK_
1091 #define MPP_RANK_ (:,:,:)
1092 #include <mpp_write.fh>
1093 
1094 #undef MPP_WRITE_
1095 #define MPP_WRITE_ mpp_write_r4D_r8
1096 #undef MPP_TYPE_
1097 #define MPP_TYPE_ real(KIND=r8_kind)
1098 #undef MPP_WRITE_RECORD_
1099 #define MPP_WRITE_RECORD_ write_record_r8( unit, field, size(data(:,:,:,:)), data, tstamp)
1100 #undef MPP_RANK_
1101 #define MPP_RANK_ (:,:,:,:)
1102 #include <mpp_write.fh>
1103 
1104 #undef MPP_WRITE_
1105 #define MPP_WRITE_ mpp_write_r0D_r4
1106 #undef MPP_TYPE_
1107 #define MPP_TYPE_ real(KIND=r4_kind)
1108 #undef MPP_RANK_
1109 #define MPP_RANK_ !
1110 #undef MPP_WRITE_RECORD_
1111 #define MPP_WRITE_RECORD_ write_record_r4( unit, field, 1, (/data/), tstamp)
1112 #include <mpp_write.fh>
1113 
1114 #undef MPP_WRITE_
1115 #define MPP_WRITE_ mpp_write_r1D_r4
1116 #undef MPP_TYPE_
1117 #define MPP_TYPE_ real(KIND=r4_kind)
1118 #undef MPP_WRITE_RECORD_
1119 #define MPP_WRITE_RECORD_ write_record_r4( unit, field, size(data(:)), data, tstamp)
1120 #undef MPP_RANK_
1121 #define MPP_RANK_ (:)
1122 #include <mpp_write.fh>
1123 
1124 #undef MPP_WRITE_
1125 #define MPP_WRITE_ mpp_write_r2D_r4
1126 #undef MPP_TYPE_
1127 #define MPP_TYPE_ real(KIND=r4_kind)
1128 #undef MPP_WRITE_RECORD_
1129 #define MPP_WRITE_RECORD_ write_record_r4( unit, field, size(data(:,:)), data, tstamp )
1130 #undef MPP_RANK_
1131 #define MPP_RANK_ (:,:)
1132 #include <mpp_write.fh>
1133 
1134 #undef MPP_WRITE_
1135 #define MPP_WRITE_ mpp_write_r3D_r4
1136 #undef MPP_TYPE_
1137 #define MPP_TYPE_ real(KIND=r4_kind)
1138 #undef MPP_WRITE_RECORD_
1139 #define MPP_WRITE_RECORD_ write_record_r4( unit, field, size(data(:,:,:)), data, tstamp)
1140 #undef MPP_RANK_
1141 #define MPP_RANK_ (:,:,:)
1142 #include <mpp_write.fh>
1143 
1144 #undef MPP_WRITE_
1145 #define MPP_WRITE_ mpp_write_r4D_r4
1146 #undef MPP_TYPE_
1147 #define MPP_TYPE_ real(KIND=r4_kind)
1148 #undef MPP_WRITE_RECORD_
1149 #define MPP_WRITE_RECORD_ write_record_r4( unit, field, size(data(:,:,:,:)), data, tstamp)
1150 #undef MPP_RANK_
1151 #define MPP_RANK_ (:,:,:,:)
1152 #include <mpp_write.fh>
1153 
1154  subroutine mpp_write_axis( unit, axis )
1155  integer, intent(in) :: unit
1156  type(axistype), intent(in) :: axis
1157  type(fieldtype) :: field
1158 
1159  call mpp_clock_begin(mpp_write_clock)
1160  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_WRITE: must first call mpp_io_init.' )
1161  if( .NOT. mpp_file(unit)%write_on_this_pe ) then
1162  call mpp_clock_end(mpp_write_clock)
1163  return
1164  endif
1165  if( .NOT.mpp_file(unit)%opened )call mpp_error( fatal, 'MPP_WRITE: invalid unit number.' )
1166 !we convert axis to type(fieldtype) in order to call write_record
1167  field = default_field
1168  allocate( field%axes(1) )
1169  field%axes(1) = axis
1170  allocate( field%size(1) )
1171  field%size(1) = axis%len
1172  field%id = axis%id
1173 
1174  field%name = axis%name
1175  field%longname = axis%longname
1176  field%units = axis%units
1177 
1178  if(ASSOCIATED(axis%data))then
1179  allocate( field%axes(1)%data(size(axis%data) ))
1180  field%axes(1)%data = axis%data
1181  call write_record( unit, field, axis%len, axis%data )
1182  elseif(ASSOCIATED(axis%idata))then
1183  allocate( field%axes(1)%data(size(axis%idata) ))
1184  field%axes(1)%data = real(axis%idata)
1185  field%pack=4
1186  call write_record( unit, field, axis%len, real(axis%idata) )
1187  else
1188  call mpp_error( fatal, 'MPP_WRITE_AXIS: No data associated with axis.' )
1189  endif
1190 
1191  deallocate(field%axes(1)%data)
1192  deallocate(field%axes,field%size)
1193 
1194  call mpp_clock_end(mpp_write_clock)
1195  return
1196  end subroutine mpp_write_axis
1197 
1198 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1199 ! !
1200 ! MPP_COPY_META !
1201 ! !
1202 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1203  subroutine mpp_copy_meta_global( unit, gatt )
1204 !writes a global metadata attribute to unit <unit>
1205 !attribute <name> can be an real, integer or character
1206 !one and only one of rval, ival, and cval should be present
1207 !the first found will be used
1208 !for a non-netCDF file, it is encoded into a string "GLOBAL <name> <val>"
1209  integer, intent(in) :: unit
1210  type(atttype), intent(in) :: gatt
1211  integer :: len, error
1212 
1213  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_WRITE_META: must first call mpp_io_init.' )
1214  if( .NOT. mpp_file(unit)%write_on_this_pe )return
1215  if( .NOT.mpp_file(unit)%opened )call mpp_error( fatal, 'MPP_WRITE_META: invalid unit number.' )
1216  if( mpp_file(unit)%initialized ) then
1217 ! File has already been written to and needs to be returned to define mode.
1218 #ifdef use_netCDF
1219  error = nf_redef(mpp_file(unit)%ncid)
1220 #endif
1221  mpp_file(unit)%initialized = .false.
1222  endif
1223 ! call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
1224 #ifdef use_netCDF
1225  if( mpp_file(unit)%format.EQ.mpp_netcdf )then
1226  if( gatt%type.EQ.nf_char )then
1227  len = gatt%len
1228  call write_attribute_netcdf( unit, nf_global, gatt%name, cval=gatt%catt(1:len) )
1229  else
1230  call write_attribute_netcdf( unit, nf_global, gatt%name, rval=gatt%fatt )
1231  endif
1232  else
1233  if( gatt%type.EQ.nf_char )then
1234  len=gatt%len
1235  call write_attribute( unit, 'GLOBAL '//trim(gatt%name), cval=gatt%catt(1:len) )
1236  else
1237  call write_attribute( unit, 'GLOBAL '//trim(gatt%name), rval=gatt%fatt )
1238  endif
1239  end if
1240 #else
1241  call mpp_error( fatal, 'MPP_READ currently requires use_netCDF option' )
1242 #endif
1243  return
1244  end subroutine mpp_copy_meta_global
1245 
1246  subroutine mpp_copy_meta_axis( unit, axis, domain )
1247 !load the values in an axistype (still need to call mpp_write)
1248 !write metadata attributes for axis. axis is declared inout
1249 !because the variable and dimension ids are altered
1250 
1251  integer, intent(in) :: unit
1252  type(axistype), intent(inout) :: axis
1253  type(domain1d), intent(in), optional :: domain
1254  character(len=512) :: text
1255  integer :: i, len, is, ie, isg, ieg, error
1256 
1257 ! call mpp_clock_begin(mpp_write_clock)
1258  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_WRITE_META: must first call mpp_io_init.' )
1259  if( .NOT. mpp_file(unit)%write_on_this_pe ) then
1260 ! call mpp_clock_end(mpp_write_clock)
1261  return
1262  endif
1263  if( .NOT.mpp_file(unit)%opened )call mpp_error( fatal, 'MPP_WRITE_META: invalid unit number.' )
1264  if( mpp_file(unit)%initialized ) then
1265 ! File has already been written to and needs to be returned to define mode.
1266 #ifdef use_netCDF
1267  error = nf_redef(mpp_file(unit)%ncid)
1268 #endif
1269  mpp_file(unit)%initialized = .false.
1270  endif
1271 ! call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
1272 
1273 ! redefine domain if present
1274  if( PRESENT(domain) )then
1275  axis%domain = domain
1276  else
1277  axis%domain = null_domain1d
1278  end if
1279 
1280 #ifdef use_netCDF
1281 !write metadata
1282  if( mpp_file(unit)%format.EQ.mpp_netcdf )then
1283 
1284 !write axis def
1285  if( ASSOCIATED(axis%data) )then !space axis
1286  if( mpp_file(unit)%fileset.EQ.mpp_multi .AND. axis%domain.NE.null_domain1d )then
1287  call mpp_get_compute_domain( axis%domain, is, ie )
1288  call mpp_get_global_domain( axis%domain, isg, ieg )
1289  ie = ie + axis%shift
1290  ieg = ieg + axis%shift
1291  error = nf_def_dim( mpp_file(unit)%ncid, axis%name, ie-is+1, axis%did )
1292  else
1293  error = nf_def_dim( mpp_file(unit)%ncid, axis%name, size(axis%data(:)), axis%did )
1294  end if
1295  call netcdf_err( error, mpp_file(unit), axis )
1296  error = nf_def_var( mpp_file(unit)%ncid, axis%name, nf_float, 1, (/axis%did/), axis%id )
1297  call netcdf_err( error, mpp_file(unit), axis )
1298  else !time axis
1299  error = nf_def_dim( mpp_file(unit)%ncid, axis%name, nf_unlimited, axis%did )
1300  call netcdf_err( error, mpp_file(unit), axis )
1301  error = nf_def_var( mpp_file(unit)%ncid, axis%name, nf_double, 1, (/axis%did/), axis%id )
1302  call netcdf_err( error, mpp_file(unit), axis )
1303  mpp_file(unit)%id = axis%id !file ID is the same as time axis varID
1304  mpp_file(unit)%recdimid = axis%did ! record dimension id
1305  end if
1306  else
1307  varnum = varnum + 1
1308  axis%id = varnum
1309  axis%did = varnum
1310 !write axis def
1311  write( text, '(a,i4,a)' )'AXIS ', axis%id, ' name'
1312  call write_attribute( unit, trim(text), cval=axis%name )
1313  write( text, '(a,i4,a)' )'AXIS ', axis%id, ' size'
1314  if( ASSOCIATED(axis%data) )then !space axis
1315  if( mpp_file(unit)%fileset.EQ.mpp_multi .AND. axis%domain.NE.null_domain1d )then
1316  call mpp_get_compute_domain(axis%domain, is, ie)
1317  call write_attribute( unit, trim(text), ival=(/ie-is+1/) ) ! ??? is, ie is not initialized
1318  else
1319  call write_attribute( unit, trim(text), ival=(/size(axis%data(:))/) )
1320  end if
1321  else !time axis
1322  if( mpp_file(unit)%id.NE.-1 ) &
1323  call mpp_error( fatal, 'MPP_WRITE_META_AXIS: There is already a time axis for this file.' )
1324  call write_attribute( unit, trim(text), ival=(/0/) ) !a size of 0 indicates time axis
1325  mpp_file(unit)%id = axis%id
1326  end if
1327  end if
1328 !write axis attributes
1329 
1330  do i=1,axis%natt
1331  if( axis%Att(i)%name.NE.default_att%name )then
1332  if( axis%Att(i)%type.EQ.nf_char )then
1333  len = axis%Att(i)%len
1334  call mpp_write_meta( unit, axis%id, axis%Att(i)%name, cval=axis%Att(i)%catt(1:len) )
1335  else
1336  call mpp_write_meta( unit, axis%id, axis%Att(i)%name, rval=axis%Att(i)%fatt)
1337  endif
1338  endif
1339  enddo
1340 
1341  if( mpp_file(unit)%threading.EQ.mpp_multi .AND. mpp_file(unit)%fileset.EQ.mpp_multi &
1342  .AND. axis%domain.NE.null_domain1d )then
1343  call mpp_write_meta( unit, axis%id, 'domain_decomposition', ival=(/isg,ieg,is,ie/) )
1344  end if
1345  if( verbose )print '(a,2i6,x,a,2i3)', &
1346  'MPP_WRITE_META: Wrote axis metadata, pe, unit, axis%name, axis%id, axis%did=', &
1347  pe, unit, trim(axis%name), axis%id, axis%did
1348 #else
1349  call mpp_error( fatal, 'MPP_READ currently requires use_netCDF option' )
1350 #endif
1351 ! call mpp_clock_end(mpp_write_clock)
1352  return
1353  end subroutine mpp_copy_meta_axis
1354 
1355  subroutine mpp_copy_meta_field( unit, field, axes )
1356 !useful for copying field metadata from a previous call to mpp_read_meta
1357 !define field: must have already called mpp_write_meta(axis) for each axis
1358  integer, intent(in) :: unit
1359  type(fieldtype), intent(inout) :: field
1360  type(axistype), intent(in), optional :: axes(:)
1361 !this array is required because of f77 binding on netCDF interface
1362  integer, allocatable :: axis_id(:)
1363  real :: a, b
1364  integer :: i, error
1365 
1366 ! call mpp_clock_begin(mpp_write_clock)
1367  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_WRITE_META: must first call mpp_io_init.' )
1368  if( .NOT. mpp_file(unit)%write_on_this_pe ) then
1369 ! call mpp_clock_end(mpp_write_clock)
1370  return
1371  endif
1372  if( .NOT.mpp_file(unit)%opened )call mpp_error( fatal, 'MPP_WRITE_META: invalid unit number.' )
1373  if( mpp_file(unit)%initialized ) then
1374 ! File has already been written to and needs to be returned to define mode.
1375 #ifdef use_netCDF
1376  error = nf_redef(mpp_file(unit)%ncid)
1377 #endif
1378  mpp_file(unit)%initialized = .false.
1379  endif
1380 ! call mpp_error( FATAL, 'MPP_WRITE_META: cannot write metadata to file after an mpp_write.' )
1381 
1382  if( field%pack.NE.1 .AND. field%pack.NE.2 )then
1383  if( field%pack.NE.4 .AND. field%pack.NE.8 ) &
1384  call mpp_error( fatal, 'MPP_WRITE_META_FIELD: only legal packing values are 1,2,4,8.' )
1385  end if
1386 
1387  if (PRESENT(axes)) then
1388  deallocate(field%axes)
1389  deallocate(field%size)
1390  allocate(field%axes(size(axes(:))))
1391  allocate(field%size(size(axes(:))))
1392  field%axes = axes
1393  do i=1,size(axes(:))
1394  if (ASSOCIATED(axes(i)%data)) then
1395  field%size(i) = size(axes(i)%data(:))
1396  else
1397  field%size(i) = 1
1398  field%time_axis_index = i
1399  endif
1400  enddo
1401  endif
1402 
1403  if( mpp_file(unit)%format.EQ.mpp_netcdf )then
1404 #ifdef use_netCDF
1405  allocate( axis_id(size(field%axes(:))) )
1406  do i = 1,size(field%axes(:))
1407  axis_id(i) = field%axes(i)%did
1408  end do
1409 !write field def
1410  select case (field%pack)
1411  case(1)
1412  error = nf_def_var( mpp_file(unit)%ncid, field%name, nf_double, &
1413  size(field%axes(:)), axis_id, field%id )
1414  case(2)
1415  error = nf_def_var( mpp_file(unit)%ncid, field%name, nf_float, &
1416  size(field%axes(:)), axis_id, field%id )
1417  case(4)
1418 ! if( field%scale.EQ.default_field%scale .OR. field%add.EQ.default_field%add ) &
1419 ! call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: scale and add must be supplied when pack=4.' )
1420  error = nf_def_var( mpp_file(unit)%ncid, field%name, nf_short, &
1421  size(field%axes(:)), axis_id, field%id )
1422  case(8)
1423 ! if( field%scale.EQ.default_field%scale .OR. field%add.EQ.default_field%add ) &
1424 ! call mpp_error( FATAL, 'MPP_WRITE_META_FIELD: scale and add must be supplied when pack=8.' )
1425  error = nf_def_var( mpp_file(unit)%ncid, field%name, nf_byte, &
1426  size(field%axes(:)), axis_id, field%id )
1427  case default
1428  call mpp_error( fatal, 'MPP_WRITE_META_FIELD: only legal packing values are 1,2,4,8.' )
1429  end select
1430  deallocate( axis_id )
1431 #endif
1432  else
1433  varnum = varnum + 1
1434  field%id = varnum
1435  if( field%pack.NE.default_field%pack ) &
1436  call mpp_error( warning, 'MPP_WRITE_META: Packing is currently available only on netCDF files.' )
1437 !write field def
1438  write( text, '(a,i4,a)' )'FIELD ', field%id, ' name'
1439  call write_attribute( unit, trim(text), cval=field%name )
1440  write( text, '(a,i4,a)' )'FIELD ', field%id, ' axes'
1441  call write_attribute( unit, trim(text), ival=field%axes(:)%did )
1442  end if
1443 !write field attributes: these names follow netCDF conventions
1444  call mpp_write_meta( unit, field%id, 'long_name', cval=field%longname )
1445  if (lowercase(trim(field%units)).ne.'none' .OR. .NOT.cf_compliance) then
1446  call mpp_write_meta( unit, field%id, 'units', cval=field%units )
1447  endif
1448 !all real attributes must be written as packed
1449  if( (field%min.NE.default_field%min) .AND. (field%max.NE.default_field%max) )then
1450  if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
1451  call mpp_write_meta( unit, field%id, 'valid_range', rval=(/field%min,field%max/), pack=field%pack )
1452  else
1453  a = nint((field%min-field%add)/field%scale)
1454  b = nint((field%max-field%add)/field%scale)
1455  call mpp_write_meta( unit, field%id, 'valid_range', rval=(/a, b /), pack=field%pack )
1456  end if
1457  else if( field%min.NE.default_field%min )then
1458  if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
1459  call mpp_write_meta( unit, field%id, 'valid_min', rval=field%min, pack=field%pack )
1460  else
1461  a = nint((field%min-field%add)/field%scale)
1462  call mpp_write_meta( unit, field%id, 'valid_min', rval=a, pack=field%pack )
1463  end if
1464  else if( field%max.NE.default_field%max )then
1465  if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
1466  call mpp_write_meta( unit, field%id, 'valid_max', rval=field%max, pack=field%pack )
1467  else
1468  a = nint((field%max-field%add)/field%scale)
1469  call mpp_write_meta( unit, field%id, 'valid_max', rval=a, pack=field%pack )
1470  end if
1471  end if
1472  if( field%missing.NE.default_field%missing )then
1473  if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
1474  call mpp_write_meta( unit, field%id, 'missing_value', rval=field%missing, pack=field%pack )
1475  else
1476  a = nint((field%missing-field%add)/field%scale)
1477  call mpp_write_meta( unit, field%id, 'missing_value', rval=a, pack=field%pack )
1478  end if
1479  end if
1480  if( field%fill.NE.default_field%fill )then
1481  if( field%pack.EQ.1 .OR. field%pack.EQ.2 )then
1482  call mpp_write_meta( unit, field%id, '_FillValue', rval=field%missing, pack=field%pack )
1483  else
1484  a = nint((field%fill-field%add)/field%scale)
1485  call mpp_write_meta( unit, field%id, '_FillValue', rval=a, pack=field%pack )
1486  end if
1487  end if
1488  if( field%pack.NE.1 .AND. field%pack.NE.2 )then
1489  call mpp_write_meta( unit, field%id, 'packing', ival=field%pack )
1490  if( field%scale.NE.default_field%scale ) &
1491  call mpp_write_meta( unit, field%id, 'scale_factor', rval=field%scale )
1492  if( field%add.NE.default_field%add ) &
1493  call mpp_write_meta( unit, field%id, 'add_offset', rval=field%add )
1494  end if
1495  if( verbose )print '(a,2i6,x,a,i3)', 'MPP_WRITE_META: Wrote field metadata: pe, unit, field%name, field%id=', &
1496  pe, unit, trim(field%name), field%id
1497 
1498 ! call mpp_clock_end(mpp_write_clock)
1499  return
1500  end subroutine mpp_copy_meta_field
1501 
1502  subroutine mpp_modify_axis_meta( axis, name, units, longname, cartesian, data )
1503 
1504  type(axistype), intent(inout) :: axis
1505  character(len=*), intent(in), optional :: name, units, longname, cartesian
1506  real, dimension(:), intent(in), optional :: data
1507 
1508  if (PRESENT(name)) axis%name = trim(name)
1509  if (PRESENT(units)) axis%units = trim(units)
1510  if (PRESENT(longname)) axis%longname = trim(longname)
1511  if (PRESENT(cartesian)) axis%cartesian = trim(cartesian)
1512  if (PRESENT(data)) then
1513  axis%len = size(data(:))
1514  if (ASSOCIATED(axis%data)) deallocate(axis%data)
1515  allocate(axis%data(axis%len))
1516  axis%data = data
1517  endif
1518 
1519  return
1520  end subroutine mpp_modify_axis_meta
1521 
1522  subroutine mpp_modify_field_meta( field, name, units, longname, min, max, missing, axes )
1523 
1524  type(fieldtype), intent(inout) :: field
1525  character(len=*), intent(in), optional :: name, units, longname
1526  real, intent(in), optional :: min, max, missing
1527  type(axistype), dimension(:), intent(inout), optional :: axes
1528 
1529  if (PRESENT(name)) field%name = trim(name)
1530  if (PRESENT(units)) field%units = trim(units)
1531  if (PRESENT(longname)) field%longname = trim(longname)
1532  if (PRESENT(min)) field%min = min
1533  if (PRESENT(max)) field%max = max
1534  if (PRESENT(missing)) field%missing = missing
1535 ! if (PRESENT(axes)) then
1536 ! axis%len = size(data(:))
1537 ! deallocate(axis%data)
1538 ! allocate(axis%data(axis%len))
1539 ! axis%data = data
1540 ! endif
1541 
1542  return
1543  end subroutine mpp_modify_field_meta
1544 
1545 
1546 !> \brief Fills in a fieldtype variable, and is used with the diag_manager when using
1547 !! fms2_io
1548  subroutine fillin_fieldtype(field, axes, name, units, longname,&
1549  min, max, missing, fill, scale, add, pack, time_method, standard_name, checksum)
1550 !define field: must have already called mpp_write_meta(axis) for each axis
1551  type(fieldtype), intent(inout) :: field
1552  type(axistype), intent(in) :: axes(:)
1553  character(len=*), intent(in) :: name, units, longname
1554  real, intent(in), optional :: min, max, missing, fill, scale, add
1555  integer, intent(in), optional :: pack
1556  character(len=*), intent(in), optional :: time_method
1557  character(len=*), intent(in), optional :: standard_name
1558  integer(i8_kind), dimension(:), intent(in), optional :: checksum
1559 !this array is required because of f77 binding on netCDF interface
1560  integer, allocatable :: axis_id(:)
1561  real :: a, b
1562  integer :: i, istat, ishift, jshift
1563  character(len=64) :: checksum_char
1564 
1565 ! call mpp_clock_begin(mpp_write_clock)
1566 
1567  !--- figure out the location of data, this is needed in mpp_write.
1568  !--- for NON-symmetry domain, the position is not an issue.
1569  !--- we may need to rethink how to address the symmetric issue.
1570  ishift = 0; jshift = 0
1571  do i = 1, size(axes(:))
1572  select case ( lowercase( axes(i)%cartesian ) )
1573  case ( 'x' )
1574  ishift = axes(i)%shift
1575  case ( 'y' )
1576  jshift = axes(i)%shift
1577  end select
1578  end do
1579 
1580  field%position = center
1581  if(ishift == 1 .AND. jshift == 1) then
1582  field%position = corner
1583  else if(ishift == 1) then
1584  field%position = east
1585  else if(jshift == 1) then
1586  field%position = north
1587  endif
1588 
1589 !pre-existing pointers need to be nullified
1590  if( ASSOCIATED(field%axes) ) DEALLOCATE(field%axes, stat=istat)
1591  if( ASSOCIATED(field%size) ) DEALLOCATE(field%size, stat=istat)
1592 !fill in field metadata
1593  field%name = name
1594  field%units = units
1595  field%longname = longname
1596  allocate( field%axes(size(axes(:))) )
1597  field%axes = axes
1598  field%ndim = size(axes(:))
1599  field%time_axis_index = -1 !this value will never match any axis index
1600 !size is buffer area for the corresponding axis info: it is required to buffer this info in the fieldtype
1601 !because axis might be reused in different files
1602  allocate( field%size(size(axes(:))) )
1603  do i = 1,size(axes(:))
1604  if( ASSOCIATED(axes(i)%data) )then !space axis
1605  field%size(i) = size(axes(i)%data(:))
1606  else !time
1607  field%size(i) = 1
1608  field%time_axis_index = i
1609  end if
1610  end do
1611 !attributes
1612  if( PRESENT(min) ) field%min = min
1613  if( PRESENT(max) ) field%max = max
1614  if( PRESENT(scale) ) field%scale = scale
1615  if( PRESENT(add) ) field%add = add
1616  if( PRESENT(standard_name)) field%standard_name = standard_name
1617  if( PRESENT(missing) ) field%missing = missing
1618  if( PRESENT(fill) ) field%fill = fill
1619  field%checksum = 0
1620  if( PRESENT(checksum) ) field%checksum(1:size(checksum)) = checksum(:)
1621 
1622  ! Issue warning if fill and missing are different
1623  if ( (present(fill).and.present(missing)) .and. (field%missing .ne. field%fill) ) then
1624  call mpp_error(warning, 'MPP_WRITE_META: NetCDF attributes _FillValue and missing_value should be equal.')
1625  end if
1626 !pack is currently used only for netCDF
1627  field%pack = 2 !default write 32-bit floats
1628  if( PRESENT(pack) )field%pack = pack
1629 
1630  return
1631  end subroutine fillin_fieldtype
1632 !> @}
type(domain2d) function, pointer mpp_get_io_domain(domain)
Set user stack size.
subroutine fillin_fieldtype(field, axes, name, units, longname, min, max, missing, fill, scale, add, pack, time_method, standard_name, checksum)
Fills in a fieldtype variable, and is used with the diag_manager when using fms2_io.
subroutine mpp_write_meta_global(unit, name, rval, ival, cval, pack)
Writes a global metadata attribute to unit <unit> attribute <name> can be an real,...