Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Large Area Processing

ESA

This notebook shows how you can run larger scale analysis on the cube. You will have to update the code for your own use case.

Since the cube is too large to load into memory, the notebook uses Dask reductions, preserving the native Zarr chunks, and returns small tables.

This notebook is designed to run on a cloud workspace where Dask Gateway is deployed and configured. The Gateway cells below will raise ValueError: No dask-gateway address provided or found in configuration on a local machine or any environment without Dask Gateway configuration.

If you are running locally, skip the Dask Gateway cells and run the local Dask cluster alternative instead. Local runs may need a smaller region or fewer datasets depending on your machine.

import dask.array as da
import dask.distributed
import warnings
import numpy as np
import xarray as xr
from dask.array.core import PerformanceWarning
from dask_gateway import Gateway

warnings.filterwarnings("ignore", message="In a future version of xarray the default value for join.*", category=FutureWarning)
warnings.filterwarnings("ignore", message="Increasing number of chunks.*", category=PerformanceWarning)
## run this line if you get a file system not found error!
# import os
# os.chdir("/tmp")
# print("cwd:", os.getcwd())

Dask Cluster Setup

Use the Dask Gateway cells on the cloud workspace. For local runs, skip the Gateway cells and run the local alternative cell instead.

# Local alternative: run this cell instead of the Dask Gateway cells below.
cluster = dask.distributed.LocalCluster(
    n_workers=2,
    threads_per_worker=2,
    memory_limit="4GB",
)
client = dask.distributed.Client(cluster)
client
Loading...

Cloud Workspace

gateway = Gateway()
cluster_options = gateway.cluster_options()
cluster_options
cluster = gateway.new_cluster(cluster_options=cluster_options)
cluster.scale(2)
cluster
client = cluster.get_client()
client

Connect with Data Cube

cube_paths = [
    "https://s3.waw4-1.cloudferro.com/EarthCODE/OSCAssets/antarctica_cube/icetemp.zarr",
    "https://s3.waw4-1.cloudferro.com/EarthCODE/OSCAssets/antarctica_cube/sec.zarr",
    "https://s3.waw4-1.cloudferro.com/EarthCODE/OSCAssets/antarctica_cube/antarctica-combined.zarr",
    "https://s3.waw4-1.cloudferro.com/EarthCODE/OSCAssets/antarctica_cube/icemask_composite.zarr/",
    "https://s3.waw4-1.cloudferro.com/EarthCODE/OSCAssets/antarctica_cube/ice_velocity.zarr",
]

ds = xr.open_mfdataset(cube_paths, engine="zarr", chunks={}, compat="no_conflicts")
ds
Loading...
dx = abs((ds.x.isel(x=1) - ds.x.isel(x=0)).load().item())
dy = abs((ds.y.isel(y=1) - ds.y.isel(y=0)).load().item())
km2 = dx * dy / 1e6

ds[[
    "bedrock_topography_mask",
    "bedrock_topography_bed",
    "bedrock_topography_thickness",
    "bedrock_topography_errbed",
    "ice_shelf_basal_melt_rate",
    "groundlines_mask",
    "subglacial_lakes_mask",
    "supra_glacial_lakes_mask",
]]
Loading...

Area of the main surface classes

Mask codes: 0 ocean, 1 ice-free land, 2 grounded ice, 3 floating ice, 4 Lake Vostok.

mask = ds["bedrock_topography_mask"]

surface_area = xr.Dataset({
    "ocean": (mask == 0).sum() * km2,
    "ice_free_land": (mask == 1).sum() * km2,
    "grounded_ice": (mask == 2).sum() * km2,
    "floating_ice": (mask == 3).sum() * km2,
    "lake_vostok": (mask == 4).sum() * km2,
}).compute()

surface_area.to_array().to_series().rename("area_km2").to_frame()
Loading...

Example 1: marine-based grounded ice exposure

This estimates how much grounded ice sits on bed below sea level, including deep basins below -1000 m.

bed = ds["bedrock_topography_bed"]
thickness = ds["bedrock_topography_thickness"]
err = ds["bedrock_topography_errbed"]
marine = (mask == 2) & (bed < 0)

marine_summary = xr.Dataset({
    "grounded_ice_bed_below_0m_km2": marine.sum() * km2,
    "grounded_ice_bed_below_minus_1000m_km2": (marine & (bed < -1000)).sum() * km2,
    "mean_bed_elevation_m": bed.where(marine).mean(),
    "mean_ice_thickness_m": thickness.where(marine).mean(),
    "mean_bed_error_m": err.where(marine).mean(),
}).compute()

marine_summary.to_array().to_series().rename("value").to_frame()
Loading...

Example 2: floating-ice basal melt and a 5 km grounding-zone buffer

from scipy.ndimage import binary_dilation

melt = ds["ice_shelf_basal_melt_rate"]
floating = (mask == 3) & melt.notnull()

gl5 = da.map_overlap(
    binary_dilation,
    ds["groundlines_mask"].data.astype(bool),
    depth=5,
    boundary=False,
    dtype=bool,
    structure=np.ones((11, 11), bool),
)
grounding_zone = xr.DataArray(gl5, coords=mask.coords, dims=mask.dims) & floating

melt_summary = xr.Dataset({
    "floating_ice_with_melt_data_km2": floating.sum() * km2,
    "floating_ice_mean_melt_m": melt.where(floating).mean(),
    "floating_ice_net_melt_km3_per_dataset_unit": melt.where(floating).sum() * km2 / 1000,
    "grounding_zone_5km_area_km2": grounding_zone.sum() * km2,
    "grounding_zone_5km_mean_melt_m": melt.where(grounding_zone).mean(),
}).compute()

melt_summary.to_array().to_series().rename("value").to_frame()
Loading...
client.close()