#!/usr/bin/env python

"""ufsatm namelist creator
"""

# 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, copy, time
from datetime import datetime

CIMEROOT = os.environ.get("CIMEROOT")
if CIMEROOT is None:
    raise SystemExit("ERROR: must set CIMEROOT environment variable")
sys.path.append(os.path.join(CIMEROOT, "scripts", "Tools"))

from standard_script_setup import *
from CIME.case import Case
from CIME.nmlgen import NamelistGenerator
from CIME.namelist import parse
from CIME.buildnml import create_namelist_infile, parse_input
from CIME.utils import expect, safe_copy, symlink_force, ls_sorted_by_mtime

logger = logging.getLogger(__name__)

def date_yyyymmddhh(case):
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod)//3600
    return yyyy, mm, dd, hh

# find factors of a number
def factors(n):
    return set(x for tup in ([i, n//i] for i in range(1, int(n**0.5)+1) if n % i == 0) for x in tup)

# check if all elements in a list are same
def checkList(lst):
    ele = lst[0]
    chk = True
    # Comparing each element with first item
    for item in lst:
        if ele != item:
            chk = False
            break
    return chk

# returns active components
def active_components(case):
    # query compset
    compset = case.get_value("COMPSET")
    # parse compset
    components = compset.split("_")[1:]
    # remove stub components
    components = [ x for x in components if "S" not in x[0] ]

    return components

# converts one item list to value, if list has more than one item then do nothing
def list_to_value(val):
    if isinstance(val, list):
        if len(val) == 1:
            return val[0]
        else:
            return val
    else:
        return val

# keep only changed namelist options
def nmlKeepChangedOnly(nmlgen_ini, nmlgen_cur, namelist_user):

    nmlgen_ret = copy.copy(nmlgen_cur)
    #pylint: disable=protected-access
    for nml in nmlgen_cur._namelist.get_group_names():
        for var in nmlgen_cur.get_group_variables(nml):
            matched = False

            # if it is an user modified parameter, add it to namelist in any case
            lst = namelist_user.get_value(var)
            if any(len(item) > 0 for item in lst):
                continue

            # list of namelist options that will always appear in the namelist file
            lst = ['ccpp_suite', 'nstf_name']
            if var in lst:
                continue

            # get type and size of namelist variable
            var_type, _, var_size, = nmlgen_cur._definition.split_type_string(var)

            # retrieve the initial (default) and current values of the namelist variable
            ini = nmlgen_ini.get_value(var)
            cur = nmlgen_cur.get_value(var)

            # if it is list and size is different from var_size
            if var_size > 1:
                # create empty list
                var_act = [None]*var_size
                var_def = [None]*var_size

                # force to have same length
                var_act_len = len(cur)
                var_act[0:var_act_len] = [x for x in cur]
                var_def_len = len(ini)
                var_def[0:var_def_len] = [x for x in ini]

                # compare
                if var_type == 'character' or var_type == 'logical':
                    var_act = ['xyz' if x is None else x.lower() for x in var_act]
                    var_def = ['xyz' if x is None else x.lower() for x in var_def]
                    matched = all(y.startswith(x) for x,y in zip(var_act,var_def))
                elif var_type == 'integer':
                    var_act = [-99 if x is None else int(x) for x in var_act]
                    var_def = [-99 if x is None else int(x) for x in var_def]
                    matched = all(x == y for x,y in zip(var_act,var_def))
                elif var_type == 'real':
                    var_act = [-99.9 if x is None else float(eval(x.lower().replace('d','e'))) for x in var_act]
                    var_def = [-99.9 if x is None else float(eval(x.lower().replace('d','e'))) for x in var_def]
                    matched = all(x == y for x,y in zip(var_act,var_def))
            else:
                if var_type == 'character' or var_type == 'logical':
                    if not ini.strip() == "":
                        if cur.startswith(ini):
                            matched = True
                    else:
                        if ini.strip() == "" and cur.strip() == "":
                            matched = True
                elif var_type == 'integer':
                    if ini:
                        if int(cur) == int(ini):
                            matched = True
                    else:
                        if not (ini and cur):
                            matched = True
                elif var_type == 'real' and ini and cur:
                    if float(eval(ini.lower().replace('d','e'))) == \
                       float(eval(cur.lower().replace('d','e'))):
                        matched = True

            # if there is a match, remove it from the list
            if matched:
                nmlgen_ret._namelist.delete_variable(nml, var)

    return nmlgen_ret

# copy namelist options from one namelist to other
def nmlOverwrite(nmlgen_src, nmlgen_dst):

    nmlgen_ret = copy.copy(nmlgen_dst)
    #pylint: disable=protected-access
    for nml in nmlgen_src.get_group_names():
        for var in nmlgen_src.get_group_variables(nml):
            nmlgen_ret.set_value(var, nmlgen_src.get_value(var))

    return nmlgen_ret

# prepares the input files of a case and places in rundir:
def prep_input(case, casedocsdir, datestamp, nmlgen_input, prefix, input_type):
    casename = case.get_value("CASE")
    Buildconf = case.get_value("CASEBUILD")
    rundir = case.get_value("RUNDIR")
    atm_grid = case.get_value("ATM_GRID").replace('r', '')
    testcase = case.get_value("TEST")
    din_loc_root = case.get_value("DIN_LOC_ROOT")
    din_loc_ic = case.get_value("DIN_LOC_IC")
    is_restart =  case.get_value("CONTINUE_RUN")
    srcroot = case.get_value("SRCROOT")
    caseroot = case.get_value("CASEROOT")

    #----------------------------------------------------
    # Query jobs
    #----------------------------------------------------

    env_workflow = case.get_env("workflow")
    jobs = env_workflow.get_jobs()

    #----------------------------------------------------
    # Create list of input files
    #----------------------------------------------------

    # TODO: make it xmlchnage variable
    new_o3forc = True
    if "s2s" in app:
        new_o3forc = False

    # Query date and time
    yyyy, mm, dd, hh = date_yyyymmddhh(case)

    # Define base directories
    icdir = os.path.join(din_loc_ic,"{:04d}{:02d}".format(yyyy,mm),"{:04d}{:02d}{:02d}".format(yyyy,mm,dd))
    if "hafs" in app:
        dir_fix_am  = os.path.join(din_loc_root,"regional","fix_am")
        dir_fix_ufsatm = os.path.join(din_loc_root,"regional","fix",atm_grid)
        dir_fix_parm = os.path.join(din_loc_root,"parm")
        dir_fix_lib = os.path.join(din_loc_root,"lib")
        dir_bdy = os.path.join(din_loc_root,"regional","bcond","{:04d}{:02d}{:02d}{:02d}".format(int(yyyy),int(mm),int(dd),hh))
    else:
        dir_fix_am  = os.path.join(din_loc_root,"global","fix","fix_am.v{}".format(datestamp))
        if "s2s" in app:
            dir_fix_ufsatm = os.path.join(din_loc_root,"global","fix","fix_fv3_gmted2010.v{}".format(datestamp),"{}_s2s".format(atm_grid))
        else:
            dir_fix_ufsatm = os.path.join(din_loc_root,"global","fix","fix_fv3_gmted2010.v{}".format(datestamp),atm_grid)
        dir_fix_parm = os.path.join(din_loc_root,"parm")
        dir_fix_lib = os.path.join(din_loc_root,"lib")

    # Create generic dictionary to hold input files: [source_file, "ln" | "cp", target_file]
    input_files = dict()

    # Grid and orography data
    if "hafs" in app:
        halo_lst = ["halo0", "halo3", "halo4"]
        for halo in halo_lst:
            input_files["grid_tile7_{}".format(halo)] = [os.path.join(dir_fix_ufsatm,atm_grid+"_grid.tile7.{}.nc".format(halo)), "ln", os.path.join("INPUT",atm_grid+"_grid.tile7.{}.nc".format(halo))]
            input_files["orog_tile7_{}".format(halo)] = [os.path.join(dir_fix_ufsatm,atm_grid+"_oro_data.tile7.{}.nc".format(halo)), "ln", os.path.join("INPUT",atm_grid+"_oro_data.tile7.{}.nc".format(halo))]
        input_files["oro_data_tile7h0"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_oro_data.tile7.halo0.nc"), "ln",  os.path.join("INPUT","oro_data.nc")]
        input_files["oro_data_tile7h4"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_oro_data.tile7.halo4.nc"), "ln",  os.path.join("INPUT","oro_data.tile7.halo4.nc")]
        input_files["grid_tile7h3"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_grid.tile7.halo3.nc"), "ln",  os.path.join("INPUT",atm_grid+"_grid.tile7.nc")]
        input_files["grid_tile7h0"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_grid.tile7.halo0.nc"), "ln",  os.path.join("INPUT","grid.tile7.halo0.nc")]
        input_files["grid_tile7h4"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_grid.tile7.halo4.nc"), "ln",  os.path.join("INPUT","grid.tile7.halo4.nc")]
    else:
        for tile in range(1,7):
            input_files["grid_tile{}".format(tile)] = [os.path.join(dir_fix_ufsatm,atm_grid+"_grid.tile{}.nc".format(tile)), "ln", os.path.join("INPUT",atm_grid+"_grid.tile{}.nc".format(tile))]
            input_files["orog_data{}".format(tile)] = [os.path.join(dir_fix_ufsatm,atm_grid+"_oro_data.tile{}.nc".format(tile)), "ln", os.path.join("INPUT","oro_data.tile{}.nc".format(tile))]

    if "s2s" in app:
        input_files["grid_spec"] = [os.path.join(icdir,"grid_spec.nc"), "ln", os.path.join("INPUT","grid_spec.nc")]
    else:
        input_files["grid_spec"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_mosaic.nc"), "ln", os.path.join("INPUT","grid_spec.nc")]
    input_files["grid_mosaic"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_mosaic.nc"), "ln", os.path.join("INPUT",atm_grid+"_mosaic.nc")]

    # GFS standard input files
    iaer = nmlgen_input.get_value('iaer')
    ico2 = nmlgen_input.get_value('ico2')

    if new_o3forc:
        input_files["o3forc"] = [os.path.join(dir_fix_am,"ozprdlos_2015_new_sbuvO3_tclm15_nuchem.f77"), "ln", "global_o3prdlos.f77"]
    else:
        input_files["o3forc"] = [os.path.join(dir_fix_am,"global_o3prdlos.f77"), "ln", "global_o3prdlos.f77"]

    input_files["h2oforc"] = [os.path.join(dir_fix_am,"global_h2o_pltc.f77"), "ln", "global_h2oprdlos.f77"]
    input_files["solcnst"] = [os.path.join(dir_fix_am,"global_solarconstant_noaa_an.txt"), "ln", "solarconstant_noaa_an.txt"]
    input_files["sfcemis"] = [os.path.join(dir_fix_am,"global_sfc_emissivity_idx.txt"), "ln", "sfc_emissivity_idx.txt"]
    input_files["co2hist"] = [os.path.join(dir_fix_am,"global_co2historicaldata_glob.txt"), "ln", "co2historicaldata_glob.txt"]
    input_files["co2mont"] = [os.path.join(dir_fix_am,"co2monthlycyc.txt"), "ln", "co2monthlycyc.txt"]

    # NOTE: CIME.namelist.parse returns single values as one element list
    ico2 = list_to_value(ico2)
    if ico2 and int(ico2) > 0:
        # TODO: make it automatic
        yy = int(time.strftime("%Y,%m,%d,%H,%M,%S").split(',')[0])
        for year in range(2009,yy+1):
            input_files["co2his_{}".format(year)] = [os.path.join(dir_fix_am,"fix_co2_proj","global_co2historicaldata_{}.txt".format(year)), "ln", "co2historicaldata_{}.txt".format(year)]

    input_files["clmaero"] = [os.path.join(dir_fix_am,"global_climaeropac_global.txt"), "ln", "aerosol.dat"]

    # NOTE: CIME.namelist.parse returns single values as one element list
    iaer = list_to_value(iaer)
    if iaer and int(iaer) > 0 and "hafs" not in app:
        for year in range(1850,2000,10):
            input_files["volaer_{}".format(year)] = [os.path.join(dir_fix_am,"global_volcanic_aerosols_{}-{}.txt".format(year,year+9)), "ln", "volcanic_aerosols_{}-{}.txt".format(year,year+9)]

    # Fixed files
    for var in nmlgen_input.get_group_variables('namsfc'):
        #pylint: disable=protected-access
        var_str = str(nmlgen_input.get_value(var)).replace("'", "").strip()
        if not var_str.startswith('igbp') and var_str.endswith('.grb'):
            input_files[var] = [var_str, "ln", os.path.basename(var_str)]

    # Look for NCEPLIBS_DIR
    nceplibs_dir = os.environ.get("NCEPLIBS_DIR")
    if "case.chgres" in jobs:
        expect(nceplibs_dir, "NCEPLIBS_DIR environment variable need to be set to process raw input data!")
        expect(os.path.exists(nceplibs_dir),"NCEPLIBS_DIR {} not found".format(nceplibs_dir))

    # Pre-processing specific files (chgres)
    if "case.chgres" in jobs:
        # Fixed files for chgres
        for tile in range(1,7):
            for f in ["facsf", "maximum_snow_albedo", "slope_type", "snowfree_albedo", "soil_type", "substrate_temperature", "vegetation_greenness", "vegetation_type"]:
                input_files["{}{}".format(f,tile)] = [os.path.join(dir_fix_ufsatm,"fix_sfc",atm_grid+".{}.tile{}.nc".format(f,tile)), "ln", os.path.join("INPUT",atm_grid+".{}.tile{}.nc".format(f,tile))]

        # Vertical layer file for chgres
        levp = nmlgen_input.get_value("levp")
        input_files["global_hyblev"] = [os.path.join(dir_fix_am,"global_hyblev.l{}.txt".format(levp)), "ln", os.path.join("INPUT","global_hyblev.l{}.txt".format(levp))]

        # Grib2 specific file
        input_files["var_map"] = [os.path.join(nceplibs_dir,"share","GFSphys_var_map.txt"), "ln", "GFSphys_var_map.txt"]

    # Post-processing specific files (gfs_post, copy from source directory)
    if "case.gfs_post" in jobs:
        src_file = os.path.join(caseroot,"SourceMods","src.ufsatm","postxconfig-NT-GFS.txt")
        dst_file = os.path.join(rundir, "postxconfig-NT-GFS.txt")
        if os.path.isfile(dst_file):
            os.remove(dst_file)

        if os.path.isfile(src_file):
            safe_copy(src_file, dst_file)
            logger.info("Using {} for post-processing".format(src_file))
        else:
            logger.info("Using {} for post-processing".format(os.path.join(nceplibs_dir,"share","postxconfig-NT-GFS.txt")))
            input_files["postxconfig1"] = [os.path.join(nceplibs_dir,"share","postxconfig-NT-GFS.txt"), "ln", "postxconfig-NT-GFS.txt"]

        src_file = os.path.join(caseroot,"SourceMods","src.ufsatm","postxconfig-NT-GFS-F00.txt")
        dst_file = os.path.join(rundir, "postxconfig-NT-GFS-F00.txt")
        if os.path.isfile(dst_file):
            os.remove(dst_file)

        if os.path.isfile(src_file):
            safe_copy(src_file, dst_file)
            logger.info("Using {} for post-processing".format(src_file))
        else:
            logger.info("Using {} for post-processing".format(os.path.join(nceplibs_dir,"share","postxconfig-NT-GFS-F00.txt")))
            input_files["postxconfig2"] = [os.path.join(nceplibs_dir,"share","postxconfig-NT-GFS-F00.txt"), "ln", "postxconfig-NT-GFS-F00.txt"]

        input_files["params_grib2_tbl"] = [os.path.join(nceplibs_dir,"share","params_grib2_tbl_new"), "ln", "params_grib2_tbl_new"]

    # Make sure that rundir exists. If not, make it:
    if not os.path.exists(rundir):
        os.makedirs(rundir)

    # Create INPUT and RESTART directories
    logger.info("\tCreating INPUT and RESTART directory")
    for _dir in ("INPUT","RESTART"):
        fulldir = os.path.join(rundir, _dir)
        if not os.path.exists(fulldir):
            os.makedirs(fulldir)

    # For cime tests that use two test directories
    rundirdst = rundir
    if rundir.endswith("case2run"):
        rundirsrc = os.path.abspath(os.path.join(rundir, os.pardir))
    else:
        rundirsrc = rundir

    # Initial conditions
    if is_restart: # Warm start
        # Tiled files
        tile_max = 7
        if "hafs" in app:
            # Regional case has only one tile (1)
            tile_max = 2

        lst = ["fv_core.res", "fv_srf_wnd.res", "fv_tracer.res", "phy_data", "sfc_data"]
        lst = [prefix + f for f in lst]
        for f in lst:
            for tile in range(1,tile_max):
                if "_data" in f and "hafs" in app:
                    src_file = os.path.join(rundir,"RESTART","{}.nc".format(f))
                    tgt_file = os.path.join(rundir,"INPUT","{}.nc".format(f.replace(prefix,"")))
                else:
                    src_file = os.path.join(rundir,"RESTART","{}.tile{}.nc".format(f,tile))
                    tgt_file = os.path.join(rundir,"INPUT","{}.tile{}.nc".format(f.replace(prefix,""),tile))
                if not os.path.isfile(src_file):
                    expect(False, "{} is missing.".format(src_file))
                else:
                    safe_copy(src_file, tgt_file)

        # Others
        lst = ["fv_core.res.nc", "coupler.res"]
        lst = [prefix + f for f in lst]
        for f in lst:
            src_file = os.path.join(rundir,"RESTART",f)
            tgt_file = os.path.join(rundir,"INPUT",f.replace(prefix,""))
            if not os.path.isfile(src_file):
                expect(False, "{} is missing.".format(src_file))
            else:
                safe_copy(src_file, tgt_file)

        # Reorder boundary files
        if "hafs" in app:
            # Boundary conditions
            lst = ls_sorted_by_mtime(dir_bdy)
            lst.remove("gfs_ctrl.nc")
            lst.remove("gfs_data.tile7.nc")
            lst.remove("sfc_data.tile7.nc")
            for f in lst:
                # Remove old links
                src_file = os.path.join(rundirdst,"INPUT",f)
                if os.path.isfile(src_file):
                    os.remove(src_file)

                # Link files again
                time_step = int(f.split(".")[2])
                #logger.warning("time_step = {}".format(time_step))
                if time_step < nhours_fcst_pre:
                    continue
                else:
                    src_file = os.path.join(dir_bdy,f)
                    dst_file = os.path.join(rundir,"INPUT","gfs_bndy.tile7.{:03d}.nc".format(time_step-nhours_fcst_pre))
                    #logger.warning("{} --> {}".format(src_file, dst_file))
                    symlink_force(src_file, dst_file)

    else: # Cold start
        if "hafs" in app:
            # Boundary conditions
            lst = ls_sorted_by_mtime(dir_bdy)
            for f in lst:
                # Link files
                symlink_force(os.path.join(dir_bdy,f), os.path.join(rundirdst,"INPUT",f))

            symlink_force(os.path.join(dir_bdy,"sfc_data.tile7.nc"), os.path.join(rundirdst,"INPUT","sfc_data.nc"))
            symlink_force(os.path.join(dir_bdy,"gfs_data.tile7.nc"), os.path.join(rundirdst,"INPUT","gfs_data.nc"))
        else:
            # Remove restart files form INPUT/ directory, if there are
            # Tiled files
            lst = ["fv_core.res", "fv_srf_wnd.res", "fv_tracer.res", "phy_data", "sfc_data"]
            for f in lst:
                for tile in range(1,7):
                    src_file = os.path.join(rundir,"INPUT","{}.tile{}.nc".format(f,tile))
                    if os.path.isfile(src_file):
                        logger.warning("removing file {}".format(src_file))
                        os.remove(src_file)

            # Others
            lst = ["fv_core.res.nc", "coupler.res"]
            for f in lst:
                src_file = os.path.join(rundir,"INPUT",f)
                if os.path.isfile(src_file):
                    logger.warning("removing file {}".format(src_file))
                    os.remove(src_file)

            # Check chgres processes initial condition exists or not?
            src_file = os.path.join(rundirsrc,"INPUT","{}.".format(atm_grid)+"{:04d}-{:02d}-{:02d}".format(yyyy,mm,dd)+"_{:02d}.gfs_ctrl.nc".format(hh))
            if os.path.isfile(src_file):
                # Link existing initial condition
                symlink_force(src_file, os.path.join(rundirdst,"INPUT","gfs_ctrl.nc"))

                lst = ["gfs_data", "sfc_data"]
                for f in lst:
                    for tile in range(1,7):
                        src_file = os.path.join(rundirsrc,"INPUT","{}.".format(atm_grid)+"{:04d}-{:02d}-{:02d}".format(yyyy,mm,dd)+"_{:02d}.".format(hh)+f+".tile{}.nc".format(tile))
                        tgt_file = os.path.join(rundirdst,"INPUT","{}.tile{}.nc".format(f,tile))
                        if os.path.isfile(src_file):
                            symlink_force(src_file, tgt_file)
                            logger.info("chgres source file {} linked to {}".format(src_file, tgt_file))
                        else:
                            logger.debug("chgres source file {} not found".format(src_file))
            else:
               # Link pre-staged initial condition, not processed by workflow with chgres
               src_file = os.path.join(icdir,"gfs_ctrl.nc")
               if os.path.isfile(src_file):
                   symlink_force(src_file, os.path.join(rundirdst,"INPUT","gfs_ctrl.nc"))
               else:
                   logger.warning("initial condition file {} is required! Exiting ...".format(src_file))

               lst = ["gfs_data", "sfc_data"]
               for f in lst:
                   for tile in range(1,7):
                       src_file = os.path.join(icdir,f+".tile{}.nc".format(tile))
                       tgt_file = os.path.join(rundirdst,"INPUT","{}.tile{}.nc".format(f,tile))
                       if os.path.isfile(src_file):
                           symlink_force(src_file, tgt_file)
                           logger.info("initial condition file {} linked to {}".format(src_file, tgt_file))
                       else:
                           logger.warning("initial condition file {} is required! Exiting ...".format(src_file))

    # Write input file list
    with open(os.path.join(Buildconf,"ufsatm.input_data_list"), 'w') as input_data_list:
        for k,v in input_files.items():
            input_data_list.write(k+" = "+v[0]+"\n")

    # Copy/Link files
    for k,v in input_files.items():
        if not os.path.isfile(v[0]):
            logger.warning("WARNING: data file {} not found, will attempt to download.".format(v[0]))
        elif "ln" in v[1]:
            symlink_force(v[0], os.path.join(rundir,v[2]))
        elif "cp":
            safe_copy(v[0], os.path.join(rundir,v[2]))

    # Copy *_table files
    cimeroot_ufsatm = os.path.join(srcroot, "src", "model", "FV3", "cime", "cime_config")
    for f in ["data_table_{}".format(app), "diag_table_{}".format(app), "field_table_{}".format(app)]:
        if not os.path.isfile(os.path.join(rundir,f)):
            safe_copy(os.path.join(cimeroot_ufsatm,"tables",f),os.path.join(rundir,f.replace("_{}".format(app),"")))

# pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements
####################################################################################
def _create_namelist_input(case, confdir, config, infile, nmlgen_model, nmlgen_input, nmlgen_input_def, namelist_user, datestamp, input_type):
####################################################################################
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    #----------------------------------------------------
    # Clear out old data.
    #----------------------------------------------------
    data_list_path = os.path.join(case.get_case_root(), "Buildconf", "ufsatm.input_data_list")

    #----------------------------------------------------
    # Initialize namelist defaults
    #----------------------------------------------------

    nmlgen_input.init_defaults(infile, config)
    nmlgen_input_def.init_defaults(infile, {})

    #----------------------------------------------------
    # Modify namelist defaults
    #----------------------------------------------------

    # Define base directory
    din_loc_root = case.get_value("DIN_LOC_ROOT")
    if "hafs" in app:
        dir_fix_am  = os.path.join(din_loc_root,"regional","fix_am")
    else:
        dir_fix_am  = os.path.join(din_loc_root,"global","fix","fix_am.v{}".format(datestamp))

    # Spectral truncation and regular grid resolution based on FV3 resolution
    atm_grid = case.get_value("ATM_GRID").replace('r', '')
    res = int(atm_grid.replace('C',''))
    jcap = 2*res-2
    lonb = 4*res
    latb = 2*res

    # Customization for S2S, just for C96
    if "s2s" in app:
        if res == 96:
            jcap = 126
            lonb = 384
            latb = 190

    # Fix files, changes based on the selected resolution
    nmlgen_input.set_value('fnglac', value=os.path.join(dir_fix_am,"global_glacier.2x2.grb"))
    nmlgen_input.set_value('fnmxic', value=os.path.join(dir_fix_am,"global_maxice.2x2.grb"))
    nmlgen_input.set_value('fntsfc', value=os.path.join(dir_fix_am,"RTGSST.1982.2012.monthly.clim.grb"))
    nmlgen_input.set_value('fnsnoc', value=os.path.join(dir_fix_am,"global_snoclim.1.875.grb"))
    nmlgen_input.set_value('fnzorc', value="igbp")
    nmlgen_input.set_value('fnalbc2', value=os.path.join(dir_fix_am,"global_albedo4.1x1.grb"))
    nmlgen_input.set_value('fnaisc', value=os.path.join(dir_fix_am,"CFSR.SEAICE.1982.2012.monthly.clim.grb"))
    nmlgen_input.set_value('fntg3c', value=os.path.join(dir_fix_am,"global_tg3clim.2.6x1.5.grb"))
    nmlgen_input.set_value('fnvegc', value=os.path.join(dir_fix_am,"global_vegfrac.0.144.decpercent.grb"))
    if "mrweather" in app:
        nmlgen_input.set_value('fnmskh', value=os.path.join(dir_fix_am,"global_slmask.t1534.3072.1536.grb"))
    else:
        nmlgen_input.set_value('fnmskh', value=os.path.join(dir_fix_am,"seaice_newland.grb"))
    nmlgen_input.set_value('fnvmnc', value=os.path.join(dir_fix_am,"global_shdmin.0.144x0.144.grb"))
    nmlgen_input.set_value('fnvmxc', value=os.path.join(dir_fix_am,"global_shdmax.0.144x0.144.grb"))
    nmlgen_input.set_value('fnslpc', value=os.path.join(dir_fix_am,"global_slope.1x1.grb"))
    nmlgen_input.set_value('fnalbc', value=os.path.join(dir_fix_am,"global_snowfree_albedo.bosu.t{}.{}.{}.rg.grb".format(jcap,lonb,latb)))
    nmlgen_input.set_value('fnvetc', value=os.path.join(dir_fix_am,"global_vegtype.igbp.t{}.{}.{}.rg.grb".format(jcap,lonb,latb)))
    nmlgen_input.set_value('fnsotc', value=os.path.join(dir_fix_am,"global_soiltype.statsgo.t{}.{}.{}.rg.grb".format(jcap,lonb,latb)))
    nmlgen_input.set_value('fnabsc', value=os.path.join(dir_fix_am,"global_mxsnoalb.uariz.t{}.{}.{}.rg.grb".format(jcap,lonb,latb)))

    # If the appropriate resolution fix file is not present, use the highest resolution available (T1534)
    if "mrweather" in app:
        if res == 96 or res == 192:
            jcap = 1534
            lonb = 3072
            latb = 1536
            nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,"global_soilmgldas.statsgo.t{}.{}.{}.grb".format(jcap,lonb,latb)))
        else:
            nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,"global_soilmgldas.statsgo.t{}.{}.{}.grb".format(jcap,lonb,latb)))
    else:
        nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,"global_soilmgldas.t{}.{}.{}.grb".format(jcap,lonb,latb)))

    # Stochastic physics
    testcase = case.get_value("TEST")
    if testcase:
        nmlgen_input.set_value('iseed_skeb', 0)
        nmlgen_input.set_value('iseed_shum', 0)
        nmlgen_input.set_value('iseed_sppt', 0)
    else:
        # TODO: member option (-1: control, 0: ensemble mean, >0: ensemble member) need to be handled, for now it is assumed as 1
        member = 1
        cdate = int(datetime.now().strftime("%Y%m%d%H"))
        nmlgen_input.set_value('iseed_skeb', cdate*1000+member*10+1)
        nmlgen_input.set_value('iseed_shum', cdate*1000+member*10+2)
        nmlgen_input.set_value('iseed_sppt', cdate*1000+member*10+3)

    # Save namelist before making restart changes
    nmlgen_input_sav = copy.copy(nmlgen_input)

    # Restart
    is_restart =  case.get_value("CONTINUE_RUN")

    if is_restart: # Warm start
        nmlgen_input.set_value('warm_start', True)
        nmlgen_input.set_value('nggps_ic', False)
        nmlgen_input.set_value('external_ic', False)
        nmlgen_input.set_value('mountain', True)
        nmlgen_input.set_value('make_nh', False)
        nmlgen_input.set_value('na_init', 0)

        # Turn off nst spin up
        nstf_name = nmlgen_input.get_value('nstf_name')
        nstf_name[1] = '0'

        # Enable adding increment on the fly to the restarts
        read_increment = nmlgen_input.get_value('read_increment')
        if read_increment:
            nmlgen_input.set_value('res_latlon_dynamics', "INPUT/fv_rst.res.nc")

        # Set the forecast hour 'off-set'
        #if "s2s" in app:
        #    nmlgen_input.set_value('fhrot', 0)

    else: # Cold start
        # Set back to their defaults
        nmlgen_input.set_value('warm_start', nmlgen_input_sav.get_value('warm_start'))
        nmlgen_input.set_value('nggps_ic', nmlgen_input_sav.get_value('nggps_ic'))
        nmlgen_input.set_value('external_ic', nmlgen_input_sav.get_value('external_ic'))
        nmlgen_input.set_value('mountain', nmlgen_input_sav.get_value('mountain'))
        nmlgen_input.set_value('make_nh', nmlgen_input_sav.get_value('make_nh'))
        nmlgen_input.set_value('na_init', nmlgen_input_sav.get_value('na_init'))

        # Turn on nst spinup for grib
        if 'hafs' not in app:
            nstf_name = nmlgen_input_sav.get_value('nstf_name')
            if input_type == 'grib2':
                nstf_name[1] = '1'
            else:
                nstf_name[1] = '0'

        nmlgen_input.set_value('read_increment', nmlgen_input_sav.get_value('read_increment'))
        nmlgen_input.set_value('res_latlon_dynamics', nmlgen_input_sav.get_value('res_latlon_dynamics'))

    if input_type == 'grib2' and 'hafs' not in app:
        # Turn off nstf for grib input
        nstf_name[0] = '0'
        logger.info("input_type {} nstf_name {}".format(input_type, nstf_name))
        nmlgen_input.set_value('nstf_name', nstf_name)
    #----------------------------------------------------
    # Write out namelist groups
    #----------------------------------------------------

    # Path for namelist files
    srcroot = case.get_value("SRCROOT")
    namelist_xml_dir = os.path.join(srcroot, "src", "model", "FV3", "cime", "cime_config")

    # Create list of groups by querying xml file
    groups = []
    with open(os.path.join(namelist_xml_dir, "namelist_definition_ufsatm.xml"), 'r') as fin:
        for s in fin:
            if "<group>" in s:
                groups.append(s.replace('<group>', '').replace('</group>', '').strip())

    # Remove duplicates
    groups = list(set(groups))

    # Keep only ufsatm related ones
    groups.remove('config') # chgres
    groups.remove('_no_group_var') # model_configure
    groups.remove('freeform') # ncep_post
    groups.remove('nampgb') # ncep_post

    # Sort group
    groups.sort()

    # Remove duplicates
    nmlgen = nmlKeepChangedOnly(nmlgen_input_def, nmlgen_input, namelist_user)

    ntask_atm = int(case.get_value('NTASKS_ATM'))
    if ntask_atm == 8:
        nmlgen.set_value('layout',value=[1,1])

    # Overwrite user_nl_ufsatm changes
    nmlgen = nmlOverwrite(namelist_user, nmlgen)

    # if nstf_name is None here then a default of 0,0,0,0,0 is assumed
    nstf_name = nmlgen.get_value('nstf_name')
    if "mrweather" in app:
        if not nstf_name[0] or nstf_name[0] == '0':
            ccpp = nmlgen.get_value('ccpp_suite')
            nmlgen.set_value('ccpp_suite',ccpp+'_no_nsst')

    # Consistency check for layout
    layout = nmlgen_input.get_value('layout')
    layout_x = int(layout[0])
    layout_y = int(layout[1])
    ntiles = int(nmlgen_input.get_value('ntiles@fv_core_nml'))
    write_tasks_per_group = int(nmlgen_model.get_value('write_tasks_per_group'))
    write_groups = int(nmlgen_model.get_value('write_groups'))

    ntask_atm_config = layout_x*layout_y*ntiles+write_tasks_per_group*write_groups
    if ntask_atm_config != ntask_atm:
        expect(False, ("Total number of PE need to be consistent with the model namelist options:\n"+
                       "\tTotal number of PE (ntask_atm) = {}\n"+
                       "\tDecomposition in x and y direction (layout) = {}x{}\n"+
                       "\tNumber of tile (ntiles) = {}\n"+
                       "\tNumber of I/O group (write_groups) = {}\n"+
                       "\tNumber of tasks in each I/O group (write_tasks_per_group) = {}\n").
                       format(ntask_atm,layout_x,layout_y,ntiles,write_groups,write_tasks_per_group))

    # Create namelist
    namelist_file = os.path.join(confdir, "atm_in")
    nmlgen.write_output_file(namelist_file, data_list_path, groups=groups, sorted_groups=False)

# pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements
####################################################################################
def _create_namelist_model_configure(case, confdir, config, infile, nmlgen, namelist_user, prefix):
####################################################################################
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    #----------------------------------------------------
    # Clear out old data.
    #----------------------------------------------------
    data_list_path = os.path.join(case.get_case_root(), "Buildconf", "ufsatm.input_data_list")
    if os.path.exists(data_list_path):
        logger.warning("removing file {}".format(data_list_path))
        os.remove(data_list_path)

    #----------------------------------------------------
    # Initialize namelist defaults
    #----------------------------------------------------
    nmlgen.init_defaults(infile, config)

    #----------------------------------------------------
    # Modify namelist defaults
    #----------------------------------------------------

    # Query previous run length, if it is warm start
    is_restart = case.get_value("CONTINUE_RUN")

    global nhours_fcst_pre
    if is_restart: # Warm start
        # Set previous forecast length based on restart file
        rundir = case.get_value("RUNDIR")
        rst_file = os.path.join(rundir, "RESTART", "{}coupler.res".format(prefix))
        if not os.path.isfile(rst_file):
            expect(False, "Couldn't find '"+rst_file+"'!")
        else:
            # TODO: it supports only gregorian and need to support others (no_calendar=0, thirty_day_months=1, julian=2, gregorian=3, noleap=4)
            # Parse coupler.res
            logger.debug("Using {} to determine previous forecast length ...".format(rst_file))
            with open(rst_file, 'r') as f:
                lines = f.readlines()
                #calendar = lines[0].split()[0]
                date1 = datetime(*[int(i) for i in lines[1].split()[0:6]])
                date2 = datetime(*[int(i) for i in lines[2].split()[0:6]])
                nhours_fcst_pre = int((date2-date1).total_seconds()//3600)
    else: # Cold start
        # Set previous forecast length as zero
        nhours_fcst_pre = 0

    # Change start date
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    nmlgen.set_value('start_year', value=yyyy)
    nmlgen.set_value('start_month', value=mm)
    nmlgen.set_value('start_day', value=dd)

    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod//3600)
    mi = int((run_start_tod-hh*3600)//60)
    ss = int(run_start_tod-hh*3600-mi*60)
    nmlgen.set_value('start_hour', value=hh)
    nmlgen.set_value('start_minute', value=mi)
    nmlgen.set_value('start_second', value=ss)

    # Change forecast length
    stop_option = case.get_value('STOP_OPTION')
    stop_n = int(case.get_value('STOP_N'))

    if 'nyears' in stop_option:
        expect(False, "Option >nyears< is not supported!")
    elif 'nmonths' in stop_option:
        expect(False, "Option >nmonths< is not supported!")
    elif 'ndays' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n*24 + nhours_fcst_pre)
    elif 'nhours' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n + nhours_fcst_pre)
    elif 'nseconds' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n/3600 + nhours_fcst_pre)
    elif 'nsteps' in stop_option:
        expect(False, "Option >nsteps< is not supported!")

    # Change restart interval
    rest_option = case.get_value('REST_OPTION')
    rest_n = int(case.get_value('REST_N'))

    if 'nyears' in rest_option:
        expect(False, "Option >nyears< is not supported!")
    elif 'nmonths' in rest_option:
        expect(False, "Option >nmonths< is not supported!")
    elif 'ndays' in rest_option:
        nmlgen.set_value('restart_interval', rest_n*24)
    elif 'nhours' in rest_option:
        nmlgen.set_value('restart_interval', rest_n)
    elif 'nseconds' in rest_option:
        nmlgen.set_value('restart_interval', rest_n/60)
    elif 'nsteps' in rest_option:
        expect(False, "Option >nsteps< is not supported!")

    # Query total number of PEs
    components = active_components(case)
    ntask = int(case.get_value('NTASKS_ATM'))
    if len(components) > 1:
        ntask = int(case.get_value('TOTALPES'))

    # Change number of used PEs if there is a mismatch
    pe_member = int(nmlgen.get_value('PE_MEMBER01'))
    if pe_member != ntask:
        nmlgen.set_value('PE_MEMBER01', ntask)

    # Change number of write group for 8 PEs
    if ntask == 8:
        nmlgen.set_value("write_tasks_per_group",2)

    # Test run specific modifications
    testcase = case.get_value("TEST")
    if testcase:
        # Change output format
        nmlgen.set_value('output_file', 'netcdf')

        # Change file prefixes
        nmlgen.set_value('filename_base', "{} {}".format(case.get_value('CASE')+'.ufsatm.atm.', case.get_value('CASE')+'.ufsatm.sfc.'))

    # Modify machine specific namelist options
    nmlgen.set_value('atmos_nthreads', case.thread_count)
    nmlgen.set_value('ncores_per_node', case.tasks_per_node)

    # Activate ESMF logs if it is a debug run
    debug = case.get_value("DEBUG")
    if debug:
        nmlgen.set_value('print_esmf', True)
    else:
        nmlgen.set_value('print_esmf', False)

    # overwrite user_nl_ufsatm changes
    nmlgen = nmlOverwrite(namelist_user, nmlgen)

    # remove parameters from the namelist based on the selected app
    remove_lst = [] 
    if "hafs" in app:
        remove_lst.extend(["imo", "jmo", "iau_offset", "ideflate", "nbits", "output_history", "write_dopost", "atm_coupling_interval_sec", "fhrot"])
    else:
        remove_lst.extend(["cen_lat", "cen_lon", "dlat", "dlon", "lat1", "lat2", "lon1", "lon2"])
        if "mrweather" in app:
            remove_lst.append("atm_coupling_interval_sec")
            remove_lst.append("fhrot")

    for var in remove_lst:
        nmlgen._namelist.delete_variable("_no_group_var", var)

    #----------------------------------------------------
    # Write out model.configure namelist
    #----------------------------------------------------
    model_config_file = os.path.join(confdir, "model_configure")
    nmlgen.write_nuopc_config_file(model_config_file, data_list_path)

# pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements
####################################################################################
def _create_namelist_chgres(case, confdir, config, infile, nmlgen, nmlgen_def, nmlgen_input, namelist_user, input_type):
####################################################################################
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    #----------------------------------------------------
    # Clear out old data.
    #----------------------------------------------------
    data_list_path = os.path.join(case.get_case_root(), "Buildconf", "ufsatm.input_data_list")

    #----------------------------------------------------
    # Initialize namelist defaults
    #----------------------------------------------------
    nmlgen.init_defaults(infile, config)
    nmlgen_def.init_defaults(infile, {})

    #----------------------------------------------------
    # Modify namelist defaults
    #----------------------------------------------------
    rundir = case.get_value("RUNDIR")
    atm_grid = case.get_value("ATM_GRID").replace('r', '')

    # Set grid
    nmlgen.set_value('mosaic_file_target_grid', os.path.join("INPUT",atm_grid+"_mosaic.nc"))

    # Set vertical layers
    levp = nmlgen_input.get_value("levp")
    if levp is None:
        expect(False, "Number of vertical layer 'levp' must be a valid number! levp = {}".format(levp))
    else:
        hyblev_file = os.path.join(rundir,"INPUT","global_hyblev.l{}.txt".format(levp))
        if not os.path.exists(hyblev_file):
            logger.warning("WARNING: {} does not exist, will attempt to download.".format(hyblev_file))
        else:
            nmlgen.set_value('vcoord_file_target_grid', os.path.join("INPUT","global_hyblev.l{}.txt".format(levp)))

    # Set location of fixed surface files
    nmlgen.set_value('fix_dir_target_grid', os.path.join("INPUT"))

    # Set cycle
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod//3600)

    nmlgen.set_value('cycle_mon', int(mm))
    nmlgen.set_value('cycle_day', int(dd))
    nmlgen.set_value('cycle_hour', int(hh))

    # Input directory
    icdir = os.path.join(case.get_value("DIN_LOC_IC"),"{:04d}{:02d}".format(yyyy,mm),"{:04d}{:02d}{:02d}".format(yyyy,mm,dd))
    nmlgen.set_value('data_dir_input_grid', icdir)

    # Set input_type specific options
    nmlgen.set_value('input_type', input_type)
    if input_type == 'gaussian_nemsio':
        nmlgen.set_value('atm_files_input_grid', ['atm.input.ic.nemsio'])
        nmlgen.set_value('sfc_files_input_grid', ['sfc.input.ic.nemsio'])
        tracers = ['sphum','liq_wat','o3mr','ice_wat','rainwat','snowwat','graupel']
        nmlgen.set_value('tracers', tracers)
        tracers_input = ['spfh','clwmr','o3mr','icmr','rwmr','snmr','grle']
        nmlgen.set_value('tracers_input', tracers_input)
        nmlgen.set_value('convert_nst', True)
    elif input_type == 'grib2':
        nmlgen.set_value('grib2_file_input_grid','atm.input.ic.grb2')
        nmlgen.set_value('varmap_file', 'GFSphys_var_map.txt')
    elif input_type == 'gaussian_netcdf':
        nmlgen.set_value('orog_dir_target_grid', os.path.join("INPUT"))
        oro_files = []
        for tile in range(1,7):
            oro_files.append("oro_data.tile{}.nc".format(tile))
        nmlgen.set_value('orog_files_target_grid', oro_files)
        nmlgen.set_value('atm_files_input_grid', ['atm.input.ic.nc'])
        nmlgen.set_value('sfc_files_input_grid', ['sfc.input.ic.nc'])
        nmlgen.set_value('convert_atm', True)
        nmlgen.set_value('convert_sfc', True)
        nmlgen.set_value('convert_nst', True)
        tracers = ['sphum','liq_wat','o3mr','ice_wat','rainwat','snowwat','graupel']
        nmlgen.set_value('tracers', tracers)
        tracers_input = ['spfh','clwmr','o3mr','icmr','rwmr','snmr','grle']
        nmlgen.set_value('tracers_input', tracers_input)

    # Check/correct task count used for pre-processing
    mach = case.get_value("MACH") 
    atm_grid = case.get_value("ATM_GRID").replace('r', '')
    tasks_per_node = 12
    task_count = {"C96"  : 1*tasks_per_node, # 1 nodes
                  "C192" : 1*tasks_per_node, # 1 nodes
                  "C384" : 2*tasks_per_node, # 2 nodes
                  "C768" : 3*tasks_per_node} # 3 nodes

    if atm_grid in task_count.keys():
        case.set_value("tasks_per_node", str(tasks_per_node), subgroup="case.chgres")
        case.set_value("task_count", str(task_count[atm_grid]), subgroup="case.chgres")
        logger.info("CHGRES task count and tasks per node are modified! task_count = {} tasks_per_node = {}".format(task_count[atm_grid], tasks_per_node))
        case.flush()

    # remove namelist options that has no specialization
    nmlgen_mod = nmlKeepChangedOnly(nmlgen_def, nmlgen, namelist_user)

    # overwrite user_nl_ufsatm changes
    nmlgen_mod = nmlOverwrite(namelist_user, nmlgen_mod)

    #----------------------------------------------------
    # Write out namelist groups
    #----------------------------------------------------
    groups=['config']

    namelist_file = os.path.join(confdir, "config.nml")
    nmlgen_mod.write_output_file(namelist_file, data_list_path, groups=groups, sorted_groups=False)

# pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements
####################################################################################
def _create_namelist_ncep_post(case, confdir, config, infile, nmlgen, nmlgen_model_configure, namelist_user):
####################################################################################
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    #----------------------------------------------------
    # Clear out old data.
    #----------------------------------------------------
    data_list_path = os.path.join(case.get_case_root(), "Buildconf", "ufsatm.input_data_list")

    #----------------------------------------------------
    # Initialize namelist defaults
    #----------------------------------------------------
    nmlgen.init_defaults(infile, config)

    #----------------------------------------------------
    # Write out namelist groups
    #----------------------------------------------------
    groups=['nampgb']

    # Make input format for post-processing consistent with model output
    output_file = nmlgen_model_configure.get_value('output_file')
    if 'netcdf' in output_file:
        nmlgen.set_value('ioform', 'netcdf')
    elif 'nemsio' in output_file:
        nmlgen.set_value('ioform', 'binarynemsiompiio')

    # Query start date and time
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yy = run_start_date[0]
    mm = run_start_date[1]
    dd = run_start_date[2]
    run_start_tod = int(case.get_value('START_TOD'))
    hh = run_start_tod//3600
    mi = (run_start_tod-hh*3600)//60
    ss = run_start_tod-hh*3600-mi*60

    # Overwrite user_nl_ufsatm changes
    nmlgen = nmlOverwrite(namelist_user, nmlgen)

    # Create namelist file for first time step / template script will update it for specific date
    namelist_file = os.path.join(confdir, "itag.tmp")
    nmlgen.write_output_file(namelist_file, data_list_path, groups=groups, sorted_groups=False)

    # Add header section to namelist
    with open(namelist_file, 'r+') as f:
        content = f.read()
        f.seek(0,0)
        f.write(nmlgen.get_value('filename')+"\n")
        f.write(nmlgen.get_value('ioform')+"\n")
        f.write(nmlgen.get_value('outform')+"\n")
        f.write("{}-{}-{}".format(yy,mm,dd)+"_"+"{hh:02d}:{mm:02d}:{ss:02d}".format(hh=hh,mm=mi,ss=ss)+"\n")
        f.write(nmlgen.get_value('modelname')+"\n")
        f.write(nmlgen.get_value('filenameflux')+"\n")
        f.write(content)

    # Check/correct task count used for post-processing
    atm_grid = case.get_value("ATM_GRID").replace('r', '')
    mach = case.get_value("MACH")

    # Specific fix for Stampede2
    tasks_per_node = int(case.get_value("MAX_TASKS_PER_NODE"))
    if ("C384" in atm_grid or "C768" in atm_grid) and "stampede2" in mach:
        tasks_per_node = 24
        case.set_value("tasks_per_node", str(tasks_per_node), subgroup="case.gfs_post")
        case.flush()
        logger.info("NCEP Post tasks per node is changed to {}!".format(tasks_per_node))

    task_count = {"C96": tasks_per_node, "C192": tasks_per_node, "C384": tasks_per_node*2, "C768": tasks_per_node*4}
    if atm_grid in task_count.keys():
        case.set_value("task_count", str(task_count[atm_grid]), subgroup="case.gfs_post")
        case.flush()
        logger.info("NCEP Post task count is changed to {}!".format(task_count[atm_grid]))

###############################################################################
def buildnml(case, caseroot, compname):
###############################################################################

    # Date stamp for input directory
    # TODO: might be a xmlchange option
    datestamp="20191213"

    # Build the component namelist
    if compname != "ufsatm":
        raise AttributeError
    srcroot = case.get_value("SRCROOT")
    rundir  = case.get_value("RUNDIR")

    # determine the confdir directory
    confdir = os.path.join(caseroot,"Buildconf","ufsatmconf")
    if not os.path.isdir(confdir):
        os.makedirs(confdir)

    # determine CaseDocs
    casedocsdir = os.path.join(caseroot,"CaseDocs")

    # query compset
    compset = case.get_value("COMPSET")

    # dictionary for app, compset/s pairs
    compset_dict = {"mrweather": ["FCST_ufsatm%v15p2_SLND_SICE_SOCN_SROF_SGLC_SWAV", \
                                  "FCST_ufsatm%v16beta_SLND_SICE_SOCN_SROF_SGLC_SWAV"], \
                    "hafs"     : ["FCST_ufsatm%v0nocp_SLND_SICE_SOCN_SROF_SGLC_SWAV"]
                   }

    # determine app using compset, the default value is s2s
    global app
    app = "s2s"
    for key, value in compset_dict.items():
        for item in value:
            if item in compset:
                app = key

    # query used CCPP suite
    ccpp_suite = case.get_value("CCPP_SUITES")

    config = {}
    config['app'] = app
    config['ccpp'] = ccpp_suite
    config['hgrid'] = case.get_value("ATM_GRID")
    config['ntasks'] = str(case.get_value("ATM_NTASKS"))

    # path for namelist files
    namelist_xml_dir = os.path.join(srcroot, "src", "model", "FV3", "cime", "cime_config")

    # set namelist definition file
    definition_file = [os.path.join(namelist_xml_dir, "namelist_definition_ufsatm.xml")]

    # create namelist_infile using user_nl_file as input
    user_nl_file = os.path.join(caseroot, "user_nl_ufsatm")
    expect(os.path.isfile(user_nl_file),
           "Missing required user_nl_file %s " %(user_nl_file))
    infile = os.path.join(confdir, "namelist_infile")
    create_namelist_infile(case, user_nl_file, infile)
    namelist_infile = [infile]

    # read user modified namelist values
    namelist_user = parse(infile)

    #----------------------------------------------------
    # Set case start date
    #----------------------------------------------------
    is_restart =  case.get_value("CONTINUE_RUN")

    if is_restart: # Warm start
        # List coupler.res files
        res_files = []
        for _, _, files in os.walk(os.path.join(rundir,"RESTART")):
            for filename in files:
                if "coupler.res" in filename:
                    res_files.append(filename)
        res_files.sort()

        # If we have multiple, get time stamp
        nres = len(res_files)
        if nres > 1:
            prefix = res_files[nres-2].replace("coupler.res", "")
        else:
            # Query restart file
            rst_file = os.path.join(rundir, "RESTART", "coupler.res")
            if not os.path.isfile(rst_file):
                expect(False, "{} is missing. It is required to restart the model!".format(os.path.join(rundir,"RESTART","coupler.res")))
            else:
                with open(rst_file, 'r') as f:
                    lines = f.readlines()
                    line = [int(x) for x in lines[2].split()[0:6]]
                    prefix = "{:04d}".format(line[0])
                    prefix = prefix + "".join("{:02d}".format(x) for x in line[1:3])
                    prefix = prefix + "." + "".join("{:02d}".format(x) for x in line[3:7])
                    prefix = prefix + "."

            # Copy files by adding prefix
            # Tiled files
            tile_max = 7
            if "hafs" in app:
                # Regional case has only one tile (1)
                tile_max = 2

            lst = ["fv_core.res", "fv_srf_wnd.res", "fv_tracer.res", "phy_data", "sfc_data"]
            for f in lst:
                for tile in range(1,tile_max):
                    if "_data" in f and "hafs" in app:
                        src_file = os.path.join(rundir,"RESTART","{}.nc".format(f))
                        tgt_file = os.path.join(rundir,"RESTART","{}.nc".format(prefix + f))
                    else:
                        src_file = os.path.join(rundir,"RESTART","{}.tile{}.nc".format(f,tile))
                        tgt_file = os.path.join(rundir,"RESTART","{}.tile{}.nc".format(prefix + f,tile))
                    if not os.path.isfile(src_file):
                        expect(False, "{} is missing.".format(src_file))
                    else:
                        safe_copy(src_file, tgt_file)

            # Others
            lst = ["fv_core.res.nc", "coupler.res"]
            for f in lst:
                src_file = os.path.join(rundir,"RESTART",f)
                tgt_file = os.path.join(rundir,"RESTART",prefix + f)
                if not os.path.isfile(src_file):
                    expect(False, "{} is missing.".format(src_file))
                else:
                    safe_copy(src_file, tgt_file)
    else:
        prefix = ""

    #----------------------------------------------------
    # Query jobs
    #----------------------------------------------------

    env_workflow = case.get_env("workflow")
    jobs = env_workflow.get_jobs()

    #----------------------------------------------------
    # Set input type
    #----------------------------------------------------

    # Query date and time 
    yyyy, mm, dd, hh = date_yyyymmddhh(case)

    # Check input directory to find input_file format (same folder could have both .nemsio and .grb2)
    if "case.chgres" in jobs:
        # Query input directory
        if "hafs" in app:
            din_loc_root = case.get_value("DIN_LOC_ROOT")
            icdir = os.path.join(din_loc_root,"regional","bcond","{:04d}{:02d}{:02d}{:02d}".format(yyyy,mm,dd,hh))
        else:
            icdir = os.path.join(case.get_value("DIN_LOC_IC"),"{:04d}{:02d}".format(yyyy,mm),"{:04d}{:02d}{:02d}".format(yyyy,mm,dd))

        logger.info("Checking {} directory to find raw input files ...".format(icdir))
        input_dict = {}
        if os.path.exists(icdir):
            for f in os.listdir(icdir):
                if "atm.input.ic.nemsio" in f or "sfc.input.ic.nemsio" in f:
                    if 'gaussian_nemsio' in input_dict.keys():
                        input_dict['gaussian_nemsio'].append(f)
                    else:
                        input_dict.update({'gaussian_nemsio':[f]})
                    logger.info("Found '{}' in input directory".format(f))
                elif "atm.input.ic.grb2" in f:
                    if 'grib2' in input_dict.keys():
                        input_dict['grib2'].append(f)
                    else:
                        input_dict.update({'grib2':[f]})
                    logger.info("Found '{}' in input directory".format(f))
                elif "atm.input.ic.nc" in f or "sfc.input.ic.nc" in f:
                    if 'gaussian_netcdf' in input_dict.keys():
                        input_dict['gaussian_netcdf'].append(f)
                    else:
                        input_dict.update({'gaussian_netcdf':[f]})
                    logger.info("Found '{}' in input directory".format(f))

        # Remove duplicates
        input_type_lst = list(set(input_dict.keys()))

        # Check user_nl_ufsatm for input_type and force buildnml to use it
        input_type_user = namelist_user.get_value('input_type')[0]
        if input_type_user:
            input_type_lst = list([input_type_user.strip('\"').strip('\'')])

        # If input_type is empty throw error
        if not input_type_lst:
            logger.info("No input file/s found locally! {}".format(icdir))
            logger.info("Please download raw initial condition and rename the file/s using following convention:")
            logger.info("\t NEMSIO: atm.input.ic.nemsio and sfc.input.ic.nemsio")
            logger.info("\t GRIB2 : atm.input.ic.grb2")
            logger.info("\t NetCDF: atm.input.ic.nc and sfc.input.ic.nc")
            expect(False, "Exiting ...")
        else:
            # Set grib2 as default, if we have multiple data formats such as both .grb2 and .nc
            if len(input_type_lst) > 1:
                input_type = "grib2"
            else:
                input_type = input_type_lst[0]

            logger.info("CHGRES namelist option 'input_type' is set to {}".format(input_type))

            # Check defined input_type exists in the dictionary
            if input_type in input_dict.keys():
                # Check the input file matched with the input_type
                for f in input_dict[input_type]:
                    if not os.path.exists(os.path.join(icdir,f)):
                        expect(False, "{} not found!".format(os.path.join(icdir,f)))
                    else:
                        logger.info("CHGRES will use {}".format(os.path.join(icdir,f)))
            else:
                # If there is no file with given input_type, print information and exit
                if "gaussian_nemsio" == input_type:
                    logger.info("Please download raw initial condition in NEMSIO format rename it as atm.input.ic.nemsio and sfc.input.ic.nemsio")
                elif "grib2" == input_type:
                    logger.info("Please download raw initial condition in GRIB2 format and rename it as atm.input.ic.grb2")
                elif "gaussian_netcdf" == input_type:
                    logger.info("Please download raw initial condition in NETCDF format and rename it as atm.input.ic.nc and sfc.input.ic.nc")
                else:
                    logger.info("Namelist option input_type can be 'gaussian_nemsio', 'grib2' or 'gaussian_netcdf'.")
                expect(False, "Exiting ...")
    else:
       input_type = None 
       logger.info("CHGRES is not the part of the workflow. There is no need to set 'input_type'!")

    #----------------------------------------------------
    # Namelist generator for atmosphere model (model_configure)
    #----------------------------------------------------

    # allow using user provided model_configure
    auto_namelist_file = os.path.join(confdir,"model_configure")
    user_namelist_file = os.path.join(caseroot,"SourceMods","src.ufsatm","model_configure")
    if os.path.isfile(user_namelist_file):
        logger.info("Using user provided model_configure file {}".format(user_namelist_file))
        if os.path.isfile(auto_namelist_file):
            os.remove(auto_namelist_file)
        safe_copy(user_namelist_file,auto_namelist_file)
        # TODO: need to implement nuopc parser to create nmlgen_model_configure from existing model_configure
        # CIME issue: https://github.com/ESMCI/cime/issues/3705
        # nmlgen_model_configure = parse_nuopc(auto_namelist_file)
    else:
        logger.info("Using auto-generated model_configure file {}".format(auto_namelist_file))

        # create the namelist generator object
        nmlgen_model_configure = NamelistGenerator(case, definition_file)

        # create namelist model_configure
        _create_namelist_model_configure(case, confdir, config, namelist_infile, nmlgen_model_configure, namelist_user, prefix)

    # copy namelist files to rundir
    if os.path.isdir(rundir):
        file1 = os.path.join(confdir, "model_configure")
        file2 = os.path.join(rundir, "model_configure")
        logger.debug("ufsatm configuration copy: file1 %s file2 %s " %(file1, file2))
        safe_copy(file1, file2)

    #----------------------------------------------------
    # Namelist generator for atmosphere model (input.nml.fv3)
    #----------------------------------------------------

    # allow using user provided atm_in aka input.nml
    auto_namelist_file = os.path.join(confdir,"atm_in")
    user_namelist_file = os.path.join(caseroot,"SourceMods","src.ufsatm","atm_in")
    if os.path.isfile(user_namelist_file):
        logger.info("Using user provided atm_in file {}".format(user_namelist_file))
        if os.path.isfile(auto_namelist_file):
            os.remove(auto_namelist_file)
        safe_copy(user_namelist_file,auto_namelist_file)
        nmlgen_input = parse(auto_namelist_file)
    else:
        logger.info("Using auto-generated atm_in file {}".format(auto_namelist_file))

        # create the namelist generator object
        nmlgen_input = NamelistGenerator(case, definition_file)

        # create copy of nmlgen_input to modify
        nmlgen_input_def = NamelistGenerator(case, definition_file)

        # create namelist input.nml.fv3
        _create_namelist_input(case, confdir, config, namelist_infile, nmlgen_model_configure, nmlgen_input, nmlgen_input_def, namelist_user, datestamp, input_type)

    # copy namelist files to rundir
    if os.path.isdir(rundir):
        file1 = os.path.join(confdir, "atm_in")
        file2 = os.path.join(rundir, "atm_in")
        logger.debug("ufsatm namelist copy: file1 %s file2 %s " %(file1, file2))
        safe_copy(file1, file2)

    # link file
    logger.info("\tLinking input namelist for ufsatm")
    if not os.path.exists(rundir):
        expect(False, "Couldn't find run direcory " + rundir)
    if "MOM6" not in compset:
        # there is no need to merge with MOM6 namelist, just link it
        symlink_force(file2, os.path.join(rundir, "input.nml"))
    else:
        symlink_force(file2, os.path.join(rundir, "input.nml.fv3"))

    #----------------------------------------------------
    # Copy module_configure and input.nml to CaseDocs
    #----------------------------------------------------

    if os.path.isdir(casedocsdir):
        file1 = os.path.join(confdir, "model_configure")
        file2 = os.path.join(casedocsdir, "model_configure")
        safe_copy(file1, file2)

        file1 = os.path.join(confdir, "atm_in")
        file2 = os.path.join(casedocsdir, "input.nml.fv3")
        safe_copy(file1, file2)

    #----------------------------------------------------
    # Copy/link input files
    #----------------------------------------------------

    prep_input(case, casedocsdir, datestamp, nmlgen_input, prefix, input_type)

    #----------------------------------------------------
    # Namelist generator for chgres
    #----------------------------------------------------

    if "case.chgres" in jobs and not is_restart:
        # create the namelist generator object
        nmlgen_chgres = NamelistGenerator(case, definition_file)

        # create copy of nmlgen_chgres to modify
        nmlgen_chgres_def = NamelistGenerator(case, definition_file)

        # create namelist config.nml
        _create_namelist_chgres(case, confdir, config, namelist_infile, nmlgen_chgres, nmlgen_chgres_def, nmlgen_input, namelist_user, input_type)

        # copy namelist to rundir
        if os.path.isdir(rundir):
            file1 = os.path.join(confdir, "config.nml")
            file2 = os.path.join(rundir, "config.nml")
            logger.debug("CHGRES configuration copy: file1 %s file2 %s " %(file1, file2))
            safe_copy(file1, file2)

        # copy namelist to CaseDocs
        if os.path.isdir(casedocsdir):
            file1 = os.path.join(confdir, "config.nml")
            file2 = os.path.join(casedocsdir, "config.nml")
            safe_copy(file1, file2)

    #----------------------------------------------------
    # Namelist generator for ncep_post
    #----------------------------------------------------

    if "case.gfs_post" in jobs:
        # create the namelist generator object
        nmlgen_ncep_post = NamelistGenerator(case, definition_file)

        # remove old configuration files
        for f in os.listdir(rundir):
            if "itag" in f:
                logger.warning("removing file {}".format(f))
                os.remove(os.path.join(rundir,f))

        # create namelist model_configure
        _create_namelist_ncep_post(case, confdir, config, namelist_infile, nmlgen_ncep_post, nmlgen_model_configure, namelist_user)

        # copy namelist to rundir
        if os.path.isdir(rundir):
            file1 = os.path.join(confdir, "itag.tmp")
            file2 = os.path.join(rundir, "itag.tmp")
            logger.debug("NCEP_POST configuration copy: file1 %s file2 %s " %(file1, file2))
            safe_copy(file1, file2)

        # copy namelist to CaseDocs
        if os.path.isdir(casedocsdir):
            file1 = os.path.join(confdir, "itag.tmp")
            file2 = os.path.join(casedocsdir, "itag.tmp")
            safe_copy(file1, file2)

    return

###############################################################################
def _main_func():

    caseroot = parse_input(sys.argv)
    with Case(caseroot) as case:
        buildnml(case, caseroot, "ufsatm")

if __name__ == "__main__":
    _main_func()

119c59079ada672e111523c83bbcc396dafdae64
<mask token>


def date_yyyymmddhh(case):
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod) // 3600
    return yyyy, mm, dd, hh


def factors(n):
    return set(x for tup in ([i, n // i] for i in range(1, int(n ** 0.5) + 
        1) if n % i == 0) for x in tup)


def checkList(lst):
    ele = lst[0]
    chk = True
    for item in lst:
        if ele != item:
            chk = False
            break
    return chk


def active_components(case):
    compset = case.get_value('COMPSET')
    components = compset.split('_')[1:]
    components = [x for x in components if 'S' not in x[0]]
    return components


def list_to_value(val):
    if isinstance(val, list):
        if len(val) == 1:
            return val[0]
        else:
            return val
    else:
        return val


def nmlKeepChangedOnly(nmlgen_ini, nmlgen_cur, namelist_user):
    nmlgen_ret = copy.copy(nmlgen_cur)
    for nml in nmlgen_cur._namelist.get_group_names():
        for var in nmlgen_cur.get_group_variables(nml):
            matched = False
            lst = namelist_user.get_value(var)
            if any(len(item) > 0 for item in lst):
                continue
            lst = ['ccpp_suite', 'nstf_name']
            if var in lst:
                continue
            var_type, _, var_size = nmlgen_cur._definition.split_type_string(
                var)
            ini = nmlgen_ini.get_value(var)
            cur = nmlgen_cur.get_value(var)
            if var_size > 1:
                var_act = [None] * var_size
                var_def = [None] * var_size
                var_act_len = len(cur)
                var_act[0:var_act_len] = [x for x in cur]
                var_def_len = len(ini)
                var_def[0:var_def_len] = [x for x in ini]
                if var_type == 'character' or var_type == 'logical':
                    var_act = [('xyz' if x is None else x.lower()) for x in
                        var_act]
                    var_def = [('xyz' if x is None else x.lower()) for x in
                        var_def]
                    matched = all(y.startswith(x) for x, y in zip(var_act,
                        var_def))
                elif var_type == 'integer':
                    var_act = [(-99 if x is None else int(x)) for x in var_act]
                    var_def = [(-99 if x is None else int(x)) for x in var_def]
                    matched = all(x == y for x, y in zip(var_act, var_def))
                elif var_type == 'real':
                    var_act = [(-99.9 if x is None else float(eval(x.lower(
                        ).replace('d', 'e')))) for x in var_act]
                    var_def = [(-99.9 if x is None else float(eval(x.lower(
                        ).replace('d', 'e')))) for x in var_def]
                    matched = all(x == y for x, y in zip(var_act, var_def))
            elif var_type == 'character' or var_type == 'logical':
                if not ini.strip() == '':
                    if cur.startswith(ini):
                        matched = True
                elif ini.strip() == '' and cur.strip() == '':
                    matched = True
            elif var_type == 'integer':
                if ini:
                    if int(cur) == int(ini):
                        matched = True
                elif not (ini and cur):
                    matched = True
            elif var_type == 'real' and ini and cur:
                if float(eval(ini.lower().replace('d', 'e'))) == float(eval
                    (cur.lower().replace('d', 'e'))):
                    matched = True
            if matched:
                nmlgen_ret._namelist.delete_variable(nml, var)
    return nmlgen_ret


def nmlOverwrite(nmlgen_src, nmlgen_dst):
    nmlgen_ret = copy.copy(nmlgen_dst)
    for nml in nmlgen_src.get_group_names():
        for var in nmlgen_src.get_group_variables(nml):
            nmlgen_ret.set_value(var, nmlgen_src.get_value(var))
    return nmlgen_ret


def prep_input(case, casedocsdir, datestamp, nmlgen_input, prefix, input_type):
    casename = case.get_value('CASE')
    Buildconf = case.get_value('CASEBUILD')
    rundir = case.get_value('RUNDIR')
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    testcase = case.get_value('TEST')
    din_loc_root = case.get_value('DIN_LOC_ROOT')
    din_loc_ic = case.get_value('DIN_LOC_IC')
    is_restart = case.get_value('CONTINUE_RUN')
    srcroot = case.get_value('SRCROOT')
    caseroot = case.get_value('CASEROOT')
    env_workflow = case.get_env('workflow')
    jobs = env_workflow.get_jobs()
    new_o3forc = True
    if 's2s' in app:
        new_o3forc = False
    yyyy, mm, dd, hh = date_yyyymmddhh(case)
    icdir = os.path.join(din_loc_ic, '{:04d}{:02d}'.format(yyyy, mm),
        '{:04d}{:02d}{:02d}'.format(yyyy, mm, dd))
    if 'hafs' in app:
        dir_fix_am = os.path.join(din_loc_root, 'regional', 'fix_am')
        dir_fix_ufsatm = os.path.join(din_loc_root, 'regional', 'fix', atm_grid
            )
        dir_fix_parm = os.path.join(din_loc_root, 'parm')
        dir_fix_lib = os.path.join(din_loc_root, 'lib')
        dir_bdy = os.path.join(din_loc_root, 'regional', 'bcond',
            '{:04d}{:02d}{:02d}{:02d}'.format(int(yyyy), int(mm), int(dd), hh))
    else:
        dir_fix_am = os.path.join(din_loc_root, 'global', 'fix',
            'fix_am.v{}'.format(datestamp))
        if 's2s' in app:
            dir_fix_ufsatm = os.path.join(din_loc_root, 'global', 'fix',
                'fix_fv3_gmted2010.v{}'.format(datestamp), '{}_s2s'.format(
                atm_grid))
        else:
            dir_fix_ufsatm = os.path.join(din_loc_root, 'global', 'fix',
                'fix_fv3_gmted2010.v{}'.format(datestamp), atm_grid)
        dir_fix_parm = os.path.join(din_loc_root, 'parm')
        dir_fix_lib = os.path.join(din_loc_root, 'lib')
    input_files = dict()
    if 'hafs' in app:
        halo_lst = ['halo0', 'halo3', 'halo4']
        for halo in halo_lst:
            input_files['grid_tile7_{}'.format(halo)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_grid.tile7.{}.nc'.format(halo)
                ), 'ln', os.path.join('INPUT', atm_grid +
                '_grid.tile7.{}.nc'.format(halo))]
            input_files['orog_tile7_{}'.format(halo)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_oro_data.tile7.{}.nc'.format(
                halo)), 'ln', os.path.join('INPUT', atm_grid +
                '_oro_data.tile7.{}.nc'.format(halo))]
        input_files['oro_data_tile7h0'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_oro_data.tile7.halo0.nc'), 'ln', os.path.join(
            'INPUT', 'oro_data.nc')]
        input_files['oro_data_tile7h4'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_oro_data.tile7.halo4.nc'), 'ln', os.path.join(
            'INPUT', 'oro_data.tile7.halo4.nc')]
        input_files['grid_tile7h3'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_grid.tile7.halo3.nc'), 'ln', os.path.join('INPUT',
            atm_grid + '_grid.tile7.nc')]
        input_files['grid_tile7h0'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_grid.tile7.halo0.nc'), 'ln', os.path.join('INPUT',
            'grid.tile7.halo0.nc')]
        input_files['grid_tile7h4'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_grid.tile7.halo4.nc'), 'ln', os.path.join('INPUT',
            'grid.tile7.halo4.nc')]
    else:
        for tile in range(1, 7):
            input_files['grid_tile{}'.format(tile)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_grid.tile{}.nc'.format(tile)),
                'ln', os.path.join('INPUT', atm_grid + '_grid.tile{}.nc'.
                format(tile))]
            input_files['orog_data{}'.format(tile)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_oro_data.tile{}.nc'.format(
                tile)), 'ln', os.path.join('INPUT', 'oro_data.tile{}.nc'.
                format(tile))]
    if 's2s' in app:
        input_files['grid_spec'] = [os.path.join(icdir, 'grid_spec.nc'),
            'ln', os.path.join('INPUT', 'grid_spec.nc')]
    else:
        input_files['grid_spec'] = [os.path.join(dir_fix_ufsatm, atm_grid +
            '_mosaic.nc'), 'ln', os.path.join('INPUT', 'grid_spec.nc')]
    input_files['grid_mosaic'] = [os.path.join(dir_fix_ufsatm, atm_grid +
        '_mosaic.nc'), 'ln', os.path.join('INPUT', atm_grid + '_mosaic.nc')]
    iaer = nmlgen_input.get_value('iaer')
    ico2 = nmlgen_input.get_value('ico2')
    if new_o3forc:
        input_files['o3forc'] = [os.path.join(dir_fix_am,
            'ozprdlos_2015_new_sbuvO3_tclm15_nuchem.f77'), 'ln',
            'global_o3prdlos.f77']
    else:
        input_files['o3forc'] = [os.path.join(dir_fix_am,
            'global_o3prdlos.f77'), 'ln', 'global_o3prdlos.f77']
    input_files['h2oforc'] = [os.path.join(dir_fix_am,
        'global_h2o_pltc.f77'), 'ln', 'global_h2oprdlos.f77']
    input_files['solcnst'] = [os.path.join(dir_fix_am,
        'global_solarconstant_noaa_an.txt'), 'ln', 'solarconstant_noaa_an.txt']
    input_files['sfcemis'] = [os.path.join(dir_fix_am,
        'global_sfc_emissivity_idx.txt'), 'ln', 'sfc_emissivity_idx.txt']
    input_files['co2hist'] = [os.path.join(dir_fix_am,
        'global_co2historicaldata_glob.txt'), 'ln',
        'co2historicaldata_glob.txt']
    input_files['co2mont'] = [os.path.join(dir_fix_am, 'co2monthlycyc.txt'),
        'ln', 'co2monthlycyc.txt']
    ico2 = list_to_value(ico2)
    if ico2 and int(ico2) > 0:
        yy = int(time.strftime('%Y,%m,%d,%H,%M,%S').split(',')[0])
        for year in range(2009, yy + 1):
            input_files['co2his_{}'.format(year)] = [os.path.join(
                dir_fix_am, 'fix_co2_proj',
                'global_co2historicaldata_{}.txt'.format(year)), 'ln',
                'co2historicaldata_{}.txt'.format(year)]
    input_files['clmaero'] = [os.path.join(dir_fix_am,
        'global_climaeropac_global.txt'), 'ln', 'aerosol.dat']
    iaer = list_to_value(iaer)
    if iaer and int(iaer) > 0 and 'hafs' not in app:
        for year in range(1850, 2000, 10):
            input_files['volaer_{}'.format(year)] = [os.path.join(
                dir_fix_am, 'global_volcanic_aerosols_{}-{}.txt'.format(
                year, year + 9)), 'ln', 'volcanic_aerosols_{}-{}.txt'.
                format(year, year + 9)]
    for var in nmlgen_input.get_group_variables('namsfc'):
        var_str = str(nmlgen_input.get_value(var)).replace("'", '').strip()
        if not var_str.startswith('igbp') and var_str.endswith('.grb'):
            input_files[var] = [var_str, 'ln', os.path.basename(var_str)]
    nceplibs_dir = os.environ.get('NCEPLIBS_DIR')
    if 'case.chgres' in jobs:
        expect(nceplibs_dir,
            'NCEPLIBS_DIR environment variable need to be set to process raw input data!'
            )
        expect(os.path.exists(nceplibs_dir), 'NCEPLIBS_DIR {} not found'.
            format(nceplibs_dir))
    if 'case.chgres' in jobs:
        for tile in range(1, 7):
            for f in ['facsf', 'maximum_snow_albedo', 'slope_type',
                'snowfree_albedo', 'soil_type', 'substrate_temperature',
                'vegetation_greenness', 'vegetation_type']:
                input_files['{}{}'.format(f, tile)] = [os.path.join(
                    dir_fix_ufsatm, 'fix_sfc', atm_grid + '.{}.tile{}.nc'.
                    format(f, tile)), 'ln', os.path.join('INPUT', atm_grid +
                    '.{}.tile{}.nc'.format(f, tile))]
        levp = nmlgen_input.get_value('levp')
        input_files['global_hyblev'] = [os.path.join(dir_fix_am,
            'global_hyblev.l{}.txt'.format(levp)), 'ln', os.path.join(
            'INPUT', 'global_hyblev.l{}.txt'.format(levp))]
        input_files['var_map'] = [os.path.join(nceplibs_dir, 'share',
            'GFSphys_var_map.txt'), 'ln', 'GFSphys_var_map.txt']
    if 'case.gfs_post' in jobs:
        src_file = os.path.join(caseroot, 'SourceMods', 'src.ufsatm',
            'postxconfig-NT-GFS.txt')
        dst_file = os.path.join(rundir, 'postxconfig-NT-GFS.txt')
        if os.path.isfile(dst_file):
            os.remove(dst_file)
        if os.path.isfile(src_file):
            safe_copy(src_file, dst_file)
            logger.info('Using {} for post-processing'.format(src_file))
        else:
            logger.info('Using {} for post-processing'.format(os.path.join(
                nceplibs_dir, 'share', 'postxconfig-NT-GFS.txt')))
            input_files['postxconfig1'] = [os.path.join(nceplibs_dir,
                'share', 'postxconfig-NT-GFS.txt'), 'ln',
                'postxconfig-NT-GFS.txt']
        src_file = os.path.join(caseroot, 'SourceMods', 'src.ufsatm',
            'postxconfig-NT-GFS-F00.txt')
        dst_file = os.path.join(rundir, 'postxconfig-NT-GFS-F00.txt')
        if os.path.isfile(dst_file):
            os.remove(dst_file)
        if os.path.isfile(src_file):
            safe_copy(src_file, dst_file)
            logger.info('Using {} for post-processing'.format(src_file))
        else:
            logger.info('Using {} for post-processing'.format(os.path.join(
                nceplibs_dir, 'share', 'postxconfig-NT-GFS-F00.txt')))
            input_files['postxconfig2'] = [os.path.join(nceplibs_dir,
                'share', 'postxconfig-NT-GFS-F00.txt'), 'ln',
                'postxconfig-NT-GFS-F00.txt']
        input_files['params_grib2_tbl'] = [os.path.join(nceplibs_dir,
            'share', 'params_grib2_tbl_new'), 'ln', 'params_grib2_tbl_new']
    if not os.path.exists(rundir):
        os.makedirs(rundir)
    logger.info('\tCreating INPUT and RESTART directory')
    for _dir in ('INPUT', 'RESTART'):
        fulldir = os.path.join(rundir, _dir)
        if not os.path.exists(fulldir):
            os.makedirs(fulldir)
    rundirdst = rundir
    if rundir.endswith('case2run'):
        rundirsrc = os.path.abspath(os.path.join(rundir, os.pardir))
    else:
        rundirsrc = rundir
    if is_restart:
        tile_max = 7
        if 'hafs' in app:
            tile_max = 2
        lst = ['fv_core.res', 'fv_srf_wnd.res', 'fv_tracer.res', 'phy_data',
            'sfc_data']
        lst = [(prefix + f) for f in lst]
        for f in lst:
            for tile in range(1, tile_max):
                if '_data' in f and 'hafs' in app:
                    src_file = os.path.join(rundir, 'RESTART', '{}.nc'.
                        format(f))
                    tgt_file = os.path.join(rundir, 'INPUT', '{}.nc'.format
                        (f.replace(prefix, '')))
                else:
                    src_file = os.path.join(rundir, 'RESTART',
                        '{}.tile{}.nc'.format(f, tile))
                    tgt_file = os.path.join(rundir, 'INPUT', '{}.tile{}.nc'
                        .format(f.replace(prefix, ''), tile))
                if not os.path.isfile(src_file):
                    expect(False, '{} is missing.'.format(src_file))
                else:
                    safe_copy(src_file, tgt_file)
        lst = ['fv_core.res.nc', 'coupler.res']
        lst = [(prefix + f) for f in lst]
        for f in lst:
            src_file = os.path.join(rundir, 'RESTART', f)
            tgt_file = os.path.join(rundir, 'INPUT', f.replace(prefix, ''))
            if not os.path.isfile(src_file):
                expect(False, '{} is missing.'.format(src_file))
            else:
                safe_copy(src_file, tgt_file)
        if 'hafs' in app:
            lst = ls_sorted_by_mtime(dir_bdy)
            lst.remove('gfs_ctrl.nc')
            lst.remove('gfs_data.tile7.nc')
            lst.remove('sfc_data.tile7.nc')
            for f in lst:
                src_file = os.path.join(rundirdst, 'INPUT', f)
                if os.path.isfile(src_file):
                    os.remove(src_file)
                time_step = int(f.split('.')[2])
                if time_step < nhours_fcst_pre:
                    continue
                else:
                    src_file = os.path.join(dir_bdy, f)
                    dst_file = os.path.join(rundir, 'INPUT',
                        'gfs_bndy.tile7.{:03d}.nc'.format(time_step -
                        nhours_fcst_pre))
                    symlink_force(src_file, dst_file)
    elif 'hafs' in app:
        lst = ls_sorted_by_mtime(dir_bdy)
        for f in lst:
            symlink_force(os.path.join(dir_bdy, f), os.path.join(rundirdst,
                'INPUT', f))
        symlink_force(os.path.join(dir_bdy, 'sfc_data.tile7.nc'), os.path.
            join(rundirdst, 'INPUT', 'sfc_data.nc'))
        symlink_force(os.path.join(dir_bdy, 'gfs_data.tile7.nc'), os.path.
            join(rundirdst, 'INPUT', 'gfs_data.nc'))
    else:
        lst = ['fv_core.res', 'fv_srf_wnd.res', 'fv_tracer.res', 'phy_data',
            'sfc_data']
        for f in lst:
            for tile in range(1, 7):
                src_file = os.path.join(rundir, 'INPUT', '{}.tile{}.nc'.
                    format(f, tile))
                if os.path.isfile(src_file):
                    logger.warning('removing file {}'.format(src_file))
                    os.remove(src_file)
        lst = ['fv_core.res.nc', 'coupler.res']
        for f in lst:
            src_file = os.path.join(rundir, 'INPUT', f)
            if os.path.isfile(src_file):
                logger.warning('removing file {}'.format(src_file))
                os.remove(src_file)
        src_file = os.path.join(rundirsrc, 'INPUT', '{}.'.format(atm_grid) +
            '{:04d}-{:02d}-{:02d}'.format(yyyy, mm, dd) +
            '_{:02d}.gfs_ctrl.nc'.format(hh))
        if os.path.isfile(src_file):
            symlink_force(src_file, os.path.join(rundirdst, 'INPUT',
                'gfs_ctrl.nc'))
            lst = ['gfs_data', 'sfc_data']
            for f in lst:
                for tile in range(1, 7):
                    src_file = os.path.join(rundirsrc, 'INPUT', '{}.'.
                        format(atm_grid) + '{:04d}-{:02d}-{:02d}'.format(
                        yyyy, mm, dd) + '_{:02d}.'.format(hh) + f +
                        '.tile{}.nc'.format(tile))
                    tgt_file = os.path.join(rundirdst, 'INPUT',
                        '{}.tile{}.nc'.format(f, tile))
                    if os.path.isfile(src_file):
                        symlink_force(src_file, tgt_file)
                        logger.info('chgres source file {} linked to {}'.
                            format(src_file, tgt_file))
                    else:
                        logger.debug('chgres source file {} not found'.
                            format(src_file))
        else:
            src_file = os.path.join(icdir, 'gfs_ctrl.nc')
            if os.path.isfile(src_file):
                symlink_force(src_file, os.path.join(rundirdst, 'INPUT',
                    'gfs_ctrl.nc'))
            else:
                logger.warning(
                    'initial condition file {} is required! Exiting ...'.
                    format(src_file))
            lst = ['gfs_data', 'sfc_data']
            for f in lst:
                for tile in range(1, 7):
                    src_file = os.path.join(icdir, f + '.tile{}.nc'.format(
                        tile))
                    tgt_file = os.path.join(rundirdst, 'INPUT',
                        '{}.tile{}.nc'.format(f, tile))
                    if os.path.isfile(src_file):
                        symlink_force(src_file, tgt_file)
                        logger.info('initial condition file {} linked to {}'
                            .format(src_file, tgt_file))
                    else:
                        logger.warning(
                            'initial condition file {} is required! Exiting ...'
                            .format(src_file))
    with open(os.path.join(Buildconf, 'ufsatm.input_data_list'), 'w'
        ) as input_data_list:
        for k, v in input_files.items():
            input_data_list.write(k + ' = ' + v[0] + '\n')
    for k, v in input_files.items():
        if not os.path.isfile(v[0]):
            logger.warning(
                'WARNING: data file {} not found, will attempt to download.'
                .format(v[0]))
        elif 'ln' in v[1]:
            symlink_force(v[0], os.path.join(rundir, v[2]))
        elif 'cp':
            safe_copy(v[0], os.path.join(rundir, v[2]))
    cimeroot_ufsatm = os.path.join(srcroot, 'src', 'model', 'FV3', 'cime',
        'cime_config')
    for f in ['data_table_{}'.format(app), 'diag_table_{}'.format(app),
        'field_table_{}'.format(app)]:
        if not os.path.isfile(os.path.join(rundir, f)):
            safe_copy(os.path.join(cimeroot_ufsatm, 'tables', f), os.path.
                join(rundir, f.replace('_{}'.format(app), '')))


