Skip to content

Allow for LoadEventNexus to properly load only a single bank on MANDI instruments#41215

Merged
SilkeSchomann merged 11 commits intomainfrom
ewm15489-reinstate-delete-banks
Apr 20, 2026
Merged

Allow for LoadEventNexus to properly load only a single bank on MANDI instruments#41215
SilkeSchomann merged 11 commits intomainfrom
ewm15489-reinstate-delete-banks

Conversation

@rboston628
Copy link
Copy Markdown
Contributor

@rboston628 rboston628 commented Apr 10, 2026

The Instrument object defines a detector cache. Though the logic of the cache should follow that of an ordered map, at some point this cache was implemented as vector. This implementation had tradeoffs.

On the positive side, detectors could be more quickly inserted into the cache.

On the negative side, searching for an element becomes $O(N)$. This was partly fixed by keeping the vector sorted and using a binary search algorithm on it, which kept search times at $O(\log N)$.

However, for a vector, the erase method is always $O(N)$. The erase method was called with the Instrument::removeDetector() method.

One particular hot path for this method occurred in LoadEventNexus::deleteBanks(), which called removeDetector() for (almost) every single detector in the instrument.

The error was discovered as the cause of a user-reported error, where calling LoadEventNexus for a single bank for data in the MANDI instrument was apparently freezing. It was in fact taking hours to loop through every pixel, find it, and erase it from the detector cache, taking at least $O(N^2)$ operations.

A previous attempt in PR #41176 tried to replace the cache with an ordered map. This did improve search speeds; however, some of the vector operations occur on hot paths, leading to longer run times in some tests.

Moreover, even when the time-complexity issue was fixed, Instrument::deleteBanks() still cause an unrelated error arising from a mismatch between the instrument's cache size, and the instrument size recorded in the DetectorInfo object. This latter error was proven to exist as far back as Mantid 6.6.

Because of the error, previous work in PR #41183 removed the feature to delete unused banks in MANDI and TOPAZ instruments (the section of code causing the freeze).

This PR has fixed the error in two ways.

  1. A new set of methods removeDetectorIncomplete and removeDetectorFinalize, analogous to the markAsMonitor methods, has been added for quickly deleting an large number of detectors from the cache. This stores up the detectors to delete, then uses remove_if to remove them all at the end, reducing time complexity. LoadEventNexus will no longer appear to freeze. (NOTE: I ended up not using these, see below)
  2. A new instrument is created, cloned from the full instrument, but with only the desired bank and its detectors added into it. This was necessary, to avoid modifying the original instrument in the Instrument data service.
  3. A new method was added to force the ComponentInfo and DetectorInfo objects to update after the instrument was reset. This fixed the issue of size mismatch.

Because both errors were addressed, the delete-banks feature was reinstated.

Refs:

To test:

Build and run the following in workbench:

outws = LoadEventNexus("MANDI_13064", BankName="bank26")

This should run in only a few seconds. There should be no errors produced during LoadEventNexus.

Right-click the workspace to check the instrument. It should be named "MANDI_clone_bank26". It should contain only bank26. There are no other banks in the instrument view, and no other banks in the instrument listing.
image

Re-run the script with "bank27", and with no bank name specified (i.e. full instrument), and ensure behavior is as expected.

Please carefully explore the instrument and the workspace data to make sure that this behaves the way we expect a single-bank-loaded workspace to behave.


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?

@rboston628 rboston628 added this to the Release 6.16 milestone Apr 10, 2026
@rboston628 rboston628 added the Bug Issues and pull requests that are regressions or would be considered a bug by users (e.g. crashing) label Apr 10, 2026
@rboston628 rboston628 changed the title Ewm15489 reinstate delete banks Allow for LoadEventNexus to properly load only a single bank on MANDI and TOPAZ instruments Apr 10, 2026
@rboston628 rboston628 requested a review from Copilot April 10, 2026 20:08
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR restores and accelerates LoadEventNexus “delete unused banks” behavior (notably for MANDI/TOPAZ single-bank loads) by introducing bulk-removal support for the Instrument detector cache and forcing regeneration of ComponentInfo/DetectorInfo after instrument resets to avoid size-mismatch errors.

Changes:

  • Add Instrument::removeDetectorIncomplete/removeDetectorFinalize to bulk-remove many detectors from the cache efficiently.
  • Re-enable/trigger bank deletion in LoadEventNexus via an instrument parameter (remove-unused-banks) and reset the workspace instrument after deletion.
  • Force fresh beamline wrapper creation for empty ParameterMap instrumentation to prevent stale ComponentInfo/DetectorInfo reuse.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
