rugnux: several stills inputs in one run; inputs are files, never a directory
Every input is a file named on the command line, read in the order given (a shell glob gives them sorted by name); a directory is refused with the glob form to use instead. - Several Jungfraujoch/NXmx masters of stills are read as one run (JFJochMultiReader): their images one after another, the pixel masks combined, the per-image efficiency placed at each input's images. They must share the detector, wavelength (0.1%), distance (0.1 mm) and beam centre (1 px), since stills geometry refinement refines one geometry for all frames; rotation sweeps are refused. The log gives the indexing rate of each input beside the run's (ProcessResult::image_indexed). - SACLA HDF5 / CXI (JFJochCrystFELReader::ReadFiles, ReadCXI, Open) and XTC (JFJochXTCReader::ReadFiles) take the list of files instead of scanning a directory; CanReadCXI and xtc::CanRead accept only a file; ListFiles and xtc::CollectStreams are gone. - Several inputs must be one format, --mode mx, and stored files (no broker, no --simulate-live); one frame of a CBF/marCCD/SMV sweep still names the whole sweep. Validated: two Jungfraujoch lysozyme stills runs together (49,920 frames, known cell) 57.1% indexed (59.3% / 54.9% per run), 1.22 A, CC1/2 0.986, R_split 8.8%, REFMAC R/R-free 0.253/0.269 against 0.257/0.273 for the earlier hand-built combined master. Rotation: 5reo and lyso_x10sa_90deg_1 unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
This commit is contained in:
1 parent
093069be29
commit
6557ba87f4
18 files changed
+547
-158
No files matched your search
@@ -0,0 +1,105 @@
|
||||
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include "../common/DiffractionExperiment.h"
|
||||
#include "../reader/JFJochMultiReader.h"
|
||||
|
||||
namespace {
|
||||
// A stills collection of n frames held in memory: frame i answers with one spot at x = tag + i, so
|
||||
// which input and which of its frames was read can be told from the spot.
|
||||
class FakeStills : public JFJochReader {
|
||||
uint64_t n;
|
||||
float tag;
|
||||
bool LoadImage_i(std::shared_ptr<JFJochReaderDataset> &, DataMessage &, std::vector<uint8_t> &,
|
||||
int64_t, bool) override { return false; }
|
||||
public:
|
||||
FakeStills(uint64_t n_, float tag_, const DiffractionExperiment &x, std::vector<uint32_t> mask,
|
||||
std::vector<float> efficiency = {}) : n(n_), tag(tag_) {
|
||||
auto d = std::make_shared<JFJochReaderDataset>();
|
||||
d->experiment = x;
|
||||
d->pixel_mask = std::make_shared<const PixelMask>(mask);
|
||||
d->efficiency = std::move(efficiency);
|
||||
SetStartMessage(d);
|
||||
}
|
||||
[[nodiscard]] uint64_t GetNumberOfImages() const override { return n; }
|
||||
void Close() override {}
|
||||
bool ReadRawImage(int64_t, JFJochReaderRawImage &) override { return true; }
|
||||
std::vector<SpotToSave> ReadSpots(int64_t image) const override {
|
||||
SpotToSave s;
|
||||
s.x = tag + static_cast<float>(image);
|
||||
return {s};
|
||||
}
|
||||
};
|
||||
|
||||
DiffractionExperiment Stills() {
|
||||
DiffractionExperiment x(DetJF(1));
|
||||
x.BeamX_pxl(500).BeamY_pxl(250).DetectorDistance_mm(100).IncidentEnergy_keV(12.4);
|
||||
return x;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> EmptyMask(const DiffractionExperiment &x) {
|
||||
return std::vector<uint32_t>(x.GetXPixelsNum() * x.GetYPixelsNum(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JFJochMultiReader_Concatenates", "[JFJochMultiReader][portable]") {
|
||||
const auto x = Stills();
|
||||
auto mask_a = EmptyMask(x);
|
||||
auto mask_b = EmptyMask(x);
|
||||
mask_a[3] = 1;
|
||||
mask_b[7] = 4;
|
||||
|
||||
std::vector<std::unique_ptr<JFJochReader>> inputs;
|
||||
inputs.push_back(std::make_unique<FakeStills>(3, 100.0f, x, mask_a, std::vector<float>{0.5f, 0.5f, 0.5f}));
|
||||
inputs.push_back(std::make_unique<FakeStills>(2, 200.0f, x, mask_b));
|
||||
JFJochMultiReader reader(std::move(inputs));
|
||||
|
||||
REQUIRE(reader.GetNumberOfImages() == 5);
|
||||
REQUIRE(reader.GetNumberOfInputs() == 2);
|
||||
CHECK(reader.FirstImage(1) == 3);
|
||||
CHECK(reader.ImagesInInput(1) == 2);
|
||||
|
||||
CHECK(reader.ReadSpots(0)[0].x == Catch::Approx(100.0f));
|
||||
CHECK(reader.ReadSpots(2)[0].x == Catch::Approx(102.0f));
|
||||
CHECK(reader.ReadSpots(3)[0].x == Catch::Approx(200.0f));
|
||||
CHECK(reader.ReadSpots(4)[0].x == Catch::Approx(201.0f));
|
||||
CHECK_THROWS(reader.ReadSpots(5));
|
||||
|
||||
const auto dataset = reader.GetDataset();
|
||||
const auto &mask = dataset->pixel_mask->GetMask();
|
||||
CHECK(mask[3] == 1);
|
||||
CHECK(mask[7] == 4);
|
||||
CHECK(mask[5] == 0);
|
||||
|
||||
// The second input states no efficiency: its frames count as fully collected.
|
||||
REQUIRE(dataset->efficiency.size() == 5);
|
||||
CHECK(dataset->efficiency[2] == Catch::Approx(0.5f));
|
||||
CHECK(dataset->efficiency[3] == Catch::Approx(1.0f));
|
||||
}
|
||||
|
||||
TEST_CASE("JFJochMultiReader_RefusesOtherGeometry", "[JFJochMultiReader][portable]") {
|
||||
const auto x = Stills();
|
||||
auto other = Stills();
|
||||
|
||||
SECTION("distance") { other.DetectorDistance_mm(101); }
|
||||
SECTION("beam centre") { other.BeamX_pxl(503); }
|
||||
SECTION("wavelength") { other.IncidentEnergy_keV(12.6); }
|
||||
|
||||
std::vector<std::unique_ptr<JFJochReader>> inputs;
|
||||
inputs.push_back(std::make_unique<FakeStills>(1, 0.0f, x, EmptyMask(x)));
|
||||
inputs.push_back(std::make_unique<FakeStills>(1, 0.0f, other, EmptyMask(other)));
|
||||
CHECK_THROWS(JFJochMultiReader(std::move(inputs)));
|
||||
}
|
||||
|
||||
TEST_CASE("JFJochMultiReader_RefusesRotation", "[JFJochMultiReader][portable]") {
|
||||
const auto x = Stills();
|
||||
auto rotation = Stills();
|
||||
rotation.Goniometer(GoniometerAxis("omega", 0.0f, 0.1f, Coord(1, 0, 0), {}));
|
||||
|
||||
std::vector<std::unique_ptr<JFJochReader>> inputs;
|
||||
inputs.push_back(std::make_unique<FakeStills>(1, 0.0f, x, EmptyMask(x)));
|
||||
inputs.push_back(std::make_unique<FakeStills>(1, 0.0f, rotation, EmptyMask(rotation)));
|
||||
CHECK_THROWS(JFJochMultiReader(std::move(inputs)));
|
||||
}
|
||||
Reference in new issue
Block a user