Skip to content

Commit 3a81a0f

Browse files
sbryngelsonclaude
andcommitted
Fix frame cleanup race, value0 guard, and clean up warnings imports
renderer.py: - Use tempfile.mkdtemp instead of hardcoded _frames/ dir to prevent concurrent-run conflicts when two instances render to the same output dir - Always clean up temporary frame files (move cleanup into finally block so frames are removed even if imageio write fails) silo_reader.py: - Add guard for missing 'value0' key in Silo attr (avoids cryptic KeyError on malformed Silo files; emits a warning and skips the variable) - Move `import warnings` to module scope (remove duplicate inline imports) reader.py: - Move `import warnings` to module scope (remove duplicate inline imports) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 01844d0 commit 3a81a0f

3 files changed

Lines changed: 13 additions & 23 deletions

File tree

toolchain/mfc/viz/reader.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import os
1515
import struct
16+
import warnings
1617
from dataclasses import dataclass, field
1718
from typing import Dict, List, Optional, Tuple
1819

@@ -395,12 +396,10 @@ def assemble(case_dir: str, step: int, fmt: str = 'binary', # pylint: disable=t
395396
for rank in ranks:
396397
fpath = os.path.join(case_dir, 'binary', f'p{rank}', f'{step}.dat')
397398
if not os.path.isfile(fpath):
398-
import warnings # pylint: disable=import-outside-toplevel
399399
warnings.warn(f"Processor file not found, skipping: {fpath}", stacklevel=2)
400400
continue
401401
pdata = read_binary_file(fpath, var_filter=var)
402402
if pdata.m == 0 and pdata.n == 0 and pdata.p == 0:
403-
import warnings # pylint: disable=import-outside-toplevel
404403
warnings.warn(f"Processor p{rank} has zero dimensions, skipping", stacklevel=2)
405404
continue
406405
proc_data.append((rank, pdata))

toolchain/mfc/viz/renderer.py

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
"""
88

99
import os
10+
import tempfile
1011

1112
import numpy as np
12-
1313
import matplotlib
1414
matplotlib.use('Agg')
1515
import matplotlib.pyplot as plt # pylint: disable=wrong-import-position
@@ -197,18 +197,10 @@ def render_mp4(varname, steps, output, fps=10, # pylint: disable=too-many-argum
197197
if auto_vmax is None and all_maxs:
198198
opts['vmax'] = max(all_maxs)
199199

200-
# Write frames as images to a temp directory next to the output file
200+
# Write frames to a unique temp directory to avoid concurrent-run conflicts
201201
output_dir = os.path.dirname(os.path.abspath(output))
202-
viz_dir = os.path.join(output_dir, '_frames')
203-
# Clean stale frames from any interrupted previous run
204-
if os.path.isdir(viz_dir):
205-
for stale in os.listdir(viz_dir):
206-
if stale.endswith('.png'):
207-
try:
208-
os.remove(os.path.join(viz_dir, stale))
209-
except OSError:
210-
pass
211-
os.makedirs(viz_dir, exist_ok=True)
202+
os.makedirs(output_dir, exist_ok=True)
203+
viz_dir = tempfile.mkdtemp(dir=output_dir, prefix='_frames_')
212204

213205
try:
214206
from tqdm import tqdm # pylint: disable=import-outside-toplevel
@@ -246,7 +238,6 @@ def render_mp4(varname, steps, output, fps=10, # pylint: disable=too-many-argum
246238
import imageio # pylint: disable=import-outside-toplevel
247239
except ImportError:
248240
print("imageio is not installed. Install it with: pip install imageio imageio-ffmpeg")
249-
print(f"Frames saved to {viz_dir}/")
250241
return False
251242

252243
writer = None
@@ -261,17 +252,14 @@ def render_mp4(varname, steps, output, fps=10, # pylint: disable=too-many-argum
261252
finally:
262253
if writer is not None:
263254
writer.close()
264-
265-
# Clean up only the frames we created
266-
if success:
255+
# Always clean up temporary frame files
267256
for fname in frame_files:
268-
fpath = os.path.join(viz_dir, fname)
269257
try:
270-
os.remove(fpath)
258+
os.remove(os.path.join(viz_dir, fname))
271259
except OSError:
272260
pass
273261
try:
274262
os.rmdir(viz_dir)
275263
except OSError:
276-
pass # directory not empty (pre-existing files)
264+
pass
277265
return success

toolchain/mfc/viz/silo_reader.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"""
1313

1414
import os
15+
import warnings
1516
from typing import Dict, List, Optional, Tuple
1617

1718
import numpy as np
@@ -115,7 +116,10 @@ def read_silo_file( # pylint: disable=too-many-locals
115116
if "silo" not in obj.attrs:
116117
continue
117118
attr = obj.attrs["silo"]
118-
data_path = attr["value0"]
119+
data_path = attr.get("value0")
120+
if data_path is None:
121+
warnings.warn(f"Variable '{key}' missing 'value0' in silo attr, skipping", stacklevel=2)
122+
continue
119123
data = _resolve_path(f, data_path).astype(np.float64)
120124

121125
# MFC's DBPUTQV1 passes the Fortran column-major array as a
@@ -160,7 +164,6 @@ def assemble_silo(
160164
for rank in ranks:
161165
silo_file = os.path.join(base, f"p{rank}", f"{step}.silo")
162166
if not os.path.isfile(silo_file):
163-
import warnings # pylint: disable=import-outside-toplevel
164167
warnings.warn(f"Processor file not found, skipping: {silo_file}", stacklevel=2)
165168
continue
166169
pdata = read_silo_file(silo_file, var_filter=var)

0 commit comments

Comments
 (0)