FMS  2025.04
Flexible Modeling System
diag_output.F90
1 !***********************************************************************
2 !* Apache License 2.0
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* Licensed under the Apache License, Version 2.0 (the "License");
7 !* you may not use this file except in compliance with the License.
8 !* You may obtain a copy of the License at
9 !*
10 !* http://www.apache.org/licenses/LICENSE-2.0
11 !*
12 !* FMS is distributed in the hope that it will be useful, but WITHOUT
13 !* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied;
14 !* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 !* PARTICULAR PURPOSE. See the License for the specific language
16 !* governing permissions and limitations under the License.
17 !***********************************************************************
18 !> @defgroup diag_output_mod diag_output_mod
19 !> @ingroup diag_manager
20 !! @brief diag_output_mod is an integral part of
21 !! diag_manager_mod. Its function is to write axis-meta-data,
22 !! field-meta-data and field data.
23 !! @author Seth Underwood
24 
25 !> @addtogroup diag_output_mod
26 !> @{
27 MODULE diag_output_mod
28 
29 use platform_mod
30 use,intrinsic :: iso_fortran_env, only: real128
31 use,intrinsic :: iso_c_binding, only: c_double,c_float,c_int64_t, &
32  c_int32_t,c_int16_t,c_intptr_t
33  USE mpp_domains_mod, ONLY: domain1d, domain2d, mpp_define_domains, mpp_get_pelist,&
34  & mpp_get_global_domain, mpp_get_compute_domains, null_domain1d, null_domain2d,&
35  & domainug, null_domainug, center, east, north, mpp_get_compute_domain,&
36  & OPERATOR(.NE.), mpp_get_layout, OPERATOR(.EQ.), mpp_get_io_domain, &
38  USE mpp_mod, ONLY: mpp_npes, mpp_pe, mpp_root_pe, mpp_get_current_pelist
39  USE diag_axis_mod, ONLY: diag_axis_init, get_diag_axis, get_axis_length,&
43  USE time_manager_mod, ONLY: get_calendar_type, valid_calendar_types
44  USE fms_mod, ONLY: error_mesg, mpp_pe, write_version_number, fms_error_handler, fatal, note
45 
46  USE netcdf, ONLY: nf90_int, nf90_float, nf90_char
47 
48  use mpp_domains_mod, only: mpp_get_ug_io_domain
49  use mpp_domains_mod, only: mpp_get_ug_domain_npes
50  use mpp_domains_mod, only: mpp_get_ug_domain_pelist
51  use mpp_mod, only: mpp_gather
52  use mpp_mod, only: uppercase,lowercase
53  use fms2_io_mod
54 
55  IMPLICIT NONE
56 
57  PRIVATE
61  TYPE(diag_global_att_type), SAVE :: diag_global_att
62 
63  INTEGER, PARAMETER :: NETCDF1 = 1
64  INTEGER, PARAMETER :: mxch = 128
65  INTEGER, PARAMETER :: mxchl = 256
66  INTEGER :: current_file_unit = -1
67  INTEGER, DIMENSION(2,2) :: max_range = reshape((/ -32767, 32767, -127, 127 /),(/2,2/))
68  INTEGER, DIMENSION(2) :: missval = (/ -32768, -128 /)
69 
70  INTEGER, PARAMETER :: max_axis_num = 20
71  INTEGER :: num_axis_in_file = 0
72  INTEGER, DIMENSION(max_axis_num) :: axis_in_file
73  LOGICAL, DIMENSION(max_axis_num) :: time_axis_flag, edge_axis_flag
74 
75  LOGICAL :: module_is_initialized = .false.
76 
77  ! Include variable "version" to be written to log file.
78  character(len=*), parameter :: version = '2020.03'
79  !> @}
80 
81 !> @addtogroup diag_output_mod
82 !> @{
83 CONTAINS
84 
85  !> @brief Opens the output file.
86  SUBROUTINE diag_output_init (file_name, file_title, file_unit,&
87  & domain, domainU, fileobj, fileobjU, fileobjND, fnum_domain, &
88  & attributes)
89  CHARACTER(len=*), INTENT(in) :: file_name !< Output file name
90  CHARACTER(len=*), INTENT(in) :: file_title !< Descriptive title for the file
91  INTEGER , INTENT(out) :: file_unit !< File unit number assigned to the output file.
92  !! Needed for subsuquent calls to
93  !! diag_output_mod
94  TYPE(domain2d) , INTENT(in) :: domain !< Domain associated with file, if domain decomposed
95  TYPE(domainug) , INTENT(in) :: domainu !< The unstructure domain
96  type(fmsnetcdfdomainfile_t),intent(inout),target :: fileobj !< Domain decomposed fileobj
97  type(fmsnetcdfunstructureddomainfile_t),intent(inout),target :: fileobju !< Unstructured domain fileobj
98  type(fmsnetcdffile_t),intent(inout),target :: fileobjnd !< Non domain decomposed fileobj
99  character(*),intent(out) :: fnum_domain !< String indicating the type of fileobj was used:
100  !! "2d" domain decomposed
101  !! "ug" unstrucuted domain decomposed
102  !! "nd" no domain
103  TYPE(diag_atttype), INTENT(in), OPTIONAL :: attributes(:) !< Array of global attributes to be written to file
104 
105  class(fmsnetcdffile_t), pointer :: fileob => null()
106  integer :: i !< For looping through number of attributes
107  TYPE(diag_global_att_type) :: gatt
108  integer, allocatable, dimension(:) :: current_pelist
109  integer :: mype !< The pe you are on
110  character(len=9) :: mype_string !< a string to store the pe
111  character(len=FMS_FILE_LEN) :: filename_tile !< Filename with the tile number included
112  !! It is needed for subregional diagnostics
113 
114  !---- initialize mpp_io ----
115  IF ( .NOT.module_is_initialized ) THEN
116  module_is_initialized = .true.
117  CALL write_version_number("DIAG_OUTPUT_MOD", version)
118  END IF
119 
120 !> Checks to make sure that only domain2D or domainUG is used. If both are not null, then FATAL
121  if (domain .NE. null_domain2d .AND. domainu .NE. null_domainug)&
122  & CALL error_mesg('diag_output_init', "Domain2D and DomainUG can not be used at the same time in "//&
123  & trim(file_name), fatal)
124 
125  !---- open output file (return file_unit id) -----
126  IF ( domain .NE. null_domain2d ) THEN
127  !> Check if there is an io_domain
128  iF ( associated(mpp_get_io_domain(domain)) ) then
129  fileob => fileobj
130  if (.not.check_if_open(fileob)) call open_check(open_file(fileobj, trim(file_name)//".nc", "overwrite", &
131  domain, is_restart=.false.))
132  fnum_domain = "2d" ! 2d domain
133  file_unit = 2
134  elSE !< No io domain, so every core is going to write its own file.
135  fileob => fileobjnd
136  mype = mpp_pe()
137  write(mype_string,'(I0.4)') mype
138  !! Add the tile number to the subregional file
139  !! This is needed for the combiner to work correctly
140  call get_mosaic_tile_file(file_name, filename_tile, .true., domain)
141  filename_tile = trim(filename_tile)//"."//trim(mype_string)
142 
143  if (.not.check_if_open(fileob)) then
144  call open_check(open_file(fileobjnd, trim(filename_tile), "overwrite", &
145  is_restart=.false.))
146  !< For regional subaxis add the NumFilesInSet attribute, which is added by fms2_io for (other)
147  !< domains with sufficient decomposition info. Note mppnccombine will work with an entry of zero.
148  call register_global_attribute(fileobjnd, "NumFilesInSet", 0)
149  endif
150  fnum_domain = "nd" ! no domain
151  if (file_unit < 0) file_unit = 10
152  endiF
153  ELSE IF (domainu .NE. null_domainug) THEN
154  fileob => fileobju
155  if (.not.check_if_open(fileob)) call open_check(open_file(fileobju, trim(file_name)//".nc", "overwrite", &
156  domainu, is_restart=.false.))
157  fnum_domain = "ug" ! unstructured grid
158  file_unit=3
159  ELSE
160  fileob => fileobjnd
161  allocate(current_pelist(mpp_npes()))
162  call mpp_get_current_pelist(current_pelist)
163  if (.not.check_if_open(fileob)) then
164  call open_check(open_file(fileobjnd, trim(file_name)//".nc", "overwrite", &
165  pelist=current_pelist, is_restart=.false.))
166  endif
167  fnum_domain = "nd" ! no domain
168  if (file_unit < 0) file_unit = 10
169  deallocate(current_pelist)
170  END IF
171 
172  !---- write global attributes ----
173  IF ( file_title(1:1) /= ' ' ) THEN
174  call register_global_attribute(fileob, 'title', trim(file_title), str_len=len_trim(file_title))
175  END IF
176 
177  IF ( PRESENT(attributes) ) THEN
178  DO i=1, SIZE(attributes)
179  SELECT CASE (attributes(i)%type)
180  CASE (nf90_int)
181  call register_global_attribute(fileob, trim(attributes(i)%name), attributes(i)%iatt)
182  CASE (nf90_float)
183 
184  call register_global_attribute(fileob, trim(attributes(i)%name), attributes(i)%fatt)
185  CASE (nf90_char)
186 
187  call register_global_attribute(fileob, trim(attributes(i)%name), attributes(i)%catt, &
188  & str_len=len_trim(attributes(i)%catt))
189  CASE default
190  ! <ERROR STATUS="FATAL">
191  ! Unknown attribute type for attribute <name> to module/input_field <module_name>/<field_name>.
192  ! Contact the developers.
193  ! </ERROR>
194  CALL error_mesg('diag_output_mod::diag_output_init', 'Unknown attribute type for global attribute "'&
195  &//trim(attributes(i)%name)//'" in file "'//trim(file_name)//'". Contact the developers.', fatal)
196  END SELECT
197  END DO
198  END IF
199  !---- write grid type (mosaic or regular)
200  CALL get_diag_global_att(gatt)
201 
202  call register_global_attribute(fileob, 'grid_type', trim(gatt%grid_type), str_len=len_trim(gatt%grid_type))
203 
204  call register_global_attribute(fileob, 'grid_tile', trim(gatt%tile_name), str_len=len_trim(gatt%tile_name))
205 
206  END SUBROUTINE diag_output_init
207 
208  !> @brief Write the axis meta data to file.
209  SUBROUTINE write_axis_meta_data(file_unit, axes, fileob, time_ops, time_axis_registered)
210  INTEGER, INTENT(in) :: file_unit !< File unit number
211  INTEGER, INTENT(in) :: axes(:) !< Array of axis ID's, including the time axis
212  class(fmsnetcdffile_t) , intent(inout) :: fileob !< FMS2_io fileobj
213  LOGICAL, INTENT(in), OPTIONAL :: time_ops !< .TRUE. if this file contains any min, max, time_rms, or time_average
214  logical, intent(inout) , optional :: time_axis_registered !< .TRUE. if the time axis was already
215  !! written to the file
216 
217  TYPE(domain1d) :: domain
218  TYPE(domainug) :: domainu
219 
220  CHARACTER(len=mxch) :: axis_name, axis_units, axis_name_current
221  CHARACTER(len=mxchl) :: axis_long_name
222  CHARACTER(len=1) :: axis_cart_name
223  INTEGER :: axis_direction, axis_edges
224  REAL, ALLOCATABLE :: axis_data(:)
225  integer :: axis_pos
226  INTEGER :: num_attributes
227  TYPE(diag_atttype), DIMENSION(:), ALLOCATABLE :: attributes
228  INTEGER :: calendar, id_axis, id_time_axis
229  INTEGER :: i, j, index, num, length, edges_index
230  INTEGER :: gend !< End index of global io_domain
231  LOGICAL :: time_ops1
232  CHARACTER(len=2048) :: err_msg
233  integer :: id_axis_current
234  logical :: is_time_axis_registered
235  integer :: istart, iend
236  integer :: gstart, cstart, cend !< Start and end of global and compute domains
237  integer :: clength !< Length of compute domain
238  character(len=32) :: type_str !< Str indicating the type of the axis data
239 
240  ! Make sure err_msg is initialized
241  err_msg = ''
242  IF ( PRESENT(time_ops) ) THEN
243  time_ops1 = time_ops
244  ELSE
245  time_ops1 = .false.
246  END IF
247  if (present(time_axis_registered)) then
248  is_time_axis_registered = time_axis_registered
249  else
250  is_time_axis_registered = .false.
251  endif
252  !---- save the current file_unit ----
253  IF ( num_axis_in_file == 0 ) current_file_unit = file_unit
254 
255  !---- dummy checks ----
256  num = SIZE(axes(:))
257  ! <ERROR STATUS="FATAL">number of axes < 1 </ERROR>
258  IF ( num < 1 ) CALL error_mesg('write_axis_meta_data', 'number of axes < 1.', fatal)
259 
260  ! <ERROR STATUS="FATAL">writing meta data out-of-order to different files.</ERROR>
261  IF ( file_unit /= current_file_unit ) CALL error_mesg('write_axis_meta_data',&
262  & 'writing meta data out-of-order to different files.', fatal)
263 
264  IF (pack_size .eq. 1) then
265  type_str = "double"
266  ELSE IF (pack_size .eq. 2) then
267  type_str = "float"
268  ENDIF
269 
270  !---- check all axes ----
271  !---- write axis meta data for new axes ----
272  DO i = 1, num
273  id_axis = axes(i)
274  index = get_axis_index( id_axis )
275 
276  !---- skip axes already written -----
277  IF ( index > 0 ) cycle
278 
279  !---- create new axistype (then point to) -----
280  num_axis_in_file = num_axis_in_file + 1
281  axis_in_file(num_axis_in_file) = id_axis
282  edge_axis_flag(num_axis_in_file) = .false.
283  length = get_axis_global_length(id_axis)
284  ALLOCATE(axis_data(length))
285 
286  CALL get_diag_axis(id_axis, axis_name, axis_units, axis_long_name,&
287  & axis_cart_name, axis_direction, axis_edges, domain, domainu, axis_data,&
288  & num_attributes, attributes, domain_position=axis_pos)
289 
290  IF ( domain .NE. null_domain1d ) THEN
291  select type (fileob)
292  type is (fmsnetcdffile_t)
293  !> If the axis is domain decomposed and the type is FmsNetcdfFile_t, this is regional diagnostic
294  !! So treat it as any other dimension
295  call mpp_get_global_domain(domain, begin=gstart, end=gend) !< Get the global indicies
296  call mpp_get_compute_domain(domain, begin=cstart, end=cend, size=clength) !< Get the compute indicies
297  iend = cend - gstart + 1 !< Get the array indicies for the axis data
298  istart = cstart - gstart + 1
299  call register_axis(fileob, axis_name, dimension_length=clength)
300  call register_field(fileob, axis_name, type_str, (/axis_name/) )
301 
302  !> For regional subaxis add the "domain_decomposition" attribute, which is added
303  !> fms2_io for (other) domains with sufficient decomposition info.
304  call register_variable_attribute(fileob, axis_name, "domain_decomposition", &
305  (/gstart, gend, cstart, cend/))
306  type is (fmsnetcdfdomainfile_t)
307  !> If the axis is domain decomposed and the type is FmsNetcdfDomainFile_t,
308  !! this is a domain decomposed dimension
309  !! so register it as one
310  call register_axis(fileob, axis_name, lowercase(trim(axis_cart_name)), domain_position=axis_pos )
311  call get_global_io_domain_indices(fileob, trim(axis_name), istart, iend)
312  call register_field(fileob, axis_name, type_str, (/axis_name/) )
313  end select
314 
315  ELSE IF ( domainu .NE. null_domainug) THEN
316  select type(fileob)
317  type is (fmsnetcdfunstructureddomainfile_t)
318  !> If the axis is in unstructured domain and the type is FmsNetcdfUnstructuredDomainFile_t,
319  !! this is an unstrucutred axis
320  !! so register it as one
321  call register_axis(fileob, axis_name )
322  end select
323  call register_field(fileob, axis_name, type_str, (/axis_name/) )
324  istart = lbound(axis_data,1)
325  iend = ubound(axis_data,1)
326  ELSE
327  !> If the axis is not in a domain, register it as a normal dimension
328  call register_axis(fileob, axis_name, dimension_length=size(axis_data))
329  call register_field(fileob, axis_name, type_str, (/axis_name/) )
330  istart = lbound(axis_data,1)
331  iend = ubound(axis_data,1)
332  ENDIF !! IF ( Domain .NE. null_domain1d )
333 
334  if (length <= 0) then
335  !> @note Check if the time variable is registered. It's possible that is_time_axis_registered
336  !! is set to true if using
337  !! time-templated files because they aren't closed when done writing. An alternative
338  !! to this set up would be to put
339  !! variable_exists into the if statement with an .or. so that it gets registered.
340  is_time_axis_registered = variable_exists(fileob,trim(axis_name),.true.)
341  if (.not. is_time_axis_registered) then
342  call register_axis(fileob, trim(axis_name), unlimited )
343  call register_field(fileob, axis_name, type_str, (/axis_name/) )
344  is_time_axis_registered = .true.
345  if (present(time_axis_registered)) time_axis_registered = is_time_axis_registered
346  endif !! if (.not. is_time_axis_registered)
347  endif !! if (length <= 0)
348 
349  !> Add the attributes
350  if(trim(axis_units) .ne. "none") call register_variable_attribute(fileob, axis_name, "units", &
351  & trim(axis_units), str_len=len_trim(axis_units))
352  call register_variable_attribute(fileob, axis_name, "long_name", trim(axis_long_name), &
353  & str_len=len_trim(axis_long_name))
354  if(trim(axis_cart_name).ne."N") call register_variable_attribute(fileob, axis_name, "axis", &
355  & trim(axis_cart_name), str_len=len_trim(axis_cart_name))
356 
357  if (length > 0 ) then
358  !> If not a time axis, add the positive attribute and write the data
359  select case (axis_direction)
360  case (1)
361  call register_variable_attribute(fileob, axis_name, "positive", "up", str_len=len_trim("up"))
362  case (-1)
363  call register_variable_attribute(fileob, axis_name, "positive", "down", str_len=len_trim("down"))
364  end select
365  call write_data(fileob, axis_name, axis_data(istart:iend) )
366  endif
367 
368  !> Write additional axis attributes, from diag_axis_add_attribute calls
369  CALL write_attribute_meta(file_unit, num_attributes, attributes, err_msg, varname=axis_name, fileob=fileob)
370  IF ( len_trim(err_msg) .GT. 0 ) THEN
371  CALL error_mesg('diag_output_mod::write_axis_meta_data', trim(err_msg), fatal)
372  END IF
373 
374  !> Write additional attribute (calendar_type) for time axis ----
375  !> @note calendar attribute is compliant with CF convention
376  !! http://www.cgd.ucar.edu/cms/eaton/netcdf/CF-current.htm#cal
377  IF ( axis_cart_name == 'T' ) THEN
378  time_axis_flag(num_axis_in_file) = .true.
379  id_time_axis = num_axis_in_file
380  calendar = get_calendar_type()
381 
382 
383  call register_variable_attribute(fileob, axis_name, "calendar_type", &
384  uppercase(trim(valid_calendar_types(calendar))), &
385  & str_len=len_trim(valid_calendar_types(calendar)) )
386  call register_variable_attribute(fileob, axis_name, "calendar", &
387  lowercase(trim(valid_calendar_types(calendar))), &
388  & str_len=len_trim(valid_calendar_types(calendar)) )
389  IF ( time_ops1 ) THEN
390  call register_variable_attribute(fileob, axis_name, 'bounds', trim(axis_name)//'_bnds', &
391  & str_len=len_trim(trim(axis_name)//'_bnds'))
392  END IF
393  call set_fileobj_time_name(fileob, axis_name)
394  ELSE
395  time_axis_flag(num_axis_in_file) = .false.
396  END IF
397 
398  DEALLOCATE(axis_data)
399 
400  !> Deallocate attributes
401  IF ( ALLOCATED(attributes) ) THEN
402  DO j=1, num_attributes
403  IF ( allocated(attributes(j)%fatt ) ) THEN
404  DEALLOCATE(attributes(j)%fatt)
405  END IF
406  IF ( allocated(attributes(j)%iatt ) ) THEN
407  DEALLOCATE(attributes(j)%iatt)
408  END IF
409  END DO
410  DEALLOCATE(attributes)
411  END IF
412 
413  !------------- write axis containing edge information ---------------
414 
415  ! --- this axis has no edges -----
416  IF ( axis_edges <= 0 ) cycle
417 
418  ! --- was this axis edge previously defined? ---
419  id_axis_current = id_axis
420  axis_name_current = axis_name
421  id_axis = axis_edges
422  edges_index = get_axis_index(id_axis)
423  IF ( edges_index > 0 ) cycle
424 
425  ! ---- get data for axis edges ----
426  length = get_axis_global_length( id_axis )
427  ALLOCATE(axis_data(length))
428  CALL get_diag_axis(id_axis, axis_name, axis_units, axis_long_name, axis_cart_name,&
429  & axis_direction, axis_edges, domain, domainu, axis_data)
430 
431  ! ---- write edges attribute to original axis ----
432  call register_variable_attribute(fileob, axis_name_current, "edges",trim(axis_name), &
433  & str_len=len_trim(axis_name))
434  ! ---- add edges index to axis list ----
435  ! ---- assume this is not a time axis ----
436  num_axis_in_file = num_axis_in_file + 1
437  axis_in_file(num_axis_in_file) = id_axis
438  edge_axis_flag(num_axis_in_file) = .true.
439  time_axis_flag(num_axis_in_file) = .false.
440 
441 !> Add edges axis with fms2_io
442  if (.not.variable_exists(fileob, axis_name)) then
443  call register_axis(fileob, axis_name, size(axis_data) )
444  call register_field(fileob, axis_name, type_str, (/axis_name/) )
445  if(trim(axis_units) .ne. "none") call register_variable_attribute(fileob, axis_name, "units", &
446  & trim(axis_units), str_len=len_trim(axis_units))
447  call register_variable_attribute(fileob, axis_name, "long_name", trim(axis_long_name), &
448  & str_len=len_trim(axis_long_name))
449  if(trim(axis_cart_name).ne."N") call register_variable_attribute(fileob, axis_name, "axis", &
450  & trim(axis_cart_name), str_len=len_trim(axis_cart_name))
451  select case (axis_direction)
452  case (1)
453  call register_variable_attribute(fileob, axis_name, "positive", "up", str_len=len_trim("up"))
454  case (-1)
455  call register_variable_attribute(fileob, axis_name, "positive", "down", str_len=len_trim("down"))
456  end select
457  call write_data(fileob, axis_name, axis_data)
458  endif !! if (.not.variable_exists(fileob, axis_name))
459 
460  DEALLOCATE (axis_data)
461  END DO
462  END SUBROUTINE write_axis_meta_data
463 
464  !> @brief Write the field meta data to file.
465  !! @return diag_fieldtype Field
466  !! @details The meta data for the field is written to the file indicated by file_unit
467  FUNCTION write_field_meta_data ( file_unit, name, axes, units, long_name, range, pack, mval,&
468  & avg_name, time_method, standard_name, interp_method, attributes, num_attributes, &
469  & use_UGdomain, fileob) result ( Field )
470  INTEGER, INTENT(in) :: file_unit !< Output file unit number
471  INTEGER, INTENT(in) :: axes(:) !< Array of axis IDs
472  CHARACTER(len=*), INTENT(in) :: name !< Field name
473  CHARACTER(len=*), INTENT(in) :: units !< Field units
474  CHARACTER(len=*), INTENT(in) :: long_name !< Field's long name
475  REAL, OPTIONAL, INTENT(in) :: range(2) !< Valid range (min, max). If min > max, the range will be ignored
476  REAL, OPTIONAL, INTENT(in) :: mval !< Missing value, must be within valid range
477  INTEGER, OPTIONAL, INTENT(in) :: pack !< Packing flag. Only valid when range specified. Valid values:
478  !! Flag | Size
479  !! --- | ---
480  !! 1 | 64bit
481  !! 2 | 32bit
482  !! 4 | 16bit
483  !! 8 | 8bit
484  CHARACTER(len=*), OPTIONAL, INTENT(in) :: avg_name !< Name of variable containing time averaging info
485  CHARACTER(len=*), OPTIONAL, INTENT(in) :: time_method !< Name of transformation applied to the time-varying data,
486  !! i.e. "avg", "min", "max"
487  CHARACTER(len=*), OPTIONAL, INTENT(in) :: standard_name !< Standard name of field
488  CHARACTER(len=*), OPTIONAL, INTENT(in) :: interp_method
489  TYPE(diag_atttype), DIMENSION(:), allocatable, OPTIONAL, INTENT(in) :: attributes
490  INTEGER, OPTIONAL, INTENT(in) :: num_attributes
491  LOGICAL, OPTIONAL, INTENT(in) :: use_ugdomain
492 class(fmsnetcdffile_t), intent(inout) :: fileob
493 
494  logical :: is_time_bounds !< Flag indicating if the variable is time_bounds
495  CHARACTER(len=256) :: standard_name2
496  TYPE(diag_fieldtype) :: field
497  LOGICAL :: coord_present
498  CHARACTER(len=128) :: aux_axes(size(axes))
499  CHARACTER(len=160) :: coord_att
500  CHARACTER(len=1024) :: err_msg
501 
502 character(len=128),dimension(size(axes)) :: axis_names
503  REAL :: scale, add
504  INTEGER :: i, indexx, num, ipack, np
505  LOGICAL :: use_range
506  INTEGER :: axis_indices(size(axes))
507  logical :: use_ugdomain_local
508  !---- Initialize err_msg to bank ----
509  err_msg = ''
510 
511  !---- dummy checks ----
512  coord_present = .false.
513  IF( PRESENT(standard_name) ) THEN
514  standard_name2 = standard_name
515  ELSE
516  standard_name2 = 'none'
517  END IF
518 
519  use_ugdomain_local = .false.
520  if(present(use_ugdomain)) use_ugdomain_local = use_ugdomain
521 
522  num = SIZE(axes(:))
523  ! <ERROR STATUS="FATAL">number of axes < 1</ERROR>
524  IF ( num < 1 ) CALL error_mesg ( 'write_meta_data', 'number of axes < 1', fatal)
525  ! <ERROR STATUS="FATAL">writing meta data out-of-order to different files</ERROR>
526  IF ( file_unit /= current_file_unit ) CALL error_mesg ( 'write_meta_data', &
527  & 'writing meta data out-of-order to different files', fatal)
528 
529  IF (trim(name) .eq. "time_bnds") then
530  is_time_bounds = .true.
531  ELSE
532  is_time_bounds = .false.
533  ENDIF
534 
535  !---- check all axes for this field ----
536  !---- set up indexing to axistypes ----
537  DO i = 1, num
538  indexx = get_axis_index(axes(i))
539  !---- point to existing axistype -----
540  IF ( indexx > 0 ) THEN
541  axis_indices(i) = indexx
542  ELSE
543  ! <ERROR STATUS="FATAL">axis data not written for field</ERROR>
544  CALL error_mesg ('write_field_meta_data',&
545  & 'axis data not written for field '//trim(name), fatal)
546  END IF
547  !Get the axes names
548  call get_diag_axis_name(axes(i),axis_names(i))
549  END DO
550 
551  ! Create coordinate attribute
552  IF ( num >= 2 .OR. (num==1 .and. use_ugdomain_local) ) THEN
553  coord_att = ' '
554  DO i = 1, num
555  aux_axes(i) = get_axis_aux(axes(i))
556  IF( trim(aux_axes(i)) /= 'none' ) THEN
557  IF(len_trim(coord_att) == 0) THEN
558  coord_att = trim(aux_axes(i))
559  ELSE
560  coord_att = trim(coord_att)// ' '//trim(aux_axes(i))
561  ENDIF
562  coord_present = .true.
563  END IF
564  END DO
565  END IF
566 
567  !--------------------- write field meta data ---------------------------
568 
569  !---- select packing? ----
570  !(packing option only valid with range option)
571  IF ( PRESENT(pack) ) THEN
572  ipack = pack
573  ELSE
574  ipack = 2
575  END IF
576 
577  !---- check range ----
578  use_range = .false.
579  add = 0.0
580  scale = 1.0
581  IF ( PRESENT(range) ) THEN
582  IF ( range(2) > range(1) ) THEN
583  use_range = .true.
584  !---- set packing parameters ----
585  IF ( ipack > 2 ) THEN
586  np = ipack/4
587  add = 0.5*(range(1)+range(2))
588  scale = (range(2)-range(1)) / real(max_range(2,np)-max_range(1,np))
589  END IF
590  END IF
591  END IF
592 
593  !---- select packing? ----
594  IF ( PRESENT(mval) ) THEN
595  field%miss = mval
596  field%miss_present = .true.
597  IF ( ipack > 2 ) THEN
598  np = ipack/4
599  field%miss_pack = real(missval(np))*scale+add
600  field%miss_pack_present = .true.
601  ELSE
602  field%miss_pack = mval
603  field%miss_pack_present = .false.
604  END IF
605  ELSE
606  field%miss_present = .false.
607  field%miss_pack_present = .false.
608  END IF
609 
610  !< Save the fieldname in the diag_fieldtype, so it can be used later
611  field%fieldname = name
612 
613  if (.not. variable_exists(fileob,name)) then
614  ! ipack Valid values:
615  ! 1 = 64bit </LI>
616  ! 2 = 32bit </LI>
617  ! 4 = 16bit </LI>
618  ! 8 = 8bit </LI>
619  select case (ipack)
620  case (1)
621  call register_field(fileob,name,"double",axis_names)
622  !< Don't write the _FillValue, missing_value if the variable is
623  !time_bounds to be cf compliant
624  if (.not. is_time_bounds) then
625  IF ( field%miss_present ) THEN
626  call register_variable_attribute(fileob,name,"_FillValue",real(field%miss_pack,8))
627  call register_variable_attribute(fileob,name,"missing_value",real(field%miss_pack,8))
628  ELSE
629  call register_variable_attribute(fileob,name,"_FillValue",real(cmor_missing_value,8))
630  call register_variable_attribute(fileob,name,"missing_value",real(cmor_missing_value,8))
631  ENDIF
632  IF ( use_range ) then
633  call register_variable_attribute(fileob,name,"valid_range", real(range,8))
634  ENDIF
635  endif !< if (.not. is_time_bounds)
636  case (2) !default
637  call register_field(fileob,name,"float",axis_names)
638  !< Don't write the _FillValue, missing_value if the variable is
639  !time_bounds to be cf compliant
640  if (.not. is_time_bounds) then
641  IF ( field%miss_present ) THEN
642  call register_variable_attribute(fileob,name,"_FillValue",real(field%miss_pack,4))
643  call register_variable_attribute(fileob,name,"missing_value",real(field%miss_pack,4))
644  ELSE
645  call register_variable_attribute(fileob,name,"_FillValue",real(cmor_missing_value,4))
646  call register_variable_attribute(fileob,name,"missing_value",real(cmor_missing_value,4))
647  ENDIF
648  IF ( use_range ) then
649  call register_variable_attribute(fileob,name,"valid_range", real(range,4))
650  ENDIF
651  endif !< if (.not. is_time_bounds)
652  case default
653  CALL error_mesg('diag_output_mod::write_field_meta_data',&
654  &"Pack values must be 1 or 2. Contact the developers.", fatal)
655  end select
656  if (trim(units) .ne. "none") call register_variable_attribute(fileob,name,"units",trim(units), &
657  & str_len=len_trim(units))
658  call register_variable_attribute(fileob,name,"long_name",long_name, str_len=len_trim(long_name))
659  IF (present(time_method) ) then
660  call register_variable_attribute(fileob,name,'cell_methods','time: '//trim(time_method), &
661  & str_len=len_trim('time: '//trim(time_method)))
662  ENDIF
663  endif
664  !---- write user defined attributes -----
665  IF ( PRESENT(num_attributes) ) THEN
666  IF ( PRESENT(attributes) ) THEN
667  IF ( num_attributes .GT. 0 .AND. allocated(attributes) ) THEN
668  CALL write_attribute_meta(file_unit, num_attributes, attributes, time_method, err_msg, &
669  & fileob=fileob, varname=name)
670  IF ( len_trim(err_msg) .GT. 0 ) THEN
671  CALL error_mesg('diag_output_mod::write_field_meta_data',&
672  & trim(err_msg)//" Contact the developers.", fatal)
673  END IF
674  ELSE
675  ! Catch some bad cases
676  IF ( num_attributes .GT. 0 .AND. .NOT.allocated(attributes) ) THEN
677  CALL error_mesg('diag_output_mod::write_field_meta_data',&
678  & 'num_attributes > 0 but attributes is not allocated for attribute '&
679  &//trim(attributes(i)%name)//' for field '//trim(name)//'. Contact the developers.', fatal)
680  ELSE IF ( num_attributes .EQ. 0 .AND. allocated(attributes) ) THEN
681  CALL error_mesg('diag_output_mod::write_field_meta_data',&
682  & 'num_attributes == 0 but attributes is allocated for attribute '&
683  &//trim(attributes(i)%name)//' for field '//trim(name)//'. Contact the developers.', fatal)
684  END IF
685  END IF
686  ELSE
687  ! More edge error cases
688  CALL error_mesg('diag_output_mod::write_field_meta_data',&
689  & 'num_attributes present but attributes missing for attribute '&
690  &//trim(attributes(i)%name)//' for field '//trim(name)//'. Contact the developers.', fatal)
691  END IF
692  ELSE IF ( PRESENT(attributes) ) THEN
693  CALL error_mesg('diag_output_mod::write_field_meta_data',&
694  & 'attributes present but num_attributes missing for attribute '&
695  &//trim(attributes(i)%name)//' for field '//trim(name)//'. Contact the developers.', fatal)
696  END IF
697 
698  !---- write additional attribute for time averaging -----
699  IF ( PRESENT(avg_name) ) THEN
700  IF ( avg_name(1:1) /= ' ' ) THEN
701  call register_variable_attribute(fileob,name,'time_avg_info',&
702  & trim(avg_name)//'_T1,'//trim(avg_name)//'_T2,'//trim(avg_name)//'_DT', &
703  & str_len=len_trim(trim(avg_name)//'_T1,'//trim(avg_name)//'_T2,'//trim(avg_name)//'_DT'))
704  END IF
705  END IF
706 
707  ! write coordinates attribute for CF compliance
708  IF ( coord_present ) then
709  call register_variable_attribute(fileob,name,'coordinates',trim(coord_att), str_len=len_trim(coord_att))
710  ENDIF
711  IF ( trim(standard_name2) /= 'none' ) then
712  call register_variable_attribute(fileob,name,'standard_name',trim(standard_name2), &
713  & str_len=len_trim(standard_name2))
714  ENDIF
715  !---- write attribute for interp_method ----
716  IF( PRESENT(interp_method) ) THEN
717  call register_variable_attribute(fileob,name,'interp_method', trim(interp_method), &
718  & str_len=len_trim(interp_method))
719  END IF
720 
721  !---- get axis domain ----
722  field%Domain = get_domain2d( axes )
723  field%tile_count = get_tile_count( axes )
724  field%DomainU = get_domainug( axes(1) )
725 
726  END FUNCTION write_field_meta_data
727 
728  !> \brief Write out attribute meta data to file
729  !!
730  !! Write out the attribute meta data to file, for field and axes
731  SUBROUTINE write_attribute_meta(file_unit, num_attributes, attributes, time_method, err_msg, varname, fileob)
732  INTEGER, INTENT(in) :: file_unit !< File unit number
733  INTEGER, INTENT(in) :: num_attributes !< Number of attributes to write
734  TYPE(diag_atttype), DIMENSION(:), INTENT(in) :: attributes !< Array of attributes
735  CHARACTER(len=*), INTENT(in), OPTIONAL :: time_method !< To include in cell_methods attribute if present
736  CHARACTER(len=*), INTENT(out), OPTIONAL :: err_msg !< Return error message
737  CHARACTER(len=*), INTENT(IN), OPTIONAL :: varname !< The name of the variable
738  class(fmsnetcdffile_t), intent(inout) :: fileob !< FMS2_io fileobj
739 
740  INTEGER :: i, att_len
741  CHARACTER(len=1280) :: att_str
742 
743  ! Clear err_msg if present
744  IF ( PRESENT(err_msg) ) err_msg = ''
745 
746  DO i = 1, num_attributes
747  SELECT CASE (attributes(i)%type)
748  CASE (nf90_int)
749  IF ( .NOT.allocated(attributes(i)%iatt) ) THEN
750  IF ( fms_error_handler('diag_output_mod::write_attribute_meta',&
751  & 'Integer attribute type indicated, but array not allocated for attribute '&
752  &//trim(attributes(i)%name)//'.', err_msg) ) THEN
753  RETURN
754  END IF
755  END IF
756  if (present(varname))call register_variable_attribute(fileob, varname,trim(attributes(i)%name), &
757  & attributes(i)%iatt)
758  CASE (nf90_float)
759  IF ( .NOT.allocated(attributes(i)%fatt) ) THEN
760  IF ( fms_error_handler('diag_output_mod::write_attribute_meta',&
761  & 'Real attribute type indicated, but array not allocated for attribute '&
762  &//trim(attributes(i)%name)//'.', err_msg) ) THEN
763  RETURN
764  END IF
765  END IF
766  if (present(varname))call register_variable_attribute(fileob, varname,trim(attributes(i)%name), &
767  & real(attributes(i)%fatt,4) )
768  CASE (nf90_char)
769  att_str = attributes(i)%catt
770  att_len = attributes(i)%len
771  IF ( trim(attributes(i)%name).EQ.'cell_methods' .AND. PRESENT(time_method) ) THEN
772  ! Append ",time: time_method" if time_method present
773  att_str = attributes(i)%catt(1:attributes(i)%len)//' time: '//time_method
774  att_len = len_trim(att_str)
775  END IF
776  if (present(varname))&
777  call register_variable_attribute(fileob, varname,trim(attributes(i)%name) , att_str(1:att_len), &
778  & str_len=att_len)
779 
780  CASE default
781  IF ( fms_error_handler('diag_output_mod::write_attribute_meta', 'Invalid type for attribute '&
782  &//trim(attributes(i)%name)//'.', err_msg) ) THEN
783  RETURN
784  END IF
785  END SELECT
786  END DO
787  END SUBROUTINE write_attribute_meta
788 
789  !> @brief Writes axis data to file.
790  !! @details Writes axis data to file. This subroutine is to be called once per file
791  !! after all <TT>write_meta_data</TT> calls, and before the first
792  !! <TT>diag_field_out</TT> call.
793  SUBROUTINE done_meta_data(file_unit)
794  INTEGER, INTENT(in) :: file_unit !< Output file unit number
795 
796  !---- write data for all non-time axes ----
797  num_axis_in_file = 0
798  END SUBROUTINE done_meta_data
799 
800  !> \brief Writes diagnostic data out using fms2_io routine.
801  subroutine diag_field_write (varname, buffer, static, file_num, fileobjU, fileobj, fileobjND, &
802  & fnum_for_domain, time_in)
803  CHARACTER(len=*), INTENT(in) :: varname !< Variable name
804  REAL , INTENT(inout) :: buffer(:,:,:,:) !< Buffer containing the variable data
805  logical, intent(in) :: static !< Flag indicating if a variable is static
806  integer, intent(in) :: file_num !< Index in the fileobj* types array
807  type(fmsnetcdfunstructureddomainfile_t), intent(inout) :: fileobju(:) !< Array of non domain decomposed fileobj
808  type(fmsnetcdfdomainfile_t), intent(inout) :: fileobj(:) !< Array of domain decomposed fileobj
809  type(fmsnetcdffile_t), intent(inout) :: fileobjnd(:) !< Array of unstructured domain fileobj
810  character(len=2), intent(in) :: fnum_for_domain !< String indicating the type of domain
811  !! "2d" domain decomposed
812  !! "ug" unstructured domain decomposed
813  !! "nd" no domain
814  INTEGER, OPTIONAL, INTENT(in) :: time_in !< Time index
815 
816  integer :: time !< Time index
817  real,allocatable :: local_buffer(:,:,:,:) !< Buffer containing the data will be sent to fms2io
818 
819 !> Set up the time. Static field and default time is 0
820  if ( static ) then
821  time = 0
822  elseif (present(time_in)) then
823  time = time_in
824  else
825  time = 0
826  endif
827 
828  if (size(buffer,3) .eq. 1 .and. size(buffer,2) .eq. 1) then
829  !> If the variable is 1D, switch the buffer so that n_diurnal_samples is
830  !! the second dimension (nx, n_diurnal_samples, 1, 1)
831  allocate(local_buffer(size(buffer,1),size(buffer,4),size(buffer,2),size(buffer,3)))
832  local_buffer(:,:,1,1) = buffer(:,1,1,:)
833  else if (size(buffer,3) .eq. 1) then
834  !> If the variable is 2D, switch the n_diurnal_samples and nz dimension, so local_buffer has
835  !! dimension (nx, ny, n_diurnal_samples, 1).
836  allocate(local_buffer(size(buffer,1),size(buffer,2),size(buffer,4),size(buffer,3)))
837  local_buffer(:,:,:,1) = buffer(:,:,1,:)
838  else
839  allocate(local_buffer(size(buffer,1),size(buffer,2),size(buffer,3),size(buffer,4)))
840  local_buffer = buffer(:,:,:,:)
841  endif
842 
843  !> Figure out which file object to write output to
844  if (fnum_for_domain == "2d" ) then
845  if (check_if_open(fileobj(file_num))) then
846  call write_data (fileobj(file_num), trim(varname), local_buffer, unlim_dim_level=time )
847  endif
848  elseif (fnum_for_domain == "nd") then
849  if (check_if_open(fileobjnd(file_num)) ) then
850  call write_data (fileobjnd(file_num), trim(varname), local_buffer, unlim_dim_level=time)
851  endif
852  elseif (fnum_for_domain == "ug") then
853  if (check_if_open(fileobju(file_num))) then
854  call write_data (fileobju(file_num), trim(varname), local_buffer, unlim_dim_level=time)
855  endif
856  else
857  call error_mesg("diag_field_write","fnum_for_domain must be '2d', 'nd', or 'ug'",fatal)
858  endif
859 
860  deallocate(local_buffer)
861  end subroutine diag_field_write
862 
863 !> \brief Writes the time data to the history file
864  subroutine diag_write_time (fileob,rtime_value,time_index,time_name)
865  class(fmsnetcdffile_t), intent(inout) :: fileob !< fms2_io file object
866  real, intent(in) :: rtime_value !< The value of time to be written
867  integer, intent(in) :: time_index !< The index of the time variable
868  character(len=*), intent(in), optional :: time_name !< The name of the time variable
869  character(len=:),allocatable :: name_time !< The name of the time variable
870 
871 !> Get the name of the time variable
872  if (present(time_name)) then
873  allocate(character(len=len(time_name)) :: name_time)
874  name_time = time_name
875  else
876  allocate(character(len=4) :: name_time)
877  name_time = "time"
878  endif
879 !> Write the time data
880  call write_data (fileob, trim(name_time), rtime_value, unlim_dim_level=time_index)
881 !> Cleanup
882  if (allocated(name_time)) deallocate(name_time)
883  end subroutine diag_write_time
884 
885  !> @brief Return the axis index number.
886  !! @return Integer index
887  FUNCTION get_axis_index(num) RESULT ( index )
888  INTEGER, INTENT(in) :: num
889 
890  INTEGER :: index
891  INTEGER :: i
892 
893  !---- get the array index for this axis type ----
894  !---- set up pointers to axistypes ----
895  !---- write axis meta data for new axes ----
896  index = 0
897  DO i = 1, num_axis_in_file
898  IF ( num == axis_in_file(i) ) THEN
899  index = i
900  EXIT
901  END IF
902  END DO
903  END FUNCTION get_axis_index
904 
905  !> @brief Return the global attribute type.
906  SUBROUTINE get_diag_global_att(gAtt)
907  TYPE(diag_global_att_type), INTENT(out) :: gatt
908 
909  gatt=diag_global_att
910  END SUBROUTINE get_diag_global_att
911 
912  !> @brief Set the global attribute type.
913  SUBROUTINE set_diag_global_att(component, gridType, tileName)
914  CHARACTER(len=*),INTENT(in) :: component, gridtype, tilename
915 
916  ! The following two lines are set to remove compile time warnings
917  ! about 'only used once'.
918  CHARACTER(len=64) :: component_tmp
919  component_tmp = component
920  ! Don't know how to set these for specific component
921  ! Want to be able to say
922  ! if(output_file has component) then
923  diag_global_att%grid_type = gridtype
924  diag_global_att%tile_name = tilename
925  ! endif
926  END SUBROUTINE set_diag_global_att
927 
928  !> @brief Flushes the file into disk
929  subroutine diag_flush(file_num, fileobjU, fileobj, fileobjND, fnum_for_domain)
930  integer, intent(in) :: file_num !< Index in the fileobj* types array
931  type(fmsnetcdfunstructureddomainfile_t),intent(inout) :: fileobju(:) !< Array of non domain decomposed fileobj
932  type(fmsnetcdfdomainfile_t), intent(inout) :: fileobj(:) !< Array of domain decomposed fileobj
933  type(fmsnetcdffile_t), intent(inout) :: fileobjnd(:) !< Array of unstructured domain fileobj
934  character(len=2), intent(in) :: fnum_for_domain !< String indicating the type of domain
935  !! "2d" domain decomposed
936  !! "ug" unstructured domain decomposed
937  !! "nd" no domain
938  if (fnum_for_domain == "2d" ) then
939  call flush_file (fileobj(file_num))
940  elseif (fnum_for_domain == "nd") then
941  call flush_file (fileobjnd(file_num))
942  elseif (fnum_for_domain == "ug") then
943  call flush_file (fileobju(file_num))
944  else
945  call error_mesg("diag_field_write","No file object is associated with this file number",fatal)
946  endif
947  end subroutine diag_flush
948 END MODULE diag_output_mod
949 !> @}
950 ! close documentation grouping
integer function, public get_axis_length(id)
Return the length of the axis.
Definition: diag_axis.F90:611
subroutine, public get_diag_axis(id, name, units, long_name, cart_name, direction, edges, Domain, DomainU, array_data, num_attributes, attributes, domain_position)
Return information about the axis with index ID.
Definition: diag_axis.F90:467
integer function, public get_axis_global_length(id)
Return the global length of the axis.
Definition: diag_axis.F90:649
type(domain2d) function, public get_domain2d(ids)
Return the 2D domain for the axis IDs given.
Definition: diag_axis.F90:698
type(domainug) function, public get_domainug(id)
Retrun the 1D domain for the axis ID given.
Definition: diag_axis.F90:730
integer function, public get_tile_count(ids)
Return the tile count for the axis.
Definition: diag_axis.F90:658
subroutine, public get_diag_axis_name(id, axis_name)
Return the short name of the axis.
Definition: diag_axis.F90:588
character(len=128) function, public get_axis_aux(id)
Return the auxiliary name for the axis.
Definition: diag_axis.F90:631
integer function, public diag_axis_init(name, array_data, units, cart_name, long_name, direction, set_name, edges, Domain, Domain2, DomainU, aux, req, tile_count, domain_position)
Initialize the axis, and return the axis ID.
Definition: diag_axis.F90:110
type(domain1d) function, public get_domain1d(id)
Retrun the 1D domain for the axis ID given.
Definition: diag_axis.F90:685
integer pack_size
1 for double and 2 for float
Definition: diag_data.F90:408
real(r8_kind), parameter cmor_missing_value
CMOR standard missing value.
Definition: diag_data.F90:111
Attribute type for diagnostic fields.
Definition: diag_data.F90:157
Diagnostic field type.
Definition: diag_data.F90:146
subroutine, public diag_field_write(varname, buffer, static, file_num, fileobjU, fileobj, fileobjND, fnum_for_domain, time_in)
Writes diagnostic data out using fms2_io routine.
subroutine, public diag_output_init(file_name, file_title, file_unit, domain, domainU, fileobj, fileobjU, fileobjND, fnum_domain, attributes)
Opens the output file.
Definition: diag_output.F90:89
subroutine, public get_diag_global_att(gAtt)
Return the global attribute type.
subroutine, public set_diag_global_att(component, gridType, tileName)
Set the global attribute type.
subroutine, public diag_flush(file_num, fileobjU, fileobj, fileobjND, fnum_for_domain)
Flushes the file into disk.
integer function get_axis_index(num)
Return the axis index number.
subroutine write_attribute_meta(file_unit, num_attributes, attributes, time_method, err_msg, varname, fileob)
Write out attribute meta data to file.
subroutine, public done_meta_data(file_unit)
Writes axis data to file.
subroutine, public diag_write_time(fileob, rtime_value, time_index, time_name)
Writes the time data to the history file.
subroutine, public write_axis_meta_data(file_unit, axes, fileob, time_ops, time_axis_registered)
Write the axis meta data to file.
type(diag_fieldtype) function, public write_field_meta_data(file_unit, name, axes, units, long_name, range, pack, mval, avg_name, time_method, standard_name, interp_method, attributes, num_attributes, use_UGdomain, fileob)
Write the field meta data to file.
Opens a given netcdf or domain file.
Definition: fms2_io.F90:121
subroutine, public write_version_number(version, tag, unit)
Prints to the log file (or a specified unit) the version id string and tag name.
Definition: fms.F90:701
logical function, public fms_error_handler(routine, message, err_msg)
Facilitates the control of fatal error conditions.
Definition: fms.F90:468
subroutine, public error_mesg(routine, message, level)
Print notes, warnings and error messages; terminates program for warning and error messages....
Definition: fms.F90:441
type(domain2d) function, pointer mpp_get_io_domain(domain)
Set user stack size.
Set up a domain decomposition.
These routines retrieve the axis specifications associated with the compute domains....
Retrieve the entire array of compute domain extents associated with a decomposition.
These routines retrieve the axis specifications associated with the global domains....
Retrieve layout associated with a domain decomposition The 1D version of this call returns the number...
Retrieve list of PEs associated with a domain decomposition. The 1D version of this call returns an a...
One dimensional domain used to manage shared data access between pes.
The domain2D type contains all the necessary information to define the global, compute and data domai...
Domain information for managing data on unstructured grids.
integer function mpp_npes()
Returns processor count for current pelist.
Definition: mpp_util.inc:420
integer function mpp_pe()
Returns processor ID.
Definition: mpp_util.inc:406
Gather data sent from pelist onto the root pe Wrapper for MPI_gather, can be used with and without in...
Definition: mpp.F90:697
character(len=24) function, public valid_calendar_types(ncal, err_msg)
Returns a character string that describes the calendar type corresponding to the input integer.
integer function, public get_calendar_type()
Returns default calendar type for mapping from time to date.