Skip to content

Zarr v3 Write Example (C)

This example demonstrates writing a 2D sea surface temperature (SST) field (360 × 180: lon × lat) to a Zarr v3 store using the NCZarr fallback mode.

What It Does

  1. Initializes AMIO with a manifest targeting the zarr3 backend
  2. Opens a dataset for writing
  3. Populates a 2D double-precision array with synthetic SST data
  4. Writes the array through AMIO's async pipeline
  5. Flushes and closes

Manifest

examples/manifests/zarr3_manifest.yaml
amio:
  version: "0.1.0"
  staging:
    buffer_count: 4
    buffer_size_bytes: 20971520   # 20 MiB per buffer
  workers:
    thread_count: 2
    pin_threads: false
  backend:
    name: "zarr3"
    options:
      mode: "nczarr"
      compression:
        algorithm: "zstd"
        level: 3
      chunking:
        auto: true
  output:
    path: "output_zarr.zarr"
    overwrite: true

Source Code

examples/c/example_zarr_write.c
/**
 * @file example_zarr_write.c
 * @brief Example: Write a 2D sea surface temperature field to Zarr v3 using AMIO.
 *
 * This example demonstrates writing a 2D double-precision array
 * (sea surface temperature, 360 x 180: lon x lat) to a Zarr v3 store
 * using the NCZarr fallback mode.
 *
 * Steps:
 *   1. Initialize AMIO with a manifest targeting the zarr3 backend
 *   2. Open a dataset for writing
 *   3. Populate a 2D double array with synthetic SST data
 *   4. Write the array through AMIO's async pipeline
 *   5. Flush and close
 *   6. Finalize AMIO
 *
 * Build:
 *   cc -o example_zarr_write example_zarr_write.c -lamio -lm
 *
 * Run:
 *   ./example_zarr_write
 */

#include <amio/amio.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Grid dimensions: 1-degree global ocean grid */
#define NLON 360 /* longitude points (0 to 359) */
#define NLAT 180 /* latitude points (-90 to +89) */

/**
 * @brief Check an AMIO return code and exit on failure.
 */
static void check_amio(amio_status_t rc, const char *context) {
    if (rc != AMIO_OK) {
        fprintf(stderr, "AMIO error in %s: %s (code %d)\n", context, amio_strerror(rc), (int)rc);
        exit(EXIT_FAILURE);
    }
}

/**
 * @brief Generate synthetic sea surface temperature data.
 *
 * Creates a realistic SST pattern:
 *   - Warm equatorial waters (~300 K / 27°C)
 *   - Cold polar waters (~271 K / -2°C)
 *   - Western boundary current warm anomalies
 *   - Land mask represented as NaN (not-a-number)
 */
static void fill_sst(double *data, int nlon, int nlat) {
    for (int j = 0; j < nlat; j++) {
        double lat = -90.0 + (double)j + 0.5; /* cell center */
        /* Base SST: warm at equator, cold at poles */
        double base_sst = 300.0 - 29.0 * (lat * lat) / (90.0 * 90.0);
        for (int i = 0; i < nlon; i++) {
            double lon = (double)i + 0.5; /* cell center */
            double sst = base_sst;
            /* Add western boundary current warm anomaly (Gulf Stream-like) */
            if (lon > 280.0 && lon < 320.0 && lat > 25.0 && lat < 45.0) {
                sst += 3.0 * exp(-0.01 * (lon - 300.0) * (lon - 300.0));
            }
            /* Add small random-like perturbation using sin */
            sst += 0.5 * sin(lon * 0.1) * cos(lat * 0.15);
            /* Clamp to physical range (freezing point to ~35°C) */
            if (sst < 271.15) sst = 271.15;
            if (sst > 308.15) sst = 308.15;
            data[j * nlon + i] = sst;
        }
    }
}

int main(void) {
    amio_status_t rc;
    amio_core_handle core = NULL;
    amio_dataset_handle dataset = NULL;
    amio_io_handle io = NULL;

    printf("AMIO Zarr v3 (NCZarr) Write Example\n");
    printf("====================================\n");
    printf("Writing 2D SST field (%d x %d) to Zarr v3 store\n\n", NLON, NLAT);

    /* ---------------------------------------------------------------
     * Step 1: Initialize AMIO with the zarr3 manifest.
     *
     * The zarr3 manifest configures NCZarr fallback mode, which uses
     * the system netCDF-c library's NCZarr implementation to write
     * Zarr v3-compatible stores.
     * --------------------------------------------------------------- */
    printf("Step 1: Initializing AMIO (Zarr v3 / NCZarr mode)...\n");
    rc = amio_init("examples/manifests/zarr3_manifest.yaml", &core);
    check_amio(rc, "amio_init");
    printf("  AMIO initialized.\n");

    /* ---------------------------------------------------------------
     * Step 2: Open a dataset for writing.
     * --------------------------------------------------------------- */
    printf("Step 2: Opening Zarr dataset for writing...\n");
    rc = amio_open_dataset(core, "examples/manifests/zarr3_manifest.yaml", AMIO_MODE_WRITE, &dataset);
    check_amio(rc, "amio_open_dataset");
    printf("  Dataset opened.\n");

    /* ---------------------------------------------------------------
     * Step 3: Allocate and fill the SST array.
     * --------------------------------------------------------------- */
    printf("Step 3: Generating synthetic SST data...\n");
    double *sst = (double *)malloc(NLON * NLAT * sizeof(double));
    if (!sst) {
        fprintf(stderr, "Failed to allocate SST array\n");
        return EXIT_FAILURE;
    }
    fill_sst(sst, NLON, NLAT);
    printf("  SST data generated: range ~ [271.2, 308.1] K\n");

    /* ---------------------------------------------------------------
     * Step 4: Write the 2D SST array.
     *
     * Shape descriptor for a 2D array:
     *   - rank = 2
     *   - extents = {NLON, NLAT}
     *   - strides = {0, 0} (contiguous)
     *   - dtype = AMIO_DTYPE_F64 (double precision)
     * --------------------------------------------------------------- */
    printf("Step 4: Writing SST field...\n");
    amio_shape_t shape;
    memset(&shape, 0, sizeof(shape));
    shape.rank = 2;
    shape.extents[0] = NLON;
    shape.extents[1] = NLAT;

    rc = amio_write(dataset, "sea_surface_temperature", sst, AMIO_DTYPE_F64, &shape, &io);
    check_amio(rc, "amio_write(sea_surface_temperature)");
    printf("  Write enqueued.\n");

    /* ---------------------------------------------------------------
     * Step 5: Flush to ensure the write completes.
     * --------------------------------------------------------------- */
    printf("Step 5: Flushing...\n");
    rc = amio_flush(dataset, 0);
    check_amio(rc, "amio_flush");
    printf("  Flush complete.\n");

    /* ---------------------------------------------------------------
     * Step 6: Close and finalize.
     * --------------------------------------------------------------- */
    printf("Step 6: Closing dataset and finalizing...\n");
    rc = amio_close_dataset(dataset);
    check_amio(rc, "amio_close_dataset");

    rc = amio_finalize(core);
    check_amio(rc, "amio_finalize");
    printf("  Done.\n");

    free(sst);

    printf("\nSST field written to Zarr v3 store.\n");
    return EXIT_SUCCESS;
}
Inline source (if includes are not configured)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <amio/amio.h>

#define NLON 360
#define NLAT 180

int main(void)
{
    amio_core_handle core = NULL;
    amio_dataset_handle dataset = NULL;
    amio_io_handle io = NULL;

    amio_status_t rc = amio_init(
        "examples/manifests/zarr3_manifest.yaml", &core);

    rc = amio_open_dataset(core,
        "examples/manifests/zarr3_manifest.yaml",
        AMIO_MODE_WRITE, &dataset);

    double *sst = malloc(NLON * NLAT * sizeof(double));
    // ... fill with synthetic SST data ...

    amio_shape_t shape = {0};
    shape.rank = 2;
    shape.extents[0] = NLON;
    shape.extents[1] = NLAT;

    rc = amio_write(dataset, "sea_surface_temperature", sst,
                    AMIO_DTYPE_F64, &shape, &io);

    rc = amio_flush(dataset, 0);
    rc = amio_close_dataset(dataset);
    rc = amio_finalize(core);
    free(sst);

    return EXIT_SUCCESS;
}

Key Points

NCZarr Fallback

When TensorStore is not available at build time, AMIO uses the system netCDF-c library's NCZarr implementation. This mode supports local file output but not cloud storage or Zarr v3 sharding.

Double Precision

This example uses AMIO_DTYPE_F64 (double) for the SST field. The shape descriptor is the same regardless of element type — only the dtype argument to amio_write changes.

Build

cc -o example_zarr_write example_zarr_write.c -lamio -lm
./example_zarr_write