def _create_namelist_input(case, confdir, config, infile, nmlgen_model,
    nmlgen_input, nmlgen_input_def, namelist_user, datestamp, input_type):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    nmlgen_input.init_defaults(infile, config)
    nmlgen_input_def.init_defaults(infile, {})
    din_loc_root = case.get_value('DIN_LOC_ROOT')
    if 'hafs' in app:
        dir_fix_am = os.path.join(din_loc_root, 'regional', 'fix_am')
    else:
        dir_fix_am = os.path.join(din_loc_root, 'global', 'fix',
            'fix_am.v{}'.format(datestamp))
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    res = int(atm_grid.replace('C', ''))
    jcap = 2 * res - 2
    lonb = 4 * res
    latb = 2 * res
    if 's2s' in app:
        if res == 96:
            jcap = 126
            lonb = 384
            latb = 190
    nmlgen_input.set_value('fnglac', value=os.path.join(dir_fix_am,
        'global_glacier.2x2.grb'))
    nmlgen_input.set_value('fnmxic', value=os.path.join(dir_fix_am,
        'global_maxice.2x2.grb'))
    nmlgen_input.set_value('fntsfc', value=os.path.join(dir_fix_am,
        'RTGSST.1982.2012.monthly.clim.grb'))
    nmlgen_input.set_value('fnsnoc', value=os.path.join(dir_fix_am,
        'global_snoclim.1.875.grb'))
    nmlgen_input.set_value('fnzorc', value='igbp')
    nmlgen_input.set_value('fnalbc2', value=os.path.join(dir_fix_am,
        'global_albedo4.1x1.grb'))
    nmlgen_input.set_value('fnaisc', value=os.path.join(dir_fix_am,
        'CFSR.SEAICE.1982.2012.monthly.clim.grb'))
    nmlgen_input.set_value('fntg3c', value=os.path.join(dir_fix_am,
        'global_tg3clim.2.6x1.5.grb'))
    nmlgen_input.set_value('fnvegc', value=os.path.join(dir_fix_am,
        'global_vegfrac.0.144.decpercent.grb'))
    if 'mrweather' in app:
        nmlgen_input.set_value('fnmskh', value=os.path.join(dir_fix_am,
            'global_slmask.t1534.3072.1536.grb'))
    else:
        nmlgen_input.set_value('fnmskh', value=os.path.join(dir_fix_am,
            'seaice_newland.grb'))
    nmlgen_input.set_value('fnvmnc', value=os.path.join(dir_fix_am,
        'global_shdmin.0.144x0.144.grb'))
    nmlgen_input.set_value('fnvmxc', value=os.path.join(dir_fix_am,
        'global_shdmax.0.144x0.144.grb'))
    nmlgen_input.set_value('fnslpc', value=os.path.join(dir_fix_am,
        'global_slope.1x1.grb'))
    nmlgen_input.set_value('fnalbc', value=os.path.join(dir_fix_am,
        'global_snowfree_albedo.bosu.t{}.{}.{}.rg.grb'.format(jcap, lonb,
        latb)))
    nmlgen_input.set_value('fnvetc', value=os.path.join(dir_fix_am,
        'global_vegtype.igbp.t{}.{}.{}.rg.grb'.format(jcap, lonb, latb)))
    nmlgen_input.set_value('fnsotc', value=os.path.join(dir_fix_am,
        'global_soiltype.statsgo.t{}.{}.{}.rg.grb'.format(jcap, lonb, latb)))
    nmlgen_input.set_value('fnabsc', value=os.path.join(dir_fix_am,
        'global_mxsnoalb.uariz.t{}.{}.{}.rg.grb'.format(jcap, lonb, latb)))
    if 'mrweather' in app:
        if res == 96 or res == 192:
            jcap = 1534
            lonb = 3072
            latb = 1536
            nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,
                'global_soilmgldas.statsgo.t{}.{}.{}.grb'.format(jcap, lonb,
                latb)))
        else:
            nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,
                'global_soilmgldas.statsgo.t{}.{}.{}.grb'.format(jcap, lonb,
                latb)))
    else:
        nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,
            'global_soilmgldas.t{}.{}.{}.grb'.format(jcap, lonb, latb)))
    testcase = case.get_value('TEST')
    if testcase:
        nmlgen_input.set_value('iseed_skeb', 0)
        nmlgen_input.set_value('iseed_shum', 0)
        nmlgen_input.set_value('iseed_sppt', 0)
    else:
        member = 1
        cdate = int(datetime.now().strftime('%Y%m%d%H'))
        nmlgen_input.set_value('iseed_skeb', cdate * 1000 + member * 10 + 1)
        nmlgen_input.set_value('iseed_shum', cdate * 1000 + member * 10 + 2)
        nmlgen_input.set_value('iseed_sppt', cdate * 1000 + member * 10 + 3)
    nmlgen_input_sav = copy.copy(nmlgen_input)
    is_restart = case.get_value('CONTINUE_RUN')
    if is_restart:
        nmlgen_input.set_value('warm_start', True)
        nmlgen_input.set_value('nggps_ic', False)
        nmlgen_input.set_value('external_ic', False)
        nmlgen_input.set_value('mountain', True)
        nmlgen_input.set_value('make_nh', False)
        nmlgen_input.set_value('na_init', 0)
        nstf_name = nmlgen_input.get_value('nstf_name')
        nstf_name[1] = '0'
        read_increment = nmlgen_input.get_value('read_increment')
        if read_increment:
            nmlgen_input.set_value('res_latlon_dynamics', 'INPUT/fv_rst.res.nc'
                )
    else:
        nmlgen_input.set_value('warm_start', nmlgen_input_sav.get_value(
            'warm_start'))
        nmlgen_input.set_value('nggps_ic', nmlgen_input_sav.get_value(
            'nggps_ic'))
        nmlgen_input.set_value('external_ic', nmlgen_input_sav.get_value(
            'external_ic'))
        nmlgen_input.set_value('mountain', nmlgen_input_sav.get_value(
            'mountain'))
        nmlgen_input.set_value('make_nh', nmlgen_input_sav.get_value('make_nh')
            )
        nmlgen_input.set_value('na_init', nmlgen_input_sav.get_value('na_init')
            )
        if 'hafs' not in app:
            nstf_name = nmlgen_input_sav.get_value('nstf_name')
            if input_type == 'grib2':
                nstf_name[1] = '1'
            else:
                nstf_name[1] = '0'
        nmlgen_input.set_value('read_increment', nmlgen_input_sav.get_value
            ('read_increment'))
        nmlgen_input.set_value('res_latlon_dynamics', nmlgen_input_sav.
            get_value('res_latlon_dynamics'))
    if input_type == 'grib2' and 'hafs' not in app:
        nstf_name[0] = '0'
        logger.info('input_type {} nstf_name {}'.format(input_type, nstf_name))
        nmlgen_input.set_value('nstf_name', nstf_name)
    srcroot = case.get_value('SRCROOT')
    namelist_xml_dir = os.path.join(srcroot, 'src', 'model', 'FV3', 'cime',
        'cime_config')
    groups = []
    with open(os.path.join(namelist_xml_dir,
        'namelist_definition_ufsatm.xml'), 'r') as fin:
        for s in fin:
            if '<group>' in s:
                groups.append(s.replace('<group>', '').replace('</group>',
                    '').strip())
    groups = list(set(groups))
    groups.remove('config')
    groups.remove('_no_group_var')
    groups.remove('freeform')
    groups.remove('nampgb')
    groups.sort()
    nmlgen = nmlKeepChangedOnly(nmlgen_input_def, nmlgen_input, namelist_user)
    ntask_atm = int(case.get_value('NTASKS_ATM'))
    if ntask_atm == 8:
        nmlgen.set_value('layout', value=[1, 1])
    nmlgen = nmlOverwrite(namelist_user, nmlgen)
    nstf_name = nmlgen.get_value('nstf_name')
    if 'mrweather' in app:
        if not nstf_name[0] or nstf_name[0] == '0':
            ccpp = nmlgen.get_value('ccpp_suite')
            nmlgen.set_value('ccpp_suite', ccpp + '_no_nsst')
    layout = nmlgen_input.get_value('layout')
    layout_x = int(layout[0])
    layout_y = int(layout[1])
    ntiles = int(nmlgen_input.get_value('ntiles@fv_core_nml'))
    write_tasks_per_group = int(nmlgen_model.get_value('write_tasks_per_group')
        )
    write_groups = int(nmlgen_model.get_value('write_groups'))
    ntask_atm_config = (layout_x * layout_y * ntiles + 
        write_tasks_per_group * write_groups)
    if ntask_atm_config != ntask_atm:
        expect(False, (
            """Total number of PE need to be consistent with the model namelist options:
"""
             + '\tTotal number of PE (ntask_atm) = {}\n' +
            """	Decomposition in x and y direction (layout) = {}x{}
""" +
            '\tNumber of tile (ntiles) = {}\n' +
            '\tNumber of I/O group (write_groups) = {}\n' +
            """	Number of tasks in each I/O group (write_tasks_per_group) = {}
"""
            ).format(ntask_atm, layout_x, layout_y, ntiles, write_groups,
            write_tasks_per_group))
    namelist_file = os.path.join(confdir, 'atm_in')
    nmlgen.write_output_file(namelist_file, data_list_path, groups=groups,
        sorted_groups=False)


def _create_namelist_model_configure(case, confdir, config, infile, nmlgen,
    namelist_user, prefix):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    if os.path.exists(data_list_path):
        logger.warning('removing file {}'.format(data_list_path))
        os.remove(data_list_path)
    nmlgen.init_defaults(infile, config)
    is_restart = case.get_value('CONTINUE_RUN')
    global nhours_fcst_pre
    if is_restart:
        rundir = case.get_value('RUNDIR')
        rst_file = os.path.join(rundir, 'RESTART', '{}coupler.res'.format(
            prefix))
        if not os.path.isfile(rst_file):
            expect(False, "Couldn't find '" + rst_file + "'!")
        else:
            logger.debug('Using {} to determine previous forecast length ...'
                .format(rst_file))
            with open(rst_file, 'r') as f:
                lines = f.readlines()
                date1 = datetime(*[int(i) for i in lines[1].split()[0:6]])
                date2 = datetime(*[int(i) for i in lines[2].split()[0:6]])
                nhours_fcst_pre = int((date2 - date1).total_seconds() // 3600)
    else:
        nhours_fcst_pre = 0
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    nmlgen.set_value('start_year', value=yyyy)
    nmlgen.set_value('start_month', value=mm)
    nmlgen.set_value('start_day', value=dd)
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod // 3600)
    mi = int((run_start_tod - hh * 3600) // 60)
    ss = int(run_start_tod - hh * 3600 - mi * 60)
    nmlgen.set_value('start_hour', value=hh)
    nmlgen.set_value('start_minute', value=mi)
    nmlgen.set_value('start_second', value=ss)
    stop_option = case.get_value('STOP_OPTION')
    stop_n = int(case.get_value('STOP_N'))
    if 'nyears' in stop_option:
        expect(False, 'Option >nyears< is not supported!')
    elif 'nmonths' in stop_option:
        expect(False, 'Option >nmonths< is not supported!')
    elif 'ndays' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n * 24 + nhours_fcst_pre)
    elif 'nhours' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n + nhours_fcst_pre)
    elif 'nseconds' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n / 3600 + nhours_fcst_pre)
    elif 'nsteps' in stop_option:
        expect(False, 'Option >nsteps< is not supported!')
    rest_option = case.get_value('REST_OPTION')
    rest_n = int(case.get_value('REST_N'))
    if 'nyears' in rest_option:
        expect(False, 'Option >nyears< is not supported!')
    elif 'nmonths' in rest_option:
        expect(False, 'Option >nmonths< is not supported!')
    elif 'ndays' in rest_option:
        nmlgen.set_value('restart_interval', rest_n * 24)
    elif 'nhours' in rest_option:
        nmlgen.set_value('restart_interval', rest_n)
    elif 'nseconds' in rest_option:
        nmlgen.set_value('restart_interval', rest_n / 60)
    elif 'nsteps' in rest_option:
        expect(False, 'Option >nsteps< is not supported!')
    components = active_components(case)
    ntask = int(case.get_value('NTASKS_ATM'))
    if len(components) > 1:
        ntask = int(case.get_value('TOTALPES'))
    pe_member = int(nmlgen.get_value('PE_MEMBER01'))
    if pe_member != ntask:
        nmlgen.set_value('PE_MEMBER01', ntask)
    if ntask == 8:
        nmlgen.set_value('write_tasks_per_group', 2)
    testcase = case.get_value('TEST')
    if testcase:
        nmlgen.set_value('output_file', 'netcdf')
        nmlgen.set_value('filename_base', '{} {}'.format(case.get_value(
            'CASE') + '.ufsatm.atm.', case.get_value('CASE') + '.ufsatm.sfc.'))
    nmlgen.set_value('atmos_nthreads', case.thread_count)
    nmlgen.set_value('ncores_per_node', case.tasks_per_node)
    debug = case.get_value('DEBUG')
    if debug:
        nmlgen.set_value('print_esmf', True)
    else:
        nmlgen.set_value('print_esmf', False)
    nmlgen = nmlOverwrite(namelist_user, nmlgen)
    remove_lst = []
    if 'hafs' in app:
        remove_lst.extend(['imo', 'jmo', 'iau_offset', 'ideflate', 'nbits',
            'output_history', 'write_dopost', 'atm_coupling_interval_sec',
            'fhrot'])
    else:
        remove_lst.extend(['cen_lat', 'cen_lon', 'dlat', 'dlon', 'lat1',
            'lat2', 'lon1', 'lon2'])
        if 'mrweather' in app:
            remove_lst.append('atm_coupling_interval_sec')
            remove_lst.append('fhrot')
    for var in remove_lst:
        nmlgen._namelist.delete_variable('_no_group_var', var)
    model_config_file = os.path.join(confdir, 'model_configure')
    nmlgen.write_nuopc_config_file(model_config_file, data_list_path)


<mask token>


def _create_namelist_ncep_post(case, confdir, config, infile, nmlgen,
    nmlgen_model_configure, namelist_user):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    nmlgen.init_defaults(infile, config)
    groups = ['nampgb']
    output_file = nmlgen_model_configure.get_value('output_file')
    if 'netcdf' in output_file:
        nmlgen.set_value('ioform', 'netcdf')
    elif 'nemsio' in output_file:
        nmlgen.set_value('ioform', 'binarynemsiompiio')
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yy = run_start_date[0]
    mm = run_start_date[1]
    dd = run_start_date[2]
    run_start_tod = int(case.get_value('START_TOD'))
    hh = run_start_tod // 3600
    mi = (run_start_tod - hh * 3600) // 60
    ss = run_start_tod - hh * 3600 - mi * 60
    nmlgen = nmlOverwrite(namelist_user, nmlgen)
    namelist_file = os.path.join(confdir, 'itag.tmp')
    nmlgen.write_output_file(namelist_file, data_list_path, groups=groups,
        sorted_groups=False)
    with open(namelist_file, 'r+') as f:
        content = f.read()
        f.seek(0, 0)
        f.write(nmlgen.get_value('filename') + '\n')
        f.write(nmlgen.get_value('ioform') + '\n')
        f.write(nmlgen.get_value('outform') + '\n')
        f.write('{}-{}-{}'.format(yy, mm, dd) + '_' +
            '{hh:02d}:{mm:02d}:{ss:02d}'.format(hh=hh, mm=mi, ss=ss) + '\n')
        f.write(nmlgen.get_value('modelname') + '\n')
        f.write(nmlgen.get_value('filenameflux') + '\n')
        f.write(content)
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    mach = case.get_value('MACH')
    tasks_per_node = int(case.get_value('MAX_TASKS_PER_NODE'))
    if ('C384' in atm_grid or 'C768' in atm_grid) and 'stampede2' in mach:
        tasks_per_node = 24
        case.set_value('tasks_per_node', str(tasks_per_node), subgroup=
            'case.gfs_post')
        case.flush()
        logger.info('NCEP Post tasks per node is changed to {}!'.format(
            tasks_per_node))
    task_count = {'C96': tasks_per_node, 'C192': tasks_per_node, 'C384': 
        tasks_per_node * 2, 'C768': tasks_per_node * 4}
    if atm_grid in task_count.keys():
        case.set_value('task_count', str(task_count[atm_grid]), subgroup=
            'case.gfs_post')
        case.flush()
        logger.info('NCEP Post task count is changed to {}!'.format(
            task_count[atm_grid]))


<mask token>


def _main_func():
    caseroot = parse_input(sys.argv)
    with Case(caseroot) as case:
        buildnml(case, caseroot, 'ufsatm')


<mask token>

<mask token>


def date_yyyymmddhh(case):
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod) // 3600
    return yyyy, mm, dd, hh


def factors(n):
    return set(x for tup in ([i, n // i] for i in range(1, int(n ** 0.5) + 
        1) if n % i == 0) for x in tup)


def checkList(lst):
    ele = lst[0]
    chk = True
    for item in lst:
        if ele != item:
            chk = False
            break
    return chk


def active_components(case):
    compset = case.get_value('COMPSET')
    components = compset.split('_')[1:]
    components = [x for x in components if 'S' not in x[0]]
    return components


def list_to_value(val):
    if isinstance(val, list):
        if len(val) == 1:
            return val[0]
        else:
            return val
    else:
        return val


def nmlKeepChangedOnly(nmlgen_ini, nmlgen_cur, namelist_user):
    nmlgen_ret = copy.copy(nmlgen_cur)
    for nml in nmlgen_cur._namelist.get_group_names():
        for var in nmlgen_cur.get_group_variables(nml):
            matched = False
            lst = namelist_user.get_value(var)
            if any(len(item) > 0 for item in lst):
                continue
            lst = ['ccpp_suite', 'nstf_name']
            if var in lst:
                continue
            var_type, _, var_size = nmlgen_cur._definition.split_type_string(
                var)
            ini = nmlgen_ini.get_value(var)
            cur = nmlgen_cur.get_value(var)
            if var_size > 1:
                var_act = [None] * var_size
                var_def = [None] * var_size
                var_act_len = len(cur)
                var_act[0:var_act_len] = [x for x in cur]
                var_def_len = len(ini)
                var_def[0:var_def_len] = [x for x in ini]
                if var_type == 'character' or var_type == 'logical':
                    var_act = [('xyz' if x is None else x.lower()) for x in
                        var_act]
                    var_def = [('xyz' if x is None else x.lower()) for x in
                        var_def]
                    matched = all(y.startswith(x) for x, y in zip(var_act,
                        var_def))
                elif var_type == 'integer':
                    var_act = [(-99 if x is None else int(x)) for x in var_act]
                    var_def = [(-99 if x is None else int(x)) for x in var_def]
                    matched = all(x == y for x, y in zip(var_act, var_def))
                elif var_type == 'real':
                    var_act = [(-99.9 if x is None else float(eval(x.lower(
                        ).replace('d', 'e')))) for x in var_act]
                    var_def = [(-99.9 if x is None else float(eval(x.lower(
                        ).replace('d', 'e')))) for x in var_def]
                    matched = all(x == y for x, y in zip(var_act, var_def))
            elif var_type == 'character' or var_type == 'logical':
                if not ini.strip() == '':
                    if cur.startswith(ini):
                        matched = True
                elif ini.strip() == '' and cur.strip() == '':
                    matched = True
            elif var_type == 'integer':
                if ini:
                    if int(cur) == int(ini):
                        matched = True
                elif not (ini and cur):
                    matched = True
            elif var_type == 'real' and ini and cur:
                if float(eval(ini.lower().replace('d', 'e'))) == float(eval
                    (cur.lower().replace('d', 'e'))):
                    matched = True
            if matched:
                nmlgen_ret._namelist.delete_variable(nml, var)
    return nmlgen_ret


def nmlOverwrite(nmlgen_src, nmlgen_dst):
    nmlgen_ret = copy.copy(nmlgen_dst)
    for nml in nmlgen_src.get_group_names():
        for var in nmlgen_src.get_group_variables(nml):
            nmlgen_ret.set_value(var, nmlgen_src.get_value(var))
    return nmlgen_ret


def prep_input(case, casedocsdir, datestamp, nmlgen_input, prefix, input_type):
    casename = case.get_value('CASE')
    Buildconf = case.get_value('CASEBUILD')
    rundir = case.get_value('RUNDIR')
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    testcase = case.get_value('TEST')
    din_loc_root = case.get_value('DIN_LOC_ROOT')
    din_loc_ic = case.get_value('DIN_LOC_IC')
    is_restart = case.get_value('CONTINUE_RUN')
    srcroot = case.get_value('SRCROOT')
    caseroot = case.get_value('CASEROOT')
    env_workflow = case.get_env('workflow')
    jobs = env_workflow.get_jobs()
    new_o3forc = True
    if 's2s' in app:
        new_o3forc = False
    yyyy, mm, dd, hh = date_yyyymmddhh(case)
    icdir = os.path.join(din_loc_ic, '{:04d}{:02d}'.format(yyyy, mm),
        '{:04d}{:02d}{:02d}'.format(yyyy, mm, dd))
    if 'hafs' in app:
        dir_fix_am = os.path.join(din_loc_root, 'regional', 'fix_am')
        dir_fix_ufsatm = os.path.join(din_loc_root, 'regional', 'fix', atm_grid
            )
        dir_fix_parm = os.path.join(din_loc_root, 'parm')
        dir_fix_lib = os.path.join(din_loc_root, 'lib')
        dir_bdy = os.path.join(din_loc_root, 'regional', 'bcond',
            '{:04d}{:02d}{:02d}{:02d}'.format(int(yyyy), int(mm), int(dd), hh))
    else:
        dir_fix_am = os.path.join(din_loc_root, 'global', 'fix',
            'fix_am.v{}'.format(datestamp))
        if 's2s' in app:
            dir_fix_ufsatm = os.path.join(din_loc_root, 'global', 'fix',
                'fix_fv3_gmted2010.v{}'.format(datestamp), '{}_s2s'.format(
                atm_grid))
        else:
            dir_fix_ufsatm = os.path.join(din_loc_root, 'global', 'fix',
                'fix_fv3_gmted2010.v{}'.format(datestamp), atm_grid)
        dir_fix_parm = os.path.join(din_loc_root, 'parm')
        dir_fix_lib = os.path.join(din_loc_root, 'lib')
    input_files = dict()
    if 'hafs' in app:
        halo_lst = ['halo0', 'halo3', 'halo4']
        for halo in halo_lst:
            input_files['grid_tile7_{}'.format(halo)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_grid.tile7.{}.nc'.format(halo)
                ), 'ln', os.path.join('INPUT', atm_grid +
                '_grid.tile7.{}.nc'.format(halo))]
            input_files['orog_tile7_{}'.format(halo)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_oro_data.tile7.{}.nc'.format(
                halo)), 'ln', os.path.join('INPUT', atm_grid +
                '_oro_data.tile7.{}.nc'.format(halo))]
        input_files['oro_data_tile7h0'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_oro_data.tile7.halo0.nc'), 'ln', os.path.join(
            'INPUT', 'oro_data.nc')]
        input_files['oro_data_tile7h4'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_oro_data.tile7.halo4.nc'), 'ln', os.path.join(
            'INPUT', 'oro_data.tile7.halo4.nc')]
        input_files['grid_tile7h3'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_grid.tile7.halo3.nc'), 'ln', os.path.join('INPUT',
            atm_grid + '_grid.tile7.nc')]
        input_files['grid_tile7h0'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_grid.tile7.halo0.nc'), 'ln', os.path.join('INPUT',
            'grid.tile7.halo0.nc')]
        input_files['grid_tile7h4'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_grid.tile7.halo4.nc'), 'ln', os.path.join('INPUT',
            'grid.tile7.halo4.nc')]
    else:
        for tile in range(1, 7):
            input_files['grid_tile{}'.format(tile)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_grid.tile{}.nc'.format(tile)),
                'ln', os.path.join('INPUT', atm_grid + '_grid.tile{}.nc'.
                format(tile))]
            input_files['orog_data{}'.format(tile)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_oro_data.tile{}.nc'.format(
                tile)), 'ln', os.path.join('INPUT', 'oro_data.tile{}.nc'.
                format(tile))]
    if 's2s' in app:
        input_files['grid_spec'] = [os.path.join(icdir, 'grid_spec.nc'),
            'ln', os.path.join('INPUT', 'grid_spec.nc')]
    else:
        input_files['grid_spec'] = [os.path.join(dir_fix_ufsatm, atm_grid +
            '_mosaic.nc'), 'ln', os.path.join('INPUT', 'grid_spec.nc')]
    input_files['grid_mosaic'] = [os.path.join(dir_fix_ufsatm, atm_grid +
        '_mosaic.nc'), 'ln', os.path.join('INPUT', atm_grid + '_mosaic.nc')]
    iaer = nmlgen_input.get_value('iaer')
    ico2 = nmlgen_input.get_value('ico2')
    if new_o3forc:
        input_files['o3forc'] = [os.path.join(dir_fix_am,
            'ozprdlos_2015_new_sbuvO3_tclm15_nuchem.f77'), 'ln',
            'global_o3prdlos.f77']
    else:
        input_files['o3forc'] = [os.path.join(dir_fix_am,
            'global_o3prdlos.f77'), 'ln', 'global_o3prdlos.f77']
    input_files['h2oforc'] = [os.path.join(dir_fix_am,
        'global_h2o_pltc.f77'), 'ln', 'global_h2oprdlos.f77']
    input_files['solcnst'] = [os.path.join(dir_fix_am,
        'global_solarconstant_noaa_an.txt'), 'ln', 'solarconstant_noaa_an.txt']
    input_files['sfcemis'] = [os.path.join(dir_fix_am,
        'global_sfc_emissivity_idx.txt'), 'ln', 'sfc_emissivity_idx.txt']
    input_files['co2hist'] = [os.path.join(dir_fix_am,
        'global_co2historicaldata_glob.txt'), 'ln',
        'co2historicaldata_glob.txt']
    input_files['co2mont'] = [os.path.join(dir_fix_am, 'co2monthlycyc.txt'),
        'ln', 'co2monthlycyc.txt']
    ico2 = list_to_value(ico2)
    if ico2 and int(ico2) > 0:
        yy = int(time.strftime('%Y,%m,%d,%H,%M,%S').split(',')[0])
        for year in range(2009, yy + 1):
            input_files['co2his_{}'.format(year)] = [os.path.join(
                dir_fix_am, 'fix_co2_proj',
                'global_co2historicaldata_{}.txt'.format(year)), 'ln',
                'co2historicaldata_{}.txt'.format(year)]
    input_files['clmaero'] = [os.path.join(dir_fix_am,
        'global_climaeropac_global.txt'), 'ln', 'aerosol.dat']
    iaer = list_to_value(iaer)
    if iaer and int(iaer) > 0 and 'hafs' not in app:
        for year in range(1850, 2000, 10):
            input_files['volaer_{}'.format(year)] = [os.path.join(
                dir_fix_am, 'global_volcanic_aerosols_{}-{}.txt'.format(
                year, year + 9)), 'ln', 'volcanic_aerosols_{}-{}.txt'.
                format(year, year + 9)]
    for var in nmlgen_input.get_group_variables('namsfc'):
        var_str = str(nmlgen_input.get_value(var)).replace("'", '').strip()
        if not var_str.startswith('igbp') and var_str.endswith('.grb'):
            input_files[var] = [var_str, 'ln', os.path.basename(var_str)]
    nceplibs_dir = os.environ.get('NCEPLIBS_DIR')
    if 'case.chgres' in jobs:
        expect(nceplibs_dir,
            'NCEPLIBS_DIR environment variable need to be set to process raw input data!'
            )
        expect(os.path.exists(nceplibs_dir), 'NCEPLIBS_DIR {} not found'.
            format(nceplibs_dir))
    if 'case.chgres' in jobs:
        for tile in range(1, 7):
            for f in ['facsf', 'maximum_snow_albedo', 'slope_type',
                'snowfree_albedo', 'soil_type', 'substrate_temperature',
                'vegetation_greenness', 'vegetation_type']:
                input_files['{}{}'.format(f, tile)] = [os.path.join(
                    dir_fix_ufsatm, 'fix_sfc', atm_grid + '.{}.tile{}.nc'.
                    format(f, tile)), 'ln', os.path.join('INPUT', atm_grid +
                    '.{}.tile{}.nc'.format(f, tile))]
        levp = nmlgen_input.get_value('levp')
        input_files['global_hyblev'] = [os.path.join(dir_fix_am,
            'global_hyblev.l{}.txt'.format(levp)), 'ln', os.path.join(
            'INPUT', 'global_hyblev.l{}.txt'.format(levp))]
        input_files['var_map'] = [os.path.join(nceplibs_dir, 'share',
            'GFSphys_var_map.txt'), 'ln', 'GFSphys_var_map.txt']
    if 'case.gfs_post' in jobs:
        src_file = os.path.join(caseroot, 'SourceMods', 'src.ufsatm',
            'postxconfig-NT-GFS.txt')
        dst_file = os.path.join(rundir, 'postxconfig-NT-GFS.txt')
        if os.path.isfile(dst_file):
            os.remove(dst_file)
        if os.path.isfile(src_file):
            safe_copy(src_file, dst_file)
            logger.info('Using {} for post-processing'.format(src_file))
        else:
            logger.info('Using {} for post-processing'.format(os.path.join(
                nceplibs_dir, 'share', 'postxconfig-NT-GFS.txt')))
            input_files['postxconfig1'] = [os.path.join(nceplibs_dir,
                'share', 'postxconfig-NT-GFS.txt'), 'ln',
                'postxconfig-NT-GFS.txt']
        src_file = os.path.join(caseroot, 'SourceMods', 'src.ufsatm',
            'postxconfig-NT-GFS-F00.txt')
        dst_file = os.path.join(rundir, 'postxconfig-NT-GFS-F00.txt')
        if os.path.isfile(dst_file):
            os.remove(dst_file)
        if os.path.isfile(src_file):
            safe_copy(src_file, dst_file)
            logger.info('Using {} for post-processing'.format(src_file))
        else:
            logger.info('Using {} for post-processing'.format(os.path.join(
                nceplibs_dir, 'share', 'postxconfig-NT-GFS-F00.txt')))
            input_files['postxconfig2'] = [os.path.join(nceplibs_dir,
                'share', 'postxconfig-NT-GFS-F00.txt'), 'ln',
                'postxconfig-NT-GFS-F00.txt']
        input_files['params_grib2_tbl'] = [os.path.join(nceplibs_dir,
            'share', 'params_grib2_tbl_new'), 'ln', 'params_grib2_tbl_new']
    if not os.path.exists(rundir):
        os.makedirs(rundir)
    logger.info('\tCreating INPUT and RESTART directory')
    for _dir in ('INPUT', 'RESTART'):
        fulldir = os.path.join(rundir, _dir)
        if not os.path.exists(fulldir):
            os.makedirs(fulldir)
    rundirdst = rundir
    if rundir.endswith('case2run'):
        rundirsrc = os.path.abspath(os.path.join(rundir, os.pardir))
    else:
        rundirsrc = rundir
    if is_restart:
        tile_max = 7
        if 'hafs' in app:
            tile_max = 2
        lst = ['fv_core.res', 'fv_srf_wnd.res', 'fv_tracer.res', 'phy_data',
            'sfc_data']
        lst = [(prefix + f) for f in lst]
        for f in lst:
            for tile in range(1, tile_max):
                if '_data' in f and 'hafs' in app:
                    src_file = os.path.join(rundir, 'RESTART', '{}.nc'.
                        format(f))
                    tgt_file = os.path.join(rundir, 'INPUT', '{}.nc'.format
                        (f.replace(prefix, '')))
                else:
                    src_file = os.path.join(rundir, 'RESTART',
                        '{}.tile{}.nc'.format(f, tile))
                    tgt_file = os.path.join(rundir, 'INPUT', '{}.tile{}.nc'
                        .format(f.replace(prefix, ''), tile))
                if not os.path.isfile(src_file):
                    expect(False, '{} is missing.'.format(src_file))
                else:
                    safe_copy(src_file, tgt_file)
        lst = ['fv_core.res.nc', 'coupler.res']
        lst = [(prefix + f) for f in lst]
        for f in lst:
            src_file = os.path.join(rundir, 'RESTART', f)
            tgt_file = os.path.join(rundir, 'INPUT', f.replace(prefix, ''))
            if not os.path.isfile(src_file):
                expect(False, '{} is missing.'.format(src_file))
            else:
                safe_copy(src_file, tgt_file)
        if 'hafs' in app:
            lst = ls_sorted_by_mtime(dir_bdy)
            lst.remove('gfs_ctrl.nc')
            lst.remove('gfs_data.tile7.nc')
            lst.remove('sfc_data.tile7.nc')
            for f in lst:
                src_file = os.path.join(rundirdst, 'INPUT', f)
                if os.path.isfile(src_file):
                    os.remove(src_file)
                time_step = int(f.split('.')[2])
                if time_step < nhours_fcst_pre:
                    continue
                else:
                    src_file = os.path.join(dir_bdy, f)
                    dst_file = os.path.join(rundir, 'INPUT',
                        'gfs_bndy.tile7.{:03d}.nc'.format(time_step -
                        nhours_fcst_pre))
                    symlink_force(src_file, dst_file)
    elif 'hafs' in app:
        lst = ls_sorted_by_mtime(dir_bdy)
        for f in lst:
            symlink_force(os.path.join(dir_bdy, f), os.path.join(rundirdst,
                'INPUT', f))
        symlink_force(os.path.join(dir_bdy, 'sfc_data.tile7.nc'), os.path.
            join(rundirdst, 'INPUT', 'sfc_data.nc'))
        symlink_force(os.path.join(dir_bdy, 'gfs_data.tile7.nc'), os.path.
            join(rundirdst, 'INPUT', 'gfs_data.nc'))
    else:
        lst = ['fv_core.res', 'fv_srf_wnd.res', 'fv_tracer.res', 'phy_data',
            'sfc_data']
        for f in lst:
            for tile in range(1, 7):
                src_file = os.path.join(rundir, 'INPUT', '{}.tile{}.nc'.
                    format(f, tile))
                if os.path.isfile(src_file):
                    logger.warning('removing file {}'.format(src_file))
                    os.remove(src_file)
        lst = ['fv_core.res.nc', 'coupler.res']
        for f in lst:
            src_file = os.path.join(rundir, 'INPUT', f)
            if os.path.isfile(src_file):
                logger.warning('removing file {}'.format(src_file))
                os.remove(src_file)
        src_file = os.path.join(rundirsrc, 'INPUT', '{}.'.format(atm_grid) +
            '{:04d}-{:02d}-{:02d}'.format(yyyy, mm, dd) +
            '_{:02d}.gfs_ctrl.nc'.format(hh))
        if os.path.isfile(src_file):
            symlink_force(src_file, os.path.join(rundirdst, 'INPUT',
                'gfs_ctrl.nc'))
            lst = ['gfs_data', 'sfc_data']
            for f in lst:
                for tile in range(1, 7):
                    src_file = os.path.join(rundirsrc, 'INPUT', '{}.'.
                        format(atm_grid) + '{:04d}-{:02d}-{:02d}'.format(
                        yyyy, mm, dd) + '_{:02d}.'.format(hh) + f +
                        '.tile{}.nc'.format(tile))
                    tgt_file = os.path.join(rundirdst, 'INPUT',
                        '{}.tile{}.nc'.format(f, tile))
                    if os.path.isfile(src_file):
                        symlink_force(src_file, tgt_file)
                        logger.info('chgres source file {} linked to {}'.
                            format(src_file, tgt_file))
                    else:
                        logger.debug('chgres source file {} not found'.
                            format(src_file))
        else:
            src_file = os.path.join(icdir, 'gfs_ctrl.nc')
            if os.path.isfile(src_file):
                symlink_force(src_file, os.path.join(rundirdst, 'INPUT',
                    'gfs_ctrl.nc'))
            else:
                logger.warning(
                    'initial condition file {} is required! Exiting ...'.
                    format(src_file))
            lst = ['gfs_data', 'sfc_data']
            for f in lst:
                for tile in range(1, 7):
                    src_file = os.path.join(icdir, f + '.tile{}.nc'.format(
                        tile))
                    tgt_file = os.path.join(rundirdst, 'INPUT',
                        '{}.tile{}.nc'.format(f, tile))
                    if os.path.isfile(src_file):
                        symlink_force(src_file, tgt_file)
                        logger.info('initial condition file {} linked to {}'
                            .format(src_file, tgt_file))
                    else:
                        logger.warning(
                            'initial condition file {} is required! Exiting ...'
                            .format(src_file))
    with open(os.path.join(Buildconf, 'ufsatm.input_data_list'), 'w'
        ) as input_data_list:
        for k, v in input_files.items():
            input_data_list.write(k + ' = ' + v[0] + '\n')
    for k, v in input_files.items():
        if not os.path.isfile(v[0]):
            logger.warning(
                'WARNING: data file {} not found, will attempt to download.'
                .format(v[0]))
        elif 'ln' in v[1]:
            symlink_force(v[0], os.path.join(rundir, v[2]))
        elif 'cp':
            safe_copy(v[0], os.path.join(rundir, v[2]))
    cimeroot_ufsatm = os.path.join(srcroot, 'src', 'model', 'FV3', 'cime',
        'cime_config')
    for f in ['data_table_{}'.format(app), 'diag_table_{}'.format(app),
        'field_table_{}'.format(app)]:
        if not os.path.isfile(os.path.join(rundir, f)):
            safe_copy(os.path.join(cimeroot_ufsatm, 'tables', f), os.path.
                join(rundir, f.replace('_{}'.format(app), '')))


