diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index bbcee5ad..82d69493 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -22,8 +22,7 @@ jobs: - name: ๐Ÿ“ฆ Install dependencies run: | python -m pip install --upgrade pip - pip install pytest pytest-mock - pip install git+https://github.com/NOC-MSM/OceanDataStore.git + pip install -e .[tests] - name: ๐Ÿงช Run Pytest run: | diff --git a/OceanDataStore/catalog/__init__.py b/OceanDataStore/catalog/__init__.py index 0db5e707..f7d4a298 100644 --- a/OceanDataStore/catalog/__init__.py +++ b/OceanDataStore/catalog/__init__.py @@ -7,6 +7,6 @@ __author__ = "Ollie Tooth, Joao Morado, Tobias Ferreira" __credits__ = "National Oceanography Centre (NOC), Southampton, UK" -from OceanDataStore.catalog.oceandatacatalog import OceanDataCatalog +from OceanDataStore.catalog.oceandatacatalog import OceanDataCatalog, CatalogSummary -__all__ = ("OceanDataCatalog",) \ No newline at end of file +__all__ = ("OceanDataCatalog", "CatalogSummary",) diff --git a/OceanDataStore/catalog/oceandatacatalog.py b/OceanDataStore/catalog/oceandatacatalog.py index 1336356b..659b412d 100644 --- a/OceanDataStore/catalog/oceandatacatalog.py +++ b/OceanDataStore/catalog/oceandatacatalog.py @@ -1,5 +1,5 @@ """ -data_catalog.py +oceandatacatalog.py Description: This module defines the OceanDataCatalog() class which is a @@ -16,6 +16,193 @@ import icechunk import xarray as xr +# -- NOC brand CSS -- # +_NOC_CSS = """ + +""" + +# -- Define CatalogSummary() class -- # +class CatalogSummary: + """ + Container for OceanDataCatalog summary. + + Parameters + ---------- + num_collections : int + The number of collections in the catalog. + num_items : int + The number of items in the catalog. + other_info : dict + Any other relevant summary information about the catalog. + """ + def __init__(self, + display_text: str | None = None, + display_html: str | None = None, + ): + self.display_text = display_text + self.display_html = display_html + + def __repr__(self): + """ + Plain text representation of the CatalogSummary. + """ + return self.display_text + + def _repr_html_(self): + """ + HTML representation of the CatalogSummary. + """ + return self.display_html + # -- Define OceanDataCatalog() class -- # class OceanDataCatalog: """ @@ -56,6 +243,54 @@ def __init__(self, # Define the Collection and Items attributes: self.Collection = None self.Items = None + # Cache the catalog name for display: + self._catalog_name = catalog_name + + def __repr__(self) -> str: + """ + Plain text representation of the OceanDataCatalog. + """ + n_collections = len(self.available_collections) + col_name = self.Collection.id if self.Collection else "โ€”" + n_items = len(self.Items) if self.Items is not None else "โ€”" + return ( + f"OceanDataCatalog\n" + f" Catalog: {self._catalog_name}\n" + f" URL: {self._stac_url}\n" + f" Collections: {n_collections} available\n" + f" Collection: {col_name}\n" + f" Search: {n_items} items" + ) + + + def _repr_html_(self) -> str: + """ + HTML representation of the OceanDataCatalog. + """ + n_collections = len(self.available_collections) + col_name = self.Collection.id if self.Collection else "none selected" + n_items = ( + f"{len(self.Items)} items" + if self.Items is not None + else "no search yet" + ) + return ( + f"{_NOC_CSS}" + f"
" + f"
" + f" OceanDataCatalog" + f" {self._catalog_name}" + f"
" + f"
" + f"
" + f"
Collections {n_collections}
" + f"
Active collection {col_name}
" + f"
Last search {n_items}
" + f"
" + f" " + f"
" + f"
" + ) @property @@ -80,29 +315,394 @@ def available_items(self) -> list[str]: return [next(scope.get_items(recursive=True), None).id for _ in range(25)] - def summary(self) -> str: + def summary(self) -> CatalogSummary: """ - Summary description of the root Catalog or a selected Collection. - """ - return (self.Collection or self.Catalog).describe() - + Summary of the most recent OceanDataCatalog search. - def item_summary(self) -> None: - """ - Summary description of the Items returned from the most recent search. + * In Jupyter / Marimo environments a styled HTML table is displayed. + * In plain Python / CLI environments a formatted text table is printed instead. """ + # -- Validate STAC Items -- # if not self.Items: raise ValueError("No Items returned in most recent query. Use 'search()' to query Catalog.") + n = len(self.Items) + + # ----- HTML Output ----- # + rows_html = "" + for item in self.Items: + title = item.properties.get("title", "") + platform = item.properties.get("platform", "โ€”") + start = item.properties.get("start_datetime", "โ€”") + end = item.properties.get("end_datetime", "โ€”") + variables = item.properties.get("variables", []) + if variables: + var_list = "
".join(variables) + vars_cell = ( + f"
" + f"{len(variables)} variable{'s' if len(variables) != 1 else ''}" + f"
{var_list}
" + f"
" + ) + else: + vars_cell = "โ€”" + + title_cell = title if title else "โ€”" + rows_html += ( + f"" + f"{item.id}" + f"{title_cell}" + f"{platform}" + f"{start}" + f"{end}" + f"{vars_cell}" + f"" + ) + + col_badge = ( + f"{self.Collection.id}" + if self.Collection else "" + ) + html = ( + f"{_NOC_CSS}" + f"
" + f"
" + f" Search Results" + f" {n} Item{'s' if n != 1 else ''} found" + f" {col_badge}" + f"
" + f"
" + f" " + f" " + f" " + f" " + f" " + f" {rows_html}" + f"
Item IDTitlePlatformStart DateEnd DateVariables
" + f"
" + f"
" + ) + + # ----- Plain-Text Output ----- # + col_w = [46, 28, 10, 12, 12, 30] + headers = ["Item ID", "Title", "Platform", "Start Date", "End Date", "Variables"] + sep = "+" + "+".join("-" * (w + 2) for w in col_w) + "+" + header_row = "| " + " | ".join(h.ljust(col_w[i]) for i, h in enumerate(headers)) + " |" + text_lines = [f"Search Results โ€” {n} Item{'s' if n != 1 else ''} found", sep, header_row, sep] for item in self.Items: - print(f""" - * Item ID: {item.id} - Title: {item.properties.get('title', 'No title available')} - Description: {item.properties.get('description', 'No description available')} - Platform: {item.properties.get('platform', 'No platform available')} - Start Date: {item.properties.get('start_datetime', 'No start date available')} - End Date: {item.properties.get('end_datetime', 'No end date available')} - """) + variables = item.properties.get("variables", []) + row = [ + item.id[:col_w[0]], + item.properties.get("title", "")[:col_w[1]], + item.properties.get("platform", "")[:col_w[2]], + item.properties.get("start_datetime", "")[:col_w[3]], + item.properties.get("end_datetime", "")[:col_w[4]], + (", ".join(variables))[:col_w[5]], + ] + text_lines.append("| " + " | ".join(v.ljust(col_w[i]) for i, v in enumerate(row)) + " |") + text_lines.append(sep) + text = "\n".join(text_lines) + + return CatalogSummary(display_text=text, display_html=html) + + + def collection_summary(self) -> CatalogSummary: + """ + Display a summary table of all Collections in the OceanDataCatalog: + + * In Jupyter / Marimo environments a styled HTML table is displayed. + * In plain Python / CLI environments a formatted text table is printed instead. + """ + collections = list(self.Catalog.get_all_collections()) + n = len(collections) + + def _extent_dates(col): + try: + ext = col.extent.temporal.intervals + start = ext[0][0].strftime("%Y-%m-%d") if ext[0][0] else "โ€”" + end = ext[0][1].strftime("%Y-%m-%d") if ext[0][1] else "present" + except Exception: + start, end = "โ€”", "โ€”" + return start, end + + # ----- HTML Output ----- # + rows_html = "" + for col in collections: + start, end = _extent_dates(col) + desc = col.description or "" + desc_cell = ( + f"
" + f"Summary" + f"
{desc.replace('**', '')}
" + f"
" + if desc else "โ€”" + ) + active = " active" if ( + self.Collection and col.id == self.Collection.id + ) else "" + col_title_cell = col.title if col.title else "โ€”" + rows_html += ( + f"" + f"{col.id}{active}" + f"{col_title_cell}" + f"{desc_cell}" + f"{start}" + f"{end}" + f"" + ) + + html = ( + f"{_NOC_CSS}" + f"
" + f"
" + f" Collections" + f" {n} available" + f"
" + f"
" + f" " + f" " + f" " + f" " + f" " + f" {rows_html}" + f"
Collection IDTitleDescriptionFromTo
" + f"
" + f"
" + ) + + # ----- Plain-Text Output ----- # + col_w = [30, 42, 12, 12] + headers = ["Collection ID", "Title", "From", "To"] + sep = "+" + "+".join("-" * (w + 2) for w in col_w) + "+" + header_row = "| " + " | ".join(h.ljust(col_w[i]) for i, h in enumerate(headers)) + " |" + text_lines = [f"Collections โ€” {n} available", sep, header_row, sep] + for col in collections: + start, end = _extent_dates(col) + row = [ + col.id[:col_w[0]], + (col.title or "")[:col_w[1]], + start[:col_w[2]], + end[:col_w[3]], + ] + text_lines.append("| " + " | ".join(v.ljust(col_w[i]) for i, v in enumerate(row)) + " |") + text_lines.append(sep) + text = "\n".join(text_lines) + + return CatalogSummary(display_text=text, display_html=html) + + + def item_summary(self, id: str) -> CatalogSummary: + """ + Display detailed metadata for a single OceanDataStore Item. + + Searches the current Items list first; if the Item is not found + there it is fetched directly from the Catalog URL. + + * In Jupyter / Marimo environments a styled HTML card is displayed with collapsible + property and asset sections. + * In plain Python / CLI environments a formatted text summary is printed instead. + + Parameters + ---------- + id : str + Item ID to display metadata for. + + Raises + ------ + TypeError + If *id* is not a string. + ValueError + If the Item ID is not found in the Catalog. + """ + if not isinstance(id, str): + raise TypeError("'id' must be a string.") + + # Collect STAC Item properties metadata: + item = None + if self.Items: + for it in self.Items: + if it.id == id: + item = it + break + if item is None: + try: + item = self._open_item(id=id) + except Exception: + raise ValueError(f"Item '{id}' not found in Catalog.") + + props = item.properties + title = props.get("title", "") + desc_raw = props.get("description", "") + desc = desc_raw.split("OceanDataCatalog Access")[0].strip() if desc_raw else "" + platform = props.get("platform", "") + start = props.get("start_datetime", "") + end = props.get("end_datetime", "") + bbox = item.bbox + bbox_str = ( + f"{bbox[0]:.2f}, {bbox[1]:.2f}, {bbox[2]:.2f}, {bbox[3]:.2f}" + if bbox else "โ€”" + ) + + # ---- HTML Output (Jupyter) ---- # + coll_badge = f"{item.collection_id}" if item.collection_id else "" + + core_stats = ( + f"
" + f"
Platform {platform or 'โ€”'}
" + f"
Start {start or 'โ€”'}
" + f"
End {end or 'โ€”'}
" + f"
BBox ({bbox_str})
" + f"
" + ) + + none_span = "โ€”" + if title or desc: + title_val = title if title else none_span + desc_val = desc if desc else none_span + title_row = ( + f"" + f" " + f" " + f"
TitleDescription
{title_val}{desc_val.replace('**', '')}
" + ) + else: + title_row = "" + + # Properties: + _shown = {"title", "description", "platform", "start_datetime", "end_datetime", "datetime"} + prop_rows = "" + for key, val in props.items(): + if key in _shown: + continue + if isinstance(val, list): + items_html = "
".join(str(v) for v in val) + val_cell = ( + f"
" + f"{len(val)} item{'s' if len(val) != 1 else ''}" + f"
{items_html}
" + f"
" + ) + elif isinstance(val, dict): + dict_html = "
".join(f"{k}: {v}" for k, v in val.items()) + val_cell = ( + f"
" + f"{len(val)} field{'s' if len(val) != 1 else ''}" + f"
{dict_html}
" + f"
" + ) + else: + val_cell = str(val) if val is not None else none_span + prop_rows += f"{key}{val_cell}" + + props_section = "" + if prop_rows: + props_section = ( + f"
Properties
" + f"" + f" " + f" {prop_rows}" + f"
PropertyValue
" + ) + + asset_rows = "" + for asset_key, asset in item.assets.items(): + af = asset.extra_fields + media_type = asset.media_type or "" + endpoint = af.get("endpoint_url", "") + bucket = af.get("bucket", "") + prefix = af.get("prefix", "") + asset_rows += ( + f"" + f"{asset_key}" + f"{media_type}" + f"{endpoint}" + f"{bucket}" + f"{prefix}" + f"" + ) + + assets_section = "" + if asset_rows: + assets_section = ( + f"
Assets
" + f"" + f" " + f" " + f" " + f" {asset_rows}" + f"
KeyMedia TypeEndpointBucketPrefix
" + ) + + access_str = f"catalog.open_dataset(id='{id}')" + _copy_js = ( + "(function(b){" + "var t=document.createElement('textarea');" + "t.value=b.dataset.copy;" + "document.body.appendChild(t);" + "t.select();" + "document.execCommand('copy');" + "document.body.removeChild(t);" + "b.textContent='Copied!';" + "setTimeout(function(){b.textContent='Copy'},1500)" + "})(this)" + ) + access_section = ( + f"
Access
" + f"
" + f" {access_str}" + f" " + f"
" + ) + + html = ( + f"{_NOC_CSS}" + f"
" + f"
" + f" {id}" + f" {coll_badge}" + f"
" + f"
" + f" {core_stats}" + f" {title_row}" + f" {access_section}" + f" {props_section}" + f" {assets_section}" + f"
" + f"
" + ) + + # ---- Plain-Text Output ---- # + _shown_text = {"title", "description", "platform", "start_datetime", "end_datetime", "datetime"} + text_lines = [ + f"Item: {id}", + f" Title: {title or 'โ€”'}", + f" Platform: {platform or 'โ€”'}", + f" Start: {start or 'โ€”'}", + f" End: {end or 'โ€”'}", + f" BBox: {bbox_str}", + "", + " Properties:", + ] + for key, val in props.items(): + if key in _shown_text: + continue + if isinstance(val, list): + preview = ", ".join(str(v) for v in val[:5]) + suffix = ", ..." if len(val) > 5 else "" + text_lines.append(f" {key}: [{preview}{suffix}]") + else: + text_lines.append(f" {key}: {val}") + if item.assets: + text_lines.append("") + text_lines.append(" Assets:") + for asset_key, asset in item.assets.items(): + af = asset.extra_fields + loc = f"{af.get('endpoint_url', '')}/{af.get('bucket', '')}/{af.get('prefix', '')}" + text_lines.append(f" {asset_key}: {asset.media_type or ''} โ€” {loc}") + text_lines += ["", f" Access: {access_str}"] + text = "\n".join(text_lines) + + return CatalogSummary(display_text=text, display_html=html) def _filter_items(self, @@ -210,7 +810,7 @@ def search(self, standard_name=standard_name, item_name=item_name ) - self.item_summary() + return self.summary() def _open_item( @@ -245,28 +845,24 @@ def _open_item( return item - def _open_icechunk_store( + def _open_icechunk_repo( self, fields: dict, - branch: str, - ) -> xr.Dataset: + ) -> icechunk.Repository: """ - Open STAC Item Icechunk store asset as xarray Dataset. + Open STAC Item asset as an Icechunk Repository. Parameters ---------- fields : dict - Dictionary of arguments to s3_storage() defining Icechunk - S3 storage instance. - branch : str - Branch of the Icechunk repository to read. + Dictionary of arguments defining Icechunk S3 storage instance. Returns ------- - xarray.Dataset - Dataset read from Item asset. + icechunk.Repository + Icechunk Repository object for the Item asset. """ - # Define S3 Object Store containing asset: + # Define S3 storage configuration: storage = icechunk.s3_storage( bucket=fields['bucket'], prefix=fields['prefix'], @@ -276,10 +872,40 @@ def _open_icechunk_store( force_path_style=True ) - # Open Item asset Icechunk repository on specified branch: + # Open Icechunk Repository from S3 storage: repo = icechunk.Repository.open(storage=storage) + return repo + + + def _open_icechunk_store( + self, + fields: dict, + branch: str, + group: str | None = None + ) -> xr.Dataset: + """ + Open STAC Item asset Icechunk store as xarray Dataset. + + Parameters + ---------- + fields : dict + Dictionary of arguments to s3_storage() defining Icechunk + S3 storage instance. + branch : str + Branch of the Icechunk repository to read. + group : str, optional + Group within the Icechunk repository to read. Default is None, + which reads from the root of the repository. + + Returns + ------- + xarray.Dataset + Dataset read from Item asset. + """ + # Open Zarr store from Icechunk repository: + repo = self._open_icechunk_repo(fields) store = repo.readonly_session(branch=branch).store - ds = xr.open_zarr(store, consolidated=False) + ds = xr.open_zarr(store, consolidated=False, group=group) return ds @@ -287,6 +913,8 @@ def _open_icechunk_store( def _open_zarr_store( self, fields: dict, + consolidated: bool = True, + group: str | None = None ) -> xr.Dataset: """ Open STAC Item Zarr store asset as xarray Dataset. @@ -296,6 +924,12 @@ def _open_zarr_store( fields : dict Dictionary of arguments to open_zarr() defining URL and version of Zarr store. + consolidated : bool, optional + Whether to open Zarr store using consolidated metadata capability. + Default is True, meaning that consolidated metadata is expected. + group : str, optional + Group within the Zarr store to read. Default is None, + which reads from the root of the store. Returns ------- @@ -304,19 +938,80 @@ def _open_zarr_store( """ # Open Item asset Zarr store via URL: url = f"{fields['endpoint_url']}/{fields['bucket']}/{fields['prefix']}" - ds = xr.open_zarr(url, zarr_format=int(fields['zarr_format']), consolidated=True) + ds = xr.open_zarr(url, zarr_format=int(fields['zarr_format']), consolidated=consolidated, group=group) return ds + def open_repo(self, + id: str, + asset_key: Optional[str] = None + ) -> icechunk.Repository: + """ + Open STAC Item asset as an Icechunk Repository. + + Parameters + ---------- + id : str + Item ID to open asset. + asset_key : str, optional + Key of the asset to open. Default is to infer the key from the Item ID. + + Returns + ------- + icechunk.Repository + Icechunk Repository for STAC Item asset. + + Raises + ------ + ValueError + If the Item ID or asset key is not found in the catalog. + ValueError + If the asset key is not found in the Item ID. + """ + # -- Validate Inputs -- # + if not isinstance(id, str): + raise TypeError("'id' must be a string.") + + # -- Collect Item Asset -- # + try: + item = self._open_item(id=id) + except Exception: + raise RuntimeError(f"Item ID '{id}' not found in Catalog.") + + # Infer asset key from Item ID if not provided: + if asset_key is None: + asset_key = list(item.assets.keys())[0] + asset = item.assets.get(asset_key) + if asset is None: + raise ValueError(f"Asset key '{asset_key}' not found in Item ID '{id}'.") + + fields = asset.extra_fields + + # -- Open Icechunk Repository -- # + if asset.to_dict()['type'] == "application/vnd.zarr+icechunk": + required_fields = ['bucket', 'prefix', 'anonymous', 'endpoint_url'] + for field in required_fields: + if field not in fields: + raise ValueError(f"Missing asset field '{field}' in item '{id}'.") + repo = self._open_icechunk_repo(fields=fields) + else: + raise ValueError(f"Item ID '{id}' asset is not an Icechunk repository.") + + return repo + + def open_dataset(self, id: str, + group: Optional[str] = None, variable_names: Optional[list[str]] = None, start_datetime: Optional[str] = None, end_datetime: Optional[str] = None, bbox: Optional[tuple[float, float, float, float]] = None, branch: str = "main", - asset_key: Optional[str] = None) -> xr.Dataset: + consolidated: bool = True, + asset_key: Optional[str] = None + ) -> xr.Dataset: """ Open STAC Item asset as an xarray Dataset. @@ -324,6 +1019,9 @@ def open_dataset(self, ---------- id : str Item ID to open asset. + group : str, optional + Group within the Zarr or Icechunk repository to read. Default is None, + which reads from the root of the repository. variable_names : list[str], optional List of variable names to be parsed from the dataset. Default is to return all variables. @@ -341,6 +1039,8 @@ def open_dataset(self, Default is to use the Item bbox. branch : str, optional Branch of the Icechunk repository to use. Default is to use the "main" branch. + consolidated : bool, optional + Whether to open Zarr stores using consolidated metadata. Default is True. asset_key : str, optional Key of the asset to open. Default is to infer the key from the Item ID. @@ -358,9 +1058,11 @@ def open_dataset(self, KeyError If the specified variable(s) are not found in the dataset. """ - # -- Validate inputs -- # + # -- Validate Inputs -- # if not isinstance(id, str): raise TypeError("'id' must be a string.") + if group is not None and not isinstance(group, str): + raise TypeError("'group' must be a string or None.") if not isinstance(variable_names, (type(None), list)): raise TypeError("'variable_names' must be a list of strings.") if variable_names is not None and not all([isinstance(var, str) for var in variable_names]): @@ -373,6 +1075,10 @@ def open_dataset(self, raise TypeError("'bbox' must be a tuple or None.") if bbox is not None and (len(bbox) != 4 or not all(isinstance(coord, float) for coord in bbox)): raise TypeError("'bbox' must be a tuple of floats in the form (lon_min, lon_max, lat_min, lat_max).") + if not isinstance(branch, str): + raise TypeError("'branch' must be a string.") + if not isinstance(consolidated, bool): + raise TypeError("'consolidated' must be a boolean.") # -- Collect Item Asset -- # try: @@ -395,7 +1101,7 @@ def open_dataset(self, for field in required_fields: if field not in fields: raise ValueError(f"Missing asset field '{field}' in item '{id}'.") - ds = self._open_icechunk_store(fields=fields, branch=branch) + ds = self._open_icechunk_store(fields=fields, branch=branch, group=group) # Open Zarr store as xarray Dataset: elif asset.to_dict()['type'] == 'application/vnd.zarr': @@ -403,7 +1109,7 @@ def open_dataset(self, for field in required_fields: if field not in fields: raise ValueError(f"Missing asset field '{field}' in item '{id}'.") - ds = self._open_zarr_store(fields=fields) + ds = self._open_zarr_store(fields=fields, group=group) else: raise ValueError(f"Unsupported media type {asset.to_dict()['type']} for Item asset.") diff --git a/examples/catalogs/OceanDataCatalog_example.ipynb b/examples/catalogs/OceanDataCatalog_example.ipynb index bd769b1e..18131b99 100644 --- a/examples/catalogs/OceanDataCatalog_example.ipynb +++ b/examples/catalogs/OceanDataCatalog_example.ipynb @@ -84,98 +84,180 @@ "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - " * Item ID: noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1y\n", - " Title: T1y Icechunk repository\n", - " Description: **Annual mean global ocean physics outputs defined at NEMO model T-points.**\n", - "\n", - "**OceanDataCatalog Access:**\n", - "`catalog.open_dataset(id='noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1y')`\n", - " Platform: gn\n", - " Start Date: 1976-01-01T00:00:00Z\n", - " End Date: 2025-07-31T00:00:00Z\n", - " \n", - "\n", - " * Item ID: noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1m\n", - " Title: T1m Icechunk repository\n", - " Description: **Monthly mean global ocean physics outputs defined at NEMO model T-points.**\n", - "\n", - "**OceanDataCatalog Access:**\n", - "`catalog.open_dataset(id='noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1m')`\n", - " Platform: gn\n", - " Start Date: 1976-01-01T00:00:00Z\n", - " End Date: 2025-07-31T00:00:00Z\n", - " \n", - "\n", - " * Item ID: noc-npd-era5/npd-eorca025-era5v1/r1i1c1f1/gn/T1y_3d\n", - " Title: T1y_3d Icechunk repository\n", - " Description: **Annual mean global ocean physics outputs defined at NEMO model T-points.**\n", - "\n", - "**OceanDataCatalog Access:**\n", - "`catalog.open_dataset(id='noc-npd-era5/npd-eorca025-era5v1/r1i1c1f1/gn/T1y_3d')`\n", - " Platform: gn\n", - " Start Date: 1976-01-01T00:00:00Z\n", - " End Date: 2025-07-31T00:00:00Z\n", - " \n", - "\n", - " * Item ID: noc-npd-era5/npd-eorca025-era5v1/r1i1c1f1/gn/T1m_3d\n", - " Title: T1m_3d Icechunk repository\n", - " Description: **Monthly mean global ocean physics outputs defined at NEMO model T-points.**\n", - "\n", - "**OceanDataCatalog Access:**\n", - "`catalog.open_dataset(id='noc-npd-era5/npd-eorca025-era5v1/r1i1c1f1/gn/T1m_3d')`\n", - " Platform: gn\n", - " Start Date: 1976-01-01T00:00:00Z\n", - " End Date: 2025-07-31T00:00:00Z\n", - " \n", - "\n", - " * Item ID: noc-npd-era5/npd-eorca025-era5v1/r1i1c1f1/gn/T5d_3d\n", - " Title: T5d_3d Icechunk repository\n", - " Description: **5-day mean global ocean physics outputs defined at NEMO model T-points.**\n", - "\n", - "**OceanDataCatalog Access:**\n", - "`catalog.open_dataset(id='noc-npd-era5/npd-eorca025-era5v1/r1i1c1f1/gn/T5d_3d')`\n", - " Platform: gn\n", - " Start Date: 1976-01-01T00:00:00Z\n", - " End Date: 2025-07-31T00:00:00Z\n", - " \n", - "\n", - " * Item ID: noc-npd-era5/npd-eorca12-era5v1/r1i1c1f1/gn/T1y_3d\n", - " Title: T1y_3d Icechunk repository\n", - " Description: **Annual mean global ocean physics outputs defined at NEMO model T-points.**\n", - "\n", - "**OceanDataCatalog Access:**\n", - "`catalog.open_dataset(id='noc-npd-era5/npd-eorca12-era5v1/r1i1c1f1/gn/T1y_3d')`\n", - " Platform: gn\n", - " Start Date: 1976-01-01T00:00:00Z\n", - " End Date: 2025-06-30T00:00:00Z\n", - " \n", - "\n", - " * Item ID: noc-npd-era5/npd-eorca12-era5v1/r1i1c1f1/gn/T1m_3d\n", - " Title: T1m_3d Icechunk repository\n", - " Description: **Monthly mean global ocean physics outputs defined at NEMO model T-points.**\n", - "\n", - "**OceanDataCatalog Access:**\n", - "`catalog.open_dataset(id='noc-npd-era5/npd-eorca12-era5v1/r1i1c1f1/gn/T1m_3d')`\n", - " Platform: gn\n", - " Start Date: 1976-01-01T00:00:00Z\n", - " End Date: 2025-06-30T00:00:00Z\n", - " \n", - "\n", - " * Item ID: noc-npd-era5/npd-eorca12-era5v1/r1i1c1f1/gn/T5d_3d\n", - " Title: T5d_3d Icechunk repository\n", - " Description: **5-day mean global ocean physics outputs defined at NEMO model T-points.**\n", - "\n", - "**OceanDataCatalog Access:**\n", - "`catalog.open_dataset(id='noc-npd-era5/npd-eorca12-era5v1/r1i1c1f1/gn/T5d_3d')`\n", - " Platform: gn\n", - " Start Date: 1990-01-01T00:00:00Z\n", - " End Date: 2025-06-30T00:00:00Z\n", - " \n" - ] + "data": { + "text/html": [ + "\n", + "\n", + "
Search Results 8 Items found noc-npd-era5
Item IDTitlePlatform Start DateEnd DateVariables
noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1yT1y Icechunk repositorygn1976-01-01T00:00:00Z2025-07-31T00:00:00Z
74 variables
empmr
e3t
hfempds
hfds
fsitherm
hflx_rnf
hfns
hfrainds
hfls
ficeberg
friver
berg_latent_heat_flux
mlotst
hfss
hfto
hfsr
hfevapds
deptht_bounds
mlotstmax
evs
mlotstsq
ocontempdiff
osaltpmdiff
osaltdiff
mlotstmin
ocontempmint
rlntds
ocontempadvect
osalttend
rsdo
prsn
osaltadvect
ocontemptend
pbo
sbt_con
snow_ai_cea
sbs_abs
rsntds
sfdsi
so_abs
ocontemppmdiff
sohfcisf
somixhgt
sos_abs
sosafldo
soicecov
somint_abs
sohflisf
rsdoabsorb
snowpre
strd_atf_li
sowaflup
sowflisf
strd_bbl_li
thetao_con
somxl010
time_centered_bounds
strd_evd_li
somxzint1
ttrd_evd_li
ttrd_atf_li
sowindsp
time_counter_bounds
tos_con
tossq_con
tnpeo
ttrd_bbl_li
sossq_abs
ttrd_qns_li
zossq
vohflisf
zos
vowflisf
vohfcisf
noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1mT1m Icechunk repositorygn1976-01-01T00:00:00Z2025-07-31T00:00:00Z
74 variables
berg_latent_heat_flux
deptht_bounds
ficeberg
fsitherm
e3t
evs
empmr
friver
hfds
hfevapds
hfrainds
hfsr
hfempds
hflx_rnf
hfls
hfns
mlotst
mlotstmin
mlotstsq
mlotstmax
hfto
hfss
ocontemptend
osaltadvect
osaltdiff
ocontempdiff
ocontempmint
osaltpmdiff
ocontempadvect
ocontemppmdiff
rlntds
rsdo
rsdoabsorb
osalttend
pbo
prsn
rsntds
sbs_abs
sbt_con
snow_ai_cea
sohfcisf
soicecov
snowpre
sohflisf
so_abs
somxl010
somint_abs
sosafldo
sos_abs
sossq_abs
strd_bbl_li
strd_atf_li
strd_evd_li
sowaflup
sowindsp
sowflisf
somixhgt
somxzint1
sfdsi
thetao_con
tossq_con
tnpeo
tos_con
time_centered_bounds
zossq
time_counter_bounds
ttrd_bbl_li
vowflisf
zos
ttrd_evd_li
ttrd_atf_li
vohfcisf
ttrd_qns_li
vohflisf
noc-npd-era5/npd-eorca025-era5v1/r1i1c1f1/gn/T1y_3dT1y_3d Icechunk repositorygn1976-01-01T00:00:00Z2025-07-31T00:00:00Z
51 variables
hfls
evs
friver
empmr
hfempds
berg_latent_heat_flux
ficeberg
hfevapds
fsitherm
hfds
hflx_rnf
hfns
hfss
mlotstmax
mlotstsq
hfto
hfsr
hfrainds
mlotstmin
mlotst
prsn
sbt_con
sbs_abs
pbo
ocontempmint
rlntds
rsntds
snow_ai_cea
sohfcisf
somint_abs
somixhgt
soicecov
sfdsi
sowflisf
somxl010
somxzint1
sohflisf
sossq_abs
sowaflup
time_centered_bounds
snowpre
time_counter_bounds
sos_abs
tnpeo
sosafldo
sowindsp
tossq_con
zos
tos_con
ttrd_qns_li
zossq
noc-npd-era5/npd-eorca025-era5v1/r1i1c1f1/gn/T1m_3dT1m_3d Icechunk repositorygn1976-01-01T00:00:00Z2025-07-31T00:00:00Z
51 variables
evs
empmr
ficeberg
fsitherm
hfds
hfls
berg_latent_heat_flux
hfevapds
hfempds
friver
hflx_rnf
hfsr
mlotst
hfss
hfrainds
hfto
hfns
mlotstsq
mlotstmax
pbo
rlntds
ocontempmint
prsn
rsntds
mlotstmin
sbs_abs
sbt_con
snowpre
sohfcisf
somixhgt
soicecov
snow_ai_cea
sfdsi
somint_abs
sohflisf
somxl010
sosafldo
sos_abs
sossq_abs
sowaflup
time_counter_bounds
somxzint1
sowindsp
time_centered_bounds
tos_con
ttrd_qns_li
tossq_con
zos
sowflisf
zossq
tnpeo
noc-npd-era5/npd-eorca025-era5v1/r1i1c1f1/gn/T5d_3dT5d_3d Icechunk repositorygn1976-01-01T00:00:00Z2025-07-31T00:00:00Z
50 variables
hfls
evs
empmr
fsitherm
berg_latent_heat_flux
hfds
hfevapds
hfss
friver
hfrainds
ficeberg
hfto
hfempds
hfns
hfsr
mlotst
hflx_rnf
rlntds
ocontempmint
mlotstmax
mlotstmin
rsntds
sbs_abs
snow_ai_cea
sbt_con
sfdsi
snowpre
sohfcisf
mlotstsq
prsn
pbo
somixhgt
sowaflup
somint_abs
sowindsp
sosafldo
sohflisf
soicecov
sowflisf
somxl010
time_centered_bounds
tossq_con
sossq_abs
tos_con
somxzint1
time_counter_bounds
tnpeo
sos_abs
zos
zossq
noc-npd-era5/npd-eorca12-era5v1/r1i1c1f1/gn/T1y_3dT1y_3d Icechunk repositorygn1976-01-01T00:00:00Z2025-06-30T00:00:00Z
50 variables
berg_latent_heat_flux
ficeberg
friver
hfds
empmr
fsitherm
hfls
evs
hfevapds
hfempds
hfns
mlotstmax
hfsr
mlotstsq
hfto
hflx_rnf
mlotst
mlotstmin
hfrainds
pbo
sbt_con
rlntds
ocontempmint
hfss
sbs_abs
sohflisf
prsn
rsntds
sohfcisf
snow_ai_cea
sfdsi
sos_abs
somxzint1
soicecov
sossq_abs
sosafldo
somixhgt
time_centered_bounds
sowflisf
sowaflup
somxl010
snowpre
somint_abs
sowindsp
time_counter_bounds
tos_con
tnpeo
zossq
zos
tossq_con
noc-npd-era5/npd-eorca12-era5v1/r1i1c1f1/gn/T1m_3dT1m_3d Icechunk repositorygn1976-01-01T00:00:00Z2025-06-30T00:00:00Z
50 variables
berg_latent_heat_flux
hfevapds
friver
ficeberg
evs
hfls
hfempds
empmr
fsitherm
mlotstmax
hfsr
hflx_rnf
hfss
hfto
mlotst
mlotstmin
hfrainds
hfns
hfds
ocontempmint
sbt_con
mlotstsq
rlntds
pbo
rsntds
prsn
sfdsi
sohflisf
somint_abs
sbs_abs
snowpre
sohfcisf
somxl010
snow_ai_cea
soicecov
somixhgt
sos_abs
sossq_abs
sowindsp
time_centered_bounds
sosafldo
tnpeo
somxzint1
sowflisf
time_counter_bounds
tos_con
sowaflup
tossq_con
zos
zossq
noc-npd-era5/npd-eorca12-era5v1/r1i1c1f1/gn/T5d_3dT5d_3d Icechunk repositorygn1990-01-01T00:00:00Z2025-06-30T00:00:00Z
50 variables
berg_latent_heat_flux
hfds
hfevapds
hflx_rnf
fsitherm
hfempds
friver
empmr
evs
ficeberg
hfls
hfns
mlotstsq
hfto
mlotst
mlotstmin
mlotstmax
hfsr
hfss
hfrainds
pbo
prsn
rsntds
sbt_con
sfdsi
sbs_abs
ocontempmint
rlntds
snow_ai_cea
sohfcisf
somixhgt
somint_abs
somxl010
soicecov
sohflisf
snowpre
sosafldo
sowaflup
sowindsp
time_centered_bounds
somxzint1
sowflisf
sossq_abs
sos_abs
time_counter_bounds
tos_con
zos
tnpeo
tossq_con
zossq
" + ], + "text/plain": [ + "Search Results โ€” 8 Items found\n", + "+------------------------------------------------+------------------------------+------------+--------------+--------------+--------------------------------+\n", + "| Item ID | Title | Platform | Start Date | End Date | Variables |\n", + "+------------------------------------------------+------------------------------+------------+--------------+--------------+--------------------------------+\n", + "| noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1y | T1y Icechunk repository | gn | 1976-01-01T0 | 2025-07-31T0 | empmr, e3t, hfempds, hfds, fsi |\n", + "| noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1m | T1m Icechunk repository | gn | 1976-01-01T0 | 2025-07-31T0 | berg_latent_heat_flux, deptht_ |\n", + "| noc-npd-era5/npd-eorca025-era5v1/r1i1c1f1/gn/T | T1y_3d Icechunk repository | gn | 1976-01-01T0 | 2025-07-31T0 | hfls, evs, friver, empmr, hfem |\n", + "| noc-npd-era5/npd-eorca025-era5v1/r1i1c1f1/gn/T | T1m_3d Icechunk repository | gn | 1976-01-01T0 | 2025-07-31T0 | evs, empmr, ficeberg, fsitherm |\n", + "| noc-npd-era5/npd-eorca025-era5v1/r1i1c1f1/gn/T | T5d_3d Icechunk repository | gn | 1976-01-01T0 | 2025-07-31T0 | hfls, evs, empmr, fsitherm, be |\n", + "| noc-npd-era5/npd-eorca12-era5v1/r1i1c1f1/gn/T1 | T1y_3d Icechunk repository | gn | 1976-01-01T0 | 2025-06-30T0 | berg_latent_heat_flux, ficeber |\n", + "| noc-npd-era5/npd-eorca12-era5v1/r1i1c1f1/gn/T1 | T1m_3d Icechunk repository | gn | 1976-01-01T0 | 2025-06-30T0 | berg_latent_heat_flux, hfevapd |\n", + "| noc-npd-era5/npd-eorca12-era5v1/r1i1c1f1/gn/T5 | T5d_3d Icechunk repository | gn | 1990-01-01T0 | 2025-06-30T0 | berg_latent_heat_flux, hfds, h |\n", + "+------------------------------------------------+------------------------------+------------+--------------+--------------+--------------------------------+" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -218,19 +300,229 @@ "catalog.available_items" ] }, + { + "cell_type": "markdown", + "id": "efaa0bb1", + "metadata": {}, + "source": [ + "* To view a STAC Item in detail, we can pass the Item **ID** to the `.item_summary()` method." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e96c7605", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1y noc-npd-era5
Platform gn
Start 1976-01-01T00:00:00Z
End 2025-07-31T00:00:00Z
BBox (-180.00, -90.00, 180.00, 90.00)
TitleDescription
T1y Icechunk repositoryAnnual mean global ocean physics outputs defined at NEMO model T-points.\n", + "\n", + "
Access
catalog.open_dataset(id='noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1y')
Properties
PropertyValue
variantr1i1c1f1
variables
74 items
empmr
e3t
hfempds
hfds
fsitherm
hflx_rnf
hfns
hfrainds
hfls
ficeberg
friver
berg_latent_heat_flux
mlotst
hfss
hfto
hfsr
hfevapds
deptht_bounds
mlotstmax
evs
mlotstsq
ocontempdiff
osaltpmdiff
osaltdiff
mlotstmin
ocontempmint
rlntds
ocontempadvect
osalttend
rsdo
prsn
osaltadvect
ocontemptend
pbo
sbt_con
snow_ai_cea
sbs_abs
rsntds
sfdsi
so_abs
ocontemppmdiff
sohfcisf
somixhgt
sos_abs
sosafldo
soicecov
somint_abs
sohflisf
rsdoabsorb
snowpre
strd_atf_li
sowaflup
sowflisf
strd_bbl_li
thetao_con
somxl010
time_centered_bounds
strd_evd_li
somxzint1
ttrd_evd_li
ttrd_atf_li
sowindsp
time_counter_bounds
tos_con
tossq_con
tnpeo
ttrd_bbl_li
sossq_abs
ttrd_qns_li
zossq
vohflisf
zos
vowflisf
vohfcisf
variable_standard_names
74 items
water_flux_out_of_sea_ice_and_sea_water
cell_thickness
ocean_surface_downward_heat_flux_from_E-P
ocean_surface_downward_total_heat_flux
fsitherm
temperature_flux_due_to_runoff_expressed_as_heat_flux_into_sea_water
surface_net_downward_non_solar_heat_flux
temperature_flux_due_to_rain_expressed_as_heat_flux_into_sea_water
ocean_surface_downward_latent_heat_flux
water_flux_into_sea_water_from_icebergs
water_flux_into_sea_water_from_rivers
latent_heat_flux_from_icebergs
ocean_mixed_layer_thickness_defined_by_sigma_theta
ocean_surface_downward_sensible_heat_flux
surface_net_downward_total_heat_flux
surface_net_downward_solar_heat_flux
temperature_flux_due_to_evaporation_expressed_as_heat_flux_out_of_sea_water
deptht_bounds
ocean_mixed_layer_thickness_defined_by_sigma_theta
water_evaporation_flux
square_of_ocean_mixed_layer_thickness_defined_by_sigma_theta
ocontempdiff
osaltpmdiff
osaltdiff
ocean_mixed_layer_thickness_defined_by_sigma_theta
integral_wrt_depth_of_product_of_density_and_conservative_temperature
ocean_surface_net_downward_longwave_heat_flux
ocontempadvect
osalttend
downwelling_shortwave_flux_in_sea_water
snowfall_flux
osaltadvect
ocontemptend
sea_water_pressure_at_sea_floor
sbt_con
snowfall_flux
sbs_abs
ocean_surface_net_downward_shortwave_heat_flux
downward_sea_ice_basal_salt_flux
sea_water_absolute_salinity
ocontemppmdiff

ocean_mixed_layer_thickness_defined_by_vertical_tracer_diffusivity
sea_surface_salinity
salt_flux_into_sea_water
sea_ice_area_fraction
integral_wrt_depth_of_product_of_density_and_absolute_salinity

rsdoabsorb
snowfall_flux
strd_atf_li
water_flux_out_of_sea_ice_and_sea_water

strd_bbl_li
sea_water_conservative_temperature
ocean_mixed_layer_thickness_defined_by_sigma_theta
time_centered_bounds
strd_evd_li
ocean_mixed_layer_thickness_defined_by_sigma_theta
ttrd_evd_li
ttrd_atf_li
wind_speed
time_counter_bounds
sea_surface_temperature
square_of_sea_surface_temperature
tnpeo
ttrd_bbl_li
square_of_sea_surface_Salinity
ttrd_qns_li
square_of_sea_surface_height_above_geoid

sea_surface_height_above_geoid

dimensions
5 items
time_counter
y
x
deptht
axis_nbounds
operationmean
operation_frequencyannual
ocean_componentNEMO v4.2.2
sea_ice_componentSI3 v4.0
biogeochemistry_componentโ€”
atmosphere_componentโ€”
statusongoing
update_frequencyquarterly
latest_data_update2026-03-22T21:41:49.874907
Assets
KeyMedia TypeEndpointBucketPrefix
T1yapplication/vnd.zarr+icechunkhttps://noc-msm-o.s3-ext.jc.rl.ac.uknpd-eorca1-era5v1T1y
" + ], + "text/plain": [ + "Item: noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1y\n", + " Title: T1y Icechunk repository\n", + " Platform: gn\n", + " Start: 1976-01-01T00:00:00Z\n", + " End: 2025-07-31T00:00:00Z\n", + " BBox: -180.00, -90.00, 180.00, 90.00\n", + "\n", + " Properties:\n", + " variant: r1i1c1f1\n", + " variables: [empmr, e3t, hfempds, hfds, fsitherm, ...]\n", + " variable_standard_names: [water_flux_out_of_sea_ice_and_sea_water, cell_thickness, ocean_surface_downward_heat_flux_from_E-P, ocean_surface_downward_total_heat_flux, fsitherm, ...]\n", + " dimensions: [time_counter, y, x, deptht, axis_nbounds]\n", + " operation: mean\n", + " operation_frequency: annual\n", + " ocean_component: NEMO v4.2.2\n", + " sea_ice_component: SI3 v4.0\n", + " biogeochemistry_component: None\n", + " atmosphere_component: None\n", + " status: ongoing\n", + " update_frequency: quarterly\n", + " latest_data_update: 2026-03-22T21:41:49.874907\n", + "\n", + " Assets:\n", + " T1y: application/vnd.zarr+icechunk โ€” https://noc-msm-o.s3-ext.jc.rl.ac.uk/npd-eorca1-era5v1/T1y\n", + "\n", + " Access: catalog.open_dataset(id='noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1y')" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "catalog.item_summary(id='noc-npd-era5/npd-eorca1-era5v1/r1i1c1f1/gn/T1y')" + ] + }, { "cell_type": "markdown", "id": "122abf01", "metadata": {}, "source": [ - "* Now, let's take a closer look at the first Item in our search results. This corresponds to the annual-mean T-grid variables output by the 1-degree NPD eORCA1 ERA5v1 simulation.\n", + "* Alternatively, we can explore the first Item in our search results by accessing the `Item` attribute of the catalog. This Item corresponds to the annual-mean T-grid variables output by the 1-degree NPD eORCA1 ERA5v1 simulation.\n", "\n", "* By looking in `properties/variable_standard_names`, we can see that 'sea_surface_temperature' is the 59th variable in this dataset and is named `tos_con`." ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "id": "d68bfa0b", "metadata": {}, "outputs": [ @@ -3043,7 +3335,7 @@ "" ] }, - "execution_count": 6, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -3064,7 +3356,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "id": "97a6eee1", "metadata": {}, "outputs": [ @@ -3625,23 +3917,23 @@ "Dimensions without coordinates: y, x, axis_nbounds\n", "Data variables: (12/74)\n", " berg_latent_heat_flux (time_counter, y, x) float32 5MB dask.array<chunksize=(1, 331, 360), meta=np.ndarray>\n", - " deptht_bounds (deptht, axis_nbounds) float32 600B dask.array<chunksize=(25, 2), meta=np.ndarray>\n", - " e3t (time_counter, deptht, y, x) float32 393MB dask.array<chunksize=(1, 25, 331, 360), meta=np.ndarray>\n", - " friver (time_counter, y, x) float32 5MB dask.array<chunksize=(1, 331, 360), meta=np.ndarray>\n", + " hfds (time_counter, y, x) float32 5MB dask.array<chunksize=(1, 331, 360), meta=np.ndarray>\n", " empmr (time_counter, y, x) float32 5MB dask.array<chunksize=(1, 331, 360), meta=np.ndarray>\n", - " ficeberg (time_counter, y, x) float32 5MB dask.array<chunksize=(1, 331, 360), meta=np.ndarray>\n", + " e3t (time_counter, deptht, y, x) float32 393MB dask.array<chunksize=(1, 25, 331, 360), meta=np.ndarray>\n", + " fsitherm (time_counter, y, x) float32 5MB dask.array<chunksize=(1, 331, 360), meta=np.ndarray>\n", + " deptht_bounds (deptht, axis_nbounds) float32 600B dask.array<chunksize=(25, 2), meta=np.ndarray>\n", " ... ...\n", + " ttrd_qns_li (time_counter, y, x) float32 5MB dask.array<chunksize=(1, 331, 360), meta=np.ndarray>\n", " vohflisf (time_counter, deptht, y, x) float32 393MB dask.array<chunksize=(1, 25, 331, 360), meta=np.ndarray>\n", - " vowflisf (time_counter, deptht, y, x) float32 393MB dask.array<chunksize=(1, 25, 331, 360), meta=np.ndarray>\n", + " zos (time_counter, y, x) float32 5MB dask.array<chunksize=(1, 331, 360), meta=np.ndarray>\n", " vohfcisf (time_counter, deptht, y, x) float32 393MB dask.array<chunksize=(1, 25, 331, 360), meta=np.ndarray>\n", - " ttrd_bbl_li (time_counter, deptht, y, x) float32 393MB dask.array<chunksize=(1, 25, 331, 360), meta=np.ndarray>\n", - " zossq (time_counter, y, x) float32 5MB dask.array<chunksize=(1, 331, 360), meta=np.ndarray>\n", - " ttrd_qns_li (time_counter, y, x) float32 5MB dask.array<chunksize=(1, 331, 360), meta=np.ndarray>