From e644347eec00666420cabe53572860955428214d Mon Sep 17 00:00:00 2001 From: Hazen Babcock Date: Tue, 8 Sep 2026 16:30:21 -0400 Subject: [PATCH] Add a headless mode, and stop the frc diagnostic plotting Running the diagnostics one after another stops on a plot window, because frc/frc_calc2d.py has show_plot = True by default and diagnostics/frc/analyze_data.py calls it without saying otherwise, once per test directory. Its command line already has --no-plot; only the programmatic call was missing the equivalent. Two changes, because the immediate cause and the general one are different. The call site now passes show_plot = False. An analyze step that loops over directories should never wait for anyone. And STORM_ANALYSIS_HEADLESS in the environment now selects a non-interactive matplotlib backend, in __init__.py, before pyplot is imported. That makes every pyplot.show() in the package a no-op, all twenty one of them, without touching a single call site. Worth having because the flags that control this are not consistent: frc/frc_calc2d.py show_plot default True multi_plane/plane_weighting no_plots inverted sense rcc/rcc_drift_correction make_plots micrometry/micrometry show, save_as Four names, three spellings, one inverted. Anyone adding a diagnostic has to know which one applies, and the failure is a hung script rather than an error. Figures written to disk are unaffected: savefig() works the same under Agg, and the multicolor diagnostic still produces its three mapping images. Checked rather than assumed, both by writing a file directly and by re-running that diagnostic headless. Verified that the default is unchanged, tkagg here, and that with the variable set the backend is Agg and show() returns instead of blocking. Also isHeadless(), for code that wants to skip building a figure it cannot display. The diagnostics README says how to use it, and, while there, that multiplane/configure.py needs --psf-model. Co-Authored-By: Claude Opus 5 --- storm_analysis/__init__.py | 27 +++++++++++++++++++ storm_analysis/diagnostics/README.txt | 15 +++++++++++ .../diagnostics/frc/analyze_data.py | 4 ++- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/storm_analysis/__init__.py b/storm_analysis/__init__.py index 112d47c0..6b3c4a30 100644 --- a/storm_analysis/__init__.py +++ b/storm_analysis/__init__.py @@ -4,12 +4,39 @@ """ import os import matplotlib + +# +# Headless operation. +# +# Setting STORM_ANALYSIS_HEADLESS in the environment selects a +# non-interactive matplotlib backend, which makes every pyplot.show() in +# the package a no-op. Nothing can then open a window and block, which is +# what you want when running the diagnostics, or anything else, without +# someone sitting there to close the windows. +# +# This has to happen before pyplot is imported, hence its position here. +# Saving figures is not affected, savefig() works the same under Agg. +# +if os.environ.get("STORM_ANALYSIS_HEADLESS"): + matplotlib.use("Agg") + import matplotlib.pyplot as pyplot class SAException(Exception): pass + +def isHeadless(): + """ + Return True if we were asked not to open any plot windows. + + Note that this reports the request, not the outcome. matplotlib will + also fall back to a non-interactive backend on its own when there is + no display available. + """ + return bool(os.environ.get("STORM_ANALYSIS_HEADLESS")) + __version__ = "2026.08.30" # Maybe there is a builtin function that does this?? diff --git a/storm_analysis/diagnostics/README.txt b/storm_analysis/diagnostics/README.txt index 44567365..7d318ef5 100644 --- a/storm_analysis/diagnostics/README.txt +++ b/storm_analysis/diagnostics/README.txt @@ -21,6 +21,21 @@ changing (1) and (3) as needed, to test analysis performance with different type simulated data. +Running without a display: + +Some of the analysis these call will open a plot window and wait for you to close +it, which is not what you want when running several diagnostics in a row. Setting + + STORM_ANALYSIS_HEADLESS=1 + +in the environment selects a non-interactive matplotlib backend, so pyplot.show() +does nothing anywhere in the package. Figures that are written to disk, such as the +mapping images from the multicolor diagnostic, are still written. + +Note that multiplane/configure.py needs --psf-model, one of psf_fft, pupilfn or +spline. The rest take no arguments. + + (Linux) C profiling tools: 1. http://valgrind.org/docs/manual/cl-manual.html 2. http://kcachegrind.sourceforge.net/html/Home.html diff --git a/storm_analysis/diagnostics/frc/analyze_data.py b/storm_analysis/diagnostics/frc/analyze_data.py index 303e3132..3c6b8051 100644 --- a/storm_analysis/diagnostics/frc/analyze_data.py +++ b/storm_analysis/diagnostics/frc/analyze_data.py @@ -22,7 +22,9 @@ def analyzeData(): frc_text = os.path.join(a_dir, "frc.txt") # Run FRC analysis. - frcCalc2d.frcCalc2d(hdf5, frc_text) + # show_plot defaults to True, and this loops over every test + # directory, so without this it stops on a window each time. + frcCalc2d.frcCalc2d(hdf5, frc_text, show_plot = False) print()