Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
dce2ee1
Add `./mfc.sh viz` command for CLI visualization of post-processed ou…
sbryngelson Feb 21, 2026
f2da047
Fix viz: validate variable name and fix lint/spelling issues
sbryngelson Feb 21, 2026
bbccee4
Fix MP4 frame directory to use output path instead of case directory
sbryngelson Feb 21, 2026
dc87759
Fix Silo-HDF5 reader to parse Named Datatype structure correctly
sbryngelson Feb 22, 2026
1968272
Fix multi-processor assembly and Silo data ordering
sbryngelson Feb 22, 2026
9f39c7e
Use imageio-ffmpeg for MP4 rendering instead of system ffmpeg
sbryngelson Feb 22, 2026
0ea6360
Add documentation for ./mfc.sh viz command
sbryngelson Feb 22, 2026
fd34eed
Address code review feedback from CodeRabbit
sbryngelson Feb 22, 2026
ebe4ff9
Fix --step input validation and clarify inclusive range in docs
sbryngelson Feb 22, 2026
1153d88
Guard LogNorm against non-positive data in log-scale rendering
sbryngelson Feb 22, 2026
269f9af
Harden readers and CLI error handling
sbryngelson Feb 22, 2026
ec1d590
Add viz to CLI reference doc generation categories
sbryngelson Feb 22, 2026
306016c
Harden binary reader: EOF check, header validation, varname union, st…
sbryngelson Feb 22, 2026
fe17687
Fix MP4 opts mutation, frame cleanup safety, silo skip warning
sbryngelson Feb 22, 2026
376963e
Harden viz: record validation, error handling, and robustness
sbryngelson Feb 22, 2026
071bfca
Guard LogNorm against NaN data, harden frame cleanup
sbryngelson Feb 22, 2026
64dc66d
Merge branch 'master' into viz-fast
sbryngelson Feb 22, 2026
9bf1918
Merge branch 'master' into viz-fast
sbryngelson Feb 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ choises = "choises" # appears in comment explaining validation purpose
ordr = "ordr" # typo for "order" in "weno_ordr" - tests param suggestions
unknwn = "unknwn" # typo for "unknown" - tests unknown param detection
tru = "tru" # typo for "true" in "when_tru" - tests dependency keys
PNGs = "PNGs"

[files]
extend-exclude = ["docs/documentation/references*", "docs/references.bib", "tests/", "toolchain/cce_simulation_workgroup_256.sh", "build-docs/"]
2 changes: 1 addition & 1 deletion docs/documentation/case.md
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ To restart the simulation from $k$-th time step, see @ref running "Restarting Ca

The table lists formatted database output parameters. The parameters define variables that are outputted from simulation and file types and formats of data as well as options for post-processing.

- `format` specifies the choice of the file format of data file outputted by MFC by an integer of 1 and 2. `format = 1` and `2` correspond to Silo-HDF5 format and binary format, respectively.
- `format` specifies the choice of the file format of data file outputted by MFC by an integer of 1 and 2. `format = 1` and `2` correspond to Silo-HDF5 format and binary format, respectively. Both formats are supported by `./mfc.sh viz` (see @ref visualization "Flow Visualization"). Silo-HDF5 requires the h5py Python package; binary has no extra dependencies.

- `precision` specifies the choice of the floating-point format of the data file outputted by MFC by an integer of 1 and 2. `precision = 1` and `2` correspond to single-precision and double-precision formats, respectively.

Expand Down
18 changes: 18 additions & 0 deletions docs/documentation/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,24 @@ MFC is **unit-agnostic**: the solver performs no internal unit conversions. What

The only requirement is **consistency** — all inputs must use the same unit system. Note that some parameters use **transformed stored forms** rather than standard physical values (e.g., `gamma` expects \f$1/(\gamma-1)\f$, not \f$\gamma\f$ itself). See @ref sec-stored-forms for details.

## Visualizing Results

After running post_process, visualize the output directly from the command line:

