diff --git a/src/climatebenchpress/compressor/compressors/__init__.py b/src/climatebenchpress/compressor/compressors/__init__.py index 1b87b38..4c58e9f 100644 --- a/src/climatebenchpress/compressor/compressors/__init__.py +++ b/src/climatebenchpress/compressor/compressors/__init__.py @@ -2,6 +2,7 @@ "BitRound", "BitRoundPco", "Ebcc", + "EbccAbsOnly", "Jpeg2000", "SafeguardedBitRoundPco", "SafeguardedSperr", @@ -13,6 +14,7 @@ "StochRound", "StochRoundPco", "Sz3", + "Sz3AbsOnly", "Tthresh", "Zfp", "ZfpRound", @@ -21,7 +23,7 @@ from . import abc as abc from .bitround import BitRound from .bitround_pco import BitRoundPco -from .ebcc import Ebcc +from .ebcc import Ebcc, EbccAbsOnly from .jpeg2000 import Jpeg2000 from .safeguarded import ( SafeguardedBitRoundPco, @@ -34,7 +36,7 @@ from .sperr import Sperr from .stochround import StochRound from .stochround_pco import StochRoundPco -from .sz3 import Sz3 +from .sz3 import Sz3, Sz3AbsOnly from .tthresh import Tthresh from .zfp import Zfp from .zfp_round import ZfpRound diff --git a/src/climatebenchpress/compressor/compressors/ebcc.py b/src/climatebenchpress/compressor/compressors/ebcc.py index 9c77e5c..72f5e2c 100644 --- a/src/climatebenchpress/compressor/compressors/ebcc.py +++ b/src/climatebenchpress/compressor/compressors/ebcc.py @@ -1,7 +1,8 @@ -__all__ = ["Ebcc"] +__all__ = ["Ebcc", "EbccAbsOnly"] import numcodecs.astype import numcodecs_wasm_ebcc +import numpy as np from numcodecs_combinators.stack import CodecStack from .abc import Compressor @@ -50,3 +51,37 @@ def rel_bound_codec(error_bound, dtype=None, **kwargs): chunk_shape="auto", ), ) + + +class EbccAbsOnly(Compressor): + """EBCC compressor but instead of using the internal EBCC relative error bound, + it converts the relative error bound to an absolute error bound..""" + + name = "ebcc-abs" + description = "EBCC-Abs" + + @staticmethod + def abs_bound_codec(error_bound, dtype=None, **kwargs): + assert dtype is not None, "dtype must be provided" + + # Ensure that the error bound is representable as a float32. + # For numbers smaller than the smallest positive normal float32, + # we use the smallest subnormal float32. + error_bound = max( + np.float32(error_bound), np.finfo(np.float32).smallest_subnormal + ) + + return CodecStack( + # EBCC only supports float32 data + numcodecs.astype.AsType( + encode_dtype="float32", + decode_dtype=dtype.name, + ), + numcodecs_wasm_ebcc.Ebcc( + # reasonable default recommended by Langwen Huang + base_cr=100, + residual="absolute", + error=float(error_bound), + chunk_shape="auto", + ), + ) diff --git a/src/climatebenchpress/compressor/compressors/sz3.py b/src/climatebenchpress/compressor/compressors/sz3.py index 6d00300..4dcb9f2 100644 --- a/src/climatebenchpress/compressor/compressors/sz3.py +++ b/src/climatebenchpress/compressor/compressors/sz3.py @@ -1,4 +1,4 @@ -__all__ = ["Sz3"] +__all__ = ["Sz3", "Sz3AbsOnly"] import numcodecs_wasm_sz3 @@ -22,3 +22,15 @@ def rel_bound_codec(error_bound, **kwargs): # based on the range of the input data: # https://github.com/szcompressor/SZ3/blob/e8a6b1569067abdd6b7d4276e91eced115be4f14/include/SZ3/utils/Statistic.hpp#L36 return numcodecs_wasm_sz3.Sz3(eb_mode="rel", eb_rel=error_bound) + + +class Sz3AbsOnly(Compressor): + """SZ3 compressor but instead of using the internal SZ3 relative error bound, + it converts the relative error bound to an absolute error bound.""" + + name = "sz3-abs" + description = "SZ3-Abs" + + @staticmethod + def abs_bound_codec(error_bound, **kwargs): + return numcodecs_wasm_sz3.Sz3(eb_mode="abs", eb_abs=error_bound)