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]: Replace static references to "time" for the time dimension name with self._dim in TemporalAccessor #311

Closed
tomvothecoder opened this issue Aug 11, 2022 · 3 comments · Fixed by #312
Assignees
Labels
type: bug Inconsistencies or issues which will cause an issue or problem for users or implementors.

Comments

@tomvothecoder
Copy link
Collaborator

tomvothecoder commented Aug 11, 2022

What happened?

The TemporalAccessor class has static references to "time" as the name of the time dimension. However, datasets can use different names for a time dimension, such as "time_centered", "T", "time_counter",

Original source:

In looking at Test 3, I see another issue here. The time axis needed for temporal operations is not actually assigned as a dimension. I thought this could be fixed by dropping time_counter and just using time_centered: this would get rid of the multiple time axes which is the issue for Test 1 and 2 and would also be the appropriate coordinate for temporal operations:

import numpy as np, xarray as xr, xcdat as xc
TS_File ='https://thredds-su.ipsl.fr/thredds/dodsC/ipsl_thredds/omamce/TestCases/XCDAT/thetao_CF_1.nc'
ds = xr.open_dataset(TS_File, decode_times=False)
ds['time_counter'] = ds['time_centered']
ds = ds.drop_vars('time_centered')
ds = ds.rename({'time_counter': 'time_centered'})
ds.to_netcdf('test.nc', 'w', 'NETCDF3_CLASSIC')

ds = xc.open_dataset('test.nc')
ds.temporal.group_average('thetao', freq='year', weighted=True)

But this yields a different error:

AttributeError: 'DataArray' object has no attribute 'time'

@tomvothecoder - do you think this is a separate issue in the temporal averaging logic?

Originally posted by @pochedls in #285 (comment)

What did you expect to happen?

Temporal averaging should work regardless of the name of the time dimension.

Minimal Complete Verifiable Example

import numpy as np, xarray as xr, xcdat as xc
TS_File ='https://thredds-su.ipsl.fr/thredds/dodsC/ipsl_thredds/omamce/TestCases/XCDAT/thetao_CF_1.nc'
ds = xr.open_dataset(TS_File, decode_times=False)
ds['time_counter'] = ds['time_centered']
ds = ds.drop_vars('time_centered')
ds = ds.rename({'time_counter': 'time_centered'})
ds.to_netcdf('test.nc', 'w', 'NETCDF3_CLASSIC')

ds = xc.open_dataset('test.nc')
ds.temporal.group_average('thetao', freq='year', weighted=True)

Relevant log output

No response

Anything else we need to know?

This is a simple fix that involves updating "time" references to self._dim.

self._dim = get_axis_coord(self._dataset, "T").name

Environment

xcdat = v0.3.0

@tomvothecoder tomvothecoder added the type: bug Inconsistencies or issues which will cause an issue or problem for users or implementors. label Aug 11, 2022
@tomvothecoder tomvothecoder changed the title [Bug]: Update static references to "time" dimension using self._dim in TemporalAccessor [Bug]: Replace static references to "time" for the time dimension name with self._dim in TemporalAccessor Aug 11, 2022
@tomvothecoder tomvothecoder self-assigned this Aug 11, 2022
@jypeter
Copy link

jypeter commented Aug 12, 2022

@tomvothecoder are you detailing somewhere (maybe some place easy to find in the documentation) what will be recognized as a time axis (you mention above such as "time_centered", "T", "time_counter")?

I'm assuming you don't make a difference between upper and lowercase, but maybe you can have a look at what cdms2 considered a time axis. There is an isTime() Axis method on page 48 of cdms5.pdf (one of the documents I provided in #170) that says (with some copy/paste errors in the original doc):

isTime()
Returns true iff the axis is a time axis. ax is a longitude axis if:
• ax.axis==’T’, or
• ax.id[0:4]==’time’, or
• ax.id is in cdms2.axis.time_aliases

Note that page 47 also describes isLevel(), isLatitude() and isLongitude() that may be worth looking at, because some problems with time could be encountered with the other dimensions

As a last resort, the units attribute of an axis could also be used to determine the axis type. And if an axis matches isCircular() (page 49), it has a good chance to be a longitude axis

@jypeter
Copy link

jypeter commented Aug 12, 2022

Note: see example usage of isTime() in #310 (comment)

@tomvothecoder
Copy link
Collaborator Author

@tomvothecoder are you detailing somewhere (maybe some place easy to find in the documentation) what will be recognized as a time axis (you mention above such as "time_centered", "T", "time_counter")?

We mention how we map to coordinates in the Planned Features section, under "Things we are striving for".

  • Robust handling of coordinates and its associated bounds

    • Coordinate variables are retrieved with cf_xarray using either the "axis", "standard_name", or dimension name attribute
    • Bounds are retrieved with cf_xarray using the "bounds" attr
    • Ability to operate on both longitudinal axis orientations, [0, 360) and [-180, 180)

The related function is called .get_axis_coord() that is used internally and also available as a public function.
Here is the function (with the docstring) and the related API documentation.

  • API Source Code:

    xcdat/xcdat/axis.py

    Lines 28 to 87 in 4c51879

    def get_axis_coord(
    obj: Union[xr.Dataset, xr.DataArray], axis: CFAxisName
    ) -> xr.DataArray:
    """Gets the coordinate variable for an axis.
    This function uses ``cf_xarray`` to try to find the matching coordinate
    variable by checking the following attributes in order:
    - ``"axis"``
    - ``"standard_name"``
    - Dimension name
    - Must follow the valid short-hand convention
    - For example, ``"lat"`` for latitude and ``"lon"`` for longitude
    Parameters
    ----------
    obj : Union[xr.Dataset, xr.DataArray]
    The Dataset or DataArray object.
    axis : CFAxisName
    The CF-compliant axis name ("X", "Y", "T", "Z").
    Returns
    -------
    xr.DataArray
    The coordinate variable.
    Raises
    ------
    KeyError
    If the coordinate variable was not found.
    Notes
    -----
    Refer to [1]_ for a list of CF-compliant ``"axis"`` and ``"standard_name"``
    attr names that can be interpreted by ``cf_xarray``.
    References
    ----------
    .. [1] https://cf-xarray.readthedocs.io/en/latest/coord_axes.html#axes-and-coordinates
    """
    keys = CF_NAME_MAP[axis]
    coord_var = None
    for key in keys:
    try:
    coord_var = obj.cf[key]
    break
    except KeyError:
    pass
    if coord_var is None:
    raise KeyError(
    f"A coordinate variable for the {axis} axis was not found. Make sure "
    "the coordinate variable exists and either the (1) 'axis' attr or (2) "
    "'standard_name' attr is set, or (3) the dimension name follows the "
    "short-hand convention (e.g.,'lat')."
    )
    return coord_var
  • API Documentation (using docstring): https://xcdat.readthedocs.io/en/latest/_modules/xcdat/axis.html#get_axis_coord
  • Intro to cf_xarray: https://cf-xarray.readthedocs.io/en/latest/howtouse.html
  • cf_xarray mapping table for "axis" and "standard_name" attrs: https://cf-xarray.readthedocs.io/en/latest/coord_axes.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Inconsistencies or issues which will cause an issue or problem for users or implementors.
Projects
No open projects
Status: Done
Development

Successfully merging a pull request may close this issue.

2 participants