Skip to content

[ESSSPECTROSCOPY] Source the BIFROST Bragg peak monitor from the NXmonitor - #698

Draft
SimonHeybrock wants to merge 2 commits into
mainfrom
bragg-peak-monitor-nxmonitor
Draft

[ESSSPECTROSCOPY] Source the BIFROST Bragg peak monitor from the NXmonitor#698
SimonHeybrock wants to merge 2 commits into
mainfrom
bragg-peak-monitor-nxmonitor

Conversation

@SimonHeybrock

@SimonHeybrock SimonHeybrock commented Aug 11, 2026

Copy link
Copy Markdown
Member

The BIFROST Bragg peak monitor workflow could not run against any real file. get_calibrated_bragg_peak_detector required NeXusComponent[NXdetector], but the Bragg peak monitor is the elastic monitor (cbm5), written as an NXmonitor in CODA files, in the geometry artifacts and in the McStas simulation file alike, so loading raised

ValueError: The NeXus group 'elastic_monitor' was expected to be a NXdetector but is a NXmonitor

Only the user guide worked, and only because it stands a detector triplet in for the monitor — it carries an explicit warning saying so.

The monitor's component, transformation and event data now come from ElasticMonitor. The workflow already declares monitor_types=(ElasticMonitor, NormalizationMonitor), so every loading node exists and this is a re-pointing rather than new machinery. A monitor has no pixel offsets, so the position is the transformed origin as in get_calibrated_monitor, and no detector_number, so the single pixel is named explicitly for event assembly. The Analyzer dependency is dropped: get_base_calibrated_detector_bifrost never reads it, and a monitor in the direct beam has no analyzer.

Event assembly no longer goes through assemble_detector_data, which groups by event_id. Streamed monitor events carry none — the file-based path hid this because CODA's cbm5_events does have event_id. It was only found by running the live path.

Along the way this fixes a latent defect that is not specific to the monitor: a time-dependent position makes ltotal carry time, but group_by_rotation has already renamed that dimension to a4, so the broadcast in detector_wavelength_data rejected it. This bites whenever the tank rotates. It stayed hidden because the user guide's stand-in position is static, and because the inelastic/Q-cut path uses a different detector_wavelength_data that takes no ltotal at all.

Testing

18/18 essspectroscopy tests pass and the user guide still computes (its EmptyDetector stand-in moved to simulation_providers).

There is no regression test for the monitor path here: the simulated data contains no Bragg peak monitor with event data — the simulation file's elastic_monitor is histogram-mode. The path is instead exercised end to end downstream in scipp/esslivedata#1236, where streamed monitor events plus the live rotation readbacks produce a populated (Qpar, Qperp) map. That branch does not work against released essspectroscopy, so it is blocked on this PR.

Open questions for reviewers

The scientists asked for the map in the laboratory frame, but project_momentum_transfer projects sample_table_momentum_transfer — the sample-table frame, which is what you would want to compare against an expected reciprocal lattice. Worth settling before merge.

single_crystal/detector.py imports _assign_detector_position from the sibling ..detector; intra-package but private. Say the word if you want it promoted.

SimonHeybrock and others added 2 commits August 11, 2026 09:17
The Bragg peak monitor is BIFROST's elastic monitor (cbm5), written as an
NXmonitor in CODA files, in the pinned geometry artifacts, and in the McStas
simulation file alike. The single-crystal workflow instead required it as
NeXusComponent[NXdetector], so loading raised

    ValueError: The NeXus group 'elastic_monitor' was expected to be a
    NXdetector but is a NXmonitor

against every real file. Only the user guide worked, by standing a detector
triplet in for the monitor.

Take the component, its transformation and its event data from ElasticMonitor,
which the workflow already declares in monitor_types, so all the loading nodes
exist. A monitor has no pixel offsets, so the position is the transformed
origin as in get_calibrated_monitor rather than compute_detector_position, and
no detector_number, so the single pixel is named explicitly for event assembly.
The Analyzer dependency is dropped: get_base_calibrated_detector_bifrost never
reads it, and a monitor in the direct beam has no analyzer.

Ltotal is a straight line for the same reason, so insert that provider rather
than leaving every caller to supply it.

Fix a latent defect this exposes: a time-dependent position makes ltotal depend
on 'time', but group_by_rotation has already renamed that same dimension to
'a4', so the broadcast in detector_wavelength_data rejected it. This bites
whenever the tank rotates; the user guide escaped it only because its stand-in
position is static.

The simulated data contains no Bragg peak monitor, so the detector stand-in
moves to simulation_providers and keeps the user guide working.
Streamed monitor events carry no event_id, so assemble_detector_data cannot
group them. The monitor is a single pixel, so assign its geometry onto the
events as assemble_monitor_data does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the essspectroscopy Issues for essspectroscopy. label Aug 11, 2026
@github-actions github-actions Bot changed the title Source the BIFROST Bragg peak monitor from the NXmonitor [ESSSPECTROSCOPY] Source the BIFROST Bragg peak monitor from the NXmonitor Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

essspectroscopy Issues for essspectroscopy.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant