Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: UndefinedUnitError: 'Celsius' is not defined in the unit registry #158

Closed
pagecp opened this issue Apr 26, 2022 · 5 comments
Closed

Comments

@pagecp
Copy link
Collaborator

pagecp commented Apr 26, 2022

  • icclim version: 5.1.1-dev0
  • Python version: 3.9.7

Description

Using E-OBS to calculate SU, I have this error:
UndefinedUnitError: 'Celsius' is not defined in the unit registry

Minimal reproducible example

summer_days = icclim.index(in_files=listOfFileNames, indice_name="SU", slice_mode='JJA')

Output received

2022-04-26 12:01:00,690 --- icclim 5.1.1-dev
2022-04-26 12:01:00,691 --- BEGIN EXECUTION
2022-04-26 12:01:00,691 Processing: 0%
2022-04-26 12:01:00,692 DEPRECATION_WARNING: `indice_name` is deprecated. Use `index_name` instead.
2022-04-26 12:01:00,766 Calculating climate index: SU
/data/home/globc/page/data/envs/icclimv5/lib/python3.9/site-packages/xclim/core/cfchecks.py:39: UserWarning: Variable has a non-conforming cell_methods: Got `time: mean`, which do not include the expected `time: maximum`
  _check_cell_methods(
---------------------------------------------------------------------------
UndefinedUnitError                        Traceback (most recent call last)
File ~/data/envs/icclimv5/lib/python3.9/site-packages/xclim/core/units.py:161, in units2pint(value)
    160 try:  # Pint compatible
--> 161     return units.parse_units(unit)
    162 except (
    163     pint.UndefinedUnitError,
    164     pint.DimensionalityError,
    165     AttributeError,
    166     TypeError,
    167 ):  # Convert from CF-units to pint-compatible

File ~/data/envs/icclimv5/lib/python3.9/site-packages/pint/registry.py:1194, in BaseRegistry.parse_units(self, input_string, as_delta, case_sensitive)
   1193     input_string = p(input_string)
-> 1194 units = self._parse_units(input_string, as_delta, case_sensitive)
   1195 return self.Unit(units)

File ~/data/envs/icclimv5/lib/python3.9/site-packages/pint/registry.py:1431, in NonMultiplicativeRegistry._parse_units(self, input_string, as_delta, case_sensitive)
   1429     as_delta = self.default_as_delta
-> 1431 return super()._parse_units(input_string, as_delta, case_sensitive)

File ~/data/envs/icclimv5/lib/python3.9/site-packages/pint/registry.py:1227, in BaseRegistry._parse_units(self, input_string, as_delta, case_sensitive)
   1226 for name in units:
-> 1227     cname = self.get_name(name, case_sensitive=case_sensitive)
   1228     value = units[name]

File ~/data/envs/icclimv5/lib/python3.9/site-packages/pint/registry.py:714, in BaseRegistry.get_name(self, name_or_alias, case_sensitive)
    713 if not candidates:
--> 714     raise UndefinedUnitError(name_or_alias)
    715 elif len(candidates) == 1:

UndefinedUnitError: 'Celsius' is not defined in the unit registry

During handling of the above exception, another exception occurred:

UndefinedUnitError                        Traceback (most recent call last)
Input In [16], in <cell line: 5>()
      3 summer_days = xr.Dataset
      4 #su = icclim.su(listOfFileNames, slice_mode='JJA')
----> 5 summer_days = icclim.index(in_files=listOfFileNames, indice_name="SU", slice_mode='JJA')

File ~/data/envs/icclimv5/lib/python3.9/site-packages/icclim/main.py:262, in index(in_files, index_name, var_name, slice_mode, time_range, out_file, threshold, callback, callback_percentage_start_value, callback_percentage_total, base_period_time_range, window_width, only_leap_years, ignore_Feb29th, interpolation, out_unit, netcdf_version, user_index, save_percentile, logs_verbosity, indice_name, user_indice, transfer_limit_Mbytes)
    260     result_ds = _compute_user_index_dataset(config=config, user_index=user_index)
    261 else:
--> 262     result_ds = _compute_ecad_index_dataset(
    263         config=config,
    264         index=index,
    265         threshold=threshold,
    266         current_history=ds.attrs.get("history", None),
    267     )
    268 if reset_coords_dict:
    269     result_ds = result_ds.rename(reset_coords_dict)

File ~/data/envs/icclimv5/lib/python3.9/site-packages/icclim/main.py:339, in _compute_ecad_index_dataset(config, index, threshold, current_history)
    337 else:
    338     config.threshold = threshold
--> 339     result_ds = _compute_ecad_index(index, config, current_history)
    340 return result_ds

File ~/data/envs/icclimv5/lib/python3.9/site-packages/icclim/main.py:391, in _compute_ecad_index(index, config, former_history)
    389 logging.info(f"Calculating climate index: {index.short_name}")
    390 result_ds = Dataset()
--> 391 res = index.compute(config)
    392 if isinstance(res, tuple):
    393     da, per = res

File ~/data/envs/icclimv5/lib/python3.9/site-packages/icclim/models/ecad_indices.py:140, in EcadIndex.<lambda>(c)
    130 VDTR = (
    131     "vDTR",
    132     lambda c: vdtr(c),
    133     IndexGroup.TEMPERATURE,
    134     [TAS_MAX, TAS_MIN],
    135 )
    137 # Heat
    138 SU = (
    139     "SU",
--> 140     lambda c: su(c),
    141     IndexGroup.HEAT,
    142     [TAS_MAX],
    143     [MODIFIABLE_THRESHOLD],
    144 )
    145 TR = (
    146     "TR",
    147     lambda c: tr(c),
   (...)
    150     [MODIFIABLE_THRESHOLD],
    151 )
    152 WSDI = (
    153     "WSDI",
    154     lambda c: wsdi(c),
   (...)
    157     [QUANTILE_BASED, MODIFIABLE_QUANTILE_WINDOW, MODIFIABLE_THRESHOLD],
    158 )

File ~/data/envs/icclimv5/lib/python3.9/site-packages/icclim/ecad_functions.py:145, in su(config)
    139     result = atmos.maximum_consecutive_dry_days(
    140         config.pr.study_da, thresh="1.0 mm/day", freq=config.freq.panda_freq
    141     )
    142     return result
--> 145 def su(config: IndexConfig) -> DataArray:
    146     return _compute_threshold_index(
    147         da=config.tasmax.study_da,
    148         threshold=25.0 if config.threshold is None else config.threshold,
    149         freq=config.freq,
    150         xclim_index_fun=atmos.tx_days_above,
    151     )
    154 def tr(config: IndexConfig) -> DataArray:

File ~/data/envs/icclimv5/lib/python3.9/site-packages/icclim/ecad_functions.py:878, in _compute_threshold_index(da, threshold, freq, xclim_index_fun)
    871     result = xclim_index_fun(da, thresh=f"{threshold} °C", freq=freq.panda_freq)
    872     return result
    875 def _compute_spell_duration(
    876     cf_var: CfVariable,
    877     freq: str,
--> 878     per_window: int,
    879     per_thresh: float,
    880     per_interpolation: QuantileInterpolation,
    881     min_spell_duration: int,
    882     save_percentile: bool,
    883     callback: Callable,
    884     xclim_index_fun: Callable,
    885 ) -> Tuple[DataArray, Optional[DataArray]]:
    886     per = _compute_percentile_doy(
    887         cf_var.reference_da,
    888         per_thresh,
   (...)
    891         callback,
    892     )
    893     run_bootstrap = _can_run_bootstrap(cf_var)

File ~/data/envs/icclimv5/lib/python3.9/site-packages/xclim/core/indicator.py:790, in Indicator.__call__(self, *args, **kwds)
    788     elif nm not in compute_das and nm in params:
    789         kwargs[nm] = params[nm]
--> 790 outs = self.compute(**compute_das, **kwargs, **var_kwargs)
    792 if isinstance(outs, DataArray):
    793     outs = [outs]

File <boltons.funcutils.FunctionBuilder-36>:2, in tx_days_above(tasmax, thresh, freq)

File ~/data/envs/icclimv5/lib/python3.9/site-packages/xclim/core/units.py:688, in declare_units.<locals>.dec.<locals>.wrapper(*args, **kwargs)
    686 bound_args = sig.bind(*args, **kwargs)
    687 for name, val in bound_args.arguments.items():
--> 688     check_units(val, bound_units.arguments.get(name, None))
    690 out = func(*args, **kwargs)
    692 # Perform very basic sanity check on the output.
    693 # Indice are responsible for unit management.
    694 # If this fails, it's a developer's error.

File <boltons.funcutils.FunctionBuilder-1>:2, in check_units(val, dim)

File ~/data/envs/icclimv5/lib/python3.9/site-packages/xclim/core/options.py:116, in datacheck.<locals>.run_check(*args, **kwargs)
    114 @wraps(func)
    115 def run_check(*args, **kwargs):
--> 116     return _run_check(func, DATA_VALIDATION, *args, **kwargs)

File ~/data/envs/icclimv5/lib/python3.9/site-packages/xclim/core/options.py:106, in _run_check(func, option, *args, **kwargs)
    104 """Run function and customize exception handling based on option."""
    105 try:
--> 106     func(*args, **kwargs)
    107 except ValidationError as err:
    108     raise_warn_or_log(err, OPTIONS[option], stacklevel=4)

File ~/data/envs/icclimv5/lib/python3.9/site-packages/xclim/core/units.py:634, in check_units(val, dim)
    632     val_units = str2pint(val)
    633 else:  # a DataArray
--> 634     val_units = units2pint(val)
    635 val_dim = val_units.dimensionality
    637 if val_dim == expected:

File ~/data/envs/icclimv5/lib/python3.9/site-packages/xclim/core/units.py:168, in units2pint(value)
    161     return units.parse_units(unit)
    162 except (
    163     pint.UndefinedUnitError,
    164     pint.DimensionalityError,
    165     AttributeError,
    166     TypeError,
    167 ):  # Convert from CF-units to pint-compatible
--> 168     return units.parse_units(_transform(unit))

File ~/data/envs/icclimv5/lib/python3.9/site-packages/pint/registry.py:1194, in BaseRegistry.parse_units(self, input_string, as_delta, case_sensitive)
   1192 for p in self.preprocessors:
   1193     input_string = p(input_string)
-> 1194 units = self._parse_units(input_string, as_delta, case_sensitive)
   1195 return self.Unit(units)

File ~/data/envs/icclimv5/lib/python3.9/site-packages/pint/registry.py:1431, in NonMultiplicativeRegistry._parse_units(self, input_string, as_delta, case_sensitive)
   1428 if as_delta is None:
   1429     as_delta = self.default_as_delta
-> 1431 return super()._parse_units(input_string, as_delta, case_sensitive)

File ~/data/envs/icclimv5/lib/python3.9/site-packages/pint/registry.py:1227, in BaseRegistry._parse_units(self, input_string, as_delta, case_sensitive)
   1225 many = len(units) > 1
   1226 for name in units:
-> 1227     cname = self.get_name(name, case_sensitive=case_sensitive)
   1228     value = units[name]
   1229     if not cname:

File ~/data/envs/icclimv5/lib/python3.9/site-packages/pint/registry.py:714, in BaseRegistry.get_name(self, name_or_alias, case_sensitive)
    712 candidates = self.parse_unit_name(name_or_alias, case_sensitive)
    713 if not candidates:
--> 714     raise UndefinedUnitError(name_or_alias)
    715 elif len(candidates) == 1:
    716     prefix, unit_name, _ = candidates[0]

UndefinedUnitError: 'Celsius' is not defined in the unit registry
@pagecp pagecp changed the title UndefinedUnitError: 'Celsius' is not defined in the unit registry BUG: UndefinedUnitError: 'Celsius' is not defined in the unit registry Apr 26, 2022
@bzah
Copy link
Member

bzah commented Apr 26, 2022

MetPy seemed to had a similar issue and raised it to Pint (the library handling unit): hgrecco/pint#1081

It seems "Celsius" should be written in lowercase "celsius" according to standards.
However, xclim already offer some additional aliases such as "C" or "deg_C". We could add either on icclim on xclim some additional aliases.

There is also some effort to backport to the cf_xarray units handling but I'm not sure how is it going: xarray-contrib/cf-xarray#284
We could try to contribute there as well.

As a workaround you could rewrite the "units" in your dataset:

ds = xarray.open_mfdataset(listOfFileNames) # you might need to glob.glob it before, I don't remember
ds.attrs["units"] = "celsius"
ds.to_netcdf("obs_fixed.nc")

@bzah
Copy link
Member

bzah commented Apr 26, 2022

I think you can also dynamically update the registry with:

from xclim.core.units import units
units.define("@alias degC = C = deg_C = Celsius")

# ...

icclim.indice(blablabla)
``

@pagecp
Copy link
Collaborator Author

pagecp commented Apr 26, 2022

Just quick note, is it spelled Celsius (not Celcius) :)

@bzah
Copy link
Member

bzah commented Apr 26, 2022

oopsi, fixing it.

@bzah
Copy link
Member

bzah commented May 3, 2022

I'm closing this as 2 PR are in progress in xclim and cf-xarray:

@bzah bzah closed this as completed May 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants