Skip to content

Commit fb89f33

Browse files
authored
Merge pull request #2838 from slevis-lmwg/hist_time_mid_of_time_bounds
Change history time to be equal to the middle of the time bounds
2 parents 203db12 + 3ffe654 commit fb89f33

File tree

8 files changed

+191
-22
lines changed

8 files changed

+191
-22
lines changed

.git-blame-ignore-revs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ d866510188d26d51bcd6d37239283db690af7e82
2727
e096358c832ab292ddfd22dd5878826c7c788968
2828
475831f0fb0e31e97f630eac4e078c886558b61c
2929
fd5f177131d63d39e79a13918390bdfb642d781e
30+
a51816e0de380300b69db9fc3e2c7fa83b267b64
3031
# Ran SystemTests and python/ctsm through black python formatter
3132
5364ad66eaceb55dde2d3d598fe4ce37ac83a93c
3233
8056ae649c1b37f5e10aaaac79005d6e3a8b2380

.gitmodules

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ fxDONOTUSEurl = https://github.com/ESCOMP/CISM-wrapper
4444
[submodule "rtm"]
4545
path = components/rtm
4646
url = https://github.com/ESCOMP/RTM
47-
fxtag = rtm1_0_80
47+
fxtag = rtm1_0_84
4848
fxrequired = ToplevelRequired
4949
# Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed
5050
fxDONOTUSEurl = https://github.com/ESCOMP/RTM
5151

5252
[submodule "mosart"]
5353
path = components/mosart
5454
url = https://github.com/ESCOMP/MOSART
55-
fxtag = mosart1.1.02
55+
fxtag = mosart1.1.06
5656
fxrequired = ToplevelRequired
5757
# Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed
5858
fxDONOTUSEurl = https://github.com/ESCOMP/MOSART

doc/ChangeLog

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,112 @@
11
===============================================================
2+
Tag name: ctsm5.3.018
3+
Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310)
4+
Date: Fri 10 Jan 2025 05:37:08 PM MST
5+
One-line Summary: Change history time to be the middle of the time bounds
6+
7+
Purpose and description of changes
8+
----------------------------------
9+
Making the change to be consistent with CAM and to make history output more intuitive.
10+
11+
Significant changes to scientifically-supported configurations
12+
--------------------------------------------------------------
13+
14+
Does this tag change answers significantly for any of the following physics configurations?
15+
(Details of any changes will be given in the "Answer changes" section below.)
16+
17+
[Put an [X] in the box for any configuration with significant answer changes.]
18+
19+
[ ] clm6_0
20+
21+
[ ] clm5_1
22+
23+
[ ] clm5_0
24+
25+
[ ] ctsm5_0-nwp
26+
27+
[ ] clm4_5
28+
29+
30+
Bugs fixed
31+
----------
32+
List of CTSM issues fixed (include CTSM Issue # and description) [one per line]:
33+
Partly addresses issue #1059
34+
35+
Notes of particular relevance for users
36+
---------------------------------------
37+
Caveats for users (e.g., need to interpolate initial conditions):
38+
The history time variable now equals the middle of the time bounds.
39+
Instantaneous history tapes now do not include time bounds.
40+
Mixed history tapes do not change the treatment of instantaneous fields or move them to separate tapes, yet.
41+
42+
Notes of particular relevance for developers:
43+
---------------------------------------------
44+
Caveats for developers (e.g., code that is duplicated that requires double maintenance):
45+
Same changes are needed separately in clm, in mosart, and in rtm.
46+
47+
Changes to tests or testing:
48+
This tag introduces changes to the mosart/rtm testlists.
49+
50+
FAIL RXCROPMATURITYSKIPGEN_Ld1097.f10_f10_mg37.IHistClm60BgcCrop.derecho_intel.clm-cropMonthOutput RUN
51+
I did not label this failure EXPECTED because the fix comes in later in this series of "history" tags, in particular ctsm5.3.020.
52+
53+
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:
54+
https://github.com/ESCOMP/CTSM/pull/2084#issuecomment-2584164690
55+
In the next tag we expect to update to the rtm/mosart tags that include the fix.
56+
57+
Testing summary:
58+
----------------
59+
60+
[PASS means all tests PASS; OK means tests PASS other than expected fails.]
61+
62+
build-namelist tests
63+
64+
derecho - PASS
65+
66+
python testing (if python code has changed; see instructions in python/README.md; document testing done):
67+
68+
derecho - PASS
69+
70+
regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing):
71+
72+
derecho ----- OK
73+
izumi ------- OK
74+
75+
mosart
76+
derecho ----- OK
77+
izumi ------- OK
78+
79+
rtm
80+
derecho ----- OK
81+
82+
Answer changes
83+
--------------
84+
85+
Changes answers relative to baseline: Only time variable, plus read caveat
86+
87+
Summarize any changes to answers, i.e.,
88+
- what code configurations: all
89+
- what platforms/compilers: all
90+
- nature of change: only the time variable
91+
92+
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:
93+
https://github.com/ESCOMP/CTSM/pull/2838#issuecomment-2477608383
94+
https://github.com/ESCOMP/MOSART/issues/103#issuecomment-2479679014
95+
96+
Other details
97+
-------------
98+
List any git submodules updated (cime, rtm, mosart, cism, fates, etc.):
99+
rtm, mosart
100+
101+
Pull Requests that document the changes (include PR ids):
102+
https://github.com/ESCOMP/ctsm/pull/2838
103+
https://github.com/ESCOMP/MOSART/pull/70
104+
https://github.com/ESCOMP/RTM/issues/54
105+
https://github.com/ESCOMP/MOSART/pull/106
106+
https://github.com/ESCOMP/RTM/pull/39
107+
108+
===============================================================
109+
===============================================================
2110
Tag name: ctsm5.3.017
3111
Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310)
4112
Date: Thu 09 Jan 2025 11:56:43 AM MST

