27 use,
intrinsic :: iso_fortran_env, only: real128
28 use,
intrinsic :: iso_c_binding, only: c_double,c_float,c_int64_t, &
29 c_int32_t,c_int16_t,c_intptr_t
43 USE diag_data_mod,
ONLY: output_fields, input_fields, files, do_diag_field_log, diag_log_unit,&
44 & very_large_axis_length, time_zero, very_large_file_freq, end_of_run, every_time,&
45 & diag_seconds, diag_minutes, diag_hours, diag_days, diag_months, diag_years,
get_base_time,&
69 USE time_manager_mod,
ONLY:
time_type,
OPERATOR(==),
OPERATOR(>), no_calendar, increment_date,&
71 &
OPERATOR(<),
OPERATOR(>=),
OPERATOR(<=),
OPERATOR(==)
73 USE constants_mod,
ONLY: seconds_per_day, seconds_per_hour, seconds_per_minute
76 USE netcdf,
ONLY: nf90_char
113 #include <file_version.h>
115 LOGICAL :: module_initialized = .false.
125 IF (module_initialized)
THEN
135 INTEGER,
INTENT(in) :: axes(:)
136 INTEGER,
INTENT(in) :: outnum
138 REAL,
ALLOCATABLE :: global_lat(:), global_lon(:), global_depth(:)
139 INTEGER :: global_axis_size, global_axis_sizey
140 INTEGER :: i,xbegin,xend,ybegin,yend,xbegin_l,xend_l,ybegin_l,yend_l
141 CHARACTER(len=1) :: cart
142 TYPE(
domain2d) :: domain2, domain2_new
143 TYPE(
domain1d) :: domain1, domain1x, domain1y
146 INTEGER :: gstart_indx(3)
147 INTEGER :: gend_indx(3)
148 REAL,
ALLOCATABLE :: subaxis_x(:)
149 REAL,
ALLOCATABLE :: subaxis_y(:)
150 REAL,
ALLOCATABLE :: subaxis_z(:)
151 CHARACTER(len=128) :: msg
152 INTEGER :: ishift, jshift
155 CHARACTER(len=128),
DIMENSION(2) :: axis_domain_name
165 IF ( region_out_use_alt_value )
THEN
166 grv = glo_reg_val_alt
172 start = output_fields(outnum)%output_grid%start
173 end = output_fields(outnum)%output_grid%end
175 CALL get_diag_axis_domain_name(axes(1), axis_domain_name(1))
176 CALL get_diag_axis_domain_name(axes(2), axis_domain_name(2))
178 IF ( index(lowercase(axis_domain_name(1)),
'cubed') == 0 .AND. &
179 & index(lowercase(axis_domain_name(2)),
'cubed') == 0 )
THEN
180 DO i = 1,
SIZE(axes(:))
181 global_axis_size = get_axis_global_length(axes(i))
182 output_fields(outnum)%output_grid%subaxes(i) = -1
183 CALL get_diag_axis_cart(axes(i), cart)
187 IF( i.NE.1 )
CALL error_mesg(
'diag_util_mod::get_subfield_size',&
188 &
'wrong order of axes, X should come first',fatal)
189 ALLOCATE(global_lon(global_axis_size))
190 CALL get_diag_axis_data(axes(i),global_lon)
191 IF( int(start(i)) == grv .AND. int(end(i)) == grv )
THEN
193 gend_indx(i) = global_axis_size
194 output_fields(outnum)%output_grid%subaxes(i) = axes(i)
196 gstart_indx(i) =
get_index(start(i),global_lon)
197 gend_indx(i) =
get_index(end(i),global_lon)
199 ALLOCATE(subaxis_x(gstart_indx(i):gend_indx(i)))
200 subaxis_x=global_lon(gstart_indx(i):gend_indx(i))
203 IF( i.NE.2 )
CALL error_mesg(
'diag_util_mod::get_subfield_size',&
204 &
'wrong order of axes, Y should come second',fatal)
205 ALLOCATE(global_lat(global_axis_size))
206 CALL get_diag_axis_data(axes(i),global_lat)
207 IF( int(start(i)) == grv .AND. int(end(i)) == grv )
THEN
209 gend_indx(i) = global_axis_size
210 output_fields(outnum)%output_grid%subaxes(i) = axes(i)
212 gstart_indx(i) =
get_index(start(i),global_lat)
213 gend_indx(i) =
get_index(end(i),global_lat)
215 ALLOCATE(subaxis_y(gstart_indx(i):gend_indx(i)))
216 subaxis_y=global_lat(gstart_indx(i):gend_indx(i))
219 IF ( start(i)*end(i)<0. )
CALL error_mesg(
'diag_util_mod::get_subfield_size',&
220 &
'wrong values in vertical axis of region',fatal)
221 IF ( start(i)>=0. .AND. end(i)>0. )
THEN
222 ALLOCATE(global_depth(global_axis_size))
223 CALL get_diag_axis_data(axes(i),global_depth)
224 gstart_indx(i) =
get_index(start(i),global_depth)
225 gend_indx(i) =
get_index(end(i),global_depth)
226 ALLOCATE(subaxis_z(gstart_indx(i):gend_indx(i)))
227 subaxis_z=global_depth(gstart_indx(i):gend_indx(i))
228 output_fields(outnum)%output_grid%subaxes(i) =&
229 & diag_subaxes_init(axes(i),subaxis_z, gstart_indx(i),gend_indx(i))
230 DEALLOCATE(subaxis_z,global_depth)
233 gend_indx(i) = global_axis_size
234 output_fields(outnum)%output_grid%subaxes(i) = axes(i)
236 IF( i /= 3 )
CALL error_mesg(
'diag_util_mod::get_subfield_size',&
237 &
'i should equal 3 for z axis', fatal)
241 CALL error_mesg(
'diag_util_mod::get_subfield_size',
'Wrong axis_cart', fatal)
245 DO i = 1,
SIZE(axes(:))
246 IF ( gstart_indx(i) == -1 .OR. gend_indx(i) == -1 )
THEN
251 WRITE(msg,
'(A,I2)')
' check region bounds for axis ', i
252 CALL error_mesg(
'diag_util_mod::get_subfield_size',
'can not find gstart_indx/gend_indx for '&
253 & //trim(output_fields(outnum)%output_name)//
','//trim(msg), fatal)
258 CALL get_local_indexes(lonstart=start(1), lonend=end(1), &
259 & latstart=start(2), latend=end(2), &
260 & istart=gstart_indx(1), iend=gend_indx(1), &
261 & jstart=gstart_indx(2), jend=gend_indx(2))
262 global_axis_size = get_axis_global_length(axes(1))
263 ALLOCATE(global_lon(global_axis_size))
264 global_axis_sizey = get_axis_global_length(axes(2))
265 ALLOCATE(global_lat(global_axis_sizey))
266 CALL get_diag_axis_data(axes(1),global_lon)
267 CALL get_diag_axis_data(axes(2),global_lat)
270 IF ((gstart_indx(1) .GT. 0 .AND. gstart_indx(2) .GT. 0) .AND. &
271 (gstart_indx(1) .LE. global_axis_size .AND. gstart_indx(2) .LE. global_axis_sizey) .AND. &
272 (gend_indx(1) .GT. 0 .AND. gend_indx(2) .GT. 0) .AND. &
273 (gend_indx(1) .LE. global_axis_size .AND. gend_indx(2) .LE. global_axis_sizey))
THEN
274 ALLOCATE(subaxis_x(gstart_indx(1):gend_indx(1)))
275 ALLOCATE(subaxis_y(gstart_indx(2):gend_indx(2)))
276 subaxis_x=global_lon(gstart_indx(1):gend_indx(1))
277 subaxis_y=global_lat(gstart_indx(2):gend_indx(2))
281 IF (
SIZE(axes(:)) > 2 )
THEN
282 global_axis_size = get_axis_global_length(axes(3))
283 output_fields(outnum)%output_grid%subaxes(3) = -1
284 CALL get_diag_axis_cart(axes(3), cart)
288 IF ( lowercase(cart) /=
'z' )
CALL error_mesg(
'diag_util_mod::get_subfield_size', &
289 &
'axis(3) should be Z-axis', fatal)
293 IF ( start(3)*end(3)<0. )
CALL error_mesg(
'diag_util_mod::get_subfield_size',&
294 &
'wrong values in vertical axis of region',fatal)
295 IF ( start(3)>=0. .AND. end(3)>0. )
THEN
296 ALLOCATE(global_depth(global_axis_size))
297 CALL get_diag_axis_data(axes(3),global_depth)
298 gstart_indx(3) =
get_index(start(3),global_depth)
299 IF( start(3) == 0.0 ) gstart_indx(3) = 1
300 gend_indx(3) =
get_index(end(3),global_depth)
301 IF( start(3) >= maxval(global_depth) ) gstart_indx(3)= global_axis_size
302 IF( end(3) >= maxval(global_depth) ) gend_indx(3) = global_axis_size
304 ALLOCATE(subaxis_z(gstart_indx(3):gend_indx(3)))
305 subaxis_z=global_depth(gstart_indx(3):gend_indx(3))
306 output_fields(outnum)%output_grid%subaxes(3) =&
307 & diag_subaxes_init(axes(3),subaxis_z, gstart_indx(3),gend_indx(3))
308 DEALLOCATE(subaxis_z,global_depth)
311 gend_indx(3) = global_axis_size
312 output_fields(outnum)%output_grid%subaxes(3) = axes(3)
323 domain2 = get_domain2d(axes)
324 IF ( domain2 .NE. null_domain2d )
THEN
325 CALL mpp_get_compute_domain(domain2, xbegin, xend, ybegin, yend)
328 DO i = 1, min(
SIZE(axes(:)),2)
329 domain1 = get_domain1d(axes(i))
330 IF ( domain1 .NE. null_domain1d )
THEN
331 CALL get_diag_axis_cart(axes(i),cart)
334 domain1x = get_domain1d(axes(i))
335 CALL mpp_get_compute_domain(domain1x, xbegin, xend)
337 domain1y = get_domain1d(axes(i))
338 CALL mpp_get_compute_domain(domain1y, ybegin, yend)
343 CALL error_mesg(
'diag_util_mod::get_subfield_size',
'NO domain available', fatal)
348 CALL get_axes_shift(axes, ishift, jshift)
352 IF ( xbegin == -1 .OR. xend == -1 .OR. ybegin == -1 .OR. yend == -1 )
THEN
354 CALL error_mesg(
'diag_util_mod::get_subfield_size',
'wrong compute domain indices',fatal)
358 IF( gstart_indx(1) > xend .OR. xbegin > gend_indx(1) )
THEN
359 output_fields(outnum)%output_grid%l_start_indx(1) = -1
360 output_fields(outnum)%output_grid%l_end_indx(1) = -1
361 output_fields(outnum)%need_compute = .false.
362 ELSEIF ( gstart_indx(2) > yend .OR. ybegin > gend_indx(2) )
THEN
363 output_fields(outnum)%output_grid%l_start_indx(2) = -1
364 output_fields(outnum)%output_grid%l_end_indx(2) = -1
365 output_fields(outnum)%need_compute = .false.
367 output_fields(outnum)%output_grid%l_start_indx(1) = max(xbegin, gstart_indx(1))
368 output_fields(outnum)%output_grid%l_start_indx(2) = max(ybegin, gstart_indx(2))
369 output_fields(outnum)%output_grid%l_end_indx(1) = min(xend, gend_indx(1))
370 output_fields(outnum)%output_grid%l_end_indx(2) = min(yend, gend_indx(2))
371 output_fields(outnum)%need_compute = .true.
374 IF ( output_fields(outnum)%need_compute )
THEN
376 xbegin_l = output_fields(outnum)%output_grid%l_start_indx(1)
377 xend_l = output_fields(outnum)%output_grid%l_end_indx(1)
378 ybegin_l = output_fields(outnum)%output_grid%l_start_indx(2)
379 yend_l = output_fields(outnum)%output_grid%l_end_indx(2)
380 CALL mpp_modify_domain(domain2, domain2_new, xbegin_l,xend_l, ybegin_l,yend_l,&
381 & gstart_indx(1),gend_indx(1), gstart_indx(2),gend_indx(2))
383 output_fields(outnum)%output_grid%subaxes(1) =&
384 & diag_subaxes_init(axes(1),subaxis_x, gstart_indx(1),gend_indx(1),domain2_new)
385 output_fields(outnum)%output_grid%subaxes(2) =&
386 & diag_subaxes_init(axes(2),subaxis_y, gstart_indx(2),gend_indx(2),domain2_new)
387 DO i = 1,
SIZE(axes(:))
388 IF ( output_fields(outnum)%output_grid%subaxes(i) == -1 )
THEN
392 WRITE(msg,
'(a,"/",I4)')
'at i = ',i
393 CALL error_mesg(
'diag_util_mod::get_subfield_size '//trim(output_fields(outnum)%output_name),&
394 'error '//trim(msg), fatal)
399 output_fields(outnum)%output_grid%l_start_indx(1) = max(xbegin, gstart_indx(1)) - xbegin + 1
400 output_fields(outnum)%output_grid%l_start_indx(2) = max(ybegin, gstart_indx(2)) - ybegin + 1
401 output_fields(outnum)%output_grid%l_end_indx(1) = min(xend, gend_indx(1)) - xbegin + 1
402 output_fields(outnum)%output_grid%l_end_indx(2) = min(yend, gend_indx(2)) - ybegin + 1
403 IF (
SIZE(axes(:))>2 )
THEN
404 output_fields(outnum)%output_grid%l_start_indx(3) = gstart_indx(3)
405 output_fields(outnum)%output_grid%l_end_indx(3) = gend_indx(3)
407 output_fields(outnum)%output_grid%l_start_indx(3) = 1
408 output_fields(outnum)%output_grid%l_end_indx(3) = 1
411 IF (
ALLOCATED(subaxis_x) )
DEALLOCATE(subaxis_x, global_lon)
412 IF (
ALLOCATED(subaxis_y) )
DEALLOCATE(subaxis_y, global_lat)
417 INTEGER,
DIMENSION(:),
INTENT(in) :: axes
418 INTEGER,
INTENT(in) :: outnum
420 REAL,
DIMENSION(3) :: start
421 REAL,
DIMENSION(3) :: end
422 REAL,
ALLOCATABLE,
DIMENSION(:) :: global_depth
423 REAL,
ALLOCATABLE,
DIMENSION(:) :: subaxis_z
424 INTEGER :: i, global_axis_size
425 INTEGER,
DIMENSION(3) :: gstart_indx
426 INTEGER,
DIMENSION(3) :: gend_indx
427 CHARACTER(len=1) :: cart
428 CHARACTER(len=128) :: msg
431 integer :: vert_dim_num
441 start= output_fields(outnum)%output_grid%start
442 end = output_fields(outnum)%output_grid%end
448 DO i = 1,
SIZE(axes(:))
449 global_axis_size = get_axis_global_length(axes(i))
450 output_fields(outnum)%output_grid%subaxes(i) = -1
451 CALL get_diag_axis_cart(axes(i), cart)
455 IF ( i.NE.1 )
CALL error_mesg(
'diag_util_mod::get_subfield_vert_size',&
456 &
'wrong order of axes, X should come first',fatal)
458 gend_indx(i) = global_axis_size
459 output_fields(outnum)%output_grid%subaxes(i) = axes(i)
462 IF( i.NE.2 )
CALL error_mesg(
'diag_util_mod::get_subfield_vert_size',&
463 &
'wrong order of axes, Y should come second',fatal)
465 gend_indx(i) = global_axis_size
466 output_fields(outnum)%output_grid%subaxes(i) = axes(i)
471 call error_mesg(
"diag_util_mod::get_subfield_vert_size", &
472 "the unstructured axis must be the first dimension.", &
476 gend_indx(i) = global_axis_size
477 output_fields(outnum)%output_grid%subaxes(i) = axes(i)
479 start(vert_dim_num) = start(3)
480 end(vert_dim_num) = end(3)
485 if (i .ne. vert_dim_num)
then
486 call error_mesg(
"diag_util_mod::get_subfield_vert_size",&
487 "i should equal vert_dim_num for z axis", &
492 IF( start(i)*end(i) < 0. )
CALL error_mesg(
'diag_util_mod::get_subfield_vert_size',&
493 &
'wrong values in vertical axis of region',fatal)
494 IF( start(i) >= 0. .AND. end(i) > 0. )
THEN
495 ALLOCATE(global_depth(global_axis_size))
496 CALL get_diag_axis_data(axes(i),global_depth)
497 gstart_indx(i) =
get_index(start(i),global_depth)
498 IF( start(i) == 0.0 ) gstart_indx(i) = 1
500 gend_indx(i) =
get_index(end(i),global_depth)
501 IF( start(i) >= maxval(global_depth) ) gstart_indx(i)= global_axis_size
502 IF( end(i) >= maxval(global_depth) ) gend_indx(i) = global_axis_size
504 ALLOCATE(subaxis_z(gstart_indx(i):gend_indx(i)))
505 subaxis_z=global_depth(gstart_indx(i):gend_indx(i))
506 output_fields(outnum)%output_grid%subaxes(i) = &
507 diag_subaxes_init(axes(i),subaxis_z, gstart_indx(i),gend_indx(i))
508 DEALLOCATE(subaxis_z,global_depth)
511 gend_indx(i) = global_axis_size
512 output_fields(outnum)%output_grid%subaxes(i) = axes(i)
516 CALL error_mesg(
'diag_util_mod::get_subfield_vert_size',
'Wrong axis_cart', fatal)
520 DO i = 1,
SIZE(axes(:))
521 IF ( gstart_indx(i) == -1 .OR. gend_indx(i) == -1 )
THEN
526 WRITE(msg,
'(A,I2)')
' check region bounds for axis ', i
527 CALL error_mesg(
'diag_util_mod::get_subfield_vert_size',
'can not find gstart_indx/gend_indx for '&
528 & //trim(output_fields(outnum)%output_name)//
','//trim(msg), fatal)
533 output_fields(outnum)%output_grid%l_start_indx(i) = gstart_indx(i)
534 output_fields(outnum)%output_grid%l_end_indx(i) = gend_indx(i)
537 IF(
SIZE(axes(:)) > 2 )
THEN
538 output_fields(outnum)%output_grid%l_start_indx(3) = gstart_indx(3)
539 output_fields(outnum)%output_grid%l_end_indx(3) = gend_indx(3)
541 output_fields(outnum)%output_grid%l_start_indx(3) = 1
542 output_fields(outnum)%output_grid%l_end_indx(3) = 1
548 REAL,
INTENT(in) :: number
549 REAL,
INTENT(in),
DIMENSION(:) :: array
557 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
559 CALL error_mesg(
'diag_util_mod::get_index',
'array NOT monotonously ordered',fatal)
566 IF ( (array(i)<=number).AND.(array(i+1)>= number) )
THEN
567 IF( number - array(i) <= array(i+1) - number )
THEN
578 IF( .NOT.found )
THEN
580 IF ( (array(i)>=number).AND.(array(i+1)<= number) )
THEN
581 IF ( array(i)-number <= number-array(i+1) )
THEN
596 IF ( .NOT. found )
THEN
597 IF ( 2*array(1)-array(3).LT.number .AND. number.LT.array(1) )
THEN
600 ELSE IF ( array(n).LT.number .AND. number.LT.2*array(n)-array(n-2) )
THEN
611 IF ( .NOT. found )
THEN
612 IF ( 2*array(1)-array(3).GT.number .AND. number.GT.array(1) )
THEN
615 ELSE IF ( array(n).GT.number .AND. number.GT.2*array(n)-array(n-2) )
THEN
636 & missing_value, range, dynamic )
637 CHARACTER(len=*),
INTENT(in) :: module_name
638 CHARACTER(len=*),
INTENT(in) :: field_name
639 INTEGER,
DIMENSION(:),
INTENT(in) :: axes
640 CHARACTER(len=*),
OPTIONAL,
INTENT(in) :: long_name
641 CHARACTER(len=*),
OPTIONAL,
INTENT(in) :: units
642 CLASS(*),
OPTIONAL,
INTENT(in) :: missing_value
643 CLASS(*),
DIMENSION(:),
OPTIONAL,
INTENT(IN) :: range
644 LOGICAL,
OPTIONAL,
INTENT(in) :: dynamic
647 CHARACTER(len=256) :: lmodule, lfield, lname, lunits
648 CHARACTER(len=64) :: lmissval, lmin, lmax
649 CHARACTER(len=8) :: numaxis, timeaxis
650 CHARACTER(len=1) :: sep =
'|'
651 CHARACTER(len=256) :: axis_name, axes_list
653 REAL :: missing_value_use
654 REAL,
DIMENSION(2) :: range_use
656 IF ( .NOT.do_diag_field_log )
RETURN
657 IF (
mpp_pe().NE.mpp_root_pe() )
RETURN
660 IF (
PRESENT(range) )
THEN
661 IF (
SIZE(range) .NE. 2 )
THEN
662 CALL error_mesg(
'diag_util_mod::fms_log_field_info',
'extent of range should be 2', fatal)
666 lmodule = trim(module_name)
667 lfield = trim(field_name)
669 IF (
PRESENT(long_name) )
THEN
670 lname = trim(long_name)
675 IF (
PRESENT(units) )
THEN
681 WRITE (numaxis,
'(i1)')
SIZE(axes)
683 IF (
PRESENT(missing_value))
THEN
685 WRITE (lmissval,*) cmor_missing_value
687 SELECT TYPE (missing_value)
688 TYPE IS (real(kind=r4_kind))
689 missing_value_use = missing_value
690 TYPE IS (real(kind=r8_kind))
691 missing_value_use = real(missing_value)
693 CALL error_mesg (
'diag_util_mod::log_diag_field_info',&
694 &
'The missing_value is not one of the supported types of real(kind=4) or real(kind=8)', fatal)
696 WRITE (lmissval,*) missing_value_use
702 IF (
PRESENT(range) )
THEN
704 TYPE IS (real(kind=r4_kind))
706 TYPE IS (real(kind=r8_kind))
707 range_use = real(range)
709 CALL error_mesg (
'diag_util_mod::log_diag_field_info',&
710 &
'The range is not one of the supported types of real(kind=4) or real(kind=8)', fatal)
712 WRITE (lmin,*) range_use(1)
713 WRITE (lmax,*) range_use(2)
719 IF (
PRESENT(dynamic) )
THEN
731 CALL get_diag_axis_name(axes(i),axis_name)
732 IF ( trim(axes_list) /=
'' ) axes_list = trim(axes_list)//
','
733 axes_list = trim(axes_list)//trim(axis_name)
736 WRITE (diag_log_unit,
'(777a)') &
747 SUBROUTINE update_bounds(out_num, lower_i, upper_i, lower_j, upper_j, lower_k, upper_k)
748 INTEGER,
INTENT(in) :: out_num
749 INTEGER,
INTENT(in) :: lower_i
750 INTEGER,
INTENT(in) :: upper_i
751 INTEGER,
INTENT(in) :: lower_j
752 INTEGER,
INTENT(in) :: upper_j
753 INTEGER,
INTENT(in) :: lower_k
754 INTEGER,
INTENT(in) :: upper_k
755 CALL output_fields(out_num)%buff_bounds%update_bounds &
756 & ( lower_i, upper_i, lower_j, upper_j, lower_k, upper_k )
768 TYPE (fmsdiagibounds_type),
INTENT(in) :: current_bounds
770 TYPE (fmsdiagibounds_type),
INTENT(in):: bounds
771 CHARACTER(*),
INTENT(out) :: error_str
776 LOGICAL FUNCTION lowerb_comp(a , b)
777 INTEGER,
INTENT(IN) :: a
778 INTEGER,
INTENT(IN) :: b
779 END FUNCTION lowerb_comp
784 LOGICAL FUNCTION upperb_comp(a, b)
785 INTEGER,
INTENT(IN) :: a
786 INTEGER,
INTENT(IN) :: b
787 END FUNCTION upperb_comp
792 IF (lowerb_comp( bounds%get_imin() , current_bounds%get_imin()) .OR. &
793 upperb_comp( bounds%get_imax() , current_bounds%get_imax()).OR.&
794 lowerb_comp( bounds%get_jmin() , current_bounds%get_jmin()) .OR.&
795 upperb_comp( bounds%get_jmax() , current_bounds%get_jmax()) .OR.&
796 lowerb_comp( bounds%get_kmin() , current_bounds%get_kmin()) .OR.&
797 upperb_comp( bounds%get_kmax() , current_bounds%get_kmax()))
THEN
799 error_str =
'Buffer bounds= : , : , : Actual bounds= : , : , : '
800 WRITE(error_str(15:17),
'(i3)') current_bounds%get_imin()
801 WRITE(error_str(19:21),
'(i3)') current_bounds%get_imax()
802 WRITE(error_str(23:25),
'(i3)') current_bounds%get_jmin()
803 WRITE(error_str(27:29),
'(i3)') current_bounds%get_jmax()
804 WRITE(error_str(31:33),
'(i3)') current_bounds%get_kmin()
805 WRITE(error_str(35:37),
'(i3)') current_bounds%get_kmax()
806 WRITE(error_str(54:56),
'(i3)') bounds%get_imin()
807 WRITE(error_str(58:60),
'(i3)') bounds%get_imax()
808 WRITE(error_str(62:64),
'(i3)') bounds%get_jmin()
809 WRITE(error_str(66:68),
'(i3)') bounds%get_jmax()
810 WRITE(error_str(70:72),
'(i3)') bounds%get_kmin()
811 WRITE(error_str(74:76),
'(i3)') bounds%get_kmax()
820 INTEGER,
INTENT(IN) :: a
821 INTEGER,
INTENT(IN) :: b
827 INTEGER,
INTENT(IN) :: a
828 INTEGER,
INTENT(IN) :: b
834 INTEGER,
INTENT(IN) :: a
835 INTEGER,
INTENT(IN) :: b
843 INTEGER,
INTENT(in) :: out_num
844 INTEGER,
INTENT(in) :: diag_field_id
845 CHARACTER(len=*),
INTENT(out) :: err_msg
848 CHARACTER(len=128) :: error_string1, error_string2
849 LOGICAL :: out_of_bounds = .true.
850 TYPE (fmsdiagibounds_type) :: array_bounds
851 associate(buff_bounds => output_fields(out_num)%buff_bounds)
853 CALL array_bounds%reset_bounds_from_array_4D(output_fields(out_num)%buffer)
858 IF (out_of_bounds .EQV. .true.)
THEN
859 WRITE(error_string1,
'(a,"/",a)') trim(input_fields(diag_field_id)%module_name), &
860 & trim(output_fields(out_num)%output_name)
861 err_msg =
'module/output_field='//trim(error_string1)//&
862 &
' Bounds of buffer exceeded. '//trim(error_string2)
864 call buff_bounds%reset(very_large_axis_length, 0)
875 REAL(kind=r4_kind),
INTENT (in),
DIMENSION(:,:,:,:,:) :: ofb
876 TYPE (fmsDiagIbounds_type),
INTENT(inout) :: bounds
877 CHARACTER(:),
ALLOCATABLE,
INTENT(in) :: output_name
878 CHARACTER(:),
ALLOCATABLE,
INTENT(in) :: module_name
879 CHARACTER(len=*),
INTENT(inout) :: err_msg
882 CHARACTER(len=128) :: error_string1, error_string2
883 LOGICAL :: out_of_bounds = .true.
884 TYPE (fmsDiagIbounds_type) :: array_bounds
886 CALL array_bounds%reset_bounds_from_array_5D(ofb)
891 IF (out_of_bounds .EQV. .true.)
THEN
892 WRITE(error_string1,
'(a,"/",a)') trim(module_name), trim(output_name)
893 err_msg =
'module/output_field='//trim(error_string1)//&
894 &
' Bounds of buffer exceeded. '//trim(error_string2)
896 call bounds%reset(very_large_axis_length,0)
907 REAL(kind=r8_kind),
INTENT (in),
DIMENSION(:,:,:,:,:) :: ofb
908 TYPE (fmsDiagIbounds_type),
INTENT(inout) :: bounds
909 CHARACTER(:),
ALLOCATABLE,
INTENT(in) :: output_name
910 CHARACTER(:),
ALLOCATABLE,
INTENT(in) :: module_name
911 CHARACTER(len=*),
INTENT(out) :: err_msg
914 CHARACTER(len=128) :: error_string1, error_string2
915 LOGICAL :: out_of_bounds = .true.
916 TYPE (fmsDiagIbounds_type) :: array_bounds
918 CALL array_bounds%reset_bounds_from_array_5D(ofb)
923 IF (out_of_bounds .EQV. .true.)
THEN
924 WRITE(error_string1,
'(a,"/",a)') trim(module_name), trim(output_name)
925 err_msg =
'module/output_field='//trim(error_string1)//&
926 &
' Bounds of buffer exceeded. '//trim(error_string2)
928 call bounds%reset(very_large_axis_length,0)
940 & Time, field_prev_Time, err_msg)
941 TYPE (fmsdiagibounds_type),
INTENT(in) :: current_bounds
943 TYPE (fmsdiagibounds_type),
INTENT(inout) :: bounds
944 CHARACTER(:),
ALLOCATABLE,
INTENT(in) :: output_name
945 CHARACTER(:),
ALLOCATABLE,
INTENT(in) :: module_name
946 TYPE(time_type),
INTENT(in) :: time
949 TYPE(time_type),
INTENT(inout) :: field_prev_time
950 CHARACTER(len=*),
INTENT(out) :: err_msg
954 CHARACTER(len=128) :: error_string1, error_string2
956 LOGICAL :: lims_not_exact
963 IF ( time == field_prev_time )
THEN
966 IF ( field_prev_time == time_zero )
THEN
973 field_prev_time = time
979 IF( lims_not_exact .eqv. .true.)
THEN
980 WRITE(error_string1,
'(a,"/",a)') trim(module_name), trim(output_name)
981 err_msg = trim(error_string1)//
' Bounds of data do not match those of buffer. '//trim(error_string2)
983 call bounds%reset(very_large_axis_length, 0)
991 INTEGER,
INTENT(in) :: out_num
992 INTEGER,
INTENT(in) :: diag_field_id
993 TYPE(time_type),
INTENT(in) :: time
996 CHARACTER(len=*),
INTENT(out) :: err_msg
999 CHARACTER(:),
ALLOCATABLE :: output_name
1000 CHARACTER(:),
ALLOCATABLE :: module_name
1001 TYPE (fmsdiagibounds_type) :: current_bounds
1003 output_name = output_fields(out_num)%output_name
1004 module_name = input_fields(diag_field_id)%module_name
1006 CALL current_bounds%reset_bounds_from_array_4D(output_fields(out_num)%buffer)
1009 & output_name, module_name, &
1010 & time, output_fields(out_num)%Time_of_prev_field_data, err_msg)
1018 INTEGER,
INTENT(in) :: out_num
1019 INTEGER,
INTENT(in) :: diag_field_id
1020 CHARACTER(len=*),
INTENT(out) :: err_msg
1022 CHARACTER(:),
ALLOCATABLE :: output_name
1023 CHARACTER(:),
ALLOCATABLE :: module_name
1024 TYPE (fmsdiagibounds_type) :: current_bounds
1026 output_name = output_fields(out_num)%output_name
1027 module_name = input_fields(diag_field_id)%module_name
1029 CALL current_bounds%reset_bounds_from_array_4D(output_fields(out_num)%buffer)
1032 & output_name, module_name, err_msg)
1040 TYPE (fmsdiagibounds_type),
INTENT(in) :: current_bounds
1042 TYPE (fmsdiagibounds_type),
INTENT(inout) :: bounds
1043 CHARACTER(:),
ALLOCATABLE,
INTENT(in) :: output_name
1044 CHARACTER(:),
ALLOCATABLE,
INTENT(in) :: module_name
1045 CHARACTER(len=*),
INTENT(out) :: err_msg
1048 CHARACTER(len=128) :: error_string1, error_string2
1049 LOGICAL :: lims_not_exact
1054 IF( lims_not_exact .eqv. .true.)
THEN
1055 WRITE(error_string1,
'(a,"/",a)') trim(module_name), trim(output_name)
1056 err_msg = trim(error_string1)//
' Bounds of data do not match those of buffer. '//trim(error_string2)
1058 call bounds%reset(very_large_axis_length, 0)
1063 SUBROUTINE init_file(name, output_freq, output_units, format, time_units, long_name, tile_count,&
1064 & new_file_freq, new_file_freq_units, start_time, file_duration, file_duration_units, filename_time_bounds)
1065 CHARACTER(len=*),
INTENT(in) :: name
1066 CHARACTER(len=*),
INTENT(in) :: long_name
1067 INTEGER,
INTENT(in) :: output_freq
1068 INTEGER,
INTENT(in) :: output_units
1069 INTEGER,
INTENT(in) :: format
1070 INTEGER,
INTENT(in) :: time_units
1071 INTEGER,
INTENT(in) :: tile_count
1072 INTEGER,
INTENT(in),
OPTIONAL :: new_file_freq
1073 INTEGER,
INTENT(in),
OPTIONAL :: new_file_freq_units
1074 INTEGER,
INTENT(in),
OPTIONAL :: file_duration
1075 INTEGER,
INTENT(in),
OPTIONAL :: file_duration_units
1076 TYPE(time_type),
INTENT(in),
OPTIONAL :: start_time
1077 CHARACTER(len=*),
INTENT(in),
OPTIONAL :: filename_time_bounds
1079 INTEGER :: new_file_freq1, new_file_freq_units1
1080 INTEGER :: file_duration1, file_duration_units1
1082 LOGICAL :: same_file_err
1085 REAL,
DIMENSION(1) :: tdata
1086 CHARACTER(len=128) :: time_units_str
1089 same_file_err=.false.
1092 IF ( trim(files(n)%name) == trim(name) )
THEN
1095 IF ( files(n)%output_freq.NE.output_freq .OR.&
1096 & files(n)%output_units.NE.output_units .OR.&
1097 & files(n)%format.NE.
format .OR.&
1098 & files(n)%time_units.NE.time_units .OR.&
1099 & trim(files(n)%long_name).NE.trim(long_name) .OR.&
1100 & files(n)%tile_count.NE.tile_count )
THEN
1101 same_file_err=.true.
1105 IF (
PRESENT(new_file_freq) )
THEN
1106 IF ( files(n)%new_file_freq.NE.new_file_freq )
THEN
1107 same_file_err=.true.
1111 IF (
PRESENT(new_file_freq_units) )
THEN
1112 IF ( files(n)%new_file_freq_units.NE.new_file_freq_units )
THEN
1113 same_file_err=.true.
1117 IF (
PRESENT(start_time) )
THEN
1118 IF ( files(n)%start_time==start_time )
THEN
1119 same_file_err=.true.
1123 IF (
PRESENT(file_duration) )
THEN
1124 IF ( files(n)%duration.NE.file_duration)
THEN
1125 same_file_err=.true.
1129 IF (
PRESENT(file_duration_units) )
THEN
1130 IF ( files(n)%duration_units.NE.file_duration_units )
THEN
1131 same_file_err=.true.
1136 IF ( same_file_err )
THEN
1139 CALL error_mesg(
'diag_util_mod::init_file',&
1140 &
'The file "'//trim(name)//
'" is defined multiple times in&
1141 & the diag_table.', fatal)
1144 CALL error_mesg(
'diag_util_mod::init_file',&
1145 &
'The file "'//trim(name)//
'" is defined multiple times in&
1146 & the diag_table.', note)
1154 num_files = num_files + 1
1155 IF ( num_files >= max_files )
THEN
1160 CALL error_mesg(
'diag_util_mod::init_file',&
1161 &
' max_files exceeded, increase max_files via the max_files variable&
1162 & in the namelist diag_manager_nml.', fatal)
1165 IF (
PRESENT(new_file_freq) )
THEN
1166 new_file_freq1 = new_file_freq
1168 new_file_freq1 = very_large_file_freq
1171 IF (
PRESENT(new_file_freq_units) )
THEN
1172 new_file_freq_units1 = new_file_freq_units
1173 ELSE IF ( get_calendar_type() == no_calendar )
THEN
1174 new_file_freq_units1 = diag_days
1176 new_file_freq_units1 = diag_years
1179 IF (
PRESENT(file_duration) )
THEN
1180 file_duration1 = file_duration
1182 file_duration1 = new_file_freq1
1185 IF (
PRESENT(file_duration_units) )
THEN
1186 file_duration_units1 = file_duration_units
1188 file_duration_units1 = new_file_freq_units1
1191 files(num_files)%tile_count = tile_count
1192 files(num_files)%name = trim(name)
1193 files(num_files)%output_freq = output_freq
1194 files(num_files)%output_units = output_units
1195 files(num_files)%format =
FORMAT
1196 files(num_files)%time_units = time_units
1197 files(num_files)%long_name = trim(long_name)
1198 files(num_files)%num_fields = 0
1199 files(num_files)%local = .false.
1200 files(num_files)%last_flush = get_base_time()
1201 files(num_files)%file_unit = -1
1202 files(num_files)%new_file_freq = new_file_freq1
1203 files(num_files)%new_file_freq_units = new_file_freq_units1
1204 files(num_files)%duration = file_duration1
1205 files(num_files)%duration_units = file_duration_units1
1207 files(num_files)%rtime_current = -1.0
1208 files(num_files)%time_index = 0
1209 files(num_files)%filename_time_bounds = filename_time_bounds
1211 IF (
PRESENT(start_time) )
THEN
1212 files(num_files)%start_time = start_time
1214 files(num_files)%start_time = get_base_time()
1216 files(num_files)%next_open=diag_time_inc(files(num_files)%start_time,new_file_freq1,new_file_freq_units1)
1217 files(num_files)%close_time = diag_time_inc(files(num_files)%start_time,file_duration1, file_duration_units1)
1218 IF ( files(num_files)%close_time>files(num_files)%next_open )
THEN
1223 CALL error_mesg(
'diag_util_mod::init_file',
'close time GREATER than next_open time, check file duration,&
1224 & file frequency in '//files(num_files)%name, fatal)
1228 WRITE(time_units_str, 11) trim(time_unit_list(files(num_files)%time_units)), get_base_year(),&
1229 & get_base_month(), get_base_day(), get_base_hour(), get_base_minute(), get_base_second()
1230 11
FORMAT(a,
' since ', i4.4,
'-', i2.2,
'-', i2.2,
' ', i2.2,
':', i2.2,
':', i2.2)
1231 files(num_files)%time_axis_id = diag_axis_init(trim(long_name), tdata, time_units_str,
'T',&
1232 & trim(long_name) , set_name=trim(name) )
1234 files(num_files)%time_bounds_id = diag_axis_init(
'nv',(/1.,2./),
'none',
'N',
'vertex number',&
1245 INTEGER,
INTENT(in) :: file_id
1246 TYPE(time_type),
INTENT(in) :: init_time
1247 CHARACTER(len=*),
OPTIONAL,
INTENT(out) :: err_msg
1249 CHARACTER(len=128) :: msg
1251 IF (
PRESENT(err_msg) ) err_msg =
''
1253 IF ( files(file_id)%start_time < init_time )
THEN
1255 files(file_id)%start_time = init_time
1257 files(file_id)%close_time = diag_time_inc(files(file_id)%start_time,&
1258 & files(file_id)%duration, files(file_id)%duration_units)
1262 DO WHILE ( files(file_id)%next_open <= init_time )
1263 files(file_id)%next_open = diag_time_inc(files(file_id)%next_open,&
1264 & files(file_id)%new_file_freq, files(file_id)%new_file_freq_units, err_msg=msg)
1265 IF ( msg /=
'' )
THEN
1266 IF ( fms_error_handler(
'diag_util_mod::sync_file_times',&
1267 &
' file='//trim(files(file_id)%name)//
': '//trim(msg), err_msg) )
RETURN
1275 INTEGER,
INTENT(in) :: tile_count
1276 CHARACTER(len=*),
INTENT(in) :: name
1282 IF( trim(files(i)%name) == trim(name) .AND. tile_count == files(i)%tile_count )
THEN
1292 CHARACTER(len=*),
INTENT(in) :: module_name
1293 CHARACTER(len=*),
INTENT(in) :: field_name
1294 INTEGER,
INTENT(in) :: tile_count
1299 DO i = 1, num_input_fields
1300 IF(tile_count == input_fields(i)%tile_count .AND.&
1301 & trim(input_fields(i)%module_name) == trim(module_name) .AND.&
1302 & lowercase(trim(input_fields(i)%field_name)) == lowercase(trim(field_name)))
THEN
1311 CHARACTER(len=*),
INTENT(in) :: module_name
1312 CHARACTER(len=*),
INTENT(in) :: field_name
1313 INTEGER,
INTENT(in) :: tile_count
1317 num_input_fields = num_input_fields + 1
1318 IF ( num_input_fields > max_input_fields )
THEN
1320 CALL error_mesg(
'diag_util_mod::init_input_field',&
1321 &
'max_input_fields exceeded, increase it via diag_manager_nml', fatal)
1328 input_fields(num_input_fields)%module_name = trim(module_name)
1329 input_fields(num_input_fields)%field_name = trim(field_name)
1330 input_fields(num_input_fields)%num_output_fields = 0
1332 input_fields(num_input_fields)%register = .false.
1333 input_fields(num_input_fields)%local = .false.
1334 input_fields(num_input_fields)%standard_name =
'none'
1335 input_fields(num_input_fields)%tile_count = tile_count
1336 input_fields(num_input_fields)%numthreads = 1
1337 input_fields(num_input_fields)%active_omp_level = 0
1338 input_fields(num_input_fields)%time = time_zero
1343 & time_method, pack, tile_count, local_coord)
1344 CHARACTER(len=*),
INTENT(in) :: module_name
1345 CHARACTER(len=*),
INTENT(in) :: field_name
1346 CHARACTER(len=*),
INTENT(in) :: output_name
1347 CHARACTER(len=*),
INTENT(in) :: output_file
1348 CHARACTER(len=*),
INTENT(in) :: time_method
1351 INTEGER,
INTENT(in) :: pack
1352 INTEGER,
INTENT(in) :: tile_count
1353 TYPE(coord_type),
INTENT(in),
OPTIONAL :: local_coord
1355 INTEGER :: out_num, in_num, file_num, file_num_tile1
1356 INTEGER :: num_fields, i, method_selected, l1
1361 CHARACTER(len=128) :: error_msg
1362 CHARACTER(len=50) :: t_method
1363 character(len=256) :: tmp_name
1367 IF ( region_out_use_alt_value )
THEN
1368 grv = glo_reg_val_alt
1375 num_output_fields = num_output_fields + 1
1376 IF ( num_output_fields > max_output_fields )
THEN
1378 WRITE (unit=error_msg,fmt=*) max_output_fields
1379 CALL error_mesg(
'diag_util_mod::init_output_field',
'max_output_fields = '//trim(error_msg)//
' exceeded.&
1380 & Increase via diag_manager_nml', fatal)
1382 out_num = num_output_fields
1386 IF ( in_num < 0 )
THEN
1387 IF ( tile_count > 1 )
THEN
1388 WRITE (error_msg,
'(A,"/",A,"/",A)') trim(module_name),trim(field_name),&
1389 &
"tile_count="//trim(string(tile_count))
1391 WRITE (error_msg,
'(A,"/",A)') trim(module_name),trim(field_name)
1395 CALL error_mesg(
'diag_util_mod::init_output_field',&
1396 &
'module_name/field_name '//trim(error_msg)//
' NOT registered', fatal)
1400 input_fields(in_num)%num_output_fields =&
1401 & input_fields(in_num)%num_output_fields + 1
1402 IF ( input_fields(in_num)%num_output_fields > max_out_per_in_field )
THEN
1408 WRITE (unit=error_msg,fmt=*) max_out_per_in_field
1409 CALL error_mesg(
'diag_util_mod::init_output_field',&
1410 &
'MAX_OUT_PER_IN_FIELD exceeded for '//trim(module_name)//
"/"//trim(field_name)//&
1411 &
', increase MAX_OUT_PER_IN_FIELD in the diag_manager_nml namelist', fatal)
1413 input_fields(in_num)%output_fields(input_fields(in_num)%num_output_fields) = out_num
1416 output_fields(out_num)%input_field = in_num
1419 IF ( trim(output_file).EQ.
'null' )
THEN
1420 file_num = max_files
1423 IF ( file_num < 0 )
THEN
1427 CALL error_mesg(
'diag_util_mod::init_output_field',
'file '&
1428 & //trim(output_file)//
' is NOT found in the diag_table', fatal)
1430 IF ( tile_count > 1 )
THEN
1431 file_num_tile1 = file_num
1432 file_num =
find_file(output_file, tile_count)
1433 IF(file_num < 0)
THEN
1434 CALL init_file(files(file_num_tile1)%name, files(file_num_tile1)%output_freq,&
1435 & files(file_num_tile1)%output_units, files(file_num_tile1)%format,&
1436 & files(file_num_tile1)%time_units, files(file_num_tile1)%long_name,&
1437 & tile_count, files(file_num_tile1)%new_file_freq,&
1438 & files(file_num_tile1)%new_file_freq_units, files(file_num_tile1)%start_time,&
1439 & files(file_num_tile1)%duration, files(file_num_tile1)%duration_units )
1440 file_num =
find_file(output_file, tile_count)
1441 IF ( file_num < 0 )
THEN
1445 CALL error_mesg(
'diag_util_mod::init_output_field',
'file '//trim(output_file)//&
1446 &
' is not initialized for tile_count = '//trim(string(tile_count)), fatal)
1453 files(file_num)%num_fields = files(file_num)%num_fields + 1
1454 IF ( files(file_num)%num_fields > max_fields_per_file )
THEN
1455 WRITE (unit=error_msg, fmt=*) max_fields_per_file
1459 CALL error_mesg(
'diag_util_mod::init_output_field',&
1460 &
'MAX_FIELDS_PER_FILE = '//trim(error_msg)// &
1461 &
' exceeded. Increase MAX_FIELDS_PER_FILE in diag_data.F90.', fatal)
1463 num_fields = files(file_num)%num_fields
1464 files(file_num)%fields(num_fields) = out_num
1467 output_fields(out_num)%output_file = file_num
1470 output_fields(out_num)%output_name = trim(output_name)
1471 output_fields(out_num)%pack = pack
1472 output_fields(out_num)%pow_value = 1
1473 output_fields(out_num)%num_axes = 0
1474 output_fields(out_num)%total_elements = 0
1475 output_fields(out_num)%region_elements = 0
1477 call output_fields(out_num)%buff_bounds%reset(very_large_axis_length, 0)
1480 output_fields(out_num)%n_diurnal_samples = 1
1484 output_fields(out_num)%time_average = .false.
1485 output_fields(out_num)%time_rms = .false.
1486 output_fields(out_num)%time_min = .false.
1487 output_fields(out_num)%time_max = .false.
1488 output_fields(out_num)%time_sum = .false.
1489 output_fields(out_num)%time_ops = .false.
1490 output_fields(out_num)%written_once = .false.
1492 t_method = lowercase(time_method)
1494 IF ( files(file_num)%output_freq == every_time )
THEN
1495 output_fields(out_num)%time_average = .false.
1496 method_selected = method_selected+1
1498 ELSEIF ( index(t_method,
'diurnal') == 1 )
THEN
1500 READ (unit=t_method(8:len_trim(t_method)), fmt=*, iostat=ioerror) output_fields(out_num)%n_diurnal_samples
1501 IF ( ioerror /= 0 )
THEN
1505 CALL error_mesg(
'diag_util_mod::init_output_field',&
1506 &
'could not find integer number of diurnal samples in string "' //trim(t_method)//
'"', fatal)
1507 ELSE IF ( output_fields(out_num)%n_diurnal_samples <= 0 )
THEN
1511 CALL error_mesg(
'diag_util_mod::init_output_field',&
1512 &
'The integer value of diurnal samples must be greater than zero.', fatal)
1514 output_fields(out_num)%time_average = .true.
1515 method_selected = method_selected+1
1517 ELSEIF ( index(t_method,
'pow') == 1 )
THEN
1519 READ (unit=t_method(4:len_trim(t_method)), fmt=*, iostat=ioerror) pow_value
1520 IF ( ioerror /= 0 .OR. output_fields(out_num)%pow_value < 1 .OR. floor(pow_value) /= ceiling(pow_value) )
THEN
1524 CALL error_mesg(
'diag_util_mod::init_output_field',&
1525 &
'Invalid power number in time operation "'//trim(t_method)//
'". Must be a positive integer', fatal)
1527 output_fields(out_num)%pow_value = int(pow_value)
1528 output_fields(out_num)%time_average = .true.
1529 method_selected = method_selected+1
1530 t_method =
'mean_pow('//t_method(4:len_trim(t_method))//
')'
1532 SELECT CASE(trim(t_method))
1533 CASE (
'.true.',
'mean',
'average',
'avg' )
1534 output_fields(out_num)%time_average = .true.
1535 method_selected = method_selected+1
1538 output_fields(out_num)%time_average = .true.
1539 output_fields(out_num)%time_rms = .true.
1540 output_fields(out_num)%pow_value = 2.0
1541 method_selected = method_selected+1
1542 t_method =
'root_mean_square'
1543 CASE (
'.false.',
'none',
'point' )
1544 output_fields(out_num)%time_average = .false.
1545 method_selected = method_selected+1
1547 CASE (
'maximum',
'max' )
1548 output_fields(out_num)%time_max = .true.
1549 l1 = len_trim(output_fields(out_num)%output_name)
1551 tmp_name = trim(adjustl(output_fields(out_num)%output_name(l1-2:l1)))
1552 IF (lowercase(trim(tmp_name)) /=
'max' )
then
1553 output_fields(out_num)%output_name = trim(output_name)//
'_max'
1556 method_selected = method_selected+1
1558 CASE (
'minimum',
'min' )
1559 output_fields(out_num)%time_min = .true.
1560 l1 = len_trim(output_fields(out_num)%output_name)
1562 tmp_name = trim(adjustl(output_fields(out_num)%output_name(l1-2:l1)))
1563 IF (lowercase(trim(tmp_name)) /=
'min' )
then
1564 output_fields(out_num)%output_name = trim(output_name)//
'_min'
1567 method_selected = method_selected+1
1569 CASE (
'sum',
'cumsum' )
1570 output_fields(out_num)%time_sum = .true.
1571 l1 = len_trim(output_fields(out_num)%output_name)
1572 IF ( output_fields(out_num)%output_name(l1-2:l1) /=
'sum' )&
1573 & output_fields(out_num)%output_name = trim(output_name)//
'_sum'
1574 method_selected = method_selected+1
1580 output_fields(out_num)%time_ops = output_fields(out_num)%time_min.OR.output_fields(out_num)%time_max&
1581 & .OR.output_fields(out_num)%time_average .OR. output_fields(out_num)%time_sum
1583 output_fields(out_num)%phys_window = .false.
1585 IF (
PRESENT(local_coord) )
THEN
1586 input_fields(in_num)%local = .true.
1587 input_fields(in_num)%local_coord = local_coord
1588 IF ( int(local_coord%xbegin) == grv .AND. int(local_coord%xend) == grv .AND.&
1589 & int(local_coord%ybegin) == grv .AND. int(local_coord%yend) == grv )
THEN
1590 output_fields(out_num)%local_output = .false.
1591 output_fields(out_num)%need_compute = .false.
1592 output_fields(out_num)%reduced_k_range = .true.
1594 output_fields(out_num)%local_output = .true.
1595 output_fields(out_num)%need_compute = .false.
1596 output_fields(out_num)%reduced_k_range = .false.
1599 output_fields(out_num)%output_grid%start(1) = local_coord%xbegin
1600 output_fields(out_num)%output_grid%start(2) = local_coord%ybegin
1601 output_fields(out_num)%output_grid%start(3) = local_coord%zbegin
1602 output_fields(out_num)%output_grid%end(1) = local_coord%xend
1603 output_fields(out_num)%output_grid%end(2) = local_coord%yend
1604 output_fields(out_num)%output_grid%end(3) = local_coord%zend
1606 output_fields(out_num)%output_grid%l_start_indx(i) = -1
1607 output_fields(out_num)%output_grid%l_end_indx(i) = -1
1608 output_fields(out_num)%output_grid%subaxes(i) = -1
1611 output_fields(out_num)%local_output = .false.
1612 output_fields(out_num)%need_compute = .false.
1613 output_fields(out_num)%reduced_k_range = .false.
1619 IF ( method_selected /= 1 )
CALL error_mesg(
'diag_util_mod::init_output_field',&
1620 &
'improper time method in diag_table for output field:'//trim(output_name),fatal)
1622 output_fields(out_num)%time_method = trim(t_method)
1627 ALLOCATE(output_fields(out_num)%count_0d(output_fields(out_num)%n_diurnal_samples))
1628 ALLOCATE(output_fields(out_num)%num_elements(output_fields(out_num)%n_diurnal_samples))
1629 output_fields(out_num)%count_0d(:) = 0
1630 output_fields(out_num)%num_elements(:) = 0
1631 output_fields(out_num)%num_attributes = 0
1637 INTEGER,
INTENT(in) :: file
1638 TYPE(time_type),
INTENT(in) :: time
1639 TYPE(time_type),
INTENT(in),
optional :: filename_time
1642 TYPE(time_type) :: fname_time
1643 REAL,
DIMENSION(2) :: open_file_data
1644 INTEGER :: j, field_num, input_field_num, num_axes, k
1645 INTEGER :: field_num1
1647 INTEGER :: dir, edges
1648 INTEGER :: year, month, day, hour, minute, second
1649 INTEGER,
DIMENSION(1) :: time_axis_id, time_bounds_id
1653 INTEGER,
DIMENSION(6) :: axes
1654 INTEGER,
ALLOCATABLE :: axesc(:)
1655 LOGICAL :: time_ops, aux_present, match_aux_name, req_present, match_req_fields
1656 CHARACTER(len=7) :: avg_name =
'average'
1657 CHARACTER(len=128) :: time_units, timeb_units, avg, error_string, aux_name, req_fields, fieldname
1658 CHARACTER(len=FMS_FILE_LEN) :: filename
1659 CHARACTER(len=128) :: suffix, base_name
1660 CHARACTER(len=32) :: time_name, timeb_name,time_longname, timeb_longname, cart_name
1661 CHARACTER(len=FMS_FILE_LEN) :: fname
1662 CHARACTER(len=24) :: start_date
1663 TYPE(domain1d) :: domain
1664 TYPE(domain2d) :: domain2
1665 TYPE(domainug) :: domainU
1666 INTEGER :: is, ie, last, ind
1667 class(fmsnetcdffile_t),
pointer :: fileob
1668 integer :: actual_num_axes
1670 aux_present = .false.
1671 match_aux_name = .false.
1672 req_present = .false.
1673 match_req_fields = .false.
1676 WRITE (time_units, 11) trim(time_unit_list(files(file)%time_units)), get_base_year(),&
1677 & get_base_month(), get_base_day(), get_base_hour(), get_base_minute(), get_base_second()
1678 11
FORMAT(a,
' since ', i4.4,
'-', i2.2,
'-', i2.2,
' ', i2.2,
':', i2.2,
':', i2.2)
1679 base_name = files(file)%name
1680 IF ( files(file)%new_file_freq < very_large_file_freq )
THEN
1681 position = index(files(file)%name,
'%')
1682 IF ( position > 0 )
THEN
1683 base_name = base_name(1:position-1)
1688 CALL error_mesg(
'diag_util_mod::opening_file',&
1689 &
'file name '//trim(files(file)%name)//
' does not contain % for time stamp string', fatal)
1691 if (
present(filename_time))
then
1692 fname_time = filename_time
1696 suffix = get_time_string(files(file)%name, fname_time)
1703 call get_instance_filename(fname, base_name)
1706 filename = trim(base_name)//trim(suffix)
1709 IF ( prepend_date )
THEN
1710 call get_date(diag_init_time, year, month, day, hour, minute, second)
1711 write (start_date,
'(1I20.4, 2I2.2)') year, month, day
1713 filename = trim(adjustl(start_date))//
'.'//trim(filename)
1718 domain2 = null_domain2d
1719 domainu = null_domainug
1720 DO j = 1, files(file)%num_fields
1721 field_num = files(file)%fields(j)
1722 if (output_fields(field_num)%local_output .AND. .NOT. output_fields(field_num)%need_compute) cycle
1723 num_axes = output_fields(field_num)%num_axes
1724 IF ( num_axes > 1 )
THEN
1725 domain2 = get_domain2d( output_fields(field_num)%axes(1:num_axes) )
1726 domainu = get_domainug( output_fields(field_num)%axes(1) )
1727 IF ( domain2 .NE. null_domain2d )
EXIT
1728 ELSEIF (num_axes == 1)
THEN
1729 if (domainu .EQ. null_domainug)
then
1730 domainu = get_domainug( output_fields(field_num)%axes(num_axes) )
1735 IF (domainu .NE. null_domainug .AND. domain2 .NE. null_domain2d)
THEN
1736 CALL error_mesg(
'diag_util_mod::opening_file', &
1737 'Domain2 and DomainU are somehow both set.', &
1741 IF (
allocated(files(file)%attributes) )
THEN
1742 CALL diag_output_init(filename, global_descriptor,&
1743 & files(file)%file_unit, domain2, domainu,&
1744 & fileobj(file),fileobju(file), fileobjnd(file), fnum_for_domain(file),&
1745 & attributes=files(file)%attributes(1:files(file)%num_attributes))
1747 CALL diag_output_init(filename, global_descriptor,&
1748 & files(file)%file_unit, domain2,domainu, &
1749 & fileobj(file),fileobju(file),fileobjnd(file),fnum_for_domain(file))
1752 files(file)%bytes_written = 0
1755 DO j = 1, files(file)%num_fields
1756 field_num = files(file)%fields(j)
1757 IF ( output_fields(field_num)%time_ops )
THEN
1763 DO j = 1, files(file)%num_fields
1764 field_num = files(file)%fields(j)
1765 input_field_num = output_fields(field_num)%input_field
1766 IF (.NOT.input_fields(input_field_num)%register)
THEN
1767 WRITE (error_string,
'(A,"/",A)') trim(input_fields(input_field_num)%module_name),&
1768 & trim(input_fields(input_field_num)%field_name)
1769 IF(
mpp_pe() .EQ. mpp_root_pe())
THEN
1770 CALL error_mesg(
'diag_util_mod::opening_file',&
1771 &
'module/field_name ('//trim(error_string)//
') NOT registered', warning)
1775 if (output_fields(field_num)%local_output .AND. .NOT. output_fields(field_num)%need_compute) cycle
1778 num_axes = output_fields(field_num)%num_axes
1779 axes(1:num_axes) = output_fields(field_num)%axes(1:num_axes)
1782 IF ( axes(k) < 0 )
THEN
1783 WRITE(error_string,
'(a)') output_fields(field_num)%output_name
1787 CALL error_mesg(
'diag_util_mod::opening_file',
'output_name '//trim(error_string)//&
1788 &
' has axis_id = -1', fatal)
1792 IF ( .NOT.aux_present )
THEN
1794 aux_name = get_axis_aux(axes(k))
1795 IF ( trim(aux_name) /=
'none' )
THEN
1796 aux_present = .true.
1802 IF ( .NOT.req_present )
THEN
1804 req_fields = get_axis_reqfld(axes(k))
1805 IF ( trim(req_fields) /=
'none' )
THEN
1806 CALL error_mesg(
'diag_util_mod::opening_file',
'required fields found: '//&
1807 &trim(req_fields)//
' in file '//trim(files(file)%name),note)
1808 req_present = .true.
1814 axes(num_axes + 1) = files(file)%time_axis_id
1816 if (.not.
allocated(files(file)%is_time_axis_registered))
then
1817 allocate(files(file)%is_time_axis_registered)
1818 files(file)%is_time_axis_registered = .false.
1822 actual_num_axes = num_axes + 2
1823 axes(num_axes + 2) = files(file)%time_bounds_id
1826 actual_num_axes = num_axes + 1
1829 if (fnum_for_domain(file) ==
"2d")
then
1830 CALL write_axis_meta_data(files(file)%file_unit, axes(1:actual_num_axes),fileobj(file), time_ops=time_ops, &
1831 time_axis_registered=files(file)%is_time_axis_registered)
1832 elseif (fnum_for_domain(file) ==
"nd")
then
1833 CALL write_axis_meta_data(files(file)%file_unit, axes(1:actual_num_axes),fileobjnd(file), time_ops=time_ops,&
1834 time_axis_registered=files(file)%is_time_axis_registered)
1835 elseif (fnum_for_domain(file) ==
"ug")
then
1836 CALL write_axis_meta_data(files(file)%file_unit, axes(1:actual_num_axes),fileobju(file), time_ops=time_ops, &
1837 time_axis_registered=files(file)%is_time_axis_registered)
1843 IF (axis_is_compressed(axes(k)))
THEN
1844 CALL get_compressed_axes_ids(axes(k), axesc)
1845 if (fnum_for_domain(file) ==
"ug")
then
1846 CALL write_axis_meta_data(files(file)%file_unit, axesc,fileobju(file), &
1847 time_axis_registered=files(file)%is_time_axis_registered)
1849 CALL error_mesg(
'diag_util_mod::opening_file::'//trim(filename),
"Compressed "//&
1850 "dimensions are only allowed with axis in the unstructured dimension", fatal)
1858 field_num1 = files(file)%fields(1)
1859 DO j = 1, files(file)%num_fields
1860 field_num = files(file)%fields(j)
1861 IF ( output_fields(field_num)%time_ops )
THEN
1862 field_num1 = field_num
1866 nfields_loop:
DO j = 1, files(file)%num_fields
1867 field_num = files(file)%fields(j)
1868 input_field_num = output_fields(field_num)%input_field
1869 IF (.NOT.input_fields(input_field_num)%register) cycle
1870 IF (output_fields(field_num)%local_output .AND. .NOT. output_fields(field_num)%need_compute) cycle
1873 IF ( .NOT.mix_snapshot_average_fields )
THEN
1874 IF ( (output_fields(field_num)%time_ops.NEQV.output_fields(field_num1)%time_ops) .AND.&
1875 & .NOT.output_fields(field_num1)%static .AND. .NOT.output_fields(field_num)%static)
THEN
1876 IF (
mpp_pe() == mpp_root_pe() )
THEN
1881 CALL error_mesg(
'diag_util_mod::opening_file',
'file '//trim(files(file)%name)// &
1882 &
' can NOT have BOTH time average AND instantaneous fields.'//&
1883 &
' Create a new file or set mix_snapshot_average_fields=.TRUE. in the namelist diag_manager_nml.'&
1889 IF ( aux_present .AND. .NOT.match_aux_name )
THEN
1890 fieldname = output_fields(field_num)%output_name
1891 IF ( index(aux_name, trim(fieldname)) > 0 ) match_aux_name = .true.
1894 IF ( req_present .AND. .NOT.match_req_fields )
THEN
1895 fieldname = output_fields(field_num)%output_name
1896 is = 1; last = len_trim(req_fields)
1898 ind = index(req_fields(is:last),
' ')
1899 IF (ind .eq. 0) ind = last-is+2
1901 if (req_fields(is:ie) .EQ. trim(fieldname))
then
1902 match_req_fields = .true.
1907 if (is .GT. last)
EXIT
1912 num_axes = output_fields(field_num)%num_axes
1913 axes(1:num_axes) = output_fields(field_num)%axes(1:num_axes)
1914 IF ( .NOT.output_fields(field_num)%static )
THEN
1916 axes(num_axes) = files(file)%time_axis_id
1918 IF(output_fields(field_num)%time_average)
THEN
1920 ELSE IF(output_fields(field_num)%time_max)
THEN
1922 ELSE IF(output_fields(field_num)%time_min)
THEN
1928 if (fnum_for_domain(file) ==
"2d")
then
1929 fileob => fileobj(file)
1930 elseif (fnum_for_domain(file) ==
"nd")
then
1931 fileob => fileobjnd(file)
1932 elseif (fnum_for_domain(file) ==
"ug")
then
1933 fileob => fileobju(file)
1935 IF ( input_fields(input_field_num)%missing_value_present )
THEN
1936 IF ( len_trim(input_fields(input_field_num)%interp_method) > 0 )
THEN
1937 output_fields(field_num)%f_type = write_field_meta_data(files(file)%file_unit,&
1938 & output_fields(field_num)%output_name, axes(1:num_axes),&
1939 & input_fields(input_field_num)%units,&
1940 & input_fields(input_field_num)%long_name,&
1941 & input_fields(input_field_num)%range, output_fields(field_num)%pack,&
1942 & input_fields(input_field_num)%missing_value, avg_name = avg,&
1943 & time_method=output_fields(field_num)%time_method,&
1944 & standard_name = input_fields(input_field_num)%standard_name,&
1945 & interp_method = input_fields(input_field_num)%interp_method,&
1946 & attributes=output_fields(field_num)%attributes,&
1947 & num_attributes=output_fields(field_num)%num_attributes,&
1948 & use_ugdomain=files(file)%use_domainUG , &
1951 output_fields(field_num)%f_type = write_field_meta_data(files(file)%file_unit,&
1952 & output_fields(field_num)%output_name, axes(1:num_axes),&
1953 & input_fields(input_field_num)%units,&
1954 & input_fields(input_field_num)%long_name,&
1955 & input_fields(input_field_num)%range, output_fields(field_num)%pack,&
1956 & input_fields(input_field_num)%missing_value, avg_name = avg,&
1957 & time_method=output_fields(field_num)%time_method,&
1958 & standard_name = input_fields(input_field_num)%standard_name,&
1959 & attributes=output_fields(field_num)%attributes,&
1960 & num_attributes=output_fields(field_num)%num_attributes,&
1961 & use_ugdomain=files(file)%use_domainUG , &
1967 IF ( len_trim(input_fields(input_field_num)%interp_method) > 0 )
THEN
1968 output_fields(field_num)%f_type = write_field_meta_data(files(file)%file_unit,&
1969 & output_fields(field_num)%output_name, axes(1:num_axes),&
1970 & input_fields(input_field_num)%units,&
1971 & input_fields(input_field_num)%long_name,&
1972 & input_fields(input_field_num)%range, output_fields(field_num)%pack,&
1974 & time_method=output_fields(field_num)%time_method,&
1975 & standard_name = input_fields(input_field_num)%standard_name,&
1976 & interp_method = input_fields(input_field_num)%interp_method,&
1977 & attributes=output_fields(field_num)%attributes,&
1978 & num_attributes=output_fields(field_num)%num_attributes,&
1979 & use_ugdomain=files(file)%use_domainUG , &
1983 output_fields(field_num)%f_type = write_field_meta_data(files(file)%file_unit,&
1984 & output_fields(field_num)%output_name, axes(1:num_axes),&
1985 & input_fields(input_field_num)%units,&
1986 & input_fields(input_field_num)%long_name,&
1987 & input_fields(input_field_num)%range, output_fields(field_num)%pack,&
1989 & time_method=output_fields(field_num)%time_method,&
1990 & standard_name = input_fields(input_field_num)%standard_name,&
1991 & attributes=output_fields(field_num)%attributes,&
1992 & num_attributes=output_fields(field_num)%num_attributes,&
1993 & use_ugdomain=files(file)%use_domainUG , &
2001 IF ( time_ops )
THEN
2002 time_axis_id(1) = files(file)%time_axis_id
2003 files(file)%f_avg_start = write_field_meta_data(files(file)%file_unit,&
2004 & avg_name //
'_T1', time_axis_id, time_units,&
2005 &
"Start time for average period", pack=pack_size , &
2007 files(file)%f_avg_end = write_field_meta_data(files(file)%file_unit,&
2008 & avg_name //
'_T2', time_axis_id, time_units,&
2009 &
"End time for average period", pack=pack_size , &
2011 files(file)%f_avg_nitems = write_field_meta_data(files(file)%file_unit,&
2012 & avg_name //
'_DT', time_axis_id,&
2013 & trim(time_unit_list(files(file)%time_units)),&
2014 &
"Length of average period", pack=pack_size , &
2018 IF ( time_ops )
THEN
2019 time_axis_id(1) = files(file)%time_axis_id
2020 time_bounds_id(1) = files(file)%time_bounds_id
2021 CALL get_diag_axis( time_axis_id(1), time_name, time_units, time_longname,&
2022 & cart_name, dir, edges, domain, domainu, open_file_data)
2023 CALL get_diag_axis( time_bounds_id(1), timeb_name, timeb_units, timeb_longname,&
2024 & cart_name, dir, edges, domain, domainu, open_file_data)
2026 files(file)%f_bounds = write_field_meta_data(files(file)%file_unit,&
2027 & trim(time_name)//
'_bnds', (/time_bounds_id,time_axis_id/),&
2028 & time_units, trim(time_name)//
' axis boundaries', pack=pack_size , &
2032 call done_meta_data(files(file)%file_unit)
2034 IF( aux_present .AND. .NOT.match_aux_name )
THEN
2039 IF (
mpp_pe() == mpp_root_pe() )
CALL error_mesg(
'diag_util_mod::opening_file',&
2040 &
'one axis has auxiliary but the corresponding field is NOT found in file '// &
2041 & trim(files(file)%name), warning)
2043 IF( req_present .AND. .NOT.match_req_fields )
THEN
2048 IF (
mpp_pe() == mpp_root_pe() )
CALL error_mesg(
'diag_util_mod::opening_file',&
2049 &
'one axis has required fields ('//trim(req_fields)//
') but the '// &
2050 &
'corresponding fields are NOT found in file '//trim(files(file)%name), fatal)
2053 if (
associated(fileob))
nullify(fileob)
2057 SUBROUTINE diag_data_out(file, field, dat, time, final_call_in, static_write_in, filename_time)
2058 INTEGER,
INTENT(in) :: file
2059 INTEGER,
INTENT(in) :: field
2060 REAL,
DIMENSION(:,:,:,:),
INTENT(inout) :: dat
2061 TYPE(time_type),
INTENT(in) :: time
2062 LOGICAL,
OPTIONAL,
INTENT(in):: final_call_in
2063 LOGICAL,
OPTIONAL,
INTENT(in):: static_write_in
2064 type(time_type),
intent(in),
optional :: filename_time
2067 LOGICAL :: final_call, do_write, static_write
2068 REAL :: dif, time_data(2, 1, 1, 1), dt_time(1, 1, 1, 1), start_dif, end_dif
2069 REAL :: time_in_file
2073 time_in_file = files(file)%rtime_current
2076 final_call = .false.
2077 IF (
PRESENT(final_call_in) ) final_call = final_call_in
2078 static_write = .false.
2079 IF (
PRESENT(static_write_in) ) static_write = static_write_in
2081 dif = get_date_dif(time, get_base_time(), files(file)%time_units)
2084 IF ( .NOT.static_write .OR. files(file)%file_unit < 0 ) &
2085 CALL check_and_open(file, time, do_write, filename_time=filename_time)
2086 IF ( .NOT.do_write )
RETURN
2088 if (dif > files(file)%rtime_current)
then
2089 files(file)%time_index = files(file)%time_index + 1
2090 files(file)%rtime_current = dif
2091 if (fnum_for_domain(file) ==
"2d")
then
2092 call diag_write_time (fileobj(file), files(file)%rtime_current, files(file)%time_index, &
2093 time_name=fileobj(file)%time_name)
2094 elseif (fnum_for_domain(file) ==
"ug")
then
2095 call diag_write_time (fileobju(file), files(file)%rtime_current, files(file)%time_index, &
2096 time_name=fileobju(file)%time_name)
2097 elseif (fnum_for_domain(file) ==
"nd")
then
2098 call diag_write_time (fileobjnd(file), files(file)%rtime_current, files(file)%time_index, &
2099 time_name=fileobjnd(file)%time_name)
2101 call error_mesg(
"diag_util_mod::diag_data_out",
"Error opening the file "//files(file)%name,fatal)
2103 elseif (dif < files(file)%rtime_current .and. .not.(static_write) )
then
2104 call error_mesg(
"diag_util_mod::diag_data_out",
"The time for the file "//trim(files(file)%name)//&
2105 " has gone backwards. There may be missing values for some of the variables",note)
2108 call diag_field_write (output_fields(field)%output_name, dat, static_write, file, fileobju, &
2109 fileobj, fileobjnd, fnum_for_domain(file), time_in=files(file)%time_index)
2111 files(file)%bytes_written = files(file)%bytes_written +&
2112 & (
SIZE(dat,1)*
SIZE(dat,2)*
SIZE(dat,3))*(8/output_fields(field)%pack)
2113 IF ( .NOT.output_fields(field)%written_once ) output_fields(field)%written_once = .true.
2115 IF ( .NOT.output_fields(field)%static )
THEN
2116 start_dif = get_date_dif(output_fields(field)%last_output, get_base_time(),files(file)%time_units)
2117 IF ( .NOT.mix_snapshot_average_fields )
THEN
2118 end_dif = get_date_dif(output_fields(field)%next_output, get_base_time(), files(file)%time_units)
2124 if (files(file)%rtime_current > time_in_file)
then
2125 if (output_fields(field)%time_ops)
then
2127 time_data(1, 1, 1, 1) = start_dif
2128 call diag_field_write (files(file)%f_avg_start%fieldname, time_data(1:1,:,:,:), static_write, file, &
2129 fileobju, fileobj, fileobjnd, &
2130 fnum_for_domain(file), time_in=files(file)%time_index)
2131 time_data(2, 1, 1, 1) = end_dif
2132 call diag_field_write (files(file)%f_avg_end%fieldname, time_data(2:2,:,:,:), static_write, file, &
2133 fileobju, fileobj, fileobjnd, &
2134 fnum_for_domain(file), time_in=files(file)%time_index)
2136 dt_time(1, 1, 1, 1) = end_dif - start_dif
2137 call diag_field_write (files(file)%f_avg_nitems%fieldname, dt_time(1:1,:,:,:), static_write, file, &
2138 fileobju, fileobj, fileobjnd, &
2139 fnum_for_domain(file), time_in=files(file)%time_index)
2141 call diag_field_write (files(file)%f_bounds%fieldname, time_data(1:2,:,:,:), static_write, file, &
2142 fileobju, fileobj, fileobjnd, &
2143 fnum_for_domain(file), time_in=files(file)%time_index)
2148 IF ( final_call )
THEN
2149 IF ( time >= files(file)%last_flush )
THEN
2150 files(file)%last_flush = time
2153 IF ( time > files(file)%last_flush .AND. (flush_nc_files.OR.debug_diag_manager) )
THEN
2154 call diag_flush(file, fileobju, fileobj, fileobjnd, fnum_for_domain(file))
2155 files(file)%last_flush = time
2165 INTEGER,
INTENT(in) :: file
2166 TYPE(time_type),
INTENT(in) :: time
2167 LOGICAL,
INTENT(out) :: do_write
2169 TYPE(time_type),
INTENT(in),
optional :: filename_time
2172 IF ( time >= files(file)%start_time )
THEN
2173 IF ( files(file)%file_unit < 0 )
THEN
2174 CALL opening_file(file, time, filename_time=filename_time)
2178 IF ( time > files(file)%close_time .AND. time < files(file)%next_open )
THEN
2180 ELSE IF ( time > files(file)%next_open )
THEN
2182 CALL opening_file(file, time, filename_time=filename_time)
2183 files(file)%time_index = 0
2184 files(file)%start_time = files(file)%next_open
2185 files(file)%close_time =&
2186 & diag_time_inc(files(file)%start_time,files(file)%duration, files(file)%duration_units)
2187 files(file)%next_open =&
2188 & diag_time_inc(files(file)%next_open, files(file)%new_file_freq,&
2189 & files(file)%new_file_freq_units)
2190 IF ( files(file)%close_time > files(file)%next_open )
THEN
2195 CALL error_mesg(
'diag_util_mod::check_and_open',&
2196 & files(file)%name// &
2197 &
' has close time GREATER than next_open time, check file duration and frequency',fatal)
2208 INTEGER,
INTENT(in) :: file
2209 INTEGER :: j, i, input_num
2211 DO j = 1, files(file)%num_fields
2212 i = files(file)%fields(j)
2213 input_num = output_fields(i)%input_field
2215 IF ( .NOT.input_fields(input_num)%register ) cycle
2216 IF ( output_fields(i)%local_output .AND. .NOT. output_fields(i)%need_compute) cycle
2218 IF ( .NOT.output_fields(i)%static ) cycle
2219 CALL diag_data_out(file, i, output_fields(i)%buffer, files(file)%last_flush, .true., .true.)
2225 if (fnum_for_domain(file) ==
"2d" )
then
2226 if (check_if_open(fileobj(file)))
call close_file (fileobj(file) )
2227 elseif (fnum_for_domain(file) ==
"nd")
then
2228 if (check_if_open(fileobjnd(file)) )
then
2229 call close_file (fileobjnd(file))
2231 elseif (fnum_for_domain(file) ==
"ug")
then
2232 if (check_if_open(fileobju(file)))
call close_file (fileobju(file))
2234 files(file)%file_unit = -1
2239 CHARACTER(len=*),
INTENT(out),
OPTIONAL :: err_msg
2241 INTEGER :: i, j, tmp_file
2242 CHARACTER(len=128) :: tmp_name
2243 CHARACTER(len=256) :: err_msg_local
2245 IF (
PRESENT(err_msg) ) err_msg=
''
2247 IF ( num_output_fields <= 1 )
RETURN
2250 i_loop:
DO i = 1, num_output_fields-1
2251 tmp_name = trim(output_fields(i)%output_name)
2252 tmp_file = output_fields(i)%output_file
2253 DO j = i+1, num_output_fields
2254 IF ( (tmp_name == trim(output_fields(j)%output_name)) .AND. &
2255 &(tmp_file == output_fields(j)%output_file))
THEN
2256 err_msg_local =
' output_field "'//trim(tmp_name)//&
2257 &
'" duplicated in file "'//trim(files(tmp_file)%name)//
'"'
2262 IF ( err_msg_local /=
'' )
THEN
2263 IF ( fms_error_handler(
' ERROR in diag_table',err_msg_local,err_msg) )
RETURN
2269 TYPE(output_field_type),
INTENT(inout) :: out_field
2270 CHARACTER(LEN=*),
INTENT(out),
OPTIONAL :: err_msg
2275 IF (
PRESENT(err_msg) ) err_msg =
''
2278 IF ( .NOT.
allocated(out_field%attributes) )
THEN
2279 ALLOCATE(out_field%attributes(max_field_attributes), stat=istat)
2280 IF ( istat.NE.0 )
THEN
2284 IF ( fms_error_handler(
'diag_util_mod::attribute_init_field',&
2285 &
'Unable to allocate memory for attributes', err_msg) )
THEN
2290 out_field%num_attributes = 0
2298 TYPE(output_field_type),
INTENT(inout) :: out_field
2299 CHARACTER(len=*),
INTENT(in) :: att_name
2300 CHARACTER(len=*),
INTENT(in) :: prepend_value
2301 CHARACTER(len=*),
INTENT(out) ,
OPTIONAL :: err_msg
2303 INTEGER :: length, i, this_attribute
2304 CHARACTER(len=512) :: err_msg_local
2308 IF (
PRESENT(err_msg) ) err_msg =
''
2312 IF ( trim(err_msg_local) .NE.
'' )
THEN
2313 IF ( fms_error_handler(
'diag_util_mod::prepend_attribute_field', trim(err_msg_local), err_msg) )
THEN
2320 DO i=1, out_field%num_attributes
2321 IF ( trim(out_field%attributes(i)%name) .EQ. trim(att_name) )
THEN
2327 IF ( this_attribute > 0 )
THEN
2328 IF ( out_field%attributes(this_attribute)%type .NE. nf90_char )
THEN
2332 IF ( fms_error_handler(
'diag_util_mod::prepend_attribute_field', &
2333 &
'Attribute "'//trim(att_name)//
'" is not a character attribute.',&
2341 this_attribute = out_field%num_attributes + 1
2342 IF ( this_attribute .GT. max_field_attributes )
THEN
2347 IF ( fms_error_handler(
'diag_util_mod::prepend_attribute_field',&
2348 &
'Number of attributes exceeds max_field_attributes for attribute "'&
2349 & //trim(att_name)//
'". Increase diag_manager_nml:max_field_attributes.',&
2354 out_field%num_attributes = this_attribute
2356 out_field%attributes(this_attribute)%name = att_name
2357 out_field%attributes(this_attribute)%type = nf90_char
2359 out_field%attributes(this_attribute)%catt =
''
2364 IF ( index(trim(out_field%attributes(this_attribute)%catt), trim(prepend_value)).EQ.0 )
THEN
2366 length = len_trim(trim(prepend_value)//
" "//trim(out_field%attributes(this_attribute)%catt))
2367 IF ( length.GT.len(out_field%attributes(this_attribute)%catt) )
THEN
2371 IF ( fms_error_handler(
'diag_util_mod::prepend_attribute_field',&
2372 &
'Prepend length for attribute "'//trim(att_name)//
'" is longer than allowed.',&
2378 out_field%attributes(this_attribute)%catt =&
2379 & trim(prepend_value)//
' '//trim(out_field%attributes(this_attribute)%catt)
2380 out_field%attributes(this_attribute)%len = length
2385 TYPE(file_type),
INTENT(inout) :: out_file
2386 CHARACTER(LEN=*),
INTENT(out),
OPTIONAL :: err_msg
2391 IF (
PRESENT(err_msg) ) err_msg =
''
2394 IF ( .NOT.
allocated(out_file%attributes) )
THEN
2395 ALLOCATE(out_file%attributes(max_field_attributes), stat=istat)
2396 IF ( istat.NE.0 )
THEN
2400 IF ( fms_error_handler(
'diag_util_mod::attribute_init_file', &
2401 &
'Unable to allocate memory for file attributes', err_msg) )
THEN
2406 out_file%num_attributes = 0
2414 TYPE(file_type),
INTENT(inout) :: out_file
2415 CHARACTER(len=*),
INTENT(in) :: att_name
2416 CHARACTER(len=*),
INTENT(in) :: prepend_value
2417 CHARACTER(len=*),
INTENT(out) ,
OPTIONAL :: err_msg
2419 INTEGER :: length, i, this_attribute
2420 CHARACTER(len=512) :: err_msg_local
2424 IF (
PRESENT(err_msg) ) err_msg =
''
2428 IF ( trim(err_msg_local) .NE.
'' )
THEN
2429 IF ( fms_error_handler(
'diag_util_mod::prepend_attribute_file', trim(err_msg_local), err_msg) )
THEN
2436 DO i=1, out_file%num_attributes
2437 IF ( trim(out_file%attributes(i)%name) .EQ. trim(att_name) )
THEN
2443 IF ( this_attribute > 0 )
THEN
2444 IF ( out_file%attributes(this_attribute)%type .NE. nf90_char )
THEN
2448 IF ( fms_error_handler(
'diag_util_mod::prepend_attribute_file',&
2449 &
'Attribute "'//trim(att_name)//
'" is not a character attribute.',&
2457 this_attribute = out_file%num_attributes + 1
2458 IF ( this_attribute .GT. max_file_attributes )
THEN
2463 IF ( fms_error_handler(
'diag_util_mod::prepend_attribute_file',&
2464 &
'Number of attributes exceeds max_file_attributes for attribute "'&
2465 &//trim(att_name)//
'". Increase diag_manager_nml:max_file_attributes.',&
2470 out_file%num_attributes = this_attribute
2472 out_file%attributes(this_attribute)%name = att_name
2473 out_file%attributes(this_attribute)%type = nf90_char
2475 out_file%attributes(this_attribute)%catt =
''
2480 IF ( index(trim(out_file%attributes(this_attribute)%catt), trim(prepend_value)).EQ.0 )
THEN
2482 length = len_trim(trim(prepend_value)//
" "//trim(out_file%attributes(this_attribute)%catt))
2483 IF ( length.GT.len(out_file%attributes(this_attribute)%catt) )
THEN
2487 IF ( fms_error_handler(
'diag_util_mod::prepend_attribute_file',&
2488 &
'Prepend length for attribute "'//trim(att_name)//
'" is longer than allowed.',&
2494 out_file%attributes(this_attribute)%catt =&
2495 & trim(prepend_value)//
' '//trim(out_file%attributes(this_attribute)%catt)
2496 out_file%attributes(this_attribute)%len = length
2504 integer,
intent(in) :: file_num
2506 TYPE(time_type) :: start_time
2508 start_time = files(file_num)%start_time
2510 END MODULE diag_util_mod
subroutine, public get_compressed_axes_ids(id, r)
given an index of compressed-by-gathering axis, return an array of axes used in compression....
subroutine, public get_diag_axis(id, name, units, long_name, cart_name, direction, edges, Domain, DomainU, array_data, num_attributes, attributes, domain_position)
Return information about the axis with index ID.
subroutine, public get_diag_axis_cart(id, cart_name)
Return the axis cartesian.
integer function, public get_axis_global_length(id)
Return the global length of the axis.
type(domain2d) function, public get_domain2d(ids)
Return the 2D domain for the axis IDs given.
type(domainug) function, public get_domainug(id)
Retrun the 1D domain for the axis ID given.
subroutine, public get_diag_axis_data(id, axis_data)
Return the axis data.
character(len=128) function, public get_axis_reqfld(id)
Return the required field names for the axis.
subroutine, public get_diag_axis_name(id, axis_name)
Return the short name of the axis.
character(len=128) function, public get_axis_aux(id)
Return the auxiliary name for the axis.
integer function, public diag_axis_init(name, array_data, units, cart_name, long_name, direction, set_name, edges, Domain, Domain2, DomainU, aux, req, tile_count, domain_position)
Initialize the axis, and return the axis ID.
logical function, public axis_is_compressed(id)
given an axis, returns TRUE if the axis uses compression-by-gathering: that is, if this is an axis fo...
subroutine, public get_axes_shift(ids, ishift, jshift)
Return the value of the shift for the axis IDs given.
subroutine, public get_diag_axis_domain_name(id, name)
Return the name of the axis' domain.
type(domain1d) function, public get_domain1d(id)
Retrun the 1D domain for the axis ID given.
integer function, public diag_subaxes_init(axis, subdata, start_indx, end_indx, domain_2d)
Create a subaxis on a parent axis.
integer max_output_fields
Maximum number of output fields. Increase via diag_manager_nml.
integer pack_size
1 for double and 2 for float
integer function get_base_minute()
gets the module variable base_minute
integer function get_base_year()
gets the module variable base_year
integer num_input_fields
Number of input fields in use.
integer function get_base_hour()
gets the module variable base_hour
logical use_cmor
Indicates if we should overwrite the MISSING_VALUE to use the CMOR missing value.
logical flush_nc_files
Control if diag_manager will force a flush of the netCDF file on each write. Note: changing this to ....
integer, parameter glo_reg_val_alt
Alternate value used in the region specification of the diag_table to indicate to use the full axis i...
type(time_type) function get_base_time()
gets the module variable base_time
integer, parameter diag_field_not_found
Return value for a diag_field that isn't found in the diag_table.
integer max_out_per_in_field
Maximum number of output_fields per input_field. Increase via diag_manager_nml.
integer max_input_fields
Maximum number of input fields. Increase via diag_manager_nml.
logical region_out_use_alt_value
Will determine which value to use when checking a regional output if the region is the full axis or a...
integer num_output_fields
Number of output fields in use.
integer function get_base_day()
gets the module variable base_day
type(time_type) diag_init_time
Time diag_manager_init called. If init_time not included in diag_manager_init call,...
integer max_files
Maximum number of output files allowed. Increase via diag_manager_nml.
integer num_files
Number of output files currenly in use by the diag_manager.
integer max_file_attributes
Maximum number of user definable global attributes per file.
real(r8_kind), parameter cmor_missing_value
CMOR standard missing value.
logical prepend_date
Should the history file have the start date prepended to the file name. .TRUE. is only supported if t...
character(len=2), dimension(:), allocatable fnum_for_domain
If this file number in the array is for the "unstructured" or "2d" domain.
integer function get_base_month()
gets the module variable base_month
integer, parameter glo_reg_val
Value used in the region specification of the diag_table to indicate to use the full axis instead of ...
integer function get_base_second()
gets the module variable base_second
integer, parameter max_fields_per_file
Maximum number of fields per file.
integer max_field_attributes
Maximum number of user definable attributes per field. Liptak: Changed from 2 to 4 20170718.
Define the region for field output.
Type to hold the output field description.
subroutine, public get_local_indexes(latStart, latEnd, lonStart, lonEnd, istart, iend, jstart, jend)
Find the local start and local end indexes on the local PE for regional output.
subroutine, public diag_field_write(varname, buffer, static, file_num, fileobjU, fileobj, fileobjND, fnum_for_domain, time_in)
Writes diagnostic data out using fms2_io routine.
subroutine, public diag_output_init(file_name, file_title, file_unit, domain, domainU, fileobj, fileobjU, fileobjND, fnum_domain, attributes)
Opens the output file.
subroutine, public diag_flush(file_num, fileobjU, fileobj, fileobjND, fnum_for_domain)
Flushes the file into disk.
subroutine, public done_meta_data(file_unit)
Writes axis data to file.
subroutine, public diag_write_time(fileob, rtime_value, time_index, time_name)
Writes the time data to the history file.
subroutine, public write_axis_meta_data(file_unit, axes, fileob, time_ops, time_axis_registered)
Write the axis meta data to file.
type(diag_fieldtype) function, public write_field_meta_data(file_unit, name, axes, units, long_name, range, pack, mval, avg_name, time_method, standard_name, interp_method, attributes, num_attributes, use_UGdomain, fileob)
Write the field meta data to file.
subroutine, public get_subfield_size(axes, outnum)
Get the size, start, and end indices for output fields.
subroutine opening_file(file, time, filename_time)
Open file for output, and write the meta data.
subroutine, public init_input_field(module_name, field_name, tile_count)
Initialize the input field.
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, filename_time_bounds)
Initialize the output file.
subroutine, public check_bounds_are_exact_static(out_num, diag_field_id, err_msg)
Check if the array indices for output_fields(out_num) are equal to the output_fields(out_num)buffer u...
integer function, public find_input_field(module_name, field_name, tile_count)
Return the field number for the given module name, field name, and tile number.
subroutine, public write_static(file)
Output all static fields in this file.
subroutine, public diag_data_out(file, field, dat, time, final_call_in, static_write_in, filename_time)
Write data out to file, and if necessary flush the buffers.
subroutine, public init_output_field(module_name, field_name, output_name, output_file, time_method, pack, tile_count, local_coord)
Initialize the output field.
type(time_type) function, public get_file_start_time(file_num)
Get the a diag_file's start_time as it is defined in the diag_table.
logical function a_lessthan_b(a, b)
return true iff a<b.
integer function find_file(name, tile_count)
Return the file number for file name and tile.
subroutine, public check_duplicate_output_fields(err_msg)
Checks to see if output_name and output_file are unique in output_fields.
subroutine, public sync_file_times(file_id, init_time, err_msg)
Synchronize the file's start and close times with the model start and end times.
subroutine fms_diag_check_out_of_bounds_r8(ofb, bounds, output_name, module_name, err_msg)
Checks if the array indices for output_field buffer (ofb) are outside the are outside the bounding bo...
character(len=1), public field_log_separator
separator used for csv-style log of registered fields set by nml in diag_manager init
subroutine, public check_out_of_bounds(out_num, diag_field_id, err_msg)
Checks if the array indices for output_fields(out_num) are outside the output_fields(out_num)buffer u...
subroutine, public fms_diag_check_bounds_are_exact_static(current_bounds, bounds, output_name, module_name, err_msg)
Check if the array indices specified in the bounding box "current_bounds" are equal to those specifie...
logical function a_greaterthan_b(a, b)
return true iff a>b.
subroutine, public get_subfield_vert_size(axes, outnum)
Get size, start and end indices for output fields.
logical function compare_buffer_bounds_to_size(current_bounds, bounds, error_str, lowerb_comp, upperb_comp)
Compares the bounding indices of an array specified in "current_bounds" to the corresponding lower an...
subroutine, public log_diag_field_info(module_name, field_name, axes, long_name, units, missing_value, range, dynamic)
Writes brief diagnostic field info to the log file.
subroutine, public update_bounds(out_num, lower_i, upper_i, lower_j, upper_j, lower_k, upper_k)
Update the output_fields x, y, and z min and max boundaries (array indices) with the six specified bo...
subroutine prepend_attribute_file(out_file, att_name, prepend_value, err_msg)
Prepends the attribute value to an already existing attribute. If the attribute isn't yet defined,...
subroutine prepend_attribute_field(out_field, att_name, prepend_value, err_msg)
Prepends the attribute value to an already existing attribute. If the attribute isn't yet defined,...
subroutine, public diag_util_init()
Write the version number of this file to the log file.
subroutine attribute_init_file(out_file, err_msg)
Allocates the atttype in out_file.
logical function a_noteq_b(a, b)
return true iff a /= b
subroutine attribute_init_field(out_field, err_msg)
Allocates the atttype in out_field.
subroutine check_and_open(file, time, do_write, filename_time)
Checks if it is time to open a new file.
subroutine, public check_bounds_are_exact_dynamic(out_num, diag_field_id, Time, err_msg)
This is an adaptor to the check_bounds_are_exact_dynamic_modern function to maintain an interface ser...
subroutine, public fms_diag_check_bounds_are_exact_dynamic(current_bounds, bounds, output_name, module_name, Time, field_prev_Time, err_msg)
Checks that array indices specified in the bounding box "current_bounds" are identical to those in th...
integer function get_index(number, array)
Find index i of array such that array(i) is closest to number.
subroutine fms_diag_check_out_of_bounds_r4(ofb, bounds, output_name, module_name, err_msg)
Checks if the array indices for output_fields(out_num) are outside the output_fields(out_num)buffer u...
Allocates the atttype in out_file.
Prepend a value to a string attribute in the output field or output file.
character(len=128) function, public get_time_string(filename, current_time)
This function determines a string based on current time. This string is used as suffix in output file...
real function, public get_date_dif(t2, t1, units)
Return the difference between two times in units.
subroutine, public write_version_number(version, tag, unit)
Prints to the log file (or a specified unit) the version id string and tag name.
logical function, public fms_error_handler(routine, message, err_msg)
Facilitates the control of fatal error conditions.
subroutine, public error_mesg(routine, message, level)
Print notes, warnings and error messages; terminates program for warning and error messages....
integer function mpp_get_tile_npes(domain)
Returns number of processors used on current tile.
subroutine mpp_get_domain_components(domain, x, y, tile_count)
Retrieve 1D components of 2D decomposition.
integer function, dimension(size(domain%tile_id(:))) mpp_get_tile_id(domain)
Returns the tile_id on current pe.
logical function mpp_mosaic_defined()
Accessor function for value of mosaic_defined.
integer function mpp_get_current_ntile(domain)
Returns number of tile on current pe.
integer function mpp_get_ntile_count(domain)
Returns number of tiles in mosaic.
These routines retrieve the axis specifications associated with the compute domains....
Modifies the extents (compute, data and global) of a given domain.
One dimensional domain used to manage shared data access between pes.
The domain2D type contains all the necessary information to define the global, compute and data domai...
Domain information for managing data on unstructured grids.
integer function mpp_npes()
Returns processor count for current pelist.
integer function mpp_pe()
Returns processor ID.
logical function, public leap_year(Time, err_msg)
Returns true if the year corresponding to the input time is a leap year (for default calendar)....
subroutine, public get_time(Time, seconds, days, ticks, err_msg)
Returns days and seconds ( < 86400 ) corresponding to a time. err_msg should be checked for any error...
subroutine, public get_date(time, year, month, day, hour, minute, second, tick, err_msg)
Gets the date for different calendar types. Given a time_interval, returns the corresponding date und...
type(time_type) function, public increment_time(Time, seconds, days, ticks, err_msg, allow_neg_inc)
Increments a time by seconds and days.
integer function, public get_calendar_type()
Returns default calendar type for mapping from time to date.
Type to represent amounts of time. Implemented as seconds and days to allow for larger intervals.
Data structure holding a 3D bounding box. It is commonlyused to represent the interval bounds or limi...