Skip to content

Commit

Permalink
Merge pull request #1863 from samsrabin/rx_crop_calendars2
Browse files Browse the repository at this point in the history
Prescribed sowing date and maturity requirements
  • Loading branch information
ekluzek authored Jul 27, 2023
2 parents 4a868c1 + 30e9189 commit 31d2369
Show file tree
Hide file tree
Showing 38 changed files with 7,617 additions and 1,229 deletions.
5 changes: 5 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ e44dc469439e02e9ee582dab274d890ebdfab104
b88e1cd1b28e3609684c79a2ec0e88f26cfc362b
51c102c5df2e0ef971b5f8eeeb477567899af63a
7dacad70e74e2ec97f6492d4e7a3cb5dd498bcd7
b771971e3299c4fa56534b93421f7a2b9c7282fd
9de88bb57ea9855da408cbec1dc8acb9079eda47
8bc4688e52ea23ef688e283698f70a44388373eb
# Ran SystemTests and python/ctsm through black python formatter
5364ad66eaceb55dde2d3d598fe4ce37ac83a93c
2 changes: 1 addition & 1 deletion Externals.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ required = True
local_path = cime
protocol = git
repo_url = https://github.com/ESMCI/cime
tag = cime6.0.108
tag = cime6.0.125
required = True

