The value means three different things at once. It is the unit of writer parallelism - whole files go round-robin to the writers, (image_number / images_per_file) % socket.size() in ZMQStream2Pusher::SendImage and TCPStreamPusher - it multiplies writer memory linearly, since every data-file plugin reserves per file, and it decides whether a legacy master is readable at all, because dxtbx follows only the first data file of one. That last point is what makes a flat default wrong. Measured with DIALS on a 2500-image rotation sweep written as legacy: split into five files it reports 2500 images and then raises IndexError beyond image 499, so it half-works silently; in one file all 2500 read. AutoPROC does not read VDS, so legacy has to stay the default, which leaves the file count as the only lever. So make it optional and resolve it from the acquisition. A rotation sweep of at most 20000 images goes into one data file - rotation datasets are small enough, and one writer keeps up with them. A grid scan splits on whole fast-axis rows, so a file is a meaningful piece of the grid. Stills and serial keep 1000, where the image count far exceeds it and the parallelism and the bounded writer memory are what matter. An explicit value is always taken literally. GetImagesPerFile is the single place this is resolved, and it must always return a fixed non-zero number, because everything downstream - receiver, pusher, puller, writer - requires one. That was already true of the old 0 = "one file" spelling; 0 is now gone from the API and omitting the field says the same thing better. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1781 lines
61 KiB
C++
1781 lines
61 KiB
C++
// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <utility>
|
|
|
|
#include "NetworkAddressConvert.h"
|
|
#include "JFJochCompressor.h" // For ZSTD_USE_JFJOCH_RLE
|
|
#include "DiffractionExperiment.h"
|
|
|
|
#include "CUDAWrapper.h"
|
|
#include "JFJochException.h"
|
|
#include "RawToConvertedGeometry.h"
|
|
#include "../include/spdlog/fmt/fmt.h"
|
|
#include "GitInfo.h"
|
|
|
|
using namespace std::literals::chrono_literals;
|
|
|
|
#define check_max(param, val, max) if ((val) > (max)) throw JFJochException(JFJochExceptionCategory::InputParameterAboveMax, param)
|
|
#define check_min(param, val, min) if ((val) < (min)) throw JFJochException(JFJochExceptionCategory::InputParameterBelowMin, param)
|
|
#define check_finite(param, val) if (!std::isfinite(val)) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, param)
|
|
|
|
DiffractionExperiment::DiffractionExperiment() : DiffractionExperiment(DetJF4M()) {}
|
|
|
|
DiffractionExperiment::DiffractionExperiment(const DetectorSetup& det_setup)
|
|
: detector(det_setup) {
|
|
|
|
ndatastreams = 1;
|
|
|
|
series_id = 0;
|
|
|
|
mode = DetectorMode::Standard;
|
|
image_format_settings.Conv();
|
|
|
|
summation = 1;
|
|
cpu_summation = false;
|
|
}
|
|
|
|
// setter functions
|
|
DiffractionExperiment &DiffractionExperiment::Detector(const DetectorSetup &input) {
|
|
detector = input;
|
|
|
|
// Conversion is always default mode after switching detectors
|
|
mode = DetectorMode::Standard;
|
|
|
|
auto settings = detector.GetDefaultSettings();
|
|
if (settings)
|
|
detector_settings = *settings;
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::Mode(DetectorMode input) {
|
|
// Handle allowed mode settings
|
|
switch (GetDetectorType()) {
|
|
case DetectorType::JUNGFRAU:
|
|
if (input == DetectorMode::DarkMask)
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Dark mask is not supported for PSI JUNGFRAU detector");
|
|
break;
|
|
case DetectorType::DECTRIS:
|
|
if ((input == DetectorMode::PedestalG0) || (input == DetectorMode::PedestalG1) || (input == DetectorMode::PedestalG2))
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Pedestal data collection is not supported for DECTRIS detector");
|
|
break;
|
|
case DetectorType::EIGER:
|
|
if ((input == DetectorMode::PedestalG0) || (input == DetectorMode::PedestalG1) || (input == DetectorMode::PedestalG2) || (input == DetectorMode::DarkMask))
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Pedestal or dark mask data collection is not supported for PSI EIGER detector");
|
|
break;
|
|
}
|
|
|
|
mode = input;
|
|
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::DataStreams(int64_t input) {
|
|
check_max("Number of data streams", input, 16);
|
|
check_min("Number of data streams", input, 1);
|
|
ndatastreams = input;
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImagesPerTrigger(int64_t input) {
|
|
dataset.ImagesPerTrigger(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::NumTriggers(int64_t input) {
|
|
dataset.NumTriggers(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::FrameTime(std::chrono::nanoseconds in_frame_time,
|
|
std::chrono::nanoseconds in_count_time) {
|
|
DetectorSettings tmp = detector_settings;
|
|
if (in_count_time.count() == 0)
|
|
tmp.FrameTime(in_frame_time);
|
|
else
|
|
tmp.FrameTime(in_frame_time, in_count_time);
|
|
ImportDetectorSettings(tmp);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::PedestalG0Frames(int64_t input) {
|
|
detector_settings.PedestalG0Frames(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::PedestalG1Frames(int64_t input) {
|
|
detector_settings.PedestalG1Frames(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::PedestalG2Frames(int64_t input) {
|
|
detector_settings.PedestalG2Frames(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::IncidentEnergy_keV(float input) {
|
|
dataset.PhotonEnergy_keV(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::BeamX_pxl(float input) {
|
|
dataset.BeamX_pxl(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::BeamY_pxl(float input) {
|
|
dataset.BeamY_pxl(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::DetectorDistance_mm(float input) {
|
|
dataset.DetectorDistance_mm(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::FilePrefix(std::string input) {
|
|
dataset.FilePrefix(std::move(input));
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::FilePrefixTrusted(std::string input) {
|
|
dataset.FilePrefixTrusted(std::move(input));
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::UseInternalPacketGenerator(bool input) {
|
|
detector_settings.InternalGeneratorEnable(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::MaskModuleEdges(bool input) {
|
|
image_format_settings.MaskModuleEdges(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::Compression(CompressionAlgorithm input) {
|
|
dataset.Compression(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::MaskChipEdges(bool input) {
|
|
image_format_settings.MaskChipEdges(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::QRangeForAzimInt_recipA(float low, std::optional<float> high) {
|
|
az_integration_settings.QRange_recipA(low, high);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment& DiffractionExperiment::BkgEstimateQRange_recipA(float low, float high) {
|
|
az_integration_settings.BkgEstimateQRange_recipA(low, high);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment& DiffractionExperiment::QSpacingForAzimInt_recipA(float input) {
|
|
az_integration_settings.QSpacing_recipA(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::SetUnitCell(const std::optional<UnitCell> &cell) {
|
|
dataset.SetUnitCell(cell);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ZMQPreviewPeriod(const std::optional<std::chrono::microseconds> &input) {
|
|
if (input.has_value()) {
|
|
check_min("Preview image generation period [ms]", input.value().count(), 0);
|
|
}
|
|
zmq_preview_period = input;
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::SpaceGroupNumber(std::optional<int64_t> input) {
|
|
dataset.SpaceGroupNumber(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::StorageCells(int64_t input) {
|
|
detector_settings.StorageCells(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::StorageCellStart(int64_t input) {
|
|
detector_settings.StorageCellStart(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::SampleName(const std::string &input) {
|
|
dataset.SampleName(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::OverwriteExistingFiles(bool input) {
|
|
file_writer.OverwriteExistingFiles(input);
|
|
return *this;
|
|
}
|
|
|
|
// getter functions
|
|
int64_t DiffractionExperiment::GetNumTriggers() const {
|
|
if (GetDetectorMode() != DetectorMode::Standard)
|
|
// For pedestal and dark mask modes
|
|
return 1;
|
|
else if (IsPulsedSource())
|
|
// For pulsed source summation happens over multiple triggers
|
|
return dataset.GetNumTriggers() * GetSummation();
|
|
else
|
|
return dataset.GetNumTriggers();
|
|
}
|
|
|
|
DetectorMode DiffractionExperiment::GetDetectorMode() const {
|
|
return mode;
|
|
}
|
|
|
|
std::chrono::nanoseconds DiffractionExperiment::GetFrameTime() const {
|
|
switch (GetDetectorMode()) {
|
|
case DetectorMode::DarkMask:
|
|
return GetDarkMaskSettings().GetFrameTime();
|
|
case DetectorMode::PedestalG1:
|
|
case DetectorMode::PedestalG2:
|
|
return detector_settings.GetFrameTimePedestalG1G2();
|
|
default:
|
|
if ((GetDetectorType() != DetectorType::JUNGFRAU) && dataset.GetImageTime().has_value())
|
|
return dataset.GetImageTime().value();
|
|
|
|
return detector_settings.GetFrameTime();
|
|
}
|
|
}
|
|
|
|
std::chrono::nanoseconds DiffractionExperiment::GetDetectorPeriod() const {
|
|
// Without storage cells - this is just frame time
|
|
if (GetStorageCellNumber() == 1)
|
|
return GetFrameTime();
|
|
|
|
// With storage cells
|
|
// Do 100 Hz repetition for pedestal G1/G2
|
|
// Do 2 kHz repetition for conversion/raw/pedestal G0
|
|
switch (GetDetectorMode()) {
|
|
case DetectorMode::PedestalG1:
|
|
case DetectorMode::PedestalG2:
|
|
return detector_settings.GetFrameTimePedestalG1G2();
|
|
default:
|
|
return MIN_FRAME_TIME_JUNGFRAU_FULL_SPEED * GetStorageCellNumber();
|
|
}
|
|
}
|
|
|
|
std::chrono::nanoseconds DiffractionExperiment::GetImageTime() const {
|
|
switch (GetDetectorMode()) {
|
|
case DetectorMode::PedestalG1:
|
|
case DetectorMode::PedestalG2:
|
|
return detector_settings.GetFrameTimePedestalG1G2();
|
|
default:
|
|
return GetFrameTime() * GetSummation();
|
|
}
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetImageNum() const {
|
|
if (GetDetectorMode() == DetectorMode::Standard)
|
|
return GetFrameNum() / GetSummation();
|
|
return 0;
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetFrameNum() const {
|
|
return GetFrameNumPerTrigger() * GetNumTriggers();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetFrameNumPerTrigger() const {
|
|
switch (GetDetectorMode()) {
|
|
case DetectorMode::DarkMask:
|
|
return GetDarkMaskSettings().GetNumberOfFrames();
|
|
case DetectorMode::PedestalG0:
|
|
return GetPedestalG0Frames() * GetStorageCellNumber();
|
|
case DetectorMode::PedestalG1:
|
|
return GetPedestalG1Frames() * GetStorageCellNumber();
|
|
case DetectorMode::PedestalG2:
|
|
return GetPedestalG2Frames()* GetStorageCellNumber();
|
|
default:
|
|
if (GetStorageCellNumber() > 1)
|
|
return GetStorageCellNumber();
|
|
else if (IsPulsedSource())
|
|
return 1;
|
|
else
|
|
return dataset.GetImageNumPerTrigger() * GetSummation();
|
|
}
|
|
}
|
|
|
|
std::chrono::nanoseconds DiffractionExperiment::GetFrameCountTime() const {
|
|
if ((GetDetectorType() != DetectorType::JUNGFRAU)
|
|
&& dataset.GetImageTime().has_value()
|
|
&& !detector_settings.GetCountTime().has_value())
|
|
return std::chrono::duration_cast<std::chrono::nanoseconds>(dataset.GetImageTime().value())
|
|
- detector.GetReadOutTime();
|
|
|
|
if (detector_settings.GetCountTime().has_value())
|
|
return detector_settings.GetCountTime().value();
|
|
|
|
return std::chrono::duration_cast<std::chrono::nanoseconds>(detector_settings.GetFrameTime())
|
|
- detector.GetReadOutTime();
|
|
}
|
|
|
|
bool DiffractionExperiment::GetFrameCountTimeAuto() const {
|
|
return !detector_settings.GetCountTime().has_value();
|
|
}
|
|
|
|
std::chrono::nanoseconds DiffractionExperiment::GetImageCountTime() const {
|
|
return GetFrameCountTime() * GetSummation();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetPedestalG0Frames() const {
|
|
if (GetDetectorType() != DetectorType::JUNGFRAU)
|
|
return 0;
|
|
return detector_settings.GetPedestalG0Frames();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetPedestalG1Frames() const {
|
|
if (GetDetectorType() != DetectorType::JUNGFRAU)
|
|
return 0;
|
|
return detector_settings.GetPedestalG1Frames();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetPedestalG2Frames() const {
|
|
if (GetDetectorType() != DetectorType::JUNGFRAU)
|
|
return 0;
|
|
return detector_settings.GetPedestalG2Frames();
|
|
}
|
|
|
|
float DiffractionExperiment::GetIncidentEnergy_keV() const {
|
|
return dataset.GetPhotonEnergy_keV();
|
|
}
|
|
|
|
float DiffractionExperiment::GetWavelength_A() const {
|
|
if (instrument.IsElectronSource()) {
|
|
const double hc = WVL_1A_IN_KEV;
|
|
const double me_c2 = 511; // in keV
|
|
return static_cast<float>(hc / sqrt(dataset.GetPhotonEnergy_keV() * (dataset.GetPhotonEnergy_keV() + 2 * me_c2)));
|
|
}
|
|
return WVL_1A_IN_KEV / dataset.GetPhotonEnergy_keV();
|
|
}
|
|
|
|
float DiffractionExperiment::GetBeamX_pxl() const {
|
|
return dataset.GetBeamX_pxl();
|
|
}
|
|
|
|
float DiffractionExperiment::GetBeamY_pxl() const {
|
|
return dataset.GetBeamY_pxl();
|
|
}
|
|
|
|
float DiffractionExperiment::GetDetectorDistance_mm() const {
|
|
return dataset.GetDetectorDistance_mm();
|
|
}
|
|
|
|
Coord DiffractionExperiment::GetScatteringVector() const {
|
|
return dataset.GetScatteringVector();
|
|
}
|
|
|
|
std::string DiffractionExperiment::GetFilePrefix() const {
|
|
return dataset.GetFilePrefix();
|
|
}
|
|
|
|
CompressionAlgorithm DiffractionExperiment::GetCompressionAlgorithm() const {
|
|
return dataset.GetCompressionAlgorithm();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetByteDepthImage() const {
|
|
if (detector.GetBitDepthImage())
|
|
return detector.GetBitDepthImage().value() / 8;
|
|
|
|
if (IsCPUSummation())
|
|
return 4;
|
|
|
|
auto bit_depth_image = image_format_settings.GetBitDepthImage();
|
|
if (!bit_depth_image.has_value()) {
|
|
if (GetBitDepthReadout() == 32)
|
|
return 4;
|
|
if (GetBitDepthReadout() == 8)
|
|
return 1;
|
|
return (GetSummation() > 2) ? 4 : 2;
|
|
}
|
|
return bit_depth_image.value() / 8;
|
|
}
|
|
|
|
bool DiffractionExperiment::IsPixelSigned() const {
|
|
auto pixel_signed = image_format_settings.IsPixelSigned();
|
|
if (!pixel_signed.has_value())
|
|
return IsJungfrauConvPhotonCnt() && (GetDetectorType() == DetectorType::JUNGFRAU);
|
|
else
|
|
return pixel_signed.value();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetDataStreamsNum() const {
|
|
return std::min<int64_t>(ndatastreams, detector.GetModulesNum());
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetModulesNum(uint16_t data_stream) const {
|
|
if (data_stream >= GetDataStreamsNum())
|
|
throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Non existing data stream");
|
|
|
|
return (detector.GetModulesNum() + (GetDataStreamsNum() - 1) - data_stream) / GetDataStreamsNum();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetModulesNum() const {
|
|
return detector.GetModulesNum();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetFirstModuleOfDataStream(uint16_t data_stream) const {
|
|
if (data_stream >= GetDataStreamsNum())
|
|
throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Non exisiting data stream");
|
|
|
|
int64_t val = 0;
|
|
for (int i = 0; i < data_stream; i++) val += GetModulesNum(i);
|
|
return val;
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetMaxCompressedSize() const {
|
|
return MaxCompressedSize(GetCompressionAlgorithm(), GetPixelsNum(), GetByteDepthImage());
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetPixelsNum() const {
|
|
return GetXPixelsNum() * GetYPixelsNum();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetXPixelsNum() const {
|
|
return detector.GetGeometry().GetWidth(IsGeometryTransformed());
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetYPixelsNum() const {
|
|
return detector.GetGeometry().GetHeight(IsGeometryTransformed());
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetPixelsNumConv() const {
|
|
return GetXPixelsNumConv() * GetYPixelsNumConv();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetXPixelsNumConv() const {
|
|
return detector.GetGeometry().GetWidth(true);
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetYPixelsNumConv() const {
|
|
return detector.GetGeometry().GetHeight(true);
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetPixel0OfModuleConv(uint16_t module_number) const {
|
|
return detector.GetGeometry().GetPixel0(module_number, true);
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetOverflow() const {
|
|
switch (GetByteDepthImage()) {
|
|
case 4:
|
|
return IsPixelSigned() ? static_cast<int64_t>(INT32_MAX) : static_cast<int64_t>(UINT32_MAX);
|
|
case 2:
|
|
return IsPixelSigned() ? INT16_MAX : UINT16_MAX;
|
|
case 1:
|
|
return IsPixelSigned() ? INT8_MAX : UINT8_MAX;
|
|
default:
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Pixel depth unsupported");
|
|
}
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetSaturationLimit() const {
|
|
auto sat = detector.GetSaturationLimit();
|
|
auto overflow = GetOverflow();
|
|
|
|
if (!sat)
|
|
return overflow;
|
|
|
|
return std::min(sat.value(), overflow);
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetUnderflow() const {
|
|
if (!IsPixelSigned())
|
|
return -1;
|
|
|
|
switch (GetByteDepthImage()) {
|
|
case 1:
|
|
return INT8_MIN;
|
|
case 2:
|
|
return INT16_MIN;
|
|
case 4:
|
|
return INT32_MIN;
|
|
default:
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Pixel depth unsupported");
|
|
}
|
|
}
|
|
|
|
std::optional<std::chrono::microseconds> DiffractionExperiment::GetZMQPreviewPeriod() const {
|
|
if (IsPedestalRun())
|
|
return {};
|
|
else
|
|
return zmq_preview_period;
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetDefaultPlotBinning() const {
|
|
// If images are equal longer than 100 ms, don't bin images
|
|
// If data collection is shorter than 5s, don't bin
|
|
if ((GetImageTime() >= 100ms) || (GetImageNum() * GetImageTime() < 5s))
|
|
return 1;
|
|
return 500ms / GetImageTime(); // 1 bin == 500 ms
|
|
}
|
|
|
|
bool DiffractionExperiment::IsUsingInternalPacketGen() const {
|
|
return detector_settings.IsInternalGeneratorEnable();
|
|
}
|
|
|
|
uint32_t DiffractionExperiment::GetSrcIPv4Address(uint32_t data_stream, uint32_t half_module) const {
|
|
uint32_t host = half_module + detector.GetUDPInterfaceCount() * GetFirstModuleOfDataStream(data_stream);
|
|
return detector.GetSrcIPv4Addr(host);
|
|
}
|
|
|
|
bool DiffractionExperiment::GetMaskModuleEdges() const {
|
|
return image_format_settings.IsMaskModuleEdges();
|
|
}
|
|
|
|
bool DiffractionExperiment::GetMaskChipEdges() const {
|
|
return image_format_settings.IsMaskChipEdges();
|
|
}
|
|
|
|
std::optional<UnitCell> DiffractionExperiment::GetUnitCell() const {
|
|
return dataset.GetUnitCell();
|
|
}
|
|
|
|
std::string DiffractionExperiment::GetUnitCellString() const {
|
|
auto uc = dataset.GetUnitCell();
|
|
if (uc.has_value()) {
|
|
return fmt::format("{:.1f}, {:.1f}, {:.1f}, {:.1f}, {:.1f} {:.1f}",
|
|
uc.value().a,
|
|
uc.value().b,
|
|
uc.value().c,
|
|
uc.value().alpha,
|
|
uc.value().beta,
|
|
uc.value().gamma);
|
|
} else
|
|
return "-";
|
|
}
|
|
|
|
std::optional<int64_t> DiffractionExperiment::GetSpaceGroupNumber() const {
|
|
return dataset.GetSpaceGroupNumber();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetStorageCellNumber() const {
|
|
auto storage_cells = detector_settings.GetStorageCells();
|
|
switch (GetDetectorMode()) {
|
|
case DetectorMode::PedestalG1:
|
|
if (IsFixedGainG1())
|
|
return storage_cells;
|
|
case DetectorMode::PedestalG2:
|
|
if (storage_cells > 1)
|
|
return 2;
|
|
else
|
|
return 1;
|
|
default:
|
|
return storage_cells;
|
|
}
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetStorageCellStart() const {
|
|
return detector_settings.GetStorageCellStart();
|
|
}
|
|
|
|
float DiffractionExperiment::GetLowQForAzimInt_recipA() const {
|
|
return az_integration_settings.GetLowQ_recipA();
|
|
}
|
|
float DiffractionExperiment::GetHighQForAzimInt_recipA() const {
|
|
return GetAzimuthalIntegrationSettings().GetHighQ_recipA();
|
|
}
|
|
|
|
float DiffractionExperiment::GetQSpacingForAzimInt_recipA() const {
|
|
return az_integration_settings.GetQSpacing_recipA();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::MaxSpotCount(int64_t input) {
|
|
dataset.MaxSpotCount(input);
|
|
return *this;
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetMaxSpotCount() const {
|
|
if (!IsSpotFindingEnabled())
|
|
return 0;
|
|
|
|
return dataset.GetMaxSpotCount();
|
|
}
|
|
|
|
std::string DiffractionExperiment::GetSampleName() const {
|
|
return dataset.GetSampleName();
|
|
}
|
|
|
|
void DiffractionExperiment::CheckDataProcessingSettings(const SpotFindingSettings &settings) {
|
|
check_finite("Signal to noise threshold", settings.signal_to_noise_threshold);
|
|
check_min("Signal to noise threshold", settings.signal_to_noise_threshold, 1);
|
|
check_min("Photon count threshold", settings.photon_count_threshold, 0);
|
|
check_min("Minimum pixels per spot", settings.min_pix_per_spot.value_or(2), 1);
|
|
check_min("Maximum pixels per spot", settings.max_pix_per_spot, settings.min_pix_per_spot.value_or(2) + 1);
|
|
// An unset limit means "no limit at that end", so there is nothing to check. A value that is present
|
|
// still has to be finite - NaN fails every comparison below, so testing it inside the branch would
|
|
// let it through unchecked.
|
|
if (settings.high_resolution_limit.has_value())
|
|
check_finite("Spot finding high resolution limit", *settings.high_resolution_limit);
|
|
if (settings.low_resolution_limit.has_value())
|
|
check_finite("Spot finding low resolution limit", *settings.low_resolution_limit);
|
|
|
|
if (settings.high_resolution_limit.has_value()) {
|
|
check_min("Spot finding high resolution limit", *settings.high_resolution_limit, 0.5);
|
|
check_max("Spot finding high resolution limit", *settings.high_resolution_limit, 50.0);
|
|
if (settings.low_resolution_limit.has_value())
|
|
check_min("Spot finding low resolution limit", *settings.low_resolution_limit,
|
|
*settings.high_resolution_limit);
|
|
} else if (settings.low_resolution_limit.has_value()) {
|
|
check_min("Spot finding low resolution limit", *settings.low_resolution_limit, 1.0);
|
|
check_max("Spot finding low resolution limit", *settings.low_resolution_limit, 50.0);
|
|
}
|
|
|
|
check_min("Ice ring width in Q-space (A^-1)", settings.ice_ring_width_Q_recipA, 0.0);
|
|
check_max("Ice ring width in Q-space (A^-1)", settings.ice_ring_width_Q_recipA, 1.0);
|
|
check_finite("Ice ring width in Q-space (A^-1)", settings.ice_ring_width_Q_recipA);
|
|
|
|
if (settings.high_res_gap_Q_recipA.has_value()) {
|
|
check_min("High resolution gap (A^-1)", settings.high_res_gap_Q_recipA.value(), 0.1);
|
|
check_max("High resolution gap (A^-1)", settings.high_res_gap_Q_recipA.value(), 5.0);
|
|
check_finite("High resolution gap (A^-1)", settings.high_res_gap_Q_recipA.value());
|
|
}
|
|
}
|
|
|
|
SpotFindingSettings DiffractionExperiment::DefaultDataProcessingSettings() {
|
|
return {};
|
|
}
|
|
|
|
void DiffractionExperiment::FillMessage(StartMessage &message) const {
|
|
message.images_per_file = GetImagesPerFile();
|
|
message.beam_center_x = GetBeamX_pxl();
|
|
message.beam_center_y = GetBeamY_pxl();
|
|
message.detector_distance = GetDetectorDistance_mm() * 1e-3f;
|
|
message.incident_wavelength = GetWavelength_A();
|
|
// NXmx incident_wavelength_spread is the FWHM of the wavelength distribution,
|
|
// in wavelength units. We store bandwidth as a relative FWHM (dlambda/lambda),
|
|
// so convert to absolute: dlambda = (dlambda/lambda) * lambda.
|
|
if (const auto bw = GetBandwidthFWHM())
|
|
message.incident_wavelength_spread = bw.value() * GetWavelength_A();
|
|
message.incident_energy = GetIncidentEnergy_keV() * 1e3f;
|
|
message.image_size_x = GetXPixelsNum();
|
|
message.image_size_y = GetYPixelsNum();
|
|
message.saturation_value = GetSaturationLimit() - 1;
|
|
message.error_value = GetUnderflow();
|
|
message.frame_time = std::chrono::duration<float>(GetImageTime()).count();
|
|
message.count_time = std::chrono::duration<float>(GetImageCountTime()).count();
|
|
message.number_of_images = GetImageNum();
|
|
message.pixel_size_x = GetPixelSize_mm() * 1e-3f;
|
|
message.pixel_size_y = GetPixelSize_mm() * 1e-3f;
|
|
message.sensor_material = detector.GetSensorMaterial();
|
|
message.sensor_thickness = detector.GetSensorThickness_um() * 1e-6f;
|
|
message.bit_depth_image = GetByteDepthImage() * 8;
|
|
// Both fields carry the depth of the STORED image, deliberately. NXmx has no field for it -
|
|
// only bit_depth_readout, defined as the depth of the detector electronics - so every NXmx
|
|
// reader takes bit_depth_readout as the width of the stored pixel (DIALS uses it to decide
|
|
// which values are masking markers, and cannot read a 32-bit image without it). The electronic
|
|
// readout depth is a constant of the detector and of no use to a data consumer, so reporting it
|
|
// here would only mislead: it differs from the image depth exactly when summation widens the
|
|
// image, which is when getting it wrong does damage.
|
|
message.bit_depth_readout = message.bit_depth_image;
|
|
// Lowest valid value, so a reader can tell the error marker from data. Unsigned images use 0;
|
|
// signed ones reserve INTx_MIN as the marker (GetUnderflow), so the lowest valid is one above.
|
|
message.underload_value = IsPixelSigned() ? GetUnderflow() + 1 : 0;
|
|
message.indexing_algorithm = GetIndexingAlgorithm();
|
|
message.images_per_trigger = dataset.GetImageNumPerTrigger();
|
|
|
|
if (GetDetectorType() == DetectorType::JUNGFRAU) {
|
|
message.storage_cell_number = GetStorageCellNumber();
|
|
message.storage_cell_delay_ns = GetStorageCellDelay().count();
|
|
}
|
|
|
|
message.file_prefix = GetFilePrefix();
|
|
message.pixel_signed = IsPixelSigned();
|
|
message.sample_name = GetSampleName();
|
|
message.max_spot_count = GetMaxSpotCount();
|
|
message.max_extra_lattices = indexing.GetMaxExtraLattices();
|
|
message.pixel_mask_enabled = IsApplyPixelMask();
|
|
message.detector_description = GetDetectorDescription();
|
|
message.space_group_number = GetSpaceGroupNumber();
|
|
message.unit_cell = GetUnitCell();
|
|
message.total_flux = GetTotalFlux();
|
|
message.attenuator_transmission = GetAttenuatorTransmission();
|
|
|
|
message.detector_translation[0] = 0.0f;
|
|
message.detector_translation[1] = 0.0f;
|
|
message.detector_translation[2] = GetDetectorDistance_mm() * 1e-3f;
|
|
|
|
message.source_name = GetSourceName();
|
|
message.source_type = GetSourceType();
|
|
|
|
message.instrument_name = GetInstrumentName();
|
|
message.summation = GetSummation();
|
|
message.user_data = GetHeaderAppendix();
|
|
|
|
message.countrate_correction_enabled = false;
|
|
message.flatfield_enabled = false;
|
|
|
|
message.goniometer = dataset.GetGoniometer();
|
|
message.grid_scan = dataset.GetGridScan();
|
|
|
|
message.run_number = GetRunNumber();
|
|
message.run_name = GetRunName();
|
|
|
|
message.gain_file_names = detector.GetGainFileNames();
|
|
message.rois = roi_mask.ExportMetadata();
|
|
message.data_reduction_factor_serialmx = GetLossyCompressionSerialMX();
|
|
message.experiment_group = dataset.GetExperimentGroup();
|
|
message.jfjoch_release = jfjoch_version();
|
|
message.detector_serial_number = detector.GetSerialNumber();
|
|
message.write_master_file = dataset.IsWriteNXmxHDF5Master();
|
|
message.overwrite = file_writer.IsOverwriteExistingFiles();
|
|
message.file_format = file_writer.GetFileFormat();
|
|
message.ring_current_mA = dataset.GetRingCurrent_mA();
|
|
message.sample_temperature_K = dataset.GetSampleTemperature_K();
|
|
message.fluorescence_spectrum = dataset.GetFluorescenceSpectrum();
|
|
|
|
message.poni_rot1 = dataset.GetPoniRot1_rad();
|
|
message.poni_rot2 = dataset.GetPoniRot2_rad();
|
|
message.poni_rot3 = dataset.GetPoniRot3_rad();
|
|
|
|
message.detect_ice_rings = dataset.IsDetectIceRings();
|
|
|
|
message.channels = {"default"};
|
|
|
|
message.smargon_position = dataset.GetSmargonPosition();
|
|
|
|
switch (GetDetectorType()) {
|
|
case DetectorType::JUNGFRAU:
|
|
message.jungfrau_conversion_enabled = IsJungfrauConvPhotonCnt();
|
|
if (IsJungfrauConvPhotonCnt())
|
|
message.jungfrau_conversion_factor = GetPhotonEnergyForConversion_keV() * 1000;
|
|
break;
|
|
case DetectorType::EIGER:
|
|
case DetectorType::DECTRIS:
|
|
message.threshold_energy["default"] = GetEigerThreshold_keV() * 1000.0f; // threshold in CBOR is in eV
|
|
break;
|
|
}
|
|
|
|
message.geometry_transformation_enabled = IsGeometryTransformed();
|
|
|
|
if (GetSummation() == 1)
|
|
message.summation_mode = "none";
|
|
else {
|
|
if (IsCPUSummation())
|
|
message.summation_mode = "cpu";
|
|
else
|
|
message.summation_mode = "fpga";
|
|
}
|
|
}
|
|
|
|
float DiffractionExperiment::GetPixelSize_mm() const {
|
|
return detector.GetPixelSize_mm();
|
|
}
|
|
|
|
std::string DiffractionExperiment::GetSourceName() const {
|
|
return instrument.GetSourceName();
|
|
}
|
|
|
|
std::string DiffractionExperiment::GetSourceType() const {
|
|
return instrument.GetSourceType();
|
|
}
|
|
|
|
std::string DiffractionExperiment::GetInstrumentName() const {
|
|
return instrument.GetInstrumentName();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetModuleFastDirectionStep(uint16_t module_number) const {
|
|
return detector.GetGeometry().GetFastDirectionStep(module_number);
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetModuleSlowDirectionStep(uint16_t module_number) const {
|
|
return detector.GetGeometry().GetSlowDirectionStep(module_number);
|
|
}
|
|
|
|
std::vector<std::string> DiffractionExperiment::GetDetectorModuleHostname() const {
|
|
return detector.GetDetectorModuleHostname();
|
|
}
|
|
|
|
std::string DiffractionExperiment::GetDetectorDescription() const {
|
|
return detector.GetDescription();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ApplySolidAngleCorr(bool input) {
|
|
az_integration_settings.SolidAngleCorrection(input);
|
|
return *this;
|
|
}
|
|
|
|
|
|
bool DiffractionExperiment::GetApplySolidAngleCorr() const {
|
|
return az_integration_settings.IsSolidAngleCorrection();
|
|
}
|
|
|
|
bool DiffractionExperiment::GetSaveCalibration() const {
|
|
auto val = dataset.IsSaveCalibration();
|
|
if (val.has_value())
|
|
return val.value();
|
|
|
|
// By default calibration is saved if more than 4 images
|
|
// to limit cases were size of the file is determined by the calibration
|
|
return (GetImageNum() > 4) && (GetDetectorType() == DetectorType::JUNGFRAU);
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::StorageCellDelay(std::chrono::nanoseconds input) {
|
|
detector_settings.StorageCellDelay(input);
|
|
return *this;
|
|
}
|
|
|
|
std::chrono::nanoseconds DiffractionExperiment::GetStorageCellDelay() const {
|
|
return detector_settings.GetStorageCellDelay();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::Summation(int64_t input) {
|
|
check_min("Summation factor", input, 1);
|
|
summation = input;
|
|
return *this;
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetSummation() const {
|
|
if (GetAutoSummation())
|
|
return summation;
|
|
return 1;
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetFPGASummation() const {
|
|
if (IsCPUSummation())
|
|
return 1;
|
|
return GetSummation();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetByteDepthFPGA() const {
|
|
if (IsCPUSummation())
|
|
return 2;
|
|
return GetByteDepthImage();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetUDPInterfaceCount() const {
|
|
return detector.GetUDPInterfaceCount();
|
|
}
|
|
|
|
std::vector<DetectorModuleConfig>
|
|
DiffractionExperiment::GetDetectorModuleConfig(const std::vector<AcquisitionDeviceNetConfig> &net_config) const{
|
|
std::vector<DetectorModuleConfig> ret;
|
|
for (int d = 0; d < GetDataStreamsNum(); d++) {
|
|
for (int m = 0; m < GetModulesNum(d); m++) {
|
|
DetectorModuleConfig mod_cfg;
|
|
mod_cfg.data_stream = d;
|
|
mod_cfg.udp_dest_port_1 = 1024 + m;
|
|
mod_cfg.udp_dest_port_2 = 1024 + m;
|
|
if (detector.GetUDPInterfaceCount() == 2) {
|
|
mod_cfg.ipv4_src_addr_1 = IPv4AddressToStr(GetSrcIPv4Address(d, 2 * m));
|
|
mod_cfg.ipv4_src_addr_2 = IPv4AddressToStr(GetSrcIPv4Address(d, 2 * m + 1));
|
|
} else {
|
|
mod_cfg.ipv4_src_addr_1 = IPv4AddressToStr(GetSrcIPv4Address(d, m));
|
|
mod_cfg.ipv4_src_addr_2 = IPv4AddressToStr(GetSrcIPv4Address(d, m)); // not used, settings just in case
|
|
}
|
|
mod_cfg.ipv4_dest_addr_1 = net_config[d].ipv4_addr;
|
|
mod_cfg.ipv4_dest_addr_2 = net_config[d].ipv4_addr;
|
|
mod_cfg.mac_addr_dest_1 = net_config[d].mac_addr;
|
|
mod_cfg.mac_addr_dest_2 = net_config[d].mac_addr;
|
|
mod_cfg.module_id_in_data_stream = m;
|
|
ret.emplace_back(std::move(mod_cfg));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
float DiffractionExperiment::GetLowQForBkgEstimate_recipA() const {
|
|
return az_integration_settings.GetBkgEstimateLowQ_recipA();
|
|
}
|
|
|
|
float DiffractionExperiment::GetHighQForBkgEstimate_recipA() const {
|
|
return az_integration_settings.GetBkgEstimateHighQ_recipA();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::AttenuatorTransmission(const std::optional<float> &input) {
|
|
dataset.AttenuatorTransmission(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::TotalFlux(const std::optional<float> &input) {
|
|
dataset.TotalFlux(input);
|
|
return *this;
|
|
}
|
|
|
|
std::optional<float> DiffractionExperiment::GetAttenuatorTransmission() const {
|
|
return dataset.GetAttenuatorTransmission();
|
|
}
|
|
|
|
std::optional<float> DiffractionExperiment::GetTotalFlux() const {
|
|
return dataset.GetTotalFlux();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::Goniometer(const std::optional<GoniometerAxis> &input) {
|
|
dataset.Goniometer(input);
|
|
return *this;
|
|
}
|
|
|
|
std::optional<GoniometerAxis> DiffractionExperiment::GetGoniometer() const {
|
|
return dataset.GetGoniometer();
|
|
}
|
|
|
|
std::optional<GridScanSettings> DiffractionExperiment::GetGridScan() const {
|
|
return dataset.GetGridScan();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::UsingGainHG0(bool input) {
|
|
detector_settings.UseGainHG0(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::FixedGainG1(bool input) {
|
|
detector_settings.FixGainG1(input);
|
|
return *this;
|
|
}
|
|
|
|
bool DiffractionExperiment::IsFixedGainG1() const {
|
|
return detector_settings.IsFixGainG1();
|
|
}
|
|
|
|
bool DiffractionExperiment::IsUsingGainHG0() const {
|
|
return detector_settings.IsUseGainHG0();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::HeaderAppendix(const nlohmann::json &input) {
|
|
dataset.HeaderAppendix(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImageAppendix(const nlohmann::json &input) {
|
|
dataset.ImageAppendix(input);
|
|
return *this;
|
|
}
|
|
|
|
const nlohmann::json& DiffractionExperiment::GetHeaderAppendix() const {
|
|
return dataset.GetHeaderAppendix();
|
|
}
|
|
|
|
const nlohmann::json& DiffractionExperiment::GetImageAppendix() const {
|
|
return dataset.GetImageAppendix();
|
|
}
|
|
|
|
uint64_t DiffractionExperiment::GetRunNumber() const {
|
|
if (dataset.GetRunNumber())
|
|
return dataset.GetRunNumber().value();
|
|
return series_id;
|
|
}
|
|
|
|
std::string DiffractionExperiment::GetRunName() const {
|
|
auto run_name = dataset.GetRunName();
|
|
if (run_name)
|
|
return run_name.value();
|
|
else
|
|
return std::to_string(series_id) + ":" + dataset.GetFilePrefix();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::IncrementRunNumber() {
|
|
series_id++;
|
|
return *this;
|
|
}
|
|
|
|
Coord DiffractionExperiment::GetModuleFastDirection(uint16_t module_number) const {
|
|
return detector.GetGeometry().GetFastDirection(module_number);
|
|
}
|
|
|
|
Coord DiffractionExperiment::GetModuleSlowDirection(uint16_t module_number) const {
|
|
return detector.GetGeometry().GetSlowDirection(module_number);
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::JungfrauConvPhotonCnt(bool input) {
|
|
image_format_settings.JungfrauConversion(input);
|
|
return *this;
|
|
}
|
|
|
|
bool DiffractionExperiment::IsJungfrauConvPhotonCnt() const {
|
|
if (!IsPedestalRun() && (GetDetectorType() == DetectorType::JUNGFRAU))
|
|
return image_format_settings.IsJungfrauConversion();
|
|
else
|
|
return false;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::DetectorDelay(std::chrono::nanoseconds input) {
|
|
detector_settings.DetectorDelay(input);
|
|
return *this;
|
|
}
|
|
|
|
std::chrono::nanoseconds DiffractionExperiment::GetDetectorDelay() const {
|
|
return detector_settings.GetDetectorDelay();
|
|
}
|
|
|
|
const DetectorSetup &DiffractionExperiment::GetDetectorSetup() const {
|
|
return detector;
|
|
}
|
|
|
|
DetectorSetup &DiffractionExperiment::Detector() {
|
|
return detector;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::PulsedSource(bool input) {
|
|
instrument.PulsedSource(input);
|
|
return *this;
|
|
}
|
|
|
|
bool DiffractionExperiment::IsPulsedSource() const {
|
|
return instrument.IsPulsedSource();
|
|
}
|
|
|
|
bool DiffractionExperiment::IsSpotFindingEnabled() const {
|
|
return dataset.IsSpotFindingEnabled() && !IsPedestalRun();
|
|
}
|
|
|
|
float DiffractionExperiment::GetPhotonEnergyForConversion_keV() const {
|
|
auto val = GetJungfrauConversionFactor_keV();
|
|
if (val.has_value())
|
|
return val.value();
|
|
else
|
|
return GetIncidentEnergy_keV();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::InternalPacketGeneratorImages(int64_t input) {
|
|
detector_settings.InternalGeneratorImages(input);
|
|
return *this;
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetInternalPacketGeneratorImages() const {
|
|
return detector_settings.GetInternalGeneratorImages();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImportDatasetSettings(const DatasetSettings &input) {
|
|
auto tmp = dataset;
|
|
dataset = input;
|
|
|
|
auto image_time = input.GetImageTime();
|
|
if (image_time) {
|
|
switch (GetDetectorType()) {
|
|
case DetectorType::EIGER:
|
|
case DetectorType::DECTRIS:
|
|
check_min("Image time [ns]", image_time.value().count(), detector.GetMinFrameTime().count());
|
|
summation = 1;
|
|
break;
|
|
case DetectorType::JUNGFRAU:
|
|
if (image_time->count() % GetFrameTime().count() != 0) {
|
|
dataset = tmp;
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Image time must be multiple of frame time");
|
|
}
|
|
if (GetFrameTime().count() == 0) {
|
|
dataset = tmp;
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Frame time cannot be zero");
|
|
}
|
|
if (image_time < GetFrameTime()) {
|
|
dataset = tmp;
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Image time cannot be less than base frame time");
|
|
}
|
|
this->Summation(image_time.value() / GetFrameTime());
|
|
break;
|
|
}
|
|
} else
|
|
summation = 1;
|
|
|
|
if (dataset.GridScan())
|
|
dataset.GridScan()->ImageNum(GetImageNum());
|
|
|
|
if (GetFrameNum() >= MAX_FRAMES) {
|
|
dataset = tmp;
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Frame number (summation * images_per_trigger * ntrigger) cannot exceed "
|
|
+ std::to_string(MAX_FRAMES));
|
|
}
|
|
|
|
if (GetImageNum() > MAX_IMAGE_NUMBER) {
|
|
dataset = tmp;
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Number of images cannot exceed " + std::to_string(MAX_IMAGE_NUMBER));
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
DatasetSettings DiffractionExperiment::GetDatasetSettings() const {
|
|
return dataset;
|
|
}
|
|
|
|
ROIMap &DiffractionExperiment::ROI() {
|
|
return roi_mask;
|
|
}
|
|
|
|
const ROIMap &DiffractionExperiment::ROI() const {
|
|
return roi_mask;
|
|
}
|
|
|
|
std::vector<uint16_t> DiffractionExperiment::ExportROIMap() const {
|
|
return roi_mask.GetROIMap(GetDiffractionGeometry(), GetXPixelsNumConv(), GetYPixelsNumConv());
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImagesPerFile(const std::optional<int64_t> &input) {
|
|
dataset.ImagesPerFile(input);
|
|
return *this;
|
|
}
|
|
|
|
// Images per file is optional in the settings, because at setup time the acquisition it should suit
|
|
// is not necessarily known yet. Everything downstream of here - receiver, pusher, puller, writer -
|
|
// requires a fixed, non-zero number, so this is the one place that resolves it and it must always
|
|
// return one.
|
|
int64_t DiffractionExperiment::GetImagesPerFile() const {
|
|
// A single self-contained file holds the whole run whatever the setting says.
|
|
if (file_writer.GetFileFormat() == FileWriterFormat::NXmxIntegrated)
|
|
return std::max<int64_t>(GetImageNum(), 1);
|
|
|
|
if (const auto requested = dataset.GetImagesPerFile())
|
|
return requested.value(); // taken literally
|
|
|
|
const int64_t image_num = GetImageNum();
|
|
|
|
// Rotation: keep the sweep in one data file. A legacy master splits over several files is read
|
|
// by some programs as only its first one, and a rotation dataset is small enough that the file
|
|
// is not a problem - past the limit the writer parallelism is worth more, one file going to one
|
|
// writer (see ZMQStream2Pusher::SendImage).
|
|
const auto goniometer = GetGoniometer();
|
|
if (goniometer.has_value() && (goniometer->GetIncrement_deg() != 0.0f)
|
|
&& (image_num > 0) && (image_num <= ROTATION_SINGLE_FILE_IMAGE_LIMIT))
|
|
return image_num;
|
|
|
|
// Grid scan: split on whole rows of the fast axis, so a file is a meaningful piece of the grid
|
|
// rather than an arbitrary cut across it.
|
|
if (const auto grid_scan = GetGridScan()) {
|
|
const int64_t n_fast = grid_scan->GetNFast();
|
|
if (n_fast > 0)
|
|
return ((DEFAULT_IMAGES_PER_FILE + n_fast - 1) / n_fast) * n_fast;
|
|
}
|
|
|
|
return DEFAULT_IMAGES_PER_FILE; // stills, serial
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetImageBufferLocationSize() const {
|
|
// A buffer slot holds the compressed image plus the per-image CBOR metadata (spot list, reflection
|
|
// list, azimuthal profile, ...). The reflection list is bounded by the ONLINE cap
|
|
// BraggPrediction::kOnlineMaxReflections at ~170 B each, but it is not the whole of it: the capped
|
|
// spot list and the three azimuthal arrays at up to 65534 bins add another megabyte, and the
|
|
// measured worst case is ~2.9 MB. A frame whose metadata still does not fit is dropped
|
|
// individually by the receiver rather than aborting the collection.
|
|
return GetMaxCompressedSize() + kImageMetadataHeadroom;
|
|
}
|
|
|
|
float DiffractionExperiment::GetLossyCompressionSerialMX() const {
|
|
return dataset.GetLossyCompressionSerialMX();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::LossyCompressionSerialMX(float input) {
|
|
dataset.LossyCompressionSerialMX(input);
|
|
return *this;
|
|
}
|
|
|
|
std::optional<int64_t> DiffractionExperiment::GetLossyCompressionPoisson() const {
|
|
return dataset.GetLossyCompressionPoisson();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::LossyCompressionPoisson(const std::optional<int64_t> &input) {
|
|
dataset.LossyCompressionPoisson(input);
|
|
return *this;
|
|
}
|
|
|
|
std::string DiffractionExperiment::GetExperimentGroup() const {
|
|
return dataset.GetExperimentGroup();
|
|
}
|
|
|
|
std::optional<int64_t> DiffractionExperiment::GetPixelValueLowThreshold() const {
|
|
return dataset.GetPixelValueLowThreshold();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::PixelValueLowThreshold(const std::optional<int64_t> &input) {
|
|
dataset.PixelValueLowThreshold(input);
|
|
return *this;
|
|
}
|
|
|
|
std::optional<int64_t> DiffractionExperiment::GetPixelValueHighThreshold() const {
|
|
return dataset.GetPixelValueHighThreshold();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::PixelValueHighThreshold(const std::optional<int64_t> &input) {
|
|
dataset.PixelValueHighThreshold(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImportInstrumentMetadata(const InstrumentMetadata &input) {
|
|
instrument = input;
|
|
return *this;
|
|
}
|
|
|
|
InstrumentMetadata DiffractionExperiment::GetInstrumentMetadata() const {
|
|
return instrument;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImportFileWriterSettings(const FileWriterSettings &input) {
|
|
file_writer = input;
|
|
return *this;
|
|
}
|
|
|
|
FileWriterSettings DiffractionExperiment::GetFileWriterSettings() const {
|
|
return file_writer;
|
|
}
|
|
|
|
bool DiffractionExperiment::IsGeometryTransformed() const {
|
|
// For DECTRIS detectors always operate in transformed geometry
|
|
return (GetDetectorType() == DetectorType::DECTRIS) || image_format_settings.IsGeometryTransformed();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::GeometryTransformation(bool input) {
|
|
image_format_settings.GeometryTransformed(input);
|
|
return *this;
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetImageFillValue() const {
|
|
switch (GetByteDepthImage()) {
|
|
case 1:
|
|
if (IsPixelSigned())
|
|
return INT8_MIN;
|
|
return UINT8_MAX;
|
|
case 2:
|
|
if (IsPixelSigned())
|
|
return INT16_MIN;
|
|
return UINT16_MAX;
|
|
|
|
case 4:
|
|
if (IsPixelSigned())
|
|
return INT32_MIN;
|
|
return UINT32_MAX;
|
|
default:
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Pixel depth unsupported");
|
|
}
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetBitDepthReadout() const {
|
|
if (GetDetectorType() == DetectorType::EIGER)
|
|
return GetEigerBitDepth();
|
|
|
|
auto det_value = detector.GetBitDepthReadout();
|
|
if (det_value)
|
|
return det_value.value();
|
|
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Bit depth readout not configured");
|
|
}
|
|
|
|
bool DiffractionExperiment::IsPedestalRun() const {
|
|
switch (GetDetectorMode()) {
|
|
case DetectorMode::PedestalG0:
|
|
case DetectorMode::PedestalG1:
|
|
case DetectorMode::PedestalG2:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool DiffractionExperiment::GetAutoSummation() const {
|
|
if (IsPedestalRun() || (GetStorageCellNumber() > 1))
|
|
return false; // for pedestal or more than 1 storage cell summation doesn't make sense and should be always turned off
|
|
else
|
|
return image_format_settings.IsAutoSummation();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::AutoSummation(bool input) {
|
|
image_format_settings.AutoSummation(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::BitDepthImage(const std::optional<int64_t> &input) {
|
|
image_format_settings.BitDepthImage(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::PixelSigned(const std::optional<bool> &input) {
|
|
image_format_settings.PixelSigned(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::JungfrauConversionFactor_keV(const std::optional<float> &input) {
|
|
image_format_settings.JungfrauConvFactor_keV(input);
|
|
return *this;
|
|
}
|
|
|
|
std::optional<float> DiffractionExperiment::GetJungfrauConversionFactor_keV() const {
|
|
return image_format_settings.GetJungfrauConvFactor_keV();
|
|
}
|
|
|
|
DiffractionExperiment & DiffractionExperiment::Conversion() {
|
|
image_format_settings.Conv();
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment & DiffractionExperiment::Raw() {
|
|
image_format_settings.Raw();
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImportDetectorSettings(const DetectorSettings &input) {
|
|
check_min("Frame time [ns]", input.GetFrameTime().count(), detector.GetMinFrameTime().count());
|
|
|
|
if (GetDetectorType() == DetectorType::JUNGFRAU) {
|
|
if (!input.GetCountTime().has_value()) {
|
|
// implicit count time
|
|
check_max("Frame time [ns]", input.GetFrameTime().count(),
|
|
(MAX_COUNT_TIME_JUNGFRAU + detector.GetReadOutTime()).count());
|
|
} else {
|
|
// explicit count time
|
|
check_max("Count time [ns]", input.GetCountTime().value().count(),
|
|
MAX_COUNT_TIME_JUNGFRAU.count());
|
|
}
|
|
|
|
if ((input.GetTiming() == DetectorTiming::Burst) ||
|
|
(input.GetTiming() == DetectorTiming::Gated))
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Burst and gated timing modes not supported in JUNGFRAU");
|
|
}
|
|
detector_settings = input;
|
|
return *this;
|
|
}
|
|
|
|
DetectorSettings DiffractionExperiment::GetDetectorSettings() const {
|
|
return detector_settings;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImportImageFormatSettings(const ImageFormatSettings &input) {
|
|
image_format_settings = input;
|
|
return *this;
|
|
}
|
|
|
|
ImageFormatSettings DiffractionExperiment::GetImageFormatSettings() const {
|
|
return image_format_settings;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImportAzimuthalIntegrationSettings(const AzimuthalIntegrationSettings &input) {
|
|
az_integration_settings = input;
|
|
return *this;
|
|
}
|
|
|
|
AzimuthalIntegrationSettings DiffractionExperiment::GetAzimuthalIntegrationSettings() const {
|
|
// An unset high q means "as far as the detector reaches", so resolve it here, where the geometry is
|
|
// known. Everyone reads the settings through this getter, so nobody sees an unresolved q range.
|
|
AzimuthalIntegrationSettings ret = az_integration_settings;
|
|
ret.ResolveHighQ(GetDetectorMaxQ_recipA());
|
|
return ret;
|
|
}
|
|
|
|
float DiffractionExperiment::GetDetectorMaxResolution_A() const {
|
|
const float q = GetDetectorMaxQ_recipA();
|
|
return q > 0.0f ? 2.0f * static_cast<float>(PI) / q : 0.0f;
|
|
}
|
|
|
|
float DiffractionExperiment::GetDetectorMaxQ_recipA() const {
|
|
const DiffractionGeometry geom = GetDiffractionGeometry();
|
|
const auto width = static_cast<float>(GetXPixelsNumConv());
|
|
const auto height = static_cast<float>(GetYPixelsNumConv());
|
|
|
|
// The largest scattering angle sits at one of the detector corners, wherever the beam centre is.
|
|
float q = 0.0f;
|
|
for (const float x: {0.0f, width})
|
|
for (const float y: {0.0f, height})
|
|
q = std::max(q, geom.PxlToQ(x, y));
|
|
return q;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::PolarizationFactor(const std::optional<float> &input) {
|
|
dataset.PolarizationFactor(input);
|
|
return *this;
|
|
}
|
|
|
|
std::optional<float> DiffractionExperiment::GetPolarizationFactor() const {
|
|
return dataset.GetPolarizationFactor();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::BandwidthFWHM(const std::optional<float> &input) {
|
|
dataset.BandwidthFWHM(input);
|
|
return *this;
|
|
}
|
|
|
|
std::optional<float> DiffractionExperiment::GetBandwidthFWHM() const {
|
|
return dataset.GetBandwidthFWHM();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::SaveCalibration(const std::optional<bool> &input) {
|
|
dataset.SaveCalibration(input);
|
|
return *this;
|
|
}
|
|
|
|
float DiffractionExperiment::GetPedestalG0RMSLimit() const {
|
|
return image_format_settings.GetPedestalG0RMSLimit();
|
|
}
|
|
|
|
uint32_t DiffractionExperiment::GetPedestalMinImageCount() const {
|
|
return detector_settings.GetPedestalMinImageCount();
|
|
}
|
|
|
|
float DiffractionExperiment::GetEigerThreshold_keV() const {
|
|
float thr;
|
|
|
|
if (GetDetectorMode() == DetectorMode::DarkMask)
|
|
thr = GetDarkMaskSettings().GetThreshold_keV();
|
|
else {
|
|
auto val = detector_settings.GetEigerThreshold_keV();
|
|
|
|
if (val)
|
|
thr = val.value();
|
|
else
|
|
thr = GetIncidentEnergy_keV() / 2.0f;
|
|
}
|
|
|
|
if (thr < detector.GetMinThreshold_keV())
|
|
thr = detector.GetMinThreshold_keV();
|
|
|
|
return thr;
|
|
}
|
|
|
|
DetectorTiming DiffractionExperiment::GetDetectorTiming() const {
|
|
// For calibration use Auto timing mode
|
|
switch (GetDetectorMode()) {
|
|
case DetectorMode::DarkMask:
|
|
return DetectorTiming::Auto;
|
|
default:
|
|
return detector_settings.GetTiming();
|
|
}
|
|
}
|
|
|
|
bool DiffractionExperiment::IsDetectorModuleSync() const {
|
|
return detector.IsModuleSync();
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetEigerBitDepth() const {
|
|
auto tmp = detector_settings.GetEigerBitDepth();
|
|
if (tmp.has_value())
|
|
return tmp.value();
|
|
if (GetFrameTime() >= std::chrono::microseconds(2622) )
|
|
return 32;
|
|
if (GetFrameTime() < std::chrono::microseconds(500))
|
|
return 8;
|
|
return 16;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::EigerBitDepth(const std::optional<int64_t> &input) {
|
|
detector_settings.EigerBitDepth(input);
|
|
return *this;
|
|
}
|
|
|
|
DetectorType DiffractionExperiment::GetDetectorType() const {
|
|
return detector.GetDetectorType();
|
|
}
|
|
|
|
bool DiffractionExperiment::IsMaskPixelsWithoutG0() const {
|
|
if (GetDetectorType() == DetectorType::JUNGFRAU)
|
|
return image_format_settings.IsMaskPixelsWithoutG0();
|
|
return false;
|
|
}
|
|
|
|
bool DiffractionExperiment::IsApplyPixelMask() const {
|
|
return image_format_settings.IsApplyPixelMask() && !IsPedestalRun();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::CPUSummation(bool input) {
|
|
cpu_summation = input;
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ApplyPixelMask(bool input) {
|
|
image_format_settings.ApplyPixelMask(input);
|
|
return *this;
|
|
}
|
|
|
|
bool DiffractionExperiment::IsCPUSummation() const {
|
|
if (summation == 1)
|
|
return false;
|
|
if (summation >= MAX_FPGA_SUMMATION)
|
|
return true;
|
|
return cpu_summation;
|
|
}
|
|
|
|
DiffractionExperiment & DiffractionExperiment::ElectronSource(bool input) {
|
|
instrument.ElectronSource(input);
|
|
return *this;
|
|
}
|
|
|
|
bool DiffractionExperiment::IsElectronSource() const {
|
|
return instrument.IsElectronSource();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::SetFileWriterFormat(FileWriterFormat input) {
|
|
file_writer.FileFormat(input);
|
|
return *this;
|
|
}
|
|
|
|
FileWriterFormat DiffractionExperiment::GetFileWriterFormat() const {
|
|
return file_writer.GetFileFormat();
|
|
}
|
|
|
|
DiffractionGeometry DiffractionExperiment::GetDiffractionGeometry() const {
|
|
DiffractionGeometry g;
|
|
g.Wavelength_A(GetWavelength_A())
|
|
.PixelSize_mm(GetPixelSize_mm())
|
|
.BeamX_pxl(dataset.GetBeamX_pxl())
|
|
.BeamY_pxl(dataset.GetBeamY_pxl())
|
|
.DetectorDistance_mm(dataset.GetDetectorDistance_mm())
|
|
.PoniRot1_rad(dataset.GetPoniRot1_rad())
|
|
.PoniRot2_rad(dataset.GetPoniRot2_rad())
|
|
.PoniRot3_rad(dataset.GetPoniRot3_rad())
|
|
.Rotation(dataset.GetGoniometer());
|
|
return g;
|
|
}
|
|
|
|
|
|
void DiffractionExperiment::CalcAzIntCorrRawCoord(float *output, size_t module_number) const {
|
|
if (module_number >= GetModulesNum())
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Wrong module number");
|
|
auto geom = GetDiffractionGeometry();
|
|
|
|
for (int i = 0; i < RAW_MODULE_SIZE; i++) {
|
|
auto [x,y] = RawToConvertedCoordinate(*this, module_number, i);
|
|
if (GetApplySolidAngleCorr())
|
|
output[i] /= geom.CalcAzIntSolidAngleCorr(static_cast<float>(x), static_cast<float>(y));
|
|
auto p = GetPolarizationFactor();
|
|
if (p.has_value())
|
|
output[i] /= geom.CalcAzIntPolarizationCorr(static_cast<float>(x), static_cast<float>(y), p.value());
|
|
}
|
|
}
|
|
|
|
void DiffractionExperiment::CalcSpotFinderResolutionMap(float *data, size_t module_number) const {
|
|
if (module_number >= GetModulesNum())
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Wrong module number");
|
|
auto geom = GetDiffractionGeometry();
|
|
|
|
for (int i = 0; i < RAW_MODULE_SIZE; i++) {
|
|
auto [x,y] = RawToConvertedCoordinate(*this, module_number, i);
|
|
data[i] = geom.PxlToRes(static_cast<float>(x), static_cast<float>(y));
|
|
}
|
|
}
|
|
|
|
CompressedImageMode DiffractionExperiment::GetImageMode() const {
|
|
switch (GetByteDepthImage()) {
|
|
case 1:
|
|
return (IsPixelSigned() ? CompressedImageMode::Int8 : CompressedImageMode::Uint8);
|
|
case 2:
|
|
return (IsPixelSigned() ? CompressedImageMode::Int16 : CompressedImageMode::Uint16);
|
|
case 4:
|
|
return (IsPixelSigned() ? CompressedImageMode::Int32 : CompressedImageMode::Uint32);
|
|
default:
|
|
throw (JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Bit depth not supported"));
|
|
}
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::IndexingAlgorithm(IndexingAlgorithmEnum input) {
|
|
indexing.Algorithm(input);
|
|
return *this;
|
|
}
|
|
|
|
IndexingAlgorithmEnum DiffractionExperiment::GetIndexingAlgorithm() const {
|
|
// Rotation indexing accumulates a dense multi-frame reciprocal-space cloud that needs a GLOBAL
|
|
// lattice finder (3D FFT). ffbidx is a single-still, known-cell ORIENTATION finder: it cannot
|
|
// recover the global orientation from that cloud (it locks onto a local wedge -> garbage). So
|
|
// rotation always uses FFT (GPU) or FFTW (CPU), regardless of the requested algorithm or whether a
|
|
// cell is known - a reference MTZ (-z) or -C supplies a cell and used to silently flip Auto->FFBIDX,
|
|
// switching the rotation indexer to the one solver that cannot handle its input.
|
|
if (IsRotationIndexing())
|
|
return get_gpu_count() == 0 ? IndexingAlgorithmEnum::FFTW : IndexingAlgorithmEnum::FFT;
|
|
|
|
auto cell = GetUnitCell().has_value();
|
|
|
|
switch (indexing.GetAlgorithm()) {
|
|
case IndexingAlgorithmEnum::FFBIDX:
|
|
if (!cell)
|
|
return IndexingAlgorithmEnum::None;
|
|
return IndexingAlgorithmEnum::FFBIDX;
|
|
case IndexingAlgorithmEnum::Auto:
|
|
if (get_gpu_count() == 0)
|
|
return IndexingAlgorithmEnum::FFTW;
|
|
if (!cell)
|
|
return IndexingAlgorithmEnum::FFT;
|
|
return IndexingAlgorithmEnum::FFBIDX;
|
|
case IndexingAlgorithmEnum::FFT:
|
|
return IndexingAlgorithmEnum::FFT;
|
|
case IndexingAlgorithmEnum::FFTW:
|
|
return IndexingAlgorithmEnum::FFTW;
|
|
default:
|
|
return IndexingAlgorithmEnum::None;
|
|
}
|
|
}
|
|
|
|
IndexingSettings DiffractionExperiment::GetIndexingSettings() const {
|
|
return indexing;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImportIndexingSettings(const IndexingSettings &input) {
|
|
indexing = input;
|
|
return *this;
|
|
}
|
|
|
|
float DiffractionExperiment::GetIndexingTolerance() const {
|
|
return indexing.GetTolerance();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::IndexingTolerance(float input) {
|
|
indexing.Tolerance(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::GridScan(const std::optional<GridScanSettings> &input) {
|
|
dataset.GridScan(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::SampleTemperature_K(const std::optional<float> &input) {
|
|
dataset.SampleTemperature_K(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::RingCurrent_mA(const std::optional<float> &input) {
|
|
dataset.RingCurrent_mA(input);
|
|
return *this;
|
|
}
|
|
|
|
std::optional<float> DiffractionExperiment::GetSampleTemperature_K() const {
|
|
return dataset.GetSampleTemperature_K();
|
|
}
|
|
|
|
std::optional<float> DiffractionExperiment::GetRingCurrent_mA() const {
|
|
return dataset.GetRingCurrent_mA();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImportBraggIntegrationSettings(const BraggIntegrationSettings &input) {
|
|
bragg_integration_settings = input;
|
|
return *this;
|
|
}
|
|
|
|
BraggIntegrationSettings DiffractionExperiment::GetBraggIntegrationSettings() const {
|
|
return bragg_integration_settings;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImportScalingSettings(const ScalingSettings &input) {
|
|
scaling_settings = input;
|
|
return *this;
|
|
}
|
|
|
|
ScalingSettings DiffractionExperiment::GetScalingSettings() const {
|
|
return scaling_settings;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::PoniRot1_rad(float input) {
|
|
dataset.PoniRot1_rad(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::PoniRot2_rad(float input) {
|
|
dataset.PoniRot2_rad(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::PoniRot3_rad(float input) {
|
|
dataset.PoniRot3_rad(input);
|
|
return *this;
|
|
}
|
|
|
|
float DiffractionExperiment::GetPoniRot1_rad() const {
|
|
return dataset.GetPoniRot1_rad();
|
|
}
|
|
|
|
float DiffractionExperiment::GetPoniRot2_rad() const {
|
|
return dataset.GetPoniRot2_rad();
|
|
}
|
|
|
|
float DiffractionExperiment::GetPoniRot3_rad() const {
|
|
return dataset.GetPoniRot3_rad();
|
|
}
|
|
|
|
GeomRefinementAlgorithmEnum DiffractionExperiment::GetGeomRefinementAlgorithm() const {
|
|
return indexing.GetGeomRefinementAlgorithm();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::GeomRefinementAlgorithm(GeomRefinementAlgorithmEnum input) {
|
|
indexing.GeomRefinementAlgorithm(input);
|
|
return *this;
|
|
}
|
|
|
|
gemmi::CrystalSystem DiffractionExperiment::GetCrystalSystem() const {
|
|
auto sg = GetGemmiSpaceGroup();
|
|
if (!sg)
|
|
return gemmi::CrystalSystem::Monoclinic;
|
|
|
|
return sg->crystal_system();
|
|
}
|
|
|
|
std::string DiffractionExperiment::GetSpaceGroupName() const {
|
|
auto sg = GetGemmiSpaceGroup();
|
|
if (!sg)
|
|
return "";
|
|
return sg->short_name();
|
|
}
|
|
|
|
std::optional<gemmi::SpaceGroup> DiffractionExperiment::GetGemmiSpaceGroup() const {
|
|
auto sg = GetSpaceGroupNumber();
|
|
if (!sg)
|
|
return std::nullopt;
|
|
const gemmi::SpaceGroup *g = gemmi::find_spacegroup_by_number(sg.value());
|
|
if (g == nullptr)
|
|
return std::nullopt;
|
|
return *g;
|
|
}
|
|
|
|
char DiffractionExperiment::GetCentering() const {
|
|
auto sg = GetGemmiSpaceGroup();
|
|
if (!sg)
|
|
return 'P';
|
|
return sg->centring_type();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::FluorescenceSpectrum(const XrayFluorescenceSpectrum &input) {
|
|
dataset.FluorescenceSpectrum(input);
|
|
return *this;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::DetectIceRings(bool input) {
|
|
dataset.DetectIceRings(input);
|
|
return *this;
|
|
}
|
|
|
|
bool DiffractionExperiment::IsDetectIceRings() const {
|
|
return dataset.IsDetectIceRings();
|
|
}
|
|
|
|
const XrayFluorescenceSpectrum &DiffractionExperiment::GetFluorescenceSpectrum() const {
|
|
return dataset.GetFluorescenceSpectrum();
|
|
}
|
|
|
|
DarkMaskSettings DiffractionExperiment::GetDarkMaskSettings() const {
|
|
return dark_mask_settings;
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::ImportDarkMaskSettings(const DarkMaskSettings &input) {
|
|
dark_mask_settings = input;
|
|
return *this;
|
|
}
|
|
|
|
int64_t DiffractionExperiment::GetDarkMaskNumberOfFrames() const {
|
|
if (GetDetectorType() == DetectorType::DECTRIS)
|
|
return GetDarkMaskSettings().GetNumberOfFrames();
|
|
return 0;
|
|
}
|
|
|
|
bool DiffractionExperiment::IsRotationIndexing() const {
|
|
return GetGoniometer().has_value() && indexing.GetRotationIndexing();
|
|
}
|
|
|
|
DiffractionExperiment &DiffractionExperiment::RunNumber(uint64_t input) {
|
|
series_id = input;
|
|
return *this;
|
|
}
|
|
|
|
std::optional<double> DiffractionExperiment::GetRotationWedgeForScaling() const {
|
|
return scaling_settings.GetRotationWedgeForScaling();
|
|
}
|
|
|
|
bool DiffractionExperiment::GetRefineRotationWedgeInScaling() const {
|
|
return scaling_settings.GetRotationWedgeForScaling().has_value() && scaling_settings.GetRefineWedge();
|
|
}
|