Source code for pycif.plugins.models.iconart.io.inputs.namelist

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)