import os
import shutil
import pandas as pd
import numpy as np
from logging import debug
import f90nml
from ......utils import path
from ..utils import (
DYN_GRID_FILENAME,
RAD_GRID_FILENAME,
EXTPAR_FILENAME,
INICOND_FILENAME,
LBC_GRID_FILENAME,
LBC_DIRNAME,
OUTPUT_DIRNAME,
OUTPUT_PATTERN,
)
[docs]
def update_namelist(self, ddi, runsubdir):
"""
Update the namelist for running ICON-ART
Args:
self: the model plugin
ddi (datetime.datetime): the start data identifying
the present simulation period
runsubdir (str): path to the current sub-simulation work directory
"""
# --------------------------------------------------------------------------------
# --------------------------------------------------------------------------------
# -- Master Namelist
# --------------------------------------------------------------------------------
# --------------------------------------------------------------------------------
# The main namelist file is required by icon
# Must be in the same folder as where the executable is run
time_format = '%Y-%m-%dT%XZ'
ddf = self.tstep_dates[ddi][-1]
ini_datetime = getattr(self, "ensrf_datei", self.datei)
# Restart options
restart_in_path = f"{runsubdir}/restart_ATMO_DOM01.nc"
restart_out_path = ddi.strftime(f"{runsubdir}/../chain/restart_%Y%m%d%H.nc")
is_restart = os.path.isfile(restart_in_path)
restart_interval = int((ddf - ddi).total_seconds() / 3600.)
# Namelist dictionary
master_namelist = {
'master_nml': {
'lrestart': is_restart,
},
'master_model_nml': {
'model_name': 'ATMO', # str for naming this component
'model_namelist_filename':'namelist_nwp.namelist', # File with the model namelist
'model_type': 1, # 1=atmosphere
# TODO: see if we can remove that to use default
'model_min_rank': 1,
'model_max_rank': 65536,
'model_inc_rank': 1,
},
'master_time_control_nml': {
'restartTimeIntval': f'PT{restart_interval}H'
},
'time_nml': {
'ini_datetime_string': ini_datetime.strftime(time_format),
'end_datetime_string': ddf.strftime(time_format),
'is_relative_time': False
}
}
nml_path = os.path.join(runsubdir, 'icon_master.namelist')
path.remove(nml_path)
f90nml.write(
master_namelist,
nml_path
)
# --------------------------------------------------------------------------------
# --------------------------------------------------------------------------------
# -- NWP Namelist
# --------------------------------------------------------------------------------
# --------------------------------------------------------------------------------
# Load the namelist with parameters to run icon and copy it to ICON rundir
original_namelist_nwp_path = self.namelist_file
namelist_nwp_path = os.path.join(runsubdir, 'namelist_nwp.namelist')
shutil.copy(
original_namelist_nwp_path,
namelist_nwp_path
)
# Read the namelist
namelist_nwp = f90nml.read(namelist_nwp_path)
# --------------------------------------------------------------------------------
# -- Initialize sections
# --------------------------------------------------------------------------------
if 'initicon_nml' not in namelist_nwp:
namelist_nwp['initicon_nml'] = {}
if 'run_nml' not in namelist_nwp:
namelist_nwp['run_nml'] = {}
if 'art_nml' not in namelist_nwp:
namelist_nwp['art_nml'] = {}
if 'oemctrl_nml' not in namelist_nwp:
namelist_nwp['oemctrl_nml'] = {}
# --------------------------------------------------------------------------------
# -- Lateral boundary conditions
# --------------------------------------------------------------------------------
if hasattr(self.domain, "lbc_grid"):
if 'limarea_nml' not in namelist_nwp:
namelist_nwp['limarea_nml'] = {}
namelist_nwp['limarea_nml']['latbc_path'] = '../' + LBC_DIRNAME
namelist_nwp['limarea_nml']['latbc_filename'] = 'ifs_<y><m><d><h>_lbc.nc'
namelist_nwp['limarea_nml']['latbc_varnames_map_file'] = 'map_file.latbc'
namelist_nwp['limarea_nml']['latbc_boundary_grid'] = LBC_GRID_FILENAME
# namelist_nwp['limarea_nml']['latbc_boundary_grid'] = ' '
# --------------------------------------------------------------------------------
# -- Grid and external parameters
# --------------------------------------------------------------------------------
# Grid namelist
namelist_nwp['grid_nml']['dynamics_grid_filename'] = DYN_GRID_FILENAME
namelist_nwp['grid_nml']['l_limited_area'] = hasattr(self.domain, "lbc_grid")
if hasattr(self.domain, "reduced_radiation_grid"):
namelist_nwp['grid_nml']['radiation_grid_filename'] = RAD_GRID_FILENAME
namelist_nwp['grid_nml']['lredgrid_phys'] = True
else:
namelist_nwp['grid_nml']['radiation_grid_filename'] = " "
namelist_nwp['grid_nml']['lredgrid_phys'] = False
# External parameters
namelist_nwp['extpar_nml']['extpar_filename'] = EXTPAR_FILENAME
# --------------------------------------------------------------------------------
# -- Meteorological initial conditions
# --------------------------------------------------------------------------------
namelist_nwp['initicon_nml']['ifs2icon_filename'] = INICOND_FILENAME
namelist_nwp['initicon_nml']['ana_varnames_map_file'] = 'map_file.ana'
# --------------------------------------------------------------------------------
# -- Tracers and initial concentrations for ART
# --------------------------------------------------------------------------------
# Tracers definition
namelist_nwp['art_nml']['cart_chemtracer_xml'] = \
os.path.join(runsubdir, 'tracers.xml')
# Ensure that ART is enabled
namelist_nwp['run_nml']['lart'] = True
namelist_nwp['run_nml']['restart_filename'] = restart_out_path
# Type of initialization (4 = file)
namelist_nwp['art_nml']['iart_init_gas'] = 4
# Inicond files (pressure coordinates and actual values)
namelist_nwp['art_nml']['cart_cheminit_coord'] = INICOND_FILENAME
namelist_nwp['art_nml']['cart_cheminit_file'] = INICOND_FILENAME
namelist_nwp['art_nml']['cart_cheminit_type'] = "ERA"
# --------------------------------------------------------------------------------
# -- Radiation (ECRAD)
# --------------------------------------------------------------------------------
namelist_nwp['radiation_nml']['ecrad_data_path'] = os.path.join(self.icon_dir, "externals/ecrad/data")
# --------------------------------------------------------------------------------
# -- OEM Fluxes
# --------------------------------------------------------------------------------
# OEM namelist
list_oem_attributes = [
'gridded_emissions',
'vertical_profile',
'ens_lambda',
'ens_reg',
'boundary_lambda',
'boundary_regions',
]
if self.use_hourofyear:
list_oem_attributes += ['hour_of_year']
else:
list_oem_attributes += [
'hour_of_day',
'day_of_week',
'month_of_year'
]
# Link the file path in the namelist
for name in list_oem_attributes:
nml_oem_name = name + '_nc'
namelist_nwp['oemctrl_nml'][nml_oem_name] = \
os.path.join(runsubdir, 'OEM', name + '.nc')
# --------------------------------------------------------------------------------
# -- Outputs
# --------------------------------------------------------------------------------
# Make a directory for the outputs
path.init_dir(os.path.join(runsubdir, OUTPUT_DIRNAME))
# Specifiy a custom output format for icon in cif
# see icon_tutorial 7.1. Settings for the Model Output
namelist_nwp['output_nml'] = {
'output_filename': os.path.join(OUTPUT_DIRNAME, OUTPUT_PATTERN.split('*')[0]),
'output_bounds': [
float(0), # start [s] (start at the beggining of the simulation)
pd.Timedelta(ddf - ini_datetime).total_seconds(), # end [s]
pd.Timedelta(self.output_resolution).total_seconds(), # increment [s]
],
# TODO: option to select specific variables, else use a default set
# Output variables
'ml_varlist': [
'u',
'v',
'w',
'temp',
'qv',
'rho',
'z_mc',
'z_ifc',
'pres',
'pres_ifc',
'pres_sfc',
'group:ART_CHEMISTRY',
],
'dom': 1, # write domain 1 only
'remap': 0, # 1: remap to lat-lon grid, 0: triangular
'filetype': 4, # output format: 2=GRIB2, 4=NETCDFv2
'steps_per_file': 1, # Max number of output steps in one output file
'steps_per_file_inclfirst': False,
'mode': 1, # 1: forecast mode (relative t-axis), 2: climate mode (absolute t-axis)
'include_last': False, # whether to include the last time step
'filename_format': '<output_filename>_<datetime2>',
'output_grid': False, # Flag whether grid information is added to output.
}
# Dump outputs remapped onto a latlon grid if needed
if getattr(self, "dump_output_latlon", False):
path.init_dir(os.path.join(runsubdir, OUTPUT_DIRNAME, 'latlon'))
lon_min = np.floor(self.domain.zlon.min())
lon_max = np.ceil(self.domain.zlon.max())
lon_res = (lon_max - lon_min) / (int(self.domain.nlon ** 0.5))
lat_min = np.floor(self.domain.zlat.min())
lat_max = np.ceil(self.domain.zlat.max())
lat_res = (lat_max - lat_min) / (int(self.domain.nlon ** 0.5))
output_nml_latlon = {
'output_filename': os.path.join(OUTPUT_DIRNAME, 'latlon',
OUTPUT_PATTERN.split('*')[0] + '_latlon'),
'output_bounds': [
float(0), # start [s] (start at the beggining of the simulation)
pd.Timedelta(ddf - ini_datetime).total_seconds(), # end [s]
pd.Timedelta(self.output_resolution).total_seconds(), # increment [s]
],
# TODO: option to select specific variables, else use a default set
# Output variables
'ml_varlist': [
'group:ATMO_ML_VARS',
'z_mc',
'z_ifc',
'group:ART_CHEMISTRY',
],
'dom': 1, # write domain 1 only
'remap': 1, # 1: remap to lat-lon grid, 0: triangular
'reg_lon_def': [ # longitude coordinate
lon_min,
lon_res,
lon_max,
],
'reg_lat_def': [ # latitude coordinate
lat_min,
lat_res,
lat_max,
],
'filetype': 4, # output format: 2=GRIB2, 4=NETCDFv2
'steps_per_file': 1, # Max number of output steps in one output file
'steps_per_file_inclfirst': False,
'mode': 1, # 1: forecast mode (relative t-axis), 2: climate mode (absolute t-axis)
'include_last': False, # whether to include the last time step
'filename_format': '<output_filename>_<datetime2>',
'output_grid': False, # Flag whether grid information is added to output.
}
namelist_nwp.add_cogroup('output_nml', output_nml_latlon)
# --------------------------------------------------------------------------------
# -- Write the namelist
# --------------------------------------------------------------------------------
f90nml.write(namelist_nwp, namelist_nwp_path, force=True)
# --------------------------------------------------------------------------------
# -- Explain which settings have been overwritten
# --------------------------------------------------------------------------------
DICT_MODIFIED_SETTINGS = {
'master_nml': [
'lrestart',
],
'master_model_nml': [
'model_name',
'model_namelist_filename',
'model_type',
'model_min_rank',
'model_max_rank',
'model_inc_rank',
],
'master_time_control_nml': [
'restartTimeIntVal',
],
'time_nml': [
'ini_datetime_string',
'end_datetime_string',
'is_relative_time',
],
'grid_nml': [
'dynamics_grid_filename',
'radiation_grid_filename',
],
'extpar_nml': [
'extpar_filename',
],
'initicon_nml': [
'ifs2icon_filename',
'ana_varnames_map_file'
],
'art_nml':[
'cart_chemtracer_xml',
'iart_init_gas',
'cart_cheminit_coord',
'cart_cheminit_file'
],
'run_nml': [
'lart',
'restart_filename',
],
'output_nml': [
'ALL SETTINGS'
]
}
if self.meteo_lbc_dir:
DICT_MODIFIED_SETTINGS.update({
'limarea_nml': [
'latbc_path',
'latbc_filename',
'latbc_varnames_map_file',
'latbc_boundary_grid'
],
})
DICT_MODIFIED_SETTINGS.update({
'oemctrl_nml': list_oem_attributes
})
_sep = "\n\t"
towrite = "The following namelist settings have been created or overwritten by the CIF:\n\n" \
+ "\n".join([f"{item}\n-----------------\n\t{_sep.join(value)}\n"
for (item, value) in DICT_MODIFIED_SETTINGS.items()])
debug(towrite)