MRVA container

The analysis stage: fits the multi-scale field to every observation and writes the NetCDF product.

This directory contains the containerized implementation of the MRVA (Multi-Resolution Variational Analysis) module for the MUR (Multi-scale Ultra-high Resolution) SST processing pipeline.

Overview

MRVA is the core analysis stage of the MUR SST pipeline that combines satellite and in-situ observations using multi-scale variational analysis to produce high-resolution (~1 km) daily foundation sea surface temperature fields.

Key Features:

  • Multi-scale progressive refinement (L=2-11, ~400 km down to ~1 km resolution)
  • Optimal interpolation with spatial smoothness constraints
  • Temporal decorrelation modeling (scale-dependent decay times)
  • GHRSST L4 compliant NetCDF4 output
  • OpenMP parallelized Fortran core with MATLAB orchestration

Architecture

The MRVA container uses a multi-stage Docker build pattern:

  1. Build Stage: Full MATLAB R2024b + gfortran compiler
    • Compiles 5 Fortran executables (mrva, spgrid, trimbip3, makehiresgrid, samplegdscsp)
    • Compiles MATLAB wrapper using mcc (includes 70+ MATLAB scripts)
    • Creates standalone deployment package
  2. Runtime Stage: MATLAB Runtime R2024b only (~4GB vs ~8GB for full MATLAB)
    • Copies compiled binaries from build stage
    • Minimal dependencies (curl, wget, gzip, libgomp1)
    • Fixed container paths for reproducibility

Prerequisites

System Requirements

  • Memory: 32GB (NRT mode) to 64GB (REA mode) recommended
    • L=11 grid: 51,712 × 23,040 = 1.19 billion points
    • Processing memory: ~15GB per scale
  • CPU: 8-16 cores (OpenMP parallelization enabled)
  • Disk: 10GB temporary space for intermediate files
  • Docker: Version 20.10+ with platform support (linux/amd64)

Build Requirements

  • Access to JPL MATLAB license servers (network connectivity required during build)
  • MATLAB R2024b license with Compiler, Image Processing, and Statistics toolboxes
  • Docker with multi-stage build support

Input Data Requirements

MRVA requires preprocessed data from earlier pipeline stages:

  1. L2P Satellite Data (BIC.gz format)
    • From l2p module: /testing/preprocessing/output/l2p/
    • Sensors: AMSR2R, MODISA, MODIST, AVMTAG, AVMTBG
    • Files: Global_SENSOR_YYYY_DDD.bic.gz
  2. iQUAM Buoy Data (BII format)
    • From iquam module: /testing/preprocessing/output/iquam/
    • Files: Global_IQUAM0_YYYY_DDD.bii
  3. Land/Ice Masks (BIP format)
    • From landice module: /testing/preprocessing/output/landice/YYYY/
    • Files: Global_ice_YYYY_DDD.bip.gz, landiceP01_YYYY_DDD.gds.gz
  4. Polar Cap Reference File (Static, one-time setup)
    • File: CylinderP01_edge.bip (~33 MB)
    • Location: testing/static-resources/landice/CylinderP01_edge.bip
    • Purpose: Synthetic observations for polar latitudes (89°-95° N/S) to handle coordinate singularity
    • Generation: One-time using makeedge.m script (see setup section below)
    • Note: This is a static reference file, not generated by preprocessing stages

Initial Setup

Generate Polar Cap Reference File (One-Time)

Before running MRVA for the first time, you must generate the static polar cap reference file:

# Navigate to the legacy code directory
    cd mur-internal/icenew/p01/

    # Run MATLAB to generate the file
    matlab -batch "addpath('../mat'); makeedge"

This creates /nas2/landice/CylinderP01_edge.bip (~33 MB). For containerized deployment, copy it to the static resources directory:

# Create static resources directory
    mkdir -p testing/static-resources/landice

    # Copy the generated file
    cp /nas2/landice/CylinderP01_edge.bip \
       testing/static-resources/landice/CylinderP01_edge.bip

What it does: Creates synthetic SST observations (-1.8°C, weight=100) at polar latitudes (89°-95° N/S) in a cylindrical grid pattern to handle coordinate singularity at the poles.

Why it's needed: The MRVA algorithm needs observations at all latitudes. Real satellite/buoy data has gaps at extreme polar latitudes due to coordinate system limitations. This file provides synthetic "observations" to fill those gaps and prevent numerical instabilities.

Note: This file never changes and only needs to be generated once. If missing, MRVA will run but with reduced accuracy at polar latitudes.

Building the Container

Quick Start

Build with build_module.sh from the mur/ directory:

