clm5/cime_config/SystemTests/mksurfdataesmf.py
2024-05-09 15:14:01 +08:00

157 lines
6.8 KiB
Python

"""
This test passes if mksurfdata_esmf generates an fsurdat (surface dataset)
and the CTSM completes a simulation with this fsurdat file.
We test res = '10x15' because it uses a lower-res topography file instead of
the 1-km topography raw dataset. The 1-km file causes the test to run out of
memory on cheyenne.
Currently casper complains that `git -C` is not a valid option.
I added -C to the `git describe` in gen_mksurfdata_namelist for this
system test to work.
"""
import os
import sys
import subprocess
from datetime import datetime
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
logger = logging.getLogger(__name__)
class MKSURFDATAESMF(SystemTestsCommon):
def __init__(self, case):
"""
initialize an object interface to the SMS system test
"""
SystemTestsCommon.__init__(self, case)
# Paths and strings needed throughout
ctsm_root = self._case.get_value("COMP_ROOT_DIR_LND")
self._tool_path = os.path.join(ctsm_root, "tools/mksurfdata_esmf")
self._tool_bld = os.path.join(self._get_caseroot(), "tool_bld")
time_stamp = datetime.today().strftime("%y%m%d")
self._res = "10x15" # see important comment in script's docstring
self._model_yr = "1850"
self._jobscript = os.path.join(
self._get_caseroot(), "mksurfdataesmf_test_jobscript_single.sh"
)
self._fsurdat_namelist = os.path.join(
self._get_caseroot(),
f"surfdata_{self._res}_hist_{self._model_yr}_78pfts_c{time_stamp}.namelist",
)
self._fsurdat_nc = os.path.join(
self._get_caseroot(),
f"surfdata_{self._res}_hist_{self._model_yr}_78pfts_c{time_stamp}.nc",
)
self._TestStatus_log_path = os.path.join(self._get_caseroot(), "TestStatus.log")
def build_phase(self, sharedlib_only=False, model_only=False):
"""
Build executable that will generate fsurdat
Generate namelist for generating fsurdat
Generate jobscript that runs executable
Modify user_nl_clm to point to the generated fsurdat
"""
# build_phase gets called twice:
# - once with sharedlib_only = True and
# - once with model_only = True
# Call the following steps only once during the test but do not skip
# if the test stops and gets restarted.
if sharedlib_only:
# Paths and strings
build_script_path = os.path.join(self._tool_path, "gen_mksurfdata_build")
nml_script_path = os.path.join(self._tool_path, "gen_mksurfdata_namelist")
gen_jobscript_path = os.path.join(self._tool_path, "gen_mksurfdata_jobscript_single")
gen_mksurfdata_namelist = f"{nml_script_path} --res {self._res} --start-year {self._model_yr} --end-year {self._model_yr}"
if not os.path.exists(nml_script_path):
sys.exit(f"ERROR The build naemlist script {nml_script_path} does NOT exist")
if not os.path.exists(gen_jobscript_path):
sys.exit(f"ERROR The jobscript script {gen_jobscript_path} does NOT exist")
gen_mksurfdata_jobscript = (
f"{gen_jobscript_path} --number-of-nodes 1 --tasks-per-node 64 --namelist-file "
+ f"{self._fsurdat_namelist} --bld-path {self._tool_bld} --jobscript-file {self._jobscript}"
)
if not os.path.exists(build_script_path):
sys.exit(f"ERROR The build script {build_script_path} does NOT exist")
# Rm tool_bld and build executable that will generate fsurdat
try:
subprocess.check_call(f"rm -rf {self._tool_bld}", shell=True)
except subprocess.CalledProcessError as e:
sys.exit(
f"{e} ERROR REMOVING {self._tool_bld}. DETAILS IN {self._TestStatus_log_path}"
)
try:
subprocess.check_call(f"{build_script_path} --blddir {self._tool_bld}", shell=True)
except subprocess.CalledProcessError as e:
print(f"build directory = {self._tool_bld}\n")
sys.exit(
f"{e} ERROR RUNNING {build_script_path} DETAILS IN {self._TestStatus_log_path}"
)
# Generate namelist for generating fsurdat (rm namelist if exists)
if os.path.exists(self._fsurdat_namelist):
os.remove(self._fsurdat_namelist)
try:
subprocess.check_call(gen_mksurfdata_namelist, shell=True)
except subprocess.CalledProcessError as e:
sys.exit(
f"{e} ERROR RUNNING {gen_mksurfdata_namelist}. DETAILS IN {self._TestStatus_log_path}"
)
# Generate jobscript that will run the executable
if os.path.exists(self._jobscript):
os.remove(self._jobscript)
try:
subprocess.check_call(gen_mksurfdata_jobscript, shell=True)
except subprocess.CalledProcessError as e:
sys.exit(
f"{e} ERROR RUNNING {gen_mksurfdata_jobscript}. DETAILS IN {self._TestStatus_log_path}"
)
# Change self._jobscript to an executable file
subprocess.check_call(f"chmod a+x {self._jobscript}", shell=True)
# Call this step only once even if the test stops and gets restarted.
if not os.path.exists(os.path.join(self._get_caseroot(), "done_MKSURFDATAESMF_setup.txt")):
# Modify user_nl_clm to point to the generated fsurdat
self._modify_user_nl()
with open("done_MKSURFDATAESMF_setup.txt", "w") as fp:
pass
self.build_indv(sharedlib_only=sharedlib_only, model_only=model_only)
def run_phase(self):
"""
Run executable to generate fsurdat
Submit CTSM run that uses fsurdat just generated
"""
# Run executable to generate fsurdat (rm fsurdat if exists)
if os.path.exists(self._fsurdat_nc):
os.remove(self._fsurdat_nc)
try:
subprocess.check_call(self._jobscript, shell=True)
except subprocess.CalledProcessError as e:
sys.exit(f"{e} ERROR RUNNING {self._jobscript}; details in {self._TestStatus_log_path}")
# Submit CTSM run that uses fsurdat just generated
self.run_indv()
def _modify_user_nl(self):
"""
Modify user_nl_clm to point to the generated fsurdat
"""
append_to_user_nl_files(
caseroot=self._get_caseroot(),
component="clm",
contents="fsurdat = '{}'".format(self._fsurdat_nc)
+ "\n"
+ "convert_ocean_to_land = .true.",
)