module dynTimeInfoMod #include "shr_assert.h" !--------------------------------------------------------------------------- ! !DESCRIPTION: ! Contains a derived type and associated methods for storing and working with time ! information for a single dynamic landuse file. The assumption is that there is a ! single time sample per year. ! ! !USES: use clm_varctl , only : iulog use shr_log_mod , only : errMsg => shr_log_errMsg use abortutils , only : endrun use shr_kind_mod, only: r8 => shr_kind_r8 ! implicit none private ! ! !PUBLIC TYPES: ! The following provides an enumeration that defines possible values for one of the ! components of time_info_type. The public instances of the type (defined below) ! effectively define the enumeration. type, public :: year_position_type private integer :: flag end type year_position_type type(year_position_type), parameter, public :: & YEAR_POSITION_START_OF_TIMESTEP = year_position_type(1), & YEAR_POSITION_END_OF_TIMESTEP = year_position_type(2) type, public :: time_info_type private ! Static information about the file: integer :: nyears ! number of years in the file integer, allocatable :: years(:) ! all years in this file ! Other static information: type(year_position_type) :: year_position ! how to obtain the model year relative to the current timestep ! Information that potentially changes each time step: integer :: time_index_lower ! lower bound index of the current interval integer :: time_index_upper ! upper bound index of the current interval contains procedure :: set_current_year ! should be called every time step to update info with the current model year procedure :: get_time_index_lower ! get lower bound index of current interval procedure :: get_time_index_upper ! get upper bound index of current interval procedure :: get_yearfrac ! get the fractional position in the current year procedure :: get_year ! get the year associated with a given time index procedure :: is_within_bounds ! return true if we are currently within the bounds of this file procedure :: is_before_time_series ! returns true if we are currently prior to the bounds of this file procedure :: is_after_time_series ! returns true if we are currently after the bounds of this file ! (if the last year of the file is (e.g.) 2005, then this is TRUE if the current year is 2005) procedure, private :: set_info_from_year ! given the current model year, sets object data appropriately procedure, private :: year_in_current_interval ! returns true if the current year is in the current interval end type time_info_type interface time_info_type module procedure constructor ! initialize a time_info_type object end interface time_info_type character(len=*), parameter, private :: sourcefile = & __FILE__ contains ! ====================================================================== ! Constructors ! ====================================================================== !----------------------------------------------------------------------- type(time_info_type) function constructor(my_years, year_position) ! ! !DESCRIPTION: ! Initialize a time_info_type object ! ! !ARGUMENTS: ! all years in this file: integer, intent(in) :: my_years(:) ! how to obtain the model year relative to the current timestep; must be one of: ! - YEAR_POSITION_START_OF_TIMESTEP: use the year at the start of the timestep ! - YEAR_POSITION_END_OF_TIMESTEP: use the year at the end of the timestep type(year_position_type), intent(in) :: year_position !----------------------------------------------------------------------- constructor%nyears = size(my_years) allocate(constructor%years(constructor%nyears)) constructor%years = my_years constructor%year_position = year_position ! Set time_index_lower and time_index_upper arbitrarily; they'll get set correctly by set_current_year constructor%time_index_lower = 1 constructor%time_index_upper = 1 ! Set time_index_lower and time_index_upper to their correct values call constructor%set_current_year() end function constructor ! ====================================================================== ! Public methods ! ====================================================================== !----------------------------------------------------------------------- subroutine set_current_year(this) ! ! !DESCRIPTION: ! Update time information (time_index_lower and time_index_upper), based on the ! current model year. ! ! Should be called every time step ! ! !USES: use clm_time_manager , only : get_curr_date, get_prev_date ! ! !ARGUMENTS: class(time_info_type), intent(inout) :: this ! this object ! ! !LOCAL VARIABLES: integer :: year ! year (0, ...) for nstep+1 integer :: mon ! month (1, ..., 12) for nstep+1 integer :: day ! day of month (1, ..., 31) for nstep+1 integer :: sec ! seconds into current date for nstep+1 character(len=*), parameter :: subname = 'set_current_year' !----------------------------------------------------------------------- select case (this%year_position%flag) case (YEAR_POSITION_START_OF_TIMESTEP%flag) call get_prev_date(year, mon, day, sec) case (YEAR_POSITION_END_OF_TIMESTEP%flag) call get_curr_date(year, mon, day, sec) case default write(iulog,*) subname, ': unknown year position: ', this%year_position%flag call endrun(msg=errMsg(sourcefile, __LINE__)) end select call this%set_info_from_year(year) end subroutine set_current_year ! ---------------------------------------------------------------------- ! Various getter routines ! ---------------------------------------------------------------------- !----------------------------------------------------------------------- pure integer function get_time_index_lower(this) ! !DESCRIPTION: Get lower bound index of current interval ! ! !ARGUMENTS: class(time_info_type), intent(in) :: this !----------------------------------------------------------------------- get_time_index_lower = this%time_index_lower end function get_time_index_lower !----------------------------------------------------------------------- pure integer function get_time_index_upper(this) ! !DESCRIPTION: Get upper bound index of current interval ! ! !ARGUMENTS: class(time_info_type), intent(in) :: this !----------------------------------------------------------------------- get_time_index_upper = this%time_index_upper end function get_time_index_upper !----------------------------------------------------------------------- real(r8) function get_yearfrac(this) ! ! !DESCRIPTION: ! Get the fractional position in the current year (0 at midnight on Jan 1, and 1 at ! the end of Dec 31). ! ! This function uses the year_position metadata of this object to determine whether ! the fractional position in the year should be determined based on the start or end ! of the current timestep. ! ! !USES: use clm_time_manager, only : get_curr_yearfrac, get_prev_yearfrac ! ! !ARGUMENTS: class(time_info_type), intent(in) :: this ! ! !LOCAL VARIABLES: character(len=*), parameter :: subname = 'get_yearfrac' !----------------------------------------------------------------------- select case (this%year_position%flag) case (YEAR_POSITION_START_OF_TIMESTEP%flag) get_yearfrac = get_prev_yearfrac() case (YEAR_POSITION_END_OF_TIMESTEP%flag) get_yearfrac = get_curr_yearfrac() case default write(iulog,*) subname, ': unknown year position: ', this%year_position%flag call endrun(errMsg(sourcefile, __LINE__)) end select end function get_yearfrac !----------------------------------------------------------------------- integer function get_year(this, nt) ! !DESCRIPTION: Get the year associated with time index nt ! ! Note this can't be a pure function because of the call to shr_assert ! ! !ARGUMENTS: class(time_info_type), intent(in) :: this integer , intent(in) :: nt ! time index character(len=*), parameter :: subname = 'get_year' !----------------------------------------------------------------------- SHR_ASSERT(1 <= nt .and. nt <= this%nyears, subname // ': nt out of bounds') get_year = this%years(nt) end function get_year !----------------------------------------------------------------------- pure logical function is_within_bounds(this) ! !DESCRIPTION: Returns true if we are currently within the bounds of this file ! ! !ARGUMENTS: class(time_info_type), intent(in) :: this !----------------------------------------------------------------------- is_within_bounds = ((.not. this%is_before_time_series()) .and. & (.not. this%is_after_time_series())) end function is_within_bounds !----------------------------------------------------------------------- pure logical function is_before_time_series(this) ! !DESCRIPTION: Returns true if we are currently prior to the bounds of this file ! ! !ARGUMENTS: class(time_info_type), intent(in) :: this !----------------------------------------------------------------------- if (this%time_index_upper == 1) then is_before_time_series = .true. else is_before_time_series = .false. end if end function is_before_time_series !----------------------------------------------------------------------- pure logical function is_after_time_series(this) ! !DESCRIPTION: Returns true if we are currently after the bounds of this file ! ! If the last year of the file is (e.g.) 2005, then this is TRUE if the current year ! is 2005 ! ! !ARGUMENTS: class(time_info_type), intent(in) :: this !----------------------------------------------------------------------- if (this%time_index_lower == this%nyears) then is_after_time_series = .true. else is_after_time_series = .false. end if end function is_after_time_series ! ====================================================================== ! Private methods ! ====================================================================== !----------------------------------------------------------------------- pure logical function year_in_current_interval(this, cur_year) ! !DESCRIPTION: ! Returns true if the current year is in the current interval ! ! !ARGUMENTS: class(time_info_type), intent(in) :: this ! this object integer, intent(in) :: cur_year ! current model year !----------------------------------------------------------------------- if (this%years(this%time_index_lower) == cur_year .and. this%years(this%time_index_upper) == (cur_year + 1)) then ! Normal case: we're within the time series, in the same interval as before year_in_current_interval = .true. else if (this%is_before_time_series() .and. cur_year < this%years(1)) then ! We were and still are before the time series year_in_current_interval = .true. else if (this%is_after_time_series() .and. cur_year >= this%years(this%nyears)) then ! We were and still are after the time series year_in_current_interval = .true. else year_in_current_interval = .false. end if end function year_in_current_interval !----------------------------------------------------------------------- subroutine set_info_from_year(this, cur_year) ! ! !DESCRIPTION: ! Given the current model year, sets time information (time_index_lower and ! time_index_upper) appropriately. ! ! !ARGUMENTS: class(time_info_type), intent(inout) :: this ! this object integer, intent(in) :: cur_year ! current model year ! ! !LOCAL VARIABLES: logical :: found ! has the correct interval been found? integer :: n ! interval index character(len=*), parameter :: subname = 'set_info_from_year' !----------------------------------------------------------------------- ! Determine if current date spans the years ! ! If current year is less than first timeseries year, then use the first year from ! dynamic land cover file for both time_index_lower and time_index_upper, forcing constant weights until the ! model year enters the dynamic land cover dataset timeseries range. ! ! If current year is equal to or greater than the last timeseries year, then use the ! last year for both time_index_lower and time_index_upper, forcing constant weights for the remainder of the ! simulation. ! ! This mechanism permits the introduction of a dynamic pft period in the middle of a ! simulation, with constant weights before and after the dynamic period. associate( & nyears => this%nyears, & years => this%years, & time_index_lower => this%time_index_lower, & time_index_upper => this%time_index_upper) if (year_in_current_interval(this, cur_year)) then ! DO NOTHING - NT1 AND NT2 ARE ALREADY CORRECT else if (cur_year < years(1)) then ! prior to the first interval time_index_lower = 1 time_index_upper = 1 else if (cur_year >= years(nyears)) then ! past the last interval time_index_lower = nyears time_index_upper = nyears else ! within the time bounds of the file found = .false. do n = 1, nyears-1 if (cur_year == years(n)) then time_index_lower = n time_index_upper = n + 1 found = .true. exit end if end do if (.not. found) then write(iulog,*) subname//' ERROR: model year not found in pftdyn timeseries' write(iulog,*)'model year = ',cur_year call endrun(msg=errMsg(sourcefile, __LINE__)) end if end if end if SHR_ASSERT(time_index_upper <= nyears, subname // ': time_index_upper should not be greater than nyears') end associate end subroutine set_info_from_year end module dynTimeInfoMod