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