Skip to content

k202077/ushow

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

102 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ushow - Unstructured Data Viewer

The ushow command uses X11 to display data: usow_example

The uterm command displays data in the terminal using ASCII or UTF characters. uterm

A fast, ncview‑inspired visualization tool for structured and unstructured geoscientific data.

Features

  • Multiple formats: Supports netCDF, zarr, GRIB, and MITgcm binary (MDS)
  • Unified data handling: Treats all data as collections of points with lon/lat coordinates
  • Fast visualization: KDTree-based nearest-neighbor interpolation to regular grid
  • X11/Xaw interface: Works over SSH with X forwarding, dark/light theme
  • Terminal quick-look mode: Separate uterm binary with raw terminal interaction (no X is needed)
  • Animation support: Step through time dimensions
  • Multiple colormaps: viridis, hot, grayscale, plus the full cmocean set

Building

Requirements:

  • NetCDF-C library
  • eccodes for GRIB support (can be build without)
  • X11 development libraries (libX11, libXt, libXaw, libXmu, libXext, libSM, libICE)
  • C compiler (gcc or clang)

macOS (Homebrew + XQuartz)

Install dependencies:

brew install netcdf

For optional Zarr support:

brew install c-blosc lz4

For optional GRIB support:

brew install eccodes

For optional YAC interpolation support (professional-grade interpolation methods):

brew install open-mpi autoconf automake libtool

Then build YAXT and YAC from source (see YAC Interpolation below).

Install XQuartz for X11 support. After installation, the X11 libraries will be in /opt/X11.

Build:

make                  # Without zarr/grib support
make WITH_ZARR=1      # With zarr support
make WITH_GRIB=1      # With grib support
make WITH_YAC=1       # With YAC interpolation support
make uterm            # Build terminal viewer only

The Makefile auto-detects XQuartz at /opt/X11.

Linux (Debian/Ubuntu)

Install dependencies:

sudo apt-get install libnetcdf-dev libx11-dev libxt-dev libxaw7-dev libxmu-dev libxext-dev

For optional Zarr support:

sudo apt-get install libblosc-dev liblz4-dev

For optional GRIB support:

sudo apt-get install libeccodes-dev

For optional YAC interpolation support:

sudo apt-get install libopenmpi-dev autoconf automake libtool

Then build YAXT and YAC from source (see YAC Interpolation below).

Build:

make                  # Without zarr/grib support
make WITH_ZARR=1      # With zarr support
make WITH_GRIB=1      # With grib support
make WITH_YAC=1       # With YAC interpolation support
make uterm            # Build terminal viewer only

DKRZ Levante

On Levante, the Makefile automatically uses the DKRZ spack-installed libraries:

  • NetCDF-C from /sw/spack-levante/netcdf-c-4.8.1-qk24yp
  • X11 libraries from /sw/spack-levante/libx*
  • c-blosc and lz4 for Zarr support
  • eccodes for GRIB support

For basic builds (without YAC), no modules need to be loaded:

make                  # Without zarr/grib support
make WITH_ZARR=1      # With zarr support (uses system blosc/lz4)
make WITH_GRIB=1      # With grib support (uses system eccodes)

For YAC support, load the OpenMPI module (provides mpicc) and build YAXT/YAC first (see YAC Interpolation):

module load openmpi/4.1.2-gcc-11.2.0
make WITH_YAC=1       # With YAC interpolation support

Full build with all optional features:

module load openmpi/4.1.2-gcc-11.2.0
make WITH_GRIB=1 WITH_YAC=1 WITH_ZARR=1

The binary will have the library paths embedded (via rpath), so it runs without setting LD_LIBRARY_PATH.

YAC Interpolation

YAC (Yet Another Coupler) provides professional-grade interpolation methods beyond the built-in nearest-neighbor. It requires building two libraries from source: YAXT and YAC.

On DKRZ Levante, load the OpenMPI module first:

module load openmpi/4.1.2-gcc-11.2.0

1. Build YAXT

Get YAXT source from https://swprojects.dkrz.de/redmine/projects/yaxt (or git clone https://gitlab.dkrz.de/dkrz-sw/yaxt.git).

cd /path/to/yaxt
scripts/reconfigure
mkdir build && cd build
../configure \
  MPI_LAUNCH="mpirun --map-by socket:OVERSUBSCRIBE" \
  --prefix=$HOME/local/yaxt \
  --disable-shared \
  CC=mpicc FC=mpif90
make -j4
make install

On macOS, a cosmetic date: illegal time format warning during scripts/reconfigure can be ignored.

2. Build YAC

Get YAC source from https://gitlab.dkrz.de/dkrz-sw/yac (version 3.14.0 or later).

cd /path/to/yac
./autogen.sh
./configure \
  --prefix=$HOME/local/yac \
  --with-yaxt-root=$HOME/local/yaxt \
  --disable-mci --disable-utils --disable-examples \
  --disable-tools --disable-fortran-bindings \
  CC=mpicc
make -j4
make install

On macOS, if configure fails with a shell error, prefix the command with CONFIG_SHELL=/bin/bash /bin/bash.

3. Build ushow with YAC

make clean
make WITH_YAC=1                              # Auto-detects at ~/local/yac
make WITH_YAC=1 YAC_PREFIX=/custom/path/yac  # Custom install location

The Makefile uses pkg-config to find YAC, with a fallback to YAC_PREFIX (default: $HOME/local/yac).

AWI Albedo

On Albedo, dependencies are provided via environment modules and spack. First load spack and the NetCDF module:

module load spack
module load netcdf-c/4.8.1-gcc12.1.0

Load the X11 development libraries from spack:

spack load /eub564f /gyimrqa /dp6g46v /aioyu3n /mxnurir /l6kzj5s /x75vrux

Basic build:

make

For optional Zarr support, load c-blosc and lz4 (c-blosc may need to be installed into your home directory first with spack install c-blosc%gcc@12.1.0):

spack load c-blosc%gcc@12.1.0
spack load /ahjcumd   # lz4

For optional GRIB support, load eccodes:

spack load /fi5kc7g   # eccodes 2.34.0

Build with Zarr and GRIB support (explicit paths needed because the Makefile's auto-detection does not cover Albedo):

make WITH_ZARR=1 WITH_GRIB=1 \
  ZARR_CFLAGS="-DHAVE_ZARR -I$HOME/.spack/sw/c-blosc/1.21.5-rrsl7wt/include -I/albedo/soft/sw/spack-sw/lz4/1.9.3-ahjcumd/include" \
  ZARR_LIBS="-L$HOME/.spack/sw/c-blosc/1.21.5-rrsl7wt/lib64 -L/albedo/soft/sw/spack-sw/lz4/1.9.3-ahjcumd/lib -lblosc -llz4 -Wl,-rpath,$HOME/.spack/sw/c-blosc/1.21.5-rrsl7wt/lib64 -Wl,-rpath,/albedo/soft/sw/spack-sw/lz4/1.9.3-ahjcumd/lib"

The binary will have library paths embedded via rpath, so no LD_LIBRARY_PATH is needed at runtime.

Custom Library Paths

If your libraries are in non-standard locations, you can override the detection:

# Custom nc-config location
make NC_CONFIG=/path/to/nc-config

# Custom X11 prefix (libraries in $X11_PREFIX/lib, headers in $X11_PREFIX/include)
make X11_PREFIX=/path/to/x11

# Both
make NC_CONFIG=/path/to/nc-config X11_PREFIX=/path/to/x11

Verifying the Build

After building, verify all libraries are found:

ldd ./ushow          # Linux
otool -L ./ushow     # macOS

No libraries should show as "not found".

Usage

./ushow [options] <data_file.nc|data.zarr|data.grib|mitgcm_dir> [file2 ...]

Options:
  -m, --mesh <file>      Mesh file with coordinates (for unstructured data)
  -r, --resolution <deg> Target grid resolution in degrees (default: 1.0)
  -i, --influence <m>    Influence radius in meters (default: 80000)
  -d, --delay <ms>       Animation frame delay in milliseconds (default: 200)
  -p, --polygon-only     Skip regridding, use polygon mode only (faster)
  --box W,E,S,N          Regional box (e.g. --box -10,30,35,70 for Europe)
  --polar <pole>         Polar LAEA projection (north or south)
  --cutoff <deg>         Cutoff latitude for polar view (default: 60)
  --light                Use light theme (default: dark)
  --yac                  Use YAC interpolation with default method (avg_arith)
  --yac-method <method>  Use YAC interpolation with specific method;
                         click the method button in the GUI to cycle methods at runtime
  --yac-3d               Per-depth masked interpolation for 3D variables
  -h, --help             Show help message

Terminal quick-look mode:

./uterm [options] <data_file.nc|data.zarr|data.grib> [file2 ...]

Options (uterm):
  -m, --mesh <file>      Mesh file with coordinates
  -r, --resolution <deg> Target grid resolution in degrees (default: 1.0)
  -i, --influence <m>    Influence radius in meters (default: 80000)
  -d, --delay <ms>       Animation frame delay in milliseconds (default: 200)
  --chars <ramp>     ASCII ramp (default: " .:-=+*#%@")
  --render <mode>    Render mode: ascii | half | braille
  --color            Force ANSI color output
  --no-color         Disable ANSI color output
  --box W,E,S,N      Regional box (e.g. --box -10,30,35,70)
  --polar <pole>     Polar LAEA projection (north or south)
  --cutoff <deg>     Cutoff latitude for polar view (default: 60)
  --yac              Use YAC interpolation (default: avg_arith)
  --yac-method <m>   Use YAC interpolation method (requires WITH_YAC=1)
  --yac-3d           Per-depth masked interpolation for 3D variables
  -h, --help             Show help

Examples

FESOM unstructured data with separate mesh file:

./ushow temp.fesom.1964.nc -m fesom.mesh.diag.nc

Standard NetCDF with embedded coordinates:

./ushow sst.nc

Higher resolution display:

./ushow data.nc -r 0.5  # 0.5 degree grid (720x360)

Multi-file time concatenation (NetCDF):

./ushow "temp.fesom.*.nc" -m mesh.nc   # Glob pattern
./ushow file1.nc file2.nc -m mesh.nc   # Explicit files

Zarr store (requires make WITH_ZARR=1):

./ushow data.zarr                      # Single zarr store
./ushow "data_*.zarr"                  # Multiple zarr stores (time concat)
./ushow data.zarr -r 0.25              # Higher resolution display

GRIB file (requires make WITH_GRIB=1):

./ushow data.grib                      # Single GRIB file
./uterm data.grib --color

MITgcm binary data (MDS format, always compiled — no extra libraries):

./ushow /path/to/mitgcm/run           # Open directory with .data/.meta files
./ushow /path/to/diags3D.0000008760.data  # Open specific .data file (uses parent dir)

MPAS unstructured data:

./ushow data.nc grid.nc                # MPAS UGRID (face_lon/face_lat)
./ushow mpas_output.nc                 # Native MPAS (lonCell/latCell)
./ushow mpas_output.nc --yac -r 0.1    # MPAS with YAC interpolation (uses native cellsOnVertex connectivity)

YAC interpolation (requires make WITH_YAC=1):

./ushow temp.nc -m mesh.nc --yac                    # Default avg_arith, cycle in GUI
./ushow temp.nc -m mesh.nc --yac-method nnn4dist    # 4-NN distance-weighted
./ushow temp.nc -m mesh.nc --yac-method nnn4gauss   # 4-NN Gaussian-weighted
./ushow temp.nc -m mesh.nc --yac-method avg_arith   # Cell averaging
./ushow temp.nc -m mesh.nc --yac-3d                 # Per-depth masking for 3D ocean data
./ushow temp.nc -m mesh.nc --yac-3d --yac-method nnn4dist  # Combine with specific method

Zarr store with consolidated metadata (faster loading):

# Zarr stores with .zmetadata file are loaded more efficiently
./ushow output.zarr                    # Auto-detects consolidated metadata

Regional box (restrict view to a geographic region):

./ushow data.nc --box -10,30,35,70               # Europe/Mediterranean
./ushow data.nc --box -80,0,20,60                 # North Atlantic
./uterm data.nc --box 120,290,-30,30 --color      # Tropical Pacific

Polar projections (Lambert Azimuthal Equal-Area):

./ushow data.nc --polar north                     # Arctic, default 60° cutoff
./ushow data.nc --polar south                     # Antarctic, default 60° cutoff
./ushow data.nc --polar north --cutoff 50         # Wider view (down to 50°N)
./uterm data.nc --polar south --cutoff 70 --color # Narrow Antarctic view
./ushow data.nc --polar north -r 0.5              # High-res polar view

Terminal mode examples:

./uterm temp.fesom.1964.nc -m fesom.mesh.diag.nc
./uterm data.zarr --color
./uterm "temp.fesom.*.nc" -m mesh.nc -d 120
./uterm data.nc --render half
./uterm data.nc --render braille --color

Testing

Run the test suite:

make test

Clean test binaries:

make test-clean

The test suite includes:

  • test_kdtree: Spatial indexing and nearest-neighbor queries
  • test_mesh: Coordinate transformations (lon/lat to Cartesian)
  • test_regrid: Interpolation to regular grids
  • test_projection: LAEA projection math, regional box, and polar grid tests
  • test_colormaps: Color mapping functions
  • test_term_render_mode: Terminal render mode parsing/cycling helpers
  • test_range_popup: Range popup logic (symmetric computation, value parsing)
  • test_timeseries: Time series reading, multi-file concatenation, and CF time unit conversion
  • test_file_netcdf: NetCDF file I/O
  • test_file_mitgcm: MITgcm MDS binary file I/O
  • test_file_zarr: Zarr file I/O (when built with WITH_ZARR=1)
  • test_yac_3d: Per-depth masked YAC interpolation (when built with WITH_YAC=1)
  • test_yac_click: Time series click-to-node lookup in YAC mode (when built with WITH_YAC=1)
  • test_integration: End-to-end workflow tests

Interface

  • Variable buttons: Click to select which variable to display
  • Animation controls:
    • < Back: Step backward one timestep
    • || Pause: Stop animation
    • Fwd >: Start forward animation
  • Time/Depth sliders: Navigate through dimensions
  • Colormap button: Click to cycle through colormaps
  • Min-/Min+/Max-/Max+: Adjust display range in 10% steps
  • Range button: Opens a popup dialog to set the display range explicitly:
    • Minimum/Maximum: Editable text fields for exact values
    • Symmetric about Zero: Sets range to [-max(|min|,|max|), max(|min|,|max|)]
    • Reset to Global Values: Restores the variable's full data range
  • Time series plot: Click on the image to show a time series popup at that location:
    • Displays value vs time for the selected variable at the clicked grid point
    • Y-axis with numeric tick labels, X-axis with CF time date formatting (when detected)
    • Blue data line with dots at data points; gaps shown for fill/missing values
    • Works with both single files and multi-file datasets
    • Works in both KDTree and YAC interpolation modes (--yac, --yac-3d)
    • When files have different time epochs, values are automatically normalized to a common reference
  • Render mode / YAC method button: Normally toggles Interp/Polygon mode. When YAC is active (--yac-method), cycles through interpolation methods instead: avg_arith -> avg_dist -> avg_bary -> nnn1 -> nnn4dist -> nnn4gauss
  • Dimension panel: Shows dimension names, ranges, current values
  • Colorbar: Min/max and intermediate labels update as you adjust range

Terminal Controls (uterm)

  • q: quit
  • space: pause/resume animation
  • j / k: previous/next time step
  • u / i: previous/next depth level
  • n / p: next/previous variable
  • 1..9: direct variable select (first 9 variables)
  • c / C: next/previous colormap
  • m: cycle render mode (ascii -> half -> braille)
  • [ / ]: decrease/increase display minimum
  • { / }: decrease/increase display maximum
  • r: reset min/max to estimated global range
  • s: save current frame as PPM (<var>_t<time>_d<depth>.ppm)
  • ?: toggle extended help line

Interpolation Methods

By default, ushow uses a fast KDTree nearest-neighbor lookup to map unstructured data onto a regular grid. With YAC support (make WITH_YAC=1), additional interpolation methods are available via --yac-method. Once launched, click the method button in the GUI to cycle through methods at runtime:

Method Description
nnn1 Single nearest neighbor (similar to default KDTree)
nnn4dist 4 nearest neighbors, distance-weighted
nnn4gauss 4 nearest neighbors, Gaussian-weighted
avg_arith Cell averaging, arithmetic mean
avg_dist Cell averaging, distance-weighted
avg_bary Cell averaging, barycentric

NNN methods work with any grid type. Averaging methods require element connectivity — for grids that lack it (reduced Gaussian, HEALPix, etc.), ushow auto-generates a triangulation from latitude bands. For MPAS grids, native dual-mesh connectivity (cellsOnVertex) is read directly from the file.

Per-Depth Masking (--yac-3d)

For 3D ocean variables (e.g., temperature at 50 depth levels), deeper levels have more land masking — fewer valid source points. Without --yac-3d, a single interpolation is used for all depths, so fill values from land points can bleed into the result at deeper levels.

With --yac-3d, ushow builds a separate masked interpolation for each depth level lazily (on first visit). Each level only uses valid (non-fill) source points, producing clean coastlines at all depths. Surface levels that have no masking reuse the base interpolation with no overhead.

The first visit to each new depth level builds a masked interpolation (timing depends on grid size); subsequent visits are instant (cached). The cache is cleared automatically when you switch variables or cycle interpolation methods.

Data Flow

  1. Load mesh coordinates (from mesh file or data file)
  2. Convert lon/lat to Cartesian coordinates on unit sphere
  3. Build KDTree from source points (one-time), or compute YAC interpolation weights
  4. For each target grid cell, find nearest source point (one-time)
  5. Per frame: read data slice, apply regrid indices, convert to pixels

Supported Data Formats

  • NetCDF: Full support (NetCDF-3 and NetCDF-4)
  • MITgcm MDS: Binary .data/.meta file pairs (always compiled, no extra libraries)
    • Reads grid coordinates from XC/YC binary files
    • Supports 2D and 3D diagnostic fields with multiple variables per file
    • Automatic land masking via hFacC grid file
    • Depth levels from RC grid file
    • Multi-timestep iteration discovery
    • Automatic velocity rotation on LLC/cubed-sphere grids: if AngleCS/AngleSN (or CS/SN) grid files are present, UVEL/VVEL and other paired velocity fields are rotated from local grid coordinates to geographic (eastward/northward) components
  • Zarr: v2 format (requires make WITH_ZARR=1)
    • Compression: LZ4, Blosc (with various inner codecs), or uncompressed
    • Data types: Float32, Float64, Int64
    • Supports consolidated metadata (.zmetadata) for faster loading
    • Supports unstructured data (ICON, FESOM, etc.)
    • Reads coordinates from embedded latitude/longitude arrays
    • Dimension metadata via _ARRAY_DIMENSIONS attribute (xarray convention)
    • Multi-file time concatenation supported via glob patterns
    • Supports multi-chunk arrays (coordinates and data can be split across multiple chunks)

Coordinate Detection

Automatically searches for coordinate variables by common names:

  • Longitude: lon, longitude, x, nav_lon, glon, xt_ocean, xu_ocean, xh, xq, face_lon, node_lon, edge_lon, lonCell, lonVertex, lonEdge
  • Latitude: lat, latitude, y, nav_lat, glat, yt_ocean, yu_ocean, yh, yq, face_lat, node_lat, edge_lat, latCell, latVertex, latEdge

Dimension Detection

Automatically identifies dimension roles:

  • Time: time, t
  • Depth: depth, z, lev, level, nz, nz1
  • Nodes: nod2, nod2d, node, nodes, ncells, npoints, n_node, n_face, n_edge, nCells, nVertices, nEdges

Output

  • Screenshot/Save: Use the Save button to write a PPM image for the current variable/time/depth.
  • Output filenames are auto-generated as: <var>_t<time>_d<depth>.ppm

Troubleshooting

X11 Issues

  • X11 on macOS: Install XQuartz and ensure it is running before launching ushow.
  • SSH: Use ssh -X (or ssh -Y for trusted forwarding) to enable X11 forwarding.
  • DISPLAY not set: Ensure the DISPLAY environment variable is set. On SSH, this is set automatically with -X.

Library Issues

  • "cannot open shared object file": The runtime linker cannot find a library. The Makefile embeds library paths via rpath, but if you move the binary or libraries, you may need to set LD_LIBRARY_PATH:

    export LD_LIBRARY_PATH=/path/to/libs:$LD_LIBRARY_PATH
    ./ushow data.nc
  • Symbol lookup error: This usually indicates mixing libraries from different installations (e.g., system vs conda). Rebuild with libraries from a single source:

    make clean
    make NC_CONFIG=/path/to/consistent/nc-config X11_PREFIX=/path/to/consistent/x11
  • Verify library paths: Use ldd ./ushow (Linux) or otool -L ./ushow (macOS) to check which libraries are being loaded.

Build Issues

  • Missing headers: Install the development packages for the missing library. On Debian/Ubuntu, these typically end in -dev.
  • nc-config not found: Install NetCDF-C or specify the path: make NC_CONFIG=/path/to/nc-config

Performance

  • KDTree built once per mesh, cached for all frames
  • Regrid indices precomputed once per resolution
  • Only current 2D slice loaded per frame (~500KB vs full ~290MB for typical data)
  • Efficient nearest-neighbor interpolation (index lookup)

Acknowledgments

ushow’s interface design is inspired by ncview.

cmocean colormap tables are sourced from the cmocean-ncmaps project (https://gitlab.dkrz.de/m300575/cmocean-ncmaps). Thanks to Lukas Kluft for providing the ncmaps data.

License

GPL-3.0

About

Viewer for unstructured and structured data inspired by ncview

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • C 97.8%
  • Makefile 2.2%