diff --git a/CMakeLists.txt b/CMakeLists.txt index df13f943a7..7bda3b1d1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -314,6 +314,7 @@ foreach(kind ${kinds}) constants axis_utils/include field_manager/include + time_interp/include tracer_manager/include) target_compile_definitions(${libTgt}_f PRIVATE "${fms_defs}") diff --git a/horiz_interp/horiz_interp_type.F90 b/horiz_interp/horiz_interp_type.F90 index ec2773f860..7f8b300a99 100644 --- a/horiz_interp/horiz_interp_type.F90 +++ b/horiz_interp/horiz_interp_type.F90 @@ -81,7 +81,7 @@ module horiz_interp_type_mod real(kind=r8_kind), dimension(:), allocatable :: area_frac_dst !< area fraction in destination grid. real(kind=r8_kind), dimension(:,:), allocatable :: mask_in real(kind=r8_kind) :: max_src_dist - logical :: is_allocated !< set to true upon field allocation + logical :: is_allocated = .false. !< set to true upon field allocation end type horizInterpReals8_type @@ -108,7 +108,7 @@ module horiz_interp_type_mod real(kind=r4_kind), dimension(:), allocatable :: area_frac_dst !< area fraction in destination grid. real(kind=r4_kind), dimension(:,:), allocatable :: mask_in real(kind=r4_kind) :: max_src_dist - logical :: is_allocated !< set to true upon field allocation + logical :: is_allocated = .false. !< set to true upon field allocation end type horizInterpReals4_type diff --git a/test_fms/time_interp/Makefile.am b/test_fms/time_interp/Makefile.am index 4299cd0e43..92156b6841 100644 --- a/test_fms/time_interp/Makefile.am +++ b/test_fms/time_interp/Makefile.am @@ -29,11 +29,20 @@ AM_CPPFLAGS = -I$(MODDIR) LDADD = $(top_builddir)/libFMS/libFMS.la # Build these test programs. -check_PROGRAMS = test_time_interp test_time_interp_external +check_PROGRAMS = test_time_interp_r4 test_time_interp_r8 test_time_interp_external_r4 test_time_interp_external_r8 # These are the sources for the tests. -test_time_interp_SOURCES = test_time_interp.F90 -test_time_interp_external_SOURCES = test_time_interp_external.F90 +test_time_interp_r4_SOURCES = test_time_interp.F90 +test_time_interp_r8_SOURCES = test_time_interp.F90 +test_time_interp_external_r4_SOURCES = test_time_interp_external.F90 +test_time_interp_external_r8_SOURCES = test_time_interp_external.F90 + +# filter out any added precision flags +# adds r8 flag, otherwise no-flag default is 4 +test_time_interp_r4_CPPFLAGS=-DTI_TEST_KIND_=4 -I$(MODDIR) +test_time_interp_r8_CPPFLAGS=-DTI_TEST_KIND_=8 -I$(MODDIR) +test_time_interp_external_r4_CPPFLAGS=-DTI_TEST_KIND_=4 -I$(MODDIR) +test_time_interp_external_r8_CPPFLAGS=-DTI_TEST_KIND_=8 -I$(MODDIR) # Run the test programs. TESTS = test_time_interp2.sh diff --git a/test_fms/time_interp/test_time_interp.F90 b/test_fms/time_interp/test_time_interp.F90 index 543d9992f0..110597b8e0 100644 --- a/test_fms/time_interp/test_time_interp.F90 +++ b/test_fms/time_interp/test_time_interp.F90 @@ -23,19 +23,24 @@ program test_time_interp use time_manager_mod, only: get_date, set_time, set_date, time_manager_init, set_calendar_type, operator(+) use time_manager_mod, only: JULIAN, time_type, increment_time, NOLEAP, print_date use time_interp_mod, only: time_interp_init, time_interp, NONE, YEAR, MONTH, DAY + use time_manager_mod, only: operator(<=), operator(>=), operator(==) + use platform_mod implicit none - integer, parameter :: num_Time=6 + integer, parameter :: num_Time=6, kindl = TI_TEST_KIND_ type(time_type) :: Time_beg, Time_end, Time(num_Time) type(time_type), allocatable, dimension(:) :: Timelist - integer :: index1, index2, mo, yr, timelist_len, outunit, ntest, nline - real :: weight + integer :: index1, index2, mo, yr, outunit, ntest, nline + real(TI_TEST_KIND_) :: weight + real(TI_TEST_KIND_) :: ref_weights(num_Time), ref_weights_leap(num_Time) + real(TI_TEST_KIND_), parameter :: SMALL = 1.0e-7_kindl ! r4 will fail with 8 + real(TI_TEST_KIND_), parameter :: midpoint = 0.483870967741935_kindl + real(TI_TEST_KIND_), parameter :: day_before_leap_day = 0.964285714285714_kindl + real(TI_TEST_KIND_), parameter :: day_before_leap_day_with_ly = 0.931034482758621_kindl integer :: nmin, nmax - namelist / test_time_interp_nml / timelist_len - call fms_init outunit = stdout() call set_calendar_type(JULIAN) @@ -50,8 +55,23 @@ program test_time_interp Time(5) = set_date(1,12,16) Time(6) = Time_end + ref_weights(1) = 0.0 ! on 'edge' (timeList value) + ref_weights(2) = midpoint ! rough midpoint of a month ie. jan 16 + ref_weights(3) = 0.0 + ref_weights(4) = 0.0 + ref_weights(5) = midpoint + ref_weights(6) = 0.0 + + ref_weights_leap(1) = 0.0 ! on 'edge' (timeList value) + ref_weights_leap(2) = day_before_leap_day ! feb 28th + ref_weights_leap(3) = midpoint + ref_weights_leap(4) = 0.0 + ref_weights_leap(5) = day_before_leap_day + ref_weights_leap(6) = day_before_leap_day ! checks that 29th gives same result + ! Tests with modulo time do nline=1,3 + if(nline == 1) then allocate(Timelist(12)) do mo=1,12 @@ -72,6 +92,7 @@ program test_time_interp endif do ntest=1,num_Time + print *, ntest call diagram(nline,ntest,modulo_time=.true.) call time_interp(Time(ntest), Time_beg, Time_end, Timelist, weight, index1, index2) write(outunit,*) 'time_interp_modulo:' @@ -84,6 +105,11 @@ program test_time_interp write(outunit,99) index1,index2,weight write(outunit,'()') + if(.not. is_valid_indices(index1, index2, Timelist, Time(ntest), weight, YEAR)) & + call mpp_error(FATAL, "test_time_interp: invalid indices from time_interp_timelist") + if(abs(weight - ref_weights(ntest)) .gt. SMALL) & + call mpp_error(FATAL, "test_time_interp: incorrect weight value with reference") + call time_interp(Time(ntest), Timelist, weight, index1, index2, modtime=YEAR) write(outunit,*) 'time_interp_list with modtime=YEAR:' write(outunit,'()') @@ -91,10 +117,18 @@ program test_time_interp call print_date(Timelist(1), 'Timelist(1)=') call print_date(Timelist(size(Timelist(:))),'Timelist(n)=') write(outunit,99) index1,index2,weight + + if(.not. is_valid_indices(index1, index2, Timelist, Time(ntest), weight, YEAR)) & + call mpp_error(FATAL, "test_time_interp: invalid indices from time_interp_modulo") + if(abs(weight - ref_weights(ntest)) .gt. SMALL) & + call mpp_error(FATAL, "test_time_interp: incorrect weight value with reference") + enddo deallocate(Timelist) enddo + + ! Tests without modulo time do nline=1,3 if(nline == 1) then @@ -132,6 +166,12 @@ program test_time_interp call print_date(Timelist(1), 'Timelist(1)=') call print_date(Timelist(size(Timelist(:))),'Timelist(n)=') write(outunit,99) index1,index2,weight + + if( .not. is_valid_indices(index1, index2, TimeList, Time(ntest), weight, NONE)) & + call mpp_error(FATAL, "invalid result without modtime") + if(abs(weight - ref_weights(ntest)) .gt. SMALL) & + call mpp_error(FATAL, "test_time_interp: incorrect weight value with reference") + enddo deallocate(Timelist) enddo @@ -171,9 +211,20 @@ program test_time_interp call print_date(Time(ntest),' Time =') write(outunit,99) index1,index2,weight write(outunit,'()') + if( .not. is_valid_indices(index1, index2, Timelist, Time(ntest), weight, YEAR)) & + call mpp_error(FATAL, 'invalid results for indices with leap year correction') + if(abs(weight - ref_weights_leap(ntest)) .gt. SMALL) & + call mpp_error(FATAL, "test_time_interp: incorrect weight value with reference") enddo deallocate(Timelist) + ! swap around ref numbers for different data set + ref_weights_leap(1) = day_before_leap_day + ref_weights_leap(2) = day_before_leap_day ! feb 28th + ref_weights_leap(3) = 0.0 + ref_weights_leap(4) = day_before_leap_day_with_ly + ref_weights_leap(5) = 0.0 + ref_weights_leap(6) = 0.0 ! Tests of modulo time and leap year inconsistency Time_beg = set_date(1978, 1, 1) Time_end = set_date(1981, 1, 1) @@ -210,6 +261,10 @@ program test_time_interp call print_date(Time(ntest),' Time=') write(outunit,99) index1,index2,weight write(outunit,'()') + if( .not. is_valid_indices(index1, index2, Timelist, Time(ntest), weight, YEAR)) & + call mpp_error(FATAL, 'invalid results for indices with leap year correction') + if(abs(weight - ref_weights_leap(ntest)) .gt. SMALL) & + call mpp_error(FATAL, "test_time_interp: incorrect weight value with reference") enddo deallocate(Timelist) @@ -297,4 +352,30 @@ subroutine diagram(nline,ntest,modulo_time) end subroutine diagram - end program test_time_interp + !> helper function to check results + !! true if invalid , false for valid + logical function is_valid_indices(ind1, ind2, tList, tintv, res_weight, mtime) + integer, intent(in) :: ind1, ind2 + type(time_type), intent(in) :: tList(:), tintv + real(TI_TEST_KIND_), intent(in) :: res_weight + integer, intent(in) :: mtime + integer :: i + + ! modulo_time determines wrap around + if( mtime .eq. NONE) then + if (ind1 .eq. SIZE(tList)) then + is_valid_indices = ind2 .eq. ind1 + else + is_valid_indices = ind2 .eq. ind1+1 + endif + else ! YEAR, default + if (ind1 .eq. 12 ) then + is_valid_indices = ind2 .eq. 1 + else + is_valid_indices = ind2 .eq. ind1+1 + endif + endif + + end function is_valid_indices + + end program test_time_interp \ No newline at end of file diff --git a/test_fms/time_interp/test_time_interp2.sh b/test_fms/time_interp/test_time_interp2.sh index d111178dc5..0f30909e7e 100755 --- a/test_fms/time_interp/test_time_interp2.sh +++ b/test_fms/time_interp/test_time_interp2.sh @@ -22,7 +22,7 @@ # This is part of the GFDL FMS package. This is a shell script to # execute tests in the test_fms/mpp directory. -# Ed Hartnett 11/29/19 +# Ed Hartnett 11/49/19 # Set common test settings. . ../test-lib.sh @@ -33,14 +33,36 @@ touch input.nml rm -rf INPUT mkdir INPUT # Run the test. -test_expect_success "test time interpolation" ' - mpirun -n 2 ./test_time_interp +test_expect_success "test time interpolation with r8_kind" ' + mpirun -n 4 ./test_time_interp_r8 ' +test_expect_success "test time interpolation with r4_kind" ' + mpirun -n 4 ./test_time_interp_r4 +' + rm -rf INPUT mkdir INPUT -test_expect_success "test time interpolation external" ' - mpirun -n 2 ./test_time_interp_external +# nml for calender type +cat <<_EOF > input.nml +&test_time_interp_external_nml +cal_type="julian" +/ +_EOF + +test_expect_success "test time interpolation external with r8_kind (julian)" ' + mpirun -n 4 ./test_time_interp_external_r8 +' +test_expect_success "test time interpolation external with r4_kind (julian)" ' + mpirun -n 4 ./test_time_interp_external_r4 +' +sed -i 's/julian/no_leap/' input.nml + +test_expect_success "test time interpolation external with r8_kind (no_leap)" ' + mpirun -n 4 ./test_time_interp_external_r8 +' +test_expect_success "test time interpolation external with r4_kind (no_leap)" ' + mpirun -n 4 ./test_time_interp_external_r4 ' test_done diff --git a/test_fms/time_interp/test_time_interp_external.F90 b/test_fms/time_interp/test_time_interp_external.F90 index ddecbdd636..c933dbddca 100644 --- a/test_fms/time_interp/test_time_interp_external.F90 +++ b/test_fms/time_interp/test_time_interp_external.F90 @@ -43,31 +43,31 @@ program test_time_interp_external integer :: id !< Time_interp_external id integer :: i !< Index for loops +integer :: ierr !< io return status character(len=128) :: filename='INPUT/aerosol.climatology.nc' character(len=128) :: fieldname='so4_anthro' type(time_type) :: time !< "model" time -real, allocatable :: data_d(:,:,:) !< interpolated data in compute domain -real, allocatable :: data_g(:,:,:) !< interpolated global data +integer, parameter :: kindl = TI_TEST_KIND_ +real(TI_TEST_KIND_) :: data_d_0d = 1.0_kindl !< interpolated data in compute domain +real(TI_TEST_KIND_) :: data_g_0d = 1.0_kindl !< interpolated global data type(domain2d) :: domain ! -! -! -! !> @brief Calculates the fractional time into the current year - subroutine time_interp_frac ( Time, weight ) + subroutine TIME_INTERP_FRAC_ ( Time, weight ) - type(time_type), intent(in) :: Time - real , intent(out) :: weight !< fractional time + type(time_type), intent(in) :: Time + real(FMS_TI_KIND_), intent(out) :: weight !< fractional time integer :: yr, mo, dy, hour, minute, second type(time_type) :: Year_beg, Year_end @@ -300,43 +39,17 @@ contains Year_beg = set_date(yr , 1, 1) Year_end = set_date(yr+1, 1, 1) - weight = real( (Time - Year_beg) // (Year_end - Year_beg) ) - - end subroutine time_interp_frac - -!####################################################################### -! -! -! Wrapper for backward compatibility -! -! + weight = real( (Time - Year_beg) // (Year_end - Year_beg) , kind=FMS_TI_KIND_) -!> @brief Wrapper function to return the fractional time into the current year -!> @param Time time to calculate fraction with -!> @return real fraction of time passed in current year - function fraction_of_year (Time) - type(time_type), intent(in) :: Time - real :: fraction_of_year + end subroutine TIME_INTERP_FRAC_ - call time_interp_frac ( Time, fraction_of_year ) - - end function fraction_of_year - -!####################################################################### -! -! -! -! -! -! -! returns fractional time between mid points of consecutive years !> @brief Calculates fractional time between mid points of consecutive years - subroutine time_interp_year ( Time, weight, year1, year2 ) + subroutine TIME_INTERP_YEAR_ ( Time, weight, year1, year2 ) - type(time_type), intent(in) :: Time - real , intent(out) :: weight !< fractional time between midpoints of year1 and year2 - integer , intent(out) :: year1, year2 + type(time_type), intent(in) :: Time + real(FMS_TI_KIND_), intent(out) :: weight !< fractional time between midpoints of year1 and year2 + integer , intent(out) :: year1, year2 integer :: yr, mo, dy, hour, minute, second type (time_type) :: Mid_year, Mid_year1, Mid_year2 @@ -354,31 +67,22 @@ contains year1 = yr year2 = yr+1 Mid_year2 = year_midpt(year2) - weight = real( (Time - Mid_year) // (Mid_year2 - Mid_year) ) + weight = real( (Time - Mid_year) // (Mid_year2 - Mid_year) , kind=FMS_TI_KIND_ ) else ! current time is before mid point of current year year2 = yr year1 = yr-1 Mid_year1 = year_midpt(year1) - weight = real( (Time - Mid_year1) // (Mid_year - Mid_year1) ) + weight = real( (Time - Mid_year1) // (Mid_year - Mid_year1), kind=FMS_TI_KIND_ ) endif - end subroutine time_interp_year - -!####################################################################### -! -! -! -! -! -! -! -! + end subroutine TIME_INTERP_YEAR_ + !> @brief Calculates fractional time between mid points of consecutive months - subroutine time_interp_month ( Time, weight, year1, year2, month1, month2 ) + subroutine TIME_INTERP_MONTH_ ( Time, weight, year1, year2, month1, month2 ) type(time_type), intent(in) :: Time - real , intent(out) :: weight + real(FMS_TI_KIND_) , intent(out) :: weight integer , intent(out) :: year1, year2, month1, month2 integer :: yr, mo, dy, hour, minute, second, & @@ -402,7 +106,7 @@ contains endif mid1 = mid_month mid2 = days_in_month(set_date(year2,month2,2)) * halfday - weight = real(cur_month - mid1) / real(mid1+mid2) + weight = real(cur_month - mid1, FMS_TI_KIND_) / real(mid1+mid2, FMS_TI_KIND_) else ! current time is before mid point of current month year2 = yr; month2 = mo @@ -419,27 +123,16 @@ contains mid1 = days_in_month(set_date(1,month1,2)) * halfday endif mid2 = mid_month - weight = real(cur_month + mid1) / real(mid1+mid2) + weight = real(cur_month + mid1, FMS_TI_KIND_) / real(mid1+mid2, FMS_TI_KIND_) endif - end subroutine time_interp_month - -!####################################################################### -! -! -! -! -! -! -! -! -! -! + end subroutine TIME_INTERP_MONTH_ + !> @brief Calculates fractional time between mid points of consecutive days - subroutine time_interp_day ( Time, weight, year1, year2, month1, month2, day1, day2 ) + subroutine TIME_INTERP_DAY_ ( Time, weight, year1, year2, month1, month2, day1, day2 ) type(time_type), intent(in) :: Time - real , intent(out) :: weight + real(FMS_TI_KIND_), intent(out) :: weight integer , intent(out) :: year1, year2, month1, month2, day1, day2 integer :: yr, mo, dy, hour, minute, second, sday @@ -455,7 +148,7 @@ contains ! current time is after mid point of day year1 = yr; month1 = mo; day1 = dy year2 = yr; month2 = mo; day2 = dy + 1 - weight = real(sday - halfday) / real(secday) + weight = real(sday - halfday, FMS_TI_KIND_) / real(secday, FMS_TI_KIND_) if (day2 > days_in_month(Time)) then month2 = month2 + 1 @@ -468,7 +161,7 @@ contains ! current time is before mid point of day year2 = yr; month2 = mo ; day2 = dy year1 = yr; month1 = mo; day1 = dy - 1 - weight = real(sday + halfday) / real(secday) + weight = real(sday + halfday,FMS_TI_KIND_) / real(secday,FMS_TI_KIND_) if (day1 < 1) then month1 = month1 - 1 @@ -479,32 +172,20 @@ contains endif endif - end subroutine time_interp_day - -!####################################################################### -! -! -! -! -! -! -! Turns on a kluge for an inconsistency which may occur in a special case. -! When the modulo time period (i.e. Time_end - Time_beg) is a whole number of years -! and is not a multiple of 4, and the calendar in use has leap years, then it is -! likely that the interpolation will involve mapping a common year onto a leap year. -! In this case it is often desirable, but not absolutely necessary, to use data for -! Feb 28 of the leap year when it is mapped onto a common year. -! To turn this on, set correct_leap_year_inconsistency=.true. -! -! -! -! - -subroutine time_interp_modulo(Time, Time_beg, Time_end, Timelist, weight, index1, index2, & + end subroutine TIME_INTERP_DAY_ + + !> Part of the time_interp interface, calculates for cyclical data + !! Time_beg and Time_end mark a repeating period + !! + !! Finds mid points and fractional weight for a time perioid +subroutine TIME_INTERP_MODULO_(Time, Time_beg, Time_end, Timelist, weight, index1, index2, & correct_leap_year_inconsistency, err_msg) -type(time_type), intent(in) :: Time, Time_beg, Time_end, Timelist(:) -real , intent(out) :: weight -integer , intent(out) :: index1, index2 +type(time_type), intent(in) :: Time !< a specific time value +type(time_type), intent(in) :: Time_beg !< begining of period to search with +type(time_type), intent(in) :: Time_end !< end of period to search with +type(time_type), intent(in) :: Timelist(:) !< ascending time values to search between +real(FMS_TI_KIND_) , intent(out) :: weight +integer , intent(out) :: index1, index2 !< indices of bounding time values within Timelist logical, intent(in), optional :: correct_leap_year_inconsistency!< When true turns on a kluge for an !! inconsistency which may occur in a special case. !! When the modulo time period (i.e. Time_end - Time_beg) is a @@ -524,6 +205,7 @@ character(len=*), intent(out), optional :: err_msg integer :: n ! size of Timelist integer :: stdoutunit logical :: correct_lyr, calendar_has_leap_years, do_the_lyr_correction + integer, parameter :: kindl = FMS_TI_KIND_ if ( .not. module_is_initialized ) call time_interp_init if( present(err_msg) ) err_msg = '' @@ -653,68 +335,21 @@ character(len=*), intent(out), optional :: err_msg if( T>=Timelist(ie) ) then ! time is after the end of the portion of the time list within the requested period index1 = ie; index2 = is - weight = real( (T-Timelist(ie))//(Period-(Timelist(ie)-Timelist(is))) ) + weight = real((T-Timelist(ie))//(Period-(Timelist(ie)-Timelist(is))), FMS_TI_KIND_ ) else if (T Given an array of times in ascending order and a specific time returns -!! values of index1 and index2 such that the Timelist(index1)<=Time and -!! Time<=Timelist(index2), and index2=index1+1 -!! index1=0, index2=1 or index=n, index2=n+1 are returned to indicate that -!! the time is out of range -subroutine bisect(Timelist,Time,index1,index2) - type(time_type) , intent(in) :: Timelist(:) - type(time_type) , intent(in) :: Time - integer, optional, intent(out) :: index1, index2 - - integer :: i,il,iu,n,i1,i2 +end subroutine TIME_INTERP_MODULO_ - n = size(Timelist(:)) - - if (Time==Timelist(1)) then - i1 = 1 ; i2 = 2 - else if (Time==Timelist(n)) then - i1 = n ; i2 = n+1 - else - il = 0; iu=n+1 - do while(iu-il > 1) - i = (iu+il)/2 - if(Timelist(i) > Time) then - iu = i - else - il = i - endif - enddo - i1 = il ; i2 = il+1 - endif - - if(PRESENT(index1)) index1 = i1 - if(PRESENT(index2)) index2 = i2 -end subroutine bisect - - -!####################################################################### -! -! -! -! -! -! -! -! - -subroutine time_interp_list ( Time, Timelist, weight, index1, index2, modtime, err_msg ) +subroutine TIME_INTERP_LIST_ ( Time, Timelist, weight, index1, index2, modtime, err_msg ) type(time_type) , intent(in) :: Time, Timelist(:) -real , intent(out) :: weight +real(FMS_TI_KIND_) , intent(out) :: weight integer , intent(out) :: index1, index2 integer, optional, intent(in) :: modtime character(len=*), intent(out), optional :: err_msg @@ -722,12 +357,13 @@ character(len=*), intent(out), optional :: err_msg integer :: n, hr, mn, se, mtime type(time_type) :: T, Ts, Te, Td, Period, Time_mod character(len=:),allocatable :: terr, tserr, teerr +integer, parameter :: kindl = FMS_TI_KIND_ if ( .not. module_is_initialized ) call time_interp_init if( present(err_msg) ) err_msg = '' - weight = 0.; index1 = 0; index2 = 0 + weight = 0.0_kindl; index1 = 0; index2 = 0 n = size(Timelist(:)) ! setup modular time axis? @@ -782,7 +418,7 @@ character(len=:),allocatable :: terr, tserr, teerr ! time falls on start or between start and end list values if ( T >= Ts .and. T < Te ) then call bisect(Timelist(1:n),T,index1,index2) - weight = real( (T-Timelist(index1)) // (Timelist(index2)-Timelist(index1)) ) + weight = real( (T-Timelist(index1)) // (Timelist(index2)-Timelist(index1)), FMS_TI_KIND_) ! time falls before starting list value else if ( T < Ts ) then @@ -797,18 +433,18 @@ character(len=:),allocatable :: terr, tserr, teerr deallocate(terr,tserr,teerr) endif Td = Te-Ts - weight = real( 1. - ((Ts-T) // (Period-Td)) ) + weight = 1.0_kindl - real(((Ts-T) // (Period-Td)), FMS_TI_KIND_ ) index1 = n index2 = 1 ! time falls on ending list value else if ( T == Te ) then if(perthlike_behavior) then - weight = 1.0 + weight = 1.0_kindl index1 = n-1 index2 = n else - weight = 0. + weight = 0.0_kindl index1 = n if (mtime == NONE) then index2 = n @@ -830,142 +466,10 @@ character(len=:),allocatable :: terr, tserr, teerr deallocate(terr,tserr,teerr) endif Td = Te-Ts - weight = real( (T-Te) // (Period-Td) ) + weight = real( (T-Te) // (Period-Td), FMS_TI_KIND_) index1 = n index2 = 1 endif -end subroutine time_interp_list - -!####################################################################### -! private routines -!####################################################################### - - function year_midpt (yr) - - integer, intent(in) :: yr - type (time_type) :: year_midpt, year_beg, year_end - - - year_beg = set_date(yr , 1, 1) - year_end = set_date(yr+1, 1, 1) - - year_midpt = (year_beg + year_end) / 2 - - end function year_midpt - -!####################################################################### - - function month_midpt (yr, mo) - - integer, intent(in) :: yr, mo - type (time_type) :: month_midpt, month_beg, month_end - -! --- beginning of this month --- - month_beg = set_date(yr, mo, 1) - -! --- start of next month --- - if (mo < 12) then - month_end = set_date(yr, mo+1, 1) - else - month_end = set_date(yr+1, 1, 1) - endif - - month_midpt = (month_beg + month_end) / 2 - - end function month_midpt - -!####################################################################### - -function set_modtime (Tin, modtime) result (Tout) -type(time_type), intent(in) :: Tin -integer, intent(in), optional :: modtime -type(time_type) :: Tout -integer :: yr, mo, dy, hr, mn, se, mtime - - if(present(modtime)) then - mtime = modtime - else - mtime = NONE - endif - - select case (mtime) - case (NONE) - Tout = Tin - case (YEAR) - call get_date (Tin, yr, mo, dy, hr, mn, se) - yr = yrmod - ! correct leap year dates - if (.not.mod_leapyear .and. mo == 2 .and. dy > 28) then - mo = 3; dy = dy-28 - endif - Tout = set_date (yr, mo, dy, hr, mn, se) - case (MONTH) - call get_date (Tin, yr, mo, dy, hr, mn, se) - yr = yrmod; mo = momod - Tout = set_date (yr, mo, dy, hr, mn, se) - case (DAY) - call get_date (Tin, yr, mo, dy, hr, mn, se) - yr = yrmod; mo = momod; dy = dymod - Tout = set_date (yr, mo, dy, hr, mn, se) - end select - -end function set_modtime - -!####################################################################### - -subroutine error_handler (string) -character(len=*), intent(in) :: string - - call error_mesg ('time_interp_mod', trim(string), FATAL) - -! write (*,'(a)') 'ERROR in time_interp: ' // trim(string) -! stop 111 - -end subroutine error_handler - -!####################################################################### - -end module time_interp_mod - -! - -! -! The list of input time types must have ascending dates. -! * -! -! The length of the current month for input Time and Time_list -! must be the same when using the modulo month option. -! The modulo month option is available but not supported. -! * -! -! The optional argument modtime must have a value set by one -! of the public parameters: NONE, YEAR, MONTH, DAY. -! The MONTH and DAY options are available but not supported. -! * -! -! The difference between the last and first values in the -! input Time list/array exceeds the length of the modulo period. -! * -! -! These errors occur when you are not using a modulo axis and the -! input Time occurs before the first value in the Time list/array -! or after the last value in the Time list/array. -! * -! -! For all routines in this module the calendar type in module -! time_manager must be set. -! -! -! The following private parameters are set by this module: -!
-!           seconds per minute = 60
-!           minutes per hour   = 60
-!           hours   per day    = 24
-!           months  per year   = 12
-! 
-!
- -!
-!> @} -! close documentation grouping +end subroutine TIME_INTERP_LIST_ +!> } \ No newline at end of file diff --git a/time_interp/include/time_interp_external2.inc b/time_interp/include/time_interp_external2.inc index 7716e17ea4..b4e6114e6d 100644 --- a/time_interp/include/time_interp_external2.inc +++ b/time_interp/include/time_interp_external2.inc @@ -16,699 +16,17 @@ !* You should have received a copy of the GNU Lesser General Public !* License along with FMS. If not, see . !*********************************************************************** -!> @defgroup time_interp_external2_mod time_interp_external2_mod !> @ingroup time_interp -!> @brief Perform I/O and time interpolation of external fields (contained in a file), using -!! fms2_io. -!! -!> @author M.J. Harrison -!! -!> Perform I/O and time interpolation for external fields. -!! Uses udunits library to calculate calendar dates and -!! convert units. Allows for reading data decomposed across -!! model horizontal grid using optional domain2d argument -!! -!! data are defined over data domain for domain2d data -!! (halo values are NOT updated by this module) - !> @addtogroup time_interp_external2_mod !> @{ -module time_interp_external2_mod - -! -! -! size of record dimension for internal buffer. This is useful for tuning i/o performance -! particularly for large datasets (e.g. daily flux fields) -! -! - - use platform_mod, only : DOUBLE_KIND => r8_kind - use fms_mod, only : write_version_number - use mpp_mod, only : mpp_error,FATAL,WARNING,mpp_pe, stdout, stdlog, NOTE - use mpp_mod, only : input_nml_file, mpp_npes, mpp_root_pe, mpp_broadcast, mpp_get_current_pelist - use time_manager_mod, only : time_type, get_date, set_date, operator ( >= ) , operator ( + ) , days_in_month, & - operator( - ), operator ( / ) , days_in_year, increment_time, & - set_time, get_time, operator( > ), get_calendar_type, NO_CALENDAR - use get_cal_time_mod, only : get_cal_time - use mpp_domains_mod, only : domain2d, mpp_get_compute_domain, mpp_get_data_domain, & - mpp_get_global_domain, NULL_DOMAIN2D - use time_interp_mod, only : time_interp, time_interp_init - use axis_utils2_mod, only : get_axis_cart, get_axis_modulo, get_axis_modulo_times - use fms_mod, only : lowercase, check_nml_error - use platform_mod, only: r8_kind - use horiz_interp_mod, only : horiz_interp, horiz_interp_type - use fms2_io_mod, only : Valid_t, FmsNetcdfDomainFile_t, open_file, get_unlimited_dimension_name, & - variable_att_exists, FmsNetcdfFile_t, & - variable_exists, get_valid, get_variable_num_dimensions, read_data, & - is_valid, close_file, get_dimension_size, get_variable_dimension_names, & - get_variable_size, get_time_calendar, get_variable_missing, get_variable_units - - implicit none - private - -! Include variable "version" to be written to log file. -#include - - integer, parameter, public :: NO_REGION=0, INSIDE_REGION=1, OUTSIDE_REGION=2 - integer, parameter, private :: modulo_year= 0001 - integer, parameter, private :: LINEAR_TIME_INTERP = 1 ! not used currently - integer, parameter, public :: SUCCESS = 0, ERR_FIELD_NOT_FOUND = 1 - real, parameter, private :: DEFAULT_MISSING_VALUE = -1e20 - integer, private :: max_fields = 100, max_files= 40 - integer, private :: num_fields = 0, num_files=0 - ! denotes time intervals in file (interpreted from metadata) - integer, private :: num_io_buffers = 2 ! set -1 to read all records from disk into memory - logical, private :: module_initialized = .false. - logical, private :: debug_this_module = .false. - - public init_external_field, time_interp_external, time_interp_external_init, & - time_interp_external_exit, get_external_field_size, get_time_axis, get_external_field_missing - public set_override_region, reset_src_data_region - public get_external_fileobj - - private find_buf_index,& - set_time_modulo - !> @} - - !> Represents external fields - !> @ingroup time_interp_external2_mod - type, private :: ext_fieldtype - type(FmsNetcdfFile_t), pointer :: fileobj=>NULL() !< keep unit open when not reading all records - character(len=128) :: name, units - integer :: siz(4), ndim - character(len=32) :: axisname(4) - type(domain2d) :: domain - type(time_type), dimension(:), pointer :: time =>NULL() !< midpoint of time interval - type(time_type), dimension(:), pointer :: start_time =>NULL(), end_time =>NULL() - type(time_type), dimension(:), pointer :: period =>NULL() - logical :: modulo_time !< denote climatological time axis - real, dimension(:,:,:,:), pointer :: data =>NULL() !< defined over data domain or global domain - logical, dimension(:,:,:,:), pointer :: mask =>NULL() !< defined over data domain or global domain - integer, dimension(:), pointer :: ibuf =>NULL() !< record numbers associated with buffers - real, dimension(:,:,:,:), pointer :: src_data =>NULL() !< input data buffer - type(valid_t) :: valid ! data validator - integer :: nbuf - logical :: domain_present - real(DOUBLE_KIND) :: slope, intercept - integer :: isc,iec,jsc,jec - type(time_type) :: modulo_time_beg, modulo_time_end - logical :: have_modulo_times, correct_leap_year_inconsistency - integer :: region_type - integer :: is_region, ie_region, js_region, je_region - integer :: is_src, ie_src, js_src, je_src - integer :: tdim - integer :: numwindows - logical, dimension(:,:), pointer :: need_compute=>NULL() - real :: missing ! missing value - end type ext_fieldtype - - !> Holds filename and file object - !> @ingroup time_interp_external2_mod - type, private :: filetype - character(len=128) :: filename = '' - type(FmsNetcdfFile_t), pointer :: fileobj => NULL() - end type filetype - - !> Provide data from external file interpolated to current model time. - !! Data may be local to current processor or global, depending on - !! "init_external_field" flags. Uses @ref fms2_io for I/O. - !! - !! @param index index of external field from previous call to init_external_field - !! @param time target time for data - !! @param [inout] data global or local data array - !! @param interp time_interp_external defined interpolation method (optional). Currently - !! this module only supports LINEAR_TIME_INTERP. - !! @param verbose verbose flag for debugging (optional). - !! - !> @ingroup time_interp_external2_mod - interface time_interp_external - module procedure time_interp_external_0d - module procedure time_interp_external_2d - module procedure time_interp_external_3d - end interface - - !> @addtogroup time_interp_external2_mod - !> @{ - - integer :: outunit - - type(ext_fieldtype), save, private, pointer :: field(:) => NULL() - type(filetype), save, private, pointer :: opened_files(:) => NULL() -!Balaji: really should use field%missing - integer, private, parameter :: dk = DOUBLE_KIND ! ensures that time_interp_missing is in range for mixed-mode - ! compiling - real(DOUBLE_KIND), private, parameter :: time_interp_missing=-1e99_dk - contains - -! -! -! -! Initialize the time_interp_external module -! -! - !> @brief Initialize the @ref time_interp_external_mod module - subroutine time_interp_external_init() - - integer :: io_status, logunit, ierr - - namelist /time_interp_external_nml/ num_io_buffers, debug_this_module, & - max_fields, max_files - - ! open and read namelist - - if(module_initialized) return - - logunit = stdlog() - outunit = stdout() - call write_version_number("TIME_INTERP_EXTERNAL_MOD", version) - - read (input_nml_file, time_interp_external_nml, iostat=io_status) - ierr = check_nml_error(io_status, 'time_interp_external_nml') - - write(logunit,time_interp_external_nml) - call realloc_fields(max_fields) - call realloc_files(max_files) - - module_initialized = .true. - - call time_interp_init() - - return - - end subroutine time_interp_external_init -! NAME="time_interp_external_init" - - -! -! -! -! initialize an external field. Buffer "num_io_buffers" (default=2) in memory to reduce memory allocations. -! distributed reads are supported using the optional "domain" flag. -! Units conversion via the optional "desired_units" flag using udunits_mod. -! -! Return integer id of field for future calls to time_interp_external. -! -! -! -! -! filename -! -! -! fieldname (in file) -! -! -! mpp_io flag for format of file (optional). Currently only "MPP_NETCDF" supported -! -! -! mpp_io flag for threading (optional). "MPP_SINGLE" means root pe reads global field and distributes to other PEs -! "MPP_MULTI" means all PEs read data -! -! -! domain flag (optional) -! -! -! Target units for data (optional), e.g. convert from deg_K to deg_C. -! Failure to convert using udunits will result in failure of this module. -! -! -! verbose flag for debugging (optional). -! -! -! MPP_IO axistype array for grid centers ordered X-Y-Z-T (optional). -! -! -! array of axis lengths ordered X-Y-Z-T (optional). -! - - - !> Initialize an external field. Buffer "num_io_buffers" (default=2) in memory to reduce memory allocations. - !! distributed reads are supported using the optional "domain" flag. - !! Units conversion via the optional "desired_units" flag using udunits_mod. - !! - !> @return integer id of field for future calls to time_interp_external. - !> @param file filename - !> @param fieldname fieldname (in file) - !> @param format mpp_io flag for format of file(optional). Currently only "MPP_NETCDF" supported - !> @param threading mpp_io flag for threading (optional). "MPP_SINGLE" means root pe reads - !! global field and distributes to other PEs. "MPP_MULTI" means all PEs read data - !> @param domain domain flag (optional) - !> @param desired_units Target units for data (optional), e.g. convert from deg_K to deg_C. - !! Failure to convert using udunits will result in failure of this module. - !> @param verbose verbose flag for debugging (optional). - !> @param [out] axis_names List of axis names (optional). - !> @param [inout] axis_sizes array of axis lengths ordered X-Y-Z-T (optional). - function init_external_field(file,fieldname,domain,desired_units,& - verbose,axis_names, axis_sizes,override,correct_leap_year_inconsistency,& - permit_calendar_conversion,use_comp_domain,ierr, nwindows, ignore_axis_atts, ongrid ) - - character(len=*), intent(in) :: file,fieldname - logical, intent(in), optional :: verbose - character(len=*), intent(in), optional :: desired_units - type(domain2d), intent(in), optional :: domain - integer, intent(inout), optional :: axis_sizes(4) - character(len=*), intent(out), optional :: axis_names(4) - logical, intent(in), optional :: override, correct_leap_year_inconsistency,& - permit_calendar_conversion,use_comp_domain - integer, intent(out), optional :: ierr - integer, intent(in), optional :: nwindows - logical, optional :: ignore_axis_atts - logical, optional :: ongrid !< Optional flag indicating if the data is ongrid - - logical :: ongrid_local !< Flag indicating if the data is ongrid - - integer :: init_external_field - - real(DOUBLE_KIND) :: slope, intercept - integer :: ndim,ntime,i,j - integer :: iscomp,iecomp,jscomp,jecomp,isglobal,ieglobal,jsglobal,jeglobal - integer :: isdata,iedata,jsdata,jedata, dxsize, dysize,dxsize_max,dysize_max - logical :: verb, transpose_xy,use_comp_domain1 - real, dimension(:), allocatable :: tstamp, tstart, tend, tavg - character(len=1) :: cart - character(len=1), dimension(4) :: cart_dir - character(len=128) :: units, fld_units - character(len=128) :: msg, calendar_type, timebeg, timeend - character(len=128) :: timename, timeunits - character(len=128), allocatable :: axisname(:) - integer, allocatable :: axislen(:) - integer :: siz(4), siz_in(4), gxsize, gysize,gxsize_max, gysize_max - type(time_type) :: tdiff - integer :: yr, mon, day, hr, minu, sec - integer :: len, nfile, nfields_orig, nbuf, nx,ny - integer :: numwindows - logical :: ignore_axatts - logical :: have_modulo_time - type(FmsNetcdfFile_t), pointer :: fileobj=>NULL() - integer, dimension(:), allocatable :: pes !< List of ranks in the current pelist - - if (.not. module_initialized) call mpp_error(FATAL,'Must call time_interp_external_init first') - if(present(ierr)) ierr = SUCCESS - ignore_axatts=.false. - cart_dir(1)='X';cart_dir(2)='Y';cart_dir(3)='Z';cart_dir(4)='T' - if(present(ignore_axis_atts)) ignore_axatts = ignore_axis_atts - use_comp_domain1 = .false. - if(PRESENT(use_comp_domain)) use_comp_domain1 = use_comp_domain - verb=.false. - if (PRESENT(verbose)) verb=verbose - if (debug_this_module) verb = .true. - numwindows = 1 - if(present(nwindows)) numwindows = nwindows - - units = 'same' - if (PRESENT(desired_units)) then - units = desired_units - call mpp_error(FATAL,'==> Unit conversion via time_interp_external & - &has been temporarily deprecated. Previous versions of& - &this module used udunits_mod to perform unit conversion.& - & Udunits_mod is in the process of being replaced since & - &there were portability issues associated with this code.& - & Please remove the desired_units argument from calls to & - &this routine.') - endif - nfile = 0 - do i=1,num_files - if(trim(opened_files(i)%filename) == trim(file)) then - nfile = i - exit ! file is already opened - endif - enddo - if(nfile == 0) then - num_files = num_files + 1 - if(num_files > max_files) then ! not enough space in the file table, reallocate it - !--- z1l: For the case of multiple thread, realoc_files will cause memory leak. - !--- If multiple threads are working on file A. One of the thread finished first and - !--- begin to work on file B, the realloc_files will cause problem for - !--- other threads are working on the file A. - ! call realloc_files(2*size(opened_files)) - call mpp_error(FATAL, "time_interp_external: num_files is greater than max_files, "// & - "increase time_interp_external_nml max_files") - endif - opened_files(num_files)%filename = trim(file) - allocate(opened_files(num_files)%fileobj) - fileobj => opened_files(num_files)%fileobj - - if(.not. open_file(opened_files(num_files)%fileobj, trim(file), 'read')) & - call mpp_error(FATAL, 'time_interp_external_mod: Error in opening file '//trim(file)) - else - fileobj => opened_files(nfile)%fileobj - endif - - call get_unlimited_dimension_name(fileobj, timename) - call get_dimension_size(fileobj, timename, ntime) - - if (ntime < 1) then - write(msg,'(a15,a,a58)') 'external field ',trim(fieldname),& - ' does not have an associated record dimension (REQUIRED) ' - call mpp_error(FATAL,trim(msg)) - endif - - !--- get time calendar_type - call get_time_calendar(fileobj, timename, calendar_type) - - !--- get time units - call get_variable_units(fileobj, timename, timeunits) - - !--- get timebeg and timeend - have_modulo_time = get_axis_modulo_times(fileobj, timename, timebeg, timeend) - - allocate(pes(mpp_npes())) - call mpp_get_current_pelist(pes) - allocate(tstamp(ntime),tstart(ntime),tend(ntime),tavg(ntime)) - - !< Only root reads the unlimited dimension and broadcasts it to the other ranks - if (mpp_root_pe() .eq. mpp_pe()) call read_data(fileobj, timename, tstamp) - call mpp_broadcast(tstamp, size(tstamp), mpp_root_pe(), pelist=pes) - deallocate(pes) - - transpose_xy = .false. - isdata=1; iedata=1; jsdata=1; jedata=1 - gxsize=1; gysize=1 - siz_in = 1 - - if (PRESENT(domain)) then - call mpp_get_compute_domain(domain,iscomp,iecomp,jscomp,jecomp) - nx = iecomp-iscomp+1; ny = jecomp-jscomp+1 - call mpp_get_data_domain(domain,isdata,iedata,jsdata,jedata,dxsize,dxsize_max,dysize,dysize_max) - call mpp_get_global_domain(domain,isglobal,ieglobal,jsglobal,jeglobal,gxsize,gxsize_max,gysize,gysize_max) - ongrid_local = .false. - if (present(ongrid)) ongrid_local = ongrid - !> If this is an ongrid case, set is[e]js[e]data to be equal to the compute domain. - !! This is what it is used to allocate space for the data! - if (ongrid_local) then - isdata=iscomp - iedata=iecomp - jsdata=jscomp - jedata=jecomp - endif - elseif(use_comp_domain1) then - call mpp_error(FATAL,"init_external_field:"//& - " use_comp_domain=true but domain is not present") - endif - - init_external_field = -1 - nfields_orig = num_fields - - if (.not. variable_exists(fileobj, fieldname) ) then - if (present(ierr)) then - ierr = ERR_FIELD_NOT_FOUND - return - else - call mpp_error(FATAL,'external field "'//trim(fieldname)//'" not found in file "'//trim(file)//'"') - endif - endif - - tavg = -1.0 - tstart = tstamp - tend = tstamp - if(variable_att_exists(fileobj, fieldname, 'time_avg_info')) then - if(variable_exists(fileobj, 'average_T1')) call read_data(fileobj, 'average_T1', tstart) - if(variable_exists(fileobj, 'average_T2')) call read_data(fileobj, 'average_T2', tend) - if(variable_exists(fileobj, 'average_DT')) call read_data(fileobj, 'average_DT', tavg) - endif - - num_fields = num_fields + 1 - if(num_fields > max_fields) then - !--- z1l: For the case of multiple thread, realoc_fields will cause memory leak. - !--- If multiple threads are working on field A. One of the thread finished first and - !--- begin to work on field B, the realloc_files will cause problem for - !--- other threads are working on the field A. - !call realloc_fields(size(field)*2) - call mpp_error(FATAL, "time_interp_external: num_fields is greater than max_fields, "// & - "increase time_interp_external_nml max_fields") - endif - - !--- get field attribute - call get_variable_units(fileobj, fieldname, fld_units) - - init_external_field = num_fields - field(num_fields)%fileobj => fileobj - field(num_fields)%name = trim(fieldname) - field(num_fields)%units = trim(fld_units) - field(num_fields)%isc = 1 - field(num_fields)%iec = 1 - field(num_fields)%jsc = 1 - field(num_fields)%jec = 1 - field(num_fields)%region_type = NO_REGION - field(num_fields)%is_region = 0 - field(num_fields)%ie_region = -1 - field(num_fields)%js_region = 0 - field(num_fields)%je_region = -1 - if (PRESENT(domain)) then - field(num_fields)%domain_present = .true. - field(num_fields)%domain = domain - field(num_fields)%isc=iscomp;field(num_fields)%iec = iecomp - field(num_fields)%jsc=jscomp;field(num_fields)%jec = jecomp - else - field(num_fields)%domain_present = .false. - endif - - field(num_fields)%valid = get_valid(fileobj, fieldname) - ndim = get_variable_num_dimensions(fileobj, fieldname) - if (ndim > 4) call mpp_error(FATAL, & - 'invalid array rank <=4d fields supported') - - field(num_fields)%siz = 1 - field(num_fields)%ndim = ndim - field(num_fields)%tdim = 4 - !--- get field missing value - field(num_fields)%missing = get_variable_missing(fileobj, fieldname) - - allocate(axisname(ndim), axislen(ndim)) - - call get_variable_dimension_names(fileobj, fieldname, axisname) - call get_variable_size(fileobj, fieldname, axislen) - do j=1,field(num_fields)%ndim - call get_axis_cart(fileobj, axisname(j), cart) - len = axislen(j) - if (cart == 'N' .and. .not. ignore_axatts) then - write(msg,'(a,"/",a)') trim(file),trim(fieldname) - call mpp_error(FATAL,'file/field '//trim(msg)// & - ' couldnt recognize axis atts in time_interp_external') - else if (cart == 'N' .and. ignore_axatts) then - cart = cart_dir(j) - endif - select case (cart) - case ('X') - if (j.eq.2) transpose_xy = .true. - if (.not.PRESENT(domain) .and. .not.PRESENT(override)) then - isdata=1;iedata=len - iscomp=1;iecomp=len - gxsize = len - dxsize = len - field(num_fields)%isc=iscomp;field(num_fields)%iec=iecomp - elseif (PRESENT(override)) then - gxsize = len - if (PRESENT(axis_sizes)) axis_sizes(1) = len - endif - field(num_fields)%axisname(1) = axisname(j) - if(use_comp_domain1) then - field(num_fields)%siz(1) = nx - else - field(num_fields)%siz(1) = dxsize - endif - if (len /= gxsize) then - write(msg,'(a,"/",a)') trim(file),trim(fieldname) - call mpp_error(FATAL,'time_interp_ext, file/field '//trim(msg)//' x dim doesnt match model') - endif - case ('Y') - field(num_fields)%axisname(2) = axisname(j) - if (.not.PRESENT(domain) .and. .not.PRESENT(override)) then - jsdata=1;jedata=len - jscomp=1;jecomp=len - gysize = len - dysize = len - field(num_fields)%jsc=jscomp;field(num_fields)%jec=jecomp - elseif (PRESENT(override)) then - gysize = len - if (PRESENT(axis_sizes)) axis_sizes(2) = len - endif - if(use_comp_domain1) then - field(num_fields)%siz(2) = ny - else - field(num_fields)%siz(2) = dysize - endif - if (len /= gysize) then - write(msg,'(a,"/",a)') trim(file),trim(fieldname) - call mpp_error(FATAL,'time_interp_ext, file/field '//trim(msg)//' y dim doesnt match model') - endif - case ('Z') - field(num_fields)%axisname(3) = axisname(j) - field(num_fields)%siz(3) = len - case ('T') - field(num_fields)%axisname(4) = axisname(j) - field(num_fields)%siz(4) = ntime - field(num_fields)%tdim = j - end select - enddo - siz = field(num_fields)%siz - if(PRESENT(axis_names)) axis_names = field(num_fields)%axisname - if (PRESENT(axis_sizes) .and. .not.PRESENT(override)) then - axis_sizes = field(num_fields)%siz - endif - - if (verb) write(outunit,'(a,4i6)') 'field x,y,z,t local size= ',siz - if (verb) write(outunit,*) 'field contains data in units = ',trim(field(num_fields)%units) - if (transpose_xy) call mpp_error(FATAL,'axis ordering not supported') - if (num_io_buffers .le. 1) call mpp_error(FATAL,'time_interp_ext:num_io_buffers should be at least 2') - nbuf = min(num_io_buffers,siz(4)) - - field(num_fields)%numwindows = numwindows - allocate(field(num_fields)%need_compute(nbuf, numwindows)) - field(num_fields)%need_compute = .true. - - allocate(field(num_fields)%data(isdata:iedata,jsdata:jedata,siz(3),nbuf),& - field(num_fields)%mask(isdata:iedata,jsdata:jedata,siz(3),nbuf) ) - field(num_fields)%mask = .false. - field(num_fields)%data = 0.0 - slope=1.0;intercept=0.0 -! if (units /= 'same') call convert_units(trim(field(num_fields)%units),trim(units),slope,intercept) -! if (verb.and.units /= 'same') then -! write(outunit,*) 'attempting to convert data to units = ',trim(units) -! write(outunit,'(a,f8.3,a,f8.3)') 'factor = ',slope,' offset= ',intercept -! endif - field(num_fields)%slope = slope - field(num_fields)%intercept = intercept - allocate(field(num_fields)%ibuf(nbuf)) - field(num_fields)%ibuf = -1 - field(num_fields)%nbuf = 0 ! initialize buffer number so that first reading fills data(:,:,:,1) - if(PRESENT(override)) then - field(num_fields)%is_src = 1 - field(num_fields)%ie_src = gxsize - field(num_fields)%js_src = 1 - field(num_fields)%je_src = gysize - allocate(field(num_fields)%src_data(gxsize,gysize,siz(3),nbuf)) - else - field(num_fields)%is_src = isdata - field(num_fields)%ie_src = iedata - field(num_fields)%js_src = jsdata - field(num_fields)%je_src = jedata - allocate(field(num_fields)%src_data(isdata:iedata,jsdata:jedata,siz(3),nbuf)) - endif - - allocate(field(num_fields)%time(ntime)) - allocate(field(num_fields)%period(ntime)) - allocate(field(num_fields)%start_time(ntime)) - allocate(field(num_fields)%end_time(ntime)) - - do j=1,ntime - field(num_fields)%time(j) = get_cal_time(tstamp(j),trim(timeunits),trim(calendar_type), & - & permit_calendar_conversion) - field(num_fields)%start_time(j) = get_cal_time(tstart(j),trim(timeunits),trim(calendar_type), & - & permit_calendar_conversion) - field(num_fields)%end_time(j) = get_cal_time( tend(j),trim(timeunits),trim(calendar_type), & - & permit_calendar_conversion) - enddo - - if (field(num_fields)%modulo_time) then - call set_time_modulo(field(num_fields)%Time) - call set_time_modulo(field(num_fields)%start_time) - call set_time_modulo(field(num_fields)%end_time) - endif - - if(present(correct_leap_year_inconsistency)) then - field(num_fields)%correct_leap_year_inconsistency = correct_leap_year_inconsistency - else - field(num_fields)%correct_leap_year_inconsistency = .false. - endif - - if(have_modulo_time) then - if(get_calendar_type() == NO_CALENDAR) then - field(num_fields)%modulo_time_beg = set_time(timebeg) - field(num_fields)%modulo_time_end = set_time(timeend) - else - field(num_fields)%modulo_time_beg = set_date(timebeg) - field(num_fields)%modulo_time_end = set_date(timeend) - endif - field(num_fields)%have_modulo_times = .true. - else - field(num_fields)%have_modulo_times = .false. - endif - if(ntime == 1) then - call mpp_error(NOTE, 'time_interp_external_mod: file '//trim(file)//' has only one time level') - else - do j= 1, ntime - field(num_fields)%period(j) = field(num_fields)%end_time(j)-field(num_fields)%start_time(j) - if (field(num_fields)%period(j) > set_time(0,0)) then - call get_time(field(num_fields)%period(j), sec, day) - sec = sec/2+mod(day,2)*43200 - day = day/2 - field(num_fields)%time(j) = field(num_fields)%start_time(j)+& - set_time(sec,day) - else - if (j > 1 .and. j < ntime) then - tdiff = field(num_fields)%time(j+1) - field(num_fields)%time(j-1) - call get_time(tdiff, sec, day) - sec = sec/2+mod(day,2)*43200 - day = day/2 - field(num_fields)%period(j) = set_time(sec,day) - sec = sec/2+mod(day,2)*43200 - day = day/2 - field(num_fields)%start_time(j) = field(num_fields)%time(j) - set_time(sec,day) - field(num_fields)%end_time(j) = field(num_fields)%time(j) + set_time(sec,day) - elseif ( j == 1) then - tdiff = field(num_fields)%time(2) - field(num_fields)%time(1) - call get_time(tdiff, sec, day) - field(num_fields)%period(j) = set_time(sec,day) - sec = sec/2+mod(day,2)*43200 - day = day/2 - field(num_fields)%start_time(j) = field(num_fields)%time(j) - set_time(sec,day) - field(num_fields)%end_time(j) = field(num_fields)%time(j) + set_time(sec,day) - else - tdiff = field(num_fields)%time(ntime) - field(num_fields)%time(ntime-1) - call get_time(tdiff, sec, day) - field(num_fields)%period(j) = set_time(sec,day) - sec = sec/2+mod(day,2)*43200 - day = day/2 - field(num_fields)%start_time(j) = field(num_fields)%time(j) - set_time(sec,day) - field(num_fields)%end_time(j) = field(num_fields)%time(j) + set_time(sec,day) - endif - endif - enddo - endif - - do j=1,ntime-1 - if (field(num_fields)%time(j) >= field(num_fields)%time(j+1)) then - write(msg,'(A,i20)') "times not monotonically increasing. Filename: " & - //TRIM(file)//" field: "//TRIM(fieldname)//" timeslice: ", j - call mpp_error(FATAL, TRIM(msg)) - endif - enddo - - field(num_fields)%modulo_time = get_axis_modulo(fileobj, timename) - - if (verb) then - if (field(num_fields)%modulo_time) write(outunit,*) 'data are being treated as modulo in time' - do j= 1, ntime - write(outunit,*) 'time index, ', j - call get_date(field(num_fields)%start_time(j),yr,mon,day,hr,minu,sec) - write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') & - 'start time: yyyy/mm/dd hh:mm:ss= ',yr,'/',mon,'/',day,hr,':',minu,':',sec - call get_date(field(num_fields)%time(j),yr,mon,day,hr,minu,sec) - write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') & - 'mid time: yyyy/mm/dd hh:mm:ss= ',yr,'/',mon,'/',day,hr,':',minu,':',sec - call get_date(field(num_fields)%end_time(j),yr,mon,day,hr,minu,sec) - write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') & - 'end time: yyyy/mm/dd hh:mm:ss= ',yr,'/',mon,'/',day,hr,':',minu,':',sec - enddo - end if - - deallocate(axisname, axislen) - deallocate(tstamp, tstart, tend, tavg) - - return - - end function init_external_field - -! NAME="init_external_field" - !> @brief 2D interpolation for @ref time_interp_external - subroutine time_interp_external_2d(index, time, data_in, interp, verbose,horz_interp, mask_out, & + subroutine TIME_INTERP_EXTERNAL_2D_(index, time, data_in, interp, verbose,horz_interp, mask_out, & is_in, ie_in, js_in, je_in, window_id) integer, intent(in) :: index type(time_type), intent(in) :: time - real, dimension(:,:), intent(inout) :: data_in + real(FMS_TI_KIND_), dimension(:,:), intent(inout) :: data_in integer, intent(in), optional :: interp logical, intent(in), optional :: verbose type(horiz_interp_type),intent(in), optional :: horz_interp @@ -716,231 +34,201 @@ module time_interp_external2_mod integer, intent(in), optional :: is_in, ie_in, js_in, je_in integer, intent(in), optional :: window_id - real , dimension(size(data_in,1), size(data_in,2), 1) :: data_out + real(FMS_TI_KIND_), dimension(size(data_in,1), size(data_in,2), 1) :: data_out logical, dimension(size(data_in,1), size(data_in,2), 1) :: mask3d data_out(:,:,1) = data_in(:,:) ! fill initial values for the portions of array that are not touched by 3d routine - call time_interp_external_3d(index, time, data_out, interp, verbose, horz_interp, mask3d, & + call time_interp_external(index, time, data_out, interp, verbose, horz_interp, mask3d, & is_in=is_in,ie_in=ie_in,js_in=js_in,je_in=je_in,window_id=window_id) data_in(:,:) = data_out(:,:,1) if (PRESENT(mask_out)) mask_out(:,:) = mask3d(:,:,1) return - end subroutine time_interp_external_2d + end subroutine TIME_INTERP_EXTERNAL_2D_ - function get_external_fileobj(filename, fileobj) - character(len=*), intent(in) :: filename - type(FmsNetcdfFile_t), intent(out) :: fileobj - logical :: get_external_fileobj - integer :: i - - get_external_fileobj = .false. - do i=1,num_files - if(trim(opened_files(i)%filename) == trim(filename)) then - fileobj = opened_files(i)%fileobj - get_external_fileobj = .true. - exit ! file is already opened - endif - enddo - - end function get_external_fileobj - - -! -! -! -! Provide data from external file interpolated to current model time. -! Data may be local to current processor or global, depending on -! "init_external_field" flags. -! -! -! -! index of external field from previous call to init_external_field -! -! -! target time for data -! -! -! global or local data array -! -! -! time_interp_external defined interpolation method (optional). Currently this module only supports -! LINEAR_TIME_INTERP. -! -! -! verbose flag for debugging (optional). -! !> 3D interpolation for @ref time_interp_external - subroutine time_interp_external_3d(index, time, data, interp,verbose,horz_interp, mask_out, is_in, ie_in, & + !! Provide data from external file interpolated to current model time. + !! Data may be local to current processor or global, depending on + !! "init_external_field" flags. + subroutine TIME_INTERP_EXTERNAL_3D_(index, time, data, interp,verbose,horz_interp, mask_out, is_in, ie_in, & & js_in, je_in, window_id) - integer, intent(in) :: index - type(time_type), intent(in) :: time - real, dimension(:,:,:), intent(inout) :: data - integer, intent(in), optional :: interp - logical, intent(in), optional :: verbose - type(horiz_interp_type), intent(in), optional :: horz_interp - logical, dimension(:,:,:), intent(out), optional :: mask_out ! set to true where output data is valid - integer, intent(in), optional :: is_in, ie_in, js_in, je_in - integer, intent(in), optional :: window_id - - integer :: nx, ny, nz, interp_method, t1, t2 - integer :: i1, i2, isc, iec, jsc, jec, mod_time - integer :: yy, mm, dd, hh, min, ss - character(len=256) :: err_msg, filename - - integer :: isw, iew, jsw, jew, nxw, nyw - ! these are boundaries of the updated portion of the "data" argument - ! they are calculated using sizes of the "data" and isc,iec,jsc,jsc - ! fileds from respective input field, to center the updated portion - ! in the output array - - real :: w1,w2 - logical :: verb - character(len=16) :: message1, message2 - - nx = size(data,1) - ny = size(data,2) - nz = size(data,3) - - interp_method = LINEAR_TIME_INTERP - if (PRESENT(interp)) interp_method = interp - verb=.false. - if (PRESENT(verbose)) verb=verbose - if (debug_this_module) verb = .true. - - if (index < 1.or.index > num_fields) & - call mpp_error(FATAL, & - & 'invalid index in call to time_interp_ext -- field was not initialized or failed to initialize') - - isc=field(index)%isc;iec=field(index)%iec - jsc=field(index)%jsc;jec=field(index)%jec - - if( field(index)%numwindows == 1 ) then - nxw = iec-isc+1 - nyw = jec-jsc+1 - else - if( .not. present(is_in) .or. .not. present(ie_in) .or. .not. present(js_in) .or. .not. present(je_in) ) then - call mpp_error(FATAL, 'time_interp_external: is_in, ie_in, js_in and je_in must be present ' // & - 'when numwindows > 1, field='//trim(field(index)%name)) - endif - nxw = ie_in - is_in + 1 - nyw = je_in - js_in + 1 - isc = isc + is_in - 1 - iec = isc + ie_in - is_in - jsc = jsc + js_in - 1 - jec = jsc + je_in - js_in - endif + integer, intent(in) :: index !< index of external field from previous call + !! to init_external_field + type(time_type), intent(in) :: time !< target time for data + real(FMS_TI_KIND_), dimension(:,:,:), intent(inout) :: data !< global or local data array + integer, intent(in), optional :: interp + logical, intent(in), optional :: verbose !< flag for debugging + type(horiz_interp_type), intent(in), optional :: horz_interp + logical, dimension(:,:,:), intent(out), optional :: mask_out !< set to true where output data is valid + integer, intent(in), optional :: is_in, ie_in, js_in, je_in + integer, intent(in), optional :: window_id + + integer :: nx, ny, nz, interp_method, t1, t2 + integer :: i1, i2, isc, iec, jsc, jec, mod_time + integer :: yy, mm, dd, hh, min, ss + character(len=256) :: err_msg, filename + + integer :: isw, iew, jsw, jew, nxw, nyw + ! these are boundaries of the updated portion of the "data" argument + ! they are calculated using sizes of the "data" and isc,iec,jsc,jsc + ! fileds from respective input field, to center the updated portion + ! in the output array + + real(FMS_TI_KIND_) :: w1,w2 + logical :: verb + character(len=16) :: message1, message2 + integer, parameter :: kindl = FMS_TI_KIND_ + + nx = size(data,1) + ny = size(data,2) + nz = size(data,3) + + interp_method = LINEAR_TIME_INTERP + if (PRESENT(interp)) interp_method = interp + verb=.false. + if (PRESENT(verbose)) verb=verbose + if (debug_this_module) verb = .true. + + if (index < 1.or.index > num_fields) & + call mpp_error(FATAL, & + & 'invalid index in call to time_interp_ext -- field was not initialized or failed to initialize') + + isc=loaded_fields(index)%isc;iec=loaded_fields(index)%iec + jsc=loaded_fields(index)%jsc;jec=loaded_fields(index)%jec + + if( loaded_fields(index)%numwindows == 1 ) then + nxw = iec-isc+1 + nyw = jec-jsc+1 + else + if(.not. present(is_in) .or. .not. present(ie_in) .or. .not. present(js_in) .or. .not. present(je_in))then + call mpp_error(FATAL, 'time_interp_external: is_in, ie_in, js_in and je_in must be present ' // & + 'when numwindows > 1, field='//trim(loaded_fields(index)%name)) + endif + nxw = ie_in - is_in + 1 + nyw = je_in - js_in + 1 + isc = isc + is_in - 1 + iec = isc + ie_in - is_in + jsc = jsc + js_in - 1 + jec = jsc + je_in - js_in + endif - isw = (nx-nxw)/2+1; iew = isw+nxw-1 - jsw = (ny-nyw)/2+1; jew = jsw+nyw-1 + isw = (nx-nxw)/2+1; iew = isw+nxw-1 + jsw = (ny-nyw)/2+1; jew = jsw+nyw-1 - if (nx < nxw .or. ny < nyw .or. nz < field(index)%siz(3)) then - write(message1,'(i6,2i5)') nx,ny,nz - call mpp_error(FATAL,'field '//trim(field(index)%name)//' Array size mismatch in time_interp_external.'// & - ' Array "data" is too small. shape(data)='//message1) - endif - if(PRESENT(mask_out)) then - if (size(mask_out,1) /= nx .or. size(mask_out,2) /= ny .or. size(mask_out,3) /= nz) then - write(message1,'(i6,2i5)') nx,ny,nz - write(message2,'(i6,2i5)') size(mask_out,1),size(mask_out,2),size(mask_out,3) - call mpp_error(FATAL,'field '//trim(field(index)%name)//' array size mismatch in time_interp_external.'// & - ' Shape of array "mask_out" does not match that of array "data".'// & - ' shape(data)='//message1//' shape(mask_out)='//message2) + if (nx < nxw .or. ny < nyw .or. nz < loaded_fields(index)%siz(3)) then + write(message1,'(i6,2i5)') nx,ny,nz + call mpp_error(FATAL,'field '//trim(loaded_fields(index)%name)// & + ' Array size mismatch in time_interp_external. Array "data" is too small. shape(data)=' & + //message1) + endif + if(PRESENT(mask_out)) then + if (size(mask_out,1) /= nx .or. size(mask_out,2) /= ny .or. size(mask_out,3) /= nz) then + write(message1,'(i6,2i5)') nx,ny,nz + write(message2,'(i6,2i5)') size(mask_out,1),size(mask_out,2),size(mask_out,3) + call mpp_error(FATAL,'field '//trim(loaded_fields(index)%name)// & + ' array size mismatch in time_interp_external.'// & + ' Shape of array "mask_out" does not match that of array "data".'// & + ' shape(data)='//message1//' shape(mask_out)='//message2) + endif endif - endif - if (field(index)%siz(4) == 1) then - ! only one record in the file => time-independent field - call load_record(field(index),1,horz_interp, is_in, ie_in ,js_in, je_in,window_id) - i1 = find_buf_index(1,field(index)%ibuf) - if( field(index)%region_type == NO_REGION ) then - where(field(index)%mask(isc:iec,jsc:jec,:,i1)) - data(isw:iew,jsw:jew,:) = field(index)%data(isc:iec,jsc:jec,:,i1) - elsewhere -! data(isw:iew,jsw:jew,:) = time_interp_missing !field(index)%missing? Balaji - data(isw:iew,jsw:jew,:) = field(index)%missing - end where - else - where(field(index)%mask(isc:iec,jsc:jec,:,i1)) - data(isw:iew,jsw:jew,:) = field(index)%data(isc:iec,jsc:jec,:,i1) - end where - endif - if(PRESENT(mask_out)) & - mask_out(isw:iew,jsw:jew,:) = field(index)%mask(isc:iec,jsc:jec,:,i1) - else - if(field(index)%have_modulo_times) then - call time_interp(time,field(index)%modulo_time_beg, field(index)%modulo_time_end, field(index)%time(:), & - w2, t1, t2, field(index)%correct_leap_year_inconsistency, err_msg=err_msg) - if(err_msg .NE. '') then - filename = trim(field(index)%fileobj%path) - call mpp_error(FATAL,"time_interp_external 1: "//trim(err_msg)//& - ",file="//trim(filename)//",field="//trim(field(index)%name) ) - endif + ! only one record in the file => time-independent field + if (loaded_fields(index)%siz(4) == 1) then + call load_record(loaded_fields(index),1,horz_interp, is_in, ie_in ,js_in, je_in,window_id) + i1 = find_buf_index(1,loaded_fields(index)%ibuf) + if( loaded_fields(index)%region_type == NO_REGION ) then + where(loaded_fields(index)%mask(isc:iec,jsc:jec,:,i1)) + data(isw:iew,jsw:jew,:) = real( loaded_fields(index)%data(isc:iec,jsc:jec,:,i1), FMS_TI_KIND_) + elsewhere + ! data(isw:iew,jsw:jew,:) = time_interp_missing !field(index)%missing? Balaji + data(isw:iew,jsw:jew,:) = real(loaded_fields(index)%missing, FMS_TI_KIND_) + end where + else + where(loaded_fields(index)%mask(isc:iec,jsc:jec,:,i1)) + data(isw:iew,jsw:jew,:) = real(loaded_fields(index)%data(isc:iec,jsc:jec,:,i1), FMS_TI_KIND_) + end where + endif + if(PRESENT(mask_out)) mask_out(isw:iew,jsw:jew,:) = loaded_fields(index)%mask(isc:iec,jsc:jec,:,i1) + ! otherwise do interpolation else - if(field(index)%modulo_time) then - mod_time=1 - else - mod_time=0 - endif - call time_interp(time,field(index)%time(:),w2,t1,t2,modtime=mod_time, err_msg=err_msg) - if(err_msg .NE. '') then - filename = trim(field(index)%fileobj%path) - call mpp_error(FATAL,"time_interp_external 2: "//trim(err_msg)//& - ",file="//trim(filename)//",field="//trim(field(index)%name) ) - endif - endif - w1 = 1.0-w2 - if (verb) then - call get_date(time,yy,mm,dd,hh,min,ss) - write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') & - 'target time yyyy/mm/dd hh:mm:ss= ',yy,'/',mm,'/',dd,hh,':',min,':',ss - write(outunit,*) 't1, t2, w1, w2= ', t1, t2, w1, w2 - endif + ! using time_interp_modulo + if(loaded_fields(index)%have_modulo_times) then + call time_interp(time,loaded_fields(index)%modulo_time_beg, loaded_fields(index)%modulo_time_end, & + loaded_fields(index)%time(:), w2, t1, t2, & + loaded_fields(index)%correct_leap_year_inconsistency, err_msg=err_msg) + if(err_msg .NE. '') then + filename = trim(loaded_fields(index)%fileobj%path) + call mpp_error(FATAL,"time_interp_external 1: "//trim(err_msg)//& + ",file="//trim(filename)//",field="//trim(loaded_fields(index)%name) ) + endif + ! using time_interp_list + else + if(loaded_fields(index)%modulo_time) then + mod_time=1 + else + mod_time=0 + endif + call time_interp(time,loaded_fields(index)%time(:),w2,t1,t2,modtime=mod_time, err_msg=err_msg) + if(err_msg .NE. '') then + filename = trim(loaded_fields(index)%fileobj%path) + call mpp_error(FATAL,"time_interp_external 2: "//trim(err_msg)//& + ",file="//trim(filename)//",field="//trim(loaded_fields(index)%name) ) + endif + endif + w1 = 1.0_kindl -w2 + if (verb) then + call get_date(time,yy,mm,dd,hh,min,ss) + write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') & + 'target time yyyy/mm/dd hh:mm:ss= ',yy,'/',mm,'/',dd,hh,':',min,':',ss + write(outunit,*) 't1, t2, w1, w2= ', t1, t2, w1, w2 + endif - call load_record(field(index),t1,horz_interp, is_in, ie_in ,js_in, je_in, window_id) - call load_record(field(index),t2,horz_interp, is_in, ie_in ,js_in, je_in, window_id) - i1 = find_buf_index(t1,field(index)%ibuf) - i2 = find_buf_index(t2,field(index)%ibuf) - if(i1<0.or.i2<0) & - call mpp_error(FATAL,'time_interp_external : records were not loaded correctly in memory') + call load_record(loaded_fields(index),t1,horz_interp, is_in, ie_in ,js_in, je_in, window_id) + call load_record(loaded_fields(index),t2,horz_interp, is_in, ie_in ,js_in, je_in, window_id) + i1 = find_buf_index(t1,loaded_fields(index)%ibuf) + i2 = find_buf_index(t2,loaded_fields(index)%ibuf) + if(i1<0.or.i2<0) & + call mpp_error(FATAL,'time_interp_external : records were not loaded correctly in memory') - if (verb) then - write(outunit,*) 'ibuf= ',field(index)%ibuf - write(outunit,*) 'i1,i2= ',i1, i2 - endif + if (verb) then + write(outunit,*) 'ibuf= ',loaded_fields(index)%ibuf + write(outunit,*) 'i1,i2= ',i1, i2 + endif - if( field(index)%region_type == NO_REGION ) then - where(field(index)%mask(isc:iec,jsc:jec,:,i1).and.field(index)%mask(isc:iec,jsc:jec,:,i2)) - data(isw:iew,jsw:jew,:) = field(index)%data(isc:iec,jsc:jec,:,i1)*w1 + & - field(index)%data(isc:iec,jsc:jec,:,i2)*w2 - elsewhere -! data(isw:iew,jsw:jew,:) = time_interp_missing !field(index)%missing? Balaji - data(isw:iew,jsw:jew,:) = field(index)%missing - end where - else - where(field(index)%mask(isc:iec,jsc:jec,:,i1).and.field(index)%mask(isc:iec,jsc:jec,:,i2)) - data(isw:iew,jsw:jew,:) = field(index)%data(isc:iec,jsc:jec,:,i1)*w1 + & - field(index)%data(isc:iec,jsc:jec,:,i2)*w2 - end where - endif - if(PRESENT(mask_out)) & - mask_out(isw:iew,jsw:jew,:) = & - field(index)%mask(isc:iec,jsc:jec,:,i1).and.& - field(index)%mask(isc:iec,jsc:jec,:,i2) - endif + if( loaded_fields(index)%region_type == NO_REGION ) then + where(loaded_fields(index)%mask(isc:iec,jsc:jec,:,i1) .and. & + loaded_fields(index)%mask(isc:iec,jsc:jec,:,i2)) + data(isw:iew,jsw:jew,:) = real(loaded_fields(index)%data(isc:iec,jsc:jec,:,i1), kindl) * w1 + & + real(loaded_fields(index)%data(isc:iec,jsc:jec,:,i2), kindl) * w2 + elsewhere + ! data(isw:iew,jsw:jew,:) = time_interp_missing !loaded_fields(index)%missing? Balaji + data(isw:iew,jsw:jew,:) = real(loaded_fields(index)%missing, kindl) + end where + else + where(loaded_fields(index)%mask(isc:iec,jsc:jec,:,i1) .and. & + loaded_fields(index)%mask(isc:iec,jsc:jec,:,i2)) + data(isw:iew,jsw:jew,:) = real( loaded_fields(index)%data(isc:iec,jsc:jec,:,i1), kindl) * w1 + & + real(loaded_fields(index)%data(isc:iec,jsc:jec,:,i2), kindl) * w2 + end where + endif + if(PRESENT(mask_out)) & + mask_out(isw:iew,jsw:jew,:) = & + loaded_fields(index)%mask(isc:iec,jsc:jec,:,i1).and.& + loaded_fields(index)%mask(isc:iec,jsc:jec,:,i2) + endif - end subroutine time_interp_external_3d + end subroutine TIME_INTERP_EXTERNAL_3D_ ! NAME="time_interp_external" !> @brief Scalar interpolation for @ref time_interp_external - subroutine time_interp_external_0d(index, time, data, verbose) + subroutine TIME_INTERP_EXTERNAL_0D_(index, time, data, verbose) integer, intent(in) :: index type(time_type), intent(in) :: time - real, intent(inout) :: data + real(FMS_TI_KIND_), intent(inout) :: data logical, intent(in), optional :: verbose integer :: t1, t2 @@ -948,8 +236,9 @@ module time_interp_external2_mod integer :: yy, mm, dd, hh, min, ss character(len=256) :: err_msg, filename - real :: w1,w2 + real(FMS_TI_KIND_) :: w1,w2 logical :: verb + integer, parameter :: kindl = FMS_TI_KIND_ verb=.false. if (PRESENT(verbose)) verb=verbose @@ -959,458 +248,56 @@ module time_interp_external2_mod call mpp_error(FATAL, & & 'invalid index in call to time_interp_ext -- field was not initialized or failed to initialize') - if (field(index)%siz(4) == 1) then - ! only one record in the file => time-independent field - call load_record_0d(field(index),1) - i1 = find_buf_index(1,field(index)%ibuf) - data = field(index)%data(1,1,1,i1) + if (loaded_fields(index)%siz(4) == 1) then + ! only one record in the file => time-independent loaded_fields + call load_record_0d(loaded_fields(index),1) + i1 = find_buf_index(1,loaded_fields(index)%ibuf) + data = real(loaded_fields(index)%data(1,1,1,i1), FMS_TI_KIND_) else - if(field(index)%have_modulo_times) then - call time_interp(time,field(index)%modulo_time_beg, field(index)%modulo_time_end, field(index)%time(:), & - w2, t1, t2, field(index)%correct_leap_year_inconsistency, err_msg=err_msg) + if(loaded_fields(index)%have_modulo_times) then + call time_interp(time,loaded_fields(index)%modulo_time_beg, loaded_fields(index)%modulo_time_end, & + loaded_fields(index)%time(:), w2, t1, t2, & + loaded_fields(index)%correct_leap_year_inconsistency, err_msg=err_msg) if(err_msg .NE. '') then - filename = trim(field(index)%fileobj%path) + filename = trim(loaded_fields(index)%fileobj%path) call mpp_error(FATAL,"time_interp_external 3:"//trim(err_msg)//& - ",file="//trim(filename)//",field="//trim(field(index)%name) ) + ",file="//trim(filename)//",field="//trim(loaded_fields(index)%name) ) endif else - if(field(index)%modulo_time) then + if(loaded_fields(index)%modulo_time) then mod_time=1 else mod_time=0 endif - call time_interp(time,field(index)%time(:),w2,t1,t2,modtime=mod_time, err_msg=err_msg) + call time_interp(time,loaded_fields(index)%time(:),w2,t1,t2,modtime=mod_time, err_msg=err_msg) if(err_msg .NE. '') then - filename = trim(field(index)%fileobj%path) + filename = trim(loaded_fields(index)%fileobj%path) call mpp_error(FATAL,"time_interp_external 4:"//trim(err_msg)// & - ",file="//trim(filename)//",field="//trim(field(index)%name) ) + ",file="//trim(filename)//",field="//trim(loaded_fields(index)%name) ) endif endif - w1 = 1.0-w2 + w1 = 1.0_kindl-w2 if (verb) then call get_date(time,yy,mm,dd,hh,min,ss) write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') & 'target time yyyy/mm/dd hh:mm:ss= ',yy,'/',mm,'/',dd,hh,':',min,':',ss write(outunit,*) 't1, t2, w1, w2= ', t1, t2, w1, w2 endif - call load_record_0d(field(index),t1) - call load_record_0d(field(index),t2) - i1 = find_buf_index(t1,field(index)%ibuf) - i2 = find_buf_index(t2,field(index)%ibuf) + call load_record_0d(loaded_fields(index),t1) + call load_record_0d(loaded_fields(index),t2) + i1 = find_buf_index(t1,loaded_fields(index)%ibuf) + i2 = find_buf_index(t2,loaded_fields(index)%ibuf) if(i1<0.or.i2<0) & call mpp_error(FATAL,'time_interp_external : records were not loaded correctly in memory') - data = field(index)%data(1,1,1,i1)*w1 + field(index)%data(1,1,1,i2)*w2 + data = real(loaded_fields(index)%data(1,1,1,i1), FMS_TI_KIND_)*w1 & + & + real(loaded_fields(index)%data(1,1,1,i2), FMS_TI_KIND_)*w2 if (verb) then - write(outunit,*) 'ibuf= ',field(index)%ibuf + write(outunit,*) 'ibuf= ',loaded_fields(index)%ibuf write(outunit,*) 'i1,i2= ',i1, i2 endif endif - end subroutine time_interp_external_0d - - subroutine set_time_modulo(Time) - - type(time_type), intent(inout), dimension(:) :: Time - - integer :: ntime, n - integer :: yr, mon, dy, hr, minu, sec - - ntime = size(Time(:)) - - do n = 1, ntime - call get_date(Time(n), yr, mon, dy, hr, minu, sec) - yr = modulo_year - Time(n) = set_date(yr, mon, dy, hr, minu, sec) - enddo - - - end subroutine set_time_modulo - -! ============================================================================ -!> load specified record from file -subroutine load_record(field, rec, interp, is_in, ie_in, js_in, je_in, window_id_in) - type(ext_fieldtype), intent(inout) :: field - integer , intent(in) :: rec ! record number - type(horiz_interp_type), intent(in), optional :: interp - integer, intent(in), optional :: is_in, ie_in, js_in, je_in - integer, intent(in), optional :: window_id_in - - ! ---- local vars - integer :: ib ! index in the array of input buffers - integer :: isw,iew,jsw,jew ! boundaries of the domain on each window - integer :: is_region, ie_region, js_region, je_region, i, j - integer :: start(4), nread(4) - logical :: need_compute - real :: mask_in(size(field%src_data,1),size(field%src_data,2),size(field%src_data,3)) - real, allocatable :: mask_out(:,:,:) - integer :: window_id - - window_id = 1 - if( PRESENT(window_id_in) ) window_id = window_id_in - need_compute = .true. - -!$OMP CRITICAL - ib = find_buf_index(rec,field%ibuf) - - if(ib>0) then - !--- do nothing - need_compute = .false. - else - ! calculate current buffer number in round-robin fasion - field%nbuf = field%nbuf + 1 - if(field%nbuf > size(field%data,4).or.field%nbuf <= 0) field%nbuf = 1 - ib = field%nbuf - field%ibuf(ib) = rec - field%need_compute(ib,:) = .true. - - if (debug_this_module) write(outunit,*) 'reading record without domain for field ',trim(field%name) - start = 1; nread = 1 - start(1) = field%is_src; nread(1) = field%ie_src - field%is_src + 1 - start(2) = field%js_src; nread(2) = field%je_src - field%js_src + 1 - start(3) = 1; nread(3) = size(field%src_data,3) - start(field%tdim) = rec; nread(field%tdim) = 1 - call read_data(field%fileobj,field%name,field%src_data(:,:,:,ib:ib),corner=start,edge_lengths=nread) - endif -!$OMP END CRITICAL - isw=field%isc;iew=field%iec - jsw=field%jsc;jew=field%jec - - if( field%numwindows > 1) then - if( .NOT. PRESENT(is_in) .OR. .NOT. PRESENT(ie_in) .OR. .NOT. PRESENT(js_in) .OR. .NOT. PRESENT(je_in) ) then - call mpp_error(FATAL, & - & 'time_interp_external(load_record): is_in, ie_in, js_in, je_in must be present when numwindows>1') - endif - isw = isw + is_in - 1 - iew = isw + ie_in - is_in - jsw = jsw + js_in - 1 - jew = jsw + je_in - js_in - endif - - ! interpolate to target grid - - need_compute = field%need_compute(ib, window_id) - if(need_compute) then - if(PRESENT(interp)) then - is_region = field%is_region; ie_region = field%ie_region - js_region = field%js_region; je_region = field%je_region - mask_in = 0.0 - where (is_valid(field%src_data(:,:,:,ib), field%valid)) mask_in = 1.0 - if ( field%region_type .NE. NO_REGION ) then - if( ANY(mask_in == 0.0) ) then - call mpp_error(FATAL, "time_interp_external: mask_in should be all 1 when region_type is not NO_REGION") - endif - if( field%region_type == OUTSIDE_REGION) then - do j = js_region, je_region - do i = is_region, ie_region - mask_in(i,j,:) = 0.0 - enddo - enddo - else ! field%region_choice == INSIDE_REGION - do j = 1, size(mask_in,2) - do i = 1, size(mask_in,1) - if( jje_region .OR. iie_region ) mask_in(i,j,:) = 0.0 - enddo - enddo - endif - endif - allocate(mask_out(isw:iew,jsw:jew, size(field%src_data,3))) - call horiz_interp(interp,field%src_data(:,:,:,ib),field%data(isw:iew,jsw:jew,:,ib), & - mask_in=mask_in, & - mask_out=mask_out) - - field%mask(isw:iew,jsw:jew,:,ib) = mask_out(isw:iew,jsw:jew,:) > 0 - deallocate(mask_out) - else - if ( field%region_type .NE. NO_REGION ) then - call mpp_error(FATAL, "time_interp_external: region_type should be NO_REGION when interp is not present") - endif - field%data(isw:iew,jsw:jew,:,ib) = field%src_data(isw:iew,jsw:jew,:,ib) - field%mask(isw:iew,jsw:jew,:,ib) = is_valid(field%data(isw:iew,jsw:jew,:,ib),field%valid) - endif - ! convert units - where(field%mask(isw:iew,jsw:jew,:,ib)) field%data(isw:iew,jsw:jew,:,ib) = & - field%data(isw:iew,jsw:jew,:,ib)*field%slope + field%intercept - field%need_compute(ib, window_id) = .false. - endif - -end subroutine load_record - - -subroutine load_record_0d(field, rec) - type(ext_fieldtype), intent(inout) :: field - integer , intent(in) :: rec ! record number - ! ---- local vars - integer :: ib ! index in the array of input buffers - integer :: start(4), nread(4) - - ib = find_buf_index(rec,field%ibuf) - - if(ib>0) then - return - else - ! calculate current buffer number in round-robin fasion - field%nbuf = field%nbuf + 1 - if(field%nbuf > size(field%data,4).or.field%nbuf <= 0) field%nbuf = 1 - ib = field%nbuf - field%ibuf(ib) = rec - - if (debug_this_module) write(outunit,*) 'reading record without domain for field ',trim(field%name) - start = 1; nread = 1 - start(3) = 1; nread(3) = size(field%src_data,3) - start(field%tdim) = rec; nread(field%tdim) = 1 - call read_data(field%fileobj,field%name,field%src_data(:,:,:,ib),corner=start,edge_lengths=nread) - if ( field%region_type .NE. NO_REGION ) then - call mpp_error(FATAL, "time_interp_external: region_type should be NO_REGION when field is scalar") - endif - field%data(1,1,:,ib) = field%src_data(1,1,:,ib) - field%mask(1,1,:,ib) = is_valid(field%data(1,1,:,ib),field%valid) - ! convert units - where(field%mask(1,1,:,ib)) field%data(1,1,:,ib) = & - field%data(1,1,:,ib)*field%slope + field%intercept - endif - -end subroutine load_record_0d - -! ============================================================================ -subroutine reset_src_data_region(index, is, ie, js, je) - integer, intent(in) :: index - integer, intent(in) :: is, ie, js, je - integer :: nk, nbuf - - if( is == field(index)%is_src .AND. ie == field(index)%ie_src .AND. & - js == field(index)%js_src .AND. ie == field(index)%je_src ) return - - if( .NOT. ASSOCIATED(field(index)%src_data) ) call mpp_error(FATAL, & - "time_interp_external: field(index)%src_data is not associated") - nk = size(field(index)%src_data,3) - nbuf = size(field(index)%src_data,4) - deallocate(field(index)%src_data) - allocate(field(index)%src_data(is:ie,js:je,nk,nbuf)) - field(index)%is_src = is - field(index)%ie_src = ie - field(index)%js_src = js - field(index)%je_src = je - - -end subroutine reset_src_data_region - -! ============================================================================ -subroutine set_override_region(index, region_type, is_region, ie_region, js_region, je_region) - integer, intent(in) :: index, region_type - integer, intent(in) :: is_region, ie_region, js_region, je_region - - field(index)%region_type = region_type - field(index)%is_region = is_region - field(index)%ie_region = ie_region - field(index)%js_region = js_region - field(index)%je_region = je_region - - return - -end subroutine set_override_region - -! ============================================================================ -! reallocates array of fields, increasing its size -subroutine realloc_files(n) - integer, intent(in) :: n ! new size - - type(filetype), pointer :: ptr(:) - integer :: i - - if (associated(opened_files)) then - if (n <= size(opened_files)) return ! do nothing, if requested size no more than current - endif - - allocate(ptr(n)) - do i = 1, size(ptr) - ptr(i)%filename = '' - if(Associated(ptr(i)%fileobj)) then - call close_file(ptr(i)%fileobj) - deallocate(ptr(i)%fileobj) - endif - enddo - - if (associated(opened_files))then - ptr(1:size(opened_files)) = opened_files(:) - deallocate(opened_files) - endif - opened_files => ptr - -end subroutine realloc_files + end subroutine TIME_INTERP_EXTERNAL_0D_ ! ============================================================================ -! reallocates array of fields,increasing its size -subroutine realloc_fields(n) - integer, intent(in) :: n ! new size - - type(ext_fieldtype), pointer :: ptr(:) - integer :: i, ier - - if (associated(field)) then - if (n <= size(field)) return ! do nothing if requested size no more then current - endif - - allocate(ptr(n)) - do i=1,size(ptr) - ptr(i)%fileobj => NULL() - ptr(i)%name='' - ptr(i)%units='' - ptr(i)%siz=-1 - ptr(i)%ndim=-1 - ptr(i)%domain = NULL_DOMAIN2D - if (ASSOCIATED(ptr(i)%time)) DEALLOCATE(ptr(i)%time, stat=ier) - if (ASSOCIATED(ptr(i)%start_time)) DEALLOCATE(ptr(i)%start_time, stat=ier) - if (ASSOCIATED(ptr(i)%end_time)) DEALLOCATE(ptr(i)%end_time, stat=ier) - if (ASSOCIATED(ptr(i)%period)) DEALLOCATE(ptr(i)%period, stat=ier) - ptr(i)%modulo_time=.false. - if (ASSOCIATED(ptr(i)%data)) DEALLOCATE(ptr(i)%data, stat=ier) - if (ASSOCIATED(ptr(i)%ibuf)) DEALLOCATE(ptr(i)%ibuf, stat=ier) - if (ASSOCIATED(ptr(i)%src_data)) DEALLOCATE(ptr(i)%src_data, stat=ier) - ptr(i)%nbuf=-1 - ptr(i)%domain_present=.false. - ptr(i)%slope=1.0 - ptr(i)%intercept=0.0 - ptr(i)%isc=-1;ptr(i)%iec=-1 - ptr(i)%jsc=-1;ptr(i)%jec=-1 - enddo - if (associated(field)) then - ptr(1:size(field)) = field(:) - deallocate(field) - endif - field=>ptr - -end subroutine realloc_fields - - - function find_buf_index(indx,buf) - integer :: indx - integer, dimension(:) :: buf - integer :: find_buf_index - - integer :: nbuf, i - - nbuf = size(buf(:)) - - find_buf_index = -1 - - do i=1,nbuf - if (buf(i) == indx) then - find_buf_index = i - exit - endif - enddo - - end function find_buf_index - -! -! -! -! return size of field after call to init_external_field. -! Ordering is X/Y/Z/T. -! This call only makes sense for non-distributed reads. -! -! -! -! returned from previous call to init_external_field. -! - - function get_external_field_size(index) - - integer :: index - integer :: get_external_field_size(4) - - if (index .lt. 1 .or. index .gt. num_fields) & - call mpp_error(FATAL,'invalid index in call to get_external_field_size') - - - get_external_field_size(1) = field(index)%siz(1) - get_external_field_size(2) = field(index)%siz(2) - get_external_field_size(3) = field(index)%siz(3) - get_external_field_size(4) = field(index)%siz(4) - - end function get_external_field_size -! NAME="get_external_field_size" - - -! -! -! -! return missing value -! -! -! -! returned from previous call to init_external_field. -! - - function get_external_field_missing(index) - - integer :: index - real :: get_external_field_missing - - if (index .lt. 1 .or. index .gt. num_fields) & - call mpp_error(FATAL,'invalid index in call to get_external_field_size') - - - get_external_field_missing = field(index)%missing - - end function get_external_field_missing -! NAME="get_external_field_missing" - -! =========================================================================== -subroutine get_time_axis(index, time) - integer , intent(in) :: index ! field id - type(time_type), intent(out) :: time(:) ! array of time values to be filled - - integer :: n ! size of the data to be assigned - - if (index < 1.or.index > num_fields) & - call mpp_error(FATAL,'invalid index in call to get_time_axis') - - n = min(size(time),size(field(index)%time)) - - time(1:n) = field(index)%time(1:n) -end subroutine - -! -! -! -! exit time_interp_external_mod. Close all open files and -! release storage -! - - subroutine time_interp_external_exit() - - integer :: i -! -! release storage arrays -! - do i=1,num_fields - deallocate(field(i)%time,field(i)%start_time,field(i)%end_time,& - field(i)%period,field(i)%data,field(i)%mask,field(i)%ibuf) - if (ASSOCIATED(field(i)%src_data)) deallocate(field(i)%src_data) - field(i)%domain = NULL_DOMAIN2D - field(i)%nbuf = 0 - field(i)%slope = 0. - field(i)%intercept = 0. - enddo - - !-- close all the files opended - do i = 1, num_files - call close_file(opened_files(i)%fileobj) - deallocate(opened_files(i)%fileobj) - enddo - - deallocate(field) - deallocate(opened_files) - - num_fields = 0 - - module_initialized = .false. - - end subroutine time_interp_external_exit -! NAME="time_interp_external_exit" - -end module time_interp_external2_mod -!> @} -! close documentation grouping diff --git a/time_interp/include/time_interp_external2_r4.fh b/time_interp/include/time_interp_external2_r4.fh new file mode 100644 index 0000000000..401e42c845 --- /dev/null +++ b/time_interp/include/time_interp_external2_r4.fh @@ -0,0 +1,31 @@ +!*********************************************************************** +!* GNU Lesser General Public License +!* +!* This file is part of the GFDL Flexible Modeling System (FMS). +!* +!* FMS is free software: you can redistribute it and/or modify it under +!* the terms of the GNU Lesser General Public License as published by +!* the Free Software Foundation, either version 3 of the License, or (at +!* your option) any later version. +!* +!* 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 Lesser General Public +!* License along with FMS. If not, see . +!*********************************************************************** +#undef FMS_TI_KIND_ +#define FMS_TI_KIND_ r4_kind + +#undef TIME_INTERP_EXTERNAL_0D_ +#define TIME_INTERP_EXTERNAL_0D_ time_interp_external_0d_r4 + +#undef TIME_INTERP_EXTERNAL_2D_ +#define TIME_INTERP_EXTERNAL_2D_ time_interp_external_2d_r4 + +#undef TIME_INTERP_EXTERNAL_3D_ +#define TIME_INTERP_EXTERNAL_3D_ time_interp_external_3d_r4 + +#include "time_interp_external2.inc" \ No newline at end of file diff --git a/time_interp/include/time_interp_external2_r8.fh b/time_interp/include/time_interp_external2_r8.fh new file mode 100644 index 0000000000..35ee1907b4 --- /dev/null +++ b/time_interp/include/time_interp_external2_r8.fh @@ -0,0 +1,31 @@ +!*********************************************************************** +!* GNU Lesser General Public License +!* +!* This file is part of the GFDL Flexible Modeling System (FMS). +!* +!* FMS is free software: you can redistribute it and/or modify it under +!* the terms of the GNU Lesser General Public License as published by +!* the Free Software Foundation, either version 3 of the License, or (at +!* your option) any later version. +!* +!* 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 Lesser General Public +!* License along with FMS. If not, see . +!*********************************************************************** +#undef FMS_TI_KIND_ +#define FMS_TI_KIND_ r8_kind + +#undef TIME_INTERP_EXTERNAL_0D_ +#define TIME_INTERP_EXTERNAL_0D_ time_interp_external_0d_r8 + +#undef TIME_INTERP_EXTERNAL_2D_ +#define TIME_INTERP_EXTERNAL_2D_ time_interp_external_2d_r8 + +#undef TIME_INTERP_EXTERNAL_3D_ +#define TIME_INTERP_EXTERNAL_3D_ time_interp_external_3d_r8 + +#include "time_interp_external2.inc" \ No newline at end of file diff --git a/time_interp/include/time_interp_r4.fh b/time_interp/include/time_interp_r4.fh new file mode 100644 index 0000000000..fb3e822cd1 --- /dev/null +++ b/time_interp/include/time_interp_r4.fh @@ -0,0 +1,43 @@ +!*********************************************************************** +!* GNU Lesser General Public License +!* +!* This file is part of the GFDL Flexible Modeling System (FMS). +!* +!* FMS is free software: you can redistribute it and/or modify it under +!* the terms of the GNU Lesser General Public License as published by +!* the Free Software Foundation, either version 3 of the License, or (at +!* your option) any later version. +!* +!* 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 Lesser General Public +!* License along with FMS. If not, see . +!*********************************************************************** +#undef FMS_TI_KIND_ +#define FMS_TI_KIND_ r4_kind + +#undef TIME_INTERP_FRAC_ +#define TIME_INTERP_FRAC_ time_interp_frac_r4 + +#undef TIME_INTERP_DAY_ +#define TIME_INTERP_DAY_ time_interp_day_r4 + +#undef TIME_INTERP_MONTH_ +#define TIME_INTERP_MONTH_ time_interp_month_r4 + +#undef TIME_INTERP_YEAR_ +#define TIME_INTERP_YEAR_ time_interp_year_r4 + +#undef TIME_INTERP_LIST_ +#define TIME_INTERP_LIST_ time_interp_list_r4 + +#undef TIME_INTERP_MODULO_ +#define TIME_INTERP_MODULO_ time_interp_modulo_r4 + +#undef FRACTION_OF_YEAR_ +#define FRACTION_OF_YEAR_ fraction_of_year_r4 + +#include "time_interp.inc" \ No newline at end of file diff --git a/time_interp/include/time_interp_r8.fh b/time_interp/include/time_interp_r8.fh new file mode 100644 index 0000000000..561b7396e9 --- /dev/null +++ b/time_interp/include/time_interp_r8.fh @@ -0,0 +1,43 @@ +!*********************************************************************** +!* GNU Lesser General Public License +!* +!* This file is part of the GFDL Flexible Modeling System (FMS). +!* +!* FMS is free software: you can redistribute it and/or modify it under +!* the terms of the GNU Lesser General Public License as published by +!* the Free Software Foundation, either version 3 of the License, or (at +!* your option) any later version. +!* +!* 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 Lesser General Public +!* License along with FMS. If not, see . +!*********************************************************************** +#undef FMS_TI_KIND_ +#define FMS_TI_KIND_ r8_kind + +#undef TIME_INTERP_FRAC_ +#define TIME_INTERP_FRAC_ time_interp_frac_r8 + +#undef TIME_INTERP_DAY_ +#define TIME_INTERP_DAY_ time_interp_day_r8 + +#undef TIME_INTERP_MONTH_ +#define TIME_INTERP_MONTH_ time_interp_month_r8 + +#undef TIME_INTERP_YEAR_ +#define TIME_INTERP_YEAR_ time_interp_year_r8 + +#undef TIME_INTERP_LIST_ +#define TIME_INTERP_LIST_ time_interp_list_r8 + +#undef TIME_INTERP_MODULO_ +#define TIME_INTERP_MODULO_ time_interp_modulo_r8 + +#undef FRACTION_OF_YEAR_ +#define FRACTION_OF_YEAR_ fraction_of_year_r8 + +#include "time_interp.inc" \ No newline at end of file diff --git a/time_interp/time_interp.F90 b/time_interp/time_interp.F90 index 87e146714a..521f0e32f0 100644 --- a/time_interp/time_interp.F90 +++ b/time_interp/time_interp.F90 @@ -44,6 +44,7 @@ module time_interp_mod check_nml_error, & fms_error_handler use mpp_mod, only: input_nml_file +use platform_mod implicit none private @@ -52,134 +53,6 @@ module time_interp_mod public :: time_interp_init, time_interp, fraction_of_year -! - -! -! Returns a weight and dates or indices for interpolating between two dates. The -! interface fraction_of_year is provided for backward compatibility with the -! previous version. -! -! -! Returns weight by interpolating Time between Time1 and Time2. -! i.e. weight = (Time-Time1)/(Time2-Time1) -! Time1 and Time2 may be specified by any of several different ways, -! which is the reason for multiple interfaces. - -! If Time1 and Time2 are the begining and end of the year in which -! Time falls, use first interface. - -! If Time1 and Time2 fall on year boundaries, use second interface. - -! If Time1 and Time2 fall on month boundaries, use third. - -! If Time1 and Time2 fall on day boundaries, use fourth. - -! If Time1 and Time2 are consecutive elements of an assending list, use fifth. -! The fifth also returns the indices of Timelist between which Time falls. - -! The sixth interface is for cyclical data. Time_beg and Time_end specify the -! begining and end of a repeating period. In this case: -! weight = (Time_adjusted - Time1) / (Time2 - Time1) -! Where: -! Time1 = Timelist(index1) -! Time2 = Timelist(index2) -! Time_adjusted = Time - N*Period -! Period = Time_end-Time_beg -! N is between (Time-Time_end)/Period and (Time-Time_beg)/Period -! That is, N is the integer that results in Time_adjusted that is between Time_beg and Time_end. -! -! -! -! -! -! -! -! -! -! The time at which the the weight is computed. -! -! -! For cyclical interpolation: Time_beg specifies the begining time of a cycle. -! -! -! For cyclical interpolation: Time_end specifies the ending time of a cycle. -! -! -! For cyclical interpolation: Timelist is an array of times between Time_beg and Time_end. -! Must be monotonically increasing. -! -! -! -! -! Timelist(index1) = The largest value of Timelist which is less than mod(Time,Time_end-Time_beg) -! -! -! Timelist(index2) = The smallest value of Timelist which is greater than mod(Time,Time_end-Time_beg) -! -! -! Turns on a kluge for an inconsistency which may occur in a special case. -! When the modulo time period (i.e. Time_end - Time_beg) is a whole number of years -! and is not a multiple of 4, and the calendar in use has leap years, then it is -! likely that the interpolation will involve mapping a common year onto a leap year. -! In this case it is often desirable, but not absolutely necessary, to use data for -! Feb 28 of the leap year when it is mapped onto a common year. -! To turn this on, set correct_leap_year_inconsistency=.true. -! -! -! weight = (mod(Time,Time_end-Time_beg) - Timelist(index1)) / (Timelist(index2) - Timelist(index1)) -! -! -! -! -! -! -! -! -! -! -! The list of input time types must have ascending dates. -! -! -! The length of the current month for input Time and Time_list -! must be the same when using the modulo month option. The -! modulo month option is available but not supported. -! -! -! The optional argument modtime must have a value set by one -! of the public parameters: NONE, YEAR, MONTH, DAY. The -! MONTH and DAY options are available but not supported. -! -! -! The difference between the last and first values in the input -! Time list/array exceeds the length of the modulo period. -! -! -! The difference between the last and first values in the input -! These errors occur when you are not using a modulo axis and -! the input Time occurs before the first value in the Time -! list/array or after the last value in the Time list/array. -! -! -! Examples: -!
-!       Time: Jan 01 00z    weight = 0.0
-!       Time: Jul 01        weight ~ 0.5
-!       Time: Dec 31 23z    weight ~ 1.0
-!     
-!
- !> Returns a weight and dates or indices for interpolating between two dates. The !! interface fraction_of_year is provided for backward compatibility with the !! previous version.\n @@ -224,13 +97,41 @@ module time_interp_mod !! call time_interp( Time, Time_beg, Time_end, Timelist, weight, index1, index2 !! [,correct_leap_year_inconsistency]) !! @endcode +!! +!! For all routines in this module the calendar type in module +!! time_manager must be set. +!! +!! The following private parameters are set by this module: +!! +!! seconds per minute = 60 +!! minutes per hour = 60 +!! hours per day = 24 +!! months per year = 12 +!! +!! @param Time The time at which the the weight is computed. +!! @param Time_beg For cyclical interpolation: Time_beg specifies the begining time of a cycle. +!! @param Time_end For cyclical interpolation: Time_end specifies the ending time of a cycle. +!! @param Timelist For cyclical interpolation: Timelist is an array of times between Time_beg and Time_end. +!! Must be monotonically increasing. +!! @param index1 Timelist(index1) = The largest value of Timelist which is less than mod(Time,Time_end-Time_beg) +!! @param index2 Timelist(index2) = The smallest value of Timelist which is greater than mod(Time,Time_end-Time_beg) +!! @param correct_leap_year_inconsistency Turns on a kluge for an inconsistency which may occur in a special case. +!! When the modulo time period (i.e. Time_end - Time_beg) is a whole number of years +!! and is not a multiple of 4, and the calendar in use has leap years, then it is +!! likely that the interpolation will involve mapping a common year onto a leap year. +!! In this case it is often desirable, but not absolutely necessary, to use data for +!! Feb 28 of the leap year when it is mapped onto a common year. +!! To turn this on, set correct_leap_year_inconsistency=.true. +!! @param weight weight = (mod(Time,Time_end-Time_beg) - Timelist(index1)) / (Timelist(index2) - Timelist(index1)) !> @ingroup time_interp_mod interface time_interp - module procedure time_interp_frac, time_interp_year, & - time_interp_month, time_interp_day, & - time_interp_list, time_interp_modulo + module procedure time_interp_frac_r8, time_interp_year_r8, & + time_interp_month_r8, time_interp_day_r8, & + time_interp_list_r8, time_interp_modulo_r8 + module procedure time_interp_frac_r4, time_interp_year_r4, & + time_interp_month_r4, time_interp_day_r4, & + time_interp_list_r4, time_interp_modulo_r4 end interface -!
!> @addtogroup time_interp_mod !> @{ @@ -239,7 +140,7 @@ module time_interp_mod !----------------------------------------------------------------------- integer, parameter :: secmin = 60, minhour = 60, hourday = 24, & - sechour = secmin*minhour, & + sechour = secmin*minhour, & secday = secmin*minhour*hourday integer, parameter :: monyear = 12 @@ -275,396 +176,19 @@ subroutine time_interp_init() end subroutine time_interp_init -!####################################################################### - -! -! -! -! - !> @brief Calculates the fractional time into the current year - subroutine time_interp_frac ( Time, weight ) - - type(time_type), intent(in) :: Time - real , intent(out) :: weight !< fractional time - - integer :: yr, mo, dy, hour, minute, second - type(time_type) :: Year_beg, Year_end - - - if ( .not. module_is_initialized ) call time_interp_init - -! ---- compute fractional time of year ----- - - call get_date (Time, yr, mo, dy, hour, minute, second) - - Year_beg = set_date(yr , 1, 1) - Year_end = set_date(yr+1, 1, 1) - - weight = real( (Time - Year_beg) // (Year_end - Year_beg) ) - - end subroutine time_interp_frac - -!####################################################################### -! -! -! Wrapper for backward compatibility -! -! !> @brief Wrapper function to return the fractional time into the current year +!! Always returns an r8_kind, conversion to r4 will be done implicitly if needed !> @param Time time to calculate fraction with -!> @return real fraction of time passed in current year +!> @return real(kind=8) fraction of time passed in current year function fraction_of_year (Time) type(time_type), intent(in) :: Time - real :: fraction_of_year + real(r8_kind) :: fraction_of_year - call time_interp_frac ( Time, fraction_of_year ) + call time_interp ( Time, fraction_of_year ) end function fraction_of_year -!####################################################################### -! -! -! -! -! -! -! returns fractional time between mid points of consecutive years - - !> @brief Calculates fractional time between mid points of consecutive years - subroutine time_interp_year ( Time, weight, year1, year2 ) - - type(time_type), intent(in) :: Time - real , intent(out) :: weight !< fractional time between midpoints of year1 and year2 - integer , intent(out) :: year1, year2 - - integer :: yr, mo, dy, hour, minute, second - type (time_type) :: Mid_year, Mid_year1, Mid_year2 - - - if ( .not. module_is_initialized ) call time_interp_init() - - call get_date (Time, yr, mo, dy, hour, minute, second) - - ! mid point of current year - Mid_year = year_midpt(yr) - - if ( Time >= Mid_year ) then - ! current time is after mid point of current year - year1 = yr - year2 = yr+1 - Mid_year2 = year_midpt(year2) - weight = real( (Time - Mid_year) // (Mid_year2 - Mid_year) ) - else - ! current time is before mid point of current year - year2 = yr - year1 = yr-1 - Mid_year1 = year_midpt(year1) - weight = real( (Time - Mid_year1) // (Mid_year - Mid_year1) ) - endif - - end subroutine time_interp_year - -!####################################################################### -! -! -! -! -! -! -! -! - !> @brief Calculates fractional time between mid points of consecutive months - subroutine time_interp_month ( Time, weight, year1, year2, month1, month2 ) - - type(time_type), intent(in) :: Time - real , intent(out) :: weight - integer , intent(out) :: year1, year2, month1, month2 - - integer :: yr, mo, dy, hour, minute, second, & - mid_month, cur_month, mid1, mid2 - - if ( .not. module_is_initialized ) call time_interp_init() - - call get_date (Time, yr, mo, dy, hour, minute, second) - - ! mid point of current month in seconds - mid_month = days_in_month(Time) * halfday - ! time into current month in seconds - cur_month = second + secmin*minute + sechour*hour + secday*(dy-1) - - if ( cur_month >= mid_month ) then - ! current time is after mid point of current month - year1 = yr; month1 = mo - year2 = yr; month2 = mo+1 - if (month2 > monyear) then - year2 = year2+1; month2 = 1 - endif - mid1 = mid_month - mid2 = days_in_month(set_date(year2,month2,2)) * halfday - weight = real(cur_month - mid1) / real(mid1+mid2) - else - ! current time is before mid point of current month - year2 = yr; month2 = mo - year1 = yr; month1 = mo-1 - if (month1 < 1) then - year1 = year1-1; month1 = monyear - endif - if (year1>0) then - mid1 = days_in_month(set_date(year1,month1,2)) * halfday - else - ! this can happen if we are at the beginning of year 1. In this case - ! use December 0001 to calculate the duration of December 0000. - ! This should work for all calendars - mid1 = days_in_month(set_date(1,month1,2)) * halfday - endif - mid2 = mid_month - weight = real(cur_month + mid1) / real(mid1+mid2) - endif - - end subroutine time_interp_month - -!####################################################################### -! -! -! -! -! -! -! -! -! -! - !> @brief Calculates fractional time between mid points of consecutive days - subroutine time_interp_day ( Time, weight, year1, year2, month1, month2, day1, day2 ) - - type(time_type), intent(in) :: Time - real , intent(out) :: weight - integer , intent(out) :: year1, year2, month1, month2, day1, day2 - - integer :: yr, mo, dy, hour, minute, second, sday - - if ( .not. module_is_initialized ) call time_interp_init() - - call get_date (Time, yr, mo, dy, hour, minute, second) - - ! time into current day in seconds - sday = second + secmin*minute + sechour*hour - - if ( sday >= halfday ) then - ! current time is after mid point of day - year1 = yr; month1 = mo; day1 = dy - year2 = yr; month2 = mo; day2 = dy + 1 - weight = real(sday - halfday) / real(secday) - - if (day2 > days_in_month(Time)) then - month2 = month2 + 1 - day2 = 1 - if (month2 > monyear) then - month2 = 1; year2 = year2+1 - endif - endif - else - ! current time is before mid point of day - year2 = yr; month2 = mo ; day2 = dy - year1 = yr; month1 = mo; day1 = dy - 1 - weight = real(sday + halfday) / real(secday) - - if (day1 < 1) then - month1 = month1 - 1 - if (month1 < 1) then - month1 = monyear; year1 = year1-1 - endif - day1 = days_in_month(set_date(year1,month1,2)) - endif - endif - - end subroutine time_interp_day - -!####################################################################### -! -! -! -! -! -! -! Turns on a kluge for an inconsistency which may occur in a special case. -! When the modulo time period (i.e. Time_end - Time_beg) is a whole number of years -! and is not a multiple of 4, and the calendar in use has leap years, then it is -! likely that the interpolation will involve mapping a common year onto a leap year. -! In this case it is often desirable, but not absolutely necessary, to use data for -! Feb 28 of the leap year when it is mapped onto a common year. -! To turn this on, set correct_leap_year_inconsistency=.true. -! -! -! -! - -subroutine time_interp_modulo(Time, Time_beg, Time_end, Timelist, weight, index1, index2, & - correct_leap_year_inconsistency, err_msg) -type(time_type), intent(in) :: Time, Time_beg, Time_end, Timelist(:) -real , intent(out) :: weight -integer , intent(out) :: index1, index2 -logical, intent(in), optional :: correct_leap_year_inconsistency!< When true turns on a kluge for an - !! inconsistency which may occur in a special case. - !! When the modulo time period (i.e. Time_end - Time_beg) is a - !! whole number of years and is not a multiple of 4, and the calendar - !! in use has leap years, then it is likely that the interpolation - !! will involve mapping a common year onto a leap year. In this case - !! it is often desirable, but not absolutely necessary, to use data - !! for Feb 28 of the leap year when it is mapped onto a common year. -character(len=*), intent(out), optional :: err_msg - - type(time_type) :: Period, T - integer :: is, ie,i1,i2 - integer :: ys,ms,ds,hs,mins,ss ! components of the starting date - integer :: ye,me,de,he,mine,se ! components of the ending date - integer :: yt,mt,dt,ht,mint,st ! components of the current date - integer :: dt1 ! temporary value for day - integer :: n ! size of Timelist - integer :: stdoutunit - logical :: correct_lyr, calendar_has_leap_years, do_the_lyr_correction - - if ( .not. module_is_initialized ) call time_interp_init - if( present(err_msg) ) err_msg = '' - - stdoutunit = stdout() - n = size(Timelist) - - if (Time_beg>=Time_end) then - if(fms_error_handler('time_interp_modulo', & - 'end of the specified time loop interval must be later than its beginning',err_msg)) return - endif - - calendar_has_leap_years = (get_calendar_type() == JULIAN .or. get_calendar_type() == GREGORIAN) - - Period = Time_end-Time_beg ! period of the time axis - - if(present(correct_leap_year_inconsistency)) then - correct_lyr = correct_leap_year_inconsistency - else - correct_lyr = .false. - endif - - ! bring the requested time inside the specified time period - T = Time - - do_the_lyr_correction = .false. - - ! Determine if the leap year correction needs to be done. - ! It never needs to be done unless 3 conditions are met: - ! 1) We are using a calendar with leap years - ! 2) optional argument correct_leap_year_inconsistency is present and equals .true. - ! 3) The modulo time period is an integer number of years - ! If all of these are true then set do_the_lyr_correction to .true. - - if(calendar_has_leap_years .and. correct_lyr) then - call get_date(Time_beg,ys,ms,ds,hs,mins,ss) - call get_date(Time_end,ye,me,de,he,mine,se) - if(ms==me.and.ds==de.and.hs==he.and.mins==mine.and.ss==se) then - ! whole number of years - do_the_lyr_correction = .true. - endif - endif - - if(do_the_lyr_correction) then - call get_date(T,yt,mt,dt,ht,mint,st) - yt = ys+modulo(yt-ys,ye-ys) - dt1 = dt - ! If it is Feb 29, but we map into a common year, use Feb 28 - if(mt==2.and.dt==29.and..not.leap_year(set_date(yt,1,1))) dt1=28 - T = set_date(yt,mt,dt1,ht,mint,st) - if (T < Time_beg) then - ! the requested time is within the first year, - ! but before the starting date. So we shift it to the last year. - if(mt==2.and.dt==29.and..not.leap_year(set_date(ye,1,1))) dt=28 - T = set_date(ye,mt,dt,ht,mint,st) - endif - else - do while ( T >= Time_end ) - T = T-Period - enddo - do while ( T < Time_beg ) - T = T+Period - enddo - endif - - ! find indices of the first and last records in the Timelist that are within - ! the requested time period. - if (Time_end<=Timelist(1).or.Time_beg>=Timelist(n)) then - if(get_calendar_type() == NO_CALENDAR) then - call print_time(Time_beg, 'Time_beg' ) - call print_time(Time_end, 'Time_end' ) - call print_time(Timelist(1), 'Timelist(1)' ) - call print_time(Timelist(n), 'Timelist(n)' ) - else - call print_date(Time_beg, 'Time_beg' ) - call print_date(Time_end, 'Time_end' ) - call print_date(Timelist(1), 'Timelist(1)' ) - call print_date(Timelist(n), 'Timelist(n)' ) - endif - write(stdoutunit,*)'where n = size(Timelist) =',n - if(fms_error_handler('time_interp_modulo', & - 'the entire time list is outside the specified time loop interval',err_msg)) return - endif - - call bisect(Timelist,Time_beg,index1=i1,index2=i2) - if (i1 < 1) then - is = 1 ! Time_beg before lower boundary - else if (Time_beg == Timelist(i1)) then - is = i1 ! Time_beg right on the lower boundary - else - is = i2 ! Time_beg inside the interval or on upper boundary - endif - call bisect(Timelist,Time_end,index1=i1,index2=i2) - if (Time_end > Timelist(i1)) then - ie = i1 - else if (Time_end == Timelist(i1)) then - if(Time_beg == Timelist(is)) then - ! Timelist includes time levels at both the lower and upper ends of the period. - ! The endpoints of Timelist specify the same point in the cycle. - ! This ambiguity is resolved by ignoring the last time level. - ie = i1-1 - else - ie = i1 - endif - else -! This should never happen because bisect does not return i1 such that Time_end < Timelist(i1) - endif - if (is>=ie) then - if(get_calendar_type() == NO_CALENDAR) then - call print_time(Time_beg, 'Time_beg =') - call print_time(Time_end, 'Time_end =') - call print_time(Timelist(1), 'Timelist(1)=') - call print_time(Timelist(n), 'Timelist(n)=') - else - call print_date(Time_beg, 'Time_beg =') - call print_date(Time_end, 'Time_end =') - call print_date(Timelist(1), 'Timelist(1)=') - call print_date(Timelist(n), 'Timelist(n)=') - endif - write(stdoutunit,*)'where n = size(Timelist) =',n - write(stdoutunit,*)'is =',is,'ie =',ie - if(fms_error_handler('time_interp_modulo', & - 'error in calculation of time list bounds within the specified time loop interval',err_msg)) return - endif - - ! handle special cases: - if( T>=Timelist(ie) ) then - ! time is after the end of the portion of the time list within the requested period - index1 = ie; index2 = is - weight = real( (T-Timelist(ie))//(Period-(Timelist(ie)-Timelist(is))) ) - else if (T Given an array of times in ascending order and a specific time returns !! values of index1 and index2 such that the Timelist(index1)<=Time and @@ -701,142 +225,6 @@ subroutine bisect(Timelist,Time,index1,index2) if(PRESENT(index2)) index2 = i2 end subroutine bisect - -!####################################################################### -! -! -! -! -! -! -! -! - -subroutine time_interp_list ( Time, Timelist, weight, index1, index2, modtime, err_msg ) -type(time_type) , intent(in) :: Time, Timelist(:) -real , intent(out) :: weight -integer , intent(out) :: index1, index2 -integer, optional, intent(in) :: modtime -character(len=*), intent(out), optional :: err_msg - -integer :: n, hr, mn, se, mtime -type(time_type) :: T, Ts, Te, Td, Period, Time_mod -character(len=:),allocatable :: terr, tserr, teerr - - if ( .not. module_is_initialized ) call time_interp_init - - if( present(err_msg) ) err_msg = '' - - weight = 0.; index1 = 0; index2 = 0 - n = size(Timelist(:)) - -! setup modular time axis? - mtime = NONE - if (present(modtime)) then - mtime = modtime - Time_mod = (Timelist(1)+Timelist(n))/2 - call get_date (Time_mod, yrmod, momod, dymod, hr, mn, se) - mod_leapyear = leap_year(Time_mod) - endif - -! set period for modulo axis - select case (mtime) - case (NONE) - ! do nothing - case (YEAR) - Period = set_time(0,days_in_year(Time_mod)) - case (MONTH) - ! month length must be equal - if (days_in_month(Time_mod) /= days_in_month(Time)) then - if(fms_error_handler ('time_interp_list','modulo months must have same length',err_msg)) return - endif - Period = set_time(0,days_in_month(Time_mod)) - case (DAY) - Period = set_time(0,1) - case default - if(fms_error_handler ('time_interp_list','invalid value for argument modtime',err_msg)) return - end select - -! If modulo time is in effect and Timelist spans a time interval exactly equal to -! the modulo period, then the endpoints of Timelist specify the same point in the cycle. -! This ambiguity is resolved by ignoring the last time level. - if (mtime /= NONE .and. Timelist(size(Timelist))-Timelist(1) == Period) then - n = size(Timelist) - 1 - else - n = size(Timelist) - endif - -! starting and ending times from list - Ts = Timelist(1) - Te = Timelist(n) - Td = Te-Ts - T = set_modtime(Time,mtime) - -! Check that Timelist does not span a time interval greater than the modulo period - if (mtime /= NONE) then - if (Td > Period) then - if(fms_error_handler ('time_interp_list','period of list exceeds modulo period',err_msg)) return - endif - endif - -! time falls on start or between start and end list values - if ( T >= Ts .and. T < Te ) then - call bisect(Timelist(1:n),T,index1,index2) - weight = real( (T-Timelist(index1)) // (Timelist(index2)-Timelist(index1)) ) - -! time falls before starting list value - else if ( T < Ts ) then - if (mtime == NONE) then - call time_list_error(T,terr) - call time_list_error(Ts,tserr) - call time_list_error(Te,teerr) - if(fms_error_handler ('time_interp_list',& - 'time '//trim(terr)//' ('//date_to_string(T)//' is before range of list '//trim(tserr)//'-'//trim(teerr)//& - '('//date_to_string(Ts)//' - '//date_to_string(Te)//')',& - err_msg)) return - deallocate(terr,tserr,teerr) - endif - Td = Te-Ts - weight = real( 1. - ((Ts-T) // (Period-Td)) ) - index1 = n - index2 = 1 - -! time falls on ending list value - else if ( T == Te ) then - if(perthlike_behavior) then - weight = 1.0 - index1 = n-1 - index2 = n - else - weight = 0. - index1 = n - if (mtime == NONE) then - index2 = n - else - index2 = 1 - endif - endif - -! time falls after ending list value - else if ( T > Te ) then - if (mtime == NONE) then - call time_list_error(T,terr) - call time_list_error(Ts,tserr) - call time_list_error(Te,teerr) - if(fms_error_handler ('time_interp_list',& - 'time '//trim(terr)//' ('//date_to_string(T)//' is after range of list '//trim(tserr)//'-'//trim(teerr)//& - '('//date_to_string(Ts)//' - '//date_to_string(Te)//')',& - err_msg)) return - deallocate(terr,tserr,teerr) - endif - Td = Te-Ts - weight = real( (T-Te) // (Period-Td) ) - index1 = n - index2 = 1 - endif - -end subroutine time_interp_list - !####################################################################### ! private routines !####################################################################### @@ -854,8 +242,6 @@ function year_midpt (yr) end function year_midpt -!####################################################################### - function month_midpt (yr, mo) integer, intent(in) :: yr, mo @@ -875,8 +261,6 @@ function month_midpt (yr, mo) end function month_midpt -!####################################################################### - function set_modtime (Tin, modtime) result (Tout) type(time_type), intent(in) :: Tin integer, intent(in), optional :: modtime @@ -912,60 +296,18 @@ function set_modtime (Tin, modtime) result (Tout) end function set_modtime -!####################################################################### - -subroutine error_handler (string) -character(len=*), intent(in) :: string +subroutine error_handler(string) + character(len=*), intent(in) :: string call error_mesg ('time_interp_mod', trim(string), FATAL) -! write (*,'(a)') 'ERROR in time_interp: ' // trim(string) -! stop 111 - end subroutine error_handler -!####################################################################### + +#include "time_interp_r4.fh" +#include "time_interp_r8.fh" end module time_interp_mod -! - -! -! The list of input time types must have ascending dates. -! * -! -! The length of the current month for input Time and Time_list -! must be the same when using the modulo month option. -! The modulo month option is available but not supported. -! * -! -! The optional argument modtime must have a value set by one -! of the public parameters: NONE, YEAR, MONTH, DAY. -! The MONTH and DAY options are available but not supported. -! * -! -! The difference between the last and first values in the -! input Time list/array exceeds the length of the modulo period. -! * -! -! These errors occur when you are not using a modulo axis and the -! input Time occurs before the first value in the Time list/array -! or after the last value in the Time list/array. -! * -! -! For all routines in this module the calendar type in module -! time_manager must be set. -! -! -! The following private parameters are set by this module: -!
-!           seconds per minute = 60
-!           minutes per hour   = 60
-!           hours   per day    = 24
-!           months  per year   = 12
-! 
-!
- -!
!> @} -! close documentation grouping +! close documentation grouping \ No newline at end of file diff --git a/time_interp/time_interp_external2.F90 b/time_interp/time_interp_external2.F90 index 7716e17ea4..638326c918 100644 --- a/time_interp/time_interp_external2.F90 +++ b/time_interp/time_interp_external2.F90 @@ -42,7 +42,7 @@ module time_interp_external2_mod ! ! - use platform_mod, only : DOUBLE_KIND => r8_kind + use platform_mod, only : r8_kind, r4_kind use fms_mod, only : write_version_number use mpp_mod, only : mpp_error,FATAL,WARNING,mpp_pe, stdout, stdlog, NOTE use mpp_mod, only : input_nml_file, mpp_npes, mpp_root_pe, mpp_broadcast, mpp_get_current_pelist @@ -73,7 +73,7 @@ module time_interp_external2_mod integer, parameter, private :: modulo_year= 0001 integer, parameter, private :: LINEAR_TIME_INTERP = 1 ! not used currently integer, parameter, public :: SUCCESS = 0, ERR_FIELD_NOT_FOUND = 1 - real, parameter, private :: DEFAULT_MISSING_VALUE = -1e20 + real(r8_kind), parameter, private :: DEFAULT_MISSING_VALUE = -1e20_r8_kind integer, private :: max_fields = 100, max_files= 40 integer, private :: num_fields = 0, num_files=0 ! denotes time intervals in file (interpreted from metadata) @@ -93,41 +93,41 @@ module time_interp_external2_mod !> Represents external fields !> @ingroup time_interp_external2_mod type, private :: ext_fieldtype - type(FmsNetcdfFile_t), pointer :: fileobj=>NULL() !< keep unit open when not reading all records - character(len=128) :: name, units - integer :: siz(4), ndim - character(len=32) :: axisname(4) - type(domain2d) :: domain - type(time_type), dimension(:), pointer :: time =>NULL() !< midpoint of time interval - type(time_type), dimension(:), pointer :: start_time =>NULL(), end_time =>NULL() - type(time_type), dimension(:), pointer :: period =>NULL() - logical :: modulo_time !< denote climatological time axis - real, dimension(:,:,:,:), pointer :: data =>NULL() !< defined over data domain or global domain - logical, dimension(:,:,:,:), pointer :: mask =>NULL() !< defined over data domain or global domain - integer, dimension(:), pointer :: ibuf =>NULL() !< record numbers associated with buffers - real, dimension(:,:,:,:), pointer :: src_data =>NULL() !< input data buffer - type(valid_t) :: valid ! data validator - integer :: nbuf - logical :: domain_present - real(DOUBLE_KIND) :: slope, intercept - integer :: isc,iec,jsc,jec - type(time_type) :: modulo_time_beg, modulo_time_end - logical :: have_modulo_times, correct_leap_year_inconsistency - integer :: region_type - integer :: is_region, ie_region, js_region, je_region - integer :: is_src, ie_src, js_src, je_src - integer :: tdim - integer :: numwindows - logical, dimension(:,:), pointer :: need_compute=>NULL() - real :: missing ! missing value - end type ext_fieldtype - - !> Holds filename and file object - !> @ingroup time_interp_external2_mod - type, private :: filetype - character(len=128) :: filename = '' - type(FmsNetcdfFile_t), pointer :: fileobj => NULL() - end type filetype + type(FmsNetcdfFile_t), pointer :: fileobj=>NULL() !< keep unit open when not reading all records + character(len=128) :: name, units + integer :: siz(4), ndim + character(len=32) :: axisname(4) + type(domain2d) :: domain + type(time_type), dimension(:), pointer :: time =>NULL() !< midpoint of time interval + type(time_type), dimension(:), pointer :: start_time =>NULL(), end_time =>NULL() + type(time_type), dimension(:), pointer :: period =>NULL() + logical :: modulo_time !< denote climatological time axis + real(r8_kind), dimension(:,:,:,:), pointer :: data =>NULL() !< defined over data domain or global domain + logical, dimension(:,:,:,:), pointer :: mask =>NULL() !< defined over data domain or global domain + integer, dimension(:), pointer :: ibuf =>NULL() !< record numbers associated with buffers + real(r8_kind), dimension(:,:,:,:), pointer :: src_data =>NULL() !< input data buffer + type(valid_t) :: valid ! data validator + integer :: nbuf + logical :: domain_present + real(r8_kind) :: slope, intercept + integer :: isc,iec,jsc,jec + type(time_type) :: modulo_time_beg, modulo_time_end + logical :: have_modulo_times, correct_leap_year_inconsistency + integer :: region_type + integer :: is_region, ie_region, js_region, je_region + integer :: is_src, ie_src, js_src, je_src + integer :: tdim + integer :: numwindows + logical, dimension(:,:), pointer :: need_compute=>NULL() + real(r8_kind) :: missing ! missing value + end type ext_fieldtype + + !> Holds filename and file object + !> @ingroup time_interp_external2_mod + type, private :: filetype + character(len=128) :: filename = '' + type(FmsNetcdfFile_t), pointer :: fileobj => NULL() + end type filetype !> Provide data from external file interpolated to current model time. !! Data may be local to current processor or global, depending on @@ -142,9 +142,12 @@ module time_interp_external2_mod !! !> @ingroup time_interp_external2_mod interface time_interp_external - module procedure time_interp_external_0d - module procedure time_interp_external_2d - module procedure time_interp_external_3d + module procedure time_interp_external_0d_r4 + module procedure time_interp_external_2d_r4 + module procedure time_interp_external_3d_r4 + module procedure time_interp_external_0d_r8 + module procedure time_interp_external_2d_r8 + module procedure time_interp_external_3d_r8 end interface !> @addtogroup time_interp_external2_mod @@ -152,12 +155,10 @@ module time_interp_external2_mod integer :: outunit - type(ext_fieldtype), save, private, pointer :: field(:) => NULL() + type(ext_fieldtype), save, private, pointer :: loaded_fields(:) => NULL() type(filetype), save, private, pointer :: opened_files(:) => NULL() !Balaji: really should use field%missing - integer, private, parameter :: dk = DOUBLE_KIND ! ensures that time_interp_missing is in range for mixed-mode - ! compiling - real(DOUBLE_KIND), private, parameter :: time_interp_missing=-1e99_dk + real(r8_kind), private, parameter :: time_interp_missing=-1e99_r8_kind contains ! @@ -196,7 +197,6 @@ subroutine time_interp_external_init() return end subroutine time_interp_external_init -! NAME="time_interp_external_init" ! @@ -278,12 +278,12 @@ function init_external_field(file,fieldname,domain,desired_units,& integer :: init_external_field - real(DOUBLE_KIND) :: slope, intercept + real(r8_kind) :: slope, intercept integer :: ndim,ntime,i,j integer :: iscomp,iecomp,jscomp,jecomp,isglobal,ieglobal,jsglobal,jeglobal integer :: isdata,iedata,jsdata,jedata, dxsize, dysize,dxsize_max,dysize_max logical :: verb, transpose_xy,use_comp_domain1 - real, dimension(:), allocatable :: tstamp, tstart, tend, tavg + real(r8_kind), dimension(:), allocatable :: tstamp, tstart, tend, tavg character(len=1) :: cart character(len=1), dimension(4) :: cart_dir character(len=128) :: units, fld_units @@ -417,7 +417,7 @@ function init_external_field(file,fieldname,domain,desired_units,& endif endif - tavg = -1.0 + tavg = -1.0_r8_kind tstart = tstamp tend = tstamp if(variable_att_exists(fileobj, fieldname, 'time_avg_info')) then @@ -441,43 +441,43 @@ function init_external_field(file,fieldname,domain,desired_units,& call get_variable_units(fileobj, fieldname, fld_units) init_external_field = num_fields - field(num_fields)%fileobj => fileobj - field(num_fields)%name = trim(fieldname) - field(num_fields)%units = trim(fld_units) - field(num_fields)%isc = 1 - field(num_fields)%iec = 1 - field(num_fields)%jsc = 1 - field(num_fields)%jec = 1 - field(num_fields)%region_type = NO_REGION - field(num_fields)%is_region = 0 - field(num_fields)%ie_region = -1 - field(num_fields)%js_region = 0 - field(num_fields)%je_region = -1 + loaded_fields(num_fields)%fileobj => fileobj + loaded_fields(num_fields)%name = trim(fieldname) + loaded_fields(num_fields)%units = trim(fld_units) + loaded_fields(num_fields)%isc = 1 + loaded_fields(num_fields)%iec = 1 + loaded_fields(num_fields)%jsc = 1 + loaded_fields(num_fields)%jec = 1 + loaded_fields(num_fields)%region_type = NO_REGION + loaded_fields(num_fields)%is_region = 0 + loaded_fields(num_fields)%ie_region = -1 + loaded_fields(num_fields)%js_region = 0 + loaded_fields(num_fields)%je_region = -1 if (PRESENT(domain)) then - field(num_fields)%domain_present = .true. - field(num_fields)%domain = domain - field(num_fields)%isc=iscomp;field(num_fields)%iec = iecomp - field(num_fields)%jsc=jscomp;field(num_fields)%jec = jecomp + loaded_fields(num_fields)%domain_present = .true. + loaded_fields(num_fields)%domain = domain + loaded_fields(num_fields)%isc=iscomp;loaded_fields(num_fields)%iec = iecomp + loaded_fields(num_fields)%jsc=jscomp;loaded_fields(num_fields)%jec = jecomp else - field(num_fields)%domain_present = .false. + loaded_fields(num_fields)%domain_present = .false. endif - field(num_fields)%valid = get_valid(fileobj, fieldname) + loaded_fields(num_fields)%valid = get_valid(fileobj, fieldname) ndim = get_variable_num_dimensions(fileobj, fieldname) if (ndim > 4) call mpp_error(FATAL, & 'invalid array rank <=4d fields supported') - field(num_fields)%siz = 1 - field(num_fields)%ndim = ndim - field(num_fields)%tdim = 4 + loaded_fields(num_fields)%siz = 1 + loaded_fields(num_fields)%ndim = ndim + loaded_fields(num_fields)%tdim = 4 !--- get field missing value - field(num_fields)%missing = get_variable_missing(fileobj, fieldname) + loaded_fields(num_fields)%missing = get_variable_missing(fileobj, fieldname) allocate(axisname(ndim), axislen(ndim)) call get_variable_dimension_names(fileobj, fieldname, axisname) call get_variable_size(fileobj, fieldname, axislen) - do j=1,field(num_fields)%ndim + do j=1,loaded_fields(num_fields)%ndim call get_axis_cart(fileobj, axisname(j), cart) len = axislen(j) if (cart == 'N' .and. .not. ignore_axatts) then @@ -495,198 +495,199 @@ function init_external_field(file,fieldname,domain,desired_units,& iscomp=1;iecomp=len gxsize = len dxsize = len - field(num_fields)%isc=iscomp;field(num_fields)%iec=iecomp + loaded_fields(num_fields)%isc=iscomp;loaded_fields(num_fields)%iec=iecomp elseif (PRESENT(override)) then gxsize = len if (PRESENT(axis_sizes)) axis_sizes(1) = len endif - field(num_fields)%axisname(1) = axisname(j) + loaded_fields(num_fields)%axisname(1) = axisname(j) if(use_comp_domain1) then - field(num_fields)%siz(1) = nx + loaded_fields(num_fields)%siz(1) = nx else - field(num_fields)%siz(1) = dxsize + loaded_fields(num_fields)%siz(1) = dxsize endif if (len /= gxsize) then write(msg,'(a,"/",a)') trim(file),trim(fieldname) call mpp_error(FATAL,'time_interp_ext, file/field '//trim(msg)//' x dim doesnt match model') endif case ('Y') - field(num_fields)%axisname(2) = axisname(j) + loaded_fields(num_fields)%axisname(2) = axisname(j) if (.not.PRESENT(domain) .and. .not.PRESENT(override)) then jsdata=1;jedata=len jscomp=1;jecomp=len gysize = len dysize = len - field(num_fields)%jsc=jscomp;field(num_fields)%jec=jecomp + loaded_fields(num_fields)%jsc=jscomp;loaded_fields(num_fields)%jec=jecomp elseif (PRESENT(override)) then gysize = len if (PRESENT(axis_sizes)) axis_sizes(2) = len endif if(use_comp_domain1) then - field(num_fields)%siz(2) = ny + loaded_fields(num_fields)%siz(2) = ny else - field(num_fields)%siz(2) = dysize + loaded_fields(num_fields)%siz(2) = dysize endif if (len /= gysize) then write(msg,'(a,"/",a)') trim(file),trim(fieldname) call mpp_error(FATAL,'time_interp_ext, file/field '//trim(msg)//' y dim doesnt match model') endif case ('Z') - field(num_fields)%axisname(3) = axisname(j) - field(num_fields)%siz(3) = len + loaded_fields(num_fields)%axisname(3) = axisname(j) + loaded_fields(num_fields)%siz(3) = len case ('T') - field(num_fields)%axisname(4) = axisname(j) - field(num_fields)%siz(4) = ntime - field(num_fields)%tdim = j + loaded_fields(num_fields)%axisname(4) = axisname(j) + loaded_fields(num_fields)%siz(4) = ntime + loaded_fields(num_fields)%tdim = j end select enddo - siz = field(num_fields)%siz - if(PRESENT(axis_names)) axis_names = field(num_fields)%axisname + siz = loaded_fields(num_fields)%siz + if(PRESENT(axis_names)) axis_names = loaded_fields(num_fields)%axisname if (PRESENT(axis_sizes) .and. .not.PRESENT(override)) then - axis_sizes = field(num_fields)%siz + axis_sizes = loaded_fields(num_fields)%siz endif if (verb) write(outunit,'(a,4i6)') 'field x,y,z,t local size= ',siz - if (verb) write(outunit,*) 'field contains data in units = ',trim(field(num_fields)%units) + if (verb) write(outunit,*) 'field contains data in units = ',trim(loaded_fields(num_fields)%units) if (transpose_xy) call mpp_error(FATAL,'axis ordering not supported') if (num_io_buffers .le. 1) call mpp_error(FATAL,'time_interp_ext:num_io_buffers should be at least 2') nbuf = min(num_io_buffers,siz(4)) - field(num_fields)%numwindows = numwindows - allocate(field(num_fields)%need_compute(nbuf, numwindows)) - field(num_fields)%need_compute = .true. + loaded_fields(num_fields)%numwindows = numwindows + allocate(loaded_fields(num_fields)%need_compute(nbuf, numwindows)) + loaded_fields(num_fields)%need_compute = .true. - allocate(field(num_fields)%data(isdata:iedata,jsdata:jedata,siz(3),nbuf),& - field(num_fields)%mask(isdata:iedata,jsdata:jedata,siz(3),nbuf) ) - field(num_fields)%mask = .false. - field(num_fields)%data = 0.0 - slope=1.0;intercept=0.0 + allocate(loaded_fields(num_fields)%data(isdata:iedata,jsdata:jedata,siz(3),nbuf),& + loaded_fields(num_fields)%mask(isdata:iedata,jsdata:jedata,siz(3),nbuf) ) + loaded_fields(num_fields)%mask = .false. + loaded_fields(num_fields)%data = 0.0_r8_kind + slope=1.0_r8_kind;intercept=0.0_r8_kind ! if (units /= 'same') call convert_units(trim(field(num_fields)%units),trim(units),slope,intercept) ! if (verb.and.units /= 'same') then ! write(outunit,*) 'attempting to convert data to units = ',trim(units) ! write(outunit,'(a,f8.3,a,f8.3)') 'factor = ',slope,' offset= ',intercept ! endif - field(num_fields)%slope = slope - field(num_fields)%intercept = intercept - allocate(field(num_fields)%ibuf(nbuf)) - field(num_fields)%ibuf = -1 - field(num_fields)%nbuf = 0 ! initialize buffer number so that first reading fills data(:,:,:,1) + loaded_fields(num_fields)%slope = slope + loaded_fields(num_fields)%intercept = intercept + allocate(loaded_fields(num_fields)%ibuf(nbuf)) + loaded_fields(num_fields)%ibuf = -1 + loaded_fields(num_fields)%nbuf = 0 ! initialize buffer number so that first reading fills data(:,:,:,1) if(PRESENT(override)) then - field(num_fields)%is_src = 1 - field(num_fields)%ie_src = gxsize - field(num_fields)%js_src = 1 - field(num_fields)%je_src = gysize - allocate(field(num_fields)%src_data(gxsize,gysize,siz(3),nbuf)) + loaded_fields(num_fields)%is_src = 1 + loaded_fields(num_fields)%ie_src = gxsize + loaded_fields(num_fields)%js_src = 1 + loaded_fields(num_fields)%je_src = gysize + allocate(loaded_fields(num_fields)%src_data(gxsize,gysize,siz(3),nbuf)) else - field(num_fields)%is_src = isdata - field(num_fields)%ie_src = iedata - field(num_fields)%js_src = jsdata - field(num_fields)%je_src = jedata - allocate(field(num_fields)%src_data(isdata:iedata,jsdata:jedata,siz(3),nbuf)) + loaded_fields(num_fields)%is_src = isdata + loaded_fields(num_fields)%ie_src = iedata + loaded_fields(num_fields)%js_src = jsdata + loaded_fields(num_fields)%je_src = jedata + allocate(loaded_fields(num_fields)%src_data(isdata:iedata,jsdata:jedata,siz(3),nbuf)) endif - allocate(field(num_fields)%time(ntime)) - allocate(field(num_fields)%period(ntime)) - allocate(field(num_fields)%start_time(ntime)) - allocate(field(num_fields)%end_time(ntime)) + allocate(loaded_fields(num_fields)%time(ntime)) + allocate(loaded_fields(num_fields)%period(ntime)) + allocate(loaded_fields(num_fields)%start_time(ntime)) + allocate(loaded_fields(num_fields)%end_time(ntime)) do j=1,ntime - field(num_fields)%time(j) = get_cal_time(tstamp(j),trim(timeunits),trim(calendar_type), & + loaded_fields(num_fields)%time(j) = get_cal_time(tstamp(j),trim(timeunits),trim(calendar_type), & & permit_calendar_conversion) - field(num_fields)%start_time(j) = get_cal_time(tstart(j),trim(timeunits),trim(calendar_type), & + loaded_fields(num_fields)%start_time(j) = get_cal_time(tstart(j),trim(timeunits),trim(calendar_type), & & permit_calendar_conversion) - field(num_fields)%end_time(j) = get_cal_time( tend(j),trim(timeunits),trim(calendar_type), & + loaded_fields(num_fields)%end_time(j) = get_cal_time( tend(j),trim(timeunits),trim(calendar_type), & & permit_calendar_conversion) enddo - if (field(num_fields)%modulo_time) then - call set_time_modulo(field(num_fields)%Time) - call set_time_modulo(field(num_fields)%start_time) - call set_time_modulo(field(num_fields)%end_time) + if (loaded_fields(num_fields)%modulo_time) then + call set_time_modulo(loaded_fields(num_fields)%Time) + call set_time_modulo(loaded_fields(num_fields)%start_time) + call set_time_modulo(loaded_fields(num_fields)%end_time) endif if(present(correct_leap_year_inconsistency)) then - field(num_fields)%correct_leap_year_inconsistency = correct_leap_year_inconsistency + loaded_fields(num_fields)%correct_leap_year_inconsistency = correct_leap_year_inconsistency else - field(num_fields)%correct_leap_year_inconsistency = .false. + loaded_fields(num_fields)%correct_leap_year_inconsistency = .false. endif if(have_modulo_time) then if(get_calendar_type() == NO_CALENDAR) then - field(num_fields)%modulo_time_beg = set_time(timebeg) - field(num_fields)%modulo_time_end = set_time(timeend) + loaded_fields(num_fields)%modulo_time_beg = set_time(timebeg) + loaded_fields(num_fields)%modulo_time_end = set_time(timeend) else - field(num_fields)%modulo_time_beg = set_date(timebeg) - field(num_fields)%modulo_time_end = set_date(timeend) + loaded_fields(num_fields)%modulo_time_beg = set_date(timebeg) + loaded_fields(num_fields)%modulo_time_end = set_date(timeend) endif - field(num_fields)%have_modulo_times = .true. + loaded_fields(num_fields)%have_modulo_times = .true. else - field(num_fields)%have_modulo_times = .false. + loaded_fields(num_fields)%have_modulo_times = .false. endif if(ntime == 1) then call mpp_error(NOTE, 'time_interp_external_mod: file '//trim(file)//' has only one time level') else do j= 1, ntime - field(num_fields)%period(j) = field(num_fields)%end_time(j)-field(num_fields)%start_time(j) - if (field(num_fields)%period(j) > set_time(0,0)) then - call get_time(field(num_fields)%period(j), sec, day) + loaded_fields(num_fields)%period(j) = loaded_fields(num_fields)%end_time(j) & + - loaded_fields(num_fields)%start_time(j) + if (loaded_fields(num_fields)%period(j) > set_time(0,0)) then + call get_time(loaded_fields(num_fields)%period(j), sec, day) sec = sec/2+mod(day,2)*43200 day = day/2 - field(num_fields)%time(j) = field(num_fields)%start_time(j)+& + loaded_fields(num_fields)%time(j) = loaded_fields(num_fields)%start_time(j)+& set_time(sec,day) else if (j > 1 .and. j < ntime) then - tdiff = field(num_fields)%time(j+1) - field(num_fields)%time(j-1) + tdiff = loaded_fields(num_fields)%time(j+1) - loaded_fields(num_fields)%time(j-1) call get_time(tdiff, sec, day) sec = sec/2+mod(day,2)*43200 day = day/2 - field(num_fields)%period(j) = set_time(sec,day) + loaded_fields(num_fields)%period(j) = set_time(sec,day) sec = sec/2+mod(day,2)*43200 day = day/2 - field(num_fields)%start_time(j) = field(num_fields)%time(j) - set_time(sec,day) - field(num_fields)%end_time(j) = field(num_fields)%time(j) + set_time(sec,day) + loaded_fields(num_fields)%start_time(j) = loaded_fields(num_fields)%time(j) - set_time(sec,day) + loaded_fields(num_fields)%end_time(j) = loaded_fields(num_fields)%time(j) + set_time(sec,day) elseif ( j == 1) then - tdiff = field(num_fields)%time(2) - field(num_fields)%time(1) + tdiff = loaded_fields(num_fields)%time(2) - loaded_fields(num_fields)%time(1) call get_time(tdiff, sec, day) - field(num_fields)%period(j) = set_time(sec,day) + loaded_fields(num_fields)%period(j) = set_time(sec,day) sec = sec/2+mod(day,2)*43200 day = day/2 - field(num_fields)%start_time(j) = field(num_fields)%time(j) - set_time(sec,day) - field(num_fields)%end_time(j) = field(num_fields)%time(j) + set_time(sec,day) + loaded_fields(num_fields)%start_time(j) = loaded_fields(num_fields)%time(j) - set_time(sec,day) + loaded_fields(num_fields)%end_time(j) = loaded_fields(num_fields)%time(j) + set_time(sec,day) else - tdiff = field(num_fields)%time(ntime) - field(num_fields)%time(ntime-1) + tdiff = loaded_fields(num_fields)%time(ntime) - loaded_fields(num_fields)%time(ntime-1) call get_time(tdiff, sec, day) - field(num_fields)%period(j) = set_time(sec,day) + loaded_fields(num_fields)%period(j) = set_time(sec,day) sec = sec/2+mod(day,2)*43200 day = day/2 - field(num_fields)%start_time(j) = field(num_fields)%time(j) - set_time(sec,day) - field(num_fields)%end_time(j) = field(num_fields)%time(j) + set_time(sec,day) + loaded_fields(num_fields)%start_time(j) = loaded_fields(num_fields)%time(j) - set_time(sec,day) + loaded_fields(num_fields)%end_time(j) = loaded_fields(num_fields)%time(j) + set_time(sec,day) endif endif enddo endif do j=1,ntime-1 - if (field(num_fields)%time(j) >= field(num_fields)%time(j+1)) then + if (loaded_fields(num_fields)%time(j) >= loaded_fields(num_fields)%time(j+1)) then write(msg,'(A,i20)') "times not monotonically increasing. Filename: " & //TRIM(file)//" field: "//TRIM(fieldname)//" timeslice: ", j call mpp_error(FATAL, TRIM(msg)) endif enddo - field(num_fields)%modulo_time = get_axis_modulo(fileobj, timename) + loaded_fields(num_fields)%modulo_time = get_axis_modulo(fileobj, timename) if (verb) then - if (field(num_fields)%modulo_time) write(outunit,*) 'data are being treated as modulo in time' + if (loaded_fields(num_fields)%modulo_time) write(outunit,*) 'data are being treated as modulo in time' do j= 1, ntime write(outunit,*) 'time index, ', j - call get_date(field(num_fields)%start_time(j),yr,mon,day,hr,minu,sec) + call get_date(loaded_fields(num_fields)%start_time(j),yr,mon,day,hr,minu,sec) write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') & 'start time: yyyy/mm/dd hh:mm:ss= ',yr,'/',mon,'/',day,hr,':',minu,':',sec - call get_date(field(num_fields)%time(j),yr,mon,day,hr,minu,sec) + call get_date(loaded_fields(num_fields)%time(j),yr,mon,day,hr,minu,sec) write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') & 'mid time: yyyy/mm/dd hh:mm:ss= ',yr,'/',mon,'/',day,hr,':',minu,':',sec - call get_date(field(num_fields)%end_time(j),yr,mon,day,hr,minu,sec) + call get_date(loaded_fields(num_fields)%end_time(j),yr,mon,day,hr,minu,sec) write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') & 'end time: yyyy/mm/dd hh:mm:ss= ',yr,'/',mon,'/',day,hr,':',minu,':',sec enddo @@ -699,35 +700,6 @@ function init_external_field(file,fieldname,domain,desired_units,& end function init_external_field -! NAME="init_external_field" - - - !> @brief 2D interpolation for @ref time_interp_external - subroutine time_interp_external_2d(index, time, data_in, interp, verbose,horz_interp, mask_out, & - is_in, ie_in, js_in, je_in, window_id) - - integer, intent(in) :: index - type(time_type), intent(in) :: time - real, dimension(:,:), intent(inout) :: data_in - integer, intent(in), optional :: interp - logical, intent(in), optional :: verbose - type(horiz_interp_type),intent(in), optional :: horz_interp - logical, dimension(:,:), intent(out), optional :: mask_out !< set to true where output data is valid - integer, intent(in), optional :: is_in, ie_in, js_in, je_in - integer, intent(in), optional :: window_id - - real , dimension(size(data_in,1), size(data_in,2), 1) :: data_out - logical, dimension(size(data_in,1), size(data_in,2), 1) :: mask3d - - data_out(:,:,1) = data_in(:,:) ! fill initial values for the portions of array that are not touched by 3d routine - call time_interp_external_3d(index, time, data_out, interp, verbose, horz_interp, mask3d, & - is_in=is_in,ie_in=ie_in,js_in=js_in,je_in=je_in,window_id=window_id) - data_in(:,:) = data_out(:,:,1) - if (PRESENT(mask_out)) mask_out(:,:) = mask3d(:,:,1) - - return - end subroutine time_interp_external_2d - function get_external_fileobj(filename, fileobj) character(len=*), intent(in) :: filename @@ -747,268 +719,6 @@ function get_external_fileobj(filename, fileobj) end function get_external_fileobj -! -! -! -! Provide data from external file interpolated to current model time. -! Data may be local to current processor or global, depending on -! "init_external_field" flags. -! -! -! -! index of external field from previous call to init_external_field -! -! -! target time for data -! -! -! global or local data array -! -! -! time_interp_external defined interpolation method (optional). Currently this module only supports -! LINEAR_TIME_INTERP. -! -! -! verbose flag for debugging (optional). -! - - !> 3D interpolation for @ref time_interp_external - subroutine time_interp_external_3d(index, time, data, interp,verbose,horz_interp, mask_out, is_in, ie_in, & - & js_in, je_in, window_id) - - integer, intent(in) :: index - type(time_type), intent(in) :: time - real, dimension(:,:,:), intent(inout) :: data - integer, intent(in), optional :: interp - logical, intent(in), optional :: verbose - type(horiz_interp_type), intent(in), optional :: horz_interp - logical, dimension(:,:,:), intent(out), optional :: mask_out ! set to true where output data is valid - integer, intent(in), optional :: is_in, ie_in, js_in, je_in - integer, intent(in), optional :: window_id - - integer :: nx, ny, nz, interp_method, t1, t2 - integer :: i1, i2, isc, iec, jsc, jec, mod_time - integer :: yy, mm, dd, hh, min, ss - character(len=256) :: err_msg, filename - - integer :: isw, iew, jsw, jew, nxw, nyw - ! these are boundaries of the updated portion of the "data" argument - ! they are calculated using sizes of the "data" and isc,iec,jsc,jsc - ! fileds from respective input field, to center the updated portion - ! in the output array - - real :: w1,w2 - logical :: verb - character(len=16) :: message1, message2 - - nx = size(data,1) - ny = size(data,2) - nz = size(data,3) - - interp_method = LINEAR_TIME_INTERP - if (PRESENT(interp)) interp_method = interp - verb=.false. - if (PRESENT(verbose)) verb=verbose - if (debug_this_module) verb = .true. - - if (index < 1.or.index > num_fields) & - call mpp_error(FATAL, & - & 'invalid index in call to time_interp_ext -- field was not initialized or failed to initialize') - - isc=field(index)%isc;iec=field(index)%iec - jsc=field(index)%jsc;jec=field(index)%jec - - if( field(index)%numwindows == 1 ) then - nxw = iec-isc+1 - nyw = jec-jsc+1 - else - if( .not. present(is_in) .or. .not. present(ie_in) .or. .not. present(js_in) .or. .not. present(je_in) ) then - call mpp_error(FATAL, 'time_interp_external: is_in, ie_in, js_in and je_in must be present ' // & - 'when numwindows > 1, field='//trim(field(index)%name)) - endif - nxw = ie_in - is_in + 1 - nyw = je_in - js_in + 1 - isc = isc + is_in - 1 - iec = isc + ie_in - is_in - jsc = jsc + js_in - 1 - jec = jsc + je_in - js_in - endif - - isw = (nx-nxw)/2+1; iew = isw+nxw-1 - jsw = (ny-nyw)/2+1; jew = jsw+nyw-1 - - if (nx < nxw .or. ny < nyw .or. nz < field(index)%siz(3)) then - write(message1,'(i6,2i5)') nx,ny,nz - call mpp_error(FATAL,'field '//trim(field(index)%name)//' Array size mismatch in time_interp_external.'// & - ' Array "data" is too small. shape(data)='//message1) - endif - if(PRESENT(mask_out)) then - if (size(mask_out,1) /= nx .or. size(mask_out,2) /= ny .or. size(mask_out,3) /= nz) then - write(message1,'(i6,2i5)') nx,ny,nz - write(message2,'(i6,2i5)') size(mask_out,1),size(mask_out,2),size(mask_out,3) - call mpp_error(FATAL,'field '//trim(field(index)%name)//' array size mismatch in time_interp_external.'// & - ' Shape of array "mask_out" does not match that of array "data".'// & - ' shape(data)='//message1//' shape(mask_out)='//message2) - endif - endif - - if (field(index)%siz(4) == 1) then - ! only one record in the file => time-independent field - call load_record(field(index),1,horz_interp, is_in, ie_in ,js_in, je_in,window_id) - i1 = find_buf_index(1,field(index)%ibuf) - if( field(index)%region_type == NO_REGION ) then - where(field(index)%mask(isc:iec,jsc:jec,:,i1)) - data(isw:iew,jsw:jew,:) = field(index)%data(isc:iec,jsc:jec,:,i1) - elsewhere -! data(isw:iew,jsw:jew,:) = time_interp_missing !field(index)%missing? Balaji - data(isw:iew,jsw:jew,:) = field(index)%missing - end where - else - where(field(index)%mask(isc:iec,jsc:jec,:,i1)) - data(isw:iew,jsw:jew,:) = field(index)%data(isc:iec,jsc:jec,:,i1) - end where - endif - if(PRESENT(mask_out)) & - mask_out(isw:iew,jsw:jew,:) = field(index)%mask(isc:iec,jsc:jec,:,i1) - else - if(field(index)%have_modulo_times) then - call time_interp(time,field(index)%modulo_time_beg, field(index)%modulo_time_end, field(index)%time(:), & - w2, t1, t2, field(index)%correct_leap_year_inconsistency, err_msg=err_msg) - if(err_msg .NE. '') then - filename = trim(field(index)%fileobj%path) - call mpp_error(FATAL,"time_interp_external 1: "//trim(err_msg)//& - ",file="//trim(filename)//",field="//trim(field(index)%name) ) - endif - else - if(field(index)%modulo_time) then - mod_time=1 - else - mod_time=0 - endif - call time_interp(time,field(index)%time(:),w2,t1,t2,modtime=mod_time, err_msg=err_msg) - if(err_msg .NE. '') then - filename = trim(field(index)%fileobj%path) - call mpp_error(FATAL,"time_interp_external 2: "//trim(err_msg)//& - ",file="//trim(filename)//",field="//trim(field(index)%name) ) - endif - endif - w1 = 1.0-w2 - if (verb) then - call get_date(time,yy,mm,dd,hh,min,ss) - write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') & - 'target time yyyy/mm/dd hh:mm:ss= ',yy,'/',mm,'/',dd,hh,':',min,':',ss - write(outunit,*) 't1, t2, w1, w2= ', t1, t2, w1, w2 - endif - - call load_record(field(index),t1,horz_interp, is_in, ie_in ,js_in, je_in, window_id) - call load_record(field(index),t2,horz_interp, is_in, ie_in ,js_in, je_in, window_id) - i1 = find_buf_index(t1,field(index)%ibuf) - i2 = find_buf_index(t2,field(index)%ibuf) - if(i1<0.or.i2<0) & - call mpp_error(FATAL,'time_interp_external : records were not loaded correctly in memory') - - if (verb) then - write(outunit,*) 'ibuf= ',field(index)%ibuf - write(outunit,*) 'i1,i2= ',i1, i2 - endif - - if( field(index)%region_type == NO_REGION ) then - where(field(index)%mask(isc:iec,jsc:jec,:,i1).and.field(index)%mask(isc:iec,jsc:jec,:,i2)) - data(isw:iew,jsw:jew,:) = field(index)%data(isc:iec,jsc:jec,:,i1)*w1 + & - field(index)%data(isc:iec,jsc:jec,:,i2)*w2 - elsewhere -! data(isw:iew,jsw:jew,:) = time_interp_missing !field(index)%missing? Balaji - data(isw:iew,jsw:jew,:) = field(index)%missing - end where - else - where(field(index)%mask(isc:iec,jsc:jec,:,i1).and.field(index)%mask(isc:iec,jsc:jec,:,i2)) - data(isw:iew,jsw:jew,:) = field(index)%data(isc:iec,jsc:jec,:,i1)*w1 + & - field(index)%data(isc:iec,jsc:jec,:,i2)*w2 - end where - endif - if(PRESENT(mask_out)) & - mask_out(isw:iew,jsw:jew,:) = & - field(index)%mask(isc:iec,jsc:jec,:,i1).and.& - field(index)%mask(isc:iec,jsc:jec,:,i2) - endif - - end subroutine time_interp_external_3d -! NAME="time_interp_external" - - !> @brief Scalar interpolation for @ref time_interp_external - subroutine time_interp_external_0d(index, time, data, verbose) - - integer, intent(in) :: index - type(time_type), intent(in) :: time - real, intent(inout) :: data - logical, intent(in), optional :: verbose - - integer :: t1, t2 - integer :: i1, i2, mod_time - integer :: yy, mm, dd, hh, min, ss - character(len=256) :: err_msg, filename - - real :: w1,w2 - logical :: verb - - verb=.false. - if (PRESENT(verbose)) verb=verbose - if (debug_this_module) verb = .true. - - if (index < 1.or.index > num_fields) & - call mpp_error(FATAL, & - & 'invalid index in call to time_interp_ext -- field was not initialized or failed to initialize') - - if (field(index)%siz(4) == 1) then - ! only one record in the file => time-independent field - call load_record_0d(field(index),1) - i1 = find_buf_index(1,field(index)%ibuf) - data = field(index)%data(1,1,1,i1) - else - if(field(index)%have_modulo_times) then - call time_interp(time,field(index)%modulo_time_beg, field(index)%modulo_time_end, field(index)%time(:), & - w2, t1, t2, field(index)%correct_leap_year_inconsistency, err_msg=err_msg) - if(err_msg .NE. '') then - filename = trim(field(index)%fileobj%path) - call mpp_error(FATAL,"time_interp_external 3:"//trim(err_msg)//& - ",file="//trim(filename)//",field="//trim(field(index)%name) ) - endif - else - if(field(index)%modulo_time) then - mod_time=1 - else - mod_time=0 - endif - call time_interp(time,field(index)%time(:),w2,t1,t2,modtime=mod_time, err_msg=err_msg) - if(err_msg .NE. '') then - filename = trim(field(index)%fileobj%path) - call mpp_error(FATAL,"time_interp_external 4:"//trim(err_msg)// & - ",file="//trim(filename)//",field="//trim(field(index)%name) ) - endif - endif - w1 = 1.0-w2 - if (verb) then - call get_date(time,yy,mm,dd,hh,min,ss) - write(outunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)') & - 'target time yyyy/mm/dd hh:mm:ss= ',yy,'/',mm,'/',dd,hh,':',min,':',ss - write(outunit,*) 't1, t2, w1, w2= ', t1, t2, w1, w2 - endif - call load_record_0d(field(index),t1) - call load_record_0d(field(index),t2) - i1 = find_buf_index(t1,field(index)%ibuf) - i2 = find_buf_index(t2,field(index)%ibuf) - - if(i1<0.or.i2<0) & - call mpp_error(FATAL,'time_interp_external : records were not loaded correctly in memory') - data = field(index)%data(1,1,1,i1)*w1 + field(index)%data(1,1,1,i2)*w2 - if (verb) then - write(outunit,*) 'ibuf= ',field(index)%ibuf - write(outunit,*) 'i1,i2= ',i1, i2 - endif - endif - - end subroutine time_interp_external_0d - subroutine set_time_modulo(Time) type(time_type), intent(inout), dimension(:) :: Time @@ -1029,6 +739,7 @@ end subroutine set_time_modulo ! ============================================================================ !> load specified record from file +!> Always loads in r8, casts down for horiz_interp if interp argument is already allocated for r4's. subroutine load_record(field, rec, interp, is_in, ie_in, js_in, je_in, window_id_in) type(ext_fieldtype), intent(inout) :: field integer , intent(in) :: rec ! record number @@ -1042,9 +753,12 @@ subroutine load_record(field, rec, interp, is_in, ie_in, js_in, je_in, window_id integer :: is_region, ie_region, js_region, je_region, i, j integer :: start(4), nread(4) logical :: need_compute - real :: mask_in(size(field%src_data,1),size(field%src_data,2),size(field%src_data,3)) - real, allocatable :: mask_out(:,:,:) integer :: window_id + real(r8_kind) :: mask_in(size(field%src_data,1),size(field%src_data,2),size(field%src_data,3)) + real(r8_kind), allocatable :: mask_out(:,:,:) + real(r4_kind), allocatable :: hi_tmp_data(:,:,:,:) !< used to hold a copy of field%data if using r4_kind + real(r4_kind), allocatable :: hi_tmp_msk_out(:,:,:) !< used return the field mask if using r4_kind + real(r4_kind), allocatable :: hi_tmp_src_data(:,:,:,:) !< used return the field mask if using r4_kind window_id = 1 if( PRESENT(window_id_in) ) window_id = window_id_in @@ -1094,33 +808,62 @@ subroutine load_record(field, rec, interp, is_in, ie_in, js_in, je_in, window_id if(PRESENT(interp)) then is_region = field%is_region; ie_region = field%ie_region js_region = field%js_region; je_region = field%je_region - mask_in = 0.0 - where (is_valid(field%src_data(:,:,:,ib), field%valid)) mask_in = 1.0 + mask_in = 0.0_r8_kind + where (is_valid(field%src_data(:,:,:,ib), field%valid)) mask_in = 1.0_r8_kind if ( field%region_type .NE. NO_REGION ) then - if( ANY(mask_in == 0.0) ) then + if( ANY(mask_in == 0.0_r8_kind) ) then call mpp_error(FATAL, "time_interp_external: mask_in should be all 1 when region_type is not NO_REGION") endif if( field%region_type == OUTSIDE_REGION) then do j = js_region, je_region do i = is_region, ie_region - mask_in(i,j,:) = 0.0 + mask_in(i,j,:) = 0.0_r8_kind enddo enddo else ! field%region_choice == INSIDE_REGION do j = 1, size(mask_in,2) do i = 1, size(mask_in,1) - if( jje_region .OR. iie_region ) mask_in(i,j,:) = 0.0 + if( jje_region .OR. iie_region ) mask_in(i,j,:) = 0.0_r8_kind enddo enddo endif endif - allocate(mask_out(isw:iew,jsw:jew, size(field%src_data,3))) - call horiz_interp(interp,field%src_data(:,:,:,ib),field%data(isw:iew,jsw:jew,:,ib), & - mask_in=mask_in, & - mask_out=mask_out) + !! added for mixed mode. if existing horiz_interp_type was initialized in r4, needs to cast down in order + !! to match up with saved values in horiz_interp_type. + !! creates some temporary arrays since intent(out) vars can't get passed in diretory + if (interp%horizInterpReals4_type%is_allocated) then + ! allocate (there may be a better way to do this, had issues with gnu) + allocate(hi_tmp_msk_out(isw:iew,jsw:jew, SIZE(field%src_data,3))) + allocate(hi_tmp_data(LBOUND(field%data,1):UBOUND(field%data,1), & + LBOUND(field%data,2):UBOUND(field%data,2), & + LBOUND(field%data,3):UBOUND(field%data,3), & + LBOUND(field%data,4):UBOUND(field%data,4))) + allocate(hi_tmp_src_data(LBOUND(field%src_data,1):UBOUND(field%src_data,1), & + LBOUND(field%src_data,2):UBOUND(field%src_data,2), & + LBOUND(field%src_data,3):UBOUND(field%src_data,3), & + LBOUND(field%src_data,4):UBOUND(field%src_data,4))) + ! assign if needed + hi_tmp_data = real(field%data, r4_kind) + hi_tmp_src_data = real(field%src_data, r4_kind) + ! do interpolation + call horiz_interp(interp, hi_tmp_src_data(:,:,:,ib), hi_tmp_data(isw:iew,jsw:jew,:,ib), & + mask_in=real(mask_in,r4_kind), mask_out=hi_tmp_msk_out) + ! assign any output + field%data = real(hi_tmp_data, r8_kind) + field%mask(isw:iew,jsw:jew,:,ib) = hi_tmp_msk_out(isw:iew,jsw:jew,:) > 0.0_r4_kind + + if(allocated(hi_tmp_data)) deallocate(hi_tmp_data) + if(allocated(hi_tmp_msk_out)) deallocate(hi_tmp_msk_out) + if(allocated(hi_tmp_src_data)) deallocate(hi_tmp_src_data) + else + allocate(mask_out(isw:iew,jsw:jew, size(field%src_data,3))) + call horiz_interp(interp, field%src_data(:,:,:,ib),field%data(isw:iew,jsw:jew,:,ib), & + mask_in=mask_in, & + mask_out=mask_out) + field%mask(isw:iew,jsw:jew,:,ib) = mask_out(isw:iew,jsw:jew,:) > 0.0_r8_kind + deallocate(mask_out) + endif - field%mask(isw:iew,jsw:jew,:,ib) = mask_out(isw:iew,jsw:jew,:) > 0 - deallocate(mask_out) else if ( field%region_type .NE. NO_REGION ) then call mpp_error(FATAL, "time_interp_external: region_type should be NO_REGION when interp is not present") @@ -1132,11 +875,12 @@ subroutine load_record(field, rec, interp, is_in, ie_in, js_in, je_in, window_id where(field%mask(isw:iew,jsw:jew,:,ib)) field%data(isw:iew,jsw:jew,:,ib) = & field%data(isw:iew,jsw:jew,:,ib)*field%slope + field%intercept field%need_compute(ib, window_id) = .false. + endif end subroutine load_record - +!> Given a initialized ext_fieldtype and record number, loads the given index into field%src_data subroutine load_record_0d(field, rec) type(ext_fieldtype), intent(inout) :: field integer , intent(in) :: rec ! record number @@ -1172,46 +916,44 @@ subroutine load_record_0d(field, rec) end subroutine load_record_0d -! ============================================================================ +!> Reallocates src_data for field from module level loaded_fields array subroutine reset_src_data_region(index, is, ie, js, je) integer, intent(in) :: index integer, intent(in) :: is, ie, js, je integer :: nk, nbuf - if( is == field(index)%is_src .AND. ie == field(index)%ie_src .AND. & - js == field(index)%js_src .AND. ie == field(index)%je_src ) return + if( is == loaded_fields(index)%is_src .AND. ie == loaded_fields(index)%ie_src .AND. & + js == loaded_fields(index)%js_src .AND. ie == loaded_fields(index)%je_src ) return - if( .NOT. ASSOCIATED(field(index)%src_data) ) call mpp_error(FATAL, & + if( .NOT. ASSOCIATED(loaded_fields(index)%src_data) ) call mpp_error(FATAL, & "time_interp_external: field(index)%src_data is not associated") - nk = size(field(index)%src_data,3) - nbuf = size(field(index)%src_data,4) - deallocate(field(index)%src_data) - allocate(field(index)%src_data(is:ie,js:je,nk,nbuf)) - field(index)%is_src = is - field(index)%ie_src = ie - field(index)%js_src = js - field(index)%je_src = je + nk = size(loaded_fields(index)%src_data,3) + nbuf = size(loaded_fields(index)%src_data,4) + deallocate(loaded_fields(index)%src_data) + allocate(loaded_fields(index)%src_data(is:ie,js:je,nk,nbuf)) + loaded_fields(index)%is_src = is + loaded_fields(index)%ie_src = ie + loaded_fields(index)%js_src = js + loaded_fields(index)%je_src = je end subroutine reset_src_data_region -! ============================================================================ subroutine set_override_region(index, region_type, is_region, ie_region, js_region, je_region) integer, intent(in) :: index, region_type integer, intent(in) :: is_region, ie_region, js_region, je_region - field(index)%region_type = region_type - field(index)%is_region = is_region - field(index)%ie_region = ie_region - field(index)%js_region = js_region - field(index)%je_region = je_region + loaded_fields(index)%region_type = region_type + loaded_fields(index)%is_region = is_region + loaded_fields(index)%ie_region = ie_region + loaded_fields(index)%js_region = js_region + loaded_fields(index)%je_region = je_region return end subroutine set_override_region -! ============================================================================ -! reallocates array of fields, increasing its size +!> reallocates array of fields, increasing its size subroutine realloc_files(n) integer, intent(in) :: n ! new size @@ -1239,16 +981,15 @@ subroutine realloc_files(n) end subroutine realloc_files -! ============================================================================ -! reallocates array of fields,increasing its size +!> reallocates array of fields,increasing its size subroutine realloc_fields(n) integer, intent(in) :: n ! new size type(ext_fieldtype), pointer :: ptr(:) integer :: i, ier - if (associated(field)) then - if (n <= size(field)) return ! do nothing if requested size no more then current + if (associated(loaded_fields)) then + if (n <= size(loaded_fields)) return ! do nothing if requested size no more then current endif allocate(ptr(n)) @@ -1269,147 +1010,124 @@ subroutine realloc_fields(n) if (ASSOCIATED(ptr(i)%src_data)) DEALLOCATE(ptr(i)%src_data, stat=ier) ptr(i)%nbuf=-1 ptr(i)%domain_present=.false. - ptr(i)%slope=1.0 - ptr(i)%intercept=0.0 + ptr(i)%slope=1.0_r8_kind + ptr(i)%intercept=0.0_r8_kind ptr(i)%isc=-1;ptr(i)%iec=-1 ptr(i)%jsc=-1;ptr(i)%jec=-1 enddo - if (associated(field)) then - ptr(1:size(field)) = field(:) - deallocate(field) + if (associated(loaded_fields)) then + ptr(1:size(loaded_fields)) = loaded_fields(:) + deallocate(loaded_fields) endif - field=>ptr + loaded_fields=>ptr end subroutine realloc_fields +!> simple linear search for given value in given list +!! TODO should use better search if this list is bigger +function find_buf_index(indx,buf) + integer :: indx + integer, dimension(:) :: buf + integer :: find_buf_index - function find_buf_index(indx,buf) - integer :: indx - integer, dimension(:) :: buf - integer :: find_buf_index - - integer :: nbuf, i - - nbuf = size(buf(:)) - - find_buf_index = -1 - - do i=1,nbuf - if (buf(i) == indx) then - find_buf_index = i - exit - endif - enddo + integer :: nbuf, i - end function find_buf_index + nbuf = size(buf(:)) -! -! -! -! return size of field after call to init_external_field. -! Ordering is X/Y/Z/T. -! This call only makes sense for non-distributed reads. -! -! -! -! returned from previous call to init_external_field. -! + find_buf_index = -1 - function get_external_field_size(index) + do i=1,nbuf + if (buf(i) == indx) then + find_buf_index = i + exit + endif + enddo - integer :: index - integer :: get_external_field_size(4) +end function find_buf_index - if (index .lt. 1 .or. index .gt. num_fields) & - call mpp_error(FATAL,'invalid index in call to get_external_field_size') +!> Returns size of field after call to init_external_field. +!! Ordering is X/Y/Z/T. +!! This call only makes sense for non-distributed reads. +function get_external_field_size(index) + integer :: index !< returned from previous call to init_external_field. + integer :: get_external_field_size(4) - get_external_field_size(1) = field(index)%siz(1) - get_external_field_size(2) = field(index)%siz(2) - get_external_field_size(3) = field(index)%siz(3) - get_external_field_size(4) = field(index)%siz(4) + if (index .lt. 1 .or. index .gt. num_fields) & + call mpp_error(FATAL,'invalid index in call to get_external_field_size') - end function get_external_field_size -! NAME="get_external_field_size" + get_external_field_size(1) = loaded_fields(index)%siz(1) + get_external_field_size(2) = loaded_fields(index)%siz(2) + get_external_field_size(3) = loaded_fields(index)%siz(3) + get_external_field_size(4) = loaded_fields(index)%siz(4) -! -! -! -! return missing value -! -! -! -! returned from previous call to init_external_field. -! +end function get_external_field_size - function get_external_field_missing(index) +!> return missing value +function get_external_field_missing(index) - integer :: index - real :: get_external_field_missing + integer :: index !< returned from previous call to init_external_field. + real(r8_kind) :: get_external_field_missing - if (index .lt. 1 .or. index .gt. num_fields) & - call mpp_error(FATAL,'invalid index in call to get_external_field_size') + if (index .lt. 1 .or. index .gt. num_fields) & + call mpp_error(FATAL,'invalid index in call to get_external_field_size') - get_external_field_missing = field(index)%missing + get_external_field_missing = loaded_fields(index)%missing - end function get_external_field_missing -! NAME="get_external_field_missing" +end function get_external_field_missing -! =========================================================================== subroutine get_time_axis(index, time) - integer , intent(in) :: index ! field id - type(time_type), intent(out) :: time(:) ! array of time values to be filled + integer , intent(in) :: index !< field id + type(time_type), intent(out) :: time(:) !< array of time values to be filled - integer :: n ! size of the data to be assigned + integer :: n !< size of the data to be assigned if (index < 1.or.index > num_fields) & call mpp_error(FATAL,'invalid index in call to get_time_axis') - n = min(size(time),size(field(index)%time)) + n = min(size(time),size(loaded_fields(index)%time)) - time(1:n) = field(index)%time(1:n) + time(1:n) = loaded_fields(index)%time(1:n) end subroutine -! -! -! -! exit time_interp_external_mod. Close all open files and -! release storage -! - subroutine time_interp_external_exit() +!> exit time_interp_external_mod. Close all open files and +!! release storage +subroutine time_interp_external_exit() - integer :: i -! -! release storage arrays -! - do i=1,num_fields - deallocate(field(i)%time,field(i)%start_time,field(i)%end_time,& - field(i)%period,field(i)%data,field(i)%mask,field(i)%ibuf) - if (ASSOCIATED(field(i)%src_data)) deallocate(field(i)%src_data) - field(i)%domain = NULL_DOMAIN2D - field(i)%nbuf = 0 - field(i)%slope = 0. - field(i)%intercept = 0. - enddo + integer :: i + ! + ! release storage arrays + ! + do i=1,num_fields + deallocate(loaded_fields(i)%time,loaded_fields(i)%start_time,loaded_fields(i)%end_time,& + loaded_fields(i)%period,loaded_fields(i)%data,loaded_fields(i)%mask,loaded_fields(i)%ibuf) + if (ASSOCIATED(loaded_fields(i)%src_data)) deallocate(loaded_fields(i)%src_data) + loaded_fields(i)%domain = NULL_DOMAIN2D + loaded_fields(i)%nbuf = 0 + loaded_fields(i)%slope = 0.0_r8_kind + loaded_fields(i)%intercept = 0.0_r8_kind + enddo - !-- close all the files opended - do i = 1, num_files - call close_file(opened_files(i)%fileobj) - deallocate(opened_files(i)%fileobj) - enddo + !-- close all the files opended + do i = 1, num_files + call close_file(opened_files(i)%fileobj) + deallocate(opened_files(i)%fileobj) + enddo + + deallocate(loaded_fields) + deallocate(opened_files) - deallocate(field) - deallocate(opened_files) + num_fields = 0 - num_fields = 0 + module_initialized = .false. - module_initialized = .false. +end subroutine time_interp_external_exit - end subroutine time_interp_external_exit -! NAME="time_interp_external_exit" +#include "time_interp_external2_r4.fh" +#include "time_interp_external2_r8.fh" end module time_interp_external2_mod !> @}