From 470c2f38dc563e5715b8c563e9c19c5ebba18850 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 12 Feb 2024 21:31:40 -0700 Subject: [PATCH 01/10] Add tests for DaysPastPlanting(). Includes a new test module with Gregorian calendar in which several tests are currently failing because DaysPastPlanting() uses days in THIS year. --- .../test/CNPhenology_test/CMakeLists.txt | 3 +- .../test/CNPhenology_test/test_CNPhenology.pf | 93 ++++++++ .../test_CNPhenology_gregorian.pf | 212 ++++++++++++++++++ 3 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 src/biogeochem/test/CNPhenology_test/test_CNPhenology_gregorian.pf diff --git a/src/biogeochem/test/CNPhenology_test/CMakeLists.txt b/src/biogeochem/test/CNPhenology_test/CMakeLists.txt index 2367c86612..4091b3917c 100644 --- a/src/biogeochem/test/CNPhenology_test/CMakeLists.txt +++ b/src/biogeochem/test/CNPhenology_test/CMakeLists.txt @@ -1,5 +1,6 @@ set (pfunit_sources - test_CNPhenology.pf) + test_CNPhenology.pf + test_CNPhenology_gregorian.pf) add_pfunit_ctest(CNPhenology TEST_SOURCES "${pfunit_sources}" diff --git a/src/biogeochem/test/CNPhenology_test/test_CNPhenology.pf b/src/biogeochem/test/CNPhenology_test/test_CNPhenology.pf index 9e06bc74e2..55b742d42d 100644 --- a/src/biogeochem/test/CNPhenology_test/test_CNPhenology.pf +++ b/src/biogeochem/test/CNPhenology_test/test_CNPhenology.pf @@ -501,4 +501,97 @@ contains end subroutine test_was_sown_in_this_window_sameday + @Test + subroutine test_DaysPastPlanting_mar25_feb9(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Feb. 9 in a non-leap year + call unittest_timemgr_set_curr_date(1, 2, 9, this%dtime) + + idop = 84 ! Mar. 25 + idpp = DaysPastPlanting(idop) + + @assertEqual(321, idpp) + + end subroutine test_DaysPastPlanting_mar25_feb9 + + @Test + subroutine test_DaysPastPlanting_feb9_mar25(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Mar. 25 in a non-leap year + call unittest_timemgr_set_curr_date(1, 3, 25, this%dtime) + + idop = 40 ! Feb. 9 + idpp = DaysPastPlanting(idop) + + @assertEqual(44, idpp) + + end subroutine test_DaysPastPlanting_feb9_mar25 + + @Test + subroutine test_DaysPastPlanting_feb9_EODdec31(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Last timestep of Dec. 31 in a non-leap year + call unittest_timemgr_set_curr_date(3, 1, 1, 0) + + idop = 40 ! Feb. 9 + idpp = DaysPastPlanting(idop) + + @assertEqual(325, idpp) + + end subroutine test_DaysPastPlanting_feb9_EODdec31 + + @Test + subroutine test_DaysPastPlanting_feb9_BODjan1(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! First timestep of Jan. 1 in a non-leap year following + ! another non-leap year + call unittest_timemgr_set_curr_date(3, 1, 1, this%dtime) + + idop = 40 ! Feb. 9 + idpp = DaysPastPlanting(idop) + + @assertEqual(326, idpp) + + end subroutine test_DaysPastPlanting_feb9_BODjan1 + + @Test + subroutine test_DaysPastPlanting_feb9_mar25_leap(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Mar. 25 in a leap year, although that doesn't matter in + ! this test module because there are no leap days + call unittest_timemgr_set_curr_date(4, 3, 25, this%dtime) + + idop = 40 ! Feb. 9 + idpp = DaysPastPlanting(idop) + + @assertEqual(44, idpp) + + end subroutine test_DaysPastPlanting_feb9_mar25_leap + + @Test + subroutine test_DaysPastPlanting_feb9_jan2_lastYrLeap(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Jan. 2 in the year AFTER a leap year, although that doesn't + ! matter in this test module because there are no leap days + call unittest_timemgr_set_curr_date(5, 1, 2, this%dtime) + + idop = 40 ! Feb. 9 + idpp = DaysPastPlanting(idop) + + @assertEqual(327, idpp) + + end subroutine test_DaysPastPlanting_feb9_jan2_lastYrLeap + end module test_CNPhenology diff --git a/src/biogeochem/test/CNPhenology_test/test_CNPhenology_gregorian.pf b/src/biogeochem/test/CNPhenology_test/test_CNPhenology_gregorian.pf new file mode 100644 index 0000000000..3cb797f6f0 --- /dev/null +++ b/src/biogeochem/test/CNPhenology_test/test_CNPhenology_gregorian.pf @@ -0,0 +1,212 @@ +module test_CNPhenology_gregorian + + ! Tests of CNPhenologyMod specific to the Gregorian calendar + + use funit + use unittestSubgridMod + use CNPhenologyMod + use unittestTimeManagerMod, only : unittest_timemgr_setup, unittest_timemgr_teardown + use unittestTimeManagerMod, only : unittest_timemgr_set_curr_date + use unittestSimpleSubgridSetupsMod, only : setup_single_veg_patch, setup_n_veg_patches + use shr_kind_mod , only : r8 => shr_kind_r8 + use pftconMod + + implicit none + + @TestCase + type, extends(TestCase) :: TestCNPhenology + integer :: dtime + real(r8) :: fracday + real(r8) :: onset_gdd + real(r8) :: onset_gddflag + real(r8) :: soilt + real(r8) :: soila10 + real(r8) :: t_a5min + real(r8) :: dayl + real(r8) :: snow_5day + real(r8) :: ws_flag + real(r8) :: crit_onset_gdd + real(r8) :: season_decid_temperate + contains + procedure :: setUp + procedure :: tearDown + end type TestCNPhenology + + real(r8), parameter :: tol = 1.e-13_r8 + +contains + + subroutine setUp(this) + use clm_varctl, only : use_crop + use clm_varcon, only: clm_varcon_init, zisoi, secspday + use clm_varpar, only: nlevlak, nlevgrnd, nlevdecomp_full + class(TestCNPhenology), intent(inout) :: this + real(r8), parameter :: my_zisoi(5) = [0.01_r8, 0.02_r8, 2._r8, 4._r8, 6._r8] + + use_crop = .false. + this%dtime = 1800 + this%fracday = real(this%dtime,r8) / secspday + ! Setup time manager + call unittest_timemgr_setup(dtime=this%dtime, use_gregorian_calendar=.true.) + + this%onset_gdd = 0._r8 ! Will be reset... + this%onset_gddflag = 0._r8 ! Will be reset... + ! Temperatures + this%soilt = 273._r8 ! Below freezing + this%soila10 = 273._r8 ! Below freezing + this%t_a5min = 273._r8 ! Below freezing + + this%dayl = 19500._r8 ! Below half of critical dayl + this%snow_5day = 1._r8 ! Above threshold + this%ws_flag = 1._r8 ! After winter solstice + this%crit_onset_gdd = 1._r8 + this%season_decid_temperate = 0._r8 ! Non temperate plant + + call setup_single_veg_patch(pft_type=1) + + nlevgrnd = size(my_zisoi) + nlevlak = 10 + nlevdecomp_full = nlevgrnd + call clm_varcon_init( is_simple_buildtemp = .true.) + zisoi(0) = 0._r8 + zisoi(1:nlevgrnd) = my_zisoi(:) + + call CNPhenologySetParams() + call CNPhenologyInit( bounds ) + call pftcon%InitForTesting() + pftcon%season_decid_temperate = this%season_decid_temperate + + end subroutine setUp + + subroutine tearDown(this) + use clm_varcon, only: clm_varcon_clean + class(TestCNPhenology), intent(inout) :: this + + call unittest_timemgr_teardown() + call clm_varcon_clean() + call unittest_subgrid_teardown() + call pftcon%Clean() + + end subroutine tearDown + + @Test + subroutine test_DaysPastPlanting_mar25_feb9(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Feb. 9 in a non-leap year preceded by a non-leap year + call unittest_timemgr_set_curr_date(3, 2, 9, this%dtime) + + idop = 84 ! Mar. 25 + idpp = DaysPastPlanting(idop) + + @assertEqual(321, idpp) + + end subroutine test_DaysPastPlanting_mar25_feb9 + + @Test + subroutine test_DaysPastPlanting_mar25_feb9_leap(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Feb. 9 in a leap year + call unittest_timemgr_set_curr_date(4, 2, 9, this%dtime) + + idop = 84 ! Mar. 25 + idpp = DaysPastPlanting(idop) + + @assertEqual(321, idpp) + + end subroutine test_DaysPastPlanting_mar25_feb9_leap + + @Test + subroutine test_DaysPastPlanting_mar25_feb9_lastYrLeap(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Feb. 9 in a non-leap year preceded by a leap year + call unittest_timemgr_set_curr_date(5, 2, 9, this%dtime) + + idop = 84 ! Mar. 25 + idpp = DaysPastPlanting(idop) + + @assertEqual(322, idpp) + + end subroutine test_DaysPastPlanting_mar25_feb9_lastYrLeap + + @Test + subroutine test_DaysPastPlanting_feb9_mar25(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Mar. 25 in a non-leap year, day 84 + call unittest_timemgr_set_curr_date(1, 3, 25, this%dtime) + + idop = 40 ! Feb. 9 + idpp = DaysPastPlanting(idop) + + @assertEqual(44, idpp) + + end subroutine test_DaysPastPlanting_feb9_mar25 + + @Test + subroutine test_DaysPastPlanting_feb9_mar25_leap(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Mar. 25 in a leap year, day 85 + call unittest_timemgr_set_curr_date(4, 3, 25, this%dtime) + + idop = 40 ! Feb. 9 + idpp = DaysPastPlanting(idop) + + @assertEqual(45, idpp) + + end subroutine test_DaysPastPlanting_feb9_mar25_leap + + @Test + subroutine test_DaysPastPlanting_feb9_jan2_leap(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Jan. 2 in a leap year + call unittest_timemgr_set_curr_date(4, 1, 2, this%dtime) + + idop = 40 ! Feb. 9 + idpp = DaysPastPlanting(idop) + + @assertEqual(327, idpp) + + end subroutine test_DaysPastPlanting_feb9_jan2_leap + + @Test + subroutine test_DaysPastPlanting_feb9_EODdec31_leap(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Last timestep of Dec. 31 in a leap year + call unittest_timemgr_set_curr_date(5, 1, 1, 0) + + idop = 40 ! Feb. 9 + idpp = DaysPastPlanting(idop) + + @assertEqual(326, idpp) + + end subroutine test_DaysPastPlanting_feb9_EODdec31_leap + + @Test + subroutine test_DaysPastPlanting_feb9_jan2_lastYrLeap(this) + class(TestCNPhenology), intent(inout) :: this + integer :: idop, idpp + + ! Jan. 2 in the year AFTER a leap year + call unittest_timemgr_set_curr_date(5, 1, 2, this%dtime) + + idop = 40 ! Feb. 9 + idpp = DaysPastPlanting(idop) + + @assertEqual(328, idpp) + + end subroutine test_DaysPastPlanting_feb9_jan2_lastYrLeap + +end module test_CNPhenology_gregorian From 698126841be43cfcc84e161b2b72c38b613fff66 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 14 Feb 2024 16:08:27 -0700 Subject: [PATCH 02/10] Edit a comment in DaysPastPlanting(). --- src/biogeochem/CNPhenologyMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index fffb19bc46..7770bfb6b2 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2747,8 +2747,8 @@ function DaysPastPlanting(idop, jday_in) if (jday >= idop) then DaysPastPlanting = jday - idop else - ! As long as crops have at most a 365-day growing season, using get_curr_days_per_year() - ! should give the same result of this function as using get_prev_days_per_year(). + ! get_curr_days_per_year() or get_prev_days_per_year() would only differ in the last timestep + ! of the year, but in that case this line is not reached. DaysPastPlanting = jday - idop + get_curr_days_per_year() end if From b4b836aeed741d0e5067efe144b7525b6fa93ef5 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 14 Feb 2024 16:10:57 -0700 Subject: [PATCH 03/10] Remove optional jday_in from DaysPastPlanting(). Doing so because it complicates the determination of "how many days were in the previous year." Was only used in CropPhenology(), and results should be identical without it. --- src/biogeochem/CNPhenologyMod.F90 | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 7770bfb6b2..d64aeee627 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2210,7 +2210,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & end if ! days past planting may determine harvest - idpp = DaysPastPlanting(idop(p), jday) + idpp = DaysPastPlanting(idop(p)) ! onset_counter initialized to zero when .not. croplive ! offset_counter relevant only at time step of harvest @@ -2723,26 +2723,19 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & end subroutine PlantCrop !----------------------------------------------------------------------- - function DaysPastPlanting(idop, jday_in) + function DaysPastPlanting(idop) ! !USES: use clm_time_manager, only : get_prev_calday, get_curr_days_per_year ! ! !ARGUMENTS: integer, intent(in) :: idop ! patch day of planting - integer, optional, intent(in) :: jday_in ! julian day of the year ! ! !LOCAL VARIABLES integer :: DaysPastPlanting integer :: jday - ! Must use separate jday_in and jday because we can't redefine an intent(in) - ! variable, even if it wasn't provided in the function call. - if (present(jday_in)) then - jday = jday_in - else - ! Use prev instead of curr to avoid jday=1 in last timestep of year - jday = get_prev_calday() - end if + ! Use prev instead of curr to avoid jday=1 in last timestep of year + jday = get_prev_calday() if (jday >= idop) then DaysPastPlanting = jday - idop From 05ee37c0ccd163faa1840bb69af58fb5a00c11b3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 14 Feb 2024 16:13:37 -0700 Subject: [PATCH 04/10] DaysPastPlanting() now uses # days in LAST year, not THIS year. * Resolves failures in test_CNPhenology_gregorian. * Contributes to ESCOMP/CTSM#1595: Bugs in crop phenology when running with a Gregorian calendar (https://github.com/ESCOMP/CTSM/issues/1595). --- src/biogeochem/CNPhenologyMod.F90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index d64aeee627..aa153187fb 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2726,6 +2726,7 @@ end subroutine PlantCrop function DaysPastPlanting(idop) ! !USES: use clm_time_manager, only : get_prev_calday, get_curr_days_per_year + use clm_varcon , only : secspday ! ! !ARGUMENTS: integer, intent(in) :: idop ! patch day of planting @@ -2742,7 +2743,7 @@ function DaysPastPlanting(idop) else ! get_curr_days_per_year() or get_prev_days_per_year() would only differ in the last timestep ! of the year, but in that case this line is not reached. - DaysPastPlanting = jday - idop + get_curr_days_per_year() + DaysPastPlanting = jday - idop + get_curr_days_per_year(offset = -365*int(secspday)) end if end function DaysPastPlanting From 5c9ce694cb08f9e3f1e5c0411cd32938897fad10 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 14 Feb 2024 16:24:10 -0700 Subject: [PATCH 05/10] CropPhenology(): Use get_prev_days_per_year() instead of _curr_. Should make no difference in standard runs. Could make a difference (harvest one timestep earlier) in Gregorian runs, but only if growing seasons of 365 days are allowed, which is not the case with default parameters except in GDD-Generating runs. --- src/biogeochem/CNPhenologyMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index aa153187fb..52da563b18 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -1792,7 +1792,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & ! handle CN fluxes during the phenological onset & offset periods. ! !USES: - use clm_time_manager , only : get_prev_calday, get_curr_days_per_year, is_beg_curr_year + use clm_time_manager , only : get_prev_calday, get_prev_days_per_year, is_beg_curr_year use clm_time_manager , only : get_average_days_per_year use clm_time_manager , only : get_prev_date use clm_time_manager , only : is_doy_in_interval, is_end_curr_day @@ -1921,7 +1921,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & ) ! get time info - dayspyr = get_curr_days_per_year() + dayspyr = get_prev_days_per_year() avg_dayspyr = get_average_days_per_year() jday = get_prev_calday() call get_prev_date(kyr, kmo, kda, mcsec) From f05ce095f5e4773ac9dda11cbc2e50e01800225c Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 5 Apr 2024 15:44:33 -0600 Subject: [PATCH 06/10] Functionize PlantDate_to_PlantJday(). --- src/biogeochem/CNPhenologyMod.F90 | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 52da563b18..c3cd4fe989 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -61,6 +61,7 @@ module CNPhenologyMod public :: SeasonalCriticalDaylength ! Critical day length needed for Seasonal decidious offset public :: get_swindow public :: was_sown_in_this_window + public :: PlantDate_to_PlantJday ! !PRIVITE MEMBER FIUNCTIONS: private :: CNPhenologyClimate ! Get climatological everages to figure out triggers for Phenology @@ -2477,6 +2478,24 @@ subroutine CropPhase(bounds, num_pcropp, filter_pcropp, & end subroutine CropPhase + function PlantDate_to_PlantJday(plantdate) result(jday) + ! + ! !DESCRIPTION: + ! Converts a plantdate from parameter file (e.g., mn[NS]Hplantdate) to a jday + ! that's actually used in the model (e.g., minplantjday). + ! + ! !USES: + use clm_time_manager, only: get_calday + ! !ARGUMENTS + integer, intent(in) :: plantdate + ! + ! Return value + integer :: jday + + jday = int( get_calday( plantdate, 0 ) ) + end function PlantDate_to_PlantJday + + !----------------------------------------------------------------------- subroutine CropPhenologyInit(bounds) ! @@ -2509,11 +2528,11 @@ subroutine CropPhenologyInit(bounds) maxplantjday(:,:) = huge(1) do n = npcropmin, npcropmax if (pftcon%is_pft_known_to_model(n)) then - minplantjday(n, inNH) = int( get_calday( pftcon%mnNHplantdate(n), 0 ) ) - maxplantjday(n, inNH) = int( get_calday( pftcon%mxNHplantdate(n), 0 ) ) + minplantjday(n, inNH) = PlantDate_to_PlantJday(pftcon%mnNHplantdate(n)) + maxplantjday(n, inNH) = PlantDate_to_PlantJday(pftcon%mxNHplantdate(n)) - minplantjday(n, inSH) = int( get_calday( pftcon%mnSHplantdate(n), 0 ) ) - maxplantjday(n, inSH) = int( get_calday( pftcon%mxSHplantdate(n), 0 ) ) + minplantjday(n, inSH) = PlantDate_to_PlantJday(pftcon%mnSHplantdate(n)) + maxplantjday(n, inSH) = PlantDate_to_PlantJday(pftcon%mxSHplantdate(n)) end if end do From ab66d5fec4c70f8e72b9ac01b7f864bba0cda5c5 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 5 Apr 2024 15:45:00 -0600 Subject: [PATCH 07/10] Unit-test PlantDate_to_PlantJday(). Failing. Fails with Gregorian calendar in non-leap years after Feb. 28, because when get_calday() is called with something like March 25 (325), it's assumed to be in year 0, which is leap. --- .../test/CNPhenology_test/test_CNPhenology.pf | 21 +++++++ .../test_CNPhenology_gregorian.pf | 62 ++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/biogeochem/test/CNPhenology_test/test_CNPhenology.pf b/src/biogeochem/test/CNPhenology_test/test_CNPhenology.pf index 55b742d42d..fd4f8a40f3 100644 --- a/src/biogeochem/test/CNPhenology_test/test_CNPhenology.pf +++ b/src/biogeochem/test/CNPhenology_test/test_CNPhenology.pf @@ -594,4 +594,25 @@ contains end subroutine test_DaysPastPlanting_feb9_jan2_lastYrLeap + @Test + subroutine test_PlantDate_to_PlantJday_jan1(this) + class(TestCNPhenology), intent(inout) :: this + + @assertEqual(1, PlantDate_to_PlantJday(101)) + end subroutine test_PlantDate_to_PlantJday_jan1 + + @Test + subroutine test_PlantDate_to_PlantJday_mar25(this) + class(TestCNPhenology), intent(inout) :: this + + @assertEqual(84, PlantDate_to_PlantJday(325)) + end subroutine test_PlantDate_to_PlantJday_mar25 + + @Test + subroutine test_PlantDate_to_PlantJday_dec31(this) + class(TestCNPhenology), intent(inout) :: this + + @assertEqual(365, PlantDate_to_PlantJday(1231)) + end subroutine test_PlantDate_to_PlantJday_dec31 + end module test_CNPhenology diff --git a/src/biogeochem/test/CNPhenology_test/test_CNPhenology_gregorian.pf b/src/biogeochem/test/CNPhenology_test/test_CNPhenology_gregorian.pf index 3cb797f6f0..006e58468b 100644 --- a/src/biogeochem/test/CNPhenology_test/test_CNPhenology_gregorian.pf +++ b/src/biogeochem/test/CNPhenology_test/test_CNPhenology_gregorian.pf @@ -56,7 +56,7 @@ contains this%soila10 = 273._r8 ! Below freezing this%t_a5min = 273._r8 ! Below freezing - this%dayl = 19500._r8 ! Below half of critical dayl + this%dayl = 19500._r8 ! Below half of critical dayl this%snow_5day = 1._r8 ! Above threshold this%ws_flag = 1._r8 ! After winter solstice this%crit_onset_gdd = 1._r8 @@ -209,4 +209,64 @@ contains end subroutine test_DaysPastPlanting_feb9_jan2_lastYrLeap + @Test + subroutine test_PlantDate_to_PlantJday_jan1(this) + class(TestCNPhenology), intent(inout) :: this + + ! Only year here should matter: non-leap + call unittest_timemgr_set_curr_date(5, 7, 24, this%dtime) + + @assertEqual(1, PlantDate_to_PlantJday(101)) + end subroutine test_PlantDate_to_PlantJday_jan1 + + @Test + subroutine test_PlantDate_to_PlantJday_mar25(this) + class(TestCNPhenology), intent(inout) :: this + + ! Only year here should matter: non-leap + call unittest_timemgr_set_curr_date(5, 7, 24, this%dtime) + + @assertEqual(84, PlantDate_to_PlantJday(325)) + end subroutine test_PlantDate_to_PlantJday_mar25 + + @Test + subroutine test_PlantDate_to_PlantJday_dec31(this) + class(TestCNPhenology), intent(inout) :: this + + ! Only year here should matter: non-leap + call unittest_timemgr_set_curr_date(5, 7, 24, this%dtime) + + @assertEqual(365, PlantDate_to_PlantJday(1231)) + end subroutine test_PlantDate_to_PlantJday_dec31 + + @Test + subroutine test_PlantDate_to_PlantJday_jan1_leap(this) + class(TestCNPhenology), intent(inout) :: this + + ! Only year here should matter: leap + call unittest_timemgr_set_curr_date(4, 7, 24, this%dtime) + + @assertEqual(1, PlantDate_to_PlantJday(101)) + end subroutine test_PlantDate_to_PlantJday_jan1_leap + + @Test + subroutine test_PlantDate_to_PlantJday_mar25_leap(this) + class(TestCNPhenology), intent(inout) :: this + + ! Only year here should matter: leap + call unittest_timemgr_set_curr_date(4, 7, 24, this%dtime) + + @assertEqual(85, PlantDate_to_PlantJday(325)) + end subroutine test_PlantDate_to_PlantJday_mar25_leap + + @Test + subroutine test_PlantDate_to_PlantJday_dec31_leap(this) + class(TestCNPhenology), intent(inout) :: this + + ! Only year here should matter: leap + call unittest_timemgr_set_curr_date(4, 7, 24, this%dtime) + + @assertEqual(366, PlantDate_to_PlantJday(1231)) + end subroutine test_PlantDate_to_PlantJday_dec31_leap + end module test_CNPhenology_gregorian From e4e03df9deca3620276241e688204fb310c130dd Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 5 Apr 2024 15:58:45 -0600 Subject: [PATCH 08/10] PlantDate_to_PlantJday() now uses the current year. Unit tests now pass. --- src/biogeochem/CNPhenologyMod.F90 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index c3cd4fe989..b74a54c112 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2485,14 +2485,20 @@ function PlantDate_to_PlantJday(plantdate) result(jday) ! that's actually used in the model (e.g., minplantjday). ! ! !USES: - use clm_time_manager, only: get_calday + use clm_time_manager, only: get_calday, get_prev_date ! !ARGUMENTS integer, intent(in) :: plantdate ! + ! !LOCAL VARIABLES + integer :: kyr, kmo, kda, mcsec + ! ! Return value integer :: jday - jday = int( get_calday( plantdate, 0 ) ) + ! Get year to add to plantdate + call get_prev_date(kyr, kmo, kda, mcsec) + + jday = int( get_calday(10000*kyr + plantdate, 0 ) ) end function PlantDate_to_PlantJday From 41d7521f1e642e4a70c118051c1f49ee302cc65c Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 5 Apr 2024 16:00:00 -0600 Subject: [PATCH 09/10] Functionize UpdateSowingWindows(). --- src/biogeochem/CNPhenologyMod.F90 | 33 ++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index b74a54c112..134272e155 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2502,6 +2502,29 @@ function PlantDate_to_PlantJday(plantdate) result(jday) end function PlantDate_to_PlantJday + subroutine UpdateSowingWindows() + ! + ! !DESCRIPTION: + ! Updates sowing windows with dates for this year. + ! + ! !USES: + use pftconMod, only: npcropmin, npcropmax + ! + ! !LOCAL VARIABLES + integer :: n + + do n = npcropmin, npcropmax + if (pftcon%is_pft_known_to_model(n)) then + minplantjday(n, inNH) = PlantDate_to_PlantJday(pftcon%mnNHplantdate(n)) + maxplantjday(n, inNH) = PlantDate_to_PlantJday(pftcon%mxNHplantdate(n)) + + minplantjday(n, inSH) = PlantDate_to_PlantJday(pftcon%mnSHplantdate(n)) + maxplantjday(n, inSH) = PlantDate_to_PlantJday(pftcon%mxSHplantdate(n)) + end if + end do + end subroutine UpdateSowingWindows + + !----------------------------------------------------------------------- subroutine CropPhenologyInit(bounds) ! @@ -2532,15 +2555,7 @@ subroutine CropPhenologyInit(bounds) ! Convert planting dates into julian day minplantjday(:,:) = huge(1) maxplantjday(:,:) = huge(1) - do n = npcropmin, npcropmax - if (pftcon%is_pft_known_to_model(n)) then - minplantjday(n, inNH) = PlantDate_to_PlantJday(pftcon%mnNHplantdate(n)) - maxplantjday(n, inNH) = PlantDate_to_PlantJday(pftcon%mxNHplantdate(n)) - - minplantjday(n, inSH) = PlantDate_to_PlantJday(pftcon%mnSHplantdate(n)) - maxplantjday(n, inSH) = PlantDate_to_PlantJday(pftcon%mxSHplantdate(n)) - end if - end do + call UpdateSowingWindows() ! Figure out what hemisphere each PATCH is in do p = bounds%begp, bounds%endp From a8ea757c02f7637d1f9eb27d9b49d5e88d16ed3a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 5 Apr 2024 16:10:12 -0600 Subject: [PATCH 10/10] Call UpdateSowingWindows() at beginning of every year. This ensures that, during Gregorian runs, the dates returned for sowing windows always are leap or non-leap according to whether the current year is. --- src/biogeochem/CNPhenologyMod.F90 | 4 ++++ .../test_CNPhenology_gregorian.pf | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 134272e155..575ebbd1ad 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -1927,6 +1927,10 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & jday = get_prev_calday() call get_prev_date(kyr, kmo, kda, mcsec) + if (is_beg_curr_year()) then + call UpdateSowingWindows() + end if + if (use_fertilizer) then ndays_on = 20._r8 ! number of days to fertilize else diff --git a/src/biogeochem/test/CNPhenology_test/test_CNPhenology_gregorian.pf b/src/biogeochem/test/CNPhenology_test/test_CNPhenology_gregorian.pf index 006e58468b..d002f2fe84 100644 --- a/src/biogeochem/test/CNPhenology_test/test_CNPhenology_gregorian.pf +++ b/src/biogeochem/test/CNPhenology_test/test_CNPhenology_gregorian.pf @@ -239,6 +239,17 @@ contains @assertEqual(365, PlantDate_to_PlantJday(1231)) end subroutine test_PlantDate_to_PlantJday_dec31 + @Test + subroutine test_PlantDate_to_PlantJday_dec31_begyr(this) + use clm_time_manager, only: is_beg_curr_year + class(TestCNPhenology), intent(inout) :: this + + ! Beginning of a non-leap year + call unittest_timemgr_set_curr_date(5, 1, 1, this%dtime) + + @assertEqual(365, PlantDate_to_PlantJday(1231)) + end subroutine test_PlantDate_to_PlantJday_dec31_begyr + @Test subroutine test_PlantDate_to_PlantJday_jan1_leap(this) class(TestCNPhenology), intent(inout) :: this @@ -269,4 +280,15 @@ contains @assertEqual(366, PlantDate_to_PlantJday(1231)) end subroutine test_PlantDate_to_PlantJday_dec31_leap + @Test + subroutine test_PlantDate_to_PlantJday_dec31_leap_begyr(this) + use clm_time_manager, only: is_beg_curr_year + class(TestCNPhenology), intent(inout) :: this + + ! Beginning of a leap year + call unittest_timemgr_set_curr_date(4, 1, 1, this%dtime) + + @assertEqual(366, PlantDate_to_PlantJday(1231)) + end subroutine test_PlantDate_to_PlantJday_dec31_leap_begyr + end module test_CNPhenology_gregorian