diff --git a/image_analysis/scale_merge/AnisotropyAnalysis.cpp b/image_analysis/scale_merge/AnisotropyAnalysis.cpp index f3aef175..893389bf 100644 --- a/image_analysis/scale_merge/AnisotropyAnalysis.cpp +++ b/image_analysis/scale_merge/AnisotropyAnalysis.cpp @@ -612,6 +612,10 @@ namespace { // ---------------------------------------------------------------- the s^2 signature struct ShapeFit { + // The regressions below all ran. Distinct from shells: there can be plenty of shells and + // still no fit, and a ShapeFit that returned early carries NaN coefficients with flat and + // convex both false - which reads as a clean "Linear" to anyone testing those two alone. + bool ok = false; int shells = 0; double c0 = NAN, c0_err = NAN, c1 = NAN, slope_through_origin = NAN; double curvature = NAN, curvature_err = NAN, curvature_share = NAN; @@ -771,6 +775,7 @@ namespace { out.convex = !out.flat && out.curvature > 0.0 && out.curvature_err > 0.0 && out.curvature / out.curvature_err > SHAPE_Z && out.curvature_share > CONVEX_SHARE; + out.ok = true; return out; } @@ -1236,14 +1241,17 @@ AnisotropyResult AnalyzeAnisotropy(const std::vector &merged, // --- the resolution signature, and whether the binning decides it --- const ShapeFit shape = FitShape(cells, constrained.b); - result.shape_shells = shape.shells; + // A fit that did not run was fitted over no shells. Reported that way rather than as the shells + // it had available, so the renderer and the cautions below do not print NaN coefficients beside a + // shell count that suggests they mean something. + result.shape_shells = shape.ok ? shape.shells : 0; result.shape_intercept = shape.c0; result.shape_intercept_z = shape.c0_err > 0.0 ? shape.c0 / shape.c0_err : NAN; result.shape_slope = shape.c1; result.shape_curvature_z = shape.curvature_err > 0.0 ? shape.curvature / shape.curvature_err : NAN; result.shape_curvature_share = shape.curvature_share; result.shape_residual = shape.residual; - if (shape.shells >= MIN_SHAPE_SHELLS) { + if (shape.ok && shape.shells >= MIN_SHAPE_SHELLS) { result.shape = shape.flat ? AnisotropyShape::Flat : shape.convex ? AnisotropyShape::Convex : AnisotropyShape::Linear; if (std::isfinite(shape.slope_through_origin) && shape.slope_through_origin != 0.0) { @@ -1262,7 +1270,7 @@ AnisotropyResult AnalyzeAnisotropy(const std::vector &merged, break; } const ShapeFit alt_shape = FitShape(alt_cells, alt_fit.b); - if (alt_shape.shells < MIN_SHAPE_SHELLS || alt_shape.flat != shape.flat) { + if (!alt_shape.ok || alt_shape.shells < MIN_SHAPE_SHELLS || alt_shape.flat != shape.flat) { result.shape_stable = false; break; } @@ -1341,7 +1349,7 @@ AnisotropyResult AnalyzeAnisotropy(const std::vector &merged, result.cautions.emplace_back( "the directional deficit does not follow exp(-1/2 s^T B s), so the fitted deltaB is a fit " "of the wrong functional form and may be an under-estimate"); - if (result.shape == AnisotropyShape::Undetermined && shape.shells >= MIN_SHAPE_SHELLS) + if (result.shape == AnisotropyShape::Undetermined && result.shape_shells >= MIN_SHAPE_SHELLS) result.cautions.emplace_back( "the resolution signature changed when the shells were rebinned, so it is reported as " "undetermined rather than as a measurement"); @@ -1391,7 +1399,11 @@ std::string AnisotropyToText(const AnisotropyResult &result) { os << "\n"; if (std::isfinite(result.fold_weakening)) os << " Strongest / weakest direction at the resolution limit: " << result.fold_weakening << "x\n"; - if (std::isfinite(result.d_min_axis[0]) || std::isfinite(result.d_min_axis[2])) { + // Every axis, not just the outer two: ConeLimits fills each independently, so a crystal whose + // first and third directions fall in cones too sparse to measure - but whose second is well + // covered - would otherwise suppress the whole line, d_min_spread included. + if (std::isfinite(result.d_min_axis[0]) || std::isfinite(result.d_min_axis[1]) + || std::isfinite(result.d_min_axis[2])) { bool any_censored = false; os << " d_min along the principal directions ( = " << CONE_I_OVER_SIGMA << " in a " << CONE_HALF_ANGLE_DEG << " deg cone):";