Skip to content

Conversation

@jclarkeSTFC
Copy link
Contributor

@jclarkeSTFC jclarkeSTFC commented Dec 17, 2025

Add and remove peaks in the new Instrument View. If a single detector is selected then one can now select Add Peak and select the peak location in the line plot. If a single peaks workspace is already selected to overlay then the peak will be added to that workspace, otherwise it will be added to a separate Instrument View peaks workspace. When one or more detectors are selected, one can select Delete Single Peak and click in the line plot, then the peak closest to where you clicked will be removed from its peaks workspace. The final option is to delete all peaks on all selected detectors.

Closes #40045 .

To test:

Add peaks, delete peaks (including in the old Instrument View, I suggest having both open), do some masking at the same time. Use the old Instrument View to check that peaks have been added and removed correctly.


Reviewer

Your comments will be used as part of the gatekeeper process. Comment clearly on what you have checked and tested during your review. Provide an audit trail for any changes requested.

As per the review guidelines:

  • Is the code of an acceptable quality? (Code standards/GUI standards)
  • Has a thorough functional test been performed? Do the changes handle unexpected input/situations?
  • Are appropriately scoped unit and/or system tests provided?
  • Do the release notes conform to the guidelines and describe the changes appropriately?
  • Has the relevant (user and developer) documentation been added/updated?
  • If the PR author isn’t in the mantid-developers or mantid-contributors teams, add a review comment rerun ci to authorize/rerun the CI

Gatekeeper

As per the gatekeeping guidelines:

  • Has a thorough first line review been conducted, including functional testing?
  • At a high-level, is the code quality sufficient?
  • Are the base, milestone and labels correct?

If one peaks workspace is selected in the list then add peaks to it,
otherwise add peaks in a special workspace like the old instrument
view.
This will delete the peak nearest to the place where the user clicks in
the line plot.
Every peak on any selected detector will be deleted from the respective
peaks workspace. Exposed removePeaks() to Python so I could remove
multiple peaks at once. This makes it easier because otherwise after
every peak removal I would have to recalculate the peak indices, plus
the interface would be refreshed on the peaks workspace changing.
Calling CreatePeaksWorkspace without StoreInADS=False will result
the add callback on the ADS observer being called before
CreatePeaksWorkspace has exited. This will end up calling a different
algorithm which cannot proceed because CreatePeaksWorkspace is still
running, hence a deadlock. The fix is to add the peaks workspace
manually, then CreatePeaksWorkspace won't be running and other
algorithms will be free to run.
@jclarkeSTFC jclarkeSTFC added this to the Release 6.15 milestone Dec 17, 2025
@jclarkeSTFC jclarkeSTFC added the Epic Used for issues and PRs that are managed under the ISIS Epic Workstream label Dec 17, 2025
@jclarkeSTFC jclarkeSTFC marked this pull request as ready for review December 18, 2025 09:38
@RichardWaiteSTFC RichardWaiteSTFC self-assigned this Jan 5, 2026
Copy link
Contributor

@RichardWaiteSTFC RichardWaiteSTFC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this - I've had a look through but not tested yet sorry!

return ads.retrieveWorkspaces(selected_peaks_workspaces)[0]
if self._instrument_view_peaks_ws_name in ads.getObjectNames():
return ads.retrieveWorkspaces([self._instrument_view_peaks_ws_name])[0]
peaks_ws = CreatePeaksWorkspace(self._workspace, 0, OutputWorkspace=self._instrument_view_peaks_ws_name, StoreInADS=False)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not set StoreInADS=True as you add it to ADS in the next line? If you do need to keep it out of the ADS then I think with StoreInADS=False you don't need to specify OutputWorkspace

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because with StoreInADS=True it will trigger the ADS callbacks before the algorithm has exited, causing a hang.

if peaks_ws.name() not in peaks_grouped_by_ws:
continue
peaks_by_detector = peaks_grouped_by_ws[peaks_ws.name()]
picked_detector_peaks = [p for p in peaks_by_detector if p.detector_id in detector_ids]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The peaks workspace has a DetID column which means this search for matching detector indices could be vectorised? It might not make much difference!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bit of a big refactor to do that, can be tested out another time I think


