Two experiment properties the panel could not state, both of which the analysis it drives has an opinion about anyway. The polarization factor is one number that corrects both communities' output - the azimuthal profile through AzimuthalIntegrationMapping and the integrated intensities through BraggIntegrationEngine - so it goes in the Geometry section, which the MX and AzInt pages already share. Files carry no polarization factor at all, so before this the interactive analysis integrated with none while "Analyze dataset" applied 0.99 from the rugnux defaults: the panel showed nothing and the two front ends disagreed. The viewer's starting experiment now takes the same rugnux defaults, so the panel shows what the analysis actually uses, and a processing job takes the panel's value over the default - as it already did for the scaling fields. The Goniometer section states which of the three things a dataset is - a still, a rotation, or a grid scan - which is exactly the choice a file makes at /entry/sample/transformations/omega vs /entry/sample/grid_scan vs neither. That choice IS the rotation/stills switch, so "Process as stills" is gone from the Indexing section rather than sitting beside it as a second control. It is not tied to what the file says: a still file can be given an axis or a grid, and a rotation file can be processed as stills. The inactive modes grey out but keep their values, so switching away and back does not lose an axis; a file that names none offers omega / -1 0 0 / 0.1 deg and a 10-point, 20 um raster. Only the fast axis of a grid gets a count field, because that is all there is: GridScanSettings derives the slow one from the image count, and the file stores n_fast alone. The grid the settings make is spelled out under them instead. The steps are signed - the sign is the direction the scan runs in - so only zero is rejected, and a half-typed entry falls back to the default rather than throwing out of a widget signal, which would abort the viewer. JFJochReader::UpdateGeomMetadata carries a fixed whitelist of the fields a panel edit may change, and it grew by three. Without them the new controls reset themselves on the next dataset refresh, since a non-whitelisted field comes back as the file's. The function has two call sites, both in the viewer's reading worker, and it is not virtual: objdump on the rugnux binary shows the code linked in and never called, so offline processing is untouched. RugnuxCommandLine emits --force-still from the axis the experiment carries, which the panel now clears when the mode is not Rotation - so the worker remembers the axis the file was opened with, and the copied command line is built with that put back. rugnux has no flag for the axis itself, so an axis edited or invented in the panel still cannot be expressed on a command line; the in-process run gets the experiment object and is unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
134 lines
5.5 KiB
C++
134 lines
5.5 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include "JFJochReader.h"
|
|
#include <future>
|
|
|
|
JFJochReader &JFJochReader::Experiment(const DiffractionExperiment &experiment) {
|
|
std::unique_lock ul(m);
|
|
default_experiment = experiment;
|
|
return *this;
|
|
}
|
|
|
|
void JFJochReader::SummationThread(int64_t image0, int64_t n_image, int64_t image_jump, JFJochReaderImage &image) {
|
|
std::vector<uint8_t> buffer;
|
|
DataMessage msg;
|
|
for (int64_t i = image0; i < n_image; i += image_jump) {
|
|
bool ret = LoadImage_i(dataset, msg, buffer, i, false);
|
|
if (ret) {
|
|
auto image_sum = std::make_shared<JFJochReaderImage>(msg, dataset);
|
|
{
|
|
std::unique_lock ul(summation_mutex);
|
|
image.AddImage(*image_sum);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<JFJochReaderImage> JFJochReader::LoadImage(int64_t image_number, int64_t summation_factor) {
|
|
// It would be a mess to load two images at the same time
|
|
// so loading is protected via mutex
|
|
// yet copying share_ptr pointer is atomic and needs no mutex protection
|
|
std::unique_lock ul(m);
|
|
|
|
std::vector<uint8_t> buffer;
|
|
DataMessage msg;
|
|
|
|
if (LoadImage_i(dataset, msg, buffer, image_number, true)) {
|
|
auto image = std::make_shared<JFJochReaderImage>(msg, dataset);
|
|
if (summation_factor > 4) {
|
|
int64_t nthread = std::min<int64_t>(summation_factor - 1, 8);
|
|
|
|
std::vector<std::future<void>> futures;
|
|
for (int i = 0; i < nthread; i++)
|
|
futures.emplace_back(std::async(std::launch::async,
|
|
&JFJochReader::SummationThread, this,
|
|
image_number + 1 + i,
|
|
image_number + summation_factor,
|
|
nthread,
|
|
std::ref(*image)));
|
|
for (auto &f: futures)
|
|
f.get();
|
|
} else if (summation_factor > 1) {
|
|
SummationThread(image_number + 1, image_number + summation_factor, 1, *image);
|
|
}
|
|
return image;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
void JFJochReader::SetStartMessage(const std::shared_ptr<JFJochReaderDataset> &val) {
|
|
std::unique_lock ul(m);
|
|
dataset = val;
|
|
}
|
|
|
|
std::shared_ptr<const JFJochReaderDataset> JFJochReader::GetDataset() const {
|
|
std::unique_lock ul(m);
|
|
return dataset;
|
|
}
|
|
|
|
void JFJochReader::UpdateGeomMetadata(const DiffractionExperiment &experiment) {
|
|
std::unique_lock ul(m);
|
|
if (!dataset)
|
|
return;
|
|
|
|
auto new_dataset = std::make_shared<JFJochReaderDataset>(*dataset);
|
|
// At the moment subset of options is limited to safe ones...need to change it in the future
|
|
|
|
new_dataset->experiment.BeamX_pxl(experiment.GetBeamX_pxl());
|
|
new_dataset->experiment.BeamY_pxl(experiment.GetBeamY_pxl());
|
|
new_dataset->experiment.DetectorDistance_mm(experiment.GetDetectorDistance_mm());
|
|
new_dataset->experiment.IncidentEnergy_keV(experiment.GetIncidentEnergy_keV());
|
|
new_dataset->experiment.PoniRot1_rad(experiment.GetDatasetSettings().GetPoniRot1_rad());
|
|
new_dataset->experiment.PoniRot2_rad(experiment.GetDatasetSettings().GetPoniRot2_rad());
|
|
new_dataset->experiment.PoniRot3_rad(experiment.GetDatasetSettings().GetPoniRot3_rad());
|
|
new_dataset->experiment.SetUnitCell(experiment.GetUnitCell());
|
|
new_dataset->experiment.SpaceGroupNumber(experiment.GetSpaceGroupNumber());
|
|
new_dataset->experiment.PolarizationFactor(experiment.GetPolarizationFactor());
|
|
new_dataset->experiment.Goniometer(experiment.GetGoniometer());
|
|
new_dataset->experiment.GridScan(experiment.GetGridScan());
|
|
new_dataset->experiment.ImportIndexingSettings(experiment.GetIndexingSettings());
|
|
new_dataset->experiment.ImportBraggIntegrationSettings(experiment.GetBraggIntegrationSettings());
|
|
new_dataset->experiment.DetectIceRings(experiment.IsDetectIceRings());
|
|
dataset = new_dataset;
|
|
}
|
|
|
|
void JFJochReader::UpdateUserMask(const std::vector<uint32_t> &mask) {
|
|
std::unique_lock ul(m);
|
|
if (!dataset)
|
|
return;
|
|
auto new_dataset = std::make_shared<JFJochReaderDataset>(*dataset);
|
|
// Copy-on-write: the mask is shared with the old snapshot, so edit a fresh copy, not in place.
|
|
auto new_mask = std::make_shared<PixelMask>(*dataset->pixel_mask);
|
|
new_mask->LoadUserMask(dataset->experiment, mask);
|
|
new_dataset->pixel_mask = new_mask;
|
|
dataset = new_dataset;
|
|
}
|
|
|
|
std::shared_ptr<JFJochReaderSpots> JFJochReader::ReadAllSpots(int64_t start_image, int64_t end_image,
|
|
int64_t stride) const {
|
|
|
|
if (start_image < 0)
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Start image must be non-negative");
|
|
|
|
if (start_image > end_image)
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Start image number is greater than end image number");
|
|
|
|
if (stride == 0)
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Stride cannot be zero");
|
|
|
|
size_t nelems = (end_image - start_image) / stride + 1;
|
|
|
|
auto ret = std::make_shared<JFJochReaderSpots>();
|
|
ret->start_image = static_cast<int64_t>(start_image);
|
|
ret->stride = static_cast<int64_t>(stride);
|
|
ret->spots.reserve(nelems);
|
|
|
|
for (int i = 0; i < nelems; i++)
|
|
ret->spots.emplace_back(ReadSpots(start_image + i * stride));
|
|
return ret;
|
|
}
|