```shell
# List available variables
./mfc.sh viz examples/2D_shockbubble/ --list-vars --step 0

# Render a pressure snapshot
./mfc.sh viz examples/2D_shockbubble/ --var pres --step 1000

# Generate a video
./mfc.sh viz examples/2D_shockbubble/ --var pres --step all --mp4
```

Output images and videos are saved to the `viz/` subdirectory of the case.
For more options, see @ref visualization "Flow Visualization" or run `./mfc.sh viz -h`.

## Helpful Tools

### Parameter Lookup
Expand Down
8 changes: 8 additions & 0 deletions docs/documentation/running.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ using 4 cores:
./mfc.sh run examples/2D_shockbubble/case.py -t simulation post_process -n 4
```

- Visualizing post-processed output:

```shell
./mfc.sh viz examples/2D_shockbubble/ --var pres --step 1000
```

See @ref visualization "Flow Visualization" for the full set of visualization options.

---

## Running on GPUs
Expand Down
42 changes: 42 additions & 0 deletions docs/documentation/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ This guide covers debugging tools, common issues, and troubleshooting workflows
./mfc.sh run case.py -v # Run with verbose output
./mfc.sh test --only <UUID> # Run a specific test
./mfc.sh clean # Clean and start fresh
./mfc.sh viz case_dir/ --list-vars --step 0 # Inspect post-processed data
```

---
Expand Down Expand Up @@ -457,6 +458,47 @@ Common issues:

---

## Visualization Issues

### "No 'binary/' or 'silo_hdf5/' directory found"

**Cause:** Post-processing has not been run, or the case directory path is wrong.

**Fix:**
1. Run post_process first:
```bash
./mfc.sh run case.py -t post_process
```
2. Verify the path points to the case directory (containing `binary/` or `silo_hdf5/`)

### "Variable 'X' not found"

**Cause:** The requested variable was not written during post-processing.

**Fix:**
1. List available variables:
```bash
./mfc.sh viz case_dir/ --list-vars --step 0
```
2. Ensure your case file enables the desired output (e.g., ``prim_vars_wrt = 'T'``, ``cons_vars_wrt = 'T'``)

### "h5py is required to read Silo-HDF5 files"

**Cause:** The case was post-processed with `format=1` (Silo-HDF5) but `h5py` is not installed.

**Fix:**
- Install h5py: `pip install h5py`
- Or re-run post_process with `format=2` in your case file to produce binary output

### Visualization looks wrong or has artifacts

**Possible causes and fixes:**
1. **Color range:** Try setting explicit `--vmin` and `--vmax` values
2. **Wrong variable:** Use `--list-vars` to check available variables
3. **3D slice position:** Adjust `--slice-axis` and `--slice-value` to view the correct plane

---

## Getting Help

If you can't resolve an issue:
Expand Down
130 changes: 125 additions & 5 deletions docs/documentation/visualization.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,133 @@

# Flow visualization

A post-processed database in Silo-HDF5 format can be visualized and analyzed using Paraview and VisIt.
After the post-processing of simulation data (see section @ref running "Running"), a directory named `silo_hdf5` contains a silo-HDF5 database.
Here, `silo_hdf5/` includes a directory named `root/` that contains index files for flow field data at each saved time step.
After running `post_process` on a simulation (see @ref running "Running"), MFC produces output in either Silo-HDF5 format (`format=1`) or binary format (`format=2`).
These can be visualized using MFC's built-in CLI tool or external tools like ParaView and VisIt.

### Visualizing with Paraview
---

Paraview is an open-source interactive parallel visualization and graphical analysis tool for viewing scientific data.
## Quick visualization with `./mfc.sh viz`

MFC includes a built-in visualization command that renders images and videos directly from post-processed output — no external GUI tools needed.

### Basic usage

```bash
# Plot pressure at timestep 1000
./mfc.sh viz case_dir/ --var pres --step 1000

# Plot density at all available timesteps
./mfc.sh viz case_dir/ --var rho --step all
```

