diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 089e9fbb12..c02e20b678 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -27,6 +27,7 @@ d866510188d26d51bcd6d37239283db690af7e82 e096358c832ab292ddfd22dd5878826c7c788968 475831f0fb0e31e97f630eac4e078c886558b61c fd5f177131d63d39e79a13918390bdfb642d781e +a51816e0de380300b69db9fc3e2c7fa83b267b64 # Ran SystemTests and python/ctsm through black python formatter 5364ad66eaceb55dde2d3d598fe4ce37ac83a93c 8056ae649c1b37f5e10aaaac79005d6e3a8b2380 diff --git a/.gitmodules b/.gitmodules index a7acd7fd08..bc66fe1682 100644 --- a/.gitmodules +++ b/.gitmodules @@ -44,7 +44,7 @@ fxDONOTUSEurl = https://github.com/ESCOMP/CISM-wrapper [submodule "rtm"] path = components/rtm url = https://github.com/ESCOMP/RTM -fxtag = rtm1_0_80 +fxtag = rtm1_0_84 fxrequired = ToplevelRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/ESCOMP/RTM @@ -52,7 +52,7 @@ fxDONOTUSEurl = https://github.com/ESCOMP/RTM [submodule "mosart"] path = components/mosart url = https://github.com/ESCOMP/MOSART -fxtag = mosart1.1.02 +fxtag = mosart1.1.06 fxrequired = ToplevelRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/ESCOMP/MOSART diff --git a/components/mosart b/components/mosart index e2ffe00004..a246344e9b 160000 --- a/components/mosart +++ b/components/mosart @@ -1 +1 @@ -Subproject commit e2ffe00004cc416cfc8bcfae2a949474075c1d1f +Subproject commit a246344e9b28e4bb42313749094fa20d45e2b212 diff --git a/components/rtm b/components/rtm index b3dfcfbba5..6899b55816 160000 --- a/components/rtm +++ b/components/rtm @@ -1 +1 @@ -Subproject commit b3dfcfbba58c151ac5a6ab513b3515ef3deff798 +Subproject commit 6899b55816ee4d9b7cf983d74ba2997b97a13c4d diff --git a/doc/ChangeLog b/doc/ChangeLog index 1b37b4be63..5357d81b5f 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,112 @@ =============================================================== +Tag name: ctsm5.3.018 +Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Fri 10 Jan 2025 05:37:08 PM MST +One-line Summary: Change history time to be the middle of the time bounds + +Purpose and description of changes +---------------------------------- + Making the change to be consistent with CAM and to make history output more intuitive. + +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.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm6_0 + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- +List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: + Partly addresses issue #1059 + +Notes of particular relevance for users +--------------------------------------- +Caveats for users (e.g., need to interpolate initial conditions): + The history time variable now equals the middle of the time bounds. + Instantaneous history tapes now do not include time bounds. + Mixed history tapes do not change the treatment of instantaneous fields or move them to separate tapes, yet. + +Notes of particular relevance for developers: +--------------------------------------------- +Caveats for developers (e.g., code that is duplicated that requires double maintenance): + Same changes are needed separately in clm, in mosart, and in rtm. + +Changes to tests or testing: + This tag introduces changes to the mosart/rtm testlists. + + FAIL RXCROPMATURITYSKIPGEN_Ld1097.f10_f10_mg37.IHistClm60BgcCrop.derecho_intel.clm-cropMonthOutput RUN + I did not label this failure EXPECTED because the fix comes in later in this series of "history" tags, in particular ctsm5.3.020. + + I resolved the izumi nag tests that failed to build (due to a bug in rtm and mosart) by introducing the bug-fix manually, as explained here: +https://github.com/ESCOMP/CTSM/pull/2084#issuecomment-2584164690 + In the next tag we expect to update to the rtm/mosart tags that include the fix. + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests + + derecho - PASS + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + derecho - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + + mosart + derecho ----- OK + izumi ------- OK + + rtm + derecho ----- OK + +Answer changes +-------------- + +Changes answers relative to baseline: Only time variable, plus read caveat + + Summarize any changes to answers, i.e., + - what code configurations: all + - what platforms/compilers: all + - nature of change: only the time variable + + Caveat: We see diffs in mosart and cpl output that will be eliminated later in this series of "history" tags, in particular ctsm5.3.020. They are discussed here: +https://github.com/ESCOMP/CTSM/pull/2838#issuecomment-2477608383 +https://github.com/ESCOMP/MOSART/issues/103#issuecomment-2479679014 + +Other details +------------- +List any git submodules updated (cime, rtm, mosart, cism, fates, etc.): + rtm, mosart + +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2838 + https://github.com/ESCOMP/MOSART/pull/70 + https://github.com/ESCOMP/RTM/issues/54 + https://github.com/ESCOMP/MOSART/pull/106 + https://github.com/ESCOMP/RTM/pull/39 + +=============================================================== +=============================================================== Tag name: ctsm5.3.017 Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) Date: Thu 09 Jan 2025 11:56:43 AM MST diff --git a/doc/ChangeSum b/doc/ChangeSum index 545a02ff25..51769e49a2 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.3.018 slevis 01/10/2025 Change history time to be the middle of the time bounds ctsm5.3.017 slevis 01/09/2025 Merge tmp-241219 branch to master tmp-241219.n03.ctsm5.3.016 01/09/2025 Bug fix for izumi nag tests to pass (slevis) tmp-241219.n02.ctsm5.3.016 01/08/2025 FATES hydro test update (glemieux) diff --git a/python/ctsm/crop_calendars/cropcal_module.py b/python/ctsm/crop_calendars/cropcal_module.py index 719d352665..3ea084e1d2 100644 --- a/python/ctsm/crop_calendars/cropcal_module.py +++ b/python/ctsm/crop_calendars/cropcal_module.py @@ -443,10 +443,7 @@ def import_output( ) # Convert time axis to integer year, saving original as 'cftime' - this_ds_gs = this_ds_gs.assign_coords( - {"cftime": this_ds["time_bounds"].isel({"hist_interval": 0})} - ) - this_ds_gs = this_ds_gs.assign_coords({"time": [t.year for t in this_ds_gs["cftime"].values]}) + this_ds_gs = convert_time_to_int_year(filename, this_ds, this_ds_gs) # Get number of harvests this_ds_gs["NHARVESTS"] = (this_ds_gs["GDDHARV_PERHARV"] > 0).sum(dim="mxharvests") @@ -458,6 +455,35 @@ def import_output( return this_ds_gs, any_bad +def convert_time_to_int_year(filename, this_ds, this_ds_gs): + """ + Convert time axis to integer year, saving original as 'cftime' + """ + if "time_bounds" in this_ds: + # Always true before PR #2838, when even files with all instantaneous variables got + # time_bounds saved. After that PR (and before the segregation of instantaneous and other + # variables onto separate files), files with an instantaneous variable first in their list + # do not get time_bounds saved. + this_ds_gs = this_ds_gs.assign_coords( + {"cftime": this_ds["time_bounds"].isel({"hist_interval": 0})} + ) + this_ds_gs = this_ds_gs.assign_coords( + {"time": [t.year for t in this_ds_gs["cftime"].values]} + ) + elif this_ds["time"].attrs["long_name"] == "time at end of time step": + # This is an "instantaneous file." + this_ds_gs = this_ds_gs.assign_coords({"cftime": this_ds["time"]}) + this_ds_gs = this_ds_gs.assign_coords( + {"time": [t.year - 1 for t in this_ds_gs["cftime"].values]} + ) + else: + raise RuntimeError( + f"{filename} is neither an instantaneous nor a combined/non-instantaneous file." + ) + + return this_ds_gs + + def handle_zombie_crops(this_ds): """ When doing transient runs, it's somehow possible for crops in newly-active patches to be diff --git a/src/main/histFileMod.F90 b/src/main/histFileMod.F90 index 381e1f1170..426fa2d7e0 100644 --- a/src/main/histFileMod.F90 +++ b/src/main/histFileMod.F90 @@ -1172,6 +1172,7 @@ subroutine htape_addfld (t, f, avgflag) integer :: beg1d,end1d ! beginning and ending indices for this field (assume already set) integer :: num1d_out ! history output 1d size type(bounds_type) :: bounds + character(len=avgflag_strlen) :: avgflag_temp ! local copy of hist_avgflag_pertape(t) character(len=*),parameter :: subname = 'htape_addfld' !----------------------------------------------------------------------- @@ -1302,6 +1303,19 @@ subroutine htape_addfld (t, f, avgflag) tape(t)%hlist(n)%avgflag = avgflag end if + ! Override this tape's avgflag if nhtfrq == 1 + if (tape(t)%nhtfrq == 1) then ! output is instantaneous + hist_avgflag_pertape(t) = 'I' + end if + ! Override this field's avgflag if the namelist or the previous line + ! has set this tape to + ! - instantaneous (I) or + ! - local time (L) + avgflag_temp = hist_avgflag_pertape(t) + if (avgflag_temp == 'I' .or. avgflag_temp(1:1) == 'L') then + tape(t)%hlist(n)%avgflag = avgflag_temp + end if + end subroutine htape_addfld !----------------------------------------------------------------------- @@ -3098,6 +3112,7 @@ subroutine htape_timeconst(t, mode) integer :: mcdate ! current date integer :: yr,mon,day,nbsec ! year,month,day,seconds components of a date integer :: hours,minutes,secs ! hours,minutes,seconds of hh:mm:ss + character(len= 12) :: step_or_bounds ! string used in long_name of several time variables character(len= 10) :: basedate ! base date (yyyymmdd) character(len= 8) :: basesec ! base seconds character(len= 8) :: cdate ! system date @@ -3357,8 +3372,18 @@ subroutine htape_timeconst(t, mode) dim1id(1) = time_dimid str = 'days since ' // basedate // " " // basesec - call ncd_defvar(nfid(t), 'time', tape(t)%ncprec, 1, dim1id, varid, & - long_name='time',units=str) + if (hist_avgflag_pertape(t) /= 'I') then ! NOT instantaneous fields tape + step_or_bounds = 'time_bounds' + long_name = 'time at exact middle of ' // step_or_bounds + call ncd_defvar(nfid(t), 'time', tape(t)%ncprec, 1, dim1id, varid, & + long_name=long_name, units=str) + call ncd_putatt(nfid(t), varid, 'bounds', 'time_bounds') + else ! instantaneous fields tape + step_or_bounds = 'time step' + long_name = 'time at end of ' // step_or_bounds + call ncd_defvar(nfid(t), 'time', tape(t)%ncprec, 1, dim1id, varid, & + long_name=long_name, units=str) + end if cal = get_calendar() if ( trim(cal) == NO_LEAP_C )then caldesc = "noleap" @@ -3366,11 +3391,11 @@ subroutine htape_timeconst(t, mode) caldesc = "gregorian" end if call ncd_putatt(nfid(t), varid, 'calendar', caldesc) - call ncd_putatt(nfid(t), varid, 'bounds', 'time_bounds') dim1id(1) = time_dimid + long_name = 'current date (YYYYMMDD) at end of ' // step_or_bounds call ncd_defvar(nfid(t) , 'mcdate', ncd_int, 1, dim1id , varid, & - long_name = 'current date (YYYYMMDD)') + long_name = long_name) ! ! add global attribute time_period_freq ! @@ -3397,18 +3422,23 @@ subroutine htape_timeconst(t, mode) call ncd_putatt(nfid(t), ncd_global, 'time_period_freq', & trim(time_period_freq)) + long_name = 'current seconds of current date at end of ' // step_or_bounds call ncd_defvar(nfid(t) , 'mcsec' , ncd_int, 1, dim1id , varid, & - long_name = 'current seconds of current date', units='s') + long_name = long_name, units='s') + long_name = 'current day (from base day) at end of ' // step_or_bounds call ncd_defvar(nfid(t) , 'mdcur' , ncd_int, 1, dim1id , varid, & - long_name = 'current day (from base day)') + long_name = long_name) + long_name = 'current seconds of current day at end of ' // step_or_bounds call ncd_defvar(nfid(t) , 'mscur' , ncd_int, 1, dim1id , varid, & - long_name = 'current seconds of current day') + long_name = long_name) call ncd_defvar(nfid(t) , 'nstep' , ncd_int, 1, dim1id , varid, & long_name = 'time step') dim2id(1) = hist_interval_dimid; dim2id(2) = time_dimid - call ncd_defvar(nfid(t), 'time_bounds', ncd_double, 2, dim2id, varid, & - long_name = 'history time interval endpoints') + if (hist_avgflag_pertape(t) /= 'I') then ! NOT instantaneous fields tape + call ncd_defvar(nfid(t), 'time_bounds', ncd_double, 2, dim2id, varid, & + long_name = 'history time interval endpoints') + end if dim2id(1) = strlen_dimid; dim2id(2) = time_dimid call ncd_defvar(nfid(t), 'date_written', ncd_char, 2, dim2id, varid) @@ -3436,13 +3466,16 @@ subroutine htape_timeconst(t, mode) call ncd_io('mscur' , mscur , 'write', nfid(t), nt=tape(t)%ntimes) call ncd_io('nstep' , nstep , 'write', nfid(t), nt=tape(t)%ntimes) - time = mdcur + mscur/secspday + timedata(1) = tape(t)%begtime ! beginning time + timedata(2) = mdcur + mscur/secspday ! end time + if (hist_avgflag_pertape(t) /= 'I') then ! NOT instantaneous fields tape + time = (timedata(1) + timedata(2)) * 0.5_r8 + call ncd_io('time_bounds', timedata, 'write', nfid(t), nt=tape(t)%ntimes) + else + time = timedata(2) + end if call ncd_io('time' , time , 'write', nfid(t), nt=tape(t)%ntimes) - timedata(1) = tape(t)%begtime - timedata(2) = time - call ncd_io('time_bounds', timedata, 'write', nfid(t), nt=tape(t)%ntimes) - call getdatetime (cdate, ctime) call ncd_io('date_written', cdate, 'write', nfid(t), nt=tape(t)%ntimes)