def _create_namelist_input(case, confdir, config, infile, nmlgen_model,
    nmlgen_input, nmlgen_input_def, namelist_user, datestamp, input_type):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    nmlgen_input.init_defaults(infile, config)
    nmlgen_input_def.init_defaults(infile, {})
    din_loc_root = case.get_value('DIN_LOC_ROOT')
    if 'hafs' in app:
        dir_fix_am = os.path.join(din_loc_root, 'regional', 'fix_am')
    else:
        dir_fix_am = os.path.join(din_loc_root, 'global', 'fix',
            'fix_am.v{}'.format(datestamp))
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    res = int(atm_grid.replace('C', ''))
    jcap = 2 * res - 2
    lonb = 4 * res
    latb = 2 * res
    if 's2s' in app:
        if res == 96:
            jcap = 126
            lonb = 384
            latb = 190
    nmlgen_input.set_value('fnglac', value=os.path.join(dir_fix_am,
        'global_glacier.2x2.grb'))
    nmlgen_input.set_value('fnmxic', value=os.path.join(dir_fix_am,
        'global_maxice.2x2.grb'))
    nmlgen_input.set_value('fntsfc', value=os.path.join(dir_fix_am,
        'RTGSST.1982.2012.monthly.clim.grb'))
    nmlgen_input.set_value('fnsnoc', value=os.path.join(dir_fix_am,
        'global_snoclim.1.875.grb'))
    nmlgen_input.set_value('fnzorc', value='igbp')
    nmlgen_input.set_value('fnalbc2', value=os.path.join(dir_fix_am,
        'global_albedo4.1x1.grb'))
    nmlgen_input.set_value('fnaisc', value=os.path.join(dir_fix_am,
        'CFSR.SEAICE.1982.2012.monthly.clim.grb'))
    nmlgen_input.set_value('fntg3c', value=os.path.join(dir_fix_am,
        'global_tg3clim.2.6x1.5.grb'))
    nmlgen_input.set_value('fnvegc', value=os.path.join(dir_fix_am,
        'global_vegfrac.0.144.decpercent.grb'))
    if 'mrweather' in app:
        nmlgen_input.set_value('fnmskh', value=os.path.join(dir_fix_am,
            'global_slmask.t1534.3072.1536.grb'))
    else:
        nmlgen_input.set_value('fnmskh', value=os.path.join(dir_fix_am,
            'seaice_newland.grb'))
    nmlgen_input.set_value('fnvmnc', value=os.path.join(dir_fix_am,
        'global_shdmin.0.144x0.144.grb'))
    nmlgen_input.set_value('fnvmxc', value=os.path.join(dir_fix_am,
        'global_shdmax.0.144x0.144.grb'))
    nmlgen_input.set_value('fnslpc', value=os.path.join(dir_fix_am,
        'global_slope.1x1.grb'))
    nmlgen_input.set_value('fnalbc', value=os.path.join(dir_fix_am,
        'global_snowfree_albedo.bosu.t{}.{}.{}.rg.grb'.format(jcap, lonb,
        latb)))
    nmlgen_input.set_value('fnvetc', value=os.path.join(dir_fix_am,
        'global_vegtype.igbp.t{}.{}.{}.rg.grb'.format(jcap, lonb, latb)))
    nmlgen_input.set_value('fnsotc', value=os.path.join(dir_fix_am,
        'global_soiltype.statsgo.t{}.{}.{}.rg.grb'.format(jcap, lonb, latb)))
    nmlgen_input.set_value('fnabsc', value=os.path.join(dir_fix_am,
        'global_mxsnoalb.uariz.t{}.{}.{}.rg.grb'.format(jcap, lonb, latb)))
    if 'mrweather' in app:
        if res == 96 or res == 192:
            jcap = 1534
            lonb = 3072
            latb = 1536
            nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,
                'global_soilmgldas.statsgo.t{}.{}.{}.grb'.format(jcap, lonb,
                latb)))
        else:
            nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,
                'global_soilmgldas.statsgo.t{}.{}.{}.grb'.format(jcap, lonb,
                latb)))
    else:
        nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,
            'global_soilmgldas.t{}.{}.{}.grb'.format(jcap, lonb, latb)))
    testcase = case.get_value('TEST')
    if testcase:
        nmlgen_input.set_value('iseed_skeb', 0)
        nmlgen_input.set_value('iseed_shum', 0)
        nmlgen_input.set_value('iseed_sppt', 0)
    else:
        member = 1
        cdate = int(datetime.now().strftime('%Y%m%d%H'))
        nmlgen_input.set_value('iseed_skeb', cdate * 1000 + member * 10 + 1)
        nmlgen_input.set_value('iseed_shum', cdate * 1000 + member * 10 + 2)
        nmlgen_input.set_value('iseed_sppt', cdate * 1000 + member * 10 + 3)
    nmlgen_input_sav = copy.copy(nmlgen_input)
    is_restart = case.get_value('CONTINUE_RUN')
    if is_restart:
        nmlgen_input.set_value('warm_start', True)
        nmlgen_input.set_value('nggps_ic', False)
        nmlgen_input.set_value('external_ic', False)
        nmlgen_input.set_value('mountain', True)
        nmlgen_input.set_value('make_nh', False)
        nmlgen_input.set_value('na_init', 0)
        nstf_name = nmlgen_input.get_value('nstf_name')
        nstf_name[1] = '0'
        read_increment = nmlgen_input.get_value('read_increment')
        if read_increment:
            nmlgen_input.set_value('res_latlon_dynamics', 'INPUT/fv_rst.res.nc'
                )
    else:
        nmlgen_input.set_value('warm_start', nmlgen_input_sav.get_value(
            'warm_start'))
        nmlgen_input.set_value('nggps_ic', nmlgen_input_sav.get_value(
            'nggps_ic'))
        nmlgen_input.set_value('external_ic', nmlgen_input_sav.get_value(
            'external_ic'))
        nmlgen_input.set_value('mountain', nmlgen_input_sav.get_value(
            'mountain'))
        nmlgen_input.set_value('make_nh', nmlgen_input_sav.get_value('make_nh')
            )
        nmlgen_input.set_value('na_init', nmlgen_input_sav.get_value('na_init')
            )
        if 'hafs' not in app:
            nstf_name = nmlgen_input_sav.get_value('nstf_name')
            if input_type == 'grib2':
                nstf_name[1] = '1'
            else:
                nstf_name[1] = '0'
        nmlgen_input.set_value('read_increment', nmlgen_input_sav.get_value
            ('read_increment'))
        nmlgen_input.set_value('res_latlon_dynamics', nmlgen_input_sav.
            get_value('res_latlon_dynamics'))
    if input_type == 'grib2' and 'hafs' not in app:
        nstf_name[0] = '0'
        logger.info('input_type {} nstf_name {}'.format(input_type, nstf_name))
        nmlgen_input.set_value('nstf_name', nstf_name)
    srcroot = case.get_value('SRCROOT')
    namelist_xml_dir = os.path.join(srcroot, 'src', 'model', 'FV3', 'cime',
        'cime_config')
    groups = []
    with open(os.path.join(namelist_xml_dir,
        'namelist_definition_ufsatm.xml'), 'r') as fin:
        for s in fin:
            if '<group>' in s:
                groups.append(s.replace('<group>', '').replace('</group>',
                    '').strip())
    groups = list(set(groups))
    groups.remove('config')
    groups.remove('_no_group_var')
    groups.remove('freeform')
    groups.remove('nampgb')
    groups.sort()
    nmlgen = nmlKeepChangedOnly(nmlgen_input_def, nmlgen_input, namelist_user)
    ntask_atm = int(case.get_value('NTASKS_ATM'))
    if ntask_atm == 8:
        nmlgen.set_value('layout', value=[1, 1])
    nmlgen = nmlOverwrite(namelist_user, nmlgen)
    nstf_name = nmlgen.get_value('nstf_name')
    if 'mrweather' in app:
        if not nstf_name[0] or nstf_name[0] == '0':
            ccpp = nmlgen.get_value('ccpp_suite')
            nmlgen.set_value('ccpp_suite', ccpp + '_no_nsst')
    layout = nmlgen_input.get_value('layout')
    layout_x = int(layout[0])
    layout_y = int(layout[1])
    ntiles = int(nmlgen_input.get_value('ntiles@fv_core_nml'))
    write_tasks_per_group = int(nmlgen_model.get_value('write_tasks_per_group')
        )
    write_groups = int(nmlgen_model.get_value('write_groups'))
    ntask_atm_config = (layout_x * layout_y * ntiles + 
        write_tasks_per_group * write_groups)
    if ntask_atm_config != ntask_atm:
        expect(False, (
            """Total number of PE need to be consistent with the model namelist options:
"""
             + '\tTotal number of PE (ntask_atm) = {}\n' +
            """	Decomposition in x and y direction (layout) = {}x{}
""" +
            '\tNumber of tile (ntiles) = {}\n' +
            '\tNumber of I/O group (write_groups) = {}\n' +
            """	Number of tasks in each I/O group (write_tasks_per_group) = {}
"""
            ).format(ntask_atm, layout_x, layout_y, ntiles, write_groups,
            write_tasks_per_group))
    namelist_file = os.path.join(confdir, 'atm_in')
    nmlgen.write_output_file(namelist_file, data_list_path, groups=groups,
        sorted_groups=False)


def _create_namelist_model_configure(case, confdir, config, infile, nmlgen,
    namelist_user, prefix):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    if os.path.exists(data_list_path):
        logger.warning('removing file {}'.format(data_list_path))
        os.remove(data_list_path)
    nmlgen.init_defaults(infile, config)
    is_restart = case.get_value('CONTINUE_RUN')
    global nhours_fcst_pre
    if is_restart:
        rundir = case.get_value('RUNDIR')
        rst_file = os.path.join(rundir, 'RESTART', '{}coupler.res'.format(
            prefix))
        if not os.path.isfile(rst_file):
            expect(False, "Couldn't find '" + rst_file + "'!")
        else:
            logger.debug('Using {} to determine previous forecast length ...'
                .format(rst_file))
            with open(rst_file, 'r') as f:
                lines = f.readlines()
                date1 = datetime(*[int(i) for i in lines[1].split()[0:6]])
                date2 = datetime(*[int(i) for i in lines[2].split()[0:6]])
                nhours_fcst_pre = int((date2 - date1).total_seconds() // 3600)
    else:
        nhours_fcst_pre = 0
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    nmlgen.set_value('start_year', value=yyyy)
    nmlgen.set_value('start_month', value=mm)
    nmlgen.set_value('start_day', value=dd)
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod // 3600)
    mi = int((run_start_tod - hh * 3600) // 60)
    ss = int(run_start_tod - hh * 3600 - mi * 60)
    nmlgen.set_value('start_hour', value=hh)
    nmlgen.set_value('start_minute', value=mi)
    nmlgen.set_value('start_second', value=ss)
    stop_option = case.get_value('STOP_OPTION')
    stop_n = int(case.get_value('STOP_N'))
    if 'nyears' in stop_option:
        expect(False, 'Option >nyears< is not supported!')
    elif 'nmonths' in stop_option:
        expect(False, 'Option >nmonths< is not supported!')
    elif 'ndays' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n * 24 + nhours_fcst_pre)
    elif 'nhours' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n + nhours_fcst_pre)
    elif 'nseconds' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n / 3600 + nhours_fcst_pre)
    elif 'nsteps' in stop_option:
        expect(False, 'Option >nsteps< is not supported!')
    rest_option = case.get_value('REST_OPTION')
    rest_n = int(case.get_value('REST_N'))
    if 'nyears' in rest_option:
        expect(False, 'Option >nyears< is not supported!')
    elif 'nmonths' in rest_option:
        expect(False, 'Option >nmonths< is not supported!')
    elif 'ndays' in rest_option:
        nmlgen.set_value('restart_interval', rest_n * 24)
    elif 'nhours' in rest_option:
        nmlgen.set_value('restart_interval', rest_n)
    elif 'nseconds' in rest_option:
        nmlgen.set_value('restart_interval', rest_n / 60)
    elif 'nsteps' in rest_option:
        expect(False, 'Option >nsteps< is not supported!')
    components = active_components(case)
    ntask = int(case.get_value('NTASKS_ATM'))
    if len(components) > 1:
        ntask = int(case.get_value('TOTALPES'))
    pe_member = int(nmlgen.get_value('PE_MEMBER01'))
    if pe_member != ntask:
        nmlgen.set_value('PE_MEMBER01', ntask)
    if ntask == 8:
        nmlgen.set_value('write_tasks_per_group', 2)
    testcase = case.get_value('TEST')
    if testcase:
        nmlgen.set_value('output_file', 'netcdf')
        nmlgen.set_value('filename_base', '{} {}'.format(case.get_value(
            'CASE') + '.ufsatm.atm.', case.get_value('CASE') + '.ufsatm.sfc.'))
    nmlgen.set_value('atmos_nthreads', case.thread_count)
    nmlgen.set_value('ncores_per_node', case.tasks_per_node)
    debug = case.get_value('DEBUG')
    if debug:
        nmlgen.set_value('print_esmf', True)
    else:
        nmlgen.set_value('print_esmf', False)
    nmlgen = nmlOverwrite(namelist_user, nmlgen)
    remove_lst = []
    if 'hafs' in app:
        remove_lst.extend(['imo', 'jmo', 'iau_offset', 'ideflate', 'nbits',
            'output_history', 'write_dopost', 'atm_coupling_interval_sec',
            'fhrot'])
    else:
        remove_lst.extend(['cen_lat', 'cen_lon', 'dlat', 'dlon', 'lat1',
            'lat2', 'lon1', 'lon2'])
        if 'mrweather' in app:
            remove_lst.append('atm_coupling_interval_sec')
            remove_lst.append('fhrot')
    for var in remove_lst:
        nmlgen._namelist.delete_variable('_no_group_var', var)
    model_config_file = os.path.join(confdir, 'model_configure')
    nmlgen.write_nuopc_config_file(model_config_file, data_list_path)


def _create_namelist_chgres(case, confdir, config, infile, nmlgen,
    nmlgen_def, nmlgen_input, namelist_user, input_type):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    nmlgen.init_defaults(infile, config)
    nmlgen_def.init_defaults(infile, {})
    rundir = case.get_value('RUNDIR')
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    nmlgen.set_value('mosaic_file_target_grid', os.path.join('INPUT', 
        atm_grid + '_mosaic.nc'))
    levp = nmlgen_input.get_value('levp')
    if levp is None:
        expect(False,
            "Number of vertical layer 'levp' must be a valid number! levp = {}"
            .format(levp))
    else:
        hyblev_file = os.path.join(rundir, 'INPUT', 'global_hyblev.l{}.txt'
            .format(levp))
        if not os.path.exists(hyblev_file):
            logger.warning(
                'WARNING: {} does not exist, will attempt to download.'.
                format(hyblev_file))
        else:
            nmlgen.set_value('vcoord_file_target_grid', os.path.join(
                'INPUT', 'global_hyblev.l{}.txt'.format(levp)))
    nmlgen.set_value('fix_dir_target_grid', os.path.join('INPUT'))
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod // 3600)
    nmlgen.set_value('cycle_mon', int(mm))
    nmlgen.set_value('cycle_day', int(dd))
    nmlgen.set_value('cycle_hour', int(hh))
    icdir = os.path.join(case.get_value('DIN_LOC_IC'), '{:04d}{:02d}'.
        format(yyyy, mm), '{:04d}{:02d}{:02d}'.format(yyyy, mm, dd))
    nmlgen.set_value('data_dir_input_grid', icdir)
    nmlgen.set_value('input_type', input_type)
    if input_type == 'gaussian_nemsio':
        nmlgen.set_value('atm_files_input_grid', ['atm.input.ic.nemsio'])
        nmlgen.set_value('sfc_files_input_grid', ['sfc.input.ic.nemsio'])
        tracers = ['sphum', 'liq_wat', 'o3mr', 'ice_wat', 'rainwat',
            'snowwat', 'graupel']
        nmlgen.set_value('tracers', tracers)
        tracers_input = ['spfh', 'clwmr', 'o3mr', 'icmr', 'rwmr', 'snmr',
            'grle']
        nmlgen.set_value('tracers_input', tracers_input)
        nmlgen.set_value('convert_nst', True)
    elif input_type == 'grib2':
        nmlgen.set_value('grib2_file_input_grid', 'atm.input.ic.grb2')
        nmlgen.set_value('varmap_file', 'GFSphys_var_map.txt')
    elif input_type == 'gaussian_netcdf':
        nmlgen.set_value('orog_dir_target_grid', os.path.join('INPUT'))
        oro_files = []
        for tile in range(1, 7):
            oro_files.append('oro_data.tile{}.nc'.format(tile))
        nmlgen.set_value('orog_files_target_grid', oro_files)
        nmlgen.set_value('atm_files_input_grid', ['atm.input.ic.nc'])
        nmlgen.set_value('sfc_files_input_grid', ['sfc.input.ic.nc'])
        nmlgen.set_value('convert_atm', True)
        nmlgen.set_value('convert_sfc', True)
        nmlgen.set_value('convert_nst', True)
        tracers = ['sphum', 'liq_wat', 'o3mr', 'ice_wat', 'rainwat',
            'snowwat', 'graupel']
        nmlgen.set_value('tracers', tracers)
        tracers_input = ['spfh', 'clwmr', 'o3mr', 'icmr', 'rwmr', 'snmr',
            'grle']
        nmlgen.set_value('tracers_input', tracers_input)
    mach = case.get_value('MACH')
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    tasks_per_node = 12
    task_count = {'C96': 1 * tasks_per_node, 'C192': 1 * tasks_per_node,
        'C384': 2 * tasks_per_node, 'C768': 3 * tasks_per_node}
    if atm_grid in task_count.keys():
        case.set_value('tasks_per_node', str(tasks_per_node), subgroup=
            'case.chgres')
        case.set_value('task_count', str(task_count[atm_grid]), subgroup=
            'case.chgres')
        logger.info(
            'CHGRES task count and tasks per node are modified! task_count = {} tasks_per_node = {}'
            .format(task_count[atm_grid], tasks_per_node))
        case.flush()
    nmlgen_mod = nmlKeepChangedOnly(nmlgen_def, nmlgen, namelist_user)
    nmlgen_mod = nmlOverwrite(namelist_user, nmlgen_mod)
    groups = ['config']
    namelist_file = os.path.join(confdir, 'config.nml')
    nmlgen_mod.write_output_file(namelist_file, data_list_path, groups=
        groups, sorted_groups=False)


def _create_namelist_ncep_post(case, confdir, config, infile, nmlgen,
    nmlgen_model_configure, namelist_user):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    nmlgen.init_defaults(infile, config)
    groups = ['nampgb']
    output_file = nmlgen_model_configure.get_value('output_file')
    if 'netcdf' in output_file:
        nmlgen.set_value('ioform', 'netcdf')
    elif 'nemsio' in output_file:
        nmlgen.set_value('ioform', 'binarynemsiompiio')
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yy = run_start_date[0]
    mm = run_start_date[1]
    dd = run_start_date[2]
    run_start_tod = int(case.get_value('START_TOD'))
    hh = run_start_tod // 3600
    mi = (run_start_tod - hh * 3600) // 60
    ss = run_start_tod - hh * 3600 - mi * 60
    nmlgen = nmlOverwrite(namelist_user, nmlgen)
    namelist_file = os.path.join(confdir, 'itag.tmp')
    nmlgen.write_output_file(namelist_file, data_list_path, groups=groups,
        sorted_groups=False)
    with open(namelist_file, 'r+') as f:
        content = f.read()
        f.seek(0, 0)
        f.write(nmlgen.get_value('filename') + '\n')
        f.write(nmlgen.get_value('ioform') + '\n')
        f.write(nmlgen.get_value('outform') + '\n')
        f.write('{}-{}-{}'.format(yy, mm, dd) + '_' +
            '{hh:02d}:{mm:02d}:{ss:02d}'.format(hh=hh, mm=mi, ss=ss) + '\n')
        f.write(nmlgen.get_value('modelname') + '\n')
        f.write(nmlgen.get_value('filenameflux') + '\n')
        f.write(content)
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    mach = case.get_value('MACH')
    tasks_per_node = int(case.get_value('MAX_TASKS_PER_NODE'))
    if ('C384' in atm_grid or 'C768' in atm_grid) and 'stampede2' in mach:
        tasks_per_node = 24
        case.set_value('tasks_per_node', str(tasks_per_node), subgroup=
            'case.gfs_post')
        case.flush()
        logger.info('NCEP Post tasks per node is changed to {}!'.format(
            tasks_per_node))
    task_count = {'C96': tasks_per_node, 'C192': tasks_per_node, 'C384': 
        tasks_per_node * 2, 'C768': tasks_per_node * 4}
    if atm_grid in task_count.keys():
        case.set_value('task_count', str(task_count[atm_grid]), subgroup=
            'case.gfs_post')
        case.flush()
        logger.info('NCEP Post task count is changed to {}!'.format(
            task_count[atm_grid]))


<mask token>


def _main_func():
    caseroot = parse_input(sys.argv)
    with Case(caseroot) as case:
        buildnml(case, caseroot, 'ufsatm')


<mask token>

<mask token>


def date_yyyymmddhh(case):
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod) // 3600
    return yyyy, mm, dd, hh


def factors(n):
    return set(x for tup in ([i, n // i] for i in range(1, int(n ** 0.5) + 
        1) if n % i == 0) for x in tup)


def checkList(lst):
    ele = lst[0]
    chk = True
    for item in lst:
        if ele != item:
            chk = False
            break
    return chk


def active_components(case):
    compset = case.get_value('COMPSET')
    components = compset.split('_')[1:]
    components = [x for x in components if 'S' not in x[0]]
    return components


def list_to_value(val):
    if isinstance(val, list):
        if len(val) == 1:
            return val[0]
        else:
            return val
    else:
        return val


def nmlKeepChangedOnly(nmlgen_ini, nmlgen_cur, namelist_user):
    nmlgen_ret = copy.copy(nmlgen_cur)
    for nml in nmlgen_cur._namelist.get_group_names():
        for var in nmlgen_cur.get_group_variables(nml):
            matched = False
            lst = namelist_user.get_value(var)
            if any(len(item) > 0 for item in lst):
                continue
            lst = ['ccpp_suite', 'nstf_name']
            if var in lst:
                continue
            var_type, _, var_size = nmlgen_cur._definition.split_type_string(
                var)
            ini = nmlgen_ini.get_value(var)
            cur = nmlgen_cur.get_value(var)
            if var_size > 1:
                var_act = [None] * var_size
                var_def = [None] * var_size
                var_act_len = len(cur)
                var_act[0:var_act_len] = [x for x in cur]
                var_def_len = len(ini)
                var_def[0:var_def_len] = [x for x in ini]
                if var_type == 'character' or var_type == 'logical':
                    var_act = [('xyz' if x is None else x.lower()) for x in
                        var_act]
                    var_def = [('xyz' if x is None else x.lower()) for x in
                        var_def]
                    matched = all(y.startswith(x) for x, y in zip(var_act,
                        var_def))
                elif var_type == 'integer':
                    var_act = [(-99 if x is None else int(x)) for x in var_act]
                    var_def = [(-99 if x is None else int(x)) for x in var_def]
                    matched = all(x == y for x, y in zip(var_act, var_def))
                elif var_type == 'real':
                    var_act = [(-99.9 if x is None else float(eval(x.lower(
                        ).replace('d', 'e')))) for x in var_act]
                    var_def = [(-99.9 if x is None else float(eval(x.lower(
                        ).replace('d', 'e')))) for x in var_def]
                    matched = all(x == y for x, y in zip(var_act, var_def))
            elif var_type == 'character' or var_type == 'logical':
                if not ini.strip() == '':
                    if cur.startswith(ini):
                        matched = True
                elif ini.strip() == '' and cur.strip() == '':
                    matched = True
            elif var_type == 'integer':
                if ini:
                    if int(cur) == int(ini):
                        matched = True
                elif not (ini and cur):
                    matched = True
            elif var_type == 'real' and ini and cur:
                if float(eval(ini.lower().replace('d', 'e'))) == float(eval
                    (cur.lower().replace('d', 'e'))):
                    matched = True
            if matched:
                nmlgen_ret._namelist.delete_variable(nml, var)
    return nmlgen_ret


def nmlOverwrite(nmlgen_src, nmlgen_dst):
    nmlgen_ret = copy.copy(nmlgen_dst)
    for nml in nmlgen_src.get_group_names():
        for var in nmlgen_src.get_group_variables(nml):
            nmlgen_ret.set_value(var, nmlgen_src.get_value(var))
    return nmlgen_ret


def prep_input(case, casedocsdir, datestamp, nmlgen_input, prefix, input_type):
    casename = case.get_value('CASE')
    Buildconf = case.get_value('CASEBUILD')
    rundir = case.get_value('RUNDIR')
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    testcase = case.get_value('TEST')
    din_loc_root = case.get_value('DIN_LOC_ROOT')
    din_loc_ic = case.get_value('DIN_LOC_IC')
    is_restart = case.get_value('CONTINUE_RUN')
    srcroot = case.get_value('SRCROOT')
    caseroot = case.get_value('CASEROOT')
    env_workflow = case.get_env('workflow')
    jobs = env_workflow.get_jobs()
    new_o3forc = True
    if 's2s' in app:
        new_o3forc = False
    yyyy, mm, dd, hh = date_yyyymmddhh(case)
    icdir = os.path.join(din_loc_ic, '{:04d}{:02d}'.format(yyyy, mm),
        '{:04d}{:02d}{:02d}'.format(yyyy, mm, dd))
    if 'hafs' in app:
        dir_fix_am = os.path.join(din_loc_root, 'regional', 'fix_am')
        dir_fix_ufsatm = os.path.join(din_loc_root, 'regional', 'fix', atm_grid
            )
        dir_fix_parm = os.path.join(din_loc_root, 'parm')
        dir_fix_lib = os.path.join(din_loc_root, 'lib')
        dir_bdy = os.path.join(din_loc_root, 'regional', 'bcond',
            '{:04d}{:02d}{:02d}{:02d}'.format(int(yyyy), int(mm), int(dd), hh))
    else:
        dir_fix_am = os.path.join(din_loc_root, 'global', 'fix',
            'fix_am.v{}'.format(datestamp))
        if 's2s' in app:
            dir_fix_ufsatm = os.path.join(din_loc_root, 'global', 'fix',
                'fix_fv3_gmted2010.v{}'.format(datestamp), '{}_s2s'.format(
                atm_grid))
        else:
            dir_fix_ufsatm = os.path.join(din_loc_root, 'global', 'fix',
                'fix_fv3_gmted2010.v{}'.format(datestamp), atm_grid)
        dir_fix_parm = os.path.join(din_loc_root, 'parm')
        dir_fix_lib = os.path.join(din_loc_root, 'lib')
    input_files = dict()
    if 'hafs' in app:
        halo_lst = ['halo0', 'halo3', 'halo4']
        for halo in halo_lst:
            input_files['grid_tile7_{}'.format(halo)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_grid.tile7.{}.nc'.format(halo)
                ), 'ln', os.path.join('INPUT', atm_grid +
                '_grid.tile7.{}.nc'.format(halo))]
            input_files['orog_tile7_{}'.format(halo)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_oro_data.tile7.{}.nc'.format(
                halo)), 'ln', os.path.join('INPUT', atm_grid +
                '_oro_data.tile7.{}.nc'.format(halo))]
        input_files['oro_data_tile7h0'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_oro_data.tile7.halo0.nc'), 'ln', os.path.join(
            'INPUT', 'oro_data.nc')]
        input_files['oro_data_tile7h4'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_oro_data.tile7.halo4.nc'), 'ln', os.path.join(
            'INPUT', 'oro_data.tile7.halo4.nc')]
        input_files['grid_tile7h3'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_grid.tile7.halo3.nc'), 'ln', os.path.join('INPUT',
            atm_grid + '_grid.tile7.nc')]
        input_files['grid_tile7h0'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_grid.tile7.halo0.nc'), 'ln', os.path.join('INPUT',
            'grid.tile7.halo0.nc')]
        input_files['grid_tile7h4'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_grid.tile7.halo4.nc'), 'ln', os.path.join('INPUT',
            'grid.tile7.halo4.nc')]
    else:
        for tile in range(1, 7):
            input_files['grid_tile{}'.format(tile)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_grid.tile{}.nc'.format(tile)),
                'ln', os.path.join('INPUT', atm_grid + '_grid.tile{}.nc'.
                format(tile))]
            input_files['orog_data{}'.format(tile)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_oro_data.tile{}.nc'.format(
                tile)), 'ln', os.path.join('INPUT', 'oro_data.tile{}.nc'.
                format(tile))]
    if 's2s' in app:
        input_files['grid_spec'] = [os.path.join(icdir, 'grid_spec.nc'),
            'ln', os.path.join('INPUT', 'grid_spec.nc')]
    else:
        input_files['grid_spec'] = [os.path.join(dir_fix_ufsatm, atm_grid +
            '_mosaic.nc'), 'ln', os.path.join('INPUT', 'grid_spec.nc')]
    input_files['grid_mosaic'] = [os.path.join(dir_fix_ufsatm, atm_grid +
        '_mosaic.nc'), 'ln', os.path.join('INPUT', atm_grid + '_mosaic.nc')]
    iaer = nmlgen_input.get_value('iaer')
    ico2 = nmlgen_input.get_value('ico2')
    if new_o3forc:
        input_files['o3forc'] = [os.path.join(dir_fix_am,
            'ozprdlos_2015_new_sbuvO3_tclm15_nuchem.f77'), 'ln',
            'global_o3prdlos.f77']
    else:
        input_files['o3forc'] = [os.path.join(dir_fix_am,
            'global_o3prdlos.f77'), 'ln', 'global_o3prdlos.f77']
    input_files['h2oforc'] = [os.path.join(dir_fix_am,
        'global_h2o_pltc.f77'), 'ln', 'global_h2oprdlos.f77']
    input_files['solcnst'] = [os.path.join(dir_fix_am,
        'global_solarconstant_noaa_an.txt'), 'ln', 'solarconstant_noaa_an.txt']
    input_files['sfcemis'] = [os.path.join(dir_fix_am,
        'global_sfc_emissivity_idx.txt'), 'ln', 'sfc_emissivity_idx.txt']
    input_files['co2hist'] = [os.path.join(dir_fix_am,
        'global_co2historicaldata_glob.txt'), 'ln',
        'co2historicaldata_glob.txt']
    input_files['co2mont'] = [os.path.join(dir_fix_am, 'co2monthlycyc.txt'),
        'ln', 'co2monthlycyc.txt']
    ico2 = list_to_value(ico2)
    if ico2 and int(ico2) > 0:
        yy = int(time.strftime('%Y,%m,%d,%H,%M,%S').split(',')[0])
        for year in range(2009, yy + 1):
            input_files['co2his_{}'.format(year)] = [os.path.join(
                dir_fix_am, 'fix_co2_proj',
                'global_co2historicaldata_{}.txt'.format(year)), 'ln',
                'co2historicaldata_{}.txt'.format(year)]
    input_files['clmaero'] = [os.path.join(dir_fix_am,
        'global_climaeropac_global.txt'), 'ln', 'aerosol.dat']
    iaer = list_to_value(iaer)
    if iaer and int(iaer) > 0 and 'hafs' not in app:
        for year in range(1850, 2000, 10):
            input_files['volaer_{}'.format(year)] = [os.path.join(
                dir_fix_am, 'global_volcanic_aerosols_{}-{}.txt'.format(
                year, year + 9)), 'ln', 'volcanic_aerosols_{}-{}.txt'.
                format(year, year + 9)]
    for var in nmlgen_input.get_group_variables('namsfc'):
        var_str = str(nmlgen_input.get_value(var)).replace("'", '').strip()
        if not var_str.startswith('igbp') and var_str.endswith('.grb'):
            input_files[var] = [var_str, 'ln', os.path.basename(var_str)]
    nceplibs_dir = os.environ.get('NCEPLIBS_DIR')
    if 'case.chgres' in jobs:
        expect(nceplibs_dir,
            'NCEPLIBS_DIR environment variable need to be set to process raw input data!'
            )
        expect(os.path.exists(nceplibs_dir), 'NCEPLIBS_DIR {} not found'.
            format(nceplibs_dir))
    if 'case.chgres' in jobs:
        for tile in range(1, 7):
            for f in ['facsf', 'maximum_snow_albedo', 'slope_type',
                'snowfree_albedo', 'soil_type', 'substrate_temperature',
                'vegetation_greenness', 'vegetation_type']:
                input_files['{}{}'.format(f, tile)] = [os.path.join(
                    dir_fix_ufsatm, 'fix_sfc', atm_grid + '.{}.tile{}.nc'.
                    format(f, tile)), 'ln', os.path.join('INPUT', atm_grid +
                    '.{}.tile{}.nc'.format(f, tile))]
        levp = nmlgen_input.get_value('levp')
        input_files['global_hyblev'] = [os.path.join(dir_fix_am,
            'global_hyblev.l{}.txt'.format(levp)), 'ln', os.path.join(
            'INPUT', 'global_hyblev.l{}.txt'.format(levp))]
        input_files['var_map'] = [os.path.join(nceplibs_dir, 'share',
            'GFSphys_var_map.txt'), 'ln', 'GFSphys_var_map.txt']
    if 'case.gfs_post' in jobs:
        src_file = os.path.join(caseroot, 'SourceMods', 'src.ufsatm',
            'postxconfig-NT-GFS.txt')
        dst_file = os.path.join(rundir, 'postxconfig-NT-GFS.txt')
        if os.path.isfile(dst_file):
            os.remove(dst_file)
        if os.path.isfile(src_file):
            safe_copy(src_file, dst_file)
            logger.info('Using {} for post-processing'.format(src_file))
        else:
            logger.info('Using {} for post-processing'.format(os.path.join(
                nceplibs_dir, 'share', 'postxconfig-NT-GFS.txt')))
            input_files['postxconfig1'] = [os.path.join(nceplibs_dir,
                'share', 'postxconfig-NT-GFS.txt'), 'ln',
                'postxconfig-NT-GFS.txt']
        src_file = os.path.join(caseroot, 'SourceMods', 'src.ufsatm',
            'postxconfig-NT-GFS-F00.txt')
        dst_file = os.path.join(rundir, 'postxconfig-NT-GFS-F00.txt')
        if os.path.isfile(dst_file):
            os.remove(dst_file)
        if os.path.isfile(src_file):
            safe_copy(src_file, dst_file)
            logger.info('Using {} for post-processing'.format(src_file))
        else:
            logger.info('Using {} for post-processing'.format(os.path.join(
                nceplibs_dir, 'share', 'postxconfig-NT-GFS-F00.txt')))
            input_files['postxconfig2'] = [os.path.join(nceplibs_dir,
                'share', 'postxconfig-NT-GFS-F00.txt'), 'ln',
                'postxconfig-NT-GFS-F00.txt']
        input_files['params_grib2_tbl'] = [os.path.join(nceplibs_dir,
            'share', 'params_grib2_tbl_new'), 'ln', 'params_grib2_tbl_new']
    if not os.path.exists(rundir):
        os.makedirs(rundir)
    logger.info('\tCreating INPUT and RESTART directory')
    for _dir in ('INPUT', 'RESTART'):
        fulldir = os.path.join(rundir, _dir)
        if not os.path.exists(fulldir):
            os.makedirs(fulldir)
    rundirdst = rundir
    if rundir.endswith('case2run'):
        rundirsrc = os.path.abspath(os.path.join(rundir, os.pardir))
    else:
        rundirsrc = rundir
    if is_restart:
        tile_max = 7
        if 'hafs' in app:
            tile_max = 2
        lst = ['fv_core.res', 'fv_srf_wnd.res', 'fv_tracer.res', 'phy_data',
            'sfc_data']
        lst = [(prefix + f) for f in lst]
        for f in lst:
            for tile in range(1, tile_max):
                if '_data' in f and 'hafs' in app:
                    src_file = os.path.join(rundir, 'RESTART', '{}.nc'.
                        format(f))
                    tgt_file = os.path.join(rundir, 'INPUT', '{}.nc'.format
                        (f.replace(prefix, '')))
                else:
                    src_file = os.path.join(rundir, 'RESTART',
                        '{}.tile{}.nc'.format(f, tile))
                    tgt_file = os.path.join(rundir, 'INPUT', '{}.tile{}.nc'
                        .format(f.replace(prefix, ''), tile))
                if not os.path.isfile(src_file):
                    expect(False, '{} is missing.'.format(src_file))
                else:
                    safe_copy(src_file, tgt_file)
        lst = ['fv_core.res.nc', 'coupler.res']
        lst = [(prefix + f) for f in lst]
        for f in lst:
            src_file = os.path.join(rundir, 'RESTART', f)
            tgt_file = os.path.join(rundir, 'INPUT', f.replace(prefix, ''))
            if not os.path.isfile(src_file):
                expect(False, '{} is missing.'.format(src_file))
            else:
                safe_copy(src_file, tgt_file)
        if 'hafs' in app:
            lst = ls_sorted_by_mtime(dir_bdy)
            lst.remove('gfs_ctrl.nc')
            lst.remove('gfs_data.tile7.nc')
            lst.remove('sfc_data.tile7.nc')
            for f in lst:
                src_file = os.path.join(rundirdst, 'INPUT', f)
                if os.path.isfile(src_file):
                    os.remove(src_file)
                time_step = int(f.split('.')[2])
                if time_step < nhours_fcst_pre:
                    continue
                else:
                    src_file = os.path.join(dir_bdy, f)
                    dst_file = os.path.join(rundir, 'INPUT',
                        'gfs_bndy.tile7.{:03d}.nc'.format(time_step -
                        nhours_fcst_pre))
                    symlink_force(src_file, dst_file)
    elif 'hafs' in app:
        lst = ls_sorted_by_mtime(dir_bdy)
        for f in lst:
            symlink_force(os.path.join(dir_bdy, f), os.path.join(rundirdst,
                'INPUT', f))
        symlink_force(os.path.join(dir_bdy, 'sfc_data.tile7.nc'), os.path.
            join(rundirdst, 'INPUT', 'sfc_data.nc'))
        symlink_force(os.path.join(dir_bdy, 'gfs_data.tile7.nc'), os.path.
            join(rundirdst, 'INPUT', 'gfs_data.nc'))
    else:
        lst = ['fv_core.res', 'fv_srf_wnd.res', 'fv_tracer.res', 'phy_data',
            'sfc_data']
        for f in lst:
            for tile in range(1, 7):
                src_file = os.path.join(rundir, 'INPUT', '{}.tile{}.nc'.
                    format(f, tile))
                if os.path.isfile(src_file):
                    logger.warning('removing file {}'.format(src_file))
                    os.remove(src_file)
        lst = ['fv_core.res.nc', 'coupler.res']
        for f in lst:
            src_file = os.path.join(rundir, 'INPUT', f)
            if os.path.isfile(src_file):
                logger.warning('removing file {}'.format(src_file))
                os.remove(src_file)
        src_file = os.path.join(rundirsrc, 'INPUT', '{}.'.format(atm_grid) +
            '{:04d}-{:02d}-{:02d}'.format(yyyy, mm, dd) +
            '_{:02d}.gfs_ctrl.nc'.format(hh))
        if os.path.isfile(src_file):
            symlink_force(src_file, os.path.join(rundirdst, 'INPUT',
                'gfs_ctrl.nc'))
            lst = ['gfs_data', 'sfc_data']
            for f in lst:
                for tile in range(1, 7):
                    src_file = os.path.join(rundirsrc, 'INPUT', '{}.'.
                        format(atm_grid) + '{:04d}-{:02d}-{:02d}'.format(
                        yyyy, mm, dd) + '_{:02d}.'.format(hh) + f +
                        '.tile{}.nc'.format(tile))
                    tgt_file = os.path.join(rundirdst, 'INPUT',
                        '{}.tile{}.nc'.format(f, tile))
                    if os.path.isfile(src_file):
                        symlink_force(src_file, tgt_file)
                        logger.info('chgres source file {} linked to {}'.
                            format(src_file, tgt_file))
                    else:
                        logger.debug('chgres source file {} not found'.
                            format(src_file))
        else:
            src_file = os.path.join(icdir, 'gfs_ctrl.nc')
            if os.path.isfile(src_file):
                symlink_force(src_file, os.path.join(rundirdst, 'INPUT',
                    'gfs_ctrl.nc'))
            else:
                logger.warning(
                    'initial condition file {} is required! Exiting ...'.
                    format(src_file))
            lst = ['gfs_data', 'sfc_data']
            for f in lst:
                for tile in range(1, 7):
                    src_file = os.path.join(icdir, f + '.tile{}.nc'.format(
                        tile))
                    tgt_file = os.path.join(rundirdst, 'INPUT',
                        '{}.tile{}.nc'.format(f, tile))
                    if os.path.isfile(src_file):
                        symlink_force(src_file, tgt_file)
                        logger.info('initial condition file {} linked to {}'
                            .format(src_file, tgt_file))
                    else:
                        logger.warning(
                            'initial condition file {} is required! Exiting ...'
                            .format(src_file))
    with open(os.path.join(Buildconf, 'ufsatm.input_data_list'), 'w'
        ) as input_data_list:
        for k, v in input_files.items():
            input_data_list.write(k + ' = ' + v[0] + '\n')
    for k, v in input_files.items():
        if not os.path.isfile(v[0]):
            logger.warning(
                'WARNING: data file {} not found, will attempt to download.'
                .format(v[0]))
        elif 'ln' in v[1]:
            symlink_force(v[0], os.path.join(rundir, v[2]))
        elif 'cp':
            safe_copy(v[0], os.path.join(rundir, v[2]))
    cimeroot_ufsatm = os.path.join(srcroot, 'src', 'model', 'FV3', 'cime',
        'cime_config')
    for f in ['data_table_{}'.format(app), 'diag_table_{}'.format(app),
        'field_table_{}'.format(app)]:
        if not os.path.isfile(os.path.join(rundir, f)):
            safe_copy(os.path.join(cimeroot_ufsatm, 'tables', f), os.path.
                join(rundir, f.replace('_{}'.format(app), '')))


