FMS  2024.03
Flexible Modeling System
mpp_io_util.inc
1 ! -*-f90-*-
2 !***********************************************************************
3 !* GNU Lesser General Public License
4 !*
5 !* This file is part of the GFDL Flexible Modeling System (FMS).
6 !*
7 !* FMS is free software: you can redistribute it and/or modify it under
8 !* the terms of the GNU Lesser General Public License as published by
9 !* the Free Software Foundation, either version 3 of the License, or (at
10 !* your option) any later version.
11 !*
12 !* FMS is distributed in the hope that it will be useful, but WITHOUT
13 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 !* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 !* for more details.
16 !*
17 !* You should have received a copy of the GNU Lesser General Public
18 !* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
19 !***********************************************************************
20 !> @file
21 !> @brief Routines to retrieve data used in @ref mpp_io_mod
22 
23 !> @addtogroup mpp_io_mod
24 !> @{
25 
26  !> @brief Get some general information about a file.
27  !!
28  !> <br>Example usage:
29  !! @code{.F90}
30  !! call mpp_get_info( unit, ndim, nvar, natt, ntime )
31  !! @endcode
32  subroutine mpp_get_info( unit, ndim, nvar, natt, ntime )
33 
34  integer, intent(in) :: unit
35  integer, intent(out) :: ndim, nvar, natt, ntime
36 
37 
38  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_GET_INFO: must first call mpp_io_init.' )
39  if( .NOT.mpp_file(unit)%opened )&
40  call mpp_error(fatal, 'MPP_GET_INFO: invalid unit number, file '//trim(mpp_file(unit)%name))
41 
42  ndim = mpp_file(unit)%ndim
43  nvar = mpp_file(unit)%nvar
44  natt = mpp_file(unit)%natt
45  ntime = mpp_file(unit)%time_level
46 
47  return
48 
49  end subroutine mpp_get_info
50 
51  !> @brief Copy global file attributes for use by user
52  subroutine mpp_get_global_atts( unit, global_atts )
53 
54  integer, intent(in) :: unit
55  type(atttype), intent(inout) :: global_atts(:) !< an attribute type which is allocated from the
56  !! calling routine
57 
58  integer :: natt,i
59 
60  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_GET_INFO: must first call mpp_io_init.' )
61  if( .NOT.mpp_file(unit)%opened )&
62  call mpp_error( fatal, 'MPP_GET_INFO: invalid unit number,file '//trim(mpp_file(unit)%name))
63 
64  if (size(global_atts(:)).lt.mpp_file(unit)%natt) &
65  call mpp_error(fatal, 'MPP_GET_ATTS: atttype not dimensioned properly in calling routine, file '// &
66  trim(mpp_file(unit)%name))
67 
68  natt = mpp_file(unit)%natt
69  global_atts = default_att
70 
71  do i=1,natt
72  global_atts(i) = mpp_file(unit)%Att(i)
73  enddo
74 
75  return
76  end subroutine mpp_get_global_atts
77 
78  !#####################################################################
79  subroutine mpp_get_field_atts(field, name, units, longname, min, max, missing, ndim, siz, axes, atts, &
80  valid, scale, add, checksum)
81 
82  type(fieldtype), intent(in) :: field
83  character(len=*), intent(out), optional :: name, units
84  character(len=*), intent(out), optional :: longname
85  real, intent(out), optional :: min,max,missing
86  integer, intent(out), optional :: ndim
87  integer, intent(out), dimension(:), optional :: siz
88  type(validtype), intent(out), optional :: valid
89  real, intent(out), optional :: scale
90  real, intent(out), optional :: add
91  integer(i8_kind), intent(out), dimension(:), optional :: checksum
92 
93  type(atttype), intent(inout), dimension(:), optional :: atts
94  type(axistype), intent(inout), dimension(:), optional :: axes
95 
96  integer :: n,m, check_exist
97 
98  if (PRESENT(name)) name = field%name
99  if (PRESENT(units)) units = field%units
100  if (PRESENT(longname)) longname = field%longname
101  if (PRESENT(min)) min = field%min
102  if (PRESENT(max)) max = field%max
103  if (PRESENT(missing)) missing = field%missing
104  if (PRESENT(ndim)) ndim = field%ndim
105  if (PRESENT(atts)) then
106  atts = default_att
107  n = size(atts(:));m=size(field%Att(:))
108  if (n.LT.m)&
109  call mpp_error(fatal,'attribute array not large enough in mpp_get_field_atts, field '//&
110  trim(field%name))
111  do n=1,m
112  atts(n) = field%Att(n)
113  end do
114  end if
115  if (PRESENT(axes)) then
116  axes = default_axis
117  n = size(axes(:));m=field%ndim
118  if (n.LT.m) &
119  call mpp_error(fatal,'axis array not large enough in mpp_get_field_atts, field '//&
120  trim(field%name))
121  do n=1,m
122  axes(n) = field%axes(n)
123  end do
124  end if
125  if (PRESENT(siz)) then
126  siz = -1
127  n = size(siz(:));m=field%ndim
128  if (n.LT.m) &
129  call mpp_error(fatal,'size array not large enough in mpp_get_field_atts, field '//&
130  trim(field%name))
131  do n=1,m
132  siz(n) = field%size(n)
133  end do
134  end if
135 
136  if(PRESENT(valid)) then
137  call mpp_get_valid(field,valid)
138  endif
139 
140  if(PRESENT(scale)) scale = field%scale
141  if(present(add)) add = field%add
142  if(present(checksum)) then
143  checksum = 0
144  check_exist = mpp_find_att(field%Att(:),"checksum")
145  if ( check_exist >= 0 ) then
146  if(size(checksum(:)) >size(field%checksum(:))) call mpp_error(fatal, &
147  & "size(checksum(:)) >size(field%checksum(:))")
148  checksum = field%checksum(1:size(checksum(:)))
149  endif
150  endif
151 
152  return
153  end subroutine mpp_get_field_atts
154 
155  !#####################################################################
156  subroutine mpp_get_axis_atts( axis, name, units, longname, cartesian, &
157  calendar, sense, len, natts, atts, compressed )
158 
159  type(axistype), intent(in) :: axis
160  character(len=*), intent(out) , optional :: name, units
161  character(len=*), intent(out), optional :: longname, cartesian
162  character(len=*), intent(out), optional :: compressed, calendar
163  integer,intent(out), optional :: sense, len , natts
164  type(atttype), intent(inout), optional, dimension(:) :: atts
165 
166  integer :: n,m
167 
168  if (PRESENT(name)) name = axis%name
169  if (PRESENT(units)) units = axis%units
170  if (PRESENT(longname)) longname = axis%longname
171  if (PRESENT(cartesian)) cartesian = axis%cartesian
172  if (PRESENT(compressed)) compressed = axis%compressed
173  if (PRESENT(calendar)) calendar = axis%calendar
174  if (PRESENT(sense)) sense = axis%sense
175  if (PRESENT(len)) len = axis%len
176  if (PRESENT(atts)) then
177  atts = default_att
178  n = size(atts(:));m=size(axis%Att(:))
179  if (n.LT.m) &
180  call mpp_error(fatal,'attribute array not large enough in mpp_get_field_atts, axis '//&
181  trim(axis%name))
182  do n=1,m
183  atts(n) = axis%Att(n)
184  end do
185  end if
186  if (PRESENT(natts)) natts = size(axis%Att(:))
187 
188  return
189  end subroutine mpp_get_axis_atts
190 
191 
192  !#####################################################################
193  !> @brief Copy variable information from file (excluding data)
194  subroutine mpp_get_fields( unit, variables )
195  integer, intent(in) :: unit
196  type(fieldtype), intent(inout) :: variables(:)
197 
198  integer :: nvar,i
199 
200  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_GET_FIELDS: must first call mpp_io_init.' )
201  if( .NOT.mpp_file(unit)%opened )call mpp_error( fatal, 'MPP_GET_FIELDS: invalid unit number.' )
202 
203  if (size(variables(:)).ne.mpp_file(unit)%nvar) &
204  call mpp_error(fatal,'MPP_GET_FIELDS: fieldtype not dimensioned properly in calling routine, file '//&
205  trim(mpp_file(unit)%name))
206 
207  nvar = mpp_file(unit)%nvar
208 
209  do i=1,nvar
210  variables(i) = mpp_file(unit)%Var(i)
211  enddo
212 
213  return
214  end subroutine mpp_get_fields
215 
216 
217 
218  !#####################################################################
219  !> @brief Copy variable information from file (excluding data)
220  subroutine mpp_get_axes( unit, axes, time_axis )
221  integer, intent(in) :: unit
222  type(axistype), intent(inout) :: axes(:)
223  type(axistype), intent(inout), optional :: time_axis
224  integer :: ndim,i
225 
226  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_GET_AXES: must first call mpp_io_init.' )
227  if( .NOT.mpp_file(unit)%opened )&
228  call mpp_error( fatal, 'MPP_GET_AXES: invalid unit number, file '//trim(mpp_file(unit)%name))
229 
230  if (size(axes(:)).ne.mpp_file(unit)%ndim) &
231  call mpp_error(fatal, 'MPP_GET_AXES: axistype not dimensioned properly in calling routine, file '//&
232  trim(mpp_file(unit)%name))
233 
234 
235  if (PRESENT(time_axis)) time_axis = default_axis
236  ndim = mpp_file(unit)%ndim
237 
238  do i=1,ndim
239  axes(i)=mpp_file(unit)%Axis(i)
240 
241  if (PRESENT(time_axis) &
242  .AND. .NOT. ASSOCIATED(mpp_file(unit)%Axis(i)%data) &
243  .AND. mpp_file(unit)%Axis(i)%type /= -1) then
244  time_axis = mpp_file(unit)%Axis(i)
245  endif
246  enddo
247 
248  return
249  end subroutine mpp_get_axes
250 
251  !#####################################################################
252  !> @brief Copy variable information from file (excluding data)
253  function mpp_get_dimension_length(unit, dimname, found)
254  integer, intent(in) :: unit
255  character(len=*), intent(in) :: dimname
256  logical, optional, intent(out) :: found
257  integer :: mpp_get_dimension_length
258  logical :: found_dim
259  integer :: i
260 
261 
262  if( .NOT.module_is_initialized ) &
263  call mpp_error( fatal, 'mpp_get_dimension_length: must first call mpp_io_init.' )
264  if( .NOT.mpp_file(unit)%opened )&
265  call mpp_error( fatal, 'mpp_get_dimension_length: invalid unit number, file '//trim(mpp_file(unit)%name))
266  found_dim = .false.
268  do i = 1, mpp_file(unit)%ndim
269  if(trim(dimname) == trim(mpp_file(unit)%Axis(i)%name)) then
270  mpp_get_dimension_length = mpp_file(unit)%Axis(i)%len
271  found_dim = .true.
272  exit
273  endif
274  enddo
275 
276  if(present(found)) found = found_dim
277 
278  end function mpp_get_dimension_length
279 
280  !#####################################################################
281  !> @brief Copy variable information from file (excluding data)
282  subroutine mpp_get_time_axis( unit, time_axis )
283  integer, intent(in) :: unit
284  type(axistype), intent(inout) :: time_axis
285 
286  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_GET_AXES: must first call mpp_io_init.' )
287  if( .NOT.mpp_file(unit)%opened )&
288  call mpp_error( fatal, 'MPP_GET_AXES: invalid unit number, file '//trim(mpp_file(unit)%name))
289 
290  time_axis = mpp_file(unit)%Axis(mpp_file(unit)%recdimid)
291 
292  return
293  end subroutine mpp_get_time_axis
294 
295  !####################################################################
296  !> @brief Copy variable information from file (excluding data)
298  character(len=len(default_axis%calendar)) :: mpp_get_default_calendar
299 
300  mpp_get_default_calendar = default_axis%calendar
301 
302  end function mpp_get_default_calendar
303 
304  !> @brief Get file time data.
305  !!
306  !> Copy time information from file and convert to time_type
307  !! <br>Example usage:
308  !! @code{.F90}
309  !! call mpp_get_times( unit, time_values)
310  !! @endcode
311  subroutine mpp_get_times( unit, time_values )
312  integer, intent(in) :: unit
313  real, intent(inout) :: time_values(:)
314 
315  integer :: ntime,i
316 
317  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_GET_TIMES: must first call mpp_io_init.' )
318  if( .NOT.mpp_file(unit)%opened )&
319  call mpp_error(fatal, 'MPP_GET_TIMES: invalid unit number, file '//trim(mpp_file(unit)%name))
320 
321 ! NF_INQ_DIM returns -1 for the length of a record dimension if
322 ! it does not exist
323 
324  if (mpp_file(unit)%time_level == -1) then
325  time_values = 0.0
326  return
327  endif
328 
329  if (size(time_values(:)).ne.mpp_file(unit)%time_level) &
330  call mpp_error(fatal,'MPP_GET_TIMES: time_values not dimensioned properly in calling routine, file '//&
331  trim(mpp_file(unit)%name))
332 
333  ntime = mpp_file(unit)%time_level
334 
335  do i=1,ntime
336  time_values(i) = mpp_file(unit)%time_values(i)
337  enddo
338 
339  return
340  end subroutine mpp_get_times
341 
342  !#####################################################################
343  function mpp_get_field_index(fields,fieldname)
344 
345  type(fieldtype), dimension(:) :: fields
346  character(len=*) :: fieldname
347  integer :: mpp_get_field_index
348 
349  integer :: n
350 
351  mpp_get_field_index = -1
352 
353  do n=1,size(fields(:))
354  if (lowercase(fields(n)%name) == lowercase(fieldname)) then
355  mpp_get_field_index = n
356  exit
357  endif
358  enddo
359 
360  return
361  end function mpp_get_field_index
362 
363  !#####################################################################
364  function mpp_get_axis_index(axes,axisname)
365 
366  type(axistype), dimension(:) :: axes
367  character(len=*) :: axisname
368  integer :: mpp_get_axis_index
369 
370  integer :: n
371 
372  mpp_get_axis_index = -1
373 
374  do n=1,size(axes(:))
375  if (lowercase(axes(n)%name) == lowercase(axisname)) then
376  mpp_get_axis_index = n
377  exit
378  endif
379  enddo
380 
381  return
382  end function mpp_get_axis_index
383 
384  !#####################################################################
385  function mpp_get_axis_by_name(unit,axisname)
386 
387  integer :: unit
388  character(len=*) :: axisname
389  type(axistype) :: mpp_get_axis_by_name
390 
391  integer :: n
392 
393  mpp_get_axis_by_name = default_axis
394 
395  do n=1,size(mpp_file(unit)%Axis(:))
396  if (lowercase(mpp_file(unit)%Axis(n)%name) == lowercase(axisname)) then
397  mpp_get_axis_by_name = mpp_file(unit)%Axis(n)
398  exit
399  endif
400  enddo
401 
402  return
403  end function mpp_get_axis_by_name
404 
405  !#####################################################################
406  function mpp_get_field_size(field)
407 
408  type(fieldtype) :: field
409  integer :: mpp_get_field_size(4)
410 
411  mpp_get_field_size = -1
412 
413  mpp_get_field_size(1) = field%size(1)
414  mpp_get_field_size(2) = field%size(2)
415  mpp_get_field_size(3) = field%size(3)
416  mpp_get_field_size(4) = field%size(4)
417 
418  return
419  end function mpp_get_field_size
420 
421 
422  !#####################################################################
423  function mpp_get_axis_length(axis)
424 
425  type(axistype) :: axis
426  integer :: mpp_get_axis_length
427 
428  mpp_get_axis_length = axis%len
429 
430  return
431  end function mpp_get_axis_length
432 
433  !#####################################################################
434  function mpp_get_axis_bounds(axis, data, name)
435  type(axistype), intent(in) :: axis
436  real, dimension(:), intent(out) :: data
437  character(len=*), optional, intent(out) :: name
438  logical :: mpp_get_axis_bounds
439 
440  if (size(data(:)).lt.axis%len+1)&
441  call mpp_error(fatal,'MPP_GET_AXIS_BOUNDS: data array not large enough, axis '//trim(axis%name))
442  if (.NOT.ASSOCIATED(axis%data_bounds)) then
443  mpp_get_axis_bounds = .false.
444  else
445  mpp_get_axis_bounds = .true.
446  data(1:axis%len+1) = axis%data_bounds(:)
447  endif
448  if(present(name)) name = trim(axis%name_bounds)
449 
450  return
451  end function mpp_get_axis_bounds
452 
453  !#####################################################################
454  subroutine mpp_get_axis_data( axis, data )
455 
456  type(axistype), intent(in) :: axis
457  real, dimension(:), intent(out) :: data
458 
459 
460  if (size(data(:)).lt.axis%len)&
461  call mpp_error(fatal,'MPP_GET_AXIS_DATA: data array not large enough, axis '//trim(axis%name))
462  if (.NOT.ASSOCIATED(axis%data)) then
463  call mpp_error(note,'MPP_GET_AXIS_DATA: use mpp_get_times for record dims')
464  data = 0.
465  else
466  data(1:axis%len) = axis%data
467  endif
468 
469  return
470  end subroutine mpp_get_axis_data
471 
472 
473  !#####################################################################
474  function mpp_get_recdimid(unit)
475 !
476  integer, intent(in) :: unit
477  integer :: mpp_get_recdimid
478 
479 
480  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_GET_RECDIMID: must first call mpp_io_init.' )
481  if( .NOT.mpp_file(unit)%opened )call mpp_error( fatal, 'MPP_GET_RECDIMID: invalid unit number.' )
482 
483  mpp_get_recdimid = mpp_file(unit)%recdimid
484 
485  return
486  end function mpp_get_recdimid
487 
488  !#####################################################################
489 ! <FUNCTION NAME="mpp_get_ncid">
490 ! <OVERVIEW>
491 ! Get netCDF ID of an open file.
492 ! </OVERVIEW>
493 ! <DESCRIPTION>
494 ! This returns the <TT>ncid</TT> associated with the open file on
495 ! <TT>unit</TT>. It is used in the instance that the user desires to
496 ! perform netCDF calls upon the file that are not provided by the
497 ! <TT>mpp_io_mod</TT> API itself.
498 ! </DESCRIPTION>
499 ! <TEMPLATE>
500 ! mpp_get_ncid(unit)
501 ! </TEMPLATE>
502 ! <IN NAME="unit" TYPE="integer"> </IN>
503 ! </FUNCTION>
504 
505  !> @brief Get netCDF ID of an open file.
506  !!
507  !> This returns the <TT>ncid</TT> associated with the open file on
508  !! <TT>unit</TT>. It is used in the instance that the user desires to
509  !! perform netCDF calls upon the file that are not provided by the
510  !! <TT>mpp_io_mod</TT> API itself.
511  function mpp_get_ncid(unit)
512  integer :: mpp_get_ncid
513  integer, intent(in) :: unit
514 
515  mpp_get_ncid = mpp_file(unit)%ncid
516  return
517  end function mpp_get_ncid
518 
519  !#####################################################################
520  function mpp_get_axis_id(axis)
521  integer mpp_get_axis_id
522  type(axistype), intent(in) :: axis
523  mpp_get_axis_id = axis%id
524  return
525  end function mpp_get_axis_id
526 
527  !#####################################################################
528  function mpp_get_field_id(field)
529  integer mpp_get_field_id
530  type(fieldtype), intent(in) :: field
531  mpp_get_field_id = field%id
532  return
533  end function mpp_get_field_id
534 
535  !#####################################################################
536  !> @brief Set the mpp_io_stack variable to be at least n LONG words long
537  subroutine mpp_io_set_stack_size(n)
538  integer, intent(in) :: n
539  character(len=10) :: text
540 
541  if( n.GT.mpp_io_stack_size .AND. allocated(mpp_io_stack) )deallocate(mpp_io_stack)
542  if( .NOT.allocated(mpp_io_stack) )then
543  allocate( mpp_io_stack(n) )
544  mpp_io_stack_size = n
545  write( text,'(i10)' )n
546  if( pe.EQ.mpp_root_pe() )call mpp_error( note, 'MPP_IO_SET_STACK_SIZE: stack size set to '//text//'.' )
547  end if
548 
549  return
550  end subroutine mpp_io_set_stack_size
551 
552  !#####################################################################
553  !> Based on presence/absence of attributes, defines valid range or missing
554  ! value. For details, see section 8.1 of NetCDF User Guide
555  subroutine mpp_get_valid(f,v)
556  type(fieldtype),intent(in) :: f ! field
557  type(validtype),intent(out) :: v ! validator
558 
559  integer :: irange,imin,imax,ifill,imissing,iscale
560  integer :: valid_T, scale_T ! types of attributes
561 
562  v%is_range = .true.
563  v%min = -huge(v%min); v%max = huge(v%max)
564  if (f%natt == 0) return
565  ! find indices of relevant attributes
566  irange = mpp_find_att(f%att,'valid_range')
567  imin = mpp_find_att(f%att,'valid_min')
568  imax = mpp_find_att(f%att,'valid_max')
569  ifill = mpp_find_att(f%att,'_FillValue')
570  imissing = mpp_find_att(f%att,'missing_value')
571 
572  ! find the widest type of scale and offset; note that the code
573  ! uses assumption that NetCDF types are arranged in th order of rank,
574  ! that is NF_BYTE < NF_SHORT < NF_INT < NF_FLOAT < NF_DOUBLE
575  scale_t = 0
576  iscale = mpp_find_att(f%att,'scale_factor')
577  if(iscale>0) scale_t = f%att(iscale)%type
578  iscale = mpp_find_att(f%att,'add_offset')
579  if(iscale>0) scale_t = max(scale_t,f%att(iscale)%type)
580 
581 
582  ! examine possible range attributes
583  valid_t = 0
584  if (irange>0) then
585  v%min = f%att(irange)%fatt(1)
586  v%max = f%att(irange)%fatt(2)
587  valid_t = f%att(irange)%type
588  else if (imax>0.or.imin>0) then
589  if(imax>0) then
590  v%max = f%att(imax)%fatt(1)
591  valid_t = max(valid_t,f%att(imax)%type)
592  endif
593  if(imin>0) then
594  v%min = f%att(imin)%fatt(1)
595  valid_t = max(valid_t,f%att(imin)%type)
596  endif
597  else if (imissing > 0) then
598  v%is_range = .false.
599  ! here we always scale, since missing_value is supposed to be in
600  ! external representation
601  v%min = f%att(imissing)%fatt(1)*f%scale + f%add
602  else if (ifill>0) then
603  !z1l ifdef is added in to be able to compile without using use_netCDF.
604 #ifdef use_netCDF
605  ! define min and max according to _FillValue
606  if(f%att(ifill)%fatt(1)>0) then
607  ! if _FillValue is positive, then it defines valid maximum
608  v%max = f%att(ifill)%fatt(1)
609  select case(f%type)
610  case (nf_byte,nf_short,nf_int)
611  v%max = v%max-1
612  case (nf_float)
613  v%max = nearest(nearest(real(v%max,4),-1.0),-1.0)
614  case (nf_double)
615  v%max = nearest(nearest(real(v%max,8),-1.0),-1.0)
616  end select
617  ! always do the scaling, as the _FillValue is in external
618  ! representation
619  v%max = v%max*f%scale + f%add
620  else
621  ! if _FillValue is negative or zero, then it defines valid minimum
622  v%min = f%att(ifill)%fatt(1)
623  select case(f%type)
624  case (nf_byte,nf_short,nf_int)
625  v%min = v%min+1
626  case (nf_float)
627  v%min = nearest(nearest(real(v%min,4),+1.0),+1.0)
628  case (nf_double)
629  v%min = nearest(nearest(real(v%min,8),+1.0),+1.0)
630  end select
631  ! always do the scaling, as the _FillValue is in external
632  ! representation
633  v%min = v%min*f%scale + f%add
634  endif
635 #endif
636  endif
637  ! If valid_range is the same type as scale_factor (actually the wider of
638  ! scale_factor and add_offset) and this is wider than the external data, then it
639  ! will be interpreted as being in the units of the internal (unpacked) data.
640  ! Otherwise it is in the units of the external (packed) data.
641  ! Note that it is not relevant if we went through the missing_data of _FillValue
642  ! brances, because in this case all irange, imin, and imax are less then 0
643  if(.not.((valid_t == scale_t).and.(scale_t>f%type))) then
644  if(irange>0 .or. imin>0) then
645  v%min = v%min*f%scale + f%add
646  endif
647  if(irange>0 .or. imax>0) then
648  v%max = v%max*f%scale + f%add
649  endif
650  endif
651 
652  end subroutine mpp_get_valid
653 
654  !#####################################################################
655  logical elemental function mpp_is_valid(x, v)
656  real , intent(in) :: x ! real value to be eaxmined
657  type(validtype), intent(in) :: v ! validator
658 
659  if (v%is_range) then
660  mpp_is_valid = (v%min<=x).and.(x<=v%max)
661  else
662  mpp_is_valid = x/=v%min
663  endif
664  end function mpp_is_valid
665 
666  !#####################################################################
667  ! finds an attribute by name in the array; returns -1 if it is not
668  ! found
669  function mpp_find_att(atts, name)
670  integer :: mpp_find_att
671  type(atttype), intent(in) :: atts(:) ! array of attributes
672  character(len=*) :: name ! name of the attributes
673 
674  integer :: i
675 
676  mpp_find_att = -1
677  do i = 1, size(atts)
678  if (trim(name)==trim(atts(i)%name)) then
679  mpp_find_att=i
680  exit
681  endif
682  enddo
683  end function mpp_find_att
684  !#####################################################################
685 
686  !> @brief return the name of an attribute.
687  function mpp_get_att_name(att)
688  type(atttype), intent(in) :: att
689  character(len=len(att%name)) :: mpp_get_att_name
690 
691  mpp_get_att_name = att%name
692  return
693 
694  end function mpp_get_att_name
695 
696  !#####################################################################
697 
698  !> @brief return the type of an attribute.
699  function mpp_get_att_type(att)
700  type(atttype), intent(in) :: att
701  integer :: mpp_get_att_type
702 
703  mpp_get_att_type = att%type
704  return
705 
706  end function mpp_get_att_type
707 
708  !#####################################################################
709 
710  !> @brief return the length of an attribute.
711  function mpp_get_att_length(att)
712  type(atttype), intent(in) :: att
713  integer :: mpp_get_att_length
714 
715  mpp_get_att_length = att%len
716 
717  return
718 
719  end function mpp_get_att_length
720 
721  !#####################################################################
722 
723  !> @brief return the char value of an attribute.
724  function mpp_get_att_char(att)
725  type(atttype), intent(in) :: att
726  character(len=att%len) :: mpp_get_att_char
727 
728  mpp_get_att_char = att%catt
729  return
730 
731  end function mpp_get_att_char
732 
733  !#####################################################################
734 
735  !> @brief return the real array value of an attribute.
736  function mpp_get_att_real(att)
737  type(atttype), intent(in) :: att
738  real, dimension(size(att%fatt(:))) :: mpp_get_att_real
739 
740  mpp_get_att_real = att%fatt
741  return
742 
743  end function mpp_get_att_real
744 
745  !#####################################################################
746 
747  !> @brief return the real array value of an attribute.
749  type(atttype), intent(in) :: att
751 
752  mpp_get_att_real_scalar = att%fatt(1)
753  return
754 
755  end function mpp_get_att_real_scalar
756 
757  !#####################################################################
758  !> @brief return the name of an field
759  function mpp_get_field_name(field)
760  type(fieldtype), intent(in) :: field
761  character(len=len(field%name)) :: mpp_get_field_name
762 
763  mpp_get_field_name = field%name
764  return
765  end function mpp_get_field_name
766 
767  !#####################################################################
768  !> @brief return the file name of corresponding unit
769  function mpp_get_file_name(unit)
770  integer, intent(in) :: unit
771  character(len=len(mpp_file(1)%name)) :: mpp_get_file_name
772 
773  mpp_get_file_name = mpp_file(unit)%name
774  return
775 
776  end function mpp_get_file_name
777 
778  !####################################################################
779  !> @brief return if certain file with unit is opened or not
780  function mpp_file_is_opened(unit)
781  integer, intent(in) :: unit
782  logical :: mpp_file_is_opened
783 
784  mpp_file_is_opened = mpp_file(unit)%opened
785  return
786 
787  end function mpp_file_is_opened
788 
789  !####################################################################
790  !> @brief return the attribute value of given field name
791  subroutine mpp_get_field_att_text(unit, fieldname, attname, attvalue)
792  integer, intent(in) :: unit
793  character(len=*), intent(in) :: fieldname, attname
794  character(len=*), intent(out) :: attvalue
795  logical :: found_field, found_att
796  integer :: i, j, length
797 
798  found_field = .false.
799  found_att = .false.
800  do i=1,mpp_file(unit)%nvar
801  if( trim(mpp_file(unit)%Var(i)%name) == trim(fieldname)) then
802  found_field = .true.
803  do j=1, size(mpp_file(unit)%Var(i)%Att(:))
804  if( trim(mpp_file(unit)%Var(i)%Att(j)%name) == trim(attname) ) then
805  found_att = .true.
806  length = mpp_file(unit)%Var(i)%Att(j)%len
807  if(len(attvalue) .LE. length ) call mpp_error(fatal, &
808  'mpp_io_util.inc: length of attvalue is less than the length of catt')
809  attvalue = trim(mpp_file(unit)%Var(i)%Att(j)%catt(1:length))
810  exit
811  end if
812  end do
813  exit
814  end if
815  end do
816 
817  if(.NOT. found_field) call mpp_error(fatal,"mpp_io_util.inc: field "//trim(fieldname)// &
818  " does not exist in the file "//trim(mpp_file(unit)%name) )
819  if(.NOT. found_att) call mpp_error(fatal,"mpp_io_util.inc: attribute "//trim(attname)//" of field "&
820  //trim(fieldname)// " does not exist in the file "//trim(mpp_file(unit)%name) )
821 
822  return
823 
824  end subroutine mpp_get_field_att_text
825 
826 
827  !####################################################################
828  !> @brief return mpp_io_nml variable io_clock_on
829  function mpp_io_clock_on()
830  logical :: mpp_io_clock_on
831 
832  mpp_io_clock_on = io_clocks_on
833  return
834 
835  end function mpp_io_clock_on
836 
837 
838  function mpp_attribute_exist(field,name)
839  logical :: mpp_attribute_exist
840  type(fieldtype), intent(in) :: field ! The field that you are searching for the attribute.
841  character(len=*), intent(in) :: name ! name of the attributes
842 
843  if(field%natt > 0) then
844  mpp_attribute_exist = ( mpp_find_att(field%Att(:),name) > 0 )
845  else
846  mpp_attribute_exist = .false.
847  endif
848 
849  end function mpp_attribute_exist
850 
851 !#######################################################################
852 subroutine mpp_dist_io_pelist(ssize,pelist)
853  integer, intent(in) :: ssize ! Stripe size for dist read
854  integer, allocatable, intent(out) :: pelist(:)
855  integer :: i, lsize, ioroot
856  logical :: is_ioroot=.false.
857 
858  ! Did you make a mistake?
859  if(ssize < 1) call mpp_error(fatal,'mpp_dist_io_pelist: I/O stripe size < 1')
860 
861  is_ioroot = mpp_is_dist_ioroot(ssize,ioroot=ioroot,lsize=lsize)
862 
863  ! Did I make a mistake?
864  if(lsize < 1) call mpp_error(fatal,'mpp_dist_io_pelist: size of pelist < 1')
865 
866  allocate(pelist(lsize))
867  do i=1,lsize
868  pelist(i) = ioroot + i - 1
869  enddo
870 end subroutine mpp_dist_io_pelist
871 
872 !#######################################################################
873 logical function mpp_is_dist_ioroot(ssize,ioroot,lsize)
874  integer, intent(in) :: ssize ! Dist io set size
875  integer, intent(out), optional :: ioroot, lsize
876  integer :: pe, npes, mypos, maxpe, d_ioroot, d_lsize, last_ioroot
877  integer :: rootpe
878 
879  if(ssize < 1) call mpp_error(fatal,'mpp_is_dist_ioroot: I/O stripe size < 1')
880 
881  mpp_is_dist_ioroot = .false.
882  rootpe = mpp_root_pe()
883  d_lsize = ssize
884  pe = mpp_pe()
885  mypos = modulo(pe-rootpe,ssize) ! Which PE am I in the io group?
886  d_ioroot = pe - mypos ! What is the io root for the group?
887  npes = mpp_npes()
888  maxpe = min(d_ioroot+ssize,npes+rootpe) - 1 ! Handle end case
889  d_lsize = maxpe - d_ioroot + 1
890  if(mod(npes,ssize) == 1)then ! Ensure there are no sets with 1 member
891  last_ioroot = (npes-1) - ssize
892  if(pe >= last_ioroot) then
893  d_ioroot = last_ioroot
894  d_lsize = ssize + 1
895  endif
896  endif
897  if(pe == d_ioroot) mpp_is_dist_ioroot = .true.
898  if(PRESENT(ioroot)) ioroot = d_ioroot
899  if(PRESENT(lsize)) lsize = d_lsize
900 end function mpp_is_dist_ioroot
901 !> @}
character(len=len(mpp_file(1)%name)) function mpp_get_file_name(unit)
return the file name of corresponding unit
subroutine mpp_get_info(unit, ndim, nvar, natt, ntime)
Get some general information about a file.
Definition: mpp_io_util.inc:33
subroutine mpp_get_global_atts(unit, global_atts)
Copy global file attributes for use by user.
Definition: mpp_io_util.inc:53
subroutine mpp_io_set_stack_size(n)
Set the mpp_io_stack variable to be at least n LONG words long.
integer function mpp_get_ncid(unit)
Get netCDF ID of an open file.
integer function mpp_get_dimension_length(unit, dimname, found)
Copy variable information from file (excluding data)
subroutine mpp_get_times(unit, time_values)
Get file time data.
character(len=len(default_axis%calendar)) function mpp_get_default_calendar()
Copy variable information from file (excluding data)
real function mpp_get_att_real_scalar(att)
return the real array value of an attribute.
logical function mpp_file_is_opened(unit)
return if certain file with unit is opened or not
subroutine mpp_get_valid(f, v)
Based on presence/absence of attributes, defines valid range or missing.
subroutine mpp_get_fields(unit, variables)
Copy variable information from file (excluding data)
subroutine mpp_get_field_att_text(unit, fieldname, attname, attvalue)
return the attribute value of given field name
character(len=len(field%name)) function mpp_get_field_name(field)
return the name of an field
subroutine mpp_get_time_axis(unit, time_axis)
Copy variable information from file (excluding data)
integer function mpp_get_att_length(att)
return the length of an attribute.
subroutine mpp_get_axes(unit, axes, time_axis)
Copy variable information from file (excluding data)
character(len=len(att%name)) function mpp_get_att_name(att)
return the name of an attribute.
real function, dimension(size(att%fatt(:))) mpp_get_att_real(att)
return the real array value of an attribute.
logical function mpp_io_clock_on()
return mpp_io_nml variable io_clock_on
integer function mpp_get_att_type(att)
return the type of an attribute.
character(len=att%len) function mpp_get_att_char(att)
return the char value of an attribute.
integer function mpp_npes()
Returns processor count for current pelist.
Definition: mpp_util.inc:421
integer function mpp_pe()
Returns processor ID.
Definition: mpp_util.inc:407