The command auto-detects the output format (binary or Silo-HDF5) and dimensionality (1D, 2D, or 3D).
Output images are saved to `case_dir/viz/` by default.

### Exploring available data

Before plotting, you can inspect what data is available:

```bash
# List all available timesteps
./mfc.sh viz case_dir/ --list-steps

# List all available variables at a given timestep
./mfc.sh viz case_dir/ --list-vars --step 0
```

### Timestep selection

The `--step` argument accepts several formats:

| Format | Example | Description |
|--------|---------|-------------|
| Single | `--step 1000` | One timestep |
| Range | `--step 0:10000:500` | Start:end:stride (inclusive) |
| All | `--step all` | Every available timestep |

### Rendering options

Customize the appearance of plots:

```bash
# Custom colormap and color range
./mfc.sh viz case_dir/ --var rho --step 1000 --cmap RdBu --vmin 0.5 --vmax 2.0

# Higher resolution
./mfc.sh viz case_dir/ --var pres --step 500 --dpi 300

# Logarithmic color scale
./mfc.sh viz case_dir/ --var schlieren --step 1000 --log-scale
```

| Option | Description | Default |
|--------|-------------|---------|
| `--cmap` | Matplotlib colormap name | `viridis` |
| `--vmin` | Minimum color scale value | auto |
| `--vmax` | Maximum color scale value | auto |
| `--dpi` | Image resolution (dots per inch) | 150 |
| `--log-scale` | Use logarithmic color scale | off |
| `--output` | Output directory for images | `case_dir/viz/` |

### 3D slicing

For 3D simulations, `viz` extracts a 2D slice for plotting.
By default, it slices at the midplane along the z-axis:

```bash
# Default z-midplane slice
./mfc.sh viz case_dir/ --var pres --step 500

# Slice along the x-axis at x=0.25
./mfc.sh viz case_dir/ --var pres --step 500 --slice-axis x --slice-value 0.25

# Slice by array index
./mfc.sh viz case_dir/ --var pres --step 500 --slice-axis y --slice-index 50
```

### Video generation

Generate MP4 videos from a range of timesteps:

```bash
# Basic video (10 fps)
./mfc.sh viz case_dir/ --var pres --step 0:10000:100 --mp4

# Custom frame rate
./mfc.sh viz case_dir/ --var schlieren --step all --mp4 --fps 24

# Video with fixed color range
./mfc.sh viz case_dir/ --var rho --step 0:5000:50 --mp4 --vmin 0.1 --vmax 1.0
```

Videos are saved as `case_dir/viz/<varname>.mp4`.
The color range is automatically computed from the first, middle, and last frames unless `--vmin`/`--vmax` are specified.

### Format selection

The output format is auto-detected from the case directory.
To override:

```bash
./mfc.sh viz case_dir/ --var pres --step 0 --format binary
./mfc.sh viz case_dir/ --var pres --step 0 --format silo
```

> [!NOTE]
> Reading Silo-HDF5 files requires the `h5py` Python package.
> If it is not installed, you will see a clear error message with installation instructions.
> Alternatively, use `format=2` (binary) in your case file to produce binary output, which has no extra dependencies.

### Complete option reference

Run `./mfc.sh viz -h` for a full list of options.

---

## Visualizing with ParaView

ParaView is an open-source interactive parallel visualization and graphical analysis tool for viewing scientific data.
Post-processed data in Silo-HDF5 format (`format=1`) can be opened directly in ParaView.
Paraview 5.11.0 has been confirmed to work with the MFC databases for some parallel environments.
Nevertheless, the installation and configuration of Paraview can be environment-dependent and are left to the user.

Expand Down
6 changes: 3 additions & 3 deletions examples/1D_inert_shocktube/viz.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import mfc.viz
import mfc.viz_legacy as mfc_viz
import os

import subprocess
Expand All @@ -8,11 +8,11 @@

