import xarray as xr
import numpy as np
import os
import glob
import xml.etree.ElementTree as etree
from logging import debug
from .namelist import update_namelist
from .lbc import dump_lbc_files
from .inicond import dump_inicond_file
from ......utils.check.errclass import CifAttributeError
from ......utils import path
from ......utils.hdf5 import _hdf5_lock
from ..utils import (
DYN_GRID_FILENAME,
EXTPAR_FILENAME,
LBC_GRID_FILENAME,
RAD_GRID_FILENAME,
)
[docs]
def make_auxiliary(self, ddi, runsubdir,
do_simu=True, mode="fwd",
**kwargs):
"""
Initialize every file or information needed by the model to run, excluding
data that are initialized through the function ``native2inputs``.
This includes name lists for Fortran, configuration files, etc.
Every basic files related to the model should be first initialized in
``self.workdir/model`` at the initialization step in the function ``compile``.
Hereafter, files are link/copied to ``runsubdir`` from the reference ones in
``self.workdir/model``
Note:
For configuration files, one should follow the following basic rules:
- paths expected by the model should always point to the current
``runsubdir``; thus the executable should be linked or copied in
``runsubdir``; in addition, every extra file should be link with a
fixed name and the corresponding name should be given in the
name-list or configuration file.
- as many model parameters should be easily modified through the yaml
configuration file; however, for some reasons, it may be
preferable to limit the possibilities for pyCIF by keeping
some parameters fixed; this question is up to the developer
implementing one model
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
do_simu (bool): if False, the simulation does not need to be run,
hence, in principle, no auxiliary data needs to be initialized
mode (str): the running mode to compute
"""
# Otherwise initialize auxiliary info
debug(
f"Initializing auxiliary information for the model {self.plugin.name}/{self.plugin.version} with parameters: \n self: {self}\n ddi: {ddi}\n runsubdir: {runsubdir}\n do_simu: {do_simu}\n mode: {mode}\n")
# --------------------------------------------------------
# Create links for important files
# --------------------------------------------------------
# Grid files
path.link(
self.domain.dynamics_grid,
os.path.join(runsubdir, DYN_GRID_FILENAME)
)
path.link(
self.domain.map_file,
os.path.join(runsubdir, 'map_file.latbc')
)
path.link(
self.domain.extpar_file,
os.path.join(runsubdir, EXTPAR_FILENAME)
)
# Grid for lateral boundary conditions
if hasattr(self.domain, "lbc_grid"):
path.link(
self.domain.lbc_grid,
os.path.join(runsubdir, LBC_GRID_FILENAME)
)
# Coarser grid for reduced radiation
if hasattr(self.domain, "reduced_radiation_grid"):
path.link(
self.domain.reduced_radiation_grid,
os.path.join(runsubdir, RAD_GRID_FILENAME)
)
# Dictionary for the mapping: DWD GRIB2 names <-> ICON internal names
path.link(
os.path.join(self.icon_dir, 'run', 'ana_varnames_map_file.txt'),
os.path.join(runsubdir, 'map_file.ana')
)
art_dir = os.path.join(self.icon_dir, 'externals', 'art')
for p in glob.glob(os.path.join(art_dir, 'runctrl_examples', 'photo_ctrl', '*')):
path.link(p, os.path.join(runsubdir, os.path.basename(p)))
for p in glob.glob(os.path.join(art_dir, 'runctrl_examples', 'init_ctrl', '*')):
path.link(p, os.path.join(runsubdir, os.path.basename(p)))
# Icon executable and wrapper
if not hasattr(self, 'icon_exe'):
raise CifAttributeError('icon_exe attribute not found.'
'You need to specify a path to ICON executable '
'in the model Plugin.')
path.link(
os.path.join(self.icon_exe),
os.path.join(runsubdir, 'icon.exe')
)
path.link(
os.path.join(os.path.dirname(self.icon_exe),
'..', 'run/run_wrapper/todi_gpu.sh'),
os.path.join(runsubdir, 'wrapper_icon.sh')
)
# --------------------------------------------------------
# Dump the OEM ensemble files for fluxes and lbc
# --------------------------------------------------------
# Create the lambdas file for fluxes
fl = self.flux_lambdas[ddi]
if len(fl) != 0:
oem_dir = os.path.join(runsubdir, 'OEM')
# Retrieve the dimensions
nsamples, nreg = fl[list(fl.keys())[0]].shape[0:2]
ntracers = len(self.chemistry.oem_tracers)
ncat = len(self.chemistry.oem_categories)
# Create the lambdas DataArray
da_flux_lambdas = xr.DataArray(
data=np.ones((nsamples, nreg, ncat, ntracers)),
dims=("ens", "reg", "cat", "tracer")
)
# Fill the DataArray with the pre-calculated scaling factors
for ispec, spec in enumerate(self.chemistry.oem_tracers):
for iemspec, emspec in enumerate(self.chemistry.oem_categories):
if emspec in self.chemistry.mapping_active2emi_ref[spec]:
da_flux_lambdas[:, :, iemspec, ispec] = fl[emspec]
# Dump the DataArray
ds_flux_lambdas = da_flux_lambdas.to_dataset(name='lambda')
flux_lambdas_filepath = os.path.join(oem_dir, 'ens_lambda.nc')
with _hdf5_lock:
ds_flux_lambdas.to_netcdf(flux_lambdas_filepath)
# Create the scaling regions file for fluxes
flux_ens_regions_filepath = os.path.join(oem_dir, 'ens_reg.nc')
# Create the mapping from (icon grid indexes) to (lambda regions in ensemble files)
da_flux_reg_map = xr.DataArray(
np.arange(1, self.domain.nlon + 1),
dims=("cell")
)
ds_flux_reg_map = da_flux_reg_map.to_dataset(name='REG')
# Create the mapping from (16 VPRM flux cat + the cats in tracers.xml) to (lambda cat in ensemble file)
ncat_vprm = 16
da_flux_cat_map = xr.DataArray(
np.ones(ncat_vprm + da_flux_lambdas.shape[2]),
dims=("cat")
)
for iemspec, emspec in enumerate(self.chemistry.oem_categories):
da_flux_cat_map[ncat_vprm + iemspec] = iemspec + 1
ds_flux_cat_map = da_flux_cat_map.to_dataset(
name='Lambda_indicies') # ... variable name must be improved
ds_flux_reg = xr.merge([ds_flux_reg_map, ds_flux_cat_map])
with _hdf5_lock:
ds_flux_reg.to_netcdf(flux_ens_regions_filepath)
# Create the lambdas file for lbc
if len(self.lbc_lambdas[ddi]) != 0:
oem_dir = os.path.join(runsubdir, 'OEM')
for spec in self.lbc_lambdas[ddi]:
lbc_lambdas = xr.DataArray(
self.lbc_lambdas[ddi][spec],
dims=("ens", "reg")
)
lbc_lambdas = lbc_lambdas.to_dataset(name='lambda')
lbc_lambdas_filepath = os.path.join(oem_dir, 'boundary_lambda.nc')
with _hdf5_lock:
lbc_lambdas.to_netcdf(lbc_lambdas_filepath)
del self.flux_lambdas[ddi]
del self.lbc_lambdas[ddi]
# --------------------------------------------------------
# Dump the input file for inicond
# --------------------------------------------------------
if hasattr(self, "dict_inicond_dataout"):
if ddi in self.dict_inicond_dataout:
dump_inicond_file(self, ddi, runsubdir)
# --------------------------------------------------------
# Dump the input files for lbc
# --------------------------------------------------------
if hasattr(self, "dict_lbc_dataout"):
if ddi in self.dict_lbc_dataout:
dump_lbc_files(self, ddi, runsubdir)
# --------------------------------------------------------
# Dump the tracers.xml
# --------------------------------------------------------
TYPES_TAGS_XML = {
'transport': 'char',
'unit': 'char',
'c_solve': 'char',
'oem_type': 'char',
'oem_bg_ens': 'char',
'oem_cat': 'char',
'oem_vp': 'char',
'oem_tp': 'char',
'oem_tscale': 'int',
'init_mode': 'int',
'init_name': 'char',
'latbc': 'char',
'iconv': 'int',
'iturb': 'int',
}
# Load the existing tracers.xml file
xml_root = etree.Element('tracers')
# Add active species to the tracers.xml file
for spec, dtr_s in self.chemistry.dict_tracers.items():
xml_tracer = etree.SubElement(xml_root, 'chemtracer', {
'id': dtr_s['id'], 'id_cif': spec})
if 'oem_cat' in dtr_s and isinstance(dtr_s['oem_cat'], list):
dtr_s['oem_cat'] = ','.join(sorted(dtr_s['oem_cat']))
dtr_s['oem_vp'] = ','.join(sorted(dtr_s['oem_vp']))
dtr_s['oem_tp'] = ','.join(sorted(dtr_s['oem_tp']))
for key, value in dtr_s.items():
if key != 'id':
tag = etree.SubElement(xml_tracer, key, {'type': TYPES_TAGS_XML[key]})
tag.text = value
# Save the xml with the correct headers
tree = etree.ElementTree(xml_root)
etree.indent(tree)
tracers_xml_file = os.path.join(runsubdir, 'tracers.xml')
tree.write(tracers_xml_file)
with open(tracers_xml_file, 'r+') as f:
content = f.read()
f.seek(0, 0)
f.write(
'<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE tracers SYSTEM "tracers.dtd">\n\n' + content)
# --------------------------------------------------------
# Update the namelist
# --------------------------------------------------------
update_namelist(self, ddi, runsubdir)