compression-safeguards
  • Home
  • Try the safeguards

API Reference

  • Overview
  • compression_safeguards
    • api
    • safeguards
      • abc
      • combinators
        • all
        • any
        • assume_safe
        • select
      • eb
      • pointwise
        • abc
        • eb
        • lossless
        • qoi
          • eb
        • same
        • sign
      • qois
      • stencil
        • abc
        • qoi
          • eb
    • utils
      • bindings
      • cast
      • error
      • intervals
      • typing
  • numcodecs_safeguards
    • checksum
    • compute
    • lossless
  • xarray_safeguards

Examples

  • Datasets
  • Special values
    • NaN values
    • Missing values
  • Regions of Interest (RoIs)
    • Latent Heat Flux
      • Compressing slhf with safeguards
      • Visual comparison of the error distributions for the surface latent heat flux
  • Quantities of Interest (QoIs)
    • Pointwise: Log-scale Specific Humidity
    • Semi-Pointwise: Kinetic energy
    • Higher-order derivatives
    • Neighbourhood: Relative Vorticity
    • Bounding the dSSIM metric
  • Topology
    • Precipitation extrema
    • Isosurfaces
  • Error bound distributions
    • Absolute error bound
  • Safeguards on chunked data
    • xarray-safeguards
  • Preview
    • Incremental safeguards

Links

  • GitHub
  • PyPI
    • compression-safeguards
    • numcodecs-safeguards
    • xarray-safeguards
compression-safeguards
  • Examples
  • Regions of Interest (RoIs)
  • Latent Heat Flux
  • Try     View Source

Preserving regionally varying error bounds (regions of interest) with safeguards¶

In this example, we show how a regionally varying error bound for surface latent heat flux, based on the land-sea mask, can be provided by the safeguards.

Copied!
import ssl

ssl._create_default_https_context = ssl._create_stdlib_context
import ssl ssl._create_default_https_context = ssl._create_stdlib_context
Copied!
from pathlib import Path

import earthkit.plots
import humanize
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
import numpy as np
import numpy.ma as ma
import pandas as pd
import xarray as xr
from pathlib import Path import earthkit.plots import humanize import matplotlib.colors as mcolors import matplotlib.pyplot as plt import numpy as np import numpy.ma as ma import pandas as pd import xarray as xr
Copied!
# Retrieve the data
ERA5 = xr.open_dataset(Path() / "data" / "era5-lh" / "data.nc")
ERA5_LH = ERA5["slhf"].sel(valid_time="2024-04-02T12:00:00")
ERA5_LSM = ERA5["lsm"].sel(valid_time="2024-04-02T12:00:00")
# Retrieve the data ERA5 = xr.open_dataset(Path() / "data" / "era5-lh" / "data.nc") ERA5_LH = ERA5["slhf"].sel(valid_time="2024-04-02T12:00:00") ERA5_LSM = ERA5["lsm"].sel(valid_time="2024-04-02T12:00:00")
Copied!
old_cmap_and_norm = earthkit.plots.styles.colors.cmap_and_norm
old_cmap_and_norm = earthkit.plots.styles.colors.cmap_and_norm
Copied!
def my_cmap_and_norm(colors, levels, normalize=True, extend=None, extend_levels=True):
    return old_cmap_and_norm(colors, levels, normalize, extend, True)


