Skip to content

Add unified native-grid visualization tool (mpas_viz) for MPAS post-processing#14

Open
daniloceano wants to merge 10 commits into
developfrom
postproc/plotting-improvements
Open

Add unified native-grid visualization tool (mpas_viz) for MPAS post-processing#14
daniloceano wants to merge 10 commits into
developfrom
postproc/plotting-improvements

Conversation

@daniloceano

Copy link
Copy Markdown

Summary

Before this branch, usp-utils/post_proc/plotting_scripts/ had a single script, mpas_plot.py, which could only draw one still map at a time and had no documentation.

This branch turns it into a proper, documented visualization tool for MPAS fields on the native Voronoi grid — with no regridding.

The work is organized into three main improvements:

  1. Improved the plotting script

    • Lists available variables when none is provided.
    • Adds friendlier -gf / --gridfile error messages.
    • Adds land masking with -ml for ocean-only fields such as SST.
  2. Added animation support

    • New capability: animate a field over time.
    • Supports frame-level parallelism with -j for long or heavy runs.
  3. Unified plotting and animation into one framework

    • Introduces mpas_viz.py.
    • Documents the workflow in a new README.md.

The result is that users can now visualize any variable on the native grid as either a still map or an animation. By controlling the available parameters, the tool can also be used to produce publication-ready figures.

Key Features

Visualization controls

  • Color control:

    • --cmap
    • --vmin / --vmax
    • --extend
    • -c / --clip, which clips extremes at mean ± 4σ for better color resolution
  • Regional zoom:

    • -lat_min
    • -lat_max
    • -lon_min
    • -lon_max
  • Land masking:

    • -ml
  • Native dual-mesh rendering

  • Optional wind-vector overlays:

    • -u
    • -v_wind
    • --stride
  • Coastline toggle

  • Derived fields:

    • --sum-vars
    • --deaccumulate

Animation support

  • Parallelism for long or heavy animations:

    • -j N
    • -j -1 to use all CPUs

Easy by default, powerful when needed

Despite the number of options, the common case remains simple:

python mpas_viz.py \
  -f x1.10242.sfc_update.nc \
  -v sst \
  -gf x1.10242.static.nc

Documentation

A new README.md documents the workflow, including:

  • A one-line mental model:

    1 step → image, >1 step → animation

  • Examples for still maps and animations.

  • A legacy-command mapping for existing users.

How the Unified Tool Works

Single tool, two modes

mpas_viz.py draws:

  • A still image when one timeline step is selected.
  • An animation when more than one timeline step is selected.

This means every option works consistently for both still images and animations.

Global timeline

The tool builds a global timeline of (file, time_index) entries.

Users can:

  • Browse available times with --list-times.
  • Select a time range with --tstart and --tend, both inclusive.

This also supports animating a single multi-time file, such as an sfc_update.nc file with 6 time steps.

Shared rendering logic

Both still plotting and animation now use one shared cell renderer instead of duplicated logic.

Backward compatibility

mpas_plot.py and mpas_animate.py are now thin shims that forward to mpas_viz.py.

This keeps existing workflows working, including:

  • Existing mpas_plot.py commands.
  • Existing mpas_animate.py commands.
  • Imports such as:
from mpas_plot import ...

The legacy --tmin / --tmax options are also aliased to --tstart / --tend.

Verification

Tested end-to-end on runs/240km_uniform/.

Verified that:

  • --list-times renders correctly.

  • The variable-listing table renders correctly.

  • A still SST map with -ml yes masks the continents.

  • The plot title shows the timestamp.

  • Animating the single multi-time sfc_update.nc file with 6 frames via -j 3 produces a valid 6-frame GIF.

  • Both shims produce equivalent output:

    • mpas_plot.py
    • mpas_animate.py --tmin/--tmax

daniloceano and others added 9 commits June 11, 2026 21:49
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- setup_environment.sh: stop inferring env existence from 'conda activate's exit
  code (which gives a misleading "Couldn't find the environment" when the env
  exists but a transient activate returns non-zero). Now: ensure the 'conda'
  shell function is loaded (source conda.sh if only the binary is on PATH),
  check the env in 'conda env list', then activate. Clearer not-found message.
- install_conda_environment.sh: add '#!/usr/bin/env bash' shebang. It uses
  bash-only features (BASH_SOURCE, '[ == ]'); running it with sh/dash failed
  with "Bad substitution" / "unexpected operator" and left SCRIPT_DIR empty.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Change -v/--var default to None. When omitted, open the file and print a
formatted table of plottable variables (name, long_name, units, grid type,
and Time/Level dependence) so the user can pick a target, then exit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mpas_plot.py:
- Replace the opaque "Recovery of [...] failed" RuntimeError (and the raw
  xarray "no IO backend" traceback) with clear messages: tell the user to
  pass a mesh file via -gf/--gridfile, and check the gridfile exists and
  actually carries the connectivity before plotting.
- Guard -g/--grid: it only accepts yes/no (draw cell edges). If a file path
  is passed (the common -g/-gf mix-up), exit with a hint to use -gf instead.

Bring mpas_animate.py (native-grid time animations, parallel rendering) over
from the stale animation-native-grid branch and fold its README into a single
plotting_scripts/README.md covering both mpas_plot and mpas_animate.

.gitignore: ignore .DS_Store and generated .gif/.mp4 animations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge the two diverged scripts into a single framework. mpas_viz.py draws a
still image when one timeline step is selected, or an animation when more than
one is, so every option works for both modes.

- Global timeline of (file, time_index) entries (--list-times, --tstart/--tend),
  which also fixes animating a single multi-time file (e.g. sfc_update.nc).
- One shared cell renderer (mask-land, dual mesh, friendly -gf errors) plus the
  animate-only features (wind vectors, --sum-vars/--deaccumulate, custom color
  scale, --no-coastlines, parallel frame rendering).
- mpas_plot.py and mpas_animate.py become thin back-compat shims forwarding to
  mpas_viz (old commands and imports keep working; --tmin/--tmax aliased).
- README rewritten for the single framework, with legacy-command mapping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n outputs

Keep only the ignore for generated .gif/.mp4 under usp-utils/post_proc/
plotting_scripts/ (the direct output of this PR's animation tool). The generic
.DS_Store rules are out of scope for the post-processing PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@daniloceano daniloceano requested a review from favba June 20, 2026 17:39
Add an explicit "quote the glob" warning to the time-model section (an unquoted
wildcard is expanded by the shell, so only the first file reaches -f), and a
step-by-step "animate a run directory" workflow with placeholders (list-times →
discover vars → short test range → full parallel animation), including the -gf
requirement and the per-cell cost caveat on fine meshes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 6, 2026 15:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a unified MPAS native-grid visualization tool (mpas_viz.py) that supports both still plots and time animations on the Voronoi mesh (no regridding), and keeps legacy entrypoints working via thin shims.

Changes:

  • Added mpas_viz.py as the single plotting/animation framework with a global timeline model, optional land masking, wind vectors, and parallel frame rendering.
  • Added end-user documentation (plotting_scripts/README.md) describing workflow, options, and legacy command mappings.
  • Improved environment tooling and repo hygiene: more robust conda activation logic, bash shebang for conda installer, and new .gitignore entries for generated artifacts and stray HOME files.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
usp-utils/setup_environment.sh Makes conda detection/activation more robust before running plotting utilities.
usp-utils/post_proc/plotting_scripts/README.md Documents the unified visualization workflow, options, and backward-compatibility behavior.
usp-utils/post_proc/plotting_scripts/mpas_viz.py New unified plotting+animation implementation on the native MPAS grid, with timeline selection and rendering helpers.
usp-utils/post_proc/plotting_scripts/mpas_plot.py Converts the legacy plot script into a shim that re-exports and forwards to mpas_viz.py.
usp-utils/post_proc/plotting_scripts/mpas_animate.py Adds a legacy animate shim that forwards to mpas_viz.py and preserves --tmin/--tmax aliases.
usp-utils/install_conda_environment.sh Adds a bash shebang and clarifies bash-only execution requirements.
.gitignore Ignores generated animations and stray per-user HOME/cache artifacts to prevent accidental commits.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +441 to +444
for cell in iterator:
# Skip land cells when masking is enabled (landmask == 1 over land)
if landmask is not None and int(landmask.sel(nCells=cell)) == 1:
continue
Comment on lines +462 to +464
# Wrap polygons that straddle the +/-180 antimeridian.
if max(lons) > 170 and min(lons) < -170:
lons = xr.where(lons >= 170.0, lons - 360.0, lons)
Comment on lines +497 to +498
if max(lons) > 170 and min(lons) < -170:
lons = xr.where(lons >= 170.0, lons - 360.0, lons)
Comment on lines +667 to +669
else:
print(f"WARNING cannot plot variable with dimensions {da.dims}")
return
Comment on lines +715 to +719
if outfile.endswith('.gif'):
from PIL import Image
images = [Image.open(f) for f in temp_files]
images[0].save(outfile, save_all=True, append_images=images[1:],
duration=int(1000 / fps), loop=0, optimize=False)
Comment on lines +46 to +52
if conda env list 2> /dev/null | awk '{print $1}' | grep -qx 'cgfd-usp-mpas'; then
conda activate cgfd-usp-mpas
echo -e "${INFO} Conda enviroment 'cgfd-usp-mpas' activated"
else
echo -e "${WARNING} The 'cgfd-usp-mpas' conda environment was not found. It will not be activated and Python scripts might not work. To install it, run: bash $SCRIPT_DIR/install_conda_environment.sh"
return 1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants