Panel id on spots and the panel table in the start message
SpotToSave gains int16_t panel (-1 = no panel table). It sits in the padding after the two bools, so sizeof(SpotToSave) stays 80. It is serialised only where it is set: CBOR spot key "panel", HDF5 /entry/MX/peakPanel (data file, plus a virtual dataset in a VDS master when the start message has panels), read back by HDF5MetadataSource. For a flat detector none of these is written, so files and streams for existing data are byte-for-byte unchanged. The start message carries the panel table (detector_modules, CBOR key absent when empty), filled from the experiment; JFJochHttpReader takes it, so a stream viewer knows the panels too. AssignModules takes the spot's panel id where it has one - the stored pixel box it was found in - and falls back to the plane position otherwise (every spot until per-panel spot finding sets the id). Reflection deliberately gets no panel id (owner decision): it is needed only between prediction and integration, and the struct feeds GPU layout and memory budgets. Tests: CBORSerialize_SpotPanelId, CBORSerialize_StartDetectorModules, JFJochReader_PanelId (VDS/legacy/integrated, with and without panels). 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
c8fb472f4d
commit
dc784c073d
15 files changed
+274
-4
No files matched your search
@@ -652,6 +652,7 @@ SpotFindingSettings DiffractionExperiment::DefaultDataProcessingSettings() {
|
||||
|
||||
void DiffractionExperiment::FillMessage(StartMessage &message) const {
|
||||
message.images_per_file = GetImagesPerFile();
|
||||
message.detector_modules = GetModules().Get();
|
||||
message.beam_center_x = GetBeamX_pxl();
|
||||
message.beam_center_y = GetBeamY_pxl();
|
||||
const auto direct_beam = GetDiffractionGeometry().GetDirectBeam_pxl();
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "CrystalLattice.h"
|
||||
#include "IndexingSettings.h"
|
||||
#include "XrayFluorescenceSpectrum.h"
|
||||
#include "DetectorModules.h"
|
||||
#include "../gemmi_gph/gemmi/symmetry.hpp"
|
||||
|
||||
constexpr const uint64_t user_data_release = 6;
|
||||
@@ -324,6 +325,10 @@ struct StartMessage {
|
||||
// Per-pixel ROI bitmask (converted geometry), bit i set for ROI i in rois.
|
||||
std::vector<uint16_t> roi_map;
|
||||
|
||||
// The detector's panels (DetectorModules.h), where the image's pixels are on the detector plane. Empty
|
||||
// for one flat detector, the image being the plane; then the CBOR key is absent.
|
||||
std::vector<DetectorModule> detector_modules;
|
||||
|
||||
std::map<std::string, float> threshold_energy;
|
||||
|
||||
std::optional<float> total_flux;
|
||||
|
||||
@@ -18,6 +18,7 @@ struct SpotToSave {
|
||||
float dist_ewald_sphere = 0.0;
|
||||
bool ice_ring = false;
|
||||
bool indexed = false;
|
||||
int16_t panel = -1; // index into the experiment's DetectorModules; -1 = no panel table
|
||||
|
||||
Coord ReciprocalCoord(const DiffractionGeometry &experiment) const;
|
||||
};
|
||||
@@ -32,6 +32,7 @@ There are minor differences at the moment:
|
||||
| mirror_y | bool | Whether the assembled image is mirrored in Y relative to the detector's raw readout order. True is the MX convention - row 0 at the top of the detector seen from the sample - and is what absence of the key means | |
|
||||
| detector_orientation_mirror_y | bool | Whether the assembled image is mirrored in Y relative to the frame the PONI angles are stated in. A different setting from `mirror_y` above, which is about the module layout; this one changes no pixel. Absence means false | |
|
||||
| detector_orientation_quarter_turns | int | How many multiples of 90 degrees about the beam the assembled image is turned by, relative to the frame the PONI angles are stated in (0-3). Absence means 0 | |
|
||||
| detector_modules | Array(object) (optional) | Panels: where the image's pixels lie on the detector plane the geometry describes. Each has `x0`, `y0`, `width`, `height` (int, the panel's box in the image) and `ox`, `oy`, `fx`, `fy`, `sx`, `sy` (double, plane pixels): image pixel (x, y) of the panel is at `(ox, oy) + (x - x0) (fx, fy) + (y - y0) (sx, sy)`. Absent for a flat detector, the image being the plane; spot and reflection positions are always plane coordinates | |
|
||||
| incident_energy | float | X-ray energy \[eV\] | X |
|
||||
| incident_wavelength | float | X-ray wavelength \[Angstrom\] | X |
|
||||
| incident_wavelength_spread | float (optional) | FWHM of the X-ray wavelength distribution \[Angstrom\] (NXmx incident_wavelength_spread); omitted when the beam is monochromatic | |
|
||||
@@ -150,6 +151,7 @@ See [DECTRIS documentation](https://github.com/dectris/documentation/tree/main/s
|
||||
| - indexed | bool | indexed solution | | |
|
||||
| - latt | int64 | Lattice to which the peak belongs (negative number = not indexed) | | |
|
||||
| - image | int64 | image number the spot belongs to | | |
|
||||
| - panel | int64 (optional) | panel (index into `detector_modules`) the spot was found on; absent for a detector without a panel table | | |
|
||||
| - h | int64 | Miller index (indexed spots only) | | |
|
||||
| - k | int64 | Miller index (indexed spots only) | | |
|
||||
| - l | int64 | Miller index (indexed spots only) | | |
|
||||
|
||||
@@ -342,6 +342,7 @@ In legacy/VDS mode these live in the data files and are linked/virtual-stacked i
|
||||
| `peakDistEwaldSphere` | Å⁻¹ | distance of the spot from the Ewald sphere | ✓ |
|
||||
| `peakIndexed` | | spot fits the indexing solution | ✓ |
|
||||
| `peakLattice` | | lattice the spot belongs to (`-1` = unindexed) | ✓ |
|
||||
| `peakPanel` | | panel the spot was found on (`-1` = none); written only for a detector with a panel table | |
|
||||
|
||||
**Per-image vectors `[n_images]`:**
|
||||
|
||||
|
||||
@@ -59,6 +59,16 @@ namespace {
|
||||
throw JFJochException(JFJochExceptionCategory::CBORError, "Float expected");
|
||||
}
|
||||
|
||||
double GetCBORDouble(CborValue &value) {
|
||||
if (cbor_value_is_double(&value)) {
|
||||
double tmp;
|
||||
cborErr(cbor_value_get_double(&value, &tmp));
|
||||
cborErr(cbor_value_advance(&value));
|
||||
return tmp;
|
||||
}
|
||||
return GetCBORFloat(value);
|
||||
}
|
||||
|
||||
bool GetCBORBool(CborValue &value) {
|
||||
if (!cbor_value_is_boolean(&value)) {
|
||||
throw JFJochException(JFJochExceptionCategory::CBORError,
|
||||
@@ -514,6 +524,8 @@ namespace {
|
||||
s.dist_ewald_sphere = GetCBORFloat(map_value);
|
||||
else if (key == "image")
|
||||
s.image = GetCBORInt(map_value);
|
||||
else if (key == "panel")
|
||||
s.panel = static_cast<int16_t>(GetCBORInt(map_value));
|
||||
else
|
||||
cbor_value_advance(&map_value);
|
||||
}
|
||||
@@ -1299,6 +1311,45 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
void GetDetectorModules(CborValue &value, std::vector<DetectorModule> &modules) {
|
||||
const size_t array_len = GetCBORArrayLen(value);
|
||||
CborValue array_value;
|
||||
cborErr(cbor_value_enter_container(&value, &array_value));
|
||||
for (size_t i = 0; i < array_len; i++) {
|
||||
DetectorModule m;
|
||||
CborValue map_value;
|
||||
cborErr(cbor_value_enter_container(&array_value, &map_value));
|
||||
while (!cbor_value_at_end(&map_value)) {
|
||||
const auto key = GetCBORString(map_value);
|
||||
if (key == "x0")
|
||||
m.x0 = GetCBORInt(map_value);
|
||||
else if (key == "y0")
|
||||
m.y0 = GetCBORInt(map_value);
|
||||
else if (key == "width")
|
||||
m.width = GetCBORInt(map_value);
|
||||
else if (key == "height")
|
||||
m.height = GetCBORInt(map_value);
|
||||
else if (key == "ox")
|
||||
m.ox = GetCBORDouble(map_value);
|
||||
else if (key == "oy")
|
||||
m.oy = GetCBORDouble(map_value);
|
||||
else if (key == "fx")
|
||||
m.fx = GetCBORDouble(map_value);
|
||||
else if (key == "fy")
|
||||
m.fy = GetCBORDouble(map_value);
|
||||
else if (key == "sx")
|
||||
m.sx = GetCBORDouble(map_value);
|
||||
else if (key == "sy")
|
||||
m.sy = GetCBORDouble(map_value);
|
||||
else
|
||||
cbor_value_advance(&map_value);
|
||||
}
|
||||
cborErr(cbor_value_leave_container(&array_value, &map_value));
|
||||
modules.push_back(m);
|
||||
}
|
||||
cborErr(cbor_value_leave_container(&value, &array_value));
|
||||
}
|
||||
|
||||
bool ProcessStartMessageElement(StartMessage &message, CborValue &value) {
|
||||
if (cbor_value_at_end(&value))
|
||||
return false;
|
||||
@@ -1370,6 +1421,8 @@ namespace {
|
||||
ProcessAzintMapElement(message, value);
|
||||
else if (key == "roi_map")
|
||||
ProcessROIMapElement(message, value);
|
||||
else if (key == "detector_modules")
|
||||
GetDetectorModules(value, message.detector_modules);
|
||||
else if (key == "channels")
|
||||
GetCBORStringArray(value, message.channels);
|
||||
else if (key == "detector_translation")
|
||||
|
||||
@@ -238,6 +238,8 @@ inline void CBOR_ENC(CborEncoder &encoder, const SpotToSave& spot) {
|
||||
CBOR_ENC(mapEncoder, "indexed", spot.indexed);
|
||||
CBOR_ENC(mapEncoder, "latt", spot.lattice);
|
||||
CBOR_ENC(mapEncoder, "image", spot.image);
|
||||
if (spot.panel >= 0)
|
||||
CBOR_ENC(mapEncoder, "panel", static_cast<int64_t>(spot.panel));
|
||||
if (spot.indexed) {
|
||||
CBOR_ENC(mapEncoder, "h", spot.h);
|
||||
CBOR_ENC(mapEncoder, "k", spot.k);
|
||||
@@ -484,6 +486,33 @@ inline void CBOR_ENC_AZINT_MAP(CborEncoder &encoder, const StartMessage &msg) {
|
||||
CBOR_ENC_2D_TYPED_ARRAY(encoder, image);
|
||||
}
|
||||
|
||||
// Not a DECTRIS field. Absent for a flat detector (no panel table), which is what every stream written
|
||||
// before the key existed means.
|
||||
inline void CBOR_ENC_DETECTOR_MODULES(CborEncoder &encoder, const std::vector<DetectorModule> &modules) {
|
||||
if (modules.empty())
|
||||
return;
|
||||
CborEncoder arrayEncoder;
|
||||
cborErr(cbor_encode_text_stringz(&encoder, "detector_modules"));
|
||||
cborErr(cbor_encoder_create_array(&encoder, &arrayEncoder, modules.size()));
|
||||
for (const auto &m: modules) {
|
||||
CborEncoder mapEncoder;
|
||||
cborErr(cbor_encoder_create_map(&arrayEncoder, &mapEncoder, 10));
|
||||
CBOR_ENC(mapEncoder, "x0", m.x0);
|
||||
CBOR_ENC(mapEncoder, "y0", m.y0);
|
||||
CBOR_ENC(mapEncoder, "width", m.width);
|
||||
CBOR_ENC(mapEncoder, "height", m.height);
|
||||
const std::pair<const char *, double> placement[] = {
|
||||
{"ox", m.ox}, {"oy", m.oy}, {"fx", m.fx}, {"fy", m.fy}, {"sx", m.sx}, {"sy", m.sy}
|
||||
};
|
||||
for (const auto &[key, val]: placement) {
|
||||
cborErr(cbor_encode_text_stringz(&mapEncoder, key));
|
||||
cborErr(cbor_encode_double(&mapEncoder, val));
|
||||
}
|
||||
cborErr(cbor_encoder_close_container(&arrayEncoder, &mapEncoder));
|
||||
}
|
||||
cborErr(cbor_encoder_close_container(&encoder, &arrayEncoder));
|
||||
}
|
||||
|
||||
inline void CBOR_ENC_ROI_MAP(CborEncoder &encoder, const StartMessage &msg) {
|
||||
if (msg.roi_map.empty())
|
||||
return;
|
||||
@@ -835,6 +864,7 @@ void CBORStream2Serializer::SerializeSequenceStart(const StartMessage& message)
|
||||
CBOR_ENC_PIXEL_MASK(mapEncoder, message);
|
||||
CBOR_ENC_AZINT_MAP(mapEncoder, message);
|
||||
CBOR_ENC_ROI_MAP(mapEncoder, message);
|
||||
CBOR_ENC_DETECTOR_MODULES(mapEncoder, message.detector_modules);
|
||||
|
||||
CBOR_ENC(mapEncoder, "channels", message.channels);
|
||||
CBOR_ENC(mapEncoder, "max_spot_count", message.max_spot_count);
|
||||
|
||||
@@ -1291,6 +1291,13 @@ static void ReadSpotsFromFiles(HDF5Object &master_file,
|
||||
{master_image, 0}, {source_image, 0}, {1, spot_count}
|
||||
);
|
||||
|
||||
// Written only for a detector with a panel table.
|
||||
auto spot_panel = ReadVectorMasterFirst<int16_t>(
|
||||
master_file, source_file,
|
||||
"/entry/MX/peakPanel",
|
||||
{master_image, 0}, {source_image, 0}, {1, spot_count}
|
||||
);
|
||||
|
||||
message.spots.reserve(message.spots.size() + spot_count);
|
||||
for (size_t i = 0; i < spot_count; i++) {
|
||||
const auto x = spot_x.at(i);
|
||||
@@ -1317,6 +1324,8 @@ static void ReadSpotsFromFiles(HDF5Object &master_file,
|
||||
s.ice_ring = (spot_ice.at(i) != 0);
|
||||
if (spot_lattice.size() > i)
|
||||
s.lattice = spot_lattice.at(i);
|
||||
if (spot_panel.size() > i)
|
||||
s.panel = spot_panel.at(i);
|
||||
message.spots.emplace_back(s);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,12 +89,14 @@ using namespace rugnux_internal;
|
||||
|
||||
namespace {
|
||||
|
||||
// The module of each spot, by where it lies on the detector plane (spots are on the plane already).
|
||||
// The module of each spot: the panel the spot finder found it on, or - where the spot carries no panel id -
|
||||
// the module where it lies on the detector plane (spots are on the plane already).
|
||||
void AssignModules(std::vector<GeomRefineFrame> &frames, const DetectorModules &modules) {
|
||||
for (auto &f: frames)
|
||||
for (auto *list: {&f.spots, &f.unindexed})
|
||||
for (auto &s: *list)
|
||||
s.module = static_cast<int16_t>(modules.ModuleOfPlanePoint(s.x, s.y));
|
||||
if (s.module < 0)
|
||||
s.module = static_cast<int16_t>(modules.ModuleOfPlanePoint(s.x, s.y));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -217,9 +219,9 @@ void Rugnux::RefineStillsGeometry(int start_image, int end_image, int images_to_
|
||||
for (const auto &s : msg.spots) {
|
||||
if (s.indexed && s.lattice == 0)
|
||||
f.spots.push_back(GeomRefineSpot{s.x, s.y,
|
||||
static_cast<int32_t>(s.h), static_cast<int32_t>(s.k), static_cast<int32_t>(s.l)});
|
||||
static_cast<int32_t>(s.h), static_cast<int32_t>(s.k), static_cast<int32_t>(s.l), s.panel});
|
||||
else
|
||||
f.unindexed.push_back(GeomRefineSpot{s.x, s.y});
|
||||
f.unindexed.push_back(GeomRefineSpot{s.x, s.y, 0, 0, 0, s.panel});
|
||||
}
|
||||
if (f.spots.size() >= 6) {
|
||||
if (static_cast<int>(f.spots.size()) >= MIN_STRONG_SPOTS)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include <algorithm>
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include "../frame_serialize/CBORStream2Serializer.h"
|
||||
@@ -1507,3 +1508,83 @@ TEST_CASE("CBORDeserialize_Start_CompressedLookupTable", "[CBOR]") {
|
||||
REQUIRE(deserialized->start_message);
|
||||
CHECK(deserialized->start_message->countrate_correction_lookup_table == lut);
|
||||
}
|
||||
|
||||
// The panel id of a spot is a CBOR key only for a detector with a panel table: a spot with panel -1
|
||||
// serialises to the same bytes as before the key existed.
|
||||
TEST_CASE("CBORSerialize_SpotPanelId", "[CBOR][PanelId]") {
|
||||
std::vector<uint8_t> buffer(1 << 20);
|
||||
const std::string key = "panel";
|
||||
std::vector<uint16_t> test(512);
|
||||
CompressedImage image(test, 256, 2);
|
||||
|
||||
auto serialize = [&](int16_t p0, int16_t p1) {
|
||||
CBORStream2Serializer serializer(buffer.data(), buffer.size());
|
||||
DataMessage message{
|
||||
.number = 1,
|
||||
.image = image,
|
||||
.spots = {SpotToSave{.x = 7, .y = 8, .intensity = 34, .panel = p0},
|
||||
SpotToSave{.x = 37, .y = 48, .intensity = 123, .panel = p1}}
|
||||
};
|
||||
serializer.SerializeImage(message);
|
||||
return std::vector<uint8_t>(buffer.begin(), buffer.begin() + serializer.GetBufferSize());
|
||||
};
|
||||
|
||||
const auto absent = serialize(-1, -1);
|
||||
CHECK(std::search(absent.begin(), absent.end(), key.begin(), key.end()) == absent.end());
|
||||
auto deserialized = CBORStream2Deserialize(absent.data(), absent.size());
|
||||
REQUIRE(deserialized->data_message);
|
||||
REQUIRE(deserialized->data_message->spots.size() == 2);
|
||||
CHECK(deserialized->data_message->spots[0].panel == -1);
|
||||
CHECK(deserialized->data_message->spots[1].panel == -1);
|
||||
|
||||
const auto present = serialize(3, 0);
|
||||
CHECK(std::search(present.begin(), present.end(), key.begin(), key.end()) != present.end());
|
||||
deserialized = CBORStream2Deserialize(present.data(), present.size());
|
||||
REQUIRE(deserialized->data_message);
|
||||
REQUIRE(deserialized->data_message->spots.size() == 2);
|
||||
CHECK(deserialized->data_message->spots[0].panel == 3);
|
||||
CHECK(deserialized->data_message->spots[1].panel == 0);
|
||||
CHECK(deserialized->data_message->spots[1].x == 37);
|
||||
}
|
||||
|
||||
// The start message carries the panel table only when there is one.
|
||||
TEST_CASE("CBORSerialize_StartDetectorModules", "[CBOR][PanelId]") {
|
||||
std::vector<uint8_t> buffer(8 * 1024 * 1024);
|
||||
const std::string key = "detector_modules";
|
||||
|
||||
StartMessage message{};
|
||||
message.image_size_x = 256;
|
||||
message.image_size_y = 128;
|
||||
{
|
||||
CBORStream2Serializer serializer(buffer.data(), buffer.size());
|
||||
serializer.SerializeSequenceStart(message);
|
||||
CHECK(std::search(buffer.begin(), buffer.begin() + serializer.GetBufferSize(), key.begin(), key.end())
|
||||
== buffer.begin() + serializer.GetBufferSize());
|
||||
auto deserialized = CBORStream2Deserialize(buffer.data(), serializer.GetBufferSize());
|
||||
REQUIRE(deserialized->start_message);
|
||||
CHECK(deserialized->start_message->detector_modules.empty());
|
||||
}
|
||||
|
||||
DetectorModule turned{.x0 = 0, .y0 = 64, .width = 256, .height = 64,
|
||||
.ox = 300.25, .oy = -12.5, .fx = 0, .fy = 1, .sx = -1, .sy = 0};
|
||||
message.detector_modules = {DetectorModule::AsImaged(0, 0, 256, 64), turned};
|
||||
{
|
||||
CBORStream2Serializer serializer(buffer.data(), buffer.size());
|
||||
serializer.SerializeSequenceStart(message);
|
||||
auto deserialized = CBORStream2Deserialize(buffer.data(), serializer.GetBufferSize());
|
||||
REQUIRE(deserialized->start_message);
|
||||
const auto &m = deserialized->start_message->detector_modules;
|
||||
REQUIRE(m.size() == 2);
|
||||
CHECK(m[0].x0 == 0);
|
||||
CHECK(m[0].height == 64);
|
||||
CHECK(m[0].fx == 1);
|
||||
CHECK(m[1].y0 == 64);
|
||||
CHECK(m[1].width == 256);
|
||||
CHECK(m[1].ox == 300.25);
|
||||
CHECK(m[1].oy == -12.5);
|
||||
CHECK(m[1].fx == 0);
|
||||
CHECK(m[1].fy == 1);
|
||||
CHECK(m[1].sx == -1);
|
||||
CHECK(m[1].sy == 0);
|
||||
}
|
||||
}
|
||||
@@ -4931,3 +4931,75 @@ TEST_CASE("JFJochXTCReader", "[portable]") {
|
||||
|
||||
std::filesystem::remove_all("xtc_test");
|
||||
}
|
||||
|
||||
// The panel id of spots goes to /entry/MX/peakPanel only for a detector with a panel table; for a flat
|
||||
// detector the dataset is not written, so such files are unchanged.
|
||||
TEST_CASE("JFJochReader_PanelId", "[HDF5][PanelId]") {
|
||||
const auto format = GENERATE(FileWriterFormat::NXmxVDS, FileWriterFormat::NXmxLegacy,
|
||||
FileWriterFormat::NXmxIntegrated);
|
||||
const bool panels = GENERATE(true, false);
|
||||
INFO("format " << static_cast<int>(format) << " panels " << panels);
|
||||
|
||||
DiffractionExperiment x(DetJF(1));
|
||||
x.FilePrefix("test_panel_id").ImagesPerTrigger(2).ImagesPerFile(10).OverwriteExistingFiles(true)
|
||||
.BitDepthImage(16).PixelSigned(true).SetFileWriterFormat(format)
|
||||
.IndexingAlgorithm(IndexingAlgorithmEnum::FFT).Compression(CompressionAlgorithm::NO_COMPRESSION);
|
||||
if (panels)
|
||||
x.Modules(DetectorModules({DetectorModule::AsImaged(0, 0, x.GetXPixelsNum(), x.GetYPixelsNum() / 2),
|
||||
DetectorModule::AsImaged(0, x.GetYPixelsNum() / 2, x.GetXPixelsNum(),
|
||||
x.GetYPixelsNum() / 2)}));
|
||||
const int16_t p0 = panels ? 0 : -1;
|
||||
const int16_t p1 = panels ? 1 : -1;
|
||||
|
||||
std::vector<int16_t> image(x.GetPixelsNum());
|
||||
RegisterHDF5Filter();
|
||||
{
|
||||
StartMessage start_message;
|
||||
x.FillMessage(start_message);
|
||||
CHECK(start_message.detector_modules.size() == (panels ? 2 : 0));
|
||||
FileWriter file_set(start_message);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
DataMessage message{};
|
||||
message.number = i;
|
||||
message.image = CompressedImage(image, x.GetXPixelsNum(), x.GetYPixelsNum());
|
||||
message.indexing_result = true;
|
||||
message.indexing_lattice = CrystalLattice(40, 50, 60, 90, 90, 90);
|
||||
message.spots = {SpotToSave{.x = 10, .y = 50, .intensity = 80, .panel = p0},
|
||||
SpotToSave{.x = 20, .y = 400, .intensity = 90, .panel = p1}};
|
||||
REQUIRE_NOTHROW(file_set.WriteHDF5(message));
|
||||
}
|
||||
|
||||
EndMessage end_message;
|
||||
end_message.max_image_number = 2;
|
||||
file_set.WriteHDF5(end_message);
|
||||
file_set.Finalize();
|
||||
}
|
||||
{
|
||||
HDF5ReadOnlyFile master("test_panel_id_master.h5");
|
||||
const bool separate_data = format != FileWriterFormat::NXmxIntegrated;
|
||||
// The master of a VDS set exposes it as a virtual dataset, the integrated file holds it itself
|
||||
CHECK(master.Exists("/entry/MX/peakPanel") == (panels && format != FileWriterFormat::NXmxLegacy));
|
||||
if (separate_data) {
|
||||
HDF5ReadOnlyFile data("test_panel_id_data_000001.h5");
|
||||
CHECK(data.Exists("/entry/MX/peakPanel") == panels);
|
||||
}
|
||||
}
|
||||
{
|
||||
JFJochHDF5Reader reader;
|
||||
REQUIRE_NOTHROW(reader.ReadFile("test_panel_id_master.h5"));
|
||||
for (int i = 0; i < 2; i++) {
|
||||
std::shared_ptr<JFJochReaderImage> reader_image;
|
||||
REQUIRE_NOTHROW(reader_image = reader.LoadImage(i));
|
||||
REQUIRE(reader_image);
|
||||
const auto &spots = reader_image->ImageData().spots;
|
||||
REQUIRE(spots.size() == 2);
|
||||
CHECK(spots[0].panel == p0);
|
||||
CHECK(spots[1].panel == p1);
|
||||
}
|
||||
}
|
||||
remove("test_panel_id_master.h5");
|
||||
remove("test_panel_id_data_000001.h5");
|
||||
|
||||
REQUIRE(H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0);
|
||||
}
|
||||
@@ -224,6 +224,8 @@ std::shared_ptr<JFJochReaderDataset> JFJochHttpReader::UpdateDataset_i() {
|
||||
detector.MinCountTime(std::chrono::microseconds(0));
|
||||
detector.ReadOutTime(std::chrono::microseconds (0));
|
||||
dataset->experiment.Detector(detector);
|
||||
// Absent (empty) for a flat detector, the image being the detector plane
|
||||
dataset->experiment.Modules(DetectorModules(msg->start_message->detector_modules));
|
||||
|
||||
dataset->experiment.FrameTime(
|
||||
std::chrono::microseconds(std::lround(msg->start_message->frame_time * 1e6)),
|
||||
|
||||
@@ -67,6 +67,7 @@ void HDF5DataFilePluginMX::OpenFile(HDF5File &data_file, const DataMessage &msg,
|
||||
spot_k.reserve(max_spots * images_per_file);
|
||||
spot_l.reserve(max_spots * images_per_file);
|
||||
spot_dist_ewald.reserve(max_spots * images_per_file);
|
||||
spot_panel.reserve(max_spots * images_per_file);
|
||||
|
||||
if (indexing) {
|
||||
spot_indexed.reserve(max_spots * images_per_file);
|
||||
@@ -120,6 +121,7 @@ void HDF5DataFilePluginMX::Write(const DataMessage &msg, uint64_t image_number)
|
||||
spot_l.resize(max_spots * (max_image_number + 1));
|
||||
spot_lattice.resize(max_spots * (max_image_number + 1), -1);
|
||||
spot_dist_ewald.resize(max_spots * (max_image_number + 1), NAN);
|
||||
spot_panel.resize(max_spots * (max_image_number + 1), -1);
|
||||
|
||||
if (indexing) {
|
||||
spot_indexed.resize(max_spots * (max_image_number + 1));
|
||||
@@ -134,6 +136,9 @@ void HDF5DataFilePluginMX::Write(const DataMessage &msg, uint64_t image_number)
|
||||
spot_x[max_spots * image_number + i] = msg.spots[i].x;
|
||||
spot_y[max_spots * image_number + i] = msg.spots[i].y;
|
||||
spot_int[max_spots * image_number + i] = msg.spots[i].intensity;
|
||||
spot_panel[max_spots * image_number + i] = msg.spots[i].panel;
|
||||
if (msg.spots[i].panel >= 0)
|
||||
spot_panel_present = true;
|
||||
if (indexing) {
|
||||
spot_indexed[max_spots * image_number + i] = msg.spots[i].indexed;
|
||||
spot_lattice[max_spots * image_number + i] = msg.spots[i].lattice;
|
||||
@@ -213,6 +218,8 @@ void HDF5DataFilePluginMX::WriteFinal(HDF5File &data_file) {
|
||||
data_file.SaveVector("/entry/MX/peakTotalIntensity", spot_int, {(hsize_t) (max_image_number + 1), max_spots});
|
||||
data_file.SaveVector("/entry/MX/peakIceRingRes", spot_ice_ring, {(hsize_t) (max_image_number + 1), max_spots});
|
||||
data_file.SaveVector("/entry/MX/nPeaks", npeaks.vec());
|
||||
if (spot_panel_present)
|
||||
data_file.SaveVector("/entry/MX/peakPanel", spot_panel, {(hsize_t) (max_image_number + 1), max_spots});
|
||||
|
||||
if (indexing) {
|
||||
data_file.SaveVector("/entry/MX/peakH", spot_h, {(hsize_t) (max_image_number + 1), max_spots});
|
||||
|
||||
@@ -21,6 +21,8 @@ class HDF5DataFilePluginMX : public HDF5DataFilePlugin {
|
||||
std::vector<int32_t> spot_k;
|
||||
std::vector<int32_t> spot_l;
|
||||
std::vector<float> spot_dist_ewald;
|
||||
std::vector<int16_t> spot_panel;
|
||||
bool spot_panel_present = false; // written only for a detector with a panel table
|
||||
|
||||
AutoIncrVector<float> beam_corr_x{NAN};
|
||||
AutoIncrVector<float> beam_corr_y{NAN};
|
||||
|
||||
@@ -132,6 +132,8 @@ void NXmx::LinkToData_VDS(const StartMessage &start, const EndMessage &end) {
|
||||
VDS(start, "/entry/MX/peakTotalIntensity",{total_images, start.max_spot_count}, HDF5DataType(0.0f));
|
||||
VDS(start, "/entry/MX/peakIceRingRes", {total_images, start.max_spot_count}, HDF5DataType(static_cast<int8_t>(0)));
|
||||
VDS(start, "/entry/MX/nPeaks", {total_images}, HDF5DataType((uint32_t) 0));
|
||||
if (!start.detector_modules.empty())
|
||||
VDS(start, "/entry/MX/peakPanel", {total_images, start.max_spot_count}, HDF5DataType(static_cast<int16_t>(-1)), -1);
|
||||
}
|
||||
|
||||
if (start.indexing_algorithm != IndexingAlgorithmEnum::None) {
|
||||
|
||||
Reference in new issue
Block a user