Source code for pycif.plugins.datastreams.fluxes.TNO_nc.read

import datetime
import os
from pathlib import Path

import numpy as np
import xarray as xr
import pandas as pd
import string
import calendar
import pytz

from logging import info, debug
from .....utils.datastores.empty import init_empty
from .....utils.hdf5 import _hdf5_lock


[docs] def read( self, name, varnames, dates, files, interpol_flx=False, comp_type=None, tracer=None, **kwargs ): """Get TNO fluxes and apply GNFR time (and, for point sources, vertical) profiles. Loads the month/day/hour (and, for point sources, height) GNFR profile CSVs from `tracer.dir_profiles`. Then, for each requested date/file (re-reading category indices, source types and emission values only when the file changes), loops over the active emission categories (`tracer.cat_select`, or all categories found in the file), matches each to its GNFR temporal profile, and applies the corresponding month-of-year, day-of-week and hour-of-day scaling coefficients to the raw category emission (and, for point sources, the height-distribution coefficients to spread across vertical levels). Results are accumulated into a ``(lev, lat, lon)`` array per date, either scattered onto point-source domain cells or gridded area cells (area-normalized using the file's ``area`` variable). Args: self: The flux tracer plugin instance (unused directly; `tracer` is used instead). name: Unused directly, kept for interface consistency with other flux plugins. varnames: Name of the emission variable to read in the file. dates: list of ``[start, end]`` date intervals to extract; only the start of each interval is used to compute the local month/weekday/hour. files: list of files matching `dates`. interpol_flx (bool): Unused, kept for interface consistency. comp_type: Unused, kept for interface consistency. tracer: The flux tracer plugin, providing ``dir_profiles``, ``point_sources``, ``cat_select`` and ``domain``. Returns: xr.DataArray: the flux data with dimensions ``(time, lev, lat, lon)``. """ # Time profiles Pdir = tracer.dir_profiles info(Pdir) TPmonth_file = Path(Pdir, 'timeprofiles-month-in-year_GNFR.csv') TPday_file = Path(Pdir, 'timeprofiles-day-in-week_GNFR.csv') TPhour_file = Path(Pdir, 'timeprofiles-hour-in-day_GNFR.csv') VerticalP_file = Path(Pdir, 'TNO_height-distribution_GNFR.csv') info(TPmonth_file) coef_dict = {} for key, f in zip(['month', 'day', 'hour', 'height'], [TPmonth_file, TPday_file, TPhour_file, VerticalP_file]): coef = pd.read_csv(f, sep=';', comment='#') l = list(coef.columns) l2 = l[l.index('GNFR_Category_Name') + 1:] coef_dict[key] = coef[l2].values profil_cat_list = coef['TNO GNFR sectors Sept 2018'].values nlevel = len(coef_dict['height'][0]) if not tracer.point_sources: nlevel = 1 debug(f"List of categories: {profil_cat_list}") debug(f"Number of levels: {nlevel}") # list of the various fields read data = [] outdate = [] nc = None opened_file = "" idate = 0 for ddi, ff in zip(dates, files): # dd_UTC = ddi.tz_localize('UTC') # dd_CET = dd_UTC.tz_convert('Europe/Berlin') dd_CET = ddi[0] yr = dd_CET.year mm = dd_CET.month dd = dd_CET.weekday() hh = dd_CET.hour 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"] if tracer.point_sources: ds["lon"] = nc["longitude_source"] ds["lat"] = nc["latitude_source"] else: ds['ilon'] = nc['longitude_index'].values ds['ilat'] = nc['latitude_index'].values areas = nc['area'].values ds = ds.loc[ds['ilat'] > 0] ds = ds.loc[ds['ilon'] > 0] if tracer.cat_select: cat_list = tracer.cat_select else: cat_list = np.arange( ds['cat_index'].min(), ds['cat_index'].max() + 1) # Mask of locations according to file and categories TNO_array = np.zeros((nlevel, nlat, nlon)) if tracer.point_sources: mask_domain = tracer.domain.value_file == ff[0] ds = ds[(ds.cat_index.isin(cat_list)) & (ds.source_type == 2)] mask_nonzero = ds['emis'] != 0 ds = ds.loc[mask_nonzero] TNO_array = np.zeros((nlevel, tracer.domain.nlat, tracer.domain.nlon)) # Looping over categories for cat in cat_list: debug(f'Processing category: {cat}') catinprofiles = np.where(profil_cat_list==list_cat_name[cat-1])[0][0] debug(f"Category names: {list_cat_name[cat-1]}") debug(f"Category profiles: {catinprofiles}") local_month_coef = coef_dict['month'][catinprofiles][mm - 1] local_nbdays = \ calendar.mdays[mm] + (mm == 2 and calendar.isleap(yr)) debug(f"Daily profiles: {coef_dict['day'][catinprofiles]}") local_wday_coef = coef_dict['day'][catinprofiles][dd] local_hour_coef = np.array(coef_dict['hour'][catinprofiles])[hh] local_height_coef = np.array(coef_dict['height'][catinprofiles]) if tracer.point_sources: # Filtering category emissions mask_loc = ds["cat_index"] == cat ds_cat = ds.loc[mask_loc] target_mask = np.where(mask_domain)[0][mask_loc] # Computing emissions for this category emis = ds_cat['emis'].values emis_hour = (emis / 12 * local_month_coef / local_nbdays * local_wday_coef / 24 * local_hour_coef) # Factorizing by levels emis_level = emis_hour[np.newaxis] * local_height_coef[:, np.newaxis] TNO_array[:, :, target_mask] += emis_level[:, np.newaxis, :] else: mask_cat = (ds.cat_index == cat) & (ds.source_type == 1) ds_cat = ds.loc[mask_cat] emis = ds_cat['emis'].values emis_hour = (emis / 12 * local_month_coef / local_nbdays * local_wday_coef / 24 * local_hour_coef) ilons = ds_cat['ilon'] ilats = ds_cat['ilat'] np.add.at(TNO_array[0], (ilats - 1, ilons - 1), emis_hour / areas[ilats - 1, ilons - 1] ) data.append(TNO_array) outdate.append(ddi[0]) idate += 1 return xr.DataArray( data, coords={"time": outdate}, dims=("time", "lev", "lat", "lon"), )