def _create_namelist_input(case, confdir, config, infile, nmlgen_model,
    nmlgen_input, nmlgen_input_def, namelist_user, datestamp, input_type):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    nmlgen_input.init_defaults(infile, config)
    nmlgen_input_def.init_defaults(infile, {})
    din_loc_root = case.get_value('DIN_LOC_ROOT')
    if 'hafs' in app:
        dir_fix_am = os.path.join(din_loc_root, 'regional', 'fix_am')
    else:
        dir_fix_am = os.path.join(din_loc_root, 'global', 'fix',
            'fix_am.v{}'.format(datestamp))
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    res = int(atm_grid.replace('C', ''))
    jcap = 2 * res - 2
    lonb = 4 * res
    latb = 2 * res
    if 's2s' in app:
        if res == 96:
            jcap = 126
            lonb = 384
            latb = 190
    nmlgen_input.set_value('fnglac', value=os.path.join(dir_fix_am,
        'global_glacier.2x2.grb'))
    nmlgen_input.set_value('fnmxic', value=os.path.join(dir_fix_am,
        'global_maxice.2x2.grb'))
    nmlgen_input.set_value('fntsfc', value=os.path.join(dir_fix_am,
        'RTGSST.1982.2012.monthly.clim.grb'))
    nmlgen_input.set_value('fnsnoc', value=os.path.join(dir_fix_am,
        'global_snoclim.1.875.grb'))
    nmlgen_input.set_value('fnzorc', value='igbp')
    nmlgen_input.set_value('fnalbc2', value=os.path.join(dir_fix_am,
        'global_albedo4.1x1.grb'))
    nmlgen_input.set_value('fnaisc', value=os.path.join(dir_fix_am,
        'CFSR.SEAICE.1982.2012.monthly.clim.grb'))
    nmlgen_input.set_value('fntg3c', value=os.path.join(dir_fix_am,
        'global_tg3clim.2.6x1.5.grb'))
    nmlgen_input.set_value('fnvegc', value=os.path.join(dir_fix_am,
        'global_vegfrac.0.144.decpercent.grb'))
    if 'mrweather' in app:
        nmlgen_input.set_value('fnmskh', value=os.path.join(dir_fix_am,
            'global_slmask.t1534.3072.1536.grb'))
    else:
        nmlgen_input.set_value('fnmskh', value=os.path.join(dir_fix_am,
            'seaice_newland.grb'))
    nmlgen_input.set_value('fnvmnc', value=os.path.join(dir_fix_am,
        'global_shdmin.0.144x0.144.grb'))
    nmlgen_input.set_value('fnvmxc', value=os.path.join(dir_fix_am,
        'global_shdmax.0.144x0.144.grb'))
    nmlgen_input.set_value('fnslpc', value=os.path.join(dir_fix_am,
        'global_slope.1x1.grb'))
    nmlgen_input.set_value('fnalbc', value=os.path.join(dir_fix_am,
        'global_snowfree_albedo.bosu.t{}.{}.{}.rg.grb'.format(jcap, lonb,
        latb)))
    nmlgen_input.set_value('fnvetc', value=os.path.join(dir_fix_am,
        'global_vegtype.igbp.t{}.{}.{}.rg.grb'.format(jcap, lonb, latb)))
    nmlgen_input.set_value('fnsotc', value=os.path.join(dir_fix_am,
        'global_soiltype.statsgo.t{}.{}.{}.rg.grb'.format(jcap, lonb, latb)))
    nmlgen_input.set_value('fnabsc', value=os.path.join(dir_fix_am,
        'global_mxsnoalb.uariz.t{}.{}.{}.rg.grb'.format(jcap, lonb, latb)))
    if 'mrweather' in app:
        if res == 96 or res == 192:
            jcap = 1534
            lonb = 3072
            latb = 1536
            nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,
                'global_soilmgldas.statsgo.t{}.{}.{}.grb'.format(jcap, lonb,
                latb)))
        else:
            nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,
                'global_soilmgldas.statsgo.t{}.{}.{}.grb'.format(jcap, lonb,
                latb)))
    else:
        nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,
            'global_soilmgldas.t{}.{}.{}.grb'.format(jcap, lonb, latb)))
    testcase = case.get_value('TEST')
    if testcase:
        nmlgen_input.set_value('iseed_skeb', 0)
        nmlgen_input.set_value('iseed_shum', 0)
        nmlgen_input.set_value('iseed_sppt', 0)
    else:
        member = 1
        cdate = int(datetime.now().strftime('%Y%m%d%H'))
        nmlgen_input.set_value('iseed_skeb', cdate * 1000 + member * 10 + 1)
        nmlgen_input.set_value('iseed_shum', cdate * 1000 + member * 10 + 2)
        nmlgen_input.set_value('iseed_sppt', cdate * 1000 + member * 10 + 3)
    nmlgen_input_sav = copy.copy(nmlgen_input)
    is_restart = case.get_value('CONTINUE_RUN')
    if is_restart:
        nmlgen_input.set_value('warm_start', True)
        nmlgen_input.set_value('nggps_ic', False)
        nmlgen_input.set_value('external_ic', False)
        nmlgen_input.set_value('mountain', True)
        nmlgen_input.set_value('make_nh', False)
        nmlgen_input.set_value('na_init', 0)
        nstf_name = nmlgen_input.get_value('nstf_name')
        nstf_name[1] = '0'
        read_increment = nmlgen_input.get_value('read_increment')
        if read_increment:
            nmlgen_input.set_value('res_latlon_dynamics', 'INPUT/fv_rst.res.nc'
                )
    else:
        nmlgen_input.set_value('warm_start', nmlgen_input_sav.get_value(
            'warm_start'))
        nmlgen_input.set_value('nggps_ic', nmlgen_input_sav.get_value(
            'nggps_ic'))
        nmlgen_input.set_value('external_ic', nmlgen_input_sav.get_value(
            'external_ic'))
        nmlgen_input.set_value('mountain', nmlgen_input_sav.get_value(
            'mountain'))
        nmlgen_input.set_value('make_nh', nmlgen_input_sav.get_value('make_nh')
            )
        nmlgen_input.set_value('na_init', nmlgen_input_sav.get_value('na_init')
            )
        if 'hafs' not in app:
            nstf_name = nmlgen_input_sav.get_value('nstf_name')
            if input_type == 'grib2':
                nstf_name[1] = '1'
            else:
                nstf_name[1] = '0'
        nmlgen_input.set_value('read_increment', nmlgen_input_sav.get_value
            ('read_increment'))
        nmlgen_input.set_value('res_latlon_dynamics', nmlgen_input_sav.
            get_value('res_latlon_dynamics'))
    if input_type == 'grib2' and 'hafs' not in app:
        nstf_name[0] = '0'
        logger.info('input_type {} nstf_name {}'.format(input_type, nstf_name))
        nmlgen_input.set_value('nstf_name', nstf_name)
    srcroot = case.get_value('SRCROOT')
    namelist_xml_dir = os.path.join(srcroot, 'src', 'model', 'FV3', 'cime',
        'cime_config')
    groups = []
    with open(os.path.join(namelist_xml_dir,
        'namelist_definition_ufsatm.xml'), 'r') as fin:
        for s in fin:
            if '<group>' in s:
                groups.append(s.replace('<group>', '').replace('</group>',
                    '').strip())
    groups = list(set(groups))
    groups.remove('config')
    groups.remove('_no_group_var')
    groups.remove('freeform')
    groups.remove('nampgb')
    groups.sort()
    nmlgen = nmlKeepChangedOnly(nmlgen_input_def, nmlgen_input, namelist_user)
    ntask_atm = int(case.get_value('NTASKS_ATM'))
    if ntask_atm == 8:
        nmlgen.set_value('layout', value=[1, 1])
    nmlgen = nmlOverwrite(namelist_user, nmlgen)
    nstf_name = nmlgen.get_value('nstf_name')
    if 'mrweather' in app:
        if not nstf_name[0] or nstf_name[0] == '0':
            ccpp = nmlgen.get_value('ccpp_suite')
            nmlgen.set_value('ccpp_suite', ccpp + '_no_nsst')
    layout = nmlgen_input.get_value('layout')
    layout_x = int(layout[0])
    layout_y = int(layout[1])
    ntiles = int(nmlgen_input.get_value('ntiles@fv_core_nml'))
    write_tasks_per_group = int(nmlgen_model.get_value('write_tasks_per_group')
        )
    write_groups = int(nmlgen_model.get_value('write_groups'))
    ntask_atm_config = (layout_x * layout_y * ntiles + 
        write_tasks_per_group * write_groups)
    if ntask_atm_config != ntask_atm:
        expect(False, (
            """Total number of PE need to be consistent with the model namelist options:
"""
             + '\tTotal number of PE (ntask_atm) = {}\n' +
            """	Decomposition in x and y direction (layout) = {}x{}
""" +
            '\tNumber of tile (ntiles) = {}\n' +
            '\tNumber of I/O group (write_groups) = {}\n' +
            """	Number of tasks in each I/O group (write_tasks_per_group) = {}
"""
            ).format(ntask_atm, layout_x, layout_y, ntiles, write_groups,
            write_tasks_per_group))
    namelist_file = os.path.join(confdir, 'atm_in')
    nmlgen.write_output_file(namelist_file, data_list_path, groups=groups,
        sorted_groups=False)


def _create_namelist_model_configure(case, confdir, config, infile, nmlgen,
    namelist_user, prefix):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    if os.path.exists(data_list_path):
        logger.warning('removing file {}'.format(data_list_path))
        os.remove(data_list_path)
    nmlgen.init_defaults(infile, config)
    is_restart = case.get_value('CONTINUE_RUN')
    global nhours_fcst_pre
    if is_restart:
        rundir = case.get_value('RUNDIR')
        rst_file = os.path.join(rundir, 'RESTART', '{}coupler.res'.format(
            prefix))
        if not os.path.isfile(rst_file):
            expect(False, "Couldn't find '" + rst_file + "'!")
        else:
            logger.debug('Using {} to determine previous forecast length ...'
                .format(rst_file))
            with open(rst_file, 'r') as f:
                lines = f.readlines()
                date1 = datetime(*[int(i) for i in lines[1].split()[0:6]])
                date2 = datetime(*[int(i) for i in lines[2].split()[0:6]])
                nhours_fcst_pre = int((date2 - date1).total_seconds() // 3600)
    else:
        nhours_fcst_pre = 0
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    nmlgen.set_value('start_year', value=yyyy)
    nmlgen.set_value('start_month', value=mm)
    nmlgen.set_value('start_day', value=dd)
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod // 3600)
    mi = int((run_start_tod - hh * 3600) // 60)
    ss = int(run_start_tod - hh * 3600 - mi * 60)
    nmlgen.set_value('start_hour', value=hh)
    nmlgen.set_value('start_minute', value=mi)
    nmlgen.set_value('start_second', value=ss)
    stop_option = case.get_value('STOP_OPTION')
    stop_n = int(case.get_value('STOP_N'))
    if 'nyears' in stop_option:
        expect(False, 'Option >nyears< is not supported!')
    elif 'nmonths' in stop_option:
        expect(False, 'Option >nmonths< is not supported!')
    elif 'ndays' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n * 24 + nhours_fcst_pre)
    elif 'nhours' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n + nhours_fcst_pre)
    elif 'nseconds' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n / 3600 + nhours_fcst_pre)
    elif 'nsteps' in stop_option:
        expect(False, 'Option >nsteps< is not supported!')
    rest_option = case.get_value('REST_OPTION')
    rest_n = int(case.get_value('REST_N'))
    if 'nyears' in rest_option:
        expect(False, 'Option >nyears< is not supported!')
    elif 'nmonths' in rest_option:
        expect(False, 'Option >nmonths< is not supported!')
    elif 'ndays' in rest_option:
        nmlgen.set_value('restart_interval', rest_n * 24)
    elif 'nhours' in rest_option:
        nmlgen.set_value('restart_interval', rest_n)
    elif 'nseconds' in rest_option:
        nmlgen.set_value('restart_interval', rest_n / 60)
    elif 'nsteps' in rest_option:
        expect(False, 'Option >nsteps< is not supported!')
    components = active_components(case)
    ntask = int(case.get_value('NTASKS_ATM'))
    if len(components) > 1:
        ntask = int(case.get_value('TOTALPES'))
    pe_member = int(nmlgen.get_value('PE_MEMBER01'))
    if pe_member != ntask:
        nmlgen.set_value('PE_MEMBER01', ntask)
    if ntask == 8:
        nmlgen.set_value('write_tasks_per_group', 2)
    testcase = case.get_value('TEST')
    if testcase:
        nmlgen.set_value('output_file', 'netcdf')
        nmlgen.set_value('filename_base', '{} {}'.format(case.get_value(
            'CASE') + '.ufsatm.atm.', case.get_value('CASE') + '.ufsatm.sfc.'))
    nmlgen.set_value('atmos_nthreads', case.thread_count)
    nmlgen.set_value('ncores_per_node', case.tasks_per_node)
    debug = case.get_value('DEBUG')
    if debug:
        nmlgen.set_value('print_esmf', True)
    else:
        nmlgen.set_value('print_esmf', False)
    nmlgen = nmlOverwrite(namelist_user, nmlgen)
    remove_lst = []
    if 'hafs' in app:
        remove_lst.extend(['imo', 'jmo', 'iau_offset', 'ideflate', 'nbits',
            'output_history', 'write_dopost', 'atm_coupling_interval_sec',
            'fhrot'])
    else:
        remove_lst.extend(['cen_lat', 'cen_lon', 'dlat', 'dlon', 'lat1',
            'lat2', 'lon1', 'lon2'])
        if 'mrweather' in app:
            remove_lst.append('atm_coupling_interval_sec')
            remove_lst.append('fhrot')
    for var in remove_lst:
        nmlgen._namelist.delete_variable('_no_group_var', var)
    model_config_file = os.path.join(confdir, 'model_configure')
    nmlgen.write_nuopc_config_file(model_config_file, data_list_path)


def _create_namelist_chgres(case, confdir, config, infile, nmlgen,
    nmlgen_def, nmlgen_input, namelist_user, input_type):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    nmlgen.init_defaults(infile, config)
    nmlgen_def.init_defaults(infile, {})
    rundir = case.get_value('RUNDIR')
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    nmlgen.set_value('mosaic_file_target_grid', os.path.join('INPUT', 
        atm_grid + '_mosaic.nc'))
    levp = nmlgen_input.get_value('levp')
    if levp is None:
        expect(False,
            "Number of vertical layer 'levp' must be a valid number! levp = {}"
            .format(levp))
    else:
        hyblev_file = os.path.join(rundir, 'INPUT', 'global_hyblev.l{}.txt'
            .format(levp))
        if not os.path.exists(hyblev_file):
            logger.warning(
                'WARNING: {} does not exist, will attempt to download.'.
                format(hyblev_file))
        else:
            nmlgen.set_value('vcoord_file_target_grid', os.path.join(
                'INPUT', 'global_hyblev.l{}.txt'.format(levp)))
    nmlgen.set_value('fix_dir_target_grid', os.path.join('INPUT'))
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod // 3600)
    nmlgen.set_value('cycle_mon', int(mm))
    nmlgen.set_value('cycle_day', int(dd))
    nmlgen.set_value('cycle_hour', int(hh))
    icdir = os.path.join(case.get_value('DIN_LOC_IC'), '{:04d}{:02d}'.
        format(yyyy, mm), '{:04d}{:02d}{:02d}'.format(yyyy, mm, dd))
    nmlgen.set_value('data_dir_input_grid', icdir)
    nmlgen.set_value('input_type', input_type)
    if input_type == 'gaussian_nemsio':
        nmlgen.set_value('atm_files_input_grid', ['atm.input.ic.nemsio'])
        nmlgen.set_value('sfc_files_input_grid', ['sfc.input.ic.nemsio'])
        tracers = ['sphum', 'liq_wat', 'o3mr', 'ice_wat', 'rainwat',
            'snowwat', 'graupel']
        nmlgen.set_value('tracers', tracers)
        tracers_input = ['spfh', 'clwmr', 'o3mr', 'icmr', 'rwmr', 'snmr',
            'grle']
        nmlgen.set_value('tracers_input', tracers_input)
        nmlgen.set_value('convert_nst', True)
    elif input_type == 'grib2':
        nmlgen.set_value('grib2_file_input_grid', 'atm.input.ic.grb2')
        nmlgen.set_value('varmap_file', 'GFSphys_var_map.txt')
    elif input_type == 'gaussian_netcdf':
        nmlgen.set_value('orog_dir_target_grid', os.path.join('INPUT'))
        oro_files = []
        for tile in range(1, 7):
            oro_files.append('oro_data.tile{}.nc'.format(tile))
        nmlgen.set_value('orog_files_target_grid', oro_files)
        nmlgen.set_value('atm_files_input_grid', ['atm.input.ic.nc'])
        nmlgen.set_value('sfc_files_input_grid', ['sfc.input.ic.nc'])
        nmlgen.set_value('convert_atm', True)
        nmlgen.set_value('convert_sfc', True)
        nmlgen.set_value('convert_nst', True)
        tracers = ['sphum', 'liq_wat', 'o3mr', 'ice_wat', 'rainwat',
            'snowwat', 'graupel']
        nmlgen.set_value('tracers', tracers)
        tracers_input = ['spfh', 'clwmr', 'o3mr', 'icmr', 'rwmr', 'snmr',
            'grle']
        nmlgen.set_value('tracers_input', tracers_input)
    mach = case.get_value('MACH')
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    tasks_per_node = 12
    task_count = {'C96': 1 * tasks_per_node, 'C192': 1 * tasks_per_node,
        'C384': 2 * tasks_per_node, 'C768': 3 * tasks_per_node}
    if atm_grid in task_count.keys():
        case.set_value('tasks_per_node', str(tasks_per_node), subgroup=
            'case.chgres')
        case.set_value('task_count', str(task_count[atm_grid]), subgroup=
            'case.chgres')
        logger.info(
            'CHGRES task count and tasks per node are modified! task_count = {} tasks_per_node = {}'
            .format(task_count[atm_grid], tasks_per_node))
        case.flush()
    nmlgen_mod = nmlKeepChangedOnly(nmlgen_def, nmlgen, namelist_user)
    nmlgen_mod = nmlOverwrite(namelist_user, nmlgen_mod)
    groups = ['config']
    namelist_file = os.path.join(confdir, 'config.nml')
    nmlgen_mod.write_output_file(namelist_file, data_list_path, groups=
        groups, sorted_groups=False)


def _create_namelist_ncep_post(case, confdir, config, infile, nmlgen,
    nmlgen_model_configure, namelist_user):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    nmlgen.init_defaults(infile, config)
    groups = ['nampgb']
    output_file = nmlgen_model_configure.get_value('output_file')
    if 'netcdf' in output_file:
        nmlgen.set_value('ioform', 'netcdf')
    elif 'nemsio' in output_file:
        nmlgen.set_value('ioform', 'binarynemsiompiio')
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yy = run_start_date[0]
    mm = run_start_date[1]
    dd = run_start_date[2]
    run_start_tod = int(case.get_value('START_TOD'))
    hh = run_start_tod // 3600
    mi = (run_start_tod - hh * 3600) // 60
    ss = run_start_tod - hh * 3600 - mi * 60
    nmlgen = nmlOverwrite(namelist_user, nmlgen)
    namelist_file = os.path.join(confdir, 'itag.tmp')
    nmlgen.write_output_file(namelist_file, data_list_path, groups=groups,
        sorted_groups=False)
    with open(namelist_file, 'r+') as f:
        content = f.read()
        f.seek(0, 0)
        f.write(nmlgen.get_value('filename') + '\n')
        f.write(nmlgen.get_value('ioform') + '\n')
        f.write(nmlgen.get_value('outform') + '\n')
        f.write('{}-{}-{}'.format(yy, mm, dd) + '_' +
            '{hh:02d}:{mm:02d}:{ss:02d}'.format(hh=hh, mm=mi, ss=ss) + '\n')
        f.write(nmlgen.get_value('modelname') + '\n')
        f.write(nmlgen.get_value('filenameflux') + '\n')
        f.write(content)
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    mach = case.get_value('MACH')
    tasks_per_node = int(case.get_value('MAX_TASKS_PER_NODE'))
    if ('C384' in atm_grid or 'C768' in atm_grid) and 'stampede2' in mach:
        tasks_per_node = 24
        case.set_value('tasks_per_node', str(tasks_per_node), subgroup=
            'case.gfs_post')
        case.flush()
        logger.info('NCEP Post tasks per node is changed to {}!'.format(
            tasks_per_node))
    task_count = {'C96': tasks_per_node, 'C192': tasks_per_node, 'C384': 
        tasks_per_node * 2, 'C768': tasks_per_node * 4}
    if atm_grid in task_count.keys():
        case.set_value('task_count', str(task_count[atm_grid]), subgroup=
            'case.gfs_post')
        case.flush()
        logger.info('NCEP Post task count is changed to {}!'.format(
            task_count[atm_grid]))


def buildnml(case, caseroot, compname):
    datestamp = '20191213'
    if compname != 'ufsatm':
        raise AttributeError
    srcroot = case.get_value('SRCROOT')
    rundir = case.get_value('RUNDIR')
    confdir = os.path.join(caseroot, 'Buildconf', 'ufsatmconf')
    if not os.path.isdir(confdir):
        os.makedirs(confdir)
    casedocsdir = os.path.join(caseroot, 'CaseDocs')
    compset = case.get_value('COMPSET')
    compset_dict = {'mrweather': [
        'FCST_ufsatm%v15p2_SLND_SICE_SOCN_SROF_SGLC_SWAV',
        'FCST_ufsatm%v16beta_SLND_SICE_SOCN_SROF_SGLC_SWAV'], 'hafs': [
        'FCST_ufsatm%v0nocp_SLND_SICE_SOCN_SROF_SGLC_SWAV']}
    global app
    app = 's2s'
    for key, value in compset_dict.items():
        for item in value:
            if item in compset:
                app = key
    ccpp_suite = case.get_value('CCPP_SUITES')
    config = {}
    config['app'] = app
    config['ccpp'] = ccpp_suite
    config['hgrid'] = case.get_value('ATM_GRID')
    config['ntasks'] = str(case.get_value('ATM_NTASKS'))
    namelist_xml_dir = os.path.join(srcroot, 'src', 'model', 'FV3', 'cime',
        'cime_config')
    definition_file = [os.path.join(namelist_xml_dir,
        'namelist_definition_ufsatm.xml')]
    user_nl_file = os.path.join(caseroot, 'user_nl_ufsatm')
    expect(os.path.isfile(user_nl_file), 
        'Missing required user_nl_file %s ' % user_nl_file)
    infile = os.path.join(confdir, 'namelist_infile')
    create_namelist_infile(case, user_nl_file, infile)
    namelist_infile = [infile]
    namelist_user = parse(infile)
    is_restart = case.get_value('CONTINUE_RUN')
    if is_restart:
        res_files = []
        for _, _, files in os.walk(os.path.join(rundir, 'RESTART')):
            for filename in files:
                if 'coupler.res' in filename:
                    res_files.append(filename)
        res_files.sort()
        nres = len(res_files)
        if nres > 1:
            prefix = res_files[nres - 2].replace('coupler.res', '')
        else:
            rst_file = os.path.join(rundir, 'RESTART', 'coupler.res')
            if not os.path.isfile(rst_file):
                expect(False,
                    '{} is missing. It is required to restart the model!'.
                    format(os.path.join(rundir, 'RESTART', 'coupler.res')))
            else:
                with open(rst_file, 'r') as f:
                    lines = f.readlines()
                    line = [int(x) for x in lines[2].split()[0:6]]
                    prefix = '{:04d}'.format(line[0])
                    prefix = prefix + ''.join('{:02d}'.format(x) for x in
                        line[1:3])
                    prefix = prefix + '.' + ''.join('{:02d}'.format(x) for
                        x in line[3:7])
                    prefix = prefix + '.'
            tile_max = 7
            if 'hafs' in app:
                tile_max = 2
            lst = ['fv_core.res', 'fv_srf_wnd.res', 'fv_tracer.res',
                'phy_data', 'sfc_data']
            for f in lst:
                for tile in range(1, tile_max):
                    if '_data' in f and 'hafs' in app:
                        src_file = os.path.join(rundir, 'RESTART', '{}.nc'.
                            format(f))
                        tgt_file = os.path.join(rundir, 'RESTART', '{}.nc'.
                            format(prefix + f))
                    else:
                        src_file = os.path.join(rundir, 'RESTART',
                            '{}.tile{}.nc'.format(f, tile))
                        tgt_file = os.path.join(rundir, 'RESTART',
                            '{}.tile{}.nc'.format(prefix + f, tile))
                    if not os.path.isfile(src_file):
                        expect(False, '{} is missing.'.format(src_file))
                    else:
                        safe_copy(src_file, tgt_file)
            lst = ['fv_core.res.nc', 'coupler.res']
            for f in lst:
                src_file = os.path.join(rundir, 'RESTART', f)
                tgt_file = os.path.join(rundir, 'RESTART', prefix + f)
                if not os.path.isfile(src_file):
                    expect(False, '{} is missing.'.format(src_file))
                else:
                    safe_copy(src_file, tgt_file)
    else:
        prefix = ''
    env_workflow = case.get_env('workflow')
    jobs = env_workflow.get_jobs()
    yyyy, mm, dd, hh = date_yyyymmddhh(case)
    if 'case.chgres' in jobs:
        if 'hafs' in app:
            din_loc_root = case.get_value('DIN_LOC_ROOT')
            icdir = os.path.join(din_loc_root, 'regional', 'bcond',
                '{:04d}{:02d}{:02d}{:02d}'.format(yyyy, mm, dd, hh))
        else:
            icdir = os.path.join(case.get_value('DIN_LOC_IC'),
                '{:04d}{:02d}'.format(yyyy, mm), '{:04d}{:02d}{:02d}'.
                format(yyyy, mm, dd))
        logger.info('Checking {} directory to find raw input files ...'.
            format(icdir))
        input_dict = {}
        if os.path.exists(icdir):
            for f in os.listdir(icdir):
                if 'atm.input.ic.nemsio' in f or 'sfc.input.ic.nemsio' in f:
                    if 'gaussian_nemsio' in input_dict.keys():
                        input_dict['gaussian_nemsio'].append(f)
                    else:
                        input_dict.update({'gaussian_nemsio': [f]})
                    logger.info("Found '{}' in input directory".format(f))
                elif 'atm.input.ic.grb2' in f:
                    if 'grib2' in input_dict.keys():
                        input_dict['grib2'].append(f)
                    else:
                        input_dict.update({'grib2': [f]})
                    logger.info("Found '{}' in input directory".format(f))
                elif 'atm.input.ic.nc' in f or 'sfc.input.ic.nc' in f:
                    if 'gaussian_netcdf' in input_dict.keys():
                        input_dict['gaussian_netcdf'].append(f)
                    else:
                        input_dict.update({'gaussian_netcdf': [f]})
                    logger.info("Found '{}' in input directory".format(f))
        input_type_lst = list(set(input_dict.keys()))
        input_type_user = namelist_user.get_value('input_type')[0]
        if input_type_user:
            input_type_lst = list([input_type_user.strip('"').strip("'")])
        if not input_type_lst:
            logger.info('No input file/s found locally! {}'.format(icdir))
            logger.info(
                'Please download raw initial condition and rename the file/s using following convention:'
                )
            logger.info(
                '\t NEMSIO: atm.input.ic.nemsio and sfc.input.ic.nemsio')
            logger.info('\t GRIB2 : atm.input.ic.grb2')
            logger.info('\t NetCDF: atm.input.ic.nc and sfc.input.ic.nc')
            expect(False, 'Exiting ...')
        else:
            if len(input_type_lst) > 1:
                input_type = 'grib2'
            else:
                input_type = input_type_lst[0]
            logger.info("CHGRES namelist option 'input_type' is set to {}".
                format(input_type))
            if input_type in input_dict.keys():
                for f in input_dict[input_type]:
                    if not os.path.exists(os.path.join(icdir, f)):
                        expect(False, '{} not found!'.format(os.path.join(
                            icdir, f)))
                    else:
                        logger.info('CHGRES will use {}'.format(os.path.
                            join(icdir, f)))
            else:
                if 'gaussian_nemsio' == input_type:
                    logger.info(
                        'Please download raw initial condition in NEMSIO format rename it as atm.input.ic.nemsio and sfc.input.ic.nemsio'
                        )
                elif 'grib2' == input_type:
                    logger.info(
                        'Please download raw initial condition in GRIB2 format and rename it as atm.input.ic.grb2'
                        )
                elif 'gaussian_netcdf' == input_type:
                    logger.info(
                        'Please download raw initial condition in NETCDF format and rename it as atm.input.ic.nc and sfc.input.ic.nc'
                        )
                else:
                    logger.info(
                        "Namelist option input_type can be 'gaussian_nemsio', 'grib2' or 'gaussian_netcdf'."
                        )
                expect(False, 'Exiting ...')
    else:
        input_type = None
        logger.info(
            "CHGRES is not the part of the workflow. There is no need to set 'input_type'!"
            )
    auto_namelist_file = os.path.join(confdir, 'model_configure')
    user_namelist_file = os.path.join(caseroot, 'SourceMods', 'src.ufsatm',
        'model_configure')
    if os.path.isfile(user_namelist_file):
        logger.info('Using user provided model_configure file {}'.format(
            user_namelist_file))
        if os.path.isfile(auto_namelist_file):
            os.remove(auto_namelist_file)
        safe_copy(user_namelist_file, auto_namelist_file)
    else:
        logger.info('Using auto-generated model_configure file {}'.format(
            auto_namelist_file))
        nmlgen_model_configure = NamelistGenerator(case, definition_file)
        _create_namelist_model_configure(case, confdir, config,
            namelist_infile, nmlgen_model_configure, namelist_user, prefix)
    if os.path.isdir(rundir):
        file1 = os.path.join(confdir, 'model_configure')
        file2 = os.path.join(rundir, 'model_configure')
        logger.debug('ufsatm configuration copy: file1 %s file2 %s ' % (
            file1, file2))
        safe_copy(file1, file2)
    auto_namelist_file = os.path.join(confdir, 'atm_in')
    user_namelist_file = os.path.join(caseroot, 'SourceMods', 'src.ufsatm',
        'atm_in')
    if os.path.isfile(user_namelist_file):
        logger.info('Using user provided atm_in file {}'.format(
            user_namelist_file))
        if os.path.isfile(auto_namelist_file):
            os.remove(auto_namelist_file)
        safe_copy(user_namelist_file, auto_namelist_file)
        nmlgen_input = parse(auto_namelist_file)
    else:
        logger.info('Using auto-generated atm_in file {}'.format(
            auto_namelist_file))
        nmlgen_input = NamelistGenerator(case, definition_file)
        nmlgen_input_def = NamelistGenerator(case, definition_file)
        _create_namelist_input(case, confdir, config, namelist_infile,
            nmlgen_model_configure, nmlgen_input, nmlgen_input_def,
            namelist_user, datestamp, input_type)
    if os.path.isdir(rundir):
        file1 = os.path.join(confdir, 'atm_in')
        file2 = os.path.join(rundir, 'atm_in')
        logger.debug('ufsatm namelist copy: file1 %s file2 %s ' % (file1,
            file2))
        safe_copy(file1, file2)
    logger.info('\tLinking input namelist for ufsatm')
    if not os.path.exists(rundir):
        expect(False, "Couldn't find run direcory " + rundir)
    if 'MOM6' not in compset:
        symlink_force(file2, os.path.join(rundir, 'input.nml'))
    else:
        symlink_force(file2, os.path.join(rundir, 'input.nml.fv3'))
    if os.path.isdir(casedocsdir):
        file1 = os.path.join(confdir, 'model_configure')
        file2 = os.path.join(casedocsdir, 'model_configure')
        safe_copy(file1, file2)
        file1 = os.path.join(confdir, 'atm_in')
        file2 = os.path.join(casedocsdir, 'input.nml.fv3')
        safe_copy(file1, file2)
    prep_input(case, casedocsdir, datestamp, nmlgen_input, prefix, input_type)
    if 'case.chgres' in jobs and not is_restart:
        nmlgen_chgres = NamelistGenerator(case, definition_file)
        nmlgen_chgres_def = NamelistGenerator(case, definition_file)
        _create_namelist_chgres(case, confdir, config, namelist_infile,
            nmlgen_chgres, nmlgen_chgres_def, nmlgen_input, namelist_user,
            input_type)
        if os.path.isdir(rundir):
            file1 = os.path.join(confdir, 'config.nml')
            file2 = os.path.join(rundir, 'config.nml')
            logger.debug('CHGRES configuration copy: file1 %s file2 %s ' %
                (file1, file2))
            safe_copy(file1, file2)
        if os.path.isdir(casedocsdir):
            file1 = os.path.join(confdir, 'config.nml')
            file2 = os.path.join(casedocsdir, 'config.nml')
            safe_copy(file1, file2)
    if 'case.gfs_post' in jobs:
        nmlgen_ncep_post = NamelistGenerator(case, definition_file)
        for f in os.listdir(rundir):
            if 'itag' in f:
                logger.warning('removing file {}'.format(f))
                os.remove(os.path.join(rundir, f))
        _create_namelist_ncep_post(case, confdir, config, namelist_infile,
            nmlgen_ncep_post, nmlgen_model_configure, namelist_user)
        if os.path.isdir(rundir):
            file1 = os.path.join(confdir, 'itag.tmp')
            file2 = os.path.join(rundir, 'itag.tmp')
            logger.debug('NCEP_POST configuration copy: file1 %s file2 %s ' %
                (file1, file2))
            safe_copy(file1, file2)
        if os.path.isdir(casedocsdir):
            file1 = os.path.join(confdir, 'itag.tmp')
            file2 = os.path.join(casedocsdir, 'itag.tmp')
            safe_copy(file1, file2)
    return


def _main_func():
    caseroot = parse_input(sys.argv)
    with Case(caseroot) as case:
        buildnml(case, caseroot, 'ufsatm')


<mask token>

<mask token>
CIMEROOT = os.environ.get('CIMEROOT')
if CIMEROOT is None:
    raise SystemExit('ERROR: must set CIMEROOT environment variable')
sys.path.append(os.path.join(CIMEROOT, 'scripts', 'Tools'))
<mask token>
logger = logging.getLogger(__name__)


def date_yyyymmddhh(case):
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod) // 3600
    return yyyy, mm, dd, hh


def factors(n):
    return set(x for tup in ([i, n // i] for i in range(1, int(n ** 0.5) + 
        1) if n % i == 0) for x in tup)


