diff --git a/image_analysis/scale_merge/RotationScaleMerge.cpp b/image_analysis/scale_merge/RotationScaleMerge.cpp index c3071337e..fbdbc000e 100644 --- a/image_analysis/scale_merge/RotationScaleMerge.cpp +++ b/image_analysis/scale_merge/RotationScaleMerge.cpp @@ -4843,7 +4843,9 @@ void RotationScaleMerge::Combine() { size_t n_used = 0; std::vector> part; // the fulls of consecutive runs, in run order - if (observation_dump || nthreads <= 1) { + // No runs at all (a lattice that integrated nothing) takes the serial path too: the chunking below + // divides by the number of threads it would use, which is then 0. + if (observation_dump || nthreads <= 1 || n_run == 0) { // Serial (diagnostic dump needs a single writer). std::ostringstream dump; if (observation_dump) { diff --git a/tests/RotationScaleWalkTest.cpp b/tests/RotationScaleWalkTest.cpp index 9b1c83385..adf23602d 100644 --- a/tests/RotationScaleWalkTest.cpp +++ b/tests/RotationScaleWalkTest.cpp @@ -281,3 +281,20 @@ TEST_CASE("TrimBlankSweepEnds_WeakCrystalKeepsItsEnds", "[RotationScale]") { CHECK(sweep.front().reflections.size() == 200); CHECK(sweep.back().reflections.size() == 200); } + +// A lattice that integrated nothing hands the merge frames without a single reflection. On more than one +// thread the 3D combine split its rocking-curve runs into per-thread chunks by dividing by the number of +// threads it would use - 0 with no runs - and the process died on SIGFPE instead of reporting the empty merge. +TEST_CASE("RotationScaleMerge_NoReflectionsIsAnErrorNotACrash", "[RotationScale]") { + DiffractionExperiment x(DetJF(1)); + x.Goniometer(GoniometerAxis("omega", 0.0f, 0.2f, Coord(1, 0, 0), std::nullopt)); + x.BeamX_pxl(512).BeamY_pxl(256).DetectorDistance_mm(150).IncidentEnergy_keV(WVL_1A_IN_KEV); + x.SetUnitCell(UnitCell(50, 60, 70, 90, 90, 90)); + std::vector outcomes(20); + for (auto &o : outcomes) + o.geom = x.GetDiffractionGeometry(); + Logger logger("RotationScaleMerge_NoReflections"); + RotationScaleMerge rsm(x, outcomes, x.GetUnitCell(), 3, 4, logger); + rsm.Ingest(); + REQUIRE_THROWS_AS(rsm.Run(false, true, false), JFJochException); +}