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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user