Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ctsm5.3.021: Standardize time metadata (we will mark this as the release tag for ctsm5.3) #2052

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7278900
Standardize time_bounds long_name.
samsrabin Jul 10, 2023
0ae9dcf
Add units to time_bounds.
samsrabin Jul 10, 2023
21fae29
Add calendar to time_bounds.
samsrabin Jul 10, 2023
1eb46c4
Renamed dimension hist_interval to nbnd.
samsrabin Jul 10, 2023
bbb8080
Use my RTM/MOSART forks' standardize-time-metadata branches.
samsrabin Jul 11, 2023
f255b06
Add calendar attribute to mcdate, mcsec, mdcur, and mscur.
samsrabin Jul 11, 2023
881fd07
Merge tag 'ctsm5.1.dev131' into standardize-time-metadata
samsrabin Aug 4, 2023
d49f74d
Merge branch 'zerothtstep' into standardize-time-metadata
slevis-lmwg Nov 19, 2024
1c3ae10
Move line inside if-statement
slevis-lmwg Nov 20, 2024
69bd98c
crop_calendars scripts: Handle center-of-period timesteps.
samsrabin Nov 22, 2024
bd7ecaa
Add SystemTests for RXCROPMATURITY with instantaneous h1.
samsrabin Nov 23, 2024
20ab4c9
RXCROPMATURITY: Fix _setup_all() call.
samsrabin Nov 25, 2024
13cbf65
Merge branch 'fix-rxcropmaturity-20241122' into standardize-time-meta…
samsrabin Dec 2, 2024
9b71518
Satisfy black and pylint.
samsrabin Dec 2, 2024
08753cd
Correction to if-statemt as in #2838, added here to facilitate testing
slevis-lmwg Dec 17, 2024
0360129
Merge tag 'ctsm5.3.019' into standardize-time-metadata
slevis-lmwg Jan 14, 2025
e4fef6e
Merge remote-tracking branch 'samsrabin/standardize-time-metadata' in…
slevis-lmwg Jan 14, 2025
d89c86e
Satisfy black
slevis-lmwg Jan 15, 2025
31b880c
Update .git-blame-ignore-revs
slevis-lmwg Jan 15, 2025
ce7f7b2
Update .gitmodules to mosart1.1.08 and rtm1_0_85
slevis-lmwg Jan 15, 2025
2c99ced
Draft ChangeLog/Sum
slevis-lmwg Jan 16, 2025
d0c4026
Merge tag 'ctsm5.3.020' into standardize-time-metadata
slevis-lmwg Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions cime_config/SystemTests/rxcropmaturity.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def __init__(self, case):
# Which conda environment should we use?
self._get_conda_env()

def _run_phase(self, skip_gen=False):
def _run_phase(self, skip_gen=False, h1_inst=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.

Expand All @@ -129,7 +129,7 @@ def _run_phase(self, skip_gen=False):
self._set_active_case(case_gddgen)

# Set up stuff that applies to both tests
self._setup_all()
self._setup_all(h1_inst)

# Add stuff specific to GDD-Generating run
logger.info("RXCROPMATURITY log: modify user_nl files: generate GDDs")
Expand Down Expand Up @@ -196,7 +196,7 @@ def _run_phase(self, skip_gen=False):
self._set_active_case(case_rxboth)

# Set up stuff that applies to both tests
self._setup_all()
self._setup_all(h1_inst)

# Add stuff specific to Prescribed Calendars run
logger.info("RXCROPMATURITY log: modify user_nl files: Prescribed Calendars")
Expand Down Expand Up @@ -264,7 +264,7 @@ def _get_rx_dates(self):
logger.error(error_message)
raise RuntimeError(error_message)

def _setup_all(self):
def _setup_all(self, h1_inst):
logger.info("RXCROPMATURITY log: _setup_all start")

# Get some info
Expand All @@ -274,7 +274,7 @@ def _setup_all(self):

# 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()
self._modify_user_nl_allruns(h1_inst)
logger.info("RXCROPMATURITY log: _setup_all done")

# Make a surface dataset that has every crop in every gridcell
Expand Down Expand Up @@ -399,7 +399,7 @@ def _run_check_rxboth_run(self, skip_gen):
tool_path,
)

def _modify_user_nl_allruns(self):
def _modify_user_nl_allruns(self, h1_inst):
nl_additions = [
"cropcals_rx = .true.",
"cropcals_rx_adapt = .false.",
Expand All @@ -417,6 +417,8 @@ def _modify_user_nl_allruns(self):
"hist_type1d_pertape(2) = 'PFTS'",
"hist_dov2xy(2) = .false.",
]
if h1_inst:
nl_additions.append("hist_avgflag_pertape(2) = 'I'")
self._append_to_user_nl_clm(nl_additions)

def _run_generate_gdds(self, case_gddgen):
Expand Down
6 changes: 6 additions & 0 deletions cime_config/SystemTests/rxcropmaturityinst.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from rxcropmaturity import RXCROPMATURITYSHARED


class RXCROPMATURITYINST(RXCROPMATURITYSHARED):
def run_phase(self):
self._run_phase(h1_inst=True)
6 changes: 6 additions & 0 deletions cime_config/SystemTests/rxcropmaturityskipgeninst.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from rxcropmaturity import RXCROPMATURITYSHARED


class RXCROPMATURITYSKIPGENINST(RXCROPMATURITYSHARED):
def run_phase(self):
self._run_phase(skip_gen=True, h1_inst=True)
20 changes: 20 additions & 0 deletions cime_config/config_tests.xml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,16 @@ This defines various CTSM-specific system tests
<HIST_N>$STOP_N</HIST_N>
</test>

<test NAME="RXCROPMATURITYINST">
<DESC>As RXCROPMATURITY but ensure instantaneous h1. Can be removed once instantaneous and other variables are on separate files.</DESC>
<INFO_DBUG>1</INFO_DBUG>
<DOUT_S>FALSE</DOUT_S>
<CONTINUE_RUN>FALSE</CONTINUE_RUN>
<REST_OPTION>never</REST_OPTION>
<HIST_OPTION>$STOP_OPTION</HIST_OPTION>
<HIST_N>$STOP_N</HIST_N>
</test>

<test NAME="RXCROPMATURITYSKIPGEN">
<DESC>As RXCROPMATURITY but don't actually generate GDDs. Allows short testing with existing GDD inputs.</DESC>
<INFO_DBUG>1</INFO_DBUG>
Expand All @@ -155,6 +165,16 @@ This defines various CTSM-specific system tests
<HIST_N>$STOP_N</HIST_N>
</test>

<test NAME="RXCROPMATURITYSKIPGENINST">
<DESC>As RXCROPMATURITYSKIPGEN but ensure instantaneous h1. Can be removed once instantaneous and other variables are on separate files.</DESC>
<INFO_DBUG>1</INFO_DBUG>
<DOUT_S>FALSE</DOUT_S>
<CONTINUE_RUN>FALSE</CONTINUE_RUN>
<REST_OPTION>never</REST_OPTION>
<HIST_OPTION>$STOP_OPTION</HIST_OPTION>
<HIST_N>$STOP_N</HIST_N>
</test>

<!--
SSP smoke CLM spinup test (only valid for CLM compsets with CLM45)
do an initial spin test (setting CLM_ACCELERATED_SPINUP to on)
Expand Down
22 changes: 22 additions & 0 deletions cime_config/testdefs/testlist_clm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3540,6 +3540,17 @@
</options>
</test>

<test name="RXCROPMATURITYINST_Lm61" grid="f10_f10_mg37" compset="IHistClm60BgcCrop" testmods="clm/cropMonthOutput">
<machines>
<machine name="derecho" compiler="intel" category="rxcropmaturity"/>
<machine name="derecho" compiler="intel" category="crop_calendars"/>
</machines>
<options>
<option name="wallclock">6:00:00</option>
<option name="comment">As RXCROPMATURITY, but ensure that h1 file is instantaneous. Can be removed once instantaneous and other variables are separated onto separate files.</option>
</options>
</test>

<test name="RXCROPMATURITYSKIPGEN_Ld1097" grid="f10_f10_mg37" compset="IHistClm60BgcCrop" testmods="clm/cropMonthOutput">
<machines>
<machine name="derecho" compiler="intel" category="aux_clm"/>
Expand All @@ -3552,6 +3563,17 @@
</options>
</test>