def checkList(lst):
    ele = lst[0]
    chk = True
    for item in lst:
        if ele != item:
            chk = False
            break
    return chk


def active_components(case):
    compset = case.get_value('COMPSET')
    components = compset.split('_')[1:]
    components = [x for x in components if 'S' not in x[0]]
    return components


def list_to_value(val):
    if isinstance(val, list):
        if len(val) == 1:
            return val[0]
        else:
            return val
    else:
        return val


def nmlKeepChangedOnly(nmlgen_ini, nmlgen_cur, namelist_user):
    nmlgen_ret = copy.copy(nmlgen_cur)
    for nml in nmlgen_cur._namelist.get_group_names():
        for var in nmlgen_cur.get_group_variables(nml):
            matched = False
            lst = namelist_user.get_value(var)
            if any(len(item) > 0 for item in lst):
                continue
            lst = ['ccpp_suite', 'nstf_name']
            if var in lst:
                continue
            var_type, _, var_size = nmlgen_cur._definition.split_type_string(
                var)
            ini = nmlgen_ini.get_value(var)
            cur = nmlgen_cur.get_value(var)
            if var_size > 1:
                var_act = [None] * var_size
                var_def = [None] * var_size
                var_act_len = len(cur)
                var_act[0:var_act_len] = [x for x in cur]
                var_def_len = len(ini)
                var_def[0:var_def_len] = [x for x in ini]
                if var_type == 'character' or var_type == 'logical':
                    var_act = [('xyz' if x is None else x.lower()) for x in
                        var_act]
                    var_def = [('xyz' if x is None else x.lower()) for x in
                        var_def]
                    matched = all(y.startswith(x) for x, y in zip(var_act,
                        var_def))
                elif var_type == 'integer':
                    var_act = [(-99 if x is None else int(x)) for x in var_act]
                    var_def = [(-99 if x is None else int(x)) for x in var_def]
                    matched = all(x == y for x, y in zip(var_act, var_def))
                elif var_type == 'real':
                    var_act = [(-99.9 if x is None else float(eval(x.lower(
                        ).replace('d', 'e')))) for x in var_act]
                    var_def = [(-99.9 if x is None else float(eval(x.lower(
                        ).replace('d', 'e')))) for x in var_def]
                    matched = all(x == y for x, y in zip(var_act, var_def))
            elif var_type == 'character' or var_type == 'logical':
                if not ini.strip() == '':
                    if cur.startswith(ini):
                        matched = True
                elif ini.strip() == '' and cur.strip() == '':
                    matched = True
            elif var_type == 'integer':
                if ini:
                    if int(cur) == int(ini):
                        matched = True
                elif not (ini and cur):
                    matched = True
            elif var_type == 'real' and ini and cur:
                if float(eval(ini.lower().replace('d', 'e'))) == float(eval
                    (cur.lower().replace('d', 'e'))):
                    matched = True
            if matched:
                nmlgen_ret._namelist.delete_variable(nml, var)
    return nmlgen_ret


def nmlOverwrite(nmlgen_src, nmlgen_dst):
    nmlgen_ret = copy.copy(nmlgen_dst)
    for nml in nmlgen_src.get_group_names():
        for var in nmlgen_src.get_group_variables(nml):
            nmlgen_ret.set_value(var, nmlgen_src.get_value(var))
    return nmlgen_ret


def prep_input(case, casedocsdir, datestamp, nmlgen_input, prefix, input_type):
    casename = case.get_value('CASE')
    Buildconf = case.get_value('CASEBUILD')
    rundir = case.get_value('RUNDIR')
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    testcase = case.get_value('TEST')
    din_loc_root = case.get_value('DIN_LOC_ROOT')
    din_loc_ic = case.get_value('DIN_LOC_IC')
    is_restart = case.get_value('CONTINUE_RUN')
    srcroot = case.get_value('SRCROOT')
    caseroot = case.get_value('CASEROOT')
    env_workflow = case.get_env('workflow')
    jobs = env_workflow.get_jobs()
    new_o3forc = True
    if 's2s' in app:
        new_o3forc = False
    yyyy, mm, dd, hh = date_yyyymmddhh(case)
    icdir = os.path.join(din_loc_ic, '{:04d}{:02d}'.format(yyyy, mm),
        '{:04d}{:02d}{:02d}'.format(yyyy, mm, dd))
    if 'hafs' in app:
        dir_fix_am = os.path.join(din_loc_root, 'regional', 'fix_am')
        dir_fix_ufsatm = os.path.join(din_loc_root, 'regional', 'fix', atm_grid
            )
        dir_fix_parm = os.path.join(din_loc_root, 'parm')
        dir_fix_lib = os.path.join(din_loc_root, 'lib')
        dir_bdy = os.path.join(din_loc_root, 'regional', 'bcond',
            '{:04d}{:02d}{:02d}{:02d}'.format(int(yyyy), int(mm), int(dd), hh))
    else:
        dir_fix_am = os.path.join(din_loc_root, 'global', 'fix',
            'fix_am.v{}'.format(datestamp))
        if 's2s' in app:
            dir_fix_ufsatm = os.path.join(din_loc_root, 'global', 'fix',
                'fix_fv3_gmted2010.v{}'.format(datestamp), '{}_s2s'.format(
                atm_grid))
        else:
            dir_fix_ufsatm = os.path.join(din_loc_root, 'global', 'fix',
                'fix_fv3_gmted2010.v{}'.format(datestamp), atm_grid)
        dir_fix_parm = os.path.join(din_loc_root, 'parm')
        dir_fix_lib = os.path.join(din_loc_root, 'lib')
    input_files = dict()
    if 'hafs' in app:
        halo_lst = ['halo0', 'halo3', 'halo4']
        for halo in halo_lst:
            input_files['grid_tile7_{}'.format(halo)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_grid.tile7.{}.nc'.format(halo)
                ), 'ln', os.path.join('INPUT', atm_grid +
                '_grid.tile7.{}.nc'.format(halo))]
            input_files['orog_tile7_{}'.format(halo)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_oro_data.tile7.{}.nc'.format(
                halo)), 'ln', os.path.join('INPUT', atm_grid +
                '_oro_data.tile7.{}.nc'.format(halo))]
        input_files['oro_data_tile7h0'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_oro_data.tile7.halo0.nc'), 'ln', os.path.join(
            'INPUT', 'oro_data.nc')]
        input_files['oro_data_tile7h4'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_oro_data.tile7.halo4.nc'), 'ln', os.path.join(
            'INPUT', 'oro_data.tile7.halo4.nc')]
        input_files['grid_tile7h3'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_grid.tile7.halo3.nc'), 'ln', os.path.join('INPUT',
            atm_grid + '_grid.tile7.nc')]
        input_files['grid_tile7h0'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_grid.tile7.halo0.nc'), 'ln', os.path.join('INPUT',
            'grid.tile7.halo0.nc')]
        input_files['grid_tile7h4'] = [os.path.join(dir_fix_ufsatm, 
            atm_grid + '_grid.tile7.halo4.nc'), 'ln', os.path.join('INPUT',
            'grid.tile7.halo4.nc')]
    else:
        for tile in range(1, 7):
            input_files['grid_tile{}'.format(tile)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_grid.tile{}.nc'.format(tile)),
                'ln', os.path.join('INPUT', atm_grid + '_grid.tile{}.nc'.
                format(tile))]
            input_files['orog_data{}'.format(tile)] = [os.path.join(
                dir_fix_ufsatm, atm_grid + '_oro_data.tile{}.nc'.format(
                tile)), 'ln', os.path.join('INPUT', 'oro_data.tile{}.nc'.
                format(tile))]
    if 's2s' in app:
        input_files['grid_spec'] = [os.path.join(icdir, 'grid_spec.nc'),
            'ln', os.path.join('INPUT', 'grid_spec.nc')]
    else:
        input_files['grid_spec'] = [os.path.join(dir_fix_ufsatm, atm_grid +
            '_mosaic.nc'), 'ln', os.path.join('INPUT', 'grid_spec.nc')]
    input_files['grid_mosaic'] = [os.path.join(dir_fix_ufsatm, atm_grid +
        '_mosaic.nc'), 'ln', os.path.join('INPUT', atm_grid + '_mosaic.nc')]
    iaer = nmlgen_input.get_value('iaer')
    ico2 = nmlgen_input.get_value('ico2')
    if new_o3forc:
        input_files['o3forc'] = [os.path.join(dir_fix_am,
            'ozprdlos_2015_new_sbuvO3_tclm15_nuchem.f77'), 'ln',
            'global_o3prdlos.f77']
    else:
        input_files['o3forc'] = [os.path.join(dir_fix_am,
            'global_o3prdlos.f77'), 'ln', 'global_o3prdlos.f77']
    input_files['h2oforc'] = [os.path.join(dir_fix_am,
        'global_h2o_pltc.f77'), 'ln', 'global_h2oprdlos.f77']
    input_files['solcnst'] = [os.path.join(dir_fix_am,
        'global_solarconstant_noaa_an.txt'), 'ln', 'solarconstant_noaa_an.txt']
    input_files['sfcemis'] = [os.path.join(dir_fix_am,
        'global_sfc_emissivity_idx.txt'), 'ln', 'sfc_emissivity_idx.txt']
    input_files['co2hist'] = [os.path.join(dir_fix_am,
        'global_co2historicaldata_glob.txt'), 'ln',
        'co2historicaldata_glob.txt']
    input_files['co2mont'] = [os.path.join(dir_fix_am, 'co2monthlycyc.txt'),
        'ln', 'co2monthlycyc.txt']
    ico2 = list_to_value(ico2)
    if ico2 and int(ico2) > 0:
        yy = int(time.strftime('%Y,%m,%d,%H,%M,%S').split(',')[0])
        for year in range(2009, yy + 1):
            input_files['co2his_{}'.format(year)] = [os.path.join(
                dir_fix_am, 'fix_co2_proj',
                'global_co2historicaldata_{}.txt'.format(year)), 'ln',
                'co2historicaldata_{}.txt'.format(year)]
    input_files['clmaero'] = [os.path.join(dir_fix_am,
        'global_climaeropac_global.txt'), 'ln', 'aerosol.dat']
    iaer = list_to_value(iaer)
    if iaer and int(iaer) > 0 and 'hafs' not in app:
        for year in range(1850, 2000, 10):
            input_files['volaer_{}'.format(year)] = [os.path.join(
                dir_fix_am, 'global_volcanic_aerosols_{}-{}.txt'.format(
                year, year + 9)), 'ln', 'volcanic_aerosols_{}-{}.txt'.
                format(year, year + 9)]
    for var in nmlgen_input.get_group_variables('namsfc'):
        var_str = str(nmlgen_input.get_value(var)).replace("'", '').strip()
        if not var_str.startswith('igbp') and var_str.endswith('.grb'):
            input_files[var] = [var_str, 'ln', os.path.basename(var_str)]
    nceplibs_dir = os.environ.get('NCEPLIBS_DIR')
    if 'case.chgres' in jobs:
        expect(nceplibs_dir,
            'NCEPLIBS_DIR environment variable need to be set to process raw input data!'
            )
        expect(os.path.exists(nceplibs_dir), 'NCEPLIBS_DIR {} not found'.
            format(nceplibs_dir))
    if 'case.chgres' in jobs:
        for tile in range(1, 7):
            for f in ['facsf', 'maximum_snow_albedo', 'slope_type',
                'snowfree_albedo', 'soil_type', 'substrate_temperature',
                'vegetation_greenness', 'vegetation_type']:
                input_files['{}{}'.format(f, tile)] = [os.path.join(
                    dir_fix_ufsatm, 'fix_sfc', atm_grid + '.{}.tile{}.nc'.
                    format(f, tile)), 'ln', os.path.join('INPUT', atm_grid +
                    '.{}.tile{}.nc'.format(f, tile))]
        levp = nmlgen_input.get_value('levp')
        input_files['global_hyblev'] = [os.path.join(dir_fix_am,
            'global_hyblev.l{}.txt'.format(levp)), 'ln', os.path.join(
            'INPUT', 'global_hyblev.l{}.txt'.format(levp))]
        input_files['var_map'] = [os.path.join(nceplibs_dir, 'share',
            'GFSphys_var_map.txt'), 'ln', 'GFSphys_var_map.txt']
    if 'case.gfs_post' in jobs:
        src_file = os.path.join(caseroot, 'SourceMods', 'src.ufsatm',
            'postxconfig-NT-GFS.txt')
        dst_file = os.path.join(rundir, 'postxconfig-NT-GFS.txt')
        if os.path.isfile(dst_file):
            os.remove(dst_file)
        if os.path.isfile(src_file):
            safe_copy(src_file, dst_file)
            logger.info('Using {} for post-processing'.format(src_file))
        else:
            logger.info('Using {} for post-processing'.format(os.path.join(
                nceplibs_dir, 'share', 'postxconfig-NT-GFS.txt')))
            input_files['postxconfig1'] = [os.path.join(nceplibs_dir,
                'share', 'postxconfig-NT-GFS.txt'), 'ln',
                'postxconfig-NT-GFS.txt']
        src_file = os.path.join(caseroot, 'SourceMods', 'src.ufsatm',
            'postxconfig-NT-GFS-F00.txt')
        dst_file = os.path.join(rundir, 'postxconfig-NT-GFS-F00.txt')
        if os.path.isfile(dst_file):
            os.remove(dst_file)
        if os.path.isfile(src_file):
            safe_copy(src_file, dst_file)
            logger.info('Using {} for post-processing'.format(src_file))
        else:
            logger.info('Using {} for post-processing'.format(os.path.join(
                nceplibs_dir, 'share', 'postxconfig-NT-GFS-F00.txt')))
            input_files['postxconfig2'] = [os.path.join(nceplibs_dir,
                'share', 'postxconfig-NT-GFS-F00.txt'), 'ln',
                'postxconfig-NT-GFS-F00.txt']
        input_files['params_grib2_tbl'] = [os.path.join(nceplibs_dir,
            'share', 'params_grib2_tbl_new'), 'ln', 'params_grib2_tbl_new']
    if not os.path.exists(rundir):
        os.makedirs(rundir)
    logger.info('\tCreating INPUT and RESTART directory')
    for _dir in ('INPUT', 'RESTART'):
        fulldir = os.path.join(rundir, _dir)
        if not os.path.exists(fulldir):
            os.makedirs(fulldir)
    rundirdst = rundir
    if rundir.endswith('case2run'):
        rundirsrc = os.path.abspath(os.path.join(rundir, os.pardir))
    else:
        rundirsrc = rundir
    if is_restart:
        tile_max = 7
        if 'hafs' in app:
            tile_max = 2
        lst = ['fv_core.res', 'fv_srf_wnd.res', 'fv_tracer.res', 'phy_data',
            'sfc_data']
        lst = [(prefix + f) for f in lst]
        for f in lst:
            for tile in range(1, tile_max):
                if '_data' in f and 'hafs' in app:
                    src_file = os.path.join(rundir, 'RESTART', '{}.nc'.
                        format(f))
                    tgt_file = os.path.join(rundir, 'INPUT', '{}.nc'.format
                        (f.replace(prefix, '')))
                else:
                    src_file = os.path.join(rundir, 'RESTART',
                        '{}.tile{}.nc'.format(f, tile))
                    tgt_file = os.path.join(rundir, 'INPUT', '{}.tile{}.nc'
                        .format(f.replace(prefix, ''), tile))
                if not os.path.isfile(src_file):
                    expect(False, '{} is missing.'.format(src_file))
                else:
                    safe_copy(src_file, tgt_file)
        lst = ['fv_core.res.nc', 'coupler.res']
        lst = [(prefix + f) for f in lst]
        for f in lst:
            src_file = os.path.join(rundir, 'RESTART', f)
            tgt_file = os.path.join(rundir, 'INPUT', f.replace(prefix, ''))
            if not os.path.isfile(src_file):
                expect(False, '{} is missing.'.format(src_file))
            else:
                safe_copy(src_file, tgt_file)
        if 'hafs' in app:
            lst = ls_sorted_by_mtime(dir_bdy)
            lst.remove('gfs_ctrl.nc')
            lst.remove('gfs_data.tile7.nc')
            lst.remove('sfc_data.tile7.nc')
            for f in lst:
                src_file = os.path.join(rundirdst, 'INPUT', f)
                if os.path.isfile(src_file):
                    os.remove(src_file)
                time_step = int(f.split('.')[2])
                if time_step < nhours_fcst_pre:
                    continue
                else:
                    src_file = os.path.join(dir_bdy, f)
                    dst_file = os.path.join(rundir, 'INPUT',
                        'gfs_bndy.tile7.{:03d}.nc'.format(time_step -
                        nhours_fcst_pre))
                    symlink_force(src_file, dst_file)
    elif 'hafs' in app:
        lst = ls_sorted_by_mtime(dir_bdy)
        for f in lst:
            symlink_force(os.path.join(dir_bdy, f), os.path.join(rundirdst,
                'INPUT', f))
        symlink_force(os.path.join(dir_bdy, 'sfc_data.tile7.nc'), os.path.
            join(rundirdst, 'INPUT', 'sfc_data.nc'))
        symlink_force(os.path.join(dir_bdy, 'gfs_data.tile7.nc'), os.path.
            join(rundirdst, 'INPUT', 'gfs_data.nc'))
    else:
        lst = ['fv_core.res', 'fv_srf_wnd.res', 'fv_tracer.res', 'phy_data',
            'sfc_data']
        for f in lst:
            for tile in range(1, 7):
                src_file = os.path.join(rundir, 'INPUT', '{}.tile{}.nc'.
                    format(f, tile))
                if os.path.isfile(src_file):
                    logger.warning('removing file {}'.format(src_file))
                    os.remove(src_file)
        lst = ['fv_core.res.nc', 'coupler.res']
        for f in lst:
            src_file = os.path.join(rundir, 'INPUT', f)
            if os.path.isfile(src_file):
                logger.warning('removing file {}'.format(src_file))
                os.remove(src_file)
        src_file = os.path.join(rundirsrc, 'INPUT', '{}.'.format(atm_grid) +
            '{:04d}-{:02d}-{:02d}'.format(yyyy, mm, dd) +
            '_{:02d}.gfs_ctrl.nc'.format(hh))
        if os.path.isfile(src_file):
            symlink_force(src_file, os.path.join(rundirdst, 'INPUT',
                'gfs_ctrl.nc'))
            lst = ['gfs_data', 'sfc_data']
            for f in lst:
                for tile in range(1, 7):
                    src_file = os.path.join(rundirsrc, 'INPUT', '{}.'.
                        format(atm_grid) + '{:04d}-{:02d}-{:02d}'.format(
                        yyyy, mm, dd) + '_{:02d}.'.format(hh) + f +
                        '.tile{}.nc'.format(tile))
                    tgt_file = os.path.join(rundirdst, 'INPUT',
                        '{}.tile{}.nc'.format(f, tile))
                    if os.path.isfile(src_file):
                        symlink_force(src_file, tgt_file)
                        logger.info('chgres source file {} linked to {}'.
                            format(src_file, tgt_file))
                    else:
                        logger.debug('chgres source file {} not found'.
                            format(src_file))
        else:
            src_file = os.path.join(icdir, 'gfs_ctrl.nc')
            if os.path.isfile(src_file):
                symlink_force(src_file, os.path.join(rundirdst, 'INPUT',
                    'gfs_ctrl.nc'))
            else:
                logger.warning(
                    'initial condition file {} is required! Exiting ...'.
                    format(src_file))
            lst = ['gfs_data', 'sfc_data']
            for f in lst:
                for tile in range(1, 7):
                    src_file = os.path.join(icdir, f + '.tile{}.nc'.format(
                        tile))
                    tgt_file = os.path.join(rundirdst, 'INPUT',
                        '{}.tile{}.nc'.format(f, tile))
                    if os.path.isfile(src_file):
                        symlink_force(src_file, tgt_file)
                        logger.info('initial condition file {} linked to {}'
                            .format(src_file, tgt_file))
                    else:
                        logger.warning(
                            'initial condition file {} is required! Exiting ...'
                            .format(src_file))
    with open(os.path.join(Buildconf, 'ufsatm.input_data_list'), 'w'
        ) as input_data_list:
        for k, v in input_files.items():
            input_data_list.write(k + ' = ' + v[0] + '\n')
    for k, v in input_files.items():
        if not os.path.isfile(v[0]):
            logger.warning(
                'WARNING: data file {} not found, will attempt to download.'
                .format(v[0]))
        elif 'ln' in v[1]:
            symlink_force(v[0], os.path.join(rundir, v[2]))
        elif 'cp':
            safe_copy(v[0], os.path.join(rundir, v[2]))
    cimeroot_ufsatm = os.path.join(srcroot, 'src', 'model', 'FV3', 'cime',
        'cime_config')
    for f in ['data_table_{}'.format(app), 'diag_table_{}'.format(app),
        'field_table_{}'.format(app)]:
        if not os.path.isfile(os.path.join(rundir, f)):
            safe_copy(os.path.join(cimeroot_ufsatm, 'tables', f), os.path.
                join(rundir, f.replace('_{}'.format(app), '')))


def _create_namelist_input(case, confdir, config, infile, nmlgen_model,
    nmlgen_input, nmlgen_input_def, namelist_user, datestamp, input_type):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    nmlgen_input.init_defaults(infile, config)
    nmlgen_input_def.init_defaults(infile, {})
    din_loc_root = case.get_value('DIN_LOC_ROOT')
    if 'hafs' in app:
        dir_fix_am = os.path.join(din_loc_root, 'regional', 'fix_am')
    else:
        dir_fix_am = os.path.join(din_loc_root, 'global', 'fix',
            'fix_am.v{}'.format(datestamp))
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    res = int(atm_grid.replace('C', ''))
    jcap = 2 * res - 2
    lonb = 4 * res
    latb = 2 * res
    if 's2s' in app:
        if res == 96:
            jcap = 126
            lonb = 384
            latb = 190
    nmlgen_input.set_value('fnglac', value=os.path.join(dir_fix_am,
        'global_glacier.2x2.grb'))
    nmlgen_input.set_value('fnmxic', value=os.path.join(dir_fix_am,
        'global_maxice.2x2.grb'))
    nmlgen_input.set_value('fntsfc', value=os.path.join(dir_fix_am,
        'RTGSST.1982.2012.monthly.clim.grb'))
    nmlgen_input.set_value('fnsnoc', value=os.path.join(dir_fix_am,
        'global_snoclim.1.875.grb'))
    nmlgen_input.set_value('fnzorc', value='igbp')
    nmlgen_input.set_value('fnalbc2', value=os.path.join(dir_fix_am,
        'global_albedo4.1x1.grb'))
    nmlgen_input.set_value('fnaisc', value=os.path.join(dir_fix_am,
        'CFSR.SEAICE.1982.2012.monthly.clim.grb'))
    nmlgen_input.set_value('fntg3c', value=os.path.join(dir_fix_am,
        'global_tg3clim.2.6x1.5.grb'))
    nmlgen_input.set_value('fnvegc', value=os.path.join(dir_fix_am,
        'global_vegfrac.0.144.decpercent.grb'))
    if 'mrweather' in app:
        nmlgen_input.set_value('fnmskh', value=os.path.join(dir_fix_am,
            'global_slmask.t1534.3072.1536.grb'))
    else:
        nmlgen_input.set_value('fnmskh', value=os.path.join(dir_fix_am,
            'seaice_newland.grb'))
    nmlgen_input.set_value('fnvmnc', value=os.path.join(dir_fix_am,
        'global_shdmin.0.144x0.144.grb'))
    nmlgen_input.set_value('fnvmxc', value=os.path.join(dir_fix_am,
        'global_shdmax.0.144x0.144.grb'))
    nmlgen_input.set_value('fnslpc', value=os.path.join(dir_fix_am,
        'global_slope.1x1.grb'))
    nmlgen_input.set_value('fnalbc', value=os.path.join(dir_fix_am,
        'global_snowfree_albedo.bosu.t{}.{}.{}.rg.grb'.format(jcap, lonb,
        latb)))
    nmlgen_input.set_value('fnvetc', value=os.path.join(dir_fix_am,
        'global_vegtype.igbp.t{}.{}.{}.rg.grb'.format(jcap, lonb, latb)))
    nmlgen_input.set_value('fnsotc', value=os.path.join(dir_fix_am,
        'global_soiltype.statsgo.t{}.{}.{}.rg.grb'.format(jcap, lonb, latb)))
    nmlgen_input.set_value('fnabsc', value=os.path.join(dir_fix_am,
        'global_mxsnoalb.uariz.t{}.{}.{}.rg.grb'.format(jcap, lonb, latb)))
    if 'mrweather' in app:
        if res == 96 or res == 192:
            jcap = 1534
            lonb = 3072
            latb = 1536
            nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,
                'global_soilmgldas.statsgo.t{}.{}.{}.grb'.format(jcap, lonb,
                latb)))
        else:
            nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,
                'global_soilmgldas.statsgo.t{}.{}.{}.grb'.format(jcap, lonb,
                latb)))
    else:
        nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,
            'global_soilmgldas.t{}.{}.{}.grb'.format(jcap, lonb, latb)))
    testcase = case.get_value('TEST')
    if testcase:
        nmlgen_input.set_value('iseed_skeb', 0)
        nmlgen_input.set_value('iseed_shum', 0)
        nmlgen_input.set_value('iseed_sppt', 0)
    else:
        member = 1
        cdate = int(datetime.now().strftime('%Y%m%d%H'))
        nmlgen_input.set_value('iseed_skeb', cdate * 1000 + member * 10 + 1)
        nmlgen_input.set_value('iseed_shum', cdate * 1000 + member * 10 + 2)
        nmlgen_input.set_value('iseed_sppt', cdate * 1000 + member * 10 + 3)
    nmlgen_input_sav = copy.copy(nmlgen_input)
    is_restart = case.get_value('CONTINUE_RUN')
    if is_restart:
        nmlgen_input.set_value('warm_start', True)
        nmlgen_input.set_value('nggps_ic', False)
        nmlgen_input.set_value('external_ic', False)
        nmlgen_input.set_value('mountain', True)
        nmlgen_input.set_value('make_nh', False)
        nmlgen_input.set_value('na_init', 0)
        nstf_name = nmlgen_input.get_value('nstf_name')
        nstf_name[1] = '0'
        read_increment = nmlgen_input.get_value('read_increment')
        if read_increment:
            nmlgen_input.set_value('res_latlon_dynamics', 'INPUT/fv_rst.res.nc'
                )
    else:
        nmlgen_input.set_value('warm_start', nmlgen_input_sav.get_value(
            'warm_start'))
        nmlgen_input.set_value('nggps_ic', nmlgen_input_sav.get_value(
            'nggps_ic'))
        nmlgen_input.set_value('external_ic', nmlgen_input_sav.get_value(
            'external_ic'))
        nmlgen_input.set_value('mountain', nmlgen_input_sav.get_value(
            'mountain'))
        nmlgen_input.set_value('make_nh', nmlgen_input_sav.get_value('make_nh')
            )
        nmlgen_input.set_value('na_init', nmlgen_input_sav.get_value('na_init')
            )
        if 'hafs' not in app:
            nstf_name = nmlgen_input_sav.get_value('nstf_name')
            if input_type == 'grib2':
                nstf_name[1] = '1'
            else:
                nstf_name[1] = '0'
        nmlgen_input.set_value('read_increment', nmlgen_input_sav.get_value
            ('read_increment'))
        nmlgen_input.set_value('res_latlon_dynamics', nmlgen_input_sav.
            get_value('res_latlon_dynamics'))
    if input_type == 'grib2' and 'hafs' not in app:
        nstf_name[0] = '0'
        logger.info('input_type {} nstf_name {}'.format(input_type, nstf_name))
        nmlgen_input.set_value('nstf_name', nstf_name)
    srcroot = case.get_value('SRCROOT')
    namelist_xml_dir = os.path.join(srcroot, 'src', 'model', 'FV3', 'cime',
        'cime_config')
    groups = []
    with open(os.path.join(namelist_xml_dir,
        'namelist_definition_ufsatm.xml'), 'r') as fin:
        for s in fin:
            if '<group>' in s:
                groups.append(s.replace('<group>', '').replace('</group>',
                    '').strip())
    groups = list(set(groups))
    groups.remove('config')
    groups.remove('_no_group_var')
    groups.remove('freeform')
    groups.remove('nampgb')
    groups.sort()
    nmlgen = nmlKeepChangedOnly(nmlgen_input_def, nmlgen_input, namelist_user)
    ntask_atm = int(case.get_value('NTASKS_ATM'))
    if ntask_atm == 8:
        nmlgen.set_value('layout', value=[1, 1])
    nmlgen = nmlOverwrite(namelist_user, nmlgen)
    nstf_name = nmlgen.get_value('nstf_name')
    if 'mrweather' in app:
        if not nstf_name[0] or nstf_name[0] == '0':
            ccpp = nmlgen.get_value('ccpp_suite')
            nmlgen.set_value('ccpp_suite', ccpp + '_no_nsst')
    layout = nmlgen_input.get_value('layout')
    layout_x = int(layout[0])
    layout_y = int(layout[1])
    ntiles = int(nmlgen_input.get_value('ntiles@fv_core_nml'))
    write_tasks_per_group = int(nmlgen_model.get_value('write_tasks_per_group')
        )
    write_groups = int(nmlgen_model.get_value('write_groups'))
    ntask_atm_config = (layout_x * layout_y * ntiles + 
        write_tasks_per_group * write_groups)
    if ntask_atm_config != ntask_atm:
        expect(False, (
            """Total number of PE need to be consistent with the model namelist options:
"""
             + '\tTotal number of PE (ntask_atm) = {}\n' +
            """	Decomposition in x and y direction (layout) = {}x{}
""" +
            '\tNumber of tile (ntiles) = {}\n' +
            '\tNumber of I/O group (write_groups) = {}\n' +
            """	Number of tasks in each I/O group (write_tasks_per_group) = {}
"""
            ).format(ntask_atm, layout_x, layout_y, ntiles, write_groups,
            write_tasks_per_group))
    namelist_file = os.path.join(confdir, 'atm_in')
    nmlgen.write_output_file(namelist_file, data_list_path, groups=groups,
        sorted_groups=False)


def _create_namelist_model_configure(case, confdir, config, infile, nmlgen,
    namelist_user, prefix):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    if os.path.exists(data_list_path):
        logger.warning('removing file {}'.format(data_list_path))
        os.remove(data_list_path)
    nmlgen.init_defaults(infile, config)
    is_restart = case.get_value('CONTINUE_RUN')
    global nhours_fcst_pre
    if is_restart:
        rundir = case.get_value('RUNDIR')
        rst_file = os.path.join(rundir, 'RESTART', '{}coupler.res'.format(
            prefix))
        if not os.path.isfile(rst_file):
            expect(False, "Couldn't find '" + rst_file + "'!")
        else:
            logger.debug('Using {} to determine previous forecast length ...'
                .format(rst_file))
            with open(rst_file, 'r') as f:
                lines = f.readlines()
                date1 = datetime(*[int(i) for i in lines[1].split()[0:6]])
                date2 = datetime(*[int(i) for i in lines[2].split()[0:6]])
                nhours_fcst_pre = int((date2 - date1).total_seconds() // 3600)
    else:
        nhours_fcst_pre = 0
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    nmlgen.set_value('start_year', value=yyyy)
    nmlgen.set_value('start_month', value=mm)
    nmlgen.set_value('start_day', value=dd)
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod // 3600)
    mi = int((run_start_tod - hh * 3600) // 60)
    ss = int(run_start_tod - hh * 3600 - mi * 60)
    nmlgen.set_value('start_hour', value=hh)
    nmlgen.set_value('start_minute', value=mi)
    nmlgen.set_value('start_second', value=ss)
    stop_option = case.get_value('STOP_OPTION')
    stop_n = int(case.get_value('STOP_N'))
    if 'nyears' in stop_option:
        expect(False, 'Option >nyears< is not supported!')
    elif 'nmonths' in stop_option:
        expect(False, 'Option >nmonths< is not supported!')
    elif 'ndays' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n * 24 + nhours_fcst_pre)
    elif 'nhours' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n + nhours_fcst_pre)
    elif 'nseconds' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n / 3600 + nhours_fcst_pre)
    elif 'nsteps' in stop_option:
        expect(False, 'Option >nsteps< is not supported!')
    rest_option = case.get_value('REST_OPTION')
    rest_n = int(case.get_value('REST_N'))
    if 'nyears' in rest_option:
        expect(False, 'Option >nyears< is not supported!')
    elif 'nmonths' in rest_option:
        expect(False, 'Option >nmonths< is not supported!')
    elif 'ndays' in rest_option:
        nmlgen.set_value('restart_interval', rest_n * 24)
    elif 'nhours' in rest_option:
        nmlgen.set_value('restart_interval', rest_n)
    elif 'nseconds' in rest_option:
        nmlgen.set_value('restart_interval', rest_n / 60)
    elif 'nsteps' in rest_option:
        expect(False, 'Option >nsteps< is not supported!')
    components = active_components(case)
    ntask = int(case.get_value('NTASKS_ATM'))
    if len(components) > 1:
        ntask = int(case.get_value('TOTALPES'))
    pe_member = int(nmlgen.get_value('PE_MEMBER01'))
    if pe_member != ntask:
        nmlgen.set_value('PE_MEMBER01', ntask)
    if ntask == 8:
        nmlgen.set_value('write_tasks_per_group', 2)
    testcase = case.get_value('TEST')
    if testcase:
        nmlgen.set_value('output_file', 'netcdf')
        nmlgen.set_value('filename_base', '{} {}'.format(case.get_value(
            'CASE') + '.ufsatm.atm.', case.get_value('CASE') + '.ufsatm.sfc.'))
    nmlgen.set_value('atmos_nthreads', case.thread_count)
    nmlgen.set_value('ncores_per_node', case.tasks_per_node)
    debug = case.get_value('DEBUG')
    if debug:
        nmlgen.set_value('print_esmf', True)
    else:
        nmlgen.set_value('print_esmf', False)
    nmlgen = nmlOverwrite(namelist_user, nmlgen)
    remove_lst = []
    if 'hafs' in app:
        remove_lst.extend(['imo', 'jmo', 'iau_offset', 'ideflate', 'nbits',
            'output_history', 'write_dopost', 'atm_coupling_interval_sec',
            'fhrot'])
    else:
        remove_lst.extend(['cen_lat', 'cen_lon', 'dlat', 'dlon', 'lat1',
            'lat2', 'lon1', 'lon2'])
        if 'mrweather' in app:
            remove_lst.append('atm_coupling_interval_sec')
            remove_lst.append('fhrot')
    for var in remove_lst:
        nmlgen._namelist.delete_variable('_no_group_var', var)
    model_config_file = os.path.join(confdir, 'model_configure')
    nmlgen.write_nuopc_config_file(model_config_file, data_list_path)


def _create_namelist_chgres(case, confdir, config, infile, nmlgen,
    nmlgen_def, nmlgen_input, namelist_user, input_type):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    nmlgen.init_defaults(infile, config)
    nmlgen_def.init_defaults(infile, {})
    rundir = case.get_value('RUNDIR')
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    nmlgen.set_value('mosaic_file_target_grid', os.path.join('INPUT', 
        atm_grid + '_mosaic.nc'))
    levp = nmlgen_input.get_value('levp')
    if levp is None:
        expect(False,
            "Number of vertical layer 'levp' must be a valid number! levp = {}"
            .format(levp))
    else:
        hyblev_file = os.path.join(rundir, 'INPUT', 'global_hyblev.l{}.txt'
            .format(levp))
        if not os.path.exists(hyblev_file):
            logger.warning(
                'WARNING: {} does not exist, will attempt to download.'.
                format(hyblev_file))
        else:
            nmlgen.set_value('vcoord_file_target_grid', os.path.join(
                'INPUT', 'global_hyblev.l{}.txt'.format(levp)))
    nmlgen.set_value('fix_dir_target_grid', os.path.join('INPUT'))
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod // 3600)
    nmlgen.set_value('cycle_mon', int(mm))
    nmlgen.set_value('cycle_day', int(dd))
    nmlgen.set_value('cycle_hour', int(hh))
    icdir = os.path.join(case.get_value('DIN_LOC_IC'), '{:04d}{:02d}'.
        format(yyyy, mm), '{:04d}{:02d}{:02d}'.format(yyyy, mm, dd))
    nmlgen.set_value('data_dir_input_grid', icdir)
    nmlgen.set_value('input_type', input_type)
    if input_type == 'gaussian_nemsio':
        nmlgen.set_value('atm_files_input_grid', ['atm.input.ic.nemsio'])
        nmlgen.set_value('sfc_files_input_grid', ['sfc.input.ic.nemsio'])
        tracers = ['sphum', 'liq_wat', 'o3mr', 'ice_wat', 'rainwat',
            'snowwat', 'graupel']
        nmlgen.set_value('tracers', tracers)
        tracers_input = ['spfh', 'clwmr', 'o3mr', 'icmr', 'rwmr', 'snmr',
            'grle']
        nmlgen.set_value('tracers_input', tracers_input)
        nmlgen.set_value('convert_nst', True)
    elif input_type == 'grib2':
        nmlgen.set_value('grib2_file_input_grid', 'atm.input.ic.grb2')
        nmlgen.set_value('varmap_file', 'GFSphys_var_map.txt')
    elif input_type == 'gaussian_netcdf':
        nmlgen.set_value('orog_dir_target_grid', os.path.join('INPUT'))
        oro_files = []
        for tile in range(1, 7):
            oro_files.append('oro_data.tile{}.nc'.format(tile))
        nmlgen.set_value('orog_files_target_grid', oro_files)
        nmlgen.set_value('atm_files_input_grid', ['atm.input.ic.nc'])
        nmlgen.set_value('sfc_files_input_grid', ['sfc.input.ic.nc'])
        nmlgen.set_value('convert_atm', True)
        nmlgen.set_value('convert_sfc', True)
        nmlgen.set_value('convert_nst', True)
        tracers = ['sphum', 'liq_wat', 'o3mr', 'ice_wat', 'rainwat',
            'snowwat', 'graupel']
        nmlgen.set_value('tracers', tracers)
        tracers_input = ['spfh', 'clwmr', 'o3mr', 'icmr', 'rwmr', 'snmr',
            'grle']
        nmlgen.set_value('tracers_input', tracers_input)
    mach = case.get_value('MACH')
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    tasks_per_node = 12
    task_count = {'C96': 1 * tasks_per_node, 'C192': 1 * tasks_per_node,
        'C384': 2 * tasks_per_node, 'C768': 3 * tasks_per_node}
    if atm_grid in task_count.keys():
        case.set_value('tasks_per_node', str(tasks_per_node), subgroup=
            'case.chgres')
        case.set_value('task_count', str(task_count[atm_grid]), subgroup=
            'case.chgres')
        logger.info(
            'CHGRES task count and tasks per node are modified! task_count = {} tasks_per_node = {}'
            .format(task_count[atm_grid], tasks_per_node))
        case.flush()
    nmlgen_mod = nmlKeepChangedOnly(nmlgen_def, nmlgen, namelist_user)
    nmlgen_mod = nmlOverwrite(namelist_user, nmlgen_mod)
    groups = ['config']
    namelist_file = os.path.join(confdir, 'config.nml')
    nmlgen_mod.write_output_file(namelist_file, data_list_path, groups=
        groups, sorted_groups=False)


