Skip to content

Commit df170bd

Browse files
authored
Merge pull request #604 from neutrons/EWM7798-DiffractionFocussing-FullBinsOnly
Use `DiffractionFocussing` with 'FullBinsOnly' in `FocusSpectraAlgorithm`
2 parents b8c0266 + 1356dbb commit df170bd

30 files changed

+609
-482
lines changed

src/snapred/backend/dao/RunMetadata.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ class RunMetadata(BaseModel, Mapping):
4949

5050
runTitle: str
5151

52-
startTime: datetime
53-
endTime: datetime
52+
startTime: datetime.datetime
53+
endTime: datetime.datetime
5454

5555
protonCharge: float
5656

src/snapred/backend/dao/ingredients/ReductionGroupProcessingIngredients.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
class ReductionGroupProcessingIngredients(BaseModel):
77
pixelGroup: PixelGroup
8+
preserveEvents: bool
89

910
model_config = ConfigDict(
1011
extra="forbid",

src/snapred/backend/dao/ingredients/ReductionIngredients.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def getDetectorPeaks(self, groupingIndex: int) -> List[GroupPeakList]:
5151
return self.detectorPeaksMany[groupingIndex]
5252

5353
def groupProcessing(self, groupingIndex: int) -> ReductionGroupProcessingIngredients:
54-
return ReductionGroupProcessingIngredients(pixelGroup=self.pixelGroups[groupingIndex])
54+
return ReductionGroupProcessingIngredients(pixelGroup=self.pixelGroups[groupingIndex], preserveEvents=False)
5555

5656
def generateFocussedVanadium(self, groupingIndex: int) -> GenerateFocussedVanadiumIngredients:
5757
return GenerateFocussedVanadiumIngredients(

src/snapred/backend/dao/request/FocusSpectraRequest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class FocusSpectraRequest(BaseModel):
99
runNumber: str
1010
useLiteMode: bool
1111
focusGroup: FocusGroup
12+
preserveEvents: bool
1213

1314
inputWorkspace: str
1415
groupingWorkspace: str

src/snapred/backend/data/GroceryService.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
77

88
import numpy as np
9+
from mantid.api import FileLoaderRegistry
910
from mantid.dataobjects import MaskWorkspace
1011
from pydantic import validate_call
1112

@@ -282,11 +283,7 @@ def _createDiffCalTableFilepath(
282283

283284
@validate_call
284285
def _lookupNormalizationWorkspaceFilename(
285-
self,
286-
normcalRunNumber: str,
287-
useLiteMode: bool,
288-
version: Optional[int],
289-
state: str,
286+
self, normcalRunNumber: str, useLiteMode: bool, version: Optional[int], state: str
290287
) -> str:
291288
# NOTE: The normCal Record currently does NOT store workspace type -> workspace name mappings.
292289
# It is just a list of workspace names. So we need to infer.
@@ -852,7 +849,14 @@ def _fetchNeutronDataSingleUse(self, item: GroceryListItem, missingDataHandler:
852849
self.getCloneOfWorkspace(rawWorkspaceName, workspaceName)
853850
elif bool(filePath) and filePath.exists() and item.liveDataArgs is None:
854851
workspaceName = self._createNeutronWorkspaceName(runNumber, useLiteMode)
855-
data = self.grocer.executeRecipe(str(filePath), workspaceName, loader)
852+
loaderArgs = "{}"
853+
if not bool(loader) and Config["nexus.dataFormat.event"]:
854+
# If the loader hasn't been specified, check if this is original input data in event format.
855+
# In this case, specifying only a single bin allows a much faster load.
856+
loader = FileLoaderRegistry.Instance().chooseLoader(str(filePath)).name()
857+
if loader == "LoadEventNexus":
858+
loaderArgs = '{"NumberOfBins": 1}'
859+
data = self.grocer.executeRecipe(str(filePath), workspaceName, loader, loaderArgs=loaderArgs)
856860
else:
857861
data = missingDataHandler()
858862
workspaceName = data["workspace"]
@@ -1242,11 +1246,12 @@ def fetchNormalizationWorkspace(self, item: GroceryListItem) -> Dict[str, Any]:
12421246
:rtype: Dict[str, Any]
12431247
"""
12441248

1245-
runNumber, useLiteMode, version, state, hidden = (
1249+
runNumber, useLiteMode, version, state, loader, hidden = (
12461250
item.runNumber,
12471251
item.useLiteMode,
12481252
item.normCalVersion,
12491253
item.state,
1254+
item.loader,
12501255
item.hidden,
12511256
)
12521257
normcalRunNumber = self._lookupNormcalRunNumber(runNumber, useLiteMode, version, state)
@@ -1262,14 +1267,14 @@ def fetchNormalizationWorkspace(self, item: GroceryListItem) -> Dict[str, Any]:
12621267
self._clearNormalizationCache()
12631268

12641269
# then load the new one
1265-
filename = self._lookupNormalizationWorkspaceFilename(normcalRunNumber, useLiteMode, version, state)
1270+
filePath = self._lookupNormalizationWorkspaceFilename(normcalRunNumber, useLiteMode, version, state)
1271+
1272+
# Unfortunately, `LoadNexusProcessed` does not support the `NumberOfBins=1` optimization for loading
1273+
# event data. As a work-around: event-format normalization data will be saved with only one bin
1274+
# to speed up reload. (See also: "nexus.dataFormat.event" flag in "application.yml".)
12661275

12671276
# Note: 'LoadNexusProcessed' neither requires nor makes use of an instrument donor.
1268-
data = self.grocer.executeRecipe(
1269-
filename=filename,
1270-
workspace=workspaceName,
1271-
loader="LoadNexusProcessed",
1272-
)
1277+
data = self.grocer.executeRecipe(filename=filePath, workspace=workspaceName, loader=loader)
12731278
self._processNeutronDataCopy(item, workspaceName)
12741279
self.normalizationCache.add(workspaceName)
12751280
return data
@@ -1620,7 +1625,7 @@ def getResidentWorkspaces(self, excludeCache: bool):
16201625
return list(workspaces)
16211626

16221627
def _filterEvents(self, runNumber, workspaceName):
1623-
# NOTE: We always want to generate the instruemnt state, to get the latest SNAPInstPRm parameters.
1628+
# NOTE: We always want to generate the instrument state, to get the latest SNAPInstPRm parameters.
16241629
instrumentState = self.dataService.generateInstrumentState(runNumber)
16251630

16261631
self.mantidSnapper.CropWorkspace(

src/snapred/backend/data/LocalDataService.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
from urllib.parse import urlparse
1616

1717
import h5py
18-
from mantid.api import Run
18+
import numpy as np
19+
from mantid.api import IEventWorkspace, Run
1920
from mantid.dataobjects import MaskWorkspace
2021
from mantid.kernel import ConfigService, PhysicalConstants
2122
from pydantic import validate_call
@@ -676,12 +677,31 @@ def writeNormalizationWorkspaces(self, record: NormalizationRecord):
676677
Record must be set with correct version and workspace names must be finalized.
677678
-- assumes that `writeNormalizationRecord` has already been called, and that the version folder exists
678679
"""
679-
state, _ = self.generateStateId(record.runNumber)
680-
indexer = self.normalizationIndexer(record.useLiteMode, state)
680+
instrumentState = self.generateInstrumentState(record.runNumber)
681+
indexer = self.normalizationIndexer(record.useLiteMode, instrumentState.id.hex)
681682
normalizationDataPath: Path = indexer.versionPath(record.version)
682683
if not normalizationDataPath.exists():
683684
normalizationDataPath.mkdir(parents=True, exist_ok=True)
685+
684686
for workspace in record.workspaceNames:
687+
ws = self.mantidSnapper.mtd[workspace]
688+
if Config["nexus.dataFormat.event"] and isinstance(ws, IEventWorkspace):
689+
# Remove binning from any event workspaces in order to speed-up reload:
690+
# (XMin, XMax, Delta) == (NaN, NaN, <outer bound>) will create a single bin
691+
# [<actual minimum>, <actual maximum>]
692+
TOFOuterBound = instrumentState.particleBounds.tof.maximum * 10.0
693+
numberOfSpectra = ws.getNumberHistograms()
694+
self.mantidSnapper.RebinRagged(
695+
"strip binning info, before saving normalization workspace",
696+
OutputWorkspace=workspace,
697+
InputWorkspace=workspace,
698+
XMin=list((np.nan,) * numberOfSpectra),
699+
XMax=list((np.nan,) * numberOfSpectra),
700+
Delta=list((TOFOuterBound,) * numberOfSpectra),
701+
PreserveEvents=True,
702+
)
703+
self.mantidSnapper.executeQueue()
704+
685705
filename = Path(workspace + ".nxs")
686706
self.writeWorkspace(normalizationDataPath, filename, workspace)
687707

src/snapred/backend/recipe/FetchGroceriesRecipe.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def executeRecipe(
2323
instrumentPropertySource=None,
2424
instrumentSource="",
2525
*,
26-
loaderArgs: str = "",
26+
loaderArgs: str = "{}",
2727
) -> Dict[str, Any]:
2828
"""
2929
Wraps the fetch groceries algorithm

src/snapred/backend/recipe/GenerateFocussedVanadiumRecipe.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from snapred.backend.dao.ingredients import GenerateFocussedVanadiumIngredients as Ingredients
44
from snapred.backend.log.logger import snapredLogger
5-
from snapred.backend.recipe.RebinFocussedGroupDataRecipe import RebinFocussedGroupDataRecipe
65
from snapred.backend.recipe.Recipe import Recipe
76

87
logger = snapredLogger.getLogger(__name__)
@@ -61,20 +60,13 @@ def queueNaturalNormalization(self):
6160
SmoothingParameter=self.smoothingParameter,
6261
)
6362

64-
def _rebinInputWorkspace(self):
65-
"""
66-
Rebins the input workspace to the pixel group.
67-
"""
68-
rebinRecipe = RebinFocussedGroupDataRecipe(self.utensils)
69-
rebinIngredients = RebinFocussedGroupDataRecipe.Ingredients(pixelGroup=self.pixelGroup)
70-
rebinRecipe.cook(rebinIngredients, {"inputWorkspace": self.inputWS})
71-
7263
def queueAlgos(self):
7364
"""
74-
Queues up the procesing algorithms for the recipe.
65+
Queues up the processing algorithms for the recipe.
7566
Requires: unbagged groceries.
7667
"""
77-
self._rebinInputWorkspace()
68+
# Note: rebinning of the input workspace is no longer required here.
69+
# After `ReductionGroupProcessing`, the workspace will already have optimal ragged binning.
7870

7971
if self.artificialNormalizationIngredients is not None:
8072
self.queueArtificialNormalization()

src/snapred/backend/recipe/GroupDiffCalRecipe.py

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,7 @@ def chopIngredients(self, ingredients: Ingredients) -> None:
8181
self.runNumber: str = ingredients.runConfig.runNumber
8282

8383
# from grouping parameters, read the overall min/max d-spacings
84-
self.dMin = ingredients.pixelGroup.dMin()
85-
self.dMax = ingredients.pixelGroup.dMax()
86-
self.dBin = ingredients.pixelGroup.dBin()
84+
self.pixelGroup = ingredients.pixelGroup
8785

8886
self.grouping = ingredients.pixelGroup.focusGroup.name
8987

@@ -325,24 +323,14 @@ def convertAndFocusAndReturn(self, inputWS: str, outputWS: str, note: str, units
325323
Target="dSpacing",
326324
)
327325

328-
# Diffraction-focus the d-spacing data
329-
self.mantidSnapper.DiffractionFocussing(
326+
# Diffraction focus, and rebin the d-spacing data.
327+
self.mantidSnapper.FocusSpectraAlgorithm(
330328
"Diffraction-focus the d-spacing data",
331329
InputWorkspace=tmpWSdsp,
332330
GroupingWorkspace=self.focusWS,
333331
OutputWorkspace=tmpWSdsp,
334-
)
335-
336-
# Ragged rebin the diffraction-focussed data
337-
self.mantidSnapper.RebinRagged(
338-
"Ragged rebin the diffraction-focussed data",
339-
InputWorkspace=tmpWSdsp,
340-
OutputWorkspace=tmpWSdsp,
341-
XMin=self.dMin,
342-
XMax=self.dMax,
343-
Delta=self.dBin,
344-
preserveEvents=keepEvents,
345-
FullBinsOnly=True,
332+
PixelGroup=self.pixelGroup.model_dump_json(),
333+
PreserveEvents=keepEvents,
346334
)
347335

348336
if units == "TOF":

src/snapred/backend/recipe/RebinFocussedGroupDataRecipe.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def unbagGroceries(self, groceries: Dict[str, WorkspaceName]):
5656

5757
def queueAlgos(self):
5858
"""
59-
Queues up the procesing algorithms for the recipe.
59+
Queues up the processing algorithms for the recipe.
6060
Requires: unbagged groceries and chopped ingredients.
6161
"""
6262
self.mantidSnapper.RebinRagged(

0 commit comments

Comments
 (0)