Skip to content

Commit d94f437

Browse files
sbryngelsonclaude
andcommitted
Fix ghost-cell dedup, 3D memory guard, and interactive host binding
- reader.py: use scale-aware rounding in assemble_from_proc_data to correctly deduplicate ghost-cell overlaps across all domain scales - viz.py: refuse to load >500 timesteps for 3D data to prevent OOM - interactive.py: bind Dash server to 127.0.0.1 instead of 0.0.0.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 45afb8b commit d94f437

3 files changed

Lines changed: 27 additions & 11 deletions

File tree

toolchain/mfc/viz/interactive.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,4 +611,4 @@ def _tf(arr): return arr
611611
f'[bold]http://localhost:{port}[/bold]')
612612
cons.print(f'[dim]SSH tunnel: ssh -L {port}:localhost:{port} <hostname>[/dim]')
613613
cons.print('[dim]Ctrl+C to stop.[/dim]\n')
614-
app.run(debug=False, port=port, host='0.0.0.0')
614+
app.run(debug=False, port=port, host='127.0.0.1')

toolchain/mfc/viz/reader.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ def _is_1d(case_dir: str) -> bool:
278278
return os.path.isdir(os.path.join(case_dir, 'binary', 'root'))
279279

280280

281-
def assemble_from_proc_data( # pylint: disable=too-many-locals
281+
def assemble_from_proc_data( # pylint: disable=too-many-locals,too-many-statements
282282
proc_data: List[Tuple[int, ProcessorData]],
283283
) -> AssembledData:
284284
"""
@@ -315,19 +315,29 @@ def assemble_from_proc_data( # pylint: disable=too-many-locals
315315
z_cc = (pd.z_cb[:-1] + pd.z_cb[1:]) / 2.0 if pd.p > 0 else np.array([0.0])
316316
proc_centers.append((rank, pd, x_cc, y_cc, z_cc))
317317

318-
# Build unique sorted global coordinate arrays (handles ghost overlap)
318+
# Build unique sorted global coordinate arrays (handles ghost overlap).
319+
# Use scale-aware rounding: 12 significant digits relative to the domain
320+
# extent, so precision is preserved for both micro-scale and large domains.
321+
def _dedup(arr):
322+
extent = arr.max() - arr.min()
323+
if extent > 0:
324+
decimals = max(0, int(np.ceil(-np.log10(extent))) + 12)
325+
else:
326+
decimals = 12
327+
return np.unique(np.round(arr, decimals)), decimals
328+
319329
all_x = np.concatenate([xc for _, _, xc, _, _ in proc_centers])
320-
global_x = np.unique(np.round(all_x, 12))
330+
global_x, xdec = _dedup(all_x)
321331
if ndim >= 2:
322332
all_y = np.concatenate([yc for _, _, _, yc, _ in proc_centers])
323-
global_y = np.unique(np.round(all_y, 12))
333+
global_y, ydec = _dedup(all_y)
324334
else:
325-
global_y = np.array([0.0])
335+
global_y, ydec = np.array([0.0]), 12
326336
if ndim >= 3:
327337
all_z = np.concatenate([zc for _, _, _, _, zc in proc_centers])
328-
global_z = np.unique(np.round(all_z, 12))
338+
global_z, zdec = _dedup(all_z)
329339
else:
330-
global_z = np.array([0.0])
340+
global_z, zdec = np.array([0.0]), 12
331341

332342
varnames = sorted({vn for _, pd in proc_data for vn in pd.variables})
333343
nx, ny, nz = len(global_x), len(global_y), len(global_z)
@@ -343,9 +353,9 @@ def assemble_from_proc_data( # pylint: disable=too-many-locals
343353

344354
# Place each processor's data using per-cell coordinate lookup
345355
for _rank, pd, x_cc, y_cc, z_cc in proc_centers:
346-
xi = np.clip(np.searchsorted(global_x, np.round(x_cc, 12)), 0, nx - 1)
347-
yi = np.clip(np.searchsorted(global_y, np.round(y_cc, 12)), 0, ny - 1) if ndim >= 2 else np.array([0])
348-
zi = np.clip(np.searchsorted(global_z, np.round(z_cc, 12)), 0, nz - 1) if ndim >= 3 else np.array([0])
356+
xi = np.clip(np.searchsorted(global_x, np.round(x_cc, xdec)), 0, nx - 1)
357+
yi = np.clip(np.searchsorted(global_y, np.round(y_cc, ydec)), 0, ny - 1) if ndim >= 2 else np.array([0])
358+
zi = np.clip(np.searchsorted(global_z, np.round(z_cc, zdec)), 0, nz - 1) if ndim >= 3 else np.array([0])
349359

350360
for vn, data in pd.variables.items():
351361
if vn not in global_vars:

toolchain/mfc/viz/viz.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,12 @@ def read_step(step):
213213
test_assembled = read_step(requested_steps[0])
214214
avail = sorted(test_assembled.variables.keys())
215215

216+
# Guard against loading too many 3D timesteps (memory)
217+
if test_assembled.ndim == 3 and len(requested_steps) > 500:
218+
raise MFCException(
219+
f"Refusing to load {len(requested_steps)} timesteps for 3D data "
220+
"(limit is 500). Use --step with a range or stride to reduce.")
221+
216222
# Tiled mode only works for 1D
217223
if tiled and not interactive:
218224
if test_assembled.ndim != 1:

0 commit comments

Comments
 (0)