v1.0.0-rc.156 (#66)
Build Packages / Unit tests (push) Skipped
Build Packages / build:windows:nocuda (push) Successful in 15m31s
Build Packages / build:viewer-tgz:cpu (push) Successful in 5m46s
Build Packages / build:viewer-tgz:cuda (push) Successful in 6m9s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 9m25s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 10m21s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 9m41s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 9m18s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 10m26s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 11m33s
Build Packages / build:rpm (rocky8) (push) Successful in 10m32s
Build Packages / build:rpm (rocky9) (push) Successful in 12m23s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 10m50s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 10m12s
Build Packages / DIALS test (push) Successful in 12m6s
Build Packages / XDS test (durin plugin) (push) Successful in 8m15s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 7m12s
Build Packages / XDS test (neggia plugin) (push) Successful in 5m35s
Build Packages / Generate python client (push) Successful in 27s
Build Packages / Build documentation (push) Successful in 54s
Build Packages / Create release (push) Skipped
Build Packages / build:windows:cuda (push) Successful in 12m37s

This is an UNSTABLE release. It includes many experimental features, as well as many AI generated fixes. We recommend using rc.152 for production use.

* jfjoch_process: Major rotation (rot3d) data processing overhaul - robust profile-fit integration, Cauchy-loss scaling with optional absorption surface, de-novo indexing and space-group/centering determination fixes, and merging statistics + ISa in the mmCIF output.
* jfjoch_process: Add EXPERIMENTAL ice-ring detection (--detect-ice-rings) that excludes ice reflections from scaling.
* Compression: Add BSHUF_ZSTD_RLE_HUFF, make compression size-aware (drop frames that don't fit rather than aborting), and add the jfjoch_recompress tool.
* jfjoch_viewer: Report "Multiple lattices detected" and grey out "Analyze dataset" on a live connection.
* jfjoch_broker: Write smargon chi/phi goniometer positions to NXmx; read sensor thickness/material from HDF5 metadata.
* CI: Build Windows (CUDA and non-CUDA) installers.Reviewed-on: #66

Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
This commit was merged in pull request #66.
This commit is contained in:
2026-07-03 19:18:56 +02:00
committed by leonarski_f
parent 54c667190f
commit d6389e12da
295 changed files with 9853 additions and 3031 deletions
+56 -13
View File
@@ -3,6 +3,7 @@
#include "../image_analysis/scale_merge/SearchSpaceGroup.h"
#include "gemmi/symmetry.hpp"
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <string>
@@ -126,25 +127,67 @@ TEST_CASE("SearchSpaceGroup detects synthetic space groups") {
const auto merged = GenerateMergedReflectionsForSpaceGroup(sg);
SearchSpaceGroupOptions opt;
opt.crystal_system.reset();
opt.centering = '\0';
opt.merge_friedel = true;
opt.d_min_limit_A = 2.0;
opt.min_operator_cc = 0.99;
opt.min_pairs_per_operator = 20;
opt.min_total_compared = 80;
opt.test_systematic_absences = true;
opt.absence_resolution_bins = 10;
opt.min_absent_reflections_per_bin = 5;
opt.min_allowed_reflections_per_bin = 10;
opt.max_absent_to_allowed_i_over_sigma_ratio = 0.05;
opt.max_absent_to_allowed_i_over_sigma_ratio_in_any_bin = 0.10;
const auto result = SearchSpaceGroup(merged, opt);
// Several inputs cannot be told apart from intensities alone: enantiomorphic partners
// (P4_3 vs P4_1) and origin-ambiguous pairs (I2_12_12_1 vs I222, I2_13 vs I2_3) share
// the same systematic absences. The search reports those as alternatives, so the
// expected group must appear among the best group and its alternatives.
std::vector<std::string> accepted;
if (result.best_space_group.has_value())
accepted.push_back(result.best_space_group->short_name());
for (const auto& alt : result.alternatives)
accepted.push_back(alt.short_name());
INFO(SearchSpaceGroupResultToText(result));
REQUIRE(result.best_space_group.has_value());
CHECK(result.best_space_group->short_name() == tc.expected_short_name);
CHECK(std::find(accepted.begin(), accepted.end(), tc.expected_short_name) != accepted.end());
}
}
}
// Regression: a real screw axis whose systematically-absent reflections carry a genuinely weak
// intensity but an UNDER-estimated sigma (so their I/sigma clears the "present" cut) must still be
// found. Reproduces the monoclinic 2_1 miss on EP_cs_02-10 / EP_cs_01-17, where the merged sigmas on
// the 0k0-odd reflections were ~2x too small and faked screw-axis violations. The E^2 intensity gate
// (present_e_squared) is what keeps those reflections classified absent.
TEST_CASE("SearchSpaceGroup finds a screw axis despite under-estimated sigmas on absent reflections") {
const gemmi::SpaceGroup& sg = gemmi::get_spacegroup_by_name("P 1 21 1");
auto merged = GenerateMergedReflectionsForSpaceGroup(sg, 18);
// Every systematically-absent (0k0, k odd) reflection: small-but-nonzero intensity (~2% of a
// normal reflection) with a far-too-small sigma, so I/sigma ~ 27 fakes a "present" reflection.
const gemmi::GroupOps gops = sg.operations();
int absent_count = 0;
for (auto& r : merged) {
const gemmi::Op::Miller hkl{{r.h, r.k, r.l}};
if (gops.is_systematically_absent(hkl)) {
r.I = 8.0f;
r.sigma = 0.3f;
++absent_count;
}
}
REQUIRE(absent_count >= 8); // enough predicted-absent reflections to be trusted
SearchSpaceGroupOptions opt;
opt.merge_friedel = true;
SECTION("intensity gate on (default): screw recovered") {
const auto result = SearchSpaceGroup(merged, opt);
INFO(SearchSpaceGroupResultToText(result));
REQUIRE(result.best_space_group.has_value());
CHECK(result.best_space_group->short_name() == "P21");
}
SECTION("intensity gate off (I/sigma only): the screw is missed") {
// Documents the failure the gate fixes: with I/sigma alone the too-small sigmas fake
// violations and the search falls back to the symmorphic group.
opt.present_e_squared = 0.0;
const auto result = SearchSpaceGroup(merged, opt);
INFO(SearchSpaceGroupResultToText(result));
REQUIRE(result.best_space_group.has_value());
CHECK(result.best_space_group->short_name() == "P2");
}
}