Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/essimaging/docs/odin/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ maxdepth: 1
---

odin-data-reduction
odin-orca-image-normalization
odin-make-wavelength-lookup-table
```
179 changes: 179 additions & 0 deletions packages/essimaging/docs/odin/odin-orca-image-normalization.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0",
"metadata": {},
"source": [
"# Odin: Orca image normalization workflow\n",
"\n",
"This notebook shows how to use the workflow to compute normalized images recorded by the Orca detector on the ODIN instrument."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1",
"metadata": {},
"outputs": [],
"source": [
"import ess.odin.data # noqa: F401\n",
"from ess import odin\n",
"from ess.imaging.types import *\n",
"import scipp as sc\n",
"import plopp as pp\n",
"\n",
"%matplotlib widget"
]
},
{
"cell_type": "markdown",
"id": "2",
"metadata": {},
"source": [
"## Workflow setup"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3",
"metadata": {},
"outputs": [],
"source": [
"wf = odin.OdinOrcaWorkflow()\n",
"\n",
"wf[Filename[AllRuns]] = odin.data.odin_lego_images()\n",
"\n",
"wf[NeXusDetectorName] = 'histogram_mode_detectors/orca'\n",
"\n",
"wf[MaskingRules] = {} # No masks to begin with\n",
"wf[UncertaintyBroadcastMode] = UncertaintyBroadcastMode.upper_bound"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4",
"metadata": {},
"outputs": [],
"source": [
"wf.visualize(NormalizedImage, compact=True, graph_attr={\"rankdir\": \"LR\"})"
]
},
{
"cell_type": "markdown",
"id": "5",
"metadata": {},
"source": [
"## Run the workflow\n",
"\n",
"We compute the final normalized image:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6",
"metadata": {},
"outputs": [],
"source": [
"image = wf.compute(NormalizedImage)\n",
"image"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7",
"metadata": {},
"outputs": [],
"source": [
"pp.slicer(image, autoscale=False)"
]
},
{
"cell_type": "markdown",
"id": "8",
"metadata": {},
"source": [
"## Adding masks\n",
"\n",
"If we want to mask some part of the image, we update the masking rules.\n",
"For example, here we mask the upper part of the image:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9",
"metadata": {},
"outputs": [],
"source": [
"wf[MaskingRules] = {'y_pixel_offset': lambda x: x > sc.scalar(0.082, unit='m')}\n",
"\n",
"pp.slicer(wf.compute(NormalizedImage), autoscale=False)"
]
},
{
"cell_type": "markdown",
"id": "10",
"metadata": {},
"source": [
"## Intermediate results\n",
"\n",
"We can also inspect intermediate results, which is useful for debugging:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "11",
"metadata": {},
"outputs": [],
"source": [
"results = wf.compute(\n",
" [\n",
" FluxNormalizedDetector[SampleRun],\n",
" FluxNormalizedDetector[OpenBeamRun],\n",
" BackgroundSubtractedDetector[SampleRun],\n",
" ]\n",
")\n",
"\n",
"fig = pp.tiled(2, 2, hspace=0.3, wspace=0.3)\n",
"fig[0, 0] = results[FluxNormalizedDetector[SampleRun]]['time', 0].plot(\n",
" title='Sample (proton-charge normalized)'\n",
")\n",
"fig[0, 1] = results[FluxNormalizedDetector[OpenBeamRun]]['time', 0].plot(\n",
" title='Open beam (proton-charge normalized)'\n",
")\n",
"fig[1, 0] = results[BackgroundSubtractedDetector[SampleRun]]['time', 0].plot(\n",
" title='Background subtracted sample'\n",
")\n",
"fig[1, 1] = image['time', 0].plot(title='Final image')\n",
"fig"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@
import scipp as sc

from ess.reduce.nexus import GenericNeXusWorkflow
from ess.reduce.nexus.types import NeXusDetectorName, NeXusName

from .. import imaging
from ..imaging.types import (
AllRuns,
CorrectedDetector,
DarkBackgroundRun,
ExposureTime,
FluxNormalizedDetector,
OpenBeamRun,
ProtonCharge,
Expand Down Expand Up @@ -58,21 +57,13 @@ def normalize_by_proton_charge_orca(
orca_providers = (normalize_by_proton_charge_orca,)


def default_parameters() -> dict:
"""Return the default NeXus names and detector name for the ORCA workflow."""
return {
NeXusDetectorName: 'orca_detector',
NeXusName[ExposureTime]: '/entry/instrument/orca_detector/camera_exposure',
}


def OrcaNormalizedImagesWorkflow(**kwargs) -> sl.Pipeline:
"""
Workflow with default parameters for ORCA image normalization.
"""

wf = GenericNeXusWorkflow(
run_types=[SampleRun, OpenBeamRun, DarkBackgroundRun],
run_types=[SampleRun, OpenBeamRun, DarkBackgroundRun, AllRuns],
monitor_types=[],
**kwargs,
)
Expand All @@ -83,6 +74,4 @@ def OrcaNormalizedImagesWorkflow(**kwargs) -> sl.Pipeline:
*orca_providers,
):
wf.insert(provider)
for key, param in default_parameters().items():
wf[key] = param
return wf
12 changes: 11 additions & 1 deletion packages/essimaging/src/ess/imaging/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@

UncertaintyBroadcastMode = _UncertaintyBroadcastMode


SampleRun = NewType("SampleRun", int)
"""Sample run; a run with a sample in the beam."""

Expand All @@ -48,6 +47,9 @@
"""Open beam run; a run with no sample in the beam, and the shutter open, to measure the
beam profile."""

AllRuns = NewType("AllRuns", int)
"""A type to represent all runs: sample, open beam, and dark background runs."""

BeamMonitor1 = NewType("BeamMonitor1", int)
"""Beam monitor number 1"""

Expand Down Expand Up @@ -100,4 +102,12 @@ class ExposureTime(sciline.Scope[RunType, sc.DataArray], sc.DataArray):
"""Exposure time of each frame recorded by the camera detector."""


ImageKey = NewType("ImageKey", sc.DataArray)
"""Key mapping image type to the time axis of the recording:
- 0: sample run
- 1: open beam run
- 2: dark background run
"""


del sc, sciline, NewType
3 changes: 2 additions & 1 deletion packages/essimaging/src/ess/odin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import importlib.metadata

from . import beamline
from .workflows import OdinBraggEdgeWorkflow, OdinWorkflow
from .workflows import OdinBraggEdgeWorkflow, OdinOrcaWorkflow, OdinWorkflow

try:
__version__ = importlib.metadata.version("esstbl")
Expand All @@ -15,6 +15,7 @@

__all__ = [
"OdinBraggEdgeWorkflow",
"OdinOrcaWorkflow",
"OdinWorkflow",
"beamline",
]
11 changes: 11 additions & 0 deletions packages/essimaging/src/ess/odin/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"iron_simulation_sample_large.nxs": "md5:c162b6abeccb51984880d8d5002bae95",
"iron_simulation_sample_small.nxs": "md5:dda6fb30aa88780c5a3d4cef6ea05278",
"ODIN-wavelength-lookup-table-5m-65m.h5": "md5:44eef2a2e826cec688aeb1b985eb9f9e", # noqa: E501
"ymir_lego_odin.hdf": "md5:8e8708891e2574046b6f372e5e3516a5",
},
)

Expand Down Expand Up @@ -69,3 +70,13 @@ def odin_wavelength_lookup_table() -> pathlib.Path:
with ``NumberOfSimulatedNeutrons = 5_000_000``.
"""
return _registry.get_path("ODIN-wavelength-lookup-table-5m-65m.h5")


def odin_lego_images() -> pathlib.Path:
"""
Return the path to the ODIN LEGO HDF5 file, created from the YMIR data.
This file was created using the tools/make-odin-images-from-ymir.ipynb notebook.
A ODIN file (coda_odin_999999_00011093.hdf) was used as a template for the NeXus
structure. The images were extracted from the YMIR LEGO run.
"""
return _registry.get_path("ymir_lego_odin.hdf")
Loading