FV3 Bundle
diag_util.F90
Go to the documentation of this file.
1 !***********************************************************************
2 !* GNU Lesser General Public License
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* FMS is free software: you can redistribute it and/or modify it under
7 !* the terms of the GNU Lesser General Public License as published by
8 !* the Free Software Foundation, either version 3 of the License, or (at
9 !* your option) any later version.
10 !*
11 !* FMS is distributed in the hope that it will be useful, but WITHOUT
12 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 !* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 !* for more details.
15 !*
16 !* You should have received a copy of the GNU Lesser General Public
17 !* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
18 !***********************************************************************
19 
21 #include <fms_platform.h>
22  ! <CONTACT EMAIL="seth.underwood@noaa.gov">
23  ! Seth Underwood
24  ! </CONTACT>
25  ! <HISTORY SRC="http://cobweb.gfdl.noaa.gov/fms-cgi-bin/viewcvs/FMS/shared/diag_manager/"/>
26 
27  ! <OVERVIEW>
28  ! Functions and subroutines necessary for the <TT>diag_manager_mod</TT>.
29  ! </OVERVIEW>
30 
31  ! <DESCRIPTION>
32  ! <TT>diag_util_mod</TT> is a set of Fortran functions and subroutines used by the <TT>diag_manager_mod</TT>.
33  ! </DESCRIPTION>
34 
35  ! <INFO>
36  ! <FUTURE>
37  ! Make an interface <TT>check_bounds_are_exact</TT> for the subroutines <TT>check_bounds_are_exact_static</TT> and
38  ! <TT>check_bounds_are_exact_dynamic</TT>.
39  ! <PRE>
40  ! INTERFACE check_bounds_are_exact
41  ! MODULE PROCEDURE check_bounds_are_exact_static
42  ! MODULE PROCEDURE check_bounds_are_exact_dynamic
43  ! END INTERFACE check_bounds_are_exact
44  ! </PRE>
45  ! </FUTURE>
46  ! </INFO>
64  USE fms_mod, ONLY: error_mesg, fatal, warning, note, mpp_pe, mpp_root_pe, lowercase, fms_error_handler,&
65  & write_version_number, do_cf_compliance
68  & OPERATOR(.NE.), OPERATOR(.EQ.), mpp_modify_domain, mpp_get_domain_components,&
69  & mpp_get_ntile_count, mpp_get_current_ntile, mpp_get_tile_id, mpp_mosaic_defined, mpp_get_tile_npes,&
71  USE time_manager_mod,ONLY: time_type, OPERATOR(==), OPERATOR(>), no_calendar, increment_date,&
73  & OPERATOR(<), OPERATOR(>=), OPERATOR(<=), OPERATOR(==)
74  USE mpp_io_mod, ONLY: mpp_close
75  USE mpp_mod, ONLY: mpp_npes
78 
79 #ifdef use_netCDF
80  USE netcdf, ONLY: nf90_char
81 #endif
82 
83  IMPLICIT NONE
84  PRIVATE
90 
91  ! <INTERFACE NAME="prepend_attribute">
92  ! <OVERVIEW>
93  ! prepend a value to a string attribute in the output field or output file
94  ! </OVERVIEW>
95  ! <TEMPLATE>
96  ! SUBROUTINE prepend_attribute(output_field, att_name, att_value)
97  ! SUBROUTINE prepend_attribute(output_file, att_name, att_value)
98  ! </TEMPLATE>
99  ! <DESCRIPTION>
100  ! Prepend a character string to a character attribute for a give field, or to a global attribute
101  ! in a give file.
102  ! </DESCRIPTION>
103  ! <IN NAME="output_field" TYPE="TYPE(output_field_type)" />
104  ! <IN NAME="output_file" TYPE="TYPE(file_type)" />
105  ! <IN NAME="att_name" TYPE="CHARACTER(len=*)" />
106  ! <IN NAME="att_value" TYPE="REAL|INTEGER|CHARACTER(len=*)" />
108  MODULE PROCEDURE prepend_attribute_field
109  MODULE PROCEDURE prepend_attribute_file
110  END INTERFACE prepend_attribute
111  ! </INTERFACE>
112 
113  ! <INTERFACE NAME="attribute_init">
114  ! <OVERVIEW>
115  ! Allocates the atttype in out_file
116  ! </OVERVIEW>
117  ! <TEMPLATE>
118  ! SUBROUTINE attribute_init(out_file, err_msg)
119  ! </TEMPLATE>
120  ! <DESCRIPTION>
121  ! Allocates memory in out_file for the attributes. Will <TT>FATAL</TT> if err_msg is not included
122  ! in the subroutine call.
123  ! </DESCRIPTION>
124  ! <INOUT NAME="out_field" TYPE="TYPE(output_field_type)">output field to allocate memory for attribute</INOUT>
125  ! <INOUT NAME="out_file" TYPE="TYPE(file_type)">output file to allocate memory for attribute</INOUT>
126  ! <OUT NAME="err_msg" TYPE="CHARACTER(len=*), OPTIONAL">Error message, passed back to calling function</OUT>
127  INTERFACE attribute_init
128  MODULE PROCEDURE attribute_init_field
129  MODULE PROCEDURE attribute_init_file
130  END INTERFACE attribute_init
131  ! </INTERFACE>
132 
133  ! Include variable "version" to be written to log file.
134 #include <file_version.h>
135 
136  LOGICAL :: module_initialized = .false.
137 
138 
139 CONTAINS
140 
141  ! <SUBROUTINE NAME="diag_util_init">
142  ! <OVERVIEW>
143  ! Write the version number of this file
144  ! </OVERVIEW>
145  ! <TEMPLATE>
146  ! SUBROUTINE diag_util_init
147  ! </TEMPLATE>
148  ! <DESCRIPTION>
149  ! Write the version number of this file to the log file.
150  ! </DESCRIPTION>
151  SUBROUTINE diag_util_init()
153  RETURN
154  END IF
155 
156  ! Write version number out to log file
157  call write_version_number("DIAG_UTIL_MOD", version)
158  END SUBROUTINE diag_util_init
159  ! </SUBROUTINE>
160 
161  ! <SUBROUTINE NAME="get_subfield_size">
162  ! <OVERVIEW>
163  ! Get the size, start, and end indices for output fields.
164  ! </OVERVIEW>
165  ! <TEMPLATE>
166  ! SUBROUTINE get_subfield_size(axes, outnum)
167  ! </TEMPLATE>
168  ! <DESCRIPTION>
169  ! Get the size, start and end indices for <TT>output_fields(outnum)</TT>, then
170  ! fill in <TT>output_fields(outnum)%output_grid%(start_indx, end_indx)</TT>
171  ! </DESCRIPTION>
172  ! <IN NAME="axes" TYPE="INTEGER, DIMENSION(:)">Axes of the <TT>input_field</TT>.</IN>
173  ! <IN NAME="outnum" TYPE="INTEGER">Position in array <TT>output_fields</TT>.</IN>
174  SUBROUTINE get_subfield_size(axes, outnum)
175  INTEGER, INTENT(in) :: axes(:) ! axes of the input_field
176  INTEGER, INTENT(in) :: outnum ! position in array output_fields
177 
178  REAL, ALLOCATABLE :: global_lat(:), global_lon(:), global_depth(:)
179  INTEGER :: global_axis_size
180  INTEGER :: i,xbegin,xend,ybegin,yend,xbegin_l,xend_l,ybegin_l,yend_l
181  CHARACTER(len=1) :: cart
182  TYPE(domain2d) :: domain2, domain2_new
183  TYPE(domain1d) :: domain1, domain1x, domain1y
184  REAL :: start(3), end(3) ! start and end coordinates in 3 axes
185  INTEGER :: gstart_indx(3), gend_indx(3) ! global start and end indices of output domain in 3 axes
186  REAL, ALLOCATABLE :: subaxis_x(:), subaxis_y(:), subaxis_z(:) !containing local coordinates in x,y,z axes
187  CHARACTER(len=128) :: msg
188  INTEGER :: ishift, jshift
189  INTEGER :: grv !< Value used to determine if the region defined in the diag_table is for the whole axis, or a sub-axis
190  CHARACTER(len=128), DIMENSION(2) :: axis_domain_name
191 
192  !initilization for local output
193  ! initially out of (lat/lon/depth) range
194  start = -1.e10
195  end = -1.e10
196  gstart_indx = -1
197  gend_indx = -1
198 
199  ! get the value to compare to determine if writing full axis data
200  IF ( region_out_use_alt_value ) THEN
201  grv = glo_reg_val_alt
202  ELSE
203  grv = glo_reg_val
204  END IF
205 
206  ! get axis data (lat, lon, depth) and indices
207  start = output_fields(outnum)%output_grid%start
208  end = output_fields(outnum)%output_grid%end
209 
210  CALL get_diag_axis_domain_name(axes(1), axis_domain_name(1))
211  CALL get_diag_axis_domain_name(axes(2), axis_domain_name(2))
212 
213  IF ( index(lowercase(axis_domain_name(1)), 'cubed') == 0 .AND. &
214  & index(lowercase(axis_domain_name(2)), 'cubed') == 0 ) THEN
215  DO i = 1, SIZE(axes(:))
216  global_axis_size = get_axis_global_length(axes(i))
217  output_fields(outnum)%output_grid%subaxes(i) = -1
218  CALL get_diag_axis_cart(axes(i), cart)
219  SELECT CASE(cart)
220  CASE ('X')
221  ! <ERROR STATUS="FATAL">wrong order of axes. X should come first.</ERROR>
222  IF( i.NE.1 ) CALL error_mesg('diag_util_mod::get_subfield_size',&
223  & 'wrong order of axes, X should come first',fatal)
224  ALLOCATE(global_lon(global_axis_size))
225  CALL get_diag_axis_data(axes(i),global_lon)
226  IF( int(start(i)) == grv .AND. int(end(i)) == grv ) THEN
227  gstart_indx(i) = 1
228  gend_indx(i) = global_axis_size
229  output_fields(outnum)%output_grid%subaxes(i) = axes(i)
230  ELSE
231  gstart_indx(i) = get_index(start(i),global_lon)
232  gend_indx(i) = get_index(end(i),global_lon)
233  END IF
234  ALLOCATE(subaxis_x(gstart_indx(i):gend_indx(i)))
235  subaxis_x=global_lon(gstart_indx(i):gend_indx(i))
236  CASE ('Y')
237  ! <ERROR STATUS="FATAL">wrong order of axes, Y should come second.</ERROR>
238  IF( i.NE.2 ) CALL error_mesg('diag_util_mod::get_subfield_size',&
239  & 'wrong order of axes, Y should come second',fatal)
240  ALLOCATE(global_lat(global_axis_size))
241  CALL get_diag_axis_data(axes(i),global_lat)
242  IF( int(start(i)) == grv .AND. int(end(i)) == grv ) THEN
243  gstart_indx(i) = 1
244  gend_indx(i) = global_axis_size
245  output_fields(outnum)%output_grid%subaxes(i) = axes(i)
246  ELSE
247  gstart_indx(i) = get_index(start(i),global_lat)
248  gend_indx(i) = get_index(end(i),global_lat)
249  END IF
250  ALLOCATE(subaxis_y(gstart_indx(i):gend_indx(i)))
251  subaxis_y=global_lat(gstart_indx(i):gend_indx(i))
252  CASE ('Z')
253  ! <ERROR STATUS="FATAL">wrong values in vertical axis of region</ERROR>
254  IF ( start(i)*end(i)<0. ) CALL error_mesg('diag_util_mod::get_subfield_size',&
255  & 'wrong values in vertical axis of region',fatal)
256  IF ( start(i)>=0. .AND. end(i)>0. ) THEN
257  ALLOCATE(global_depth(global_axis_size))
258  CALL get_diag_axis_data(axes(i),global_depth)
259  gstart_indx(i) = get_index(start(i),global_depth)
260  gend_indx(i) = get_index(end(i),global_depth)
261  ALLOCATE(subaxis_z(gstart_indx(i):gend_indx(i)))
262  subaxis_z=global_depth(gstart_indx(i):gend_indx(i))
263  output_fields(outnum)%output_grid%subaxes(i) =&
264  & diag_subaxes_init(axes(i),subaxis_z, gstart_indx(i),gend_indx(i))
265  DEALLOCATE(subaxis_z,global_depth)
266  ELSE ! regional vertical axis is the same as global vertical axis
267  gstart_indx(i) = 1
268  gend_indx(i) = global_axis_size
269  output_fields(outnum)%output_grid%subaxes(i) = axes(i)
270  ! <ERROR STATUS="FATAL">i should equal 3 for z axis</ERROR>
271  IF( i /= 3 ) CALL error_mesg('diag_util_mod::get_subfield_size',&
272  & 'i should equal 3 for z axis', fatal)
273  END IF
274  CASE default
275  ! <ERROR STATUS="FATAL">Wrong axis_cart</ERROR>
276  CALL error_mesg('diag_util_mod::get_subfield_size', 'Wrong axis_cart', fatal)
277  END SELECT
278  END DO
279 
280  DO i = 1, SIZE(axes(:))
281  IF ( gstart_indx(i) == -1 .OR. gend_indx(i) == -1 ) THEN
282  ! <ERROR STATUS="FATAL">
283  ! can not find gstart_indx/gend_indx for <output_fields(outnum)%output_name>,
284  ! check region bounds for axis <i>.
285  ! </ERROR>
286  WRITE(msg,'(A,I2)') ' check region bounds for axis ', i
287  CALL error_mesg('diag_util_mod::get_subfield_size', 'can not find gstart_indx/gend_indx for '&
288  & //trim(output_fields(outnum)%output_name)//','//trim(msg), fatal)
289  END IF
290  END DO
291  ELSE ! cubed sphere
292  ! get the i and j start and end indexes
293  CALL get_local_indexes(lonstart=start(1), lonend=end(1), &
294  & latstart=start(2), latend=end(2), &
295  & istart=gstart_indx(1), iend=gend_indx(1), &
296  & jstart=gstart_indx(2), jend=gend_indx(2))
297  global_axis_size = get_axis_global_length(axes(1))
298  ALLOCATE(global_lon(global_axis_size))
299  global_axis_size = get_axis_global_length(axes(2))
300  ALLOCATE(global_lat(global_axis_size))
301  CALL get_diag_axis_data(axes(1),global_lon)
302  CALL get_diag_axis_data(axes(2),global_lat)
303 
304  !Potential fix for out-of-bounds error for global_lon and global_lat.
305  IF ((gstart_indx(1) .GT. 0 .AND. gstart_indx(2) .GT. 0) .AND. &
306  (gstart_indx(1) .LE. global_axis_size .AND. gstart_indx(2) .LE. global_axis_size) .AND. &
307  (gend_indx(1) .GT. 0 .AND. gend_indx(2) .GT. 0) .AND. &
308  (gend_indx(1) .LE. global_axis_size .AND. gend_indx(2) .LE. global_axis_size)) THEN
309  ALLOCATE(subaxis_x(gstart_indx(1):gend_indx(1)))
310  ALLOCATE(subaxis_y(gstart_indx(2):gend_indx(2)))
311  subaxis_x=global_lon(gstart_indx(1):gend_indx(1))
312  subaxis_y=global_lat(gstart_indx(2):gend_indx(2))
313  END IF
314 
315  ! Now deal with the Z component
316  IF ( SIZE(axes(:)) > 2 ) THEN
317  global_axis_size = get_axis_global_length(axes(3))
318  output_fields(outnum)%output_grid%subaxes(3) = -1
319  CALL get_diag_axis_cart(axes(3), cart)
320  ! <ERROR STATUS="FATAL">
321  ! axis(3) should be Z-axis
322  ! </ERROR>
323  IF ( lowercase(cart) /= 'z' ) CALL error_mesg('diag_util_mod::get_subfield_size', &
324  &'axis(3) should be Z-axis', fatal)
325  ! <ERROR STATUS="FATAL">
326  ! wrong values in vertical axis of region
327  ! </ERROR>
328  IF ( start(3)*end(3)<0. ) CALL error_mesg('diag_util_mod::get_subfield_size',&
329  & 'wrong values in vertical axis of region',fatal)
330  IF ( start(3)>=0. .AND. end(3)>0. ) THEN
331  ALLOCATE(global_depth(global_axis_size))
332  CALL get_diag_axis_data(axes(3),global_depth)
333  gstart_indx(3) = get_index(start(3),global_depth)
334  IF( start(3) == 0.0 ) gstart_indx(3) = 1
335  gend_indx(3) = get_index(end(3),global_depth)
336  IF( start(3) >= maxval(global_depth) ) gstart_indx(3)= global_axis_size
337  IF( end(3) >= maxval(global_depth) ) gend_indx(3) = global_axis_size
338 
339  ALLOCATE(subaxis_z(gstart_indx(3):gend_indx(3)))
340  subaxis_z=global_depth(gstart_indx(3):gend_indx(3))
341  output_fields(outnum)%output_grid%subaxes(3) =&
342  & diag_subaxes_init(axes(3),subaxis_z, gstart_indx(3),gend_indx(3))
343  DEALLOCATE(subaxis_z,global_depth)
344  ELSE ! regional vertical axis is the same as global vertical axis
345  gstart_indx(3) = 1
346  gend_indx(3) = global_axis_size
347  output_fields(outnum)%output_grid%subaxes(3) = axes(3)
348  END IF
349  END IF
350  END IF
351 
352  ! get domain and compute_domain(xbegin,xend,ybegin,yend)
353  xbegin = -1
354  xend = -1
355  ybegin = -1
356  yend = -1
357 
358  domain2 = get_domain2d(axes)
359  IF ( domain2 .NE. null_domain2d ) THEN
360  CALL mpp_get_compute_domain(domain2, xbegin, xend, ybegin, yend)
361  CALL mpp_get_domain_components(domain2, domain1x, domain1y)
362  ELSE
363  DO i = 1, min(SIZE(axes(:)),2)
364  domain1 = get_domain1d(axes(i))
365  IF ( domain1 .NE. null_domain1d ) THEN
366  CALL get_diag_axis_cart(axes(i),cart)
367  SELECT CASE(cart)
368  CASE ('X')
369  domain1x = get_domain1d(axes(i))
370  CALL mpp_get_compute_domain(domain1x, xbegin, xend)
371  CASE ('Y')
372  domain1y = get_domain1d(axes(i))
373  CALL mpp_get_compute_domain(domain1y, ybegin, yend)
374  CASE default ! do nothing here
375  END SELECT
376  ELSE
377  ! <ERROR STATUS="FATAL">No domain available</ERROR>
378  CALL error_mesg('diag_util_mod::get_subfield_size', 'NO domain available', fatal)
379  END IF
380  END DO
381  END IF
382 
383  CALL get_axes_shift(axes, ishift, jshift)
384  xend = xend+ishift
385  yend = yend+jshift
386 
387  IF ( xbegin == -1 .OR. xend == -1 .OR. ybegin == -1 .OR. yend == -1 ) THEN
388  ! <ERROR STATUS="FATAL">wrong compute domain indices</ERROR>
389  CALL error_mesg('diag_util_mod::get_subfield_size', 'wrong compute domain indices',fatal)
390  END IF
391 
392  ! get the area containing BOTH compute domain AND local output area
393  IF( gstart_indx(1) > xend .OR. xbegin > gend_indx(1) ) THEN
394  output_fields(outnum)%output_grid%l_start_indx(1) = -1
395  output_fields(outnum)%output_grid%l_end_indx(1) = -1
396  output_fields(outnum)%need_compute = .false. ! not involved
397  ELSEIF ( gstart_indx(2) > yend .OR. ybegin > gend_indx(2) ) THEN
398  output_fields(outnum)%output_grid%l_start_indx(2) = -1
399  output_fields(outnum)%output_grid%l_end_indx(2) = -1
400  output_fields(outnum)%need_compute = .false. ! not involved
401  ELSE
402  output_fields(outnum)%output_grid%l_start_indx(1) = max(xbegin, gstart_indx(1))
403  output_fields(outnum)%output_grid%l_start_indx(2) = max(ybegin, gstart_indx(2))
404  output_fields(outnum)%output_grid%l_end_indx(1) = min(xend, gend_indx(1))
405  output_fields(outnum)%output_grid%l_end_indx(2) = min(yend, gend_indx(2))
406  output_fields(outnum)%need_compute = .true. ! involved in local output
407  END IF
408 
409  IF ( output_fields(outnum)%need_compute ) THEN
410  ! need to modify domain1d and domain2d for subaxes
411  xbegin_l = output_fields(outnum)%output_grid%l_start_indx(1)
412  xend_l = output_fields(outnum)%output_grid%l_end_indx(1)
413  ybegin_l = output_fields(outnum)%output_grid%l_start_indx(2)
414  yend_l = output_fields(outnum)%output_grid%l_end_indx(2)
415  CALL mpp_modify_domain(domain2, domain2_new, xbegin_l,xend_l, ybegin_l,yend_l,&
416  & gstart_indx(1),gend_indx(1), gstart_indx(2),gend_indx(2))
417 
418  output_fields(outnum)%output_grid%subaxes(1) =&
419  & diag_subaxes_init(axes(1),subaxis_x, gstart_indx(1),gend_indx(1),domain2_new)
420  output_fields(outnum)%output_grid%subaxes(2) =&
421  & diag_subaxes_init(axes(2),subaxis_y, gstart_indx(2),gend_indx(2),domain2_new)
422  DO i = 1, SIZE(axes(:))
423  IF ( output_fields(outnum)%output_grid%subaxes(i) == -1 ) THEN
424  ! <ERROR STATUS="FATAL">
425  ! <output_fields(outnum)%output_name> error at i = <i>
426  ! </ERROR>
427  WRITE(msg,'(a,"/",I4)') 'at i = ',i
428  CALL error_mesg('diag_util_mod::get_subfield_size '//trim(output_fields(outnum)%output_name),&
429  'error '//trim(msg), fatal)
430  END IF
431  END DO
432 
433  ! local start index should start from 1
434  output_fields(outnum)%output_grid%l_start_indx(1) = max(xbegin, gstart_indx(1)) - xbegin + 1
435  output_fields(outnum)%output_grid%l_start_indx(2) = max(ybegin, gstart_indx(2)) - ybegin + 1
436  output_fields(outnum)%output_grid%l_end_indx(1) = min(xend, gend_indx(1)) - xbegin + 1
437  output_fields(outnum)%output_grid%l_end_indx(2) = min(yend, gend_indx(2)) - ybegin + 1
438  IF ( SIZE(axes(:))>2 ) THEN
439  output_fields(outnum)%output_grid%l_start_indx(3) = gstart_indx(3)
440  output_fields(outnum)%output_grid%l_end_indx(3) = gend_indx(3)
441  ELSE
442  output_fields(outnum)%output_grid%l_start_indx(3) = 1
443  output_fields(outnum)%output_grid%l_end_indx(3) = 1
444  END IF
445  END IF
446  IF ( ALLOCATED(subaxis_x) ) DEALLOCATE(subaxis_x, global_lon)
447  IF ( ALLOCATED(subaxis_y) ) DEALLOCATE(subaxis_y, global_lat)
448  END SUBROUTINE get_subfield_size
449  ! </SUBROUTINE>
450 
451  ! <SUBROUTINE NAME="get_subfield_vert_size">
452  ! <OVERVIEW>
453  ! Get size, start and end indices for output fields.
454  ! </OVERVIEW>
455  ! <TEMPLATE>
456  ! SUBROUTINE get_subfield_vert_size(axes, outnum)
457  ! </TEMPLATE>
458  ! <DESCRIPTION>
459  ! Get size, start and end indices for <TT>output_fields(outnum)</TT>, fill in
460  ! <TT>output_fields(outnum)%output_grid%(start_indx, end_indx)</TT>.
461  ! </DESCRIPTION>
462  ! <IN NAME="axes" TYPE="INTEGER, DIMENSION(:)">Axes of the <TT>input_field</TT></IN>
463  ! <IN NAME="outnum" TYPE="INTEGER">Position in array <TT>output_fields</TT>.</IN>
464  SUBROUTINE get_subfield_vert_size(axes, outnum)
465  INTEGER, DIMENSION(:), INTENT(in) :: axes ! axes of the input_field
466  INTEGER, INTENT(in) :: outnum ! position in array output_fields
467 
468  REAL, DIMENSION(3) :: start, end ! start and end coordinates in 3 axes
469  REAL, ALLOCATABLE, DIMENSION(:) :: global_depth
470  REAL, ALLOCATABLE, DIMENSION(:) :: subaxis_z !containing local coordinates in x,y,z axes
471  INTEGER :: i, global_axis_size
472  INTEGER, DIMENSION(3) :: gstart_indx, gend_indx ! global start and end indices of output domain in 3 axes
473  CHARACTER(len=1) :: cart
474  CHARACTER(len=128) :: msg
475 !----------
476 !ug support
477  integer :: vert_dim_num
478 !----------
479 
480  !initilization for local output
481  start = -1.e10
482  end = -1.e10 ! initially out of (lat/lon/depth) range
483  gstart_indx = -1
484  gend_indx=-1
485 
486  ! get axis data (lat, lon, depth) and indices
487  start= output_fields(outnum)%output_grid%start
488  end = output_fields(outnum)%output_grid%end
489 
490 !----------
491 !ug support
492  vert_dim_num = 3
493 !----------
494  DO i = 1, SIZE(axes(:))
495  global_axis_size = get_axis_global_length(axes(i))
496  output_fields(outnum)%output_grid%subaxes(i) = -1
497  CALL get_diag_axis_cart(axes(i), cart)
498  SELECT CASE(cart)
499  CASE ('X')
500  ! <ERROR STATUS="FATAL">wrong order of axes, X should come first</ERROR>
501  IF ( i.NE.1 ) CALL error_mesg('diag_util_mod::get_subfield_vert_size',&
502  & 'wrong order of axes, X should come first',fatal)
503  gstart_indx(i) = 1
504  gend_indx(i) = global_axis_size
505  output_fields(outnum)%output_grid%subaxes(i) = axes(i)
506  CASE ('Y')
507  ! <ERROR STATUS="FATAL">wrong order of axes, Y should come second</ERROR>
508  IF( i.NE.2 ) CALL error_mesg('diag_util_mod::get_subfield_vert_size',&
509  & 'wrong order of axes, Y should come second',fatal)
510  gstart_indx(i) = 1
511  gend_indx(i) = global_axis_size
512  output_fields(outnum)%output_grid%subaxes(i) = axes(i)
513 !----------
514 !ug support
515  case ("U")
516  if (i .ne. 1) then
517  call error_mesg("diag_util_mod::get_subfield_vert_size", &
518  "the unstructured axis must be the first dimension.", &
519  fatal)
520  endif
521  gstart_indx(i) = 1
522  gend_indx(i) = global_axis_size
523  output_fields(outnum)%output_grid%subaxes(i) = axes(i)
524  vert_dim_num = 2
525  start(vert_dim_num) = start(3)
526  end(vert_dim_num) = end(3)
527 !----------
528  CASE ('Z')
529 !----------
530 !ug support
531  if (i .ne. vert_dim_num) then
532  call error_mesg("diag_util_mod::get_subfield_vert_size",&
533  "i should equal vert_dim_num for z axis", &
534  fatal)
535  endif
536 !----------
537  ! <ERROR STATUS="FATAL">wrong values in vertical axis of region</ERROR>
538  IF( start(i)*end(i) < 0. ) CALL error_mesg('diag_util_mod::get_subfield_vert_size',&
539  & 'wrong values in vertical axis of region',fatal)
540  IF( start(i) >= 0. .AND. end(i) > 0. ) THEN
541  ALLOCATE(global_depth(global_axis_size))
542  CALL get_diag_axis_data(axes(i),global_depth)
543  gstart_indx(i) = get_index(start(i),global_depth)
544  IF( start(i) == 0.0 ) gstart_indx(i) = 1
545 
546  gend_indx(i) = get_index(end(i),global_depth)
547  IF( start(i) >= maxval(global_depth) ) gstart_indx(i)= global_axis_size
548  IF( end(i) >= maxval(global_depth) ) gend_indx(i) = global_axis_size
549 
550  ALLOCATE(subaxis_z(gstart_indx(i):gend_indx(i)))
551  subaxis_z=global_depth(gstart_indx(i):gend_indx(i))
552  output_fields(outnum)%output_grid%subaxes(i) = &
553  diag_subaxes_init(axes(i),subaxis_z, gstart_indx(i),gend_indx(i))
554  DEALLOCATE(subaxis_z,global_depth)
555  ELSE ! vertical axis is the same as global vertical axis
556  gstart_indx(i) = 1
557  gend_indx(i) = global_axis_size
558  output_fields(outnum)%output_grid%subaxes(i) = axes(i)
559  END IF
560  CASE default
561  ! <ERROR STATUS="FATAL">Wrong axis_cart</ERROR>
562  CALL error_mesg('diag_util_mod::get_subfield_vert_size', 'Wrong axis_cart', fatal)
563  END SELECT
564  END DO
565 
566  DO i = 1,SIZE(axes(:))
567  IF ( gstart_indx(i) == -1 .OR. gend_indx(i) == -1 ) THEN
568  ! <ERROR STATUS="FATAL">
569  ! can not find gstart_indx/gend_indx for <output_fields(outnum)%output_name>
570  ! check region bounds for axis
571  ! </ERROR>
572  WRITE(msg,'(A,I2)') ' check region bounds for axis ', i
573  CALL error_mesg('diag_util_mod::get_subfield_vert_size', 'can not find gstart_indx/gend_indx for '&
574  & //trim(output_fields(outnum)%output_name)//','//trim(msg), fatal)
575  END IF
576  END DO
577 
578  DO i= 1, 2
579  output_fields(outnum)%output_grid%l_start_indx(i) = gstart_indx(i)
580  output_fields(outnum)%output_grid%l_end_indx(i) = gend_indx(i)
581  END DO
582 
583  IF( SIZE(axes(:)) > 2 ) THEN
584  output_fields(outnum)%output_grid%l_start_indx(3) = gstart_indx(3)
585  output_fields(outnum)%output_grid%l_end_indx(3) = gend_indx(3)
586  ELSE
587  output_fields(outnum)%output_grid%l_start_indx(3) = 1
588  output_fields(outnum)%output_grid%l_end_indx(3) = 1
589  END IF
590  END SUBROUTINE get_subfield_vert_size
591  ! </SUBROUTINE>
592 
593  ! <PRIVATE>
594  ! <FUNCTION NAME="get_index">
595  ! <OVERVIEW>
596  ! Find index <TT>i</TT> of array such that <TT>array(i)</TT> is closest to number.
597  ! </OVERVIEW>
598  ! <TEMPLATE>
599  ! INTEGER FUNCTION get_index(number, array)
600  ! </TEMPLATE>
601  ! <DESCRIPTION>
602  ! Find index <TT>i</TT> of array such that <TT>array(i)</TT> is closest to number.
603  ! Array must be monotonouslly ordered.
604  ! </DESCRIPTION>
605  ! <IN NAME="number" TYPE="REAL"></IN>
606  ! <IN NAME="array" TYPE="REAL, DIMENSION(:)"></IN>
607  INTEGER FUNCTION get_index(number, array)
608  REAL, INTENT(in) :: number
609  REAL, INTENT(in), DIMENSION(:) :: array
610 
611  INTEGER :: i, n
612  LOGICAL :: found
613 
614  n = SIZE(array(:))
615  ! check if array is monotonous
616  DO i = 2, n-1
617  IF( (array(i-1)<array(i).AND.array(i)>array(i+1)) .OR. (array(i-1)>array(i).AND.array(i)<array(i+1))) THEN
618  ! <ERROR STATUS="FATAL">array NOT monotonously ordered</ERROR>
619  CALL error_mesg('diag_util_mod::get_index', 'array NOT monotonously ordered',fatal)
620  END IF
621  END DO
622  get_index = -1
623  found = .false.
624  ! search in increasing array
625  DO i = 1, n-1
626  IF ( (array(i)<=number).AND.(array(i+1)>= number) ) THEN
627  IF( number - array(i) <= array(i+1) - number ) THEN
628  get_index = i
629  found=.true.
630  ELSE
631  get_index = i+1
632  found=.true.
633  ENDIF
634  EXIT
635  END IF
636  END DO
637  ! if not found, search in decreasing array
638  IF( .NOT.found ) THEN
639  DO i = 1, n-1
640  IF ( (array(i)>=number).AND.(array(i+1)<= number) ) THEN
641  IF ( array(i)-number <= number-array(i+1) ) THEN
642  get_index = i
643  found = .true.
644  ELSE
645  get_index = i+1
646  found = .true.
647  END IF
648  EXIT
649  END IF
650  END DO
651  END IF
652  ! if still not found, is it less than the first element
653  ! or greater than last element? (Increasing Array)
654  ! But it must be within 2x the axis spacing
655  ! i.e. array(1)-(array(3)-array(1)).LT.number .AND. or 2*array(1)-array(3).LT.number
656  IF ( .NOT. found ) THEN
657  IF ( 2*array(1)-array(3).LT.number .AND. number.LT.array(1) ) THEN
658  get_index = 1
659  found = .true.
660  ELSE IF ( array(n).LT.number .AND. number.LT.2*array(n)-array(n-2) ) THEN
661  get_index = n
662  found = .true.
663  ELSE
664  found = .false.
665  END IF
666  END IF
667 
668  ! if still not found, is it greater than the first element
669  ! or less than the last element? (Decreasing Array)
670  ! But it must be within 2x the axis spacing (see above)
671  IF ( .NOT. found ) THEN
672  IF ( 2*array(1)-array(3).GT.number .AND. number.GT.array(1) ) THEN
673  get_index = 1
674  found = .true.
675  ELSE IF ( array(n).GT.number .AND. number.GT.2*array(n)-array(n-2) ) THEN
676  get_index = n
677  found = .true.
678  ELSE
679  found = .false.
680  END IF
681  END IF
682  END FUNCTION get_index
683  ! </FUNCTION>
684  ! </PRIVATE>
685 
686  ! <SUBROUTINE NAME="log_diag_field_info">
687  ! <OVERVIEW>
688  ! Writes brief diagnostic field info to the log file.
689  ! </OVERVIEW>
690  ! <TEMPLATE>
691  ! SUBROUTINE log_diag_field_info(module_name, field_name, axes, long_name, units,
692  ! missing_value, range, dynamic)
693  ! </TEMPLATE>
694  ! <DESCRIPTION>
695  ! If the <TT>do_diag_field_log</TT> namelist parameter is .TRUE.,
696  ! then a line briefly describing diagnostic field is added to
697  ! the log file. Normally users should not call this subroutine
698  ! directly, since it is called by register_static_field and
699  ! register_diag_field if do_not_log is not set to .TRUE.. It is
700  ! used, however, in LM3 to avoid excessive logs due to the
701  ! number of fields registered for each of the tile types. LM3
702  ! code uses a do_not_log parameter in the registration calls,
703  ! and subsequently calls this subroutine to log field information
704  ! under a generic name.
705  ! </DESCRIPTION>
706  ! <IN NAME="module_name" TYPE="CHARACTER(len=*)">Module name.</IN>
707  ! <IN NAME="field_name" TYPE="CHARACTER(len=*)">Field name.</IN>
708  ! <IN NAME="axes" TYPE="INTEGER, DIMENSION(:)">Axis IDs.</IN>
709  ! <IN NAME="long_name" TYPE="CHARACTER(len=*), OPTIONAL">Long name for field.</IN>
710  ! <IN NAME="units" TYPE="CHARACTER(len=*), OPTIONAL">Unit of field.</IN>
711  ! <IN NAME="missing_value" TYPE="REAL, OPTIONAL">Missing value value.</IN>
712  ! <IN NAME="range" TYPE="REAL, DIMENSION(2), OPTIONAL">Valid range of values for field.</IN>
713  ! <IN NAME="dynamic" TYPE="LOGICAL, OPTIONAL"><TT>.TRUE.</TT> if field is not static.</IN>
714  SUBROUTINE log_diag_field_info(module_name, field_name, axes, long_name, units,&
715  & missing_value, range, dynamic)
716  CHARACTER(len=*), INTENT(in) :: module_name, field_name
717  INTEGER, DIMENSION(:), INTENT(in) :: axes
718  CHARACTER(len=*), OPTIONAL, INTENT(in) :: long_name, units
719  REAL, OPTIONAL, INTENT(in) :: missing_value
720  REAL, DIMENSION(2), OPTIONAL, INTENT(IN) :: range
721  LOGICAL, OPTIONAL, INTENT(in) :: dynamic
722 
723  ! ---- local vars
724  CHARACTER(len=256) :: lmodule, lfield, lname, lunits
725  CHARACTER(len=64) :: lmissval, lmin, lmax
726  CHARACTER(len=8) :: numaxis, timeaxis
727  CHARACTER(len=1) :: sep = '|'
728  CHARACTER(len=256) :: axis_name, axes_list
729  INTEGER :: i
730 
731  IF ( .NOT.do_diag_field_log ) RETURN
732  IF ( mpp_pe().NE.mpp_root_pe() ) RETURN
733 
734  lmodule = trim(module_name)
735  lfield = trim(field_name)
736 
737  IF ( PRESENT(long_name) ) THEN
738  lname = trim(long_name)
739  ELSE
740  lname = ''
741  END IF
742 
743  IF ( PRESENT(units) ) THEN
744  lunits = trim(units)
745  ELSE
746  lunits = ''
747  END IF
748 
749  WRITE (numaxis,'(i1)') SIZE(axes)
750 
751  IF (PRESENT(missing_value)) THEN
752  IF ( use_cmor ) THEN
753  WRITE (lmissval,*) cmor_missing_value
754  ELSE
755  WRITE (lmissval,*) missing_value
756  END IF
757  ELSE
758  lmissval = ''
759  ENDIF
760 
761  IF ( PRESENT(range) ) THEN
762  WRITE (lmin,*) range(1)
763  WRITE (lmax,*) range(2)
764  ELSE
765  lmin = ''
766  lmax = ''
767  END IF
768 
769  IF ( PRESENT(dynamic) ) THEN
770  IF (dynamic) THEN
771  timeaxis = 'T'
772  ELSE
773  timeaxis = 'F'
774  END IF
775  ELSE
776  timeaxis = ''
777  END IF
778 
779  axes_list=''
780  DO i = 1, SIZE(axes)
781  CALL get_diag_axis_name(axes(i),axis_name)
782  IF ( trim(axes_list) /= '' ) axes_list = trim(axes_list)//','
783  axes_list = trim(axes_list)//trim(axis_name)
784  END DO
785 
786  !write (diag_log_unit,'(8(a,a),a)') &
787  WRITE (diag_log_unit,'(777a)') &
788  & trim(lmodule), sep, trim(lfield), sep, trim(lname), sep,&
789  & trim(lunits), sep, trim(numaxis), sep, trim(timeaxis), sep,&
790  & trim(lmissval), sep, trim(lmin), sep, trim(lmax), sep,&
791  & trim(axes_list)
792  END SUBROUTINE log_diag_field_info
793  ! </SUBROUTINE>
794 
795  ! <SUBROUTINE NAME="update_bounds">
796  ! <OVERVIEW>
797  ! Update the <TT>output_fields</TT> min and max boundaries.
798  ! </OVERVIEW>
799  ! <TEMPLATE>
800  ! SUBROUTINE update_bounds(out_num, lower_i, upper_i, lower_j, upper_j, lower_k, upper_k)
801  ! </TEMPLATE>
802  ! <DESCRIPTION>
803  ! Update the <TT>output_fields</TT> x, y, and z min and max boundaries (array indices).
804  ! </DESCRIPTION>
805  ! <IN NAME="out_num" TYPE="INTEGER"><TT>output_field</TT> ID.</IN>
806  ! <IN NAME="lower_i" TYPE="INTEGER">Lower <TT>i</TT> bound.</IN>
807  ! <IN NAME="upper_i" TYPE="INTEGER">Upper <TT>i</TT> bound.</IN>
808  ! <IN NAME="lower_j" TYPE="INTEGER">Lower <TT>j</TT> bound.</IN>
809  ! <IN NAME="upper_j" TYPE="INTEGER">Upper <TT>j</TT> bound.</IN>
810  ! <IN NAME="lower_k" TYPE="INTEGER">Lower <TT>k</TT> bound.</IN>
811  ! <IN NAME="upper_k" TYPE="INTEGER">Upper <TT>k</TT> bound.</IN>
812  SUBROUTINE update_bounds(out_num, lower_i, upper_i, lower_j, upper_j, lower_k, upper_k)
813  INTEGER, INTENT(in) :: out_num, lower_i, upper_i, lower_j, upper_j, lower_k, upper_k
814 
815  output_fields(out_num)%imin = min(output_fields(out_num)%imin, lower_i)
816  output_fields(out_num)%imax = max(output_fields(out_num)%imax, upper_i)
817  output_fields(out_num)%jmin = min(output_fields(out_num)%jmin, lower_j)
818  output_fields(out_num)%jmax = max(output_fields(out_num)%jmax, upper_j)
819  output_fields(out_num)%kmin = min(output_fields(out_num)%kmin, lower_k)
820  output_fields(out_num)%kmax = max(output_fields(out_num)%kmax, upper_k)
821  END SUBROUTINE update_bounds
822  ! </SUBROUTINE>
823 
824  ! <SUBROUTINE NAME="check_out_of_bounds">
825  ! <OVERVIEW>
826  ! Checks if the array indices for <TT>output_fields(out_num)</TT> are outside the <TT>output_fields(out_num)%buffer</TT> upper
827  ! and lower bounds.
828  ! </OVERVIEW>
829  ! <TEMPLATE>
830  ! SUBROUTINE check_out_of_bounds(out_num, diag_field_id, err_msg)
831  ! </TEMPLATE>
832  ! <DESCRIPTION>
833  ! <TT>check_out_of_bounds</TT> verifies the array min and max indices in the x, y, and z directions of <TT>
834  ! output_fields(out_num)</TT> are not outside the upper and lower array boundaries of
835  ! <TT>output_fields(out_num)%buffer</TT>. If the min and max indices are outside the upper and lower bounds of the buffer
836  ! array, then <TT>check_out_of_bounds</TT> returns an error string.
837  ! </DESCRIPTION>
838  ! <IN NAME="out_num" TYPE="INTEGER">
839  ! Output field ID number.
840  ! </IN>
841  ! <IN NAME="diag_field_id" TYPE="INTEGER">
842  ! Input field ID number.
843  ! </IN>
844  ! <OUT NAME="err_msg" TYPE="CHARACTER(len=*)">
845  ! Return status of <TT>check_out_of_bounds</TT>. An empty error string indicates the x, y, and z indices are not outside the
846  ! buffer array boundaries.
847  ! </OUT>
848  SUBROUTINE check_out_of_bounds(out_num, diag_field_id, err_msg)
849  INTEGER, INTENT(in) :: out_num, diag_field_id
850  CHARACTER(len=*), INTENT(out) :: err_msg
851 
852  CHARACTER(len=128) :: error_string1, error_string2
853 
854  IF ( output_fields(out_num)%imin < lbound(output_fields(out_num)%buffer,1) .OR.&
855  & output_fields(out_num)%imax > ubound(output_fields(out_num)%buffer,1) .OR.&
856  & output_fields(out_num)%jmin < lbound(output_fields(out_num)%buffer,2) .OR.&
857  & output_fields(out_num)%jmax > ubound(output_fields(out_num)%buffer,2) .OR.&
858  & output_fields(out_num)%kmin < lbound(output_fields(out_num)%buffer,3) .OR.&
859  & output_fields(out_num)%kmax > ubound(output_fields(out_num)%buffer,3) ) THEN
860  WRITE(error_string1,'(a,"/",a)') trim(input_fields(diag_field_id)%module_name),&
861  & trim(output_fields(out_num)%output_name)
862  error_string2 ='Buffer bounds= : , : , : Actual bounds= : , : , : '
863  WRITE(error_string2(15:17),'(i3)') lbound(output_fields(out_num)%buffer,1)
864  WRITE(error_string2(19:21),'(i3)') ubound(output_fields(out_num)%buffer,1)
865  WRITE(error_string2(23:25),'(i3)') lbound(output_fields(out_num)%buffer,2)
866  WRITE(error_string2(27:29),'(i3)') ubound(output_fields(out_num)%buffer,2)
867  WRITE(error_string2(31:33),'(i3)') lbound(output_fields(out_num)%buffer,3)
868  WRITE(error_string2(35:37),'(i3)') ubound(output_fields(out_num)%buffer,3)
869  WRITE(error_string2(54:56),'(i3)') output_fields(out_num)%imin
870  WRITE(error_string2(58:60),'(i3)') output_fields(out_num)%imax
871  WRITE(error_string2(62:64),'(i3)') output_fields(out_num)%jmin
872  WRITE(error_string2(66:68),'(i3)') output_fields(out_num)%jmax
873  WRITE(error_string2(70:72),'(i3)') output_fields(out_num)%kmin
874  WRITE(error_string2(74:76),'(i3)') output_fields(out_num)%kmax
875  err_msg = 'module/output_field='//trim(error_string1)//&
876  & ' Bounds of buffer exceeded. '//trim(error_string2)
877  ! imax, imin, etc need to be reset in case the program is not terminated.
878  output_fields(out_num)%imax = 0
879  output_fields(out_num)%imin = very_large_axis_length
880  output_fields(out_num)%jmax = 0
881  output_fields(out_num)%jmin = very_large_axis_length
882  output_fields(out_num)%kmax = 0
883  output_fields(out_num)%kmin = very_large_axis_length
884  ELSE
885  err_msg = ''
886  END IF
887 
888  END SUBROUTINE check_out_of_bounds
889  ! </SUBROUTINE>
890 
891  ! <SUBROUTINE NAME="check_bounds_are_exact_dynamic">
892  ! <OVERVIEW>
893  ! Check if the array indices for <TT>output_fields(out_num)</TT> are equal to the <TT>output_fields(out_num)%buffer</TT>
894  ! upper and lower bounds.
895  ! </OVERVIEW>
896  ! <TEMPLATE>
897  ! SUBROUTINE check_bounds_are_exact_dynamic(out_num, diag_field_id, Time, err_msg)
898  ! </TEMPLATE>
899  ! <DESCRIPTION>
900  ! <TT>check_bounds_are_exact_dynamic</TT> checks if the min and max array indices for <TT>output_fields(out_num)</TT> are
901  ! equal to the upper and lower bounds of <TT>output_fields(out_num)%buffer</TT>. This check is only performed if
902  ! <TT>output_fields(out_num)%Time_of_prev_field_data</TT> doesn't equal <TT>Time</TT> or <TT>Time_zero</TT>.
903  ! <TT>check_bounds_are_exact_dynamic</TT> returns an error string if the array indices do not match the buffer bounds.
904  ! </DESCRIPTION>
905  ! <IN NAME="out_num" TYPE="INTEGER">
906  ! Output field ID number.
907  ! </IN>
908  ! <IN NAME="diag_field_id" TYPE="INTEGER">
909  ! Input field ID number.
910  ! </IN>
911  ! <IN NAME="Time" TYPE="TYPE(time_type)">
912  ! Time to use in check. The check is only performed if <TT>output_fields(out_num)%Time_of_prev_field_data</TT> is not
913  ! equal to <TT>Time</TT> or <TT>Time_zero</TT>.
914  ! </IN>
915  ! <OUT NAME="err_msg" TYPE="CHARACTER(len=*)">
916  ! Return status of <TT>check_bounds_are_exact_dynamic</TT>. An empty error string indicates the x, y, and z indices are
917  ! equal to the buffer array boundaries.
918  ! </OUT>
919  SUBROUTINE check_bounds_are_exact_dynamic(out_num, diag_field_id, Time, err_msg)
920  INTEGER, INTENT(in) :: out_num, diag_field_id
921  TYPE(time_type), INTENT(in) :: time
922  CHARACTER(len=*), INTENT(out) :: err_msg
923 
924  CHARACTER(len=128) :: error_string1, error_string2
925  LOGICAL :: do_check
926 
927  err_msg = ''
928 
929  ! Check bounds only when the value of Time changes. When windows are used,
930  ! a change in Time indicates that a new loop through the windows has begun,
931  ! so a check of the previous loop can be done.
932  IF ( time == output_fields(out_num)%Time_of_prev_field_data ) THEN
933  do_check = .false.
934  ELSE
935  IF ( output_fields(out_num)%Time_of_prev_field_data == time_zero ) THEN
936  ! It may or may not be OK to check, I don't know how to tell.
937  ! Check will be done on subsequent calls anyway.
938  do_check = .false.
939  ELSE
940  do_check = .true.
941  END IF
942  output_fields(out_num)%Time_of_prev_field_data = time
943  END IF
944 
945  IF ( do_check ) THEN
946  IF ( output_fields(out_num)%imin /= lbound(output_fields(out_num)%buffer,1) .OR.&
947  & output_fields(out_num)%imax /= ubound(output_fields(out_num)%buffer,1) .OR.&
948  & output_fields(out_num)%jmin /= lbound(output_fields(out_num)%buffer,2) .OR.&
949  & output_fields(out_num)%jmax /= ubound(output_fields(out_num)%buffer,2) .OR.&
950  & output_fields(out_num)%kmin /= lbound(output_fields(out_num)%buffer,3) .OR.&
951  & output_fields(out_num)%kmax /= ubound(output_fields(out_num)%buffer,3) ) THEN
952  WRITE(error_string1,'(a,"/",a)') trim(input_fields(diag_field_id)%module_name),&
953  & trim(output_fields(out_num)%output_name)
954  error_string2 ='Buffer bounds= : , : , : Actual bounds= : , : , : '
955  WRITE(error_string2(15:17),'(i3)') lbound(output_fields(out_num)%buffer,1)
956  WRITE(error_string2(19:21),'(i3)') ubound(output_fields(out_num)%buffer,1)
957  WRITE(error_string2(23:25),'(i3)') lbound(output_fields(out_num)%buffer,2)
958  WRITE(error_string2(27:29),'(i3)') ubound(output_fields(out_num)%buffer,2)
959  WRITE(error_string2(31:33),'(i3)') lbound(output_fields(out_num)%buffer,3)
960  WRITE(error_string2(35:37),'(i3)') ubound(output_fields(out_num)%buffer,3)
961  WRITE(error_string2(54:56),'(i3)') output_fields(out_num)%imin
962  WRITE(error_string2(58:60),'(i3)') output_fields(out_num)%imax
963  WRITE(error_string2(62:64),'(i3)') output_fields(out_num)%jmin
964  WRITE(error_string2(66:68),'(i3)') output_fields(out_num)%jmax
965  WRITE(error_string2(70:72),'(i3)') output_fields(out_num)%kmin
966  WRITE(error_string2(74:76),'(i3)') output_fields(out_num)%kmax
967  err_msg = trim(error_string1)//' Bounds of data do not match those of buffer. '//trim(error_string2)
968  END IF
969  output_fields(out_num)%imax = 0
970  output_fields(out_num)%imin = very_large_axis_length
971  output_fields(out_num)%jmax = 0
972  output_fields(out_num)%jmin = very_large_axis_length
973  output_fields(out_num)%kmax = 0
974  output_fields(out_num)%kmin = very_large_axis_length
975  END IF
976  END SUBROUTINE check_bounds_are_exact_dynamic
977  ! </SUBROUTINE>
978 
979  ! <SUBROUTINE NAME="check_bounds_are_exact_static">
980  ! <OVERVIEW>
981  ! Check if the array indices for <TT>output_fields(out_num)</TT> are equal to the <TT>output_fields(out_num)%buffer</TT>
982  ! upper and lower bounds.
983  ! </OVERVIEW>
984  ! <TEMPLATE>
985  ! SUBROUTINE check_bounds_are_exact_static(out_num, diag_field_id, err_msg)
986  ! </TEMPLATE>
987  ! <DESCRIPTION>
988  ! </DESCRIPTION>
989  ! <IN NAME="out_num" TYPE="INTEGER">Output field ID</IN>
990  ! <IN NAME="diag_field_id" TYPE="INTEGER">Input field ID.</IN>
991  ! <OUT NAME="err_msg" TYPE="CHARACTER(len=*)"></OUT>
992  SUBROUTINE check_bounds_are_exact_static(out_num, diag_field_id, err_msg)
993  INTEGER, INTENT(in) :: out_num, diag_field_id
994  CHARACTER(len=*), INTENT(out) :: err_msg
995 
996  CHARACTER(len=128) :: error_string1, error_string2
997 
998  err_msg = ''
999 
1000  IF ( output_fields(out_num)%imin /= lbound(output_fields(out_num)%buffer,1) .OR.&
1001  & output_fields(out_num)%imax /= ubound(output_fields(out_num)%buffer,1) .OR.&
1002  & output_fields(out_num)%jmin /= lbound(output_fields(out_num)%buffer,2) .OR.&
1003  & output_fields(out_num)%jmax /= ubound(output_fields(out_num)%buffer,2) .OR.&
1004  & output_fields(out_num)%kmin /= lbound(output_fields(out_num)%buffer,3) .OR.&
1005  & output_fields(out_num)%kmax /= ubound(output_fields(out_num)%buffer,3) ) THEN
1006  WRITE(error_string1,'(a,"/",a)') trim(input_fields(diag_field_id)%module_name),&
1007  & trim(output_fields(out_num)%output_name)
1008  error_string2 ='Buffer bounds= : , : , : Actual bounds= : , : , : '
1009  WRITE(error_string2(15:17),'(i3)') lbound(output_fields(out_num)%buffer,1)
1010  WRITE(error_string2(19:21),'(i3)') ubound(output_fields(out_num)%buffer,1)
1011  WRITE(error_string2(23:25),'(i3)') lbound(output_fields(out_num)%buffer,2)
1012  WRITE(error_string2(27:29),'(i3)') ubound(output_fields(out_num)%buffer,2)
1013  WRITE(error_string2(31:33),'(i3)') lbound(output_fields(out_num)%buffer,3)
1014  WRITE(error_string2(35:37),'(i3)') ubound(output_fields(out_num)%buffer,3)
1015  WRITE(error_string2(54:56),'(i3)') output_fields(out_num)%imin
1016  WRITE(error_string2(58:60),'(i3)') output_fields(out_num)%imax
1017  WRITE(error_string2(62:64),'(i3)') output_fields(out_num)%jmin
1018  WRITE(error_string2(66:68),'(i3)') output_fields(out_num)%jmax
1019  WRITE(error_string2(70:72),'(i3)') output_fields(out_num)%kmin
1020  WRITE(error_string2(74:76),'(i3)') output_fields(out_num)%kmax
1021  err_msg = trim(error_string1)//' Bounds of data do not match those of buffer. '//trim(error_string2)
1022  END IF
1023  output_fields(out_num)%imax = 0
1024  output_fields(out_num)%imin = very_large_axis_length
1025  output_fields(out_num)%jmax = 0
1026  output_fields(out_num)%jmin = very_large_axis_length
1027  output_fields(out_num)%kmax = 0
1028  output_fields(out_num)%kmin = very_large_axis_length
1029 
1030  END SUBROUTINE check_bounds_are_exact_static
1031  ! </SUBROUTINE>
1032 
1033  ! <SUBROUTINE NAME="init_file">
1034  ! <OVERVIEW>
1035  ! Initialize the output file.
1036  ! </OVERVIEW>
1037  ! <TEMPLATE>
1038  ! SUBROUTINE init_file(name, output_freq, output_units, format, time_units
1039  ! long_name, tile_count, new_file_freq, new_file_freq_units, start_time,
1040  ! file_duration, file_duration_units)
1041  ! </TEMPLATE>
1042  ! <DESCRIPTION>
1043  ! Initialize the output file.
1044  ! </DESCRIPTION>
1045  ! <IN NAME="name" TYPE="CHARACTER(len=*)">File name.</IN>
1046  ! <IN NAME="output_freq" TYPE="INTEGER">How often data is to be written to the file.</IN>
1047  ! <IN NAME="output_units" TYPE="INTEGER">The output frequency unit. (MIN, HOURS, DAYS, etc.)</IN>
1048  ! <IN NAME="format" TYPE="INTEGER">Number type/kind the data is to be written out to the file.</IN>
1049  ! <IN NAME="time_units" TYPE="INTEGER">Time axis units.</IN>
1050  ! <IN NAME="log_name" TYPE="CHARACTER(len=*)">Long name for time axis.</IN>
1051  ! <IN NAME="tile_count" TYPE="INTEGER">Tile number.</IN>
1052  ! <IN NAME="new_file_freq" TYPE="INTEGER, OPTIONAL">How often a new file is to be created.</IN>
1053  ! <IN NAME="new_file_freq_units" TYPE="INTEGER, OPTIONAL">The new file frequency unit. (MIN, HOURS, DAYS, etc.)</IN>
1054  ! <IN NAME="start_time" TYPE="TYPE(time_type), OPTIONAL">Time when the file is to start </IN>
1055  ! <IN NAME="file_duration" TYPE="INTEGER, OPTIONAL">How long file is to be used.</IN>
1056  ! <IN NAME="file_duration_units" TYPE="INTEGER, OPTIONAL">File duration unit. (MIN, HOURS, DAYS, etc.)</IN>
1057  SUBROUTINE init_file(name, output_freq, output_units, format, time_units, long_name, tile_count,&
1058  & new_file_freq, new_file_freq_units, start_time, file_duration, file_duration_units)
1059  CHARACTER(len=*), INTENT(in) :: name, long_name
1060  INTEGER, INTENT(in) :: output_freq, output_units, format, time_units
1061  INTEGER, INTENT(in) :: tile_count
1062  INTEGER, INTENT(in), OPTIONAL :: new_file_freq, new_file_freq_units
1063  INTEGER, INTENT(in), OPTIONAL :: file_duration, file_duration_units
1064  TYPE(time_type), INTENT(in), OPTIONAL :: start_time
1065 
1066  INTEGER :: new_file_freq1, new_file_freq_units1
1067  INTEGER :: file_duration1, file_duration_units1
1068  INTEGER :: n
1069  LOGICAL :: same_file_err !< .FALSE. indicates that if the file name had
1070  !! previously been registered, this new file
1071  !! contained differences from the previous.
1072  REAL, DIMENSION(1) :: tdata
1073  CHARACTER(len=128) :: time_units_str
1074 
1075  ! Check if this file has already been defined
1076  same_file_err=.false. ! To indicate that if this file was previously defined
1077  ! no differences in this registration was detected.
1078  DO n=1,num_files
1079  IF ( trim(files(n)%name) == trim(name) ) THEN
1080  ! File is defined, check if all inputs are the same
1081  ! Start with the required parameters
1082  IF ( files(n)%output_freq.NE.output_freq .OR.&
1083  & files(n)%output_units.NE.output_units .OR.&
1084  & files(n)%format.NE.format .OR.&
1085  & files(n)%time_units.NE.time_units .OR.&
1086  & trim(files(n)%long_name).NE.trim(long_name) .OR.&
1087  & files(n)%tile_count.NE.tile_count ) THEN
1088  same_file_err=.true.
1089  END IF
1090 
1091  ! Now check the OPTIONAL parameters
1092  IF ( PRESENT(new_file_freq) ) THEN
1093  IF ( files(n)%new_file_freq.NE.new_file_freq ) THEN
1094  same_file_err=.true.
1095  END IF
1096  END IF
1097 
1098  IF ( PRESENT(new_file_freq_units) ) THEN
1099  IF ( files(n)%new_file_freq_units.NE.new_file_freq_units ) THEN
1100  same_file_err=.true.
1101  END IF
1102  END IF
1103 
1104  IF ( PRESENT(start_time) ) THEN
1105  IF ( files(n)%start_time==start_time ) THEN
1106  same_file_err=.true.
1107  END IF
1108  END IF
1109 
1110  IF ( PRESENT(file_duration) ) THEN
1111  IF ( files(n)%duration.NE.file_duration) THEN
1112  same_file_err=.true.
1113  END IF
1114  END IF
1115 
1116  IF ( PRESENT(file_duration_units) ) THEN
1117  IF ( files(n)%duration_units.NE.file_duration_units ) THEN
1118  same_file_err=.true.
1119  END IF
1120  END IF
1121 
1122  ! If the same file was defined twice, simply return, else FATAL
1123  IF ( same_file_err ) THEN
1124  ! Something in this file is not identical to the previously defined
1125  ! file of the same name. FATAL
1126  CALL error_mesg('diag_util_mod::init_file',&
1127  & 'The file "'//trim(name)//'" is defined multiple times in&
1128  & the diag_table.', fatal)
1129  ELSE
1130  ! Issue a note that the same file is defined multiple times
1131  CALL error_mesg('diag_util_mod::init_file',&
1132  & 'The file "'//trim(name)//'" is defined multiple times in&
1133  & the diag_table.', note)
1134  ! Return to the calling function
1135  RETURN
1136  END IF
1137  END IF
1138  END DO
1139 
1140  ! Get a number for this file
1141  num_files = num_files + 1
1142  IF ( num_files >= max_files ) THEN
1143  ! <ERROR STATUS="FATAL">
1144  ! max_files exceeded, increase max_files via the max_files variable
1145  ! in the namelist diag_manager_nml.
1146  ! </ERROR>
1147  CALL error_mesg('diag_util_mod::init_file',&
1148  & ' max_files exceeded, increase max_files via the max_files variable&
1149  & in the namelist diag_manager_nml.', fatal)
1150  END IF
1151 
1152  IF ( PRESENT(new_file_freq) ) THEN
1153  new_file_freq1 = new_file_freq
1154  ELSE
1155  new_file_freq1 = very_large_file_freq
1156  END IF
1157 
1158  IF ( PRESENT(new_file_freq_units) ) THEN
1159  new_file_freq_units1 = new_file_freq_units
1160  ELSE IF ( get_calendar_type() == no_calendar ) THEN
1161  new_file_freq_units1 = diag_days
1162  ELSE
1163  new_file_freq_units1 = diag_years
1164  END IF
1165 
1166  IF ( PRESENT(file_duration) ) THEN
1167  file_duration1 = file_duration
1168  ELSE
1169  file_duration1 = new_file_freq1
1170  END IF
1171 
1172  IF ( PRESENT(file_duration_units) ) THEN
1173  file_duration_units1 = file_duration_units
1174  ELSE
1175  file_duration_units1 = new_file_freq_units1
1176  END IF
1177 
1178  files(num_files)%tile_count = tile_count
1179  files(num_files)%name = trim(name)
1180  files(num_files)%output_freq = output_freq
1181  files(num_files)%output_units = output_units
1182  files(num_files)%format = FORMAT
1183  files(num_files)%time_units = time_units
1184  files(num_files)%long_name = trim(long_name)
1185  files(num_files)%num_fields = 0
1186  files(num_files)%local = .false.
1187  files(num_files)%last_flush = base_time
1188  files(num_files)%file_unit = -1
1189  files(num_files)%new_file_freq = new_file_freq1
1190  files(num_files)%new_file_freq_units = new_file_freq_units1
1191  files(num_files)%duration = file_duration1
1192  files(num_files)%duration_units = file_duration_units1
1193  IF ( PRESENT(start_time) ) THEN
1194  files(num_files)%start_time = start_time
1195  ELSE
1196  files(num_files)%start_time = base_time
1197  END IF
1198  files(num_files)%next_open=diag_time_inc(files(num_files)%start_time,new_file_freq1,new_file_freq_units1)
1199  files(num_files)%close_time = diag_time_inc(files(num_files)%start_time,file_duration1, file_duration_units1)
1200  IF ( files(num_files)%close_time>files(num_files)%next_open ) THEN
1201  ! <ERROR STATUS="FATAL">
1202  ! close time GREATER than next_open time, check file duration,
1203  ! file frequency in <files(num_files)%name>
1204  ! </ERROR>
1205  CALL error_mesg('diag_util_mod::init_file', 'close time GREATER than next_open time, check file duration,&
1206  & file frequency in '//files(num_files)%name, fatal)
1207  END IF
1208 
1209  ! add time_axis_id and time_bounds_id here
1210  WRITE(time_units_str, 11) trim(time_unit_list(files(num_files)%time_units)), base_year,&
1211  & base_month, base_day, base_hour, base_minute, base_second
1212 11 FORMAT(a, ' since ', i4.4, '-', i2.2, '-', i2.2, ' ', i2.2, ':', i2.2, ':', i2.2)
1213  files(num_files)%time_axis_id = diag_axis_init(trim(long_name), tdata, time_units_str, 'T',&
1214  & trim(long_name) , set_name=trim(name) )
1215  !---- register axis for storing time boundaries
1216  files(num_files)%time_bounds_id = diag_axis_init( 'nv',(/1.,2./),'none','N','vertex number',&
1217  & set_name='nv')
1218  END SUBROUTINE init_file
1219  ! </SUBROUTINE>
1220 
1221  ! <SUBROUTINE NAME="sync_file_times">
1222  ! <OVERVIEW>
1223  ! Synchronize the file's start and close times with the model start and end times.
1224  ! </OVERVIEW>
1225  ! <TEMPLATE>
1226  ! SUBROUTINE sync_file_times(init_time)
1227  ! </TEMPLATE>
1228  ! <DESCRIPTION>
1229  ! <TT>sync_file_times</TT> checks to see if the file start time is less than the
1230  ! model's init time (passed in as the only argument). If it is less, then the
1231  ! both the file start time and end time are synchronized using the passed in initial time
1232  ! and the duration as calculated by the <TT>diag_time_inc</TT> function. <TT>sync_file_times</TT>
1233  ! will also increase the <TT>next_open</TT> until it is greater than the init_time.
1234  ! </DESCRIPTION>
1235  ! <IN NAME="file_id" TYPE="INTEGER">The file ID</IN>
1236  ! <IN NAME="init_time" TYPE="TYPE(time_type)">Initial time use for the synchronization.</IN>
1237  ! <OUT NAME="err_msg" TYPE="CHARACTER(len=*), OPTIONAL">Return error message</OUT>
1238  SUBROUTINE sync_file_times(file_id, init_time, err_msg)
1239  INTEGER, INTENT(in) :: file_id
1240  TYPE(time_type), INTENT(in) :: init_time
1241  CHARACTER(len=*), OPTIONAL, INTENT(out) :: err_msg
1242 
1243  CHARACTER(len=128) :: msg
1244 
1245  IF ( PRESENT(err_msg) ) err_msg = ''
1246 
1247  IF ( files(file_id)%start_time < init_time ) THEN
1248  ! Sync the start_time of the file with the initial time of the model
1249  files(file_id)%start_time = init_time
1250  ! Sync the file's close time also
1251  files(file_id)%close_time = diag_time_inc(files(file_id)%start_time,&
1252  & files(file_id)%duration, files(file_id)%duration_units)
1253  END IF
1254 
1255  ! Need to increase next_open until it is greate than init_time
1256  DO WHILE ( files(file_id)%next_open <= init_time )
1257  files(file_id)%next_open = diag_time_inc(files(file_id)%next_open,&
1258  & files(file_id)%new_file_freq, files(file_id)%new_file_freq_units, err_msg=msg)
1259  IF ( msg /= '' ) THEN
1260  IF ( fms_error_handler('diag_util_mod::sync_file_times',&
1261  & ' file='//trim(files(file_id)%name)//': '//trim(msg), err_msg) ) RETURN
1262  END IF
1263  END DO
1264  END SUBROUTINE sync_file_times
1265  ! </SUBROUTINE>
1266 
1267  ! <FUNCTION NAME="diag_time_inc">
1268  ! <OVERVIEW>
1269  ! Return the next time data/file is to be written based on the frequency and units.
1270  ! </OVERVIEW>
1271  ! <TEMPLATE>
1272  ! TYPE(time_type) FUNCTION diag_time_inc(time, output_freq, output_units, err_msg)
1273  ! </TEMPLATE>
1274  ! <DESCRIPTION>
1275  ! Return the next time data/file is to be written. This value is based on the current time and the frequency and units.
1276  ! Function completed successful if the optional <TT>err_msg</TT> is empty.
1277  ! </DESCRIPTION>
1278  ! <IN NAME="time" TYPE="TYPE(time_type)">Current model time.</IN>
1279  ! <IN NAME="output_freq" TYPE="INTEGER">Output frequency number value.</IN>
1280  ! <IN NAME="output_units" TYPE="INTEGER">Output frequency unit.</IN>
1281  ! <OUT NAME="err_msg" TYPE="CHARACTER, OPTIONAL">
1282  ! Function error message. An empty string indicates the next output time was found successfully.
1283  ! </OUT>
1284  TYPE(time_type) function diag_time_inc(time, output_freq, output_units, err_msg)
1285  TYPE(time_type), INTENT(in) :: time
1286  INTEGER, INTENT(in):: output_freq, output_units
1287  CHARACTER(len=*), INTENT(out), OPTIONAL :: err_msg
1288 
1289  CHARACTER(len=128) :: error_message_local
1290 
1291  IF ( PRESENT(err_msg) ) err_msg = ''
1292  error_message_local = ''
1293 
1294  ! special values for output frequency are -1 for output at end of run
1295  ! and 0 for every timestep. Need to check for these here?
1296  ! Return zero time increment, hopefully this value is never used
1297  IF ( output_freq == end_of_run .OR. output_freq == every_time ) THEN
1298  diag_time_inc = time
1299  RETURN
1300  END IF
1301 
1302  ! Make sure calendar was not set after initialization
1303  IF ( output_units == diag_seconds ) THEN
1304  IF ( get_calendar_type() == no_calendar ) THEN
1305  diag_time_inc = increment_time(time, output_freq, 0, err_msg=error_message_local)
1306  ELSE
1307  diag_time_inc = increment_date(time, 0, 0, 0, 0, 0, output_freq, err_msg=error_message_local)
1308  END IF
1309  ELSE IF ( output_units == diag_minutes ) THEN
1310  IF ( get_calendar_type() == no_calendar ) THEN
1311  diag_time_inc = increment_time(time, nint(output_freq*seconds_per_minute), 0, &
1312  &err_msg=error_message_local)
1313  ELSE
1314  diag_time_inc = increment_date(time, 0, 0, 0, 0, output_freq, 0, err_msg=error_message_local)
1315  END IF
1316  ELSE IF ( output_units == diag_hours ) THEN
1317  IF ( get_calendar_type() == no_calendar ) THEN
1318  diag_time_inc = increment_time(time, nint(output_freq*seconds_per_hour), 0, err_msg=error_message_local)
1319  ELSE
1320  diag_time_inc = increment_date(time, 0, 0, 0, output_freq, 0, 0, err_msg=error_message_local)
1321  END IF
1322  ELSE IF ( output_units == diag_days ) THEN
1323  IF (get_calendar_type() == no_calendar) THEN
1324  diag_time_inc = increment_time(time, 0, output_freq, err_msg=error_message_local)
1325  ELSE
1326  diag_time_inc = increment_date(time, 0, 0, output_freq, 0, 0, 0, err_msg=error_message_local)
1327  END IF
1328  ELSE IF ( output_units == diag_months ) THEN
1329  IF (get_calendar_type() == no_calendar) THEN
1330  error_message_local = 'output units of months NOT allowed with no calendar'
1331  ELSE
1332  diag_time_inc = increment_date(time, 0, output_freq, 0, 0, 0, 0, err_msg=error_message_local)
1333  END IF
1334  ELSE IF ( output_units == diag_years ) THEN
1335  IF ( get_calendar_type() == no_calendar ) THEN
1336  error_message_local = 'output units of years NOT allowed with no calendar'
1337  ELSE
1338  diag_time_inc = increment_date(time, output_freq, 0, 0, 0, 0, 0, err_msg=error_message_local)
1339  END IF
1340  ELSE
1341  error_message_local = 'illegal output units'
1342  END IF
1343 
1344  IF ( error_message_local /= '' ) THEN
1345  IF ( fms_error_handler('diag_time_inc',error_message_local,err_msg) ) RETURN
1346  END IF
1347  END FUNCTION diag_time_inc
1348  ! </FUNCTION>
1349 
1350  ! <PRIVATE>
1351  ! <FUNCTION NAME="find_file">
1352  ! <OVERVIEW>
1353  ! Return the file number for file name and tile.
1354  ! </OVERVIEW>
1355  ! <TEMPLATE>
1356  ! INTEGER FUNCTION fild_file(name, time_count)
1357  ! </TEMPLATE>
1358  ! <DESCRIPTION>
1359  ! Find the file number for the file name and tile number given. A return value of <TT>-1</TT> indicates the file was not found.
1360  ! </DESCRIPTION>
1361  ! <IN NAME="name=" TYPE="CHARACTER(len=*)">File name.</IN>
1362  ! <IN NAME="tile_count" TYPE="INTEGER">Tile number.</IN>
1363  INTEGER FUNCTION find_file(name, tile_count)
1364  INTEGER, INTENT(in) :: tile_count
1365  CHARACTER(len=*), INTENT(in) :: name
1366 
1367  INTEGER :: i
1368 
1369  find_file = -1
1370  DO i = 1, num_files
1371  IF( trim(files(i)%name) == trim(name) .AND. tile_count == files(i)%tile_count ) THEN
1372  find_file = i
1373  RETURN
1374  END IF
1375  END DO
1376  END FUNCTION find_file
1377  ! </FUNCTION>
1378  ! </PRIVATE>
1379 
1380  ! <FUNCTION NAME="find_input_field">
1381  ! <OVERVIEW>
1382  ! Return the field number for the given module name, field name, and tile number.
1383  ! </OVERVIEW>
1384  ! <TEMPLATE>
1385  ! INTEGER FUNCTION find_input_field(module_name, field_name, tile_count)
1386  ! </TEMPLATE>
1387  ! <DESCRIPTION>
1388  ! Return the field number for the given module name, field name and tile number. A return value of <TT>-1</TT> indicates
1389  ! the field was not found.
1390  ! </DESCRIPTION>
1391  ! <IN NAME="module_name" TYPE="CHARACTER(len=*)">Module name.</IN>
1392  ! <IN NAME="field_name" TYPE="CHARACTER(len=*)">field name.</IN>
1393  ! <IN NAME="tile_count" TYPE="INTEGER">Tile number.</IN>
1394  INTEGER FUNCTION find_input_field(module_name, field_name, tile_count)
1395  CHARACTER(len=*), INTENT(in) :: module_name, field_name
1396  INTEGER, INTENT(in) :: tile_count
1397 
1398  INTEGER :: i
1399 
1400  find_input_field = diag_field_not_found ! Default return value if not found.
1401  DO i = 1, num_input_fields
1402  IF(tile_count == input_fields(i)%tile_count .AND.&
1403  & trim(input_fields(i)%module_name) == trim(module_name) .AND.&
1404  & lowercase(trim(input_fields(i)%field_name)) == lowercase(trim(field_name))) THEN
1405  find_input_field = i
1406  RETURN
1407  END IF
1408  END DO
1409  END FUNCTION find_input_field
1410  ! </FUNCTION>
1411 
1412  ! <SUBROUTINE NAME="init_input_field">
1413  ! <OVERVIEW>
1414  ! Initialize the input field.
1415  ! </OVERVIEW>
1416  ! <TEMPLATE>
1417  ! SUBROUTINE init_input_field(module_name, field_name, tile_count)
1418  ! </TEMPLATE>
1419  ! Initialize the input field.
1420  ! <DESCRIPTION>
1421  ! </DESCRIPTION>
1422  ! <IN NAME="module_name" TYPE="CHARACTER(len=*)">Module name.</IN>
1423  ! <IN NAME="field_name" TYPE="CHARACTER(len=*)">Input field name.</IN>
1424  ! <IN NAME="tile_count" TYPE="INTEGER">Tile number.</IN>
1425  SUBROUTINE init_input_field(module_name, field_name, tile_count)
1426  CHARACTER(len=*), INTENT(in) :: module_name, field_name
1427  INTEGER, INTENT(in) :: tile_count
1428 
1429  ! Get a number for this input_field if not already set up
1430  IF ( find_input_field(module_name, field_name, tile_count) < 0 ) THEN
1431  num_input_fields = num_input_fields + 1
1432  IF ( num_input_fields > max_input_fields ) THEN
1433  ! <ERROR STATUS="FATAL">max_input_fields exceeded, increase it via diag_manager_nml</ERROR>
1434  CALL error_mesg('diag_util_mod::init_input_field',&
1435  & 'max_input_fields exceeded, increase it via diag_manager_nml', fatal)
1436  END IF
1437  ELSE
1438  ! If this is already initialized do not need to do anything
1439  RETURN
1440  END IF
1441 
1442  input_fields(num_input_fields)%module_name = trim(module_name)
1443  input_fields(num_input_fields)%field_name = trim(field_name)
1444  input_fields(num_input_fields)%num_output_fields = 0
1445  ! Set flag that this field has not been registered
1446  input_fields(num_input_fields)%register = .false.
1447  input_fields(num_input_fields)%local = .false.
1448  input_fields(num_input_fields)%standard_name = 'none'
1449  input_fields(num_input_fields)%tile_count = tile_count
1450  input_fields(num_input_fields)%numthreads = 1
1451  input_fields(num_input_fields)%active_omp_level = 0
1452  input_fields(num_input_fields)%time = time_zero
1453  END SUBROUTINE init_input_field
1454  ! </SUBROUTINE>
1455 
1456  ! <SUBROUTINE NAME="init_output_field">
1457  ! <OVERVIEW>
1458  ! Initialize the output field.
1459  ! </OVERVIEW>
1460  ! <TEMPLATE>
1461  ! SUBROUTINE init_output_field(module_name, field_name, output_name, output_file
1462  ! time_method, pack, tile_count, local_coord)
1463  ! </TEMPLATE>
1464  ! <DESCRIPTION>
1465  ! Initialize the output field.
1466  ! </DESCRIPTION>
1467  ! <IN NAME="module_name" TYPE="CHARACTER(len=*)">Module name.</IN>
1468  ! <IN NAME="field_name" TYPE="CHARACTER(len=*)">Output field name.</IN>
1469  ! <IN NAME="output_name" TYPE="CHARACTER(len=*)">Output name written to file.</IN>
1470  ! <IN NAME="output_file" TYPE="CHARACTER(len=*)">File where field should be written.</IN>
1471  ! <IN NAME="time_method" TYPE="CHARACTER(len=*)">
1472  ! Data reduction method. See <LINK SRC="diag_manager.html">diag_manager_mod</LINK> for valid methods.</IN>
1473  ! <IN NAME="pack" TYPE="INTEGER">Packing method.</IN>
1474  ! <IN NAME="tile_count" TYPE="INTEGER">Tile number.</IN>
1475  ! <IN NAME="local_coord" TYPE="INTEGER, OPTIONAL">Region to be written. If missing, then all data to be written.</IN>
1476  SUBROUTINE init_output_field(module_name, field_name, output_name, output_file,&
1477  & time_method, pack, tile_count, local_coord)
1478  CHARACTER(len=*), INTENT(in) :: module_name, field_name, output_name, output_file
1479  CHARACTER(len=*), INTENT(in) :: time_method
1480  INTEGER, INTENT(in) :: pack
1481  INTEGER, INTENT(in) :: tile_count
1482  TYPE(coord_type), INTENT(in), OPTIONAL :: local_coord
1483  INTEGER :: out_num, in_num, file_num, file_num_tile1
1484  INTEGER :: num_fields, i, method_selected, l1
1485  INTEGER :: ioerror
1486  REAL :: pow_value
1487  INTEGER :: grv !< Value used to determine if the region defined in the diag_table is for the whole axis, or a sub-axis
1488  CHARACTER(len=128) :: error_msg
1489  CHARACTER(len=50) :: t_method
1490  character(len=256) :: tmp_name
1491 
1492  ! Value to use to determine if a region is to be output on the full axis, or sub-axis
1493  ! get the value to compare to determine if writing full axis data
1494  IF ( region_out_use_alt_value ) THEN
1495  grv = glo_reg_val_alt
1496  ELSE
1497  grv = glo_reg_val
1498  END IF
1499 
1500 
1501  ! Get a number for this output field
1502  num_output_fields = num_output_fields + 1
1503  IF ( num_output_fields > max_output_fields ) THEN
1504  ! <ERROR STATUS="FATAL">max_output_fields = <max_output_fields> exceeded. Increase via diag_manager_nml</ERROR>
1505  WRITE (unit=error_msg,fmt=*) max_output_fields
1506  CALL error_mesg('diag_util_mod::init_output_field', 'max_output_fields = '//trim(error_msg)//' exceeded.&
1507  & Increase via diag_manager_nml', fatal)
1508  END IF
1509  out_num = num_output_fields
1510 
1511  ! First, find the index to the associated input field
1512  in_num = find_input_field(module_name, field_name, tile_count)
1513  IF ( in_num < 0 ) THEN
1514  IF ( tile_count > 1 ) THEN
1515  WRITE (error_msg,'(A,"/",A,"/",A)') trim(module_name),trim(field_name),&
1516  & "tile_count="//trim(string(tile_count))
1517  ELSE
1518  WRITE (error_msg,'(A,"/",A)') trim(module_name),trim(field_name)
1519  END IF
1520  ! <ERROR STATUS="FATAL">module_name/field_name <module_name>/<field_name>[/tile_count=<tile_count>] NOT registered</ERROR>
1521  CALL error_mesg('diag_util_mod::init_output_field',&
1522  & 'module_name/field_name '//trim(error_msg)//' NOT registered', fatal)
1523  END IF
1524 
1525  ! Add this output field into the list for this input field
1526  input_fields(in_num)%num_output_fields =&
1527  & input_fields(in_num)%num_output_fields + 1
1528  IF ( input_fields(in_num)%num_output_fields > max_out_per_in_field ) THEN
1529  ! <ERROR STATUS="FATAL">
1530  ! MAX_OUT_PER_IN_FIELD = <MAX_OUT_PER_IN_FIELD> exceeded for <module_name>/<field_name>, increase MAX_OUT_PER_IN_FIELD
1531  ! in the diag_manager_nml namelist.
1532  ! </ERROR>
1533  WRITE (unit=error_msg,fmt=*) max_out_per_in_field
1534  CALL error_mesg('diag_util_mod::init_output_field',&
1535  & 'MAX_OUT_PER_IN_FIELD exceeded for '//trim(module_name)//"/"//trim(field_name)//&
1536  &', increase MAX_OUT_PER_IN_FIELD in the diag_manager_nml namelist', fatal)
1537  END IF
1538  input_fields(in_num)%output_fields(input_fields(in_num)%num_output_fields) = out_num
1539 
1540  ! Also put pointer to input field in this output field
1541  output_fields(out_num)%input_field = in_num
1542 
1543  ! Next, find the number for the corresponding file
1544  IF ( trim(output_file).EQ.'null' ) THEN
1545  file_num = max_files
1546  ELSE
1547  file_num = find_file(output_file, 1)
1548  IF ( file_num < 0 ) THEN
1549  ! <ERROR STATUS="FATAL">
1550  ! file <file_name> is NOT found in the diag_table.
1551  ! </ERROR>
1552  CALL error_mesg('diag_util_mod::init_output_field', 'file '&
1553  & //trim(output_file)//' is NOT found in the diag_table', fatal)
1554  END IF
1555  IF ( tile_count > 1 ) THEN
1556  file_num_tile1 = file_num
1557  file_num = find_file(output_file, tile_count)
1558  IF(file_num < 0) THEN
1559  CALL init_file(files(file_num_tile1)%name, files(file_num_tile1)%output_freq,&
1560  & files(file_num_tile1)%output_units, files(file_num_tile1)%format,&
1561  & files(file_num_tile1)%time_units, files(file_num_tile1)%long_name,&
1562  & tile_count, files(file_num_tile1)%new_file_freq,&
1563  & files(file_num_tile1)%new_file_freq_units, files(file_num_tile1)%start_time,&
1564  & files(file_num_tile1)%duration, files(file_num_tile1)%duration_units )
1565  file_num = find_file(output_file, tile_count)
1566  IF ( file_num < 0 ) THEN
1567  ! <ERROR STATUS="FATAL">
1568  ! file <output_file> is not initialized for tile_count = <tile_count>
1569  ! </ERROR>
1570  CALL error_mesg('diag_util_mod::init_output_field', 'file '//trim(output_file)//&
1571  & ' is not initialized for tile_count = '//trim(string(tile_count)), fatal)
1572  END IF
1573  END IF
1574  END IF
1575  END IF
1576 
1577  ! Insert this field into list for this file
1578  files(file_num)%num_fields = files(file_num)%num_fields + 1
1579  IF ( files(file_num)%num_fields > max_fields_per_file ) THEN
1580  WRITE (unit=error_msg, fmt=*) max_fields_per_file
1581  ! <ERROR STATUS="FATAL">
1582  ! MAX_FIELDS_PER_FILE = <MAX_FIELDS_PER_FILE> exceeded. Increase MAX_FIELDS_PER_FILE in diag_data.F90.
1583  ! </ERROR>
1584  CALL error_mesg('diag_util_mod::init_output_field',&
1585  & 'MAX_FIELDS_PER_FILE = '//trim(error_msg)//' exceeded. Increase MAX_FIELDS_PER_FILE in diag_data.F90.', fatal)
1586  END IF
1587  num_fields = files(file_num)%num_fields
1588  files(file_num)%fields(num_fields) = out_num
1589 
1590  ! Set the file for this output field
1591  output_fields(out_num)%output_file = file_num
1592 
1593  ! Enter the other data for this output field
1594  output_fields(out_num)%output_name = trim(output_name)
1595  output_fields(out_num)%pack = pack
1596  output_fields(out_num)%pow_value = 1
1597  output_fields(out_num)%num_axes = 0
1598  output_fields(out_num)%total_elements = 0
1599  output_fields(out_num)%region_elements = 0
1600  output_fields(out_num)%imax = 0
1601  output_fields(out_num)%jmax = 0
1602  output_fields(out_num)%kmax = 0
1603  output_fields(out_num)%imin = very_large_axis_length
1604  output_fields(out_num)%jmin = very_large_axis_length
1605  output_fields(out_num)%kmin = very_large_axis_length
1606 
1607  ! initialize the size of the diurnal axis to 1
1608  output_fields(out_num)%n_diurnal_samples = 1
1609 
1610  ! Initialize all time method to false
1611  method_selected = 0
1612  output_fields(out_num)%time_average = .false.
1613  output_fields(out_num)%time_rms = .false.
1614  output_fields(out_num)%time_min = .false.
1615  output_fields(out_num)%time_max = .false.
1616  output_fields(out_num)%time_sum = .false.
1617  output_fields(out_num)%time_ops = .false.
1618  output_fields(out_num)%written_once = .false.
1619 
1620  t_method = lowercase(time_method)
1621  ! cannot time average fields output every time
1622  IF ( files(file_num)%output_freq == every_time ) THEN
1623  output_fields(out_num)%time_average = .false.
1624  method_selected = method_selected+1
1625  t_method = 'point'
1626  ELSEIF ( index(t_method,'diurnal') == 1 ) THEN
1627  ! get the integer number from the t_method
1628  READ (unit=t_method(8:len_trim(t_method)), fmt=*, iostat=ioerror) output_fields(out_num)%n_diurnal_samples
1629  IF ( ioerror /= 0 ) THEN
1630  ! <ERROR STATUS="FATAL">
1631  ! could not find integer number of diurnal samples in string "<t_method>"
1632  ! </ERROR>
1633  CALL error_mesg('diag_util_mod::init_output_field',&
1634  & 'could not find integer number of diurnal samples in string "' //trim(t_method)//'"', fatal)
1635  ELSE IF ( output_fields(out_num)%n_diurnal_samples <= 0 ) THEN
1636  ! <ERROR STATUS="FATAL">
1637  ! The integer value of diurnal samples must be greater than zero.
1638  ! </ERROR>
1639  CALL error_mesg('diag_util_mod::init_output_field',&
1640  & 'The integer value of diurnal samples must be greater than zero.', fatal)
1641  END IF
1642  output_fields(out_num)%time_average = .true.
1643  method_selected = method_selected+1
1644  t_method='mean'
1645  ELSEIF ( index(t_method,'pow') == 1 ) THEN
1646  ! Get the integer number from the t_method
1647  READ (unit=t_method(4:len_trim(t_method)), fmt=*, iostat=ioerror) pow_value
1648  IF ( ioerror /= 0 .OR. output_fields(out_num)%pow_value < 1 .OR. floor(pow_value) /= ceiling(pow_value) ) THEN
1649  ! <ERROR STATUS="FATAL">
1650  ! Invalid power number in time operation "<t_method>". Must be a positive integer.
1651  ! </ERROR>
1652  CALL error_mesg('diag_util_mod::init_output_field',&
1653  & 'Invalid power number in time operation "'//trim(t_method)//'". Must be a positive integer', fatal)
1654  END IF
1655  output_fields(out_num)%pow_value = int(pow_value)
1656  output_fields(out_num)%time_average = .true.
1657  method_selected = method_selected+1
1658  t_method = 'mean_pow('//t_method(4:len_trim(t_method))//')'
1659  ELSE
1660  SELECT CASE(trim(t_method))
1661  CASE ( '.true.', 'mean', 'average', 'avg' )
1662  output_fields(out_num)%time_average = .true.
1663  method_selected = method_selected+1
1664  t_method = 'mean'
1665  CASE ( 'rms' )
1666  output_fields(out_num)%time_average = .true.
1667  output_fields(out_num)%time_rms = .true.
1668  output_fields(out_num)%pow_value = 2.0
1669  method_selected = method_selected+1
1670  t_method = 'root_mean_square'
1671  CASE ( '.false.', 'none', 'point' )
1672  output_fields(out_num)%time_average = .false.
1673  method_selected = method_selected+1
1674  t_method = 'point'
1675  CASE ( 'maximum', 'max' )
1676  output_fields(out_num)%time_max = .true.
1677  l1 = len_trim(output_fields(out_num)%output_name)
1678  if (l1 .ge. 3) then
1679  tmp_name = trim(adjustl(output_fields(out_num)%output_name(l1-2:l1)))
1680  IF (lowercase(trim(tmp_name)) /= 'max' ) then
1681  output_fields(out_num)%output_name = trim(output_name)//'_max'
1682  endif
1683  endif
1684  method_selected = method_selected+1
1685  t_method = 'max'
1686  CASE ( 'minimum', 'min' )
1687  output_fields(out_num)%time_min = .true.
1688  l1 = len_trim(output_fields(out_num)%output_name)
1689  if (l1 .ge. 3) then
1690  tmp_name = trim(adjustl(output_fields(out_num)%output_name(l1-2:l1)))
1691  IF (lowercase(trim(tmp_name)) /= 'min' ) then
1692  output_fields(out_num)%output_name = trim(output_name)//'_min'
1693  endif
1694  endif
1695  method_selected = method_selected+1
1696  t_method = 'min'
1697  CASE ( 'sum', 'cumsum' )
1698  output_fields(out_num)%time_sum = .true.
1699  l1 = len_trim(output_fields(out_num)%output_name)
1700  IF ( output_fields(out_num)%output_name(l1-2:l1) /= 'sum' )&
1701  & output_fields(out_num)%output_name = trim(output_name)//'_sum'
1702  method_selected = method_selected+1
1703  t_method = 'sum'
1704  END SELECT
1705  END IF
1706 
1707  ! reconcile logical flags
1708  output_fields(out_num)%time_ops = output_fields(out_num)%time_min.OR.output_fields(out_num)%time_max&
1709  & .OR.output_fields(out_num)%time_average .OR. output_fields(out_num)%time_sum
1710 
1711  output_fields(out_num)%phys_window = .false.
1712  ! need to initialize grid_type = -1(start, end, l_start_indx,l_end_indx etc...)
1713  IF ( PRESENT(local_coord) ) THEN
1714  input_fields(in_num)%local = .true.
1715  input_fields(in_num)%local_coord = local_coord
1716  IF ( int(local_coord%xbegin) == grv .AND. int(local_coord%xend) == grv .AND.&
1717  & int(local_coord%ybegin) == grv .AND. int(local_coord%yend) == grv ) THEN
1718  output_fields(out_num)%local_output = .false.
1719  output_fields(out_num)%need_compute = .false.
1720  output_fields(out_num)%reduced_k_range = .true.
1721  ELSE
1722  output_fields(out_num)%local_output = .true.
1723  output_fields(out_num)%need_compute = .false.
1724  output_fields(out_num)%reduced_k_range = .false.
1725  END IF
1726 
1727  output_fields(out_num)%output_grid%start(1) = local_coord%xbegin
1728  output_fields(out_num)%output_grid%start(2) = local_coord%ybegin
1729  output_fields(out_num)%output_grid%start(3) = local_coord%zbegin
1730  output_fields(out_num)%output_grid%end(1) = local_coord%xend
1731  output_fields(out_num)%output_grid%end(2) = local_coord%yend
1732  output_fields(out_num)%output_grid%end(3) = local_coord%zend
1733  DO i = 1, 3
1734  output_fields(out_num)%output_grid%l_start_indx(i) = -1
1735  output_fields(out_num)%output_grid%l_end_indx(i) = -1
1736  output_fields(out_num)%output_grid%subaxes(i) = -1
1737  END DO
1738  ELSE
1739  output_fields(out_num)%local_output = .false.
1740  output_fields(out_num)%need_compute = .false.
1741  output_fields(out_num)%reduced_k_range = .false.
1742  END IF
1743 
1744  ! <ERROR STATUS="FATAL">
1745  ! improper time method in diag_table for output field <output_name>
1746  ! </ERROR>
1747  IF ( method_selected /= 1 ) CALL error_mesg('diag_util_mod::init_output_field',&
1748  &'improper time method in diag_table for output field:'//trim(output_name),fatal)
1749 
1750  output_fields(out_num)%time_method = trim(t_method)
1751 
1752  ! allocate counters: NOTE that for simplicity we always allocate them, even
1753  ! if they are superceeded by 4D "counter" array. This isn't most memory
1754  ! efficient, approach, but probably tolerable since they are so small anyway
1755  ALLOCATE(output_fields(out_num)%count_0d(output_fields(out_num)%n_diurnal_samples))
1756  ALLOCATE(output_fields(out_num)%num_elements(output_fields(out_num)%n_diurnal_samples))
1757  output_fields(out_num)%count_0d(:) = 0
1758  output_fields(out_num)%num_elements(:) = 0
1759  output_fields(out_num)%num_attributes = 0
1760  END SUBROUTINE init_output_field
1761  ! </SUBROUTINE>
1762 
1763  ! <PRIVATE>
1764  ! <SUBROUTINE NAME="opening_file">
1765  ! <OVERVIEW>
1766  ! Open file for output.
1767  ! </OVERVIEW>
1768  ! <TEMPLATE>
1769  ! SUBROUTINE opening_file(file, time)
1770  ! </TEMPLATE>
1771  ! <DESCRIPTION>
1772  ! Open file for output, and write the meta data. <BB>Warning:</BB> Assumes all data structures have been fully initialized.
1773  ! </DESCRIPTION>
1774  ! <IN NAME="file" TYPE="INTEGER">File ID.</IN>
1775  ! <IN NAME="tile" TYPE="TYPE(time_type)">Time for the file time stamp</IN>
1776  SUBROUTINE opening_file(file, time)
1777  ! WARNING: Assumes that all data structures are fully initialized
1778  INTEGER, INTENT(in) :: file
1779  TYPE(time_type), INTENT(in) :: time
1780 
1781  REAL, DIMENSION(2) :: DATA
1782  INTEGER :: j, field_num, input_field_num, num_axes, k
1783  INTEGER :: field_num1
1784  INTEGER :: position
1785  INTEGER :: dir, edges
1786  INTEGER :: ntileMe
1787  INTEGER :: year, month, day, hour, minute, second
1788  INTEGER, ALLOCATABLE :: tile_id(:)
1789  INTEGER, DIMENSION(1) :: time_axis_id, time_bounds_id
1790  ! size of this axes array must be at least max num. of
1791  ! axes per field + 2; the last two elements are for time
1792  ! and time bounds dimensions
1793  INTEGER, DIMENSION(6) :: axes
1794  INTEGER, ALLOCATABLE :: axesc(:) ! indices if compressed axes associated with the field
1795  LOGICAL :: time_ops, aux_present, match_aux_name, req_present, match_req_fields
1796  LOGICAL :: all_scalar_or_1d
1797  CHARACTER(len=7) :: prefix
1798  CHARACTER(len=7) :: avg_name = 'average'
1799  CHARACTER(len=128) :: time_units, timeb_units, avg, error_string, filename, aux_name, req_fields, fieldname
1800  CHARACTER(len=128) :: suffix, base_name
1801  CHARACTER(len=32) :: time_name, timeb_name,time_longname, timeb_longname, cart_name
1802  CHARACTER(len=256) :: fname
1803  CHARACTER(len=24) :: start_date
1804  TYPE(domain1d) :: domain
1805  TYPE(domain2d) :: domain2
1806  TYPE(domainUG) :: domainU
1807  INTEGER :: is, ie, last, ind
1808 
1809 
1810  aux_present = .false.
1811  match_aux_name = .false.
1812  req_present = .false.
1813  match_req_fields = .false.
1814 
1815  ! Here is where time_units string must be set up; time since base date
1816  WRITE (time_units, 11) trim(time_unit_list(files(file)%time_units)), base_year,&
1817  & base_month, base_day, base_hour, base_minute, base_second
1818 11 FORMAT(a, ' since ', i4.4, '-', i2.2, '-', i2.2, ' ', i2.2, ':', i2.2, ':', i2.2)
1819  base_name = files(file)%name
1820  IF ( files(file)%new_file_freq < very_large_file_freq ) THEN
1821  position = index(files(file)%name, '%')
1822  IF ( position > 0 ) THEN
1823  base_name = base_name(1:position-1)
1824  ELSE
1825  ! <ERROR STATUS="FATAL">
1826  ! filename <files(file)%name> does not contain % for time stamp string
1827  ! </ERROR>
1828  CALL error_mesg('diag_util_mod::opening_file',&
1829  & 'file name '//trim(files(file)%name)//' does not contain % for time stamp string', fatal)
1830  END IF
1831  suffix = get_time_string(files(file)%name, time)
1832  ELSE
1833  suffix = ' '
1834  END IF
1835 
1836  ! Add ensemble ID to filename
1837  fname=base_name
1838  call get_instance_filename(fname, base_name)
1839 
1840  ! Set the filename
1841  filename = trim(base_name)//trim(suffix)
1842 
1843  ! prepend the file start date if prepend_date == .TRUE.
1844  IF ( prepend_date ) THEN
1845  call get_date(diag_init_time, year, month, day, hour, minute, second)
1846  write (start_date, '(1I20.4, 2I2.2)') year, month, day
1847 
1848  filename = trim(adjustl(start_date))//'.'//trim(filename)
1849  END IF
1850 
1851  ! Loop through all fields with this file to output axes
1852  ! JWD: This is a klooge; need something more robust
1853  domain2 = null_domain2d
1854  domainu = null_domainug
1855  all_scalar_or_1d = .true.
1856  DO j = 1, files(file)%num_fields
1857  field_num = files(file)%fields(j)
1858  if (output_fields(field_num)%local_output .AND. .NOT. output_fields(field_num)%need_compute) cycle
1859  num_axes = output_fields(field_num)%num_axes
1860  IF ( num_axes > 1 ) THEN
1861  all_scalar_or_1d = .false.
1862  domain2 = get_domain2d( output_fields(field_num)%axes(1:num_axes) )
1863  domainu = get_domainug( output_fields(field_num)%axes(1) )
1864  IF ( domain2 .NE. null_domain2d ) EXIT
1865  ELSEIF (num_axes == 1) THEN
1866  if (domainu .EQ. null_domainug) then
1867  domainu = get_domainug( output_fields(field_num)%axes(num_axes) )
1868  endif
1869  END IF
1870  END DO
1871 
1872  IF (.NOT. all_scalar_or_1d) THEN
1873  IF (domainu .NE. null_domainug .AND. domain2 .NE. null_domain2d) THEN
1874  CALL error_mesg('diag_util_mod::opening_file', &
1875  'Domain2 and DomainU are somehow both set.', &
1876  fatal)
1877  ELSEIF (domainu .EQ. null_domainug) THEN
1878  IF (domain2 .EQ. null_domain2d) THEN
1879  CALL return_domain(domain2)
1880  ENDIF
1881 
1882  IF (domain2 .EQ. null_domain2d) THEN
1883 
1884  !Fix for the corner-case when you have a file that contains
1885  !2D field(s) that is not associated with a domain tile, as
1886  !is usually assumed.
1887 
1888  !This is very confusing, but I will try to explain. The
1889  !all_scalar_or_1d flag determines if the file name is associated
1890  !with a domain (i.e. has ".tilex." in the file name). A value
1891  !of .FALSE. for the all_scalar_or_1d flag signals that the
1892  !file name is associated with a domain tile. Normally,
1893  !files that contain at least one two-dimensional field are
1894  !assumed to be associated with a specific domain tile, and
1895  !thus have the value of the all_scalar_or_1d flag set to
1896  !.FALSE. It is possible, however, to have a file that contains
1897  !two-dimensional fields that is not associated with a domain tile
1898  !(i.e., if you make it into this branch.). If that is the
1899  !case, then reset the all_scalar_or_1d flag back to .TRUE.
1900  !Got that?
1901  all_scalar_or_1d = .true.
1902 
1903  ELSE
1904  ntileme = mpp_get_current_ntile(domain2)
1905  ALLOCATE(tile_id(ntileme))
1906  tile_id = mpp_get_tile_id(domain2)
1907  fname = trim(filename)
1908  IF ( mpp_get_ntile_count(domain2) > 1 ) THEN
1909  CALL get_tile_string(filename, trim(fname)//'.tile' , tile_id(files(file)%tile_count))
1910  ELSEIF ( tile_id(1) > 1 ) then
1911  CALL get_tile_string(filename, trim(fname)//'.tile' , tile_id(1))
1912  ENDIF
1913  DEALLOCATE(tile_id)
1914  ENDIF
1915  ENDIF
1916  ENDIF
1917  IF ( domainu .ne. null_domainug) then
1918 ! ntileMe = mpp_get_UG_current_ntile(domainU)
1919 ! ALLOCATE(tile_id(ntileMe))
1920 ! tile_id = mpp_get_UG_tile_id(domainU)
1921 ! fname = TRIM(filename)
1922 ! ntiles = mpp_get_UG_domain_ntiles(domainU)
1923 ! my_tile_id = mpp_get_UG_domain_tile_id(domainU)
1924 ! CALL get_tile_string(filename, TRIM(fname)//'.tile' , tile_id(files(file)%tile_count))
1925 ! DEALLOCATE(tile_id)
1926  fname = trim(filename)
1927  CALL get_mosaic_tile_file_ug(fname,filename,domainu)
1928  ENDIF
1929  IF ( _allocated(files(file)%attributes) ) THEN
1930  CALL diag_output_init(filename, files(file)%format, global_descriptor,&
1931  & files(file)%file_unit, all_scalar_or_1d, domain2, domainu,&
1932  & attributes=files(file)%attributes(1:files(file)%num_attributes))
1933  ELSE
1934  CALL diag_output_init(filename, files(file)%format, global_descriptor,&
1935  & files(file)%file_unit, all_scalar_or_1d, domain2,domainu)
1936  END IF
1937  files(file)%bytes_written = 0
1938  ! Does this file contain time_average fields?
1939  time_ops = .false.
1940  DO j = 1, files(file)%num_fields
1941  field_num = files(file)%fields(j)
1942  IF ( output_fields(field_num)%time_ops ) THEN
1943  time_ops = .true.
1944  EXIT
1945  END IF
1946  END DO
1947  ! Loop through all fields with this file to output axes
1948  DO j = 1, files(file)%num_fields
1949  field_num = files(file)%fields(j)
1950  input_field_num = output_fields(field_num)%input_field
1951  IF (.NOT.input_fields(input_field_num)%register) THEN
1952  WRITE (error_string,'(A,"/",A)') trim(input_fields(input_field_num)%module_name),&
1953  & trim(input_fields(input_field_num)%field_name)
1954  IF(mpp_pe() .EQ. mpp_root_pe()) THEN
1955  ! <ERROR STATUS="WARNING">
1956  ! module/field_name (<input_fields(input_field_num)%module_name>/<input_fields(input_field_num)%field_name>)
1957  ! NOT registered
1958  ! </ERROR>
1959  CALL error_mesg('diag_util_mod::opening_file',&
1960  & 'module/field_name ('//trim(error_string)//') NOT registered', warning)
1961  END IF
1962  cycle
1963  END IF
1964  if (output_fields(field_num)%local_output .AND. .NOT. output_fields(field_num)%need_compute) cycle
1965 
1966  ! Put the time axis in the axis field
1967  num_axes = output_fields(field_num)%num_axes
1968  axes(1:num_axes) = output_fields(field_num)%axes(1:num_axes)
1969  ! make sure that axis_id are not -1
1970  DO k = 1, num_axes
1971  IF ( axes(k) < 0 ) THEN
1972  WRITE(error_string,'(a)') output_fields(field_num)%output_name
1973  ! <ERROR STATUS="FATAL">
1974  ! ouptut_name <output_fields(field_num)%output_name> has axis_id = -1
1975  ! </ERROR>
1976  CALL error_mesg('diag_util_mod::opening_file','output_name '//trim(error_string)//&
1977  & ' has axis_id = -1', fatal)
1978  END IF
1979  END DO
1980  ! check if aux is present in any axes
1981  IF ( .NOT.aux_present ) THEN
1982  DO k = 1, num_axes
1983  aux_name = get_axis_aux(axes(k))
1984  IF ( trim(aux_name) /= 'none' ) THEN
1985  aux_present = .true.
1986  EXIT
1987  END IF
1988  END DO
1989  END IF
1990  ! check if required fields are present in any axes
1991  IF ( .NOT.req_present ) THEN
1992  DO k = 1, num_axes
1993  req_fields = get_axis_reqfld(axes(k))
1994  IF ( trim(req_fields) /= 'none' ) THEN
1995  CALL error_mesg('diag_util_mod::opening_file','required fields found: '//&
1996  &trim(req_fields)//' in file '//trim(files(file)%name),note)
1997  req_present = .true.
1998  EXIT
1999  END IF
2000  END DO
2001  END IF
2002 
2003  axes(num_axes + 1) = files(file)%time_axis_id
2004  CALL write_axis_meta_data(files(file)%file_unit, axes(1:num_axes + 1), time_ops)
2005  IF ( time_ops ) THEN
2006  axes(num_axes + 2) = files(file)%time_bounds_id
2007  CALL write_axis_meta_data(files(file)%file_unit, axes(1:num_axes + 2))
2008  END IF
2009  ! write metadata for axes used in compression-by-gathering, e.g. for unstructured
2010  ! grid
2011  DO k = 1, num_axes
2012  IF (axis_is_compressed(axes(k))) THEN
2013  CALL get_compressed_axes_ids(axes(k), axesc) ! returns allocatable array
2014  CALL write_axis_meta_data(files(file)%file_unit, axesc)
2015  DEALLOCATE(axesc)
2016  ENDIF
2017  ENDDO
2018 
2019  END DO
2020 
2021  ! Looking for the first NON-static field in a file
2022  field_num1 = files(file)%fields(1)
2023  DO j = 1, files(file)%num_fields
2024  field_num = files(file)%fields(j)
2025  IF ( output_fields(field_num)%time_ops ) THEN
2026  field_num1 = field_num
2027  EXIT
2028  END IF
2029  END DO
2030  DO j = 1, files(file)%num_fields
2031  field_num = files(file)%fields(j)
2032  input_field_num = output_fields(field_num)%input_field
2033  IF (.NOT.input_fields(input_field_num)%register) cycle
2034  IF (output_fields(field_num)%local_output .AND. .NOT. output_fields(field_num)%need_compute) cycle
2035  ! Make sure that 1 file contains either time_average or instantaneous fields
2036  ! cannot have both time_average and instantaneous in 1 file
2037  IF ( .NOT.mix_snapshot_average_fields ) THEN
2038  IF ( (output_fields(field_num)%time_ops.NEQV.output_fields(field_num1)%time_ops) .AND.&
2039  & .NOT.output_fields(field_num1)%static .AND. .NOT.output_fields(field_num)%static) THEN
2040  IF ( mpp_pe() == mpp_root_pe() ) THEN
2041  ! <ERROR STATUS="FATAL">
2042  ! <files(file)%name> can NOT have BOTH time average AND instantaneous fields.
2043  ! Create a new file or set mix_snapshot_average_fields=.TRUE. in the namelist diag_manager_nml.
2044  ! </ERROR>
2045  CALL error_mesg('diag_util_mod::opening_file','file '//&
2046  & trim(files(file)%name)//' can NOT have BOTH time average AND instantaneous fields.'//&
2047  & ' Create a new file or set mix_snapshot_average_fields=.TRUE. in the namelist diag_manager_nml.' , fatal)
2048  END IF
2049  END IF
2050  END IF
2051  ! check if any field has the same name as aux_name
2052  IF ( aux_present .AND. .NOT.match_aux_name ) THEN
2053  fieldname = output_fields(field_num)%output_name
2054  IF ( index(aux_name, trim(fieldname)) > 0 ) match_aux_name = .true.
2055  END IF
2056  ! check if any field has the same name as req_fields
2057  IF ( req_present .AND. .NOT.match_req_fields ) THEN
2058  fieldname = output_fields(field_num)%output_name
2059  is = 1; last = len_trim(req_fields)
2060  DO
2061  ind = index(req_fields(is:last),' ')
2062  IF (ind .eq. 0) ind = last-is+2
2063  ie = is+(ind-2)
2064  if (req_fields(is:ie) .EQ. trim(fieldname)) then
2065  match_req_fields = .true.
2066  !CALL error_mesg('diag_util_mod::opening_file','matched required field: '//TRIM(fieldname),NOTE)
2067  EXIT
2068  END IF
2069  is = is+ind
2070  if (is .GT. last) EXIT
2071  END DO
2072  END IF
2073 
2074  ! Put the time axis in the axis field
2075  num_axes = output_fields(field_num)%num_axes
2076  axes(1:num_axes) = output_fields(field_num)%axes(1:num_axes)
2077  IF ( .NOT.output_fields(field_num)%static ) THEN
2078  num_axes=num_axes+1
2079  axes(num_axes) = files(file)%time_axis_id
2080  END IF
2081  IF(output_fields(field_num)%time_average) THEN
2082  avg = avg_name
2083  ELSE IF(output_fields(field_num)%time_max) THEN
2084  avg = avg_name
2085  ELSE IF(output_fields(field_num)%time_min) THEN
2086  avg = avg_name
2087  ELSE
2088  avg = " "
2089  END IF
2090  IF ( input_fields(input_field_num)%missing_value_present ) THEN
2091  IF ( len_trim(input_fields(input_field_num)%interp_method) > 0 ) THEN
2092  output_fields(field_num)%f_type = write_field_meta_data(files(file)%file_unit,&
2093  & output_fields(field_num)%output_name, axes(1:num_axes),&
2094  & input_fields(input_field_num)%units,&
2095  & input_fields(input_field_num)%long_name,&
2096  & input_fields(input_field_num)%range, output_fields(field_num)%pack,&
2097  & input_fields(input_field_num)%missing_value, avg_name = avg,&
2098  & time_method=output_fields(field_num)%time_method,&
2099  & standard_name = input_fields(input_field_num)%standard_name,&
2100  & interp_method = input_fields(input_field_num)%interp_method,&
2101  & attributes=output_fields(field_num)%attributes,&
2102  & num_attributes=output_fields(field_num)%num_attributes,&
2103  & use_ugdomain=files(file)%use_domainUG)
2104  ELSE
2105  output_fields(field_num)%f_type = write_field_meta_data(files(file)%file_unit,&
2106  & output_fields(field_num)%output_name, axes(1:num_axes),&
2107  & input_fields(input_field_num)%units,&
2108  & input_fields(input_field_num)%long_name,&
2109  & input_fields(input_field_num)%range, output_fields(field_num)%pack,&
2110  & input_fields(input_field_num)%missing_value, avg_name = avg,&
2111  & time_method=output_fields(field_num)%time_method,&
2112  & standard_name = input_fields(input_field_num)%standard_name,&
2113  & attributes=output_fields(field_num)%attributes,&
2114  & num_attributes=output_fields(field_num)%num_attributes,&
2115  & use_ugdomain=files(file)%use_domainUG)
2116  END IF
2117  ! NEED TO TAKE CARE OF TIME AVERAGING INFO TOO BOTH CASES
2118  ELSE
2119  IF ( len_trim(input_fields(input_field_num)%interp_method) > 0 ) THEN
2120  output_fields(field_num)%f_type = write_field_meta_data(files(file)%file_unit,&
2121  & output_fields(field_num)%output_name, axes(1:num_axes),&
2122  & input_fields(input_field_num)%units,&
2123  & input_fields(input_field_num)%long_name,&
2124  & input_fields(input_field_num)%range, output_fields(field_num)%pack,&
2125  & avg_name = avg,&
2126  & time_method=output_fields(field_num)%time_method,&
2127  & standard_name = input_fields(input_field_num)%standard_name,&
2128  & interp_method = input_fields(input_field_num)%interp_method,&
2129  & attributes=output_fields(field_num)%attributes,&
2130  & num_attributes=output_fields(field_num)%num_attributes,&
2131  & use_ugdomain=files(file)%use_domainUG)
2132  ELSE
2133  output_fields(field_num)%f_type = write_field_meta_data(files(file)%file_unit,&
2134  & output_fields(field_num)%output_name, axes(1:num_axes),&
2135  & input_fields(input_field_num)%units,&
2136  & input_fields(input_field_num)%long_name,&
2137  & input_fields(input_field_num)%range, output_fields(field_num)%pack,&
2138  & avg_name = avg,&
2139  & time_method=output_fields(field_num)%time_method,&
2140  & standard_name = input_fields(input_field_num)%standard_name,&
2141  & attributes=output_fields(field_num)%attributes,&
2142  & num_attributes=output_fields(field_num)%num_attributes,&
2143  & use_ugdomain=files(file)%use_domainUG)
2144  END IF
2145  END IF
2146  END DO
2147 
2148  ! If any of the fields in the file are time averaged, need to output the axes
2149  ! Use double precision since time axis is double precision
2150  IF ( time_ops ) THEN
2151  time_axis_id(1) = files(file)%time_axis_id
2152  files(file)%f_avg_start = write_field_meta_data(files(file)%file_unit,&
2153  & avg_name // '_T1', time_axis_id, time_units,&
2154  & "Start time for average period", pack=pack_size)
2155  files(file)%f_avg_end = write_field_meta_data(files(file)%file_unit,&
2156  & avg_name // '_T2', time_axis_id, time_units,&
2157  & "End time for average period", pack=pack_size)
2158  files(file)%f_avg_nitems = write_field_meta_data(files(file)%file_unit,&
2159  & avg_name // '_DT', time_axis_id,&
2160  & trim(time_unit_list(files(file)%time_units)),&
2161  & "Length of average period", pack=pack_size)
2162  END IF
2163 
2164  IF ( time_ops ) THEN
2165  time_axis_id(1) = files(file)%time_axis_id
2166  time_bounds_id(1) = files(file)%time_bounds_id
2167  CALL get_diag_axis( time_axis_id(1), time_name, time_units, time_longname,&
2168  & cart_name, dir, edges, domain, domainu, data)
2169  CALL get_diag_axis( time_bounds_id(1), timeb_name, timeb_units, timeb_longname,&
2170  & cart_name, dir, edges, domain, domainu, data)
2171  IF ( do_cf_compliance() ) THEN
2172  ! CF Compliance requires the unit on the _bnds axis is the same as 'time'
2173  files(file)%f_bounds = write_field_meta_data(files(file)%file_unit,&
2174  & trim(time_name)//'_bnds', (/time_bounds_id,time_axis_id/),&
2175  & time_units, trim(time_name)//' axis boundaries', pack=pack_size)
2176  ELSE
2177  files(file)%f_bounds = write_field_meta_data(files(file)%file_unit,&
2178  & trim(time_name)//'_bnds', (/time_bounds_id,time_axis_id/),&
2179  & trim(time_unit_list(files(file)%time_units)),&
2180  & trim(time_name)//' axis boundaries', pack=pack_size)
2181  END IF
2182  END IF
2183  ! Let lower levels know that all meta data has been sent
2184  CALL done_meta_data(files(file)%file_unit)
2185  IF( aux_present .AND. .NOT.match_aux_name ) THEN
2186  ! <ERROR STATUS="WARNING">
2187  ! one axis has auxiliary but the corresponding field is NOT
2188  ! found in file <file_name>
2189  ! </ERROR>
2190  IF ( mpp_pe() == mpp_root_pe() ) CALL error_mesg('diag_util_mod::opening_file',&
2191  &'one axis has auxiliary but the corresponding field is NOT found in file '//trim(files(file)%name), warning)
2192  END IF
2193  IF( req_present .AND. .NOT.match_req_fields ) THEN
2194  ! <ERROR STATUS="FATAL">
2195  ! one axis has required fields but the corresponding field is NOT
2196  ! found in file <file_name>
2197  ! </ERROR>
2198  IF ( mpp_pe() == mpp_root_pe() ) CALL error_mesg('diag_util_mod::opening_file',&
2199  &'one axis has required fields ('//trim(req_fields)//') but the '// &
2200  &'corresponding fields are NOT found in file '//trim(files(file)%name), fatal)
2201  END IF
2202  END SUBROUTINE opening_file
2203  ! </SUBROUTINE>
2204  ! </PRIVATE>
2205 
2206  ! <PRIVATE>
2207  ! <FUNCTION NAME="get_time_string">
2208  ! <OVERVIEW>
2209  ! This function determines a string based on current time.
2210  ! This string is used as suffix in output file name
2211  ! </OVERVIEW>
2212  ! <TEMPLATE>
2213  ! CHARACTER(len=128) FUNCTION get_time_string(filename, current_time)
2214  ! </TEMPLATE>
2215  ! <DESCRIPTION>
2216  ! This function determines a string based on current time.
2217  ! This string is used as suffix in output file name
2218  ! </DESCRIPTION>
2219  ! <IN NAME="filename" TYPE="CHARACTER(len=128)">File name.</IN>
2220  ! <IN NAME="current_time" TYPE="TYPE(time_type)">Current model time.</IN>
2221  CHARACTER(len=128) FUNCTION get_time_string(filename, current_time)
2222  CHARACTER(len=128), INTENT(in) :: filename
2223  TYPE(time_type), INTENT(in) :: current_time
2224 
2225  INTEGER :: yr1, mo1, dy1, hr1, mi1, sc1 ! get from current time
2226  INTEGER :: yr2, dy2, hr2, mi2 ! for computing next_level time unit
2227  INTEGER :: yr1_s, mo1_s, dy1_s, hr1_s, mi1_s, sc1_s ! actual values to write string
2228  INTEGER :: abs_sec, abs_day ! component of current_time
2229  INTEGER :: days_per_month(12) = (/31,28,31,30,31,30,31,31,30,31,30,31/)
2230  INTEGER :: julian_day, i, position, len, first_percent
2231  CHARACTER(len=1) :: width ! width of the field in format write
2232  CHARACTER(len=10) :: format
2233  CHARACTER(len=20) :: yr, mo, dy, hr, mi, sc ! string of current time (output)
2234  CHARACTER(len=128) :: filetail
2235 
2236  format = '("_",i*.*)'
2237  CALL get_date(current_time, yr1, mo1, dy1, hr1, mi1, sc1)
2238  len = len_trim(filename)
2239  first_percent = index(filename, '%')
2240  filetail = filename(first_percent:len)
2241  ! compute year string
2242  position = index(filetail, 'yr')
2243  IF ( position > 0 ) THEN
2244  width = filetail(position-1:position-1)
2245  yr1_s = yr1
2246  format(7:9) = width//'.'//width
2247  WRITE(yr, format) yr1_s
2248  yr2 = 0
2249  ELSE
2250  yr = ' '
2251  yr2 = yr1 - 1
2252  END IF
2253  ! compute month string
2254  position = index(filetail, 'mo')
2255  IF ( position > 0 ) THEN
2256  width = filetail(position-1:position-1)
2257  mo1_s = yr2*12 + mo1
2258  format(7:9) = width//'.'//width
2259  WRITE(mo, format) mo1_s
2260  ELSE
2261  mo = ' '
2262  END IF
2263  ! compute day string
2264  IF ( len_trim(mo) > 0 ) THEN ! month present
2265  dy1_s = dy1
2266  dy2 = dy1_s - 1
2267  ELSE IF ( len_trim(yr) >0 ) THEN ! no month, year present
2268  ! compute julian day
2269  IF ( mo1 == 1 ) THEN
2270  dy1_s = dy1
2271  ELSE
2272  julian_day = 0
2273  DO i = 1, mo1-1
2274  julian_day = julian_day + days_per_month(i)
2275  END DO
2276  IF ( leap_year(current_time) .AND. mo1 > 2 ) julian_day = julian_day + 1
2277  julian_day = julian_day + dy1
2278  dy1_s = julian_day
2279  END IF
2280  dy2 = dy1_s - 1
2281  ELSE ! no month, no year
2282  CALL get_time(current_time, abs_sec, abs_day)
2283  dy1_s = abs_day
2284  dy2 = dy1_s
2285  END IF
2286  position = index(filetail, 'dy')
2287  IF ( position > 0 ) THEN
2288  width = filetail(position-1:position-1)
2289  FORMAT(7:9) = width//'.'//width
2290  WRITE(dy, format) dy1_s
2291  ELSE
2292  dy = ' '
2293  END IF
2294  ! compute hour string
2295  IF ( len_trim(dy) > 0 ) THEN
2296  hr1_s = hr1
2297  ELSE
2298  hr1_s = dy2*24 + hr1
2299  END IF
2300  hr2 = hr1_s
2301  position = index(filetail, 'hr')
2302  IF ( position > 0 ) THEN
2303  width = filetail(position-1:position-1)
2304  format(7:9) = width//'.'//width
2305  WRITE(hr, format) hr1_s
2306  ELSE
2307  hr = ' '
2308  END IF
2309  ! compute minute string
2310  IF ( len_trim(hr) > 0 ) THEN
2311  mi1_s = mi1
2312  ELSE
2313  mi1_s = hr2*60 + mi1
2314  END IF
2315  mi2 = mi1_s
2316  position = index(filetail, 'mi')
2317  IF(position>0) THEN
2318  width = filetail(position-1:position-1)
2319  format(7:9) = width//'.'//width
2320  WRITE(mi, format) mi1_s
2321  ELSE
2322  mi = ' '
2323  END IF
2324  ! compute second string
2325  IF ( len_trim(mi) > 0 ) THEN
2326  sc1_s = sc1
2327  ELSE
2328  sc1_s = nint(mi2*seconds_per_minute) + sc1
2329  END IF
2330  position = index(filetail, 'sc')
2331  IF ( position > 0 ) THEN
2332  width = filetail(position-1:position-1)
2333  format(7:9) = width//'.'//width
2334  WRITE(sc, format) sc1_s
2335  ELSE
2336  sc = ' '
2337  ENDIF
2338  get_time_string = trim(yr)//trim(mo)//trim(dy)//trim(hr)//trim(mi)//trim(sc)
2339  END FUNCTION get_time_string
2340  ! </FUNCTION>
2341  ! </PRIVATE>
2342 
2343  ! <FUNCTION NAME="get_date_dif">
2344  ! <OVERVIEW>
2345  ! Return the difference between two times in units.
2346  ! </OVERVIEW>
2347  ! <TEMPLATE>
2348  ! REAL FUNCTION get_date_dif(t2, t1, units)
2349  ! </TEMPLATE>
2350  ! <DESCRIPTION>
2351  ! Calculate and return the difference between the two times given in the unit given using the function <TT>t2 - t1</TT>.
2352  ! </DESCRIPTION>
2353  ! <IN NAME="t2" TYPE="TYPE(time_type)">Most recent time.</IN>
2354  ! <IN NAME="t1" TYPE="TYPE(time_type)">Most distant time.</IN>
2355  ! <IN NAME="units" TYPE="INTEGER">Unit of return value.</IN>
2356  REAL FUNCTION get_date_dif(t2, t1, units)
2357  TYPE(time_type), INTENT(in) :: t2, t1
2358  INTEGER, INTENT(in) :: units
2359 
2360  INTEGER :: dif_seconds, dif_days
2361  TYPE(time_type) :: dif_time
2362 
2363  ! Compute time axis label value
2364  ! <ERROR STATUS="FATAL">
2365  ! variable t2 is less than in variable t1
2366  ! </ERROR>
2367  IF ( t2 < t1 ) CALL error_mesg('diag_util_mod::get_date_dif', &
2368  & 'in variable t2 is less than in variable t1', fatal)
2369 
2370  dif_time = t2 - t1
2371 
2372  CALL get_time(dif_time, dif_seconds, dif_days)
2373 
2374  IF ( units == diag_seconds ) THEN
2375  get_date_dif = dif_seconds + seconds_per_day * dif_days
2376  ELSE IF ( units == diag_minutes ) THEN
2377  get_date_dif = 1440 * dif_days + dif_seconds / seconds_per_minute
2378  ELSE IF ( units == diag_hours ) THEN
2379  get_date_dif = 24 * dif_days + dif_seconds / seconds_per_hour
2380  ELSE IF ( units == diag_days ) THEN
2381  get_date_dif = dif_days + dif_seconds / seconds_per_day
2382  ELSE IF ( units == diag_months ) THEN
2383  ! <ERROR STATUS="FATAL">
2384  ! months not supported as output units
2385  ! </ERROR>
2386  CALL error_mesg('diag_util_mod::get_date_dif', 'months not supported as output units', fatal)
2387  ELSE IF ( units == diag_years ) THEN
2388  ! <ERROR STATUS="FATAL">
2389  ! years not suppored as output units
2390  ! </ERROR>
2391  CALL error_mesg('diag_util_mod::get_date_dif', 'years not supported as output units', fatal)
2392  ELSE
2393  ! <ERROR STATUS="FATAL">
2394  ! illegal time units
2395  ! </ERROR>
2396  CALL error_mesg('diag_util_mod::diag_date_dif', 'illegal time units', fatal)
2397  END IF
2398  END FUNCTION get_date_dif
2399  ! </FUNCTION>
2400 
2401  ! <SUBROUTINE NAME="diag_data_out">
2402  ! <OVERVIEW>
2403  ! Write data out to file.
2404  ! </OVERVIEW>
2405  ! <TEMPLATE>
2406  ! SUBROUTINE diag_data_out(file, field, dat, time, fianl_call_in, static_write_in)
2407  ! </TEMPLATE>
2408  ! <DESCRIPTION>
2409  ! Write data out to file, and if necessary flush the buffers.
2410  ! </DESCRIPTION>
2411  ! <IN NAME="file" TYPE="INTEGER">File ID.</IN>
2412  ! <IN NAME="field" TYPE="INTEGER">Field ID.</IN>
2413  ! <INOUT NAME="dat" TYPE="REAL, DIMENSION(:,:,:,:)">Data to write out.</INOUT>
2414  ! <IN NAME="time" TYPE="TYPE(time_type)">Current model time.</IN>
2415  ! <IN NAME="final_call_in" TYPE="LOGICAL, OPTIONAL"><TT>.TRUE.</TT> if this is the last write for file.</IN>
2416  ! <IN NAME="static_write_in" TYPE="LOGICAL, OPTIONAL"><TT>.TRUE.</TT> if static fields are to be written to file.</IN>
2417  SUBROUTINE diag_data_out(file, field, dat, time, final_call_in, static_write_in)
2418  INTEGER, INTENT(in) :: file, field
2419  REAL, DIMENSION(:,:,:,:), INTENT(inout) :: dat
2420  TYPE(time_type), INTENT(in) :: time
2421  LOGICAL, OPTIONAL, INTENT(in):: final_call_in, static_write_in
2422 
2423  LOGICAL :: final_call, do_write, static_write
2424  INTEGER :: i, num
2425  REAL :: dif, time_data(2, 1, 1, 1), dt_time(1, 1, 1, 1), start_dif, end_dif
2426 
2427  do_write = .true.
2428  final_call = .false.
2429  IF ( PRESENT(final_call_in) ) final_call = final_call_in
2430  static_write = .false.
2431  IF ( PRESENT(static_write_in) ) static_write = static_write_in
2432  dif = get_date_dif(time, base_time, files(file)%time_units)
2433  ! get file_unit, open new file and close curent file if necessary
2434  IF ( .NOT.static_write .OR. files(file)%file_unit < 0 ) CALL check_and_open(file, time, do_write)
2435  IF ( .NOT.do_write ) RETURN ! no need to write data
2436  CALL diag_field_out(files(file)%file_unit, output_fields(field)%f_type, dat, dif)
2437  ! record number of bytes written to this file
2438  files(file)%bytes_written = files(file)%bytes_written +&
2439  & (SIZE(dat,1)*SIZE(dat,2)*SIZE(dat,3))*(8/output_fields(field)%pack)
2440  IF ( .NOT.output_fields(field)%written_once ) output_fields(field)%written_once = .true.
2441  ! *** inserted this line because start_dif < 0 for static fields ***
2442  IF ( .NOT.output_fields(field)%static ) THEN
2443  start_dif = get_date_dif(output_fields(field)%last_output, base_time,files(file)%time_units)
2444  IF ( .NOT.mix_snapshot_average_fields ) THEN
2445  end_dif = get_date_dif(output_fields(field)%next_output, base_time, files(file)%time_units)
2446  ELSE
2447  end_dif = dif
2448  END IF
2449  END IF
2450 
2451  ! Need to write average axes out;
2452  DO i = 1, files(file)%num_fields
2453  num = files(file)%fields(i)
2454  IF ( output_fields(num)%time_ops .AND. &
2455  input_fields(output_fields(num)%input_field)%register) THEN
2456  IF ( num == field ) THEN
2457  ! Output the axes if this is first time-averaged field
2458  time_data(1, 1, 1, 1) = start_dif
2459  CALL diag_field_out(files(file)%file_unit, files(file)%f_avg_start, time_data(1:1,:,:,:), dif)
2460  time_data(2, 1, 1, 1) = end_dif
2461  CALL diag_field_out(files(file)%file_unit, files(file)%f_avg_end, time_data(2:2,:,:,:), dif)
2462  ! Compute the length of the average
2463  dt_time(1, 1, 1, 1) = end_dif - start_dif
2464  CALL diag_field_out(files(file)%file_unit, files(file)%f_avg_nitems, dt_time(1:1,:,:,:), dif)
2465 
2466  ! Include boundary variable for CF compliance
2467  CALL diag_field_out(files(file)%file_unit, files(file)%f_bounds, time_data(1:2,:,:,:), dif)
2468  EXIT
2469  END IF
2470  END IF
2471  END DO
2472 
2473  ! If write time is greater (equal for the last call) than last_flush for this file, flush it
2474  IF ( final_call ) THEN
2475  IF ( time >= files(file)%last_flush ) THEN
2476  CALL diag_flush(files(file)%file_unit)
2477  files(file)%last_flush = time
2478  END IF
2479  ELSE
2480  IF ( time > files(file)%last_flush .AND. (flush_nc_files.OR.debug_diag_manager) ) THEN
2481  CALL diag_flush(files(file)%file_unit)
2482  files(file)%last_flush = time
2483  END IF
2484  END IF
2485  END SUBROUTINE diag_data_out
2486  ! </SUBROUTINE>
2487 
2488  ! <PRIVATE>
2489  ! <SUBROUTINE NAME="check_and_open">
2490  ! <OVERVIEW>
2491  ! Checks if it is time to open a new file.
2492  ! </OVERVIEW>
2493  ! <TEMPLATE>
2494  ! SUBROUTINE check_and_open(file, time, do_write)
2495  ! </TEMPLATE>
2496  ! <DESCRIPTION>
2497  ! Checks if it is time to open a new file. If yes, it first closes the
2498  ! current file, opens a new file and returns file_unit
2499  ! previous diag_manager_end is replaced by closing_file and output_setup by opening_file.
2500  ! </DESCRIPTION>
2501  ! <IN NAME="file" TYPE="INTEGER">File ID.</IN>
2502  ! <IN NAME="time" TYPE="TYPE(time_type)">Current model time.</IN>
2503  ! <OUT NAME="do_write" TYPE="LOGICAL"><TT>.TRUE.</TT> if file is expecting more data to write, <TT>.FALSE.</TT> otherwise.</OUT>
2504  SUBROUTINE check_and_open(file, time, do_write)
2505  INTEGER, INTENT(in) :: file
2506  TYPE(time_type), INTENT(in) :: time
2507  LOGICAL, INTENT(out) :: do_write
2508 
2509  IF ( time >= files(file)%start_time ) THEN
2510  IF ( files(file)%file_unit < 0 ) THEN ! need to open a new file
2511  CALL opening_file(file, time)
2512  do_write = .true.
2513  ELSE
2514  do_write = .true.
2515  IF ( time > files(file)%close_time .AND. time < files(file)%next_open ) THEN
2516  do_write = .false. ! file still open but receives NO MORE data
2517  ELSE IF ( time > files(file)%next_open ) THEN ! need to close current file and open a new one
2518  CALL write_static(file) ! write all static fields and close this file
2519  CALL opening_file(file, time)
2520  files(file)%start_time = files(file)%next_open
2521  files(file)%close_time =&
2522  & diag_time_inc(files(file)%start_time,files(file)%duration, files(file)%duration_units)
2523  files(file)%next_open =&
2524  & diag_time_inc(files(file)%next_open, files(file)%new_file_freq,&
2525  & files(file)%new_file_freq_units)
2526  IF ( files(file)%close_time > files(file)%next_open ) THEN
2527  ! <ERROR STATUS="FATAL">
2528  ! <file_name> has close time GREATER than next_open time,
2529  ! check file duration and frequency
2530  ! </ERROR>
2531  CALL error_mesg('diag_util_mod::check_and_open',&
2532  & files(file)%name//' has close time GREATER than next_open time, check file duration and frequency',fatal)
2533  END IF
2534  END IF ! no need to open new file, simply return file_unit
2535  END IF
2536  ELSE
2537  do_write = .false.
2538  END IF
2539  END SUBROUTINE check_and_open
2540  ! </SUBROUTINE>
2541  ! </PRIVATE>
2542 
2543  ! <SUBROUTINE NAME="write_static">
2544  ! <OVERVIEW>
2545  ! Output all static fields in this file
2546  ! </OVERVIEW>
2547  ! <TEMPLATE>
2548  ! SUBROUTINE write_static(file)
2549  ! </TEMPLATE>
2550  ! <DESCRIPTION>
2551  ! Write the static data to the file.
2552  ! </DESCRIPTION>
2553  ! <IN NAME="file" TYPE="INTEGER">File ID.</IN>
2554  SUBROUTINE write_static(file)
2555  INTEGER, INTENT(in) :: file
2556 
2557  INTEGER :: j, i, input_num
2558 
2559  DO j = 1, files(file)%num_fields
2560  i = files(file)%fields(j)
2561  input_num = output_fields(i)%input_field
2562  ! skip fields that were not registered
2563  IF ( .NOT.input_fields(input_num)%register ) cycle
2564  IF ( output_fields(i)%local_output .AND. .NOT. output_fields(i)%need_compute) cycle
2565  ! only output static fields here
2566  IF ( .NOT.output_fields(i)%static ) cycle
2567  CALL diag_data_out(file, i, output_fields(i)%buffer, files(file)%last_flush, .true., .true.)
2568  END DO
2569  ! Close up this file
2570  IF ( files(file)%file_unit.NE.-1 ) then
2571  ! File is stil open. This is to protect when the diag_table has no Fields
2572  ! going to this file, and it was never opened (b/c diag_data_out was not
2573  ! called)
2574  CALL mpp_close(files(file)%file_unit)
2575  files(file)%file_unit = -1
2576  END IF
2577  END SUBROUTINE write_static
2578  ! </SUBROUTINE>
2579 
2580  ! <SUBROUTINE NAME="check_duplicate_output_fields">
2581  ! <OVERVIEW>
2582  ! Checks to see if <TT>output_name</TT> and <TT>output_file</TT> are unique in <TT>output_fields</TT>.
2583  ! </OVERVIEW>
2584  ! <TEMPLATE>
2585  ! SUBROUTINE check_duplicate_output_fields(err_msg)
2586  ! </TEMPLATE>
2587  ! <DESCRIPTION>
2588  ! Check to see if <TT>output_name</TT> and <TT>output_file</TT> are unique in <TT>output_fields</TT>. An empty
2589  ! <TT>err_msg</TT> indicates no duplicates found.
2590  ! </DESCRIPTION>
2591  ! <OUT NAME="err_msg" TYPE="CHARACTER(len=*), OPTIONAL">Error message. If empty, then no duplicates found.</OUT>
2592  SUBROUTINE check_duplicate_output_fields(err_msg)
2593  CHARACTER(len=*), INTENT(out), OPTIONAL :: err_msg
2594 
2595  INTEGER :: i, j, tmp_file
2596  CHARACTER(len=128) :: tmp_name
2597  CHARACTER(len=256) :: err_msg_local
2598 
2599  IF ( PRESENT(err_msg) ) err_msg=''
2600  ! Do the checking when more than 1 output_fileds present
2601  IF ( num_output_fields <= 1 ) RETURN
2602  err_msg_local = ''
2603 
2604  i_loop: DO i = 1, num_output_fields-1
2605  tmp_name = trim(output_fields(i)%output_name)
2606  tmp_file = output_fields(i)%output_file
2607  DO j = i+1, num_output_fields
2608  IF ( (tmp_name == trim(output_fields(j)%output_name)) .AND. &
2609  &(tmp_file == output_fields(j)%output_file)) THEN
2610  err_msg_local = ' output_field "'//trim(tmp_name)//&
2611  &'" duplicated in file "'//trim(files(tmp_file)%name)//'"'
2612  EXIT i_loop
2613  END IF
2614  END DO
2615  END DO i_loop
2616  IF ( err_msg_local /= '' ) THEN
2617  IF ( fms_error_handler(' ERROR in diag_table',err_msg_local,err_msg) ) RETURN
2618  END IF
2619  END SUBROUTINE check_duplicate_output_fields
2620  ! </SUBROUTINE>
2621 
2622  ! <SUBROUTINE NAME="attribute_init_field" INTERFACE="attribute_init">
2623  ! <OVERVIEW>
2624  ! Allocates the atttype in out_field
2625  ! </OVERVIEW>
2626  ! <TEMPLATE>
2627  ! SUBROUTINE attribute_init(out_field, err_msg)
2628  ! </TEMPLATE>
2629  ! <DESCRIPTION>
2630  ! Allocates memory in out_field for the attributes. Will <TT>FATAL</TT> if err_msg is not included
2631  ! in the subroutine call.
2632  ! </DESCRIPTION>
2633  ! <INOUT NAME="out_field" TYPE="TYPE(output_field_type)">output field to allocate memory for attribute</INOUT>
2634  ! <OUT NAME="err_msg" TYPE="CHARACTER(len=*), OPTIONAL">Error message, passed back to calling function</OUT>
2635  SUBROUTINE attribute_init_field(out_field, err_msg)
2636  TYPE(output_field_type), INTENT(inout) :: out_field
2637  CHARACTER(LEN=*), INTENT(out), OPTIONAL :: err_msg
2638 
2639  INTEGER :: istat
2640 
2641  ! Need to initialize err_msg if present
2642  IF ( PRESENT(err_msg) ) err_msg = ''
2643 
2644  ! Allocate memory for the attributes
2645  IF ( .NOT._allocated(out_field%attributes) ) THEN
2646  ALLOCATE(out_field%attributes(max_field_attributes), stat=istat)
2647  IF ( istat.NE.0 ) THEN
2648  ! <ERROR STATUS="FATAL">
2649  ! Unable to allocate memory for attribute <name> to module/input_field <module_name>/<field_name>
2650  ! </ERROR>
2651  IF ( fms_error_handler('diag_util_mod::attribute_init_field',&
2652  & 'Unable to allocate memory for attributes', err_msg) ) THEN
2653  RETURN
2654  END IF
2655  ELSE
2656  ! Set equal to 0. It will be increased when attributes added
2657  out_field%num_attributes = 0
2658  END IF
2659  END IF
2660  END SUBROUTINE attribute_init_field
2661  ! </SUBROUTINE>
2662 
2663  ! <SUBROUTINE NAME="prepend_attribute_field" INTERFACE="prepend_attribute">
2664  ! <OVERVIEW>
2665  ! Prepends the attribute value to an already existing attribute. If the
2666  ! attribute isn't yet defined, then creates a new attribute
2667  ! </OVERVIEW>
2668  ! <TEMPLATE>
2669  ! SUBROUTINE prepend_attribute(out_field, attribute, prepend_value)
2670  ! </TEMPLATE>
2671  ! <DESCRIPTION>
2672  ! Checks if the attribute already exists in the <TT>out_field</TT>. If it exists,
2673  ! then prepend the <TT>prepend_value</TT>, otherwise, create the attribute
2674  ! with the <TT>prepend_value</TT>. <TT>err_msg</TT> indicates no duplicates found.
2675  ! </DESCRIPTION>
2676  ! <INOUT NAME="out_field" TYPE="TYPE(output_field_type)">output field that will get the attribute</INOUT>
2677  ! <IN NAME="att_name" TYPE="CHARACTER(len=*)">Name of the attribute</IN>
2678  ! <IN NAME="prepend_value" TYPE="CHARACTER(len=*)">Value to prepend</IN>
2679  ! <OUT NAME="err_msg" TYPE="CHARACTER(len=*), OPTIONAL">Error message, passed back to calling routine</OUT>
2680  SUBROUTINE prepend_attribute_field(out_field, att_name, prepend_value, err_msg)
2681  TYPE(output_field_type), INTENT(inout) :: out_field
2682  CHARACTER(len=*), INTENT(in) :: att_name, prepend_value
2683  CHARACTER(len=*), INTENT(out) , OPTIONAL :: err_msg
2684 
2685  INTEGER :: length, i, this_attribute
2686  CHARACTER(len=512) :: err_msg_local
2687 
2688  ! Initialize string characters
2689  err_msg_local=''
2690  IF ( PRESENT(err_msg) ) err_msg = ''
2691 
2692  ! Make sure the attributes for this out field have been initialized
2693  CALL attribute_init_field(out_field, err_msg_local)
2694  IF ( trim(err_msg_local) .NE. '' ) THEN
2695  IF ( fms_error_handler('diag_util_mod::prepend_attribute_field', trim(err_msg_local), err_msg) ) THEN
2696  RETURN
2697  END IF
2698  END IF
2699 
2700  ! Find if attribute exists
2701  this_attribute = 0
2702  DO i=1, out_field%num_attributes
2703  IF ( trim(out_field%attributes(i)%name) .EQ. trim(att_name) ) THEN
2704  this_attribute = i
2705  EXIT
2706  END IF
2707  END DO
2708 
2709  IF ( this_attribute > 0 ) THEN
2710  IF ( out_field%attributes(this_attribute)%type .NE. nf90_char ) THEN
2711  ! <ERROR STATUS="FATAL">
2712  ! Attribute <name> is not a character attribute.
2713  ! </ERROR>
2714  IF ( fms_error_handler('diag_util_mod::prepend_attribute_field', &
2715  & 'Attribute "'//trim(att_name)//'" is not a character attribute.',&
2716  & err_msg) ) THEN
2717  RETURN
2718  END IF
2719  END IF
2720  ELSE
2721  ! Defining a new attribute
2722  ! Increase the number of field attributes
2723  this_attribute = out_field%num_attributes + 1
2724  IF ( this_attribute .GT. max_field_attributes ) THEN
2725  ! <ERROR STATUS="FATAL">
2726  ! Number of attributes exceeds max_field_attributes for attribute <name>.
2727  ! Increase diag_manager_nml:max_field_attributes.
2728  ! </ERROR>
2729  IF ( fms_error_handler('diag_util_mod::prepend_attribute_field',&
2730  & 'Number of attributes exceeds max_field_attributes for attribute "'&
2731  & //trim(att_name)//'". Increase diag_manager_nml:max_field_attributes.',&
2732  & err_msg) ) THEN
2733  RETURN
2734  END IF
2735  ELSE
2736  out_field%num_attributes = this_attribute
2737  ! Set name and type
2738  out_field%attributes(this_attribute)%name = att_name
2739  out_field%attributes(this_attribute)%type = nf90_char
2740  ! Initialize catt to a blank string, as len_trim doesn't always work on an uninitialized string
2741  out_field%attributes(this_attribute)%catt = ''
2742  END IF
2743  END IF
2744 
2745  ! Check if string is already included, and return if found
2746  IF ( index(trim(out_field%attributes(this_attribute)%catt), trim(prepend_value)).EQ.0 ) THEN
2747  ! Check if new string length goes beyond the length of catt
2748  length = len_trim(trim(prepend_value)//" "//trim(out_field%attributes(this_attribute)%catt))
2749  IF ( length.GT.len(out_field%attributes(this_attribute)%catt) ) THEN
2750  ! <ERROR STATUS="FATAL">
2751  ! Prepend length for attribute <name> is longer than allowed.
2752  ! </ERROR>
2753  IF ( fms_error_handler('diag_util_mod::prepend_attribute_field',&
2754  & 'Prepend length for attribute "'//trim(att_name)//'" is longer than allowed.',&
2755  & err_msg) ) THEN
2756  RETURN
2757  END IF
2758  END IF
2759  ! Set fields
2760  out_field%attributes(this_attribute)%catt =&
2761  & trim(prepend_value)//' '//trim(out_field%attributes(this_attribute)%catt)
2762  out_field%attributes(this_attribute)%len = length
2763  END IF
2764  END SUBROUTINE prepend_attribute_field
2765  ! </SUBROUTINE>
2766 
2767  ! <SUBROUTINE NAME="attribute_init_file" INTERFACE="attribute_init">
2768  ! <OVERVIEW>
2769  ! Allocates the atttype in out_file
2770  ! </OVERVIEW>
2771  ! <TEMPLATE>
2772  ! SUBROUTINE attribute_init(out_file, err_msg)
2773  ! </TEMPLATE>
2774  ! <DESCRIPTION>
2775  ! Allocates memory in out_file for the attributes. Will <TT>FATAL</TT> if err_msg is not included
2776  ! in the subroutine call.
2777  ! </DESCRIPTION>
2778  ! <INOUT NAME="out_file" TYPE="TYPE(file_type)">output file to allocate memory for attribute</INOUT>
2779  ! <OUT NAME="err_msg" TYPE="CHARACTER(len=*), OPTIONAL">Error message, passed back to calling function</OUT>
2780  SUBROUTINE attribute_init_file(out_file, err_msg)
2781  TYPE(file_type), INTENT(inout) :: out_file
2782  CHARACTER(LEN=*), INTENT(out), OPTIONAL :: err_msg
2783 
2784  INTEGER :: istat
2785 
2786  ! Initialize err_msg
2787  IF ( PRESENT(err_msg) ) err_msg = ''
2788 
2789  ! Allocate memory for the attributes
2790  IF ( .NOT._allocated(out_file%attributes) ) THEN
2791  ALLOCATE(out_file%attributes(max_field_attributes), stat=istat)
2792  IF ( istat.NE.0 ) THEN
2793  ! <ERROR STATUS="FATAL">
2794  ! Unable to allocate memory for file attributes
2795  ! </ERROR>
2796  IF ( fms_error_handler('diag_util_mod::attribute_init_file', 'Unable to allocate memory for file attributes', err_msg) ) THEN
2797  RETURN
2798  END IF
2799  ELSE
2800  ! Set equal to 0. It will be increased when attributes added
2801  out_file%num_attributes = 0
2802  END IF
2803  END IF
2804  END SUBROUTINE attribute_init_file
2805  ! </SUBROUTINE>
2806 
2807  ! <SUBROUTINE NAME="prepend_attribute_file" INTERFACE="prepend_attribute">
2808  ! <OVERVIEW>
2809  ! Prepends the attribute value to an already existing attribute. If the
2810  ! attribute isn't yet defined, then creates a new attribute
2811  ! </OVERVIEW>
2812  ! <TEMPLATE>
2813  ! SUBROUTINE prepend_attribute(out_file, attribute, prepend_value)
2814  ! </TEMPLATE>
2815  ! <DESCRIPTION>
2816  ! Checks if the attribute already exists in the <TT>out_file</TT>. If it exists,
2817  ! then prepend the <TT>prepend_value</TT>, otherwise, create the attribute
2818  ! with the <TT>prepend_value</TT>. <TT>err_msg</TT> indicates no duplicates found.
2819  ! </DESCRIPTION>
2820  ! <INOUT NAME="out_file" TYPE="TYPE(file_type)">output file that will get the attribute</INOUT>
2821  ! <IN NAME="att_name" TYPE="CHARACTER(len=*)">Name of the attribute</IN>
2822  ! <IN NAME="prepend_value" TYPE="CHARACTER(len=*)">Value to prepend</IN>
2823  ! <OUT NAME="err_msg" TYPE="CHARACTER(len=*), OPTIONAL">Error message, passed back to calling routine</OUT>
2824  SUBROUTINE prepend_attribute_file(out_file, att_name, prepend_value, err_msg)
2825  TYPE(file_type), INTENT(inout) :: out_file
2826  CHARACTER(len=*), INTENT(in) :: att_name, prepend_value
2827  CHARACTER(len=*), INTENT(out) , OPTIONAL :: err_msg
2828 
2829  INTEGER :: length, i, this_attribute
2830  CHARACTER(len=512) :: err_msg_local
2831 
2832  ! Initialize string variables
2833  err_msg_local = ''
2834  IF ( PRESENT(err_msg) ) err_msg = ''
2835 
2836  ! Make sure the attributes for this out file have been initialized
2837  CALL attribute_init_file(out_file, err_msg_local)
2838  IF ( trim(err_msg_local) .NE. '' ) THEN
2839  IF ( fms_error_handler('diag_util_mod::prepend_attribute_file', trim(err_msg_local), err_msg) ) THEN
2840  RETURN
2841  END IF
2842  END IF
2843 
2844  ! Find if attribute exists
2845  this_attribute = 0
2846  DO i=1, out_file%num_attributes
2847  IF ( trim(out_file%attributes(i)%name) .EQ. trim(att_name) ) THEN
2848  this_attribute = i
2849  EXIT
2850  END IF
2851  END DO
2852 
2853  IF ( this_attribute > 0 ) THEN
2854  IF ( out_file%attributes(this_attribute)%type .NE. nf90_char ) THEN
2855  ! <ERROR STATUS="FATAL">
2856  ! Attribute <name> is not a character attribute.
2857  ! </ERROR>
2858  IF ( fms_error_handler('diag_util_mod::prepend_attribute_file',&
2859  & 'Attribute "'//trim(att_name)//'" is not a character attribute.',&
2860  & err_msg) ) THEN
2861  RETURN
2862  END IF
2863  END IF
2864  ELSE
2865  ! Defining a new attribute
2866  ! Increase the number of file attributes
2867  this_attribute = out_file%num_attributes + 1
2868  IF ( this_attribute .GT. max_file_attributes ) THEN
2869  ! <ERROR STATUS="FATAL">
2870  ! Number of attributes exceeds max_file_attributes for attribute <name>.
2871  ! Increase diag_manager_nml:max_file_attributes.
2872  ! </ERROR>
2873  IF ( fms_error_handler('diag_util_mod::prepend_attribute_file',&
2874  & 'Number of attributes exceeds max_file_attributes for attribute "'&
2875  &//trim(att_name)//'". Increase diag_manager_nml:max_file_attributes.',&
2876  & err_msg) ) THEN
2877  RETURN
2878  END IF
2879  ELSE
2880  out_file%num_attributes = this_attribute
2881  ! Set name and type
2882  out_file%attributes(this_attribute)%name = att_name
2883  out_file%attributes(this_attribute)%type = nf90_char
2884  ! Initialize catt to a blank string, as len_trim doesn't always work on an uninitialized string
2885  out_file%attributes(this_attribute)%catt = ''
2886  END IF
2887  END IF
2888 
2889  ! Only add string only if not already defined
2890  IF ( index(trim(out_file%attributes(this_attribute)%catt), trim(prepend_value)).EQ.0 ) THEN
2891  ! Check if new string length goes beyond the length of catt
2892  length = len_trim(trim(prepend_value)//" "//trim(out_file%attributes(this_attribute)%catt))
2893  IF ( length.GT.len(out_file%attributes(this_attribute)%catt) ) THEN
2894  ! <ERROR STATUS="FATAL">
2895  ! Prepend length for attribute <name> is longer than allowed.
2896  ! </ERROR>
2897  IF ( fms_error_handler('diag_util_mod::prepend_attribute_file',&
2898  & 'Prepend length for attribute "'//trim(att_name)//'" is longer than allowed.',&
2899  & err_msg) ) THEN
2900  RETURN
2901  END IF
2902  END IF
2903  ! Set files
2904  out_file%attributes(this_attribute)%catt =&
2905  & trim(prepend_value)//' '//trim(out_file%attributes(this_attribute)%catt)
2906  out_file%attributes(this_attribute)%len = length
2907  END IF
2908  END SUBROUTINE prepend_attribute_file
2909  ! </SUBROUTINE>
2910 END MODULE diag_util_mod
Definition: fms.F90:20
subroutine, public get_subfield_vert_size(axes, outnum)
Definition: diag_util.F90:465
type(time_type) function, public increment_time(Time, seconds, days, ticks, err_msg, allow_neg_inc)
subroutine, public get_date(time, year, month, day, hour, minute, second, tick, err_msg)
real, parameter cmor_missing_value
CMOR standard missing value.
Definition: diag_data.F90:110
type(time_type) function, public increment_date(Time, years, months, days, hours, minutes, seconds, ticks, err_msg, allow_neg_inc)
integer, parameter every_time
Definition: diag_data.F90:103
subroutine, public diag_field_out(file_unit, Field, DATA, time)
character(len=256) global_descriptor
Definition: diag_data.F90:774
subroutine, public get_diag_axis_cart(id, cart_name)
Definition: diag_axis.F90:624
integer base_year
Definition: diag_data.F90:773
subroutine attribute_init_field(out_field, err_msg)
Definition: diag_util.F90:2636
integer num_output_fields
Definition: diag_data.F90:649
integer function, public find_input_field(module_name, field_name, tile_count)
Definition: diag_util.F90:1395
type(domainug) function, public get_domainug(id)
Definition: diag_axis.F90:894
integer, parameter diag_seconds
Definition: diag_data.F90:105
character(len=128) function, public get_axis_aux(id)
Definition: diag_axis.F90:740
type(domain1d) function, public get_domain1d(id)
Definition: diag_axis.F90:835
integer max_field_attributes
Maximum number of user definable attributes per field. Liptak: Changed from 2 to 4 20170718...
Definition: diag_data.F90:731
subroutine, public done_meta_data(file_unit)
integer base_month
Definition: diag_data.F90:773
integer num_files
Definition: diag_data.F90:647
subroutine, public diag_flush(file_unit)
subroutine attribute_init_file(out_file, err_msg)
Definition: diag_util.F90:2781
integer function get_index(number, array)
Definition: diag_util.F90:608
integer max_out_per_in_field
Maximum number of output_fields per input_field. Increase via diag_manager_nml.
Definition: diag_data.F90:714
subroutine, public diag_util_init()
Definition: diag_util.F90:152
real function, public get_date_dif(t2, t1, units)
Definition: diag_util.F90:2357
subroutine, public get_diag_axis_name(id, name)
Definition: diag_axis.F90:671
character(len=10), dimension(6) time_unit_list
Definition: diag_data.F90:798
subroutine, public get_local_indexes(latStart, latEnd, lonStart, lonEnd, istart, iend, jstart, jend)
Definition: diag_grid.F90:479
type(time_type) base_time
Definition: diag_data.F90:772
subroutine, public check_duplicate_output_fields(err_msg)
Definition: diag_util.F90:2593
real, parameter, public seconds_per_minute
Seconds in a minute [s].
Definition: constants.F90:118
subroutine, public update_bounds(out_num, lower_i, upper_i, lower_j, upper_j, lower_k, upper_k)
Definition: diag_util.F90:813
integer, parameter glo_reg_val
Definition: diag_data.F90:108
integer base_day
Definition: diag_data.F90:773
integer, parameter end_of_run
Definition: diag_data.F90:104
subroutine, public check_bounds_are_exact_dynamic(out_num, diag_field_id, Time, err_msg)
Definition: diag_util.F90:920
integer max_file_attributes
Maximum number of user definable global attributes per file.
Definition: diag_data.F90:732
logical function, public fms_error_handler(routine, message, err_msg)
Definition: fms.F90:573
integer function, public diag_subaxes_init(axis, subdata, start_indx, end_indx, domain_2d)
Definition: diag_axis.F90:412
subroutine, public diag_output_init(file_name, FORMAT, file_title, file_unit, all_scalar_or_1d, domain, domainU, attributes)
subroutine, public get_diag_axis_data(id, DATA)
Definition: diag_axis.F90:645
integer, parameter diag_field_not_found
Definition: diag_data.F90:111
subroutine, public get_diag_axis(id, name, units, long_name, cart_name, direction, edges, Domain, DomainU, DATA, num_attributes, attributes)
Definition: diag_axis.F90:528
logical flush_nc_files
Control if diag_manager will force a flush of the netCDF file on each write. Note: changing this to ...
Definition: diag_data.F90:719
subroutine, public write_static(file)
Definition: diag_util.F90:2555
type(output_field_type), dimension(:), allocatable output_fields
Definition: diag_data.F90:782
subroutine, public write_axis_meta_data(file_unit, axes, time_ops)
integer base_second
Definition: diag_data.F90:773
Definition: mpp.F90:39
logical function, public axis_is_compressed(id)
Definition: diag_axis.F90:1505
subroutine prepend_attribute_field(out_field, att_name, prepend_value, err_msg)
Definition: diag_util.F90:2681
subroutine, public init_output_field(module_name, field_name, output_name, output_file, time_method, pack, tile_count, local_coord)
Definition: diag_util.F90:1478
integer function, public get_axis_global_length(id)
Definition: diag_axis.F90:778
subroutine, public check_out_of_bounds(out_num, diag_field_id, err_msg)
Definition: diag_util.F90:849
subroutine, public get_axes_shift(ids, ishift, jshift)
Definition: diag_axis.F90:1013
type(time_type) diag_init_time
Definition: diag_data.F90:771
type(file_type), dimension(:), allocatable, save files
Definition: diag_data.F90:780
integer, parameter very_large_axis_length
Definition: diag_data.F90:102
integer, parameter diag_hours
Definition: diag_data.F90:105
integer, parameter glo_reg_val_alt
Definition: diag_data.F90:109
type(domain2d), save, public null_domain2d
logical module_initialized
Definition: diag_util.F90:136
real, parameter, public seconds_per_hour
Seconds in an hour [s].
Definition: constants.F90:117
character(len=128) function get_time_string(filename, current_time)
Definition: diag_util.F90:2222
subroutine, public get_compressed_axes_ids(id, r)
Definition: diag_axis.F90:1525
subroutine, public sync_file_times(file_id, init_time, err_msg)
Definition: diag_util.F90:1239
logical use_cmor
Definition: diag_data.F90:726
subroutine, public get_diag_axis_domain_name(id, name)
Definition: diag_axis.F90:692
subroutine opening_file(file, time)
Definition: diag_util.F90:1777
integer function, public get_calendar_type()
type(input_field_type), dimension(:), allocatable input_fields
Definition: diag_data.F90:781
integer, parameter diag_minutes
Definition: diag_data.F90:105
integer function find_file(name, tile_count)
Definition: diag_util.F90:1364
integer function, public diag_axis_init(name, DATA, units, cart_name, long_name, direction, set_name, edges, Domain, Domain2, DomainU, aux, req, tile_count)
Definition: diag_axis.F90:157
integer, parameter max_fields_per_file
Maximum number of fields per file.
Definition: diag_data.F90:97
subroutine, public log_diag_field_info(module_name, field_name, axes, long_name, units, missing_value, range, dynamic)
Definition: diag_util.F90:716
integer max_files
Maximum number of output files allowed. Increase via diag_manager_nml.
Definition: diag_data.F90:711
integer, parameter, public no_calendar
subroutine prepend_attribute_file(out_file, att_name, prepend_value, err_msg)
Definition: diag_util.F90:2825
integer max_output_fields
Maximum number of output fields. Increase via diag_manager_nml.
Definition: diag_data.F90:712
integer, parameter diag_years
Definition: diag_data.F90:106
type(domainug), save, public null_domainug
subroutine, public get_mosaic_tile_file_ug(file_in, file_out, domain)
Definition: fms_io.F90:7813
integer pack_size
Definition: diag_data.F90:749
type(time_type) function, public diag_time_inc(time, output_freq, output_units, err_msg)
Definition: diag_util.F90:1285
subroutine check_and_open(file, time, do_write)
Definition: diag_util.F90:2505
logical function, public leap_year(Time, err_msg)
type(domain2d) function, public get_domain2d(ids)
Definition: diag_axis.F90:860
subroutine, public check_bounds_are_exact_static(out_num, diag_field_id, err_msg)
Definition: diag_util.F90:993
integer num_input_fields
Definition: diag_data.F90:648
subroutine, public get_subfield_size(axes, outnum)
Definition: diag_util.F90:175
#define max(a, b)
Definition: mosaic_util.h:33
integer base_hour
Definition: diag_data.F90:773
subroutine, public get_tile_string(str_out, str_in, tile, str2_in)
Definition: fms_io.F90:7735
subroutine, public init_input_field(module_name, field_name, tile_count)
Definition: diag_util.F90:1426
logical do_diag_field_log
Definition: diag_data.F90:716
logical mix_snapshot_average_fields
Definition: diag_data.F90:710
logical prepend_date
Should the history file have the start date prepended to the file name.
Definition: diag_data.F90:734
character(len=128) function, public get_axis_reqfld(id)
Definition: diag_axis.F90:759
integer diag_log_unit
Definition: diag_data.F90:797
integer max_input_fields
Maximum number of input fields. Increase via diag_manager_nml.
Definition: diag_data.F90:713
type(time_type) time_zero
Definition: diag_data.F90:794
subroutine, public get_instance_filename(name_in, name_out)
Definition: fms_io.F90:8379
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)
logical region_out_use_alt_value
Definition: diag_data.F90:729
integer, parameter diag_days
Definition: diag_data.F90:106
subroutine, public init_file(name, output_freq, output_units, format, time_units, long_name, tile_count, new_file_freq, new_file_freq_units, start_time, file_duration, file_duration_units)
Definition: diag_util.F90:1059
real, parameter, public seconds_per_day
Seconds in a day [s].
Definition: constants.F90:116
integer, parameter very_large_file_freq
Definition: diag_data.F90:101
#define min(a, b)
Definition: mosaic_util.h:32
subroutine, public get_time(Time, seconds, days, ticks, err_msg)
subroutine, public error_mesg(routine, message, level)
Definition: fms.F90:529
logical debug_diag_manager
Definition: diag_data.F90:718
integer, parameter diag_months
Definition: diag_data.F90:106
integer base_minute
Definition: diag_data.F90:773
subroutine, public return_domain(domain2)
Definition: fms_io.F90:7444
subroutine, public diag_data_out(file, field, dat, time, final_call_in, static_write_in)
Definition: diag_util.F90:2418
type(domain1d), save, public null_domain1d