[cmeps]
Expand Down
61 changes: 57 additions & 4 deletions bld/CLMBuildNamelist.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1625,9 +1625,9 @@ sub process_namelist_inline_logic {
setup_logic_urban($opts, $nl_flags, $definition, $defaults, $nl);

###############################
# namelist group: crop #
# namelist group: crop_inparm #
###############################
setup_logic_crop($opts, $nl_flags, $definition, $defaults, $nl);
setup_logic_crop_inparm($opts, $nl_flags, $definition, $defaults, $nl);

###############################
# namelist group: ch4par_in #
Expand Down Expand Up @@ -1695,6 +1695,11 @@ sub process_namelist_inline_logic {
##################################
setup_logic_lai_streams($opts, $nl_flags, $definition, $defaults, $nl);

##################################
# namelist group: cropcal_streams #
##################################
setup_logic_cropcal_streams($opts, $nl_flags, $definition, $defaults, $nl);

##########################################
# namelist group: soil_moisture_streams #
##########################################
Expand Down Expand Up @@ -2149,7 +2154,7 @@ sub setup_logic_urban {

#-------------------------------------------------------------------------------

sub setup_logic_crop {
sub setup_logic_crop_inparm {
my ($opts, $nl_flags, $definition, $defaults, $nl) = @_;

if ( &value_is_true($nl->get_value('use_crop')) ) {
Expand Down Expand Up @@ -3920,6 +3925,53 @@ 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, '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') ;
my $sdate_file = $nl->get_value('stream_fldFileName_sdate') ;
my $gdd_file = $nl->get_value('stream_fldFileName_cultivar_gdds') ;
my $mesh_file = $nl->get_value('stream_meshfile_cropcal') ;
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 ( $sdate_file eq '' ) {
$log->fatal_error("If generate_crop_gdds is true, you must specify stream_fldFileName_sdate")
}
if ( $gdd_file ne '' ) {
$log->fatal_error("If generate_crop_gdds is true, do not specify stream_fldFileName_cultivar_gdds")
}
}
if ( $mesh_file eq '' and ( $sdate_file ne '' or $gdd_file ne '' ) ) {
$log->fatal_error("If prescribing crop sowing dates and/or maturity requirements, you must specify stream_meshfile_cropcal")
}
}

#-------------------------------------------------------------------------------

sub setup_logic_soilwater_movement {
my ($opts, $nl_flags, $definition, $defaults, $nl) = @_;

Expand Down Expand Up @@ -4297,14 +4349,15 @@ sub write_output_files {
my @groups;
@groups = qw(clm_inparm ndepdyn_nml popd_streams urbantv_streams light_streams
soil_moisture_streams lai_streams atm2lnd_inparm lnd2atm_inparm clm_canopyhydrology_inparm cnphenology
cropcal_streams
clm_soilhydrology_inparm dynamic_subgrid cnvegcarbonstate
finidat_consistency_checks dynpft_consistency_checks
clm_initinterp_inparm century_soilbgcdecompcascade
soilhydrology_inparm luna friction_velocity mineral_nitrogen_dynamics
soilwater_movement_inparm rooting_profile_inparm
soil_resis_inparm bgc_shared canopyfluxes_inparm aerosol
clmu_inparm clm_soilstate_inparm clm_nitrogen clm_snowhydrology_inparm
cnprecision_inparm clm_glacier_behavior crop irrigation_inparm
cnprecision_inparm clm_glacier_behavior crop_inparm irrigation_inparm
surfacealbedo_inparm water_tracers_inparm);

#@groups = qw(clm_inparm clm_canopyhydrology_inparm clm_soilhydrology_inparm
Expand Down
7 changes: 7 additions & 0 deletions bld/namelist_files/namelist_defaults_ctsm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,8 @@ attributes from the config_cache.xml file (with keys converted to upper-case).
<min_critical_dayl_method use_cn=".true." phys="clm5_1" >DependsOnLat</min_critical_dayl_method>
<onset_thresh_depends_on_veg >.false.</onset_thresh_depends_on_veg>
<min_critical_dayl_method >Constant</min_critical_dayl_method>
<generate_crop_gdds >.false.</generate_crop_gdds>
<use_mxmat >.true.</use_mxmat>

<!-- use additional stress deciduous onset trigger -->
<constrain_stress_deciduous_onset phys="clm4_5" >.false.</constrain_stress_deciduous_onset>
Expand Down Expand Up @@ -1665,6 +1667,11 @@ use_crop=".true.">lnd/clm2/surfdata_map/ctsm5.1.dev052/landuse.timeseries_mpasa1
<lai_mapalgo hgrid="1x1_asphaltjungleNJ" >nn</lai_mapalgo>
<lai_mapalgo hgrid="5x5_amazon" >nn</lai_mapalgo>

<!-- crop calendar streams namelist defaults -->
<stream_year_first_cropcal >1850</stream_year_first_cropcal>
<stream_year_last_cropcal >2100</stream_year_last_cropcal>
<model_year_align_cropcal >1850</model_year_align_cropcal>

<!-- lightning streams namelist defaults -->

<light_res use_cn=".false." >none</light_res>
Expand Down
51 changes: 48 additions & 3 deletions bld/namelist_files/namelist_definition_ctsm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1090,20 +1090,20 @@ Toggle to turn on the 1-year grain product pool in the crop model
</entry>

<entry id="baset_mapping" type="char*20" category="physics"
group="crop" valid_values="constant,varytropicsbylat" value="constant">
group="crop_inparm" valid_values="constant,varytropicsbylat" value="constant">
Type of mapping to use for base temperature for prognostic crop model
constant = Just use baset from the PFT parameter file
varytropicsbylat = Vary the tropics by latitude
</entry>

<entry id="baset_latvary_slope" type="real" category="physics"
group="crop" valid_values="" value="0.4d00">
group="crop_inparm" valid_values="" value="0.4d00">
Only used when baset_mapping == varytropicsbylat
Slope with latitude in degrees to vary tropical baset by
</entry>

<entry id="baset_latvary_intercept" type="real" category="physics"
group="crop" valid_values="" value="12.0d00">
group="crop_inparm" valid_values="" value="12.0d00">
Only used when baset_mapping == varytropicsbylat
Intercept at zero latitude to add to baset from the PFT parameter file
</entry>
Expand All @@ -1129,6 +1129,16 @@ Phenology onset depends on the vegetation type
(only used when CN is on)
</entry>

<entry id="generate_crop_gdds" type="logical" category="physics"
group="cnphenology" valid_values="" value=".false.">
Set to .true. in order to override crop harvesting logic and to instead harvest the day before the next sowing date. Used to generate growing-degree day outputs that can be used with an external script to generate new GDD requirement ("cultivar") files.
</entry>

<entry id="use_mxmat" type="logical" category="physics"
group="cnphenology" valid_values="" value=".true.">
Set to .false. in order to ignore crop PFT parameter for maximum growing season length (mxmat). Must be set to .false. when generate_crop_gdds is .true.
</entry>

<entry id="min_critical_dayl_method" type="char*25" category="physics"
group="cnphenology" valid_values="Constant,DependsOnLat,DependsOnVeg,DependsOnLatAndVeg">
Method for determining what the minimum critical day length for seasonal decidious leaf offset depends on
Expand Down Expand Up @@ -1805,6 +1815,41 @@ Mapping method from LAI input file to the model resolution
copy = copy using the same indices
</entry>

<!-- ======================================================================================== -->
<!-- cropcal_streams streams Namelist -->
<!-- ======================================================================================== -->

<!-- Crop calendars -->
<entry id="stream_year_first_cropcal" type="integer" category="datasets"
group="cropcal_streams" valid_values="" >
First year to loop over for crop calendar data
</entry>

<entry id="stream_year_last_cropcal" type="integer" category="datasets"
group="cropcal_streams" valid_values="" >
Last year to loop over for crop calendar data
</entry>

<entry id="model_year_align_cropcal" type="integer" category="datasets"
group="cropcal_streams" valid_values="" >
Simulation year that aligns with stream_year_first_cropcal value
</entry>

<entry id="stream_fldfilename_sdate" type="char*256(30)" category="datasets"
input_pathname="abs" group="cropcal_streams" valid_values="" >
Filename of input stream data for sowing dates
</entry>

<entry id="stream_fldfilename_cultivar_gdds" type="char*256(30)" category="datasets"
input_pathname="abs" group="cropcal_streams" valid_values="" >
Filename of input stream data for cultivar growing degree-day targets
</entry>

<entry id="stream_meshfile_cropcal" type="char*256" category="datasets"
input_pathname="abs" group="cropcal_streams" valid_values="" >
Filename of input stream data for crop calendar inputs
</entry>

<!-- ======================================================================================== -->
<!-- light_streams streams Namelist (when CN an CLM4_5 is active) -->
<!-- ======================================================================================== -->
Expand Down
57 changes: 8 additions & 49 deletions cime_config/SystemTests/fsurdatmodifyctsm.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import os
import re
import subprocess
import systemtest_utils as stu
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
Expand Down Expand Up @@ -69,58 +69,17 @@ def _run_modify_fsurdat(self):
tool_path = os.path.join(self._ctsm_root, "tools/modify_input_files/fsurdat_modifier")

self._case.load_env(reset=True)
conda_env = ". " + self._get_caseroot() + "/.env_mach_specific.sh; "
# Preprend the commands to get the conda environment for python first
conda_env += self._get_conda_env()
# Source the env
try:
subprocess.run(
conda_env + "python3 " + tool_path + " " + self._cfg_file_path,
shell=True,
check=True,
)
except subprocess.CalledProcessError as error:
print("ERROR while getting the conda environment and/or ")
print("running the fsurdat_modifier tool: ")
print("(1) If your ctsm_pylib environment is out of date or you ")
print("have not created the ctsm_pylib environment, yet, you may ")
print("get past this error by running ./py_env_create ")
print("in your ctsm directory and trying this test again. ")
print("(2) If conda is not available, install and load conda, ")
print("run ./py_env_create, and then try this test again. ")
print("(3) If (1) and (2) are not the issue, then you may be ")
print("getting an error within the fsurdat_modifier tool itself. ")
print("Default error message: ")
print(error.output)
raise
except:
print("ERROR trying to run fsurdat_modifier tool.")
raise
command = f"python3 {tool_path} {self._cfg_file_path}"
stu.run_python_script(
self._get_caseroot(),
"ctsm_pylib",
command,
tool_path,
)

def _modify_user_nl(self):
append_to_user_nl_files(
caseroot=self._get_caseroot(),
component="clm",
contents="fsurdat = '{}'".format(self._fsurdat_out),
)

def _get_conda_env(self):
#
# Add specific commands needed on different machines to get conda available
# Use semicolon here since it's OK to fail
#
# Execute the module unload/load when "which conda" fails
# eg on cheyenne
try:
subprocess.run("which conda", shell=True, check=True)
conda_env = " "
except subprocess.CalledProcessError:
# Remove python and add conda to environment for cheyennne
conda_env = "module unload python; module load conda;"

# Activate the python environment
conda_env += " conda activate ctsm_pylib"
# End above to get to actual command
conda_env += " && "

return conda_env
14 changes: 14 additions & 0 deletions cime_config/SystemTests/lreprstruct.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ def _case_one_setup(self):
contents="for_testing_use_repr_structure_pool=.true.",
)

# Replace any GRAIN outputs with the same outputs for REPRODUCTIVE1 and REPRODUCTIVE2
user_nl_clm_path = os.path.join(self._get_caseroot(), "user_nl_clm")
with open(user_nl_clm_path) as f:
user_nl_clm_text = f.read()
for grain_output in re.findall("GRAIN\w*", user_nl_clm_text):
user_nl_clm_text = user_nl_clm_text.replace(
grain_output,
grain_output.replace("GRAIN", "REPRODUCTIVE1")
+ "', '"
+ grain_output.replace("GRAIN", "REPRODUCTIVE2"),
)
with open(user_nl_clm_path, "w") as f:
f.write(user_nl_clm_text)

def _case_two_setup(self):
# This is needed in the nearly-standard case to prevent grain from being used to
# replenish crop seed deficits, thus making grain act like the reproductive
Expand Down
Loading

0 comments on commit 31d2369

Please sign in to comment.