diff --git a/CMakeLists.txt b/CMakeLists.txt index 28fb2a269..bc7cce6df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,7 +99,7 @@ endif() target_include_directories(dshr PUBLIC $ $) -foreach(COMP datm dice dlnd docn drof dwav) +foreach(COMP datm dice dglc dlnd docn drof dwav) add_subdirectory("${COMP}") if(BLD_STANDALONE) target_include_directories(${COMP} PUBLIC $ diff --git a/cime_config/buildlib b/cime_config/buildlib index a692a4e50..2da096a7a 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -184,7 +184,7 @@ def buildlib(bldroot, libroot, case): expect(False, nextline) # Link the CDEPS component directories to the location expected by cime - for comp in ("atm", "lnd", "ice", "ocn", "rof", "wav"): + for comp in ("atm", "glc", "lnd", "ice", "ocn", "rof", "wav"): compname = case.get_value("COMP_{}".format(comp.upper())) comppath = os.path.join(case.get_value("EXEROOT"), comp, "obj") if compname == "d" + comp: diff --git a/cime_config/testdefs/testlist_cdeps.xml b/cime_config/testdefs/testlist_cdeps.xml deleted file mode 100644 index 2147b04d3..000000000 --- a/cime_config/testdefs/testlist_cdeps.xml +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/datm/atm_comp_nuopc.F90 b/datm/atm_comp_nuopc.F90 index dbd441085..7a2a41c7b 100644 --- a/datm/atm_comp_nuopc.F90 +++ b/datm/atm_comp_nuopc.F90 @@ -29,7 +29,7 @@ module cdeps_datm_comp use shr_const_mod , only : shr_const_cday use shr_sys_mod , only : shr_sys_abort use shr_cal_mod , only : shr_cal_ymd2date - use shr_log_mod , only : shr_log_setLogUnit + use shr_log_mod , only : shr_log_setLogUnit use dshr_methods_mod , only : dshr_state_diagnose, chkerr, memcheck use dshr_strdata_mod , only : shr_strdata_type, shr_strdata_init_from_config, shr_strdata_advance use dshr_strdata_mod , only : shr_strdata_get_stream_pointer, shr_strdata_setOrbs @@ -264,7 +264,7 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) ! Obtain flds_scalar values, mpi values, multi-instance values and ! set logunit and set shr logging to my log file - call dshr_init(gcomp, 'ATM', sdat, mpicom, my_task, inst_index, inst_suffix, & + call dshr_init(gcomp, 'ATM', mpicom, my_task, inst_index, inst_suffix, & flds_scalar_name, flds_scalar_num, flds_scalar_index_nx, flds_scalar_index_ny, & logunit, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return diff --git a/datm/cime_config/testdefs/testlist_datm.xml b/datm/cime_config/testdefs/testlist_datm.xml index eab4c646e..a20e56305 100644 --- a/datm/cime_config/testdefs/testlist_datm.xml +++ b/datm/cime_config/testdefs/testlist_datm.xml @@ -1,14 +1,16 @@ - + + + @@ -17,6 +19,7 @@ + @@ -25,6 +28,7 @@ + @@ -33,6 +37,7 @@ + @@ -41,6 +46,7 @@ + @@ -49,6 +55,7 @@ + @@ -57,6 +64,7 @@ + @@ -65,6 +73,7 @@ + @@ -73,6 +82,7 @@ + @@ -81,6 +91,7 @@ + @@ -89,6 +100,7 @@ + @@ -97,14 +109,7 @@ - - - - - - - - + diff --git a/datm/datm_datamode_core2_mod.F90 b/datm/datm_datamode_core2_mod.F90 index 552b35dae..41d2caae8 100644 --- a/datm/datm_datamode_core2_mod.F90 +++ b/datm/datm_datamode_core2_mod.F90 @@ -40,6 +40,8 @@ module datm_datamode_core2_mod ! export state pointers real(r8), pointer :: Sa_u(:) => null() real(r8), pointer :: Sa_v(:) => null() + real(r8), pointer :: Sa_u10m(:) => null() + real(r8), pointer :: Sa_v10m(:) => null() real(r8), pointer :: Sa_z(:) => null() real(r8), pointer :: Sa_tbot(:) => null() real(r8), pointer :: Sa_ptem(:) => null() @@ -115,6 +117,8 @@ subroutine datm_datamode_core2_advertise(exportState, fldsexport, flds_scalar_na call dshr_fldList_add(fldsExport, 'Sa_z' ) call dshr_fldList_add(fldsExport, 'Sa_u' ) call dshr_fldList_add(fldsExport, 'Sa_v' ) + call dshr_fldList_add(fldsExport, 'Sa_u10m' ) + call dshr_fldList_add(fldsExport, 'Sa_v10m' ) call dshr_fldList_add(fldsExport, 'Sa_ptem' ) call dshr_fldList_add(fldsExport, 'Sa_dens' ) call dshr_fldList_add(fldsExport, 'Sa_pslv' ) @@ -219,6 +223,10 @@ subroutine datm_datamode_core2_init_pointers(exportState, sdat, datamode, factor if (ChkErr(rc,__LINE__,u_FILE_u)) return call dshr_state_getfldptr(exportState, 'Sa_v' , fldptr1=Sa_v , rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return + call dshr_state_getfldptr(exportState, 'Sa_u10m' , fldptr1=Sa_u10m , rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call dshr_state_getfldptr(exportState, 'Sa_v10m' , fldptr1=Sa_v10m , rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return call dshr_state_getfldptr(exportState, 'Sa_tbot' , fldptr1=Sa_tbot , rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return call dshr_state_getfldptr(exportState, 'Sa_pbot' , fldptr1=Sa_pbot , rc=rc) @@ -315,6 +323,10 @@ subroutine datm_datamode_core2_advance(datamode, target_ymd, target_tod, target_ Sa_u(n) = uprime*cos(winddFactor(n)*degtorad) - vprime*sin(winddFactor(n)*degtorad) Sa_v(n) = uprime*sin(winddFactor(n)*degtorad) + vprime*cos(winddFactor(n)*degtorad) + ! Set Sa_u10m and Sa_v10m to Sa_u and Sa_v + Sa_u10m(n) = Sa_u(n) + Sa_v10m(n) = Sa_v(n) + !--- density and pslv taken directly from input stream, set pbot --- Sa_pbot(n) = Sa_pslv(n) diff --git a/datm/datm_datamode_jra_mod.F90 b/datm/datm_datamode_jra_mod.F90 index d280bf860..9eb815150 100644 --- a/datm/datm_datamode_jra_mod.F90 +++ b/datm/datm_datamode_jra_mod.F90 @@ -25,6 +25,10 @@ module datm_datamode_jra_mod ! export state pointers real(r8), pointer :: Sa_z(:) => null() + real(r8), pointer :: Sa_u(:) => null() + real(r8), pointer :: Sa_v(:) => null() + real(r8), pointer :: Sa_u10m(:) => null() + real(r8), pointer :: Sa_v10m(:) => null() real(r8), pointer :: Sa_tbot(:) => null() real(r8), pointer :: Sa_ptem(:) => null() real(r8), pointer :: Sa_shum(:) => null() @@ -88,6 +92,8 @@ subroutine datm_datamode_jra_advertise(exportState, fldsexport, flds_scalar_name call dshr_fldList_add(fldsExport, 'Sa_z' ) call dshr_fldList_add(fldsExport, 'Sa_u' ) call dshr_fldList_add(fldsExport, 'Sa_v' ) + call dshr_fldList_add(fldsExport, 'Sa_u10m' ) + call dshr_fldList_add(fldsExport, 'Sa_v10m' ) call dshr_fldList_add(fldsExport, 'Sa_ptem' ) call dshr_fldList_add(fldsExport, 'Sa_dens' ) call dshr_fldList_add(fldsExport, 'Sa_pslv' ) @@ -174,6 +180,14 @@ subroutine datm_datamode_jra_init_pointers(exportState, sdat, rc) call shr_strdata_get_stream_pointer( sdat, 'Faxa_swdn' , strm_swdn , rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return + call dshr_state_getfldptr(exportState, 'Sa_u' , fldptr1=Sa_u , rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call dshr_state_getfldptr(exportState, 'Sa_v' , fldptr1=Sa_v , rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call dshr_state_getfldptr(exportState, 'Sa_u10m' , fldptr1=Sa_u10m , rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call dshr_state_getfldptr(exportState, 'Sa_v10m' , fldptr1=Sa_v10m , rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return call dshr_state_getfldptr(exportState, 'Sa_z' , fldptr1=Sa_z , rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return call dshr_state_getfldptr(exportState, 'Sa_tbot' , fldptr1=Sa_tbot , rc=rc) @@ -253,6 +267,10 @@ subroutine datm_datamode_jra_advance(exportstate, target_ymd, target_tod, model_ Sa_pbot(n) = Sa_pslv(n) Sa_ptem(n) = Sa_tbot(n) + ! Set Sa_u10m and Sa_v10m to Sa_u and Sa_v + Sa_u10m(n) = Sa_u(n) + Sa_v10m(n) = Sa_v(n) + ! density computation for JRA55 forcing Sa_dens(n) = Sa_pbot(n)/(rdair*Sa_tbot(n)*(1 + 0.608*Sa_shum(n))) diff --git a/dglc/CMakeLists.txt b/dglc/CMakeLists.txt new file mode 100644 index 000000000..2c62d04b0 --- /dev/null +++ b/dglc/CMakeLists.txt @@ -0,0 +1,34 @@ +project(dglc Fortran) +set(SRCFILES glc_comp_nuopc.F90 + dglc_datamode_noevolve_mod.F90) + +foreach(FILE ${SRCFILES}) + if(EXISTS "${CASEROOT}/SourceMods/src.dglc/${FILE}") + list(REMOVE_ITEM SRCFILES ${FILE}) + list(APPEND SRCFILES "${CASEROOT}/SourceMods/src.dglc/${FILE}") + message("Using ${FILE} from ${CASEROOT}/SourceMods/src.dglc") + endif() +endforeach() + +message("DGLC srcfiles are ${SRCFILES}") + +add_library(dglc ${SRCFILES}) + +add_dependencies(dglc dshr streams) +target_include_directories (dglc PRIVATE ${ESMF_F90COMPILEPATHS}) +target_include_directories (dglc PRIVATE ${CMAKE_SOURCE_DIR}) +target_include_directories (dglc PRIVATE ${PIO_Fortran_INCLUDE_DIR}) +if(NOT DISABLE_FoX) + target_include_directories (dglc PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../fox/include) +endif() + +if(BLD_STANDALONE) + # ESMX requires mod files + foreach (SRC ${SRCFILES}) + string(REGEX REPLACE "[.]F90$" ".mod" MOD ${SRC}) + if (NOT DEFINED CIMEROOT AND MOD STREQUAL glc_comp_nuopc.mod) + set(MOD cdeps_dglc_comp.mod) + endif() + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${MOD}" DESTINATION include) + endforeach () +endif() diff --git a/dglc/cime_config/buildlib b/dglc/cime_config/buildlib new file mode 120000 index 000000000..0c5e984ac --- /dev/null +++ b/dglc/cime_config/buildlib @@ -0,0 +1 @@ +../../cime_config/buildlib_comps \ No newline at end of file diff --git a/dglc/cime_config/buildnml b/dglc/cime_config/buildnml new file mode 100755 index 000000000..b68516429 --- /dev/null +++ b/dglc/cime_config/buildnml @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 + +"""Namelist creator for CIME's data glc model. +""" + +# Typically ignore this. +# pylint: disable=invalid-name + +# Disable these because this is our standard setup +# pylint: disable=wildcard-import,unused-wildcard-import,wrong-import-position + +import os, sys + +_CDEPS_CONFIG = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir,os.pardir,"cime_config") +_CIMEROOT = os.environ.get("CIMEROOT") +if _CIMEROOT is None: + raise SystemExit("ERROR: must set CIMEROOT environment variable") +_LIBDIR = os.path.join(_CIMEROOT, "CIME", "Tools") +sys.path.append(_LIBDIR) +sys.path.append(_CDEPS_CONFIG) + +from standard_script_setup import * +from CIME.case import Case +from CIME.nmlgen import NamelistGenerator +from CIME.utils import expect, safe_copy +from CIME.XML.files import Files +from CIME.buildnml import create_namelist_infile, parse_input, copy_inputs_to_rundir +from stream_cdeps import StreamCDEPS + +logger = logging.getLogger(__name__) + +# pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements +#################################################################################### +def _create_namelists(case, confdir, inst_string, infile, nmlgen, data_list_path): +#################################################################################### + """Write out the namelist and stream xml files for this component. + + Most arguments are the same as those for `NamelistGenerator`. The + `inst_string` argument is used as a suffix to distinguish files for + different instances. The `confdir` argument is used to specify the directory + in which output files will be placed. + """ + #---------------------------------------------------- + # Write out dglc_in and dglc.streams.xml + #---------------------------------------------------- + caseroot = case.get_value("CASEROOT") + dglc_mode = case.get_value("DGLC_MODE") + glc_grid = case.get_value("GLC_GRID") + + # Check for incompatible options. + expect(glc_grid != "null", + "DGLC_GRID cannot be null") + + # Do not allow single column mode for dglc + if case.get_value('PTS_LON'): + scol_lon = float(case.get_value('PTS_LON')) + else: + scol_lon = -999. + if case.get_value('PTS_LAT'): + scol_lat = float(case.get_value('PTS_LAT')) + else: + scol_lat = -999. + if (scol_lon > -999. or scol_lat > -999.): + expect(False, + "single column mode for DGLC is not currently allowed") + + # Log some settings. + logger.debug("DGLC mode is {}".format(dglc_mode)) + + # Initialize namelist defaults + config = {} + config['dglc_mode'] = dglc_mode + config['glc_grid'] = glc_grid + + # Initialize nmlgen + nmlgen.init_defaults(infile, config) + + # Generate dglc_in + namelist_file = os.path.join(confdir, "dglc_in") + nmlgen.write_output_file(namelist_file, data_list_path, groups=['dglc_nml']) + + # Generate dglc.streams.xml + logger.debug("dglc_mode is {}".format(dglc_mode)) + + if 'noevolve' in dglc_mode: + generate_stream_file = False + else: + generate_stream_file = True + #endif + if generate_stream_file: + # Determine streams + streamlist = nmlgen.get_streams() + if type(streamlist) == type(str()): + streamlist = [] + outfile = os.path.join(confdir, "dglc.streams"+inst_string+".xml" ) + schema_file = os.path.join(_CDEPS_CONFIG,"stream_definition_v2.0.xsd") + stream_file = os.path.join(_CDEPS_CONFIG,os.pardir, "dglc","cime_config","stream_definition_dglc.xml") + streams = StreamCDEPS(stream_file, schema_file) + streams.create_stream_xml(streamlist, case, outfile, data_list_path, + os.path.join(caseroot,'user_nl_dglc_streams'+inst_string)) + #endif + +############################################################################### +def buildnml(case, caseroot, compname): +############################################################################### + rundir = case.get_value("RUNDIR") + inst_name = compname.upper()[1:] + ninst = case.get_value("NINST_"+inst_name) + if ninst is None: + ninst = case.get_value("NINST") + confdir = os.path.join(caseroot,"Buildconf",compname + "conf") + if not os.path.isdir(confdir): + os.makedirs(confdir) + + #---------------------------------------------------- + # Construct the namelist generator + #---------------------------------------------------- + # determine directory for user modified namelist_definitions.xml + user_xml_dir = os.path.join(caseroot, "SourceMods", "src." + compname) + expect (os.path.isdir(user_xml_dir), + "user_xml_dir {} does not exist ".format(user_xml_dir)) + + # NOTE: User definition *replaces* existing definition. + files = Files(comp_interface="nuopc") + definition_file = [files.get_value("NAMELIST_DEFINITION_FILE", {"component":compname})] + + user_definition = os.path.join(user_xml_dir, "namelist_definition_"+compname+".xml") + if os.path.isfile(user_definition): + definition_file = [user_definition] + for file_ in definition_file: + expect(os.path.isfile(file_), "Namelist XML file {} not found!".format(file_)) + + # Create the namelist generator object - independent of instance + nmlgen = NamelistGenerator(case, definition_file, files=files) + + #---------------------------------------------------- + # Clear out old data. + #---------------------------------------------------- + + data_list_path = os.path.join(caseroot, "Buildconf", compname+".input_data_list") + if os.path.exists(data_list_path): + os.remove(data_list_path) + + #---------------------------------------------------- + # Loop over instances + #---------------------------------------------------- + for inst_counter in range(1, ninst+1): + # determine instance string + inst_string = "" + if ninst > 1: + inst_string = '_' + "{:04d}".format(inst_counter) + + # If multi-instance case does not have restart file, use + # single-case restart for each instance + rpointer = "rpointer." + compname + if (os.path.isfile(os.path.join(rundir,rpointer)) and + (not os.path.isfile(os.path.join(rundir,rpointer + inst_string)))): + safe_copy(os.path.join(rundir, rpointer), + os.path.join(rundir, rpointer + inst_string)) + + inst_string_label = inst_string + if not inst_string_label: + inst_string_label = "\"\"" + + # create namelist output infile using user_nl_file as input + user_nl_file = os.path.join(caseroot, "user_nl_" + compname + inst_string) + expect(os.path.isfile(user_nl_file), + "Missing required user_nl_file {} ".format(user_nl_file)) + infile = os.path.join(confdir, "namelist_infile") + create_namelist_infile(case, user_nl_file, infile) + namelist_infile = [infile] + + # create namelist and xml stream file(s) + _create_namelists(case, confdir, inst_string, namelist_infile, nmlgen, data_list_path) + + # copy namelist files and stream text files, to rundir + copy_inputs_to_rundir(caseroot, compname, confdir, rundir, inst_string) + +############################################################################### +def get_user_nl_list(case): +############################################################################### + """Returns a list of user_nl_dglc* files needed in this case + This function is called by CIME to stage the user_nl_dglc* files in the case + directory. + """ + user_nl_list = ["user_nl_dglc", "user_nl_dglc_streams"] + return user_nl_list + +############################################################################### +def _main_func(): + # Build the component namelist and required stream xml files + caseroot = parse_input(sys.argv) + with Case(caseroot) as case: + buildnml(case, caseroot, "dglc") + +if __name__ == "__main__": + _main_func() diff --git a/dglc/cime_config/config_archive.xml b/dglc/cime_config/config_archive.xml new file mode 100644 index 000000000..f61daec79 --- /dev/null +++ b/dglc/cime_config/config_archive.xml @@ -0,0 +1,11 @@ + + + r + rs1 + unset + + rpointer.glc$NINST_STRING + $CASE.dglc$NINST_STRING.r.$DATENAME.nc,$CASE.dglc$NINST_STRING.rs1.$DATENAME.bin + + + diff --git a/dglc/cime_config/config_component.xml b/dglc/cime_config/config_component.xml new file mode 100644 index 000000000..15271a5b8 --- /dev/null +++ b/dglc/cime_config/config_component.xml @@ -0,0 +1,92 @@ + + + + + + + + Data glc model (DGLC) + no evolve mode + + + + char + dglc + dglc + case_comp + env_case.xml + Name of land component + + + + char + noevolve + noevolve + + noevolve + + run_component_dglc + env_run.xml + + NOEVOLVE mode is used in CESM as follows. + In typical runs, CISM is not evolving; CLM computes the surface mass + balance (SMB) and sends it to CISM, but CISM’s ice sheet geometry + remains fixed over the course of the run. In these runs, CISM serves + two roles in the system: + + + + + logical + FALSE + run_component_dglc + env_run.xml + + TRUE + + Whether to include the Greenland Ice Sheet in this DGLC simulation + This should generally be set at create_newcase time (via the compset). In principle it + can be changed later, but great care is needed to change a number of other variables + to be consistent (GLC_GRID, GLC_DOMAIN_MESH and possibly others). + + + + + logical + FALSE + run_component_dglc + env_run.xml + + TRUE + + Whether to include the Antarctica Ice Sheet in this DGLC simulation + This should generally be set at create_newcase time (via the compset). In principle it + can be changed later, but great care is needed to change a number of other variables + to be consistent (GLC_GRID, GLC_DOMAIN_MESH and possibly others). + + + + + logical + TRUE,FALSE + FALSE + run_component_dglc + env_run.xml + If set to true, than dglc restarts will not be read on a continuation run. + + + + + ========================================= + DGLC naming conventions + ========================================= + + + diff --git a/dglc/cime_config/namelist_definition_dglc.xml b/dglc/cime_config/namelist_definition_dglc.xml new file mode 100644 index 000000000..8bc4e0c0e --- /dev/null +++ b/dglc/cime_config/namelist_definition_dglc.xml @@ -0,0 +1,140 @@ + + + + + + + + + + char(100) + streams + streams_file + List of streams used for each supported dglc_mode + + none + + + + + char + dglc + dglc_nml + noevolve + + Copies all fields directly from the input data streams Any required + fields not found on an input stream will be set to zero. + + + noevolve + + + + + char + streams + abs + dglc_nml + + $DIN_LOC_ROOT/glc/cism/Antarctica/ISMIP6_Antarctica_8km.init.c210908.nc:$DIN_LOC_ROOT/glc/cism/Greenland/greenland_4km_epsg3413_c171126.nc + $DIN_LOC_ROOT/glc/cism/Greenland/greenland_4km_epsg3413_c171126.nc + $DIN_LOC_ROOT/glc/cism/Antarctica/ISMIP6_Antarctica_8km.init.c210908.nc + + + colon deliminted string of inputdata files + + + + + char + streams + abs + dglc_nml + + $GLC_DOMAIN_MESH + + + colon deliminted string of file(s) specifying model mesh + for more than one ice sheets this will contain an array of mesh file names + + + + + real(20) + streams + dglc_nml + + 8000,4000 + 4000 + 8000 + + + model internal grid size(s) in m which is used to compute the internal model + model area in radians**2 using the formula (model_internal_gridsize/shr_const_rearth)**2 - + for more than one ice sheet this will contain an array of model_internal_gridsize values. + + + + + integer(20) + streams + dglc_nml + + 704,416 + 704 + 416 + 76 + + + global size(s) of nx where for more than one ice sheet this + will contain an array of nx values + + + + + integer(20) + streams + dglc_nml + + 576,704 + 576 + 704 + 141 + + + global size(s) of ny where for more than one ice sheet this + will contain an array of ny values + + + + + char + dglc + dglc_nml + + main restart file name for dglc model + + + null + + + + + logical + dglc + dglc_nml + + If set to true, than dglc restarts will not be read on a continuation run. + This capability is used, for example, in CTSM spinup runs. + + + $DGLC_SKIP_RESTART_READ + + + + diff --git a/dglc/cime_config/stream_definition_dglc.xml b/dglc/cime_config/stream_definition_dglc.xml new file mode 100644 index 000000000..80ba75c5b --- /dev/null +++ b/dglc/cime_config/stream_definition_dglc.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + unset + + + unset + + + unset unset + + null + + bilinear + + null + 0 + 0 + 0 + 0 + + lower + + + cycle + + + 1.5 + + single + + + diff --git a/dglc/cime_config/testdefs/testlist_dglc.xml b/dglc/cime_config/testdefs/testlist_dglc.xml new file mode 100644 index 000000000..194773fa7 --- /dev/null +++ b/dglc/cime_config/testdefs/testlist_dglc.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dglc/cime_config/user_nl_dglc b/dglc/cime_config/user_nl_dglc new file mode 100644 index 000000000..2cbcf3997 --- /dev/null +++ b/dglc/cime_config/user_nl_dglc @@ -0,0 +1,13 @@ +!------------------------------------------------------------------------ +! Users should ONLY USE user_nl_dglc to change namelists variables +! Users should add all user specific namelist changes below in the form of +! namelist_var = new_namelist_value +! Note that any namelist variable from shr_strdata_nml and dglc_nml can +! be modified below using the above syntax +! User preview_namelists to view (not modify) the output namelist in the +! directory $CASEROOT/CaseDocs +! To modify the contents of a stream txt file, first use preview_namelists +! to obtain the contents of the stream txt files in CaseDocs, and then +! place a copy of the modified stream txt file in $CASEROOT with the string +! user_ prepended. +!------------------------------------------------------------------------ diff --git a/dglc/cime_config/user_nl_dglc_streams b/dglc/cime_config/user_nl_dglc_streams new file mode 100644 index 000000000..856ee8944 --- /dev/null +++ b/dglc/cime_config/user_nl_dglc_streams @@ -0,0 +1,33 @@ +!------------------------------------------------------------------------ +! This file is used to modify datm.streams.xml generated in $RUNDIR +! Entries should have the form +! :<= new stream_value> +! The following are accepted values for an assume streamname of foo +! foo:meshfile = character string +! foo:datafiles = comma separated string of full pathnames (e.g. file1,file2,file3...) +! foo:datavars = comma separated string of field pairs (e.g. foo foobar,foo2 foobar2...) +! foo:taxmode = one of [cycle, extend, limit] +! foo:tintalgo = one of [lower,upper,nearest,linear,coszen] +! foo:readmode = single (only suported mode right now) +! foo:mapalgo = one of [bilinear,redist,nn,consf,consd,none] +! foo:dtlimit = real (1.5 is default) +! foo:year_first = integer +! foo:year_last = integer +! foo:year_align = integer +! foo:vectors = one of [none,u:v] +! foo:lev_dimname: = one of [null,name of level dimenion name] +! foo:offset = integer +! As an example: +! foo:year_first = 1950 +! would change the stream year_first stream_entry to 1950 for the foo stream block +! NOTE: multi-line inputs are enabled by adding a \ at the end of the line +! As an emaple: +! foo:datafiles=foo1,foo2, \ +! foo3 +! Will yield the following new entry for datafiles in stream foo +! +! foo1 +! foo2 +! foo3 +! +!------------------------------------------------------------------------ diff --git a/dglc/dglc_datamode_noevolve_mod.F90 b/dglc/dglc_datamode_noevolve_mod.F90 new file mode 100644 index 000000000..9a5431e62 --- /dev/null +++ b/dglc/dglc_datamode_noevolve_mod.F90 @@ -0,0 +1,381 @@ +module dglc_datamode_noevolve_mod + + use ESMF , only : ESMF_State, ESMF_LOGMSG_INFO, ESMF_LogWrite, ESMF_SUCCESS + use ESMF , only : ESMF_Mesh, ESMF_DistGrid, ESMF_FieldBundle, ESMF_Field + use ESMF , only : ESMF_FieldBundleCreate, ESMF_FieldCreate, ESMF_MeshLoc_Element + use ESMF , only : ESMF_FieldBundleAdd, ESMF_MeshGet, ESMF_DistGridGet, ESMF_Typekind_R8 + use NUOPC , only : NUOPC_Advertise, NUOPC_IsConnected + use shr_kind_mod , only : r8=>shr_kind_r8, i8=>shr_kind_i8, cl=>shr_kind_cl, cs=>shr_kind_cs + use shr_sys_mod , only : shr_sys_abort + use shr_const_mod , only : SHR_CONST_RHOICE, SHR_CONST_RHOSW, SHR_CONST_REARTH + use dshr_methods_mod , only : dshr_state_getfldptr, dshr_fldbun_getfldptr, chkerr + use dshr_fldlist_mod , only : fldlist_type, dshr_fldlist_add + use dshr_strdata_mod , only : shr_strdata_type + use pio , only : file_desc_t, io_desc_t, var_desc_t, iosystem_desc_t + use pio , only : pio_openfile, pio_inq_varid, pio_inq_varndims, pio_inq_vardimid + use pio , only : pio_inq_dimlen, pio_initdecomp, pio_read_darray, pio_double + use pio , only : pio_closefile, pio_freedecomp, PIO_BCAST_ERROR, PIO_NOWRITE + use pio , only : pio_seterrorhandling + + implicit none + private ! except + + public :: dglc_datamode_noevolve_advertise + public :: dglc_datamode_noevolve_init_pointers + public :: dglc_datamode_noevolve_advance + + logical :: initialized_noevolve = .false. + integer :: num_icesheets + real(r8) :: thk0 = 1._r8 + + ! Data structure to enable multiple ice sheets + type icesheet_ptr_t + real(r8), pointer :: ptr(:) => null() ! pointer to array + endtype icesheet_ptr_t + + ! Export fields + type(icesheet_ptr_t), allocatable :: Sg_area(:) + type(icesheet_ptr_t), allocatable :: Sg_topo(:) + type(icesheet_ptr_t), allocatable :: Sg_ice_covered(:) + type(icesheet_ptr_t), allocatable :: Sg_icemask(:) + type(icesheet_ptr_t), allocatable :: Sg_icemask_coupled_fluxes(:) + + ! Import fields + type(icesheet_ptr_t), allocatable :: Sl_tsrf(:) + type(icesheet_ptr_t), allocatable :: Flgl_qice(:) + + ! Export Field names + character(len=*), parameter :: field_out_area = 'Sg_area' + character(len=*), parameter :: field_out_topo = 'Sg_topo' + character(len=*), parameter :: field_out_ice_covered = 'Sg_ice_covered' + character(len=*), parameter :: field_out_icemask = 'Sg_icemask' + character(len=*), parameter :: field_out_icemask_coupled_fluxes = 'Sg_icemask_coupled_fluxes' + + ! Import Field names + character(len=*), parameter :: field_in_tsrf = 'Sl_tsrf' + character(len=*), parameter :: field_in_qice = 'Flgl_qice' + + character(*) , parameter :: rpfile = 'rpointer.glc' + character(*) , parameter :: u_FILE_u = & + __FILE__ + +!=============================================================================== +contains +!=============================================================================== + + subroutine dglc_datamode_noevolve_advertise(NStateExp, fldsexport, NStateImp, fldsimport, & + flds_scalar_name, rc) + + ! input/output variables + type(ESMF_State) , intent(inout) :: NStateExp(:) + type(fldlist_type), pointer :: fldsexport + type(ESMF_State) , intent(inout) :: NStateImp(:) + type(fldlist_type), pointer :: fldsimport + character(len=*) , intent(in) :: flds_scalar_name + integer , intent(out) :: rc + + ! local variables + integer :: ns + character(len=CS) :: cnum + type(fldlist_type), pointer :: fldList + !------------------------------------------------------------------------------- + + rc = ESMF_SUCCESS + + !-------------------------------- + ! Create nested state for active ice sheets only + !-------------------------------- + + ! Set module variable for number of ice sheets + num_icesheets = size(NStateExp) + + ! Advertise export fields + call dshr_fldList_add(fldsExport, trim(flds_scalar_name)) + call dshr_fldList_add(fldsExport, field_out_area) + call dshr_fldList_add(fldsExport, field_out_ice_covered) + call dshr_fldList_add(fldsExport, field_out_topo) + call dshr_fldList_add(fldsExport, field_out_icemask) + call dshr_fldList_add(fldsExport, field_out_icemask_coupled_fluxes) + + do ns = 1,num_icesheets + write(cnum,'(i0)') ns + fldlist => fldsExport ! the head of the linked list + do while (associated(fldlist)) + call NUOPC_Advertise(NStateExp(ns), standardName=fldlist%stdname, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_LogWrite('(dglc_comp_advertise): Fr_glc'//trim(cnum)//"_"//trim(fldList%stdname), ESMF_LOGMSG_INFO) + fldList => fldList%next + end do + enddo + + ! Advertise import fields if appropriate + call dshr_fldList_add(fldsImport, trim(flds_scalar_name)) + call dshr_fldList_add(fldsImport, field_in_tsrf) + call dshr_fldList_add(fldsImport, field_in_qice) + + do ns = 1,num_icesheets + write(cnum,'(i0)') ns + fldlist => fldsImport ! the head of the linked list + do while (associated(fldlist)) + call NUOPC_Advertise(NStateImp(ns), standardName=fldlist%stdname, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_LogWrite('(dglc_comp_advertise): To_glc'//trim(cnum)//"_"//trim(fldList%stdname), ESMF_LOGMSG_INFO) + fldList => fldList%next + end do + enddo + + end subroutine dglc_datamode_noevolve_advertise + + !=============================================================================== + subroutine dglc_datamode_noevolve_init_pointers(NStateExp, NstateImp, rc) + + ! input/output variables + type(ESMF_State) , intent(inout) :: NStateExp(:) + type(ESMF_State) , intent(inout) :: NStateImp(:) + integer , intent(out) :: rc + + ! local variables + integer :: ns + character(len=*), parameter :: subname='(dglc_init_pointers): ' + !------------------------------------------------------------------------------- + + rc = ESMF_SUCCESS + + ! So this is tricky since you need pointers to fields in the nested state + ! So this will have to be done below in a loop + + ! initialize pointers to export fields + allocate(Sg_area(num_icesheets)) + allocate(Sg_topo(num_icesheets)) + allocate(Sg_ice_covered(num_icesheets)) + allocate(Sg_icemask(num_icesheets)) + allocate(Sg_icemask_coupled_fluxes(num_icesheets)) + + do ns = 1,num_icesheets + call dshr_state_getfldptr(NStateExp(ns), field_out_area, fldptr1=Sg_area(ns)%ptr, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + call dshr_state_getfldptr(NStateExp(ns), field_out_topo, fldptr1=Sg_topo(ns)%ptr, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + call dshr_state_getfldptr(NStateExp(ns), field_out_ice_covered, fldptr1=Sg_ice_covered(ns)%ptr, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + call dshr_state_getfldptr(NStateExp(ns), field_out_icemask, fldptr1=Sg_icemask(ns)%ptr, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + call dshr_state_getfldptr(NStateExp(ns), field_out_icemask_coupled_fluxes, fldptr1=Sg_icemask_coupled_fluxes(ns)%ptr, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + end do + + ! initialize pointers to import fields if appropriate + allocate(Sl_tsrf(num_icesheets)) + allocate(Flgl_qice(num_icesheets)) + + do ns = 1,num_icesheets + ! NOTE: the field is connected ONLY if the MED->GLC entry is in the nuopc.runconfig file + ! This restriction occurs even if the field was advertised + if (NUOPC_IsConnected(NStateImp(ns), fieldName=field_in_tsrf)) then + call dshr_state_getfldptr(NStateImp(ns), field_in_tsrf, fldptr1=Sl_tsrf(ns)%ptr, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + end if + if (NUOPC_IsConnected(NStateImp(ns), fieldName=field_in_qice)) then + call dshr_state_getfldptr(NStateImp(ns), field_in_qice, fldptr1=Flgl_qice(ns)%ptr, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + end if + end do + + end subroutine dglc_datamode_noevolve_init_pointers + + !=============================================================================== + subroutine dglc_datamode_noevolve_advance(pio_subsystem, io_type, io_format, & + model_meshes, model_internal_gridsize, model_datafiles, rc) + + ! Assume that the model mesh is the same as the input data mesh + + ! input/output variables + type(iosystem_desc_t) , pointer :: pio_subsystem ! pio info + integer , intent(in) :: io_type ! pio info + integer , intent(in) :: io_format ! pio info + type(ESMF_Mesh) , intent(in) :: model_meshes(:) ! ice sheets meshes + real(r8) , intent(in) :: model_internal_gridsize(:) ! internal model gridsizes (m) + character(len=*) , intent(in) :: model_datafiles(:) ! input file names + integer , intent(out) :: rc + + ! local variables + type(ESMF_FieldBundle) :: fldbun_noevolve + type(ESMF_DistGrid) :: distgrid + type(ESMF_Field) :: field_noevolve + type(file_desc_t) :: pioid + type(io_desc_t) :: pio_iodesc + integer :: ns ! ice sheet index + integer :: ng ! grid cell index + integer :: lsize + integer, pointer :: gindex(:) ! domain decomposition of data + integer :: ndims ! number of dims + integer, allocatable :: dimid(:) + type(var_desc_t) :: varid + integer :: rcode + integer :: nxg, nyg + real(r8), pointer :: topog(:) + real(r8), pointer :: thck(:) + logical :: exists + real(r8) :: rhoi ! density of ice ~ kg/m^3 + real(r8) :: rhoo ! density of sea water ~ kg/m^3 + real(r8) :: eus ! eustatic sea level + real(r8), allocatable :: lsrf(:) + real(r8), allocatable :: usrf(:) + character(len=*), parameter :: subname='(dglc_datamode_noevolve_advance): ' + !------------------------------------------------------------------------------- + + rc = ESMF_SUCCESS + + if (initialized_noevolve) then + RETURN + end if + + do ns = 1,num_icesheets + + ! Get mesh info + call ESMF_MeshGet(model_meshes(ns), elementdistGrid=distGrid, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_DistGridGet(distGrid, localDe=0, elementCount=lsize, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + allocate(gindex(lsize)) + call ESMF_DistGridGet(distGrid, localDe=0, seqIndexList=gindex, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + ! Determine "glc_area" ; + ! Sg_areas is in radians + ! SHR_CONST_REARTH is the radius of earth in m + ! model_internal_gridsize is the internal model gridsize in m + do ng = 1,lsize + Sg_area(ns)%ptr(ng) = (model_internal_gridsize(ns)/SHR_CONST_REARTH)**2 + end do + + ! Create module level field bundle + fldbun_noevolve = ESMF_FieldBundleCreate(rc=rc) ! input field bundle + + ! "ice thickness" ; + field_noevolve = ESMF_FieldCreate(model_meshes(ns), ESMF_TYPEKIND_R8, name='thk', meshloc=ESMF_MESHLOC_ELEMENT, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + call ESMF_FieldBundleAdd(fldbun_noevolve, (/field_noevolve/), rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + + ! "bed topography" ; + field_noevolve = ESMF_FieldCreate(model_meshes(ns), ESMF_TYPEKIND_R8, name='topg', meshloc=ESMF_MESHLOC_ELEMENT, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + call ESMF_FieldBundleAdd(fldbun_noevolve, (/field_noevolve/), rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + + ! Create pioid and pio_iodesc at the module level + inquire(file=trim(model_datafiles(ns)), exist=exists) + if (.not.exists) then + write(6,'(a)')' ERROR: model input file '//trim(model_datafiles(ns))//' does not exist' + call shr_sys_abort() + end if + rcode = pio_openfile(pio_subsystem, pioid, io_type, trim(model_datafiles(ns)), pio_nowrite) + call pio_seterrorhandling(pioid, PIO_BCAST_ERROR) + rcode = pio_inq_varid(pioid, 'thk', varid) + rcode = pio_inq_varndims(pioid, varid, ndims) + allocate(dimid(ndims)) + rcode = pio_inq_vardimid(pioid, varid, dimid(1:ndims)) + rcode = pio_inq_dimlen(pioid, dimid(1), nxg) + rcode = pio_inq_dimlen(pioid, dimid(2), nyg) + call pio_initdecomp(pio_subsystem, pio_double, (/nxg,nyg/), gindex, pio_iodesc) + deallocate(dimid) + deallocate(gindex) + + ! Read in the data into the appropriate field bundle pointers + ! Note that Sg_ice_covered(ns)%ptr points into the data for + ! the Sg_ice_covered field in NStateExp(ns) + ! Note that Sg_topo(ns)%ptr points into the data for + ! the Sg_topon NStateExp(ns) + ! Note that topog is bedrock topography + + call dshr_fldbun_getFldPtr(fldbun_noevolve, 'topg', topog, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + rcode = pio_inq_varid(pioid, 'topg', varid) + call pio_read_darray(pioid, varid, pio_iodesc, topog, rcode) + + call dshr_fldbun_getFldPtr(fldbun_noevolve, 'thk', thck, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + rcode = pio_inq_varid(pioid, 'thk', varid) + call pio_read_darray(pioid, varid, pio_iodesc, thck, rcode) + + allocate(lsrf(lsize)) + allocate(usrf(lsize)) + + rhoi = SHR_CONST_RHOICE ! 0.917e3 + rhoo = SHR_CONST_RHOSW ! 1.026e3 + eus = 0 + do ng = 1,lsize + if (topog(ng) - eus < (-rhoi/rhoo) * thck(ng)) then + lsrf(ng) = (-rhoi/rhoo) * thck(ng) + else + lsrf(ng) = topog(ng) + end if + usrf(ng) = max(0.d0, thck(ng) + lsrf(ng)) + + if (is_in_active_grid(usrf(ng))) then + Sg_icemask(ns)%ptr(ng) = 1.d0 + Sg_icemask_coupled_fluxes(ns)%ptr(ng) = 0.d0 + if (is_ice_covered(thck(ng))) then + Sg_ice_covered(ns)%ptr(ng) = 1.d0 + else + Sg_ice_covered(ns)%ptr(ng) = 0.d0 + end if + ! Note that we use the same method for computing topo whether this point is + ! ice-covered or ice-free. This is in contrast to the method for computing + ! ice-free topo in glint_upscaling_gcm. + Sg_topo(ns)%ptr(ng) = thk0 * usrf(ng) + else + ! Note that this logic implies that if (in theory) we had an ice-covered + ! point outside the "active grid", it will get classified as ice-free for + ! these purposes. This mimics the logic currently in glint_upscaling_gcm. + Sg_icemask(ns)%ptr(ng) = 0.d0 + Sg_icemask_coupled_fluxes(ns)%ptr(ng) = 0.d0 + Sg_ice_covered(ns)%ptr(ng) = 0.d0 + Sg_topo(ns)%ptr(ng) = 0.d0 + end if + end do + + deallocate(lsrf) + deallocate(usrf) + + call pio_closefile(pioid) + call pio_freedecomp(pio_subsystem, pio_iodesc) + + end do ! end loop over ice sheets + + initialized_noevolve = .true. + + end subroutine dglc_datamode_noevolve_advance + + !=============================================================================== + logical function is_in_active_grid(usrf) + ! Return true if the given point is inside the "active grid". The active grid includes + ! any point that can receive a positive surface mass balance, which includes any + ! point classified as land or ice sheet. + + real(r8), intent(in) :: usrf ! surface elevation (m) + + if (thk0 * usrf > 0.d0) then + ! points not at sea level are assumed to be land or ice sheet + is_in_active_grid = .true. + else + is_in_active_grid = .false. + end if + end function is_in_active_grid + + !=============================================================================== + logical function is_ice_covered(thck) + ! Return true if the given point is ice-covered + + real(r8), intent(in) :: thck ! ice thickness (m) + real(r8), parameter :: min_thck = 0.d0 + + if (thk0 * thck > min_thck) then + is_ice_covered = .true. + else + is_ice_covered = .false. + end if + end function is_ice_covered + +end module dglc_datamode_noevolve_mod diff --git a/dglc/glc_comp_nuopc.F90 b/dglc/glc_comp_nuopc.F90 new file mode 100644 index 000000000..233794fd3 --- /dev/null +++ b/dglc/glc_comp_nuopc.F90 @@ -0,0 +1,652 @@ +#ifdef CESMCOUPLED +module glc_comp_nuopc +#else +module cdeps_dglc_comp +#endif + + !---------------------------------------------------------------------------- + ! This is the NUOPC cap for DGLC + !---------------------------------------------------------------------------- + use ESMF , only : ESMF_VM, ESMF_VmGet, ESMF_VMBroadcast, ESMF_GridCompGet + use ESMF , only : ESMF_Mesh, ESMF_GridComp, ESMF_State, ESMF_Clock, ESMF_Time + use ESMF , only : ESMF_SUCCESS, ESMF_LogWrite, ESMF_LOGMSG_INFO, ESMF_METHOD_INITIALIZE + use ESMF , only : ESMF_TraceRegionEnter, ESMF_TraceRegionExit, ESMF_ClockGet + use ESMF , only : ESMF_TimeGet, ESMF_TimeInterval, ESMF_Field, ESMF_MAXSTR + use ESMF , only : ESMF_Alarm, ESMF_MethodRemove, ESMF_MethodAdd + use ESMF , only : ESMF_GridCompSetEntryPoint, ESMF_ClockGetAlarm, ESMF_AlarmIsRinging + use ESMF , only : ESMF_StateGet, operator(+), ESMF_AlarmRingerOff, ESMF_LogWrite + use ESMF , only : ESMF_Field, ESMF_FieldGet, ESMF_VmLogMemInfo + use ESMF , only : ESMF_MeshCreate, ESMF_FILEFORMAT_ESMFMESH + use NUOPC , only : NUOPC_CompDerive, NUOPC_CompSetEntryPoint, NUOPC_CompSpecialize + use NUOPC , only : NUOPC_Advertise, NUOPC_CompAttributeGet, NUOPC_CompAttributeSet + use NUOPC , only : NUOPC_AddNestedState, NUOPC_IsConnected + use NUOPC_Model , only : model_routine_SS => SetServices + use NUOPC_Model , only : model_label_Advance => label_Advance + use NUOPC_Model , only : model_label_SetRunClock => label_SetRunClock + use NUOPC_Model , only : model_label_Finalize => label_Finalize + use NUOPC_Model , only : NUOPC_ModelGet, SetVM + use shr_kind_mod , only : r8=>shr_kind_r8, i8=>shr_kind_i8, cl=>shr_kind_cl, cs=>shr_kind_cs + use shr_sys_mod , only : shr_sys_abort + use shr_cal_mod , only : shr_cal_ymd2date + use shr_log_mod , only : shr_log_setLogUnit + use shr_string_mod , only : shr_string_listGetNum, shr_string_listGetName +#ifdef CESMCOUPLED + use shr_pio_mod , only : shr_pio_getiosys, shr_pio_getiotype, shr_pio_getioformat +#endif + use dshr_methods_mod , only : dshr_state_diagnose, chkerr, memcheck + use dshr_strdata_mod , only : shr_strdata_type, shr_strdata_advance, shr_strdata_init_from_config + use dshr_mod , only : dshr_model_initphase, dshr_init, dshr_mesh_init + use dshr_mod , only : dshr_state_setscalar, dshr_set_runclock, dshr_check_restart_alarm + use dshr_dfield_mod , only : dfield_type, dshr_dfield_add, dshr_dfield_copy + use dshr_fldlist_mod , only : fldlist_type, dshr_fldlist_realize + + ! Datamode specialized modules + use dglc_datamode_noevolve_mod, only : dglc_datamode_noevolve_advertise + use dglc_datamode_noevolve_mod, only : dglc_datamode_noevolve_init_pointers + use dglc_datamode_noevolve_mod, only : dglc_datamode_noevolve_advance + + implicit none + private ! except + + public :: SetServices + public :: SetVM + private :: InitializeAdvertise + private :: InitializeRealize + private :: ModelAdvance + private :: dglc_comp_run + private :: ModelFinalize + + !-------------------------------------------------------------------------- + ! Private module data + !-------------------------------------------------------------------------- + + character(*) , parameter :: nullstr = 'null' + integer , parameter :: max_icesheets = 10 ! maximum number of ice sheets for namelist input + integer :: num_icesheets ! actual number of ice sheets + + ! namelist input + character(CL) :: model_meshfiles(max_icesheets) = nullstr + character(CL) :: model_datafiles(max_icesheets) = nullstr + integer :: nx_global(max_icesheets) = 0 + integer :: ny_global(max_icesheets) = 0 + real(r8) :: model_internal_gridsize(max_icesheets) = 1.e36 + + ! module variables for multiple ice sheets + type(shr_strdata_type) , allocatable :: sdat(:) + type(ESMF_State) , allocatable :: NStateImp(:) + type(ESMF_State) , allocatable :: NStateExp(:) + type(ESMF_Mesh) , allocatable :: model_meshes(:) + + ! module variables common to all data models + character(CS) :: flds_scalar_name = '' + integer :: flds_scalar_num = 0 + integer :: flds_scalar_index_nx = 0 + integer :: flds_scalar_index_ny = 0 + integer :: mpicom ! mpi communicator + integer :: my_task ! my task in mpi communicator mpicom + logical :: mainproc ! true of my_task == main_task + character(len=16) :: inst_suffix = "" ! char string associated with instance (ie. "_0001" or "") + integer :: logunit ! logging unit number + logical :: restart_read ! start from restart + character(CL) :: case_name + + ! dglc_in namelist input + character(CL) :: streamfilename = nullstr ! filename to obtain stream info from + character(CL) :: nlfilename = nullstr ! filename to obtain namelist info from + character(CL) :: datamode = nullstr ! flags physics options wrt input data + character(CL) :: restfilm = nullstr ! model restart file namelist + logical :: skip_restart_read = .false. ! true => skip restart read in continuation run + logical :: export_all = .false. ! true => export all fields, do not check connected or not + + ! linked lists + type(fldList_type) , pointer :: fldsImport => null() + type(fldList_type) , pointer :: fldsExport => null() + + type dfields_icesheets_type + type(dfield_type), pointer :: dfields => null() + end type dfields_icesheets_type + type(dfields_icesheets_type), allocatable :: dfields_icesheets(:) + + ! constants + logical :: diagnose_data = .true. + integer , parameter :: main_task = 0 ! task number of main task +#ifdef CESMCOUPLED + character(*) , parameter :: module_name = "(glc_comp_nuopc)" +#else + character(*) , parameter :: module_name = "(cdeps_dglc_comp)" +#endif + character(*) , parameter :: modelname = 'dglc' + character(*) , parameter :: u_FILE_u = & + __FILE__ + +!=============================================================================== +contains +!=============================================================================== + + subroutine SetServices(gcomp, rc) + type(ESMF_GridComp) :: gcomp + integer, intent(out) :: rc + + ! Local varaibles + character(len=*),parameter :: subname=trim(module_name)//':(SetServices) ' + !-------------------------------- + + rc = ESMF_SUCCESS + call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO) + + ! the NUOPC gcomp component will register the generic methods + call NUOPC_CompDerive(gcomp, model_routine_SS, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + ! switching to IPD versions + call ESMF_GridCompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, & + userRoutine=dshr_model_initphase, phase=0, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + ! set entry point for methods that require specific implementation + call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, & + phaseLabelList=(/"IPDv01p1"/), userRoutine=InitializeAdvertise, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, & + phaseLabelList=(/"IPDv01p3"/), userRoutine=InitializeRealize, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + ! attach specializing method(s) + call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Advance, specRoutine=ModelAdvance, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + call ESMF_MethodRemove(gcomp, label=model_label_SetRunClock, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call NUOPC_CompSpecialize(gcomp, specLabel=model_label_SetRunClock, specRoutine=dshr_set_runclock, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Finalize, specRoutine=ModelFinalize, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO) + + end subroutine SetServices + + !=============================================================================== + subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) + + use shr_nl_mod, only: shr_nl_find_group_name + + ! input/output variables + type(ESMF_GridComp) :: gcomp + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer, intent(out) :: rc + + ! local variables + type(ESMF_VM) :: vm + integer :: inst_index ! number of current instance (ie. 1) + integer :: nu ! unit number + integer :: ierr ! error code + integer :: bcasttmp(2) + integer :: ns + character(len=CS) :: cnum + character(len=ESMF_MAXSTR) :: model_datafiles_list ! colon separated string containing input datafiles + character(len=ESMF_MAXSTR) :: model_meshfiles_list ! colon separated string containing model meshfiles + character(len=*),parameter :: subname=trim(module_name)//':(InitializeAdvertise) ' + !------------------------------------------------------------------------------- + + ! Note that the suffix '-list' refers to a colon delimited string of names + namelist / dglc_nml / datamode, & + model_meshfiles_list, model_datafiles_list, model_internal_gridsize, nx_global, ny_global, & + restfilm, skip_restart_read + + rc = ESMF_SUCCESS + + call NUOPC_CompAttributeGet(gcomp, name='case_name', value=case_name, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + ! Determine logical mainproc + mainproc = (my_task == main_task) + + ! Obtain flds_scalar values, mpi values, multi-instance values and + ! set logunit and set shr logging to my log file + call dshr_init(gcomp, 'GLC', mpicom, my_task, inst_index, inst_suffix, & + flds_scalar_name, flds_scalar_num, flds_scalar_index_nx, flds_scalar_index_ny, logunit, rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + ! Read dglc_nml from nlfilename + if (my_task == main_task) then + nlfilename = "dglc_in"//trim(inst_suffix) + open (newunit=nu,file=trim(nlfilename),status="old",action="read") + call shr_nl_find_group_name(nu, 'dglc_nml', status=ierr) + read (nu,nml=dglc_nml,iostat=ierr) + close(nu) + if (ierr > 0) then + write(logunit,'(a,i8)') 'ERROR: reading input namelist, '//trim(nlfilename)//' iostat=',ierr + call shr_sys_abort(subName//': namelist read error '//trim(nlfilename)) + end if + + ! Determine number of ice sheets + num_icesheets = shr_string_listGetNum(model_meshfiles_list) + + ! Determine array of model meshfile(s) and model input datafile(s) + do ns = 1,num_icesheets + ! determine mesh filename(s) + call shr_string_listGetName(model_meshfiles_list, ns, model_meshfiles(ns)) + ! determine input datafile name(s) + call shr_string_listGetName(model_datafiles_list, ns, model_datafiles(ns)) + end do + + ! Write diagnostics + write(logunit,'(a,a)')' case_name = ',trim(case_name) + write(logunit,'(a,a)')' datamode = ',trim(datamode) + do ns = 1,num_icesheets + write(logunit,'(a,i4 )') ' ice_sheet index = ',ns + write(logunit,'(a,a )') ' model_meshfile = ',trim(model_meshfiles(ns)) + write(logunit,'(a,a )') ' model_datafile = ',trim(model_datafiles(ns)) + write(logunit,'(a,d13.5)')' model_internal_gridsize = ',model_internal_gridsize(ns) + write(logunit,'(a,i10)') ' nx_global = ',nx_global(ns) + write(logunit,'(a,i10)') ' ny_global = ',ny_global(ns) + end do + write(logunit,'(a,a )')' restfilm = ',trim(restfilm) + write(logunit,'(a,l6)')' skip_restart_read = ',skip_restart_read + + bcasttmp(1) = 0 + if(skip_restart_read) bcasttmp(1) = 1 + bcasttmp(2) = num_icesheets + endif + + ! Broadcast namelist input + call ESMF_GridCompGet(gcomp, vm=vm, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_VMBroadcast(vm, datamode, CL, main_task, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_VMBroadcast(vm, restfilm, CL, main_task, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_VMBroadcast(vm, model_meshfiles, CL*max_icesheets, main_task, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_VMBroadcast(vm, model_datafiles, CL*max_icesheets, main_task, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_VMBroadcast(vm, model_internal_gridsize, max_icesheets, main_task, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_VMBroadcast(vm, nx_global, max_icesheets, main_task, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_VMBroadcast(vm, ny_global, max_icesheets, main_task, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_VMBroadcast(vm, bcasttmp, 3, main_task, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + skip_restart_read = (bcasttmp(1) == 1) + num_icesheets = bcasttmp(2) + + ! Validate datamode + if ( trim(datamode) == 'noevolve') then ! read stream, no import data + ! do nothing + else + call shr_sys_abort(' ERROR illegal dglc datamode = '//trim(datamode)) + endif + + ! Allocate module variables + allocate(sdat(num_icesheets)) + allocate(NStateImp(num_icesheets)) + allocate(NStateExp(num_icesheets)) + allocate(model_meshes(num_icesheets)) + + ! Create nested states + do ns = 1,num_icesheets + write(cnum,'(i0)') ns + call NUOPC_AddNestedState(importState, CplSet="GLC"//trim(cnum), nestedState=NStateImp(ns), rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call NUOPC_AddNestedState(exportState, CplSet="GLC"//trim(cnum), nestedState=NStateExp(ns), rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + end do + + ! Advertise dglc fields + if (trim(datamode)=='noevolve') then + call dglc_datamode_noevolve_advertise(NStateExp, fldsexport, NStateImp, fldsimport, & + flds_scalar_name, rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + end if + + end subroutine InitializeAdvertise + + !=============================================================================== + subroutine InitializeRealize(gcomp, importState, exportState, clock, rc) + + ! input/output variables + type(ESMF_GridComp) :: gcomp + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer, intent(out) :: rc + + ! local variables + type(ESMF_VM) :: vm + type(ESMF_Time) :: currTime + integer :: current_ymd ! model date + integer :: current_year ! model year + integer :: current_mon ! model month + integer :: current_day ! model day + integer :: current_tod ! model sec into model date + integer :: ns + character(CL) :: cvalue + character(CS) :: cns + logical :: ispresent, isset + logical :: read_restart + logical :: exists + character(len=*), parameter :: subname=trim(module_name)//':(InitializeRealize) ' + !------------------------------------------------------------------------------- + + rc = ESMF_SUCCESS + + ! Initialize model mesh, restart flag, logunit, model_mask and model_frac + call ESMF_VMLogMemInfo("Entering "//trim(subname)) + call ESMF_TraceRegionEnter('dglc_strdata_init') + + ! Determine stream filename + streamfilename = trim(modelname)//'.streams'//trim(inst_suffix) +#ifndef DISABLE_FoX + streamfilename = trim(streamfilename)//'.xml' +#endif + + ! generate local mpi comm + call ESMF_GridCompGet(gcomp, vm=vm, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_VMGet(vm, localPet=my_task, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + mainproc = (my_task == main_task) + call shr_log_setLogUnit(logunit) + + ! Set restart flag + call NUOPC_CompAttributeGet(gcomp, name='read_restart', value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + if (isPresent .and. isSet) then + read(cvalue,*) read_restart + else + call shr_sys_abort(subname//' ERROR: read restart flag must be present') + end if + + ! Get the time to interpolate the stream data to + call ESMF_ClockGet(clock, currTime=currTime, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_TimeGet(currTime, yy=current_year, mm=current_mon, dd=current_day, s=current_tod, rc=rc ) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call shr_cal_ymd2date(current_year, current_mon, current_day, current_ymd) + + ! Loop over ice sheets + do ns = 1,num_icesheets + + write(cns,'(i0)') ns + + ! Initialize pio subsystem +#ifdef CESMCOUPLED + sdat(ns)%pio_subsystem => shr_pio_getiosys('GLC') + sdat(ns)%io_type = shr_pio_getiotype('GLC') + sdat(ns)%io_format = shr_pio_getioformat('GLC') +#else + call dshr_pio_init(gcomp, sdat(ns), logunit, rc) +#endif + + ! Check that model_meshfile exists + if (my_task == main_task) then + inquire(file=trim(model_meshfiles(ns)), exist=exists) + if (.not.exists) then + write(logunit,'(a)')' ERROR: model_meshfile '//trim(model_meshfiles(ns))//' does not exist' + call shr_sys_abort(trim(subname)//' ERROR: model_meshfile '//trim(model_meshfiles(ns))//' does not exist') + end if + endif + + ! Read in model mesh for given ice sheet + model_meshes(ns) = ESMF_MeshCreate(trim(model_meshfiles(ns)), fileformat=ESMF_FILEFORMAT_ESMFMESH, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + ! Initialize stream data type + if (trim(datamode) /= 'noevolve') then + call shr_strdata_init_from_config(sdat(ns), streamfilename, model_meshes(ns), clock, 'GLC', logunit, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + end if + + ! Realize the actively coupled fields, now that a mesh is established and + ! NUOPC_Realize "realizes" a previously advertised field in the importState and exportState + ! by replacing the advertised fields with the newly created fields of the same name. + + call ESMF_LogWrite(subname//' calling dshr_fldlist_realize export for ice sheet '//trim(cns), ESMF_LOGMSG_INFO) + call dshr_fldlist_realize( NStateExp(ns), fldsExport, flds_scalar_name, flds_scalar_num, model_meshes(ns), & + subname//trim(modelname)//':Export', export_all, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + call ESMF_LogWrite(subname//' calling dshr_fldlist_realize importfor ice sheet '//trim(cns), ESMF_LOGMSG_INFO) + call dshr_fldlist_realize( NStateImp(ns), fldsImport, flds_scalar_name, flds_scalar_num, model_meshes(ns), & + subname//trim(modelname)//':Import', .false., rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + ! Add scalars to export state + call dshr_state_SetScalar(dble(nx_global(ns)),flds_scalar_index_nx, & + NStateExp(ns), flds_scalar_name, flds_scalar_num, rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call dshr_state_SetScalar(dble(ny_global(ns)),flds_scalar_index_ny,& + NStateExp(ns), flds_scalar_name, flds_scalar_num, rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + end do ! end loop over ice sheets + + ! Run dglc + call dglc_comp_run(clock, current_ymd, current_tod, restart_write=.false., rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + call ESMF_TraceRegionExit('dglc_strdata_init') + call ESMF_VMLogMemInfo("Leaving "//trim(subname)) + + end subroutine InitializeRealize + + !=============================================================================== + subroutine ModelAdvance(gcomp, rc) + + ! input/output variables + type(ESMF_GridComp) :: gcomp + integer, intent(out) :: rc + + ! local variables + type(ESMF_Clock) :: clock + type(ESMF_TimeInterval) :: timeStep + type(ESMF_Time) :: currTime, nextTime + integer :: next_ymd ! model date + integer :: next_tod ! model sec into model date + integer :: yr ! year + integer :: mon ! month + integer :: day ! day in month + logical :: restart_write + character(len=*),parameter :: subname=trim(module_name)//':(ModelAdvance) ' + !------------------------------------------------------------------------------- + + + rc = ESMF_SUCCESS + call shr_log_setLogUnit(logunit) + + call memcheck(subname, 5, my_task == main_task) + + ! query the Component for its clock + call NUOPC_ModelGet(gcomp, modelClock=clock, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + ! For nuopc - the component clock is advanced at the end of the time interval + ! Need to advance nuopc one timestep ahead for shr_strdata time interpolation + call ESMF_ClockGet( clock, currTime=currTime, timeStep=timeStep, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + nextTime = currTime + timeStep + call ESMF_TimeGet( nextTime, yy=yr, mm=mon, dd=day, s=next_tod, rc=rc ) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call shr_cal_ymd2date(yr, mon, day, next_ymd) + + ! determine if will write restart + restart_write = dshr_check_restart_alarm(clock, rc=rc) + + ! run dglc + call dglc_comp_run(clock, next_ymd, next_tod, restart_write, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + end subroutine ModelAdvance + + !=============================================================================== + subroutine dglc_comp_run(clock, target_ymd, target_tod, restart_write, rc) + + ! -------------------------- + ! advance dglc + ! -------------------------- + + ! input/output variables: + type(ESMF_Clock) , intent(in) :: clock + integer , intent(in) :: target_ymd ! model date + integer , intent(in) :: target_tod ! model sec into model date + logical , intent(in) :: restart_write + integer , intent(out) :: rc + + ! local variables + character(len=CS) :: cnum + integer :: ns ! ice sheet index + logical :: first_time = .true. + character(*), parameter :: subName = "(dglc_comp_run) " + !------------------------------------------------------------------------------- + + rc = ESMF_SUCCESS + + call ESMF_TraceRegionEnter('DGLC_RUN') + + !-------------------- + ! First time initialization + !-------------------- + + if (first_time) then + ! Initialize dfields for all ice sheets + if (trim(datamode) /= 'noevolve') then + call dglc_init_dfields(rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + end if + + ! Initialize datamode module ponters + select case (trim(datamode)) + case('noevolve') + call dglc_datamode_noevolve_init_pointers(NStateExp, NStateImp, rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + end select + + ! Read restart if needed + if (trim(datamode) /= 'noevolve') then + if (restart_read .and. .not. skip_restart_read) then + ! placeholder for future datamodes + end if + end if + + ! Reset first_time + first_time = .false. + end if + + !-------------------- + ! Update export (and possibly import data model states) + !-------------------- + + if (trim(datamode) /= 'noevolve') then + if (.not. allocated(dfields_icesheets)) then + allocate(dfields_icesheets(num_icesheets)) + end if + + ! Loop over ice sheets + do ns = 1,num_icesheets + ! Advance data model streams - time and spatially interpolate to model time and grid + ! Note that loop over ice sheets is done inside shr_strdata_advance + call ESMF_TraceRegionEnter('dglc_strdata_advance') + call shr_strdata_advance(sdat(ns), target_ymd, target_tod, logunit, 'dglc', rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_TraceRegionExit('dglc_strdata_advance') + + ! Copy all fields from streams to export state as default + ! This automatically will update the fields in the export state + call ESMF_TraceRegionEnter('dglc_dfield_copy') + call dshr_dfield_copy(dfields_icesheets(ns)%dfields, sdat(ns), rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call ESMF_TraceRegionExit('dglc_dfield_copy') + end do + end if + + ! Perform data mode specific calculations + select case (trim(datamode)) + case('noevolve') + call dglc_datamode_noevolve_advance(sdat(1)%pio_subsystem, sdat(1)%io_type, sdat(1)%io_format, & + model_meshes, model_internal_gridsize, model_datafiles, rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + end select + + ! Write restarts if needed + if (restart_write) then + if (trim(datamode) /= 'evolve') then + ! this is a place holder for future datamode + end if + end if + + ! Write diagnostics + if (diagnose_data) then + do ns = 1,num_icesheets + write(cnum,'(i0)') ns + call dshr_state_diagnose(NStateExp(ns), flds_scalar_name, trim(subname)//':ES_'//trim(cnum), rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + end do + end if + + call ESMF_TraceRegionExit('DGLC_RUN') + + contains + + subroutine dglc_init_dfields(rc) + ! ----------------------------- + ! Initialize dfields arrays + ! ----------------------------- + + ! input/output variables + integer, intent(out) :: rc + + ! local variables + integer :: nf, ns + integer :: fieldcount + type(ESMF_Field) :: lfield + character(ESMF_MAXSTR) ,pointer :: lfieldnamelist(:) + character(*), parameter :: subName = "(dglc_init_dfields) " + !------------------------------------------------------------------------------- + + rc = ESMF_SUCCESS + + ! Loop over ice sheets + ! Initialize dfields data type (to map streams to export state fields) + ! Create dfields linked list - used for copying stream fields to export state fields + do ns = 1,num_icesheets + call ESMF_StateGet(NStateExp(ns), itemCount=fieldCount, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + allocate(lfieldnamelist(fieldCount)) + call ESMF_StateGet(NStateExp(ns), itemNameList=lfieldnamelist, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + do nf = 1, fieldCount + call ESMF_StateGet(NStateExp(ns), itemName=trim(lfieldNameList(nf)), field=lfield, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + if (trim(lfieldnamelist(nf)) /= flds_scalar_name) then + call dshr_dfield_add( dfields_icesheets(ns)%dfields, sdat(ns), & + trim(lfieldnamelist(nf)), trim(lfieldnamelist(nf)), NStateExp(ns), logunit, mainproc, rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + end if + end do + deallocate(lfieldnamelist) + end do + end subroutine dglc_init_dfields + + end subroutine dglc_comp_run + + !=============================================================================== + subroutine ModelFinalize(gcomp, rc) + type(ESMF_GridComp) :: gcomp + integer, intent(out) :: rc + !------------------------------------------------------------------------------- + rc = ESMF_SUCCESS + if (my_task == main_task) then + write(logunit,*) + write(logunit,*) 'dglc : end of main integration loop' + write(logunit,*) + end if + end subroutine ModelFinalize + +#ifdef CESMCOUPLED +end module glc_comp_nuopc +#else +end module cdeps_dglc_comp +#endif diff --git a/dice/cime_config/testdefs/testlist_dice.xml b/dice/cime_config/testdefs/testlist_dice.xml index 2a05956e0..909980ce6 100644 --- a/dice/cime_config/testdefs/testlist_dice.xml +++ b/dice/cime_config/testdefs/testlist_dice.xml @@ -3,7 +3,8 @@ - + + @@ -11,7 +12,8 @@ - + + diff --git a/dice/ice_comp_nuopc.F90 b/dice/ice_comp_nuopc.F90 index fd552bbcb..63539e812 100644 --- a/dice/ice_comp_nuopc.F90 +++ b/dice/ice_comp_nuopc.F90 @@ -195,7 +195,7 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) ! Obtain flds_scalar values, mpi values, multi-instance values and ! set logunit and set shr logging to my log file - call dshr_init(gcomp, 'ICE', sdat, mpicom, my_task, inst_index, inst_suffix, & + call dshr_init(gcomp, 'ICE', mpicom, my_task, inst_index, inst_suffix, & flds_scalar_name, flds_scalar_num, flds_scalar_index_nx, flds_scalar_index_ny, & logunit, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return diff --git a/dlnd/cime_config/testdefs/testlist_dlnd.xml b/dlnd/cime_config/testdefs/testlist_dlnd.xml index 9ace7ec7d..59026878c 100644 --- a/dlnd/cime_config/testdefs/testlist_dlnd.xml +++ b/dlnd/cime_config/testdefs/testlist_dlnd.xml @@ -3,7 +3,8 @@ - + + diff --git a/dlnd/lnd_comp_nuopc.F90 b/dlnd/lnd_comp_nuopc.F90 index ff84bae05..56a360c83 100644 --- a/dlnd/lnd_comp_nuopc.F90 +++ b/dlnd/lnd_comp_nuopc.F90 @@ -25,7 +25,7 @@ module cdeps_dlnd_comp use shr_kind_mod , only : r8=>shr_kind_r8, i8=>shr_kind_i8, cl=>shr_kind_cl, cs=>shr_kind_cs use shr_sys_mod , only : shr_sys_abort use shr_cal_mod , only : shr_cal_ymd2date - use shr_log_mod , only : shr_log_setLogUnit + use shr_log_mod , only : shr_log_setLogUnit use dshr_methods_mod , only : dshr_state_getfldptr, dshr_state_diagnose, chkerr, memcheck use dshr_strdata_mod , only : shr_strdata_type, shr_strdata_advance, shr_strdata_get_stream_domain use dshr_strdata_mod , only : shr_strdata_init_from_config @@ -187,7 +187,7 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) ! Obtain flds_scalar values, mpi values, multi-instance values and ! set logunit and set shr logging to my log file - call dshr_init(gcomp, 'LND', sdat, mpicom, my_task, inst_index, inst_suffix, & + call dshr_init(gcomp, 'LND', mpicom, my_task, inst_index, inst_suffix, & flds_scalar_name, flds_scalar_num, flds_scalar_index_nx, flds_scalar_index_ny, & logunit, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return diff --git a/doc/source/dglc.rst b/doc/source/dglc.rst new file mode 100644 index 000000000..51175681e --- /dev/null +++ b/doc/source/dglc.rst @@ -0,0 +1,117 @@ +.. _dglc: + +Data Land-Ice (DGLC) +================= + +DGLC is normally used as a substitute for CESM/CISM running in NOEVOLVE mode. +The various ways of running DGLC is referred to as its mode. + +NOEVOLVE mode is used in CESM as follows. +In typical runs, CISM is not evolving; CLM computes the surface mass +balance (SMB) and sends it to CISM, but CISM’s ice sheet geometry +remains fixed over the course of the run. In these runs, CISM serves +two roles in the system: + + - Over the CISM domain (typically Greenland in + CESM2), CISM dictates glacier areas and topographic elevations, + overriding the values on CLM’s surface dataset. CISM also dictates the + elevation of non-glacier land units in its domain, and only in this + domain are atmospheric fields downscaled to non-glacier land + units. (So if you run with a stub glacier model - SGLC - then glacier + areas and elevations will be taken entirely from CLM’s surface + dataset, and no downscaling will be done over non-glacier land units.) + + - CISM provides the grid onto which SMB is downscaled. (If you run with + SGLC then SMB will still be computed in CLM, but it won’t be + downscaled to a high-resolution ice sheet grid.) + +-------------------- +Supported Data Modes +-------------------- + +DGLC has the capability of supporting multiple ice sheets (as is the +case with CISM/CMEPS coupling). This is configured via the following +``dglc_in`` namelist settings: + + - ``model_meshfiles_list`` is a colon separated string containing model + meshfiles describing the different ice sheets. + + - ``model_datafiles_list`` is colon separated string containing + input datafiles that specify are used to obtain data for bedrock + topography and the ice thickness. + + - ``model_internal_gridsize`` is an array that is the size of the number of ice + sheets and that specifies the internal gridcell size that corresponds + what internal gridcell areas the prognostic land-ice component + uses internally (in this case CISM). From this value the internal grid areas in + radians squared are given by (model_internal_gridsize/radius_earth)**2. + Both model_internal_gridsize and radius_earth have units of meters. + + + - ``nx_global`` is an array that is the size of the number of ice + sheets and that specifies the global longitude dimension of the + each ice sheet. + + - ``ny_global`` is an array that is the size of the number of ice + sheets and that specifies the global latitude dimension of the + each ice sheet. + +.. note:: + Each element of ``model_data_filelist``, ``model_areas``, + ``nx_global`` and ``ny_global`` corresponds to a different ice + sheet mesh and should have the **same order** as those in the + ``model_meshfiles_list``. + +DGLC has its own set of supported ``datamode`` values that appears in +the ``dglc_in`` namelist input. The datamode specifies what additional +operations need to be done by DGLC on *ALL* of the streams in the +``dglc.streams.xml`` file. Each datamode value is also associated with +a DGLC source file that carries out these operations and these are +listed in parentheses next to the mode name. Currently, the only +supported ``datamode`` is ``noevolve``. + +noevolve (``dglc_datamode_noevolve_mod.F90``) + - This mode is an analytic mode that has no associated stream files. + This mode uses ``dglc_in`` namelist variables as follows: + +.. _dglc-cime-vars: + +--------------------------------------- +Configuring DGLC using CIME-CCS +--------------------------------------- + +If CDEPS is coupled to the CIME-CCS then the CIME ``$CASEROOT`` xml +variable ``DGLC_MODE`` will be generated based on the compset +specification ``DGLC%{DGLC_MODE}``. ``DGLC_MODE`` will in term be +used in the ``namelist_definition_dglc.xml`` file to determine the +collection of streams that are associated with DGLC and also sets the +dglc namelist variable ``datamode`` in the file ``dglc_in``. + +The following list describes the valid values of ``DGLC_MODE`` +(defined in the ``config_component.xml`` file for DGLC), and how they +relate to the associated input streams and the ``datamode`` namelist +variable. + +DGLC%NOEVOLVE + - fields sent to mediator are created analytically without stream + input + - dglc_mode: NOEVOLVE + - streams: none + - datamode: noevolve + +In addition, the following DGLC specific CIME-CCS xml variables will appear in ``$CASEROOT/env_run.xml``: + +DGLC_USE_GREENLAND + - Whether to include the Greenland Ice Sheet in this DGLC simulation + This should generally be set at create_newcase time (via the compset). In principle it + can be changed later, but great care is needed to change a number of other variables + to be consistent (GLC_GRID, GLC_DOMAIN_MESH and possibly others). + +DGLC_USE_ANTARCTICA + - Whether to include the Antarctic Ice Sheet in this DGLC simulation + This should generally be set at create_newcase time (via the compset). In principle it + can be changed later, but great care is needed to change a number of other variables + to be consistent (GLC_GRID, GLC_DOMAIN_MESH and possibly others). + +DGLC_SKIP_RESTART_READ + - If set to true, than dglc restarts will not be read on a continuation run. diff --git a/doc/source/index.rst b/doc/source/index.rst index 3990258c3..f1a826f07 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -29,4 +29,5 @@ Table of contents dlnd.rst drof.rst dwav.rst + dglc.rst extending.rst diff --git a/doc/source/introduction.rst b/doc/source/introduction.rst index b34a67419..dcdf2ef1b 100644 --- a/doc/source/introduction.rst +++ b/doc/source/introduction.rst @@ -59,6 +59,7 @@ cime_config CIME Case Control System cmake Build (can be used with or without CIME) datm Data atmosphere component dice Data sea-ice component +dglc Data land-ice component dlnd Data land component docn Data ocean component drof Data river component @@ -132,7 +133,7 @@ CDEPS and CIME Control System (CCS) If the CDEPS data models are used in conjunction with the CIME Case Control System (CCS) then the following will also hold: Each data model has an xml variable in ``env_run.xml`` that specifies the data model mode. -These are: ``DATM_MODE``, ``DICE_MODE``, ``DLND_MODE``, ``DOCN_MODE``, ``DROF_MODE``, ``DWAV_MODE``. +These are: ``DATM_MODE``, ``DICE_MODE``, ``DGLC_MODE``, ``DLND_MODE``, ``DOCN_MODE``, ``DROF_MODE``, ``DWAV_MODE``. Each data model mode specifies the streams that are associated with that data model. More details of the data model design are covered in :ref:`design details`. diff --git a/doc/source/streams.rst b/doc/source/streams.rst index 3ce4d8023..0b6fa7235 100644 --- a/doc/source/streams.rst +++ b/doc/source/streams.rst @@ -365,7 +365,7 @@ Data Model Namelist Input ------------------------- Each data model has an associated input namelist file, ``d{model_name}_in``, -where ``model_name=[datm,dlnd,dice,docn,drof,dwav]``. +where ``model_name=[datm,dlnd,dglc,dice,docn,drof,dwav]``. The following namelist variables appear in each data model namelist: @@ -381,6 +381,8 @@ The following namelist variables appear in each data model namelist: :ref:`Data Land ` + :ref:`Data Land-Ice ` + :ref:`Data Ocean ` :ref:`Data Runoff ` diff --git a/docn/cime_config/testdefs/testlist_docn.xml b/docn/cime_config/testdefs/testlist_docn.xml index 2549b1233..5c279cabf 100644 --- a/docn/cime_config/testdefs/testlist_docn.xml +++ b/docn/cime_config/testdefs/testlist_docn.xml @@ -3,7 +3,8 @@ - + + @@ -11,7 +12,8 @@ - + + @@ -19,11 +21,12 @@ - + + - + diff --git a/docn/ocn_comp_nuopc.F90 b/docn/ocn_comp_nuopc.F90 index 462989995..4bdeb81f6 100644 --- a/docn/ocn_comp_nuopc.F90 +++ b/docn/ocn_comp_nuopc.F90 @@ -212,7 +212,7 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) ! Obtain flds_scalar values, mpi values, multi-instance values and ! set logunit and set shr logging to my log file - call dshr_init(gcomp, 'OCN', sdat, mpicom, my_task, inst_index, inst_suffix, & + call dshr_init(gcomp, 'OCN', mpicom, my_task, inst_index, inst_suffix, & flds_scalar_name, flds_scalar_num, flds_scalar_index_nx, flds_scalar_index_ny, logunit, rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return diff --git a/drof/cime_config/testdefs/testlist_drof.xml b/drof/cime_config/testdefs/testlist_drof.xml index 0fa691baf..e45c72a3e 100644 --- a/drof/cime_config/testdefs/testlist_drof.xml +++ b/drof/cime_config/testdefs/testlist_drof.xml @@ -3,7 +3,8 @@ - + + diff --git a/drof/rof_comp_nuopc.F90 b/drof/rof_comp_nuopc.F90 index c3602cd97..959421a70 100644 --- a/drof/rof_comp_nuopc.F90 +++ b/drof/rof_comp_nuopc.F90 @@ -27,7 +27,7 @@ module cdeps_drof_comp use shr_const_mod , only : SHR_CONST_SPVAL use shr_sys_mod , only : shr_sys_abort use shr_cal_mod , only : shr_cal_ymd2date - use shr_log_mod , only : shr_log_setLogUnit + use shr_log_mod , only : shr_log_setLogUnit use dshr_methods_mod , only : dshr_state_getfldptr, dshr_state_diagnose, chkerr, memcheck use dshr_strdata_mod , only : shr_strdata_type, shr_strdata_advance, shr_strdata_get_stream_domain use dshr_strdata_mod , only : shr_strdata_init_from_config @@ -183,7 +183,7 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) ! Obtain flds_scalar values, mpi values, multi-instance values and ! set logunit and set shr logging to my log file - call dshr_init(gcomp, 'ROF', sdat, mpicom, my_task, inst_index, inst_suffix, & + call dshr_init(gcomp, 'ROF', mpicom, my_task, inst_index, inst_suffix, & flds_scalar_name, flds_scalar_num, flds_scalar_index_nx, flds_scalar_index_ny, & logunit, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return diff --git a/dshr/dshr_fldlist_mod.F90 b/dshr/dshr_fldlist_mod.F90 index 398e1ce61..a06912fc1 100644 --- a/dshr/dshr_fldlist_mod.F90 +++ b/dshr/dshr_fldlist_mod.F90 @@ -79,6 +79,7 @@ subroutine dshr_fldlist_realize(state, fldLists, flds_scalar_name, flds_scalar_n stdname = fldList%stdname if (NUOPC_IsConnected(state, fieldName=stdname) .or. export_all) then + ! Check field name since linked list might have empty string if (trim(stdname) == '') then fldList => fldList%next diff --git a/dshr/dshr_mod.F90 b/dshr/dshr_mod.F90 index f253adc05..977b9d439 100644 --- a/dshr/dshr_mod.F90 +++ b/dshr/dshr_mod.F90 @@ -111,14 +111,13 @@ subroutine dshr_model_initphase(gcomp, importState, exportState, clock, rc) end subroutine dshr_model_initphase !=============================================================================== - subroutine dshr_init(gcomp, compname, sdat, mpicom, my_task, inst_index, inst_suffix, & + subroutine dshr_init(gcomp, compname, mpicom, my_task, inst_index, inst_suffix, & flds_scalar_name, flds_scalar_num, flds_scalar_index_nx, flds_scalar_index_ny, logunit, rc) #ifdef CESMCOUPLED use nuopc_shr_methods, only : set_component_logging #endif ! input/output variables type(ESMF_GridComp) :: gcomp - type(shr_strdata_type), intent(in) :: sdat ! No longer used character(len=*) , intent(in) :: compname !e.g. ATM, OCN, ... integer , intent(inout) :: mpicom integer , intent(out) :: my_task @@ -246,7 +245,7 @@ subroutine dshr_mesh_init(gcomp, sdat, nullstr, logunit, compname, model_nxg, mo ! input/output variables type(ESMF_GridComp) , intent(inout) :: gcomp - type(shr_strdata_type) , intent(inout) :: sdat + type(shr_strdata_type) , intent(inout) :: sdat integer , intent(in) :: logunit character(len=*) , intent(in) :: compname !e.g. ATM, OCN, ... character(len=*) , intent(in) :: nullstr diff --git a/dwav/cime_config/testdefs/testlist_dwav.xml b/dwav/cime_config/testdefs/testlist_dwav.xml index b0e93a65d..1864398ad 100644 --- a/dwav/cime_config/testdefs/testlist_dwav.xml +++ b/dwav/cime_config/testdefs/testlist_dwav.xml @@ -3,7 +3,8 @@ - + + diff --git a/dwav/wav_comp_nuopc.F90 b/dwav/wav_comp_nuopc.F90 index e200e00b6..13728738a 100644 --- a/dwav/wav_comp_nuopc.F90 +++ b/dwav/wav_comp_nuopc.F90 @@ -181,7 +181,7 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) ! Obtain flds_scalar values, mpi values, multi-instance values and ! set logunit and set shr logging to my log file - call dshr_init(gcomp, 'WAV', sdat, mpicom, my_task, inst_index, inst_suffix, & + call dshr_init(gcomp, 'WAV', mpicom, my_task, inst_index, inst_suffix, & flds_scalar_name, flds_scalar_num, flds_scalar_index_nx, flds_scalar_index_ny, & logunit, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return diff --git a/streams/dshr_methods_mod.F90 b/streams/dshr_methods_mod.F90 index b1972f7c8..8e721bed1 100644 --- a/streams/dshr_methods_mod.F90 +++ b/streams/dshr_methods_mod.F90 @@ -541,6 +541,7 @@ subroutine dshr_field_getfldptr(field, fldptr1, fldptr2, rank, abort, rc) integer :: ungriddedUBound(1) integer :: lrank logical :: labort + character(len=CS) :: name character(len=*), parameter :: subname='(field_getfldptr)' ! ---------------------------------------------- rc = ESMF_SUCCESS @@ -554,7 +555,9 @@ subroutine dshr_field_getfldptr(field, fldptr1, fldptr2, rank, abort, rc) if (chkerr(rc,__LINE__,u_FILE_u)) return if (status /= ESMF_FIELDSTATUS_COMPLETE) then if (labort) then - call ESMF_LogWrite(trim(subname)//": ERROR data not allocated ", ESMF_LOGMSG_ERROR, rc=rc) + call ESMF_FieldGet(field, name=name, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + call ESMF_LogWrite(trim(subname)//": field "//trim(name)//" has no data not allocated ", ESMF_LOGMSG_ERROR, rc=rc) rc = ESMF_FAILURE return else