def _update_peak_buttons(self) -> None:
self._view.set_add_peak_button_enabled(
len(self._model.picked_detector_ids) == 1 and self._peak_interaction_status != PeakInteractionStatus.Adding
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused why we need the check for self._peak_interaction_status != PeakInteractionStatus.Adding - surely if this is set then the add button should be/is enabled? Is this because you expect it to be in the state PeakInteractionStatus.Disabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you click the add button then the button is disabled until you've selected the peak in the line plot. When going into this method it could be either adding or deleting peaks.

def closeEvent(self, QCloseEvent: QEvent) -> None:
"""When closing, make sure to close the plotters and figure correctly to prevent errors"""
super().closeEvent(QCloseEvent)
with suppress(TypeError):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it fixes an unrelated bug - does it require a separate release note?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never actually ran into this bug, this is just bombproofing.

These are updated by the calibration, and will give different numbers
to the values from the detector info.

Co-authored-by: RichardWaiteSTFC
Co-authored-by: RichardWaiteSTFC
Co-authored-by: <RichardWaiteSTFC>
Copy link
Contributor

@RichardWaiteSTFC RichardWaiteSTFC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, small bug with unit conversion still!

Also a usability thing - you often need to zoom into a peak on 1D plot before adding. Here you have to remember to do that before clicking Add.
If you do zoom into the 1d plot but leave the zoom button clicked and add a peak the red line doesn't show up in the plot - is that easy to fix?

if source_unit == target_unit:
return value
diffraction_constants = self._spectrum_info.diffractometerConstants(int(spectrum_no))
return UnitConversion.run(source_unit, target_unit, value, self._l1, DeltaEModeType.Elastic, diffraction_constants)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per previous comment (albeit edited yesterday - sorry for not drawing your attention to it earlier) I think for conversion TOF <-> wavelength you still need L2 as well!

I now see this error when x unit of 1D plot set to wavelength and workspace has units of TOF.

RuntimeError: An l2 value must be supplied in the extra parameters when initialising Wavelength for conversion via TOF

FYI For conversion between wavelength <-> d-spacing you'd think it needs the two-theta, but it looks like it goes via TOF because I get the same error above with a workspace in wavelength and 1D plot in d-spacing and vice versa. So L2 alone should suffice!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for spotting this, fixed now hopefully.

self._workspace_x_unit = x_unit.unitID()
self._workspace_x_unit_display = f"{str(x_unit.caption())} ({str(x_unit.symbol())})"
self._selected_peaks_workspaces = []
self._instrument_view_peaks_ws_name = f"instrument_view_peaks_{self._workspace.name()}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason to preserve backward compatibility by naming the workspace SingleCrystalPeakTable?
I do like however the table doesn't overwrite a table produced on another workspace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know of a specific reason

Trying to zoom or do anything else is not possible because the
interface is waiting for the peak to be picked.
If the user is in the middle of adding a peak, then they select a
detector, then stop the adding peak process. You can only add a peak
if exactly one detector is selected, otherwise we don't know which
detector the peak is on.
@jclarkeSTFC
Copy link
Contributor Author

Sorry, small bug with unit conversion still!

Also a usability thing - you often need to zoom into a peak on 1D plot before adding. Here you have to remember to do that before clicking Add. If you do zoom into the 1d plot but leave the zoom button clicked and add a peak the red line doesn't show up in the plot - is that easy to fix?

It looks to me like the red line doesn't show up, but it does add the peak in the correct place. I've changed it slightly so that if you click 'Add Peak' then it disables the toolbar until the peak is added, otherwise could be some odd behaviour if the zoom tool is selected with the red line enabled as well. For getting the red line to appear while zoomed in, wasn't immediately obvious how to fix that, perhaps a separate PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Epic Used for issues and PRs that are managed under the ISIS Epic Workstream

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add/remove peaks in new Instrument View

2 participants