api: record the beam size at the sample, and write it where NXmx puts it
dataset_settings gains beam_size_x_um and beam_size_y_um, the horizontal and vertical size of the X-ray beam where it meets the sample. They follow the same route total_flux takes - OpenAPI, DatasetSettings, the CBOR start message, the HDF5 master, and back out of a stored file - and nothing consumes them; this is metadata a beamline can state and a downstream program can read. NXmx puts this in the application definition rather than the base class: not NXbeam's extent (rank 2, nP x 2, per scan point, always FWHM of a rectangular aperture) but NXmx's own incident_beam_size, a recommended rank-1 two-element array in the order x, y. Both are live and neither is deprecated, so the choice matters; the MX definition wins in an MX file. Written as one array with a units attribute of "m", like every other length in the master, so the settings hold micrometres and FillMessage converts once. The unit table of ReadLength_m becomes LengthUnitFactor so the array read can share it: a master written elsewhere may state this in millimetres. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EFEJG6WBQv8th4UJFNe53N
This commit is contained in:
@@ -663,6 +663,10 @@ DatasetSettings Convert(const org::openapitools::server::model::Dataset_settings
|
||||
|
||||
if (input.totalFluxIsSet())
|
||||
ret.TotalFlux(input.getTotalFlux());
|
||||
if (input.beamSizeXUmIsSet())
|
||||
ret.BeamSizeX_um(input.getBeamSizeXUm());
|
||||
if (input.beamSizeYUmIsSet())
|
||||
ret.BeamSizeY_um(input.getBeamSizeYUm());
|
||||
if (input.transmissionIsSet())
|
||||
ret.AttenuatorTransmission(input.getTransmission());
|
||||
// Not alternatives: a grid scan is often collected at a given head position, so an axis and a
|
||||
|
||||
@@ -45,6 +45,10 @@ Dataset_settings::Dataset_settings()
|
||||
m_Total_fluxIsSet = false;
|
||||
m_Transmission = 0.0f;
|
||||
m_TransmissionIsSet = false;
|
||||
m_Beam_size_x_um = 0.0f;
|
||||
m_Beam_size_x_umIsSet = false;
|
||||
m_Beam_size_y_um = 0.0f;
|
||||
m_Beam_size_y_umIsSet = false;
|
||||
m_GoniometerIsSet = false;
|
||||
m_Grid_scanIsSet = false;
|
||||
m_Header_appendixIsSet = false;
|
||||
@@ -237,6 +241,34 @@ bool Dataset_settings::validate(std::stringstream& msg, const std::string& pathP
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (beamSizeXUmIsSet())
|
||||
{
|
||||
const float& value = m_Beam_size_x_um;
|
||||
const std::string currentValuePath = _pathPrefix + ".beamSizeXUm";
|
||||
|
||||
|
||||
if (value < static_cast<float>(0.0))
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be greater than or equal to 0.0;";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (beamSizeYUmIsSet())
|
||||
{
|
||||
const float& value = m_Beam_size_y_um;
|
||||
const std::string currentValuePath = _pathPrefix + ".beamSizeYUm";
|
||||
|
||||
|
||||
if (value < static_cast<float>(0.0))
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be greater than or equal to 0.0;";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (dataReductionFactorSerialmxIsSet())
|
||||
{
|
||||
@@ -478,6 +510,12 @@ bool Dataset_settings::operator==(const Dataset_settings& rhs) const
|
||||
((!transmissionIsSet() && !rhs.transmissionIsSet()) || (transmissionIsSet() && rhs.transmissionIsSet() && getTransmission() == rhs.getTransmission())) &&
|
||||
|
||||
|
||||
((!beamSizeXUmIsSet() && !rhs.beamSizeXUmIsSet()) || (beamSizeXUmIsSet() && rhs.beamSizeXUmIsSet() && getBeamSizeXUm() == rhs.getBeamSizeXUm())) &&
|
||||
|
||||
|
||||
((!beamSizeYUmIsSet() && !rhs.beamSizeYUmIsSet()) || (beamSizeYUmIsSet() && rhs.beamSizeYUmIsSet() && getBeamSizeYUm() == rhs.getBeamSizeYUm())) &&
|
||||
|
||||
|
||||
((!goniometerIsSet() && !rhs.goniometerIsSet()) || (goniometerIsSet() && rhs.goniometerIsSet() && getGoniometer() == rhs.getGoniometer())) &&
|
||||
|
||||
|
||||
@@ -587,6 +625,10 @@ void to_json(nlohmann::json& j, const Dataset_settings& o)
|
||||
j["total_flux"] = o.m_Total_flux;
|
||||
if(o.transmissionIsSet())
|
||||
j["transmission"] = o.m_Transmission;
|
||||
if(o.beamSizeXUmIsSet())
|
||||
j["beam_size_x_um"] = o.m_Beam_size_x_um;
|
||||
if(o.beamSizeYUmIsSet())
|
||||
j["beam_size_y_um"] = o.m_Beam_size_y_um;
|
||||
if(o.goniometerIsSet())
|
||||
j["goniometer"] = o.m_Goniometer;
|
||||
if(o.gridScanIsSet())
|
||||
@@ -696,6 +738,16 @@ void from_json(const nlohmann::json& j, Dataset_settings& o)
|
||||
j.at("transmission").get_to(o.m_Transmission);
|
||||
o.m_TransmissionIsSet = true;
|
||||
}
|
||||
if(j.find("beam_size_x_um") != j.end())
|
||||
{
|
||||
j.at("beam_size_x_um").get_to(o.m_Beam_size_x_um);
|
||||
o.m_Beam_size_x_umIsSet = true;
|
||||
}
|
||||
if(j.find("beam_size_y_um") != j.end())
|
||||
{
|
||||
j.at("beam_size_y_um").get_to(o.m_Beam_size_y_um);
|
||||
o.m_Beam_size_y_umIsSet = true;
|
||||
}
|
||||
if(j.find("goniometer") != j.end())
|
||||
{
|
||||
j.at("goniometer").get_to(o.m_Goniometer);
|
||||
@@ -1026,6 +1078,40 @@ void Dataset_settings::unsetTransmission()
|
||||
{
|
||||
m_TransmissionIsSet = false;
|
||||
}
|
||||
float Dataset_settings::getBeamSizeXUm() const
|
||||
{
|
||||
return m_Beam_size_x_um;
|
||||
}
|
||||
void Dataset_settings::setBeamSizeXUm(float const value)
|
||||
{
|
||||
m_Beam_size_x_um = value;
|
||||
m_Beam_size_x_umIsSet = true;
|
||||
}
|
||||
bool Dataset_settings::beamSizeXUmIsSet() const
|
||||
{
|
||||
return m_Beam_size_x_umIsSet;
|
||||
}
|
||||
void Dataset_settings::unsetBeam_size_x_um()
|
||||
{
|
||||
m_Beam_size_x_umIsSet = false;
|
||||
}
|
||||
float Dataset_settings::getBeamSizeYUm() const
|
||||
{
|
||||
return m_Beam_size_y_um;
|
||||
}
|
||||
void Dataset_settings::setBeamSizeYUm(float const value)
|
||||
{
|
||||
m_Beam_size_y_um = value;
|
||||
m_Beam_size_y_umIsSet = true;
|
||||
}
|
||||
bool Dataset_settings::beamSizeYUmIsSet() const
|
||||
{
|
||||
return m_Beam_size_y_umIsSet;
|
||||
}
|
||||
void Dataset_settings::unsetBeam_size_y_um()
|
||||
{
|
||||
m_Beam_size_y_umIsSet = false;
|
||||
}
|
||||
org::openapitools::server::model::Rotation_axis Dataset_settings::getGoniometer() const
|
||||
{
|
||||
return m_Goniometer;
|
||||
|
||||
@@ -155,6 +155,20 @@ public:
|
||||
bool transmissionIsSet() const;
|
||||
void unsetTransmission();
|
||||
/// <summary>
|
||||
/// First element of /entry/instrument/beam/incident_beam_size in NXmx Horizontal size of the X-ray beam where it meets the sample - the FWHM of a focused beam, the full width of a slit-defined one. [um]
|
||||
/// </summary>
|
||||
float getBeamSizeXUm() const;
|
||||
void setBeamSizeXUm(float const value);
|
||||
bool beamSizeXUmIsSet() const;
|
||||
void unsetBeam_size_x_um();
|
||||
/// <summary>
|
||||
/// Second element of /entry/instrument/beam/incident_beam_size in NXmx Vertical size of the X-ray beam where it meets the sample. [um]
|
||||
/// </summary>
|
||||
float getBeamSizeYUm() const;
|
||||
void setBeamSizeYUm(float const value);
|
||||
bool beamSizeYUmIsSet() const;
|
||||
void unsetBeam_size_y_um();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
org::openapitools::server::model::Rotation_axis getGoniometer() const;
|
||||
@@ -361,6 +375,10 @@ protected:
|
||||
bool m_Total_fluxIsSet;
|
||||
float m_Transmission;
|
||||
bool m_TransmissionIsSet;
|
||||
float m_Beam_size_x_um;
|
||||
bool m_Beam_size_x_umIsSet;
|
||||
float m_Beam_size_y_um;
|
||||
bool m_Beam_size_y_umIsSet;
|
||||
org::openapitools::server::model::Rotation_axis m_Goniometer;
|
||||
bool m_GoniometerIsSet;
|
||||
org::openapitools::server::model::Grid_scan m_Grid_scan;
|
||||
|
||||
@@ -476,6 +476,21 @@ components:
|
||||
description: |
|
||||
/entry/instrument/attenuator/attenuator_transmission
|
||||
Transmission of attenuator (filter) [no units]
|
||||
beam_size_x_um:
|
||||
type: number
|
||||
format: float
|
||||
minimum: 0.0
|
||||
description: |
|
||||
First element of /entry/instrument/beam/incident_beam_size in NXmx
|
||||
Horizontal size of the X-ray beam where it meets the sample - the FWHM of a focused beam,
|
||||
the full width of a slit-defined one. [um]
|
||||
beam_size_y_um:
|
||||
type: number
|
||||
format: float
|
||||
minimum: 0.0
|
||||
description: |
|
||||
Second element of /entry/instrument/beam/incident_beam_size in NXmx
|
||||
Vertical size of the X-ray beam where it meets the sample. [um]
|
||||
goniometer:
|
||||
$ref: "#/components/schemas/rotation_axis"
|
||||
grid_scan:
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -170,6 +170,24 @@ DatasetSettings &DatasetSettings::TotalFlux(const std::optional<float> &input) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
DatasetSettings &DatasetSettings::BeamSizeX_um(const std::optional<float> &input) {
|
||||
if (input) {
|
||||
check_finite("Beam size X", input.value());
|
||||
check_min("Beam size X", input.value(), 0.0);
|
||||
}
|
||||
beam_size_x_um = input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DatasetSettings &DatasetSettings::BeamSizeY_um(const std::optional<float> &input) {
|
||||
if (input) {
|
||||
check_finite("Beam size Y", input.value());
|
||||
check_min("Beam size Y", input.value(), 0.0);
|
||||
}
|
||||
beam_size_y_um = input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DatasetSettings &DatasetSettings::Goniometer(const std::optional<GoniometerAxis> &input) {
|
||||
goniometer = input;
|
||||
return *this;
|
||||
@@ -193,6 +211,14 @@ std::optional<float> DatasetSettings::GetTotalFlux() const {
|
||||
return total_flux;
|
||||
}
|
||||
|
||||
std::optional<float> DatasetSettings::GetBeamSizeX_um() const {
|
||||
return beam_size_x_um;
|
||||
}
|
||||
|
||||
std::optional<float> DatasetSettings::GetBeamSizeY_um() const {
|
||||
return beam_size_y_um;
|
||||
}
|
||||
|
||||
const std::optional<GoniometerAxis> &DatasetSettings::GetGoniometer() const {
|
||||
return goniometer;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ class DatasetSettings {
|
||||
|
||||
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;
|
||||
@@ -90,6 +93,8 @@ public:
|
||||
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);
|
||||
@@ -121,6 +126,8 @@ public:
|
||||
|
||||
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();
|
||||
|
||||
@@ -665,6 +665,12 @@ void DiffractionExperiment::FillMessage(StartMessage &message) const {
|
||||
if (const auto bw = GetBandwidthFWHM())
|
||||
message.incident_wavelength_spread = bw.value() * GetWavelength_A();
|
||||
message.incident_energy = GetIncidentEnergy_keV() * 1e3f;
|
||||
// NXmx incident_beam_size is a length like every other in this message, so micrometres in
|
||||
// the settings become metres here.
|
||||
if (const auto beam_size_x = GetBeamSizeX_um())
|
||||
message.beam_size_x = beam_size_x.value() * 1e-6f;
|
||||
if (const auto beam_size_y = GetBeamSizeY_um())
|
||||
message.beam_size_y = beam_size_y.value() * 1e-6f;
|
||||
message.image_size_x = GetXPixelsNum();
|
||||
message.image_size_y = GetYPixelsNum();
|
||||
message.mirror_y = IsDetectorMirroredY();
|
||||
@@ -920,6 +926,16 @@ DiffractionExperiment &DiffractionExperiment::TotalFlux(const std::optional<floa
|
||||
return *this;
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::BeamSizeX_um(const std::optional<float> &input) {
|
||||
dataset.BeamSizeX_um(input);
|
||||
return *this;
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::BeamSizeY_um(const std::optional<float> &input) {
|
||||
dataset.BeamSizeY_um(input);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::optional<float> DiffractionExperiment::GetAttenuatorTransmission() const {
|
||||
return dataset.GetAttenuatorTransmission();
|
||||
}
|
||||
@@ -928,6 +944,14 @@ std::optional<float> DiffractionExperiment::GetTotalFlux() const {
|
||||
return dataset.GetTotalFlux();
|
||||
}
|
||||
|
||||
std::optional<float> DiffractionExperiment::GetBeamSizeX_um() const {
|
||||
return dataset.GetBeamSizeX_um();
|
||||
}
|
||||
|
||||
std::optional<float> DiffractionExperiment::GetBeamSizeY_um() const {
|
||||
return dataset.GetBeamSizeY_um();
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::Goniometer(const std::optional<GoniometerAxis> &input) {
|
||||
dataset.Goniometer(input);
|
||||
return *this;
|
||||
|
||||
@@ -144,6 +144,8 @@ public:
|
||||
DiffractionExperiment& SampleName(const std::string &input);
|
||||
DiffractionExperiment& AttenuatorTransmission(const std::optional<float> &input);
|
||||
DiffractionExperiment& TotalFlux(const std::optional<float> &input);
|
||||
DiffractionExperiment& BeamSizeX_um(const std::optional<float> &input);
|
||||
DiffractionExperiment& BeamSizeY_um(const std::optional<float> &input);
|
||||
DiffractionExperiment& Goniometer(const std::optional<GoniometerAxis> &input);
|
||||
DiffractionExperiment& Smargon(const std::optional<SmargonPosition> &input);
|
||||
DiffractionExperiment& HeaderAppendix(const nlohmann::json& input);
|
||||
@@ -334,6 +336,8 @@ public:
|
||||
|
||||
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> GetGoniometer() const;
|
||||
std::optional<GridScanSettings> GetGridScan() const;
|
||||
|
||||
|
||||
@@ -244,6 +244,9 @@ struct StartMessage {
|
||||
float incident_energy;
|
||||
float incident_wavelength;
|
||||
std::optional<float> incident_wavelength_spread; // NXmx incident_wavelength_spread: FWHM of dlambda (Angstrom)
|
||||
// NXmx incident_beam_size: size of the beam where it meets the sample, x then y (m)
|
||||
std::optional<float> beam_size_x;
|
||||
std::optional<float> beam_size_y;
|
||||
|
||||
float frame_time;
|
||||
float count_time;
|
||||
|
||||
@@ -33,6 +33,8 @@ There are minor differences at the moment:
|
||||
| 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 | |
|
||||
| beam_size_x | float (optional) | Horizontal size of the X-ray beam at the sample \[m\] (first element of NXmx incident_beam_size) | |
|
||||
| beam_size_y | float (optional) | Vertical size of the X-ray beam at the sample \[m\] (second element of NXmx incident_beam_size) | |
|
||||
| frame_time | float | Frame time, if multiple frames per trigger \[s\] | X |
|
||||
| count_time | float | Exposure time \[s\] | X |
|
||||
| saturation_value | int64 | Maximum valid sample value | X |
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
* `jfjoch_writer` writes `direct_beam_x`/`direct_beam_y` in the HDF5 master - where the undeflected beam lands - beside the `beam_center_x`/`beam_center_y` PONI.
|
||||
* `jfjoch_writer` writes `/entry/MX/peakCountUnfiltered` in the HDF5 master beside the other per-image spot counts, instead of only in the data files.
|
||||
* `jfjoch_broker` sends `direct_beam_x`/`direct_beam_y` on the CBOR start message.
|
||||
* `dataset_settings` takes `beam_size_x_um`/`beam_size_y_um`, the size of the X-ray beam at the sample, and `jfjoch_writer` writes them as `incident_beam_size` in the HDF5 master.
|
||||
* `jfjoch_viewer` reads PILATUS miniCBF sweeps natively, and draws grid scan cells in the proportion of the scan steps.
|
||||
* The rugnux manual is reorganised into task pages with a run overview, and gains worked phenix / REFMAC5 / POINTLESS-AIMLESS / careless examples.
|
||||
* `jfjoch_viewer` labels the merge-statistics plot over the range the axis is drawn on, so the CC1/2 curve is no longer read against tick labels covering only part of it.
|
||||
|
||||
@@ -154,6 +154,7 @@ File-level HDF5 attributes `file_name`, `file_time`, `HDF5_Version` are also set
|
||||
| `incident_wavelength` | NXmx | angstrom |
|
||||
| `incident_wavelength_spread` | NXmx | angstrom (only if polychromatic) |
|
||||
| `total_flux` | NXmx | Hz |
|
||||
| `incident_beam_size` | NXmx | m (two elements, x then y; written only when both beam sizes are given) |
|
||||
|
||||
### `/entry/instrument/attenuator` (NXattenuator)
|
||||
|
||||
|
||||
@@ -1276,6 +1276,10 @@ namespace {
|
||||
message.incident_wavelength = GetCBORFloat(value);
|
||||
else if (key == "incident_wavelength_spread")
|
||||
message.incident_wavelength_spread = GetCBORFloat(value);
|
||||
else if (key == "beam_size_x")
|
||||
message.beam_size_x = GetCBORFloat(value);
|
||||
else if (key == "beam_size_y")
|
||||
message.beam_size_y = GetCBORFloat(value);
|
||||
else if (key == "frame_time")
|
||||
message.frame_time = GetCBORFloat(value);
|
||||
else if (key == "count_time")
|
||||
|
||||
@@ -702,6 +702,8 @@ void CBORStream2Serializer::SerializeSequenceStart(const StartMessage& message)
|
||||
CBOR_ENC(mapEncoder, "incident_energy", message.incident_energy);
|
||||
CBOR_ENC(mapEncoder, "incident_wavelength", message.incident_wavelength);
|
||||
CBOR_ENC(mapEncoder, "incident_wavelength_spread", message.incident_wavelength_spread);
|
||||
CBOR_ENC(mapEncoder, "beam_size_x", message.beam_size_x);
|
||||
CBOR_ENC(mapEncoder, "beam_size_y", message.beam_size_y);
|
||||
|
||||
CBOR_ENC(mapEncoder, "frame_time", message.frame_time);
|
||||
CBOR_ENC(mapEncoder, "count_time", message.count_time);
|
||||
|
||||
@@ -228,6 +228,19 @@ export type dataset_settings = {
|
||||
*
|
||||
*/
|
||||
transmission?: number;
|
||||
/**
|
||||
* First element of /entry/instrument/beam/incident_beam_size in NXmx
|
||||
* Horizontal size of the X-ray beam where it meets the sample - the FWHM of a focused beam,
|
||||
* the full width of a slit-defined one. [um]
|
||||
*
|
||||
*/
|
||||
beam_size_x_um?: number;
|
||||
/**
|
||||
* Second element of /entry/instrument/beam/incident_beam_size in NXmx
|
||||
* Vertical size of the X-ray beam where it meets the sample. [um]
|
||||
*
|
||||
*/
|
||||
beam_size_y_um?: number;
|
||||
goniometer?: rotation_axis;
|
||||
grid_scan?: grid_scan;
|
||||
/**
|
||||
|
||||
@@ -125,6 +125,8 @@ export const zDatasetSettings = z.object({
|
||||
]).optional().default('bslz4'),
|
||||
total_flux: z.number().optional(),
|
||||
transmission: z.number().gte(0).lte(1).optional(),
|
||||
beam_size_x_um: z.number().gte(0).optional(),
|
||||
beam_size_y_um: z.number().gte(0).optional(),
|
||||
goniometer: zRotationAxis.optional(),
|
||||
grid_scan: zGridScan.optional(),
|
||||
header_appendix: z.unknown().optional(),
|
||||
|
||||
@@ -197,23 +197,25 @@ std::string ResolveRelativeToMaster(const std::string &directory,
|
||||
// silent factor of a thousand, so the unit is read rather than assumed. An undeclared unit means
|
||||
// metres - what every DECTRIS master and everything this system writes means by one. An unknown
|
||||
// unit is refused rather than guessed at, for the same reason.
|
||||
float ReadLength_m(HDF5Object &file, const std::string &name) {
|
||||
HDF5DataSet dataset(file, name);
|
||||
const float value = dataset.ReadScalar<float>();
|
||||
|
||||
float LengthUnitFactor(HDF5DataSet &dataset, const std::string &name) {
|
||||
if (!dataset.AttrExists("units"))
|
||||
return value;
|
||||
return 1.0f;
|
||||
|
||||
const std::string units = dataset.ReadAttrStr("units");
|
||||
if (units == "m")
|
||||
return value;
|
||||
return 1.0f;
|
||||
if (units == "mm")
|
||||
return value * 1e-3f;
|
||||
return 1e-3f;
|
||||
if (units == "um")
|
||||
return value * 1e-6f;
|
||||
return 1e-6f;
|
||||
throw JFJochException(JFJochExceptionCategory::HDF5, name + ": unknown length unit " + units);
|
||||
}
|
||||
|
||||
float ReadLength_m(HDF5Object &file, const std::string &name) {
|
||||
HDF5DataSet dataset(file, name);
|
||||
return dataset.ReadScalar<float>() * LengthUnitFactor(dataset, name);
|
||||
}
|
||||
|
||||
// The same value under different names. DECTRIS Eiger firmware 1.x writes detector_distance where
|
||||
// NXmx says distance, and a file from that era is still what a repository hands you; a Diamond
|
||||
// master puts the distance one level up, in NXinstrument rather than in NXdetector. The NXmx
|
||||
@@ -926,6 +928,19 @@ HDF5MetadataSource::OpenResult HDF5MetadataSource::Open(const std::string &filen
|
||||
total_flux.reset(); // negative value is an "unknown flux" sentinel; treat as absent
|
||||
dataset->experiment.TotalFlux(total_flux);
|
||||
|
||||
// NXmx incident_beam_size is one two-element array in the order x, y; the settings hold
|
||||
// it as two lengths in micrometres.
|
||||
if (master_file->Exists("/entry/instrument/beam/incident_beam_size")) {
|
||||
HDF5DataSet beam_size(*master_file, "/entry/instrument/beam/incident_beam_size");
|
||||
std::vector<float> size;
|
||||
beam_size.ReadVector(size);
|
||||
if (size.size() == 2) {
|
||||
const float to_um = LengthUnitFactor(beam_size, "incident_beam_size") * 1e6f;
|
||||
dataset->experiment.BeamSizeX_um(size[0] * to_um);
|
||||
dataset->experiment.BeamSizeY_um(size[1] * to_um);
|
||||
}
|
||||
}
|
||||
|
||||
if (master_file->Exists("/entry/azint") && master_file->Exists("/entry/azint/bin_to_q")) {
|
||||
HDF5DataSet bin_to_q_dataset(*master_file, "/entry/azint/bin_to_q");
|
||||
HDF5DataSpace bin_to_q_dataspace(bin_to_q_dataset);
|
||||
|
||||
@@ -28,6 +28,8 @@ TEST_CASE("CBORSerialize_Start", "[CBOR]") {
|
||||
.countrate_correction_enabled = true,
|
||||
.incident_energy = 12400,
|
||||
.incident_wavelength = 0.988,
|
||||
.beam_size_x = 8e-5,
|
||||
.beam_size_y = 3e-5,
|
||||
.frame_time = 0.0001,
|
||||
.count_time = 0.000098,
|
||||
.saturation_value = 65534,
|
||||
@@ -145,6 +147,10 @@ TEST_CASE("CBORSerialize_Start", "[CBOR]") {
|
||||
CHECK(output_message.unit_cell->beta == message.unit_cell->beta);
|
||||
CHECK(output_message.unit_cell->gamma == message.unit_cell->gamma);
|
||||
|
||||
REQUIRE(output_message.beam_size_x);
|
||||
CHECK(output_message.beam_size_x.value() == message.beam_size_x.value());
|
||||
REQUIRE(output_message.beam_size_y);
|
||||
CHECK(output_message.beam_size_y.value() == message.beam_size_y.value());
|
||||
REQUIRE(output_message.total_flux);
|
||||
CHECK(output_message.total_flux.value() == message.total_flux.value());
|
||||
REQUIRE(output_message.attenuator_transmission);
|
||||
|
||||
@@ -627,6 +627,11 @@ void NXmx::Beam(const StartMessage &start) {
|
||||
SaveScalar(group, "incident_wavelength_spread", start.incident_wavelength_spread.value())->Units("angstrom");
|
||||
if (start.total_flux)
|
||||
SaveScalar(group, "total_flux", start.total_flux.value())->Units("Hz");
|
||||
// NXmx asks for the beam size as one two-element array in the order x, y, so it is written
|
||||
// only when both have been given.
|
||||
if (start.beam_size_x && start.beam_size_y)
|
||||
group.SaveVector("incident_beam_size",
|
||||
std::vector<float>{start.beam_size_x.value(), start.beam_size_y.value()})->Units("m");
|
||||
}
|
||||
|
||||
void NXmx::Fluorescence(const StartMessage &start) {
|
||||
|
||||
Reference in New Issue
Block a user