doc/ChangeSum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Tag Who Date Summary
22
============================================================================================================================
3+
ctsm5.3.018 slevis 01/10/2025 Change history time to be the middle of the time bounds
34
ctsm5.3.017 slevis 01/09/2025 Merge tmp-241219 branch to master
45
tmp-241219.n03.ctsm5.3.016 01/09/2025 Bug fix for izumi nag tests to pass (slevis)
56
tmp-241219.n02.ctsm5.3.016 01/08/2025 FATES hydro test update (glemieux)

python/ctsm/crop_calendars/cropcal_module.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -443,10 +443,7 @@ def import_output(
443443
)
444444

445445
# Convert time axis to integer year, saving original as 'cftime'
446-
this_ds_gs = this_ds_gs.assign_coords(
447-
{"cftime": this_ds["time_bounds"].isel({"hist_interval": 0})}
448-
)
449-
this_ds_gs = this_ds_gs.assign_coords({"time": [t.year for t in this_ds_gs["cftime"].values]})
446+
this_ds_gs = convert_time_to_int_year(filename, this_ds, this_ds_gs)
450447

451448
# Get number of harvests
452449
this_ds_gs["NHARVESTS"] = (this_ds_gs["GDDHARV_PERHARV"] > 0).sum(dim="mxharvests")
@@ -458,6 +455,35 @@ def import_output(
458455
return this_ds_gs, any_bad
459456

460457

458+
def convert_time_to_int_year(filename, this_ds, this_ds_gs):
459+
"""
460+
Convert time axis to integer year, saving original as 'cftime'
461+
"""
462+
if "time_bounds" in this_ds:
463+
# Always true before PR #2838, when even files with all instantaneous variables got
464+
# time_bounds saved. After that PR (and before the segregation of instantaneous and other
465+
# variables onto separate files), files with an instantaneous variable first in their list
466+
# do not get time_bounds saved.
467+
this_ds_gs = this_ds_gs.assign_coords(
468+
{"cftime": this_ds["time_bounds"].isel({"hist_interval": 0})}
469+
)
470+
this_ds_gs = this_ds_gs.assign_coords(
471+
{"time": [t.year for t in this_ds_gs["cftime"].values]}
472+
)
473+
elif this_ds["time"].attrs["long_name"] == "time at end of time step":
474+
# This is an "instantaneous file."
475+
this_ds_gs = this_ds_gs.assign_coords({"cftime": this_ds["time"]})
476+
this_ds_gs = this_ds_gs.assign_coords(
477+
{"time": [t.year - 1 for t in this_ds_gs["cftime"].values]}
478+
)
479+
else:
480+
raise RuntimeError(
481+
f"{filename} is neither an instantaneous nor a combined/non-instantaneous file."
482+
)
483+
484+
return this_ds_gs
485+
486+
461487
def handle_zombie_crops(this_ds):
462488
"""
463489
When doing transient runs, it's somehow possible for crops in newly-active patches to be

src/main/histFileMod.F90

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,7 @@ subroutine htape_addfld (t, f, avgflag)
11721172
integer :: beg1d,end1d ! beginning and ending indices for this field (assume already set)
11731173
integer :: num1d_out ! history output 1d size
11741174
type(bounds_type) :: bounds
1175+
character(len=avgflag_strlen) :: avgflag_temp ! local copy of hist_avgflag_pertape(t)
11751176
character(len=*),parameter :: subname = 'htape_addfld'
11761177
!-----------------------------------------------------------------------
11771178

@@ -1302,6 +1303,19 @@ subroutine htape_addfld (t, f, avgflag)
13021303
tape(t)%hlist(n)%avgflag = avgflag
13031304
end if
13041305

1306+
! Override this tape's avgflag if nhtfrq == 1
1307+
if (tape(t)%nhtfrq == 1) then ! output is instantaneous
1308+
hist_avgflag_pertape(t) = 'I'
1309+
end if
1310+
! Override this field's avgflag if the namelist or the previous line
1311+
! has set this tape to
1312+
! - instantaneous (I) or
1313+
! - local time (L)
1314+
avgflag_temp = hist_avgflag_pertape(t)
1315+
if (avgflag_temp == 'I' .or. avgflag_temp(1:1) == 'L') then
1316+
tape(t)%hlist(n)%avgflag = avgflag_temp
1317+
end if
1318+
13051319
end subroutine htape_addfld
13061320

13071321
!-----------------------------------------------------------------------
@@ -3098,6 +3112,7 @@ subroutine htape_timeconst(t, mode)
30983112
integer :: mcdate ! current date
30993113
integer :: yr,mon,day,nbsec ! year,month,day,seconds components of a date
31003114
integer :: hours,minutes,secs ! hours,minutes,seconds of hh:mm:ss
3115+
character(len= 12) :: step_or_bounds ! string used in long_name of several time variables
31013116
character(len= 10) :: basedate ! base date (yyyymmdd)
31023117
character(len= 8) :: basesec ! base seconds
31033118
character(len= 8) :: cdate ! system date
@@ -3357,20 +3372,30 @@ subroutine htape_timeconst(t, mode)
33573372

33583373
dim1id(1) = time_dimid
33593374
str = 'days since ' // basedate // " " // basesec
3360-
call ncd_defvar(nfid(t), 'time', tape(t)%ncprec, 1, dim1id, varid, &
3361-
long_name='time',units=str)
3375+
if (hist_avgflag_pertape(t) /= 'I') then ! NOT instantaneous fields tape
3376+
step_or_bounds = 'time_bounds'
3377+
long_name = 'time at exact middle of ' // step_or_bounds
3378+
call ncd_defvar(nfid(t), 'time', tape(t)%ncprec, 1, dim1id, varid, &
3379+
long_name=long_name, units=str)
3380+
call ncd_putatt(nfid(t), varid, 'bounds', 'time_bounds')
3381+
else ! instantaneous fields tape
3382+
step_or_bounds = 'time step'
3383+
long_name = 'time at end of ' // step_or_bounds
3384+
call ncd_defvar(nfid(t), 'time', tape(t)%ncprec, 1, dim1id, varid, &
3385+
long_name=long_name, units=str)
3386+
end if
33623387
cal = get_calendar()
33633388
if ( trim(cal) == NO_LEAP_C )then
33643389
caldesc = "noleap"
33653390
else if ( trim(cal) == GREGORIAN_C )then
33663391
caldesc = "gregorian"
33673392
end if
33683393
call ncd_putatt(nfid(t), varid, 'calendar', caldesc)
3369-
call ncd_putatt(nfid(t), varid, 'bounds', 'time_bounds')
33703394

33713395
dim1id(1) = time_dimid
3396+
long_name = 'current date (YYYYMMDD) at end of ' // step_or_bounds
33723397
call ncd_defvar(nfid(t) , 'mcdate', ncd_int, 1, dim1id , varid, &
3373-
long_name = 'current date (YYYYMMDD)')
3398+
long_name = long_name)
33743399
!
33753400
! add global attribute time_period_freq
33763401
!
@@ -3397,18 +3422,23 @@ subroutine htape_timeconst(t, mode)
33973422
call ncd_putatt(nfid(t), ncd_global, 'time_period_freq', &
33983423
trim(time_period_freq))
33993424

3425+
long_name = 'current seconds of current date at end of ' // step_or_bounds
34003426
call ncd_defvar(nfid(t) , 'mcsec' , ncd_int, 1, dim1id , varid, &
3401-
long_name = 'current seconds of current date', units='s')
3427+
long_name = long_name, units='s')
3428+
long_name = 'current day (from base day) at end of ' // step_or_bounds
34023429
call ncd_defvar(nfid(t) , 'mdcur' , ncd_int, 1, dim1id , varid, &
3403-
long_name = 'current day (from base day)')
3430+
long_name = long_name)
3431+
long_name = 'current seconds of current day at end of ' // step_or_bounds
34043432
call ncd_defvar(nfid(t) , 'mscur' , ncd_int, 1, dim1id , varid, &
3405-
long_name = 'current seconds of current day')
3433+
long_name = long_name)
34063434
call ncd_defvar(nfid(t) , 'nstep' , ncd_int, 1, dim1id , varid, &
34073435
long_name = 'time step')
34083436

34093437
dim2id(1) = hist_interval_dimid; dim2id(2) = time_dimid
3410-
call ncd_defvar(nfid(t), 'time_bounds', ncd_double, 2, dim2id, varid, &
3411-
long_name = 'history time interval endpoints')
3438+
if (hist_avgflag_pertape(t) /= 'I') then ! NOT instantaneous fields tape
3439+
call ncd_defvar(nfid(t), 'time_bounds', ncd_double, 2, dim2id, varid, &
3440+
long_name = 'history time interval endpoints')
3441+
end if
34123442

34133443
dim2id(1) = strlen_dimid; dim2id(2) = time_dimid
34143444
call ncd_defvar(nfid(t), 'date_written', ncd_char, 2, dim2id, varid)
@@ -3436,13 +3466,16 @@ subroutine htape_timeconst(t, mode)
34363466
call ncd_io('mscur' , mscur , 'write', nfid(t), nt=tape(t)%ntimes)
34373467
call ncd_io('nstep' , nstep , 'write', nfid(t), nt=tape(t)%ntimes)
34383468

3439-
time = mdcur + mscur/secspday
3469+
timedata(1) = tape(t)%begtime ! beginning time
3470+
timedata(2) = mdcur + mscur/secspday ! end time
3471+
if (hist_avgflag_pertape(t) /= 'I') then ! NOT instantaneous fields tape
3472+
time = (timedata(1) + timedata(2)) * 0.5_r8
3473+
call ncd_io('time_bounds', timedata, 'write', nfid(t), nt=tape(t)%ntimes)
3474+
else
3475+
time = timedata(2)
3476+
end if
34403477
call ncd_io('time' , time , 'write', nfid(t), nt=tape(t)%ntimes)
34413478

3442-
timedata(1) = tape(t)%begtime
3443-
timedata(2) = time
3444-
call ncd_io('time_bounds', timedata, 'write', nfid(t), nt=tape(t)%ntimes)
3445-
34463479
call getdatetime (cdate, ctime)
34473480
call ncd_io('date_written', cdate, 'write', nfid(t), nt=tape(t)%ntimes)
34483481

0 commit comments

Comments
 (0)