earthkit.plots.styles.colors.cmap_and_norm = my_cmap_and_norm
def my_cmap_and_norm(colors, levels, normalize=True, extend=None, extend_levels=True): return old_cmap_and_norm(colors, levels, normalize, extend, True) earthkit.plots.styles.colors.cmap_and_norm = my_cmap_and_norm
Copied!
def plot_surface_latent_heat_flux(
    my_ERA5_LH: xr.Dataset,
    cr,
    chart,
    title,
    span,
    eb_abs,
    error=False,
    colors=None,
    norm=None,
    ticks=None,
    corr=None,
):
    import copy

    import dask

    if error:
        with xr.set_options(keep_attrs=True):
            da = (my_ERA5_LH - ERA5_LH).compute()
        da.attrs.update(long_name=f"Absolute error over {da.long_name.lower()}")
    else:
        with xr.set_options(keep_attrs=True):
            da = np.sign(my_ERA5_LH) * np.sqrt(np.abs(my_ERA5_LH))
        da.attrs.update(long_name=f"sqrt({da.long_name.lower()})")

    # compute the default style that earthkit.maps would apply
    source = earthkit.plots.sources.XarraySource(da)
    style = copy.deepcopy(
        earthkit.plots.styles.auto.guess_style(
            source,
            units=source.units,
        )
    )

    style._levels = earthkit.plots.styles.levels.Levels(np.linspace(-span, span, 22))
    style._legend_kwargs["ticks"] = (
        np.linspace(-span, span, 5) if ticks is None else ticks
    )
    style._colors = ("coolwarm" if error else "BrBG") if colors is None else colors

    if norm is not None:
        style._kwargs["norm"] = norm

    extend_left = np.nanmin(da) < -span
    extend_right = np.nanmax(da) > span

    extend = {
        (False, False): "neither",
        (True, False): "min",
        (False, True): "max",
        (True, True): "both",
    }[(extend_left, extend_right)]

    if error:
        style._legend_kwargs["extend"] = extend
        chart.pcolormesh(da, style=style, zorder=-12)

        with xr.set_options(keep_attrs=True):
            da_hatch = my_ERA5_LH == corr

        da_hatch = da_hatch.copy(
            data=dask.array.from_array(da_hatch.values)
            .rechunk(4)
            .map_blocks(lambda x: np.broadcast_to(np.mean(x), x.shape))
            .compute()
        )

        with plt.rc_context(
            {
                "hatch.color": (1.0, 1.0, 1.0, 1.0),
                "hatch.linewidth": 0.5,
            }
        ):
            chart.contourf(
                x=np.broadcast_to(
                    da_hatch.longitude.values.reshape(1, -1), da_hatch.shape
                ),
                y=np.broadcast_to(
                    da_hatch.latitude.values.reshape(-1, 1), da_hatch.shape
                ),
                z=da_hatch.values,
                colors=["none"],
                levels=[-0.5, 0.9, 1.5],
                hatches=["O", None],
                legend_style=None,
                zorder=-11,
            )
    else:
        chart.quickplot(da, style=style, extend=extend, zorder=-11)

    chart.ax.set_rasterization_zorder(-10)

    chart.title(title)

    t = chart.ax.text(
        0.95,
        0.9,
        rf"$\times$ {np.round(cr, 2)}"
        if error
        else humanize.naturalsize(ERA5_LH.nbytes, binary=True),
        ha="right",
        va="top",
        transform=chart.ax.transAxes,
    )
    t.set_bbox(dict(facecolor="white", alpha=0.75, edgecolor="black"))

    for m in earthkit.plots.schemas.schema.quickmap_subplot_workflow:
        if m != "title":
            getattr(chart, m)()

    for m in earthkit.plots.schemas.schema.quickmap_figure_workflow:
        getattr(chart, m)()

    cb_norm = (lambda x: x) if norm is None else norm

    counts, bins = np.histogram(
        cb_norm(da.values.flatten()), range=(cb_norm(-span), cb_norm(span)), bins=21
    )
    bins = bins if norm is None else np.linspace(0, 1, 22)
    midpoints = bins[:-1] + np.diff(bins) / 2
    cb = chart.ax.collections[0].colorbar
    cax = cb.ax.inset_axes([0.0, 1.25, 1.0, 1.0])
    cax.bar(
        midpoints,
        height=counts,
        width=(bins[-1] - bins[0]) / len(counts),
        color=cb.cmap(cb.norm(midpoints) if norm is None else midpoints),
    )
    q1, q2, q3 = da.quantile([0.25, 0.5, 0.75]).values
    cax.axvline(cb_norm(da.mean().item()), ls=":", ymin=0.1, ymax=0.9, c="w", lw=2)
    cax.axvline(cb_norm(q1), ymin=0.25, ymax=0.75, c="w", lw=2)
    cax.axvline(cb_norm(q2), ymin=0.1, ymax=0.9, c="w", lw=2)
    cax.axvline(cb_norm(q3), ymin=0.25, ymax=0.75, c="w", lw=2)
    cax.axvline(cb_norm(da.mean().item()), ymin=0.1, ymax=0.9, ls=":", c="k", lw=1)
    cax.axvline(cb_norm(q1), ymin=0.25, ymax=0.75, c="k", lw=1)
    cax.axvline(cb_norm(q2), ymin=0.1, ymax=0.9, c="k", lw=1)
    cax.axvline(cb_norm(q3), ymin=0.25, ymax=0.75, c="k", lw=1)
    cax.set_xlim(-span if norm is None else 0, span if norm is None else 1)
    cax.set_xticks([])
    cax.set_yticks([])
    cax.spines[:].set_visible(False)