cd mur
    ./build_module.sh mrva

It builds the mur-matlab-base:r2024b image first if missing, checks that network.lic exists, builds --platform linux/amd64 with the parent mur/ directory as context, and tags the image mur-mrva:latest — the name config.json expects.

Build Options:

# Force rebuild without cache (useful after code changes)
    ./build_module.sh mrva --no-cache

    # Debug build: symbols, bounds checking, uninitialized-variable and FP-exception traps
    # (still tagged :latest, so the pipeline picks it up unchanged)
    ./build_module.sh mrva --debug

Build Time: ~15-30 minutes (depends on network speed and CPU), plus ~15-20 minutes the first time the base image is built.

Build Stages:

  1. Compile Fortran executables with the Intel oneAPI toolchain (~2 min)
  2. Compile the MATLAB application against mur-matlab-base:r2024b (~10-20 min)
  3. Create runtime image (~2 min)

Manual Build (Advanced)

Only when build_module.sh doesn't fit — a custom tag, external CI, or debugging the Dockerfile. Build context must be the parent mur directory (for common/), and the base image must already exist (./build_matlab_base.sh from mur/):

cd mur/mrva
    docker build --platform linux/amd64 -t mur-mrva:latest -f Dockerfile ..

    # Debug variant (what --debug passes)
    docker build --platform linux/amd64 --build-arg DEBUG=1 -t mur-mrva:latest -f Dockerfile ..

See Manual Builds (Advanced).

Running the Container

Container Interface

Named args only — every input is an explicit flag, resolved by the calling Python orchestrator (run_mur_pipeline.py / run_mur_maap.py). See mrva/bin/entrypoint.sh's usage() for the authoritative flag list; the static and dynamic input flags mirror static input data.

docker run [OPTIONS] mur-mrva:latest \
      --year YEAR --doy DOY --mode MODE [--sensors LIST] \
      --polar-cap-edge-file FILE --seasonal-file FILE \
      --landice-ice-p011-file FILE --landice-grid-p01-file FILE --landice-icefiles-p011-file FILE \
      --sensor-inputs-manifest FILE \
      [--mur25-grid-file FILE] [--prior-csp-file FILE] [--l4-reference-root DIR] [--debug]

Arguments:

ArgumentRequiredDescriptionExample
--yearYes4-digit year2025
--doyYesDay of year (1-366)220
--modeYesProcessing mode: nrt or reanrt
--sensorsNoComma-separated sensor list (default: all configured)AMSR2R,MODISA
--sensor-inputs-manifestYesPath/href to a manifest JSON listing this run's BIC + iQuam files (schema: the input contract, §3)
--mur25-grid-fileNoAbsent → skip MUR25 generation
--prior-csp-fileNoAbsent → bootstrap reference field from L4 instead

Every FILE/DIR value may be a local path or an s3:// href — resolved via common/bin/localize.sh before MATLAB runs.

Processing Modes:

  • NRT (Near Real-Time): L0=6 → LF=11 (starts at ~50 km resolution)
    • Faster processing (~1-2 hours)
    • Used for recent data within stability latency
    • Interim product (may be reprocessed)
  • REA (Reanalysis): L0=2 → LF=11 (starts at ~400 km resolution)
    • Comprehensive processing (~3-4 hours)
    • Used for stable historical data
    • Final product (not reprocessed)

Volume Mounts

Output/working paths are still fixed container-internal bind mounts, unchanged from before — only the input side became explicit flags:

Container PathPurposeExample Host Path
/data/output/csp/Coefficient files (CSP format)/local/mrva/output/csp/
/data/output/netcdf/Final NetCDF4 products/local/mrva/output/netcdf/
/data/cache/Temporary working files/local/mrva/cache/
/data/logs/Execution logs/local/mrva/logs/

Every input flag's value (a local path or an s3:// href) needs to be reachable from inside the container — for local paths, bind-mount the source root at the same absolute path (run_mur_pipeline.py does this automatically); for s3:// hrefs, no mount is needed at all.

Example Usage

In practice, use run_mur_pipeline.py (local) or run_mur_maap.py (MAAP) — both resolve every flag automatically (static-resource files, per-day landice outputs, and the sensor-inputs manifest) and invoke the container correctly. The flags below are for manual/debug invocation only:

docker run --rm \
      --memory=32g --shm-size=4g \
      -v /local/static-resources:/local/static-resources:ro \
      -v /local/preprocessing:/local/preprocessing:ro \
      -v /local/manifest.json:/local/manifest.json:ro \
      -v /local/mrva/output:/data/output \
      -v /local/mrva/cache:/data/cache \
      -v /local/mrva/logs:/data/logs \
      mur-mrva:latest \
      --year 2025 --doy 220 --mode nrt \
      --polar-cap-edge-file /local/static-resources/landice/CylinderP01_edge.bip \
      --seasonal-file /local/static-resources/seasonal/mur_220.nc \
      --landice-ice-p011-file /local/preprocessing/landice-p011/2025/Global_ice_2025_220.bip \
      --landice-grid-p01-file /local/preprocessing/landice-p01/2025/landiceP01_2025_220.gds.gz \
      --landice-icefiles-p011-file /local/preprocessing/landice-p011/2025/icefiles_2025_220.txt \
      --sensor-inputs-manifest /local/manifest.json \
      --l4-reference-root /local/static-resources/L4

Testing

Basic Validation

Validates that the container is built correctly and all components are present:

cd mur/mrva
    ./test_container.sh basic

Tests:

  • ✓ Image exists
  • ✓ Entrypoint shows usage
  • ✓ Fortran executables present
  • ✓ MATLAB executable present
  • ✓ Directory structure correct

Full Processing Test

Runs MRVA with sample data from preprocessing pipeline:

cd mur/mrva
    ./test_container.sh full

Prerequisites:

  • Preprocessing stages must be run first to generate test data
  • Run: uv run run_mur_pipeline.py --config config.json --date 2024-08-08 --execute landice,iquam,l2p

Integration with Orchestrator

MRVA is integrated into the main pipeline orchestrator (run_mur_pipeline.py).

Configuration

Add MRVA configuration to mur/config.json:

{
      "mrva": {
        "container_image": "mur-mrva:latest",
        "input_dir_bic": "testing/preprocessing/output/l2p",
        "input_dir_iquam": "testing/preprocessing/output/iquam",
        "input_dir_landice": "testing/preprocessing/output/landice",
        "output_dir_csp": "testing/mrva/output/csp",
        "output_dir_netcdf": "testing/mrva/output/netcdf",
        "cache_dir": "testing/mrva/cache",
        "logs_dir": "testing/mrva/logs",
        "active_sensors": ["IQUAM0", "AMSR2R", "MODISA", "MODIST", "AVMTAG", "AVMTBG"],
        "L0_nrt": 6,
        "L0_rea": 2,
        "LF": 11,
        "decay": [48, 48, 48, 48, 48, 48, 42, 36, 30, 24, 18, 12]
      }
    }

Running via Orchestrator

There is no separate --run-mrva flag — MRVA runs by default alongside landice/iquam/l2p unless you narrow the run with --preprocess-only or --execute:

# Run full pipeline including MRVA
    uv run run_mur_pipeline.py --config config.json --date 2024-08-08

    # Run only MRVA stage (requires preprocessed inputs already present)
    uv run run_mur_pipeline.py --config config.json --date 2024-08-08 --execute mrva --keep-containers

    # Run full window (9 days) including MRVA
    uv run run_mur_pipeline.py --config config.json --all-stages

--keep-containers is worth adding when debugging a specific day — it stops the container from being auto-removed on failure, so docker logs <container_name> still works afterward.

Output Files

Coefficient Files (CSP Format)

Location: /data/output/csp/YYYY/

Files:

  • YYYYMMDDH_MRVA4_Global.c02 through .c11 - Analysis coefficients per scale
  • YYYYMMDDH_MRVA4_Global.u06 through .u08 - Uncertainty estimates (optional)

Format: Binary Fortran unformatted (see filemod.f for structure)

NetCDF4 Output (GHRSST L4)

Location: /data/output/netcdf/YYYY/DDD/

File: YYYYMMDD090000-JPL-L4_GHRSST-SSTfnd-MUR-GLOB-v02.0-fv04.1.nc

Variables:

  • analysed_sst(time, lat, lon) - Foundation SST [Kelvin, float32]
  • analysis_error(time, lat, lon) - Uncertainty (1σ) [Kelvin, float32]
  • mask(time, lat, lon) - Land/sea/ice classification [int8]
  • sea_ice_fraction(time, lat, lon) - Ice concentration [0-1, float32]
  • dt_1km_data(time, lat, lon) - Distance to high-res obs [km, float32]

Grid:

  • Longitude: -179.995° to 180.005° (36,000 points, 0.01° resolution)
  • Latitude: -89.995° to 89.995° (17,999 points, 0.01° resolution)
  • Time: Single daily analysis at 09:00 UTC

Compliance: GHRSST Data Specification v2.0

Performance Characteristics

Processing Time