Framework/Geometry/src/Instrument/ParameterMap.cpp Uses a “force new beamline” path when the parameter map is empty to avoid stale wrapper reuse.
Framework/Geometry/src/Instrument/InstrumentVisitor.cpp Minor control-flow braces adjustment.
Framework/Geometry/src/Instrument.cpp Refactors detector-cache access and adds bulk-removal + cache helper methods.
Framework/Geometry/inc/MantidGeometry/Instrument.h Introduces DetectorCacheEntry/DetectorCache types and new bulk-removal APIs.
Framework/DataHandling/src/LoadEventNexus.cpp Reintroduces bank deletion path and switches to bulk detector-cache removal; resets instrument after mutation.
Framework/API/src/ExperimentInfo.cpp Improves mismatch error message ordering and adds braces for clarity.
docs/source/release/v6.16.0/Framework/Algorithms/Bugfixes/41215.rst Adds release note for the restored/fixed single-bank loading behavior.
docs/source/release/v6.16.0/Framework/Algorithms/Bugfixes/41183.rst Removes the prior release note stating the feature was removed.
buildconfig/CMake/CppCheck_Suppressions.txt.in Updates a suppression line number after source changes.
Comments suppressed due to low confidence (1)

Framework/Geometry/src/Instrument.cpp:795

  • The exception message here uses "parameterized" while most other messages in this file use "parametrized". Consider using a consistent spelling across Instrument error messages (and update the other new occurrences in this patch) to keep logs/searchability consistent.
void Instrument::removeDetector(IDetector *det) {
  if (m_map)
    throw std::runtime_error("Instrument::removeDetector() called on a "
                             "parameterized Instrument object.");
  if (!isFinalized())

Comment thread Framework/DataHandling/src/LoadEventNexus.cpp Outdated
Comment thread Framework/Geometry/src/Instrument.cpp
Comment thread Framework/Geometry/src/Instrument.cpp
Comment thread Framework/Geometry/inc/MantidGeometry/Instrument.h
@github-actions github-actions bot added the Has Conflicts Used by the bot to label pull requests that have conflicts label Apr 13, 2026
@github-actions

This comment was marked as outdated.

@rboston628 rboston628 force-pushed the ewm15489-reinstate-delete-banks branch from 5fd6030 to 042024b Compare April 13, 2026 20:42
@github-actions github-actions bot removed the Has Conflicts Used by the bot to label pull requests that have conflicts label Apr 13, 2026
@rboston628 rboston628 marked this pull request as ready for review April 14, 2026 16:49
@rboston628 rboston628 changed the title Allow for LoadEventNexus to properly load only a single bank on MANDI and TOPAZ instruments Allow for LoadEventNexus to properly load only a single bank on MANDI instruments Apr 14, 2026
@rboston628
Copy link
Copy Markdown
Contributor Author

rboston628 commented Apr 14, 2026

Note on TOPAZ

The feature to remove unused banks was originally created for TOPAZ, over 13 years ago. See commit 406d04f.

At a slightly later date, it was guarded to only run on instruments with the "remove-unused-banks" parameter flag, which was then limited to TOPAZ. See commits

The parameter was then deliberately removed, and then added back in set to 0:

So for about 12 years, this feature intended for TOPAZ has not actually been running on TOPAZ. This can be fixed by changing this parameter back.

This has been set on MANDI for 12 years:

It is unclear why no one has noticed the error for MANDI in this time.

@rboston628 rboston628 force-pushed the ewm15489-reinstate-delete-banks branch from ce68e34 to 7ff5936 Compare April 15, 2026 18:49
Comment thread Framework/DataHandling/src/LoadEventNexus.cpp Outdated
Instrument_sptr inst = std::const_pointer_cast<Instrument>(workspace->getInstrument()->baseInstrument());
// work from the base instrument of the current workspace
auto const &ws = workspace->getSingleHeldWorkspace();
auto const &inst = ws->getInstrument()->baseInstrument();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does this affect the singleton in the InstrumentDataService? I think the test is to load the same file into two different workspaces with different single banks selected.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You can run with bank26, bank27, and with no bank given, and all three will have different instruments

TS_ASSERT_EQUALS(instr->getNumberDetectors(/*skip monitors*/ true), bankSize);
TS_ASSERT_EQUALS(instr->getNumberDetectors(/*skip monitors*/ false), bankSize + monitors);

// load the full file to ensure that the original instrument still exists
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It looks like this test is answering the question I had

@SilkeSchomann SilkeSchomann self-assigned this Apr 20, 2026
@SilkeSchomann SilkeSchomann merged commit 330485a into main Apr 20, 2026
11 checks passed
@SilkeSchomann SilkeSchomann deleted the ewm15489-reinstate-delete-banks branch April 20, 2026 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Issues and pull requests that are regressions or would be considered a bug by users (e.g. crashing)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants