Running Examples and Tests
This guide covers how to run SHORE's example scripts and the full test suite.
Prerequisites
Ensure SHORE is installed in development mode with all extras:
git clone https://github.com/szaghi/shore.git
cd shore
python -m venv .venv && source .venv/bin/activate
make dev # pip install -e ".[dev,vis]"Running Examples
SHORE ships a set of standalone example scripts and a Jupyter notebook that serve as both documentation and sanity checks.
examples/01-body-fitted/visualize_cubed_sphere.py — cubed-sphere topology around a sphere
The script generates a unit-sphere body-fitted test case (always 6-block cubed-sphere topology) and optionally visualises it. When called without --generate, it acts as a generic .geo / .npz viewer.
# Generate sphere.stl + 6 sphere_<label>.geo files and open the viewer
python examples/01-body-fitted/visualize_cubed_sphere.py --generate
# Generate and save a screenshot instead of opening a window
python examples/01-body-fitted/visualize_cubed_sphere.py --generate --screenshot sphere_preview.png
# Generate only (skip the viewer)
python examples/01-body-fitted/visualize_cubed_sphere.py --generate --no-view
# Export to a single VTK MultiBlock .vtm for ParaView
python examples/01-body-fitted/visualize_cubed_sphere.py --generate --vtkKey parameters:
| Flag | Default | Description |
|---|---|---|
--ni | 40 | Equator meridional resolution |
--nj | 60 | Longitude resolution (multiple of 4) |
--nk | 20 | Wall-normal layers |
--ds | 0.02 | First-layer thickness (geometric) |
--growth | 1.10 | Layer growth ratio (geometric) |
--theta-cap | π/6 | Polar exclusion angle in radians |
--theta-cap-deg | — | Polar exclusion angle in degrees |
--smooth | 0.2 | Laplacian blend strength [0–1] |
--smooth-iters | 2 | Laplacian sweeps per layer |
--surface-i-spacing | uniform | Equator meridional spacing law |
--surface-i-beta | 3.0 | Clustering strength for tanh/tanh2 i-spacing |
--surface-j-spacing | uniform | Cap-edge tangent-plane spacing law |
--surface-j-beta | 3.0 | Clustering strength for tanh2 j-spacing |
--volume-k-spacing | geometric | Wall-normal spacing law |
--volume-k-beta | 3.0 | Clustering strength for tanh/tanh2 k-spacing |
--volume-k-thickness | — | Total wall-normal thickness (required for tanh/tanh2) |
--blend-normals-k | 0 | Layers over which to blend STL normals (0 = off) |
Spacing examples:
# Tanh wall-normal spacing — fixed far-field boundary
python examples/01-body-fitted/visualize_cubed_sphere.py --generate \
--volume-k-spacing tanh --volume-k-beta 4.0 --volume-k-thickness 2.0
# Equator meridional clustering at the cap seams
python examples/01-body-fitted/visualize_cubed_sphere.py --generate \
--surface-i-spacing tanh --surface-i-beta 4.0
# Cap-corner clustering (tanh2 j-spacing — inherited by equator seam)
python examples/01-body-fitted/visualize_cubed_sphere.py --generate \
--surface-j-spacing tanh2 --surface-j-beta 5.0See Spacing laws for an explanation of each distribution and what physical region it clusters toward.
Visualise an existing .geo or .npz file directly:
python examples/01-body-fitted/visualize_cubed_sphere.py sphere_sub0.geo
python examples/01-body-fitted/visualize_cubed_sphere.py sphere_k0.npz
python examples/01-body-fitted/visualize_cubed_sphere.py sphere_sub0.geo --screenshot out.png --no-edgesOutput: 6 .geo files per run
sphere_sub0.geo .. sphere_sub3.geo # equatorial sub-blocks
sphere_cap_north.geo # north polar cap
sphere_cap_south.geo # south polar capexamples/01-body-fitted/config_fuselage.toml — config-driven workflow
When the parameter set gets large enough that the CLI invocation becomes a wall of flags, switch to a TOML config file. Every CLI flag on shore mesh has an equivalent TOML key; the file is parsed, validated, and merged with any explicit CLI flags before the run starts (CLI flag > config value > built-in default).
# bootstrap a fresh config from defaults
shore config template > shore.toml
# or start from the annotated example
cp examples/01-body-fitted/config_fuselage.toml my_case.toml
# parse-check before running (catches typos, version mismatches, etc.)
shore config validate my_case.toml
# run the full pipeline; INPUT is taken from the config's `input` key
shore mesh -c my_case.toml -v
# CLI overrides still apply: this picks up everything from the file
# but uses --nk 80 instead of the configured value.
shore mesh -c my_case.toml --nk 80The example config (examples/01-body-fitted/config_fuselage.toml) demonstrates every section with a non-default choice — tanh wall-normal spacing with fixed thickness, equator clustering at the cap seams, blended STL normals in the near-wall region — so you can see how the knobs combine in a single recipe.
See the full schema and precedence rules in Config files.
Primitive examples
For the parametric primitives (box, annulus, flat-caps) there is one standalone script per primitive plus a matching annotated TOML config. Each script generates the primitive in-memory, writes a .geo (or several, for multi-block primitives) and a .vtm MultiBlock for ParaView, and optionally opens the PyVista viewer.
| Script | Primitive | Config | Output |
|---|---|---|---|
examples/02-primitives/visualize_box.py | Cartesian box | config_primitive_box.toml | box.geo, box.vtm |
examples/02-primitives/visualize_annulus.py | annular cylinder | config_primitive_annulus.toml | annulus.geo, annulus.vtm |
examples/02-primitives/visualize_flat_caps.py | 5-block flat-caps cylinder | config_primitive_flat_caps.toml | flat_caps_<label>.geo ×5, flat_caps.vtm |
Each script accepts the same shape of options:
# Defaults — open the viewer
python examples/02-primitives/visualize_box.py
# Skip the viewer (CI-friendly)
python examples/02-primitives/visualize_box.py --no-view
# Save a screenshot instead of opening a window
python examples/02-primitives/visualize_box.py --screenshot box.png
# Drive the parameters from a TOML config (CLI flags still override)
python examples/02-primitives/visualize_annulus.py --config examples/02-primitives/config_primitive_annulus.toml --no-view
python examples/02-primitives/visualize_flat_caps.py --config examples/02-primitives/config_primitive_flat_caps.toml --no-view
# Skip the .vtm export (only .geo is written)
python examples/02-primitives/visualize_box.py --no-vtk --no-viewThe .vtm file is a small XML manifest pointing at one .vts per block stored under a sister directory (box/, annulus/, or flat_caps/). Open the .vtm in ParaView; it pulls in every block automatically.
examples/05-chimera-assembly/assembly_sphere_in_cylinder.py — first-tentative assembly
The assembly example combines a near-body cubed-sphere mesh and a background flat-caps cylinder into a single overset stack that shares one world-coordinate domain. A 6-block sphere-mesh wraps a unit-radius (diameter 1.0) sphere body at the origin, marched outward to a wall-normal extent of ~0.2; a 5-block flat-caps cylinder spans [-10, 10]² × [-10, 50]. All 11 blocks are bundled into a single ParaView-friendly assembly.vtm.
# Generate everything; open the viewer.
python examples/05-chimera-assembly/assembly_sphere_in_cylinder.py
# Generate only — skip the viewer.
python examples/05-chimera-assembly/assembly_sphere_in_cylinder.py --no-view
# Drive the two pieces from TOML configs.
python examples/05-chimera-assembly/assembly_sphere_in_cylinder.py \
--near-body-config examples/05-chimera-assembly/config_assembly_near_body.toml \
--background-config examples/05-chimera-assembly/config_assembly_background.toml \
--no-viewOutput:
examples_out/
sphere.stl # in-memory icosphere
wall_sub0..3.geo # 6 cubed-sphere blocks
wall_cap_north/south.geo
background_center.geo # 5 flat-caps blocks
background_sub_e/n/w/s.geo
assembly.vtm # 11-block ParaView MultiBlock
assembly/ # 11 .vts files (manifest internals)The walkthrough — including the rationale for the parameter choices, ParaView inspection workflow, and the explicit boundary between what SHORE produces and what the downstream solver does — lives in Assembly walkthrough.
examples/05-chimera-assembly/chimera_assembly.py — assembly + Xall cc.par generation
Extends the assembly example by adding adjacency JSON sidecars, VTK MultiBlock exports, and cc.par generation for both meshes. Viewer is off by default; pass --view to open it.
# Generate everything headlessly.
python examples/05-chimera-assembly/chimera_assembly.py
# Custom output directory.
python examples/05-chimera-assembly/chimera_assembly.py -o my_run
# Open the interactive viewer after generation.
python examples/05-chimera-assembly/chimera_assembly.py --viewOutput:
examples_out/
sphere.stl
wall_sub0..3.geo # near-body cubed-sphere blocks
wall_cap_north/south.geo
wall.vtm # near-body ParaView MultiBlock
wall.grd # near-body Xall binary grid
background_center.geo # background flat-caps blocks
background_sub_e/n/w/s.geo
background.vtm # background ParaView MultiBlock
background.grd # background Xall binary grid
wall.adjacency.json
background.adjacency.json
wall.cc.par # near-body Xall cc.par
background.cc.par # background Xall cc.par
assembly.{grd,cc.par,proc.input} # merged Xall jobCLI-only equivalent
examples/05-chimera-assembly/chimera_assembly.sh runs the same pipeline through shore subcommands alone — no Python imports. The Python and bash paths produce byte-identicalassembly.{grd, cc.par, proc.input} outputs:
./examples/05-chimera-assembly/chimera_assembly.sh # default examples_out/
./examples/05-chimera-assembly/chimera_assembly.sh -o my_run
./examples/05-chimera-assembly/chimera_assembly.sh --np 16examples/06-c-grid/visualize_naca0012.py — C-grid around a NACA 0012 wing
Builds a NACA 0012 wing analytically (shore.profiles + shore.shapes), runs the C-grid pipeline (TE detection → span slice → C-curve build → hyperbolic march), and writes a ParaView .vtm.
# Generate naca0012.{stl,geo,vtm}; opens the PyVista viewer at the end.
python examples/06-c-grid/visualize_naca0012.py --generate
# Larger C-grid, finer wake clustering, no viewer.
python examples/06-c-grid/visualize_naca0012.py --generate \
--ni-body 160 --ni-wake 40 --n-stations 8 \
--wake-length 25 --wake-growth 1.20 \
--nk 30 --ds 5e-4 --growth 1.15 --no-viewOutput:
examples_out/
naca0012.stl # extruded wing STL
naca0012.geo # marched C-grid block
naca0012.vtm # ParaView MultiBlockCLI-only equivalent
examples/06-c-grid/visualize_naca0012.sh chains shore profile naca → shore body extrude → shore mesh --topology cgrid → shore export to produce the same outputs (plus the intermediate naca0012.dat Selig section):
./examples/06-c-grid/visualize_naca0012.sh
./examples/06-c-grid/visualize_naca0012.sh --ni-body 160 --nk 30examples/01-body-fitted/tour.ipynb — Jupyter Notebook
A complete notebook covering mesh generation, Jacobian analysis, spacing-law comparison, and the C1 seam-matching demo:
- Sections 0–7: generation, inspection, Jacobian profiles, VTK export
- Sections 8–9: surface and volume spacing distributions
- Section 10: cap/equator C1 seam matching, with side-by-side comparison of compatible and incompatible parameter regimes
jupyter notebook examples/01-body-fitted/tour.ipynbFor headless CI environments:
import pyvista as pv
pv.set_jupyter_backend("static") # no display requiredRunning the Test Suite
Full suite
make testOr directly via pytest:
source .venv/bin/activate
python -m pytest tests/ -vRunning a specific test file
python -m pytest tests/test_cap_gnomonic.py -v # gnomonic cap construction + seam C1
python -m pytest tests/test_jacobian.py -v # Jacobian calculations
python -m pytest tests/test_cli.py -v # CLI commands
python -m pytest tests/test_vtk.py -v # VTK exportRunning a single test
python -m pytest tests/test_cli.py::TestMesh::test_exit_code_ok -vTest file descriptions
| File | What it tests |
|---|---|
tests/conftest.py | Shared fixtures (sphere_stl, sphere_layer) |
tests/test_geo.py | Geo file read/write round-trip fidelity |
tests/test_grd.py | Xall binary .grd writer — format structure, ordering, CLI |
tests/test_vtk.py | VTK XML export correctness |
tests/test_vis.py | PyVista conversion helpers |
tests/test_jacobian.py | Jacobian field calculations and inversion detection |
tests/test_cap_gnomonic.py | Gnomonic cap, seam C0/C1, volume seam continuity |
tests/test_mesh.py | OOP Mesh API, CubedSphereTopology.build |
tests/test_anchor.py / test_load_stl_hygiene.py | Mesh preconditioning |
tests/test_projector.py / test_projector_validation.py | Ray-cast Projector |
tests/test_cli.py | All CLI commands end-to-end |
tests/test_spacing.py | 1D spacing laws, domain builders, error cases |
Filtering tests by keyword
# Run only seam-related tests
python -m pytest tests/ -k seam -v
# Run only the gnomonic cap tests
python -m pytest tests/test_cap_gnomonic.py -vTest parameters
The test suite uses small default grids to keep execution fast. The full suite runs in under 5 seconds on a modern laptop.
For coverage reporting:
python -m pytest tests/ --cov=shore --cov-report=html -vHTML coverage report is written to htmlcov/index.html.
Makefile Targets
All development tasks are available via make:
make dev # install package in editable mode with dev + vis extras
make test # run test suite with pytest
make lint # ruff check + format check (read-only)
make fmt # ruff fix + format (destructive — auto-fixes issues)
make clean # remove build artefacts, .ruff_cache, .pytest_cacheTroubleshooting
pytest collects no tests
If you see No tests collected, ensure:
- You are running from the project root (not inside
tests/). - The
.venvis activated:source .venv/bin/activate. - pytest is installed:
pip show pytest.
Virtual environment issues (WSL2)
On WSL2 the system Python is externally managed. Always use .venv:
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"Visualisation not available
The shore view command and the example script's interactive mode require pyvista:
pip install shore-mesh[vis]Without pyvista, --screenshot and --no-view still work for offline generation.
CI / Automated Environments
For headless CI (no display required):
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
# Lint
make lint
# Test (headless)
export DISPLAY=
make test
# Smoke-generate the example mesh
python examples/01-body-fitted/visualize_cubed_sphere.py --generate --no-view
shore check sphere_sub0.geo
shore info sphere_sub0.geo