<test name="RXCROPMATURITYSKIPGENINST_Ld1097" grid="f10_f10_mg37" compset="IHistClm60BgcCrop" testmods="clm/cropMonthOutput">
<machines>
<machine name="derecho" compiler="intel" category="rxcropmaturity"/>
<machine name="derecho" compiler="intel" category="crop_calendars"/>
</machines>
<options>
<option name="wallclock">00:45:00</option>
<option name="comment">As RXCROPMATURITYSKIPGEN, but ensure that h1 file is instantaneous. Can be removed once instantaneous and other variables are separated onto separate files.</option>
</options>
</test>

<test name="ERP_D_P64x2_Ld10" grid="f10_f10_mg37" compset="I2000Clm60Bgc" testmods="clm/Hillslope">
<machines>
<machine name="derecho" compiler="intel" category="aux_clm"/>
Expand Down
3 changes: 2 additions & 1 deletion python/ctsm/crop_calendars/convert_axis_time2gs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import sys
import numpy as np
import xarray as xr
from ctsm.crop_calendars.cropcal_utils import get_integer_years

try:
import pandas as pd
Expand Down Expand Up @@ -85,7 +86,7 @@ def set_up_ds_with_gs_axis(ds_in):
if not any(x in ["mxsowings", "mxharvests"] for x in ds_in[var].dims):
data_vars[var] = ds_in[var]
# Set up the new dataset
gs_years = [t.year - 1 for t in ds_in.time.values[:-1]]
gs_years = get_integer_years(ds_in)[:-1]
coords = ds_in.coords
coords["gs"] = gs_years
ds_out = xr.Dataset(data_vars=data_vars, coords=coords, attrs=ds_in.attrs)
Expand Down
1 change: 1 addition & 0 deletions python/ctsm/crop_calendars/cropcal_figs_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from matplotlib import cm
import matplotlib.collections as mplcol

# pylint: disable=abstract-class-instantiated

