1 Installation¶
The following scripts are provided by the author for installing WRF-CTSM on a Linux workstation. Users should adapt these scripts to suit their own systems. For example, users on HPC systems may choose to load pre-installed modules (e.g., compiler, MPI, and libraries) rather than building them from source.
1.1 Preparation¶
Install libraries¶
-
gcc-8
orgcc-9
is recommended.
Set environment¶
WORK_ROOT="/home/yuansun/"
INROOT=${WORK_ROOT}/software
COMPILER=gcc
export MPICHDIR=${INROOT}/${COMPILER}/mpich/4.0.2
export ZLIBDIR=${INROOT}/${COMPILER}/zlib/1.3.1
export HDF5DIR=${INROOT}/${COMPILER}/hdf5/1.12.3
export PNETCDFDIR=${INROOT}/${COMPILER}/pnetcdf/1.12.3
export NETCDFCDIR=${INROOT}/${COMPILER}/netcdf-c/4.9.2
export NETCDFFDIR=${INROOT}/${COMPILER}/netcdf-fortran/4.6.1
export PIODIR=${INROOT}/${COMPILER}/pio/2.6.6
export ESMFDIR=${INROOT}/${COMPILER}/esmf/8.8.1
export JASPERDIR=${INROOT}/${COMPILER}/jasper/4.2.5
export LIBPNGDIR=${INROOT}/${COMPILER}/libpng/1.6.39
export LD_LIBRARY_PATH=$ZLIBDIR/lib:$HDF5DIR/lib:$NETCDFCDIR/lib:$NETCDFFDIR/lib:$MPICHDIR/lib:$PNETCDFDIR/lib:$PIODIR/lib:$ESMFDIR/lib:$JASPERDIR/lib:$LIBPNGDIR/lib:$LD_LIBRARY_PATH
export PATH=$HDF5DIR/bin:$NETCDFCDIR/bin:$NETCDFFDIR/bin:$MPICHDIR/bin:$PNETCDFDIR/bin:$ESMFDIR/bin:$JASPERDIR/bin:$PATH
export CPATH=$ZLIBDIR/include:$HDF5DIR/include:$NETCDFCDIR/include:$NETCDFFDIR/include:$MPICHDIR/include:$PNETCDFDIR/include:$PIODIR/include:$ESMFDIR/include:$JASPERDIR/include:$LIBPNGDIR/include:$CPATH
export MANPATH=$ZLIBDIR/share/man:$HDF5DIR/share/man:$NETCDFCDIR/share/man:$NETCDFFDIR/share/man:$MPICHDIR/share/man:$PNETCDFDIR/share/man:$JASPERDIR/share/man:$LIBPNGDIR/share/man:$MANPATH
export CC=gcc-9
export CXX=g++-9
export FC=gfortran-9
export FCFLAGS="-I$ESMFDIR/mod -I$ESMFDIR/include -I$NETCDFCDIR/include -I$NETCDFFDIR/include -I$PNETCDFDIR/include -I$PIODIR/include -I$ESMFDIR/include -I$JASPERDIR/include -I$LIBPNGDIR/include"
export CPPFLAGS="$FCFLAGS"
export LDFLAGS="-L$ZLIBDIR/lib -L$HDF5DIR/lib -L$NETCDFCDIR/lib -L$NETCDFFDIR/lib -L$MPICHDIR/lib -L$PNETCDFDIR/lib -L$PIODIR/lib -L$ESMFDIR/lib -L$ESMFDIR/lib -L$JASPERDIR/lib -L$LIBPNGDIR/lib"
export LIBRARY_PATH=$LD_LIBRARY_PATH
Check the environment (optional)¶
Check
git-lfs
git lfs version
Check
gcc-9
which gcc-9
Check
ncl
which ncl
Check
nco
ncks --version
Check
netcdf-c
nc-config --version nc-config --all
Check
netcdf-fortran
nf-config --all
1.2 Source Code Download¶
Download WRF code¶
export WRF_ROOT="${WORK_ROOT}wrf"
export WRFNAME=WRF-CTSM
cd ${WRF_ROOT}
git clone https://github.com/wrf-model/WRF.git ${WRFNAME}
cd ${WRFNAME}
git checkout release-v4.7.0
Modify WRF code¶
According to CTSM-Norway tutorial, modify
${WRF_ROOT}/${WRFNAME}/phys/module_sf_mynn.F
, around Line 1149,From:
Q2(I)=QSFCMR(I)+(QV1D(I)-QSFCMR(I))*PSIQ2/PSIQ Q2(I)= MAX(Q2(I), MIN(QSFCMR(I), QV1D(I))) Q2(I)= MIN(Q2(I), 1.05*QV1D(I)) IF ( debug_code ) THEN yesno = 0 IF (HFX(I) > 1200. .OR. HFX(I) < -700.)THEN print*,"SUSPICIOUS VALUES IN MYNN SFCLAYER",& I,J, "HFX: ",HFX(I) yesno = 1 ENDIF
To:
Q2(I)=QSFCMR(I)+(QV1D(I)-QSFCMR(I))*PSIQ2/PSIQ Q2(I)= MAX(Q2(I), MIN(QSFCMR(I), QV1D(I))) Q2(I)= MIN(Q2(I), 1.05*QV1D(I)) !YS IF (Q2(I) .LT. 0.0) THEN print*,"DEBUG: NEGATIVE Q2 VALUE IN MYNN SFCLAYER",& I,J, "Q2: ",Q2(I) print*,"WARNING: NEGATIVE Q2 SET TO ZERO" Q2(I)=0.0 ENDIF IF (QSFC(I) .LT. 0.0) THEN print*,"DEBUG: NEGATIVE QSFC VALUE IN MYNN SFCLAYER",& I,J, "QSFC: ",QSFC(I) ENDIF !YS IF ( debug_code ) THEN yesno = 0 IF (HFX(I) > 1200. .OR. HFX(I) < -700.)THEN print*,"SUSPICIOUS VALUES IN MYNN SFCLAYER",& I,J, "HFX: ",HFX(I) yesno = 1 ENDIF
Download CTSM code¶
export CTSMNAME=CTSMdev
cd ${WRF_ROOT}/${WRFNAME}
git clone https://github.com/ESCOMP/CTSM ${CTSMNAME}
cd ${CTSMNAME}
git checkout ctsm5.3.024
./bin/git-fleximod update
Modify CTSM code¶
Modify
${WRF_ROOT}/${WRFNAME}/${CTSMNAME}/src/cpl/lilac/lnd_comp_esmf.F90
, around Line 38,From:
use clm_varctl , only : nsrStartup, nsrContinue
To:
!YS !use clm_varctl , only : nsrStartup, nsrContinue use clm_varctl , only : nsrStartup, nsrContinue, nsrBranch !YS
According to WRF-CTSM restart issue, modify
${WRF_ROOT}/${WRFNAME}/${CTSMNAME}/src/cpl/lilac/lnd_comp_esmf.F90
, around Line 293,From:
if (trim(starttype) == trim('startup')) then nsrest = nsrStartup else if (trim(starttype) == trim('continue') ) then nsrest = nsrContinue
To:
if (trim(starttype) == trim('startup')) then nsrest = nsrStartup else if (trim(starttype) == trim('continue') ) then !YS !nsrest = nsrContinue nsrest = nsrBranch !YS
According to CTSM-Norway tutorial, modify
${WRF_ROOT}/${WRFNAME}/${CTSMNAME}/src/cpl/utils/lnd_import_export_utils.F90
, around Line 131,From:
if ( wateratm2lndbulk_inst%forc_q_not_downscaled_grc(g) < 0.0_r8 )then call shr_sys_abort( subname//& ' ERROR: Bottom layer specific humidty sent from the atmosphere model is less than zero' ) end if
To:
!YS !if ( wateratm2lndbulk_inst%forc_q_not_downscaled_grc(g) < 0.0_r8 )then !call shr_sys_abort( subname//& ! ' ERROR: Bottom layer specific humidty sent from the atmosphere model is less than zero' ) !end if !YS
According to using the CTSM lake model, modify ``${WRF_ROOT}/${WRFNAME}/${CTSMNAME}/tools/create_scrip_file.ncl` by adding:
lake_depth = wrf_file->LAKE_DEPTH(0,:,:) lu_index = wrf_file->LU_INDEX(0,:,:) lakemask = where(lu_index.eq.21, 1,0) landmask = where (lakemask.eq.1,1,landmask)
According to CTSM-Norway tutorial, modify
${WRF_ROOT}/${WRFNAME}/${CTSMNAME}/tools/site_and_regional/mkunitymap.ncl
, around Line 74:From:
if ( n_a .ne. n_b )then print( "ERROR: dimensions of input SCRIP grid files is NOT the same!" ); exit end if if ( any(ncb->grid_imask .ne. 1.0d00) )then print( "ERROR: the mask of the second file isn't identically 1!" ); print( "(second file should be land grid file)"); exit end if chkvars = (/ "grid_center_lat", "grid_center_lon", "grid_corner_lat", "grid_corner_lon" /);
To:
if ( n_a .ne. n_b )then print( "ERROR: dimensions of input SCRIP grid files is NOT the same!" ); exit end if ;YS ; if ( any(ncb->grid_imask .ne. 1.0d00) )then ; print( "ERROR: the mask of the second file isn't identically 1!" ); ; print( "(second file should be land grid file)"); ; exit ; end if ;YS chkvars = (/ "grid_center_lat", "grid_center_lon", "grid_corner_lat", "grid_corner_lon" /);
Download WPS code¶
export WPSNAME=WPS
cd ${WRF_ROOT}
git clone https://github.com/wrf-model/WPS ${WPSNAME}
cd ${WPSNAME}
git checkout v4.3
1.3 Build Model¶
Build CTSM¶
Customize machine configuration files under
${WRF_ROOT}/${WRFNAME}/${CTSMNAME}/ccs_config/machines/${MACHINENAME}
Compile CTSM code
export MACHINENAME=e-10uxx4b9fmw3 cd ${WRF_ROOT}/${WRFNAME}/${CTSMNAME}/lilac/ ./build_ctsm ctsm_build_dir --machine ${MACHINENAME} --compiler gnu >build_ctsm.log 2>&1
Check if compiled successully. The end of
build_ctsm.log
should be:clm built in 42.152718 seconds Initial setup complete; it is now safe to work with the runtime inputs in /home/yuansun/wrf/WRFnoleap-CTSMbranch/CTSMdev/lilac/ctsm_build_dir/runtime_inputs
Set CTSM path for building WRF-CTSM
source ctsm_build_dir/ctsm_build_environment.sh export WRF_CTSM_MKFILE=${WRF_ROOT}/${WRFNAME}/${CTSMNAME}/lilac/ctsm_build_dir/ctsm.mk export WRF_EM_CORE=1 export NETCDF_classic=1 export WRFIO_NCD_LARGE_FILE_SUPPORT=1
Build mksurfdata_esmf¶
Compile mksurfdata_esmf
cd ${WRF_ROOT}/${WRFNAME}/${CTSMNAME}/tools/mksurfdata_esmf ./gen_mksurfdata_build --machine ${MACHINENAME} >gen_mksurfdata_build.log 2>&1
Check if compiled successfully. The end of
gen_mksurfdata_build.log
should be:Successfully created input namelist file surfdata.namelist
Build geo_domain¶
According to CTSM-Norway tutorial, build the
gen_domain
tool:cd ${WRF_ROOT}/${WRFNAME}/${CTSMNAME}/tools touch configure_wrf-ctsm
Copy scripts below to
configure_wrf-ctsm
:#!/usr/bin/env python3 """This script writes CIME build information to a directory. The pieces of information that will be written include: 1. Machine-specific build settings (i.e. the "Macros" file). 2. File-specific build settings (i.e. "Depends" files). 3. Environment variable loads (i.e. the env_mach_specific files). The .env_mach_specific.sh and .env_mach_specific.csh files are specific to a given compiler, MPI library, and DEBUG setting. By default, these will be the machine's default compiler, the machine's default MPI library, and FALSE, respectively. These can be changed by setting the environment variables COMPILER, MPILIB, and DEBUG, respectively. """ # pylint: disable=W1505 import os import sys real_file_dir = os.path.dirname(os.path.realpath(__file__)) cimeroot = os.path.abspath(os.path.join(real_file_dir, "..")) sys.path.insert(0, cimeroot) from CIME.Tools.standard_script_setup import * from CIME.utils import expect, get_model from CIME.BuildTools.configure import configure from CIME.XML.machines import Machines logger = logging.getLogger(__name__) def parse_command_line(args): """Command line argument parser for configure.""" description = __doc__ parser = argparse.ArgumentParser(description=description) CIME.utils.setup_standard_logging_options(parser) parser.add_argument( "--machine", help="The machine to create build information for." ) parser.add_argument( "--machines-dir", help="The machines directory to take build information " "from. Overrides the CIME_MODEL environment variable, " "and must be specified if that variable is not set.", ) parser.add_argument( "--macros-format", action="append", choices=["Makefile", "CMake"], help="The format of Macros file to generate. If " "'Makefile' is passed in, a file called 'Macros.make' " "is generated. If 'CMake' is passed in, a file called " "'Macros.cmake' is generated. This option can be " "specified multiple times to generate multiple files. " "If not used at all, Macros generation is skipped. " "Note that Depends files are currently always in " "Makefile format, regardless of this option.", ) parser.add_argument( "--output-dir", default=os.getcwd(), help="The directory to write files to. If not " "specified, defaults to the current working directory.", ) parser.add_argument( "--compiler", "-compiler", help="Specify a compiler. " "To see list of supported compilers for each machine, use the utility query_config in this directory", ) parser.add_argument( "--mpilib", "-mpilib", help="Specify the mpilib. " "To see list of supported mpilibs for each machine, use the utility query_config in this directory. " "The default is the first listing in MPILIBS in config_machines.xml", ) parser.add_argument( "--clean", action="store_true", help="Remove old Macros and env files before attempting to create new ones", ) parser.add_argument( "--comp-interface", default="mct", help="""The cime driver/cpl interface to use.""", ) argcnt = len(args) args = parser.parse_args() CIME.utils.parse_args_and_handle_standard_logging_options(args) opts = {} if args.machines_dir is not None: machines_file = os.path.join(args.machines_dir, "config_machines.xml") machobj = Machines(infile=machines_file, machine=args.machine) else: model = get_model() if model is not None: machobj = Machines(machine=args.machine) else: expect( False, "Either --mach-dir or the CIME_MODEL environment " "variable must be specified!", ) opts["machobj"] = machobj if args.macros_format is None: opts["macros_format"] = [] else: opts["macros_format"] = args.macros_format expect( os.path.isdir(args.output_dir), "Output directory '%s' does not exist." % args.output_dir, ) opts["output_dir"] = args.output_dir # Set compiler. if args.compiler is not None: compiler = args.compiler elif "COMPILER" in os.environ: compiler = os.environ["COMPILER"] else: compiler = machobj.get_default_compiler() os.environ["COMPILER"] = compiler expect( opts["machobj"].is_valid_compiler(compiler), "Invalid compiler vendor given in COMPILER environment variable: %s" % compiler, ) opts["compiler"] = compiler opts["os"] = machobj.get_value("OS") opts["comp_interface"] = args.comp_interface if args.clean: files = [ "Macros.make", "Macros.cmake", "env_mach_specific.xml", ".env_mach_specific.sh", ".env_mach_specific.csh", "Depends.%s" % compiler, "Depends.%s" % args.machine, "Depends.%s.%s" % (args.machine, compiler), ] for file_ in files: if os.path.isfile(file_): logger.warn("Removing file %s" % file_) os.remove(file_) if argcnt == 2: opts["clean_only"] = True return opts # Set MPI library. if args.mpilib is not None: mpilib = args.mpilib elif "MPILIB" in os.environ: mpilib = os.environ["MPILIB"] else: mpilib = machobj.get_default_MPIlib(attributes={"compiler": compiler}) os.environ["MPILIB"] = mpilib expect( opts["machobj"].is_valid_MPIlib(mpilib, attributes={"compiler": compiler}), "Invalid MPI library name given in MPILIB environment variable: %s" % mpilib, ) opts["mpilib"] = mpilib # Set DEBUG flag. if "DEBUG" in os.environ: expect( os.environ["DEBUG"].lower() in ("true", "false"), "Invalid DEBUG environment variable value (must be 'TRUE' or " "'FALSE'): %s" % os.environ["DEBUG"], ) debug = os.environ["DEBUG"].lower() == "true" else: debug = False os.environ["DEBUG"] = "FALSE" opts["debug"] = debug return opts def _main(): opts = parse_command_line(sys.argv) if "clean_only" not in opts or not opts["clean_only"]: configure( opts["machobj"], opts["output_dir"], opts["macros_format"], opts["compiler"], opts["mpilib"], opts["debug"], opts["comp_interface"], opts["os"], ) if __name__ == "__main__": _main()
Run the scripts
chmod +x configure_wrf-ctsm cd ${WRF_ROOT}/${WRFNAME}/${CTSMNAME}/tools/mapping/gen_domain_files/src/ ../../../configure_wrf-ctsm --machine ${MACHINENAME} --compiler gnu --mpilib mpich --macros-format Makefile . ./.env_mach_specific.sh ; make
Build WRF¶
Create a configuration file named
configure.wrf
export NETCDF_C=/home/yuansun/software/gcc/netcdf-c/4.9.2 export NETCDF=/home/yuansun/software/gcc/netcdf-fortran/4.6.1 cd ${WRF_ROOT}/${WRFNAME} ./clean -a ./configure # - 34: (dmpar: distributed memory parallelization) # - 1: nesting option (basic)
Modify
configure.wrf
Link libraries:
From:
LIB_EXTERNAL = \ -L$(WRF_SRC_ROOT_DIR)/external/io_netcdf -lwrfio_nf -L/home/yuansun/software/gcc/netcdf-fortran/4.6.1/lib -lnetcdff -lnetcdf
To:
LIB_EXTERNAL = \ -L$(WRF_SRC_ROOT_DIR)/external/io_netcdf -lwrfio_nf \ -L /home/yuansun/software/gcc/netcdf-c/4.9.2/lib -L /home/yuansun/software/gcc/netcdf-fortran/4.6.1/lib -lnetcdff -lnetcdf \ -L /home/yuansun/software/gcc/hdf5/1.12.3/lib -lhdf5_hl -lhdf5 \ -L /home/yuansun/software/gcc/pnetcdf/1.12.3/lib -lpnetcdf \ -L /home/yuansun/software/gcc/jasper/4.2.5/lib -ljasper \ -L /home/yuansun/software/gcc/libpng/1.6.39/lib -lpng \ -L /home/yuansun/software/gcc/zlib/1.3.1/lib -lz \ -L /home/yuansun/software/gcc/lapack/3.9.0 -llapack -lblas \ -lgomp -lm -ldl
Setting:
From:
SFC = gfortran SCC = gcc CCOMP = gcc DM_CC = mpicc -cc=$(SCC) CC = $(DM_CC) ARCH_LOCAL = -DNONSTANDARD_SYSTEM_SUBR -DWRF_USE_CTSM -DNO_IEEE_MODULE -DNO_ISO_C_SUPPORT -DNO_FLUSH_SUPPORT -DNO_GAMMA_SUPPORT FCCOMPAT = -fallow-argument-mismatch -fallow-invalid-boz
To:
SFC = gfortran-9 SCC = gcc-9 CCOMP = gcc-9 DM_CC = mpicc CC = $(DM_CC) -DFSEEKO64_OK ARCH_LOCAL = -DNONSTANDARD_SYSTEM_SUBR -DWRF_USE_CTSM FCCOMPAT =
Adding include path
From:
INCLUDE_MODULES = $(MODULE_SRCH_FLAG) \ $(ESMF_MOD_INC) $(ESMF_LIB_FLAGS) \ -I$(WRF_SRC_ROOT_DIR)/main \ -I$(WRF_SRC_ROOT_DIR)/external/io_netcdf \ -I$(WRF_SRC_ROOT_DIR)/external/io_int \ -I$(WRF_SRC_ROOT_DIR)/frame \ -I$(WRF_SRC_ROOT_DIR)/share \ -I$(WRF_SRC_ROOT_DIR)/phys \ -I$(WRF_SRC_ROOT_DIR)/wrftladj \ -I$(WRF_SRC_ROOT_DIR)/chem -I$(WRF_SRC_ROOT_DIR)/inc \ -I$(NETCDFPATH)/include \ $(CTSM_INCLUDES) HDF5PATH = PNETCDFPATH =
To:
INCLUDE_MODULES = $(MODULE_SRCH_FLAG) \ $(ESMF_MOD_INC) $(ESMF_LIB_FLAGS) \ -I$(WRF_SRC_ROOT_DIR)/main \ -I$(WRF_SRC_ROOT_DIR)/external/io_netcdf \ -I$(WRF_SRC_ROOT_DIR)/external/io_int \ -I$(WRF_SRC_ROOT_DIR)/frame \ -I$(WRF_SRC_ROOT_DIR)/share \ -I$(WRF_SRC_ROOT_DIR)/phys \ -I$(WRF_SRC_ROOT_DIR)/wrftladj \ -I$(WRF_SRC_ROOT_DIR)/chem -I$(WRF_SRC_ROOT_DIR)/inc \ -I$(NETCDFPATH)/include \ -I${HDF5PATH}/include \ -I${JASPERPATH}/include \ -I${PNETCDFPATH}/include \ -I${LIBPNGPATH}/include \ $(CTSM_INCLUDES) HDF5PATH = /home/yuansun/software/gcc/hdf5/1.12.3 PNETCDFPATH = /home/yuansun/software/gcc/pnetcdf/1.12.3 JASPERPATH = /home/yuansun/software/gcc/jasper/4.2.5 LIBPNGPATH = /home/yuansun/software/gcc/libpng/1.6.39 LAPACKPATH = /home/yuansun/software/gcc/lapack/3.9.0
Compile WRF (taking around 20 minutes)
nohup ./compile em_real 2>&1 > compile.log &
Check if compiled successfully by listing excutable files. Must be four files:
ndown.exe
,real.exe
,tc.exe
,wrf. exe
ls main/*exe
Build WPS¶
Compile WPS
export WRF_DIR=${WRF_ROOT}/${WRFNAME} ./clean -a ./configure # - 3 ./compile >& compile.log
Check if compiled successfully by listing excutable files. Must be three files:
geogrid.exe
metgrid.exe
,ungrib.exe
ls *exe