Source code for pycif.plugins.datastreams.fluxes.CAMSREG_nc.get_domain

import numpy as np
import xarray as xr
import glob
import datetime
import os
import pandas as pd
import itertools
from .....utils.classes.setup import Setup
from .....utils.classes.domains import Domain
from logging import info, debug
from .....utils.check.errclass import CifError
from .....utils.hdf5 import _hdf5_lock


[docs] def get_domain(ref_dir, ref_file, input_interval, target_dir, tracer=None): """Build the horizontal (and, for point sources, vertical) domain. Dispatches to :func:`get_area_domain` for the default gridded area-source mode, or to :func:`get_point_domain` when ``tracer.point_sources`` is set. Args: ref_dir (str): directory where the original files are found. ref_file (str): (template) name of the original files. input_interval (list): simulation interval, as a list of the two bounding dates. target_dir (str): directory where links to the original files are created. tracer: the tracer Plugin, corresponding to the paragraph :bash:`datavect/components/fluxes/parameters/my_species` in the configuration yaml. Returns: Domain: a domain class object, gridded (area mode) or unstructured (point-source mode). """ if not tracer.point_sources: domain = get_area_domain(tracer) else: domain = get_point_domain(tracer) return domain
[docs] def get_area_domain(tracer): """Build a regular gridded domain from a TNO/CAMS-REG reference file. Reads ``longitude``/``latitude`` and their ``_bounds`` variables from the first fetched input file to build the grid centers and corners, with a single dummy vertical level (surface only). Args: tracer: the tracer Plugin holding the list of fetched input files (``tracer.input_files``). Returns: Domain: a gridded, single-level domain. Raises: CifError: if no reference file could be found among the fetched input files. """ # Looking for a reference file to read lon/lat in domain_file = None ref_file = list(itertools.chain.from_iterable(tracer.input_files.values())) if len(ref_file) != 0: domain_file = ref_file[0][0] if domain_file is None: raise CifError( "TNO domain could not be initialized as no file was found" ) # Read lon/lat in with _hdf5_lock: nc = xr.open_dataset(domain_file, decode_times=False) llon = nc['longitude'].values llat = nc['latitude'].values llonb = nc['longitude_bounds'].values llatb = nc['latitude_bounds'].values # compute the corner matrix llonc = np.append(llonb[:, 0], llonb[-1, 1]) llatc = np.append(llatb[:, 0], llatb[-1, 1]) lon, lat = np.meshgrid(llon, llat) lonc, latc = np.meshgrid(llonc, llatc) nlat, nlon = lat.shape[0], lat.shape[1] # If no vertical dimension for emissions, provide dummy vertical punit = "Pa" nlevs = 1 sigma_a_mid = np.array([0]) sigma_b_mid = np.array([1]) # Put it to a domain Plugin domain = Domain(nlon=nlon, nlat=nlat, zlon=lon, zlat=lat, zlonc=lonc, zlatc=latc, nlev=nlevs, pressure_unit=punit, sigma_b_mid=sigma_b_mid, sigma_a_mid=sigma_a_mid) return domain
[docs] def get_point_domain(tracer): """Build an unstructured point-source domain from TNO/CAMS-REG files. Reads the TNO vertical height-distribution profile CSV to determine the vertical levels, then loops over all fetched input files to extract non-zero point-source emission locations (filtered by emission category and source type), building an unstructured domain where each point is associated with the file it came from (``value_file``). Args: tracer: the tracer Plugin, giving access to ``dir_profiles``, ``cat_select``, and the fetched ``input_dates``/``input_files``. Returns: Domain: an unstructured domain with one horizontal "cell" per point source and ``nlev`` vertical height bins. """ # Get vertical extent of point sources VerticalP_file = tracer.dir_profiles + '/TNO_height-distribution_GNFR.csv' coef = pd.read_csv(VerticalP_file, sep=';', comment='#') l = list(coef.columns) info(l) height = l[l.index('GNFR_Category_Name') + 1:] height_down = np.array([float(x.split('-')[0].replace(" ", "")) for x in height]) height_top = np.array( [float(x.split('-')[1].replace("m", "").replace(" ", "")) for x in height]) nlevs = len(height_top) info(f'Top heighs: {height_top}') # Now loop over files to get lon/lat of point sources dates = tracer.input_dates files = tracer.input_files varnames = tracer.varname TNO_array = [] nc = None opened_file = "" for dd_ref in dates: for ddi, ff in zip(dates[dd_ref], files[dd_ref]): # Open file if not already processed if ff != opened_file or nc is None: debug(f'Reading of {[varnames]} in {ff} for {ddi}') opened_file = ff ds = pd.DataFrame({}) with _hdf5_lock: nc = xr.open_dataset(ff[0], decode_times=False) list_cat_name = nc['emis_cat_code'].values list_cat_name = [b.decode("utf-8") for b in list_cat_name] debug(f"List of category names: {list_cat_name}") ds['cat_index'] = nc['emission_category_index'].values ds['source_type'] = nc['source_type_index'].values ds['emis'] = nc[varnames].values nlon = nc.dims["longitude"] nlat = nc.dims["latitude"] ds["lon"] = nc["longitude_source"] ds["lat"] = nc["latitude_source"] if tracer.cat_select: cat_list = tracer.cat_select else: cat_list = np.arange( ds['cat_index'].min(), ds['cat_index'].max() + 1) else: continue # Loop over point sources ds_cat = ds[(ds.cat_index.isin(cat_list)) & (ds.source_type == 2)] mask_nonzero = ds_cat['emis'] != 0 ds_cat = ds_cat.loc[mask_nonzero] lons = ds_cat["lon"] lats = ds_cat["lat"] tmp_ds = pd.DataFrame({ "lon": lons, "lat": lats, "file": len(lats) * [ff[0]], }) tmp_ds["file"] = tmp_ds["file"].astype("category") TNO_array.append(tmp_ds) TNO_array = pd.concat(TNO_array) # Now prepare variables for the domain zlon = TNO_array["lon"].values[np.newaxis, :] zlat = TNO_array["lat"].values[np.newaxis, :] zlonc = zlon[:, :] zlatc = zlat[:, :] nlon = len(TNO_array["lon"]) nlat = 1 # Put it to a domain Plugin domain = Domain( nlon=nlon, nlat=nlat, zlon=zlon, zlat=zlat, zlonc=zlonc, zlatc=zlatc, nlev=nlevs, heights=height_top, height_unit="m", unstructured_domain=True, value_file=TNO_array["file"], ) return domain