ModeScalesTime (16 cores)Memory
NRTL=6-11~1-2 hours~32 GB
REAL=2-11~3-4 hours~64 GB

Breakdown:

  • Data preparation (BIQ generation): ~5 min
  • Reference field generation: ~10 min
  • Outlier detection: ~5 min
  • Bias correction: ~5 min
  • MRVA core (L=2-11): ~30-45 min per scale
  • NetCDF generation: ~20 min

Parallelization

  • Fortran: OpenMP parallelization across observation loop
    • Set --cpus=N to control thread count
    • Scales well up to 16 cores
    • Memory bandwidth limited beyond 16 cores
  • MATLAB: Single-threaded orchestration
    • NetCDF generation can be backgrounded

Troubleshooting

Build Issues

Problem: Compilation timeout after 10 minutes

ERROR: Compilation failed or timed out after 10 minutes

Solution: Check MATLAB license server connectivity

  • Verify network access to JPL license servers
  • Check firewall rules for port 7282
  • Try building with --no-cache to force re-download of mpm

Problem: Fortran compilation fails with gfortran

Error: Unclassifiable statement at (1)

Solution: Fortran source uses fixed-form format

  • Ensure Makefile uses -ffixed-line-length-none flag
  • Original code uses Intel Fortran; gfortran compatibility may vary
  • Consider using Intel oneAPI HPC Toolkit base image for production

Runtime Issues

Problem: Container exits with "No such file or directory"

ERROR: /opt/mrva/bin/mrva: No such file or directory

Solution: Platform mismatch (common on Apple Silicon)

  • Add --platform linux/amd64 to docker run command
  • Rebuild with ./build_module.sh mrva (from mur/), which always builds --platform linux/amd64

Problem: Out of memory error during L=10 or L=11

Error: Memory allocation failed

Solution: Increase container memory limits

  • NRT mode: Minimum 32GB (--memory=32g)
  • REA mode: Minimum 64GB (--memory=64g)
  • Also increase swap: --memory-swap=72g
  • Check host system has sufficient RAM available

Problem: Missing input files

WARNING: L2P data directory not found

Solution: Run preprocessing stages first

# Generate required input data
    uv run run_mur_pipeline.py --config config.json \
      --date 2024-08-08 \
      --execute landice,iquam,l2p

Performance Issues

Problem: Processing slower than expected

Solutions:

  1. Check CPU allocation: Ensure --cpus=16 or similar
  2. Verify OpenMP threads: Container should log thread count at startup
  3. Check I/O: Slow disk or network mounts can bottleneck
  4. Review logs: Look for repeated warnings or error-recovery loops

Advanced Configuration

Custom Decay Times

Edit config.json to adjust temporal decorrelation parameters:

{
      "mrva": {
        "decay": [48, 48, 48, 48, 48, 48, 42, 36, 30, 24, 18, 12]
      }
    }
  • 12 values corresponding to L=0 through L=11
  • Units: hours (time scale for exponential decay)
  • Lower values = stronger temporal constraint
  • Default values from production system

Scale Range Adjustment

Modify start/end scales:

{
      "mrva": {
        "L0_nrt": 6,   # NRT starts at L=6 (~50 km)
        "L0_rea": 2,   # REA starts at L=2 (~400 km)
        "LF": 11        # Both end at L=11 (~1 km)
      }
    }

Trade-offs:

  • Higher L0 → Faster, but less large-scale constraint
  • Lower LF → Faster, but coarser output resolution

References

Documentation

Source Code

  • Fortran: src/fortran/
    • mrva.f - Main variational analysis
    • spmm.f - Spline module (1266 lines)
    • filemod.f - Binary I/O module
    • spgrid.f - Coefficient sampling
    • trimbip3.f - Outlier filtering
    • makehiresgrid.f - Distance-to-land grid
  • MATLAB: src/matlab/
    • workflow/mrva4com.m - Main orchestration
    • preprocessing/ - Data preparation (14 scripts)
    • io/ - Binary file I/O (16 scripts)
    • output/csp2nc4a.m - NetCDF generation

Publications

  • Chin, T. M., et al. (2017). "A multi-scale high-resolution analysis of global sea surface temperature." Remote Sensing of Environment, 200, 154-169.
  • JPL MUR Project: https://podaac.jpl.nasa.gov/dataset/MUR-JPL-L4-GLOB-v4.1

License

This software is subject to NASA JPL licensing terms. Contact the MUR team for details.

Support

For questions or issues:

  1. Check Troubleshooting section
  2. Review logs in /data/logs/
  3. Contact MUR development team

The image ships MATLAB Runtime R2024b and Fortran built with gfortran (GCC).