def plot_surface_latent_heat_flux( my_ERA5_LH: xr.Dataset, cr, chart, title, span, eb_abs, error=False, colors=None, norm=None, ticks=None, corr=None, ): import copy import dask if error: with xr.set_options(keep_attrs=True): da = (my_ERA5_LH - ERA5_LH).compute() da.attrs.update(long_name=f"Absolute error over {da.long_name.lower()}") else: with xr.set_options(keep_attrs=True): da = np.sign(my_ERA5_LH) * np.sqrt(np.abs(my_ERA5_LH)) da.attrs.update(long_name=f"sqrt({da.long_name.lower()})") # compute the default style that earthkit.maps would apply source = earthkit.plots.sources.XarraySource(da) style = copy.deepcopy( earthkit.plots.styles.auto.guess_style( source, units=source.units, ) ) style._levels = earthkit.plots.styles.levels.Levels(np.linspace(-span, span, 22)) style._legend_kwargs["ticks"] = ( np.linspace(-span, span, 5) if ticks is None else ticks ) style._colors = ("coolwarm" if error else "BrBG") if colors is None else colors if norm is not None: style._kwargs["norm"] = norm extend_left = np.nanmin(da) < -span extend_right = np.nanmax(da) > span extend = { (False, False): "neither", (True, False): "min", (False, True): "max", (True, True): "both", }[(extend_left, extend_right)] if error: style._legend_kwargs["extend"] = extend chart.pcolormesh(da, style=style, zorder=-12) with xr.set_options(keep_attrs=True): da_hatch = my_ERA5_LH == corr da_hatch = da_hatch.copy( data=dask.array.from_array(da_hatch.values) .rechunk(4) .map_blocks(lambda x: np.broadcast_to(np.mean(x), x.shape)) .compute() ) with plt.rc_context( { "hatch.color": (1.0, 1.0, 1.0, 1.0), "hatch.linewidth": 0.5, } ): chart.contourf( x=np.broadcast_to( da_hatch.longitude.values.reshape(1, -1), da_hatch.shape ), y=np.broadcast_to( da_hatch.latitude.values.reshape(-1, 1), da_hatch.shape ), z=da_hatch.values, colors=["none"], levels=[-0.5, 0.9, 1.5], hatches=["O", None], legend_style=None, zorder=-11, ) else: chart.quickplot(da, style=style, extend=extend, zorder=-11) chart.ax.set_rasterization_zorder(-10) chart.title(title) t = chart.ax.text( 0.95, 0.9, rf"$\times$ {np.round(cr, 2)}" if error else humanize.naturalsize(ERA5_LH.nbytes, binary=True), ha="right", va="top", transform=chart.ax.transAxes, ) t.set_bbox(dict(facecolor="white", alpha=0.75, edgecolor="black")) for m in earthkit.plots.schemas.schema.quickmap_subplot_workflow: if m != "title": getattr(chart, m)() for m in earthkit.plots.schemas.schema.quickmap_figure_workflow: getattr(chart, m)() cb_norm = (lambda x: x) if norm is None else norm counts, bins = np.histogram( cb_norm(da.values.flatten()), range=(cb_norm(-span), cb_norm(span)), bins=21 ) bins = bins if norm is None else np.linspace(0, 1, 22) midpoints = bins[:-1] + np.diff(bins) / 2 cb = chart.ax.collections[0].colorbar cax = cb.ax.inset_axes([0.0, 1.25, 1.0, 1.0]) cax.bar( midpoints, height=counts, width=(bins[-1] - bins[0]) / len(counts), color=cb.cmap(cb.norm(midpoints) if norm is None else midpoints), ) q1, q2, q3 = da.quantile([0.25, 0.5, 0.75]).values cax.axvline(cb_norm(da.mean().item()), ls=":", ymin=0.1, ymax=0.9, c="w", lw=2) cax.axvline(cb_norm(q1), ymin=0.25, ymax=0.75, c="w", lw=2) cax.axvline(cb_norm(q2), ymin=0.1, ymax=0.9, c="w", lw=2) cax.axvline(cb_norm(q3), ymin=0.25, ymax=0.75, c="w", lw=2) cax.axvline(cb_norm(da.mean().item()), ymin=0.1, ymax=0.9, ls=":", c="k", lw=1) cax.axvline(cb_norm(q1), ymin=0.25, ymax=0.75, c="k", lw=1) cax.axvline(cb_norm(q2), ymin=0.1, ymax=0.9, c="k", lw=1) cax.axvline(cb_norm(q3), ymin=0.25, ymax=0.75, c="k", lw=1) cax.set_xlim(-span if norm is None else 0, span if norm is None else 1) cax.set_xticks([]) cax.set_yticks([]) cax.spines[:].set_visible(False)
Copied!
def table_surface_latent_heat_flux(
    my_ERA5_LH: xr.Dataset,
    cr,
    title,
    eb_abs,
    corr,
):
    err_inf_LH = np.amax(np.abs(my_ERA5_LH - ERA5_LH))
    err_2_LH = np.sqrt(np.mean(np.square(my_ERA5_LH - ERA5_LH)))

    err_v = np.mean(~(np.abs(my_ERA5_LH - ERA5_LH) <= eb_abs))
    err_v = (
        0
        if err_v == 0
        else np.format_float_positional(100 * err_v, precision=1, min_digits=1) + "%"
    )
    if err_v == "0.0%":
        err_v = "<0.05%"

    corr = np.mean(corr != 0)
    corr = (
        0
        if corr == 0
        else np.format_float_positional(100 * corr, precision=1, min_digits=1) + "%"
    )
    if corr == "0.0%":
        corr = "<0.05%"

    return pd.DataFrame(
        {
            "Safeguarded(0, ...)": [title],
            r"$L_{\infty}$": [
                f"{err_inf_LH:.05}",
            ],
            r"$L_{2}$": [
                f"{err_2_LH:.05}",
            ],
            "V": [err_v],
            "C": [corr],
            "CR": [
                rf"$\times$ {np.round(cr, 2)}",
            ],
        }
    )