def _create_namelist_ncep_post(case, confdir, config, infile, nmlgen,
    nmlgen_model_configure, namelist_user):
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    data_list_path = os.path.join(case.get_case_root(), 'Buildconf',
        'ufsatm.input_data_list')
    nmlgen.init_defaults(infile, config)
    groups = ['nampgb']
    output_file = nmlgen_model_configure.get_value('output_file')
    if 'netcdf' in output_file:
        nmlgen.set_value('ioform', 'netcdf')
    elif 'nemsio' in output_file:
        nmlgen.set_value('ioform', 'binarynemsiompiio')
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yy = run_start_date[0]
    mm = run_start_date[1]
    dd = run_start_date[2]
    run_start_tod = int(case.get_value('START_TOD'))
    hh = run_start_tod // 3600
    mi = (run_start_tod - hh * 3600) // 60
    ss = run_start_tod - hh * 3600 - mi * 60
    nmlgen = nmlOverwrite(namelist_user, nmlgen)
    namelist_file = os.path.join(confdir, 'itag.tmp')
    nmlgen.write_output_file(namelist_file, data_list_path, groups=groups,
        sorted_groups=False)
    with open(namelist_file, 'r+') as f:
        content = f.read()
        f.seek(0, 0)
        f.write(nmlgen.get_value('filename') + '\n')
        f.write(nmlgen.get_value('ioform') + '\n')
        f.write(nmlgen.get_value('outform') + '\n')
        f.write('{}-{}-{}'.format(yy, mm, dd) + '_' +
            '{hh:02d}:{mm:02d}:{ss:02d}'.format(hh=hh, mm=mi, ss=ss) + '\n')
        f.write(nmlgen.get_value('modelname') + '\n')
        f.write(nmlgen.get_value('filenameflux') + '\n')
        f.write(content)
    atm_grid = case.get_value('ATM_GRID').replace('r', '')
    mach = case.get_value('MACH')
    tasks_per_node = int(case.get_value('MAX_TASKS_PER_NODE'))
    if ('C384' in atm_grid or 'C768' in atm_grid) and 'stampede2' in mach:
        tasks_per_node = 24
        case.set_value('tasks_per_node', str(tasks_per_node), subgroup=
            'case.gfs_post')
        case.flush()
        logger.info('NCEP Post tasks per node is changed to {}!'.format(
            tasks_per_node))
    task_count = {'C96': tasks_per_node, 'C192': tasks_per_node, 'C384': 
        tasks_per_node * 2, 'C768': tasks_per_node * 4}
    if atm_grid in task_count.keys():
        case.set_value('task_count', str(task_count[atm_grid]), subgroup=
            'case.gfs_post')
        case.flush()
        logger.info('NCEP Post task count is changed to {}!'.format(
            task_count[atm_grid]))


def buildnml(case, caseroot, compname):
    datestamp = '20191213'
    if compname != 'ufsatm':
        raise AttributeError
    srcroot = case.get_value('SRCROOT')
    rundir = case.get_value('RUNDIR')
    confdir = os.path.join(caseroot, 'Buildconf', 'ufsatmconf')
    if not os.path.isdir(confdir):
        os.makedirs(confdir)
    casedocsdir = os.path.join(caseroot, 'CaseDocs')
    compset = case.get_value('COMPSET')
    compset_dict = {'mrweather': [
        'FCST_ufsatm%v15p2_SLND_SICE_SOCN_SROF_SGLC_SWAV',
        'FCST_ufsatm%v16beta_SLND_SICE_SOCN_SROF_SGLC_SWAV'], 'hafs': [
        'FCST_ufsatm%v0nocp_SLND_SICE_SOCN_SROF_SGLC_SWAV']}
    global app
    app = 's2s'
    for key, value in compset_dict.items():
        for item in value:
            if item in compset:
                app = key
    ccpp_suite = case.get_value('CCPP_SUITES')
    config = {}
    config['app'] = app
    config['ccpp'] = ccpp_suite
    config['hgrid'] = case.get_value('ATM_GRID')
    config['ntasks'] = str(case.get_value('ATM_NTASKS'))
    namelist_xml_dir = os.path.join(srcroot, 'src', 'model', 'FV3', 'cime',
        'cime_config')
    definition_file = [os.path.join(namelist_xml_dir,
        'namelist_definition_ufsatm.xml')]
    user_nl_file = os.path.join(caseroot, 'user_nl_ufsatm')
    expect(os.path.isfile(user_nl_file), 
        'Missing required user_nl_file %s ' % user_nl_file)
    infile = os.path.join(confdir, 'namelist_infile')
    create_namelist_infile(case, user_nl_file, infile)
    namelist_infile = [infile]
    namelist_user = parse(infile)
    is_restart = case.get_value('CONTINUE_RUN')
    if is_restart:
        res_files = []
        for _, _, files in os.walk(os.path.join(rundir, 'RESTART')):
            for filename in files:
                if 'coupler.res' in filename:
                    res_files.append(filename)
        res_files.sort()
        nres = len(res_files)
        if nres > 1:
            prefix = res_files[nres - 2].replace('coupler.res', '')
        else:
            rst_file = os.path.join(rundir, 'RESTART', 'coupler.res')
            if not os.path.isfile(rst_file):
                expect(False,
                    '{} is missing. It is required to restart the model!'.
                    format(os.path.join(rundir, 'RESTART', 'coupler.res')))
            else:
                with open(rst_file, 'r') as f:
                    lines = f.readlines()
                    line = [int(x) for x in lines[2].split()[0:6]]
                    prefix = '{:04d}'.format(line[0])
                    prefix = prefix + ''.join('{:02d}'.format(x) for x in
                        line[1:3])
                    prefix = prefix + '.' + ''.join('{:02d}'.format(x) for
                        x in line[3:7])
                    prefix = prefix + '.'
            tile_max = 7
            if 'hafs' in app:
                tile_max = 2
            lst = ['fv_core.res', 'fv_srf_wnd.res', 'fv_tracer.res',
                'phy_data', 'sfc_data']
            for f in lst:
                for tile in range(1, tile_max):
                    if '_data' in f and 'hafs' in app:
                        src_file = os.path.join(rundir, 'RESTART', '{}.nc'.
                            format(f))
                        tgt_file = os.path.join(rundir, 'RESTART', '{}.nc'.
                            format(prefix + f))
                    else:
                        src_file = os.path.join(rundir, 'RESTART',
                            '{}.tile{}.nc'.format(f, tile))
                        tgt_file = os.path.join(rundir, 'RESTART',
                            '{}.tile{}.nc'.format(prefix + f, tile))
                    if not os.path.isfile(src_file):
                        expect(False, '{} is missing.'.format(src_file))
                    else:
                        safe_copy(src_file, tgt_file)
            lst = ['fv_core.res.nc', 'coupler.res']
            for f in lst:
                src_file = os.path.join(rundir, 'RESTART', f)
                tgt_file = os.path.join(rundir, 'RESTART', prefix + f)
                if not os.path.isfile(src_file):
                    expect(False, '{} is missing.'.format(src_file))
                else:
                    safe_copy(src_file, tgt_file)
    else:
        prefix = ''
    env_workflow = case.get_env('workflow')
    jobs = env_workflow.get_jobs()
    yyyy, mm, dd, hh = date_yyyymmddhh(case)
    if 'case.chgres' in jobs:
        if 'hafs' in app:
            din_loc_root = case.get_value('DIN_LOC_ROOT')
            icdir = os.path.join(din_loc_root, 'regional', 'bcond',
                '{:04d}{:02d}{:02d}{:02d}'.format(yyyy, mm, dd, hh))
        else:
            icdir = os.path.join(case.get_value('DIN_LOC_IC'),
                '{:04d}{:02d}'.format(yyyy, mm), '{:04d}{:02d}{:02d}'.
                format(yyyy, mm, dd))
        logger.info('Checking {} directory to find raw input files ...'.
            format(icdir))
        input_dict = {}
        if os.path.exists(icdir):
            for f in os.listdir(icdir):
                if 'atm.input.ic.nemsio' in f or 'sfc.input.ic.nemsio' in f:
                    if 'gaussian_nemsio' in input_dict.keys():
                        input_dict['gaussian_nemsio'].append(f)
                    else:
                        input_dict.update({'gaussian_nemsio': [f]})
                    logger.info("Found '{}' in input directory".format(f))
                elif 'atm.input.ic.grb2' in f:
                    if 'grib2' in input_dict.keys():
                        input_dict['grib2'].append(f)
                    else:
                        input_dict.update({'grib2': [f]})
                    logger.info("Found '{}' in input directory".format(f))
                elif 'atm.input.ic.nc' in f or 'sfc.input.ic.nc' in f:
                    if 'gaussian_netcdf' in input_dict.keys():
                        input_dict['gaussian_netcdf'].append(f)
                    else:
                        input_dict.update({'gaussian_netcdf': [f]})
                    logger.info("Found '{}' in input directory".format(f))
        input_type_lst = list(set(input_dict.keys()))
        input_type_user = namelist_user.get_value('input_type')[0]
        if input_type_user:
            input_type_lst = list([input_type_user.strip('"').strip("'")])
        if not input_type_lst:
            logger.info('No input file/s found locally! {}'.format(icdir))
            logger.info(
                'Please download raw initial condition and rename the file/s using following convention:'
                )
            logger.info(
                '\t NEMSIO: atm.input.ic.nemsio and sfc.input.ic.nemsio')
            logger.info('\t GRIB2 : atm.input.ic.grb2')
            logger.info('\t NetCDF: atm.input.ic.nc and sfc.input.ic.nc')
            expect(False, 'Exiting ...')
        else:
            if len(input_type_lst) > 1:
                input_type = 'grib2'
            else:
                input_type = input_type_lst[0]
            logger.info("CHGRES namelist option 'input_type' is set to {}".
                format(input_type))
            if input_type in input_dict.keys():
                for f in input_dict[input_type]:
                    if not os.path.exists(os.path.join(icdir, f)):
                        expect(False, '{} not found!'.format(os.path.join(
                            icdir, f)))
                    else:
                        logger.info('CHGRES will use {}'.format(os.path.
                            join(icdir, f)))
            else:
                if 'gaussian_nemsio' == input_type:
                    logger.info(
                        'Please download raw initial condition in NEMSIO format rename it as atm.input.ic.nemsio and sfc.input.ic.nemsio'
                        )
                elif 'grib2' == input_type:
                    logger.info(
                        'Please download raw initial condition in GRIB2 format and rename it as atm.input.ic.grb2'
                        )
                elif 'gaussian_netcdf' == input_type:
                    logger.info(
                        'Please download raw initial condition in NETCDF format and rename it as atm.input.ic.nc and sfc.input.ic.nc'
                        )
                else:
                    logger.info(
                        "Namelist option input_type can be 'gaussian_nemsio', 'grib2' or 'gaussian_netcdf'."
                        )
                expect(False, 'Exiting ...')
    else:
        input_type = None
        logger.info(
            "CHGRES is not the part of the workflow. There is no need to set 'input_type'!"
            )
    auto_namelist_file = os.path.join(confdir, 'model_configure')
    user_namelist_file = os.path.join(caseroot, 'SourceMods', 'src.ufsatm',
        'model_configure')
    if os.path.isfile(user_namelist_file):
        logger.info('Using user provided model_configure file {}'.format(
            user_namelist_file))
        if os.path.isfile(auto_namelist_file):
            os.remove(auto_namelist_file)
        safe_copy(user_namelist_file, auto_namelist_file)
    else:
        logger.info('Using auto-generated model_configure file {}'.format(
            auto_namelist_file))
        nmlgen_model_configure = NamelistGenerator(case, definition_file)
        _create_namelist_model_configure(case, confdir, config,
            namelist_infile, nmlgen_model_configure, namelist_user, prefix)
    if os.path.isdir(rundir):
        file1 = os.path.join(confdir, 'model_configure')
        file2 = os.path.join(rundir, 'model_configure')
        logger.debug('ufsatm configuration copy: file1 %s file2 %s ' % (
            file1, file2))
        safe_copy(file1, file2)
    auto_namelist_file = os.path.join(confdir, 'atm_in')
    user_namelist_file = os.path.join(caseroot, 'SourceMods', 'src.ufsatm',
        'atm_in')
    if os.path.isfile(user_namelist_file):
        logger.info('Using user provided atm_in file {}'.format(
            user_namelist_file))
        if os.path.isfile(auto_namelist_file):
            os.remove(auto_namelist_file)
        safe_copy(user_namelist_file, auto_namelist_file)
        nmlgen_input = parse(auto_namelist_file)
    else:
        logger.info('Using auto-generated atm_in file {}'.format(
            auto_namelist_file))
        nmlgen_input = NamelistGenerator(case, definition_file)
        nmlgen_input_def = NamelistGenerator(case, definition_file)
        _create_namelist_input(case, confdir, config, namelist_infile,
            nmlgen_model_configure, nmlgen_input, nmlgen_input_def,
            namelist_user, datestamp, input_type)
    if os.path.isdir(rundir):
        file1 = os.path.join(confdir, 'atm_in')
        file2 = os.path.join(rundir, 'atm_in')
        logger.debug('ufsatm namelist copy: file1 %s file2 %s ' % (file1,
            file2))
        safe_copy(file1, file2)
    logger.info('\tLinking input namelist for ufsatm')
    if not os.path.exists(rundir):
        expect(False, "Couldn't find run direcory " + rundir)
    if 'MOM6' not in compset:
        symlink_force(file2, os.path.join(rundir, 'input.nml'))
    else:
        symlink_force(file2, os.path.join(rundir, 'input.nml.fv3'))
    if os.path.isdir(casedocsdir):
        file1 = os.path.join(confdir, 'model_configure')
        file2 = os.path.join(casedocsdir, 'model_configure')
        safe_copy(file1, file2)
        file1 = os.path.join(confdir, 'atm_in')
        file2 = os.path.join(casedocsdir, 'input.nml.fv3')
        safe_copy(file1, file2)
    prep_input(case, casedocsdir, datestamp, nmlgen_input, prefix, input_type)
    if 'case.chgres' in jobs and not is_restart:
        nmlgen_chgres = NamelistGenerator(case, definition_file)
        nmlgen_chgres_def = NamelistGenerator(case, definition_file)
        _create_namelist_chgres(case, confdir, config, namelist_infile,
            nmlgen_chgres, nmlgen_chgres_def, nmlgen_input, namelist_user,
            input_type)
        if os.path.isdir(rundir):
            file1 = os.path.join(confdir, 'config.nml')
            file2 = os.path.join(rundir, 'config.nml')
            logger.debug('CHGRES configuration copy: file1 %s file2 %s ' %
                (file1, file2))
            safe_copy(file1, file2)
        if os.path.isdir(casedocsdir):
            file1 = os.path.join(confdir, 'config.nml')
            file2 = os.path.join(casedocsdir, 'config.nml')
            safe_copy(file1, file2)
    if 'case.gfs_post' in jobs:
        nmlgen_ncep_post = NamelistGenerator(case, definition_file)
        for f in os.listdir(rundir):
            if 'itag' in f:
                logger.warning('removing file {}'.format(f))
                os.remove(os.path.join(rundir, f))
        _create_namelist_ncep_post(case, confdir, config, namelist_infile,
            nmlgen_ncep_post, nmlgen_model_configure, namelist_user)
        if os.path.isdir(rundir):
            file1 = os.path.join(confdir, 'itag.tmp')
            file2 = os.path.join(rundir, 'itag.tmp')
            logger.debug('NCEP_POST configuration copy: file1 %s file2 %s ' %
                (file1, file2))
            safe_copy(file1, file2)
        if os.path.isdir(casedocsdir):
            file1 = os.path.join(confdir, 'itag.tmp')
            file2 = os.path.join(casedocsdir, 'itag.tmp')
            safe_copy(file1, file2)
    return


def _main_func():
    caseroot = parse_input(sys.argv)
    with Case(caseroot) as case:
        buildnml(case, caseroot, 'ufsatm')


if __name__ == '__main__':
    _main_func()

#!/usr/bin/env python

"""ufsatm namelist creator
"""

# 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, copy, time
from datetime import datetime

CIMEROOT = os.environ.get("CIMEROOT")
if CIMEROOT is None:
    raise SystemExit("ERROR: must set CIMEROOT environment variable")
sys.path.append(os.path.join(CIMEROOT, "scripts", "Tools"))

from standard_script_setup import *
from CIME.case import Case
from CIME.nmlgen import NamelistGenerator
from CIME.namelist import parse
from CIME.buildnml import create_namelist_infile, parse_input
from CIME.utils import expect, safe_copy, symlink_force, ls_sorted_by_mtime

logger = logging.getLogger(__name__)

def date_yyyymmddhh(case):
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod)//3600
    return yyyy, mm, dd, hh

# find factors of a number
def factors(n):
    return set(x for tup in ([i, n//i] for i in range(1, int(n**0.5)+1) if n % i == 0) for x in tup)

# check if all elements in a list are same
def checkList(lst):
    ele = lst[0]
    chk = True
    # Comparing each element with first item
    for item in lst:
        if ele != item:
            chk = False
            break
    return chk

# returns active components
def active_components(case):
    # query compset
    compset = case.get_value("COMPSET")
    # parse compset
    components = compset.split("_")[1:]
    # remove stub components
    components = [ x for x in components if "S" not in x[0] ]

    return components

# converts one item list to value, if list has more than one item then do nothing
def list_to_value(val):
    if isinstance(val, list):
        if len(val) == 1:
            return val[0]
        else:
            return val
    else:
        return val

# keep only changed namelist options
def nmlKeepChangedOnly(nmlgen_ini, nmlgen_cur, namelist_user):

    nmlgen_ret = copy.copy(nmlgen_cur)
    #pylint: disable=protected-access
    for nml in nmlgen_cur._namelist.get_group_names():
        for var in nmlgen_cur.get_group_variables(nml):
            matched = False

            # if it is an user modified parameter, add it to namelist in any case
            lst = namelist_user.get_value(var)
            if any(len(item) > 0 for item in lst):
                continue

            # list of namelist options that will always appear in the namelist file
            lst = ['ccpp_suite', 'nstf_name']
            if var in lst:
                continue

            # get type and size of namelist variable
            var_type, _, var_size, = nmlgen_cur._definition.split_type_string(var)

            # retrieve the initial (default) and current values of the namelist variable
            ini = nmlgen_ini.get_value(var)
            cur = nmlgen_cur.get_value(var)

            # if it is list and size is different from var_size
            if var_size > 1:
                # create empty list
                var_act = [None]*var_size
                var_def = [None]*var_size

                # force to have same length
                var_act_len = len(cur)
                var_act[0:var_act_len] = [x for x in cur]
                var_def_len = len(ini)
                var_def[0:var_def_len] = [x for x in ini]

                # compare
                if var_type == 'character' or var_type == 'logical':
                    var_act = ['xyz' if x is None else x.lower() for x in var_act]
                    var_def = ['xyz' if x is None else x.lower() for x in var_def]
                    matched = all(y.startswith(x) for x,y in zip(var_act,var_def))
                elif var_type == 'integer':
                    var_act = [-99 if x is None else int(x) for x in var_act]
                    var_def = [-99 if x is None else int(x) for x in var_def]
                    matched = all(x == y for x,y in zip(var_act,var_def))
                elif var_type == 'real':
                    var_act = [-99.9 if x is None else float(eval(x.lower().replace('d','e'))) for x in var_act]
                    var_def = [-99.9 if x is None else float(eval(x.lower().replace('d','e'))) for x in var_def]
                    matched = all(x == y for x,y in zip(var_act,var_def))
            else:
                if var_type == 'character' or var_type == 'logical':
                    if not ini.strip() == "":
                        if cur.startswith(ini):
                            matched = True
                    else:
                        if ini.strip() == "" and cur.strip() == "":
                            matched = True
                elif var_type == 'integer':
                    if ini:
                        if int(cur) == int(ini):
                            matched = True
                    else:
                        if not (ini and cur):
                            matched = True
                elif var_type == 'real' and ini and cur:
                    if float(eval(ini.lower().replace('d','e'))) == \
                       float(eval(cur.lower().replace('d','e'))):
                        matched = True

            # if there is a match, remove it from the list
            if matched:
                nmlgen_ret._namelist.delete_variable(nml, var)

    return nmlgen_ret

# copy namelist options from one namelist to other
def nmlOverwrite(nmlgen_src, nmlgen_dst):

    nmlgen_ret = copy.copy(nmlgen_dst)
    #pylint: disable=protected-access
    for nml in nmlgen_src.get_group_names():
        for var in nmlgen_src.get_group_variables(nml):
            nmlgen_ret.set_value(var, nmlgen_src.get_value(var))

    return nmlgen_ret

# prepares the input files of a case and places in rundir:
def prep_input(case, casedocsdir, datestamp, nmlgen_input, prefix, input_type):
    casename = case.get_value("CASE")
    Buildconf = case.get_value("CASEBUILD")
    rundir = case.get_value("RUNDIR")
    atm_grid = case.get_value("ATM_GRID").replace('r', '')
    testcase = case.get_value("TEST")
    din_loc_root = case.get_value("DIN_LOC_ROOT")
    din_loc_ic = case.get_value("DIN_LOC_IC")
    is_restart =  case.get_value("CONTINUE_RUN")
    srcroot = case.get_value("SRCROOT")
    caseroot = case.get_value("CASEROOT")

    #----------------------------------------------------
    # Query jobs
    #----------------------------------------------------

    env_workflow = case.get_env("workflow")
    jobs = env_workflow.get_jobs()

    #----------------------------------------------------
    # Create list of input files
    #----------------------------------------------------

    # TODO: make it xmlchnage variable
    new_o3forc = True
    if "s2s" in app:
        new_o3forc = False

    # Query date and time
    yyyy, mm, dd, hh = date_yyyymmddhh(case)

    # Define base directories
    icdir = os.path.join(din_loc_ic,"{:04d}{:02d}".format(yyyy,mm),"{:04d}{:02d}{:02d}".format(yyyy,mm,dd))
    if "hafs" in app:
        dir_fix_am  = os.path.join(din_loc_root,"regional","fix_am")
        dir_fix_ufsatm = os.path.join(din_loc_root,"regional","fix",atm_grid)
        dir_fix_parm = os.path.join(din_loc_root,"parm")
        dir_fix_lib = os.path.join(din_loc_root,"lib")
        dir_bdy = os.path.join(din_loc_root,"regional","bcond","{:04d}{:02d}{:02d}{:02d}".format(int(yyyy),int(mm),int(dd),hh))
    else:
        dir_fix_am  = os.path.join(din_loc_root,"global","fix","fix_am.v{}".format(datestamp))
        if "s2s" in app:
            dir_fix_ufsatm = os.path.join(din_loc_root,"global","fix","fix_fv3_gmted2010.v{}".format(datestamp),"{}_s2s".format(atm_grid))
        else:
            dir_fix_ufsatm = os.path.join(din_loc_root,"global","fix","fix_fv3_gmted2010.v{}".format(datestamp),atm_grid)
        dir_fix_parm = os.path.join(din_loc_root,"parm")
        dir_fix_lib = os.path.join(din_loc_root,"lib")

    # Create generic dictionary to hold input files: [source_file, "ln" | "cp", target_file]
    input_files = dict()

    # Grid and orography data
    if "hafs" in app:
        halo_lst = ["halo0", "halo3", "halo4"]
        for halo in halo_lst:
            input_files["grid_tile7_{}".format(halo)] = [os.path.join(dir_fix_ufsatm,atm_grid+"_grid.tile7.{}.nc".format(halo)), "ln", os.path.join("INPUT",atm_grid+"_grid.tile7.{}.nc".format(halo))]
            input_files["orog_tile7_{}".format(halo)] = [os.path.join(dir_fix_ufsatm,atm_grid+"_oro_data.tile7.{}.nc".format(halo)), "ln", os.path.join("INPUT",atm_grid+"_oro_data.tile7.{}.nc".format(halo))]
        input_files["oro_data_tile7h0"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_oro_data.tile7.halo0.nc"), "ln",  os.path.join("INPUT","oro_data.nc")]
        input_files["oro_data_tile7h4"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_oro_data.tile7.halo4.nc"), "ln",  os.path.join("INPUT","oro_data.tile7.halo4.nc")]
        input_files["grid_tile7h3"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_grid.tile7.halo3.nc"), "ln",  os.path.join("INPUT",atm_grid+"_grid.tile7.nc")]
        input_files["grid_tile7h0"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_grid.tile7.halo0.nc"), "ln",  os.path.join("INPUT","grid.tile7.halo0.nc")]
        input_files["grid_tile7h4"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_grid.tile7.halo4.nc"), "ln",  os.path.join("INPUT","grid.tile7.halo4.nc")]
    else:
        for tile in range(1,7):
            input_files["grid_tile{}".format(tile)] = [os.path.join(dir_fix_ufsatm,atm_grid+"_grid.tile{}.nc".format(tile)), "ln", os.path.join("INPUT",atm_grid+"_grid.tile{}.nc".format(tile))]
            input_files["orog_data{}".format(tile)] = [os.path.join(dir_fix_ufsatm,atm_grid+"_oro_data.tile{}.nc".format(tile)), "ln", os.path.join("INPUT","oro_data.tile{}.nc".format(tile))]

    if "s2s" in app:
        input_files["grid_spec"] = [os.path.join(icdir,"grid_spec.nc"), "ln", os.path.join("INPUT","grid_spec.nc")]
    else:
        input_files["grid_spec"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_mosaic.nc"), "ln", os.path.join("INPUT","grid_spec.nc")]
    input_files["grid_mosaic"] = [os.path.join(dir_fix_ufsatm,atm_grid+"_mosaic.nc"), "ln", os.path.join("INPUT",atm_grid+"_mosaic.nc")]

    # GFS standard input files
    iaer = nmlgen_input.get_value('iaer')
    ico2 = nmlgen_input.get_value('ico2')

    if new_o3forc:
        input_files["o3forc"] = [os.path.join(dir_fix_am,"ozprdlos_2015_new_sbuvO3_tclm15_nuchem.f77"), "ln", "global_o3prdlos.f77"]
    else:
        input_files["o3forc"] = [os.path.join(dir_fix_am,"global_o3prdlos.f77"), "ln", "global_o3prdlos.f77"]

    input_files["h2oforc"] = [os.path.join(dir_fix_am,"global_h2o_pltc.f77"), "ln", "global_h2oprdlos.f77"]
    input_files["solcnst"] = [os.path.join(dir_fix_am,"global_solarconstant_noaa_an.txt"), "ln", "solarconstant_noaa_an.txt"]
    input_files["sfcemis"] = [os.path.join(dir_fix_am,"global_sfc_emissivity_idx.txt"), "ln", "sfc_emissivity_idx.txt"]
    input_files["co2hist"] = [os.path.join(dir_fix_am,"global_co2historicaldata_glob.txt"), "ln", "co2historicaldata_glob.txt"]
    input_files["co2mont"] = [os.path.join(dir_fix_am,"co2monthlycyc.txt"), "ln", "co2monthlycyc.txt"]

    # NOTE: CIME.namelist.parse returns single values as one element list
    ico2 = list_to_value(ico2)
    if ico2 and int(ico2) > 0:
        # TODO: make it automatic
        yy = int(time.strftime("%Y,%m,%d,%H,%M,%S").split(',')[0])
        for year in range(2009,yy+1):
            input_files["co2his_{}".format(year)] = [os.path.join(dir_fix_am,"fix_co2_proj","global_co2historicaldata_{}.txt".format(year)), "ln", "co2historicaldata_{}.txt".format(year)]

    input_files["clmaero"] = [os.path.join(dir_fix_am,"global_climaeropac_global.txt"), "ln", "aerosol.dat"]

    # NOTE: CIME.namelist.parse returns single values as one element list
    iaer = list_to_value(iaer)
    if iaer and int(iaer) > 0 and "hafs" not in app:
        for year in range(1850,2000,10):
            input_files["volaer_{}".format(year)] = [os.path.join(dir_fix_am,"global_volcanic_aerosols_{}-{}.txt".format(year,year+9)), "ln", "volcanic_aerosols_{}-{}.txt".format(year,year+9)]

    # Fixed files
    for var in nmlgen_input.get_group_variables('namsfc'):
        #pylint: disable=protected-access
        var_str = str(nmlgen_input.get_value(var)).replace("'", "").strip()
        if not var_str.startswith('igbp') and var_str.endswith('.grb'):
            input_files[var] = [var_str, "ln", os.path.basename(var_str)]

    # Look for NCEPLIBS_DIR
    nceplibs_dir = os.environ.get("NCEPLIBS_DIR")
    if "case.chgres" in jobs:
        expect(nceplibs_dir, "NCEPLIBS_DIR environment variable need to be set to process raw input data!")
        expect(os.path.exists(nceplibs_dir),"NCEPLIBS_DIR {} not found".format(nceplibs_dir))

    # Pre-processing specific files (chgres)
    if "case.chgres" in jobs:
        # Fixed files for chgres
        for tile in range(1,7):
            for f in ["facsf", "maximum_snow_albedo", "slope_type", "snowfree_albedo", "soil_type", "substrate_temperature", "vegetation_greenness", "vegetation_type"]:
                input_files["{}{}".format(f,tile)] = [os.path.join(dir_fix_ufsatm,"fix_sfc",atm_grid+".{}.tile{}.nc".format(f,tile)), "ln", os.path.join("INPUT",atm_grid+".{}.tile{}.nc".format(f,tile))]

        # Vertical layer file for chgres
        levp = nmlgen_input.get_value("levp")
        input_files["global_hyblev"] = [os.path.join(dir_fix_am,"global_hyblev.l{}.txt".format(levp)), "ln", os.path.join("INPUT","global_hyblev.l{}.txt".format(levp))]

        # Grib2 specific file
        input_files["var_map"] = [os.path.join(nceplibs_dir,"share","GFSphys_var_map.txt"), "ln", "GFSphys_var_map.txt"]

    # Post-processing specific files (gfs_post, copy from source directory)
    if "case.gfs_post" in jobs:
        src_file = os.path.join(caseroot,"SourceMods","src.ufsatm","postxconfig-NT-GFS.txt")
        dst_file = os.path.join(rundir, "postxconfig-NT-GFS.txt")
        if os.path.isfile(dst_file):
            os.remove(dst_file)

        if os.path.isfile(src_file):
            safe_copy(src_file, dst_file)
            logger.info("Using {} for post-processing".format(src_file))
        else:
            logger.info("Using {} for post-processing".format(os.path.join(nceplibs_dir,"share","postxconfig-NT-GFS.txt")))
            input_files["postxconfig1"] = [os.path.join(nceplibs_dir,"share","postxconfig-NT-GFS.txt"), "ln", "postxconfig-NT-GFS.txt"]

        src_file = os.path.join(caseroot,"SourceMods","src.ufsatm","postxconfig-NT-GFS-F00.txt")
        dst_file = os.path.join(rundir, "postxconfig-NT-GFS-F00.txt")
        if os.path.isfile(dst_file):
            os.remove(dst_file)

        if os.path.isfile(src_file):
            safe_copy(src_file, dst_file)
            logger.info("Using {} for post-processing".format(src_file))
        else:
            logger.info("Using {} for post-processing".format(os.path.join(nceplibs_dir,"share","postxconfig-NT-GFS-F00.txt")))
            input_files["postxconfig2"] = [os.path.join(nceplibs_dir,"share","postxconfig-NT-GFS-F00.txt"), "ln", "postxconfig-NT-GFS-F00.txt"]

        input_files["params_grib2_tbl"] = [os.path.join(nceplibs_dir,"share","params_grib2_tbl_new"), "ln", "params_grib2_tbl_new"]

    # Make sure that rundir exists. If not, make it:
    if not os.path.exists(rundir):
        os.makedirs(rundir)

    # Create INPUT and RESTART directories
    logger.info("\tCreating INPUT and RESTART directory")
    for _dir in ("INPUT","RESTART"):
        fulldir = os.path.join(rundir, _dir)
        if not os.path.exists(fulldir):
            os.makedirs(fulldir)

    # For cime tests that use two test directories
    rundirdst = rundir
    if rundir.endswith("case2run"):
        rundirsrc = os.path.abspath(os.path.join(rundir, os.pardir))
    else:
        rundirsrc = rundir

    # Initial conditions
    if is_restart: # Warm start
        # Tiled files
        tile_max = 7
        if "hafs" in app:
            # Regional case has only one tile (1)
            tile_max = 2

        lst = ["fv_core.res", "fv_srf_wnd.res", "fv_tracer.res", "phy_data", "sfc_data"]
        lst = [prefix + f for f in lst]
        for f in lst:
            for tile in range(1,tile_max):
                if "_data" in f and "hafs" in app:
                    src_file = os.path.join(rundir,"RESTART","{}.nc".format(f))
                    tgt_file = os.path.join(rundir,"INPUT","{}.nc".format(f.replace(prefix,"")))
                else:
                    src_file = os.path.join(rundir,"RESTART","{}.tile{}.nc".format(f,tile))
                    tgt_file = os.path.join(rundir,"INPUT","{}.tile{}.nc".format(f.replace(prefix,""),tile))
                if not os.path.isfile(src_file):
                    expect(False, "{} is missing.".format(src_file))
                else:
                    safe_copy(src_file, tgt_file)

        # Others
        lst = ["fv_core.res.nc", "coupler.res"]
        lst = [prefix + f for f in lst]
        for f in lst:
            src_file = os.path.join(rundir,"RESTART",f)
            tgt_file = os.path.join(rundir,"INPUT",f.replace(prefix,""))
            if not os.path.isfile(src_file):
                expect(False, "{} is missing.".format(src_file))
            else:
                safe_copy(src_file, tgt_file)

        # Reorder boundary files
        if "hafs" in app:
            # Boundary conditions
            lst = ls_sorted_by_mtime(dir_bdy)
            lst.remove("gfs_ctrl.nc")
            lst.remove("gfs_data.tile7.nc")
            lst.remove("sfc_data.tile7.nc")
            for f in lst:
                # Remove old links
                src_file = os.path.join(rundirdst,"INPUT",f)
                if os.path.isfile(src_file):
                    os.remove(src_file)

                # Link files again
                time_step = int(f.split(".")[2])
                #logger.warning("time_step = {}".format(time_step))
                if time_step < nhours_fcst_pre:
                    continue
                else:
                    src_file = os.path.join(dir_bdy,f)
                    dst_file = os.path.join(rundir,"INPUT","gfs_bndy.tile7.{:03d}.nc".format(time_step-nhours_fcst_pre))
                    #logger.warning("{} --> {}".format(src_file, dst_file))
                    symlink_force(src_file, dst_file)

    else: # Cold start
        if "hafs" in app:
            # Boundary conditions
            lst = ls_sorted_by_mtime(dir_bdy)
            for f in lst:
                # Link files
                symlink_force(os.path.join(dir_bdy,f), os.path.join(rundirdst,"INPUT",f))

            symlink_force(os.path.join(dir_bdy,"sfc_data.tile7.nc"), os.path.join(rundirdst,"INPUT","sfc_data.nc"))
            symlink_force(os.path.join(dir_bdy,"gfs_data.tile7.nc"), os.path.join(rundirdst,"INPUT","gfs_data.nc"))
        else:
            # Remove restart files form INPUT/ directory, if there are
            # Tiled files
            lst = ["fv_core.res", "fv_srf_wnd.res", "fv_tracer.res", "phy_data", "sfc_data"]
            for f in lst:
                for tile in range(1,7):
                    src_file = os.path.join(rundir,"INPUT","{}.tile{}.nc".format(f,tile))
                    if os.path.isfile(src_file):
                        logger.warning("removing file {}".format(src_file))
                        os.remove(src_file)

            # Others
            lst = ["fv_core.res.nc", "coupler.res"]
            for f in lst:
                src_file = os.path.join(rundir,"INPUT",f)
                if os.path.isfile(src_file):
                    logger.warning("removing file {}".format(src_file))
                    os.remove(src_file)

            # Check chgres processes initial condition exists or not?
            src_file = os.path.join(rundirsrc,"INPUT","{}.".format(atm_grid)+"{:04d}-{:02d}-{:02d}".format(yyyy,mm,dd)+"_{:02d}.gfs_ctrl.nc".format(hh))
            if os.path.isfile(src_file):
                # Link existing initial condition
                symlink_force(src_file, os.path.join(rundirdst,"INPUT","gfs_ctrl.nc"))

                lst = ["gfs_data", "sfc_data"]
                for f in lst:
                    for tile in range(1,7):
                        src_file = os.path.join(rundirsrc,"INPUT","{}.".format(atm_grid)+"{:04d}-{:02d}-{:02d}".format(yyyy,mm,dd)+"_{:02d}.".format(hh)+f+".tile{}.nc".format(tile))
                        tgt_file = os.path.join(rundirdst,"INPUT","{}.tile{}.nc".format(f,tile))
                        if os.path.isfile(src_file):
                            symlink_force(src_file, tgt_file)
                            logger.info("chgres source file {} linked to {}".format(src_file, tgt_file))
                        else:
                            logger.debug("chgres source file {} not found".format(src_file))
            else:
               # Link pre-staged initial condition, not processed by workflow with chgres
               src_file = os.path.join(icdir,"gfs_ctrl.nc")
               if os.path.isfile(src_file):
                   symlink_force(src_file, os.path.join(rundirdst,"INPUT","gfs_ctrl.nc"))
               else:
                   logger.warning("initial condition file {} is required! Exiting ...".format(src_file))

               lst = ["gfs_data", "sfc_data"]
               for f in lst:
                   for tile in range(1,7):
                       src_file = os.path.join(icdir,f+".tile{}.nc".format(tile))
                       tgt_file = os.path.join(rundirdst,"INPUT","{}.tile{}.nc".format(f,tile))
                       if os.path.isfile(src_file):
                           symlink_force(src_file, tgt_file)
                           logger.info("initial condition file {} linked to {}".format(src_file, tgt_file))
                       else:
                           logger.warning("initial condition file {} is required! Exiting ...".format(src_file))

    # Write input file list
    with open(os.path.join(Buildconf,"ufsatm.input_data_list"), 'w') as input_data_list:
        for k,v in input_files.items():
            input_data_list.write(k+" = "+v[0]+"\n")

    # Copy/Link files
    for k,v in input_files.items():
        if not os.path.isfile(v[0]):
            logger.warning("WARNING: data file {} not found, will attempt to download.".format(v[0]))
        elif "ln" in v[1]:
            symlink_force(v[0], os.path.join(rundir,v[2]))
        elif "cp":
            safe_copy(v[0], os.path.join(rundir,v[2]))

    # Copy *_table files
    cimeroot_ufsatm = os.path.join(srcroot, "src", "model", "FV3", "cime", "cime_config")
    for f in ["data_table_{}".format(app), "diag_table_{}".format(app), "field_table_{}".format(app)]:
        if not os.path.isfile(os.path.join(rundir,f)):
            safe_copy(os.path.join(cimeroot_ufsatm,"tables",f),os.path.join(rundir,f.replace("_{}".format(app),"")))

# pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements
####################################################################################
def _create_namelist_input(case, confdir, config, infile, nmlgen_model, nmlgen_input, nmlgen_input_def, namelist_user, datestamp, input_type):
####################################################################################
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    #----------------------------------------------------
    # Clear out old data.
    #----------------------------------------------------
    data_list_path = os.path.join(case.get_case_root(), "Buildconf", "ufsatm.input_data_list")

    #----------------------------------------------------
    # Initialize namelist defaults
    #----------------------------------------------------

    nmlgen_input.init_defaults(infile, config)
    nmlgen_input_def.init_defaults(infile, {})

    #----------------------------------------------------
    # Modify namelist defaults
    #----------------------------------------------------

    # Define base directory
    din_loc_root = case.get_value("DIN_LOC_ROOT")
    if "hafs" in app:
        dir_fix_am  = os.path.join(din_loc_root,"regional","fix_am")
    else:
        dir_fix_am  = os.path.join(din_loc_root,"global","fix","fix_am.v{}".format(datestamp))

    # Spectral truncation and regular grid resolution based on FV3 resolution
    atm_grid = case.get_value("ATM_GRID").replace('r', '')
    res = int(atm_grid.replace('C',''))
    jcap = 2*res-2
    lonb = 4*res
    latb = 2*res

    # Customization for S2S, just for C96
    if "s2s" in app:
        if res == 96:
            jcap = 126
            lonb = 384
            latb = 190

    # Fix files, changes based on the selected resolution
    nmlgen_input.set_value('fnglac', value=os.path.join(dir_fix_am,"global_glacier.2x2.grb"))
    nmlgen_input.set_value('fnmxic', value=os.path.join(dir_fix_am,"global_maxice.2x2.grb"))
    nmlgen_input.set_value('fntsfc', value=os.path.join(dir_fix_am,"RTGSST.1982.2012.monthly.clim.grb"))
    nmlgen_input.set_value('fnsnoc', value=os.path.join(dir_fix_am,"global_snoclim.1.875.grb"))
    nmlgen_input.set_value('fnzorc', value="igbp")
    nmlgen_input.set_value('fnalbc2', value=os.path.join(dir_fix_am,"global_albedo4.1x1.grb"))
    nmlgen_input.set_value('fnaisc', value=os.path.join(dir_fix_am,"CFSR.SEAICE.1982.2012.monthly.clim.grb"))
    nmlgen_input.set_value('fntg3c', value=os.path.join(dir_fix_am,"global_tg3clim.2.6x1.5.grb"))
    nmlgen_input.set_value('fnvegc', value=os.path.join(dir_fix_am,"global_vegfrac.0.144.decpercent.grb"))
    if "mrweather" in app:
        nmlgen_input.set_value('fnmskh', value=os.path.join(dir_fix_am,"global_slmask.t1534.3072.1536.grb"))
    else:
        nmlgen_input.set_value('fnmskh', value=os.path.join(dir_fix_am,"seaice_newland.grb"))
    nmlgen_input.set_value('fnvmnc', value=os.path.join(dir_fix_am,"global_shdmin.0.144x0.144.grb"))
    nmlgen_input.set_value('fnvmxc', value=os.path.join(dir_fix_am,"global_shdmax.0.144x0.144.grb"))
    nmlgen_input.set_value('fnslpc', value=os.path.join(dir_fix_am,"global_slope.1x1.grb"))
    nmlgen_input.set_value('fnalbc', value=os.path.join(dir_fix_am,"global_snowfree_albedo.bosu.t{}.{}.{}.rg.grb".format(jcap,lonb,latb)))
    nmlgen_input.set_value('fnvetc', value=os.path.join(dir_fix_am,"global_vegtype.igbp.t{}.{}.{}.rg.grb".format(jcap,lonb,latb)))
    nmlgen_input.set_value('fnsotc', value=os.path.join(dir_fix_am,"global_soiltype.statsgo.t{}.{}.{}.rg.grb".format(jcap,lonb,latb)))
    nmlgen_input.set_value('fnabsc', value=os.path.join(dir_fix_am,"global_mxsnoalb.uariz.t{}.{}.{}.rg.grb".format(jcap,lonb,latb)))

    # If the appropriate resolution fix file is not present, use the highest resolution available (T1534)
    if "mrweather" in app:
        if res == 96 or res == 192:
            jcap = 1534
            lonb = 3072
            latb = 1536
            nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,"global_soilmgldas.statsgo.t{}.{}.{}.grb".format(jcap,lonb,latb)))
        else:
            nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,"global_soilmgldas.statsgo.t{}.{}.{}.grb".format(jcap,lonb,latb)))
    else:
        nmlgen_input.set_value('fnsmcc', value=os.path.join(dir_fix_am,"global_soilmgldas.t{}.{}.{}.grb".format(jcap,lonb,latb)))

    # Stochastic physics
    testcase = case.get_value("TEST")
    if testcase:
        nmlgen_input.set_value('iseed_skeb', 0)
        nmlgen_input.set_value('iseed_shum', 0)
        nmlgen_input.set_value('iseed_sppt', 0)
    else:
        # TODO: member option (-1: control, 0: ensemble mean, >0: ensemble member) need to be handled, for now it is assumed as 1
        member = 1
        cdate = int(datetime.now().strftime("%Y%m%d%H"))
        nmlgen_input.set_value('iseed_skeb', cdate*1000+member*10+1)
        nmlgen_input.set_value('iseed_shum', cdate*1000+member*10+2)
        nmlgen_input.set_value('iseed_sppt', cdate*1000+member*10+3)

    # Save namelist before making restart changes
    nmlgen_input_sav = copy.copy(nmlgen_input)

    # Restart
    is_restart =  case.get_value("CONTINUE_RUN")

    if is_restart: # Warm start
        nmlgen_input.set_value('warm_start', True)
        nmlgen_input.set_value('nggps_ic', False)
        nmlgen_input.set_value('external_ic', False)
        nmlgen_input.set_value('mountain', True)
        nmlgen_input.set_value('make_nh', False)
        nmlgen_input.set_value('na_init', 0)

        # Turn off nst spin up
        nstf_name = nmlgen_input.get_value('nstf_name')
        nstf_name[1] = '0'

        # Enable adding increment on the fly to the restarts
        read_increment = nmlgen_input.get_value('read_increment')
        if read_increment:
            nmlgen_input.set_value('res_latlon_dynamics', "INPUT/fv_rst.res.nc")

        # Set the forecast hour 'off-set'
        #if "s2s" in app:
        #    nmlgen_input.set_value('fhrot', 0)

    else: # Cold start
        # Set back to their defaults
        nmlgen_input.set_value('warm_start', nmlgen_input_sav.get_value('warm_start'))
        nmlgen_input.set_value('nggps_ic', nmlgen_input_sav.get_value('nggps_ic'))
        nmlgen_input.set_value('external_ic', nmlgen_input_sav.get_value('external_ic'))
        nmlgen_input.set_value('mountain', nmlgen_input_sav.get_value('mountain'))
        nmlgen_input.set_value('make_nh', nmlgen_input_sav.get_value('make_nh'))
        nmlgen_input.set_value('na_init', nmlgen_input_sav.get_value('na_init'))

        # Turn on nst spinup for grib
        if 'hafs' not in app:
            nstf_name = nmlgen_input_sav.get_value('nstf_name')
            if input_type == 'grib2':
                nstf_name[1] = '1'
            else:
                nstf_name[1] = '0'

        nmlgen_input.set_value('read_increment', nmlgen_input_sav.get_value('read_increment'))
        nmlgen_input.set_value('res_latlon_dynamics', nmlgen_input_sav.get_value('res_latlon_dynamics'))

    if input_type == 'grib2' and 'hafs' not in app:
        # Turn off nstf for grib input
        nstf_name[0] = '0'
        logger.info("input_type {} nstf_name {}".format(input_type, nstf_name))
        nmlgen_input.set_value('nstf_name', nstf_name)
    #----------------------------------------------------
    # Write out namelist groups
    #----------------------------------------------------

    # Path for namelist files
    srcroot = case.get_value("SRCROOT")
    namelist_xml_dir = os.path.join(srcroot, "src", "model", "FV3", "cime", "cime_config")

    # Create list of groups by querying xml file
    groups = []
    with open(os.path.join(namelist_xml_dir, "namelist_definition_ufsatm.xml"), 'r') as fin:
        for s in fin:
            if "<group>" in s:
                groups.append(s.replace('<group>', '').replace('</group>', '').strip())

    # Remove duplicates
    groups = list(set(groups))

    # Keep only ufsatm related ones
    groups.remove('config') # chgres
    groups.remove('_no_group_var') # model_configure
    groups.remove('freeform') # ncep_post
    groups.remove('nampgb') # ncep_post

    # Sort group
    groups.sort()

    # Remove duplicates
    nmlgen = nmlKeepChangedOnly(nmlgen_input_def, nmlgen_input, namelist_user)

    ntask_atm = int(case.get_value('NTASKS_ATM'))
    if ntask_atm == 8:
        nmlgen.set_value('layout',value=[1,1])

    # Overwrite user_nl_ufsatm changes
    nmlgen = nmlOverwrite(namelist_user, nmlgen)

    # if nstf_name is None here then a default of 0,0,0,0,0 is assumed
    nstf_name = nmlgen.get_value('nstf_name')
    if "mrweather" in app:
        if not nstf_name[0] or nstf_name[0] == '0':
            ccpp = nmlgen.get_value('ccpp_suite')
            nmlgen.set_value('ccpp_suite',ccpp+'_no_nsst')

    # Consistency check for layout
    layout = nmlgen_input.get_value('layout')
    layout_x = int(layout[0])
    layout_y = int(layout[1])
    ntiles = int(nmlgen_input.get_value('ntiles@fv_core_nml'))
    write_tasks_per_group = int(nmlgen_model.get_value('write_tasks_per_group'))
    write_groups = int(nmlgen_model.get_value('write_groups'))

    ntask_atm_config = layout_x*layout_y*ntiles+write_tasks_per_group*write_groups
    if ntask_atm_config != ntask_atm:
        expect(False, ("Total number of PE need to be consistent with the model namelist options:\n"+
                       "\tTotal number of PE (ntask_atm) = {}\n"+
                       "\tDecomposition in x and y direction (layout) = {}x{}\n"+
                       "\tNumber of tile (ntiles) = {}\n"+
                       "\tNumber of I/O group (write_groups) = {}\n"+
                       "\tNumber of tasks in each I/O group (write_tasks_per_group) = {}\n").
                       format(ntask_atm,layout_x,layout_y,ntiles,write_groups,write_tasks_per_group))

    # Create namelist
    namelist_file = os.path.join(confdir, "atm_in")
    nmlgen.write_output_file(namelist_file, data_list_path, groups=groups, sorted_groups=False)

# pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements
####################################################################################
def _create_namelist_model_configure(case, confdir, config, infile, nmlgen, namelist_user, prefix):
####################################################################################
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    #----------------------------------------------------
    # Clear out old data.
    #----------------------------------------------------
    data_list_path = os.path.join(case.get_case_root(), "Buildconf", "ufsatm.input_data_list")
    if os.path.exists(data_list_path):
        logger.warning("removing file {}".format(data_list_path))
        os.remove(data_list_path)

    #----------------------------------------------------
    # Initialize namelist defaults
    #----------------------------------------------------
    nmlgen.init_defaults(infile, config)

    #----------------------------------------------------
    # Modify namelist defaults
    #----------------------------------------------------

    # Query previous run length, if it is warm start
    is_restart = case.get_value("CONTINUE_RUN")

    global nhours_fcst_pre
    if is_restart: # Warm start
        # Set previous forecast length based on restart file
        rundir = case.get_value("RUNDIR")
        rst_file = os.path.join(rundir, "RESTART", "{}coupler.res".format(prefix))
        if not os.path.isfile(rst_file):
            expect(False, "Couldn't find '"+rst_file+"'!")
        else:
            # TODO: it supports only gregorian and need to support others (no_calendar=0, thirty_day_months=1, julian=2, gregorian=3, noleap=4)
            # Parse coupler.res
            logger.debug("Using {} to determine previous forecast length ...".format(rst_file))
            with open(rst_file, 'r') as f:
                lines = f.readlines()
                #calendar = lines[0].split()[0]
                date1 = datetime(*[int(i) for i in lines[1].split()[0:6]])
                date2 = datetime(*[int(i) for i in lines[2].split()[0:6]])
                nhours_fcst_pre = int((date2-date1).total_seconds()//3600)
    else: # Cold start
        # Set previous forecast length as zero
        nhours_fcst_pre = 0

    # Change start date
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    nmlgen.set_value('start_year', value=yyyy)
    nmlgen.set_value('start_month', value=mm)
    nmlgen.set_value('start_day', value=dd)

    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod//3600)
    mi = int((run_start_tod-hh*3600)//60)
    ss = int(run_start_tod-hh*3600-mi*60)
    nmlgen.set_value('start_hour', value=hh)
    nmlgen.set_value('start_minute', value=mi)
    nmlgen.set_value('start_second', value=ss)

    # Change forecast length
    stop_option = case.get_value('STOP_OPTION')
    stop_n = int(case.get_value('STOP_N'))

    if 'nyears' in stop_option:
        expect(False, "Option >nyears< is not supported!")
    elif 'nmonths' in stop_option:
        expect(False, "Option >nmonths< is not supported!")
    elif 'ndays' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n*24 + nhours_fcst_pre)
    elif 'nhours' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n + nhours_fcst_pre)
    elif 'nseconds' in stop_option:
        nmlgen.set_value('nhours_fcst', stop_n/3600 + nhours_fcst_pre)
    elif 'nsteps' in stop_option:
        expect(False, "Option >nsteps< is not supported!")

    # Change restart interval
    rest_option = case.get_value('REST_OPTION')
    rest_n = int(case.get_value('REST_N'))

    if 'nyears' in rest_option:
        expect(False, "Option >nyears< is not supported!")
    elif 'nmonths' in rest_option:
        expect(False, "Option >nmonths< is not supported!")
    elif 'ndays' in rest_option:
        nmlgen.set_value('restart_interval', rest_n*24)
    elif 'nhours' in rest_option:
        nmlgen.set_value('restart_interval', rest_n)
    elif 'nseconds' in rest_option:
        nmlgen.set_value('restart_interval', rest_n/60)
    elif 'nsteps' in rest_option:
        expect(False, "Option >nsteps< is not supported!")

    # Query total number of PEs
    components = active_components(case)
    ntask = int(case.get_value('NTASKS_ATM'))
    if len(components) > 1:
        ntask = int(case.get_value('TOTALPES'))

    # Change number of used PEs if there is a mismatch
    pe_member = int(nmlgen.get_value('PE_MEMBER01'))
    if pe_member != ntask:
        nmlgen.set_value('PE_MEMBER01', ntask)

    # Change number of write group for 8 PEs
    if ntask == 8:
        nmlgen.set_value("write_tasks_per_group",2)

    # Test run specific modifications
    testcase = case.get_value("TEST")
    if testcase:
        # Change output format
        nmlgen.set_value('output_file', 'netcdf')

        # Change file prefixes
        nmlgen.set_value('filename_base', "{} {}".format(case.get_value('CASE')+'.ufsatm.atm.', case.get_value('CASE')+'.ufsatm.sfc.'))

    # Modify machine specific namelist options
    nmlgen.set_value('atmos_nthreads', case.thread_count)
    nmlgen.set_value('ncores_per_node', case.tasks_per_node)

    # Activate ESMF logs if it is a debug run
    debug = case.get_value("DEBUG")
    if debug:
        nmlgen.set_value('print_esmf', True)
    else:
        nmlgen.set_value('print_esmf', False)

    # overwrite user_nl_ufsatm changes
    nmlgen = nmlOverwrite(namelist_user, nmlgen)

    # remove parameters from the namelist based on the selected app
    remove_lst = [] 
    if "hafs" in app:
        remove_lst.extend(["imo", "jmo", "iau_offset", "ideflate", "nbits", "output_history", "write_dopost", "atm_coupling_interval_sec", "fhrot"])
    else:
        remove_lst.extend(["cen_lat", "cen_lon", "dlat", "dlon", "lat1", "lat2", "lon1", "lon2"])
        if "mrweather" in app:
            remove_lst.append("atm_coupling_interval_sec")
            remove_lst.append("fhrot")

    for var in remove_lst:
        nmlgen._namelist.delete_variable("_no_group_var", var)

    #----------------------------------------------------
    # Write out model.configure namelist
    #----------------------------------------------------
    model_config_file = os.path.join(confdir, "model_configure")
    nmlgen.write_nuopc_config_file(model_config_file, data_list_path)

# pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements
####################################################################################
def _create_namelist_chgres(case, confdir, config, infile, nmlgen, nmlgen_def, nmlgen_input, namelist_user, input_type):
####################################################################################
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    #----------------------------------------------------
    # Clear out old data.
    #----------------------------------------------------
    data_list_path = os.path.join(case.get_case_root(), "Buildconf", "ufsatm.input_data_list")

    #----------------------------------------------------
    # Initialize namelist defaults
    #----------------------------------------------------
    nmlgen.init_defaults(infile, config)
    nmlgen_def.init_defaults(infile, {})

    #----------------------------------------------------
    # Modify namelist defaults
    #----------------------------------------------------
    rundir = case.get_value("RUNDIR")
    atm_grid = case.get_value("ATM_GRID").replace('r', '')

    # Set grid
    nmlgen.set_value('mosaic_file_target_grid', os.path.join("INPUT",atm_grid+"_mosaic.nc"))

    # Set vertical layers
    levp = nmlgen_input.get_value("levp")
    if levp is None:
        expect(False, "Number of vertical layer 'levp' must be a valid number! levp = {}".format(levp))
    else:
        hyblev_file = os.path.join(rundir,"INPUT","global_hyblev.l{}.txt".format(levp))
        if not os.path.exists(hyblev_file):
            logger.warning("WARNING: {} does not exist, will attempt to download.".format(hyblev_file))
        else:
            nmlgen.set_value('vcoord_file_target_grid', os.path.join("INPUT","global_hyblev.l{}.txt".format(levp)))

    # Set location of fixed surface files
    nmlgen.set_value('fix_dir_target_grid', os.path.join("INPUT"))

    # Set cycle
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yyyy = int(run_start_date[0])
    mm = int(run_start_date[1])
    dd = int(run_start_date[2])
    run_start_tod = case.get_value('START_TOD')
    hh = int(run_start_tod//3600)

    nmlgen.set_value('cycle_mon', int(mm))
    nmlgen.set_value('cycle_day', int(dd))
    nmlgen.set_value('cycle_hour', int(hh))

    # Input directory
    icdir = os.path.join(case.get_value("DIN_LOC_IC"),"{:04d}{:02d}".format(yyyy,mm),"{:04d}{:02d}{:02d}".format(yyyy,mm,dd))
    nmlgen.set_value('data_dir_input_grid', icdir)

    # Set input_type specific options
    nmlgen.set_value('input_type', input_type)
    if input_type == 'gaussian_nemsio':
        nmlgen.set_value('atm_files_input_grid', ['atm.input.ic.nemsio'])
        nmlgen.set_value('sfc_files_input_grid', ['sfc.input.ic.nemsio'])
        tracers = ['sphum','liq_wat','o3mr','ice_wat','rainwat','snowwat','graupel']
        nmlgen.set_value('tracers', tracers)
        tracers_input = ['spfh','clwmr','o3mr','icmr','rwmr','snmr','grle']
        nmlgen.set_value('tracers_input', tracers_input)
        nmlgen.set_value('convert_nst', True)
    elif input_type == 'grib2':
        nmlgen.set_value('grib2_file_input_grid','atm.input.ic.grb2')
        nmlgen.set_value('varmap_file', 'GFSphys_var_map.txt')
    elif input_type == 'gaussian_netcdf':
        nmlgen.set_value('orog_dir_target_grid', os.path.join("INPUT"))
        oro_files = []
        for tile in range(1,7):
            oro_files.append("oro_data.tile{}.nc".format(tile))
        nmlgen.set_value('orog_files_target_grid', oro_files)
        nmlgen.set_value('atm_files_input_grid', ['atm.input.ic.nc'])
        nmlgen.set_value('sfc_files_input_grid', ['sfc.input.ic.nc'])
        nmlgen.set_value('convert_atm', True)
        nmlgen.set_value('convert_sfc', True)
        nmlgen.set_value('convert_nst', True)
        tracers = ['sphum','liq_wat','o3mr','ice_wat','rainwat','snowwat','graupel']
        nmlgen.set_value('tracers', tracers)
        tracers_input = ['spfh','clwmr','o3mr','icmr','rwmr','snmr','grle']
        nmlgen.set_value('tracers_input', tracers_input)

    # Check/correct task count used for pre-processing
    mach = case.get_value("MACH") 
    atm_grid = case.get_value("ATM_GRID").replace('r', '')
    tasks_per_node = 12
    task_count = {"C96"  : 1*tasks_per_node, # 1 nodes
                  "C192" : 1*tasks_per_node, # 1 nodes
                  "C384" : 2*tasks_per_node, # 2 nodes
                  "C768" : 3*tasks_per_node} # 3 nodes

    if atm_grid in task_count.keys():
        case.set_value("tasks_per_node", str(tasks_per_node), subgroup="case.chgres")
        case.set_value("task_count", str(task_count[atm_grid]), subgroup="case.chgres")
        logger.info("CHGRES task count and tasks per node are modified! task_count = {} tasks_per_node = {}".format(task_count[atm_grid], tasks_per_node))
        case.flush()

    # remove namelist options that has no specialization
    nmlgen_mod = nmlKeepChangedOnly(nmlgen_def, nmlgen, namelist_user)

    # overwrite user_nl_ufsatm changes
    nmlgen_mod = nmlOverwrite(namelist_user, nmlgen_mod)

    #----------------------------------------------------
    # Write out namelist groups
    #----------------------------------------------------
    groups=['config']

    namelist_file = os.path.join(confdir, "config.nml")
    nmlgen_mod.write_output_file(namelist_file, data_list_path, groups=groups, sorted_groups=False)

# pylint: disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements
####################################################################################
def _create_namelist_ncep_post(case, confdir, config, infile, nmlgen, nmlgen_model_configure, namelist_user):
####################################################################################
    """Write out the namelist for this component.

    Most arguments are the same as those for `NamelistGenerator`.
    The `confdir` argument is used to specify the directory  in which output files will be placed.
    """
    #----------------------------------------------------
    # Clear out old data.
    #----------------------------------------------------
    data_list_path = os.path.join(case.get_case_root(), "Buildconf", "ufsatm.input_data_list")

    #----------------------------------------------------
    # Initialize namelist defaults
    #----------------------------------------------------
    nmlgen.init_defaults(infile, config)

    #----------------------------------------------------
    # Write out namelist groups
    #----------------------------------------------------
    groups=['nampgb']

    # Make input format for post-processing consistent with model output
    output_file = nmlgen_model_configure.get_value('output_file')
    if 'netcdf' in output_file:
        nmlgen.set_value('ioform', 'netcdf')
    elif 'nemsio' in output_file:
        nmlgen.set_value('ioform', 'binarynemsiompiio')

    # Query start date and time
    run_start_date = case.get_value('RUN_STARTDATE').split('-')
    yy = run_start_date[0]
    mm = run_start_date[1]
    dd = run_start_date[2]
    run_start_tod = int(case.get_value('START_TOD'))
    hh = run_start_tod//3600
    mi = (run_start_tod-hh*3600)//60
    ss = run_start_tod-hh*3600-mi*60

    # Overwrite user_nl_ufsatm changes
    nmlgen = nmlOverwrite(namelist_user, nmlgen)

    # Create namelist file for first time step / template script will update it for specific date
    namelist_file = os.path.join(confdir, "itag.tmp")
    nmlgen.write_output_file(namelist_file, data_list_path, groups=groups, sorted_groups=False)

    # Add header section to namelist
    with open(namelist_file, 'r+') as f:
        content = f.read()
        f.seek(0,0)
        f.write(nmlgen.get_value('filename')+"\n")
        f.write(nmlgen.get_value('ioform')+"\n")
        f.write(nmlgen.get_value('outform')+"\n")
        f.write("{}-{}-{}".format(yy,mm,dd)+"_"+"{hh:02d}:{mm:02d}:{ss:02d}".format(hh=hh,mm=mi,ss=ss)+"\n")
        f.write(nmlgen.get_value('modelname')+"\n")
        f.write(nmlgen.get_value('filenameflux')+"\n")
        f.write(content)

    # Check/correct task count used for post-processing
    atm_grid = case.get_value("ATM_GRID").replace('r', '')
    mach = case.get_value("MACH")

    # Specific fix for Stampede2
    tasks_per_node = int(case.get_value("MAX_TASKS_PER_NODE"))
    if ("C384" in atm_grid or "C768" in atm_grid) and "stampede2" in mach:
        tasks_per_node = 24
        case.set_value("tasks_per_node", str(tasks_per_node), subgroup="case.gfs_post")
        case.flush()
        logger.info("NCEP Post tasks per node is changed to {}!".format(tasks_per_node))

    task_count = {"C96": tasks_per_node, "C192": tasks_per_node, "C384": tasks_per_node*2, "C768": tasks_per_node*4}
    if atm_grid in task_count.keys():
        case.set_value("task_count", str(task_count[atm_grid]), subgroup="case.gfs_post")
        case.flush()
        logger.info("NCEP Post task count is changed to {}!".format(task_count[atm_grid]))

###############################################################################
def buildnml(case, caseroot, compname):
###############################################################################

    # Date stamp for input directory
    # TODO: might be a xmlchange option
    datestamp="20191213"

    # Build the component namelist
    if compname != "ufsatm":
        raise AttributeError
    srcroot = case.get_value("SRCROOT")
    rundir  = case.get_value("RUNDIR")

    # determine the confdir directory
    confdir = os.path.join(caseroot,"Buildconf","ufsatmconf")
    if not os.path.isdir(confdir):
        os.makedirs(confdir)

    # determine CaseDocs
    casedocsdir = os.path.join(caseroot,"CaseDocs")

    # query compset
    compset = case.get_value("COMPSET")

    # dictionary for app, compset/s pairs
    compset_dict = {"mrweather": ["FCST_ufsatm%v15p2_SLND_SICE_SOCN_SROF_SGLC_SWAV", \
                                  "FCST_ufsatm%v16beta_SLND_SICE_SOCN_SROF_SGLC_SWAV"], \
                    "hafs"     : ["FCST_ufsatm%v0nocp_SLND_SICE_SOCN_SROF_SGLC_SWAV"]
                   }

    # determine app using compset, the default value is s2s
    global app
    app = "s2s"
    for key, value in compset_dict.items():
        for item in value:
            if item in compset:
                app = key

    # query used CCPP suite
    ccpp_suite = case.get_value("CCPP_SUITES")

    config = {}
    config['app'] = app
    config['ccpp'] = ccpp_suite
    config['hgrid'] = case.get_value("ATM_GRID")
    config['ntasks'] = str(case.get_value("ATM_NTASKS"))

    # path for namelist files
    namelist_xml_dir = os.path.join(srcroot, "src", "model", "FV3", "cime", "cime_config")

    # set namelist definition file
    definition_file = [os.path.join(namelist_xml_dir, "namelist_definition_ufsatm.xml")]

    # create namelist_infile using user_nl_file as input
    user_nl_file = os.path.join(caseroot, "user_nl_ufsatm")
    expect(os.path.isfile(user_nl_file),
           "Missing required user_nl_file %s " %(user_nl_file))
    infile = os.path.join(confdir, "namelist_infile")
    create_namelist_infile(case, user_nl_file, infile)
    namelist_infile = [infile]

    # read user modified namelist values
    namelist_user = parse(infile)

    #----------------------------------------------------
    # Set case start date
    #----------------------------------------------------
    is_restart =  case.get_value("CONTINUE_RUN")

    if is_restart: # Warm start
        # List coupler.res files
        res_files = []
        for _, _, files in os.walk(os.path.join(rundir,"RESTART")):
            for filename in files:
                if "coupler.res" in filename:
                    res_files.append(filename)
        res_files.sort()

        # If we have multiple, get time stamp
        nres = len(res_files)
        if nres > 1:
            prefix = res_files[nres-2].replace("coupler.res", "")
        else:
            # Query restart file
            rst_file = os.path.join(rundir, "RESTART", "coupler.res")
            if not os.path.isfile(rst_file):
                expect(False, "{} is missing. It is required to restart the model!".format(os.path.join(rundir,"RESTART","coupler.res")))
            else:
                with open(rst_file, 'r') as f:
                    lines = f.readlines()
                    line = [int(x) for x in lines[2].split()[0:6]]
                    prefix = "{:04d}".format(line[0])
                    prefix = prefix + "".join("{:02d}".format(x) for x in line[1:3])
                    prefix = prefix + "." + "".join("{:02d}".format(x) for x in line[3:7])
                    prefix = prefix + "."

            # Copy files by adding prefix
            # Tiled files
            tile_max = 7
            if "hafs" in app:
                # Regional case has only one tile (1)
                tile_max = 2

            lst = ["fv_core.res", "fv_srf_wnd.res", "fv_tracer.res", "phy_data", "sfc_data"]
            for f in lst:
                for tile in range(1,tile_max):
                    if "_data" in f and "hafs" in app:
                        src_file = os.path.join(rundir,"RESTART","{}.nc".format(f))
                        tgt_file = os.path.join(rundir,"RESTART","{}.nc".format(prefix + f))
                    else:
                        src_file = os.path.join(rundir,"RESTART","{}.tile{}.nc".format(f,tile))
                        tgt_file = os.path.join(rundir,"RESTART","{}.tile{}.nc".format(prefix + f,tile))
                    if not os.path.isfile(src_file):
                        expect(False, "{} is missing.".format(src_file))
                    else:
                        safe_copy(src_file, tgt_file)

            # Others
            lst = ["fv_core.res.nc", "coupler.res"]
            for f in lst:
                src_file = os.path.join(rundir,"RESTART",f)
                tgt_file = os.path.join(rundir,"RESTART",prefix + f)
                if not os.path.isfile(src_file):
                    expect(False, "{} is missing.".format(src_file))
                else:
                    safe_copy(src_file, tgt_file)
    else:
        prefix = ""

    #----------------------------------------------------
    # Query jobs
    #----------------------------------------------------

    env_workflow = case.get_env("workflow")
    jobs = env_workflow.get_jobs()

    #----------------------------------------------------
    # Set input type
    #----------------------------------------------------

    # Query date and time 
    yyyy, mm, dd, hh = date_yyyymmddhh(case)

    # Check input directory to find input_file format (same folder could have both .nemsio and .grb2)
    if "case.chgres" in jobs:
        # Query input directory
        if "hafs" in app:
            din_loc_root = case.get_value("DIN_LOC_ROOT")
            icdir = os.path.join(din_loc_root,"regional","bcond","{:04d}{:02d}{:02d}{:02d}".format(yyyy,mm,dd,hh))
        else:
            icdir = os.path.join(case.get_value("DIN_LOC_IC"),"{:04d}{:02d}".format(yyyy,mm),"{:04d}{:02d}{:02d}".format(yyyy,mm,dd))

        logger.info("Checking {} directory to find raw input files ...".format(icdir))
        input_dict = {}
        if os.path.exists(icdir):
            for f in os.listdir(icdir):
                if "atm.input.ic.nemsio" in f or "sfc.input.ic.nemsio" in f:
                    if 'gaussian_nemsio' in input_dict.keys():
                        input_dict['gaussian_nemsio'].append(f)
                    else:
                        input_dict.update({'gaussian_nemsio':[f]})
                    logger.info("Found '{}' in input directory".format(f))
                elif "atm.input.ic.grb2" in f:
                    if 'grib2' in input_dict.keys():
                        input_dict['grib2'].append(f)
                    else:
                        input_dict.update({'grib2':[f]})
                    logger.info("Found '{}' in input directory".format(f))
                elif "atm.input.ic.nc" in f or "sfc.input.ic.nc" in f:
                    if 'gaussian_netcdf' in input_dict.keys():
                        input_dict['gaussian_netcdf'].append(f)
                    else:
                        input_dict.update({'gaussian_netcdf':[f]})
                    logger.info("Found '{}' in input directory".format(f))

        # Remove duplicates
        input_type_lst = list(set(input_dict.keys()))

        # Check user_nl_ufsatm for input_type and force buildnml to use it
        input_type_user = namelist_user.get_value('input_type')[0]
        if input_type_user:
            input_type_lst = list([input_type_user.strip('\"').strip('\'')])

        # If input_type is empty throw error
        if not input_type_lst:
            logger.info("No input file/s found locally! {}".format(icdir))
            logger.info("Please download raw initial condition and rename the file/s using following convention:")
            logger.info("\t NEMSIO: atm.input.ic.nemsio and sfc.input.ic.nemsio")
            logger.info("\t GRIB2 : atm.input.ic.grb2")
            logger.info("\t NetCDF: atm.input.ic.nc and sfc.input.ic.nc")
            expect(False, "Exiting ...")
        else:
            # Set grib2 as default, if we have multiple data formats such as both .grb2 and .nc
            if len(input_type_lst) > 1:
                input_type = "grib2"
            else:
                input_type = input_type_lst[0]

            logger.info("CHGRES namelist option 'input_type' is set to {}".format(input_type))

            # Check defined input_type exists in the dictionary
            if input_type in input_dict.keys():
                # Check the input file matched with the input_type
                for f in input_dict[input_type]:
                    if not os.path.exists(os.path.join(icdir,f)):
                        expect(False, "{} not found!".format(os.path.join(icdir,f)))
                    else:
                        logger.info("CHGRES will use {}".format(os.path.join(icdir,f)))
            else:
                # If there is no file with given input_type, print information and exit
                if "gaussian_nemsio" == input_type:
                    logger.info("Please download raw initial condition in NEMSIO format rename it as atm.input.ic.nemsio and sfc.input.ic.nemsio")
                elif "grib2" == input_type:
                    logger.info("Please download raw initial condition in GRIB2 format and rename it as atm.input.ic.grb2")
                elif "gaussian_netcdf" == input_type:
                    logger.info("Please download raw initial condition in NETCDF format and rename it as atm.input.ic.nc and sfc.input.ic.nc")
                else:
                    logger.info("Namelist option input_type can be 'gaussian_nemsio', 'grib2' or 'gaussian_netcdf'.")
                expect(False, "Exiting ...")
    else:
       input_type = None 
       logger.info("CHGRES is not the part of the workflow. There is no need to set 'input_type'!")

    #----------------------------------------------------
    # Namelist generator for atmosphere model (model_configure)
    #----------------------------------------------------

    # allow using user provided model_configure
    auto_namelist_file = os.path.join(confdir,"model_configure")
    user_namelist_file = os.path.join(caseroot,"SourceMods","src.ufsatm","model_configure")
    if os.path.isfile(user_namelist_file):
        logger.info("Using user provided model_configure file {}".format(user_namelist_file))
        if os.path.isfile(auto_namelist_file):
            os.remove(auto_namelist_file)
        safe_copy(user_namelist_file,auto_namelist_file)
        # TODO: need to implement nuopc parser to create nmlgen_model_configure from existing model_configure
        # CIME issue: https://github.com/ESMCI/cime/issues/3705
        # nmlgen_model_configure = parse_nuopc(auto_namelist_file)
    else:
        logger.info("Using auto-generated model_configure file {}".format(auto_namelist_file))

        # create the namelist generator object
        nmlgen_model_configure = NamelistGenerator(case, definition_file)

        # create namelist model_configure
        _create_namelist_model_configure(case, confdir, config, namelist_infile, nmlgen_model_configure, namelist_user, prefix)

    # copy namelist files to rundir
    if os.path.isdir(rundir):
        file1 = os.path.join(confdir, "model_configure")
        file2 = os.path.join(rundir, "model_configure")
        logger.debug("ufsatm configuration copy: file1 %s file2 %s " %(file1, file2))
        safe_copy(file1, file2)

    #----------------------------------------------------
    # Namelist generator for atmosphere model (input.nml.fv3)
    #----------------------------------------------------

    # allow using user provided atm_in aka input.nml
    auto_namelist_file = os.path.join(confdir,"atm_in")
    user_namelist_file = os.path.join(caseroot,"SourceMods","src.ufsatm","atm_in")
    if os.path.isfile(user_namelist_file):
        logger.info("Using user provided atm_in file {}".format(user_namelist_file))
        if os.path.isfile(auto_namelist_file):
            os.remove(auto_namelist_file)
        safe_copy(user_namelist_file,auto_namelist_file)
        nmlgen_input = parse(auto_namelist_file)
    else:
        logger.info("Using auto-generated atm_in file {}".format(auto_namelist_file))

        # create the namelist generator object
        nmlgen_input = NamelistGenerator(case, definition_file)

        # create copy of nmlgen_input to modify
        nmlgen_input_def = NamelistGenerator(case, definition_file)

        # create namelist input.nml.fv3
        _create_namelist_input(case, confdir, config, namelist_infile, nmlgen_model_configure, nmlgen_input, nmlgen_input_def, namelist_user, datestamp, input_type)

    # copy namelist files to rundir
    if os.path.isdir(rundir):
        file1 = os.path.join(confdir, "atm_in")
        file2 = os.path.join(rundir, "atm_in")
        logger.debug("ufsatm namelist copy: file1 %s file2 %s " %(file1, file2))
        safe_copy(file1, file2)

    # link file
    logger.info("\tLinking input namelist for ufsatm")
    if not os.path.exists(rundir):
        expect(False, "Couldn't find run direcory " + rundir)
    if "MOM6" not in compset:
        # there is no need to merge with MOM6 namelist, just link it
        symlink_force(file2, os.path.join(rundir, "input.nml"))
    else:
        symlink_force(file2, os.path.join(rundir, "input.nml.fv3"))

    #----------------------------------------------------
    # Copy module_configure and input.nml to CaseDocs
    #----------------------------------------------------

    if os.path.isdir(casedocsdir):
        file1 = os.path.join(confdir, "model_configure")
        file2 = os.path.join(casedocsdir, "model_configure")
        safe_copy(file1, file2)

        file1 = os.path.join(confdir, "atm_in")
        file2 = os.path.join(casedocsdir, "input.nml.fv3")
        safe_copy(file1, file2)

    #----------------------------------------------------
    # Copy/link input files
    #----------------------------------------------------

    prep_input(case, casedocsdir, datestamp, nmlgen_input, prefix, input_type)

    #----------------------------------------------------
    # Namelist generator for chgres
    #----------------------------------------------------

    if "case.chgres" in jobs and not is_restart:
        # create the namelist generator object
        nmlgen_chgres = NamelistGenerator(case, definition_file)

        # create copy of nmlgen_chgres to modify
        nmlgen_chgres_def = NamelistGenerator(case, definition_file)

        # create namelist config.nml
        _create_namelist_chgres(case, confdir, config, namelist_infile, nmlgen_chgres, nmlgen_chgres_def, nmlgen_input, namelist_user, input_type)

        # copy namelist to rundir
        if os.path.isdir(rundir):
            file1 = os.path.join(confdir, "config.nml")
            file2 = os.path.join(rundir, "config.nml")
            logger.debug("CHGRES configuration copy: file1 %s file2 %s " %(file1, file2))
            safe_copy(file1, file2)

        # copy namelist to CaseDocs
        if os.path.isdir(casedocsdir):
            file1 = os.path.join(confdir, "config.nml")
            file2 = os.path.join(casedocsdir, "config.nml")
            safe_copy(file1, file2)

    #----------------------------------------------------
    # Namelist generator for ncep_post
    #----------------------------------------------------

    if "case.gfs_post" in jobs:
        # create the namelist generator object
        nmlgen_ncep_post = NamelistGenerator(case, definition_file)

        # remove old configuration files
        for f in os.listdir(rundir):
            if "itag" in f:
                logger.warning("removing file {}".format(f))
                os.remove(os.path.join(rundir,f))

        # create namelist model_configure
        _create_namelist_ncep_post(case, confdir, config, namelist_infile, nmlgen_ncep_post, nmlgen_model_configure, namelist_user)

        # copy namelist to rundir
        if os.path.isdir(rundir):
            file1 = os.path.join(confdir, "itag.tmp")
            file2 = os.path.join(rundir, "itag.tmp")
            logger.debug("NCEP_POST configuration copy: file1 %s file2 %s " %(file1, file2))
            safe_copy(file1, file2)

        # copy namelist to CaseDocs
        if os.path.isdir(casedocsdir):
            file1 = os.path.join(confdir, "itag.tmp")
            file2 = os.path.join(casedocsdir, "itag.tmp")
            safe_copy(file1, file2)

    return

###############################################################################
def _main_func():

    caseroot = parse_input(sys.argv)
    with Case(caseroot) as case:
        buildnml(case, caseroot, "ufsatm")

if __name__ == "__main__":
    _main_func()