# Colormaps (maps)
cropcal_colors = {
Expand Down
20 changes: 6 additions & 14 deletions python/ctsm/crop_calendars/cropcal_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,16 @@ def check_and_trim_years(year_1, year_n, ds_in):
"""
After importing a file, restrict it to years of interest.
"""
### In annual outputs, file with name Y is actually results from year Y-1.
### Note that time values refer to when it was SAVED. So 1981-01-01 is for year 1980.

def get_year_from_cftime(cftime_date):
# Subtract 1 because the date for annual files is when it was SAVED
return cftime_date.year - 1

# Check that all desired years are included
if get_year_from_cftime(ds_in.time.values[0]) > year_1:
raise RuntimeError(
f"Requested year_1 is {year_1} but first year in outputs is "
+ f"{get_year_from_cftime(ds_in.time.values[0])}"
)
if get_year_from_cftime(ds_in.time.values[-1]) < year_1:
year = utils.get_timestep_year(ds_in, ds_in.time.values[0])
if year > year_1:
raise RuntimeError(
f"Requested year_n is {year_n} but last year in outputs is "
+ f"{get_year_from_cftime(ds_in.time.values[-1])}"
f"Requested year_1 is {year_1} but first year in outputs is {year}"
)
year = utils.get_timestep_year(ds_in, ds_in.time.values[-1])
if year < year_1:
raise RuntimeError(f"Requested year_n is {year_n} but last year in outputs is {year}")

# Remove years outside range of interest
### Include an extra year at the end to finish out final seasons.
Expand Down
42 changes: 42 additions & 0 deletions python/ctsm/crop_calendars/cropcal_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,3 +430,45 @@ def make_lon_increasing(xr_obj):
raise RuntimeError("Unable to rearrange longitude axis so it's monotonically increasing")

return xr_obj.roll(lon=shift, roll_coords=True)


def is_inst_file(dsa):
"""
Check whether Dataset or DataArray has time data from an "instantaneous file"
"""
return "at end of" in dsa["time"].attrs["long_name"]


def get_beg_inst_timestep_year(timestep):
"""
Get year associated with the BEGINNING of a timestep in an
instantaneous file
"""
year = timestep.year

is_jan1 = timestep.dayofyr == 1
is_midnight = timestep.hour == timestep.minute == timestep.second == 0
if is_jan1 and is_midnight:
year -= 1

return year


def get_timestep_year(dsa, timestep):
"""
Get the year associated with a timestep, with different handling
depending on whether the file is instantaneous
"""
if is_inst_file(dsa):
year = get_beg_inst_timestep_year(timestep)
else:
year = timestep.year
return year


def get_integer_years(dsa):
"""
Convert time axis to numpy array of integer years
"""
out_array = [get_timestep_year(dsa, t) for t in dsa["time"].values]
return out_array
6 changes: 3 additions & 3 deletions python/ctsm/crop_calendars/generate_gdds_functions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
Functions to support generate_gdds.py
"""
# pylint: disable=too-many-lines,too-many-statements
# pylint: disable=too-many-lines,too-many-statements,abstract-class-instantiated
import warnings
import os
import glob
Expand Down Expand Up @@ -553,8 +553,8 @@ def import_and_process_1yr(
clm_gdd_var = "GDDACCUM"
my_vars = [clm_gdd_var, "GDDHARV"]
patterns = [f"*h2.{this_year-1}-01*.nc", f"*h2.{this_year-1}-01*.nc.base"]
for p in patterns:
pattern = os.path.join(indir, p)
for pat in patterns:
pattern = os.path.join(indir, pat)
h2_files = glob.glob(pattern)
if h2_files:
break
Expand Down
14 changes: 10 additions & 4 deletions src/main/histFileMod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ end subroutine copy_entry_interface
type(file_desc_t), target :: nfid(max_tapes) ! file ids
type(file_desc_t), target :: ncid_hist(max_tapes) ! file ids for history restart files
integer :: time_dimid ! time dimension id
integer :: hist_interval_dimid ! time bounds dimension id
integer :: nbnd_dimid ! time bounds dimension id
integer :: strlen_dimid ! string dimension id
!
! Time Constant variable names and filename
Expand Down Expand Up @@ -2542,7 +2542,7 @@ subroutine htape_create (t, histrest)
end if

if ( .not. lhistrest )then
call ncd_defdim(lnfid, 'hist_interval', 2, hist_interval_dimid)
call ncd_defdim(lnfid, 'nbnd', 2, nbnd_dimid)
call ncd_defdim(lnfid, 'time', ncd_unlimited, time_dimid)
if (masterproc)then
write(iulog,*) trim(subname), &
Expand Down Expand Up @@ -3396,6 +3396,7 @@ subroutine htape_timeconst(t, mode)
long_name = 'current date (YYYYMMDD) at end of ' // step_or_bounds
call ncd_defvar(nfid(t) , 'mcdate', ncd_int, 1, dim1id , varid, &
long_name = long_name)
call ncd_putatt(nfid(t), varid, 'calendar', caldesc)
!
! add global attribute time_period_freq
!
Expand Down Expand Up @@ -3425,19 +3426,24 @@ subroutine htape_timeconst(t, mode)
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 = long_name, units='s')
call ncd_putatt(nfid(t), varid, 'calendar', caldesc)
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 = long_name)
call ncd_putatt(nfid(t), varid, 'calendar', caldesc)
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 = long_name)
call ncd_putatt(nfid(t), varid, 'calendar', caldesc)
call ncd_defvar(nfid(t) , 'nstep' , ncd_int, 1, dim1id , varid, &
long_name = 'time step')

dim2id(1) = hist_interval_dimid; dim2id(2) = time_dimid
dim2id(1) = nbnd_dimid; dim2id(2) = time_dimid
if (tape(t)%hlist(1)%avgflag /= 'I') then ! NOT instantaneous fields tape
call ncd_defvar(nfid(t), 'time_bounds', ncd_double, 2, dim2id, varid, &
long_name = 'history time interval endpoints')
long_name = 'time interval endpoints', &
units = str)
call ncd_putatt(nfid(t), varid, 'calendar', caldesc)
end if

dim2id(1) = strlen_dimid; dim2id(2) = time_dimid
Expand Down