def table_surface_latent_heat_flux( my_ERA5_LH: xr.Dataset, cr, title, eb_abs, corr, ): err_inf_LH = np.amax(np.abs(my_ERA5_LH - ERA5_LH)) err_2_LH = np.sqrt(np.mean(np.square(my_ERA5_LH - ERA5_LH))) err_v = np.mean(~(np.abs(my_ERA5_LH - ERA5_LH) <= eb_abs)) err_v = ( 0 if err_v == 0 else np.format_float_positional(100 * err_v, precision=1, min_digits=1) + "%" ) if err_v == "0.0%": err_v = "<0.05%" corr = np.mean(corr != 0) corr = ( 0 if corr == 0 else np.format_float_positional(100 * corr, precision=1, min_digits=1) + "%" ) if corr == "0.0%": corr = "<0.05%" return pd.DataFrame( { "Safeguarded(0, ...)": [title], r"$L_{\infty}$": [ f"{err_inf_LH:.05}", ], r"$L_{2}$": [ f"{err_2_LH:.05}", ], "V": [err_v], "C": [corr], "CR": [ rf"$\times$ {np.round(cr, 2)}", ], } )
Copied!
x = np.linspace(0.0, 1.0, 21)
colors_high = plt.get_cmap("PiYG_r")(x)
colors_low = plt.get_cmap("coolwarm")(x * 3 - 1)
colors_roi = np.where(
    (np.abs(x - 0.5) < (1 / 6)).reshape(-1, 1), colors_low, colors_high
)
x = np.linspace(0.0, 1.0, 21) colors_high = plt.get_cmap("PiYG_r")(x) colors_low = plt.get_cmap("coolwarm")(x * 3 - 1) colors_roi = np.where( (np.abs(x - 0.5) < (1 / 6)).reshape(-1, 1), colors_low, colors_high )
Copied!
def roi_norm(x: np.array) -> np.array:
    a = np.where(
        x < -1e2,
        ((x + 1e4) / (1e4 - 1e2)) * (2 / 6),
        np.where(
            x <= 1e2,
            0.5 + ((x) / (1e2)) * (1 / 6),
            4 / 6 + ((x - 1e2) / (1e4 - 1e2)) * (2 / 6),
        ),
    )
    return ma.array(a, mask=x.mask) if isinstance(x, ma.MaskedArray) else a


def roi_norm_inverse(x: np.array) -> np.array:
    a = np.where(
        x < (2 / 6),
        ((x - (2 / 6)) / (2 / 6)) * (1e4 - 1e2) - 1e2,
        np.where(
            x < 4 / 6,
            ((x - (3 / 6)) / (1 / 6)) * 1e2,
            1e2 + ((x - (4 / 6)) / (2 / 6)) * (1e4 - 1e2),
        ),
    )
    return ma.array(a, mask=x.mask) if isinstance(x, ma.MaskedArray) else a
def roi_norm(x: np.array) -> np.array: a = np.where( x < -1e2, ((x + 1e4) / (1e4 - 1e2)) * (2 / 6), np.where( x <= 1e2, 0.5 + ((x) / (1e2)) * (1 / 6), 4 / 6 + ((x - 1e2) / (1e4 - 1e2)) * (2 / 6), ), ) return ma.array(a, mask=x.mask) if isinstance(x, ma.MaskedArray) else a def roi_norm_inverse(x: np.array) -> np.array: a = np.where( x < (2 / 6), ((x - (2 / 6)) / (2 / 6)) * (1e4 - 1e2) - 1e2, np.where( x < 4 / 6, ((x - (3 / 6)) / (1 / 6)) * 1e2, 1e2 + ((x - (4 / 6)) / (2 / 6)) * (1e4 - 1e2), ), ) return ma.array(a, mask=x.mask) if isinstance(x, ma.MaskedArray) else a

Compressing slhf with safeguards¶

We configure the safeguards with absolute error bounds of $10^{2}$ and $10^{4}$ $\text{J}/\text{m}^{2}$, which roughly match the orders of magnitude of surface latent heat flux above land and above the sea, respectively. Finally, we also configure safeguards with a regions of interest selector to apply these two error bounds based on the land-sea mask (where we define "sea" as $< 90\%$ land).

Copied!
from numcodecs_safeguards.lossless import _default_lossless_for_corrections

from compression_safeguards import Safeguards
from compression_safeguards.utils.bindings import Bindings
from numcodecs_safeguards.lossless import _default_lossless_for_corrections from compression_safeguards import Safeguards from compression_safeguards.utils.bindings import Bindings
Copied!
approximation = np.zeros_like(ERA5_LH.values)
approximation = np.zeros_like(ERA5_LH.values)
Copied!
lossless = _default_lossless_for_corrections()
lossless = _default_lossless_for_corrections()
Copied!
eb_low = dict(kind="eb", type="abs", eb=1e2)
eb_high = dict(kind="eb", type="abs", eb=1e4)
eb_low = dict(kind="eb", type="abs", eb=1e2) eb_high = dict(kind="eb", type="abs", eb=1e4)
Copied!
sg_low = Safeguards(safeguards=[eb_low])

correction_sg_low = sg_low.compute_correction(ERA5_LH.values, approximation)
encoded_sg_low = np.asarray(lossless.encode(correction_sg_low))

ERA5_LH_sg_low = ERA5_LH.copy(
    data=sg_low.apply_correction(approximation, correction_sg_low)
)
sg_low = Safeguards(safeguards=[eb_low]) correction_sg_low = sg_low.compute_correction(ERA5_LH.values, approximation) encoded_sg_low = np.asarray(lossless.encode(correction_sg_low)) ERA5_LH_sg_low = ERA5_LH.copy( data=sg_low.apply_correction(approximation, correction_sg_low) )
Copied!
sg_high = Safeguards(safeguards=[eb_high])

correction_sg_high = sg_high.compute_correction(ERA5_LH.values, approximation)
encoded_sg_high = np.asarray(lossless.encode(correction_sg_high))

ERA5_LH_sg_high = ERA5_LH.copy(
    data=sg_high.apply_correction(approximation, correction_sg_high)
)
sg_high = Safeguards(safeguards=[eb_high]) correction_sg_high = sg_high.compute_correction(ERA5_LH.values, approximation) encoded_sg_high = np.asarray(lossless.encode(correction_sg_high)) ERA5_LH_sg_high = ERA5_LH.copy( data=sg_high.apply_correction(approximation, correction_sg_high) )
Copied!
sg_roi = Safeguards(
    safeguards=[
        dict(
            kind="select",
            selector="lsm",
            safeguards=[
                # 0: absolute error for <90% land (sea)
                eb_high,
                # 1: absolute error for >=90% land (land)
                eb_low,
            ],
        ),
    ]
)

