From 51d9f095ebe73eb5e1af0731cc85e36988b2b9b7 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 13:42:59 -0700 Subject: [PATCH 001/206] Break up a long line. --- src/main/clm_driver.F90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90 index 00a98e61b4..c4fa1b53f4 100644 --- a/src/main/clm_driver.F90 +++ b/src/main/clm_driver.F90 @@ -1078,7 +1078,8 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! ============================================================================ ! Update crop calendars ! ============================================================================ - call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, filter_inactive_and_active(nc)%pcropp, crop_inst) + call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, & + filter_inactive_and_active(nc)%pcropp, crop_inst) end if ! ============================================================================ From c763d664f23d15cc44d7afbb807359eea6cde30e Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 13:45:32 -0700 Subject: [PATCH 002/206] use_cropcal_rx_cultivar_gdds not needed in CropPhenology(). --- src/biogeochem/CNPhenologyMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index fffb19bc46..bf91532e5a 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -1808,7 +1808,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & use clm_varctl , only : use_fertilizer use clm_varctl , only : use_c13, use_c14 use clm_varcon , only : c13ratio, c14ratio - use clm_varctl , only : use_cropcal_rx_swindows, use_cropcal_rx_cultivar_gdds, use_cropcal_streams + use clm_varctl , only : use_cropcal_rx_swindows, use_cropcal_streams ! ! !ARGUMENTS: integer , intent(in) :: num_pcropp ! number of prog crop patches in filter From 846f1cc0485e6f69682159d9c3a54b0cd6c8d941 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 13:54:13 -0700 Subject: [PATCH 003/206] PlantCrop(): Be strict about PFT identification. --- src/biogeochem/CNPhenologyMod.F90 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index bf91532e5a..09515042b8 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2679,8 +2679,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & if (ivt(p) == ntmp_soybean .or. ivt(p) == nirrig_tmp_soybean .or. & ivt(p) == ntrp_soybean .or. ivt(p) == nirrig_trp_soybean) then gddmaturity(p) = min(gdd1020(p), hybgdd(ivt(p))) - end if - if (ivt(p) == ntmp_corn .or. ivt(p) == nirrig_tmp_corn .or. & + else if (ivt(p) == ntmp_corn .or. ivt(p) == nirrig_tmp_corn .or. & ivt(p) == ntrp_corn .or. ivt(p) == nirrig_trp_corn .or. & ivt(p) == nsugarcane .or. ivt(p) == nirrig_sugarcane .or. & ivt(p) == nmiscanthus .or. ivt(p) == nirrig_miscanthus .or. & @@ -2689,11 +2688,13 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & if (do_plant_normal) then gddmaturity(p) = max(950._r8, min(gddmaturity(p)+150._r8, 1850._r8)) end if - end if - if (ivt(p) == nswheat .or. ivt(p) == nirrig_swheat .or. & + else if (ivt(p) == nswheat .or. ivt(p) == nirrig_swheat .or. & ivt(p) == ncotton .or. ivt(p) == nirrig_cotton .or. & ivt(p) == nrice .or. ivt(p) == nirrig_rice) then gddmaturity(p) = min(gdd020(p), hybgdd(ivt(p))) + else + write(iulog, *) 'ERROR: PlantCrop(): unrecognized ivt ',ivt(p) + call endrun(msg="Stopping") end if endif From 5b056b744e64ac59aae5cbc44c99a1b3d9815f59 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 14:07:42 -0700 Subject: [PATCH 004/206] PlantCrop: Get gdd20 before setting gddmaturity. --- src/biogeochem/CNPhenologyMod.F90 | 36 ++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 09515042b8..efc581cffc 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2590,6 +2590,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & ! LOCAL VARAIBLES: integer s ! growing season index integer k ! grain pool index + real(r8) gdd20 ! GDD*20 value for this crop type real(r8) gdd_target ! cultivar GDD target this growing season real(r8) this_sowing_reason ! number representing sowing reason(s) logical did_rx_gdds ! did this patch use a prescribed harvest requirement? @@ -2668,6 +2669,26 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & endif endif + ! which GDD*20 variable does this crop use? + if (ivt(p) == ntmp_soybean .or. ivt(p) == nirrig_tmp_soybean .or. & + ivt(p) == ntrp_soybean .or. ivt(p) == nirrig_trp_soybean) then + gdd20 = gdd1020(p) + else if (ivt(p) == ntmp_corn .or. ivt(p) == nirrig_tmp_corn .or. & + ivt(p) == ntrp_corn .or. ivt(p) == nirrig_trp_corn .or. & + ivt(p) == nsugarcane .or. ivt(p) == nirrig_sugarcane .or. & + ivt(p) == nmiscanthus .or. ivt(p) == nirrig_miscanthus .or. & + ivt(p) == nswitchgrass .or. ivt(p) == nirrig_switchgrass) then + gdd20 = gdd820(p) + else if (ivt(p) == nswheat .or. ivt(p) == nirrig_swheat .or. & + ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat .or. & + ivt(p) == ncotton .or. ivt(p) == nirrig_cotton .or. & + ivt(p) == nrice .or. ivt(p) == nirrig_rice) then + gdd20 = gdd020(p) + else + write(iulog, *) 'ERROR: PlantCrop(): unrecognized ivt for gdd20: ',ivt(p) + call endrun(msg="Stopping") + end if + ! set GDD target did_rx_gdds = .false. if (use_cropcal_rx_cultivar_gdds .and. crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) .ge. 0._r8) then @@ -2677,23 +2698,22 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & gddmaturity(p) = hybgdd(ivt(p)) else if (ivt(p) == ntmp_soybean .or. ivt(p) == nirrig_tmp_soybean .or. & - ivt(p) == ntrp_soybean .or. ivt(p) == nirrig_trp_soybean) then - gddmaturity(p) = min(gdd1020(p), hybgdd(ivt(p))) + ivt(p) == ntrp_soybean .or. ivt(p) == nirrig_trp_soybean .or. & + ivt(p) == nswheat .or. ivt(p) == nirrig_swheat .or. & + ivt(p) == ncotton .or. ivt(p) == nirrig_cotton .or. & + ivt(p) == nrice .or. ivt(p) == nirrig_rice) then + gddmaturity(p) = min(gdd20, hybgdd(ivt(p))) else if (ivt(p) == ntmp_corn .or. ivt(p) == nirrig_tmp_corn .or. & ivt(p) == ntrp_corn .or. ivt(p) == nirrig_trp_corn .or. & ivt(p) == nsugarcane .or. ivt(p) == nirrig_sugarcane .or. & ivt(p) == nmiscanthus .or. ivt(p) == nirrig_miscanthus .or. & ivt(p) == nswitchgrass .or. ivt(p) == nirrig_switchgrass) then - gddmaturity(p) = max(950._r8, min(gdd820(p)*0.85_r8, hybgdd(ivt(p)))) + gddmaturity(p) = max(950._r8, min(gdd20*0.85_r8, hybgdd(ivt(p)))) if (do_plant_normal) then gddmaturity(p) = max(950._r8, min(gddmaturity(p)+150._r8, 1850._r8)) end if - else if (ivt(p) == nswheat .or. ivt(p) == nirrig_swheat .or. & - ivt(p) == ncotton .or. ivt(p) == nirrig_cotton .or. & - ivt(p) == nrice .or. ivt(p) == nirrig_rice) then - gddmaturity(p) = min(gdd020(p), hybgdd(ivt(p))) else - write(iulog, *) 'ERROR: PlantCrop(): unrecognized ivt ',ivt(p) + write(iulog, *) 'ERROR: PlantCrop(): unrecognized ivt for GDD target: ',ivt(p) call endrun(msg="Stopping") end if From 08906bb13054f32bb48e9a412beefd02bff9a3e2 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 14:25:50 -0700 Subject: [PATCH 005/206] Correct a comment. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 0ea63f2c6d..23f00a1759 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -35,7 +35,7 @@ module cropcalStreamMod integer, allocatable :: g_to_ig(:) ! Array matching gridcell index to data index type(shr_strdata_type) :: sdat_cropcal_swindow_start ! sowing window start input data stream type(shr_strdata_type) :: sdat_cropcal_swindow_end ! sowing window end input data stream - type(shr_strdata_type) :: sdat_cropcal_cultivar_gdds ! sdate input data stream + type(shr_strdata_type) :: sdat_cropcal_cultivar_gdds ! maturity requirement input data stream character(len=CS), allocatable :: stream_varnames_sdate(:) ! used for both start and end dates character(len=CS), allocatable :: stream_varnames_cultivar_gdds(:) integer :: ncft ! Number of crop functional types (excl. generic crops) From 6ef52cdc8a980d62a596e21d9e7e57878145a5bb Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 14:26:11 -0700 Subject: [PATCH 006/206] Remove a troubleshooting write to iulog. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 23f00a1759..75c3820e7e 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -490,7 +490,6 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) call ESMF_Finalize(endflag=ESMF_END_ABORT) endif end do - write(iulog,*) 'cropcal_interp(): Reading cultivar_gdds file DONE' end if ! use_cropcal_rx_cultivar_gdds deallocate(dataptr2d_cultivar_gdds) From 98f68e784fe4ab30fd559be0126cb847675b74fc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jan 2024 14:43:16 -0700 Subject: [PATCH 007/206] Optionally adjust rx maturity reqt based on recent climate. --- bld/CLMBuildNamelist.pm | 7 ++ .../namelist_definition_ctsm.xml | 5 + src/biogeochem/CNPhenologyMod.F90 | 6 +- src/biogeochem/CropType.F90 | 2 + src/cpl/share_esmf/cropcalStreamMod.F90 | 113 +++++++++++++++++- src/main/clm_driver.F90 | 2 +- src/main/clm_initializeMod.F90 | 2 +- src/main/clm_varctl.F90 | 1 + 8 files changed, 133 insertions(+), 5 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index fb44023cd5..54752b6dbc 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4198,10 +4198,14 @@ sub setup_logic_cropcal_streams { my $swindow_start_file = $nl->get_value('stream_fldFileName_swindow_start') ; my $swindow_end_file = $nl->get_value('stream_fldFileName_swindow_end') ; my $gdd_file = $nl->get_value('stream_fldFileName_cultivar_gdds') ; + my $gdd20_baseline_file = $nl->get_value('stream_fldFileName_gdd20_baseline') ; my $mesh_file = $nl->get_value('stream_meshfile_cropcal') ; if ( ($swindow_start_file eq '' and $swindow_start_file ne '') or ($swindow_start_file ne '' and $swindow_start_file eq '') ) { $log->fatal_error("When specifying sowing window dates, you must provide both swindow_start_file and swindow_end_file. To specify exact sowing dates, use the same file." ); } + if ( $gdd_file eq '' and $gdd20_baseline_file ne '' ) { + $log->fatal_error("If not providing stream_fldFileName_cultivar_gdds, don't provide stream_fldFileName_gdd20_baseline"); + } if ( $generate_crop_gdds eq '.true.' ) { if ( $use_mxmat eq '.true.' ) { $log->fatal_error("If generate_crop_gdds is true, you must also set use_mxmat to false" ); @@ -4215,6 +4219,9 @@ sub setup_logic_cropcal_streams { if ( $gdd_file ne '' ) { $log->fatal_error("If generate_crop_gdds is true, do not specify stream_fldFileName_cultivar_gdds") } + if ( $gdd20_baseline_file ne '' ) { + $log->fatal_error("If generate_crop_gdds is true, do not specify stream_fldFileName_gdd20_baseline") + } } if ( $mesh_file eq '' and ( $swindow_start_file ne '' or $gdd_file ne '' ) ) { $log->fatal_error("If prescribing crop sowing dates and/or maturity requirements, you must specify stream_meshfile_cropcal") diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 37c457141c..c88e987756 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1808,6 +1808,11 @@ Filename of input stream data for date (day of year) of end of sowing window. Ce Filename of input stream data for cultivar growing degree-day targets + +Filename of input stream data for baseline GDD20 values + + Filename of input stream data for crop calendar inputs diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index efc581cffc..f1f2d5fcc0 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2559,7 +2559,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & ! !USES: use clm_varctl , only : use_c13, use_c14 - use clm_varctl , only : use_cropcal_rx_cultivar_gdds, use_cropcal_streams + use clm_varctl , only : use_cropcal_rx_cultivar_gdds, use_cropcal_streams, adapt_cropcal_rx_cultivar_gdds use clm_varcon , only : c13ratio, c14ratio use clm_varpar , only : mxsowings use pftconMod , only : ntmp_corn, nswheat, nwwheat, ntmp_soybean @@ -2694,6 +2694,10 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & if (use_cropcal_rx_cultivar_gdds .and. crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) .ge. 0._r8) then gddmaturity(p) = crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) did_rx_gdds = .true. + if (adapt_cropcal_rx_cultivar_gdds) then + gddmaturity(p) = gddmaturity(p) * gdd20 / crop_inst%gdd20_baseline_patch(p) + !TODO SSR: Set maximum gddmaturity + end if else if (ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat) then gddmaturity(p) = hybgdd(ivt(p)) else diff --git a/src/biogeochem/CropType.F90 b/src/biogeochem/CropType.F90 index 760aa21b76..a7bccf1a73 100644 --- a/src/biogeochem/CropType.F90 +++ b/src/biogeochem/CropType.F90 @@ -52,6 +52,7 @@ module CropType integer , pointer :: rx_swindow_starts_thisyr_patch(:,:) ! all prescribed sowing window start dates for this patch this year (day of year) [patch, mxsowings] integer , pointer :: rx_swindow_ends_thisyr_patch (:,:) ! all prescribed sowing window end dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: rx_cultivar_gdds_thisyr_patch (:,:) ! all cultivar GDD targets for this patch this year (ddays) [patch, mxsowings] + real(r8), pointer :: gdd20_baseline_patch (:) ! GDD20 baseline for this patch (ddays) [patch] real(r8), pointer :: sdates_thisyr_patch (:,:) ! all actual sowing dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_starts_thisyr_patch(:,:) ! all sowing window start dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_ends_thisyr_patch (:,:) ! all sowing window end dates for this patch this year (day of year) [patch, mxsowings] @@ -235,6 +236,7 @@ subroutine InitAllocate(this, bounds) allocate(this%rx_swindow_starts_thisyr_patch(begp:endp,1:mxsowings)); this%rx_swindow_starts_thisyr_patch(:,:) = -1 allocate(this%rx_swindow_ends_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_swindow_ends_thisyr_patch (:,:) = -1 allocate(this%rx_cultivar_gdds_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_cultivar_gdds_thisyr_patch(:,:) = spval + allocate(this%gdd20_baseline_patch(begp:endp)) ; this%gdd20_baseline_patch(:) = spval allocate(this%sdates_thisyr_patch(begp:endp,1:mxsowings)) ; this%sdates_thisyr_patch(:,:) = spval allocate(this%swindow_starts_thisyr_patch(begp:endp,1:mxsowings)) ; this%swindow_starts_thisyr_patch(:,:) = spval allocate(this%swindow_ends_thisyr_patch (begp:endp,1:mxsowings)) ; this%swindow_ends_thisyr_patch (:,:) = spval diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 75c3820e7e..83f7fc0eb0 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -15,6 +15,7 @@ module cropcalStreamMod use abortutils , only : endrun use clm_varctl , only : iulog use clm_varctl , only : use_cropcal_rx_swindows, use_cropcal_rx_cultivar_gdds, use_cropcal_streams + use clm_varctl , only : adapt_cropcal_rx_cultivar_gdds use clm_varpar , only : mxpft use clm_varpar , only : mxsowings use perf_mod , only : t_startf, t_stopf @@ -36,13 +37,16 @@ module cropcalStreamMod type(shr_strdata_type) :: sdat_cropcal_swindow_start ! sowing window start input data stream type(shr_strdata_type) :: sdat_cropcal_swindow_end ! sowing window end input data stream type(shr_strdata_type) :: sdat_cropcal_cultivar_gdds ! maturity requirement input data stream + type(shr_strdata_type) :: sdat_cropcal_gdd20_baseline ! GDD20 baseline input data stream character(len=CS), allocatable :: stream_varnames_sdate(:) ! used for both start and end dates character(len=CS), allocatable :: stream_varnames_cultivar_gdds(:) + character(len=CS), allocatable :: stream_varnames_gdd20_baseline(:) integer :: ncft ! Number of crop functional types (excl. generic crops) logical :: allow_invalid_swindow_inputs ! Fall back on paramfile sowing windows in cases of invalid values in stream_fldFileName_swindow_start and _end? character(len=CL) :: stream_fldFileName_swindow_start ! sowing window start stream filename to read character(len=CL) :: stream_fldFileName_swindow_end ! sowing window end stream filename to read character(len=CL) :: stream_fldFileName_cultivar_gdds ! cultivar growing degree-days stream filename to read + character(len=CL) :: stream_fldFileName_gdd20_baseline ! GDD20 baseline stream filename to read character(len=*), parameter :: sourcefile = & __FILE__ @@ -90,6 +94,7 @@ subroutine cropcal_init(bounds) stream_fldFileName_swindow_start, & stream_fldFileName_swindow_end, & stream_fldFileName_cultivar_gdds, & + stream_fldFileName_gdd20_baseline, & stream_meshfile_cropcal ! Default values for namelist @@ -101,14 +106,17 @@ subroutine cropcal_init(bounds) stream_fldFileName_swindow_start = '' stream_fldFileName_swindow_end = '' stream_fldFileName_cultivar_gdds = '' + stream_fldFileName_gdd20_baseline = '' ! Will need modification to work with mxsowings > 1 ncft = mxpft - npcropmin + 1 ! Ignores generic crops allocate(stream_varnames_sdate(ncft)) allocate(stream_varnames_cultivar_gdds(ncft)) + allocate(stream_varnames_gdd20_baseline(ncft)) do n = 1,ncft ivt = npcropmin + n - 1 write(stream_varnames_sdate(n),'(a,i0)') "sdate1_",ivt write(stream_varnames_cultivar_gdds(n),'(a,i0)') "gdd1_",ivt + write(stream_varnames_gdd20_baseline(n),'(a,i0)') "gdd20bl_",ivt end do ! Read cropcal_streams namelist @@ -132,6 +140,7 @@ subroutine cropcal_init(bounds) call shr_mpi_bcast(stream_fldFileName_swindow_start, mpicom) call shr_mpi_bcast(stream_fldFileName_swindow_end , mpicom) call shr_mpi_bcast(stream_fldFileName_cultivar_gdds, mpicom) + call shr_mpi_bcast(stream_fldFileName_gdd20_baseline, mpicom) call shr_mpi_bcast(stream_meshfile_cropcal , mpicom) if (masterproc) then @@ -144,10 +153,12 @@ subroutine cropcal_init(bounds) write(iulog,'(a,a)' ) ' stream_fldFileName_swindow_start = ',trim(stream_fldFileName_swindow_start) write(iulog,'(a,a)' ) ' stream_fldFileName_swindow_end = ',trim(stream_fldFileName_swindow_end) write(iulog,'(a,a)' ) ' stream_fldFileName_cultivar_gdds = ',trim(stream_fldFileName_cultivar_gdds) + write(iulog,'(a,a)' ) ' stream_fldFileName_gdd20_baseline = ',trim(stream_fldFileName_gdd20_baseline) write(iulog,'(a,a)' ) ' stream_meshfile_cropcal = ',trim(stream_meshfile_cropcal) do n = 1,ncft write(iulog,'(a,a)' ) ' stream_varnames_sdate = ',trim(stream_varnames_sdate(n)) write(iulog,'(a,a)' ) ' stream_varnames_cultivar_gdds = ',trim(stream_varnames_cultivar_gdds(n)) + write(iulog,'(a,a)' ) ' stream_varnames_gdd20_baseline = ',trim(stream_varnames_gdd20_baseline(n)) end do write(iulog,*) endif @@ -155,6 +166,7 @@ subroutine cropcal_init(bounds) ! CLMBuildNamelist checks that both start and end files are provided if either is use_cropcal_rx_swindows = stream_fldFileName_swindow_start /= '' use_cropcal_rx_cultivar_gdds = stream_fldFileName_cultivar_gdds /= '' + adapt_cropcal_rx_cultivar_gdds = stream_fldFileName_gdd20_baseline /= '' use_cropcal_streams = use_cropcal_rx_swindows .or. use_cropcal_rx_cultivar_gdds if (use_cropcal_rx_swindows) then @@ -242,6 +254,36 @@ subroutine cropcal_init(bounds) end if end if + ! Initialize the cdeps data type sdat_cropcal_gdd20_baseline + ! NOTE: stream_dtlimit 1.5 didn't work for some reason + !TODO SSR: Do not allow time axis length > 1 + if (adapt_cropcal_rx_cultivar_gdds) then + call shr_strdata_init_from_inline(sdat_cropcal_gdd20_baseline, & + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock, & + model_mesh = mesh, & + stream_meshfile = trim(stream_meshfile_cropcal), & + stream_lev_dimname = 'null', & + stream_mapalgo = 'bilinear', & + stream_filenames = (/trim(stream_fldFileName_gdd20_baseline)/), & + stream_fldlistFile = stream_varnames_gdd20_baseline, & + stream_fldListModel = stream_varnames_gdd20_baseline, & + stream_yearFirst = 2000, & + stream_yearLast = 2000, & + stream_yearAlign = 2000, & + stream_offset = cropcal_offset, & + stream_taxmode = 'extend', & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = cropcal_tintalgo, & + stream_name = 'GDD20 baseline data', & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if + end subroutine cropcal_init !================================================================ @@ -285,6 +327,8 @@ subroutine cropcal_advance( bounds ) end if end if + ! GDD20 baseline values do not have an associated time axis and thus will not be advanced here + if ( .not. allocated(g_to_ig) )then allocate (g_to_ig(bounds%begg:bounds%endg) ) ig = 0 @@ -298,7 +342,7 @@ end subroutine cropcal_advance !================================================================ - subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) + subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! ! Interpolate data stream information for crop calendars. ! @@ -314,6 +358,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) type(bounds_type) , intent(in) :: bounds integer , intent(in) :: num_pcropp ! number of prog. crop patches in filter integer , intent(in) :: filter_pcropp(:) ! filter for prognostic crop patches + logical , intent(in) :: init ! is this being called as initialization? type(crop_type) , intent(inout) :: crop_inst ! ! !LOCAL VARIABLES: @@ -327,9 +372,11 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) real(r8), pointer :: dataptr1d_swindow_start(:) real(r8), pointer :: dataptr1d_swindow_end (:) real(r8), pointer :: dataptr1d_cultivar_gdds(:) + real(r8), pointer :: dataptr1d_gdd20_baseline(:) real(r8), pointer :: dataptr2d_swindow_start(:,:) real(r8), pointer :: dataptr2d_swindow_end (:,:) real(r8), pointer :: dataptr2d_cultivar_gdds(:,:) + real(r8), pointer :: dataptr2d_gdd20_baseline(:,:) !----------------------------------------------------------------------- associate( & @@ -345,7 +392,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) lsize = bounds%endg - bounds%begg + 1 begp = bounds%begp - endp= bounds%endp + endp = bounds%endp dayspyr = get_curr_days_per_year() @@ -494,6 +541,68 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, crop_inst) deallocate(dataptr2d_cultivar_gdds) + allocate(dataptr2d_gdd20_baseline(lsize, ncft)) + if (adapt_cropcal_rx_cultivar_gdds .and. init) then + ! Read GDD20 baselines from input files + ! Starting with npcropmin will skip generic crops + do n = 1, ncft + call dshr_fldbun_getFldPtr(sdat_cropcal_gdd20_baseline%pstrm(1)%fldbun_model, trim(stream_varnames_gdd20_baseline(n)), & + fldptr1=dataptr1d_gdd20_baseline, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize + ! So an explicit loop is required here + do g = 1,lsize + + ! Ensure valid values + if (dataptr1d_gdd20_baseline(g) < 0 .or. dataptr1d_gdd20_baseline(g) > 1000000._r8) then + write(iulog, *) 'ERROR: invalid read-in gdd20_baseline value: ',dataptr1d_gdd20_baseline(g) + call ESMF_Finalize(endflag=ESMF_END_ABORT) + else if (dataptr1d_gdd20_baseline(g) == 0) then + write(iulog, *) 'ERROR: read-in gdd20_baseline value 0 will cause inf' + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + dataptr2d_gdd20_baseline(g,n) = dataptr1d_gdd20_baseline(g) + end do + end do + + ! Set gdd20_baseline_patch for each gridcell/patch combination + do fp = 1, num_pcropp + p = filter_pcropp(fp) + + ivt = patch%itype(p) + ! Will skip generic crops + if (ivt >= npcropmin) then + n = ivt - npcropmin + 1 + + if (n > ncft) then + write(iulog,'(a,i0,a,i0,a)') 'n (',n,') > ncft (',ncft,')' + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + ! vegetated pft + ig = g_to_ig(patch%gridcell(p)) + + if (ig > lsize) then + write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') > lsize (',lsize,')' + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + crop_inst%gdd20_baseline_patch(p) = dataptr2d_gdd20_baseline(ig,n) + + else + write(iulog,'(a,i0)') 'cropcal_interp(), rx_gdd20_baseline: Crop patch has ivt ',ivt + call ESMF_Finalize(endflag=ESMF_END_ABORT) + endif + end do + end if ! adapt_cropcal_rx_cultivar_gdds + + deallocate(dataptr2d_gdd20_baseline) + + end associate end subroutine cropcal_interp diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90 index c4fa1b53f4..2a3cef4b8b 100644 --- a/src/main/clm_driver.F90 +++ b/src/main/clm_driver.F90 @@ -1079,7 +1079,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! Update crop calendars ! ============================================================================ call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, & - filter_inactive_and_active(nc)%pcropp, crop_inst) + filter_inactive_and_active(nc)%pcropp, .false., crop_inst) end if ! ============================================================================ diff --git a/src/main/clm_initializeMod.F90 b/src/main/clm_initializeMod.F90 index 9bf0cc59a2..e8f70bdef8 100644 --- a/src/main/clm_initializeMod.F90 +++ b/src/main/clm_initializeMod.F90 @@ -667,7 +667,7 @@ subroutine initialize2(ni,nj) do nc = 1,nclumps call get_clump_bounds(nc, bounds_clump) call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, & - filter_inactive_and_active(nc)%pcropp, crop_inst) + filter_inactive_and_active(nc)%pcropp, .true., crop_inst) end do !$OMP END PARALLEL DO end if diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index 7d0b2b55ad..fe67380fd2 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -391,6 +391,7 @@ module clm_varctl logical, public :: use_cropcal_streams = .false. logical, public :: use_cropcal_rx_swindows = .false. logical, public :: use_cropcal_rx_cultivar_gdds = .false. + logical, public :: adapt_cropcal_rx_cultivar_gdds = .false. !---------------------------------------------------------- ! biomass heat storage switch From b73bcfdd104d8d591afb41ef6d6c18398cf8ef69 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 23 May 2024 15:09:16 -0600 Subject: [PATCH 008/206] =?UTF-8?q?Don't=20adapt=20rx=20cultivar=20gdd=20r?= =?UTF-8?q?eqts=20if=20baseline=20gdd20=20is=20=E2=89=A40.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/biogeochem/CNPhenologyMod.F90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index f1f2d5fcc0..cfeee0b867 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -143,6 +143,9 @@ module CNPhenologyMod logical, public :: generate_crop_gdds = .false. ! If true, harvest the day before next sowing logical, public :: use_mxmat = .true. ! If true, ignore crop maximum growing season length + ! For use with adapt_cropcal_rx_cultivar_gdds .true. + real(r8), parameter :: min_gdd20_baseline = 0._r8 ! If gdd20_baseline_patch is ≤ this, do not consider baseline. + ! Constants for seasonal decidious leaf onset and offset logical, private :: onset_thresh_depends_on_veg = .false. ! If onset threshold depends on vegetation type integer, public, parameter :: critical_daylight_constant = 1 @@ -2694,9 +2697,9 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & if (use_cropcal_rx_cultivar_gdds .and. crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) .ge. 0._r8) then gddmaturity(p) = crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) did_rx_gdds = .true. - if (adapt_cropcal_rx_cultivar_gdds) then + if (adapt_cropcal_rx_cultivar_gdds .and. crop_inst%gdd20_baseline_patch(p) > min_gdd20_baseline) then gddmaturity(p) = gddmaturity(p) * gdd20 / crop_inst%gdd20_baseline_patch(p) - !TODO SSR: Set maximum gddmaturity + !TODO SSR: Set maximum and minimum gddmaturity end if else if (ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat) then gddmaturity(p) = hybgdd(ivt(p)) From 20f32c1d70d709a96416c70a1d143d142b52fecf Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 24 May 2024 12:45:18 -0600 Subject: [PATCH 009/206] Ignore 'invalid' values in stream_fldFileName_gdd20_baseline on read. They're being handled in PlantCrop() instead. (cherry picked from commit 045b3d72c54319769e50500b2bd445215e07446d) --- src/cpl/share_esmf/cropcalStreamMod.F90 | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 83f7fc0eb0..f60ab51000 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -555,16 +555,6 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here do g = 1,lsize - - ! Ensure valid values - if (dataptr1d_gdd20_baseline(g) < 0 .or. dataptr1d_gdd20_baseline(g) > 1000000._r8) then - write(iulog, *) 'ERROR: invalid read-in gdd20_baseline value: ',dataptr1d_gdd20_baseline(g) - call ESMF_Finalize(endflag=ESMF_END_ABORT) - else if (dataptr1d_gdd20_baseline(g) == 0) then - write(iulog, *) 'ERROR: read-in gdd20_baseline value 0 will cause inf' - call ESMF_Finalize(endflag=ESMF_END_ABORT) - end if - dataptr2d_gdd20_baseline(g,n) = dataptr1d_gdd20_baseline(g) end do end do From a7cd0573ddfdef805f97c1f9aba3083492515ca3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 24 May 2024 14:05:36 -0600 Subject: [PATCH 010/206] Add generate_gdd20_baseline tool. (cherry picked from commit 3ae50b7402fb1e3580f11ba09883e9ddaa563fda) --- .../crop_calendars/generate_gdd20_baseline.py | 243 ++++++++++++++++++ tools/crop_calendars/generate_gdd20_baseline | 19 ++ 2 files changed, 262 insertions(+) create mode 100644 python/ctsm/crop_calendars/generate_gdd20_baseline.py create mode 100755 tools/crop_calendars/generate_gdd20_baseline diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py new file mode 100644 index 0000000000..d5cbd779e0 --- /dev/null +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -0,0 +1,243 @@ +""" +Generate stream_fldFileName_gdd20_baseline file from CTSM outputs +""" + +import sys +import argparse +import os +import datetime as dt +import numpy as np +import xarray as xr +import cftime + +# -- add python/ctsm to path (needed if we want to run generate_gdd20_baseline stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) + +# pylint: disable=wrong-import-position +from ctsm.crop_calendars.import_ds import import_ds +import ctsm.crop_calendars.cropcal_utils as utils + +VAR_LIST_IN = ["GDD0", "GDD8", "GDD10"] +VAR_LIST_IN = [x + "20" for x in VAR_LIST_IN] # TODO: Delete this once using the right variables +MISSING_FILL = -1 # Something negative to ensure that gddmaturity never changes (see PlantCrop) +STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of + # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline + + +def _parse_args(): + """ + Set up and parse input arguments + """ + parser = argparse.ArgumentParser( + description=( + "Given a list of CTSM history files, generate stream_fldFileName_gdd20_baseline input" + + "file from the GDD0, GDD8, and GDD10 variables." + ) + ) + + # Required + parser.add_argument( + "-i", + "--input-files", + help="Space-separated string of CTSM history files", + type=str, + required=True, + ) + parser.add_argument( + "-o", + "--output-file", + help="Path to which output file should be saved", + type=str, + required=True, + ) + parser.add_argument( + "-a", + "--author", + help=( + "String to be saved in author attribute of output files." + + "E.g., 'Author Name (authorname@ucar.edu)'" + ), + type=str, + required=True, + ) + + # Optional + parser.add_argument( + "--overwrite", + help="Overwrite existing output file, if any", + action="store_true", + ) + + # Get arguments + args = parser.parse_args(sys.argv[1:]) + + # Check arguments + if os.path.exists(args.output_file) and not args.overwrite: + raise FileExistsError("Output file exists but --overwrite is not specified") + + return args + + +def _get_cft_list(crop_list): + """ + Given a list of strings, return a list of CFT names that contain any of those strings. + Will include both irrigated and rainfed! + + Args: + crop_list (list): List of crops to look for. + E.g.: ["corn", "cotton"] + + Returns: + cft_str_list: List of CFTs containing any of the crop names in crop_list. + E.g.: ["tropical_corn", "irrigated_tropical_corn", + "temperate_corn", "irrigated_temperate_corn", + "cotton", "irrigated_cotton"] + """ + + mgdcrop_list = utils.define_mgdcrop_list() + cft_str_list = [] + for crop_str in crop_list: + cft_str_list += [x for x in mgdcrop_list if crop_str in x] + return cft_str_list + + +def _get_gddn_for_cft(cft_str): + """ + Given a CFT name, return the GDDN variable it uses. + + Args: + cft_str (str): E.g., "irrigated_temperate_corn" + + Returns: + str or None: Name of variable to use (e.g., "GDD8"). If crop isn't yet handled, return None. + """ + + gddn = None + + gdd0_list_str = ["wheat", "cotton", "rice"] + if cft_str in _get_cft_list(gdd0_list_str): + gddn = "GDD0" + + gdd8_list_str = ["corn", "sugarcane", "miscanthus", "switchgrass"] + if cft_str in _get_cft_list(gdd8_list_str): + gddn = "GDD8" + + gdd10_list_str = ["soybean"] + if cft_str in _get_cft_list(gdd10_list_str): + gddn = "GDD10" + + # TODO: Delete this once using the right variables + if gddn is not None: + gddn += "20" + + return gddn + + +def _get_output_varname(cft_str): + cft_int = utils.vegtype_str2int(cft_str)[0] + return f"gdd20bl_{cft_int}" + + +def _add_time_axis(da_in): + """ + Adds a size-1 time axis to a DataArray. Needed because CDEPS streams code requires a time axis, + even if the data in question is not supposed to vary over time. + + Args: + da_in (DataArray): xarray DataArray which needs a time axis added + + Returns: + DataArray: da_in with a new 1-step time axis + """ + this_date = np.array(cftime.DatetimeNoLeap(STREAM_YEAR, 1, 1, 0, 0, 0, 0, has_year_zero=True)) + this_date = np.expand_dims(this_date, axis=0) + da_time = xr.DataArray( + data=this_date, + dims={"time": this_date}, + ) + da_out = da_in.expand_dims(time=da_time) + return da_out + + +def generate_gdd20_baseline(input_files, output_file, author): + """ + Generate stream_fldFileName_gdd20_baseline file from CTSM outputs + """ + + # Get input file list + input_files = input_files.split(sep=" ") + # Get unique values and sort + input_files = list(set(input_files)) + input_files.sort() + + # Import history files and ensure they have lat/lon dims + ds_in = import_ds(input_files, VAR_LIST_IN) + if not all(x in ds_in.dims for x in ["lat", "lon"]): + raise RuntimeError("Input files must have lat and lon dimensions") + + # If needed, find mean over time + if "time" in ds_in.dims: + ds_in = ds_in.mean(dim="time", skipna=True) + + # Set up a dummy DataArray to use for crops without an assigned GDDN variable + dummy_da = xr.DataArray( + data=MISSING_FILL * np.ones_like(ds_in[VAR_LIST_IN[0]].values), + dims=ds_in[VAR_LIST_IN[0]].dims, + coords=ds_in[VAR_LIST_IN[0]].coords, + ) + dummy_da = _add_time_axis(dummy_da) + + # Process all crops + ds_out = xr.Dataset( + data_vars=None, + attrs={ + "author": author, + "created": dt.datetime.now().astimezone().isoformat(), + }, + ) + for cft_str in utils.define_mgdcrop_list(): + cft_int = utils.vegtype_str2int(cft_str)[0] + print(f"{cft_str} ({cft_int})") + + # Which GDDN history variable does this crop use? E.g., GDD0, GDD10 + gddn = _get_gddn_for_cft(cft_str) + + # Fill any missing values with MISSING_FILL. This will mean that gddmaturity in these cells + # never changes. + if gddn is None: + # Crop not handled yet? Fill it entirely with missing value + this_da = dummy_da + long_name = "Dummy GDD20" + print(" dummy GDD20") + else: + this_da = ds_in[gddn].fillna(MISSING_FILL) + this_da = _add_time_axis(this_da) + long_name = gddn + print(f" {gddn}") + + # Add attributes + this_da.attrs["long_name"] = long_name + f" baseline for {cft_str}" + this_da.attrs["units"] = "°C days" + + # Copy that to ds_out + var_out = _get_output_varname(cft_str) + print(f" Output variable {var_out}") + ds_out[var_out] = this_da + + # Save + ds_out.to_netcdf(output_file) + + print("Done!") + + +def main(): + """ + main() function for calling generate_gdd20_baseline.py from command line. + """ + args = _parse_args() + generate_gdd20_baseline( + args.input_files, + args.output_file, + args.author, + ) diff --git a/tools/crop_calendars/generate_gdd20_baseline b/tools/crop_calendars/generate_gdd20_baseline new file mode 100755 index 0000000000..a0238c8d0f --- /dev/null +++ b/tools/crop_calendars/generate_gdd20_baseline @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +""" +For description and instructions, please see README. +""" + +import os +import sys + +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), + os.pardir, + os.pardir, + 'python') +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm.crop_calendars.generate_gdd20_baseline import main + +if __name__ == "__main__": + main() + From fd5f177131d63d39e79a13918390bdfb642d781e Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 28 May 2024 11:27:09 -0600 Subject: [PATCH 011/206] Format generate_gdd20_baseline.py with black. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index d5cbd779e0..d28bfda0e7 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -22,7 +22,7 @@ VAR_LIST_IN = [x + "20" for x in VAR_LIST_IN] # TODO: Delete this once using the right variables MISSING_FILL = -1 # Something negative to ensure that gddmaturity never changes (see PlantCrop) STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of - # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline +# shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline def _parse_args(): From 5eafae73aeeaec3950123a955d9c781d3840e157 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 28 May 2024 11:28:14 -0600 Subject: [PATCH 012/206] Add previous commit to .git-blame-ignore-revs. --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index e769d3187c..2620839223 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -26,6 +26,7 @@ d866510188d26d51bcd6d37239283db690af7e82 0dcd0a3c1abcaffe5529f8d79a6bc34734b195c7 e096358c832ab292ddfd22dd5878826c7c788968 475831f0fb0e31e97f630eac4e078c886558b61c +fd5f177131d63d39e79a13918390bdfb642d781e # Ran SystemTests and python/ctsm through black python formatter 5364ad66eaceb55dde2d3d598fe4ce37ac83a93c 8056ae649c1b37f5e10aaaac79005d6e3a8b2380 From 38d6888fd04cef4f2d507df99e9c4d1a3a16c4ac Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 07:50:21 -0600 Subject: [PATCH 013/206] Add testmods RxCropCals, RxCropCalsAdapt. --- .../testmods_dirs/clm/RxCropCals/include_user_mods | 1 + .../testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm | 8 ++++++++ .../testmods_dirs/clm/RxCropCalsAdapt/include_user_mods | 1 + .../testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm | 3 +++ 4 files changed, 13 insertions(+) create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCals/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/include_user_mods new file mode 100644 index 0000000000..23ea3745e6 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/include_user_mods @@ -0,0 +1 @@ +../crop diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm new file mode 100644 index 0000000000..9cbfea97f0 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm @@ -0,0 +1,8 @@ + +! Sowing windows +stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' +stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' +stream_fldFileName_cultivar_gdds = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc' +stream_year_first_cropcal = 2000 +stream_year_last_cropcal = 2000 +stream_meshfile_cropcal = '$DIN_LOC_ROOT/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc' diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods new file mode 100644 index 0000000000..88c9694365 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods @@ -0,0 +1 @@ +../RxCropCals diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm new file mode 100644 index 0000000000..1bba9d2a89 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm @@ -0,0 +1,3 @@ + +! Eventually replace this with half-degree version in the proper place +stream_fldFileName_gdd20_baseline = '/glade/u/home/samrabin/ctsm_scale-mat-reqs/tools/crop_calendars/test.nc' From 317f77d7b692a58644e679d48c8f4ec026c8a697 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 11:30:54 -0600 Subject: [PATCH 014/206] Add cropcals_rx namelist boolean. --- bld/namelist_files/namelist_defaults_ctsm.xml | 15 +++++++++++---- bld/namelist_files/namelist_definition_ctsm.xml | 9 +++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 655e97c47c..08f67a2712 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1686,10 +1686,17 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c nn nn - -1850 -2100 -1850 + +.false. +2000 +2000 +2000 + + +lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc +lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc +lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc +share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index c88e987756..366b7498e8 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1773,14 +1773,19 @@ Mapping method from LAI input file to the model resolution + +Flag to enable prescribed crop calendars (sowing window dates and maturity requirement) + + -First year to loop over for crop calendar data +First year to loop over for crop calendar data (not including gdd20_baseline file) -Last year to loop over for crop calendar data +Last year to loop over for crop calendar data (not including gdd20_baseline file) Date: Wed, 29 May 2024 11:38:29 -0600 Subject: [PATCH 015/206] CLMBuildNamelist: Fix logic and message for check of sowing window start/end files. --- bld/CLMBuildNamelist.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 54752b6dbc..9753549db1 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4200,8 +4200,8 @@ sub setup_logic_cropcal_streams { my $gdd_file = $nl->get_value('stream_fldFileName_cultivar_gdds') ; my $gdd20_baseline_file = $nl->get_value('stream_fldFileName_gdd20_baseline') ; my $mesh_file = $nl->get_value('stream_meshfile_cropcal') ; - if ( ($swindow_start_file eq '' and $swindow_start_file ne '') or ($swindow_start_file ne '' and $swindow_start_file eq '') ) { - $log->fatal_error("When specifying sowing window dates, you must provide both swindow_start_file and swindow_end_file. To specify exact sowing dates, use the same file." ); + if ( ($swindow_start_file eq '' and $swindow_end_file ne '') or ($swindow_start_file ne '' and $swindow_end_file eq '') ) { + $log->fatal_error("When specifying sowing window dates, you must provide both stream_fldFileName_swindow_start and stream_fldFileName_swindow_end. To specify exact sowing dates, use the same file." ); } if ( $gdd_file eq '' and $gdd20_baseline_file ne '' ) { $log->fatal_error("If not providing stream_fldFileName_cultivar_gdds, don't provide stream_fldFileName_gdd20_baseline"); From b19573466744c3426de70a1edbb9a3725821de67 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 11:42:11 -0600 Subject: [PATCH 016/206] CLMBuildNamelist: Use &string_is_undef_or_empty() instead of comparing to ''. --- bld/CLMBuildNamelist.pm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 9753549db1..833a254f1a 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4200,30 +4200,30 @@ sub setup_logic_cropcal_streams { my $gdd_file = $nl->get_value('stream_fldFileName_cultivar_gdds') ; my $gdd20_baseline_file = $nl->get_value('stream_fldFileName_gdd20_baseline') ; my $mesh_file = $nl->get_value('stream_meshfile_cropcal') ; - if ( ($swindow_start_file eq '' and $swindow_end_file ne '') or ($swindow_start_file ne '' and $swindow_end_file eq '') ) { + if ( (&string_is_undef_or_empty($swindow_start_file) and !&string_is_undef_or_empty($swindow_end_file)) or (!&string_is_undef_or_empty($swindow_start_file) and &string_is_undef_or_empty($swindow_end_file)) ) { $log->fatal_error("When specifying sowing window dates, you must provide both stream_fldFileName_swindow_start and stream_fldFileName_swindow_end. To specify exact sowing dates, use the same file." ); } - if ( $gdd_file eq '' and $gdd20_baseline_file ne '' ) { + if ( &string_is_undef_or_empty($gdd_file) and ! &string_is_undef_or_empty($gdd20_baseline_file) ) { $log->fatal_error("If not providing stream_fldFileName_cultivar_gdds, don't provide stream_fldFileName_gdd20_baseline"); } if ( $generate_crop_gdds eq '.true.' ) { if ( $use_mxmat eq '.true.' ) { $log->fatal_error("If generate_crop_gdds is true, you must also set use_mxmat to false" ); } - if ( $swindow_start_file eq '' or $swindow_end_file eq '' ) { + if ( &string_is_undef_or_empty($swindow_start_file) or &string_is_undef_or_empty($swindow_end_file) ) { $log->fatal_error("If generate_crop_gdds is true, you must specify stream_fldFileName_swindow_start and stream_fldFileName_swindow_end") } if ( $swindow_start_file ne $swindow_end_file ) { $log->fatal_error("If generate_crop_gdds is true, you must specify exact sowing dates by setting stream_fldFileName_swindow_start and stream_fldFileName_swindow_end to the same file") } - if ( $gdd_file ne '' ) { + if ( ! &string_is_undef_or_empty($gdd_file) ) { $log->fatal_error("If generate_crop_gdds is true, do not specify stream_fldFileName_cultivar_gdds") } - if ( $gdd20_baseline_file ne '' ) { + if ( ! &string_is_undef_or_empty($gdd20_baseline_file) ) { $log->fatal_error("If generate_crop_gdds is true, do not specify stream_fldFileName_gdd20_baseline") } } - if ( $mesh_file eq '' and ( $swindow_start_file ne '' or $gdd_file ne '' ) ) { + if ( &string_is_undef_or_empty($mesh_file) and ( ! &string_is_undef_or_empty($swindow_start_file) or ! &string_is_undef_or_empty($gdd_file) ) ) { $log->fatal_error("If prescribing crop sowing dates and/or maturity requirements, you must specify stream_meshfile_cropcal") } } From 4b18a5bb159e1a98a5905ec939dffb5981e55185 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 11:48:50 -0600 Subject: [PATCH 017/206] CLMBuildNamelist: Use &value_is_true() instead of comparing to '.true.' --- bld/CLMBuildNamelist.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 833a254f1a..d35649bef4 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4206,8 +4206,8 @@ sub setup_logic_cropcal_streams { if ( &string_is_undef_or_empty($gdd_file) and ! &string_is_undef_or_empty($gdd20_baseline_file) ) { $log->fatal_error("If not providing stream_fldFileName_cultivar_gdds, don't provide stream_fldFileName_gdd20_baseline"); } - if ( $generate_crop_gdds eq '.true.' ) { - if ( $use_mxmat eq '.true.' ) { + if ( &value_is_true($generate_crop_gdds) ) { + if ( &value_is_true($use_mxmat) ) { $log->fatal_error("If generate_crop_gdds is true, you must also set use_mxmat to false" ); } if ( &string_is_undef_or_empty($swindow_start_file) or &string_is_undef_or_empty($swindow_end_file) ) { From e76fbbc134ab5c4c6186f35732d11b46879a5a13 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 12:02:17 -0600 Subject: [PATCH 018/206] CLMBuildNamelist: Update setup_logic_cropcal_streams() with cropcals_rx. --- bld/CLMBuildNamelist.pm | 77 ++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index d35649bef4..aff46be3f0 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4172,41 +4172,73 @@ sub setup_logic_lai_streams { sub setup_logic_cropcal_streams { my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; - # Set first and last stream years - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal', - 'sim_year'=>$nl_flags->{'sim_year'}, - 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal', - 'sim_year'=>$nl_flags->{'sim_year'}, - 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - - # Set align year, if first and last years are different - if ( $nl->get_value('stream_year_first_cropcal') != - $nl->get_value('stream_year_last_cropcal') ) { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, - 'model_year_align_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, - 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - } - # Set up other crop calendar parameters + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'cropcals_rx'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'generate_crop_gdds'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_mxmat'); - # Option checks - my $generate_crop_gdds = $nl->get_value('generate_crop_gdds') ; - my $use_mxmat = $nl->get_value('use_mxmat') ; + # Add defaults if using prescribed crop calendars + my $cropcals_rx = $nl->get_value('cropcals_rx') ; + if ( &value_is_true($cropcals_rx) ) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_swindow_start'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_swindow_end'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_cultivar_gdds'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_meshfile_cropcal'); + } + + # Add defaults if using any potentially time-varying crop calendar input files my $swindow_start_file = $nl->get_value('stream_fldFileName_swindow_start') ; my $swindow_end_file = $nl->get_value('stream_fldFileName_swindow_end') ; my $gdd_file = $nl->get_value('stream_fldFileName_cultivar_gdds') ; my $gdd20_baseline_file = $nl->get_value('stream_fldFileName_gdd20_baseline') ; my $mesh_file = $nl->get_value('stream_meshfile_cropcal') ; - if ( (&string_is_undef_or_empty($swindow_start_file) and !&string_is_undef_or_empty($swindow_end_file)) or (!&string_is_undef_or_empty($swindow_start_file) and &string_is_undef_or_empty($swindow_end_file)) ) { - $log->fatal_error("When specifying sowing window dates, you must provide both stream_fldFileName_swindow_start and stream_fldFileName_swindow_end. To specify exact sowing dates, use the same file." ); + if ( !&string_is_undef_or_empty($swindow_start_file) or !&string_is_undef_or_empty($swindow_end_file) or !&string_is_undef_or_empty($gdd_file) or !&string_is_undef_or_empty($gdd20_baseline_file) or !&string_is_undef_or_empty($mesh_file)) { + + # User gave an input file without specifying cropcals_rx = .true. + # Changing this means nothing to the code, but helps namelist make more sense + if ( ! &value_is_true($cropcals_rx) ){ + $log->fatal_error("If providing any crop calendar input file(s), cropcals_rx must be true" ); + } + + # User provided an input file but set mesh file to empty + if ( &string_is_undef_or_empty($mesh_file) ) { + $log->fatal_error("If providing any crop calendar input file(s), you must provide stream_meshfile_cropcal" ); + } + + # Set first and last stream years + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + + # Set align year, if first and last years are different + if ( $nl->get_value('stream_year_first_cropcal') != + $nl->get_value('stream_year_last_cropcal') ) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, + 'model_year_align_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + } } + + # If running with prescribed crop calendars, certain files must be provided + my $generate_crop_gdds = $nl->get_value('generate_crop_gdds') ; + if ( &value_is_true($cropcals_rx) ) { + if ( &string_is_undef_or_empty($swindow_start_file) or &string_is_undef_or_empty($swindow_end_file) ) { + $log->fatal_error("If cropcals_rx is true, sowing window start and end files must be provided. To specify exact sowing dates, use the same file." ); + } + if ( &string_is_undef_or_empty($gdd_file) and (! &value_is_true($generate_crop_gdds)) ){ + $log->fatal_error("If cropcals_rx is true and generate_crop_gdds is false, maturity requirement file stream_fldFileName_cultivar_gdds must be provided" ); + } + } + + # Option checks if ( &string_is_undef_or_empty($gdd_file) and ! &string_is_undef_or_empty($gdd20_baseline_file) ) { $log->fatal_error("If not providing stream_fldFileName_cultivar_gdds, don't provide stream_fldFileName_gdd20_baseline"); } if ( &value_is_true($generate_crop_gdds) ) { + my $use_mxmat = $nl->get_value('use_mxmat') ; if ( &value_is_true($use_mxmat) ) { $log->fatal_error("If generate_crop_gdds is true, you must also set use_mxmat to false" ); } @@ -4223,9 +4255,6 @@ sub setup_logic_cropcal_streams { $log->fatal_error("If generate_crop_gdds is true, do not specify stream_fldFileName_gdd20_baseline") } } - if ( &string_is_undef_or_empty($mesh_file) and ( ! &string_is_undef_or_empty($swindow_start_file) or ! &string_is_undef_or_empty($gdd_file) ) ) { - $log->fatal_error("If prescribing crop sowing dates and/or maturity requirements, you must specify stream_meshfile_cropcal") - } } #------------------------------------------------------------------------------- From 0f729d8edcec2fa97db471bb766cc8964b01ed81 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 14:16:45 -0600 Subject: [PATCH 019/206] Bugfix: cropcalStreamMod now handles cropcals_rx. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index f60ab51000..101ad9f2a4 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -47,6 +47,7 @@ module cropcalStreamMod character(len=CL) :: stream_fldFileName_swindow_end ! sowing window end stream filename to read character(len=CL) :: stream_fldFileName_cultivar_gdds ! cultivar growing degree-days stream filename to read character(len=CL) :: stream_fldFileName_gdd20_baseline ! GDD20 baseline stream filename to read + logical :: cropcals_rx ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash character(len=*), parameter :: sourcefile = & __FILE__ @@ -95,7 +96,8 @@ subroutine cropcal_init(bounds) stream_fldFileName_swindow_end, & stream_fldFileName_cultivar_gdds, & stream_fldFileName_gdd20_baseline, & - stream_meshfile_cropcal + stream_meshfile_cropcal, & + cropcals_rx ! Default values for namelist stream_year_first_cropcal = 1 ! first year in stream to use From 74411ea31189969410205a3edba96c14670a38f4 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 14:17:05 -0600 Subject: [PATCH 020/206] Update RxCropCals testmod to use cropcals_rx true. --- .../testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm index 9cbfea97f0..8a0b4a91be 100644 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm @@ -1,8 +1,2 @@ -! Sowing windows -stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' -stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' -stream_fldFileName_cultivar_gdds = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc' -stream_year_first_cropcal = 2000 -stream_year_last_cropcal = 2000 -stream_meshfile_cropcal = '$DIN_LOC_ROOT/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc' +cropcals_rx = .true. From 9d1f88d15ac942cb7176b30bc48163ab6e5031fc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 15:30:41 -0600 Subject: [PATCH 021/206] Add RxCropCals and RxCropCalsAdapt tests to new suite crop_calendars. --- cime_config/testdefs/testlist_clm.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 9cfba6f5b3..bf763c4775 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3719,5 +3719,23 @@ + + + + + + + + + + + + + + + + + + From 7e5257a94efa920f7b608a97b041fef63b1d9aa7 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 29 May 2024 16:12:06 -0600 Subject: [PATCH 022/206] Add cropcals_rx_adapt option. --- bld/CLMBuildNamelist.pm | 50 +++++++++++++++---- bld/namelist_files/namelist_defaults_ctsm.xml | 6 +++ .../namelist_definition_ctsm.xml | 5 ++ .../clm/RxCropCalsAdapt/user_nl_clm | 4 +- src/cpl/share_esmf/cropcalStreamMod.F90 | 4 +- 5 files changed, 55 insertions(+), 14 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index aff46be3f0..fc80379201 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4174,19 +4174,29 @@ sub setup_logic_cropcal_streams { # Set up other crop calendar parameters add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'cropcals_rx'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'cropcals_rx_adapt'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'generate_crop_gdds'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_mxmat'); - # Add defaults if using prescribed crop calendars + # These can't both be true my $cropcals_rx = $nl->get_value('cropcals_rx') ; - if ( &value_is_true($cropcals_rx) ) { + my $cropcals_rx_adapt = $nl->get_value('cropcals_rx_adapt') ; + if (&value_is_true($cropcals_rx) and &value_is_true($cropcals_rx_adapt)) { + $log->fatal_error("cropcals_rx and cropcals_rx_adapt may not both be true" ); + } + + # Add defaults if using prescribed crop calendars + if ( &value_is_true($cropcals_rx) or &value_is_true($cropcals_rx_adapt) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_swindow_start'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_swindow_end'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_cultivar_gdds'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_meshfile_cropcal'); + if ( &value_is_true($cropcals_rx_adapt) ) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_baseline'); + } } - # Add defaults if using any potentially time-varying crop calendar input files + # Add defaults if using any crop calendar input files my $swindow_start_file = $nl->get_value('stream_fldFileName_swindow_start') ; my $swindow_end_file = $nl->get_value('stream_fldFileName_swindow_end') ; my $gdd_file = $nl->get_value('stream_fldFileName_cultivar_gdds') ; @@ -4194,10 +4204,20 @@ sub setup_logic_cropcal_streams { my $mesh_file = $nl->get_value('stream_meshfile_cropcal') ; if ( !&string_is_undef_or_empty($swindow_start_file) or !&string_is_undef_or_empty($swindow_end_file) or !&string_is_undef_or_empty($gdd_file) or !&string_is_undef_or_empty($gdd20_baseline_file) or !&string_is_undef_or_empty($mesh_file)) { - # User gave an input file without specifying cropcals_rx = .true. - # Changing this means nothing to the code, but helps namelist make more sense - if ( ! &value_is_true($cropcals_rx) ){ - $log->fatal_error("If providing any crop calendar input file(s), cropcals_rx must be true" ); + # User gave an input file without specifying cropcals_rx or cropcals_rx_adapt = .true. + # Requiring this means nothing to the code, but helps namelist make more sense + if ( !&value_is_true($cropcals_rx) and !&value_is_true($cropcals_rx_adapt) ){ + $log->fatal_error("If providing any crop calendar input file(s), cropcals_rx or cropcals_rx_adapt must be true" ); + } + + # User set cropcals_rx_adapt to true but set stream_fldFileName_gdd20_baseline to empty + if ( &value_is_true($cropcals_rx_adapt) and &string_is_undef_or_empty($gdd20_baseline_file) ) { + $log->fatal_error("If cropcals_rx_adapt is true, stream_fldFileName_gdd20_baseline must be provided" ); + } + + # cropcals_rx_adapt is false but user provided stream_fldFileName_gdd20_baseline + if ( !&value_is_true($cropcals_rx_adapt) and !&string_is_undef_or_empty($gdd20_baseline_file) ) { + $log->fatal_error("If stream_fldFileName_gdd20_baseline provided, cropcals_rx_adapt must be true" ); } # User provided an input file but set mesh file to empty @@ -4213,9 +4233,17 @@ sub setup_logic_cropcal_streams { 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - # Set align year, if first and last years are different + # Check/set things if first and last years are different if ( $nl->get_value('stream_year_first_cropcal') != $nl->get_value('stream_year_last_cropcal') ) { + + # Do not allow maturity requirements to change over time if stream_fldFileName_gdd20_baseline is provided. That would be nonsensical. + # Note that this restricts sowing windows from changing over time as well, because there are not separate stream_year settings for that. + if ( !&string_is_undef_or_empty($gdd20_baseline_file) ) { + $log->fatal_error("If cropcals_rx_adapt is true (i.e., stream_fldFileName_gdd20_baseline is provided), no crop calendar input is allowed to vary over time (i.e., stream_year_first_cropcal and stream_year_last_cropcal must be the same)." ); + } + + # Set align year add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'model_year_align_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); @@ -4224,12 +4252,12 @@ sub setup_logic_cropcal_streams { # If running with prescribed crop calendars, certain files must be provided my $generate_crop_gdds = $nl->get_value('generate_crop_gdds') ; - if ( &value_is_true($cropcals_rx) ) { + if ( &value_is_true($cropcals_rx) or &value_is_true($cropcals_rx_adapt) ) { if ( &string_is_undef_or_empty($swindow_start_file) or &string_is_undef_or_empty($swindow_end_file) ) { - $log->fatal_error("If cropcals_rx is true, sowing window start and end files must be provided. To specify exact sowing dates, use the same file." ); + $log->fatal_error("If cropcals_rx or cropcals_rx_adapt is true, sowing window start and end files must be provided. To specify exact sowing dates, use the same file." ); } if ( &string_is_undef_or_empty($gdd_file) and (! &value_is_true($generate_crop_gdds)) ){ - $log->fatal_error("If cropcals_rx is true and generate_crop_gdds is false, maturity requirement file stream_fldFileName_cultivar_gdds must be provided" ); + $log->fatal_error("If cropcals_rx or cropcals_rx_adapt is true and generate_crop_gdds is false, maturity requirement file stream_fldFileName_cultivar_gdds must be provided" ); } } diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 08f67a2712..85f37a22fd 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1688,6 +1688,7 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c .false. +.false. 2000 2000 2000 @@ -1697,6 +1698,11 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc +lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc +lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc +lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc +lnd/clm2/testdata/gdd20baseline.tmp_dontupload.nc +share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 366b7498e8..f37ada6847 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1778,6 +1778,11 @@ Mapping method from LAI input file to the model resolution Flag to enable prescribed crop calendars (sowing window dates and maturity requirement) + +Flag to enable prescribed crop calendars (sowing window dates and maturity requirement), with maturity requirement adaptive based on recent climate + + First year to loop over for crop calendar data (not including gdd20_baseline file) diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm index 1bba9d2a89..709c7221e0 100644 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm @@ -1,3 +1,3 @@ -! Eventually replace this with half-degree version in the proper place -stream_fldFileName_gdd20_baseline = '/glade/u/home/samrabin/ctsm_scale-mat-reqs/tools/crop_calendars/test.nc' +cropcals_rx = .false. +cropcals_rx_adapt = .true. diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 101ad9f2a4..3333b6cfdc 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -48,6 +48,7 @@ module cropcalStreamMod character(len=CL) :: stream_fldFileName_cultivar_gdds ! cultivar growing degree-days stream filename to read character(len=CL) :: stream_fldFileName_gdd20_baseline ! GDD20 baseline stream filename to read logical :: cropcals_rx ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash + logical :: cropcals_rx_adapt ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash character(len=*), parameter :: sourcefile = & __FILE__ @@ -97,7 +98,8 @@ subroutine cropcal_init(bounds) stream_fldFileName_cultivar_gdds, & stream_fldFileName_gdd20_baseline, & stream_meshfile_cropcal, & - cropcals_rx + cropcals_rx, & + cropcals_rx_adapt ! Default values for namelist stream_year_first_cropcal = 1 ! first year in stream to use From 5911cea434581d33f1a4892699f029c32132e63e Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 31 May 2024 13:13:02 -0600 Subject: [PATCH 023/206] Move add_default() of model_year_align_cropcal to be with other year params. --- bld/CLMBuildNamelist.pm | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index fc80379201..bbe83a3dc5 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4225,13 +4225,16 @@ sub setup_logic_cropcal_streams { $log->fatal_error("If providing any crop calendar input file(s), you must provide stream_meshfile_cropcal" ); } - # Set first and last stream years + # Set stream years add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'model_year_align_cropcal', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); # Check/set things if first and last years are different if ( $nl->get_value('stream_year_first_cropcal') != @@ -4242,11 +4245,6 @@ sub setup_logic_cropcal_streams { if ( !&string_is_undef_or_empty($gdd20_baseline_file) ) { $log->fatal_error("If cropcals_rx_adapt is true (i.e., stream_fldFileName_gdd20_baseline is provided), no crop calendar input is allowed to vary over time (i.e., stream_year_first_cropcal and stream_year_last_cropcal must be the same)." ); } - - # Set align year - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, - 'model_year_align_cropcal', 'sim_year'=>$nl_flags->{'sim_year'}, - 'sim_year_range'=>$nl_flags->{'sim_year_range'}); } } From d89cb9d4bc3cd8ab5761d23cf398b9b682a047fa Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 31 May 2024 13:38:31 -0600 Subject: [PATCH 024/206] Split streams year params for swindows vs maturity reqts. --- bld/CLMBuildNamelist.pm | 29 +++++---- bld/namelist_files/namelist_defaults_ctsm.xml | 9 ++- .../namelist_definition_ctsm.xml | 27 ++++++-- cime_config/SystemTests/rxcropmaturity.py | 6 +- .../clm/sowingWindows/user_nl_clm | 4 +- .../Running-with-custom-crop-calendars.rst | 10 +-- src/cpl/share_esmf/cropcalStreamMod.F90 | 63 ++++++++++++------- 7 files changed, 93 insertions(+), 55 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index bbe83a3dc5..26725b7d96 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4226,25 +4226,30 @@ sub setup_logic_cropcal_streams { } # Set stream years - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal', + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal_swindows', 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal', + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal_swindows', 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'model_year_align_cropcal', + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'model_year_align_cropcal_swindows', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_first_cropcal_cultivar_gdds', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_year_last_cropcal_cultivar_gdds', + 'sim_year'=>$nl_flags->{'sim_year'}, + 'sim_year_range'=>$nl_flags->{'sim_year_range'}); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'model_year_align_cropcal_cultivar_gdds', 'sim_year'=>$nl_flags->{'sim_year'}, 'sim_year_range'=>$nl_flags->{'sim_year_range'}); - # Check/set things if first and last years are different - if ( $nl->get_value('stream_year_first_cropcal') != - $nl->get_value('stream_year_last_cropcal') ) { - - # Do not allow maturity requirements to change over time if stream_fldFileName_gdd20_baseline is provided. That would be nonsensical. - # Note that this restricts sowing windows from changing over time as well, because there are not separate stream_year settings for that. - if ( !&string_is_undef_or_empty($gdd20_baseline_file) ) { - $log->fatal_error("If cropcals_rx_adapt is true (i.e., stream_fldFileName_gdd20_baseline is provided), no crop calendar input is allowed to vary over time (i.e., stream_year_first_cropcal and stream_year_last_cropcal must be the same)." ); - } + # Do not allow maturity requirements to change over time if stream_fldFileName_gdd20_baseline is provided. That would be nonsensical. + if ( $nl->get_value('stream_year_first_cropcal_cultivar_gdds') != + $nl->get_value('stream_year_last_cropcal_cultivar_gdds') + and !&string_is_undef_or_empty($gdd20_baseline_file) ) { + $log->fatal_error("If cropcals_rx_adapt is true (i.e., stream_fldFileName_gdd20_baseline is provided), baseline maturity requirements are allowed to vary over time (i.e., stream_year_first_cropcal_cultivar_gdds and stream_year_last_cropcal_cultivar_gdds must be the same)." ); } } diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 85f37a22fd..da0a523adc 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1689,9 +1689,12 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c .false. .false. -2000 -2000 -2000 +2000 +2000 +2000 +2000 +2000 +2000 lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index f37ada6847..50e645519c 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1783,19 +1783,34 @@ Flag to enable prescribed crop calendars (sowing window dates and maturity requi Flag to enable prescribed crop calendars (sowing window dates and maturity requirement), with maturity requirement adaptive based on recent climate - -First year to loop over for crop calendar data (not including gdd20_baseline file) +First year to loop over for crop sowing windows - -Last year to loop over for crop calendar data (not including gdd20_baseline file) +Last year to loop over for crop sowing windows - -Simulation year that aligns with stream_year_first_cropcal value +Simulation year that aligns with stream_year_first_cropcal_swindows value + + + +First year to loop over for crop maturity requirements + + + +Last year to loop over for crop maturity requirements + + + +Simulation year that aligns with stream_year_first_cropcal_cultivar_gdds value Date: Fri, 31 May 2024 13:55:29 -0600 Subject: [PATCH 025/206] Explain hard-coding of 2000 when reading GDD20 baseline file. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 8f444a0479..413ddbefce 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -274,8 +274,9 @@ subroutine cropcal_init(bounds) end if ! Initialize the cdeps data type sdat_cropcal_gdd20_baseline - ! NOTE: stream_dtlimit 1.5 didn't work for some reason - !TODO SSR: Do not allow time axis length > 1 + ! NOTE: Hard-coded to one particular year because it should NOT vary over time. Note that the + ! particular year chosen doesn't matter. Users can base their file on whatever baseline they + ! want; they just need to put 2000 on the time axis. if (adapt_cropcal_rx_cultivar_gdds) then call shr_strdata_init_from_inline(sdat_cropcal_gdd20_baseline, & my_task = iam, & From 75d7df65dc721083f19a92e801f883c6e4b5d8cb Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 31 May 2024 15:07:12 -0600 Subject: [PATCH 026/206] Functionize UpdateAccVars_CropGDDs(). --- src/biogeophys/TemperatureType.F90 | 121 ++++++++++++++++------------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index ab310650c8..1b28b2e1aa 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -129,6 +129,7 @@ module TemperatureType procedure, public :: InitAccBuffer procedure, public :: InitAccVars procedure, public :: UpdateAccVars + procedure, private :: UpdateAccVars_CropGDDs end type temperature_type @@ -1357,6 +1358,71 @@ subroutine InitAccVars(this, bounds) end subroutine InitAccVars + subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, dtime, nstep, basetemp_int, gddx_patch) + ! + ! USES + use shr_const_mod , only : SHR_CONST_CDAY, SHR_CONST_TKFRZ + use accumulMod , only : update_accum_field, extract_accum_field, accumResetVal + ! + ! !ARGUMENTS + class(temperature_type) :: this + real(r8), intent(inout), pointer, dimension(:) :: rbufslp ! temporary single level - pft level + integer, intent(in) :: begp, endp + integer, intent(in) :: month, day, secs, dtime, nstep + integer, intent(in) :: basetemp_int ! Crop base temperature. Integer to avoid possible float weirdness + real(r8), intent(inout), pointer, dimension(:) :: gddx_patch ! E.g., gdd0_patch + ! + ! !LOCAL VARIABLES + real(r8) :: basetemp_r8 ! real(r8) version of basetemp for arithmetic + real(r8) :: max_accum ! Maximum daily accumulation + character(8) :: field_name ! E.g., GDD0 + character(32) :: format_string + integer :: p, g + + basetemp_r8 = real(basetemp_int, r8) + + ! Get maximum daily accumulation + if (basetemp_int == 0) then + ! SSR 2024-05-31: I'm not sure why this was different for base temp 0, but I'm keeping it as I refactor into UpdateAccVars_CropGDDs() + max_accum = 26._r8 + else + max_accum = 30._r8 + end if + + do p = begp,endp + + ! Avoid unnecessary calculations over inactive points + if (.not. patch%active(p)) then + cycle + end if + + g = patch%gridcell(p) + if (month==1 .and. day==1 .and. secs==dtime) then + rbufslp(p) = accumResetVal ! reset gdd + else if (( month > 3 .and. month < 10 .and. grc%latdeg(g) >= 0._r8) .or. & + ((month > 9 .or. month < 4) .and. grc%latdeg(g) < 0._r8) ) then + rbufslp(p) = max(0._r8, min(max_accum, & + this%t_ref2m_patch(p)-(SHR_CONST_TKFRZ + basetemp_r8))) * dtime/SHR_CONST_CDAY + else + rbufslp(p) = 0._r8 ! keeps gdd unchanged at other times (eg, through Dec in NH) + end if + end do + + ! Get field name + if (basetemp_int < 10) then + format_string = "(A3,I1)" + else if (basetemp_int < 100) then + format_string = "(A3,I2)" + else + format_string = "(A3,I3)" + end if + write(field_name, format_string) "GDD",basetemp_int + + ! Save + call update_accum_field (trim(field_name), rbufslp, nstep) + call extract_accum_field (trim(field_name), gddx_patch, nstep) + end subroutine UpdateAccVars_CropGDDs + !----------------------------------------------------------------------- subroutine UpdateAccVars (this, bounds) ! @@ -1538,63 +1604,14 @@ subroutine UpdateAccVars (this, bounds) ! Accumulate and extract GDD0 - - do p = begp,endp - ! Avoid unnecessary calculations over inactive points - if (patch%active(p)) then - g = patch%gridcell(p) - if (month==1 .and. day==1 .and. secs==dtime) then - rbufslp(p) = accumResetVal ! reset gdd - else if (( month > 3 .and. month < 10 .and. grc%latdeg(g) >= 0._r8) .or. & - ((month > 9 .or. month < 4) .and. grc%latdeg(g) < 0._r8) ) then - rbufslp(p) = max(0._r8, min(26._r8, this%t_ref2m_patch(p)-SHR_CONST_TKFRZ)) * dtime/SHR_CONST_CDAY - else - rbufslp(p) = 0._r8 ! keeps gdd unchanged at other times (eg, through Dec in NH) - end if - end if - end do - call update_accum_field ('GDD0', rbufslp, nstep) - call extract_accum_field ('GDD0', this%gdd0_patch, nstep) + call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 0, this%gdd0_patch) ! Accumulate and extract GDD8 - - do p = begp,endp - ! Avoid unnecessary calculations over inactive points - if (patch%active(p)) then - g = patch%gridcell(p) - if (month==1 .and. day==1 .and. secs==dtime) then - rbufslp(p) = accumResetVal ! reset gdd - else if (( month > 3 .and. month < 10 .and. grc%latdeg(g) >= 0._r8) .or. & - ((month > 9 .or. month < 4) .and. grc%latdeg(g) < 0._r8) ) then - rbufslp(p) = max(0._r8, min(30._r8, & - this%t_ref2m_patch(p)-(SHR_CONST_TKFRZ + 8._r8))) * dtime/SHR_CONST_CDAY - else - rbufslp(p) = 0._r8 ! keeps gdd unchanged at other times (eg, through Dec in NH) - end if - end if - end do - call update_accum_field ('GDD8', rbufslp, nstep) - call extract_accum_field ('GDD8', this%gdd8_patch, nstep) + call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 8, this%gdd8_patch) ! Accumulate and extract GDD10 + call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 10, this%gdd10_patch) - do p = begp,endp - ! Avoid unnecessary calculations over inactive points - if (patch%active(p)) then - g = patch%gridcell(p) - if (month==1 .and. day==1 .and. secs==dtime) then - rbufslp(p) = accumResetVal ! reset gdd - else if (( month > 3 .and. month < 10 .and. grc%latdeg(g) >= 0._r8) .or. & - ((month > 9 .or. month < 4) .and. grc%latdeg(g) < 0._r8) ) then - rbufslp(p) = max(0._r8, min(30._r8, & - this%t_ref2m_patch(p)-(SHR_CONST_TKFRZ + 10._r8))) * dtime/SHR_CONST_CDAY - else - rbufslp(p) = 0._r8 ! keeps gdd unchanged at other times (eg, through Dec in NH) - end if - end if - end do - call update_accum_field ('GDD10', rbufslp, nstep) - call extract_accum_field ('GDD10', this%gdd10_patch, nstep) ! Accumulate and extract running 20-year means if (is_end_curr_year()) then From 45aff290b33baed02b48b089858de6f0f4a89a74 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 31 May 2024 17:08:01 -0600 Subject: [PATCH 027/206] Optionally read/use gdd20 accum seasons from stream files. --- bld/CLMBuildNamelist.pm | 15 ++ bld/namelist_files/namelist_defaults_ctsm.xml | 3 + .../namelist_definition_ctsm.xml | 15 ++ src/biogeochem/CropType.F90 | 4 + src/biogeophys/TemperatureType.F90 | 54 +++-- src/cpl/share_esmf/cropcalStreamMod.F90 | 198 ++++++++++++++++-- src/main/clm_driver.F90 | 2 +- 7 files changed, 265 insertions(+), 26 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 26725b7d96..dddd17ced9 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4175,6 +4175,7 @@ sub setup_logic_cropcal_streams { # Set up other crop calendar parameters add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'cropcals_rx'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'cropcals_rx_adapt'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_gdd20_seasons'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'generate_crop_gdds'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'use_mxmat'); @@ -4185,6 +4186,20 @@ sub setup_logic_cropcal_streams { $log->fatal_error("cropcals_rx and cropcals_rx_adapt may not both be true" ); } + # Add defaults if reading gdd20 seasons from stream files + my $stream_gdd20_seasons = $nl->get_value('stream_gdd20_seasons') ; + my $gdd20_season_start_file = $nl->get_value('stream_fldFileName_gdd20_season_start') ; + my $gdd20_season_end_file = $nl->get_value('stream_fldFileName_gdd20_season_end') ; + if ( &value_is_true($stream_gdd20_seasons)) { + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_season_start'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_season_end'); + + # Check + if ( &string_is_undef_or_empty($gdd20_season_start_file) or &string_is_undef_or_empty($gdd20_season_end_file) ) { + $log->fatal_error("If stream_gdd20_seasons is true, gdd20 season start and end files must be provided." ); + } + } + # Add defaults if using prescribed crop calendars if ( &value_is_true($cropcals_rx) or &value_is_true($cropcals_rx_adapt) ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_swindow_start'); diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index da0a523adc..7e3ddb7c64 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1689,6 +1689,7 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c .false. .false. +.false. 2000 2000 2000 @@ -1705,6 +1706,8 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc lnd/clm2/testdata/gdd20baseline.tmp_dontupload.nc +lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc +lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 50e645519c..036b9aca72 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1838,6 +1838,21 @@ Filename of input stream data for cultivar growing degree-day targets Filename of input stream data for baseline GDD20 values + +Filename of input stream data for date (day of year) of start of gdd20 accumulation season. + + + +Set this to true to read gdd20 accumulation season start and end dates from stream files, rather than using hard-coded hemisphere-specific "warm seasons." + + + +Filename of input stream data for date (day of year) of end of gdd20 accumulation season. + + Filename of input stream data for crop calendar inputs diff --git a/src/biogeochem/CropType.F90 b/src/biogeochem/CropType.F90 index a7bccf1a73..04806f9349 100644 --- a/src/biogeochem/CropType.F90 +++ b/src/biogeochem/CropType.F90 @@ -53,6 +53,8 @@ module CropType integer , pointer :: rx_swindow_ends_thisyr_patch (:,:) ! all prescribed sowing window end dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: rx_cultivar_gdds_thisyr_patch (:,:) ! all cultivar GDD targets for this patch this year (ddays) [patch, mxsowings] real(r8), pointer :: gdd20_baseline_patch (:) ! GDD20 baseline for this patch (ddays) [patch] + integer , pointer :: gdd20_season_start_patch(:) ! gdd20 season start date for this patch (day of year) [patch] + integer , pointer :: gdd20_season_end_patch (:) ! gdd20 season end date for this patch (day of year) [patch] real(r8), pointer :: sdates_thisyr_patch (:,:) ! all actual sowing dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_starts_thisyr_patch(:,:) ! all sowing window start dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_ends_thisyr_patch (:,:) ! all sowing window end dates for this patch this year (day of year) [patch, mxsowings] @@ -237,6 +239,8 @@ subroutine InitAllocate(this, bounds) allocate(this%rx_swindow_ends_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_swindow_ends_thisyr_patch (:,:) = -1 allocate(this%rx_cultivar_gdds_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_cultivar_gdds_thisyr_patch(:,:) = spval allocate(this%gdd20_baseline_patch(begp:endp)) ; this%gdd20_baseline_patch(:) = spval + allocate(this%gdd20_season_start_patch(begp:endp)); this%gdd20_season_start_patch(:) = -1 + allocate(this%gdd20_season_end_patch(begp:endp)) ; this%gdd20_season_end_patch (:) = -1 allocate(this%sdates_thisyr_patch(begp:endp,1:mxsowings)) ; this%sdates_thisyr_patch(:,:) = spval allocate(this%swindow_starts_thisyr_patch(begp:endp,1:mxsowings)) ; this%swindow_starts_thisyr_patch(:,:) = spval allocate(this%swindow_ends_thisyr_patch (begp:endp,1:mxsowings)) ; this%swindow_ends_thisyr_patch (:,:) = spval diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 1b28b2e1aa..31fba16274 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -1358,11 +1358,13 @@ subroutine InitAccVars(this, bounds) end subroutine InitAccVars - subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, dtime, nstep, basetemp_int, gddx_patch) + subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, dtime, nstep, basetemp_int, gddx_patch, crop_inst) ! ! USES use shr_const_mod , only : SHR_CONST_CDAY, SHR_CONST_TKFRZ use accumulMod , only : update_accum_field, extract_accum_field, accumResetVal + use clm_time_manager , only : is_doy_in_interval + use CropType, only : crop_type ! ! !ARGUMENTS class(temperature_type) :: this @@ -1371,13 +1373,22 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d integer, intent(in) :: month, day, secs, dtime, nstep integer, intent(in) :: basetemp_int ! Crop base temperature. Integer to avoid possible float weirdness real(r8), intent(inout), pointer, dimension(:) :: gddx_patch ! E.g., gdd0_patch + type(crop_type), intent(inout) :: crop_inst ! ! !LOCAL VARIABLES real(r8) :: basetemp_r8 ! real(r8) version of basetemp for arithmetic real(r8) :: max_accum ! Maximum daily accumulation character(8) :: field_name ! E.g., GDD0 character(32) :: format_string - integer :: p, g + integer :: p + logical :: in_accumulation_season + real(r8) :: lat ! latitude + integer :: gdd20_season_start, gdd20_season_end + + associate( & + gdd20_season_starts => crop_inst%gdd20_season_start_patch, & + gdd20_season_ends => crop_inst%gdd20_season_end_patch & + ) basetemp_r8 = real(basetemp_int, r8) @@ -1396,15 +1407,28 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d cycle end if - g = patch%gridcell(p) + ! Is this patch in its gdd20 accumulation season? + ! First, check based on latitude. This will be fallback if read-in gdd20 accumulation season is invalid. + lat = grc%latdeg(patch%gridcell(p)) + in_accumulation_season = & + ((month > 3 .and. month < 10) .and. lat >= 0._r8) .or. & + ((month > 9 .or. month < 4) .and. lat < 0._r8) + ! Replace with read-in gdd20 accumulation season, if valid + ! (If these aren't being read in or they're invalid, they'll be -1) + gdd20_season_start = crop_inst%gdd20_season_start_patch(p) + gdd20_season_end = crop_inst%gdd20_season_end_patch(p) + if (gdd20_season_start >= 1 .and. gdd20_season_end >= 1) then + in_accumulation_season = is_doy_in_interval( & + gdd20_season_starts(p), gdd20_season_ends(p), day) + end if + if (month==1 .and. day==1 .and. secs==dtime) then rbufslp(p) = accumResetVal ! reset gdd - else if (( month > 3 .and. month < 10 .and. grc%latdeg(g) >= 0._r8) .or. & - ((month > 9 .or. month < 4) .and. grc%latdeg(g) < 0._r8) ) then + else if (in_accumulation_season) then rbufslp(p) = max(0._r8, min(max_accum, & this%t_ref2m_patch(p)-(SHR_CONST_TKFRZ + basetemp_r8))) * dtime/SHR_CONST_CDAY else - rbufslp(p) = 0._r8 ! keeps gdd unchanged at other times (eg, through Dec in NH) + rbufslp(p) = 0._r8 ! keeps gdd unchanged outside accumulation season end if end do @@ -1421,24 +1445,28 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d ! Save call update_accum_field (trim(field_name), rbufslp, nstep) call extract_accum_field (trim(field_name), gddx_patch, nstep) + + end associate end subroutine UpdateAccVars_CropGDDs !----------------------------------------------------------------------- - subroutine UpdateAccVars (this, bounds) + subroutine UpdateAccVars (this, bounds, crop_inst) ! ! USES - use shr_const_mod , only : SHR_CONST_CDAY, SHR_CONST_TKFRZ + use shr_const_mod , only : SHR_CONST_TKFRZ use clm_time_manager , only : get_step_size, get_nstep, is_end_curr_day, get_curr_date, is_end_curr_year - use accumulMod , only : update_accum_field, extract_accum_field, accumResetVal + use accumulMod , only : update_accum_field, extract_accum_field use CNSharedParamsMod, only : upper_soil_layer + use CropType , only : crop_type ! ! !ARGUMENTS: class(temperature_type) :: this type(bounds_type) , intent(in) :: bounds + type(crop_type), intent(inout) :: crop_inst ! ! !LOCAL VARIABLES: - integer :: m,g,l,c,p ! indices + integer :: m,l,c,p ! indices integer :: ier ! error status integer :: dtime ! timestep size [seconds] integer :: nstep ! timestep number @@ -1604,13 +1632,13 @@ subroutine UpdateAccVars (this, bounds) ! Accumulate and extract GDD0 - call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 0, this%gdd0_patch) + call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 0, this%gdd0_patch, crop_inst) ! Accumulate and extract GDD8 - call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 8, this%gdd8_patch) + call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 8, this%gdd8_patch, crop_inst) ! Accumulate and extract GDD10 - call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 10, this%gdd10_patch) + call this%UpdateAccVars_CropGDDs(rbufslp, begp, endp, month, day, secs, dtime, nstep, 10, this%gdd10_patch, crop_inst) ! Accumulate and extract running 20-year means diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 413ddbefce..8196ca1dfb 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -38,9 +38,12 @@ module cropcalStreamMod type(shr_strdata_type) :: sdat_cropcal_swindow_end ! sowing window end input data stream type(shr_strdata_type) :: sdat_cropcal_cultivar_gdds ! maturity requirement input data stream type(shr_strdata_type) :: sdat_cropcal_gdd20_baseline ! GDD20 baseline input data stream + type(shr_strdata_type) :: sdat_cropcal_gdd20_season_start ! gdd20 season start input data stream + type(shr_strdata_type) :: sdat_cropcal_gdd20_season_end ! gdd20 season end input data stream character(len=CS), allocatable :: stream_varnames_sdate(:) ! used for both start and end dates character(len=CS), allocatable :: stream_varnames_cultivar_gdds(:) character(len=CS), allocatable :: stream_varnames_gdd20_baseline(:) + character(len=CS), allocatable :: stream_varnames_gdd20_season_enddate(:) ! start uses stream_varnames_sdate integer :: ncft ! Number of crop functional types (excl. generic crops) logical :: allow_invalid_swindow_inputs ! Fall back on paramfile sowing windows in cases of invalid values in stream_fldFileName_swindow_start and _end? character(len=CL) :: stream_fldFileName_swindow_start ! sowing window start stream filename to read @@ -49,6 +52,10 @@ module cropcalStreamMod character(len=CL) :: stream_fldFileName_gdd20_baseline ! GDD20 baseline stream filename to read logical :: cropcals_rx ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash logical :: cropcals_rx_adapt ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash + logical :: stream_gdd20_seasons ! Read stream file for GDD20 accumulation seasons, instead of per-hemisphere periods + logical :: allow_invalid_gdd20_season_inputs ! Fall back on hemisphere "warm periods" in cases of invalid values in stream_fldFileName_gdd20_season_start and _end? + character(len=CL) :: stream_fldFileName_gdd20_season_start ! Stream filename to read for start of gdd20 season + character(len=CL) :: stream_fldFileName_gdd20_season_end ! Stream filename to read for end of gdd20 season character(len=*), parameter :: sourcefile = & __FILE__ @@ -105,7 +112,11 @@ subroutine cropcal_init(bounds) stream_fldFileName_gdd20_baseline, & stream_meshfile_cropcal, & cropcals_rx, & - cropcals_rx_adapt + cropcals_rx_adapt, & + stream_gdd20_seasons, & + allow_invalid_gdd20_season_inputs, & + stream_fldFileName_gdd20_season_start, & + stream_fldFileName_gdd20_season_end ! Default values for namelist stream_year_first_cropcal_swindows = 1 ! first year in sowing window streams to use @@ -120,16 +131,22 @@ subroutine cropcal_init(bounds) stream_fldFileName_swindow_end = '' stream_fldFileName_cultivar_gdds = '' stream_fldFileName_gdd20_baseline = '' + stream_gdd20_seasons = .false. + allow_invalid_gdd20_season_inputs = .false. + stream_fldFileName_gdd20_season_start = '' + stream_fldFileName_gdd20_season_end = '' ! Will need modification to work with mxsowings > 1 ncft = mxpft - npcropmin + 1 ! Ignores generic crops allocate(stream_varnames_sdate(ncft)) allocate(stream_varnames_cultivar_gdds(ncft)) allocate(stream_varnames_gdd20_baseline(ncft)) + allocate(stream_varnames_gdd20_season_enddate(ncft)) do n = 1,ncft ivt = npcropmin + n - 1 write(stream_varnames_sdate(n),'(a,i0)') "sdate1_",ivt write(stream_varnames_cultivar_gdds(n),'(a,i0)') "gdd1_",ivt write(stream_varnames_gdd20_baseline(n),'(a,i0)') "gdd20bl_",ivt + write(stream_varnames_gdd20_season_enddate(n),'(a,i0)') "hdate1_",ivt end do ! Read cropcal_streams namelist @@ -158,6 +175,10 @@ subroutine cropcal_init(bounds) call shr_mpi_bcast(stream_fldFileName_cultivar_gdds, mpicom) call shr_mpi_bcast(stream_fldFileName_gdd20_baseline, mpicom) call shr_mpi_bcast(stream_meshfile_cropcal , mpicom) + call shr_mpi_bcast(stream_gdd20_seasons, mpicom) + call shr_mpi_bcast(allow_invalid_gdd20_season_inputs, mpicom) + call shr_mpi_bcast(stream_fldFileName_gdd20_season_start, mpicom) + call shr_mpi_bcast(stream_fldFileName_gdd20_season_end, mpicom) if (masterproc) then write(iulog,*) @@ -174,9 +195,14 @@ subroutine cropcal_init(bounds) write(iulog,'(a,a)' ) ' stream_fldFileName_cultivar_gdds = ',trim(stream_fldFileName_cultivar_gdds) write(iulog,'(a,a)' ) ' stream_fldFileName_gdd20_baseline = ',trim(stream_fldFileName_gdd20_baseline) write(iulog,'(a,a)' ) ' stream_meshfile_cropcal = ',trim(stream_meshfile_cropcal) + write(iulog,'(a,l1)') ' stream_gdd20_seasons = ',stream_gdd20_seasons + write(iulog,'(a,l1)') ' allow_invalid_gdd20_season_inputs = ',allow_invalid_gdd20_season_inputs + write(iulog,'(a,a)' ) ' stream_fldFileName_gdd20_season_start = ',stream_fldFileName_gdd20_season_start + write(iulog,'(a,a)' ) ' stream_fldFileName_gdd20_season_end = ',stream_fldFileName_gdd20_season_end do n = 1,ncft write(iulog,'(a,a)' ) ' stream_varnames_sdate = ',trim(stream_varnames_sdate(n)) write(iulog,'(a,a)' ) ' stream_varnames_cultivar_gdds = ',trim(stream_varnames_cultivar_gdds(n)) + write(iulog,'(a,a)' ) ' stream_varnames_gdd20_season_enddate = ',trim(stream_varnames_gdd20_season_enddate(n)) write(iulog,'(a,a)' ) ' stream_varnames_gdd20_baseline = ',trim(stream_varnames_gdd20_baseline(n)) end do write(iulog,*) @@ -304,6 +330,65 @@ subroutine cropcal_init(bounds) end if end if + if (stream_gdd20_seasons) then + ! Initialize the cdeps data type sdat_cropcal_gdd20_season_start + ! NOTE: Hard-coded to one particular year because it should NOT vary over time. Note that the + ! particular year chosen doesn't matter. + call shr_strdata_init_from_inline(sdat_cropcal_gdd20_season_start, & + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock, & + model_mesh = mesh, & + stream_meshfile = trim(stream_meshfile_cropcal), & + stream_lev_dimname = 'null', & + stream_mapalgo = trim(cropcal_mapalgo), & + stream_filenames = (/trim(stream_fldFileName_gdd20_season_start)/), & + stream_fldlistFile = stream_varnames_sdate, & + stream_fldListModel = stream_varnames_sdate, & + stream_yearFirst = 2000, & + stream_yearLast = 2000, & + stream_yearAlign = 2000, & + stream_offset = cropcal_offset, & + stream_taxmode = 'extend', & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = cropcal_tintalgo, & + stream_name = 'gdd20 season start data', & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + ! Initialize the cdeps data type sdat_cropcal_gdd20_season_end + ! NOTE: Hard-coded to one particular year because it should NOT vary over time. Note that the + ! particular year chosen doesn't matter. + call shr_strdata_init_from_inline(sdat_cropcal_gdd20_season_end, & + my_task = iam, & + logunit = iulog, & + compname = 'LND', & + model_clock = model_clock, & + model_mesh = mesh, & + stream_meshfile = trim(stream_meshfile_cropcal), & + stream_lev_dimname = 'null', & + stream_mapalgo = trim(cropcal_mapalgo), & + stream_filenames = (/trim(stream_fldFileName_gdd20_season_end)/), & + stream_fldlistFile = stream_varnames_gdd20_season_enddate, & + stream_fldListModel = stream_varnames_gdd20_season_enddate, & + stream_yearFirst = 2000, & + stream_yearLast = 2000, & + stream_yearAlign = 2000, & + stream_offset = cropcal_offset, & + stream_taxmode = 'extend', & + stream_dtlimit = 1.0e30_r8, & + stream_tintalgo = cropcal_tintalgo, & + stream_name = 'gdd20 season start data', & + rc = rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + + end if + end subroutine cropcal_init !================================================================ @@ -347,7 +432,10 @@ subroutine cropcal_advance( bounds ) end if end if - ! GDD20 baseline values do not have an associated time axis and thus will not be advanced here + ! The following should not have an associated time axis and thus will not be advanced here: + ! - GDD20 baseline values + ! - GDD20 season start dates + ! - GDD20 season end dates if ( .not. allocated(g_to_ig) )then allocate (g_to_ig(bounds%begg:bounds%endg) ) @@ -393,15 +481,21 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) real(r8), pointer :: dataptr1d_swindow_end (:) real(r8), pointer :: dataptr1d_cultivar_gdds(:) real(r8), pointer :: dataptr1d_gdd20_baseline(:) + real(r8), pointer :: dataptr1d_gdd20_season_start(:) + real(r8), pointer :: dataptr1d_gdd20_season_end (:) real(r8), pointer :: dataptr2d_swindow_start(:,:) real(r8), pointer :: dataptr2d_swindow_end (:,:) real(r8), pointer :: dataptr2d_cultivar_gdds(:,:) real(r8), pointer :: dataptr2d_gdd20_baseline(:,:) + real(r8), pointer :: dataptr2d_gdd20_season_start(:,:) + real(r8), pointer :: dataptr2d_gdd20_season_end (:,:) !----------------------------------------------------------------------- associate( & - starts => crop_inst%rx_swindow_starts_thisyr_patch, & - ends => crop_inst%rx_swindow_ends_thisyr_patch & + swindow_starts => crop_inst%rx_swindow_starts_thisyr_patch, & + swindow_ends => crop_inst%rx_swindow_ends_thisyr_patch, & + gdd20_season_starts => crop_inst%gdd20_season_start_patch, & + gdd20_season_ends => crop_inst%gdd20_season_end_patch & ) SHR_ASSERT_FL( (lbound(g_to_ig,1) <= bounds%begg ), sourcefile, __LINE__) @@ -459,8 +553,8 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) n = ivt - npcropmin + 1 ! vegetated pft ig = g_to_ig(patch%gridcell(p)) - starts(p,1) = dataptr2d_swindow_start(ig,n) - ends(p,1) = dataptr2d_swindow_end (ig,n) + swindow_starts(p,1) = dataptr2d_swindow_start(ig,n) + swindow_ends(p,1) = dataptr2d_swindow_end (ig,n) else write(iulog,'(a,i0)') 'cropcal_interp(), prescribed sowing windows: Crop patch has ivt ',ivt call ESMF_Finalize(endflag=ESMF_END_ABORT) @@ -469,23 +563,23 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Ensure that, if mxsowings > 1, sowing windows are ordered such that ENDS are monotonically increasing. This is necessary because of how get_swindow() works. if (mxsowings > 1) then - if (any(ends(begp:endp,2:mxsowings) <= ends(begp:endp,1:mxsowings-1) .and. & - ends(begp:endp,2:mxsowings) >= 1)) then + if (any(swindow_ends(begp:endp,2:mxsowings) <= swindow_ends(begp:endp,1:mxsowings-1) .and. & + swindow_ends(begp:endp,2:mxsowings) >= 1)) then write(iulog, *) 'Sowing window inputs must be ordered such that end dates are monotonically increasing.' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if end if ! Handle invalid sowing window values - if (any(starts(begp:endp,:) < 1 .or. ends(begp:endp,:) < 1)) then + if (any(swindow_starts(begp:endp,:) < 1 .or. swindow_ends(begp:endp,:) < 1)) then ! Fail if not allowing fallback to paramfile sowing windows - if ((.not. allow_invalid_swindow_inputs) .and. any(all(starts(begp:endp,:) < 1, dim=2) .and. patch%wtgcell > 0._r8 .and. patch%itype >= npcropmin)) then + if ((.not. allow_invalid_swindow_inputs) .and. any(all(swindow_starts(begp:endp,:) < 1, dim=2) .and. patch%wtgcell > 0._r8 .and. patch%itype >= npcropmin)) then write(iulog, *) 'At least one crop in one gridcell has invalid prescribed sowing window start date(s). To ignore and fall back to paramfile sowing windows, set allow_invalid_swindow_inputs to .true.' write(iulog, *) 'Affected crops:' do ivt = npcropmin, mxpft do fp = 1, num_pcropp p = filter_pcropp(fp) - if (ivt == patch%itype(p) .and. patch%wtgcell(p) > 0._r8 .and. all(starts(p,:) < 1)) then + if (ivt == patch%itype(p) .and. patch%wtgcell(p) > 0._r8 .and. all(swindow_starts(p,:) < 1)) then write(iulog, *) ' ',pftname(ivt),' (',ivt,')' exit ! Stop looking for patches of this type end if @@ -494,7 +588,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) call ESMF_Finalize(endflag=ESMF_END_ABORT) ! Fail if a sowing window start date is prescribed without an end date (or vice versa) - else if (any((starts(begp:endp,:) >= 1 .and. ends(begp:endp,:) < 1) .or. (starts(begp:endp,:) < 1 .and. ends(begp:endp,:) >= 1))) then + else if (any((swindow_starts(begp:endp,:) >= 1 .and. swindow_ends(begp:endp,:) < 1) .or. (swindow_starts(begp:endp,:) < 1 .and. swindow_ends(begp:endp,:) >= 1))) then write(iulog, *) 'Every prescribed sowing window start date must have a corresponding end date.' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -613,6 +707,86 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_gdd20_baseline) + ! Read prescribed gdd20 season start dates from input files + allocate(dataptr2d_gdd20_season_start(lsize, ncft)) + dataptr2d_gdd20_season_start(:,:) = -1._r8 + allocate(dataptr2d_gdd20_season_end (lsize, ncft)) + dataptr2d_gdd20_season_end(:,:) = -1._r8 + if (stream_gdd20_seasons .and. init) then + ! Starting with npcropmin will skip generic crops + do n = 1, ncft + call dshr_fldbun_getFldPtr(sdat_cropcal_gdd20_season_start%pstrm(1)%fldbun_model, trim(stream_varnames_sdate(n)), & + fldptr1=dataptr1d_gdd20_season_start, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + call dshr_fldbun_getFldPtr(sdat_cropcal_gdd20_season_end%pstrm(1)%fldbun_model, trim(stream_varnames_gdd20_season_enddate(n)), & + fldptr1=dataptr1d_gdd20_season_end, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize + ! So an explicit loop is required here + do g = 1,lsize + + ! If read-in value is invalid, set to -1. Will be handled later in this subroutine. + if (dataptr1d_gdd20_season_start(g) <= 0 .or. dataptr1d_gdd20_season_start(g) > 366 & + .or. dataptr1d_gdd20_season_end(g) <= 0 .or. dataptr1d_gdd20_season_end(g) > 366) then + dataptr1d_gdd20_season_start(g) = -1 + dataptr1d_gdd20_season_end (g) = -1 + end if + + dataptr2d_gdd20_season_start(g,n) = dataptr1d_gdd20_season_start(g) + dataptr2d_gdd20_season_end (g,n) = dataptr1d_gdd20_season_end (g) + end do + end do + + ! Set gdd20 season for each gridcell/patch combination + do fp = 1, num_pcropp + p = filter_pcropp(fp) + ivt = patch%itype(p) + ! Will skip generic crops + if (ivt >= npcropmin) then + n = ivt - npcropmin + 1 + ! vegetated pft + ig = g_to_ig(patch%gridcell(p)) + gdd20_season_starts(p) = dataptr2d_gdd20_season_start(ig,n) + gdd20_season_ends(p) = dataptr2d_gdd20_season_end (ig,n) + else + write(iulog,'(a,i0)') 'cropcal_interp(), gdd20 seasons: Crop patch has ivt ',ivt + call ESMF_Finalize(endflag=ESMF_END_ABORT) + endif + end do + + ! Handle invalid gdd20 season values + if (any(gdd20_season_starts(begp:endp) < 1 .or. gdd20_season_ends(begp:endp) < 1)) then + ! Fail if not allowing fallback to paramfile sowing windows + if ((.not. allow_invalid_gdd20_season_inputs) .and. any(gdd20_season_starts(begp:endp) < 1 .and. patch%wtgcell(begp:endp) > 0._r8 .and. patch%itype(begp:endp) >= npcropmin)) then + write(iulog, *) 'At least one crop in one gridcell has invalid gdd20 season start date(s). To ignore and fall back to paramfile sowing windows, set allow_invalid_gdd20_season_inputs to .true.' + write(iulog, *) 'Affected crops:' + do ivt = npcropmin, mxpft + do fp = 1, num_pcropp + p = filter_pcropp(fp) + if (ivt == patch%itype(p) .and. patch%wtgcell(p) > 0._r8 .and. gdd20_season_starts(p) < 1) then + write(iulog, *) ' ',pftname(ivt),' (',ivt,')' + exit ! Stop looking for patches of this type + end if + end do + end do + call ESMF_Finalize(endflag=ESMF_END_ABORT) + + ! Fail if a gdd20 season start date is given without an end date (or vice versa) + else if (any((gdd20_season_starts(begp:endp) >= 1 .and. gdd20_season_ends(begp:endp) < 1) .or. (gdd20_season_starts(begp:endp) < 1 .and. gdd20_season_ends(begp:endp) >= 1))) then + write(iulog, *) 'Every gdd20 season start date must have a corresponding end date.' + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if + + end if ! stream_gdd20_seasons and init + deallocate(dataptr2d_gdd20_season_start) + deallocate(dataptr2d_gdd20_season_end) + + end associate end subroutine cropcal_interp diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90 index 2a3cef4b8b..e660ab9d8d 100644 --- a/src/main/clm_driver.F90 +++ b/src/main/clm_driver.F90 @@ -1374,7 +1374,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro call atm2lnd_inst%UpdateAccVars(bounds_proc) - call temperature_inst%UpdateAccVars(bounds_proc) + call temperature_inst%UpdateAccVars(bounds_proc, crop_inst) call canopystate_inst%UpdateAccVars(bounds_proc) From 0feff1f3d641ec47c616d41d8c6fe3e4b9208db1 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 31 May 2024 18:26:51 -0600 Subject: [PATCH 028/206] Fix check of invalid swindow inputs. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 8196ca1dfb..4c4925f88e 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -573,7 +573,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Handle invalid sowing window values if (any(swindow_starts(begp:endp,:) < 1 .or. swindow_ends(begp:endp,:) < 1)) then ! Fail if not allowing fallback to paramfile sowing windows - if ((.not. allow_invalid_swindow_inputs) .and. any(all(swindow_starts(begp:endp,:) < 1, dim=2) .and. patch%wtgcell > 0._r8 .and. patch%itype >= npcropmin)) then + if ((.not. allow_invalid_swindow_inputs) .and. any(all(swindow_starts(begp:endp,:) < 1, dim=2) .and. patch%wtgcell(begp:endp) > 0._r8 .and. patch%itype(begp:endp) >= npcropmin)) then write(iulog, *) 'At least one crop in one gridcell has invalid prescribed sowing window start date(s). To ignore and fall back to paramfile sowing windows, set allow_invalid_swindow_inputs to .true.' write(iulog, *) 'Affected crops:' do ivt = npcropmin, mxpft From 3729b6db9f2139399618e39c4c48084144eecf12 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 31 May 2024 22:35:35 -0600 Subject: [PATCH 029/206] Add RxCropCalsAdaptGGCMI testmod and test. --- cime_config/testdefs/testlist_clm.xml | 9 +++++++++ .../clm/RxCropCalsAdaptGGCMI/include_user_mods | 1 + .../testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm | 2 ++ 3 files changed, 12 insertions(+) create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index bf763c4775..edb932d24e 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3737,5 +3737,14 @@ + + + + + + + + + diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods new file mode 100644 index 0000000000..af5fe8591e --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods @@ -0,0 +1 @@ +../RxCropCalsAdapt diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm new file mode 100644 index 0000000000..fa0e4ec663 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm @@ -0,0 +1,2 @@ + +stream_gdd20_seasons = .true. From d020eada257e99055b2b90600f78c630286492c1 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 09:10:40 -0600 Subject: [PATCH 030/206] Fix check that gdd20 season files are provided. --- bld/CLMBuildNamelist.pm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index dddd17ced9..d5c2ffd54f 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4188,14 +4188,17 @@ sub setup_logic_cropcal_streams { # Add defaults if reading gdd20 seasons from stream files my $stream_gdd20_seasons = $nl->get_value('stream_gdd20_seasons') ; - my $gdd20_season_start_file = $nl->get_value('stream_fldFileName_gdd20_season_start') ; - my $gdd20_season_end_file = $nl->get_value('stream_fldFileName_gdd20_season_end') ; if ( &value_is_true($stream_gdd20_seasons)) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_season_start'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_season_end'); # Check + my $gdd20_season_start_file = $nl->get_value('stream_fldFileName_gdd20_season_start') ; + my $gdd20_season_end_file = $nl->get_value('stream_fldFileName_gdd20_season_end') ; if ( &string_is_undef_or_empty($gdd20_season_start_file) or &string_is_undef_or_empty($gdd20_season_end_file) ) { + $log->message($gdd20_season_start_file); + $log->message('abcd'); + $log->message($gdd20_season_end_file); $log->fatal_error("If stream_gdd20_seasons is true, gdd20 season start and end files must be provided." ); } } From 42b0dbb9f7e02ab2138710904af129704867e466 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 09:11:09 -0600 Subject: [PATCH 031/206] Minor rearrangement in cropcal namelist definition. --- bld/namelist_files/namelist_definition_ctsm.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 036b9aca72..5b6dcef87e 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1838,16 +1838,16 @@ Filename of input stream data for cultivar growing degree-day targets Filename of input stream data for baseline GDD20 values - -Filename of input stream data for date (day of year) of start of gdd20 accumulation season. - - Set this to true to read gdd20 accumulation season start and end dates from stream files, rather than using hard-coded hemisphere-specific "warm seasons." + +Filename of input stream data for date (day of year) of start of gdd20 accumulation season. + + Filename of input stream data for date (day of year) of end of gdd20 accumulation season. From 92555b9c394a4542ff7412335a459dfab57a8979 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 12:13:20 -0600 Subject: [PATCH 032/206] generate_gdd20_baseline.py now saves NETCDF3_CLASSIC. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index d28bfda0e7..7b5fd625d6 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -226,7 +226,7 @@ def generate_gdd20_baseline(input_files, output_file, author): ds_out[var_out] = this_da # Save - ds_out.to_netcdf(output_file) + ds_out.to_netcdf(output_file, format="NETCDF3_CLASSIC") print("Done!") From 30eb31ce8e92a05bd104268e01e3d62b964ee486 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 12:16:49 -0600 Subject: [PATCH 033/206] Update default to point to classic version. --- bld/namelist_files/namelist_defaults_ctsm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 7e3ddb7c64..2683c2e535 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1705,7 +1705,7 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc -lnd/clm2/testdata/gdd20baseline.tmp_dontupload.nc +lnd/clm2/testdata/gdd20baseline.tmp_dontupload.classic.nc lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc From a6e2c1e23964b6e9c3bf35d1778a14187a0b62e9 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 12:59:29 -0600 Subject: [PATCH 034/206] Add allow_invalid_gdd20_season_inputs to namelist XML. --- bld/namelist_files/namelist_definition_ctsm.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 5b6dcef87e..675c912900 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1843,6 +1843,11 @@ Filename of input stream data for baseline GDD20 values Set this to true to read gdd20 accumulation season start and end dates from stream files, rather than using hard-coded hemisphere-specific "warm seasons." + +By default, a value in stream_fldFileName_gdd20_season_start or _end outside the range [1, 365] (or 366 in leap years) will cause the run to fail. Set this to .true. to instead have such cells fall back to the hard-coded hemisphere-specific "warm seasons." + + Filename of input stream data for date (day of year) of start of gdd20 accumulation season. From 9f04d71e0ac7f5e4f0bcadd195281dfcc2134f81 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 13:22:59 -0600 Subject: [PATCH 035/206] Update missing values from generate_gdd20_baseline.py. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 7b5fd625d6..fd56d61550 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -20,7 +20,8 @@ VAR_LIST_IN = ["GDD0", "GDD8", "GDD10"] VAR_LIST_IN = [x + "20" for x in VAR_LIST_IN] # TODO: Delete this once using the right variables -MISSING_FILL = -1 # Something negative to ensure that gddmaturity never changes (see PlantCrop) +MISSING_FILL = -1 # Something impossible to ensure that you can mark it as a missing value, to be +# bilinear-interpolated STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline @@ -211,7 +212,8 @@ def generate_gdd20_baseline(input_files, output_file, author): long_name = "Dummy GDD20" print(" dummy GDD20") else: - this_da = ds_in[gddn].fillna(MISSING_FILL) + # this_da = ds_in[gddn].fillna(MISSING_FILL) + this_da = ds_in[gddn] this_da = _add_time_axis(this_da) long_name = gddn print(f" {gddn}") @@ -219,6 +221,7 @@ def generate_gdd20_baseline(input_files, output_file, author): # Add attributes this_da.attrs["long_name"] = long_name + f" baseline for {cft_str}" this_da.attrs["units"] = "°C days" + # this_da.attrs["_FillValue"] = MISSING_FILL # Copy that to ds_out var_out = _get_output_varname(cft_str) From 625fd5e7b9eebc61bf8e93824e92a3079bf937cd Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 13:23:16 -0600 Subject: [PATCH 036/206] RxCropCalsAdaptGGCMI now uses allow_invalid_gdd20_season_inputs. --- .../testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm index fa0e4ec663..42e57a675c 100644 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm @@ -1,2 +1,4 @@ stream_gdd20_seasons = .true. +!TODO SSR: Try without this once you have half-degree inputs +allow_invalid_gdd20_season_inputs = .true. From 0156d1aa928f5bc002c1eb8508962138c76f4a1d Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 13:25:02 -0600 Subject: [PATCH 037/206] Move hist_addfld1d() for GDD0 to be with its siblings. --- src/biogeophys/TemperatureType.F90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 31fba16274..89dfd11074 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -597,9 +597,7 @@ subroutine InitHistory(this, bounds, is_simple_buildtemp, is_prog_buildtemp ) call hist_addfld1d (fname='GDD0', units='ddays', & avgflag='A', long_name='Growing degree days base 0C from planting', & ptr_patch=this%gdd0_patch, default='inactive') - end if - if (use_crop) then this%gdd8_patch(begp:endp) = spval call hist_addfld1d (fname='GDD8', units='ddays', & avgflag='A', long_name='Growing degree days base 8C from planting', & From b7c34ffcd7b4421f5266051d3bb7aeef2f790744 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 13:34:16 -0600 Subject: [PATCH 038/206] Add max versions of GDD season accum outputs. --- src/biogeophys/TemperatureType.F90 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 89dfd11074..f8d5206a1e 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -608,6 +608,21 @@ subroutine InitHistory(this, bounds, is_simple_buildtemp, is_prog_buildtemp ) avgflag='A', long_name='Growing degree days base 10C from planting', & ptr_patch=this%gdd10_patch, default='inactive') + this%gdd0_patch(begp:endp) = spval + call hist_addfld1d (fname='GDD0X', units='ddays', & + avgflag='X', long_name='Growing degree days base 0C from planting, max', & + ptr_patch=this%gdd0_patch, default='inactive') + + this%gdd8_patch(begp:endp) = spval + call hist_addfld1d (fname='GDD8X', units='ddays', & + avgflag='X', long_name='Growing degree days base 8C from planting, max', & + ptr_patch=this%gdd8_patch, default='inactive') + + this%gdd10_patch(begp:endp) = spval + call hist_addfld1d (fname='GDD10X', units='ddays', & + avgflag='X', long_name='Growing degree days base 10C from planting, max', & + ptr_patch=this%gdd10_patch, default='inactive') + this%gdd020_patch(begp:endp) = spval call hist_addfld1d (fname='GDD020', units='ddays', & avgflag='A', long_name='Twenty year average of growing degree days base 0C from planting', & From 535e2c5a43f309ea3bbe8eb851c963ee7cb33dcf Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 17:01:44 -0600 Subject: [PATCH 039/206] Even gdd20 files need to be advance()d. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 4c4925f88e..faa13b5fda 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -432,10 +432,26 @@ subroutine cropcal_advance( bounds ) end if end if - ! The following should not have an associated time axis and thus will not be advanced here: + ! The following should not have an associated time axis, but still need to be here ! - GDD20 baseline values ! - GDD20 season start dates ! - GDD20 season end dates + if (adapt_cropcal_rx_cultivar_gdds) then + call shr_strdata_advance(sdat_cropcal_gdd20_baseline, ymd=mcdate, tod=sec, logunit=iulog, istr='cropcaldyn', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if + if (stream_gdd20_seasons) then + call shr_strdata_advance(sdat_cropcal_gdd20_season_start, ymd=mcdate, tod=sec, logunit=iulog, istr='cropcaldyn', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + call shr_strdata_advance(sdat_cropcal_gdd20_season_end, ymd=mcdate, tod=sec, logunit=iulog, istr='cropcaldyn', rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) then + call ESMF_Finalize(endflag=ESMF_END_ABORT) + end if + end if if ( .not. allocated(g_to_ig) )then allocate (g_to_ig(bounds%begg:bounds%endg) ) From 3e524db1aa4f34a70cd08c48c740c8c35033db9d Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 17:03:43 -0600 Subject: [PATCH 040/206] generate_gdd20_baseline.py now saves to double instead of float. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index fd56d61550..8cf30bf44d 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -197,6 +197,7 @@ def generate_gdd20_baseline(input_files, output_file, author): "created": dt.datetime.now().astimezone().isoformat(), }, ) + encoding_dict = {} for cft_str in utils.define_mgdcrop_list(): cft_int = utils.vegtype_str2int(cft_str)[0] print(f"{cft_str} ({cft_int})") @@ -227,9 +228,10 @@ def generate_gdd20_baseline(input_files, output_file, author): var_out = _get_output_varname(cft_str) print(f" Output variable {var_out}") ds_out[var_out] = this_da + encoding_dict[var_out] = {"dtype": "float64"} # Save - ds_out.to_netcdf(output_file, format="NETCDF3_CLASSIC") + ds_out.to_netcdf(output_file, format="NETCDF3_CLASSIC", encoding=encoding_dict) print("Done!") From 18df6b4a04ac901737ccb5d9b806619e66ebe532 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 1 Jun 2024 17:06:19 -0600 Subject: [PATCH 041/206] stream_fldFileName_gdd20_baseline now defaults to half-deg fake file. --- bld/namelist_files/namelist_defaults_ctsm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 2683c2e535..bd5f7132ca 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1705,7 +1705,7 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc -lnd/clm2/testdata/gdd20baseline.tmp_dontupload.classic.nc +lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.nc lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc From a3feed96de079cdc04f42e60a5cfc4ff22959606 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sun, 2 Jun 2024 08:25:06 -0600 Subject: [PATCH 042/206] Use fixed gdd20 baseline file. --- bld/namelist_files/namelist_defaults_ctsm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index bd5f7132ca..8ed75be01a 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1705,7 +1705,7 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc -lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.nc +lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.v2.nc lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc From 5144b36a7993ae6363bd8bb16b41219ed3e032ed Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 10:58:43 -0600 Subject: [PATCH 043/206] gdd20 baseline now interpolated w/ nearest-neighbor. Revert this! --- src/cpl/share_esmf/cropcalStreamMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index faa13b5fda..ce595c0d70 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -312,7 +312,7 @@ subroutine cropcal_init(bounds) model_mesh = mesh, & stream_meshfile = trim(stream_meshfile_cropcal), & stream_lev_dimname = 'null', & - stream_mapalgo = 'bilinear', & + stream_mapalgo = 'nn', & stream_filenames = (/trim(stream_fldFileName_gdd20_baseline)/), & stream_fldlistFile = stream_varnames_gdd20_baseline, & stream_fldListModel = stream_varnames_gdd20_baseline, & From 90b938b5bbf8bc3e13d86e1211bb9519409b477d Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 10:59:37 -0600 Subject: [PATCH 044/206] generate_gdd20_nbaseline: Use GDDNX. --- .../ctsm/crop_calendars/generate_gdd20_baseline.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 8cf30bf44d..7f60fb6a68 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -18,8 +18,7 @@ from ctsm.crop_calendars.import_ds import import_ds import ctsm.crop_calendars.cropcal_utils as utils -VAR_LIST_IN = ["GDD0", "GDD8", "GDD10"] -VAR_LIST_IN = [x + "20" for x in VAR_LIST_IN] # TODO: Delete this once using the right variables +VAR_LIST_IN = ["GDD0X", "GDD8X", "GDD10X"] MISSING_FILL = -1 # Something impossible to ensure that you can mark it as a missing value, to be # bilinear-interpolated STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of @@ -111,26 +110,25 @@ def _get_gddn_for_cft(cft_str): cft_str (str): E.g., "irrigated_temperate_corn" Returns: - str or None: Name of variable to use (e.g., "GDD8"). If crop isn't yet handled, return None. + str or None: Name of variable to use (e.g., "GDD8X"). If crop isn't yet handled, return None. """ gddn = None gdd0_list_str = ["wheat", "cotton", "rice"] if cft_str in _get_cft_list(gdd0_list_str): - gddn = "GDD0" + gddn = 0 gdd8_list_str = ["corn", "sugarcane", "miscanthus", "switchgrass"] if cft_str in _get_cft_list(gdd8_list_str): - gddn = "GDD8" + gddn = 8 gdd10_list_str = ["soybean"] if cft_str in _get_cft_list(gdd10_list_str): - gddn = "GDD10" + gddn = 10 - # TODO: Delete this once using the right variables if gddn is not None: - gddn += "20" + gddn = f"GDD{gddn}X" return gddn From e82679df56e1a514f28ee4a38ae4d9c9b581b120 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 11:25:37 -0600 Subject: [PATCH 045/206] generate_gdd20_baseline: Grid, if needed. --- .../ctsm/crop_calendars/generate_gdd20_baseline.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 7f60fb6a68..71125e332d 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -17,8 +17,10 @@ # pylint: disable=wrong-import-position from ctsm.crop_calendars.import_ds import import_ds import ctsm.crop_calendars.cropcal_utils as utils +from ctsm.crop_calendars.grid_one_variable import grid_one_variable VAR_LIST_IN = ["GDD0X", "GDD8X", "GDD10X"] +GRIDDING_VAR_LIST = ["patches1d_ixy", "patches1d_jxy", "lat", "lon"] MISSING_FILL = -1 # Something impossible to ensure that you can mark it as a missing value, to be # bilinear-interpolated STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of @@ -171,7 +173,7 @@ def generate_gdd20_baseline(input_files, output_file, author): input_files.sort() # Import history files and ensure they have lat/lon dims - ds_in = import_ds(input_files, VAR_LIST_IN) + ds_in = import_ds(input_files, VAR_LIST_IN + GRIDDING_VAR_LIST) if not all(x in ds_in.dims for x in ["lat", "lon"]): raise RuntimeError("Input files must have lat and lon dimensions") @@ -188,8 +190,11 @@ def generate_gdd20_baseline(input_files, output_file, author): dummy_da = _add_time_axis(dummy_da) # Process all crops + data_var_dict = {} + for v in GRIDDING_VAR_LIST: + data_var_dict[v] = ds_in[v] ds_out = xr.Dataset( - data_vars=None, + data_vars=data_var_dict, attrs={ "author": author, "created": dt.datetime.now().astimezone().isoformat(), @@ -228,6 +233,10 @@ def generate_gdd20_baseline(input_files, output_file, author): ds_out[var_out] = this_da encoding_dict[var_out] = {"dtype": "float64"} + # Grid, if needed + if any(x not in this_da.dims for x in ["lat", "lon"]): + ds_out[var_out] = grid_one_variable(ds_out, var_out) + # Save ds_out.to_netcdf(output_file, format="NETCDF3_CLASSIC", encoding=encoding_dict) From 8b43fec5168d56df1b7c5ac6e22635d70024c3b0 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 11:28:32 -0600 Subject: [PATCH 046/206] generate_gdd20_baseline: Improve long names. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 71125e332d..89a38a0a08 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -219,7 +219,7 @@ def generate_gdd20_baseline(input_files, output_file, author): # this_da = ds_in[gddn].fillna(MISSING_FILL) this_da = ds_in[gddn] this_da = _add_time_axis(this_da) - long_name = gddn + long_name = gddn.replace("X", "20") print(f" {gddn}") # Add attributes From dcacbd4003585edf1a8608bc9d70232285f01202 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 11:47:38 -0600 Subject: [PATCH 047/206] Minor cleanup in UpdateAccVars_CropGDDs. --- src/biogeophys/TemperatureType.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index f8d5206a1e..5ae0b7b20c 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -1428,11 +1428,11 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d ((month > 9 .or. month < 4) .and. lat < 0._r8) ! Replace with read-in gdd20 accumulation season, if valid ! (If these aren't being read in or they're invalid, they'll be -1) - gdd20_season_start = crop_inst%gdd20_season_start_patch(p) - gdd20_season_end = crop_inst%gdd20_season_end_patch(p) + gdd20_season_start = gdd20_season_starts(p) + gdd20_season_end = gdd20_season_ends(p) if (gdd20_season_start >= 1 .and. gdd20_season_end >= 1) then in_accumulation_season = is_doy_in_interval( & - gdd20_season_starts(p), gdd20_season_ends(p), day) + gdd20_season_start, gdd20_season_end, day) end if if (month==1 .and. day==1 .and. secs==dtime) then From 2cf491da0127f1caeec5ae6d7152d637f2975599 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 11:53:44 -0600 Subject: [PATCH 048/206] Add outputs: GDD20_BASELINE, GDD20_SEASON_START/END. --- src/biogeochem/CropType.F90 | 28 +++++++++++++++++++++---- src/biogeophys/TemperatureType.F90 | 5 +++-- src/cpl/share_esmf/cropcalStreamMod.F90 | 18 ++++++++++------ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/biogeochem/CropType.F90 b/src/biogeochem/CropType.F90 index 04806f9349..fdfdaa05aa 100644 --- a/src/biogeochem/CropType.F90 +++ b/src/biogeochem/CropType.F90 @@ -53,8 +53,11 @@ module CropType integer , pointer :: rx_swindow_ends_thisyr_patch (:,:) ! all prescribed sowing window end dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: rx_cultivar_gdds_thisyr_patch (:,:) ! all cultivar GDD targets for this patch this year (ddays) [patch, mxsowings] real(r8), pointer :: gdd20_baseline_patch (:) ! GDD20 baseline for this patch (ddays) [patch] - integer , pointer :: gdd20_season_start_patch(:) ! gdd20 season start date for this patch (day of year) [patch] - integer , pointer :: gdd20_season_end_patch (:) ! gdd20 season end date for this patch (day of year) [patch] + + ! REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + real(r8), pointer :: gdd20_season_start_patch(:) ! gdd20 season start date for this patch (day of year) [patch] + real(r8), pointer :: gdd20_season_end_patch (:) ! gdd20 season end date for this patch (day of year) [patch] + real(r8), pointer :: sdates_thisyr_patch (:,:) ! all actual sowing dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_starts_thisyr_patch(:,:) ! all sowing window start dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_ends_thisyr_patch (:,:) ! all sowing window end dates for this patch this year (day of year) [patch, mxsowings] @@ -239,8 +242,11 @@ subroutine InitAllocate(this, bounds) allocate(this%rx_swindow_ends_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_swindow_ends_thisyr_patch (:,:) = -1 allocate(this%rx_cultivar_gdds_thisyr_patch(begp:endp,1:mxsowings)) ; this%rx_cultivar_gdds_thisyr_patch(:,:) = spval allocate(this%gdd20_baseline_patch(begp:endp)) ; this%gdd20_baseline_patch(:) = spval - allocate(this%gdd20_season_start_patch(begp:endp)); this%gdd20_season_start_patch(:) = -1 - allocate(this%gdd20_season_end_patch(begp:endp)) ; this%gdd20_season_end_patch (:) = -1 + + ! REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + allocate(this%gdd20_season_start_patch(begp:endp)); this%gdd20_season_start_patch(:) = spval + allocate(this%gdd20_season_end_patch(begp:endp)) ; this%gdd20_season_end_patch (:) = spval + allocate(this%sdates_thisyr_patch(begp:endp,1:mxsowings)) ; this%sdates_thisyr_patch(:,:) = spval allocate(this%swindow_starts_thisyr_patch(begp:endp,1:mxsowings)) ; this%swindow_starts_thisyr_patch(:,:) = spval allocate(this%swindow_ends_thisyr_patch (begp:endp,1:mxsowings)) ; this%swindow_ends_thisyr_patch (:,:) = spval @@ -364,6 +370,20 @@ subroutine InitHistory(this, bounds) avgflag='I', long_name='Reason for each crop harvest; should only be output annually', & ptr_patch=this%harvest_reason_thisyr_patch, default='inactive') + ! DEVELOPMENT ONLY; DELETE BEFORE MERGE + this%gdd20_baseline_patch(begp:endp) = spval + call hist_addfld1d (fname='GDD20_BASELINE', units='ddays', & + avgflag='A', long_name='Baseline mean growing-degree days accumulated during accumulation period (from input)', & + ptr_patch=this%gdd20_baseline_patch, default='inactive') + this%gdd20_season_start_patch(begp:endp) = spval + call hist_addfld1d (fname='GDD20_SEASON_START', units='day of year', & + avgflag='A', long_name='Start of the GDD20 accumulation season (from input)', & + ptr_patch=this%gdd20_season_start_patch, default='inactive') + this%gdd20_season_end_patch(begp:endp) = spval + call hist_addfld1d (fname='GDD20_SEASON_END', units='day of year', & + avgflag='A', long_name='End of the GDD20 accumulation season (from input)', & + ptr_patch=this%gdd20_season_end_patch, default='inactive') + end subroutine InitHistory subroutine InitCold(this, bounds) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 5ae0b7b20c..ae5703237e 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -1428,8 +1428,9 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d ((month > 9 .or. month < 4) .and. lat < 0._r8) ! Replace with read-in gdd20 accumulation season, if valid ! (If these aren't being read in or they're invalid, they'll be -1) - gdd20_season_start = gdd20_season_starts(p) - gdd20_season_end = gdd20_season_ends(p) + ! REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + gdd20_season_start = int(gdd20_season_starts(p)) + gdd20_season_end = int(gdd20_season_ends(p)) if (gdd20_season_start >= 1 .and. gdd20_season_end >= 1) then in_accumulation_season = is_doy_in_interval( & gdd20_season_start, gdd20_season_end, day) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index ce595c0d70..802b669905 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -766,8 +766,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) n = ivt - npcropmin + 1 ! vegetated pft ig = g_to_ig(patch%gridcell(p)) - gdd20_season_starts(p) = dataptr2d_gdd20_season_start(ig,n) - gdd20_season_ends(p) = dataptr2d_gdd20_season_end (ig,n) + + ! REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + gdd20_season_starts(p) = real(dataptr2d_gdd20_season_start(ig,n), r8) + gdd20_season_ends(p) = real(dataptr2d_gdd20_season_end (ig,n), r8) else write(iulog,'(a,i0)') 'cropcal_interp(), gdd20 seasons: Crop patch has ivt ',ivt call ESMF_Finalize(endflag=ESMF_END_ABORT) @@ -775,15 +777,18 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) end do ! Handle invalid gdd20 season values - if (any(gdd20_season_starts(begp:endp) < 1 .or. gdd20_season_ends(begp:endp) < 1)) then + ! gdd20_season_starts and gdd20_season_ends REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + if (any(gdd20_season_starts(begp:endp) < 1._r8 .or. gdd20_season_ends(begp:endp) < 1._r8)) then ! Fail if not allowing fallback to paramfile sowing windows - if ((.not. allow_invalid_gdd20_season_inputs) .and. any(gdd20_season_starts(begp:endp) < 1 .and. patch%wtgcell(begp:endp) > 0._r8 .and. patch%itype(begp:endp) >= npcropmin)) then + ! gdd20_season_starts REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + if ((.not. allow_invalid_gdd20_season_inputs) .and. any(gdd20_season_starts(begp:endp) < 1._r8 .and. patch%wtgcell(begp:endp) > 0._r8 .and. patch%itype(begp:endp) >= npcropmin)) then write(iulog, *) 'At least one crop in one gridcell has invalid gdd20 season start date(s). To ignore and fall back to paramfile sowing windows, set allow_invalid_gdd20_season_inputs to .true.' write(iulog, *) 'Affected crops:' do ivt = npcropmin, mxpft do fp = 1, num_pcropp p = filter_pcropp(fp) - if (ivt == patch%itype(p) .and. patch%wtgcell(p) > 0._r8 .and. gdd20_season_starts(p) < 1) then + ! gdd20_season_starts REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + if (ivt == patch%itype(p) .and. patch%wtgcell(p) > 0._r8 .and. gdd20_season_starts(p) < 1._r8) then write(iulog, *) ' ',pftname(ivt),' (',ivt,')' exit ! Stop looking for patches of this type end if @@ -792,7 +797,8 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) call ESMF_Finalize(endflag=ESMF_END_ABORT) ! Fail if a gdd20 season start date is given without an end date (or vice versa) - else if (any((gdd20_season_starts(begp:endp) >= 1 .and. gdd20_season_ends(begp:endp) < 1) .or. (gdd20_season_starts(begp:endp) < 1 .and. gdd20_season_ends(begp:endp) >= 1))) then + ! gdd20_season_starts and gdd20_season_ends REAL FOR DEVELOPMENT ONLY; REVERT TO INTEGER BEFORE MERGE + else if (any((gdd20_season_starts(begp:endp) >= 1._r8 .and. gdd20_season_ends(begp:endp) < 1._r8) .or. (gdd20_season_starts(begp:endp) < 1._r8 .and. gdd20_season_ends(begp:endp) >= 1._r8))) then write(iulog, *) 'Every gdd20 season start date must have a corresponding end date.' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if From 0083a08432859948707cf4e8e7b4937dc7f1d92c Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 12:03:15 -0600 Subject: [PATCH 049/206] rxcropmaturity.py is now parent rxcropmaturityshared.py. --- ...ropmaturity.py => rxcropmaturityshared.py} | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) rename cime_config/SystemTests/{rxcropmaturity.py => rxcropmaturityshared.py} (97%) diff --git a/cime_config/SystemTests/rxcropmaturity.py b/cime_config/SystemTests/rxcropmaturityshared.py similarity index 97% rename from cime_config/SystemTests/rxcropmaturity.py rename to cime_config/SystemTests/rxcropmaturityshared.py index 75fff8a0e0..f8e4b1c9bb 100644 --- a/cime_config/SystemTests/rxcropmaturity.py +++ b/cime_config/SystemTests/rxcropmaturityshared.py @@ -11,6 +11,10 @@ code do the interpolation. However, that wouldn't act on harvest dates (which are needed for generate_gdds.py). I could have Python interpolate those, but this would cause a potential inconsistency. + +Note that this is just a parent class. The actual tests are RXCROPMATURITY and +RXCROPMATURITY_SKIPRUN, the latter of which does everything except perform and +check the CTSM runs. """ import os @@ -25,7 +29,7 @@ logger = logging.getLogger(__name__) -class RXCROPMATURITY(SystemTestsCommon): +class RXCROPMATURITYSHARED(SystemTestsCommon): def __init__(self, case): # initialize an object interface to the SMS system test SystemTestsCommon.__init__(self, case) @@ -84,7 +88,7 @@ def __init__(self, case): # Which conda environment should we use? self._get_conda_env() - def run_phase(self): + def _run_phase(self, skip_run=False): # Modeling this after the SSP test, we create a clone to be the case whose outputs we don't # want to be saved as baseline. @@ -146,9 +150,10 @@ def run_phase(self): # "No history files expected, set suffix=None to avoid compare error" # We *do* expect history files here, but anyway. This works. self._skip_pnl = False - self.run_indv(suffix=None, st_archive=True) - self._run_generate_gdds(case_gddgen) + if not skip_run: + self.run_indv(suffix=None, st_archive=True) + self._run_generate_gdds(case_gddgen) # ------------------------------------------------------------------- # (3) Set up and perform Prescribed Calendars run @@ -168,13 +173,15 @@ def run_phase(self): ] ) - self.run_indv() + if not skip_run: + self.run_indv() # ------------------------------------------------------------------- # (4) Check Prescribed Calendars run # ------------------------------------------------------------------- logger.info("RXCROPMATURITY log: output check: Prescribed Calendars") - self._run_check_rxboth_run() + if not skip_run: + self._run_check_rxboth_run() # Get sowing and harvest dates for this resolution. def _get_rx_dates(self): From f728f9676e5103298dd255fcec2a6796d22e0144 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 13:44:07 -0600 Subject: [PATCH 050/206] Added back RXCROPMATURITY. --- cime_config/SystemTests/rxcropmaturity.py | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 cime_config/SystemTests/rxcropmaturity.py diff --git a/cime_config/SystemTests/rxcropmaturity.py b/cime_config/SystemTests/rxcropmaturity.py new file mode 100644 index 0000000000..3eadccfeb3 --- /dev/null +++ b/cime_config/SystemTests/rxcropmaturity.py @@ -0,0 +1,5 @@ +from RXCROPMATURITYSHARED import RXCROPMATURITYSHARED + +class RXCROPMATURITY(RXCROPMATURITYSHARED): + def run_phase(self): + self._run_phase() \ No newline at end of file From e4b97a233cd722ab915a9b0be27f5ece509c8ca9 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 3 Jun 2024 13:44:51 -0600 Subject: [PATCH 051/206] Add RXCROPMATURITYSKIPRUN. --- cime_config/SystemTests/rxcropmaturityskiprun.py | 6 ++++++ cime_config/config_tests.xml | 10 ++++++++++ 2 files changed, 16 insertions(+) create mode 100644 cime_config/SystemTests/rxcropmaturityskiprun.py diff --git a/cime_config/SystemTests/rxcropmaturityskiprun.py b/cime_config/SystemTests/rxcropmaturityskiprun.py new file mode 100644 index 0000000000..d52742f95a --- /dev/null +++ b/cime_config/SystemTests/rxcropmaturityskiprun.py @@ -0,0 +1,6 @@ +print("pre-import") +from RXCROPMATURITYSHARED import RXCROPMATURITYSHARED + +class RXCROPMATURITYSKIPRUN(RXCROPMATURITYSHARED): + def run_phase(self): + self._run_phase(skip_run=True) \ No newline at end of file diff --git a/cime_config/config_tests.xml b/cime_config/config_tests.xml index c0b6afed9d..98434da10e 100644 --- a/cime_config/config_tests.xml +++ b/cime_config/config_tests.xml @@ -133,6 +133,16 @@ This defines various CTSM-specific system tests $STOP_N + + As RXCROPMATURITY but don't actually run or postprocess; just setup + 1 + FALSE + FALSE + never + $STOP_OPTION + $STOP_N + + lnd/clm2/surfdata_esmf/ctsm5.2.0/landuse.timeseries_1x1_smallvilleIA_SSP2-4.5_1850-1855_78pfts_c240221.nc + + + diff --git a/bld/namelist_files/namelist_defaults_overall.xml b/bld/namelist_files/namelist_defaults_overall.xml index 479b2a02b7..5b7ae1bdd9 100644 --- a/bld/namelist_files/namelist_defaults_overall.xml +++ b/bld/namelist_files/namelist_defaults_overall.xml @@ -62,6 +62,7 @@ determine default values for namelists. 1x1_urbanc_alpha 1x1_numaIA 1x1_smallvilleIA +1x1_cidadinhoBR 2000 @@ -110,6 +111,7 @@ determine default values for namelists. test navy test +test gx1v7 diff --git a/bld/unit_testers/build-namelist_test.pl b/bld/unit_testers/build-namelist_test.pl index b0ddb1e448..5c8aa3468d 100755 --- a/bld/unit_testers/build-namelist_test.pl +++ b/bld/unit_testers/build-namelist_test.pl @@ -1590,21 +1590,23 @@ sub cat_and_create_namelistinfile { print "==================================================\n"; # Check for crop resolutions -my $crop1850_res = "1x1_smallvilleIA"; -$options = "-bgc bgc -crop -res $crop1850_res -use_case 1850_control -envxml_dir ."; -&make_env_run(); -eval{ system( "$bldnml $options > $tempfile 2>&1 " ); }; -is( $@, '', "$options" ); -$cfiles->checkfilesexist( "$options", $mode ); -$cfiles->shownmldiff( "default", "standard" ); -if ( defined($opts{'compare'}) ) { - $cfiles->doNOTdodiffonfile( "$tempfile", "$options", $mode ); - $cfiles->comparefiles( "$options", $mode, $opts{'compare'} ); -} -if ( defined($opts{'generate'}) ) { - $cfiles->copyfiles( "$options", $mode ); +my @crop1850_res = ( "1x1_smallvilleIA", "1x1_cidadinhoBR" ); +foreach my $res ( @crop1850_res ) { + $options = "-bgc bgc -crop -res $res -use_case 1850_control -envxml_dir ."; + &make_env_run(); + eval{ system( "$bldnml $options > $tempfile 2>&1 " ); }; + is( $@, '', "$options" ); + $cfiles->checkfilesexist( "$options", $mode ); + $cfiles->shownmldiff( "default", "standard" ); + if ( defined($opts{'compare'}) ) { + $cfiles->doNOTdodiffonfile( "$tempfile", "$options", $mode ); + $cfiles->comparefiles( "$options", $mode, $opts{'compare'} ); + } + if ( defined($opts{'generate'}) ) { + $cfiles->copyfiles( "$options", $mode ); + } + &cleanup(); } -&cleanup(); my @crop_res = ( "1x1_numaIA", "4x5", "10x15", "0.9x1.25", "1.9x2.5", "ne3np4.pg3", "ne30np4", "ne30np4.pg3", "C96", "mpasa120" ); foreach my $res ( @crop_res ) { diff --git a/tools/mksurfdata_esmf/Makefile b/tools/mksurfdata_esmf/Makefile index d8bacdc5dd..c344843d06 100644 --- a/tools/mksurfdata_esmf/Makefile +++ b/tools/mksurfdata_esmf/Makefile @@ -54,7 +54,10 @@ SUBSETDATA_POINT_URBAN = $(SUBSETDATA_POINT) --include-nonveg # Subset data sites... SUBSETDATA_1X1_BRAZIL := --lat -7 --lon -55 --site 1x1_brazil SUBSETDATA_1X1_NUMAIA := --lat 40.6878 --lon 267.0228 --site 1x1_numaIA -SUBSETDATA_1X1_SMALL := --lat 40.6878 --lon 267.0228 --site 1x1_smallvilleIA \ +SUBSETDATA_1X1_SMALL_IA := --lat 40.6878 --lon 267.0228 --site 1x1_smallvilleIA \ + --dompft 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 \ + --pctpft 6.5 1.5 1.6 1.7 1.8 1.9 1.5 1.6 1.7 1.8 1.9 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 +SUBSETDATA_1X1_SMALL_BR := --lat -12.9952 --lon 305.3233 --site 1x1_cidadinhoBR \ --dompft 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 \ --pctpft 6.5 1.5 1.6 1.7 1.8 1.9 1.5 1.6 1.7 1.8 1.9 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 # NOTE: The 1850 smallvilleIA site is constructed to start with 100% natural vegetation, so we can test transition to crops @@ -112,6 +115,7 @@ all-subset : \ 1x1-smallville-present \ 1x1-smallville-1850 \ 1x1-smallville-transient \ + 1x1-cidadinho-present \ urban DEBUG: @@ -239,7 +243,7 @@ crop-global-hist-ne30 : FORCE $(SUBSETDATA_POINT_ALLLU) --create-surface $(SUBSETDATA_1X1_NUMAIA) 1x1-smallville-present : FORCE - $(SUBSETDATA_POINT) --create-surface $(SUBSETDATA_1X1_SMALL) + $(SUBSETDATA_POINT) --create-surface $(SUBSETDATA_1X1_SMALL_IA) # Note that the smallville 1850 dataset is entirely natural vegetation. This # facilitates testing a transient case that starts with no crop, and then later @@ -254,6 +258,9 @@ crop-global-hist-ne30 : FORCE $(SUBSETDATA_POINT) --create-landuse $(SUBSETDATA_1X1_SMALLTRANSIENT) ../modify_input_files/modify_smallville.sh +1x1-cidadinho-present : FORCE + $(SUBSETDATA_POINT) --create-surface $(SUBSETDATA_1X1_SMALL_BR) + # # Crop with future scenarios # From 46307bca0a850876c67a3041bf786a76441ca853 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 14 Jun 2024 14:19:01 -0600 Subject: [PATCH 096/206] generate_gdds: Don't fail if making figures but GDDHARV alll NaN. --- .../crop_calendars/generate_gdds_functions.py | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdds_functions.py b/python/ctsm/crop_calendars/generate_gdds_functions.py index 8af2fdc049..38c5d44384 100644 --- a/python/ctsm/crop_calendars/generate_gdds_functions.py +++ b/python/ctsm/crop_calendars/generate_gdds_functions.py @@ -575,6 +575,7 @@ def import_and_process_1yr( this_crop_gddaccum_da = this_crop_ds[clm_gdd_var] if save_figs: this_crop_gddharv_da = this_crop_ds["GDDHARV"] + check_gddharv = True if not this_crop_gddaccum_da.size: continue log(logger, f" {vegtype_str}...") @@ -625,11 +626,18 @@ def import_and_process_1yr( + "NaN after extracting GDDs accumulated at harvest", ) if save_figs and np.any(np.isnan(gddharv_atharv_p)): - log( - logger, - f" ❗ {np.sum(np.isnan(gddharv_atharv_p))}/{len(gddharv_atharv_p)} " - + "NaN after extracting GDDHARV", - ) + if np.all(np.isnan(gddharv_atharv_p)): + log( + logger, + " ❗ All GDDHARV are NaN; should only affect figure" + ) + check_gddharv = False + else: + log( + logger, + f" ❗ {np.sum(np.isnan(gddharv_atharv_p))}/{len(gddharv_atharv_p)} " + + "NaN after extracting GDDHARV", + ) # Assign these to growing seasons based on whether gs crossed new year this_year_active_patch_indices = [ @@ -712,7 +720,7 @@ def import_and_process_1yr( ) else: error(logger, "Unexpected NaN for last season's GDD accumulation.") - if save_figs and np.any( + if save_figs and check_gddharv and np.any( np.isnan( gddharv_yp_list[var][year_index - 1, active_this_year_where_gs_lastyr_indices] ) @@ -1160,9 +1168,13 @@ def make_figures( else: error(logger, f"layout {layout} not recognized") - this_min = int(np.round(np.nanmin(gddharv_map_yx))) - this_max = int(np.round(np.nanmax(gddharv_map_yx))) - this_title = f"{run1_name} (range {this_min}–{this_max})" + gddharv_all_nan = np.all(np.isnan(gddharv_map_yx.values)) + if gddharv_all_nan: + this_title = f"{run1_name} (GDDHARV all NaN?)" + else: + this_min = int(np.round(np.nanmin(gddharv_map_yx))) + this_max = int(np.round(np.nanmax(gddharv_map_yx))) + this_title = f"{run1_name} (range {this_min}–{this_max})" make_gengdd_map( this_axis, gddharv_map_yx, @@ -1195,7 +1207,7 @@ def make_figures( ) # Difference - if layout == "3x2": + if not gddharv_all_nan and layout == "3x2": this_axis = fig.add_subplot(spec[2, 0], projection=ccrs.PlateCarree()) this_min = int(np.round(np.nanmin(gdd_map_yx))) this_max = int(np.round(np.nanmax(gdd_map_yx))) From faa536eb00c0afb70cacce239423a5af82ccaf5a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 14 Jun 2024 14:47:19 -0600 Subject: [PATCH 097/206] Revert "PlantCrop: GDD-gen runs get gddmaturity 1e36." GDD-generating runs should get the CLM default gddmaturity values. This reverts commit f2518b611408c12ae262c1b71b9b0a128196c934. --- src/biogeochem/CNPhenologyMod.F90 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 41d4107f3f..b346cb119f 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2694,10 +2694,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & ! set GDD target did_rx_gdds = .false. - if (generate_crop_gdds) then - ! Value mostly doesn't matter; it just needs to be large enough to avoid divide-by-zero errors. - gddmaturity(p) = 1.e36_r8 - else if (use_cropcal_rx_cultivar_gdds .and. crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) .ge. 0._r8) then + if (use_cropcal_rx_cultivar_gdds .and. crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) .ge. 0._r8) then gddmaturity(p) = crop_inst%rx_cultivar_gdds_thisyr_patch(p,sowing_count(p)) did_rx_gdds = .true. if (adapt_cropcal_rx_cultivar_gdds .and. crop_inst%gdd20_baseline_patch(p) > min_gdd20_baseline) then From 81177dd169e3c2f15f97a23216eb9793de4d8f3a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 14 Jun 2024 16:10:34 -0600 Subject: [PATCH 098/206] Revert all RXCROPMATURITY changes. --- cime_config/SystemTests/rxcropmaturity.py | 445 ++++++++++++++++- .../SystemTests/rxcropmaturityshared.py | 459 ------------------ .../SystemTests/rxcropmaturityskipbuild.py | 9 - .../SystemTests/rxcropmaturityskiprun.py | 9 - cime_config/config_tests.xml | 20 - 5 files changed, 442 insertions(+), 500 deletions(-) delete mode 100644 cime_config/SystemTests/rxcropmaturityshared.py delete mode 100644 cime_config/SystemTests/rxcropmaturityskipbuild.py delete mode 100644 cime_config/SystemTests/rxcropmaturityskiprun.py diff --git a/cime_config/SystemTests/rxcropmaturity.py b/cime_config/SystemTests/rxcropmaturity.py index 206476f051..75fff8a0e0 100644 --- a/cime_config/SystemTests/rxcropmaturity.py +++ b/cime_config/SystemTests/rxcropmaturity.py @@ -1,5 +1,444 @@ -from rxcropmaturityshared import RXCROPMATURITYSHARED +""" +CTSM-specific test that first performs a GDD-generating run, then calls +Python code to generate the maturity requirement file. This is then used +in a sowing+maturity forced run, which finally is tested to ensure +correct behavior. + +Currently only supports 0.9x1.25, 1.9x2.5, and 10x15 resolutions. Eventually, +this test should be able to generate its own files at whatever resolution it's +called at. Well, really, the ultimate goal would be to give CLM the files +at the original resolution (for GGCMI phase 3, 0.5°) and have the stream +code do the interpolation. However, that wouldn't act on harvest dates +(which are needed for generate_gdds.py). I could have Python interpolate +those, but this would cause a potential inconsistency. +""" + +import os +import re +import systemtest_utils as stu +import subprocess +from CIME.SystemTests.system_tests_common import SystemTestsCommon +from CIME.XML.standard_module_setup import * +from CIME.SystemTests.test_utils.user_nl_utils import append_to_user_nl_files +import shutil, glob + +logger = logging.getLogger(__name__) + + +class RXCROPMATURITY(SystemTestsCommon): + def __init__(self, case): + # initialize an object interface to the SMS system test + SystemTestsCommon.__init__(self, case) + + # Ensure run length is at least 5 years. Minimum to produce one complete growing season (i.e., two complete calendar years) actually 4 years, but that only gets you 1 season usable for GDD generation, so you can't check for season-to-season consistency. + stop_n = self._case.get_value("STOP_N") + stop_option = self._case.get_value("STOP_OPTION") + stop_n_orig = stop_n + stop_option_orig = stop_option + if "nsecond" in stop_option: + stop_n /= 60 + stop_option = "nminutes" + if "nminute" in stop_option: + stop_n /= 60 + stop_option = "nhours" + if "nhour" in stop_option: + stop_n /= 24 + stop_option = "ndays" + if "nday" in stop_option: + stop_n /= 365 + stop_option = "nyears" + if "nmonth" in stop_option: + stop_n /= 12 + stop_option = "nyears" + error_message = None + if "nyear" not in stop_option: + error_message = ( + f"STOP_OPTION ({stop_option_orig}) must be nsecond(s), nminute(s), " + + "nhour(s), nday(s), nmonth(s), or nyear(s)" + ) + elif stop_n < 5: + error_message = ( + "RXCROPMATURITY must be run for at least 5 years; you requested " + + f"{stop_n_orig} {stop_option_orig[1:]}" + ) + if error_message is not None: + logger.error(error_message) + raise RuntimeError(error_message) + + # Get the number of complete years that will be run + self._run_Nyears = int(stop_n) + + # Only allow RXCROPMATURITY to be called with test cropMonthOutput + casebaseid = self._case.get_value("CASEBASEID") + if casebaseid.split("-")[-1] != "cropMonthOutput": + error_message = ( + "Only call RXCROPMATURITY with test cropMonthOutput " + + "to avoid potentially huge sets of daily outputs." + ) + logger.error(error_message) + raise RuntimeError(error_message) + + # Get files with prescribed sowing and harvest dates + self._get_rx_dates() + + # Which conda environment should we use? + self._get_conda_env() -class RXCROPMATURITY(RXCROPMATURITYSHARED): def run_phase(self): - self._run_phase() \ No newline at end of file + # Modeling this after the SSP test, we create a clone to be the case whose outputs we don't + # want to be saved as baseline. + + # ------------------------------------------------------------------- + # (1) Set up GDD-generating run + # ------------------------------------------------------------------- + # Create clone to be GDD-Generating case + logger.info("RXCROPMATURITY log: cloning setup") + case_rxboth = self._case + caseroot = self._case.get_value("CASEROOT") + clone_path = f"{caseroot}.gddgen" + self._path_gddgen = clone_path + if os.path.exists(self._path_gddgen): + shutil.rmtree(self._path_gddgen) + logger.info("RXCROPMATURITY log: cloning") + case_gddgen = self._case.create_clone(clone_path, keepexe=True) + logger.info("RXCROPMATURITY log: done cloning") + + os.chdir(self._path_gddgen) + self._set_active_case(case_gddgen) + + # Set up stuff that applies to both tests + self._setup_all() + + # Add stuff specific to GDD-Generating run + logger.info("RXCROPMATURITY log: modify user_nl files: generate GDDs") + self._append_to_user_nl_clm( + [ + "generate_crop_gdds = .true.", + "use_mxmat = .false.", + " ", + "! (h2) Daily outputs for GDD generation and figure-making", + "hist_fincl3 = 'GDDACCUM', 'GDDHARV'", + "hist_nhtfrq(3) = -24", + "hist_mfilt(3) = 365", + "hist_type1d_pertape(3) = 'PFTS'", + "hist_dov2xy(3) = .false.", + ] + ) + + # If flanduse_timeseries is defined, we need to make a static version for this test. This + # should have every crop in most of the world. + self._get_flanduse_timeseries_in(case_gddgen) + if self._flanduse_timeseries_in is not None: + + # Download files from the server, if needed + case_gddgen.check_all_input_data() + + # Make custom version of surface file + logger.info("RXCROPMATURITY log: run fsurdat_modifier") + self._run_fsurdat_modifier() + + # ------------------------------------------------------------------- + # (2) Perform GDD-generating run and generate prescribed GDDs file + # ------------------------------------------------------------------- + logger.info("RXCROPMATURITY log: Start GDD-Generating run") + + # As per SSP test: + # "No history files expected, set suffix=None to avoid compare error" + # We *do* expect history files here, but anyway. This works. + self._skip_pnl = False + self.run_indv(suffix=None, st_archive=True) + + self._run_generate_gdds(case_gddgen) + + # ------------------------------------------------------------------- + # (3) Set up and perform Prescribed Calendars run + # ------------------------------------------------------------------- + os.chdir(caseroot) + self._set_active_case(case_rxboth) + + # Set up stuff that applies to both tests + self._setup_all() + + # Add stuff specific to Prescribed Calendars run + logger.info("RXCROPMATURITY log: modify user_nl files: Prescribed Calendars") + self._append_to_user_nl_clm( + [ + "generate_crop_gdds = .false.", + f"stream_fldFileName_cultivar_gdds = '{self._gdds_file}'", + ] + ) + + self.run_indv() + + # ------------------------------------------------------------------- + # (4) Check Prescribed Calendars run + # ------------------------------------------------------------------- + logger.info("RXCROPMATURITY log: output check: Prescribed Calendars") + self._run_check_rxboth_run() + + # Get sowing and harvest dates for this resolution. + def _get_rx_dates(self): + # Eventually, I want to remove these hard-coded resolutions so that this test can generate + # its own sowing and harvest date files at whatever resolution is requested. + lnd_grid = self._case.get_value("LND_GRID") + input_data_root = self._case.get_value("DIN_LOC_ROOT") + processed_crop_dates_dir = f"{input_data_root}/lnd/clm2/cropdata/calendars/processed" + if lnd_grid == "10x15": + self._sdatefile = os.path.join( + processed_crop_dates_dir, + "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f10_f10_mg37.2000-2000.20230330_165301.nc", + ) + self._hdatefile = os.path.join( + processed_crop_dates_dir, + "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f10_f10_mg37.2000-2000.20230330_165301.nc", + ) + elif lnd_grid == "1.9x2.5": + self._sdatefile = os.path.join( + processed_crop_dates_dir, + "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f19_g17.2000-2000.20230102_175625.nc", + ) + self._hdatefile = os.path.join( + processed_crop_dates_dir, + "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f19_g17.2000-2000.20230102_175625.nc", + ) + elif lnd_grid == "0.9x1.25": + self._sdatefile = os.path.join( + processed_crop_dates_dir, + "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f09_g17.2000-2000.20230520_134417.nc", + ) + self._hdatefile = os.path.join( + processed_crop_dates_dir, + "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f09_g17.2000-2000.20230520_134418.nc", + ) + else: + error_message = "ERROR: RXCROPMATURITY currently only supports 0.9x1.25, 1.9x2.5, and 10x15 resolutions" + logger.error(error_message) + raise RuntimeError(error_message) + + # Ensure files exist + error_message = None + if not os.path.exists(self._sdatefile): + error_message = f"ERROR: Sowing date file not found: {self._sdatefile}" + elif not os.path.exists(self._hdatefile): + error_message = f"ERROR: Harvest date file not found: {self._sdatefile}" + if error_message is not None: + logger.error(error_message) + raise RuntimeError(error_message) + + def _setup_all(self): + logger.info("RXCROPMATURITY log: _setup_all start") + + # Get some info + self._ctsm_root = self._case.get_value("COMP_ROOT_DIR_LND") + run_startdate = self._case.get_value("RUN_STARTDATE") + self._run_startyear = int(run_startdate.split("-")[0]) + + # Set sowing dates file (and other crop calendar settings) for all runs + logger.info("RXCROPMATURITY log: modify user_nl files: all tests") + self._modify_user_nl_allruns() + logger.info("RXCROPMATURITY log: _setup_all done") + + # Make a surface dataset that has every crop in every gridcell + def _run_fsurdat_modifier(self): + + # fsurdat should be defined. Where is it? + self._fsurdat_in = None + with open(self._lnd_in_path, "r") as lnd_in: + for line in lnd_in: + fsurdat_in = re.match(r" *fsurdat *= *'(.*)'", line) + if fsurdat_in: + self._fsurdat_in = fsurdat_in.group(1) + break + if self._fsurdat_in is None: + error_message = "fsurdat not defined" + logger.error(error_message) + raise RuntimeError(error_message) + + # Where we will save the fsurdat version for this test + path, ext = os.path.splitext(self._fsurdat_in) + dir_in, filename_in_noext = os.path.split(path) + self._fsurdat_out = os.path.join( + self._path_gddgen, f"{filename_in_noext}.all_crops_everywhere{ext}" + ) + + # Make fsurdat for this test, if not already done + if not os.path.exists(self._fsurdat_out): + tool_path = os.path.join( + self._ctsm_root, + "tools", + "modify_input_files", + "fsurdat_modifier", + ) + + # Create configuration file for fsurdat_modifier + self._cfg_path = os.path.join( + self._path_gddgen, + "modify_fsurdat_allcropseverywhere.cfg", + ) + self._create_config_file_evenlysplitcrop() + + command = f"python3 {tool_path} {self._cfg_path} " + stu.run_python_script( + self._get_caseroot(), + self._this_conda_env, + command, + tool_path, + ) + + # Modify namelist + logger.info("RXCROPMATURITY log: modify user_nl files: new fsurdat") + self._append_to_user_nl_clm( + [ + "fsurdat = '{}'".format(self._fsurdat_out), + "do_transient_crops = .false.", + "flanduse_timeseries = ''", + "use_init_interp = .true.", + ] + ) + + def _create_config_file_evenlysplitcrop(self): + """ + Open the new and the template .cfg files + Loop line by line through the template .cfg file + When string matches, replace that line's content + """ + cfg_template_path = os.path.join( + self._ctsm_root, "tools/modify_input_files/modify_fsurdat_template.cfg" + ) + + with open(self._cfg_path, "w", encoding="utf-8") as cfg_out: + # Copy template, replacing some lines + with open(cfg_template_path, "r", encoding="utf-8") as cfg_in: + for line in cfg_in: + if re.match(r" *evenly_split_cropland *=", line): + line = f"evenly_split_cropland = True" + elif re.match(r" *fsurdat_in *=", line): + line = f"fsurdat_in = {self._fsurdat_in}" + elif re.match(r" *fsurdat_out *=", line): + line = f"fsurdat_out = {self._fsurdat_out}" + elif re.match(r" *process_subgrid_section *=", line): + line = f"process_subgrid_section = True" + cfg_out.write(line) + + # Add new lines + cfg_out.write("\n") + cfg_out.write("[modify_fsurdat_subgrid_fractions]\n") + cfg_out.write("PCT_CROP = 100.0\n") + cfg_out.write("PCT_NATVEG = 0.0\n") + cfg_out.write("PCT_GLACIER = 0.0\n") + cfg_out.write("PCT_WETLAND = 0.0\n") + cfg_out.write("PCT_LAKE = 0.0\n") + cfg_out.write("PCT_OCEAN = 0.0\n") + cfg_out.write("PCT_URBAN = 0.0 0.0 0.0\n") + + def _run_check_rxboth_run(self): + + output_dir = os.path.join(self._get_caseroot(), "run") + first_usable_year = self._run_startyear + 2 + last_usable_year = self._run_startyear + self._run_Nyears - 2 + + tool_path = os.path.join( + self._ctsm_root, "python", "ctsm", "crop_calendars", "check_rxboth_run.py" + ) + command = ( + f"python3 {tool_path} " + + f"--directory {output_dir} " + + f"-y1 {first_usable_year} " + + f"-yN {last_usable_year} " + + f"--rx-sdates-file {self._sdatefile} " + + f"--rx-gdds-file {self._gdds_file} " + ) + stu.run_python_script( + self._get_caseroot(), + self._this_conda_env, + command, + tool_path, + ) + + def _modify_user_nl_allruns(self): + nl_additions = [ + "stream_meshfile_cropcal = '{}'".format(self._case.get_value("LND_DOMAIN_MESH")), + "stream_fldFileName_swindow_start = '{}'".format(self._sdatefile), + "stream_fldFileName_swindow_end = '{}'".format(self._sdatefile), + "stream_year_first_cropcal_swindows = 2000", + "stream_year_last_cropcal_swindows = 2000", + "model_year_align_cropcal_swindows = 2000", + " ", + "! (h1) Annual outputs on sowing or harvest axis", + "hist_fincl2 = 'GRAINC_TO_FOOD_PERHARV', 'GRAINC_TO_FOOD_ANN', 'SDATES', 'SDATES_PERHARV', 'SYEARS_PERHARV', 'HDATES', 'GDDHARV_PERHARV', 'GDDACCUM_PERHARV', 'HUI_PERHARV', 'SOWING_REASON_PERHARV', 'HARVEST_REASON_PERHARV'", + "hist_nhtfrq(2) = 17520", + "hist_mfilt(2) = 999", + "hist_type1d_pertape(2) = 'PFTS'", + "hist_dov2xy(2) = .false.", + ] + self._append_to_user_nl_clm(nl_additions) + + def _run_generate_gdds(self, case_gddgen): + self._generate_gdds_dir = os.path.join(self._path_gddgen, "generate_gdds_out") + os.makedirs(self._generate_gdds_dir) + + # Get arguments to generate_gdds.py + dout_sr = case_gddgen.get_value("DOUT_S_ROOT") + input_dir = os.path.join(dout_sr, "lnd", "hist") + first_season = self._run_startyear + 2 + last_season = self._run_startyear + self._run_Nyears - 2 + sdates_file = self._sdatefile + hdates_file = self._hdatefile + + # It'd be much nicer to call generate_gdds.main(), but I can't import generate_gdds. + tool_path = os.path.join( + self._ctsm_root, "python", "ctsm", "crop_calendars", "generate_gdds.py" + ) + command = " ".join( + [ + f"python3 {tool_path}", + f"--input-dir {input_dir}", + f"--first-season {first_season}", + f"--last-season {last_season}", + f"--sdates-file {sdates_file}", + f"--hdates-file {hdates_file}", + f"--output-dir generate_gdds_out", + f"--skip-crops miscanthus,irrigated_miscanthus", + ] + ) + stu.run_python_script( + self._get_caseroot(), + self._this_conda_env, + command, + tool_path, + ) + + # Where were the prescribed maturity requirements saved? + generated_gdd_files = glob.glob(os.path.join(self._generate_gdds_dir, "gdds_*.nc")) + if len(generated_gdd_files) != 1: + error_message = f"ERROR: Expected one matching prescribed maturity requirements file; found {len(generated_gdd_files)}: {generated_gdd_files}" + logger.error(error_message) + raise RuntimeError(error_message) + self._gdds_file = generated_gdd_files[0] + + def _get_conda_env(self): + conda_setup_commands = stu.cmds_to_setup_conda(self._get_caseroot()) + + # If npl conda environment is available, use that (It has dask, which + # enables chunking, which makes reading daily 1-degree netCDF files + # much more efficient. + if "npl " in os.popen(conda_setup_commands + "conda env list").read(): + self._this_conda_env = "npl" + else: + self._this_conda_env = "ctsm_pylib" + + def _append_to_user_nl_clm(self, additions): + caseroot = self._get_caseroot() + append_to_user_nl_files(caseroot=caseroot, component="clm", contents=additions) + + # Is flanduse_timeseries defined? If so, where is it? + def _get_flanduse_timeseries_in(self, case): + case.create_namelists(component="lnd") + self._lnd_in_path = os.path.join(self._path_gddgen, "CaseDocs", "lnd_in") + self._flanduse_timeseries_in = None + with open(self._lnd_in_path, "r") as lnd_in: + for line in lnd_in: + flanduse_timeseries_in = re.match(r" *flanduse_timeseries *= *'(.*)'", line) + if flanduse_timeseries_in: + self._flanduse_timeseries_in = flanduse_timeseries_in.group(1) + break diff --git a/cime_config/SystemTests/rxcropmaturityshared.py b/cime_config/SystemTests/rxcropmaturityshared.py deleted file mode 100644 index 8a944efdf9..0000000000 --- a/cime_config/SystemTests/rxcropmaturityshared.py +++ /dev/null @@ -1,459 +0,0 @@ -""" -CTSM-specific test that first performs a GDD-generating run, then calls -Python code to generate the maturity requirement file. This is then used -in a sowing+maturity forced run, which finally is tested to ensure -correct behavior. - -Currently only supports 0.9x1.25, 1.9x2.5, and 10x15 resolutions. Eventually, -this test should be able to generate its own files at whatever resolution it's -called at. Well, really, the ultimate goal would be to give CLM the files -at the original resolution (for GGCMI phase 3, 0.5°) and have the stream -code do the interpolation. However, that wouldn't act on harvest dates -(which are needed for generate_gdds.py). I could have Python interpolate -those, but this would cause a potential inconsistency. - -Note that this is just a parent class. The actual tests are RXCROPMATURITY and -RXCROPMATURITY_SKIPRUN, the latter of which does everything except perform and -check the CTSM runs. -""" - -import os -import re -import systemtest_utils as stu -import subprocess -from CIME.SystemTests.system_tests_common import SystemTestsCommon -from CIME.XML.standard_module_setup import * -from CIME.SystemTests.test_utils.user_nl_utils import append_to_user_nl_files -import shutil, glob - -logger = logging.getLogger(__name__) - - -class RXCROPMATURITYSHARED(SystemTestsCommon): - def __init__(self, case): - # initialize an object interface to the SMS system test - SystemTestsCommon.__init__(self, case) - - # Ensure run length is at least 5 years. Minimum to produce one complete growing season (i.e., two complete calendar years) actually 4 years, but that only gets you 1 season usable for GDD generation, so you can't check for season-to-season consistency. - stop_n = self._case.get_value("STOP_N") - stop_option = self._case.get_value("STOP_OPTION") - stop_n_orig = stop_n - stop_option_orig = stop_option - if "nsecond" in stop_option: - stop_n /= 60 - stop_option = "nminutes" - if "nminute" in stop_option: - stop_n /= 60 - stop_option = "nhours" - if "nhour" in stop_option: - stop_n /= 24 - stop_option = "ndays" - if "nday" in stop_option: - stop_n /= 365 - stop_option = "nyears" - if "nmonth" in stop_option: - stop_n /= 12 - stop_option = "nyears" - error_message = None - if "nyear" not in stop_option: - error_message = ( - f"STOP_OPTION ({stop_option_orig}) must be nsecond(s), nminute(s), " - + "nhour(s), nday(s), nmonth(s), or nyear(s)" - ) - elif stop_n < 5: - error_message = ( - "RXCROPMATURITY must be run for at least 5 years; you requested " - + f"{stop_n_orig} {stop_option_orig[1:]}" - ) - if error_message is not None: - logger.error(error_message) - raise RuntimeError(error_message) - - # Get the number of complete years that will be run - self._run_Nyears = int(stop_n) - - # Only allow RXCROPMATURITY to be called with test cropMonthOutput - casebaseid = self._case.get_value("CASEBASEID") - if casebaseid.split("-")[-1] != "cropMonthOutput": - error_message = ( - "Only call RXCROPMATURITY with test cropMonthOutput " - + "to avoid potentially huge sets of daily outputs." - ) - logger.error(error_message) - raise RuntimeError(error_message) - - # Get files with prescribed sowing and harvest dates - self._get_rx_dates() - - # Which conda environment should we use? - self._get_conda_env() - - def _run_phase(self, skip_run=False): - # Modeling this after the SSP test, we create a clone to be the case whose outputs we don't - # want to be saved as baseline. - - # ------------------------------------------------------------------- - # (1) Set up GDD-generating run - # ------------------------------------------------------------------- - # Create clone to be GDD-Generating case - logger.info("RXCROPMATURITY log: cloning setup") - case_rxboth = self._case - caseroot = self._case.get_value("CASEROOT") - clone_path = f"{caseroot}.gddgen" - self._path_gddgen = clone_path - if os.path.exists(self._path_gddgen): - shutil.rmtree(self._path_gddgen) - logger.info("RXCROPMATURITY log: cloning") - case_gddgen = self._case.create_clone(clone_path, keepexe=True) - logger.info("RXCROPMATURITY log: done cloning") - - os.chdir(self._path_gddgen) - self._set_active_case(case_gddgen) - - # Set up stuff that applies to both tests - self._setup_all() - - # Add stuff specific to GDD-Generating run - logger.info("RXCROPMATURITY log: modify user_nl files: generate GDDs") - self._append_to_user_nl_clm( - [ - "generate_crop_gdds = .true.", - "stream_fldFileName_cultivar_gdds = ''", - "use_mxmat = .false.", - " ", - "! (h2) Daily outputs for GDD generation and figure-making", - "hist_fincl3 = 'GDDACCUM', 'GDDHARV'", - "hist_nhtfrq(3) = -24", - "hist_mfilt(3) = 365", - "hist_type1d_pertape(3) = 'PFTS'", - "hist_dov2xy(3) = .false.", - ] - ) - - # If flanduse_timeseries is defined, we need to make a static version for this test. This - # should have every crop in most of the world. - self._get_flanduse_timeseries_in(case_gddgen) - if self._flanduse_timeseries_in is not None: - - # Download files from the server, if needed - case_gddgen.check_all_input_data() - - # Copy needed file from original to gddgen directory - shutil.copyfile( - os.path.join(caseroot, ".env_mach_specific.sh"), - os.path.join(self._path_gddgen, ".env_mach_specific.sh"), - ) - - # Make custom version of surface file - logger.info("RXCROPMATURITY log: run fsurdat_modifier") - self._run_fsurdat_modifier() - - # ------------------------------------------------------------------- - # (2) Perform GDD-generating run and generate prescribed GDDs file - # ------------------------------------------------------------------- - logger.info("RXCROPMATURITY log: Start GDD-Generating run") - - # As per SSP test: - # "No history files expected, set suffix=None to avoid compare error" - # We *do* expect history files here, but anyway. This works. - self._skip_pnl = False - - if not skip_run: - self.run_indv(suffix=None, st_archive=True) - self._run_generate_gdds(case_gddgen) - - # ------------------------------------------------------------------- - # (3) Set up and perform Prescribed Calendars run - # ------------------------------------------------------------------- - os.chdir(caseroot) - self._set_active_case(case_rxboth) - - # Set up stuff that applies to both tests - self._setup_all() - - # Add stuff specific to Prescribed Calendars run - logger.info("RXCROPMATURITY log: modify user_nl files: Prescribed Calendars") - self._append_to_user_nl_clm( - [ - "generate_crop_gdds = .false.", - f"stream_fldFileName_cultivar_gdds = '{self._gdds_file}'", - ] - ) - - if not skip_run: - self.run_indv() - - # ------------------------------------------------------------------- - # (4) Check Prescribed Calendars run - # ------------------------------------------------------------------- - logger.info("RXCROPMATURITY log: output check: Prescribed Calendars") - if not skip_run: - self._run_check_rxboth_run() - - # Get sowing and harvest dates for this resolution. - def _get_rx_dates(self): - # Eventually, I want to remove these hard-coded resolutions so that this test can generate - # its own sowing and harvest date files at whatever resolution is requested. - lnd_grid = self._case.get_value("LND_GRID") - input_data_root = self._case.get_value("DIN_LOC_ROOT") - processed_crop_dates_dir = f"{input_data_root}/lnd/clm2/cropdata/calendars/processed" - if lnd_grid == "10x15": - self._sdatefile = os.path.join( - processed_crop_dates_dir, - "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f10_f10_mg37.2000-2000.20230330_165301.nc", - ) - self._hdatefile = os.path.join( - processed_crop_dates_dir, - "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f10_f10_mg37.2000-2000.20230330_165301.nc", - ) - elif lnd_grid == "1.9x2.5": - self._sdatefile = os.path.join( - processed_crop_dates_dir, - "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f19_g17.2000-2000.20230102_175625.nc", - ) - self._hdatefile = os.path.join( - processed_crop_dates_dir, - "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f19_g17.2000-2000.20230102_175625.nc", - ) - elif lnd_grid == "0.9x1.25": - self._sdatefile = os.path.join( - processed_crop_dates_dir, - "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f09_g17.2000-2000.20230520_134417.nc", - ) - self._hdatefile = os.path.join( - processed_crop_dates_dir, - "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-f09_g17.2000-2000.20230520_134418.nc", - ) - else: - error_message = "ERROR: RXCROPMATURITY currently only supports 0.9x1.25, 1.9x2.5, and 10x15 resolutions" - logger.error(error_message) - raise RuntimeError(error_message) - - # Ensure files exist - error_message = None - if not os.path.exists(self._sdatefile): - error_message = f"ERROR: Sowing date file not found: {self._sdatefile}" - elif not os.path.exists(self._hdatefile): - error_message = f"ERROR: Harvest date file not found: {self._sdatefile}" - if error_message is not None: - logger.error(error_message) - raise RuntimeError(error_message) - - def _setup_all(self): - logger.info("RXCROPMATURITY log: _setup_all start") - - # Get some info - self._ctsm_root = self._case.get_value("COMP_ROOT_DIR_LND") - run_startdate = self._case.get_value("RUN_STARTDATE") - self._run_startyear = int(run_startdate.split("-")[0]) - - # Set sowing dates file (and other crop calendar settings) for all runs - logger.info("RXCROPMATURITY log: modify user_nl files: all tests") - self._modify_user_nl_allruns() - logger.info("RXCROPMATURITY log: _setup_all done") - - # Make a surface dataset that has every crop in every gridcell - def _run_fsurdat_modifier(self): - - # fsurdat should be defined. Where is it? - self._fsurdat_in = None - with open(self._lnd_in_path, "r") as lnd_in: - for line in lnd_in: - fsurdat_in = re.match(r" *fsurdat *= *'(.*)'", line) - if fsurdat_in: - self._fsurdat_in = fsurdat_in.group(1) - break - if self._fsurdat_in is None: - error_message = "fsurdat not defined" - logger.error(error_message) - raise RuntimeError(error_message) - - # Where we will save the fsurdat version for this test - path, ext = os.path.splitext(self._fsurdat_in) - dir_in, filename_in_noext = os.path.split(path) - self._fsurdat_out = os.path.join( - self._path_gddgen, f"{filename_in_noext}.all_crops_everywhere{ext}" - ) - - # Make fsurdat for this test, if not already done - if not os.path.exists(self._fsurdat_out): - tool_path = os.path.join( - self._ctsm_root, - "tools", - "modify_input_files", - "fsurdat_modifier", - ) - - # Create configuration file for fsurdat_modifier - self._cfg_path = os.path.join( - self._path_gddgen, - "modify_fsurdat_allcropseverywhere.cfg", - ) - self._create_config_file_evenlysplitcrop() - - command = f"python3 {tool_path} {self._cfg_path} " - stu.run_python_script( - self._get_caseroot(), - self._this_conda_env, - command, - tool_path, - ) - - # Modify namelist - logger.info("RXCROPMATURITY log: modify user_nl files: new fsurdat") - self._append_to_user_nl_clm( - [ - "fsurdat = '{}'".format(self._fsurdat_out), - "do_transient_crops = .false.", - "flanduse_timeseries = ''", - "use_init_interp = .true.", - ] - ) - - def _create_config_file_evenlysplitcrop(self): - """ - Open the new and the template .cfg files - Loop line by line through the template .cfg file - When string matches, replace that line's content - """ - cfg_template_path = os.path.join( - self._ctsm_root, "tools/modify_input_files/modify_fsurdat_template.cfg" - ) - - with open(self._cfg_path, "w", encoding="utf-8") as cfg_out: - # Copy template, replacing some lines - with open(cfg_template_path, "r", encoding="utf-8") as cfg_in: - for line in cfg_in: - if re.match(r" *evenly_split_cropland *=", line): - line = f"evenly_split_cropland = True" - elif re.match(r" *fsurdat_in *=", line): - line = f"fsurdat_in = {self._fsurdat_in}" - elif re.match(r" *fsurdat_out *=", line): - line = f"fsurdat_out = {self._fsurdat_out}" - elif re.match(r" *process_subgrid_section *=", line): - line = f"process_subgrid_section = True" - cfg_out.write(line) - - # Add new lines - cfg_out.write("\n") - cfg_out.write("[modify_fsurdat_subgrid_fractions]\n") - cfg_out.write("PCT_CROP = 100.0\n") - cfg_out.write("PCT_NATVEG = 0.0\n") - cfg_out.write("PCT_GLACIER = 0.0\n") - cfg_out.write("PCT_WETLAND = 0.0\n") - cfg_out.write("PCT_LAKE = 0.0\n") - cfg_out.write("PCT_OCEAN = 0.0\n") - cfg_out.write("PCT_URBAN = 0.0 0.0 0.0\n") - - def _run_check_rxboth_run(self): - - output_dir = os.path.join(self._get_caseroot(), "run") - first_usable_year = self._run_startyear + 2 - last_usable_year = self._run_startyear + self._run_Nyears - 2 - - tool_path = os.path.join( - self._ctsm_root, "python", "ctsm", "crop_calendars", "check_rxboth_run.py" - ) - command = ( - f"python3 {tool_path} " - + f"--directory {output_dir} " - + f"-y1 {first_usable_year} " - + f"-yN {last_usable_year} " - + f"--rx-sdates-file {self._sdatefile} " - + f"--rx-gdds-file {self._gdds_file} " - ) - stu.run_python_script( - self._get_caseroot(), - self._this_conda_env, - command, - tool_path, - ) - - def _modify_user_nl_allruns(self): - nl_additions = [ - "cropcals_rx = .true.", - "stream_meshfile_cropcal = '{}'".format(self._case.get_value("LND_DOMAIN_MESH")), - "stream_fldFileName_swindow_start = '{}'".format(self._sdatefile), - "stream_fldFileName_swindow_end = '{}'".format(self._sdatefile), - "stream_year_first_cropcal_swindows = 2000", - "stream_year_last_cropcal_swindows = 2000", - "model_year_align_cropcal_swindows = 2000", - " ", - "! (h1) Annual outputs on sowing or harvest axis", - "hist_fincl2 = 'GRAINC_TO_FOOD_PERHARV', 'GRAINC_TO_FOOD_ANN', 'SDATES', 'SDATES_PERHARV', 'SYEARS_PERHARV', 'HDATES', 'GDDHARV_PERHARV', 'GDDACCUM_PERHARV', 'HUI_PERHARV', 'SOWING_REASON_PERHARV', 'HARVEST_REASON_PERHARV'", - "hist_nhtfrq(2) = 17520", - "hist_mfilt(2) = 999", - "hist_type1d_pertape(2) = 'PFTS'", - "hist_dov2xy(2) = .false.", - ] - self._append_to_user_nl_clm(nl_additions) - - def _run_generate_gdds(self, case_gddgen): - self._generate_gdds_dir = os.path.join(self._path_gddgen, "generate_gdds_out") - os.makedirs(self._generate_gdds_dir) - - # Get arguments to generate_gdds.py - dout_sr = case_gddgen.get_value("DOUT_S_ROOT") - input_dir = os.path.join(dout_sr, "lnd", "hist") - first_season = self._run_startyear + 2 - last_season = self._run_startyear + self._run_Nyears - 2 - sdates_file = self._sdatefile - hdates_file = self._hdatefile - - # It'd be much nicer to call generate_gdds.main(), but I can't import generate_gdds. - tool_path = os.path.join( - self._ctsm_root, "python", "ctsm", "crop_calendars", "generate_gdds.py" - ) - command = " ".join( - [ - f"python3 {tool_path}", - f"--input-dir {input_dir}", - f"--first-season {first_season}", - f"--last-season {last_season}", - f"--sdates-file {sdates_file}", - f"--hdates-file {hdates_file}", - f"--output-dir generate_gdds_out", - f"--skip-crops miscanthus,irrigated_miscanthus", - ] - ) - stu.run_python_script( - self._get_caseroot(), - self._this_conda_env, - command, - tool_path, - ) - - # Where were the prescribed maturity requirements saved? - generated_gdd_files = glob.glob(os.path.join(self._generate_gdds_dir, "gdds_*.nc")) - if len(generated_gdd_files) != 1: - error_message = f"ERROR: Expected one matching prescribed maturity requirements file; found {len(generated_gdd_files)}: {generated_gdd_files}" - logger.error(error_message) - raise RuntimeError(error_message) - self._gdds_file = generated_gdd_files[0] - - def _get_conda_env(self): - conda_setup_commands = stu.cmds_to_setup_conda(self._get_caseroot()) - - # If npl conda environment is available, use that (It has dask, which - # enables chunking, which makes reading daily 1-degree netCDF files - # much more efficient. - if "npl " in os.popen(conda_setup_commands + "conda env list").read(): - self._this_conda_env = "npl" - else: - self._this_conda_env = "ctsm_pylib" - - def _append_to_user_nl_clm(self, additions): - caseroot = self._get_caseroot() - append_to_user_nl_files(caseroot=caseroot, component="clm", contents=additions) - - # Is flanduse_timeseries defined? If so, where is it? - def _get_flanduse_timeseries_in(self, case): - case.create_namelists(component="lnd") - self._lnd_in_path = os.path.join(self._path_gddgen, "CaseDocs", "lnd_in") - self._flanduse_timeseries_in = None - with open(self._lnd_in_path, "r") as lnd_in: - for line in lnd_in: - flanduse_timeseries_in = re.match(r" *flanduse_timeseries *= *'(.*)'", line) - if flanduse_timeseries_in: - self._flanduse_timeseries_in = flanduse_timeseries_in.group(1) - break diff --git a/cime_config/SystemTests/rxcropmaturityskipbuild.py b/cime_config/SystemTests/rxcropmaturityskipbuild.py deleted file mode 100644 index 4c3a69fb47..0000000000 --- a/cime_config/SystemTests/rxcropmaturityskipbuild.py +++ /dev/null @@ -1,9 +0,0 @@ -from rxcropmaturityshared import RXCROPMATURITYSHARED - -class RXCROPMATURITYSKIPBUILD(RXCROPMATURITYSHARED): - def build_indv(self, sharedlib_only=False, model_only=False): - self._case.set_value("BUILD_COMPLETE", "TRUE") - - - def run_phase(self): - self._run_phase(skip_run=False) \ No newline at end of file diff --git a/cime_config/SystemTests/rxcropmaturityskiprun.py b/cime_config/SystemTests/rxcropmaturityskiprun.py deleted file mode 100644 index 7fd217c4ff..0000000000 --- a/cime_config/SystemTests/rxcropmaturityskiprun.py +++ /dev/null @@ -1,9 +0,0 @@ -from rxcropmaturityshared import RXCROPMATURITYSHARED - -class RXCROPMATURITYSKIPRUN(RXCROPMATURITYSHARED): - def build_indv(self, sharedlib_only=False, model_only=False): - self._case.set_value("BUILD_COMPLETE", "TRUE") - - - def run_phase(self): - self._run_phase(skip_run=True) \ No newline at end of file diff --git a/cime_config/config_tests.xml b/cime_config/config_tests.xml index 115f4c0e89..c0b6afed9d 100644 --- a/cime_config/config_tests.xml +++ b/cime_config/config_tests.xml @@ -133,26 +133,6 @@ This defines various CTSM-specific system tests $STOP_N - - As RXCROPMATURITY but don't actually run or postprocess; just setup - 1 - FALSE - FALSE - never - $STOP_OPTION - $STOP_N - - - - As RXCROPMATURITY but don't actually build; just setup and then start "run." Will fail when run start is requested, but should successfully test preprocessing. - 1 - FALSE - FALSE - never - $STOP_OPTION - $STOP_N - - From d13508bef6c0b31e8569daa758c44c379f9309b8 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 25 Jun 2024 12:05:45 -0600 Subject: [PATCH 121/206] run_sys_tests: Check Python env for RXCROPMATURITY. --- python/ctsm/run_sys_tests.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/python/ctsm/run_sys_tests.py b/python/ctsm/run_sys_tests.py index de93081504..9961fd325d 100644 --- a/python/ctsm/run_sys_tests.py +++ b/python/ctsm/run_sys_tests.py @@ -736,13 +736,29 @@ def _check_py_env(test_attributes): # whether import is possible. # pylint: disable=import-error disable - # Check requirements for FSURDATMODIFYCTSM, if needed - if any("FSURDATMODIFYCTSM" in t for t in test_attributes): + # Check requirements for using modify_fsurdat, if needed + modify_fsurdat_users = ["FSURDATMODIFYCTSM", "RXCROPMATURITY"] + if any(any(u in t for u in modify_fsurdat_users) for t in test_attributes): try: import ctsm.modify_input_files.modify_fsurdat except ModuleNotFoundError as err: raise ModuleNotFoundError("modify_fsurdat" + err_msg) from err + # Check requirements for RXCROPMATURITY, if needed + if any("RXCROPMATURITY" in t for t in test_attributes): + try: + import ctsm.crop_calendars.check_rxboth_run + except ModuleNotFoundError as err: + raise ModuleNotFoundError("check_rxboth_run" + err_msg) from err + try: + import ctsm.crop_calendars.generate_gdds + except ModuleNotFoundError as err: + raise ModuleNotFoundError("generate_gdds" + err_msg) from err + try: + import ctsm.crop_calendars.interpolate_gdds + except ModuleNotFoundError as err: + raise ModuleNotFoundError("interpolate_gdds" + err_msg) from err + # Check that list for any testmods that use modify_fates_paramfile.py testmods_to_check = ["clm-FatesColdTwoStream", "clm-FatesColdTwoStreamNoCompFixedBioGeo"] testmods = _get_testmod_list(test_attributes) From a5da93ec5d016aabc11452870d8c3fab1677ef25 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 26 Jun 2024 12:31:05 -0600 Subject: [PATCH 122/206] generate_gdd20_baseline: Add optional -y1 and -yN args. --- .../crop_calendars/generate_gdd20_baseline.py | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 89a38a0a08..4357bd6dd1 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -70,6 +70,24 @@ def _parse_args(): help="Overwrite existing output file, if any", action="store_true", ) + parser.add_argument( + "-y1", + "--first-year", + help=( + "First calendar year to include" + ), + type=int, + required=False, + ) + parser.add_argument( + "-yN", + "--last-year", + help=( + "Last calendar year to include" + ), + type=int, + required=False, + ) # Get arguments args = parser.parse_args(sys.argv[1:]) @@ -78,7 +96,19 @@ def _parse_args(): if os.path.exists(args.output_file) and not args.overwrite: raise FileExistsError("Output file exists but --overwrite is not specified") - return args + # Process time slice + # Assumes CESM behavior where data for e.g. 1987 is saved as 1988-01-01 + if args.first_year is not None: + date_1 = f"{args.first_year+1}-01-01" + else: + date_1 = "0000-01-01" + if args.last_year is not None: + date_n = f"{args.last_year+1}-01-01" + else: + date_n = "9999-12-31" + time_slice = slice(date_1, date_n) + + return args, time_slice def _get_cft_list(crop_list): @@ -161,7 +191,7 @@ def _add_time_axis(da_in): return da_out -def generate_gdd20_baseline(input_files, output_file, author): +def generate_gdd20_baseline(input_files, output_file, author, time_slice): """ Generate stream_fldFileName_gdd20_baseline file from CTSM outputs """ @@ -173,7 +203,7 @@ def generate_gdd20_baseline(input_files, output_file, author): input_files.sort() # Import history files and ensure they have lat/lon dims - ds_in = import_ds(input_files, VAR_LIST_IN + GRIDDING_VAR_LIST) + ds_in = import_ds(input_files, VAR_LIST_IN + GRIDDING_VAR_LIST, time_slice=time_slice) if not all(x in ds_in.dims for x in ["lat", "lon"]): raise RuntimeError("Input files must have lat and lon dimensions") @@ -247,9 +277,10 @@ def main(): """ main() function for calling generate_gdd20_baseline.py from command line. """ - args = _parse_args() + args, time_slice = _parse_args() generate_gdd20_baseline( args.input_files, args.output_file, args.author, + time_slice ) From 23c8743ae67e8b550ee3ba89c10f2a2d17a51c3b Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 26 Jun 2024 14:27:22 -0600 Subject: [PATCH 123/206] generate_gdds: Map sdate in/out diffs. --- .../crop_calendars/generate_gdd20_baseline.py | 5 +++- python/ctsm/crop_calendars/generate_gdds.py | 1 + .../crop_calendars/generate_gdds_functions.py | 28 +++++++++++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 4357bd6dd1..476717b887 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -97,7 +97,10 @@ def _parse_args(): raise FileExistsError("Output file exists but --overwrite is not specified") # Process time slice - # Assumes CESM behavior where data for e.g. 1987 is saved as 1988-01-01 + # Assumes CESM behavior where data for e.g. 1987 is saved as 1988-01-01. + # It would be more robust, accounting for upcoming behavior (where timestamp for a year is the + # middle of that year), to do slice("YEAR1-01-03", "YEARN-01-02"), but that's not compatible + # with ctsm_pylib as of the version using python 3.7.9. See safer_timeslice() in cropcal_utils. if args.first_year is not None: date_1 = f"{args.first_year+1}-01-01" else: diff --git a/python/ctsm/crop_calendars/generate_gdds.py b/python/ctsm/crop_calendars/generate_gdds.py index 156ebfb20e..49226e6f75 100644 --- a/python/ctsm/crop_calendars/generate_gdds.py +++ b/python/ctsm/crop_calendars/generate_gdds.py @@ -178,6 +178,7 @@ def main( mxmats, cc.get_gs_len_da, skip_crops, + outdir_figs, logger, ) diff --git a/python/ctsm/crop_calendars/generate_gdds_functions.py b/python/ctsm/crop_calendars/generate_gdds_functions.py index 38c5d44384..2658e1de87 100644 --- a/python/ctsm/crop_calendars/generate_gdds_functions.py +++ b/python/ctsm/crop_calendars/generate_gdds_functions.py @@ -22,6 +22,7 @@ # pylint: disable=import-error from ctsm.crop_calendars.cropcal_figs_module import * from matplotlib.transforms import Bbox + import matplotlib.pyplot as plt warnings.filterwarnings( "ignore", @@ -63,7 +64,7 @@ def error(logger, string): raise RuntimeError(string) -def check_sdates(dates_ds, sdates_rx, logger, verbose=False): +def check_sdates(dates_ds, sdates_rx, outdir_figs, logger, verbose=False): """ Checking that input and output sdates match """ @@ -106,8 +107,28 @@ def check_sdates(dates_ds, sdates_rx, logger, verbose=False): log(logger, out_map_notnan[here][0:4]) log(logger, "diff:") log(logger, diff_map_notnan[here][0:4]) + first_diff = all_ok all_ok = False + if CAN_PLOT: + sdate_diffs_dir = os.path.join(outdir_figs, "sdate_diffs") + if first_diff: + log(logger, f"Saving sdate difference figures to {sdate_diffs_dir}") + if not os.path.exists(sdate_diffs_dir): + os.makedirs(sdate_diffs_dir) + in_map.where(~np.isnan(out_map)).plot() + plt.title(f"{vegtype_str} sdates in (masked)") + plt.savefig(os.path.join(sdate_diffs_dir, f"{vegtype_str}.in.png")) + plt.close() + out_map.plot() + plt.title(f"{vegtype_str} sdates out") + plt.savefig(os.path.join(sdate_diffs_dir, f"{vegtype_str}.out.png")) + plt.close() + diff_map.plot() + plt.title(f"{vegtype_str} sdates diff (out - in)") + plt.savefig(os.path.join(sdate_diffs_dir, f"{vegtype_str}.diff.png")) + plt.close() + if not any_found: error(logger, "No matching variables found in sdates_rx!") @@ -234,6 +255,7 @@ def import_and_process_1yr( mxmats, get_gs_len_da, skip_crops, + outdir_figs, logger, ): """ @@ -272,6 +294,8 @@ def import_and_process_1yr( time_slice=slice(f"{this_year}-01-01", f"{this_year}-12-31"), chunks=chunks, ) + for timestep in dates_ds["time"].values: + print(timestep) if dates_ds.dims["time"] > 1: if dates_ds.dims["time"] == 365: @@ -466,7 +490,7 @@ def import_and_process_1yr( # Import expected sowing dates. This will also be used as our template output file. imported_sdates = isinstance(sdates_rx, str) sdates_rx = import_rx_dates("s", sdates_rx, incl_patches1d_itype_veg, mxsowings, logger) - check_sdates(dates_incl_ds, sdates_rx, logger) + check_sdates(dates_incl_ds, sdates_rx, outdir_figs, logger) # Import hdates, if needed imported_hdates = isinstance(hdates_rx, str) From 7994c82a4427860494c79611fc32fa0efecadeb3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 11 Jul 2024 16:29:12 -0600 Subject: [PATCH 124/206] Save crop date variables in testmods more frequently. --- cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm | 7 +++++-- .../testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm index a368e97593..aea8e39e6c 100644 --- a/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/crop/user_nl_clm @@ -9,9 +9,12 @@ hist_fincl2 += 'DYN_COL_SOIL_ADJUSTMENTS_C' -! Annual instantaneous crop variables (including per-sowing/per-harvest axes), per PFT. +! Instantaneous crop variables (including per-sowing/per-harvest axes), per PFT. +! Note that, under normal circumstances, these should only be saved annually. +! That's needed for the mxsowings and mxharvests axes to make sense. +! However, for testing purposes, it makes sense to save more frequently. hist_fincl3 = 'SDATES', 'SDATES_PERHARV', 'SYEARS_PERHARV', 'HDATES', 'GRAINC_TO_FOOD_PERHARV', 'GRAINC_TO_FOOD_ANN', 'GRAINN_TO_FOOD_PERHARV', 'GRAINN_TO_FOOD_ANN', 'GRAINC_TO_SEED_PERHARV', 'GRAINC_TO_SEED_ANN', 'GRAINN_TO_SEED_PERHARV', 'GRAINN_TO_SEED_ANN', 'HDATES', 'GDDHARV_PERHARV', 'GDDACCUM_PERHARV', 'HUI_PERHARV', 'SOWING_REASON_PERHARV', 'HARVEST_REASON_PERHARV', 'SWINDOW_STARTS', 'SWINDOW_ENDS', 'GDD20_BASELINE', 'GDD20_SEASON_START', 'GDD20_SEASON_END' -hist_nhtfrq(3) = 17520 +hist_nhtfrq(3) = -24 hist_mfilt(3) = 1 hist_type1d_pertape(3) = 'PFTS' hist_dov2xy(3) = .false. diff --git a/cime_config/testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm index 8f779ed011..18220de5ef 100644 --- a/cime_config/testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/cropMonthOutput/user_nl_clm @@ -1,4 +1,4 @@ - hist_nhtfrq = 0,-240,17520 + hist_nhtfrq = 0,-240,0 hist_mfilt = 1,1,1 ! NOTE slevis (2024/2/23) Adding option for tests to pass. In the long term From 014800df4825a909f3a0297951fcd4f8f05f232b Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 11 Jul 2024 16:32:25 -0600 Subject: [PATCH 125/206] Add tweak_latlons.py. --- python/ctsm/crop_calendars/tweak_latlons.py | 168 ++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 python/ctsm/crop_calendars/tweak_latlons.py diff --git a/python/ctsm/crop_calendars/tweak_latlons.py b/python/ctsm/crop_calendars/tweak_latlons.py new file mode 100644 index 0000000000..de35ced0d1 --- /dev/null +++ b/python/ctsm/crop_calendars/tweak_latlons.py @@ -0,0 +1,168 @@ +# %% +import numpy as np +import xarray as xr +import os +import sys +from netCDF4 import Dataset +import contextlib + +# -- add python/ctsm to path (needed if we want to run this stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) +# pylint: disable=wrong-import-position +from ctsm.mesh_maker import main as mesh_maker + +topdir = "/glade/campaign/cesm/cesmdata/inputdata/lnd/clm2/cropdata/calendars/processed/" +file_list_in = [ + "swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc", + "swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc", + "gdds_20230829_161011.nc", +] +file_mesh_in = ( + "/glade/campaign/cesm/cesmdata/inputdata/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc" +) + +file_list_out = [] +coord_list = ["lat", "lon"] + +# %% + + +def apply_tweak(ds, coord_str, tweak=1e-6): + # Apply tweak + da = ds[coord_str] + coord2 = da.values + coord2 += tweak + while np.any(coord2 == da.values): + tweak *= 10 + coord2 = da.values + coord2 += tweak + coord_tweak = np.full_like(coord2, tweak) + + # Ensure that no value is above maximum in input data. This is needed for mesh_maker to work. + max_coord = np.max(da.values) + where_toohigh = np.where(coord2 > max_coord) + Ntoohigh = len(where_toohigh[0]) + if Ntoohigh != 1: + raise RuntimeError(f"Expected 1 coordinate value too high; got {Ntoohigh}") + coord2[where_toohigh] = max_coord + coord_tweak[where_toohigh] = max_coord + + # Convert to DataArray + new_coords_dict = {coord_str: coord2} + da2 = xr.DataArray( + data=coord2, + coords=new_coords_dict, + dims=da.dims, + attrs=da.attrs, + ) + + # Replace coordinate in dataset + ds[coord_str] = da2 + + # Add a variable with the amount of the tweak + tweak_attrs = {} + if "standard_name" in da: + coord_name = da.attrs["standard_name"] + else: + coord_name = da.attrs["long_name"].replace("coordinate_", "") + tweak_attrs["standard_name"] = coord_name + "_tweak" + tweak_attrs[ + "long_name" + ] = f"Amount {coord_name} was shifted to avoid ambiguous nearest neighbors" + tweak_attrs["units"] = da.attrs["units"] + da_tweak = xr.DataArray( + data=coord_tweak, + coords=new_coords_dict, + dims=da.dims, + attrs=tweak_attrs, + ) + tweak_name = coord_str + "_tweak" + ds[tweak_name] = da_tweak + + return ds + + +# %% Apply tweak to all files + +for filename in file_list_in: + file_in = os.path.join(topdir, filename) + ds = xr.open_dataset(file_in) + + for coord in coord_list: + ds = apply_tweak(ds, coord, tweak=1e-6) + + # Set up for save + file_out = file_in.replace(".nc", ".tweaked_latlons.nc") + with Dataset(file_in, "r") as netcdf_file: + netcdf_format = netcdf_file.data_model + + # Save + print(f"Saving {file_out}") + ds.to_netcdf(file_out, format=netcdf_format) + file_list_out.append(file_out) +print("Done") + + +# %% Ensure all files got the same tweaks + +ds = xr.open_dataset(file_list_out[0]) + +for filename in file_list_out[1:]: + ds2 = xr.open_dataset(filename) + for coord in coord_list: + # Ensure that coordinates are the same + var = coord + assert np.array_equal(ds[var].values, ds2[var].values) + # Ensure that tweaks were the same + var = coord + "_tweak" + assert np.array_equal(ds[var].values, ds2[var].values) +print("All good!") + + +# %% Save new mesh file + +outfile_name = os.path.basename(file_mesh_in) +outfile_name = outfile_name.replace(".nc", ".tweaked_latlons.nc") +outdir = os.path.dirname(file_list_out[0]) +file_mesh_out = os.path.join(outdir, outfile_name) + +@contextlib.contextmanager +def redirect_argv(arglist): + argv_tmp = sys.argv[:] + sys.argv=arglist + yield + sys.argv = argv_tmp + + +mesh_maker_args = [ + "mesh_maker", + "--input", + file_list_out[0], + "--output", + file_mesh_out, + "--lat", + "lat", + "--lon", + "lon", + "--overwrite", +] +print(f"Saving {file_mesh_out}...") +with redirect_argv(mesh_maker_args): + mesh_maker() + +# Change format, if needed +with Dataset(file_mesh_in, "r") as netcdf_file: + netcdf_format_in = netcdf_file.data_model +with Dataset(file_mesh_out, "r") as netcdf_file: + netcdf_format_out = netcdf_file.data_model +if netcdf_format_in != netcdf_format_out: + file_mesh_out_tmp = file_mesh_out + ".tmp" + os.rename(file_mesh_out, file_mesh_out_tmp) + ds = xr.open_dataset(file_mesh_out_tmp) + ds.to_netcdf(file_mesh_out, format=netcdf_format_in) + os.remove(file_mesh_out_tmp) + + +print("Done") +# %% From 4c1d66f88985340b2b95dc909b55587475d75514 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 11 Jul 2024 16:37:20 -0600 Subject: [PATCH 126/206] mesh_type.py: Handle a broadcast error. --- python/ctsm/site_and_regional/mesh_type.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/python/ctsm/site_and_regional/mesh_type.py b/python/ctsm/site_and_regional/mesh_type.py index be785e745d..0260f1c816 100644 --- a/python/ctsm/site_and_regional/mesh_type.py +++ b/python/ctsm/site_and_regional/mesh_type.py @@ -235,8 +235,16 @@ def create_2d_coords(self): lons_size = self.center_lons.size # -- convert center points from 1d to 2d - self.center_lat2d = np.broadcast_to(self.center_lats[:], (lons_size, lats_size)) - self.center_lon2d = np.broadcast_to(self.center_lons[:], (lons_size, lats_size)) + try: + self.center_lat2d = np.broadcast_to(self.center_lats[:], (lons_size, lats_size)) + self.center_lon2d = np.broadcast_to(self.center_lons[:], (lons_size, lats_size)) + except ValueError: + self.center_lat2d = np.broadcast_to( + np.expand_dims(self.center_lats[:], 0), (lons_size, lats_size) + ) + self.center_lon2d = np.broadcast_to( + np.expand_dims(self.center_lons[:], 1), (lons_size, lats_size) + ) elif self.lat_dims == 2: # -- 2D lats and lons dims = self.center_lons.shape From f11003ba4e751e7749ebf2ddfb7a26a73ebb04f7 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 11 Jul 2024 16:37:46 -0600 Subject: [PATCH 127/206] mesh_type.py: Improve error for lon > 360. --- python/ctsm/site_and_regional/mesh_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/site_and_regional/mesh_type.py b/python/ctsm/site_and_regional/mesh_type.py index 0260f1c816..47c0295593 100644 --- a/python/ctsm/site_and_regional/mesh_type.py +++ b/python/ctsm/site_and_regional/mesh_type.py @@ -359,7 +359,7 @@ def calculate_corners(self, unit="degrees"): ) # Longitudes should stay within 0 to 360 if np.any(self.corner_lons > 360.0): - abort("Corners have longitudes greater than 360") + abort(f"Corners have longitudes greater than 360 (max: {np.max(self.corner_lons)})") if np.any(self.corner_lons < 0.0): logger.warning( "Corners have longitudes less than zero -- %s %s", From 231b4ac4372421a714106ec10178416dcb3cb2c6 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 11 Jul 2024 16:44:31 -0600 Subject: [PATCH 128/206] tweak_latlons.py: Add more files. --- python/ctsm/crop_calendars/tweak_latlons.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/ctsm/crop_calendars/tweak_latlons.py b/python/ctsm/crop_calendars/tweak_latlons.py index de35ced0d1..89144a80ae 100644 --- a/python/ctsm/crop_calendars/tweak_latlons.py +++ b/python/ctsm/crop_calendars/tweak_latlons.py @@ -17,6 +17,9 @@ "swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc", "swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc", "gdds_20230829_161011.nc", + "gdd20bl.copied_from.gdds_20230829_161011.v2.nc", + "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc", + "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc", ] file_mesh_in = ( "/glade/campaign/cesm/cesmdata/inputdata/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc" From 9f186d489d9ce2d3b982cd367756dd43102262a8 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 11 Jul 2024 16:45:50 -0600 Subject: [PATCH 129/206] Use tweaked_latlon cropcal inputs. This avoids different "nearest neighbors" being chosen when using a different number of processors. See https://github.com/orgs/esmf-org/discussions/261 --- bld/namelist_files/namelist_defaults_ctsm.xml | 22 +++++++++---------- .../clm/sowingWindows/user_nl_clm | 6 ++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index ae9f9dee6a..d1f9d8545c 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1704,17 +1704,17 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c 2000 -lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc -lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc -lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc -share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc -lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc -lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc -lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.nc -lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.v2.nc -lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc -lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc -share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc +lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.v2.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc diff --git a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm index 545e5f6ebd..7024e99b96 100644 --- a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm @@ -1,5 +1,5 @@ -stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' -stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc' -stream_meshfile_cropcal = '$DIN_LOC_ROOT/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc' +stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc' +stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc' +stream_meshfile_cropcal = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc' stream_year_first_cropcal_swindows = 2000 stream_year_last_cropcal_swindows = 2000 From 1377d48022c5db5a7352744b7356aa4cce1e8413 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 12 Jul 2024 10:12:26 -0600 Subject: [PATCH 130/206] Fix default stream_meshfile_cropcal. --- bld/namelist_files/namelist_defaults_ctsm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index d1f9d8545c..3f8bc1f382 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1707,14 +1707,14 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.tweaked_latlons.nc -lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/360x720_120830_ESMFmesh_c20210507_cdf5.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.v2.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc -lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/360x720_120830_ESMFmesh_c20210507_cdf5.tweaked_latlons.nc From d60dc80d74609096b63c942ca41e0ca38177683b Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 12 Jul 2024 10:13:06 -0600 Subject: [PATCH 131/206] Fix reset of sowing_reason_perharv_patch. --- src/biogeochem/CNPhenologyMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index d4f486acb7..d1d3c85b21 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -1976,7 +1976,7 @@ subroutine CropPhenology(num_pcropp, filter_pcropp , & cnveg_state_inst%gddmaturity_thisyr(p,s) = -1._r8 crop_inst%gddaccum_thisyr_patch(p,s) = -1._r8 crop_inst%hui_thisyr_patch(p,s) = -1._r8 - crop_inst%sowing_reason_perharv_patch = -1._r8 + crop_inst%sowing_reason_perharv_patch(p,s) = -1._r8 crop_inst%harvest_reason_thisyr_patch(p,s) = -1._r8 do k = repr_grain_min, repr_grain_max cnveg_carbonflux_inst%repr_grainc_to_food_perharv_patch(p,s,k) = 0._r8 From 5c3e71fe608c73c47f8f3e9b8ac6ee4102ce63a5 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 12 Jul 2024 11:46:58 -0600 Subject: [PATCH 132/206] Remove midDecStart-RxCropCalsAdaptGGCMI test from expected fails. --- cime_config/testdefs/ExpectedTestFails.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml index 0fac518374..03eb6a157d 100644 --- a/cime_config/testdefs/ExpectedTestFails.xml +++ b/cime_config/testdefs/ExpectedTestFails.xml @@ -170,13 +170,6 @@ - - - FAIL - #2593 - - - From dc488c77a715bba7115c92c43948b668c6c38bc0 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 12 Jul 2024 14:46:59 -0600 Subject: [PATCH 133/206] Improve interpolate_gdds.py. --- python/ctsm/crop_calendars/interpolate_gdds.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/python/ctsm/crop_calendars/interpolate_gdds.py b/python/ctsm/crop_calendars/interpolate_gdds.py index 2aa0b79997..809be98826 100755 --- a/python/ctsm/crop_calendars/interpolate_gdds.py +++ b/python/ctsm/crop_calendars/interpolate_gdds.py @@ -64,6 +64,14 @@ def _setup_process_args(): type=str, required=True, ) + parser.add_argument( + "-p", + "--variable-prefix", + help="Interpolate variables whose names start with this string", + type=str, + required=False, + default="gdd1_" + ) parser.add_argument( "--overwrite", help="If output file exists, overwrite it.", @@ -110,8 +118,12 @@ def interpolate_gdds(args): for var in ds_in: # Check variable - if "gdd1_" not in var: - raise RuntimeError(f"Unexpected variable {var} on input file") + if "lat" not in ds_in[var].dims and "lon" not in ds_in[var].dims: + print(f"Skipping variable {var} with dimensions {ds_in[var].dims}") + continue + elif args.variable_prefix not in var: + print(f"Unexpected variable {var} on input file. Skipping.") + continue if args.dry_run: continue From 3db9bf2788141d9eaea0d9f8dd331c96f7d01165 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 12 Jul 2024 15:08:03 -0600 Subject: [PATCH 134/206] tweak_latlons: Include gdd20 files. --- python/ctsm/crop_calendars/tweak_latlons.py | 105 +++++++++++++++----- 1 file changed, 80 insertions(+), 25 deletions(-) diff --git a/python/ctsm/crop_calendars/tweak_latlons.py b/python/ctsm/crop_calendars/tweak_latlons.py index 89144a80ae..0031d492e0 100644 --- a/python/ctsm/crop_calendars/tweak_latlons.py +++ b/python/ctsm/crop_calendars/tweak_latlons.py @@ -20,6 +20,8 @@ "gdd20bl.copied_from.gdds_20230829_161011.v2.nc", "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc", "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc", + "/glade/work/samrabin/cropCals_testing_20240626/gdds_20240712_114642_10x15_interpd_halfdeg.nc", + "/glade/work/samrabin/gdd20_baselines/gswp3.10x15_interpd_halfdeg.1980-2009.nc", ] file_mesh_in = ( "/glade/campaign/cesm/cesmdata/inputdata/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc" @@ -27,19 +29,43 @@ file_list_out = [] coord_list = ["lat", "lon"] +COORD_DATATYPE = np.float64 -# %% - +# %% Define functions -def apply_tweak(ds, coord_str, tweak=1e-6): - # Apply tweak - da = ds[coord_str] - coord2 = da.values +def get_ds(topdir, file_in): + if not os.path.exists(file_in): + file_in = os.path.join(topdir, file_in) + ds = xr.open_dataset(file_in) + return file_in, ds + +def get_tweak(ds_in, coord_str, init_tweak): + """ + Get the tweak that will be applied to all datasets' lat/lon coordinates + """ + da = ds_in[coord_str] + coord2_orig = da.values.astype(COORD_DATATYPE) + coord2 = coord2_orig + tweak = init_tweak coord2 += tweak + + # This is necessary if precision is lower than float64 + max_tweak = 1e-2 while np.any(coord2 == da.values): tweak *= 10 - coord2 = da.values + if tweak > max_tweak: + raise RuntimeError(f"Tweaking by +{max_tweak} failed to 'take'") + coord2 = coord2_orig coord2 += tweak + return tweak + +def apply_tweak(ds_in, coord_str, tweak): + # Apply tweak + da = ds_in[coord_str] + coord2 = da.values.astype(COORD_DATATYPE) + coord2 += tweak + if np.any(coord2 == da.values): + raise RuntimeError('Tweak didn''t "take"') coord_tweak = np.full_like(coord2, tweak) # Ensure that no value is above maximum in input data. This is needed for mesh_maker to work. @@ -61,19 +87,22 @@ def apply_tweak(ds, coord_str, tweak=1e-6): ) # Replace coordinate in dataset - ds[coord_str] = da2 + ds_in[coord_str] = da2 # Add a variable with the amount of the tweak tweak_attrs = {} - if "standard_name" in da: + if "standard_name" in da.attrs: coord_name = da.attrs["standard_name"] - else: + elif "long_name" in da.attrs: coord_name = da.attrs["long_name"].replace("coordinate_", "") + else: + coord_name = coord_str tweak_attrs["standard_name"] = coord_name + "_tweak" tweak_attrs[ "long_name" ] = f"Amount {coord_name} was shifted to avoid ambiguous nearest neighbors" - tweak_attrs["units"] = da.attrs["units"] + if "units" in da.attrs: + tweak_attrs["units"] = da.attrs["units"] da_tweak = xr.DataArray( data=coord_tweak, coords=new_coords_dict, @@ -81,19 +110,48 @@ def apply_tweak(ds, coord_str, tweak=1e-6): attrs=tweak_attrs, ) tweak_name = coord_str + "_tweak" - ds[tweak_name] = da_tweak + ds_in[tweak_name] = da_tweak + + return ds_in + +def check(ds, f0_base, ds2, f_base, var): + if not np.array_equal(ds[var].values, ds2[var].values): + if not np.array_equal(ds[var].shape, ds2[var].shape): + msg = f"{var} shapes differ b/w {f0_base} ({ds[var].shape}) and {f_base} ({ds2[var].shape})" + raise RuntimeError(msg) + max_diff = np.max(np.abs(ds[var].values - ds2[var].values)) + msg = f"{var}s differ between {f0_base} and {f_base}; max = {max_diff}" + type0 = type(ds[var].values[0]) + type2 = type(ds2[var].values[0]) + if type0 != type2: + msg += f"\nTypes also differ: {type0} vs. {type2}" + raise RuntimeError(msg) - return ds +# %% Apply tweak to all files +# Set up empty dicts +tweak_dict = {} +coord_type_dict = {} +for coord in coord_list: + tweak_dict[coord] = -np.inf -# %% Apply tweak to all files +# Get tweaks +for file_in in file_list_in: + file_in, ds = get_ds(topdir, file_in) + for coord in coord_list: + this_tweak = get_tweak(ds, coord, init_tweak=1e-6) + if this_tweak > tweak_dict[coord]: + tweak_dict[coord] = this_tweak +for coord in coord_list: + print(f"Tweaking {coord} by {tweak_dict[coord]}") +print(" ") -for filename in file_list_in: - file_in = os.path.join(topdir, filename) - ds = xr.open_dataset(file_in) +# Apply tweaks +for file_in in file_list_in: + file_in, ds = get_ds(topdir, file_in) for coord in coord_list: - ds = apply_tweak(ds, coord, tweak=1e-6) + ds = apply_tweak(ds, coord, tweak_dict[coord]) # Set up for save file_out = file_in.replace(".nc", ".tweaked_latlons.nc") @@ -110,16 +168,14 @@ def apply_tweak(ds, coord_str, tweak=1e-6): # %% Ensure all files got the same tweaks ds = xr.open_dataset(file_list_out[0]) +f0_base = os.path.basename(file_list_out[0]) for filename in file_list_out[1:]: ds2 = xr.open_dataset(filename) + f_base = os.path.basename(filename) for coord in coord_list: - # Ensure that coordinates are the same - var = coord - assert np.array_equal(ds[var].values, ds2[var].values) - # Ensure that tweaks were the same - var = coord + "_tweak" - assert np.array_equal(ds[var].values, ds2[var].values) + check(ds, f0_base, ds2, f_base, coord) + check(ds, f0_base, ds2, f_base, coord + "_tweak") print("All good!") @@ -168,4 +224,3 @@ def redirect_argv(arglist): print("Done") -# %% From 64302ad0c48b2afd7a074659c6509ae3344a4ac3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 12 Jul 2024 16:54:46 -0600 Subject: [PATCH 135/206] Additions/tweaks to CLM testlist. --- cime_config/testdefs/testlist_clm.xml | 44 +++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 40f351ab4a..3fde176440 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3663,6 +3663,8 @@ + + @@ -3673,6 +3675,7 @@ + @@ -3684,6 +3687,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3754,13 +3793,14 @@ - + + @@ -3773,7 +3813,7 @@ - + From 0da6eb5c706217606c1323b2b1a57eb136e32f1a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sun, 14 Jul 2024 12:01:58 -0600 Subject: [PATCH 136/206] Remove Clm50 RXCROPMATURITY tests. --- cime_config/testdefs/testlist_clm.xml | 37 +-------------------------- 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 3fde176440..86210256ff 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3660,42 +3660,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + From 87d1fa1a4b2a69c2d01898be9d8f9e661af8d163 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sun, 14 Jul 2024 12:43:31 -0600 Subject: [PATCH 137/206] Move tweak_latlons.py to tools/contrib/. This is hopefully not something that will be needed permanently or even in the medium term, so it's not worth cleaning up and unit-/system-testing. --- {python/ctsm/crop_calendars => tools/contrib}/tweak_latlons.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {python/ctsm/crop_calendars => tools/contrib}/tweak_latlons.py (100%) diff --git a/python/ctsm/crop_calendars/tweak_latlons.py b/tools/contrib/tweak_latlons.py similarity index 100% rename from python/ctsm/crop_calendars/tweak_latlons.py rename to tools/contrib/tweak_latlons.py From f6ff13409d58a50299d0d62207e99a12b2c426a6 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sun, 14 Jul 2024 14:07:51 -0600 Subject: [PATCH 138/206] Rework tweak_latlons.py to run from command line. --- tools/contrib/tweak_latlons.py | 285 +++++++++++++++++++-------------- 1 file changed, 164 insertions(+), 121 deletions(-) diff --git a/tools/contrib/tweak_latlons.py b/tools/contrib/tweak_latlons.py index 0031d492e0..2bae06d229 100644 --- a/tools/contrib/tweak_latlons.py +++ b/tools/contrib/tweak_latlons.py @@ -1,44 +1,23 @@ -# %% -import numpy as np -import xarray as xr +""" +'Tweak' the latitude and longitude coordinates to avoid ambiguous nearest neighbors +""" import os import sys -from netCDF4 import Dataset import contextlib +import argparse +import numpy as np +import xarray as xr +from netCDF4 import Dataset # pylint: disable=no-name-in-module # -- add python/ctsm to path (needed if we want to run this stand-alone) -_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir, "python") sys.path.insert(1, _CTSM_PYTHON) # pylint: disable=wrong-import-position from ctsm.mesh_maker import main as mesh_maker -topdir = "/glade/campaign/cesm/cesmdata/inputdata/lnd/clm2/cropdata/calendars/processed/" -file_list_in = [ - "swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc", - "swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.nc", - "gdds_20230829_161011.nc", - "gdd20bl.copied_from.gdds_20230829_161011.v2.nc", - "sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc", - "hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.nc", - "/glade/work/samrabin/cropCals_testing_20240626/gdds_20240712_114642_10x15_interpd_halfdeg.nc", - "/glade/work/samrabin/gdd20_baselines/gswp3.10x15_interpd_halfdeg.1980-2009.nc", -] -file_mesh_in = ( - "/glade/campaign/cesm/cesmdata/inputdata/share/meshes/360x720_120830_ESMFmesh_c20210507_cdf5.nc" -) - -file_list_out = [] -coord_list = ["lat", "lon"] +COORD_LIST = ["lat", "lon"] COORD_DATATYPE = np.float64 -# %% Define functions - -def get_ds(topdir, file_in): - if not os.path.exists(file_in): - file_in = os.path.join(topdir, file_in) - ds = xr.open_dataset(file_in) - return file_in, ds - def get_tweak(ds_in, coord_str, init_tweak): """ Get the tweak that will be applied to all datasets' lat/lon coordinates @@ -73,7 +52,9 @@ def apply_tweak(ds_in, coord_str, tweak): where_toohigh = np.where(coord2 > max_coord) Ntoohigh = len(where_toohigh[0]) if Ntoohigh != 1: - raise RuntimeError(f"Expected 1 coordinate value too high; got {Ntoohigh}") + raise RuntimeError( + f"Expected 1 coordinate value too high; got {Ntoohigh}" + ) coord2[where_toohigh] = max_coord coord_tweak[where_toohigh] = max_coord @@ -127,100 +108,162 @@ def check(ds, f0_base, ds2, f_base, var): msg += f"\nTypes also differ: {type0} vs. {type2}" raise RuntimeError(msg) -# %% Apply tweak to all files - -# Set up empty dicts -tweak_dict = {} -coord_type_dict = {} -for coord in coord_list: - tweak_dict[coord] = -np.inf - -# Get tweaks -for file_in in file_list_in: - file_in, ds = get_ds(topdir, file_in) - for coord in coord_list: - this_tweak = get_tweak(ds, coord, init_tweak=1e-6) - if this_tweak > tweak_dict[coord]: - tweak_dict[coord] = this_tweak -for coord in coord_list: - print(f"Tweaking {coord} by {tweak_dict[coord]}") -print(" ") - -# Apply tweaks -for file_in in file_list_in: - file_in, ds = get_ds(topdir, file_in) - - for coord in coord_list: - ds = apply_tweak(ds, coord, tweak_dict[coord]) - - # Set up for save - file_out = file_in.replace(".nc", ".tweaked_latlons.nc") - with Dataset(file_in, "r") as netcdf_file: - netcdf_format = netcdf_file.data_model - - # Save - print(f"Saving {file_out}") - ds.to_netcdf(file_out, format=netcdf_format) - file_list_out.append(file_out) -print("Done") - - -# %% Ensure all files got the same tweaks - -ds = xr.open_dataset(file_list_out[0]) -f0_base = os.path.basename(file_list_out[0]) - -for filename in file_list_out[1:]: - ds2 = xr.open_dataset(filename) - f_base = os.path.basename(filename) - for coord in coord_list: - check(ds, f0_base, ds2, f_base, coord) - check(ds, f0_base, ds2, f_base, coord + "_tweak") -print("All good!") - - -# %% Save new mesh file - -outfile_name = os.path.basename(file_mesh_in) -outfile_name = outfile_name.replace(".nc", ".tweaked_latlons.nc") -outdir = os.path.dirname(file_list_out[0]) -file_mesh_out = os.path.join(outdir, outfile_name) - @contextlib.contextmanager def redirect_argv(arglist): + """ + Preserve actual arg list while giving a new one to mesh_maker + """ argv_tmp = sys.argv[:] - sys.argv=arglist + sys.argv = arglist yield sys.argv = argv_tmp +def main(input_files, mesh_file_in, output_files): + """ + Apply tweak to all files + """ + + # Set up + tweak_dict = {} + for coord in COORD_LIST: + tweak_dict[coord] = -np.inf + mesh_file_out = output_files[-1] + output_files = output_files[:-1] + + # Get tweaks + for file_in in input_files: + ds = xr.open_dataset(file_in) + for coord in COORD_LIST: + this_tweak = get_tweak(ds, coord, init_tweak=1e-6) + if this_tweak > tweak_dict[coord]: + tweak_dict[coord] = this_tweak + for coord in COORD_LIST: + print(f"Tweaking {coord} by {tweak_dict[coord]}") + print(" ") + + # Apply tweaks + for i, file_in in enumerate(input_files): + ds = xr.open_dataset(file_in) + + for coord in COORD_LIST: + ds = apply_tweak(ds, coord, tweak_dict[coord]) + + # Set up for save + file_out = output_files[i] + with Dataset(file_in, "r") as netcdf_file: + netcdf_format = netcdf_file.data_model + + # Make output dir, if needed + output_dir = os.path.dirname(file_out) + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + # Save + print(f"Saving {file_out}") + ds.to_netcdf(file_out, format=netcdf_format) + print("Done") + + + # Ensure all files got the same tweaks + ds = xr.open_dataset(output_files[0]) + f0_base = os.path.basename(output_files[0]) + for file_out in output_files[1:]: + ds2 = xr.open_dataset(file_out) + f_base = os.path.basename(file_out) + for coord in COORD_LIST: + check(ds, f0_base, ds2, f_base, coord) + check(ds, f0_base, ds2, f_base, coord + "_tweak") + + + # Save new mesh file + mesh_maker_args = [ + "mesh_maker", + "--input", + output_files[0], + "--output", + mesh_file_out, + "--lat", + "lat", + "--lon", + "lon", + "--overwrite", + ] + print(f"Saving {mesh_file_out}...") + with redirect_argv(mesh_maker_args): + mesh_maker() + + # Change format, if needed + with Dataset(mesh_file_in, "r") as netcdf_file: + netcdf_format_in = netcdf_file.data_model + with Dataset(mesh_file_out, "r") as netcdf_file: + netcdf_format_out = netcdf_file.data_model + if netcdf_format_in != netcdf_format_out: + mesh_file_out_tmp = mesh_file_out + ".tmp" + os.rename(mesh_file_out, mesh_file_out_tmp) + ds = xr.open_dataset(mesh_file_out_tmp) + ds.to_netcdf(mesh_file_out, format=netcdf_format_in) + os.remove(mesh_file_out_tmp) + + print("Done") + + + + +if __name__ == "__main__": + ############################### + ### Process input arguments ### + ############################### + parser = argparse.ArgumentParser( + description="'Tweak' the latitude and longitude coordinates to avoid ambiguous nearest neighbors", + ) + + # Required + parser.add_argument( + "-i", + "--input-files", + help="Comma-separated stream files whose coordinates need tweaking", + required=True, + ) + parser.add_argument( + "-m", + "--mesh-file", + help="Mesh file associated with input files", + required=True, + ) + + # Optional + parser.add_argument( + "--overwrite", + help="Overwrite any existing output files", + action="store_true", + default=False, + ) + default_output_dir = os.getcwd() + parser.add_argument( + "-o", + "--output-dir", + help=f"Directory where output files should be saved. Default is current working directory: {default_output_dir}", + default=default_output_dir, + ) -mesh_maker_args = [ - "mesh_maker", - "--input", - file_list_out[0], - "--output", - file_mesh_out, - "--lat", - "lat", - "--lon", - "lon", - "--overwrite", -] -print(f"Saving {file_mesh_out}...") -with redirect_argv(mesh_maker_args): - mesh_maker() - -# Change format, if needed -with Dataset(file_mesh_in, "r") as netcdf_file: - netcdf_format_in = netcdf_file.data_model -with Dataset(file_mesh_out, "r") as netcdf_file: - netcdf_format_out = netcdf_file.data_model -if netcdf_format_in != netcdf_format_out: - file_mesh_out_tmp = file_mesh_out + ".tmp" - os.rename(file_mesh_out, file_mesh_out_tmp) - ds = xr.open_dataset(file_mesh_out_tmp) - ds.to_netcdf(file_mesh_out, format=netcdf_format_in) - os.remove(file_mesh_out_tmp) - - -print("Done") + # Get arguments + args = parser.parse_args(sys.argv[1:]) + + # Check/process input and output files + _input_files = args.input_files.split(",") + _output_files = [] + for file in _input_files + [args.mesh_file]: + if not os.path.exists(file): + raise FileNotFoundError(f"File not found: {file}") + + filename, ext = os.path.splitext(os.path.basename(file)) + output_file = os.path.join( + args.output_dir, filename + ".tweaked_latlons" + ext + ) + if os.path.exists(output_file) and not args.overwrite: + raise FileExistsError( + f"Output file exists but --overwrite not specified: {output_file}" + ) + _output_files.append(output_file) + + main(_input_files, args.mesh_file, _output_files) From 227426345fcd0bd552e755b5c4cceb503cea94c6 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sun, 14 Jul 2024 14:28:50 -0600 Subject: [PATCH 139/206] Refine RxCropCals* testlist. --- cime_config/testdefs/testlist_clm.xml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 86210256ff..c2926a25e7 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3755,7 +3755,7 @@ - + @@ -3765,7 +3765,8 @@ - + + @@ -3782,6 +3783,17 @@ + + + + + + + + + + + From e14313858df6e9e6f2488951af2551349e349c65 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 10:11:33 -0600 Subject: [PATCH 140/206] generate_gdd20_baseline: Clarity rearrangement. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 476717b887..5d4fac3aff 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -246,16 +246,18 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice): if gddn is None: # Crop not handled yet? Fill it entirely with missing value this_da = dummy_da - long_name = "Dummy GDD20" print(" dummy GDD20") else: # this_da = ds_in[gddn].fillna(MISSING_FILL) this_da = ds_in[gddn] this_da = _add_time_axis(this_da) - long_name = gddn.replace("X", "20") print(f" {gddn}") - # Add attributes + # Add attributes of output file + if gddn is None: + long_name = "Dummy GDD20" + else: + long_name = gddn.replace("X", "20") this_da.attrs["long_name"] = long_name + f" baseline for {cft_str}" this_da.attrs["units"] = "°C days" # this_da.attrs["_FillValue"] = MISSING_FILL From 647fc52df1eb32b10646eb54ed4240d4cc9cfc00 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:07:00 -0600 Subject: [PATCH 141/206] import_ds: Workaround to import multiple files without dask. --- python/ctsm/crop_calendars/import_ds.py | 53 +++++++++++++++++-------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/python/ctsm/crop_calendars/import_ds.py b/python/ctsm/crop_calendars/import_ds.py index 77a22b626b..0526d3c720 100644 --- a/python/ctsm/crop_calendars/import_ds.py +++ b/python/ctsm/crop_calendars/import_ds.py @@ -41,6 +41,29 @@ def compute_derived_vars(ds_in, var): return ds_in +def manual_mfdataset(filelist, my_vars, my_vegtypes, time_slice): + """ + Opening a list of files with Xarray's open_mfdataset requires dask. This function is a + workaround for Python environments that don't have dask. + """ + ds_out = None + for filename in filelist: + ds_in = xr.open_dataset(filename) + ds_in = mfdataset_preproc(ds_in, my_vars, my_vegtypes, time_slice) + if ds_out is None: + ds_out = ds_in + else: + ds_out = xr.concat( + [ds_out, ds_in], + data_vars="minimal", + compat="override", + coords="all", + dim="time", + # combine="nested", + ) + return ds_out + + def mfdataset_preproc(ds_in, vars_to_import, vegtypes_to_import, time_slice): """ Function to drop unwanted variables in preprocessing of open_mfdataset(). @@ -221,22 +244,20 @@ def import_ds( if isinstance(filelist, list): with warnings.catch_warnings(): warnings.filterwarnings(action="ignore", category=DeprecationWarning) - if find_spec("dask") is None: - raise ModuleNotFoundError( - "You have asked xarray to import a list of files as a single Dataset using" - " open_mfdataset(), but this requires dask, which is not available.\nFile" - f" list: {filelist}" - ) - this_ds = xr.open_mfdataset( - sorted(filelist), - data_vars="minimal", - preprocess=mfdataset_preproc_closure, - compat="override", - coords="all", - concat_dim="time", - combine="nested", - chunks=chunks, - ) + dask_unavailable = find_spec("dask") is None + if dask_unavailable: + this_ds = manual_mfdataset(filelist, my_vars, my_vegtypes, time_slice) + else: + this_ds = xr.open_mfdataset( + sorted(filelist), + data_vars="minimal", + preprocess=mfdataset_preproc_closure, + compat="override", + coords="all", + concat_dim="time", + combine="nested", + chunks=chunks, + ) elif isinstance(filelist, str): this_ds = xr.open_dataset(filelist, chunks=chunks) this_ds = mfdataset_preproc(this_ds, my_vars, my_vegtypes, time_slice) From 96015dacbc7a22d57bcd242e358356f5c0cb028b Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:11:52 -0600 Subject: [PATCH 142/206] generate_gdd20_baseline: Can now specify -v/--variable GDDBX or GDDB20. --- .../crop_calendars/generate_gdd20_baseline.py | 64 +++++++++++++------ 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 5d4fac3aff..4b8e68e8f9 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -19,7 +19,6 @@ import ctsm.crop_calendars.cropcal_utils as utils from ctsm.crop_calendars.grid_one_variable import grid_one_variable -VAR_LIST_IN = ["GDD0X", "GDD8X", "GDD10X"] GRIDDING_VAR_LIST = ["patches1d_ixy", "patches1d_jxy", "lat", "lon"] MISSING_FILL = -1 # Something impossible to ensure that you can mark it as a missing value, to be # bilinear-interpolated @@ -88,6 +87,16 @@ def _parse_args(): type=int, required=False, ) + parser.add_argument( + "-v", + "--variable", + help=( + "Which type of variable should be processed?" + ), + required=False, + default="GDDBX", + choices=["GDDBX", "GDDB20"], + ) # Get arguments args = parser.parse_args(sys.argv[1:]) @@ -96,6 +105,12 @@ def _parse_args(): if os.path.exists(args.output_file) and not args.overwrite: raise FileExistsError("Output file exists but --overwrite is not specified") + # Get and check input files + args.input_files = args.input_files.split(" ") + for filename in args.input_files: + if not os.path.exists(filename): + raise FileNotFoundError(f"Input file not found: {filename}") + # Process time slice # Assumes CESM behavior where data for e.g. 1987 is saved as 1988-01-01. # It would be more robust, accounting for upcoming behavior (where timestamp for a year is the @@ -137,7 +152,7 @@ def _get_cft_list(crop_list): return cft_str_list -def _get_gddn_for_cft(cft_str): +def _get_gddn_for_cft(cft_str, variable): """ Given a CFT name, return the GDDN variable it uses. @@ -149,6 +164,7 @@ def _get_gddn_for_cft(cft_str): """ gddn = None + gddn_str = None gdd0_list_str = ["wheat", "cotton", "rice"] if cft_str in _get_cft_list(gdd0_list_str): @@ -163,9 +179,9 @@ def _get_gddn_for_cft(cft_str): gddn = 10 if gddn is not None: - gddn = f"GDD{gddn}X" + gddn_str = variable.replace("B", str(gddn)) - return gddn + return gddn, gddn_str def _get_output_varname(cft_str): @@ -194,19 +210,28 @@ def _add_time_axis(da_in): return da_out -def generate_gdd20_baseline(input_files, output_file, author, time_slice): +def generate_gdd20_baseline(input_files, output_file, author, time_slice, variable): """ Generate stream_fldFileName_gdd20_baseline file from CTSM outputs """ - # Get input file list - input_files = input_files.split(sep=" ") + # Define variables to process + if variable == "GDDBX": + suffix = "X" + elif variable == "GDDB20": + suffix = "20" + else: + raise ValueError(f"-v/--variable {variable} not recoginzed") + var_list_in = [] + for base_temp in [0, 8, 10]: + var_list_in.append(f"GDD{base_temp}{suffix}") + # Get unique values and sort input_files = list(set(input_files)) input_files.sort() # Import history files and ensure they have lat/lon dims - ds_in = import_ds(input_files, VAR_LIST_IN + GRIDDING_VAR_LIST, time_slice=time_slice) + ds_in = import_ds(input_files, var_list_in + GRIDDING_VAR_LIST, time_slice=time_slice) if not all(x in ds_in.dims for x in ["lat", "lon"]): raise RuntimeError("Input files must have lat and lon dimensions") @@ -216,9 +241,9 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice): # Set up a dummy DataArray to use for crops without an assigned GDDN variable dummy_da = xr.DataArray( - data=MISSING_FILL * np.ones_like(ds_in[VAR_LIST_IN[0]].values), - dims=ds_in[VAR_LIST_IN[0]].dims, - coords=ds_in[VAR_LIST_IN[0]].coords, + data=MISSING_FILL * np.ones_like(ds_in[var_list_in[0]].values), + dims=ds_in[var_list_in[0]].dims, + coords=ds_in[var_list_in[0]].coords, ) dummy_da = _add_time_axis(dummy_da) @@ -239,25 +264,27 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice): print(f"{cft_str} ({cft_int})") # Which GDDN history variable does this crop use? E.g., GDD0, GDD10 - gddn = _get_gddn_for_cft(cft_str) + gddn, gddn_str = _get_gddn_for_cft(cft_str, variable) # Fill any missing values with MISSING_FILL. This will mean that gddmaturity in these cells # never changes. - if gddn is None: + if gddn_str is None: # Crop not handled yet? Fill it entirely with missing value this_da = dummy_da print(" dummy GDD20") else: # this_da = ds_in[gddn].fillna(MISSING_FILL) - this_da = ds_in[gddn] + this_da = ds_in[gddn_str] this_da = _add_time_axis(this_da) - print(f" {gddn}") + print(f" {gddn_str}") # Add attributes of output file - if gddn is None: + if (gddn is None) != (gddn_str is None): + raise RuntimeError("gddn and gddn_str must either both be None or both be not None") + if gddn_str is None: long_name = "Dummy GDD20" else: - long_name = gddn.replace("X", "20") + long_name = f"GDD{gddn}20" this_da.attrs["long_name"] = long_name + f" baseline for {cft_str}" this_da.attrs["units"] = "°C days" # this_da.attrs["_FillValue"] = MISSING_FILL @@ -287,5 +314,6 @@ def main(): args.input_files, args.output_file, args.author, - time_slice + time_slice, + args.variable, ) From 3cc1d0fafb3290c1b249d22f828429c92a3719c4 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:14:33 -0600 Subject: [PATCH 143/206] generate_gdd20_baseline: Satisfy pylint. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 4b8e68e8f9..d2f96965b7 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -160,7 +160,7 @@ def _get_gddn_for_cft(cft_str, variable): cft_str (str): E.g., "irrigated_temperate_corn" Returns: - str or None: Name of variable to use (e.g., "GDD8X"). If crop isn't yet handled, return None. + str or None: Name of variable to use (e.g., "GDD8X"). If crop not yet handled, return None. """ gddn = None @@ -249,8 +249,8 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab # Process all crops data_var_dict = {} - for v in GRIDDING_VAR_LIST: - data_var_dict[v] = ds_in[v] + for gridding_var in GRIDDING_VAR_LIST: + data_var_dict[gridding_var] = ds_in[gridding_var] ds_out = xr.Dataset( data_vars=data_var_dict, attrs={ From b1bbe15eb03a57a26eb010a850a285fd600dfe3b Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:27:10 -0600 Subject: [PATCH 144/206] generate_gdd20_baseline: Save input file list as attribute(s). --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index d2f96965b7..b03d1fd658 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -247,7 +247,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab ) dummy_da = _add_time_axis(dummy_da) - # Process all crops + # Set up output Dataset data_var_dict = {} for gridding_var in GRIDDING_VAR_LIST: data_var_dict[gridding_var] = ds_in[gridding_var] @@ -258,6 +258,14 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab "created": dt.datetime.now().astimezone().isoformat(), }, ) + all_files_in_same_dir = len(np.unique([os.path.dirname(file) for file in input_files])) == 1 + if all_files_in_same_dir: + ds_out.attrs["input_files_dir"] = os.path.dirname(input_files[0]) + ds_out.attrs["input_files"] = ", ".join([os.path.basename(file) for file in input_files]) + else: + ds_out.attrs["input_files"] = ", ".join(input_files) + + # Process all crops encoding_dict = {} for cft_str in utils.define_mgdcrop_list(): cft_int = utils.vegtype_str2int(cft_str)[0] From a703cb6549576c63cedde68756292fab630543a5 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:31:46 -0600 Subject: [PATCH 145/206] generate_gdd20_baseline: Save input year range as attribute. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index b03d1fd658..14e2d5974f 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -210,7 +210,7 @@ def _add_time_axis(da_in): return da_out -def generate_gdd20_baseline(input_files, output_file, author, time_slice, variable): +def generate_gdd20_baseline(input_files, output_file, author, time_slice, variable, year_args): """ Generate stream_fldFileName_gdd20_baseline file from CTSM outputs """ @@ -256,6 +256,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab attrs={ "author": author, "created": dt.datetime.now().astimezone().isoformat(), + "input_year_range": f"{year_args[0]}-{year_args[1]}", }, ) all_files_in_same_dir = len(np.unique([os.path.dirname(file) for file in input_files])) == 1 @@ -324,4 +325,5 @@ def main(): args.author, time_slice, args.variable, + [args.first_year, args.last_year], ) From 7a5e067dd09970d74708ed9c678f1de7d8f29af6 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:32:33 -0600 Subject: [PATCH 146/206] generate_gdd20_baseline: Save input variable type as attribute. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 14e2d5974f..a87a2edb79 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -257,6 +257,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab "author": author, "created": dt.datetime.now().astimezone().isoformat(), "input_year_range": f"{year_args[0]}-{year_args[1]}", + "input_variable": variable, }, ) all_files_in_same_dir = len(np.unique([os.path.dirname(file) for file in input_files])) == 1 From 25896e460ac556a5cef78201f55b8f04fc6ce154 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 11:34:19 -0600 Subject: [PATCH 147/206] generate_gdd20_baseline: Functionize setup_output_dataset(). --- .../crop_calendars/generate_gdd20_baseline.py | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index a87a2edb79..5d6809f7ff 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -210,6 +210,31 @@ def _add_time_axis(da_in): return da_out +def setup_output_dataset(input_files, author, variable, year_args, ds_in): + """ + Set up output Dataset + """ + data_var_dict = {} + for gridding_var in GRIDDING_VAR_LIST: + data_var_dict[gridding_var] = ds_in[gridding_var] + ds_out = xr.Dataset( + data_vars=data_var_dict, + attrs={ + "author": author, + "created": dt.datetime.now().astimezone().isoformat(), + "input_year_range": f"{year_args[0]}-{year_args[1]}", + "input_variable": variable, + }, + ) + all_files_in_same_dir = len(np.unique([os.path.dirname(file) for file in input_files])) == 1 + if all_files_in_same_dir: + ds_out.attrs["input_files_dir"] = os.path.dirname(input_files[0]) + ds_out.attrs["input_files"] = ", ".join([os.path.basename(file) for file in input_files]) + else: + ds_out.attrs["input_files"] = ", ".join(input_files) + return ds_out + + def generate_gdd20_baseline(input_files, output_file, author, time_slice, variable, year_args): """ Generate stream_fldFileName_gdd20_baseline file from CTSM outputs @@ -248,24 +273,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab dummy_da = _add_time_axis(dummy_da) # Set up output Dataset - data_var_dict = {} - for gridding_var in GRIDDING_VAR_LIST: - data_var_dict[gridding_var] = ds_in[gridding_var] - ds_out = xr.Dataset( - data_vars=data_var_dict, - attrs={ - "author": author, - "created": dt.datetime.now().astimezone().isoformat(), - "input_year_range": f"{year_args[0]}-{year_args[1]}", - "input_variable": variable, - }, - ) - all_files_in_same_dir = len(np.unique([os.path.dirname(file) for file in input_files])) == 1 - if all_files_in_same_dir: - ds_out.attrs["input_files_dir"] = os.path.dirname(input_files[0]) - ds_out.attrs["input_files"] = ", ".join([os.path.basename(file) for file in input_files]) - else: - ds_out.attrs["input_files"] = ", ".join(input_files) + ds_out = setup_output_dataset(input_files, author, variable, year_args, ds_in) # Process all crops encoding_dict = {} From f909d2da504328459dd8129df708e289dfe33de3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 13:15:49 -0600 Subject: [PATCH 148/206] Fill all land cells with MISSING_RX_GDD_VAL. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 5d6809f7ff..c0c9adb30b 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -18,10 +18,9 @@ from ctsm.crop_calendars.import_ds import import_ds import ctsm.crop_calendars.cropcal_utils as utils from ctsm.crop_calendars.grid_one_variable import grid_one_variable +from ctsm.crop_calendars.cropcal_module import MISSING_RX_GDD_VAL GRIDDING_VAR_LIST = ["patches1d_ixy", "patches1d_jxy", "lat", "lon"] -MISSING_FILL = -1 # Something impossible to ensure that you can mark it as a missing value, to be -# bilinear-interpolated STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline @@ -266,7 +265,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab # Set up a dummy DataArray to use for crops without an assigned GDDN variable dummy_da = xr.DataArray( - data=MISSING_FILL * np.ones_like(ds_in[var_list_in[0]].values), + data=np.full_like(ds_in[var_list_in[0]].values, MISSING_RX_GDD_VAL), dims=ds_in[var_list_in[0]].dims, coords=ds_in[var_list_in[0]].coords, ) @@ -284,10 +283,10 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab # Which GDDN history variable does this crop use? E.g., GDD0, GDD10 gddn, gddn_str = _get_gddn_for_cft(cft_str, variable) - # Fill any missing values with MISSING_FILL. This will mean that gddmaturity in these cells + # Fill any missing values with MISSING_RX_GDD_VAL. This will mean that gddmaturity there # never changes. if gddn_str is None: - # Crop not handled yet? Fill it entirely with missing value + # Crop not handled yet? It's already filled with missing value this_da = dummy_da print(" dummy GDD20") else: @@ -295,6 +294,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab this_da = ds_in[gddn_str] this_da = _add_time_axis(this_da) print(f" {gddn_str}") + this_da = this_da.fillna(MISSING_RX_GDD_VAL) # Add attributes of output file if (gddn is None) != (gddn_str is None): @@ -305,7 +305,6 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab long_name = f"GDD{gddn}20" this_da.attrs["long_name"] = long_name + f" baseline for {cft_str}" this_da.attrs["units"] = "°C days" - # this_da.attrs["_FillValue"] = MISSING_FILL # Copy that to ds_out var_out = _get_output_varname(cft_str) From 8fa8506ceb753e6e1571f4b93adb4c43ea51d8d1 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 13:39:15 -0600 Subject: [PATCH 149/206] Set different gdd20 baseline file if using default gdd20 baseline seasons. --- bld/namelist_files/namelist_defaults_ctsm.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 3f8bc1f382..70ff048aa7 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -1711,7 +1711,8 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/gdds_20230829_161011.tweaked_latlons.nc -lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.v2.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/20230714_cropcals_pr2_1deg.actually2deg.1980-2009.from_GDDB20.interpd_halfdeg.tweaked_latlons.nc +lnd/clm2/cropdata/calendars/processed/gdd20bl.copied_from.gdds_20230829_161011.v2.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/hdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc lnd/clm2/cropdata/calendars/processed/360x720_120830_ESMFmesh_c20210507_cdf5.tweaked_latlons.nc From 945c8bbd142d0c528cbddbafd529a790a88c2dd8 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 14:34:03 -0600 Subject: [PATCH 150/206] Add utility function to get list of managed crops WITH grasses. define_mgdcrop_list() had excluded foddergrass and switchgrass. New function define_mgdcrop_list_withgrasses includes them. Renamed the existing function to define_mgdcrop_list_nograsses for clarity. --- python/ctsm/crop_calendars/cropcal_module.py | 2 +- python/ctsm/crop_calendars/cropcal_utils.py | 11 ++++++++++- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 4 ++-- python/ctsm/crop_calendars/generate_gdds_functions.py | 4 ++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/python/ctsm/crop_calendars/cropcal_module.py b/python/ctsm/crop_calendars/cropcal_module.py index b87d26816f..08754f8823 100644 --- a/python/ctsm/crop_calendars/cropcal_module.py +++ b/python/ctsm/crop_calendars/cropcal_module.py @@ -345,7 +345,7 @@ def import_output( my_vars, year_1=None, year_n=None, - my_vegtypes=utils.define_mgdcrop_list(), + my_vegtypes=utils.define_mgdcrop_list_nograsses(), sdates_rx_ds=None, gdds_rx_ds=None, verbose=False, diff --git a/python/ctsm/crop_calendars/cropcal_utils.py b/python/ctsm/crop_calendars/cropcal_utils.py index 00ed2413d2..2157dc87f4 100644 --- a/python/ctsm/crop_calendars/cropcal_utils.py +++ b/python/ctsm/crop_calendars/cropcal_utils.py @@ -207,7 +207,7 @@ def is_each_vegtype(this_vegtypelist, this_filter, this_method): return [is_this_vegtype(x, this_filter, this_method) for x in this_vegtypelist] -def define_mgdcrop_list(): +def define_mgdcrop_list_nograsses(): """ List (strings) of managed crops in CLM. """ @@ -216,6 +216,15 @@ def define_mgdcrop_list(): is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] +def define_mgdcrop_list_withgrasses(): + """ + List (strings) of managed crops in CLM. + """ + notcrop_list = ["tree", "c3_arctic_grass", "c3_non-arctic_grass", "c4_grass", "shrub", "unmanaged", "not_vegetated"] + defined_pftlist = define_pftlist() + is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") + return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] + def vegtype_str2int(vegtype_str, vegtype_mainlist=None): """ diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index c0c9adb30b..64cfd804fa 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -144,7 +144,7 @@ def _get_cft_list(crop_list): "cotton", "irrigated_cotton"] """ - mgdcrop_list = utils.define_mgdcrop_list() + mgdcrop_list = utils.define_mgdcrop_list_nograsses() cft_str_list = [] for crop_str in crop_list: cft_str_list += [x for x in mgdcrop_list if crop_str in x] @@ -276,7 +276,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab # Process all crops encoding_dict = {} - for cft_str in utils.define_mgdcrop_list(): + for cft_str in utils.define_mgdcrop_list_nograsses(): cft_int = utils.vegtype_str2int(cft_str)[0] print(f"{cft_str} ({cft_int})") diff --git a/python/ctsm/crop_calendars/generate_gdds_functions.py b/python/ctsm/crop_calendars/generate_gdds_functions.py index 2658e1de87..83c167af00 100644 --- a/python/ctsm/crop_calendars/generate_gdds_functions.py +++ b/python/ctsm/crop_calendars/generate_gdds_functions.py @@ -282,9 +282,9 @@ def import_and_process_1yr( # Get list of crops to include if skip_crops is not None: - crops_to_read = [c for c in utils.define_mgdcrop_list() if c not in skip_crops] + crops_to_read = [c for c in utils.define_mgdcrop_list_nograsses() if c not in skip_crops] else: - crops_to_read = utils.define_mgdcrop_list() + crops_to_read = utils.define_mgdcrop_list_nograsses() print(h1_filelist) dates_ds = import_ds( From ec9f07f2a6cc5f79e6c207e8050db74ff2d857ea Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 14:36:41 -0600 Subject: [PATCH 151/206] generate_gdd20_baseline: Use MGDCROP_LIST constant. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 64cfd804fa..1de6b5a739 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -23,6 +23,7 @@ GRIDDING_VAR_LIST = ["patches1d_ixy", "patches1d_jxy", "lat", "lon"] STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline +MGDCROP_LIST = utils.define_mgdcrop_list_nograsses() def _parse_args(): @@ -144,10 +145,9 @@ def _get_cft_list(crop_list): "cotton", "irrigated_cotton"] """ - mgdcrop_list = utils.define_mgdcrop_list_nograsses() cft_str_list = [] for crop_str in crop_list: - cft_str_list += [x for x in mgdcrop_list if crop_str in x] + cft_str_list += [x for x in MGDCROP_LIST if crop_str in x] return cft_str_list @@ -276,7 +276,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab # Process all crops encoding_dict = {} - for cft_str in utils.define_mgdcrop_list_nograsses(): + for cft_str in MGDCROP_LIST: cft_int = utils.vegtype_str2int(cft_str)[0] print(f"{cft_str} ({cft_int})") From 669f227921e1bdf59157cf1da1c9b4e5176851bc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 14:37:05 -0600 Subject: [PATCH 152/206] generate_gdd20_baseline: Include mgd grasses. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 1de6b5a739..effc8e54ec 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -23,7 +23,7 @@ GRIDDING_VAR_LIST = ["patches1d_ixy", "patches1d_jxy", "lat", "lon"] STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline -MGDCROP_LIST = utils.define_mgdcrop_list_nograsses() +MGDCROP_LIST = utils.define_mgdcrop_list_withgrasses() def _parse_args(): From a62460642403d827b58807cfe5961df0db026c71 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 18 Jul 2024 14:41:46 -0600 Subject: [PATCH 153/206] generate_gdd20_baseline: Include unmanaged crops. --- python/ctsm/crop_calendars/cropcal_utils.py | 8 ++++++++ python/ctsm/crop_calendars/generate_gdd20_baseline.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/cropcal_utils.py b/python/ctsm/crop_calendars/cropcal_utils.py index 2157dc87f4..1a80448c1b 100644 --- a/python/ctsm/crop_calendars/cropcal_utils.py +++ b/python/ctsm/crop_calendars/cropcal_utils.py @@ -206,6 +206,14 @@ def is_each_vegtype(this_vegtypelist, this_filter, this_method): return [is_this_vegtype(x, this_filter, this_method) for x in this_vegtypelist] +def define_crop_list(): + """ + List (strings) of managed crops in CLM. + """ + notcrop_list = ["tree", "c3_arctic_grass", "c3_non-arctic_grass", "c4_grass", "shrub", "not_vegetated"] + defined_pftlist = define_pftlist() + is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") + return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] def define_mgdcrop_list_nograsses(): """ diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index effc8e54ec..3d082d0fde 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -23,7 +23,7 @@ GRIDDING_VAR_LIST = ["patches1d_ixy", "patches1d_jxy", "lat", "lon"] STREAM_YEAR = 2000 # The year specified for stream_yearFirst and stream_yearLast in the call of # shr_strdata_init_from_inline() for sdat_cropcal_gdd20_baseline -MGDCROP_LIST = utils.define_mgdcrop_list_withgrasses() +MGDCROP_LIST = utils.define_crop_list() def _parse_args(): From 753fda3ff0147837231a73c9c728dd9ce47b5997 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 23 Jul 2024 09:27:15 -0600 Subject: [PATCH 154/206] Format with black. --- python/ctsm/crop_calendars/cropcal_utils.py | 22 +++++++++++++++++-- .../crop_calendars/generate_gdd20_baseline.py | 12 +++------- .../crop_calendars/generate_gdds_functions.py | 17 ++++++++------ .../ctsm/crop_calendars/interpolate_gdds.py | 2 +- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/python/ctsm/crop_calendars/cropcal_utils.py b/python/ctsm/crop_calendars/cropcal_utils.py index 1a80448c1b..584046edee 100644 --- a/python/ctsm/crop_calendars/cropcal_utils.py +++ b/python/ctsm/crop_calendars/cropcal_utils.py @@ -206,15 +206,24 @@ def is_each_vegtype(this_vegtypelist, this_filter, this_method): return [is_this_vegtype(x, this_filter, this_method) for x in this_vegtypelist] + def define_crop_list(): """ List (strings) of managed crops in CLM. """ - notcrop_list = ["tree", "c3_arctic_grass", "c3_non-arctic_grass", "c4_grass", "shrub", "not_vegetated"] + notcrop_list = [ + "tree", + "c3_arctic_grass", + "c3_non-arctic_grass", + "c4_grass", + "shrub", + "not_vegetated", + ] defined_pftlist = define_pftlist() is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] + def define_mgdcrop_list_nograsses(): """ List (strings) of managed crops in CLM. @@ -224,11 +233,20 @@ def define_mgdcrop_list_nograsses(): is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] + def define_mgdcrop_list_withgrasses(): """ List (strings) of managed crops in CLM. """ - notcrop_list = ["tree", "c3_arctic_grass", "c3_non-arctic_grass", "c4_grass", "shrub", "unmanaged", "not_vegetated"] + notcrop_list = [ + "tree", + "c3_arctic_grass", + "c3_non-arctic_grass", + "c4_grass", + "shrub", + "unmanaged", + "not_vegetated", + ] defined_pftlist = define_pftlist() is_crop = is_each_vegtype(defined_pftlist, notcrop_list, "notok_contains") return [defined_pftlist[i] for i, x in enumerate(is_crop) if x] diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 3d082d0fde..06d3a9cd21 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -72,27 +72,21 @@ def _parse_args(): parser.add_argument( "-y1", "--first-year", - help=( - "First calendar year to include" - ), + help=("First calendar year to include"), type=int, required=False, ) parser.add_argument( "-yN", "--last-year", - help=( - "Last calendar year to include" - ), + help=("Last calendar year to include"), type=int, required=False, ) parser.add_argument( "-v", "--variable", - help=( - "Which type of variable should be processed?" - ), + help=("Which type of variable should be processed?"), required=False, default="GDDBX", choices=["GDDBX", "GDDB20"], diff --git a/python/ctsm/crop_calendars/generate_gdds_functions.py b/python/ctsm/crop_calendars/generate_gdds_functions.py index 83c167af00..50e2ac3d00 100644 --- a/python/ctsm/crop_calendars/generate_gdds_functions.py +++ b/python/ctsm/crop_calendars/generate_gdds_functions.py @@ -651,10 +651,7 @@ def import_and_process_1yr( ) if save_figs and np.any(np.isnan(gddharv_atharv_p)): if np.all(np.isnan(gddharv_atharv_p)): - log( - logger, - " ❗ All GDDHARV are NaN; should only affect figure" - ) + log(logger, " ❗ All GDDHARV are NaN; should only affect figure") check_gddharv = False else: log( @@ -744,9 +741,15 @@ def import_and_process_1yr( ) else: error(logger, "Unexpected NaN for last season's GDD accumulation.") - if save_figs and check_gddharv and np.any( - np.isnan( - gddharv_yp_list[var][year_index - 1, active_this_year_where_gs_lastyr_indices] + if ( + save_figs + and check_gddharv + and np.any( + np.isnan( + gddharv_yp_list[var][ + year_index - 1, active_this_year_where_gs_lastyr_indices + ] + ) ) ): if incorrectly_daily: diff --git a/python/ctsm/crop_calendars/interpolate_gdds.py b/python/ctsm/crop_calendars/interpolate_gdds.py index 809be98826..830df18c73 100755 --- a/python/ctsm/crop_calendars/interpolate_gdds.py +++ b/python/ctsm/crop_calendars/interpolate_gdds.py @@ -70,7 +70,7 @@ def _setup_process_args(): help="Interpolate variables whose names start with this string", type=str, required=False, - default="gdd1_" + default="gdd1_", ) parser.add_argument( "--overwrite", From c0a3556886e31c5c4bfc55ceccec0925e407b1f4 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Tue, 23 Jul 2024 09:27:53 -0600 Subject: [PATCH 155/206] Add previous commit to .git-blame-ignore-revs. --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 44a697c343..31f63ef8ae 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -48,3 +48,4 @@ aa04d1f7d86cc2503b98b7e2b2d84dbfff6c316b 9660667b1267dcd4150889f5f39db540158be74a 665cf86102e09b4c4c5a140700676dca23bc55a9 045d90f1d80f713eb3ae0ac58f6c2352937f1eb0 +753fda3ff0147837231a73c9c728dd9ce47b5997 From 71ca96d2d8e14fbbe9bc7686c2d905907b4d43d5 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:29:29 -0600 Subject: [PATCH 156/206] Delete an unused ncd_log. --- src/biogeophys/TemperatureType.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index bb579b8031..5ba64f9b1e 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -897,7 +897,7 @@ subroutine Restart(this, bounds, ncid, flag, is_simple_buildtemp, is_prog_buildt use shr_log_mod , only : errMsg => shr_log_errMsg use spmdMod , only : masterproc use abortutils , only : endrun - use ncdio_pio , only : file_desc_t, ncd_double, ncd_int, ncd_log + use ncdio_pio , only : file_desc_t, ncd_double, ncd_int use restUtilMod ! ! !ARGUMENTS: From ad2c7f3af389ad2b2b6464987a06d1200049a813 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:30:31 -0600 Subject: [PATCH 157/206] Delete a troubleshooting log message. --- bld/CLMBuildNamelist.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index ad110b017e..5202717d74 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4200,7 +4200,6 @@ sub setup_logic_cropcal_streams { my $gdd20_season_end_file = $nl->get_value('stream_fldFileName_gdd20_season_end') ; if ( &string_is_undef_or_empty($gdd20_season_start_file) or &string_is_undef_or_empty($gdd20_season_end_file) ) { $log->message($gdd20_season_start_file); - $log->message('abcd'); $log->message($gdd20_season_end_file); $log->fatal_error("If stream_gdd20_seasons is true, gdd20 season start and end files must be provided." ); } From 592476f7c6ae5751b6360c372ed70ad4f94af0c7 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:34:29 -0600 Subject: [PATCH 158/206] Improve comments/error at "Handle invalid gdd20 season values." --- src/cpl/share_esmf/cropcalStreamMod.F90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index ef3bab03c5..d17ce1c259 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -793,9 +793,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Handle invalid gdd20 season values if (any(gdd20_season_starts(begp:endp) < 1._r8 .or. gdd20_season_ends(begp:endp) < 1._r8)) then - ! Fail if not allowing fallback to paramfile sowing windows + ! Fail if not allowing fallback to paramfile sowing windows. Only need to check for + ! values < 1 because values outside [1, 366] are set to -1 above. if ((.not. allow_invalid_gdd20_season_inputs) .and. any(gdd20_season_starts(begp:endp) < 1._r8 .and. patch%wtgcell(begp:endp) > 0._r8 .and. patch%itype(begp:endp) >= npcropmin)) then - write(iulog, *) 'At least one crop in one gridcell has invalid gdd20 season start date(s). To ignore and fall back to paramfile sowing windows, set allow_invalid_gdd20_season_inputs to .true.' + write(iulog, *) 'At least one crop in one gridcell has invalid gdd20 season start and/or end date(s). To ignore and fall back to paramfile sowing windows for such crop-gridcells, set allow_invalid_gdd20_season_inputs to .true.' write(iulog, *) 'Affected crops:' do ivt = npcropmin, mxpft do fp = 1, num_pcropp From 013028c4687f564953ef5443e159b02f00220297 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:35:49 -0600 Subject: [PATCH 159/206] Replace SSR with Sam Rabin. --- src/biogeochem/CNPhenologyMod.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index d1d3c85b21..159e688dbd 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2699,7 +2699,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & did_rx_gdds = .true. if (adapt_cropcal_rx_cultivar_gdds .and. crop_inst%gdd20_baseline_patch(p) > min_gdd20_baseline) then gddmaturity(p) = gddmaturity(p) * gdd20 / crop_inst%gdd20_baseline_patch(p) - !TODO SSR: Set maximum and minimum gddmaturity + !TODO Sam Rabin: Set maximum and minimum gddmaturity end if else if (ivt(p) == nwwheat .or. ivt(p) == nirrig_wwheat) then gddmaturity(p) = hybgdd(ivt(p)) @@ -2716,11 +2716,11 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & ivt(p) == nmiscanthus .or. ivt(p) == nirrig_miscanthus .or. & ivt(p) == nswitchgrass .or. ivt(p) == nirrig_switchgrass) then gddmaturity(p) = max(950._r8, min(gdd20*0.85_r8, hybgdd(ivt(p)))) - if (do_plant_normal) then ! TODO SSR: Add ".and. .not. do_plant_prescribed"? + if (do_plant_normal) then ! TODO Sam Rabin: Add ".and. .not. do_plant_prescribed"? gddmaturity(p) = max(950._r8, min(gddmaturity(p)+150._r8, 1850._r8)) end if else - ! TODO SSR: Add more descriptive error message + ! TODO Sam Rabin: Add more descriptive error message call endrun(msg="Stopping") end if From 96c7c2d96cf39347cd6e259366f5d321971e75eb Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:37:30 -0600 Subject: [PATCH 160/206] import_ds.py: Delete a commented-out line. --- python/ctsm/crop_calendars/import_ds.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/ctsm/crop_calendars/import_ds.py b/python/ctsm/crop_calendars/import_ds.py index 0526d3c720..486757492f 100644 --- a/python/ctsm/crop_calendars/import_ds.py +++ b/python/ctsm/crop_calendars/import_ds.py @@ -59,7 +59,6 @@ def manual_mfdataset(filelist, my_vars, my_vegtypes, time_slice): compat="override", coords="all", dim="time", - # combine="nested", ) return ds_out From 1d9e93989c656c91b44389a307bf07602727bcfd Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:38:29 -0600 Subject: [PATCH 161/206] Improve a comment in run_sys_tests.py. --- python/ctsm/run_sys_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/run_sys_tests.py b/python/ctsm/run_sys_tests.py index 9961fd325d..b3a3b78379 100644 --- a/python/ctsm/run_sys_tests.py +++ b/python/ctsm/run_sys_tests.py @@ -736,7 +736,7 @@ def _check_py_env(test_attributes): # whether import is possible. # pylint: disable=import-error disable - # Check requirements for using modify_fsurdat, if needed + # Check requirements for using modify_fsurdat Python module, if needed modify_fsurdat_users = ["FSURDATMODIFYCTSM", "RXCROPMATURITY"] if any(any(u in t for u in modify_fsurdat_users) for t in test_attributes): try: From 78d9f98060c62ec486b32ef20dd2b14718d3c428 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:40:49 -0600 Subject: [PATCH 162/206] Improve error messages in PlantCrop(). --- 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 159e688dbd..855a1476f8 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2720,7 +2720,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & gddmaturity(p) = max(950._r8, min(gddmaturity(p)+150._r8, 1850._r8)) end if else - ! TODO Sam Rabin: Add more descriptive error message + write(iulog, *) 'ERROR: PlantCrop(): unrecognized ivt for GDD target: ',ivt(p) call endrun(msg="Stopping") end if @@ -2729,7 +2729,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & if (gddmaturity(p) < min_gddmaturity) then if (use_cropcal_rx_cultivar_gdds .or. generate_crop_gdds) then if (did_rx_gdds) then - write(iulog,*) 'Some patch with ivt ',ivt(p),' has rx gddmaturity',gddmaturity(p),'; using min_gddmaturity instead (',min_gddmaturity,')' + write(iulog,*) 'Some patch with ivt ',ivt(p),' has rx gddmaturity ',gddmaturity(p),'; using min_gddmaturity instead (',min_gddmaturity,')' end if gddmaturity(p) = min_gddmaturity else From d3e6cbf13c4347ad2e61bbd5841b1bb9a7363fb8 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:41:54 -0600 Subject: [PATCH 163/206] CropType: Fix a comment. --- src/biogeochem/CropType.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeochem/CropType.F90 b/src/biogeochem/CropType.F90 index 77da895135..0f650a4a9f 100644 --- a/src/biogeochem/CropType.F90 +++ b/src/biogeochem/CropType.F90 @@ -54,7 +54,7 @@ module CropType real(r8), pointer :: rx_cultivar_gdds_thisyr_patch (:,:) ! all cultivar GDD targets for this patch this year (ddays) [patch, mxsowings] real(r8), pointer :: gdd20_baseline_patch (:) ! GDD20 baseline for this patch (ddays) [patch] real(r8), pointer :: gdd20_season_start_patch(:) ! gdd20 season start date for this patch (day of year) [patch]. Real to enable history field. - real(r8), pointer :: gdd20_season_end_patch (:) ! gdd20 season end date for this patch (day of year) [patch]/ Real to enable history field. + real(r8), pointer :: gdd20_season_end_patch (:) ! gdd20 season end date for this patch (day of year) [patch]. Real to enable history field. real(r8), pointer :: sdates_thisyr_patch (:,:) ! all actual sowing dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_starts_thisyr_patch(:,:) ! all sowing window start dates for this patch this year (day of year) [patch, mxsowings] real(r8), pointer :: swindow_ends_thisyr_patch (:,:) ! all sowing window end dates for this patch this year (day of year) [patch, mxsowings] From 6546a681b92f736e460a106b33b4523b290f1cbc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:42:57 -0600 Subject: [PATCH 164/206] TemperatureType Restart(): Delete unused p. --- src/biogeophys/TemperatureType.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 5ba64f9b1e..787efa81a0 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -909,7 +909,7 @@ subroutine Restart(this, bounds, ncid, flag, is_simple_buildtemp, is_prog_buildt logical , intent(in) :: is_prog_buildtemp ! Prognostic building temp is being used ! ! !LOCAL VARIABLES: - integer :: j,c,p ! indices + integer :: j,c ! indices logical :: readvar ! determine if variable is on initial file integer :: idata !----------------------------------------------------------------------- From c96ce350ed85600c8525b655f3acbfd2f6831059 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:49:19 -0600 Subject: [PATCH 165/206] Delete unused sowingWindows testmod. --- .../testmods_dirs/clm/sowingWindows/include_user_mods | 1 - .../testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm | 5 ----- 2 files changed, 6 deletions(-) delete mode 100644 cime_config/testdefs/testmods_dirs/clm/sowingWindows/include_user_mods delete mode 100644 cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/sowingWindows/include_user_mods deleted file mode 100644 index fe0e18cf88..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../default diff --git a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm deleted file mode 100644 index 7024e99b96..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/sowingWindows/user_nl_clm +++ /dev/null @@ -1,5 +0,0 @@ -stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc' -stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_ends_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc' -stream_meshfile_cropcal = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/swindow_starts_ggcmi_crop_calendar_phase3_v1.01.2000-2000.20231005_145103.tweaked_latlons.nc' -stream_year_first_cropcal_swindows = 2000 -stream_year_last_cropcal_swindows = 2000 From b96ec9107fe8e5b02260689190cd79799f589938 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:52:49 -0600 Subject: [PATCH 166/206] check_rxboth_run.py: Improve error message. --- python/ctsm/crop_calendars/check_rxboth_run.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/check_rxboth_run.py b/python/ctsm/crop_calendars/check_rxboth_run.py index a1014b5e66..fa4affd220 100644 --- a/python/ctsm/crop_calendars/check_rxboth_run.py +++ b/python/ctsm/crop_calendars/check_rxboth_run.py @@ -156,7 +156,16 @@ def main(argv): any_bad = any_bad or gdds_not_obeyed if any_bad: - raise RuntimeError("Unexpected behavior in rxboth run") + msg = "\n ".join( + [ + "Unexpected behavior in rxboth run:", + f"any_bad_import_output: {any_bad_import_output}", + f"any_bad_check_const_vars: {any_bad_check_const_vars}", + f"sdate_not_obeyed: {sdate_not_obeyed}", + f"gdds_not_obeyed: {gdds_not_obeyed}", + ] + ) + raise RuntimeError(msg) if __name__ == "__main__": From 876e23bb4fe4c6274e5f328212b4377ae1dd9c1d Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 15:59:46 -0600 Subject: [PATCH 167/206] interpolate_gdds.py: Strict check for prefix match. --- python/ctsm/crop_calendars/interpolate_gdds.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/interpolate_gdds.py b/python/ctsm/crop_calendars/interpolate_gdds.py index 830df18c73..92bb112e3f 100755 --- a/python/ctsm/crop_calendars/interpolate_gdds.py +++ b/python/ctsm/crop_calendars/interpolate_gdds.py @@ -6,6 +6,7 @@ import sys import argparse import logging +import re import xarray as xr # -- add python/ctsm to path (needed if we want to run this stand-alone) @@ -121,7 +122,7 @@ def interpolate_gdds(args): if "lat" not in ds_in[var].dims and "lon" not in ds_in[var].dims: print(f"Skipping variable {var} with dimensions {ds_in[var].dims}") continue - elif args.variable_prefix not in var: + if not re.compile("^" + args.variable_prefix).match(var) print(f"Unexpected variable {var} on input file. Skipping.") continue if args.dry_run: From fecad299757de00f235217add67f70e60f7cda33 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:06:22 -0600 Subject: [PATCH 168/206] Delete unneeded cropAnnOutputMonthly testmod. --- cime_config/testdefs/testlist_clm.xml | 4 ++-- .../testmods_dirs/clm/cropAnnOutputMonthly/user_nl_clm | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 cime_config/testdefs/testmods_dirs/clm/cropAnnOutputMonthly/user_nl_clm diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index c2926a25e7..e5a1ae7ef4 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3794,7 +3794,7 @@ - + @@ -3803,7 +3803,7 @@ - + diff --git a/cime_config/testdefs/testmods_dirs/clm/cropAnnOutputMonthly/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/cropAnnOutputMonthly/user_nl_clm deleted file mode 100644 index 1c47a2ebd1..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/cropAnnOutputMonthly/user_nl_clm +++ /dev/null @@ -1,3 +0,0 @@ -! These variables SHOULD only be saved annually in real runs, but for testing purposes it's fine to have them monthly. -! Modifies h2 history file defined in crop testmod. -hist_nhtfrq(3) = 0 From c94aeeab68a6f98188f0f94127b3685d2c89dd49 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:09:35 -0600 Subject: [PATCH 169/206] Add READMEs to testmods decStart and midDecStart. --- cime_config/testdefs/testmods_dirs/clm/decStart/README | 1 + cime_config/testdefs/testmods_dirs/clm/midDecStart/README | 1 + 2 files changed, 2 insertions(+) create mode 100644 cime_config/testdefs/testmods_dirs/clm/decStart/README create mode 100644 cime_config/testdefs/testmods_dirs/clm/midDecStart/README diff --git a/cime_config/testdefs/testmods_dirs/clm/decStart/README b/cime_config/testdefs/testmods_dirs/clm/decStart/README new file mode 100644 index 0000000000..7cdab6abfd --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/decStart/README @@ -0,0 +1 @@ +Use midDecStart instead of decStart if you want ERP/ERS/etc. tests longer than 2 days to be able to have the split in December instead of January (i.e., before rather than after new year). \ No newline at end of file diff --git a/cime_config/testdefs/testmods_dirs/clm/midDecStart/README b/cime_config/testdefs/testmods_dirs/clm/midDecStart/README new file mode 100644 index 0000000000..7cdab6abfd --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/midDecStart/README @@ -0,0 +1 @@ +Use midDecStart instead of decStart if you want ERP/ERS/etc. tests longer than 2 days to be able to have the split in December instead of January (i.e., before rather than after new year). \ No newline at end of file From 833d4f67e888c747cfb2c35d8ae8354974987061 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:11:48 -0600 Subject: [PATCH 170/206] import_output(): Bugfix in call of check_v0_le_v1(). --- python/ctsm/crop_calendars/cropcal_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/cropcal_module.py b/python/ctsm/crop_calendars/cropcal_module.py index 08754f8823..927bdc9100 100644 --- a/python/ctsm/crop_calendars/cropcal_module.py +++ b/python/ctsm/crop_calendars/cropcal_module.py @@ -425,7 +425,7 @@ def import_output( # Check that e.g., GDDACCUM <= HUI for var_list in [["GDDACCUM", "HUI"], ["SYEARS", "HYEARS"]]: if all(v in this_ds_gs for v in var_list): - any_bad = check_v0_le_v1( + any_bad = any_bad or check_v0_le_v1( this_ds_gs, var_list, both_nan_ok=True, throw_error=throw_errors ) From 56523e4cbb4969e9648adb2601c40d847cb9d408 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:15:28 -0600 Subject: [PATCH 171/206] Remove 1-degree RXCROPMATURITY test from rxcropmaturity, crop_calendars suites. --- cime_config/testdefs/testlist_clm.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index e5a1ae7ef4..5d3fbeb11a 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3663,8 +3663,6 @@ - - From d06e8642a180190fea9f31c0a6af042758c31b81 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:18:38 -0600 Subject: [PATCH 172/206] Remove unneeded RxCropCalsAdaptFlush testmod. --- .../testmods_dirs/clm/RxCropCalsAdaptFlush/include_user_mods | 1 - .../testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/user_nl_clm | 2 -- 2 files changed, 3 deletions(-) delete mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/include_user_mods delete mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/user_nl_clm diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/include_user_mods deleted file mode 100644 index af5fe8591e..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../RxCropCalsAdapt diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/user_nl_clm deleted file mode 100644 index 4c6af0f6d2..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptFlush/user_nl_clm +++ /dev/null @@ -1,2 +0,0 @@ - -flush_gdd20 = .true. From a2bf12483a8fd1f33488b6df09ffe2b291aa2f43 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:21:18 -0600 Subject: [PATCH 173/206] generate_gdd20_baseline.py: Add a clarifying comment. --- python/ctsm/crop_calendars/generate_gdd20_baseline.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index 06d3a9cd21..13668fc850 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -284,8 +284,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab this_da = dummy_da print(" dummy GDD20") else: - # this_da = ds_in[gddn].fillna(MISSING_FILL) - this_da = ds_in[gddn_str] + this_da = ds_in[gddn_str] # Already did ds_in.mean(dim="time") above this_da = _add_time_axis(this_da) print(f" {gddn_str}") this_da = this_da.fillna(MISSING_RX_GDD_VAL) From c9ff0ee33410b82462fbf4fc876b4130811e13cc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:24:14 -0600 Subject: [PATCH 174/206] PlantCrop(): Resolve (dismiss) a TODO. --- src/biogeochem/CNPhenologyMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/biogeochem/CNPhenologyMod.F90 b/src/biogeochem/CNPhenologyMod.F90 index 855a1476f8..1fb13d32eb 100644 --- a/src/biogeochem/CNPhenologyMod.F90 +++ b/src/biogeochem/CNPhenologyMod.F90 @@ -2716,7 +2716,7 @@ subroutine PlantCrop(p, leafcn_in, jday, kyr, do_plant_normal, & ivt(p) == nmiscanthus .or. ivt(p) == nirrig_miscanthus .or. & ivt(p) == nswitchgrass .or. ivt(p) == nirrig_switchgrass) then gddmaturity(p) = max(950._r8, min(gdd20*0.85_r8, hybgdd(ivt(p)))) - if (do_plant_normal) then ! TODO Sam Rabin: Add ".and. .not. do_plant_prescribed"? + if (do_plant_normal) then gddmaturity(p) = max(950._r8, min(gddmaturity(p)+150._r8, 1850._r8)) end if else From bc055919bd68719b53d6563d1f7a37976ec11c44 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:47:49 -0600 Subject: [PATCH 175/206] Improve flush_gdd20 restart logic. Previously, TemperatureType%flush_gdd20 would get set to true upon restart unless it was read and false. This change makes it so that: - If TemperatureType%flush_gdd20 isn't read from the restart file, it falls back to the flush_gdd20 from clm_varctl. - If TemperatureType%flush_gdd20 IS read from the restart file, it is set to true if it's true on the restart file OR if the flush_gdd20 from clm_varctl is true. --- src/biogeophys/TemperatureType.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 787efa81a0..98f6d0f638 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -1148,10 +1148,10 @@ subroutine Restart(this, bounds, ncid, flag, is_simple_buildtemp, is_prog_buildt long_name='Flag indicating that GDD20 values need to be flushed', & units='none', interpinic_flag='copy', readvar=readvar, data=idata) if (flag == 'read') then - if (readvar .and. idata == 0) then - this%flush_gdd20 = .false. + if (readvar) then + this%flush_gdd20 = flush_gdd20 .or. idata == 1 else - this%flush_gdd20 = .true. + this%flush_gdd20 = flush_gdd20 end if end if end if From d298aef9438d0b13bf37ccd162e1f5a303f6e4b4 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 16:53:54 -0600 Subject: [PATCH 176/206] Do not use flush_gdd20 from clm_varctl in flush decision. Not directly, at least. Instead, rely on TemperatureType%flush_gdd20 having been set correctly. --- src/biogeophys/TemperatureType.F90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 98f6d0f638..4929b24307 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -169,6 +169,9 @@ subroutine Init(this, bounds, & em_perroad_lun(bounds%begl:bounds%endl), & is_simple_buildtemp, is_prog_buildtemp) + ! Finish up + this%flush_gdd20 = flush_gdd20 + end subroutine Init !------------------------------------------------------------------------ @@ -1693,13 +1696,13 @@ subroutine UpdateAccVars (this, bounds, crop_inst) ! Accumulate and extract running 20-year means if (is_end_curr_year()) then ! Flush, if needed - if (flush_gdd20 .or. this%flush_gdd20) then + if (this%flush_gdd20) then write(iulog, *) 'Flushing GDD20 variables' call markreset_accum_field('GDD020') call markreset_accum_field('GDD820') call markreset_accum_field('GDD1020') this%flush_gdd20 = .false. - flush_gdd20 = .false. + flush_gdd20 = .false. ! Shouldn't be necessary, because flush_gdd20 shouldn't be considered after Init and Restart. But just in case... end if call update_accum_field ('GDD020', this%gdd0_patch, nstep) call extract_accum_field ('GDD020', this%gdd020_patch, nstep) From 827632e12b1f55253c8c75b999f4d8abd6f87b7a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 18:08:09 -0600 Subject: [PATCH 177/206] Actually don't use TemperatureType%flush_gdd20 at all. Having that alongside flush_gdd20 from clm_varctl means that they can disagree when restarting, and it's not obvious which should win. Note that the user being able to request a flush at all is dangerous. If they start a run with it true, they might continue that run without setting it to false. Instead, the restart decision should be made by the model---if a gridcell has different prescribed gdd20 season from what its restart file says, then that gridcell should be flushed. --- src/biogeophys/TemperatureType.F90 | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 4929b24307..71722d84fb 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -95,7 +95,6 @@ module TemperatureType real(r8), pointer :: gdd020_patch (:) ! patch 20-year average of gdd0 (ddays) real(r8), pointer :: gdd820_patch (:) ! patch 20-year average of gdd8 (ddays) real(r8), pointer :: gdd1020_patch (:) ! patch 20-year average of gdd10 (ddays) - logical :: flush_gdd20 = .false. ! whether accumulated GDD20s need to be flushed ! Heat content real(r8), pointer :: beta_col (:) ! coefficient of convective velocity [-] @@ -169,9 +168,6 @@ subroutine Init(this, bounds, & em_perroad_lun(bounds%begl:bounds%endl), & is_simple_buildtemp, is_prog_buildtemp) - ! Finish up - this%flush_gdd20 = flush_gdd20 - end subroutine Init !------------------------------------------------------------------------ @@ -1139,26 +1135,6 @@ subroutine Restart(this, bounds, ncid, flag, is_simple_buildtemp, is_prog_buildt end if end if - if (use_crop) then - if (flag == 'write') then - if (this%flush_gdd20) then - idata = 1 - else - idata = 0 - end if - end if - call restartvar(ncid=ncid, flag=flag, varname='flush_gdd20', xtype=ncd_int, & - long_name='Flag indicating that GDD20 values need to be flushed', & - units='none', interpinic_flag='copy', readvar=readvar, data=idata) - if (flag == 'read') then - if (readvar) then - this%flush_gdd20 = flush_gdd20 .or. idata == 1 - else - this%flush_gdd20 = flush_gdd20 - end if - end if - end if - end subroutine Restart @@ -1696,13 +1672,12 @@ subroutine UpdateAccVars (this, bounds, crop_inst) ! Accumulate and extract running 20-year means if (is_end_curr_year()) then ! Flush, if needed - if (this%flush_gdd20) then + if (flush_gdd20) then write(iulog, *) 'Flushing GDD20 variables' call markreset_accum_field('GDD020') call markreset_accum_field('GDD820') call markreset_accum_field('GDD1020') - this%flush_gdd20 = .false. - flush_gdd20 = .false. ! Shouldn't be necessary, because flush_gdd20 shouldn't be considered after Init and Restart. But just in case... + flush_gdd20 = .false. end if call update_accum_field ('GDD020', this%gdd0_patch, nstep) call extract_accum_field ('GDD020', this%gdd020_patch, nstep) From 4ab30a5ddb9ac54cba659abc8682f1152e8eb844 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 18:20:21 -0600 Subject: [PATCH 178/206] crop_calendars Python stuff now includes crop grasses. --- python/ctsm/crop_calendars/cropcal_module.py | 2 +- python/ctsm/crop_calendars/generate_gdds_functions.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/ctsm/crop_calendars/cropcal_module.py b/python/ctsm/crop_calendars/cropcal_module.py index 927bdc9100..719d352665 100644 --- a/python/ctsm/crop_calendars/cropcal_module.py +++ b/python/ctsm/crop_calendars/cropcal_module.py @@ -345,7 +345,7 @@ def import_output( my_vars, year_1=None, year_n=None, - my_vegtypes=utils.define_mgdcrop_list_nograsses(), + my_vegtypes=utils.define_mgdcrop_list_withgrasses(), sdates_rx_ds=None, gdds_rx_ds=None, verbose=False, diff --git a/python/ctsm/crop_calendars/generate_gdds_functions.py b/python/ctsm/crop_calendars/generate_gdds_functions.py index 50e2ac3d00..14bd6b2e40 100644 --- a/python/ctsm/crop_calendars/generate_gdds_functions.py +++ b/python/ctsm/crop_calendars/generate_gdds_functions.py @@ -282,9 +282,9 @@ def import_and_process_1yr( # Get list of crops to include if skip_crops is not None: - crops_to_read = [c for c in utils.define_mgdcrop_list_nograsses() if c not in skip_crops] + crops_to_read = [c for c in utils.define_mgdcrop_list_withgrasses() if c not in skip_crops] else: - crops_to_read = utils.define_mgdcrop_list_nograsses() + crops_to_read = utils.define_mgdcrop_list_withgrasses() print(h1_filelist) dates_ds = import_ds( From aeb4182302dc63bc88a3c8a5186233901f310f37 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 18:22:58 -0600 Subject: [PATCH 179/206] interpolate_gdds.py: Syntax fix. --- python/ctsm/crop_calendars/interpolate_gdds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctsm/crop_calendars/interpolate_gdds.py b/python/ctsm/crop_calendars/interpolate_gdds.py index 92bb112e3f..123d40af38 100755 --- a/python/ctsm/crop_calendars/interpolate_gdds.py +++ b/python/ctsm/crop_calendars/interpolate_gdds.py @@ -122,7 +122,7 @@ def interpolate_gdds(args): if "lat" not in ds_in[var].dims and "lon" not in ds_in[var].dims: print(f"Skipping variable {var} with dimensions {ds_in[var].dims}") continue - if not re.compile("^" + args.variable_prefix).match(var) + if not re.compile("^" + args.variable_prefix).match(var): print(f"Unexpected variable {var} on input file. Skipping.") continue if args.dry_run: From 929ec5d6c43a4234e35b060b02223888466554fc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 23:08:27 -0600 Subject: [PATCH 180/206] Skip switchgrass in RXCROPMATURITY. --- cime_config/SystemTests/rxcropmaturity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cime_config/SystemTests/rxcropmaturity.py b/cime_config/SystemTests/rxcropmaturity.py index 46ed8f8be3..965398821f 100644 --- a/cime_config/SystemTests/rxcropmaturity.py +++ b/cime_config/SystemTests/rxcropmaturity.py @@ -438,7 +438,7 @@ def _run_generate_gdds(self, case_gddgen): f"--sdates-file {sdates_file}", f"--hdates-file {hdates_file}", f"--output-dir generate_gdds_out", - f"--skip-crops miscanthus,irrigated_miscanthus", + f"--skip-crops miscanthus,irrigated_miscanthus,switchgrass,irrigated_switchgrass", ] ) stu.run_python_script( From b70c156fed30f835a8b32cbca4d82dcbfe4888b2 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Wed, 24 Jul 2024 23:16:00 -0600 Subject: [PATCH 181/206] Manually specify sowing date file for GddGen testmod. --- cime_config/testdefs/testmods_dirs/clm/GddGen/user_nl_clm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cime_config/testdefs/testmods_dirs/clm/GddGen/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/GddGen/user_nl_clm index 87cdd5d5b5..cfde517fd9 100644 --- a/cime_config/testdefs/testmods_dirs/clm/GddGen/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/GddGen/user_nl_clm @@ -1,4 +1,6 @@ cropcals_rx = .true. +stream_fldFileName_swindow_start = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc' +stream_fldFileName_swindow_end = '$DIN_LOC_ROOT/lnd/clm2/cropdata/calendars/processed/sdates_ggcmi_crop_calendar_phase3_v1.01_nninterp-hcru_hcru_mt13.2000-2000.20230728_165845.tweaked_latlons.nc' stream_fldFileName_cultivar_gdds = '' generate_crop_gdds = .true. use_mxmat = .false. From 7400d5bb153bd64be3eeb7f560e85128d736ed5c Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 05:14:01 -0600 Subject: [PATCH 182/206] Add izumi_nag.clm-RxCropCalsAdaptGGCMI test to expected fails. SMS_P128x1_Lm25.f10_f10_mg37.IHistClm60BgcCrop.izumi_nag.clm-RxCropCalsAdaptGGCMI --- cime_config/testdefs/ExpectedTestFails.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml index 03eb6a157d..e5e1410d1e 100644 --- a/cime_config/testdefs/ExpectedTestFails.xml +++ b/cime_config/testdefs/ExpectedTestFails.xml @@ -170,6 +170,13 @@ + + + FAIL + #2659 + + + From 388c7ed45e9561206a88efeecdb6faec054522d1 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 05:55:45 -0600 Subject: [PATCH 183/206] Move stream_gdd20_seasons to cropCalStreamMod. --- bld/namelist_files/namelist_definition_ctsm.xml | 2 +- src/biogeophys/TemperatureType.F90 | 7 +++++-- src/cpl/share_esmf/cropcalStreamMod.F90 | 5 ++++- src/main/clm_varctl.F90 | 1 - src/main/controlMod.F90 | 4 +--- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 31ce220d9c..a24f59339a 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -1839,7 +1839,7 @@ Filename of input stream data for baseline GDD20 values + group="cropcal_streams" valid_values="" > Set this to true to read gdd20 accumulation season start and end dates from stream files, rather than using hard-coded hemisphere-specific "warm seasons." diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 71722d84fb..707218cc27 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -8,7 +8,7 @@ module TemperatureType use decompMod , only : bounds_type use abortutils , only : endrun use clm_varctl , only : use_cndv, iulog, use_luna, use_crop, use_biomass_heat_storage - use clm_varctl , only : stream_gdd20_seasons, flush_gdd20 + use clm_varctl , only : flush_gdd20 use clm_varpar , only : nlevsno, nlevgrnd, nlevlak, nlevurb, nlevmaxurbgrnd use clm_varcon , only : spval, ispval use GridcellType , only : grc @@ -1401,6 +1401,7 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d real(r8) :: lat ! latitude integer :: gdd20_season_start, gdd20_season_end integer :: jday ! Julian day of year (1, ..., 366) + logical :: stream_gdd20_seasons_tt ! Local derivation of this to avoid circular dependency associate( & gdd20_season_starts => crop_inst%gdd20_season_start_patch, & @@ -1432,6 +1433,8 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d end if write(field_name, format_string) "GDD",basetemp_int + stream_gdd20_seasons_tt = any(gdd20_season_starts(begp:endp) > 0.5_r8) .and. any(gdd20_season_starts(begp:endp) < 366.5_r8) + do p = begp,endp ! Avoid unnecessary calculations over inactive points @@ -1447,7 +1450,7 @@ subroutine UpdateAccVars_CropGDDs(this, rbufslp, begp, endp, month, day, secs, d ((month > 9 .or. month < 4) .and. lat < 0._r8) ! Replace with read-in gdd20 accumulation season, if needed and valid ! (If these aren't being read in or they're invalid, they'll be -1) - if (stream_gdd20_seasons .and. patch%itype(p) >= npcropmin) then + if (stream_gdd20_seasons_tt .and. patch%itype(p) >= npcropmin) then gdd20_season_start = int(gdd20_season_starts(p)) gdd20_season_end = int(gdd20_season_ends(p)) if (gdd20_season_start >= 1 .and. gdd20_season_end >= 1) then diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index d17ce1c259..85218a0e1c 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -16,7 +16,6 @@ module cropcalStreamMod use clm_varctl , only : iulog use clm_varctl , only : use_cropcal_rx_swindows, use_cropcal_rx_cultivar_gdds, use_cropcal_streams use clm_varctl , only : adapt_cropcal_rx_cultivar_gdds - use clm_varctl , only : stream_gdd20_seasons use clm_varpar , only : mxpft use clm_varpar , only : mxsowings use perf_mod , only : t_startf, t_stopf @@ -53,6 +52,7 @@ module cropcalStreamMod character(len=CL) :: stream_fldFileName_gdd20_baseline ! GDD20 baseline stream filename to read logical :: cropcals_rx ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash logical :: cropcals_rx_adapt ! Used only for setting input files in namelist; does nothing in code, but needs to be here so namelist read doesn't crash + logical :: stream_gdd20_seasons ! Read start and end dates for gdd20 seasons from streams instead of using hemisphere-specific values logical :: allow_invalid_gdd20_season_inputs ! Fall back on hemisphere "warm periods" in cases of invalid values in stream_fldFileName_gdd20_season_start and _end? character(len=CL) :: stream_fldFileName_gdd20_season_start ! Stream filename to read for start of gdd20 season character(len=CL) :: stream_fldFileName_gdd20_season_end ! Stream filename to read for end of gdd20 season @@ -113,6 +113,7 @@ subroutine cropcal_init(bounds) stream_meshfile_cropcal, & cropcals_rx, & cropcals_rx_adapt, & + stream_gdd20_seasons, & allow_invalid_gdd20_season_inputs, & stream_fldFileName_gdd20_season_start, & stream_fldFileName_gdd20_season_end @@ -130,6 +131,7 @@ subroutine cropcal_init(bounds) stream_fldFileName_swindow_end = '' stream_fldFileName_cultivar_gdds = '' stream_fldFileName_gdd20_baseline = '' + stream_gdd20_seasons = .false. allow_invalid_gdd20_season_inputs = .false. stream_fldFileName_gdd20_season_start = '' stream_fldFileName_gdd20_season_end = '' @@ -173,6 +175,7 @@ subroutine cropcal_init(bounds) call shr_mpi_bcast(stream_fldFileName_cultivar_gdds, mpicom) call shr_mpi_bcast(stream_fldFileName_gdd20_baseline, mpicom) call shr_mpi_bcast(stream_meshfile_cropcal , mpicom) + call shr_mpi_bcast(stream_gdd20_seasons, mpicom) call shr_mpi_bcast(allow_invalid_gdd20_season_inputs, mpicom) call shr_mpi_bcast(stream_fldFileName_gdd20_season_start, mpicom) call shr_mpi_bcast(stream_fldFileName_gdd20_season_end, mpicom) diff --git a/src/main/clm_varctl.F90 b/src/main/clm_varctl.F90 index a1ca2fd3f7..678f386f23 100644 --- a/src/main/clm_varctl.F90 +++ b/src/main/clm_varctl.F90 @@ -392,7 +392,6 @@ module clm_varctl logical, public :: use_cropcal_rx_swindows = .false. logical, public :: use_cropcal_rx_cultivar_gdds = .false. logical, public :: adapt_cropcal_rx_cultivar_gdds = .false. - logical, public :: stream_gdd20_seasons = .false. logical, public :: flush_gdd20 = .false. !---------------------------------------------------------- diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index 98e0d1afac..634128798b 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -302,7 +302,7 @@ subroutine control_init(dtime) use_lch4, use_nitrif_denitrif, use_extralakelayers, & use_vichydro, use_cn, use_cndv, use_crop, use_fertilizer, & use_grainproduct, use_snicar_frc, use_vancouver, use_mexicocity, use_noio, & - use_nguardrail, crop_residue_removal_frac, stream_gdd20_seasons, flush_gdd20 + use_nguardrail, crop_residue_removal_frac, flush_gdd20 ! SNICAR namelist /clm_inparm/ & @@ -711,7 +711,6 @@ subroutine control_spmd() call mpi_bcast (use_cndv, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_nguardrail, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_crop, 1, MPI_LOGICAL, 0, mpicom, ier) - call mpi_bcast (stream_gdd20_seasons, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (flush_gdd20, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_fertilizer, 1, MPI_LOGICAL, 0, mpicom, ier) call mpi_bcast (use_grainproduct, 1, MPI_LOGICAL, 0, mpicom, ier) @@ -985,7 +984,6 @@ subroutine control_print () write(iulog,*) ' use_cn = ', use_cn write(iulog,*) ' use_cndv = ', use_cndv write(iulog,*) ' use_crop = ', use_crop - write(iulog,*) ' stream_gdd20_seasons = ', stream_gdd20_seasons write(iulog,*) ' flush_gdd20 = ', flush_gdd20 write(iulog,*) ' use_fertilizer = ', use_fertilizer write(iulog,*) ' use_grainproduct = ', use_grainproduct From 0d4a300112f16ee35332af5e72e8016ea1990c8a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 14:21:40 -0600 Subject: [PATCH 184/206] Fix setting of stream_fldFileName_gdd20_baseline. --- bld/CLMBuildNamelist.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 232b15c3b1..c2d23ce962 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -4247,7 +4247,7 @@ sub setup_logic_cropcal_streams { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_swindow_end'); add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldfilename_cultivar_gdds'); if ( &value_is_true($cropcals_rx_adapt) ) { - add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_baseline'); + add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'stream_fldFileName_gdd20_baseline', 'stream_gdd20_seasons'=>$stream_gdd20_seasons); } } From 649547c5015e59ab582436c8c1bcfc39e296dd8f Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 16:21:16 -0600 Subject: [PATCH 185/206] Don't specify PE layout for SMS crop_calendars tests. --- cime_config/testdefs/testlist_clm.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index b8e3dd2f31..ecdeeb659d 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3560,7 +3560,7 @@ - + @@ -3578,7 +3578,7 @@ - + From 26ce0bbb0ac7835f11251c0585eec778b4a79859 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 16:04:00 -0600 Subject: [PATCH 186/206] Add izumi versions of most derecho crop_calendars tests. --- cime_config/testdefs/testlist_clm.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index ecdeeb659d..18e66d4d64 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3563,6 +3563,7 @@ + @@ -3581,6 +3582,7 @@ + @@ -3593,6 +3595,7 @@ + @@ -3603,6 +3606,7 @@ + @@ -3613,6 +3617,7 @@ + @@ -3622,6 +3627,7 @@ + From ae4af6b53cf0e525cd125a1eea97472ad2c72799 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 16:26:14 -0600 Subject: [PATCH 187/206] Fix PE layouts of izumi crop_calendars tests. --- cime_config/testdefs/testlist_clm.xml | 30 +++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 18e66d4d64..edbfd3c4eb 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3579,6 +3579,15 @@ + + + + + + + + + @@ -3595,18 +3604,25 @@ - + + + + + + + + + - @@ -3614,6 +3630,16 @@ + + + + + + + + + + From e757d3df5154d3671129a26d9afab667f337591f Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 14:54:24 -0600 Subject: [PATCH 188/206] Add oldCropCals testmod and two aux_clm tests that use it. --- cime_config/testdefs/testlist_clm.xml | 27 +++++++++++++++++++ .../testmods_dirs/clm/oldCropCals/user_nl_clm | 3 +++ 2 files changed, 30 insertions(+) create mode 100644 cime_config/testdefs/testmods_dirs/clm/oldCropCals/user_nl_clm diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index edbfd3c4eb..ffaf4edc29 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3660,5 +3660,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cime_config/testdefs/testmods_dirs/clm/oldCropCals/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/oldCropCals/user_nl_clm new file mode 100644 index 0000000000..5885f6f2a0 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/oldCropCals/user_nl_clm @@ -0,0 +1,3 @@ +cropcals_rx = .false. +cropcals_rx_adapt = .false. +stream_gdd20_seasons = .false. \ No newline at end of file From 7070abcd9272cc4b0f55d086321ea6968f1d2e83 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 16:29:34 -0600 Subject: [PATCH 189/206] Add cropcals_rx(_adapt) in some testmods; rename RxCropCals to RxCropCalsNoAdapt. --- cime_config/testdefs/testlist_clm.xml | 2 +- cime_config/testdefs/testmods_dirs/clm/GddGen/include_user_mods | 1 + cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm | 2 -- .../testmods_dirs/clm/RxCropCalsAdapt/include_user_mods | 2 +- .../clm/{RxCropCals => RxCropCalsNoAdapt}/include_user_mods | 0 .../testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/user_nl_clm | 2 ++ 6 files changed, 5 insertions(+), 4 deletions(-) delete mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm rename cime_config/testdefs/testmods_dirs/clm/{RxCropCals => RxCropCalsNoAdapt}/include_user_mods (100%) create mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/user_nl_clm diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index ffaf4edc29..cca879604d 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3560,7 +3560,7 @@ - + diff --git a/cime_config/testdefs/testmods_dirs/clm/GddGen/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/GddGen/include_user_mods index 02ec13743f..4d75082583 100644 --- a/cime_config/testdefs/testmods_dirs/clm/GddGen/include_user_mods +++ b/cime_config/testdefs/testmods_dirs/clm/GddGen/include_user_mods @@ -1 +1,2 @@ ../cropMonthOutput +../oldCropCals \ No newline at end of file diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm deleted file mode 100644 index 8a0b4a91be..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/user_nl_clm +++ /dev/null @@ -1,2 +0,0 @@ - -cropcals_rx = .true. diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods index 88c9694365..4f46c0ce95 100644 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods @@ -1 +1 @@ -../RxCropCals +../RxCropCalsNoAdapt diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCals/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/include_user_mods similarity index 100% rename from cime_config/testdefs/testmods_dirs/clm/RxCropCals/include_user_mods rename to cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/include_user_mods diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/user_nl_clm new file mode 100644 index 0000000000..625960b389 --- /dev/null +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsNoAdapt/user_nl_clm @@ -0,0 +1,2 @@ +cropcals_rx = .true. +cropcals_rx_adapt = .false. From f0a221f0a68c0aac10cf0c449406c1dcd27a6294 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 15:38:20 -0600 Subject: [PATCH 190/206] Rework RxCropCalsAdaptGGCMI to not require RxCropCalsAdapt. The latter will be deleted in the next commit. --- .../testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods | 2 +- .../testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods index af5fe8591e..23ea3745e6 100644 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/include_user_mods @@ -1 +1 @@ -../RxCropCalsAdapt +../crop diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm index dd4ac3117c..fdf5a86c26 100644 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdaptGGCMI/user_nl_clm @@ -1,4 +1,5 @@ - +cropcals_rx = .false. +cropcals_rx_adapt = .true. stream_gdd20_seasons = .true. flush_gdd20 = .true. !TODO SSR: Try without this once you have half-degree inputs From 5dabe6287e9ad843003f08122cf58327aaf356b3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 16:31:07 -0600 Subject: [PATCH 191/206] Delete RxCropCalsAdapt test. --- cime_config/testdefs/testlist_clm.xml | 18 ------------------ .../clm/RxCropCalsAdapt/include_user_mods | 1 - .../clm/RxCropCalsAdapt/user_nl_clm | 3 --- 3 files changed, 22 deletions(-) delete mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods delete mode 100644 cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index cca879604d..4e5574932e 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3570,24 +3570,6 @@ - - - - - - - - - - - - - - - - - - diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods deleted file mode 100644 index 4f46c0ce95..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/include_user_mods +++ /dev/null @@ -1 +0,0 @@ -../RxCropCalsNoAdapt diff --git a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm deleted file mode 100644 index 709c7221e0..0000000000 --- a/cime_config/testdefs/testmods_dirs/clm/RxCropCalsAdapt/user_nl_clm +++ /dev/null @@ -1,3 +0,0 @@ - -cropcals_rx = .false. -cropcals_rx_adapt = .true. From ca6b608ce59eb9043297d4452a5d0107eb5b8e82 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 15:52:10 -0600 Subject: [PATCH 192/206] Add 'cropcals_rx_adapt = .false.' to RXCROPMATURITYSHARED. --- cime_config/SystemTests/rxcropmaturity.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cime_config/SystemTests/rxcropmaturity.py b/cime_config/SystemTests/rxcropmaturity.py index b1de55836b..fb254c408f 100644 --- a/cime_config/SystemTests/rxcropmaturity.py +++ b/cime_config/SystemTests/rxcropmaturity.py @@ -402,6 +402,7 @@ def _run_check_rxboth_run(self, skip_gen): def _modify_user_nl_allruns(self): nl_additions = [ "cropcals_rx = .true.", + "cropcals_rx_adapt = .false.", "stream_meshfile_cropcal = '{}'".format(self._case.get_value("LND_DOMAIN_MESH")), "stream_fldFileName_swindow_start = '{}'".format(self._sdatefile), "stream_fldFileName_swindow_end = '{}'".format(self._sdatefile), From 904e6b2f99164165a4a197436f4b583148bdc087 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 18:22:36 -0600 Subject: [PATCH 193/206] Change a test name in ExpectedTestFails.xml. --- cime_config/testdefs/ExpectedTestFails.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml index 9b653d8e0b..d5e3e4e378 100644 --- a/cime_config/testdefs/ExpectedTestFails.xml +++ b/cime_config/testdefs/ExpectedTestFails.xml @@ -163,7 +163,7 @@ - + FAIL #2659 From a3d8158c65dcff4cabc1a9e3dd86fe762050f7f3 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 20:01:54 -0600 Subject: [PATCH 194/206] Turn on cropcals_rx_adapt by default for physics other than clm4_5, clm5_0, and clm5_1. --- bld/namelist_files/namelist_defaults_ctsm.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 706b4dd26d..5f336dabf8 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -2199,7 +2199,10 @@ lnd/clm2/surfdata_esmf/NEON/surfdata_1x1_NEON_TOOL_hist_78pfts_CMIP6_simyr2000_c .false. -.false. +.true. +.false. +.false. +.false. .false. .false. 2000 From dcdd587848f18f2087543966856f6b0373a64adb Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 22:36:49 -0600 Subject: [PATCH 195/206] Remove most Izumi crop_calendars tests. --- cime_config/testdefs/testlist_clm.xml | 32 --------------------------- 1 file changed, 32 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index 4e5574932e..efeb011844 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -3563,7 +3563,6 @@ - @@ -3573,7 +3572,6 @@ - @@ -3592,15 +3590,6 @@ - - - - - - - - - @@ -3612,20 +3601,9 @@ - - - - - - - - - - - @@ -3635,7 +3613,6 @@ - @@ -3660,14 +3637,5 @@ - - - - - - - - - From af02bca9e98661c1f9450cf7602c96dc3aab2b15 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 25 Jul 2024 23:17:56 -0700 Subject: [PATCH 196/206] Update ChangeLog and ChangeSum. --- doc/ChangeLog | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++ doc/ChangeSum | 1 + 2 files changed, 138 insertions(+) diff --git a/doc/ChangeLog b/doc/ChangeLog index 506b3d5ad0..501a67720f 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,141 @@ =============================================================== +Tag name: ctsm5.2.016 +Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) +Date: Thu Jul 25 22:39:39 MST 2024 +One-line Summary: Enable new crop calendars for clm60 compsets + +Purpose and description of changes +---------------------------------- + +This commit switches clm60 compsets (really, any compset other than clm45, clm50, and clm51) to use: +- Per-gridcell and -crop sowing windows derived from the GGCMI phase 3b group II static growing seasons +- Per-gridcell and -crop maturity requirements derived from those same growing seasons, over the 1980-2009 growing seasons +- Code to adjust those prescribed maturity requirements based on recent climate + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + +[X] clm6_0 + +[ ] clm5_1 + +[ ] clm5_0 + +[?] ctsm5_0-nwp: Probably? Haven't tested, but didn't exclude that setup. + +[ ] clm4_5 + + +Bugs fixed +---------- + +List of CTSM issues fixed (include CTSM Issue # and description): +- Fixes ESCOMP/CTSM#2584: Reset accumulators to initval instead of 0 (https://github.com/ESCOMP/CTSM/issues/2584) + +Notes of particular relevance for users +--------------------------------------- +[Remove any lines that don't apply. Remove entire section if nothing applies.] + +Caveats for users (e.g., need to interpolate initial conditions): + +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): +- Add cropcals_rx, default false. +- Add cropcals_rx_adapt, default true. This is what changes default behavior. +- Rename stream_year_first_cropcal to stream_year_first_cropcal_swindows +- Rename stream_year_last_cropcal to stream_year_last_cropcal_swindows +- Rename model_year_align_cropcal to model_year_align_cropcal_swindows +- Add stream_year_first_cropcal_cultivar_gdds +- Add stream_year_last_cropcal_cultivar_gdds +- Add model_year_align_cropcal_cultivar_gdds +- Add stream_fldFileName_gdd20_baseline +- Add stream_gdd20_seasons (experimental) +- Add flush_gdd20 (experimental) +- Add stream_fldFileName_gdd20_season_start (experimental) +- Add stream_fldFileName_gdd20_season_end (experimental) +- Add allow_invalid_gdd20_season_inputs + +Changes to the datasets (e.g., parameter, surface or initial files): +- Many new default and optional stream files added. + +Changes to documentation: +- None yet. + +Notes of particular relevance for developers: +--------------------------------------------- + +Caveats for developers: +- tools/contrib/tweak_latlons.py: This is a script I made to avoid the "ambiguous nearest neighbor" issue described in the discussion at https://github.com/orgs/esmf-org/discussions/261. It's hopefully just a temporary thing as we wait for ESMF to fix that bug. If they end up deciding not to, then this should be moved into python/ctsm/ and fully tested. +- Unit and system testing needs to be added for generate_gdd20_baseline.py. +- RXCROPMATURITY SystemTest should be updated to also call generate_gdd20_baseline.py. + +Changes to tests or testing: +- Adds testmods: + - midDecStart: Like decStart, except starts Dec. 15 instead of 30. Useful for ERP/ERS tests where you want the split in December. + - GddGen + - oldCropCals: To recreate previous crop calendars + - RxCropCalsAdaptGGCMI (experimental) + - RxCropCalsNoAdapt + - StreamGDD20Seasons (experimental) +- Removes sowingWindows testmod +- Adds and changes various tests related to crop calendars + +Note that testmods marked "experimental" are fine to be marked as expected failures, if needed. + + +Testing summary: +---------------- + +[Remove any lines that don't apply.] + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + derecho - + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + (any machine) - + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- + izumi ------- + +Answer changes +-------------- + +Changes answers relative to baseline: + + Summarize any changes to answers, i.e., + - what code configurations: crop-enabled compsets with clm60 (or more specifically, other than clm45, clm50, clm51) + - what platforms/compilers: all + - nature of change (roundoff; larger than roundoff/same climate; new climate): new climate + + Probably? At least, there can be large regional shifts in crop growing season. Results are similar to those for the Prescribed Calendars setup in Rabin et al. (2023; https://gmd.copernicus.org/articles/16/7253/2023/gmd-16-7253-2023.html). + + If this tag changes climate describe the run(s) done to evaluate the new + climate (put details of the simulations in the experiment database) + - None + + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +- ESCOMP/CTSM#2560: Allow prescribed gddmaturity to vary based on recent climate (initial work, v2) (https://github.com/ESCOMP/CTSM/pull/2560) +- ESCOMP/CTSM#2585: Allow "manual" resets of all accumulator types (https://github.com/ESCOMP/CTSM/pull/2585) +- ESCOMP/CTSM#2593: Optionally base GDD20 on per-gridcell windows, not per-hemisphere (https://github.com/ESCOMP/CTSM/pull/2593) +- ESCOMP/CTSM#2661: Merge ctsm5.2.015 into scale-maturity-reqs (https://github.com/ESCOMP/CTSM/pull/2661) +- ESCOMP/CTSM#2664: Enable new crop calendars for clm60 compsets (https://github.com/ESCOMP/CTSM/pull/2664) +- ESCOMP/CTSM#2665: Merge new crop calendars into master (https://github.com/ESCOMP/CTSM/pull/2665) + +=============================================================== +=============================================================== Tag name: ctsm5.2.015 Originator(s): multiple (Samuel Levis,UCAR/TSS,303-665-1310, @mvertens, @jedwards4b, @billsacks, @Katetc) Date: Mon 22 Jul 2024 12:46:17 PM MDT diff --git a/doc/ChangeSum b/doc/ChangeSum index 9c6524b33d..f043ab8b34 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.2.016 samrabin 07/25/2024 Enable new crop calendars for clm60 compsets ctsm5.2.015 multiple 07/22/2024 Update submodule tags to pass runoff from cism to rof ctsm5.2.014 multiple 07/19/2024 use_matrixcn, use_soil_matrixcn come in as default .false. ctsm5.2.013 glemieux 07/18/2024 FATES Land Use V2 From 542f7080432e76073ea2734be0ed50393e9a6bb8 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 01:40:34 -0600 Subject: [PATCH 197/206] Wrap cropcal subroutine calls in use_crop. Avoids issue with undefined pointer to crop_inst%rx_swindow_starts_thisyr_patch and probably other arrays. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 1 + src/main/clm_driver.F90 | 4 ++-- src/main/clm_initializeMod.F90 | 26 +++++++++++++------------ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 85218a0e1c..a39ba40754 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -14,6 +14,7 @@ module cropcalStreamMod use decompMod , only : bounds_type use abortutils , only : endrun use clm_varctl , only : iulog + use clm_varctl , only : use_crop use clm_varctl , only : use_cropcal_rx_swindows, use_cropcal_rx_cultivar_gdds, use_cropcal_streams use clm_varctl , only : adapt_cropcal_rx_cultivar_gdds use clm_varpar , only : mxpft diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90 index 01c0ec409e..53b1e55f7c 100644 --- a/src/main/clm_driver.F90 +++ b/src/main/clm_driver.F90 @@ -475,7 +475,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro ! When crop calendar streams are being used ! NOTE: This call needs to happen outside loops over nclumps (as streams are not threadsafe) - if (use_cropcal_streams .and. is_beg_curr_year()) then + if (use_crop .and. use_cropcal_streams .and. is_beg_curr_year()) then call cropcal_advance( bounds_proc ) end if @@ -1073,7 +1073,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro frictionvel_inst, photosyns_inst, drydepvel_inst) call t_stopf('depvel') - if (use_cropcal_streams .and. is_beg_curr_year()) then + if (use_crop .and. use_cropcal_streams .and. is_beg_curr_year()) then ! ============================================================================ ! Update crop calendars ! ============================================================================ diff --git a/src/main/clm_initializeMod.F90 b/src/main/clm_initializeMod.F90 index e8f70bdef8..0c0570c577 100644 --- a/src/main/clm_initializeMod.F90 +++ b/src/main/clm_initializeMod.F90 @@ -659,19 +659,21 @@ subroutine initialize2(ni,nj) end if ! Initialize crop calendars - call t_startf('init_cropcal') - call cropcal_init(bounds_proc) - if (use_cropcal_streams) then - call cropcal_advance( bounds_proc ) - !$OMP PARALLEL DO PRIVATE (nc, bounds_clump) - do nc = 1,nclumps - call get_clump_bounds(nc, bounds_clump) - call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, & - filter_inactive_and_active(nc)%pcropp, .true., crop_inst) - end do - !$OMP END PARALLEL DO + if (use_crop) then + call t_startf('init_cropcal') + call cropcal_init(bounds_proc) + if (use_cropcal_streams) then + call cropcal_advance( bounds_proc ) + !$OMP PARALLEL DO PRIVATE (nc, bounds_clump) + do nc = 1,nclumps + call get_clump_bounds(nc, bounds_clump) + call cropcal_interp(bounds_clump, filter_inactive_and_active(nc)%num_pcropp, & + filter_inactive_and_active(nc)%pcropp, .true., crop_inst) + end do + !$OMP END PARALLEL DO + end if + call t_stopf('init_cropcal') end if - call t_stopf('init_cropcal') ! Initialize active history fields. ! This is only done if not a restart run. If a restart run, then this From 126ce292f1380e96ff22750ec4cd0dc1a518e8d6 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 07:49:53 -0600 Subject: [PATCH 198/206] Allocate first dim on dataptr2d_*_start/end arrays with begp:endp. Instead of just :. Hopefully will eliminate too-high index errors in threading tests. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index a39ba40754..3fdf1ba06c 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -545,9 +545,9 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Read prescribed sowing window start dates from input files allocate(dataptr2d_swindow_start(lsize, ncft)) - dataptr2d_swindow_start(:,:) = -1._r8 + dataptr2d_swindow_start(begp:endp,:) = -1._r8 allocate(dataptr2d_swindow_end (lsize, ncft)) - dataptr2d_swindow_end(:,:) = -1._r8 + dataptr2d_swindow_end(begp:endp,:) = -1._r8 if (use_cropcal_rx_swindows) then ! Starting with npcropmin will skip generic crops do n = 1, ncft @@ -742,9 +742,9 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Read prescribed gdd20 season start dates from input files allocate(dataptr2d_gdd20_season_start(lsize, ncft)) - dataptr2d_gdd20_season_start(:,:) = -1._r8 + dataptr2d_gdd20_season_start(begp:endp,:) = -1._r8 allocate(dataptr2d_gdd20_season_end (lsize, ncft)) - dataptr2d_gdd20_season_end(:,:) = -1._r8 + dataptr2d_gdd20_season_end(begp:endp,:) = -1._r8 if (stream_gdd20_seasons) then ! Starting with npcropmin will skip generic crops do n = 1, ncft From b7a8fb58e2186d9d6b9dcd20b8b6c57301bfcd8e Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 08:14:51 -0600 Subject: [PATCH 199/206] Allocate 2d arrays with begp:endp, not lsize. Hopefully will eliminate too-high index errors in threading tests. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 3fdf1ba06c..36c6c78f22 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -544,9 +544,9 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) dayspyr = get_curr_days_per_year() ! Read prescribed sowing window start dates from input files - allocate(dataptr2d_swindow_start(lsize, ncft)) + allocate(dataptr2d_swindow_start(begp:endp, ncft)) dataptr2d_swindow_start(begp:endp,:) = -1._r8 - allocate(dataptr2d_swindow_end (lsize, ncft)) + allocate(dataptr2d_swindow_end (begp:endp, ncft)) dataptr2d_swindow_end(begp:endp,:) = -1._r8 if (use_cropcal_rx_swindows) then ! Starting with npcropmin will skip generic crops @@ -631,7 +631,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_swindow_start) deallocate(dataptr2d_swindow_end) - allocate(dataptr2d_cultivar_gdds(lsize, ncft)) + allocate(dataptr2d_cultivar_gdds(begp:endp, ncft)) if (use_cropcal_rx_cultivar_gdds) then ! Read prescribed cultivar GDDs from input files ! Starting with npcropmin will skip generic crops @@ -688,7 +688,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_cultivar_gdds) - allocate(dataptr2d_gdd20_baseline(lsize, ncft)) + allocate(dataptr2d_gdd20_baseline(begp:endp, ncft)) if (adapt_cropcal_rx_cultivar_gdds) then ! Read GDD20 baselines from input files ! Starting with npcropmin will skip generic crops @@ -741,9 +741,9 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Read prescribed gdd20 season start dates from input files - allocate(dataptr2d_gdd20_season_start(lsize, ncft)) + allocate(dataptr2d_gdd20_season_start(begp:endp, ncft)) dataptr2d_gdd20_season_start(begp:endp,:) = -1._r8 - allocate(dataptr2d_gdd20_season_end (lsize, ncft)) + allocate(dataptr2d_gdd20_season_end (begp:endp, ncft)) dataptr2d_gdd20_season_end(begp:endp,:) = -1._r8 if (stream_gdd20_seasons) then ! Starting with npcropmin will skip generic crops From 63b66b66944e0bacf55da5e87f48a1a860ca9211 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 08:22:57 -0600 Subject: [PATCH 200/206] Add ERP_D_Ld10_P64x2.f10_f10_mg37.IHistClm60BgcCrop.derecho_intel.clm-default to crop_calendars suite. --- cime_config/testdefs/testlist_clm.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index efeb011844..bb421c8e9e 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -1016,6 +1016,7 @@ + From 7e04126d66429ee7419441538447bb3a3cc4b448 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 08:26:32 -0600 Subject: [PATCH 201/206] dataptr2d arrays' first dim is gridcell, not patch. Hopefully will eliminate too-high index errors in threading tests. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 36c6c78f22..d19c74ad37 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -544,10 +544,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) dayspyr = get_curr_days_per_year() ! Read prescribed sowing window start dates from input files - allocate(dataptr2d_swindow_start(begp:endp, ncft)) - dataptr2d_swindow_start(begp:endp,:) = -1._r8 - allocate(dataptr2d_swindow_end (begp:endp, ncft)) - dataptr2d_swindow_end(begp:endp,:) = -1._r8 + allocate(dataptr2d_swindow_start(bounds%begg:bounds%endg, ncft)) + dataptr2d_swindow_start(bounds%begg:bounds%endg,:) = -1._r8 + allocate(dataptr2d_swindow_end (bounds%begg:bounds%endg, ncft)) + dataptr2d_swindow_end(bounds%begg:bounds%endg,:) = -1._r8 if (use_cropcal_rx_swindows) then ! Starting with npcropmin will skip generic crops do n = 1, ncft @@ -563,7 +563,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) end if ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = 1,lsize + do g = bounds%begg, bounds%endg ! If read-in value is invalid, set to -1. Will be handled later in this subroutine. if (dataptr1d_swindow_start(g) <= 0 .or. dataptr1d_swindow_start(g) > dayspyr & @@ -631,7 +631,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_swindow_start) deallocate(dataptr2d_swindow_end) - allocate(dataptr2d_cultivar_gdds(begp:endp, ncft)) + allocate(dataptr2d_cultivar_gdds(bounds%begg:bounds%endg, ncft)) if (use_cropcal_rx_cultivar_gdds) then ! Read prescribed cultivar GDDs from input files ! Starting with npcropmin will skip generic crops @@ -688,7 +688,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_cultivar_gdds) - allocate(dataptr2d_gdd20_baseline(begp:endp, ncft)) + allocate(dataptr2d_gdd20_baseline(bounds%begg:bounds%endg, ncft)) if (adapt_cropcal_rx_cultivar_gdds) then ! Read GDD20 baselines from input files ! Starting with npcropmin will skip generic crops @@ -741,10 +741,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Read prescribed gdd20 season start dates from input files - allocate(dataptr2d_gdd20_season_start(begp:endp, ncft)) - dataptr2d_gdd20_season_start(begp:endp,:) = -1._r8 - allocate(dataptr2d_gdd20_season_end (begp:endp, ncft)) - dataptr2d_gdd20_season_end(begp:endp,:) = -1._r8 + allocate(dataptr2d_gdd20_season_start(bounds%begg:bounds%endg, ncft)) + dataptr2d_gdd20_season_start(bounds%begg:bounds%endg,:) = -1._r8 + allocate(dataptr2d_gdd20_season_end (bounds%begg:bounds%endg, ncft)) + dataptr2d_gdd20_season_end(bounds%begg:bounds%endg,:) = -1._r8 if (stream_gdd20_seasons) then ! Starting with npcropmin will skip generic crops do n = 1, ncft From d47b28a5e3e44b66a11fc8055d57f24c02d8bbbc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 08:36:47 -0600 Subject: [PATCH 202/206] Use begg and endg instead of lsize. Hopefully will eliminate too-high index errors in threading tests. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index d19c74ad37..02cfc5e67a 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -507,7 +507,6 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) integer :: nc, fp integer :: dayspyr integer :: n, g - integer :: lsize integer :: rc integer :: begp, endp real(r8), pointer :: dataptr1d_swindow_start(:) @@ -536,7 +535,6 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Get pointer for stream data that is time and spatially interpolate to model time and grid ! Place all data from each type into a temporary 2d array - lsize = bounds%endg - bounds%begg + 1 begp = bounds%begp endp = bounds%endp @@ -644,7 +642,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = 1,lsize + do g = bounds%begg, bounds%endg ! If read-in value is invalid, have PlantCrop() set gddmaturity to PFT-default value. if (dataptr1d_cultivar_gdds(g) < 0 .or. dataptr1d_cultivar_gdds(g) > 1000000._r8) then @@ -672,8 +670,8 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! vegetated pft ig = g_to_ig(patch%gridcell(p)) - if (ig > lsize) then - write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') > lsize (',lsize,')' + if (ig < bounds%begg .or. ig > bounds%endg) then + write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') < begg (',bounds%begg,') or > endg (',bounds%endg,')' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -701,7 +699,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = 1,lsize + do g = bounds%begg, bounds%endg dataptr2d_gdd20_baseline(g,n) = dataptr1d_gdd20_baseline(g) end do end do @@ -723,8 +721,8 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! vegetated pft ig = g_to_ig(patch%gridcell(p)) - if (ig > lsize) then - write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') > lsize (',lsize,')' + if (ig < bounds%begg .or. ig > bounds%endg) then + write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') < begg (',bounds%begg,') or > endg (',bounds%endg,')' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -760,7 +758,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) end if ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = 1,lsize + do g = bounds%begg, bounds%endg ! If read-in value is invalid, set to -1. Will be handled later in this subroutine. if (dataptr1d_gdd20_season_start(g) <= 0 .or. dataptr1d_gdd20_season_start(g) > 366 & From 2156c7206c61a131d3a4a6da69940acafb3d1dcf Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 08:40:18 -0600 Subject: [PATCH 203/206] Use begg/endg instead of referring to bounds%. --- src/cpl/share_esmf/cropcalStreamMod.F90 | 51 ++++++++++++++----------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/cpl/share_esmf/cropcalStreamMod.F90 b/src/cpl/share_esmf/cropcalStreamMod.F90 index 02cfc5e67a..5587641c21 100644 --- a/src/cpl/share_esmf/cropcalStreamMod.F90 +++ b/src/cpl/share_esmf/cropcalStreamMod.F90 @@ -422,6 +422,7 @@ subroutine cropcal_advance( bounds ) ! ! !LOCAL VARIABLES: integer :: g, ig ! Indices + integer :: begg, endg ! gridcell bounds 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 @@ -430,6 +431,9 @@ subroutine cropcal_advance( bounds ) integer :: rc !----------------------------------------------------------------------- + begg = bounds%begg + endg = bounds%endg + call get_curr_date(year, mon, day, sec) mcdate = year*10000 + mon*100 + day if (use_cropcal_rx_swindows) then @@ -471,9 +475,9 @@ subroutine cropcal_advance( bounds ) end if if ( .not. allocated(g_to_ig) )then - allocate (g_to_ig(bounds%begg:bounds%endg) ) + allocate (g_to_ig(begg:endg) ) ig = 0 - do g = bounds%begg,bounds%endg + do g = begg,endg ig = ig+1 g_to_ig(g) = ig end do @@ -508,6 +512,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) integer :: dayspyr integer :: n, g integer :: rc + integer :: begg, endg integer :: begp, endp real(r8), pointer :: dataptr1d_swindow_start(:) real(r8), pointer :: dataptr1d_swindow_end (:) @@ -530,8 +535,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) gdd20_season_ends => crop_inst%gdd20_season_end_patch & ) - SHR_ASSERT_FL( (lbound(g_to_ig,1) <= bounds%begg ), sourcefile, __LINE__) - SHR_ASSERT_FL( (ubound(g_to_ig,1) >= bounds%endg ), sourcefile, __LINE__) + begg = bounds%begg + endg = bounds%endg + SHR_ASSERT_FL( (lbound(g_to_ig,1) <= begg ), sourcefile, __LINE__) + SHR_ASSERT_FL( (ubound(g_to_ig,1) >= endg ), sourcefile, __LINE__) ! Get pointer for stream data that is time and spatially interpolate to model time and grid ! Place all data from each type into a temporary 2d array @@ -542,10 +549,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) dayspyr = get_curr_days_per_year() ! Read prescribed sowing window start dates from input files - allocate(dataptr2d_swindow_start(bounds%begg:bounds%endg, ncft)) - dataptr2d_swindow_start(bounds%begg:bounds%endg,:) = -1._r8 - allocate(dataptr2d_swindow_end (bounds%begg:bounds%endg, ncft)) - dataptr2d_swindow_end(bounds%begg:bounds%endg,:) = -1._r8 + allocate(dataptr2d_swindow_start(begg:endg, ncft)) + dataptr2d_swindow_start(begg:endg,:) = -1._r8 + allocate(dataptr2d_swindow_end (begg:endg, ncft)) + dataptr2d_swindow_end(begg:endg,:) = -1._r8 if (use_cropcal_rx_swindows) then ! Starting with npcropmin will skip generic crops do n = 1, ncft @@ -561,7 +568,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) end if ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = bounds%begg, bounds%endg + do g = begg, endg ! If read-in value is invalid, set to -1. Will be handled later in this subroutine. if (dataptr1d_swindow_start(g) <= 0 .or. dataptr1d_swindow_start(g) > dayspyr & @@ -629,7 +636,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_swindow_start) deallocate(dataptr2d_swindow_end) - allocate(dataptr2d_cultivar_gdds(bounds%begg:bounds%endg, ncft)) + allocate(dataptr2d_cultivar_gdds(begg:endg, ncft)) if (use_cropcal_rx_cultivar_gdds) then ! Read prescribed cultivar GDDs from input files ! Starting with npcropmin will skip generic crops @@ -642,7 +649,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = bounds%begg, bounds%endg + do g = begg, endg ! If read-in value is invalid, have PlantCrop() set gddmaturity to PFT-default value. if (dataptr1d_cultivar_gdds(g) < 0 .or. dataptr1d_cultivar_gdds(g) > 1000000._r8) then @@ -670,8 +677,8 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! vegetated pft ig = g_to_ig(patch%gridcell(p)) - if (ig < bounds%begg .or. ig > bounds%endg) then - write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') < begg (',bounds%begg,') or > endg (',bounds%endg,')' + if (ig < begg .or. ig > endg) then + write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') < begg (',begg,') or > endg (',endg,')' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -686,7 +693,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) deallocate(dataptr2d_cultivar_gdds) - allocate(dataptr2d_gdd20_baseline(bounds%begg:bounds%endg, ncft)) + allocate(dataptr2d_gdd20_baseline(begg:endg, ncft)) if (adapt_cropcal_rx_cultivar_gdds) then ! Read GDD20 baselines from input files ! Starting with npcropmin will skip generic crops @@ -699,7 +706,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = bounds%begg, bounds%endg + do g = begg, endg dataptr2d_gdd20_baseline(g,n) = dataptr1d_gdd20_baseline(g) end do end do @@ -721,8 +728,8 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! vegetated pft ig = g_to_ig(patch%gridcell(p)) - if (ig < bounds%begg .or. ig > bounds%endg) then - write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') < begg (',bounds%begg,') or > endg (',bounds%endg,')' + if (ig < begg .or. ig > endg) then + write(iulog,'(a,i0,a,i0,a)') 'ig (',ig,') < begg (',begg,') or > endg (',endg,')' call ESMF_Finalize(endflag=ESMF_END_ABORT) end if @@ -739,10 +746,10 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) ! Read prescribed gdd20 season start dates from input files - allocate(dataptr2d_gdd20_season_start(bounds%begg:bounds%endg, ncft)) - dataptr2d_gdd20_season_start(bounds%begg:bounds%endg,:) = -1._r8 - allocate(dataptr2d_gdd20_season_end (bounds%begg:bounds%endg, ncft)) - dataptr2d_gdd20_season_end(bounds%begg:bounds%endg,:) = -1._r8 + allocate(dataptr2d_gdd20_season_start(begg:endg, ncft)) + dataptr2d_gdd20_season_start(begg:endg,:) = -1._r8 + allocate(dataptr2d_gdd20_season_end (begg:endg, ncft)) + dataptr2d_gdd20_season_end(begg:endg,:) = -1._r8 if (stream_gdd20_seasons) then ! Starting with npcropmin will skip generic crops do n = 1, ncft @@ -758,7 +765,7 @@ subroutine cropcal_interp(bounds, num_pcropp, filter_pcropp, init, crop_inst) end if ! Note that the size of dataptr1d includes ocean points so it will be around 3x larger than lsize ! So an explicit loop is required here - do g = bounds%begg, bounds%endg + do g = begg, endg ! If read-in value is invalid, set to -1. Will be handled later in this subroutine. if (dataptr1d_gdd20_season_start(g) <= 0 .or. dataptr1d_gdd20_season_start(g) > 366 & From b1aed3f36e79cb8eaf278f1a72be77de88ad1c64 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 09:12:57 -0600 Subject: [PATCH 204/206] Update ChangeLog and ChangeSum. --- doc/ChangeLog | 13 ++++++------- doc/ChangeSum | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 501a67720f..88c322e990 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,7 +1,7 @@ =============================================================== Tag name: ctsm5.2.016 Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) -Date: Thu Jul 25 22:39:39 MST 2024 +Date: Fri 26 Jul 2024 09:11:52 AM MDT One-line Summary: Enable new crop calendars for clm60 compsets Purpose and description of changes @@ -89,22 +89,21 @@ Note that testmods marked "experimental" are fine to be marked as expected failu Testing summary: ---------------- -[Remove any lines that don't apply.] - [PASS means all tests PASS; OK means tests PASS other than expected fails.] build-namelist tests (if CLMBuildNamelist.pm has changed): - derecho - + derecho - FAIL + 1x1_cidadinhoBR tests only. That's fine. Filed Issue #2666: 1x1_cidadinhoBR tests fail in build-namelist_test.pl (https://github.com/ESCOMP/CTSM/issues/2666). python testing (if python code has changed; see instructions in python/README.md; document testing done): - (any machine) - + derecho - PASS regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): - derecho ----- - izumi ------- + derecho ----- OK + izumi ------- OK Answer changes -------------- diff --git a/doc/ChangeSum b/doc/ChangeSum index f043ab8b34..edc9e4cada 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,6 +1,6 @@ Tag Who Date Summary ============================================================================================================================ - ctsm5.2.016 samrabin 07/25/2024 Enable new crop calendars for clm60 compsets + ctsm5.2.016 samrabin 07/26/2024 Enable new crop calendars for clm60 compsets ctsm5.2.015 multiple 07/22/2024 Update submodule tags to pass runoff from cism to rof ctsm5.2.014 multiple 07/19/2024 use_matrixcn, use_soil_matrixcn come in as default .false. ctsm5.2.013 glemieux 07/18/2024 FATES Land Use V2 From 66e28d184b6d68f55c8ac2bf3d26b5d46399e7bb Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 26 Jul 2024 12:12:23 -0600 Subject: [PATCH 205/206] Changes one Izumi test from f19 to 10x15 resolution. - Was: ERS_D.f19_g17.I1850Clm50BgcCrop.izumi_nag.clm-ciso_monthly_matrixcn_spinup - Now: ERS_D.f10_f10_mg37.I1850Clm50BgcCrop.izumi_nag.clm-ciso_monthly_matrixcn_spinup Fixes ESCOMP/CTSM#2667: Change out a f19 resolution test on Izumi for f10 (https://github.com/ESCOMP/CTSM/issues/2667) --- cime_config/testdefs/testlist_clm.xml | 8 ++++++++ doc/ChangeLog | 9 +++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml index bb421c8e9e..951e0d8d5b 100644 --- a/cime_config/testdefs/testlist_clm.xml +++ b/cime_config/testdefs/testlist_clm.xml @@ -863,6 +863,14 @@ + + + + + + + + diff --git a/doc/ChangeLog b/doc/ChangeLog index 88c322e990..0057567561 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,7 +1,7 @@ =============================================================== Tag name: ctsm5.2.016 Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) -Date: Fri 26 Jul 2024 09:11:52 AM MDT +Date: Fri Jul 26 12:20:46 MDT 2024 One-line Summary: Enable new crop calendars for clm60 compsets Purpose and description of changes @@ -35,12 +35,10 @@ Bugs fixed List of CTSM issues fixed (include CTSM Issue # and description): - Fixes ESCOMP/CTSM#2584: Reset accumulators to initval instead of 0 (https://github.com/ESCOMP/CTSM/issues/2584) +- Fixes ESCOMP/CTSM#2667: Change out a f19 resolution test on Izumi for f10 (https://github.com/ESCOMP/CTSM/issues/2667) Notes of particular relevance for users --------------------------------------- -[Remove any lines that don't apply. Remove entire section if nothing applies.] - -Caveats for users (e.g., need to interpolate initial conditions): Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): - Add cropcals_rx, default false. @@ -82,6 +80,9 @@ Changes to tests or testing: - StreamGDD20Seasons (experimental) - Removes sowingWindows testmod - Adds and changes various tests related to crop calendars +- Changes one Izumi test from f19 to 10x15 resolution: + - Was: ERS_D.f19_g17.I1850Clm50BgcCrop.izumi_nag.clm-ciso_monthly_matrixcn_spinup + - Now: ERS_D.f10_f10_mg37.I1850Clm50BgcCrop.izumi_nag.clm-ciso_monthly_matrixcn_spinup Note that testmods marked "experimental" are fine to be marked as expected failures, if needed. From 2422cef7f1e0ef1cc184eb51c0f973f2125396e9 Mon Sep 17 00:00:00 2001 From: Erik Kluzek Date: Sat, 27 Jul 2024 17:15:10 -0600 Subject: [PATCH 206/206] Update date on Change files --- doc/ChangeLog | 2 +- doc/ChangeSum | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 0057567561..79d640242d 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,7 +1,7 @@ =============================================================== Tag name: ctsm5.2.016 Originator(s): samrabin (Sam Rabin, UCAR/TSS, samrabin@ucar.edu) -Date: Fri Jul 26 12:20:46 MDT 2024 +Date: Sat 27 Jul 2024 05:13:08 PM MDT One-line Summary: Enable new crop calendars for clm60 compsets Purpose and description of changes diff --git a/doc/ChangeSum b/doc/ChangeSum index edc9e4cada..382e5cf7cb 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,6 +1,6 @@ Tag Who Date Summary ============================================================================================================================ - ctsm5.2.016 samrabin 07/26/2024 Enable new crop calendars for clm60 compsets + ctsm5.2.016 samrabin 07/27/2024 Enable new crop calendars for clm60 compsets ctsm5.2.015 multiple 07/22/2024 Update submodule tags to pass runoff from cism to rof ctsm5.2.014 multiple 07/19/2024 use_matrixcn, use_soil_matrixcn come in as default .false. ctsm5.2.013 glemieux 07/18/2024 FATES Land Use V2