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.

Convert NetCDF To Zarr - Example 2

ESA

This notebook shows a more detailed example of how the so-fresh data was processed;

This notebook is not runnable

This notebook is a direct copy of the example post-processing of a dataset. Since the data requires custom download configurations and longer processing - this notebook is mainly a showcase of transforming nc files to zarr.

# 1. Define your connection info
host = "eodata-bec.icm.csic.es"
port = 25288 
username = ""
password = ""
import paramiko
# 2. Initialize the SSH client
ssh = paramiko.SSHClient()

# Automatically add the server's host key (prevents "unknown host" errors)
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# Connect to the server
ssh.connect(hostname=host, port=port, username=username, password=password)

# Open the SFTP session on the SSH connection
sftp = ssh.open_sftp()
print(f"Successfully connected to {host} via SFTP!\n")

# 3. List the files in the current directory
print("Files currently on the server:")
server_files = sftp.listdir() # listdir() returns a list of file names
for file_name in server_files:
    print(f" - {file_name}")
print("\n")
Successfully connected to eodata-bec.icm.csic.es via SFTP!

Files currently on the server:
 - LAND
 - OCEAN


root_path = 'OCEAN/SSS/SMOS/SouthernOcean/v1.0/L3/9day/'
import os

for folder in sorted(sftp.listdir(root_path)):
    if folder.isalnum() and folder != '2011':
        os.mkdir(f'./data/{folder}/')
        print('Processing year :' + folder)
        print('Downloading : ', len(sorted(sftp.listdir(root_path + folder))))
        for f in sorted(sftp.listdir(root_path + folder)):
            sftp.get(root_path + folder + '/' + f, f'./data/{folder}/{f}')
Processing year :2012
Downloading :  366
Processing year :2013
Downloading :  365
Processing year :2014
Downloading :  365
Processing year :2015
Downloading :  365
Processing year :2016
Downloading :  366
Processing year :2017
Downloading :  365
Processing year :2018
Downloading :  365
Processing year :2019
Downloading :  365
Processing year :2020
Downloading :  366
Processing year :2021
Downloading :  365
Processing year :2022
Downloading :  365
Processing year :2023
Downloading :  94
import xarray as xr
xr.open_dataset('./data/2011/BEC_SSS___SMOS__SO__L3__B_20110208T120000_25km__9d_REP_v100.nc')
Loading...

Merge data into zarr

import re
import numpy as np
import xarray as xr
from typing import Iterable, List, Sequence
from datetime import datetime, timedelta
from pathlib import Path

FILENAME_RE = re.compile(r"_(\d{8})T120000_")


input_root = Path('./data/')
files = sorted(input_root.glob("[0-9][0-9][0-9][0-9]/*.nc"))
len(files)
4441
def file_center_datetime(file_path: Path) -> datetime:
    match = FILENAME_RE.search(file_path.name)
    if not match:
        raise ValueError(f"Cannot parse date from filename: {file_path.name}")
    return datetime.strptime(match.group(1), "%Y%m%d")


def build_time_bounds(files: Iterable[Path]) -> np.ndarray:
    starts: List[np.datetime64] = []
    ends: List[np.datetime64] = []
    for f in files:
        center = file_center_datetime(f)
        start = center
        end = center + timedelta(days=8, hours=23, minutes=59, seconds=59)
        starts.append(np.datetime64(start, "ns"))
        ends.append(np.datetime64(end, "ns"))
    return np.stack([starts, ends], axis=1)


def open_dataset(
    files: Sequence[Path],
    t_chunk: int = 10,
    y_chunk: int = 720,
    x_chunk: int = 720,
    parallel: bool = True,
    engine: str = 'netcdf4',
) -> xr.Dataset:
    ds = xr.open_mfdataset(
        [str(f) for f in files],
        combine="nested",
        concat_dim="time",
        data_vars="minimal",
        coords="minimal",
        compat="override",
        parallel=parallel,
        engine=engine,
        chunks={"time": 1, "y": 720, "x": 720},
    ).sortby("time")

    ds = ds.chunk({"time": t_chunk, "y": y_chunk, "x": x_chunk})

    time_bnds = build_time_bounds(files)
    ds["time_bnds"] = xr.DataArray(
        time_bnds,
        dims=("time", "nv"),
        attrs={
            "long_name": "time bounds",
        },
    )
    ds["time"].attrs["bounds"] = "time_bnds"
    return ds
ds = open_dataset(files=files)
ds.attrs['time_coverage_end'] = ds.time.values[-1]
ds
Loading...
outputdir = 'sofresh.zarr'
ds.to_zarr(
    outputdir,
    mode='w',
    zarr_format = 3,
    consolidated=True

)
/home/krasen/data/sofresh/.pixi/envs/default/lib/python3.14/site-packages/zarr/api/asynchronous.py:247: ZarrUserWarning: Consolidated metadata is currently not part in the Zarr format 3 specification. It may not be supported by other zarr implementations and may change in the future.
  warnings.warn(
<xarray.backends.zarr.ZarrStore at 0x7daa9e4bc400>

Test values are all the same

ds = xr.open_zarr(outputdir)
ds
rnd_file = np.random.choice(files[:1000], size=1)[0]
orig = xr.open_dataset(rnd_file)
orig
Loading...
assert np.isclose(ds.sel(time=orig.time.values[0]).sss.values, orig.sss.values, equal_nan=True).all()
assert np.isclose(ds.sel(time=orig.time.values[0]).uncertainty_sss.values, orig.uncertainty_sss.values, equal_nan=True).all()