Source code for pycif.plugins.transforms.basic.regrid.utils.get_weights

from __future__ import annotations

import shutil
from logging import debug
from os import PathLike
from pathlib import Path
from typing import Any

import numpy as np
import xarray as xr

from ......utils.check.errclass import CifValueError
from ......utils.hdf5 import _hdf5_lock
from .bilinear import bilinear
from .conservative import conservative
from .find_gridcells import find_gridcells
from .reproject import reproject_emissions


[docs] def get_weights( transform, trid, mapper, domain_in, domain_out, is_lbc, ddi, is_sparse_in=False, is_sparse_out=False, **kwargs, ): # If sparse data in inputs, switch domain_in and domain_out if is_sparse_in: dummy_domain = domain_out domain_out = domain_in domain_in = dummy_domain is_sparse_out = True is_sparse_in = False # Fetching infos from original domain nlon_in = domain_in.nlon zlon_in = domain_in.zlon zlonc_in = getattr(domain_in, "zlonc", zlon_in) nlat_in = domain_in.nlat zlat_in = domain_in.zlat zlatc_in = getattr(domain_in, "zlatc", zlat_in) # Dealing with boundary output domain if is_lbc: zlon_out = domain_out.zlon_side zlonc_out = getattr(domain_out, "zlonc_side", zlon_out) zlat_out = domain_out.zlat_side zlatc_out = getattr(domain_out, "zlatc_side", zlat_out) else: zlon_out = domain_out.zlon zlonc_out = getattr(domain_out, "zlonc", zlon_out) zlat_out = domain_out.zlat zlatc_out = getattr(domain_out, "zlatc", zlat_out) # Find correct weight file copy_weights( transform, mapper, domain_in, domain_out, ddi, is_sparse_in, is_sparse_out, trid, ) wgt = mapper["inputs"][trid]["weight_file"] wgt_file = Path(wgt[ddi]) if is_sparse_out else Path(wgt) # Loading weights if available if wgt_file.is_file(): return load_weights(wgt_file) debug( "No weight file is available, " f"recomputing weight with method '{transform.method}' for {trid}" ) # Check compatibility of method with sparse data if (is_sparse_out or is_sparse_in) and transform.method == "mass-conservation": raise CifValueError( "Mass-conservation method is not compatible with sparse data" ) # Otherwise, creating them if transform.method == "mass-conservation": weights = reproject_emissions( 0.0 * zlon_in, zlonc_in, zlatc_in, zlonc_out, zlatc_out, orig_regular=transform.orig_regular, orig_unstructured=getattr(domain_in, "unstructured_domain", False), orig_lon_cyclic=getattr(domain_in, "lon_cyclic", False), target_unstructured=getattr(domain_out, "unstructured_domain", False), return_weight=True, resol=getattr(transform, "resol", 10), rounding_domain=transform.rounding_domain, ) elif transform.method == "fast-conservative": processes = getattr(transform, "processes", None) weights = conservative(domain_in, domain_out, transform.chunk_size, processes) elif transform.method == "bilinear": weights = bilinear( domain_in, nlon_in, nlat_in, zlon_in, zlat_in, zlon_out, zlat_out ) elif transform.method == "gridcell": direction = getattr(transform, "direction", "forward") grid_to_surface = getattr(transform, "grid_to_surface", False) chunk_size = transform.chunk_size weights = find_gridcells( domain_in, domain_out, forward_direction=direction == "forward", grid_to_surface=grid_to_surface, chunksize=chunk_size, ) else: raise CifValueError( f"Regrid method not known: {transform.method}\n" "Please check your Yaml file" ) save_weights(wgt_file, weights, domain_in, domain_out) return weights
[docs] def save_weights( path: str | PathLike[str], weights: dict[str, np.ndarray], domain_in: Any | None = None, domain_out: Any | None = None, ) -> None: if not set(weights).issubset({"i", "j", "wgt", "filtered", "non_filtered"}): raise CifValueError( f"unexpected keys in weights, got {list(weights)}, " + "expected a subset of ['i', 'j', 'wgt', 'filtered', 'non_filtered']" ) ds = xr.Dataset() for varname in ["i", "j", "wgt"]: val = weights[varname] ds[varname] = (["n"], val.flatten()) if val.ndim > 1: ds[varname].attrs["shape"] = val.shape for varname in ["filtered", "non_filtered"]: if varname in weights: ds[varname] = ([f"n_{varname}"], weights[varname]) if domain_in is not None: ds.attrs.update( { "domain_in_nlat": domain_in.nlat, "domain_in_nlon": domain_in.nlon, } ) if domain_out is not None: ds.attrs.update( { "domain_out_nlat": domain_out.nlat, "domain_out_nlon": domain_out.nlon, } ) # Save as NetCDF with compression enabled encoding = {varname: {"zlib": True, "complevel": 4} for varname in ds.data_vars} with _hdf5_lock: ds.to_netcdf(path, encoding=encoding)
[docs] def load_weights(path: str | PathLike[str]) -> dict[str, np.ndarray]: weights = {} with _hdf5_lock: with xr.open_dataset(path) as ds: for varname, da in ds.data_vars.items(): if "shape" in da.attrs: weights[varname] = da.values.reshape(da.attrs["shape"]) else: weights[varname] = da.values return weights
[docs] def copy_weights( transform, mapper, domain_in, domain_out, ddi, is_sparse_in, is_sparse_out, trid, ): orig_dir = Path(getattr(transform, "dir_wgt", "")) prefix = f"wgt_file_{domain_in.nlat}-{domain_in.nlon}" # Copy weigth file if specified if not is_sparse_out: # Pass if already defined if "weight_file" in mapper["inputs"][trid]: return if getattr(transform, "target_lbc", False): nlon_out = domain_out.nlon_side nlat_out = domain_out.nlat_side else: nlat_out, nlon_out = domain_out.zlat.shape filename = f"{prefix}_{nlat_out}-{nlon_out}_{transform.method}.nc" target_file = Path(transform.dir_regrid, filename) mapper["inputs"][trid]["weight_file"] = target_file else: # Pass if already defined if "weight_file" not in mapper["inputs"][trid]: mapper["inputs"][trid]["weight_file"] = {} if ddi in mapper["inputs"][trid]["weight_file"]: return # Import all possible files in dir_wgt filename = f"{prefix}_{domain_out.nlat}_{transform.method}_{ddi:%Y%m%d%H%M}.nc" target_file = Path(transform.dir_regrid, filename) mapper["inputs"][trid]["weight_file"][ddi] = target_file orig_file = orig_dir / filename if orig_file.is_file(): shutil.copy(orig_file, target_file)