Skip to content

Commit d8da454

Browse files
committed
fixed stuck UI results in Bayesian
1 parent 092bd50 commit d8da454

2 files changed

Lines changed: 46 additions & 28 deletions

File tree

‎EasyReflectometryApp/Backends/Py/analysis.py‎

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -662,35 +662,29 @@ def _on_sample_finished(self, results: list) -> None:
662662
"""Handle successful completion of Bayesian sampling."""
663663
if self._is_stale_worker_signal():
664664
return
665-
if not results:
666-
logger.error('Bayesian sampling finished with empty results list')
667-
self._fitting_logic.on_sample_finished()
668-
self._fitter_thread = None
669-
self.fittingChanged.emit()
670-
self.externalFittingChanged.emit()
671-
return
672-
try:
673-
posterior = results[0] # {'draws', 'param_names', 'internal_bumps_object', 'logp'}
674-
self._bayesian_logic.posterior = posterior
675-
self._fitting_logic.on_sample_finished()
676-
self._fitter_thread = None
677-
except Exception:
678-
logger.exception('Error storing Bayesian posterior result')
679-
self._fitter_thread = None
680-
self.fittingChanged.emit()
681-
self.externalFittingChanged.emit()
665+
# {'draws', 'param_names', 'internal_bumps_object', 'logp'}
666+
posterior = results[0] if results else None
667+
if not isinstance(posterior, dict) or 'draws' not in posterior:
668+
# A malformed worker result is a failed run, not a finished one with no posterior.
669+
logger.error('Bayesian sampling returned no usable posterior: %r', results)
670+
self._on_fit_failed('Bayesian sampling returned no posterior')
682671
return
683-
# Phase 2: compute posterior predictive, diagnostics, and rendered plots
684-
try:
685-
self._compute_and_publish_posterior_predictive()
686-
self._compute_diagnostics()
687-
self._render_corner_plot()
688-
self._render_trace_plot()
689-
except Exception:
690-
logger.exception('Error during posterior computation / rendering')
691-
finally:
692-
self.fittingChanged.emit()
693-
self.externalFittingChanged.emit()
672+
self._bayesian_logic.posterior = posterior
673+
self._fitting_logic.on_sample_finished()
674+
self._fitter_thread = None
675+
# Each post-processing step runs on its own, so one failing does not skip the others.
676+
for step in (
677+
self._compute_and_publish_posterior_predictive,
678+
self._compute_diagnostics,
679+
self._render_corner_plot,
680+
self._render_trace_plot,
681+
):
682+
try:
683+
step()
684+
except Exception:
685+
logger.exception('Bayesian post-processing step %s failed', getattr(step, '__name__', step))
686+
self.fittingChanged.emit()
687+
self.externalFittingChanged.emit()
694688

695689
def _compute_and_publish_posterior_predictive(self) -> None:
696690
"""Compute posterior predictive reflectivity and SLD, publish to plotting."""

‎tests/test_analysis_bayesian.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,30 @@ def test_emits_fitting_signals(self, analysis):
970970
assert emissions['fitting'] >= 1
971971
assert emissions['external'] >= 1
972972

973+
@pytest.mark.parametrize('results', [[], [None], [{'param_names': ['thickness']}]])
974+
def test_malformed_result_is_reported_as_failure(self, analysis, results):
975+
analysis._fitter_thread = 'some-worker'
976+
received = []
977+
analysis.fitFailed.connect(received.append)
978+
979+
analysis._on_sample_finished(results)
980+
981+
assert received == ['Bayesian sampling returned no posterior']
982+
assert analysis._fitting_logic.fit_error_message == 'Bayesian sampling returned no posterior'
983+
assert analysis._bayesian_logic.posterior is None
984+
assert analysis._fitter_thread is None
985+
986+
def test_failing_step_does_not_skip_the_others(self, analysis):
987+
with patch.object(analysis, '_compute_and_publish_posterior_predictive', side_effect=RuntimeError('boom')):
988+
with patch.object(analysis, '_compute_diagnostics') as mock_diag:
989+
with patch.object(analysis, '_render_corner_plot') as mock_corner:
990+
with patch.object(analysis, '_render_trace_plot') as mock_trace:
991+
analysis._on_sample_finished([SAMPLE_POSTERIOR_2D])
992+
mock_diag.assert_called_once()
993+
mock_corner.assert_called_once()
994+
mock_trace.assert_called_once()
995+
assert analysis._bayesian_logic.posterior is SAMPLE_POSTERIOR_2D
996+
973997

974998
# ===================================================================
975999
# Bayesian param names

0 commit comments

Comments
 (0)