Until now nothing in the tree said what analysis a run performed. The answer was composed
at each site out of four independent scalars - the detector type, two separate "spot finding
off" switches, an indexing flag and a rotation flag - so what was configured and what
actually ran were different things, and no single place could be read to find out which.
AnalysisMode {None, MXRotation, MXStills, Azint, Grid, PowderCalibration} is that statement,
in common/ because all three programs configure the DiffractionExperiment that carries it.
AnalysisSettings sits on the experiment beside IndexingSettings, outside the DatasetSettings
member, which is the one thing a /start replaces wholesale - so the mode is persistent by
construction rather than by a rule someone has to remember.
The mode does not label a run, it decides it. AnalysisModeStages() is a table - modes as
rows, pipeline stages as columns - and every gate reads that table instead of testing the
mode: spot finding in DiffractionExperiment::IsSpotFindingEnabled, indexing (and with it
prediction and integration, which never run without a lattice) in one gate inside
IndexAndRefine that serves all three front ends, azimuthal integration where the CPU engine
is built. Two rows carry a judgement worth reviewing: powder calibration keeps spot finding,
because --calibration spots fits the pooled spots; grid does not index, because a raster is
thousands of frames and the per-image scoring it ranks on deliberately avoids an indexer that
fires on ice.
There is deliberately no Auto value. GetIndexingAlgorithm() resolves Auto at read time, which
is exactly why an indexing setting cannot be read back off the configuration; removing that
kind of implicitness is the point here, so the mode getter stays a plain accessor. MXStills
is the default because None would silently switch analysis off on every deployment whose
configuration predates the field.
Rotation MX is absent from the OpenAPI schema rather than present and refused: jfjoch_broker
has no rotation analysis path, so the REST and configuration-file routes cannot express it at
all. The shared enum can still carry the value from elsewhere, so CheckAnalysisSettingsOnline
refuses it on both routes with a message naming rugnux. A sweep collected under an MX mode is
not refused - collecting rotation data online is normal and live spot counts are useful - but
it is said out loud in the log, since the mistake worth preventing is the silence about what
was done to it, not the acquisition.
Powder calibration forces azimuthal integration onto the CPU and supplies 32 sectors where
fewer than four were asked for. The FPGA integration core holds 2048 bins in total, so 32
sectors would leave 64 q bins - far too coarse to fit a ring. Frame rate is what this costs
and a calibration exposure does not need it.
The two existing "no analysis" switches, per-dataset dataset_settings.spot_finding and
persistent SpotFindingSettings::enable, are interfaces in too many places to remove now. They
are marked deprecated in the schema and in both headers, and the mode takes precedence over
them: a mode that analyses no spots wins outright, while under a mode that does find spots
they remain the finer control. The precedence is written where it is enforced.
rugnux's ProcessMode is gone, replaced by the shared enum; RugnuxMode stays as the CLI
spelling layer and no existing spelling changes. --mode gains mx_rotation and mx_stills, which
are spellings of -R and --force-still rather than new switches; plain mx still chooses between
them from the goniometer. scale keeps no shared counterpart, since it runs no analysis over
images at all.
The mode reaches the CBOR start message and /entry/MX/analysis_mode in the HDF5 master, so a
written file records which analysis produced it. It is read back as provenance only - what a
stored file was produced by is not what the next run should do.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EFEJG6WBQv8th4UJFNe53N
189 lines
7.7 KiB
C++
189 lines
7.7 KiB
C++
// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <optional>
|
|
|
|
#include "UnitCell.h"
|
|
#include "Coord.h"
|
|
#include "../compression/CompressionAlgorithmEnum.h"
|
|
#include "JFJochMessages.h"
|
|
#include "XrayFluorescenceSpectrum.h"
|
|
|
|
#include "gemmi/symmetry.hpp"
|
|
|
|
class DatasetSettings {
|
|
int64_t images_per_trigger;
|
|
int64_t ntrigger;
|
|
|
|
float beam_x_pxl;
|
|
float beam_y_pxl;
|
|
float detector_distance_mm;
|
|
float photon_energy_keV;
|
|
|
|
std::string file_prefix;
|
|
std::optional<int64_t> images_per_file;
|
|
|
|
CompressionAlgorithm compression;
|
|
|
|
std::string sample_name;
|
|
std::optional<UnitCell> unit_cell;
|
|
// The whole group, not its number: a number can only name the reference setting, so
|
|
// "P 1 1 2" and "R 3:R" are lost the moment a number is all that is kept.
|
|
std::optional<gemmi::SpaceGroup> space_group;
|
|
|
|
std::optional<float> total_flux;
|
|
std::optional<float> attenuator_transmission;
|
|
// Size of the beam where it meets the sample, x then y (NXmx incident_beam_size)
|
|
std::optional<float> beam_size_x_um;
|
|
std::optional<float> beam_size_y_um;
|
|
std::optional<float> ring_current_mA;
|
|
std::optional<GoniometerAxis> goniometer;
|
|
std::optional<GridScanSettings> grid_scan;
|
|
std::optional<float> sample_temperature_K;
|
|
|
|
nlohmann::json image_appendix;
|
|
nlohmann::json header_appendix;
|
|
|
|
float data_reduction_factor_serialmx;
|
|
|
|
std::optional<std::chrono::nanoseconds> image_time;
|
|
std::optional<uint64_t> run_number;
|
|
std::optional<std::string> run_name;
|
|
std::string group;
|
|
std::optional<int64_t> compression_poisson_factor;
|
|
std::optional<int64_t> pixel_value_low_threshold;
|
|
std::optional<int64_t> pixel_value_high_threshold;
|
|
std::optional<bool> save_calibration;
|
|
|
|
bool write_nxmx_hdf5_master;
|
|
|
|
std::optional<float> polarization_factor;
|
|
std::optional<float> bandwidth_fwhm; // relative X-ray bandwidth, FWHM of dlambda/lambda (e.g. 0.01 for 1%)
|
|
float poni_rot_1_rad;
|
|
float poni_rot_2_rad;
|
|
float poni_rot_3_rad;
|
|
|
|
// DEPRECATED - the analysis mode (AnalysisSettings) is the switch now. It still gates spot finding
|
|
// beneath a mode that finds spots; see DiffractionExperiment::IsSpotFindingEnabled for the
|
|
// precedence between the three switches that answer this one question.
|
|
bool spot_finding_enable;
|
|
int64_t max_spot_count;
|
|
|
|
bool detect_ice_rings; // Enable special handling of ice rings
|
|
XrayFluorescenceSpectrum fluorescence_spectrum;
|
|
|
|
std::optional<SmargonPosition> smargon_position;
|
|
public:
|
|
|
|
DatasetSettings();
|
|
|
|
DatasetSettings& ImagesPerTrigger(int64_t input);
|
|
DatasetSettings& NumTriggers(int64_t triggers);
|
|
DatasetSettings& PhotonEnergy_keV(float input);
|
|
DatasetSettings& BeamX_pxl(float input);
|
|
DatasetSettings& BeamY_pxl(float input);
|
|
DatasetSettings& DetectorDistance_mm(float input);
|
|
DatasetSettings& FilePrefix(std::string input);
|
|
DatasetSettings& FilePrefixTrusted(std::string input); // offline/local: no CheckPath guard (absolute allowed)
|
|
DatasetSettings& Compression(CompressionAlgorithm input);
|
|
DatasetSettings& SetUnitCell(const std::optional<UnitCell> &cell);
|
|
DatasetSettings& SpaceGroupNumber(std::optional<int64_t> input);
|
|
DatasetSettings& SetSpaceGroup(const std::optional<gemmi::SpaceGroup> &input);
|
|
DatasetSettings& SampleName(std::string input);
|
|
DatasetSettings& AttenuatorTransmission(const std::optional<float> &input);
|
|
DatasetSettings& TotalFlux(const std::optional<float> &input);
|
|
DatasetSettings& BeamSizeX_um(const std::optional<float> &input);
|
|
DatasetSettings& BeamSizeY_um(const std::optional<float> &input);
|
|
DatasetSettings& Goniometer(const std::optional<GoniometerAxis>& input);
|
|
DatasetSettings& GridScan(const std::optional<GridScanSettings>& input);
|
|
DatasetSettings& HeaderAppendix(const nlohmann::json& input);
|
|
DatasetSettings& ImageAppendix(const nlohmann::json& input);
|
|
DatasetSettings& ImagesPerFile(const std::optional<int64_t> &input);
|
|
DatasetSettings& RunNumber(const std::optional<uint64_t> &run_number);
|
|
DatasetSettings& RunName(const std::optional<std::string> &input);
|
|
DatasetSettings& ExperimentGroup(const std::string &group);
|
|
DatasetSettings& ImageTime(const std::optional<std::chrono::nanoseconds> &input);
|
|
|
|
DatasetSettings& LossyCompressionSerialMX(float input);
|
|
DatasetSettings& LossyCompressionPoisson(std::optional<int64_t> input);
|
|
DatasetSettings& WriteNXmxHDF5Master(bool input);
|
|
DatasetSettings& SaveCalibration(std::optional<bool> input);
|
|
DatasetSettings& PixelValueLowThreshold(const std::optional<int64_t> &input);
|
|
DatasetSettings& PixelValueHighThreshold(const std::optional<int64_t> &input);
|
|
DatasetSettings& PolarizationFactor(const std::optional<float> &input);
|
|
DatasetSettings& BandwidthFWHM(const std::optional<float> &input);
|
|
DatasetSettings& PoniRot1_rad(float input);
|
|
DatasetSettings& PoniRot2_rad(float input);
|
|
DatasetSettings& PoniRot3_rad(float input);
|
|
DatasetSettings& RingCurrent_mA(const std::optional<float> &input);
|
|
DatasetSettings& SampleTemperature_K(const std::optional<float> &input);
|
|
DatasetSettings& SpotFindingEnable(bool input);
|
|
DatasetSettings& MaxSpotCount(int64_t input);
|
|
DatasetSettings& DetectIceRings(bool input);
|
|
DatasetSettings& FluorescenceSpectrum(const XrayFluorescenceSpectrum& input);
|
|
DatasetSettings& Smargon(const std::optional<SmargonPosition>& input);
|
|
|
|
std::optional<float> GetAttenuatorTransmission() const;
|
|
std::optional<float> GetTotalFlux() const;
|
|
std::optional<float> GetBeamSizeX_um() const;
|
|
std::optional<float> GetBeamSizeY_um() const;
|
|
|
|
std::optional<GoniometerAxis> &Goniometer();
|
|
std::optional<GridScanSettings> &GridScan();
|
|
const std::optional<GoniometerAxis> &GetGoniometer() const;
|
|
const std::optional<GridScanSettings> &GetGridScan() const;
|
|
|
|
const nlohmann::json& GetHeaderAppendix() const;
|
|
const nlohmann::json& GetImageAppendix() const;
|
|
std::optional<UnitCell> GetUnitCell() const;
|
|
std::optional<int64_t> GetSpaceGroupNumber() const;
|
|
const std::optional<gemmi::SpaceGroup> &GetSpaceGroup() const;
|
|
std::string GetSampleName() const;
|
|
float GetPhotonEnergy_keV() const;
|
|
float GetBeamX_pxl() const;
|
|
float GetBeamY_pxl() const;
|
|
float GetDetectorDistance_mm() const;
|
|
|
|
Coord GetScatteringVector() const;
|
|
|
|
std::string GetFilePrefix() const;
|
|
|
|
CompressionAlgorithm GetCompressionAlgorithm() const;
|
|
int64_t GetNumTriggers() const;
|
|
int64_t GetImageNumPerTrigger() const;
|
|
std::optional<int64_t> GetImagesPerFile() const;
|
|
|
|
std::optional<uint64_t> GetRunNumber() const;
|
|
std::optional<std::string> GetRunName() const;
|
|
std::string GetExperimentGroup() const;
|
|
std::optional<std::chrono::nanoseconds> GetImageTime() const;
|
|
|
|
float GetLossyCompressionSerialMX() const;
|
|
std::optional<int64_t> GetLossyCompressionPoisson() const;
|
|
|
|
std::optional<int64_t> GetPixelValueLowThreshold() const;
|
|
std::optional<int64_t> GetPixelValueHighThreshold() const;
|
|
bool IsWriteNXmxHDF5Master() const;
|
|
std::optional<bool> IsSaveCalibration() const;
|
|
|
|
std::optional<float> GetPolarizationFactor() const;
|
|
std::optional<float> GetBandwidthFWHM() const;
|
|
float GetPoniRot1_rad() const;
|
|
float GetPoniRot2_rad() const;
|
|
float GetPoniRot3_rad() const;
|
|
|
|
std::optional<float> GetRingCurrent_mA() const;
|
|
std::optional<float> GetSampleTemperature_K() const;
|
|
|
|
bool IsSpotFindingEnabled() const;
|
|
int64_t GetMaxSpotCount() const;
|
|
|
|
bool IsDetectIceRings() const;
|
|
const XrayFluorescenceSpectrum &GetFluorescenceSpectrum() const;
|
|
std::optional<SmargonPosition> GetSmargonPosition() const;
|
|
};
|