!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! !!
!! GNU General Public License !!
!! !!
!! This file is part of the Flexible Modeling System (FMS). !!
!! !!
!! FMS is free software; you can redistribute it and/or modify !!
!! it and are expected to follow the terms of the GNU General Public !!
!! License as published by the Free Software Foundation. !!
!! !!
!! FMS is distributed in the hope that it will be useful, !!
!! but WITHOUT ANY WARRANTY; without even the implied warranty of !!
!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the !!
!! GNU General Public License for more details. !!
!! !!
!! You should have received a copy of the GNU General Public License !!
!! along with FMS; if not, write to: !!
!! Free Software Foundation, Inc. !!
!! 59 Temple Place, Suite 330 !!
!! Boston, MA 02111-1307 USA !!
!! or see: !!
!! http://www.gnu.org/licenses/gpl.txt !!
!! !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
MODULE diag_util_mod
!
! Seth Underwood
!
!
!
!
!
!
USE diag_data_mod, ONLY : output_fields, input_fields, files, do_diag_field_log, diag_log_unit,&
& VERY_LARGE_AXIS_LENGTH, time_zero, VERY_LARGE_FILE_FREQ, END_OF_RUN, EVERY_TIME,&
& DIAG_SECONDS, DIAG_MINUTES, DIAG_HOURS, DIAG_DAYS, DIAG_MONTHS, DIAG_YEARS, base_time,&
& time_unit_list, max_files, base_year, base_month, base_day, base_hour, base_minute,&
& base_second, num_files, max_files, max_fields_per_file, max_out_per_in_field,&
& max_input_fields,num_input_fields, max_output_fields, num_output_fields, coord_type,&
& mix_snapshot_average_fields, global_descriptor, CMOR_MISSING_VALUE, use_cmor
USE diag_axis_mod, ONLY : get_diag_axis_data, get_axis_global_length, get_diag_axis_cart,&
& get_domain1d, get_domain2d, diag_subaxes_init, diag_axis_init, get_diag_axis, get_axis_aux,&
& get_axes_shift, get_diag_axis_name, get_diag_axis_domain_name
USE diag_output_mod, ONLY: diag_flush, diag_field_out, diag_output_init, write_axis_meta_data,&
& write_field_meta_data, done_meta_data
USE diag_grid_mod, ONLY: get_local_indexes
USE fms_mod, ONLY : error_mesg, FATAL, WARNING, mpp_pe, mpp_root_pe, lowercase, fms_error_handler
USE fms_io_mod, ONLY : get_tile_string, return_domain, string
USE mpp_domains_mod,ONLY : domain1d, domain2d, mpp_get_compute_domain, null_domain1d, null_domain2d,&
& OPERATOR(.NE.), OPERATOR(.EQ.), mpp_modify_domain, mpp_get_domain_components,&
& mpp_get_ntile_count, mpp_get_current_ntile, mpp_get_tile_id, mpp_mosaic_defined, mpp_get_tile_npes
USE time_manager_mod,ONLY: time_type, OPERATOR(==), OPERATOR(>), NO_CALENDAR, increment_date,&
& increment_time, get_calendar_type, get_date, get_time, leap_year, OPERATOR(-),&
& OPERATOR(<), OPERATOR(>=), OPERATOR(<=)
USE mpp_io_mod, ONLY : mpp_close
USE mpp_mod, ONLY : mpp_npes
USE constants_mod, ONLY : SECONDS_PER_DAY, SECONDS_PER_HOUR, SECONDS_PER_MINUTE
IMPLICIT NONE
PRIVATE
PUBLIC get_subfield_size, log_diag_field_info, update_bounds, check_out_of_bounds,&
& check_bounds_are_exact_dynamic, check_bounds_are_exact_static, init_file, diag_time_inc,&
& find_input_field, init_input_field, init_output_field, diag_data_out, write_static,&
& check_duplicate_output_fields, get_date_dif, get_subfield_vert_size, sync_file_times
CHARACTER(len=128),PRIVATE :: version =&
& '$Id: diag_util.F90,v 17.0.4.7 2009/12/10 16:24:12 sdu Exp $'
CHARACTER(len=128),PRIVATE :: tagname =&
& '$Name: mom4p1_pubrel_dec2009_nnz $'
CONTAINS
!
!
!
!
! SUBROUTINE get_subfield_size(axes, outnum)
!
!
! Get the size, start and end indices for output_fields(outnum), then
! fill in output_fields(outnum)%output_grid%(start_indx, end_indx)
!
! Axes of the input_field.
! Position in array output_fields.
SUBROUTINE get_subfield_size(axes, outnum)
INTEGER, INTENT(in) :: axes(:) ! axes of the input_field
INTEGER, INTENT(in) :: outnum ! position in array output_fields
REAL, ALLOCATABLE :: global_lat(:), global_lon(:), global_depth(:)
INTEGER :: global_axis_size
INTEGER :: i,xbegin,xend,ybegin,yend,xbegin_l,xend_l,ybegin_l,yend_l
CHARACTER(len=1) :: cart
TYPE(domain2d) :: Domain2, Domain2_new
TYPE(domain1d) :: Domain1,Domain1x,Domain1y,Domain1x_new,Domain1y_new
REAL :: start(3), end(3) ! start and end coordinates in 3 axes
INTEGER :: gstart_indx(3), gend_indx(3) ! global start and end indices of output domain in 3 axes
REAL, ALLOCATABLE :: subaxis_x(:), subaxis_y(:), subaxis_z(:) !containing local coordinates in x,y,z axes
CHARACTER(len=128) :: msg
INTEGER :: ishift, jshift
CHARACTER(len=128), DIMENSION(2) :: axis_domain_name
!initilization for local output
! initially out of (lat/lon/depth) range
start = -1.e10
end = -1.e10
gstart_indx = -1
gend_indx=-1
! get axis data (lat, lon, depth) and indices
start = output_fields(outnum)%output_grid%start
end = output_fields(outnum)%output_grid%end
CALL get_diag_axis_domain_name(axes(1), axis_domain_name(1))
CALL get_diag_axis_domain_name(axes(2), axis_domain_name(2))
IF ( INDEX(lowercase(axis_domain_name(1)), 'cubed') == 0 .AND. &
& INDEX(lowercase(axis_domain_name(2)), 'cubed') == 0 ) THEN
DO i = 1, SIZE(axes(:))
global_axis_size = get_axis_global_length(axes(i))
output_fields(outnum)%output_grid%subaxes(i) = -1
CALL get_diag_axis_cart(axes(i), cart)
SELECT CASE(cart)
CASE ('X')
! wrong order of axes. X should come first.
IF( i.NE.1 ) CALL error_mesg ('diag_util, get subfield size',&
& 'wrong order of axes, X should come first',FATAL)
ALLOCATE(global_lon(global_axis_size))
CALL get_diag_axis_data(axes(i),global_lon)
IF( INT( start(i)*END(i) ) == 1 ) THEN
gstart_indx(i) = 1
gend_indx(i) = global_axis_size
output_fields(outnum)%output_grid%subaxes(i) = axes(i)
ELSE
gstart_indx(i) = get_index(start(i),global_lon)
gend_indx(i) = get_index(END(i),global_lon)
END IF
ALLOCATE(subaxis_x(gstart_indx(i):gend_indx(i)))
subaxis_x=global_lon(gstart_indx(i):gend_indx(i))
CASE ('Y')
! wrong order of axes, Y should come second.
IF( i.NE.2 ) CALL error_mesg ('diag_util, get subfield size',&
& 'wrong order of axes, Y should come second',FATAL)
ALLOCATE(global_lat(global_axis_size))
CALL get_diag_axis_data(axes(i),global_lat)
IF( INT( start(i)*END(i) ) == 1 ) THEN
gstart_indx(i) = 1
gend_indx(i) = global_axis_size
output_fields(outnum)%output_grid%subaxes(i) = axes(i)
ELSE
gstart_indx(i) = get_index(start(i),global_lat)
gend_indx(i) = get_index(END(i),global_lat)
END IF
ALLOCATE(subaxis_y(gstart_indx(i):gend_indx(i)))
subaxis_y=global_lat(gstart_indx(i):gend_indx(i))
CASE ('Z')
! wrong values in vertical axis of region
IF ( start(i)*END(i)<0 ) CALL error_mesg ('diag_util, get subfield size',&
& 'wrong values in vertical axis of region',FATAL)
IF ( start(i)>0 .AND. END(i)>0 ) THEN
ALLOCATE(global_depth(global_axis_size))
CALL get_diag_axis_data(axes(i),global_depth)
gstart_indx(i) = get_index(start(i),global_depth)
gend_indx(i) = get_index(END(i),global_depth)
ALLOCATE(subaxis_z(gstart_indx(i):gend_indx(i)))
subaxis_z=global_depth(gstart_indx(i):gend_indx(i))
output_fields(outnum)%output_grid%subaxes(i) =&
& diag_subaxes_init(axes(i),subaxis_z, gstart_indx(i),gend_indx(i))
DEALLOCATE(subaxis_z,global_depth)
ELSE ! regional vertical axis is the same as global vertical axis
gstart_indx(i) = 1
gend_indx(i) = global_axis_size
output_fields(outnum)%output_grid%subaxes(i) = axes(i)
! i should equal 3 for z axis
IF( i /= 3 ) CALL error_mesg ('diag_util, get subfield size',&
& 'i should equal 3 for z axis', FATAL)
END IF
CASE default
! Wrong axis_cart
CALL error_mesg ('diag_util, get_subfield_size', 'Wrong axis_cart', FATAL)
END SELECT
END DO
DO i = 1, SIZE(axes(:))
IF( gstart_indx(i) == -1 .OR. gend_indx(i) == -1 ) THEN
!
! can not find gstart_indx/gend_indx for
! check region bounds for axis .
!
WRITE(msg,'(a,I2)') ' check region bounds for axis ', i
CALL error_mesg ('diag_util, get_subfield_size', 'can not find gstart_indx/gend_indx for '&
& //TRIM(output_fields(outnum)%output_name)//','//TRIM(msg), FATAL)
END IF
END DO
ELSE ! cubed sphere
! get the i and j start and end indexes
CALL get_local_indexes(LONSTART=start(1), LONEND=END(1), &
& LATSTART=start(2), LATEND=END(2), &
& ISTART=gstart_indx(1), IEND=gend_indx(1), &
& JSTART=gstart_indx(2), JEND=gend_indx(2))
global_axis_size = get_axis_global_length(axes(1))
ALLOCATE(global_lon(global_axis_size))
global_axis_size = get_axis_global_length(axes(2))
ALLOCATE(global_lat(global_axis_size))
CALL get_diag_axis_data(axes(1),global_lon)
CALL get_diag_axis_data(axes(2),global_lat)
IF ( (gstart_indx(1) > 0 .AND. gstart_indx(2) > 0) .AND. &
& (gend_indx(1) > 0 .AND. gend_indx(2) > 0) ) THEN
ALLOCATE(subaxis_x(gstart_indx(1):gend_indx(1)))
ALLOCATE(subaxis_y(gstart_indx(2):gend_indx(2)))
subaxis_x=global_lon(gstart_indx(1):gend_indx(1))
subaxis_y=global_lat(gstart_indx(2):gend_indx(2))
END IF
! Now deal with the Z component
IF ( SIZE(axes(:)) > 2 ) THEN
global_axis_size = get_axis_global_length(axes(3))
output_fields(outnum)%output_grid%subaxes(3) = -1
CALL get_diag_axis_cart(axes(3), cart)
IF ( lowercase(cart) /= 'z' ) CALL error_mesg('diag_util_mod::get_subfield_size', &
&'axis(3) should be Z-axis', FATAL)
! wrong values in vertical axis of region
IF ( start(3)*END(3)<0 ) CALL error_mesg ('diag_util, get subfield size',&
& 'wrong values in vertical axis of region',FATAL)
IF ( start(3)>0 .AND. END(3)>0 ) THEN
ALLOCATE(global_depth(global_axis_size))
CALL get_diag_axis_data(axes(3),global_depth)
gstart_indx(3) = get_index(start(3),global_depth)
IF( start(3) == 0.0 ) gstart_indx(3) = 1
gend_indx(3) = get_index(END(3),global_depth)
IF( start(3) >= MAXVAL(global_depth) ) gstart_indx(3)= global_axis_size
IF( END(3) >= MAXVAL(global_depth) ) gend_indx(3) = global_axis_size
ALLOCATE(subaxis_z(gstart_indx(3):gend_indx(3)))
subaxis_z=global_depth(gstart_indx(3):gend_indx(3))
output_fields(outnum)%output_grid%subaxes(3) =&
& diag_subaxes_init(axes(3),subaxis_z, gstart_indx(3),gend_indx(3))
DEALLOCATE(subaxis_z,global_depth)
ELSE ! regional vertical axis is the same as global vertical axis
gstart_indx(3) = 1
gend_indx(3) = global_axis_size
output_fields(outnum)%output_grid%subaxes(3) = axes(3)
END IF
END IF
END IF
! get domain and compute_domain(xbegin,xend,ybegin,yend)
xbegin=-1
xend=-1
ybegin=-1
yend=-1
Domain2 = get_domain2d(axes)
IF ( Domain2 .NE. NULL_DOMAIN2D ) THEN
CALL mpp_get_compute_domain(Domain2,xbegin,xend,ybegin,yend)
CALL mpp_get_domain_components(Domain2, Domain1x, Domain1y)
ELSE
DO i = 1, MIN(SIZE(axes(:)),2)
Domain1 = get_domain1d(axes(i))
IF ( Domain1 .NE. NULL_DOMAIN1D ) THEN
CALL get_diag_axis_cart(axes(i),cart)
SELECT CASE(cart)
CASE ('X')
Domain1x = get_domain1d(axes(i))
CALL mpp_get_compute_domain(Domain1x,xbegin,xend)
CASE ('Y')
Domain1y = get_domain1d(axes(i))
CALL mpp_get_compute_domain(Domain1y,ybegin,yend)
CASE default ! do nothing here
END SELECT
ELSE
! No domain available
CALL error_mesg ('diag_util, get_subfield_size', 'NO domain available', FATAL)
END IF
END DO
END IF
CALL get_axes_shift(axes, ishift, jshift)
xend = xend+ishift
yend = yend+jshift
IF ( xbegin== -1 .OR. xend==-1 .OR. ybegin==-1 .OR. yend==-1 ) THEN
! wrong compute domain indices
CALL error_mesg ('diag_util, get_subfield_size', 'wrong compute domain indices',FATAL)
END IF
! get the area containing BOTH compute domain AND local output area
IF(gstart_indx(1)> xend .OR. xbegin > gend_indx(1)) THEN
output_fields(outnum)%output_grid%l_start_indx(1) = -1
output_fields(outnum)%output_grid%l_end_indx(1) = -1
output_fields(outnum)%need_compute = .FALSE. ! not involved
ELSEIF (gstart_indx(2)> yend .OR. ybegin > gend_indx(2)) THEN
output_fields(outnum)%output_grid%l_start_indx(2) = -1
output_fields(outnum)%output_grid%l_end_indx(2) = -1
output_fields(outnum)%need_compute = .FALSE. ! not involved
ELSE
output_fields(outnum)%output_grid%l_start_indx(1) = MAX(xbegin, gstart_indx(1))
output_fields(outnum)%output_grid%l_start_indx(2) = MAX(ybegin, gstart_indx(2))
output_fields(outnum)%output_grid%l_end_indx(1) = MIN(xend, gend_indx(1))
output_fields(outnum)%output_grid%l_end_indx(2) = MIN(yend, gend_indx(2))
output_fields(outnum)%need_compute = .TRUE. ! involved in local output
END IF
IF ( output_fields(outnum)%need_compute ) THEN
! need to modify domain1d and domain2d for subaxes
xbegin_l = output_fields(outnum)%output_grid%l_start_indx(1)
xend_l = output_fields(outnum)%output_grid%l_end_indx(1)
ybegin_l = output_fields(outnum)%output_grid%l_start_indx(2)
yend_l = output_fields(outnum)%output_grid%l_end_indx(2)
CALL mpp_modify_domain(Domain2, Domain2_new, xbegin_l,xend_l, ybegin_l,yend_l,&
& gstart_indx(1),gend_indx(1), gstart_indx(2),gend_indx(2))
CALL mpp_get_domain_components(Domain2_new, Domain1x_new, Domain1y_new)
output_fields(outnum)%output_grid%subaxes(1) =&
& diag_subaxes_init(axes(1),subaxis_x, gstart_indx(1),gend_indx(1),Domain2_new)
output_fields(outnum)%output_grid%subaxes(2) =&
& diag_subaxes_init(axes(2),subaxis_y, gstart_indx(2),gend_indx(2),Domain2_new)
DO i = 1, SIZE(axes(:))
IF(output_fields(outnum)%output_grid%subaxes(i) == -1) THEN
! error at i =
WRITE(msg,'(a,"/",I4)') 'at i = ',i
CALL error_mesg ('diag_util, get_subfield_size '//TRIM(output_fields(outnum)%output_name),&
'error '//TRIM(msg), FATAL)
END IF
END DO
! local start index should start from 1
output_fields(outnum)%output_grid%l_start_indx(1) = MAX(xbegin, gstart_indx(1)) - xbegin + 1
output_fields(outnum)%output_grid%l_start_indx(2) = MAX(ybegin, gstart_indx(2)) - ybegin + 1
output_fields(outnum)%output_grid%l_end_indx(1) = MIN(xend, gend_indx(1)) - xbegin + 1
output_fields(outnum)%output_grid%l_end_indx(2) = MIN(yend, gend_indx(2)) - ybegin + 1
IF ( SIZE(axes(:))>2 ) THEN
output_fields(outnum)%output_grid%l_start_indx(3) = gstart_indx(3)
output_fields(outnum)%output_grid%l_end_indx(3) = gend_indx(3)
ELSE
output_fields(outnum)%output_grid%l_start_indx(3) = 1
output_fields(outnum)%output_grid%l_end_indx(3) = 1
END IF
END IF
IF ( ALLOCATED(subaxis_x) ) DEALLOCATE(subaxis_x, global_lon)
IF ( ALLOCATED(subaxis_y) ) DEALLOCATE(subaxis_y, global_lat)
END SUBROUTINE get_subfield_size
!
!
!
! Get size, start and end indices for output_fields(outnum), fill in
! output_fields(outnum)%output_grid%(start_indx, end_indx)
!
!
! SUBROUTINE get_subfield_ver_size(axes, outnum)
!
!
! Get size, start and end indices for output_fields(outnum), fill in
! output_fields(outnum)%output_grid%(start_indx, end_indx).
!
! Axes of the input_field
! Position in array output_fields.
SUBROUTINE get_subfield_vert_size(axes, outnum)
INTEGER, DIMENSION(:), INTENT(in) :: axes ! axes of the input_field
INTEGER, INTENT(in) :: outnum ! position in array output_fields
REAL, DIMENSION(3) :: start, end ! start and end coordinates in 3 axes
REAL, ALLOCATABLE, DIMENSION(:) :: global_depth
REAL, ALLOCATABLE, DIMENSION(:) :: subaxis_z !containing local coordinates in x,y,z axes
INTEGER :: i, global_axis_size
INTEGER, DIMENSION(3) :: gstart_indx, gend_indx ! global start and end indices of output domain in 3 axes
CHARACTER(len=1) :: cart
CHARACTER(len=128) :: msg
!initilization for local output
start = -1.e10
end = -1.e10 ! initially out of (lat/lon/depth) range
gstart_indx = -1
gend_indx=-1
! get axis data (lat, lon, depth) and indices
start= output_fields(outnum)%output_grid%start
end = output_fields(outnum)%output_grid%end
DO i = 1, SIZE(axes(:))
global_axis_size = get_axis_global_length(axes(i))
output_fields(outnum)%output_grid%subaxes(i) = -1
CALL get_diag_axis_cart(axes(i), cart)
SELECT CASE(cart)
CASE ('X')
! wrong order of axes, X should come first
IF ( i.NE.1 ) CALL error_mesg ('diag_util, get subfield vert size',&
& 'wrong order of axes, X should come first',FATAL)
gstart_indx(i) = 1
gend_indx(i) = global_axis_size
output_fields(outnum)%output_grid%subaxes(i) = axes(i)
CASE ('Y')
! wrong order of axes, Y should come second
IF( i.NE.2 ) CALL error_mesg ('diag_util, get subfield vert size',&
& 'wrong order of axes, Y should come second',FATAL)
gstart_indx(i) = 1
gend_indx(i) = global_axis_size
output_fields(outnum)%output_grid%subaxes(i) = axes(i)
CASE ('Z')
! wrong values in vertical axis of region
IF( start(i)*END(i) < 0 ) CALL error_mesg ('diag_util, get subfield vert size',&
& 'wrong values in vertical axis of region',FATAL)
IF( start(i) >= 0 .AND. END(i) > 0 ) THEN
ALLOCATE(global_depth(global_axis_size))
CALL get_diag_axis_data(axes(i),global_depth)
gstart_indx(i) = get_index(start(i),global_depth)
IF( start(i) == 0.0 ) gstart_indx(i) = 1
gend_indx(i) = get_index(END(i),global_depth)
IF( start(i) >= MAXVAL(global_depth) ) gstart_indx(i)= global_axis_size
IF( END(i) >= MAXVAL(global_depth) ) gend_indx(i) = global_axis_size
ALLOCATE(subaxis_z(gstart_indx(i):gend_indx(i)))
subaxis_z=global_depth(gstart_indx(i):gend_indx(i))
output_fields(outnum)%output_grid%subaxes(i) =&
& diag_subaxes_init(axes(i),subaxis_z, gstart_indx(i),gend_indx(i))
DEALLOCATE(subaxis_z,global_depth)
ELSE ! vertical axis is the same as global vertical axis
gstart_indx(i) = 1
gend_indx(i) = global_axis_size
output_fields(outnum)%output_grid%subaxes(i) = axes(i)
! i should equal 3 for z axis
IF( i /= 3 ) CALL error_mesg ('diag_util, get subfield vert size',&
& 'i should equal 3 for z axis', FATAL)
END IF
CASE default
! Wrong axis_cart
CALL error_mesg ('diag_util, get_subfield_vert_size', 'Wrong axis_cart', FATAL)
END SELECT
END DO
DO i = 1,SIZE(axes(:))
IF ( gstart_indx(i)== -1 .OR. gend_indx(i)== -1 ) THEN
!
! can not find gstart_indx/gend_indx for
! check region bounds for axis
!
WRITE(msg,'(a,I2)') ' check region bounds for axis ', i
CALL error_mesg ('diag_util, get_subfield_vert_size', 'can not find gstart_indx/gend_indx for '&
& //TRIM(output_fields(outnum)%output_name)//','//TRIM(msg), FATAL)
END IF
END DO
DO i= 1, 2
output_fields(outnum)%output_grid%l_start_indx(i) = gstart_indx(i)
output_fields(outnum)%output_grid%l_end_indx(i) = gend_indx(i)
END DO
IF( SIZE(axes(:)) > 2 ) THEN
output_fields(outnum)%output_grid%l_start_indx(3) = gstart_indx(3)
output_fields(outnum)%output_grid%l_end_indx(3) = gend_indx(3)
ELSE
output_fields(outnum)%output_grid%l_start_indx(3) = 1
output_fields(outnum)%output_grid%l_end_indx(3) = 1
END IF
END SUBROUTINE get_subfield_vert_size
!
!
!
!
! Find index i of array such that array(i) is closest to number
! array must be monotonouslly ordered
!
!
! INTEGER FUNCTION get_index(number, array)
!
!
! Find index i of array such that array(i) is closest to number
! array must be monotonouslly ordered
!
!
!
INTEGER FUNCTION get_index(number, array)
REAL, INTENT(in) :: number
REAL, INTENT(in), DIMENSION(:) :: array
INTEGER :: i, n
LOGICAL :: found
n = SIZE(array(:))
! check if array is monotonous
DO i = 2, n-1
IF( (array(i-1)array(i+1)) .OR. (array(i-1)>array(i).AND.array(i)array NOT monotonously ordered
CALL error_mesg('diag_util', 'get_index, array NOT monotonously ordered',FATAL)
END IF
END DO
get_index = -1
found = .FALSE.
! search in increasing array
DO i = 1, n-1
IF ( (array(i)<=number).AND.(array(i+1)>= number) ) THEN
IF( number - array(i) <= array(i+1) - number ) THEN
get_index = i
found=.TRUE.
ELSE
get_index = i+1
found=.TRUE.
ENDIF
EXIT
END IF
END DO
! if not found, search in decreasing array
IF( .NOT.found ) THEN
DO i = 1, n-1
IF ( (array(i)>=number).AND.(array(i+1)<= number) ) THEN
IF ( array(i)-number <= number-array(i+1) ) THEN
get_index = i
found = .TRUE.
ELSE
get_index = i+1
found = .TRUE.
END IF
EXIT
END IF
END DO
END IF
! if still not found, is it less than the first element
! or greater than last element? (Increasing Array)
IF ( .NOT. found ) THEN
IF ( array(1).GT.number ) THEN
get_index = 1
found = .TRUE.
ELSE IF ( array(n).LT.number ) THEN
get_index = n
found = .TRUE.
ELSE
found = .FALSE.
END IF
END IF
! if still not found, is it greater than the first element
! or less than the last element? (Decreasing Array)
IF ( .NOT. found ) THEN
IF ( array(1).LT.number ) THEN
get_index = 1
found = .TRUE.
ELSE IF ( array(n).GT.number ) THEN
get_index = n
found = .TRUE.
ELSE
found = .FALSE.
END IF
END IF
END FUNCTION get_index
!
!
!
!
! Writes brief diagnostic field info to the log file.
!
!
! SUBROUTINE log_diag_field_info(module_name, field_name, axes, long_name, units,
! missing_value, range, dynamic)
!
!
! If the do_diag_field_log namelist parameter is .TRUE.,
! then a line briefly describing diagnostic field is added to
! the log file. Normally users should not call this subroutine
! directly, since it is called by register_static_field and
! register_diag_field if do_not_log is not set to .TRUE.. It is
! used, however, in LM3 to avoid excessive logs due to the
! number of fields registered for each of the tile types. LM3
! code uses a do_not_log parameter in the registration calls,
! and subsequently calls this subroutine to log field information
! under a generic name.
!
!
!
!
!
!
!
!
!
SUBROUTINE log_diag_field_info(module_name, field_name, axes, long_name, units,&
& missing_value, range, dynamic)
CHARACTER(len=*), INTENT(in) :: module_name, field_name
INTEGER, DIMENSION(:), INTENT(in) :: axes
CHARACTER(len=*), OPTIONAL, INTENT(in) :: long_name, units
REAL, OPTIONAL, INTENT(in) :: missing_value
REAL, DIMENSION(2), OPTIONAL, INTENT(IN) :: range
LOGICAL, OPTIONAL, INTENT(in) :: dynamic
! ---- local vars
CHARACTER(len=256) :: lmodule, lfield, lname, lunits
CHARACTER(len=64) :: lmissval, lmin, lmax
CHARACTER(len=8) :: numaxis, timeaxis
CHARACTER(len=1) :: sep = '|'
CHARACTER(len=256) :: axis_name, axes_list
INTEGER :: i
IF ( .NOT.do_diag_field_log ) RETURN
IF ( mpp_pe().NE.mpp_root_pe() ) RETURN
lmodule = TRIM(module_name)
lfield = TRIM(field_name)
IF ( PRESENT(long_name) ) THEN
lname = TRIM(long_name)
ELSE
lname = ''
END IF
IF ( PRESENT(units) ) THEN
lunits = TRIM(units)
ELSE
lunits = ''
END IF
WRITE (numaxis,'(i1)') SIZE(axes)
IF (PRESENT(missing_value)) THEN
IF ( use_cmor ) THEN
WRITE (lmissval,*) CMOR_MISSING_VALUE
ELSE
WRITE (lmissval,*) missing_value
END IF
ELSE
lmissval = ''
ENDIF
IF ( PRESENT(range) ) THEN
WRITE (lmin,*) range(1)
WRITE (lmax,*) range(2)
ELSE
lmin = ''
lmax = ''
END IF
IF ( PRESENT(dynamic) ) THEN
IF (dynamic) THEN
timeaxis = 'T'
ELSE
timeaxis = 'F'
END IF
ELSE
timeaxis = ''
END IF
axes_list=''
DO i = 1, SIZE(axes)
CALL get_diag_axis_name(axes(i),axis_name)
IF ( TRIM(axes_list) /= '' ) axes_list = TRIM(axes_list)//','
axes_list = TRIM(axes_list)//TRIM(axis_name)
END DO
!write (diag_log_unit,'(8(a,a),a)') &
WRITE (diag_log_unit,'(777a)') &
& TRIM(lmodule), sep, TRIM(lfield), sep, TRIM(lname), sep,&
& TRIM(lunits), sep, TRIM(numaxis), sep, TRIM(timeaxis), sep,&
& TRIM(lmissval), sep, TRIM(lmin), sep, TRIM(lmax), sep,&
& TRIM(axes_list)
END SUBROUTINE log_diag_field_info
!
!
!
!
!
! SUBROUTINE update_bonds(out_num, lower_i, upper_i, lower_j, upper_j, lower_k, upper_k)
!
!
!
!
!
!
!
!
!
!
SUBROUTINE update_bounds(out_num, lower_i, upper_i, lower_j, upper_j, lower_k, upper_k)
INTEGER, INTENT(in) :: out_num, lower_i, upper_i, lower_j, upper_j, lower_k, upper_k
output_fields(out_num)%imin = MIN(output_fields(out_num)%imin, lower_i)
output_fields(out_num)%imax = MAX(output_fields(out_num)%imax, upper_i)
output_fields(out_num)%jmin = MIN(output_fields(out_num)%jmin, lower_j)
output_fields(out_num)%jmax = MAX(output_fields(out_num)%jmax, upper_j)
output_fields(out_num)%kmin = MIN(output_fields(out_num)%kmin, lower_k)
output_fields(out_num)%kmax = MAX(output_fields(out_num)%kmax, upper_k)
END SUBROUTINE update_bounds
!
!
!
!
!
! SUBROUTINE check_out_of_bounds(out_num, diag_field_id, err_msg)
!
!
!
!
!
!
SUBROUTINE check_out_of_bounds(out_num, diag_field_id, err_msg)
INTEGER, INTENT(in) :: out_num, diag_field_id
CHARACTER(len=*), INTENT(out) :: err_msg
CHARACTER(len=128) :: error_string1, error_string2
IF(output_fields(out_num)%imin < LBOUND(output_fields(out_num)%buffer,1) .OR.&
& output_fields(out_num)%imax > UBOUND(output_fields(out_num)%buffer,1) .OR.&
& output_fields(out_num)%jmin < LBOUND(output_fields(out_num)%buffer,2) .OR.&
& output_fields(out_num)%jmax > UBOUND(output_fields(out_num)%buffer,2) .OR.&
& output_fields(out_num)%kmin < LBOUND(output_fields(out_num)%buffer,3) .OR.&
& output_fields(out_num)%kmax > UBOUND(output_fields(out_num)%buffer,3) ) THEN
WRITE(error_string1,'(a,"/",a)') TRIM(input_fields(diag_field_id)%module_name),&
& TRIM(output_fields(out_num)%output_name)
error_string2 ='Buffer bounds= : , : , : Actual bounds= : , : , : '
WRITE(error_string2(15:17),'(i3)') LBOUND(output_fields(out_num)%buffer,1)
WRITE(error_string2(19:21),'(i3)') UBOUND(output_fields(out_num)%buffer,1)
WRITE(error_string2(23:25),'(i3)') LBOUND(output_fields(out_num)%buffer,2)
WRITE(error_string2(27:29),'(i3)') UBOUND(output_fields(out_num)%buffer,2)
WRITE(error_string2(31:33),'(i3)') LBOUND(output_fields(out_num)%buffer,3)
WRITE(error_string2(35:37),'(i3)') UBOUND(output_fields(out_num)%buffer,3)
WRITE(error_string2(54:56),'(i3)') output_fields(out_num)%imin
WRITE(error_string2(58:60),'(i3)') output_fields(out_num)%imax
WRITE(error_string2(62:64),'(i3)') output_fields(out_num)%jmin
WRITE(error_string2(66:68),'(i3)') output_fields(out_num)%jmax
WRITE(error_string2(70:72),'(i3)') output_fields(out_num)%kmin
WRITE(error_string2(74:76),'(i3)') output_fields(out_num)%kmax
err_msg = 'module/output_field='//TRIM(error_string1)//&
& ' Bounds of buffer exceeded. '//TRIM(error_string2)
! imax, imin, etc need to be reset in case the program is not terminated.
output_fields(out_num)%imax = 0
output_fields(out_num)%imin = VERY_LARGE_AXIS_LENGTH
output_fields(out_num)%jmax = 0
output_fields(out_num)%jmin = VERY_LARGE_AXIS_LENGTH
output_fields(out_num)%kmax = 0
output_fields(out_num)%kmin = VERY_LARGE_AXIS_LENGTH
ELSE
err_msg = ''
END IF
END SUBROUTINE check_out_of_bounds
!
!
!
!
!
! SUBROUTINE check_bounds_are_exact_dynamic(out_num, diag_field_id, Time, err_msg)
!
!
!
!
!
!
!
SUBROUTINE check_bounds_are_exact_dynamic(out_num, diag_field_id, Time, err_msg)
INTEGER, INTENT(in) :: out_num, diag_field_id
TYPE(time_type), INTENT(in) :: Time
CHARACTER(len=*), INTENT(out) :: err_msg
CHARACTER(len=128) :: error_string1, error_string2
LOGICAL :: do_check
err_msg = ''
! Check bounds only when the value of Time changes. When windows are used,
! a change in Time indicates that a new loop through the windows has begun,
! so a check of the previous loop can be done.
IF ( Time == output_fields(out_num)%Time_of_prev_field_data ) THEN
do_check = .FALSE.
ELSE
IF ( output_fields(out_num)%Time_of_prev_field_data == Time_zero ) THEN
! It may or may not be OK to check, I don't know how to tell.
! Check will be done on subsequent calls anyway.
do_check = .FALSE.
ELSE
do_check = .TRUE.
END IF
output_fields(out_num)%Time_of_prev_field_data = Time
END IF
IF ( do_check ) THEN
IF ( output_fields(out_num)%imin /= LBOUND(output_fields(out_num)%buffer,1 ) .OR.&
& output_fields(out_num)%imax /= UBOUND(output_fields(out_num)%buffer,1) .OR.&
& output_fields(out_num)%jmin /= LBOUND(output_fields(out_num)%buffer,2) .OR.&
& output_fields(out_num)%jmax /= UBOUND(output_fields(out_num)%buffer,2) .OR.&
& output_fields(out_num)%kmin /= LBOUND(output_fields(out_num)%buffer,3) .OR.&
& output_fields(out_num)%kmax /= UBOUND(output_fields(out_num)%buffer,3) ) THEN
WRITE(error_string1,'(a,"/",a)') TRIM(input_fields(diag_field_id)%module_name),&
& TRIM(output_fields(out_num)%output_name)
error_string2 ='Buffer bounds= : , : , : Actual bounds= : , : , : '
WRITE(error_string2(15:17),'(i3)') LBOUND(output_fields(out_num)%buffer,1)
WRITE(error_string2(19:21),'(i3)') UBOUND(output_fields(out_num)%buffer,1)
WRITE(error_string2(23:25),'(i3)') LBOUND(output_fields(out_num)%buffer,2)
WRITE(error_string2(27:29),'(i3)') UBOUND(output_fields(out_num)%buffer,2)
WRITE(error_string2(31:33),'(i3)') LBOUND(output_fields(out_num)%buffer,3)
WRITE(error_string2(35:37),'(i3)') UBOUND(output_fields(out_num)%buffer,3)
WRITE(error_string2(54:56),'(i3)') output_fields(out_num)%imin
WRITE(error_string2(58:60),'(i3)') output_fields(out_num)%imax
WRITE(error_string2(62:64),'(i3)') output_fields(out_num)%jmin
WRITE(error_string2(66:68),'(i3)') output_fields(out_num)%jmax
WRITE(error_string2(70:72),'(i3)') output_fields(out_num)%kmin
WRITE(error_string2(74:76),'(i3)') output_fields(out_num)%kmax
err_msg = TRIM(error_string1)//' Bounds of data do not match those of buffer. '//TRIM(error_string2)
END IF
output_fields(out_num)%imax = 0
output_fields(out_num)%imin = VERY_LARGE_AXIS_LENGTH
output_fields(out_num)%jmax = 0
output_fields(out_num)%jmin = VERY_LARGE_AXIS_LENGTH
output_fields(out_num)%kmax = 0
output_fields(out_num)%kmin = VERY_LARGE_AXIS_LENGTH
END IF
END SUBROUTINE check_bounds_are_exact_dynamic
!
!
!
!
!
! SUBROUTINE check_bounds_are_exact_static(out_num, diag_field_id, err_msg)
!
!
!
!
!
!
SUBROUTINE check_bounds_are_exact_static(out_num, diag_field_id, err_msg)
INTEGER, INTENT(in) :: out_num, diag_field_id
CHARACTER(len=*), INTENT(out) :: err_msg
CHARACTER(len=128) :: error_string1, error_string2
err_msg = ''
IF( output_fields(out_num)%imin /= LBOUND(output_fields(out_num)%buffer,1 ) .OR.&
& output_fields(out_num)%imax /= UBOUND(output_fields(out_num)%buffer,1) .OR.&
& output_fields(out_num)%jmin /= LBOUND(output_fields(out_num)%buffer,2) .OR.&
& output_fields(out_num)%jmax /= UBOUND(output_fields(out_num)%buffer,2) .OR.&
& output_fields(out_num)%kmin /= LBOUND(output_fields(out_num)%buffer,3) .OR.&
& output_fields(out_num)%kmax /= UBOUND(output_fields(out_num)%buffer,3) ) THEN
WRITE(error_string1,'(a,"/",a)') TRIM(input_fields(diag_field_id)%module_name),&
& TRIM(output_fields(out_num)%output_name)
error_string2 ='Buffer bounds= : , : , : Actual bounds= : , : , : '
WRITE(error_string2(15:17),'(i3)') LBOUND(output_fields(out_num)%buffer,1)
WRITE(error_string2(19:21),'(i3)') UBOUND(output_fields(out_num)%buffer,1)
WRITE(error_string2(23:25),'(i3)') LBOUND(output_fields(out_num)%buffer,2)
WRITE(error_string2(27:29),'(i3)') UBOUND(output_fields(out_num)%buffer,2)
WRITE(error_string2(31:33),'(i3)') LBOUND(output_fields(out_num)%buffer,3)
WRITE(error_string2(35:37),'(i3)') UBOUND(output_fields(out_num)%buffer,3)
WRITE(error_string2(54:56),'(i3)') output_fields(out_num)%imin
WRITE(error_string2(58:60),'(i3)') output_fields(out_num)%imax
WRITE(error_string2(62:64),'(i3)') output_fields(out_num)%jmin
WRITE(error_string2(66:68),'(i3)') output_fields(out_num)%jmax
WRITE(error_string2(70:72),'(i3)') output_fields(out_num)%kmin
WRITE(error_string2(74:76),'(i3)') output_fields(out_num)%kmax
err_msg = TRIM(error_string1)//' Bounds of data do not match those of buffer. '//TRIM(error_string2)
END IF
output_fields(out_num)%imax = 0
output_fields(out_num)%imin = VERY_LARGE_AXIS_LENGTH
output_fields(out_num)%jmax = 0
output_fields(out_num)%jmin = VERY_LARGE_AXIS_LENGTH
output_fields(out_num)%kmax = 0
output_fields(out_num)%kmin = VERY_LARGE_AXIS_LENGTH
END SUBROUTINE check_bounds_are_exact_static
!
!
!
!
!
! SUBROUTINE 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)
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
SUBROUTINE 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)
CHARACTER(len=*), INTENT(in) :: name, long_name
INTEGER, INTENT(in) :: output_freq, output_units, FORMAT, time_units
INTEGER, INTENT(in) :: tile_count
INTEGER, INTENT(in), OPTIONAL :: new_file_freq, new_file_freq_units
INTEGER, INTENT(in), OPTIONAL :: file_duration, file_duration_units
TYPE(time_type), INTENT(in), OPTIONAL :: start_time
INTEGER :: new_file_freq1, new_file_freq_units1
INTEGER :: file_duration1, file_duration_units1
REAL, DIMENSION(1) :: tdata
CHARACTER(len=128) :: time_units_str
! Get a number for this file
num_files = num_files + 1
IF ( num_files >= max_files ) THEN
!
! max_files exceeded, increase max_files via the max_files variable
! in the namelist diag_manager_nml
!
CALL error_mesg('diag_util, init_file',&
& ' max_files exceeded, increase max_files via the max_files variable&
& in the namelist diag_manager_nml.', FATAL)
END IF
IF ( PRESENT(new_file_freq) ) THEN
new_file_freq1 = new_file_freq
ELSE
new_file_freq1 = VERY_LARGE_FILE_FREQ
END IF
IF ( PRESENT(new_file_freq_units) ) THEN
new_file_freq_units1 = new_file_freq_units
ELSE IF ( get_calendar_type() == NO_CALENDAR ) THEN
new_file_freq_units1 = DIAG_DAYS
ELSE
new_file_freq_units1 = DIAG_YEARS
END IF
IF ( PRESENT(file_duration) ) THEN
file_duration1 = file_duration
ELSE
file_duration1 = new_file_freq1
END IF
IF ( PRESENT(file_duration_units) ) THEN
file_duration_units1 = file_duration_units
ELSE
file_duration_units1 = new_file_freq_units1
END IF
files(num_files)%tile_count = tile_count
files(num_files)%name = TRIM(name)
files(num_files)%output_freq = output_freq
files(num_files)%output_units = output_units
files(num_files)%format = FORMAT
files(num_files)%time_units = time_units
files(num_files)%long_name = TRIM(long_name)
files(num_files)%num_fields = 0
files(num_files)%local = .FALSE.
files(num_files)%last_flush = base_time
files(num_files)%file_unit = -1
files(num_files)%new_file_freq = new_file_freq1
files(num_files)%new_file_freq_units = new_file_freq_units1
files(num_files)%duration = file_duration1
files(num_files)%duration_units = file_duration_units1
IF ( PRESENT(start_time) ) THEN
files(num_files)%start_time = start_time
ELSE
files(num_files)%start_time = base_time
END IF
files(num_files)%next_open=diag_time_inc(files(num_files)%start_time,new_file_freq1,new_file_freq_units1)
files(num_files)%close_time = diag_time_inc(files(num_files)%start_time,file_duration1, file_duration_units1)
IF ( files(num_files)%close_time>files(num_files)%next_open ) THEN
!
! close time GREATER than next_open time, check file duration,
! file frequency in < files(num_files)%name>
!
CALL error_mesg('init_file', 'close time '//'GREATER than'//&
& ' next_open time, check file duration, file frequency in '// files(num_files)%name, FATAL)
END IF
! add time_axis_id and time_bounds_id here
WRITE(time_units_str, 11) TRIM(time_unit_list(files(num_files)%time_units)), base_year,&
& base_month, base_day, base_hour, base_minute, base_second
11 FORMAT(a, ' since ', i4.4, '-', i2.2, '-', i2.2, ' ', i2.2, ':', i2.2, ':', i2.2)
files(num_files)%time_axis_id = diag_axis_init (TRIM(long_name), tdata, time_units_str, 'T',&
& TRIM(long_name) , set_name=TRIM(name) )
!---- register axis for storing time boundaries
files(num_files)%time_bounds_id = diag_axis_init( 'nv',(/1.,2./),'none','N','vertex number',&
& set_name=TRIM(name))
END SUBROUTINE init_file
!
!
!
! Synchronize the file's start and close times with the model start and end times.
!
!
! SUBROUTINE sync_file_times(init_time)
!
!
! sync_file_times checks to see if the file start time is less than the
! model's init time (passed in as the only argument). If it is less, then the
! both the file start time and end time are synchronized using the passed in initial time
! and the duration as calculated by the diag_time_inc function. sync_file_times
! will also increase the next_open until it is greater than the init_time.
!
! The file ID
! Initial time use for the synchronization.
! Return error message
SUBROUTINE sync_file_times(file_id, init_time, err_msg)
INTEGER, INTENT(in) :: file_id
TYPE(time_type), INTENT(in) :: init_time
CHARACTER(len=*), OPTIONAL, INTENT(out) :: err_msg
CHARACTER(len=128) :: msg
IF ( PRESENT(err_msg) ) err_msg = ''
IF ( files(file_id)%start_time < init_time ) THEN
! Sync the start_time of the file with the initial time of the model
files(file_id)%start_time = init_time
! Sync the file's close time also
files(file_id)%close_time = diag_time_inc(files(file_id)%start_time,&
& files(file_id)%duration, files(file_id)%duration_units)
END IF
! Need to increase next_open until it is greate than init_time
DO WHILE ( files(file_id)%next_open <= init_time )
files(file_id)%next_open = diag_time_inc(files(file_id)%next_open,&
& files(file_id)%new_file_freq, files(file_id)%new_file_freq_units, err_msg=msg)
IF ( msg /= '' ) THEN
IF ( fms_error_handler('diag_util_mod::sync_file_times',&
& ' file='//TRIM(files(file_id)%name)//': '//TRIM(msg), err_msg) ) RETURN
END IF
END DO
END SUBROUTINE sync_file_times
!
!
!
!
!
! TYPE(time_type) FUNCTION diag_time_inc(time, output_freq, output_units, err_msg)
!
!
!
!
!
!
!
TYPE(time_type) FUNCTION diag_time_inc(time, output_freq, output_units, err_msg)
TYPE(time_type), INTENT(in) :: time
INTEGER, INTENT(in):: output_freq, output_units
CHARACTER(len=*), INTENT(out), OPTIONAL :: err_msg
CHARACTER(len=128) :: error_message_local
IF ( PRESENT(err_msg) ) err_msg = ''
error_message_local = ''
! special values for output frequency are -1 for output at end of run
! and 0 for every timestep. Need to check for these here?
! Return zero time increment, hopefully this value is never used
IF ( output_freq == END_OF_RUN .OR. output_freq == EVERY_TIME ) THEN
diag_time_inc = time
RETURN
END IF
! Make sure calendar was not set after initialization
IF ( output_units == DIAG_SECONDS ) THEN
IF ( get_calendar_type() == NO_CALENDAR ) THEN
diag_time_inc = increment_time(time, output_freq, 0, err_msg=error_message_local)
ELSE
diag_time_inc = increment_date(time, 0, 0, 0, 0, 0, output_freq, err_msg=error_message_local)
END IF
ELSE IF ( output_units == DIAG_MINUTES ) THEN
IF ( get_calendar_type() == NO_CALENDAR ) THEN
diag_time_inc = increment_time(time, NINT(output_freq*SECONDS_PER_MINUTE), 0, &
&err_msg=error_message_local)
ELSE
diag_time_inc = increment_date(time, 0, 0, 0, 0, output_freq, 0, err_msg=error_message_local)
END IF
ELSE IF ( output_units == DIAG_HOURS ) THEN
IF ( get_calendar_type() == NO_CALENDAR ) THEN
diag_time_inc = increment_time(time, NINT(output_freq*SECONDS_PER_HOUR), 0, err_msg=error_message_local)
ELSE
diag_time_inc = increment_date(time, 0, 0, 0, output_freq, 0, 0, err_msg=error_message_local)
END IF
ELSE IF ( output_units == DIAG_DAYS ) THEN
IF (get_calendar_type() == NO_CALENDAR) THEN
diag_time_inc = increment_time(time, 0, output_freq, err_msg=error_message_local)
ELSE
diag_time_inc = increment_date(time, 0, 0, output_freq, 0, 0, 0, err_msg=error_message_local)
END IF
ELSE IF ( output_units == DIAG_MONTHS ) THEN
IF (get_calendar_type() == NO_CALENDAR) THEN
error_message_local = 'output units of months NOT allowed with no calendar'
ELSE
diag_time_inc = increment_date(time, 0, output_freq, 0, 0, 0, 0, err_msg=error_message_local)
END IF
ELSE IF ( output_units == DIAG_YEARS ) THEN
IF ( get_calendar_type() == NO_CALENDAR ) THEN
error_message_local = 'output units of years NOT allowed with no calendar'
ELSE
diag_time_inc = increment_date(time, output_freq, 0, 0, 0, 0, 0, err_msg=error_message_local)
END IF
ELSE
error_message_local = 'illegal output units'
END IF
IF ( error_message_local /= '' ) THEN
IF ( fms_error_handler('diag_time_inc',error_message_local,err_msg) ) RETURN
END IF
END FUNCTION diag_time_inc
!
!
!
!
!
!
! INTEGER FUNCTION fild_file(name, time_count)
!
!
!
!
!
INTEGER FUNCTION find_file(name, tile_count)
INTEGER, INTENT(in) :: tile_count
CHARACTER(len=*), INTENT(in) :: name
INTEGER :: i
find_file = -1
DO i = 1, num_files
IF( TRIM(files(i)%name) == TRIM(name) .AND. tile_count == files(i)%tile_count ) THEN
find_file = i
RETURN
END IF
END DO
END FUNCTION find_file
!
!
!
!
!
!
! INTEGER FUNCTION find_input_field(module_name, field_name, tile_count)
!
!
!
!
!
!
INTEGER FUNCTION find_input_field(module_name, field_name, tile_count)
CHARACTER(len=*), INTENT(in) :: module_name, field_name
INTEGER, INTENT(in) :: tile_count
INTEGER :: i
find_input_field = -1 ! Default return value if not found.
DO i = 1, num_input_fields
IF(tile_count == input_fields(i)%tile_count .AND.&
& TRIM(input_fields(i)%module_name) == TRIM(module_name) .AND.&
& lowercase(TRIM(input_fields(i)%field_name)) == lowercase(TRIM(field_name))) THEN
find_input_field = i
RETURN
END IF
END DO
END FUNCTION find_input_field
!
!
!
!
!
! SUBROUTINE init_input_field(module_name, field_name, tile_count)
!
!
!
!
!
!
SUBROUTINE init_input_field(module_name, field_name, tile_count)
CHARACTER(len=*), INTENT(in) :: module_name, field_name
INTEGER, INTENT(in) :: tile_count
! Get a number for this input_field if not already set up
IF ( find_input_field(module_name, field_name, tile_count) < 0 ) THEN
num_input_fields = num_input_fields + 1
IF ( num_input_fields > max_input_fields ) THEN
! max_input_fields exceeded, increase it via diag_manager_nml
CALL error_mesg('diag_util,init_input_field',&
& 'max_input_fields exceeded, increase it via diag_manager_nml', FATAL)
END IF
ELSE
! If this is already initialized do not need to do anything
RETURN
END IF
input_fields(num_input_fields)%module_name = TRIM(module_name)
input_fields(num_input_fields)%field_name = TRIM(field_name)
input_fields(num_input_fields)%num_output_fields = 0
! Set flag that this field has not been registered
input_fields(num_input_fields)%register = .FALSE.
input_fields(num_input_fields)%local = .FALSE.
input_fields(num_input_fields)%standard_name = 'none'
input_fields(num_input_fields)%tile_count = tile_count
END SUBROUTINE init_input_field
!
!
!
!
!
! SUBROUTINE init_output_field(module_name, field_name, output_name, output_file
! time_method, pack, tile_count, local_coord)
!
!
!
!
!
!
!
!
!
!
!
SUBROUTINE init_output_field(module_name, field_name, output_name, output_file,&
& time_method, pack, tile_count, local_coord)
CHARACTER(len=*), INTENT(in) :: module_name, field_name, output_name, output_file
CHARACTER(len=*), INTENT(in) :: time_method
INTEGER, INTENT(in) :: pack
INTEGER, INTENT(in) :: tile_count
TYPE(coord_type), INTENT(in), OPTIONAL :: local_coord
INTEGER :: out_num, in_num, file_num, file_num_tile1
INTEGER :: num_fields, i, method_selected, l1
INTEGER :: ioerror
CHARACTER(len=128) :: error_msg
CHARACTER(len=50) :: t_method
! Get a number for this output field
num_output_fields = num_output_fields + 1
IF ( num_output_fields > max_output_fields ) THEN
! max_output_fields exceeded, increase it via diag_manager_nml
CALL error_mesg('diag_util', 'max_output_fields exceeded, increase it via diag_manager_nml', FATAL)
END IF
out_num = num_output_fields
! First, find the index to the associated input field
in_num = find_input_field(module_name, field_name, tile_count)
IF ( in_num < 0 ) THEN
IF ( tile_count > 1 ) THEN
WRITE (error_msg,'(a,"/",a,"/",a)') TRIM(module_name),TRIM(field_name),&
& "tile_count="//TRIM(string(tile_count))
ELSE
WRITE (error_msg,'(a,"/",a)') TRIM(module_name),TRIM(field_name)
END IF
! module_name/field_name /[/tile_count=] NOT registered
CALL error_mesg('diag_util,init_output_field',&
& 'module_name/field_name '//TRIM(error_msg)//' NOT registered', FATAL)
END IF
! Add this output field into the list for this input field
input_fields(in_num)%num_output_fields =&
& input_fields(in_num)%num_output_fields + 1
IF ( input_fields(in_num)%num_output_fields > max_out_per_in_field ) THEN
! max_out_per_in_field exceeded, increase max_out_per_in_field
CALL error_mesg('diag_util,init_output_field',&
& 'max_out_per_in_field exceeded for '//TRIM(module_name)//"/"//TRIM(field_name)//', increase max_out_per_in_field', FATAL)
END IF
input_fields(in_num)%output_fields(input_fields(in_num)%num_output_fields) = out_num
! Also put pointer to input field in this output field
output_fields(out_num)%input_field = in_num
! Next, find the number for the corresponding file
IF ( TRIM(output_file).EQ.'null' ) THEN
file_num = max_files
ELSE
file_num = find_file(output_file, 1)
IF ( file_num < 0 ) THEN
CALL error_mesg('diag_util,init_output_field', 'file '&
& //TRIM(output_file)//' is NOT found in diag_table', FATAL)
END IF
IF ( tile_count > 1 ) THEN
file_num_tile1 = file_num
file_num = find_file(output_file, tile_count)
IF(file_num < 0) THEN
CALL init_file(files(file_num_tile1)%name, files(file_num_tile1)%output_freq,&
& files(file_num_tile1)%output_units, files(file_num_tile1)%format,&
& files(file_num_tile1)%time_units, files(file_num_tile1)%long_name,&
& tile_count, files(file_num_tile1)%new_file_freq,&
& files(file_num_tile1)%new_file_freq_units, files(file_num_tile1)%start_time,&
& files(file_num_tile1)%duration, files(file_num_tile1)%duration_units )
file_num = find_file(output_file, tile_count)
IF ( file_num < 0 ) THEN
CALL error_mesg('diag_util,init_output_field', 'file '//TRIM(output_file)//&
& ' is not initialized for tile_count = '//TRIM(string(tile_count)), FATAL)
END IF
END IF
END IF
END IF
! Insert this field into list for this file
files(file_num)%num_fields = files(file_num)%num_fields + 1
IF ( files(file_num)%num_fields > max_fields_per_file ) THEN
CALL error_mesg('diag_util,init_output_field',&
& 'max_fields_per_file exceeded, increase max_fields_per_file ', FATAL)
END IF
num_fields = files(file_num)%num_fields
files(file_num)%fields(num_fields) = out_num
! Set the file for this output field
output_fields(out_num)%output_file = file_num
! Enter the other data for this output field
output_fields(out_num)%output_name = TRIM(output_name)
output_fields(out_num)%pack = pack
!output_fields(out_num)%num_elements = 0
output_fields(out_num)%total_elements = 0
output_fields(out_num)%region_elements = 0
output_fields(out_num)%imax = 0
output_fields(out_num)%jmax = 0
output_fields(out_num)%kmax = 0
output_fields(out_num)%imin = VERY_LARGE_AXIS_LENGTH
output_fields(out_num)%jmin = VERY_LARGE_AXIS_LENGTH
output_fields(out_num)%kmin = VERY_LARGE_AXIS_LENGTH
! initialize the size of the diurnal axis to 1
output_fields(out_num)%n_diurnal_samples = 1
! Initialize all time method to false
method_selected = 0
output_fields(out_num)%time_average = .FALSE.
output_fields(out_num)%time_min = .FALSE.
output_fields(out_num)%time_max = .FALSE.
output_fields(out_num)%time_ops = .FALSE.
output_fields(out_num)%written_once = .FALSE.
t_method = lowercase(time_method)
! cannot time average fields output every time
IF ( files(file_num)%output_freq == EVERY_TIME ) THEN
output_fields(out_num)%time_average = .FALSE.
method_selected = method_selected+1
t_method = 'point'
ELSEIF ( INDEX(t_method,'diurnal') == 1 ) THEN
! get the integer number from the t_method
READ (UNIT=t_method(8:LEN_TRIM(t_method)), FMT='(I)', IOSTAT=ioerror) output_fields(out_num)%n_diurnal_samples
IF ( ioerror /= 0 ) THEN
! could not find integer number of diurnal samples in string
CALL error_mesg('diag_util_mod::init_output_field',&
& 'could not find integer number of diurnal samples in string "'&
& //TRIM(t_method)//'"', FATAL)
ELSE IF ( output_fields(out_num)%n_diurnal_samples <= 0 ) THEN
! The integer value of diurnal samples must be greater than zero.
CALL error_mesg('diag_util_mod::init_output_field',&
& 'The integer value of diurnal samples must be greater than zero.', FATAL)
END IF
output_fields(out_num)%time_average = .TRUE.
method_selected = method_selected+1
t_method='mean'
ELSE
SELECT CASE(TRIM(t_method))
CASE ( '.true.', 'mean', 'average', 'avg' )
output_fields(out_num)%time_average = .TRUE.
method_selected = method_selected+1
t_method = 'mean'
CASE ( '.false.', 'none', 'point' )
output_fields(out_num)%time_average = .FALSE.
method_selected = method_selected+1
t_method = 'point'
CASE ( 'maximum', 'max' )
output_fields(out_num)%time_max = .TRUE.
l1 = LEN_TRIM(output_fields(out_num)%output_name)
IF ( output_fields(out_num)%output_name(l1-2:l1) /= 'max' ) &
output_fields(out_num)%output_name = TRIM(output_name)//'_max'
method_selected = method_selected+1
t_method = 'max'
CASE ( 'minimum', 'min' )
output_fields(out_num)%time_min = .TRUE.
l1 = LEN_TRIM(output_fields(out_num)%output_name)
IF ( output_fields(out_num)%output_name(l1-2:l1) /= 'min' )&
& output_fields(out_num)%output_name = TRIM(output_name)//'_min'
method_selected = method_selected+1
t_method = 'min'
END SELECT
END IF
! reconcile logical flags
output_fields(out_num)%time_ops = output_fields(out_num)%time_min.OR.output_fields(out_num)%time_max&
& .OR.output_fields(out_num)%time_average
output_fields(out_num)%phys_window = .FALSE.
! need to initialize grid_type = -1(start, end, l_start_indx,l_end_indx etc...)
IF ( PRESENT(local_coord) ) THEN
input_fields(in_num)%local = .TRUE.
input_fields(in_num)%local_coord = local_coord
IF ( INT(local_coord%xbegin * local_coord%xbegin) == 1 .AND.&
& INT(local_coord%ybegin * local_coord%ybegin) ==1 ) THEN
output_fields(out_num)%local_output = .FALSE.
output_fields(out_num)%need_compute = .FALSE.
output_fields(out_num)%reduced_k_range = .TRUE.
ELSE
output_fields(out_num)%local_output = .TRUE.
output_fields(out_num)%need_compute = .FALSE.
output_fields(out_num)%reduced_k_range = .FALSE.
END IF
output_fields(out_num)%output_grid%start(1) = local_coord%xbegin
output_fields(out_num)%output_grid%start(2) = local_coord%ybegin
output_fields(out_num)%output_grid%start(3) = local_coord%zbegin
output_fields(out_num)%output_grid%end(1) = local_coord%xend
output_fields(out_num)%output_grid%end(2) = local_coord%yend
output_fields(out_num)%output_grid%end(3) = local_coord%zend
DO i = 1, 3
output_fields(out_num)%output_grid%l_start_indx(i) = -1
output_fields(out_num)%output_grid%l_end_indx(i) = -1
output_fields(out_num)%output_grid%subaxes(i) = -1
END DO
ELSE
output_fields(out_num)%local_output = .FALSE.
output_fields(out_num)%need_compute = .FALSE.
output_fields(out_num)%reduced_k_range = .FALSE.
END IF
! improper time method in diag_table for output field
IF ( method_selected /= 1 ) CALL error_mesg('init_output_field','improper &
&time method in diag_table for output field:'//TRIM(output_name),FATAL)
output_fields(out_num)%time_method = TRIM(t_method)
! allocate counters: NOTE that for simplicity we always allocate them, even
! if they are superceeded by 4D "counter" array. This isn't most memory
! efficient, approach, but probably tolerable since they are so small anyway
ALLOCATE(output_fields(out_num)%count_0d(output_fields(out_num)%n_diurnal_samples))
ALLOCATE(output_fields(out_num)%num_elements(output_fields(out_num)%n_diurnal_samples))
output_fields(out_num)%count_0d(:) = 0
output_fields(out_num)%num_elements(:) = 0
END SUBROUTINE init_output_field
!
!
!
!
!
!
! SUBROUTINE opening_file(file, time)
!
!
!
!
!
SUBROUTINE opening_file(file, time)
! WARNING: Assumes that all data structures are fully initialized
INTEGER, INTENT(in) :: file
TYPE(time_type), INTENT(in) :: time
REAL, DIMENSION(2) :: DATA
INTEGER :: j, field_num, input_field_num, num_axes, k
INTEGER :: field_num1
INTEGER :: position
INTEGER :: dir, edges
INTEGER :: ntileMe
INTEGER, ALLOCATABLE :: tile_id(:)
INTEGER, DIMENSION(1) :: time_axis_id, time_bounds_id
! size of this axes array must be at least max num. of
! axes per field + 2; the last two elements are for time
! and time bounds dimensions
INTEGER, DIMENSION(6) :: axes
LOGICAL :: time_ops, aux_present, match_aux_name
LOGICAL :: all_scalar_or_1d
CHARACTER(len=7) :: prefix
CHARACTER (len = 7) :: avg_name = 'average'
CHARACTER(len=128) :: time_units, timeb_units, avg, error_string, filename, aux_name,fieldname
CHARACTER(len=128) :: suffix, base_name
CHARACTER(len=32) :: time_name, timeb_name,time_longname, timeb_longname, cart_name
CHARACTER(len=256) :: fname
TYPE(domain1d) :: domain
TYPE(domain2d) :: domain2
aux_present = .FALSE.
match_aux_name = .FALSE.
! it's unlikely that a file starts with word "rregion", need to check anyway.
IF ( LEN(files(file)%name) >=7 .AND. .NOT.files(file)%local ) THEN
prefix = files(file)%name(1:7)
IF ( lowercase(prefix) == 'rregion' ) THEN
! file name should not start with word "rregion"
IF ( mpp_pe() == mpp_root_pe() )&
&CALL error_mesg ('diag_util opening_file', 'file name should not start with'&
& //' word "rregion"', WARNING)
END IF
END IF
! Here is where time_units string must be set up; time since base date
WRITE(time_units, 11) TRIM(time_unit_list(files(file)%time_units)), base_year,&
& base_month, base_day, base_hour, base_minute, base_second
11 FORMAT(a, ' since ', i4.4, '-', i2.2, '-', i2.2, ' ', i2.2, ':', i2.2, ':', i2.2)
base_name = files(file)%name
IF ( files(file)%new_file_freq < VERY_LARGE_FILE_FREQ ) THEN
position = INDEX(files(file)%name, '%')
IF ( position > 0 ) THEN
base_name = base_name(1:position-1)
ELSE
!
! filename does not contain % for time stamp string
!
CALL error_mesg ('diag_util opening_file', 'file name '//TRIM(files(file)%name)//&
& ' does not contain % for time stamp string', FATAL)
END IF
suffix = get_time_string(files(file)%name, time)
ELSE
suffix = ' '
END IF
! Add CVS tag as prefix of filename (currently not implemented)
! i1 = INDEX(tagname,':') + 2
! i2 = len_trim(tagname) - 2
! if(i2 <=i1) call error_mesg ('diag_util opening_file','error in CVS tagname index',FATAL)
! prefix2 = tagname(i1:i2)//'_'
IF ( files(file)%local ) THEN
! prepend "rregion" to all local files for post processing, the prefix will be removed in postprocessing
filename = 'rregion'//TRIM(base_name)//TRIM(suffix)
ELSE
! filename = trim(prefix2)//trim(base_name)//trim(suffix)
filename = TRIM(base_name)//TRIM(suffix)
END IF
! Loop through all fields with this file to output axes
! JWD: This is a klooge; need something more robust
domain2 = NULL_DOMAIN2D
all_scalar_or_1d = .TRUE.
DO j = 1, files(file)%num_fields
field_num = files(file)%fields(j)
num_axes = output_fields(field_num)%num_axes
IF ( num_axes > 1 ) THEN
all_scalar_or_1d = .FALSE.
domain2 = get_domain2d ( output_fields(field_num)%axes(1:num_axes) )
IF ( domain2 .NE. NULL_DOMAIN2D ) EXIT
END IF
END DO
IF( .NOT.all_scalar_or_1d ) THEN
IF ( domain2 .EQ. NULL_DOMAIN2D ) CALL return_domain(domain2)
IF ( domain2 .EQ. NULL_DOMAIN2D ) THEN
!
! Domain not defined through set_domain interface; cannot retrieve tile info
!
CALL error_mesg ('diag_util opening_file',&
& 'Domain not defined through set_domain interface; cannot retrieve tile info', FATAL)
END IF
IF ( mpp_get_ntile_count(domain2) > 1 ) THEN
ntileMe = mpp_get_current_ntile(domain2)
ALLOCATE(tile_id(ntileMe))
tile_id = mpp_get_tile_id(domain2)
fname = TRIM(filename)
CALL get_tile_string(filename, TRIM(fname)//'.tile' , tile_id(files(file)%tile_count))
DEALLOCATE(tile_id)
END IF
END IF
CALL diag_output_init(filename, files(file)%format, global_descriptor,&
& files(file)%file_unit, all_scalar_or_1d, domain2)
files(file)%bytes_written = 0
! Does this file contain time_average fields?
time_ops = .FALSE.
DO j = 1, files(file)%num_fields
field_num = files(file)%fields(j)
IF ( output_fields(field_num)%time_ops ) THEN
time_ops = .TRUE.
EXIT
END IF
END DO
! Loop through all fields with this file to output axes
DO j = 1, files(file)%num_fields
field_num = files(file)%fields(j)
input_field_num = output_fields(field_num)%input_field
IF (.NOT.input_fields(input_field_num)%register) THEN
WRITE (error_string,'(a,"/",a)') TRIM(input_fields(input_field_num)%module_name),&
& TRIM(input_fields(input_field_num)%field_name)
IF(mpp_pe() .EQ. mpp_root_pe()) THEN
!
! module/field_name
! /
! NOT registered
!
CALL error_mesg ('diag_util::opening_file',&
& 'module/field_name ('//TRIM(error_string)//') NOT registered', WARNING)
END IF
CYCLE
END IF
! Put the time axis in the axis field
num_axes = output_fields(field_num)%num_axes
axes(1:num_axes) = output_fields(field_num)%axes(1:num_axes)
! make sure that axis_id are not -1
DO k = 1, num_axes
IF ( axes(k) < 0 ) THEN
WRITE(error_string,'(a)') output_fields(field_num)%output_name
!
! ouptut_name has axis_id = -1
!
CALL error_mesg ('diag_util opening_file','output_name '//TRIM(error_string)//&
& ' has axis_id = -1', FATAL)
END IF
END DO
! check if aux is present in any axes
IF ( .NOT.aux_present ) THEN
DO k = 1, num_axes
aux_name = get_axis_aux(axes(k))
IF ( TRIM(aux_name) /= 'none' ) THEN
aux_present = .TRUE.
EXIT
END IF
END DO
END IF
axes(num_axes + 1) = files(file)%time_axis_id
CALL write_axis_meta_data(files(file)%file_unit, axes(1:num_axes + 1), time_ops)
IF ( time_ops ) THEN
axes(num_axes + 2) = files(file)%time_bounds_id
CALL write_axis_meta_data(files(file)%file_unit, axes(1:num_axes + 2))
END IF
END DO
! Looking for the first NON-static field in a file
field_num1 = files(file)%fields(1)
DO j = 1, files(file)%num_fields
field_num = files(file)%fields(j)
IF ( output_fields(field_num)%time_ops ) THEN
field_num1 = field_num
EXIT
END IF
END DO
DO j = 1, files(file)%num_fields
field_num = files(file)%fields(j)
input_field_num = output_fields(field_num)%input_field
IF (.NOT.input_fields(input_field_num)%register) CYCLE
! Make sure that 1 file contains either time_average or instantaneous fields
! cannot have both time_average and instantaneous in 1 file
IF ( .NOT.mix_snapshot_average_fields ) THEN
IF ( (output_fields(field_num)%time_ops.NEQV.output_fields(field_num1)%time_ops) .AND.&
& .NOT.output_fields(field_num1)%static .AND. .NOT.output_fields(field_num)%static) THEN
IF ( mpp_pe() == mpp_root_pe() ) THEN
!
! can NOT have BOTH time average AND instantaneous fields.
! Create a new file or set mix_snapshot_average_fields=.TRUE.
!
CALL error_mesg ('diag_util opening_file','file '//&
& TRIM(files(file)%name)//' can NOT have BOTH time average AND instantaneous fields.'//&
& ' Create a new file or set mix_snapshot_average_fields=.TRUE.' , FATAL)
END IF
END IF
END IF
! check if any field has the same name as aux_name
IF ( aux_present .AND. .NOT.match_aux_name ) THEN
fieldname = output_fields(field_num)%output_name
IF ( INDEX(aux_name, TRIM(fieldname)) > 0 ) match_aux_name = .TRUE.
END IF
! Put the time axis in the axis field
num_axes = output_fields(field_num)%num_axes
axes(1:num_axes) = output_fields(field_num)%axes(1:num_axes)
IF ( .NOT.output_fields(field_num)%static ) THEN
num_axes=num_axes+1
axes(num_axes) = files(file)%time_axis_id
END IF
IF(output_fields(field_num)%time_average) THEN
avg = avg_name
ELSE IF(output_fields(field_num)%time_max) THEN
avg = avg_name
ELSE IF(output_fields(field_num)%time_min) THEN
avg = avg_name
ELSE
avg = " "
END IF
IF ( input_fields(input_field_num)%missing_value_present ) THEN
IF ( LEN_TRIM(input_fields(input_field_num)%interp_method) > 0 ) THEN
output_fields(field_num)%f_type = write_field_meta_data(files(file)%file_unit,&
& output_fields(field_num)%output_name, axes(1:num_axes),&
& input_fields(input_field_num)%units,&
& input_fields(input_field_num)%long_name,&
& input_fields(input_field_num)%range, output_fields(field_num)%pack,&
& input_fields(input_field_num)%missing_value, avg_name = avg,&
& time_method=output_fields(field_num)%time_method,&
& standard_name = input_fields(input_field_num)%standard_name,&
& interp_method = input_fields(input_field_num)%interp_method)
ELSE
output_fields(field_num)%f_type = write_field_meta_data(files(file)%file_unit,&
& output_fields(field_num)%output_name, axes(1:num_axes),&
& input_fields(input_field_num)%units,&
& input_fields(input_field_num)%long_name,&
& input_fields(input_field_num)%range, output_fields(field_num)%pack,&
& input_fields(input_field_num)%missing_value, avg_name = avg,&
& time_method=output_fields(field_num)%time_method,&
& standard_name = input_fields(input_field_num)%standard_name)
END IF
! NEED TO TAKE CARE OF TIME AVERAGING INFO TOO BOTH CASES
ELSE
IF ( LEN_TRIM(input_fields(input_field_num)%interp_method) > 0 ) THEN
output_fields(field_num)%f_type = write_field_meta_data(files(file)%file_unit,&
& output_fields(field_num)%output_name, axes(1:num_axes),&
& input_fields(input_field_num)%units,&
& input_fields(input_field_num)%long_name,&
& input_fields(input_field_num)%range, output_fields(field_num)%pack,&
& avg_name = avg,&
& time_method=output_fields(field_num)%time_method,&
& standard_name = input_fields(input_field_num)%standard_name,&
& interp_method = input_fields(input_field_num)%interp_method)
ELSE
output_fields(field_num)%f_type = write_field_meta_data(files(file)%file_unit,&
& output_fields(field_num)%output_name, axes(1:num_axes),&
& input_fields(input_field_num)%units,&
& input_fields(input_field_num)%long_name,&
& input_fields(input_field_num)%range, output_fields(field_num)%pack,&
& avg_name = avg,&
& time_method=output_fields(field_num)%time_method,&
& standard_name = input_fields(input_field_num)%standard_name)
END IF
END IF
END DO
! If any of the fields in the file are time averaged, need to output the axes
! Use double precision since time axis is double precision
IF ( time_ops ) THEN
time_axis_id(1) = files(file)%time_axis_id
files(file)%f_avg_start = write_field_meta_data(files(file)%file_unit,&
& avg_name // '_T1', time_axis_id, time_units,&
& "Start time for average period", pack=1)
files(file)%f_avg_end = write_field_meta_data(files(file)%file_unit,&
& avg_name // '_T2', time_axis_id, time_units,&
& "End time for average period", pack=1)
files(file)%f_avg_nitems = write_field_meta_data(files(file)%file_unit,&
& avg_name // '_DT', time_axis_id,&
& TRIM(time_unit_list(files(file)%time_units)),&
& "Length of average period", pack=1)
END IF
IF ( time_ops ) THEN
time_axis_id(1) = files(file)%time_axis_id
time_bounds_id(1) = files(file)%time_bounds_id
CALL get_diag_axis( time_axis_id(1), time_name, time_units, time_longname,&
& cart_name, dir, edges, Domain, DATA)
CALL get_diag_axis( time_bounds_id(1), timeb_name, timeb_units, timeb_longname,&
& cart_name, dir, edges, Domain, DATA)
files(file)%f_bounds = write_field_meta_data(files(file)%file_unit,&
& TRIM(time_name)//'_bounds', (/time_bounds_id,time_axis_id/),&
& TRIM(time_unit_list(files(file)%time_units)),&
& TRIM(time_name)//' axis boundaries', pack=1)
END IF
! Let lower levels know that all meta data has been sent
CALL done_meta_data(files(file)%file_unit)
IF( aux_present .AND. .NOT.match_aux_name ) THEN
!
! one axis has auxiliary but the corresponding field is NOT
! found in file
!
IF ( mpp_pe() == mpp_root_pe() )&
& CALL error_mesg ('diag_util opening_file',&
&'one axis has auxiliary but the corresponding field'//&
&' is NOT found in file '//files(file)%name , WARNING)
END IF
END SUBROUTINE opening_file
!
!
!
!
!
! This function determines a string based on current time.
! This string is used as suffix in output file name
!
!
! CHARACTER(len=128) FUNCTION get_time_string(filename, current_time)
!
!
! This function determines a string based on current time.
! This string is used as suffix in output file name
!
!
!
CHARACTER(len=128) FUNCTION get_time_string(filename, current_time)
CHARACTER(len=128), INTENT(in) :: filename
TYPE(time_type), INTENT(in) :: current_time
INTEGER :: yr1, mo1, dy1, hr1, mi1, sc1 ! get from current time
INTEGER :: yr2, dy2, hr2, mi2 ! for computing next_level time unit
INTEGER :: yr1_s, mo1_s, dy1_s, hr1_s, mi1_s, sc1_s ! actual values to write string
INTEGER :: abs_sec, abs_day ! component of current_time
INTEGER :: days_per_month(12) = (/31,28,31,30,31,30,31,31,30,31,30,31/)
INTEGER :: julian_day, i, position, len, first_percent
CHARACTER(len=1) :: width ! width of the field in format write
CHARACTER(len=10) :: format
CHARACTER(len=20) :: yr, mo, dy, hr, mi, sc ! string of current time (output)
CHARACTER(len=128) :: filetail
format = '("_",i*.*)'
CALL get_date(current_time, yr1, mo1, dy1, hr1, mi1, sc1)
len = LEN_TRIM(filename)
first_percent = INDEX(filename, '%')
filetail = filename(first_percent:len)
! compute year string
position = INDEX(filetail, 'yr')
IF ( position > 0 ) THEN
width = filetail(position-1:position-1)
yr1_s = yr1
format(7:9) = width//'.'//width
WRITE(yr, format) yr1_s
yr2 = 0
ELSE
yr = ' '
yr2 = yr1 - 1
END IF
! compute month string
position = INDEX(filetail, 'mo')
IF ( position > 0 ) THEN
width = filetail(position-1:position-1)
mo1_s = yr2*12 + mo1
format(7:9) = width//'.'//width
WRITE(mo, format) mo1_s
ELSE
mo = ' '
END IF
! compute day string
IF ( LEN_TRIM(mo) > 0 ) THEN ! month present
dy1_s = dy1
dy2 = dy1_s - 1
ELSE IF ( LEN_TRIM(yr) >0 ) THEN ! no month, year present
! compute julian day
IF ( mo1 == 1 ) THEN
dy1_s = dy1
ELSE
julian_day = 0
DO i = 1, mo1-1
julian_day = julian_day + days_per_month(i)
END DO
IF ( leap_year(current_time) .AND. mo1 > 2 ) julian_day = julian_day + 1
julian_day = julian_day + dy1
dy1_s = julian_day
END IF
dy2 = dy1_s - 1
ELSE ! no month, no year
CALL get_time(current_time, abs_sec, abs_day)
dy1_s = abs_day
dy2 = dy1_s
END IF
position = INDEX(filetail, 'dy')
IF ( position > 0 ) THEN
width = filetail(position-1:position-1)
FORMAT(7:9) = width//'.'//width
WRITE(dy, FORMAT) dy1_s
ELSE
dy = ' '
END IF
! compute hour string
IF ( LEN_TRIM(dy) > 0 ) THEN
hr1_s = hr1
ELSE
hr1_s = dy2*24 + hr1
END IF
hr2 = hr1_s
position = INDEX(filetail, 'hr')
IF ( position > 0 ) THEN
width = filetail(position-1:position-1)
format(7:9) = width//'.'//width
WRITE(hr, format) hr1_s
ELSE
hr = ' '
END IF
! compute minute string
IF ( LEN_TRIM(hr) > 0 ) THEN
mi1_s = mi1
ELSE
mi1_s = hr2*60 + mi1
END IF
mi2 = mi1_s
position = INDEX(filetail, 'mi')
IF(position>0) THEN
width = filetail(position-1:position-1)
format(7:9) = width//'.'//width
WRITE(mi, format) mi1_s
ELSE
mi = ' '
END IF
! compute second string
IF ( LEN_TRIM(mi) > 0 ) THEN
sc1_s = sc1
ELSE
sc1_s = NINT(mi2*SECONDS_PER_MINUTE) + sc1
END IF
position = INDEX(filetail, 'sc')
IF ( position > 0 ) THEN
width = filetail(position-1:position-1)
format(7:9) = width//'.'//width
WRITE(sc, format) sc1_s
ELSE
sc = ' '
ENDIF
get_time_string = TRIM(yr)//TRIM(mo)//TRIM(dy)//TRIM(hr)//TRIM(mi)//TRIM(sc)
END FUNCTION get_time_string
!
!
!
!
!
!
! REAL FUNCTION get_date_dif(t2, t1, units)
!
!
!
!
!
!
REAL FUNCTION get_date_dif(t2, t1, units)
TYPE(time_type), INTENT(in) :: t2, t1
INTEGER, INTENT(in) :: units
INTEGER :: dif_seconds, dif_days
TYPE(time_type) :: dif_time
! Compute time axis label value
IF ( t2 < t1 ) CALL error_mesg('get_date_dif', &
& 't2 is less than t1', FATAL)
dif_time = t2 - t1
CALL get_time(dif_time, dif_seconds, dif_days)
IF ( units == DIAG_SECONDS ) THEN
get_date_dif = dif_seconds + SECONDS_PER_DAY * dif_days
ELSE IF ( units == DIAG_MINUTES ) THEN
get_date_dif = 1440 * dif_days + dif_seconds / SECONDS_PER_MINUTE
ELSE IF ( units == DIAG_HOURS ) THEN
get_date_dif = 24 * dif_days + dif_seconds / SECONDS_PER_HOUR
ELSE IF ( units == DIAG_DAYS ) THEN
get_date_dif = dif_days + dif_seconds / SECONDS_PER_DAY
ELSE IF ( units == DIAG_MONTHS ) THEN
! months not supported as output units
CALL error_mesg('diag_data_out', 'months not supported as output units', FATAL)
ELSE IF ( units == DIAG_YEARS ) THEN
! years not suppored as output units
CALL error_mesg('diag_data_out', 'years not supported as output units', FATAL)
ELSE
! illegal time units
CALL error_mesg('diag_data_out', 'illegal time units', FATAL)
END IF
END FUNCTION get_date_dif
!
!
!
!
!
! SUBROUTINE diag_data_out(file, field, dat, time, fianl_call_in, static_write_in)
!
!
!
!
!
!
!
!
!
SUBROUTINE diag_data_out(file, field, dat, time, final_call_in, static_write_in)
INTEGER, INTENT(in) :: file, field
REAL, DIMENSION(:,:,:,:), INTENT(inout) :: dat
TYPE(time_type), INTENT(in) :: time
LOGICAL, OPTIONAL, INTENT(in):: final_call_in, static_write_in
LOGICAL :: final_call, do_write, static_write
INTEGER :: i, num
REAL :: dif, time_data(2, 1, 1, 1), dt_time(1, 1, 1, 1), start_dif, end_dif
do_write = .TRUE.
final_call = .FALSE.
IF ( PRESENT(final_call_in) ) final_call = final_call_in
static_write = .FALSE.
IF ( PRESENT(static_write_in) ) static_write = static_write_in
dif = get_date_dif(time, base_time, files(file)%time_units)
! get file_unit, open new file and close curent file if necessary
IF ( .NOT.static_write .OR. files(file)%file_unit < 0 ) CALL check_and_open(file, time, do_write)
IF ( .NOT.do_write ) RETURN ! no need to write data
CALL diag_field_out(files(file)%file_unit,output_fields(field)%f_type, dat, dif)
! record number of bytes written to this file
files(file)%bytes_written = files(file)%bytes_written +&
& (SIZE(dat,1)*SIZE(dat,2)*SIZE(dat,3))*(8/output_fields(field)%pack)
IF ( .NOT.output_fields(field)%written_once ) output_fields(field)%written_once = .TRUE.
! *** inserted this line because start_dif < 0 for static fields ***
IF ( .NOT.output_fields(field)%static ) THEN
start_dif = get_date_dif(output_fields(field)%last_output, base_time,files(file)%time_units)
IF ( .NOT.mix_snapshot_average_fields ) THEN
end_dif = get_date_dif(output_fields(field)%next_output, base_time, files(file)%time_units)
ELSE
end_dif = dif
END IF
END IF
! Need to write average axes out;
DO i = 1, files(file)%num_fields
num = files(file)%fields(i)
IF ( output_fields(num)%time_ops .AND. &
input_fields(output_fields(num)%input_field)%register) THEN
IF ( num == field ) THEN
! Output the axes if this is first time-averaged field
time_data(1, 1, 1, 1) = start_dif
CALL diag_field_out(files(file)%file_unit, files(file)%f_avg_start, time_data(1:1,:,:,:), dif)
time_data(2, 1, 1, 1) = end_dif
CALL diag_field_out(files(file)%file_unit, files(file)%f_avg_end, time_data(2:2,:,:,:), dif)
! Compute the length of the average
dt_time(1, 1, 1, 1) = end_dif - start_dif
CALL diag_field_out(files(file)%file_unit, files(file)%f_avg_nitems, dt_time(1:1,:,:,:), dif)
! Include boundary variable for CF compliance
CALL diag_field_out(files(file)%file_unit, files(file)%f_bounds, time_data(1:2,:,:,:), dif)
EXIT
END IF
END IF
END DO
! If write time is greater (equal for the last call) than last_flush for this file, flush it
IF ( final_call ) THEN
IF ( time >= files(file)%last_flush ) THEN
CALL diag_flush(files(file)%file_unit)
files(file)%last_flush = time
END IF
ELSE
IF ( time > files(file)%last_flush ) THEN
CALL diag_flush(files(file)%file_unit)
files(file)%last_flush = time
END IF
END IF
END SUBROUTINE diag_data_out
!
!
!
!
! Checks if it is time to open a new file.
!
!
! SUBROUTINE check_and_open(file, time, do_write)
!
!
! Checks if it is time to open a new file. If yes, it first closes the
! current file, opens a new file and returns file_unit
! previous diag_manager_end is replaced by closing_file and output_setup by opening_file.
!
!
!
!
SUBROUTINE check_and_open(file, time, do_write)
INTEGER, INTENT(in) :: file
TYPE(time_type), INTENT(in) :: time
LOGICAL, INTENT(out) :: do_write
IF ( time >= files(file)%start_time ) THEN
IF ( files(file)%file_unit < 0 ) THEN ! need to open a new file
CALL opening_file(file, time)
do_write = .TRUE.
ELSE
do_write = .TRUE.
IF ( time > files(file)%close_time .AND. time < files(file)%next_open ) THEN
do_write = .FALSE. ! file still open but receives NO MORE data
ELSE IF ( time > files(file)%next_open ) THEN ! need to close current file and open a new one
CALL write_static(file) ! write all static fields and close this file
CALL opening_file(file, time)
files(file)%start_time = files(file)%next_open
files(file)%close_time =&
& diag_time_inc(files(file)%start_time,files(file)%duration, files(file)%duration_units)
files(file)%next_open =&
& diag_time_inc(files(file)%next_open, files(file)%new_file_freq,&
& files(file)%new_file_freq_units)
IF ( files(file)%close_time > files(file)%next_open ) THEN
!
! has close time GREATER than next_open time,
! check file duration and frequency
!
CALL error_mesg('check_and_open', files(file)%name//&
& ' has close time GREATER than next_open time, check file duration and frequency',FATAL)
END IF
END IF ! no need to open new file, simply return file_unit
END IF
ELSE
do_write = .FALSE.
END IF
END SUBROUTINE check_and_open
!
!
!
!
! Output all static fields in this file
!
!
! SUBROUTINE write_static(file)
!
!
!
!
SUBROUTINE write_static(file)
INTEGER, INTENT(in) :: file
INTEGER :: j, i, input_num
DO j = 1, files(file)%num_fields
i = files(file)%fields(j)
input_num = output_fields(i)%input_field
! skip fields that were not registered
IF ( .NOT.input_fields(input_num)%register ) CYCLE
! only output static fields here
IF ( .NOT.output_fields(i)%static ) CYCLE
CALL diag_data_out(file, i, output_fields(i)%buffer, files(file)%last_flush, .TRUE., .TRUE.)
END DO
! Close up this file
CALL mpp_close(files(file)%file_unit)
files(file)%file_unit = -1
END SUBROUTINE write_static
!
!
!
! Checks to see if output_name and output_file are unique in output_fields.
!
!
! SUBROUTINE check_duplicate_output_fields(err_msg)
!
!
!
!
SUBROUTINE check_duplicate_output_fields(err_msg)
CHARACTER(len=*), INTENT(out), OPTIONAL :: err_msg
INTEGER :: i, j, tmp_file
CHARACTER(len=128) :: tmp_name
CHARACTER(len=256) :: err_msg_local
IF ( PRESENT(err_msg) ) err_msg=''
! Do the checking when more than 1 output_fileds present
IF ( num_output_fields <= 1 ) RETURN
err_msg_local = ''
i_loop: DO i = 1, num_output_fields-1
tmp_name = TRIM(output_fields(i)%output_name)
tmp_file = output_fields(i)%output_file
DO j = i+1, num_output_fields
IF ( (tmp_name == TRIM(output_fields(j)%output_name)) .AND. &
&(tmp_file == output_fields(j)%output_file)) THEN
err_msg_local = ' output_field "'//TRIM(tmp_name)//&
&'" duplicated in file "'//TRIM(files(tmp_file)%name)//'"'
EXIT i_loop
END IF
END DO
END DO i_loop
IF ( err_msg_local /= '' ) THEN
IF ( fms_error_handler(' ERROR in diag_table',err_msg_local,err_msg) ) RETURN
END IF
END SUBROUTINE check_duplicate_output_fields
!
END MODULE diag_util_mod