correction_sg_roi = sg_roi.compute_correction(
    ERA5_LH.values,
    approximation,
    late_bound=Bindings(
        # lsm: 1 for >=90% land, 0 otherwise
        lsm=(ERA5_LSM >= 0.9).values,
    ),
)
encoded_sg_roi = np.asarray(lossless.encode(correction_sg_roi))

ERA5_LH_sg_roi = ERA5_LH.copy(
    data=sg_roi.apply_correction(approximation, correction_sg_roi)
)
sg_roi = Safeguards( safeguards=[ dict( kind="select", selector="lsm", safeguards=[ # 0: absolute error for <90% land (sea) eb_high, # 1: absolute error for >=90% land (land) eb_low, ], ), ] ) correction_sg_roi = sg_roi.compute_correction( ERA5_LH.values, approximation, late_bound=Bindings( # lsm: 1 for >=90% land, 0 otherwise lsm=(ERA5_LSM >= 0.9).values, ), ) encoded_sg_roi = np.asarray(lossless.encode(correction_sg_roi)) ERA5_LH_sg_roi = ERA5_LH.copy( data=sg_roi.apply_correction(approximation, correction_sg_roi) )

Visual comparison of the error distributions for the surface latent heat flux¶

Copied!
fig = earthkit.plots.Figure(rows=2, columns=2, size=(10, 7.5))

plot_surface_latent_heat_flux(
    ERA5_LH,
    1.0,
    fig.add_map(0, 0),
    "Original",
    span=1.5e3,
    eb_abs=eb_high["eb"],
)
plot_surface_latent_heat_flux(
    ERA5_LH_sg_roi,
    ERA5_LH.nbytes / encoded_sg_roi.nbytes,
    fig.add_map(0, 1),
    r"Safeguarded(0, $\epsilon_{{abs}}=\text{{lsm}}(10^{{2}}, 10^{{4}})$)",
    span=eb_high["eb"],
    eb_abs=np.where(ERA5_LSM.values >= 0.9, eb_low["eb"], eb_high["eb"]),
    error=True,
    colors=colors_roi,
    ticks=[-1e4, -5e3, -1e2, 0, 1e2, 5e3, 1e4],
    norm=mcolors.FuncNorm((roi_norm, roi_norm_inverse), vmin=-1e4, vmax=1e4),
    corr=correction_sg_roi,
)

plot_surface_latent_heat_flux(
    ERA5_LH_sg_high,
    ERA5_LH.nbytes / encoded_sg_high.nbytes,
    fig.add_map(1, 0),
    r"Safeguarded(0, $\epsilon_{{abs}}=10^{{4}}$)",
    span=eb_high["eb"],
    eb_abs=eb_high["eb"],
    error=True,
    colors="PiYG_r",
    corr=correction_sg_high,
)
plot_surface_latent_heat_flux(
    ERA5_LH_sg_low,
    ERA5_LH.nbytes / encoded_sg_low.nbytes,
    fig.add_map(1, 1),
    r"Safeguarded(0, $\epsilon_{{abs}}=10^{{2}}$)",
    span=eb_low["eb"],
    eb_abs=eb_low["eb"],
    error=True,
    colors="coolwarm",
    corr=correction_sg_low,
)

fig.save(Path("plots") / "latent-heat-flux.pdf")
fig = earthkit.plots.Figure(rows=2, columns=2, size=(10, 7.5)) plot_surface_latent_heat_flux( ERA5_LH, 1.0, fig.add_map(0, 0), "Original", span=1.5e3, eb_abs=eb_high["eb"], ) plot_surface_latent_heat_flux( ERA5_LH_sg_roi, ERA5_LH.nbytes / encoded_sg_roi.nbytes, fig.add_map(0, 1), r"Safeguarded(0, $\epsilon_{{abs}}=\text{{lsm}}(10^{{2}}, 10^{{4}})$)", span=eb_high["eb"], eb_abs=np.where(ERA5_LSM.values >= 0.9, eb_low["eb"], eb_high["eb"]), error=True, colors=colors_roi, ticks=[-1e4, -5e3, -1e2, 0, 1e2, 5e3, 1e4], norm=mcolors.FuncNorm((roi_norm, roi_norm_inverse), vmin=-1e4, vmax=1e4), corr=correction_sg_roi, ) plot_surface_latent_heat_flux( ERA5_LH_sg_high, ERA5_LH.nbytes / encoded_sg_high.nbytes, fig.add_map(1, 0), r"Safeguarded(0, $\epsilon_{{abs}}=10^{{4}}$)", span=eb_high["eb"], eb_abs=eb_high["eb"], error=True, colors="PiYG_r", corr=correction_sg_high, ) plot_surface_latent_heat_flux( ERA5_LH_sg_low, ERA5_LH.nbytes / encoded_sg_low.nbytes, fig.add_map(1, 1), r"Safeguarded(0, $\epsilon_{{abs}}=10^{{2}}$)", span=eb_low["eb"], eb_abs=eb_low["eb"], error=True, colors="coolwarm", corr=correction_sg_low, ) fig.save(Path("plots") / "latent-heat-flux.pdf")
No description has been provided for this image
Copied!
lh_table = pd.concat(
    [
        table_surface_latent_heat_flux(
            ERA5_LH_sg_low,
            ERA5_LH.nbytes / encoded_sg_low.nbytes,
            r"$\epsilon_{{abs}}=10^{{2}}$",
            eb_abs=eb_low["eb"],
            corr=correction_sg_low,
        ),
        table_surface_latent_heat_flux(
            ERA5_LH_sg_roi,
            ERA5_LH.nbytes / encoded_sg_roi.nbytes,
            r"$\epsilon_{abs}=\text{lsm}(10^{2}, 10^{4})$",
            eb_abs=np.where(ERA5_LSM.values >= 0.9, eb_low["eb"], eb_high["eb"]),
            corr=correction_sg_roi,
        ),
        table_surface_latent_heat_flux(
            ERA5_LH_sg_high,
            ERA5_LH.nbytes / encoded_sg_high.nbytes,
            r"$\epsilon_{{abs}}=10^{{4}}$",
            eb_abs=eb_high["eb"],
            corr=correction_sg_high,
        ),
    ]
).set_index(["Safeguarded(0, ...)"])

Path("tables").joinpath("latent-heat-flux.tex").write_text(
    lh_table.to_latex(escape=False)
    .replace("%", r"\%")
    .replace("\\cline{1-7}\n\\bottomrule", "\\bottomrule")
)

lh_table
lh_table = pd.concat( [ table_surface_latent_heat_flux( ERA5_LH_sg_low, ERA5_LH.nbytes / encoded_sg_low.nbytes, r"$\epsilon_{{abs}}=10^{{2}}$", eb_abs=eb_low["eb"], corr=correction_sg_low, ), table_surface_latent_heat_flux( ERA5_LH_sg_roi, ERA5_LH.nbytes / encoded_sg_roi.nbytes, r"$\epsilon_{abs}=\text{lsm}(10^{2}, 10^{4})$", eb_abs=np.where(ERA5_LSM.values >= 0.9, eb_low["eb"], eb_high["eb"]), corr=correction_sg_roi, ), table_surface_latent_heat_flux( ERA5_LH_sg_high, ERA5_LH.nbytes / encoded_sg_high.nbytes, r"$\epsilon_{{abs}}=10^{{4}}$", eb_abs=eb_high["eb"], corr=correction_sg_high, ), ] ).set_index(["Safeguarded(0, ...)"]) Path("tables").joinpath("latent-heat-flux.tex").write_text( lh_table.to_latex(escape=False) .replace("%", r"\%") .replace("\\cline{1-7}\n\\bottomrule", "\\bottomrule") ) lh_table
$L_{\infty}$ $L_{2}$ V C CR
Safeguarded(0, ...)
$\epsilon_{{abs}}=10^{{2}}$ 85.0 46.824 0 99.5% $\times$ 3.28
$\epsilon_{abs}=\text{lsm}(10^{2}, 10^{4})$ 9995.0 4195.1 0 90.3% $\times$ 6.57
$\epsilon_{{abs}}=10^{{4}}$ 9995.0 4953.2 0 75.4% $\times$ 12.24
Copied!

Next

Built with ProperDocs using a theme provided by Read the Docs.