Skip to content

Position coord on CorrectedMonitor causes transmission_fraction to fail #244

@SimonHeybrock

Description

@SimonHeybrock

Summary

transmission_fraction in ess.sans.normalization fails with a DatasetError (coordinate mismatch on position) when computing the transmission fraction using monitors from the GenericTofWorkflow path (i.e., via LokiWorkflow).

The root cause: monitor_to_wavelength calls transform_coords('wavelength', graph=graph, keep_inputs=False), but position is not an input to the MonitorCoordTransformGraph. The graph starts from pre-computed Ltotal, so position is a bystander coordinate that survives the conversion. The transmission_fraction provider then multiplies ratios from incident and transmission monitors, which sit at different physical positions, causing scipp to reject the operation.

Reproduction

The error occurs when:

  • Monitor data is loaded from a NeXus geometry file (via assemble_monitor_data), which attaches position from EmptyMonitor
  • The data flows through monitor_to_wavelengthpreprocess_monitor_dataCorrectedMonitor
  • transmission_fraction computes (sample_transmission / direct_transmission) * (direct_incident / sample_incident) — each division preserves the respective monitor's position, and the final multiply fails
scipp._scipp.core.DatasetError: Mismatch in coordinate 'position' in operation 'multiply':
()    vector3              [m]  (0, 0, 0.2)
vs
()    vector3              [m]  (0, 0, -8.4)

Verification

import scipp as sc

# Simulates what monitor_to_wavelength does
da = sc.DataArray(
    sc.ones(dims=['tof'], shape=[10]),
    coords={
        'tof': sc.linspace('tof', 1000, 50000, 10, unit='us'),
        'position': sc.vector([0, 0, -8.4], unit='m'),
        'Ltotal': sc.scalar(25.0, unit='m'),
    }
)

from scippneutron.conversion import graph as conv_graph
graph = {
    **conv_graph.beamline.beamline(scatter=False),
    **conv_graph.tof.elastic_wavelength('tof'),
    'source_position': lambda: sc.vector([0, 0, -60.0], unit='m'),
}

result = da.transform_coords('wavelength', graph=graph, keep_inputs=False)
print(list(result.coords))  # ['position', 'wavelength'] — position survives!

Impact

This affects any workflow using GenericTofWorkflow (e.g., LokiWorkflow) that computes TransmissionFraction with monitors at different physical positions. The LokiAtLarmorWorkflow is unaffected because its custom larmor_monitor_to_tof conversion path doesn't attach position the same way.

Possible fixes

  • Drop position explicitly in monitor_to_wavelength after the transform_coords call.
  • Or include position in the MonitorCoordTransformGraph so it gets consumed (and thus dropped by keep_inputs=False).
  • Or drop non-dimension coords in preprocess_monitor_data / transmission_fraction.

Workaround

We currently work around this in ESSlivedata by stripping non-wavelength coords when mapping CorrectedMonitor values between run types.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    Status

    Selected

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions