Wolfresults_2D & wolfres2DGPU — 2D simulation results
``Wolfresults_2D`` — base class for WOLF2D results (CPU multi-block)
``wolfres2DGPU`` — GPU-specific subclass (single-block,
ResultsStore-backed)``OneWolfResult`` — per-block container (waterdepth, qx, qy, topography, …)
``views_2D`` — enum of available result views
Architecture
Wolfresults_2D (or wolfres2DGPU)
├── myblocks: dict[str, OneWolfResult]
│ └── 'block1' -> OneWolfResult
│ ├── .waterdepth (WolfArray)
│ ├── .qx (WolfArray) — discharge X [m²/s]
│ ├── .qy (WolfArray) — discharge Y [m²/s]
│ ├── .top (WolfArray) — topography
│ └── .rough_n (WolfArray) — Manning coefficient
├── head_blocks: dict[str, header_wolf]
├── times: list[float] — real times [s]
├── current_result: int — currently loaded step (0-based)
├── epsilon: float — wet/dry threshold
└── mypal: wolfpalette — color palette
[1]:
from wolfhece.wolfresults_2D import Wolfresults_2D, views_2D, OneWolfResult
from wolfhece.Results2DGPU import wolfres2DGPU
from wolfhece.wolf_array import WolfArray, getkeyblock
views_2D — available result views
The views_2D enum defines all possible computed views. The most commonly used are shown below.
[2]:
# List all views
for v in views_2D:
print(f"{v.name:40s} {v.value}")
WATERDEPTH Water depth [m]
WATERLEVEL Water level [m]
TOPOGRAPHY Bottom level [m]
QX Discharge X [m2s-1]
QY Discharge Y [m2s-1]
QNORM Discharge norm [m2s-1]
UX Velocity X [ms-1]
UY Velocity Y [ms-1]
UNORM Velocity norm [ms-1]
HEAD Head [m]
FROUDE Froude [-]
KINETIC_ENERGY Kinetic energy k
EPSILON Rate of dissipation e
TURB_VISC_2D Turbulent viscosity 2D
TURB_VISC_3D Turbulent viscosity 3D
VECTOR_FIELD_Q Discharge vector field
VECTOR_FIELD_U Velocity vector field
U_SHEAR Shear velocity [ms-1]
SHIELDS_NUMBER Shields number - Manning-Strickler
CRITICAL_DIAMETER_SHIELDS Critical grain diameter - Shields
CRITICAL_DIAMETER_IZBACH Critical grain diameter - Izbach
CRITICAL_DIAMETER_SUSPENSION_50 Critical grain diameter - Suspension 50%
CRITICAL_DIAMETER_SUSPENSION_100 Critical grain diameter - Suspension 100%
QNORM_FIELD Q norm + Field
UNORM_FIELD U norm + Field
WL_Q WL + Q
WD_Q WD + Q
WL_U WL + U
WD_U WD + U
T_WL_Q Top + WL + Q
T_WD_Q Top + WD + Q
T_WD_U Top + WD + U
getkeyblock — block key convention
getkeyblock(i, addone=True) converts a 0-based Python index to a block key string:
getkeyblock(0)→'block1'getkeyblock(1)→'block2'
With addone=False, the number is used as-is:
getkeyblock(1, False)→'block1'
[3]:
print(f"getkeyblock(0) = '{getkeyblock(0)}'")
print(f"getkeyblock(1) = '{getkeyblock(1)}'")
print(f"getkeyblock(1, False) = '{getkeyblock(1, False)}'")
getkeyblock(0) = 'block1'
getkeyblock(1) = 'block2'
getkeyblock(1, False) = 'block1'
Loading GPU results with wolfres2DGPU
wolfres2DGPU can load results from:
A local directory containing
parameters.jsonandsimul_gpu_results/An HTTP(S) URL (triggers automatic download via
download_gpu_simulation)A pre-built ``ResultsStore`` (via the
storeparameter)
GPU simulations are always single-block ('block1').
[4]:
from wolfhece.pydownloader import toys_gpu_dataset, DATADIR
# Download a toy GPU simulation (cached)
store = toys_gpu_dataset('channel_w_archbridge_fully_man004')
print(f"ResultsStore: {store.nb_results} saved time steps")
# Load into wolfres2DGPU
sim_path = DATADIR / 'channel_w_archbridge_fully_man004'
sim = wolfres2DGPU(str(sim_path), eps=0.01)
print(f"\nBlocks: {list(sim.myblocks.keys())}")
print(f"Epsilon (wet/dry threshold): {sim.epsilon}")
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\NAP.npy already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\bathymetry.npy already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\bridge_roof.npy already exists. Skipping download.
ERROR:root:HTTP error occurred while downloading https://gitlab.uliege.be/HECE/wolfgpu_examples/-/raw/main/channel_w_archbridge_fully_man004/bridge_deck.npy: 404 Client Error: Not Found for url: https://gitlab.uliege.be/HECE/wolfgpu_examples/-/raw/main/channel_w_archbridge_fully_man004/bridge_deck.npy
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\h.npy already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\manning.npy already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\qx.npy already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\qy.npy already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\parameters.json already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\metadata.json already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\nap.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\nb_results.txt already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\sim_times.csv already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\h_0000001.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\qx_0000001.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\qy_0000001.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\h_0000002.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\qx_0000002.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\qy_0000002.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\h_0000003.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\qx_0000003.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\qy_0000003.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\h_0000004.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\qx_0000004.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\qy_0000004.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\h_0000005.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\qx_0000005.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\qy_0000005.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\h_0000006.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\qx_0000006.npz already exists. Skipping download.
INFO:root:File C:\Users\pierre\Documents\Gitlab\HECEPython\wolfhece\data\downloads\channel_w_archbridge_fully_man004\simul_gpu_results\qy_0000006.npz already exists. Skipping download.
ResultsStore: 6 saved time steps
Blocks: ['block1']
Epsilon (wet/dry threshold): 0.01
Querying time steps
[5]:
nb = sim.get_nbresults()
print(f"Number of saved time steps: {nb}")
# Get all real times and calculation step numbers
times = sim.times
print(f"\nFirst 5 times [s]: {times[:5]}")
print(f"Last time [s]: {times[-1]}")
Number of saved time steps: 6
First 5 times [s]: [0.0, 40.60536419227719, 81.21073305234313, 121.81609793752432, 162.4214591383934]
Last time [s]: 203.02682216465473
Reading a single time step
read_oneresult(which) loads a specific saved step (0-based) into the myblocks arrays. Pass -1 for the last step.
[6]:
# Read the last time step
sim.read_oneresult(-1)
print(f"Loaded step: {sim.current_result}")
# Access the block
block = sim.myblocks[getkeyblock(1, False)]
# Water depth statistics
h = block.waterdepth
print(f"\nWater depth array: {h.nbx} x {h.nby}")
print(f" max h = {h.array.max():.3f} m")
print(f" wet cells = {h.nbnotnull}")
# Discharges
print(f"\nMax |qx| = {abs(block.qx.array).max():.3f} m\u00b2/s")
print(f"Max |qy| = {abs(block.qy.array).max():.3f} m\u00b2/s")
Loaded step: 5
Water depth array: 500 x 21
max h = 8.042 m
wet cells = 8466
Max |qx| = 6.845 m²/s
Max |qy| = 2.085 m²/s
Switching views
set_currentview(views_2D.XXX) recomputes the active display array (e.g. velocity from h and q). The result is then accessible via as_WolfArray().
[7]:
# Switch to velocity norm
sim.set_currentview(views_2D.UNORM)
v_array = sim.as_WolfArray(copyarray=True)
print(f"Velocity norm: max = {v_array.array.max():.3f} m/s")
# Switch to water level (h + topography)
sim.set_currentview(views_2D.WATERLEVEL)
wl_array = sim.as_WolfArray(copyarray=True)
print(f"Water level: max = {wl_array.array.max():.3f} m")
# Switch back to water depth
sim.set_currentview(views_2D.WATERDEPTH)
Velocity norm: max = 0.856 m/s
Water level: max = 8.042 m
Exporting as WolfArray
as_WolfArray(copyarray=True) creates a standalone WolfArray from the current view. This is useful for saving, plotting, or further processing.
[8]:
# Export current view as a WolfArray
wa = sim.as_WolfArray(copyarray=True)
print(f"Exported WolfArray: {wa.nbx} x {wa.nby}")
print(f"dx={wa.dx}, dy={wa.dy}")
print(f"Origin: ({wa.origx:.0f}, {wa.origy:.0f})")
# Save to disk
import tempfile
from pathlib import Path
tmpdir = Path(tempfile.mkdtemp())
wa.write_all(str(tmpdir / 'waterdepth_last.bin'))
print(f"\nSaved to: {tmpdir / 'waterdepth_last.bin'}")
Exported WolfArray: 500 x 21
dx=1.0, dy=1.0
Origin: (0, 0)
Saved to: C:\Users\pierre\AppData\Local\Temp\tmptth17b7q\waterdepth_last.bin
Topography access
[9]:
# Topography for the block
top = sim.get_top_for_block(getkeyblock(1, False))
print(f"Topography: min = {top.array.min():.2f} m, max = {top.array.max():.2f} m")
Topography: min = 0.00 m, max = 100.00 m
Epsilon — wet/dry threshold
The epsilon attribute controls which cells are considered “wet”:
epsilon > 0: cells withh < epsilonare maskedepsilon == 0: only cells withh == 0are masked
Changing epsilon and re-reading updates the mask.
[10]:
# Default epsilon
print(f"Current epsilon: {sim.epsilon}")
sim.read_oneresult(-1)
wet_default = sim.myblocks[getkeyblock(1, False)].waterdepth.nbnotnull
print(f"Wet cells (eps={sim.epsilon}): {wet_default}")
# Increase epsilon to filter thin water layers
sim.epsilon = 0.1
sim.read_oneresult(-1)
wet_filtered = sim.myblocks[getkeyblock(1, False)].waterdepth.nbnotnull
print(f"Wet cells (eps={sim.epsilon}): {wet_filtered}")
# Reset
sim.epsilon = 0.01
Current epsilon: 0.01
Wet cells (eps=0.01): 8466
Wet cells (eps=0.1): 8466
Iterating over time steps
[11]:
# Read every 10th step and track maximum water depth
import numpy as np
nb = sim.get_nbresults()
step_indices = range(0, nb, max(1, nb // 10)) # ~10 samples
max_depths = []
for i in step_indices:
sim.read_oneresult(i)
h_max = sim.myblocks[getkeyblock(1, False)].waterdepth.array.max()
max_depths.append((sim.times[i], float(h_max)))
print(f"{'Time [s]':>10s} {'Max h [m]':>10s}")
print('-' * 22)
for t, hm in max_depths:
print(f"{t:10.1f} {hm:10.3f}")
Time [s] Max h [m]
----------------------
0.0 8.042
40.6 8.042
81.2 8.042
121.8 8.042
162.4 8.042
203.0 8.042
In-memory cache (GPU only)
setup_cache() preloads a range of results into RAM for faster repeated access. Use only_h=True to load only water depth and save memory.
[12]:
# Cache the first 20 steps
sim.setup_cache(start_idx=0, end_idx=20, only_h=True)
# Reads from cache (fast)
sim.read_oneresult(5)
print(f"Step 5 from cache: max h = {sim.myblocks[getkeyblock(1, False)].waterdepth.array.max():.3f} m")
# Release cache
sim.clear_cache()
print("Cache cleared")
INFO:root:Estimated memory size for one result: 0.07 MB
Step 5 from cache: max h = 8.042 m
Cache cleared
Spatial sub-region reading (GPU only)
read_oneresult_subarray() loads only a spatial sub-region, which is faster when the full domain is large and you only need a local area.
Bounds can be:
[[xmin, xmax], [ymin, ymax]]— list of listsA
vectorobject (uses its bounding box)A tuple
((xmin, xmax), (ymin, ymax))
Important: the actual loaded region may differ slightly from the requested bounds. A safety border (default max(dx, dy)) is added, and world coordinates are converted to cell indices via floor(), so the loaded footprint is always aligned to the grid and can be a few cells larger than requested. All cells outside the sub-region are set to the null value (or left untouched if nullify_all_outside=False).
[ ]:
# Read only a sub-region
block = sim.myblocks[getkeyblock(1, False)]
bounds = block.waterdepth.get_bounds()
xmid = (bounds[0][0] + bounds[0][1]) / 2
ymid = (bounds[1][0] + bounds[1][1]) / 2
# Define a small window around the center.
# Note: the actually loaded region may be slightly larger than the requested
# bounds because coordinates are converted to cell indices via floor(), and a
# safety border (default = max(dx, dy)) is added on each side.
# The data outside the loaded sub-region is set to the null value.
sub_bounds = [[xmid - 50, xmid + 50],
[ymid - 50, ymid + 50]]
sim.read_oneresult_subarray(which=-1, bounds=sub_bounds)
wet = sim.myblocks[getkeyblock(1, False)].waterdepth.nbnotnull
print(f"Wet cells in sub-region: {wet}")
Wet cells in sub-region: 412
Danger maps
Danger maps iterate over all (or a subset of) time steps and compute maximum envelopes and temporal indicators.
Returns a tuple of 9 WolfArray:
Index |
Name |
Description |
|---|---|---|
0 |
H |
Maximum water depth [m] |
1 |
U |
Maximum velocity norm [m/s] |
2 |
Q |
Maximum momentum norm [m:nbsphinx-math:`u0`0b2/s] |
3 |
Z |
Maximum water level [m] |
4 |
Head |
Maximum total head [m] |
5 |
ToA |
Time of arrival [s] |
6 |
ToM |
Time of maximum [s] |
7 |
DoI |
Duration of inundation [s] |
8 |
ToE |
Time of ending [s] |
[14]:
# Compute danger maps (using every 5th step for speed)
H, U, Q, Z, Head, ToA, ToM, DoI, ToE = sim.danger_map(
start=0, end=-1, every=5, hmin=0.01
)
print(f"Max water depth: {H.array.max():.3f} m")
print(f"Max velocity: {U.array.max():.3f} m/s")
print(f"Max momentum: {Q.array.max():.3f} m\u00b2/s")
print(f"Max water level: {Z.array.max():.3f} m")
print(f"Time of arrival: min = {ToA.array.min():.0f} s (excluding dry)")
100%|██████████| 2/2 [00:00<00:00, 116.29it/s]
Max water depth: 8.042 m
Max velocity: 0.856 m/s
Max momentum: 6.846 m²/s
Max water level: 8.042 m
Time of arrival: min = 203 s (excluding dry)
GPU-optimized tiled danger maps
danger_map_gpu_tiled() operates directly on tiled GPU arrays without per-step untiling, which is significantly faster for large domains. Same return format.
[15]:
# Tiled version (GPU only, faster)
try:
H2, U2, Q2, Z2, Head2, ToA2, ToM2, DoI2, ToE2 = sim.danger_map_gpu_tiled(
start=0, end=-1, every=5, hmin=0.01
)
if H2 is not None:
print(f"Tiled max water depth: {H2.array.max():.3f} m")
else:
print("Tile packer not available (requires wolfgpu >= 1.4.0)")
except Exception as e:
print(f"Tiled danger map not available: {e}")
100%|██████████| 2/2 [00:00<00:00, 420.52it/s]
Tiled max water depth: 8.042 m
Loading CPU results
CPU simulations use Wolfresults_2D directly and may have multiple blocks (multi-domain models).
from wolfhece.wolfresults_2D import Wolfresults_2D
res = Wolfresults_2D(fname='path/to/simul')
nb = res.get_nbresults()
res.read_oneresult(10) # 0-based
# Access blocks
for key, block in res.myblocks.items():
print(f"{key}: {block.waterdepth.nbx} x {block.waterdepth.nby}")
# Multi-block export
wa_mb = res.as_WolfArray(copyarray=True, force_mb=True) # WolfArrayMB
Loading from a URL (GPU)
wolfres2DGPU can download a simulation directly from a URL:
sim = wolfres2DGPU('https://gitlab.uliege.be/.../my_simulation/')
This calls download_gpu_simulation internally, downloads all input maps and result files to DATADIR, and loads the simulation.
GPU metadata properties
[16]:
# Access time series metadata from ResultsStore
try:
dt_series = sim.all_dt
print(f"Time step series length: {len(dt_series)}")
print(f"First few dt values: {dt_series[:5]}")
except Exception as e:
print(f"Metadata not available: {e}")
Time step series length: 6
First few dt values: [0.0, 1000.0, 2000.0, 3000.0, 4000.0]
Independent zone filtering
When to_filter_independent = True, each read_oneresult call automatically keeps only the largest connected wet zone(s), removing isolated puddles.
[17]:
# Enable independent zone filtering
sim.to_filter_independent = True
sim.read_oneresult(-1)
wet_filtered = sim.myblocks[getkeyblock(1, False)].waterdepth.nbnotnull
sim.to_filter_independent = False
sim.read_oneresult(-1)
wet_all = sim.myblocks[getkeyblock(1, False)].waterdepth.nbnotnull
print(f"Wet cells without filtering: {wet_all}")
print(f"Wet cells with filtering: {wet_filtered}")
Wet cells without filtering: 8466
Wet cells with filtering: 8466
Summary
Task |
Code |
|---|---|
Load GPU results |
|
Load CPU results |
|
Number of steps |
|
Read a step |
|
Get times |
|
Switch view |
|
Export to WolfArray |
|
Get topography |
|
Set threshold |
|
Sub-region read |
|
Danger map |
|
Tiled danger map |
|
Cache results |
|
Filter islands |
|
See also:
toys_dataset — downloading example simulations
WolfArray — array manipulation
Synthetic dike — generating dike geometry