from case import sol_L as sol

case = mfc.viz.Case(".")
case = mfc_viz.Case(".")

os.makedirs("viz", exist_ok=True)

# sns.set_theme(style=mfc.viz.generate_cpg_style())
# sns.set_theme(style=mfc_viz.generate_cpg_style())

Y_VARS = ["H2", "O2", "H2O", "N2"]

Expand Down
6 changes: 3 additions & 3 deletions examples/1D_reactive_shocktube/viz.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import mfc.viz
import mfc.viz_legacy as mfc_viz
import os

import subprocess
Expand All @@ -8,11 +8,11 @@

from case import sol_L as sol

case = mfc.viz.Case(".")
case = mfc_viz.Case(".")

os.makedirs("viz", exist_ok=True)

sns.set_theme(style=mfc.viz.generate_cpg_style())
sns.set_theme(style=mfc_viz.generate_cpg_style())

Y_VARS = ["H2", "O2", "H2O", "N2"]

Expand Down
6 changes: 3 additions & 3 deletions examples/nD_perfect_reactor/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from tqdm import tqdm
import matplotlib.pyplot as plt

import mfc.viz
import mfc.viz_legacy as mfc_viz
from case import dt, Tend, SAVE_COUNT, sol

case = mfc.viz.Case(".", dt)
case = mfc_viz.Case(".", dt)

sns.set_theme(style=mfc.viz.generate_cpg_style())
sns.set_theme(style=mfc_viz.generate_cpg_style())

Y_MAJORS = set(["H", "O", "OH", "HO2"])
Y_MINORS = set(["H2O", "H2O2"])
Expand Down
4 changes: 2 additions & 2 deletions examples/nD_perfect_reactor/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
import cantera as ct
from tqdm import tqdm

import mfc.viz
import mfc.viz_legacy as mfc_viz
from case import dt, NS, Tend, SAVE_COUNT, sol

case = mfc.viz.Case(".", dt)
case = mfc_viz.Case(".", dt)

for name in tqdm(sol.species_names, desc="Loading Variables"):
case.load_variable(f"Y_{name}", f"prim.{5 + sol.species_index(name)}")
Expand Down
5 changes: 5 additions & 0 deletions toolchain/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ def __print_greeting():


def __checks():
if ARG("command") in ("viz", "params", "completion", "help"):
return
if not does_command_exist("cmake"):
raise MFCException("CMake is required to build MFC but couldn't be located on your system. Please ensure it installed and discoverable (e.g in your system's $PATH).")

Expand Down Expand Up @@ -175,6 +177,9 @@ def __run(): # pylint: disable=too-many-branches
elif cmd == "generate":
from mfc import generate # pylint: disable=import-outside-toplevel
generate.generate()
elif cmd == "viz":
from mfc.viz import viz # pylint: disable=import-outside-toplevel
viz.viz()
elif cmd == "params":
from mfc import params_cmd # pylint: disable=import-outside-toplevel
params_cmd.params()
Expand Down
4 changes: 2 additions & 2 deletions toolchain/mfc/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def custom_error(message):
# Add default arguments of other subparsers
# This ensures all argument keys exist even for commands that don't define them
# Only process subparsers that have common arguments we need
relevant_subparsers = ["run", "test", "build", "clean", "count", "count_diff", "validate"]
relevant_subparsers = ["run", "test", "build", "clean", "count", "count_diff", "validate", "viz"]
for name in relevant_subparsers:
if args["command"] == name:
continue
Expand All @@ -120,7 +120,7 @@ def custom_error(message):
# Parse with dummy input to get defaults (suppress errors for required positionals)
try:
# Commands with required positional input need a dummy value
if name in ["run", "validate"]:
if name in ["run", "validate", "viz"]:
vals, _ = subparser.parse_known_args(["dummy_input.py"])
elif name == "build":
vals, _ = subparser.parse_known_args([])
Expand Down
Loading
Loading