Source code for pycif.plugins.controlvects.standard.utils.dimensions

import numpy as np
import os
from netCDF4 import Dataset
import datetime
import itertools
from osgeo import ogr
from logging import info, debug
from .....utils.check.errclass import CifError


[docs] def hresol2dim(tracer, dom, **kwargs): """Computes the horizontal size of a control vector from its resolution Args: tracer (Plugin): definition of the tracer, including the resolution and additional information on the resolution domain (dict): the domain grid Returns int: the size of the control vector for this component """ if tracer.hresol == "hpixels": out_dimensions = ( dom.zlon.size if not getattr(tracer, "is_lbc", False) else dom.zlon_side.size ) elif tracer.hresol == "bands": # Check that the lists are sorted if ( not sorted(tracer.bands_lat) == tracer.bands_lat or not sorted(tracer.bands_lon) == tracer.bands_lon ): raise CifError( "Bands in the control vector are not sorted!\n" "Please check your configuration file." ) # Compute the total number of dimensions in the grid if not hasattr(tracer, "nbands"): tracer.nbands = (len(tracer.bands_lat) - 1) * ( len(tracer.bands_lon) - 1 ) out_dimensions = tracer.nbands elif tracer.hresol == "ibands": # Check that the lists are sorted if ( not sorted(tracer.bands_i) == tracer.bands_i or not sorted(tracer.bands_j) == tracer.bands_j ): raise CifError( "Bands in the control vector are not sorted!\n" "Please check your configuration file." ) # Compute the total number of dimensions in the grid if not hasattr(tracer, "nbands"): tracer.nbands = (len(tracer.bands_i) - 1) * ( len(tracer.bands_j) - 1 ) out_dimensions = tracer.nbands elif tracer.hresol == "regions": if not hasattr(tracer, "regions"): region_infos = tracer.regions_infos if hasattr(region_infos, "read") \ and not getattr(region_infos, "default_read", False): tracer.regions = region_infos.read( "regions", "regions", [[datetime.datetime(1970, 1, 1), datetime.datetime(1970, 1, 1)]], [os.path.join(region_infos.dir, region_infos.file)], tracer=region_infos )[0, 0].values file_regions = f"{region_infos.dir} / {region_infos.file}" else: file_regions = os.path.join( region_infos.dir, region_infos.file) with Dataset(file_regions, "r") as f: tracer.regions = f.variables["regions"][:] # Unmask arrays if np.ma.isMaskedArray(tracer.regions): tracer.regions = tracer.regions.data # Check that regions have the correct dimensions if tracer.regions.shape != dom.zlat.shape: raise CifError( f"Regions were not correctly defined in {file_regions}" ) # Number of regions tracer.region_ids = np.unique( tracer.regions[~np.isnan(tracer.regions)]) tracer.nregions = len(tracer.region_ids) # Deal with land sea mask regions_lsm = getattr(tracer, 'regions_lsm', False) if regions_lsm: # Set ocean and land mask according to the region definition file # Negative numbers are for ocean, positive numbers are for land tracer.regions_lsmask = np.ndarray(tracer.regions.shape) # Set ocean and land mask tracer.regions_lsmask[tracer.regions < 0] = 0 tracer.regions_lsmask[tracer.regions > 0] = 1 # Default behaviour: optimize ocean boxes tracer.inc_ocean = getattr(tracer, 'inc_ocean', True) out_dimensions = tracer.nregions elif tracer.hresol == "global": out_dimensions = 1 else: raise CifError(f"This resolution is not processed by the CIF: {tracer.hresol}") # Pre-compute areas if not already done if not hasattr(tracer.domain, "areas"): if hasattr(tracer, "glob_err") \ or tracer.hresol not in ["global", "hpixels"]: info("Computing areas") tracer.domain.calc_areas(**kwargs) # Stop here if not regions nor bands if tracer.hresol in ["global", "hpixels"]: return out_dimensions # Compute region areas debug("Computing region total areas. This can take a while...") zlon = tracer.domain.zlon zlat = tracer.domain.zlat if tracer.hresol == "regions": mask_regions = ~np.isnan(tracer.regions) _, idx, _ = np.unique( tracer.regions[mask_regions], return_counts=True, return_inverse=True ) tracer.region_areas = np.bincount( idx, tracer.domain.areas[mask_regions].flatten()) elif tracer.hresol == "bands": dlat = zlat[..., np.newaxis] - \ np.array(tracer.bands_lat)[np.newaxis, np.newaxis, 1:] ilat = np.argmax(dlat < 0, axis=2).astype(float) ilat[(zlat < np.min(tracer.bands_lat)) | (zlat > np.max(tracer.bands_lat))] = np.nan dlon = zlon[..., np.newaxis] - \ np.array(tracer.bands_lon)[np.newaxis, np.newaxis, 1:] ilon = np.argmax(dlon < 0, axis=2).astype(float) ilon[(zlon < np.min(tracer.bands_lon)) | (zlon > np.max(tracer.bands_lon))] = np.nan bands_idx = ilat * (len(tracer.bands_lon) - 1) + ilon mask_bands = ~np.isnan(bands_idx) _, idx, _ = np.unique( bands_idx[mask_bands], return_counts=True, return_inverse=True ) tracer.region_areas = np.bincount( idx, tracer.domain.areas[mask_bands].flatten()) elif tracer.hresol == "ibands": ilon = -1 * np.ones_like(zlon) jbands = np.maximum(0, tracer.bands_j[:-1]).astype(int) jbands_index = np.arange(len(jbands) - 1) ilon[:, np.array(tracer.bands_j[:-1]).astype(int) ] = np.arange(len(tracer.bands_j) - 1) ilon = np.maximum.accumulate(ilon, axis=1) ilat = -1 * np.ones_like(zlat) ilat[np.array(tracer.bands_i[:-1]).astype(int) ] = np.arange(len(tracer.bands_i) - 1)[:, np.newaxis] ilat = np.maximum.accumulate(ilat, axis=0) # Crop outside of bands meshj, meshi = np.meshgrid( np.arange(zlat.shape[1]), np.arange(zlat.shape[0]) ) ilon[(meshj < np.min(tracer.bands_j)) | (meshj > np.max(tracer.bands_j))] = np.nan ilat[(meshi < np.min(tracer.bands_i)) | (meshi > np.max(tracer.bands_i))] = np.nan # Compute bands areas bands_idx = ilat * (len(tracer.bands_j) - 1) + ilon mask_bands = ~np.isnan(bands_idx) _, idx, _ = np.unique( bands_idx[mask_bands], return_counts=True, return_inverse=True ) tracer.region_areas = np.bincount( idx, tracer.domain.areas[mask_bands].flatten()) else: raise CifError( f"This resolution is not processed by the CIF: {tracer.hresol}" ) # Compute centroids of regions if hasattr(tracer, "hcorrelations"): debug("Computing centroids. This can take very long...") # TODO: switch to geopandas to optimze the computation tracer.centroids_lons = [] tracer.centroids_lats = [] # List of regions depending on type of resolution reg_list = [] if tracer.hresol == "regions": reg_list = tracer.region_ids elif tracer.hresol in ["ibands", "bands"]: reg_list = range(tracer.nbands) tracer.region_areas2 = [] for regID in reg_list: # Get list of lon/lat of the sub-region zlon = tracer.domain.zlon zlat = tracer.domain.zlat if tracer.hresol == "regions": mask = tracer.regions == regID lons = zlon[mask] lats = zlat[mask] # tracer.region_areas.append( # tracer.domain.areas[mask].sum()) elif tracer.hresol == "bands": lat_bounds, lon_bounds = list(itertools.product( zip(tracer.bands_lat[:-1], tracer.bands_lat[1:]), zip(tracer.bands_lon[:-1], tracer.bands_lon[1:]) ))[regID] mask = ( (zlon >= lon_bounds[0]) & (zlon < lon_bounds[1]) & (zlat >= lat_bounds[0]) & (zlat < lat_bounds[1]) ) lons = zlon[mask] lats = zlat[mask] elif tracer.hresol == "ibands": i_bounds, j_bounds = list(itertools.product( zip(tracer.bands_i[:-1], tracer.bands_i[1:]), zip(tracer.bands_j[:-1], tracer.bands_j[1:]) ))[regID] lons = zlon[i_bounds[0]:i_bounds[1], j_bounds[0]:j_bounds[1]].flatten() lats = zlat[i_bounds[0]:i_bounds[1], j_bounds[0]:j_bounds[1]].flatten() tracer.region_areas2.append( tracer.domain.areas[i_bounds[0]:i_bounds[1], j_bounds[0]:j_bounds[1]].sum()) else: raise CifError( f"This resolution is not processed by the CIF: {tracer.hresol}" ) multipoint = ogr.Geometry(ogr.wkbMultiPoint) for lon, lat in zip(lons, lats): pt = ogr.Geometry(ogr.wkbPoint) pt.AddPoint(float(lon), float(lat)) multipoint.AddGeometry(pt) centroid = multipoint.Centroid() tracer.centroids_lons.append(centroid.GetX()) tracer.centroids_lats.append(centroid.GetY()) tracer.region_areas = np.array(tracer.region_areas)[:, np.newaxis] tracer.centroids_lons = np.array( tracer.centroids_lons)[:, np.newaxis] tracer.centroids_lats = np.array( tracer.centroids_lats)[:, np.newaxis] return out_dimensions
[docs] def vresol2dim(tracer, dom, **kwargs): """Computes the horizontal size of a control vector from its resolution Args: tracer (Plugin): definition of the tracer, including the resolution and additional information on the resolution domain (dict): the domain grid Returns int: the size of the control vector for this component """ # Default vertical resolution is integrated columns tracer.vresol = getattr(tracer, "vresol", "column") # Getting the number of vertical levels form the tracer domain if not # already defined (defaults to nlev = 1) if not hasattr(tracer, "nlev"): if hasattr(tracer, "domain"): tracer.nlev = tracer.domain.nlev else: tracer.nlev = 1 # Loop over possible vertical resolution if tracer.vresol == "vpixels": tracer.levels = np.arange(tracer.nlev) return tracer.nlev elif tracer.vresol == "kbands": # Compute the total number of dimensions in the grid if not hasattr(tracer, "nvbands"): tracer.nvbands = len(tracer.kbands) - 1 tracer.levels = np.array(tracer.kbands[:-1]) return tracer.nvbands elif tracer.vresol == "column": tracer.levels = np.array([0]) tracer.nlev = 1 return tracer.nlev