diff --git a/docs/ess/dream.ipynb b/docs/ess/dream.ipynb index 07d6a6b..7e1a9fb 100644 --- a/docs/ess/dream.ipynb +++ b/docs/ess/dream.ipynb @@ -20,12 +20,7 @@ "source": [ "import scipp as sc\n", "import plopp as pp\n", - "import tof\n", - "\n", - "Hz = sc.Unit('Hz')\n", - "deg = sc.Unit('deg')\n", - "meter = sc.Unit('m')\n", - "AA = sc.Unit('angstrom')" + "import tof" ] }, { @@ -56,144 +51,35 @@ "source": [ "## Component set-up\n", "\n", - "## Choppers\n", - "\n", "The DREAM chopper cascade consists of:\n", "\n", "- Two counter-rotating pulse-shaping choppers (PSC) that are very close to each other, located ~6m from the source\n", "- An overlap chopper placed right after the two PSCs\n", "- A band control chopper\n", - "- A T0 chopper" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5", - "metadata": {}, - "outputs": [], - "source": [ - "choppers = [\n", - " tof.Chopper(\n", - " frequency=14 * Hz,\n", - " direction=tof.AntiClockwise,\n", - " centers=sc.array(\n", - " dims=['cutout'],\n", - " values=[0, 72, 86.4, 115.2, 172.8, 273.6, 288.0, 302.4],\n", - " unit='deg',\n", - " ),\n", - " widths=sc.array(\n", - " dims=['cutout'],\n", - " values=[2.46, 3.02, 3.27, 3.27, 5.02, 3.93, 3.93, 2.46],\n", - " unit='deg',\n", - " ),\n", - " phase=(286 - 180) * deg,\n", - " distance=6.145 * meter,\n", - " name=\"PSC1\",\n", - " ),\n", - " tof.Chopper(\n", - " frequency=14 * Hz,\n", - " direction=tof.Clockwise,\n", - " centers=sc.array(\n", - " dims=['cutout'],\n", - " values=[0, 28.8, 57.6, 144, 158.4, 216, 259.2, 316.8],\n", - " unit='deg',\n", - " ),\n", - " widths=sc.array(\n", - " dims=['cutout'],\n", - " values=[2.46, 3.60, 3.60, 3.23, 3.27, 3.77, 3.94, 2.62],\n", - " unit='deg',\n", - " ),\n", - " phase=236 * deg,\n", - " distance=6.155 * meter,\n", - " name=\"PSC2\",\n", - " ),\n", - " tof.Chopper(\n", - " frequency=14 * Hz,\n", - " direction=tof.AntiClockwise,\n", - " centers=sc.array(\n", - " dims=['cutout'],\n", - " values=[0.0],\n", - " unit='deg',\n", - " ),\n", - " widths=sc.array(\n", - " dims=['cutout'],\n", - " values=[27.6],\n", - " unit='deg',\n", - " ),\n", - " phase=(297 - 180 - 90) * deg,\n", - " distance=6.174 * meter,\n", - " name=\"OC\",\n", - " ),\n", - " tof.Chopper(\n", - " frequency=112 * Hz,\n", - " direction=tof.AntiClockwise,\n", - " centers=sc.array(\n", - " dims=['cutout'],\n", - " values=[0.0, 180.0],\n", - " unit='deg',\n", - " ),\n", - " widths=sc.array(\n", - " dims=['cutout'],\n", - " values=[73.75, 73.75],\n", - " unit='deg',\n", - " ),\n", - " phase=(240 - 180) * deg,\n", - " distance=9.78 * meter,\n", - " name=\"BC\",\n", - " ),\n", - " tof.Chopper(\n", - " frequency=28 * Hz,\n", - " direction=tof.AntiClockwise,\n", - " centers=sc.array(\n", - " dims=['cutout'],\n", - " values=[0.0],\n", - " unit='deg',\n", - " ),\n", - " widths=sc.array(\n", - " dims=['cutout'],\n", - " values=[314.9],\n", - " unit='deg',\n", - " ),\n", - " phase=(280 - 180) * deg,\n", - " distance=13.05 * meter,\n", - " name=\"T0\",\n", - " ),\n", - "]" - ] - }, - { - "cell_type": "markdown", - "id": "6", - "metadata": {}, - "source": [ - "### Detector banks and monitors\n", + "- A T0 chopper\n", "\n", "DREAM has 5 detector banks: the Mantle, two End-caps, a High-resolution detector and a SANS detector.\n", "\n", - "For each detector bank, we use a single mean distance (in practice, one could have a different distance for each pixel)." + "For each detector bank, we use a single mean distance (in practice, one could have a different distance for each pixel).\n", + "\n", + "These component parameters are pre-configured in `tof.facilities.ess`.\n", + "Here we select the configuration in high-flux mode:" ] }, { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "5", "metadata": {}, "outputs": [], "source": [ - "sample_position = 76.55 * meter\n", - "\n", - "detectors = [\n", - " tof.Detector(distance=sample_position + 1.125 * meter, name='mantle'),\n", - " tof.Detector(distance=sample_position + 1.125 * meter, name='end-cap'),\n", - " tof.Detector(distance=sample_position + 2.5 * meter, name='high-resolution'),\n", - " tof.Detector(distance=sample_position + 2.5 * meter, name='sans'),\n", - "]" + "dream_params = tof.facilities.ess.dream(high_flux=True)\n", + "dream_params" ] }, { "cell_type": "markdown", - "id": "8", + "id": "6", "metadata": {}, "source": [ "## Run the simulation\n", @@ -204,18 +90,18 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "7", "metadata": {}, "outputs": [], "source": [ - "model = tof.Model(source=source, choppers=choppers, detectors=detectors)\n", + "model = tof.Model(source=source, **dream_params)\n", "results = model.run()\n", "results.plot(blocked_rays=5000)" ] }, { "cell_type": "markdown", - "id": "10", + "id": "8", "metadata": {}, "source": [ "## Wavelength as a function of time-of-arrival\n", @@ -231,7 +117,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -246,7 +132,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "10", "metadata": {}, "source": [ "### Defining a conversion from `toa` to `wavelength`\n", @@ -259,7 +145,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -278,7 +164,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "12", "metadata": {}, "source": [ "## Computing wavelengths\n", @@ -289,7 +175,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -314,7 +200,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "14", "metadata": {}, "source": [ "We can now compare our computed wavelengths to the true wavelengths of the neutrons." @@ -323,7 +209,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -351,7 +237,8 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" + "pygments_lexer": "ipython3", + "version": "3.12.7" } }, "nbformat": 4, diff --git a/docs/ess/odin.ipynb b/docs/ess/odin.ipynb index 71cb681..1501e04 100644 --- a/docs/ess/odin.ipynb +++ b/docs/ess/odin.ipynb @@ -20,11 +20,7 @@ "source": [ "import scipp as sc\n", "import plopp as pp\n", - "import tof\n", - "\n", - "Hz = sc.Unit(\"Hz\")\n", - "deg = sc.Unit(\"deg\")\n", - "meter = sc.Unit(\"m\")" + "import tof" ] }, { @@ -34,7 +30,7 @@ "source": [ "## Create a source pulse\n", "\n", - "We first create a source with 4 pulses containing 800,000 neutrons each,\n", + "We first create a source with 4 pulses containing 500,000 neutrons each,\n", "and whose distribution follows the ESS time and wavelength profiles (both thermal and cold neutrons are included)." ] }, @@ -56,142 +52,33 @@ "source": [ "## Component set-up\n", "\n", - "### Choppers\n", - "\n", "The ODIN chopper cascade consists of:\n", "\n", "- 2 WFM choppers\n", "- 5 frame-overlap choppers\n", "- 2 band-control choppers\n", - "- 1 T0 chopper" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5", - "metadata": {}, - "outputs": [], - "source": [ - "parameters = {\n", - " \"WFMC_1\": {\n", - " \"frequency\": 56.0,\n", - " \"phase\": 93.244,\n", - " \"distance\": 6.85,\n", - " \"open\": [-1.9419, 49.5756, 98.9315, 146.2165, 191.5176, 234.9179],\n", - " \"close\": [1.9419, 55.7157, 107.2332, 156.5891, 203.8741, 249.1752],\n", - " },\n", - " \"WFMC_2\": {\n", - " \"frequency\": 56.0,\n", - " \"phase\": 97.128,\n", - " \"distance\": 7.15,\n", - " \"open\": [-1.9419, 51.8318, 103.3493, 152.7052, 199.9903, 245.2914],\n", - " \"close\": [1.9419, 57.9719, 111.6510, 163.0778, 212.3468, 259.5486],\n", - " },\n", - " \"FOC_1\": {\n", - " \"frequency\": 42.0,\n", - " \"phase\": 81.303297,\n", - " \"distance\": 8.4,\n", - " \"open\": [-5.1362, 42.5536, 88.2425, 132.0144, 173.9497, 216.7867],\n", - " \"close\": [5.1362, 54.2095, 101.2237, 146.2653, 189.417, 230.7582],\n", - " },\n", - " \"BP_1\": {\n", - " \"frequency\": 7.0,\n", - " \"phase\": 31.080,\n", - " \"distance\": 8.45,\n", - " \"open\": [-23.6029],\n", - " \"close\": [23.6029],\n", - " },\n", - " \"FOC_2\": {\n", - " \"frequency\": 42.0,\n", - " \"phase\": 107.013442,\n", - " \"distance\": 12.2,\n", - " \"open\": [-16.3227, 53.7401, 120.8633, 185.1701, 246.7787, 307.0165],\n", - " \"close\": [16.3227, 86.8303, 154.3794, 218.7551, 280.7508, 340.3188],\n", - " },\n", - " \"BP_2\": {\n", - " \"frequency\": 7.0,\n", - " \"phase\": 44.224,\n", - " \"distance\": 12.25,\n", - " \"open\": [-34.4663],\n", - " \"close\": [34.4663],\n", - " },\n", - " \"T0_alpha\": {\n", - " \"frequency\": 14.0,\n", - " \"phase\": 179.672,\n", - " \"distance\": 13.5,\n", - " \"open\": [-167.8986],\n", - " \"close\": [167.8986],\n", - " },\n", - " \"T0_beta\": {\n", - " \"frequency\": 14.0,\n", - " \"phase\": 179.672,\n", - " \"distance\": 13.7,\n", - " \"open\": [-167.8986],\n", - " \"close\": [167.8986],\n", - " },\n", - " \"FOC_3\": {\n", - " \"frequency\": 28.0,\n", - " \"phase\": 92.993,\n", - " \"distance\": 17.0,\n", - " \"open\": [-20.302, 45.247, 108.0457, 168.2095, 225.8489, 282.2199],\n", - " \"close\": [20.302, 85.357, 147.6824, 207.3927, 264.5977, 319.4024],\n", - " },\n", - " \"FOC_4\": {\n", - " \"frequency\": 14.0,\n", - " \"phase\": 61.584,\n", - " \"distance\": 23.69,\n", - " \"open\": [-16.7157, 29.1882, 73.1661, 115.2988, 155.6636, 195.5254],\n", - " \"close\": [16.7157, 61.8217, 105.0352, 146.4355, 186.0987, 224.0978],\n", - " },\n", - " \"FOC_5\": {\n", - " \"frequency\": 14.0,\n", - " \"phase\": 82.581,\n", - " \"distance\": 33.0,\n", - " \"open\": [-25.8514, 38.3239, 99.8064, 160.1254, 217.4321, 272.5426],\n", - " \"close\": [25.8514, 88.4621, 147.4729, 204.0245, 257.7603, 313.7139],\n", - " },\n", - "}\n", + "- 1 T0 chopper\n", "\n", - "choppers = [\n", - " tof.Chopper(\n", - " frequency=ch[\"frequency\"] * Hz,\n", - " direction=tof.Clockwise,\n", - " open=sc.array(dims=[\"cutout\"], values=ch[\"open\"], unit=\"deg\"),\n", - " close=sc.array(dims=[\"cutout\"], values=ch[\"close\"], unit=\"deg\"),\n", - " phase=ch[\"phase\"] * deg,\n", - " distance=ch[\"distance\"] * meter,\n", - " name=key,\n", - " )\n", - " for key, ch in parameters.items()\n", - "]" - ] - }, - { - "cell_type": "markdown", - "id": "6", - "metadata": {}, - "source": [ - "### Detector\n", + "It also has a single detector panel 60.5 meters from the source.\n", "\n", - "ODIN has a single detector panel 60.5 meters from the source." + "These component parameters are pre-configured in `tof.facilities.ess`.\n", + "Here we select the configuration in pulse-skipping mode:" ] }, { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "5", "metadata": {}, "outputs": [], "source": [ - "detectors = [\n", - " tof.Detector(distance=60.5 * meter, name=\"detector\"),\n", - "]" + "odin_params = tof.facilities.ess.odin(pulse_skipping=True)\n", + "odin_params" ] }, { "cell_type": "markdown", - "id": "8", + "id": "6", "metadata": {}, "source": [ "## Run the simulation\n", @@ -202,18 +89,18 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "7", "metadata": {}, "outputs": [], "source": [ - "model = tof.Model(source=source, choppers=choppers, detectors=detectors)\n", + "model = tof.Model(source=source, **odin_params)\n", "results = model.run()\n", "results.plot(blocked_rays=5000)" ] }, { "cell_type": "markdown", - "id": "10", + "id": "8", "metadata": {}, "source": [ "We can see that the chopper cascade is implementing WFM and pulse-skipping at the same time!\n", @@ -231,7 +118,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -245,7 +132,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "10", "metadata": {}, "source": [ "### Defining a conversion from `toa` to `wavelength`\n", @@ -258,7 +145,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -277,7 +164,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "12", "metadata": {}, "source": [ "We can now overlay our mean wavelength function on the image above:" @@ -286,7 +173,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -306,7 +193,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "14", "metadata": {}, "source": [ "## Computing wavelengths\n", @@ -317,7 +204,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -338,7 +225,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "16", "metadata": {}, "source": [ "We can now compare our computed wavelengths to the true wavelengths of the neutrons:" @@ -347,7 +234,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -375,7 +262,8 @@ "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython3" + "pygments_lexer": "ipython3", + "version": "3.12.7" } }, "nbformat": 4, diff --git a/src/tof/__init__.py b/src/tof/__init__.py index 00623db..d3a75af 100644 --- a/src/tof/__init__.py +++ b/src/tof/__init__.py @@ -14,7 +14,7 @@ __getattr__, __dir__, __all__ = lazy.attach( __name__, - submodules=[], + submodules=['facilities'], submod_attrs={ 'chopper': ['AntiClockwise', 'Chopper', 'ChopperReading', 'Clockwise'], 'dashboard': ['Dashboard'], diff --git a/src/tof/dashboard.py b/src/tof/dashboard.py index ceab4af..37c5164 100644 --- a/src/tof/dashboard.py +++ b/src/tof/dashboard.py @@ -12,6 +12,7 @@ from matplotlib.backend_bases import PickEvent from mpl_toolkits.axes_grid1 import make_axes_locatable +from . import facilities from .chopper import AntiClockwise, Chopper, Clockwise from .detector import Detector from .model import Model @@ -113,13 +114,29 @@ def continuous_update(self, callback: Callable): self.enabled_widget.observe(callback, names="value") -class SourceWidget(ipw.VBox): +class SetupWidget(ipw.VBox): def __init__(self): self.facility_widget = ipw.Dropdown(options=["ess"], description="Facility") self.neutrons_widget = ipw.IntText(value=100_000, description="Neutrons") self.pulses_widget = ipw.IntText(value=1, description="Pulses") + self.instrument_widget = ipw.Dropdown( + options=[ + "ESS: Odin", + "ESS: Odin (pulse-skipping)", + "ESS: Dream (high-flux)", + ], + description="Instrument", + value=None, + ) super().__init__( - [self.facility_widget, self.neutrons_widget, self.pulses_widget] + [ + ipw.Label(value="Source:"), + self.facility_widget, + self.neutrons_widget, + self.pulses_widget, + ipw.HTML("
"), + self.instrument_widget, + ] ) def continuous_update(self, callback: Callable): @@ -128,6 +145,13 @@ def continuous_update(self, callback: Callable): self.pulses_widget.observe(callback, names="value") +INSTRUMENT_LIBRARY = { + "ESS: Odin": facilities.ess.odin(pulse_skipping=False), + "ESS: Odin (pulse-skipping)": facilities.ess.odin(pulse_skipping=True), + "ESS: Dream (high-flux)": facilities.ess.dream(high_flux=True), +} + + class TofWidget: def __init__(self): self.top_bar = ipw.HBox() @@ -165,8 +189,11 @@ def __init__(self): ("Logy", "Toggle log scale", "arrows-alt-v", "toggle_yscale"), ] - self.source_widget = SourceWidget() - self.source_widget.continuous_update(self.maybe_update) + self.setup_widget = SetupWidget() + self.setup_widget.continuous_update(self.maybe_update) + self.setup_widget.instrument_widget.observe( + self.populate_from_instrument, names="value" + ) self.choppers_container = ipw.Accordion() self.add_chopper_button = ipw.Button(description="Add chopper") @@ -182,8 +209,8 @@ def __init__(self): [self.add_detector_button, self.detectors_container] ) - tab_contents = ["Source", "Choppers", "Detectors"] - children = [self.source_widget, self.choppers_widget, self.detectors_widget] + tab_contents = ["Setup", "Choppers", "Detectors"] + children = [self.setup_widget, self.choppers_widget, self.detectors_widget] self.tab = ipw.Tab(layout={"height": "650px", "width": "374px"}) self.tab.children = children self.tab.titles = tab_contents @@ -212,6 +239,35 @@ def __init__(self): self.main_widget = ipw.VBox([self.top_bar, self.toa_wav_fig.canvas]) + def populate_from_instrument(self, change): + cont_update_value = self.continuous_update.value + self.continuous_update.value = False + for ch in self.choppers_container.children: + self.remove_chopper(None, uid=ch._uid) + for det in self.detectors_container.children: + self.remove_detector(None, uid=det._uid) + params = INSTRUMENT_LIBRARY[change["new"]] + for ch in params["choppers"]: + self.add_chopper(None) + chop = self.choppers_container.children[-1] + chop.frequency_widget.value = ch.frequency.to(unit='Hz').value + chop.open_widget.value = ", ".join( + str(x) for x in ch.open.to(unit='deg').values + ) + chop.close_widget.value = ", ".join( + str(x) for x in ch.close.to(unit='deg').values + ) + chop.phase_widget.value = ch.phase.to(unit='deg').value + chop.distance_widget.value = ch.distance.to(unit='m').value + chop.name_widget.value = ch.name + for d in params["detectors"]: + self.add_detector(None) + det = self.detectors_container.children[-1] + det.distance_widget.value = d.distance.to(unit='m').value + det.name_widget.value = d.name + self.run(None) + self.continuous_update.value = cont_update_value + def toggle_yscale(self): scale = "log" if self.toa_wav_ax[0].get_yscale() == "linear" else "linear" self.toa_wav_ax[0].set_yscale(scale) @@ -297,9 +353,9 @@ def plot_time_distance(self, _: Any | None = None): def run(self, _: Any): source = Source( - facility=self.source_widget.facility_widget.value, - neutrons=int(self.source_widget.neutrons_widget.value), - pulses=int(self.source_widget.pulses_widget.value), + facility=self.setup_widget.facility_widget.value, + neutrons=int(self.setup_widget.neutrons_widget.value), + pulses=int(self.setup_widget.pulses_widget.value), ) choppers = [ Chopper( @@ -425,16 +481,5 @@ def Dashboard(): dashboard. """ w = TofWidget() - w.add_chopper_button.click() - ch = w.choppers_container.children[0] - ch.frequency_widget.value = 48.0 - ch.open_widget.value = "10, 50, 150, 270" - ch.close_widget.value = "50, 90, 190, 320" - ch.distance_widget.value = 10.0 - ch.name_widget.value = "ChopChop" - w.add_detector_button.click() - d = w.detectors_container.children[0] - d.distance_widget.value = 35.0 - d.name_widget.value = "Monitor" - w.run(None) + w.setup_widget.instrument_widget.value = "ESS: Odin (pulse-skipping)" return w.main_widget diff --git a/src/tof/facilities/__init__.py b/src/tof/facilities/__init__.py index 2d643f8..83ef51f 100644 --- a/src/tof/facilities/__init__.py +++ b/src/tof/facilities/__init__.py @@ -1,2 +1,6 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2025 Scipp contributors (https://github.com/scipp) + +from . import ess + +__all__ = ["ess"] diff --git a/src/tof/facilities/common.py b/src/tof/facilities/common.py new file mode 100644 index 0000000..25efbe7 --- /dev/null +++ b/src/tof/facilities/common.py @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2025 Scipp contributors (https://github.com/scipp) + +import scipp as sc + +from ..chopper import AntiClockwise, Chopper, Clockwise +from ..detector import Detector + + +def _array_or_none(container, key): + return ( + sc.array(dims=["cutout"], values=container[key], unit="deg") + if key in container + else None + ) + + +def make_beamline(instrument) -> dict[str, list[Chopper] | list[Detector]]: + choppers = [ + Chopper( + frequency=ch["frequency"] * sc.Unit("Hz"), + direction={"clockwise": Clockwise, "anti-clockwise": AntiClockwise}[ + ch["direction"] + ], + open=_array_or_none(ch, "open"), + close=_array_or_none(ch, "close"), + centers=_array_or_none(ch, "centers"), + widths=_array_or_none(ch, "widths"), + phase=ch["phase"] * sc.Unit("deg"), + distance=ch["distance"] * sc.Unit("m"), + name=key, + ) + for key, ch in instrument["choppers"].items() + ] + detectors = [ + Detector(distance=det["distance"] * sc.Unit("m"), name=key) + for key, det in instrument["detectors"].items() + ] + return {"choppers": choppers, "detectors": detectors} diff --git a/src/tof/facilities/ess/__init__.py b/src/tof/facilities/ess/__init__.py new file mode 100644 index 0000000..8275b51 --- /dev/null +++ b/src/tof/facilities/ess/__init__.py @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2025 Scipp contributors (https://github.com/scipp) + +from .dream import dream +from .odin import odin +from .pulse_profile import pulse + +__all__ = ["dream", "odin", "pulse"] diff --git a/src/tof/facilities/ess/dream.py b/src/tof/facilities/ess/dream.py new file mode 100644 index 0000000..0822f89 --- /dev/null +++ b/src/tof/facilities/ess/dream.py @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2025 Scipp contributors (https://github.com/scipp) +""" +Pre-configured chopper and detector parameters for Dream. +""" + +from ...chopper import Chopper +from ...detector import Detector +from ..common import make_beamline + +dream_high_flux = { + "choppers": { + "PSC1": { + "frequency": 14.0, + "phase": 286.0 - 180.0, + "distance": 6.145, + "centers": [0, 72, 86.4, 115.2, 172.8, 273.6, 288.0, 302.4], + "widths": [2.46, 3.02, 3.27, 3.27, 5.02, 3.93, 3.93, 2.46], + "direction": "anti-clockwise", + }, + "PSC2": { + "frequency": 14.0, + "phase": 236.0, + "distance": 6.155, + "centers": [0, 28.8, 57.6, 144.0, 158.4, 216.0, 259.2, 316.8], + "widths": [2.46, 3.60, 3.60, 3.23, 3.27, 3.77, 3.94, 2.62], + "direction": "clockwise", + }, + "OC": { + "frequency": 14.0, + "phase": 297.0 - 180.0 - 90.0, + "distance": 6.174, + "centers": [0.0], + "widths": [27.6], + "direction": "anti-clockwise", + }, + "BC": { + "frequency": 112.0, + "phase": 240.0 - 180.0, + "distance": 9.78, + "centers": [0.0, 180.0], + "widths": [73.75, 73.75], + "direction": "anti-clockwise", + }, + "T0": { + "frequency": 28.0, + "phase": 280.0 - 180.0, + "distance": 13.05, + "centers": [0.0], + "widths": [314.9], + "direction": "anti-clockwise", + }, + }, + "detectors": { + "mantle": {"distance": 77.675}, + "end-cap": {"distance": 77.675}, + "high-resolution": {"distance": 79.05}, + "sans": {"distance": 79.05}, + }, +} + + +def dream( + high_flux=False, high_resolution=False +) -> dict[str, list[Chopper] | list[Detector]]: + if high_flux and high_resolution: + raise ValueError("Select either high flux or high resolution, not both.") + if high_flux: + return make_beamline(dream_high_flux) + if high_resolution: + # TODO: Add high-resolution configuration + raise NotImplementedError("High-resolution configuration not yet implemented.") + raise ValueError("Either high_flux or high_resolution must be True.") diff --git a/src/tof/facilities/ess/odin.py b/src/tof/facilities/ess/odin.py new file mode 100644 index 0000000..acc9cd2 --- /dev/null +++ b/src/tof/facilities/ess/odin.py @@ -0,0 +1,129 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2025 Scipp contributors (https://github.com/scipp) +""" +Pre-configured chopper and detector parameters for Odin. +""" + +import copy + +from ...chopper import Chopper +from ...detector import Detector +from ..common import make_beamline + +odin_non_pulse_skipping = { + "choppers": { + "WFMC_1": { + "frequency": 56.0, + "phase": 93.244, + "distance": 6.85, + "open": [-1.9419, 49.5756, 98.9315, 146.2165, 191.5176, 234.9179], + "close": [1.9419, 55.7157, 107.2332, 156.5891, 203.8741, 249.1752], + "direction": "clockwise", + }, + "WFMC_2": { + "frequency": 56.0, + "phase": 97.128, + "distance": 7.15, + "open": [-1.9419, 51.8318, 103.3493, 152.7052, 199.9903, 245.2914], + "close": [1.9419, 57.9719, 111.6510, 163.0778, 212.3468, 259.5486], + "direction": "clockwise", + }, + "FOC_1": { + "frequency": 42.0, + "phase": 81.303297, + "distance": 8.4, + "open": [-5.1362, 42.5536, 88.2425, 132.0144, 173.9497, 216.7867], + "close": [5.1362, 54.2095, 101.2237, 146.2653, 189.417, 230.7582], + "direction": "clockwise", + }, + "BP_1": { + "frequency": 14.0, + "phase": 31.080 + 14.0, + "distance": 8.45, + "open": [-23.6029], + "close": [23.6029], + "direction": "clockwise", + }, + "FOC_2": { + "frequency": 42.0, + "phase": 107.013442, + "distance": 12.2, + "open": [-16.3227, 53.7401, 120.8633, 185.1701, 246.7787, 307.0165], + "close": [16.3227, 86.8303, 154.3794, 218.7551, 280.7508, 340.3188], + "direction": "clockwise", + }, + "BP_2": { + "frequency": 14.0, + "phase": 44.224 + 14.0, + "distance": 12.25, + "open": [-34.4663], + "close": [34.4663], + "direction": "clockwise", + }, + "T0_alpha": { + "frequency": 14.0, + "phase": 179.672, + "distance": 13.5, + "open": [-167.8986], + "close": [167.8986], + "direction": "clockwise", + }, + "T0_beta": { + "frequency": 14.0, + "phase": 179.672, + "distance": 13.7, + "open": [-167.8986], + "close": [167.8986], + "direction": "clockwise", + }, + "FOC_3": { + "frequency": 28.0, + "phase": 92.993, + "distance": 17.0, + "open": [-20.302, 45.247, 108.0457, 168.2095, 225.8489, 282.2199], + "close": [20.302, 85.357, 147.6824, 207.3927, 264.5977, 319.4024], + "direction": "clockwise", + }, + "FOC_4": { + "frequency": 14.0, + "phase": 61.584, + "distance": 23.69, + "open": [-16.7157, 29.1882, 73.1661, 115.2988, 155.6636, 195.5254], + "close": [16.7157, 61.8217, 105.0352, 146.4355, 186.0987, 224.0978], + "direction": "clockwise", + }, + "FOC_5": { + "frequency": 14.0, + "phase": 82.581, + "distance": 33.0, + "open": [-25.8514, 38.3239, 99.8064, 160.1254, 217.4321, 272.5426], + "close": [25.8514, 88.4621, 147.4729, 204.0245, 257.7603, 313.7139], + "direction": "clockwise", + }, + }, + "detectors": {"detector": {"distance": 60.5}}, +} + + +odin_pulse_skipping = copy.deepcopy(odin_non_pulse_skipping) +odin_pulse_skipping["choppers"]["BP_1"]["frequency"] = 7.0 +odin_pulse_skipping["choppers"]["BP_1"]["phase"] = 31.080 +odin_pulse_skipping["choppers"]["BP_2"]["frequency"] = 7.0 +odin_pulse_skipping["choppers"]["BP_2"]["phase"] = 44.224 + + +def odin( + pulse_skipping: bool = False, +) -> dict[str, list[Chopper] | list[Detector]]: + """ + Return a pre-configured Odin beamline containing choppers and a detector. + + Parameters + ---------- + pulse_skipping: + If True, configure the beamline for pulse skipping mode (band-pass choppers will + rotate at 7 Hz instead of 14 Hz). + """ + return make_beamline( + odin_pulse_skipping if pulse_skipping else odin_non_pulse_skipping + ) diff --git a/src/tof/facilities/ess/pulse_profile.py b/src/tof/facilities/ess/pulse_profile.py new file mode 100644 index 0000000..fe64d73 --- /dev/null +++ b/src/tof/facilities/ess/pulse_profile.py @@ -0,0 +1,649 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2023 Scipp contributors (https://github.com/scipp) + +""" +The data here was obtained from the McStas model of the ESS moderator. +See https://www2.mcstas.org/download/components/3.4_current/sources/ESS_butterfly.html. +""" + +import scipp as sc + +from ...utils import PulseProfile + +pulse = PulseProfile( + birth_time=sc.DataArray( + data=sc.array( + dims=['birth_time'], + values=[ + 3.35837383e-04, + 7.11462527e-04, + 1.19279849e-03, + 1.75844384e-03, + 2.30312776e-03, + 2.81888175e-03, + 3.29973291e-03, + 3.74342320e-03, + 4.12944139e-03, + 4.50699573e-03, + 4.84309044e-03, + 5.14929712e-03, + 5.45855067e-03, + 5.69936363e-03, + 5.93094105e-03, + 6.17477200e-03, + 6.37362606e-03, + 6.53680137e-03, + 6.73889852e-03, + 6.89076164e-03, + 7.03303086e-03, + 7.16725029e-03, + 7.28150493e-03, + 7.38097004e-03, + 7.47766769e-03, + 7.58743639e-03, + 7.68332632e-03, + 7.76191703e-03, + 7.81004156e-03, + 7.89307474e-03, + 7.99204102e-03, + 8.00486001e-03, + 8.06517551e-03, + 8.12961675e-03, + 8.15018882e-03, + 8.20005390e-03, + 8.24155829e-03, + 8.29947221e-03, + 8.32367988e-03, + 8.35031681e-03, + 8.38183866e-03, + 8.40430754e-03, + 8.41246895e-03, + 8.47906647e-03, + 8.48321568e-03, + 8.51947204e-03, + 8.50648386e-03, + 8.50133270e-03, + 8.52613383e-03, + 8.54115343e-03, + 8.57264133e-03, + 8.56943450e-03, + 8.55472574e-03, + 8.59122615e-03, + 8.62517530e-03, + 8.60719002e-03, + 8.60040235e-03, + 8.63208702e-03, + 8.62533445e-03, + 8.64678861e-03, + 8.64471591e-03, + 8.64182350e-03, + 8.63327922e-03, + 8.66950735e-03, + 8.65129688e-03, + 8.64664447e-03, + 8.64770277e-03, + 8.68294600e-03, + 8.67019185e-03, + 8.66156150e-03, + 8.67539908e-03, + 8.69230509e-03, + 8.66817676e-03, + 8.67640973e-03, + 8.67257086e-03, + 8.71476117e-03, + 8.69738903e-03, + 8.70019388e-03, + 8.68198365e-03, + 8.70585101e-03, + 8.69709781e-03, + 8.70056388e-03, + 8.70854571e-03, + 8.67909089e-03, + 8.68674970e-03, + 8.68150346e-03, + 8.70191501e-03, + 8.69301298e-03, + 8.69085698e-03, + 8.71649943e-03, + 8.67369456e-03, + 8.69969514e-03, + 8.72322806e-03, + 8.70185411e-03, + 8.68939648e-03, + 8.71606837e-03, + 8.68724841e-03, + 8.70218613e-03, + 8.72040120e-03, + 8.69293491e-03, + 8.69351789e-03, + 8.73419233e-03, + 8.69075919e-03, + 8.70522638e-03, + 8.71477349e-03, + 8.70507452e-03, + 8.70227301e-03, + 8.71420193e-03, + 8.71897903e-03, + 8.70425294e-03, + 8.68891152e-03, + 8.68402639e-03, + 8.70486831e-03, + 8.69707245e-03, + 8.70321249e-03, + 8.27644038e-03, + 7.95684870e-03, + 7.43411585e-03, + 6.84769508e-03, + 6.31981817e-03, + 5.79771415e-03, + 5.33668341e-03, + 4.90722076e-03, + 4.51723727e-03, + 4.14674875e-03, + 3.81695136e-03, + 3.51640474e-03, + 3.22513571e-03, + 2.95911995e-03, + 2.73056190e-03, + 2.51716771e-03, + 2.31576802e-03, + 2.12938206e-03, + 1.96285779e-03, + 1.80590850e-03, + 1.66301586e-03, + 1.52912399e-03, + 1.41144832e-03, + 1.29986696e-03, + 1.20219471e-03, + 1.10136923e-03, + 1.01839892e-03, + 9.39143113e-04, + 8.64814579e-04, + 7.96703989e-04, + 7.35063247e-04, + 6.78739795e-04, + 6.24226207e-04, + 5.76731522e-04, + 5.31877313e-04, + 4.91432490e-04, + 4.53724222e-04, + 4.17097140e-04, + 3.84883668e-04, + 3.56053519e-04, + 3.28615077e-04, + 3.03171707e-04, + 2.80212744e-04, + 2.58806676e-04, + 2.39233542e-04, + 2.20830164e-04, + 2.03703468e-04, + 1.88784094e-04, + 1.74177494e-04, + 1.61290974e-04, + 1.48847234e-04, + 1.37594011e-04, + 1.27396903e-04, + 1.17512734e-04, + 1.08817617e-04, + 1.00626721e-04, + 9.30799915e-05, + 8.58308478e-05, + 7.97150769e-05, + 7.36523610e-05, + 6.84294010e-05, + 6.32182918e-05, + 5.85268482e-05, + 5.40528453e-05, + 5.02368808e-05, + 4.65180897e-05, + 4.30575884e-05, + 3.98835225e-05, + 3.70504980e-05, + 3.42857719e-05, + 3.18132215e-05, + 2.95287409e-05, + 2.73577012e-05, + 2.53724318e-05, + 2.35572592e-05, + 2.18844303e-05, + 2.02605458e-05, + 1.88596686e-05, + 1.75580663e-05, + 1.62935792e-05, + 1.51211989e-05, + 1.40303609e-05, + 1.30832864e-05, + 1.21382107e-05, + 1.12829800e-05, + 1.05060755e-05, + ], + ), + coords={ + 'birth_time': sc.array( + dims=['birth_time'], + values=[ + 1.24378109e-05, + 3.73134328e-05, + 6.21890547e-05, + 8.70646766e-05, + 1.11940299e-04, + 1.36815920e-04, + 1.61691542e-04, + 1.86567164e-04, + 2.11442786e-04, + 2.36318408e-04, + 2.61194030e-04, + 2.86069652e-04, + 3.10945274e-04, + 3.35820896e-04, + 3.60696517e-04, + 3.85572139e-04, + 4.10447761e-04, + 4.35323383e-04, + 4.60199005e-04, + 4.85074627e-04, + 5.09950249e-04, + 5.34825871e-04, + 5.59701493e-04, + 5.84577114e-04, + 6.09452736e-04, + 6.34328358e-04, + 6.59203980e-04, + 6.84079602e-04, + 7.08955224e-04, + 7.33830846e-04, + 7.58706468e-04, + 7.83582090e-04, + 8.08457711e-04, + 8.33333333e-04, + 8.58208955e-04, + 8.83084577e-04, + 9.07960199e-04, + 9.32835821e-04, + 9.57711443e-04, + 9.82587065e-04, + 1.00746269e-03, + 1.03233831e-03, + 1.05721393e-03, + 1.08208955e-03, + 1.10696517e-03, + 1.13184080e-03, + 1.15671642e-03, + 1.18159204e-03, + 1.20646766e-03, + 1.23134328e-03, + 1.25621891e-03, + 1.28109453e-03, + 1.30597015e-03, + 1.33084577e-03, + 1.35572139e-03, + 1.38059701e-03, + 1.40547264e-03, + 1.43034826e-03, + 1.45522388e-03, + 1.48009950e-03, + 1.50497512e-03, + 1.52985075e-03, + 1.55472637e-03, + 1.57960199e-03, + 1.60447761e-03, + 1.62935323e-03, + 1.65422886e-03, + 1.67910448e-03, + 1.70398010e-03, + 1.72885572e-03, + 1.75373134e-03, + 1.77860697e-03, + 1.80348259e-03, + 1.82835821e-03, + 1.85323383e-03, + 1.87810945e-03, + 1.90298507e-03, + 1.92786070e-03, + 1.95273632e-03, + 1.97761194e-03, + 2.00248756e-03, + 2.02736318e-03, + 2.05223881e-03, + 2.07711443e-03, + 2.10199005e-03, + 2.12686567e-03, + 2.15174129e-03, + 2.17661692e-03, + 2.20149254e-03, + 2.22636816e-03, + 2.25124378e-03, + 2.27611940e-03, + 2.30099502e-03, + 2.32587065e-03, + 2.35074627e-03, + 2.37562189e-03, + 2.40049751e-03, + 2.42537313e-03, + 2.45024876e-03, + 2.47512438e-03, + 2.50000000e-03, + 2.52487562e-03, + 2.54975124e-03, + 2.57462687e-03, + 2.59950249e-03, + 2.62437811e-03, + 2.64925373e-03, + 2.67412935e-03, + 2.69900498e-03, + 2.72388060e-03, + 2.74875622e-03, + 2.77363184e-03, + 2.79850746e-03, + 2.82338308e-03, + 2.84825871e-03, + 2.87313433e-03, + 2.89800995e-03, + 2.92288557e-03, + 2.94776119e-03, + 2.97263682e-03, + 2.99751244e-03, + 3.02238806e-03, + 3.04726368e-03, + 3.07213930e-03, + 3.09701493e-03, + 3.12189055e-03, + 3.14676617e-03, + 3.17164179e-03, + 3.19651741e-03, + 3.22139303e-03, + 3.24626866e-03, + 3.27114428e-03, + 3.29601990e-03, + 3.32089552e-03, + 3.34577114e-03, + 3.37064677e-03, + 3.39552239e-03, + 3.42039801e-03, + 3.44527363e-03, + 3.47014925e-03, + 3.49502488e-03, + 3.51990050e-03, + 3.54477612e-03, + 3.56965174e-03, + 3.59452736e-03, + 3.61940299e-03, + 3.64427861e-03, + 3.66915423e-03, + 3.69402985e-03, + 3.71890547e-03, + 3.74378109e-03, + 3.76865672e-03, + 3.79353234e-03, + 3.81840796e-03, + 3.84328358e-03, + 3.86815920e-03, + 3.89303483e-03, + 3.91791045e-03, + 3.94278607e-03, + 3.96766169e-03, + 3.99253731e-03, + 4.01741294e-03, + 4.04228856e-03, + 4.06716418e-03, + 4.09203980e-03, + 4.11691542e-03, + 4.14179104e-03, + 4.16666667e-03, + 4.19154229e-03, + 4.21641791e-03, + 4.24129353e-03, + 4.26616915e-03, + 4.29104478e-03, + 4.31592040e-03, + 4.34079602e-03, + 4.36567164e-03, + 4.39054726e-03, + 4.41542289e-03, + 4.44029851e-03, + 4.46517413e-03, + 4.49004975e-03, + 4.51492537e-03, + 4.53980100e-03, + 4.56467662e-03, + 4.58955224e-03, + 4.61442786e-03, + 4.63930348e-03, + 4.66417910e-03, + 4.68905473e-03, + 4.71393035e-03, + 4.73880597e-03, + 4.76368159e-03, + 4.78855721e-03, + 4.81343284e-03, + 4.83830846e-03, + 4.86318408e-03, + 4.88805970e-03, + 4.91293532e-03, + 4.93781095e-03, + 4.96268657e-03, + 4.98756219e-03, + ], + unit='s', + ) + }, + ), + wavelength=sc.DataArray( + data=sc.array( + dims=['wavelength'], + values=[ + 2.47910521e-02, + 3.15394565e-02, + 2.46673832e-02, + 4.24700432e-02, + 6.22005065e-02, + 6.41430908e-02, + 5.63970675e-02, + 4.79885540e-02, + 4.28010531e-02, + 4.17366412e-02, + 4.51519899e-02, + 5.35348380e-02, + 6.07649341e-02, + 5.33537571e-02, + 4.52415054e-02, + 3.85305979e-02, + 3.29632299e-02, + 2.82808267e-02, + 2.43136951e-02, + 2.09961870e-02, + 1.81474939e-02, + 1.57517036e-02, + 1.36839501e-02, + 1.19127479e-02, + 1.04094236e-02, + 9.09390901e-03, + 7.97376111e-03, + 7.03987735e-03, + 6.19641665e-03, + 5.48516928e-03, + 4.84954606e-03, + 4.31560357e-03, + 3.84683395e-03, + 3.43486380e-03, + 3.07473495e-03, + 2.75943204e-03, + 2.48705338e-03, + 2.24190868e-03, + 2.02792878e-03, + 1.83647522e-03, + 1.66751194e-03, + 1.52037927e-03, + 1.38568580e-03, + 1.26625902e-03, + 1.15985092e-03, + 1.06326666e-03, + 9.77402425e-04, + 8.97152751e-04, + 8.28473264e-04, + 7.65517629e-04, + 7.06753047e-04, + 6.53870845e-04, + 6.05626744e-04, + 5.61775168e-04, + 5.21407086e-04, + 4.83706064e-04, + 4.50096590e-04, + 4.18595081e-04, + 3.89942860e-04, + 3.63082390e-04, + 3.38196875e-04, + 3.16116766e-04, + 2.94433976e-04, + 2.74611217e-04, + 2.56804279e-04, + 2.39866263e-04, + 2.24103151e-04, + 2.09428428e-04, + 1.96024411e-04, + 1.83244400e-04, + 1.71452766e-04, + 1.60372237e-04, + 1.50247463e-04, + 1.40620774e-04, + 1.31338349e-04, + 1.23209023e-04, + 1.15219833e-04, + 1.07938245e-04, + 1.01217712e-04, + 9.47807301e-05, + 8.87686984e-05, + 8.31031332e-05, + 7.78786212e-05, + 7.29971694e-05, + 6.83232132e-05, + 6.40614634e-05, + 6.00522902e-05, + 5.63009205e-05, + 5.26437650e-05, + 4.93458816e-05, + 4.62942251e-05, + 4.33867986e-05, + 4.06646952e-05, + 3.80409296e-05, + 3.57000207e-05, + 3.34181377e-05, + 3.13558896e-05, + 2.93784366e-05, + 2.75483925e-05, + 2.57593945e-05, + 2.41535573e-05, + ], + ), + coords={ + 'wavelength': sc.array( + dims=['wavelength'], + values=[ + 1.98514851e-01, + 3.95544554e-01, + 5.92574257e-01, + 7.89603960e-01, + 9.86633663e-01, + 1.18366337e00, + 1.38069307e00, + 1.57772277e00, + 1.77475248e00, + 1.97178218e00, + 2.16881188e00, + 2.36584158e00, + 2.56287129e00, + 2.75990099e00, + 2.95693069e00, + 3.15396040e00, + 3.35099010e00, + 3.54801980e00, + 3.74504950e00, + 3.94207921e00, + 4.13910891e00, + 4.33613861e00, + 4.53316832e00, + 4.73019802e00, + 4.92722772e00, + 5.12425743e00, + 5.32128713e00, + 5.51831683e00, + 5.71534653e00, + 5.91237624e00, + 6.10940594e00, + 6.30643564e00, + 6.50346535e00, + 6.70049505e00, + 6.89752475e00, + 7.09455446e00, + 7.29158416e00, + 7.48861386e00, + 7.68564356e00, + 7.88267327e00, + 8.07970297e00, + 8.27673267e00, + 8.47376238e00, + 8.67079208e00, + 8.86782178e00, + 9.06485149e00, + 9.26188119e00, + 9.45891089e00, + 9.65594059e00, + 9.85297030e00, + 1.00500000e01, + 1.02470297e01, + 1.04440594e01, + 1.06410891e01, + 1.08381188e01, + 1.10351485e01, + 1.12321782e01, + 1.14292079e01, + 1.16262376e01, + 1.18232673e01, + 1.20202970e01, + 1.22173267e01, + 1.24143564e01, + 1.26113861e01, + 1.28084158e01, + 1.30054455e01, + 1.32024752e01, + 1.33995050e01, + 1.35965347e01, + 1.37935644e01, + 1.39905941e01, + 1.41876238e01, + 1.43846535e01, + 1.45816832e01, + 1.47787129e01, + 1.49757426e01, + 1.51727723e01, + 1.53698020e01, + 1.55668317e01, + 1.57638614e01, + 1.59608911e01, + 1.61579208e01, + 1.63549505e01, + 1.65519802e01, + 1.67490099e01, + 1.69460396e01, + 1.71430693e01, + 1.73400990e01, + 1.75371287e01, + 1.77341584e01, + 1.79311881e01, + 1.81282178e01, + 1.83252475e01, + 1.85222772e01, + 1.87193069e01, + 1.89163366e01, + 1.91133663e01, + 1.93103960e01, + 1.95074257e01, + 1.97044554e01, + 1.99014851e01, + ], + unit='angstrom', + ) + }, + ), + frequency=14.0 * sc.Unit('Hz'), +) diff --git a/src/tof/facilities/ess_pulse.py b/src/tof/facilities/ess_pulse.py deleted file mode 100644 index 48f5b24..0000000 --- a/src/tof/facilities/ess_pulse.py +++ /dev/null @@ -1,647 +0,0 @@ -# SPDX-License-Identifier: BSD-3-Clause -# Copyright (c) 2023 Scipp contributors (https://github.com/scipp) - -""" -The data here was obtained from the McStas model of the ESS moderator. -See https://www2.mcstas.org/download/components/3.4_current/sources/ESS_butterfly.html. -""" - -import scipp as sc - -birth_time = sc.DataArray( - data=sc.array( - dims=['birth_time'], - values=[ - 3.35837383e-04, - 7.11462527e-04, - 1.19279849e-03, - 1.75844384e-03, - 2.30312776e-03, - 2.81888175e-03, - 3.29973291e-03, - 3.74342320e-03, - 4.12944139e-03, - 4.50699573e-03, - 4.84309044e-03, - 5.14929712e-03, - 5.45855067e-03, - 5.69936363e-03, - 5.93094105e-03, - 6.17477200e-03, - 6.37362606e-03, - 6.53680137e-03, - 6.73889852e-03, - 6.89076164e-03, - 7.03303086e-03, - 7.16725029e-03, - 7.28150493e-03, - 7.38097004e-03, - 7.47766769e-03, - 7.58743639e-03, - 7.68332632e-03, - 7.76191703e-03, - 7.81004156e-03, - 7.89307474e-03, - 7.99204102e-03, - 8.00486001e-03, - 8.06517551e-03, - 8.12961675e-03, - 8.15018882e-03, - 8.20005390e-03, - 8.24155829e-03, - 8.29947221e-03, - 8.32367988e-03, - 8.35031681e-03, - 8.38183866e-03, - 8.40430754e-03, - 8.41246895e-03, - 8.47906647e-03, - 8.48321568e-03, - 8.51947204e-03, - 8.50648386e-03, - 8.50133270e-03, - 8.52613383e-03, - 8.54115343e-03, - 8.57264133e-03, - 8.56943450e-03, - 8.55472574e-03, - 8.59122615e-03, - 8.62517530e-03, - 8.60719002e-03, - 8.60040235e-03, - 8.63208702e-03, - 8.62533445e-03, - 8.64678861e-03, - 8.64471591e-03, - 8.64182350e-03, - 8.63327922e-03, - 8.66950735e-03, - 8.65129688e-03, - 8.64664447e-03, - 8.64770277e-03, - 8.68294600e-03, - 8.67019185e-03, - 8.66156150e-03, - 8.67539908e-03, - 8.69230509e-03, - 8.66817676e-03, - 8.67640973e-03, - 8.67257086e-03, - 8.71476117e-03, - 8.69738903e-03, - 8.70019388e-03, - 8.68198365e-03, - 8.70585101e-03, - 8.69709781e-03, - 8.70056388e-03, - 8.70854571e-03, - 8.67909089e-03, - 8.68674970e-03, - 8.68150346e-03, - 8.70191501e-03, - 8.69301298e-03, - 8.69085698e-03, - 8.71649943e-03, - 8.67369456e-03, - 8.69969514e-03, - 8.72322806e-03, - 8.70185411e-03, - 8.68939648e-03, - 8.71606837e-03, - 8.68724841e-03, - 8.70218613e-03, - 8.72040120e-03, - 8.69293491e-03, - 8.69351789e-03, - 8.73419233e-03, - 8.69075919e-03, - 8.70522638e-03, - 8.71477349e-03, - 8.70507452e-03, - 8.70227301e-03, - 8.71420193e-03, - 8.71897903e-03, - 8.70425294e-03, - 8.68891152e-03, - 8.68402639e-03, - 8.70486831e-03, - 8.69707245e-03, - 8.70321249e-03, - 8.27644038e-03, - 7.95684870e-03, - 7.43411585e-03, - 6.84769508e-03, - 6.31981817e-03, - 5.79771415e-03, - 5.33668341e-03, - 4.90722076e-03, - 4.51723727e-03, - 4.14674875e-03, - 3.81695136e-03, - 3.51640474e-03, - 3.22513571e-03, - 2.95911995e-03, - 2.73056190e-03, - 2.51716771e-03, - 2.31576802e-03, - 2.12938206e-03, - 1.96285779e-03, - 1.80590850e-03, - 1.66301586e-03, - 1.52912399e-03, - 1.41144832e-03, - 1.29986696e-03, - 1.20219471e-03, - 1.10136923e-03, - 1.01839892e-03, - 9.39143113e-04, - 8.64814579e-04, - 7.96703989e-04, - 7.35063247e-04, - 6.78739795e-04, - 6.24226207e-04, - 5.76731522e-04, - 5.31877313e-04, - 4.91432490e-04, - 4.53724222e-04, - 4.17097140e-04, - 3.84883668e-04, - 3.56053519e-04, - 3.28615077e-04, - 3.03171707e-04, - 2.80212744e-04, - 2.58806676e-04, - 2.39233542e-04, - 2.20830164e-04, - 2.03703468e-04, - 1.88784094e-04, - 1.74177494e-04, - 1.61290974e-04, - 1.48847234e-04, - 1.37594011e-04, - 1.27396903e-04, - 1.17512734e-04, - 1.08817617e-04, - 1.00626721e-04, - 9.30799915e-05, - 8.58308478e-05, - 7.97150769e-05, - 7.36523610e-05, - 6.84294010e-05, - 6.32182918e-05, - 5.85268482e-05, - 5.40528453e-05, - 5.02368808e-05, - 4.65180897e-05, - 4.30575884e-05, - 3.98835225e-05, - 3.70504980e-05, - 3.42857719e-05, - 3.18132215e-05, - 2.95287409e-05, - 2.73577012e-05, - 2.53724318e-05, - 2.35572592e-05, - 2.18844303e-05, - 2.02605458e-05, - 1.88596686e-05, - 1.75580663e-05, - 1.62935792e-05, - 1.51211989e-05, - 1.40303609e-05, - 1.30832864e-05, - 1.21382107e-05, - 1.12829800e-05, - 1.05060755e-05, - ], - ), - coords={ - 'birth_time': sc.array( - dims=['birth_time'], - values=[ - 1.24378109e-05, - 3.73134328e-05, - 6.21890547e-05, - 8.70646766e-05, - 1.11940299e-04, - 1.36815920e-04, - 1.61691542e-04, - 1.86567164e-04, - 2.11442786e-04, - 2.36318408e-04, - 2.61194030e-04, - 2.86069652e-04, - 3.10945274e-04, - 3.35820896e-04, - 3.60696517e-04, - 3.85572139e-04, - 4.10447761e-04, - 4.35323383e-04, - 4.60199005e-04, - 4.85074627e-04, - 5.09950249e-04, - 5.34825871e-04, - 5.59701493e-04, - 5.84577114e-04, - 6.09452736e-04, - 6.34328358e-04, - 6.59203980e-04, - 6.84079602e-04, - 7.08955224e-04, - 7.33830846e-04, - 7.58706468e-04, - 7.83582090e-04, - 8.08457711e-04, - 8.33333333e-04, - 8.58208955e-04, - 8.83084577e-04, - 9.07960199e-04, - 9.32835821e-04, - 9.57711443e-04, - 9.82587065e-04, - 1.00746269e-03, - 1.03233831e-03, - 1.05721393e-03, - 1.08208955e-03, - 1.10696517e-03, - 1.13184080e-03, - 1.15671642e-03, - 1.18159204e-03, - 1.20646766e-03, - 1.23134328e-03, - 1.25621891e-03, - 1.28109453e-03, - 1.30597015e-03, - 1.33084577e-03, - 1.35572139e-03, - 1.38059701e-03, - 1.40547264e-03, - 1.43034826e-03, - 1.45522388e-03, - 1.48009950e-03, - 1.50497512e-03, - 1.52985075e-03, - 1.55472637e-03, - 1.57960199e-03, - 1.60447761e-03, - 1.62935323e-03, - 1.65422886e-03, - 1.67910448e-03, - 1.70398010e-03, - 1.72885572e-03, - 1.75373134e-03, - 1.77860697e-03, - 1.80348259e-03, - 1.82835821e-03, - 1.85323383e-03, - 1.87810945e-03, - 1.90298507e-03, - 1.92786070e-03, - 1.95273632e-03, - 1.97761194e-03, - 2.00248756e-03, - 2.02736318e-03, - 2.05223881e-03, - 2.07711443e-03, - 2.10199005e-03, - 2.12686567e-03, - 2.15174129e-03, - 2.17661692e-03, - 2.20149254e-03, - 2.22636816e-03, - 2.25124378e-03, - 2.27611940e-03, - 2.30099502e-03, - 2.32587065e-03, - 2.35074627e-03, - 2.37562189e-03, - 2.40049751e-03, - 2.42537313e-03, - 2.45024876e-03, - 2.47512438e-03, - 2.50000000e-03, - 2.52487562e-03, - 2.54975124e-03, - 2.57462687e-03, - 2.59950249e-03, - 2.62437811e-03, - 2.64925373e-03, - 2.67412935e-03, - 2.69900498e-03, - 2.72388060e-03, - 2.74875622e-03, - 2.77363184e-03, - 2.79850746e-03, - 2.82338308e-03, - 2.84825871e-03, - 2.87313433e-03, - 2.89800995e-03, - 2.92288557e-03, - 2.94776119e-03, - 2.97263682e-03, - 2.99751244e-03, - 3.02238806e-03, - 3.04726368e-03, - 3.07213930e-03, - 3.09701493e-03, - 3.12189055e-03, - 3.14676617e-03, - 3.17164179e-03, - 3.19651741e-03, - 3.22139303e-03, - 3.24626866e-03, - 3.27114428e-03, - 3.29601990e-03, - 3.32089552e-03, - 3.34577114e-03, - 3.37064677e-03, - 3.39552239e-03, - 3.42039801e-03, - 3.44527363e-03, - 3.47014925e-03, - 3.49502488e-03, - 3.51990050e-03, - 3.54477612e-03, - 3.56965174e-03, - 3.59452736e-03, - 3.61940299e-03, - 3.64427861e-03, - 3.66915423e-03, - 3.69402985e-03, - 3.71890547e-03, - 3.74378109e-03, - 3.76865672e-03, - 3.79353234e-03, - 3.81840796e-03, - 3.84328358e-03, - 3.86815920e-03, - 3.89303483e-03, - 3.91791045e-03, - 3.94278607e-03, - 3.96766169e-03, - 3.99253731e-03, - 4.01741294e-03, - 4.04228856e-03, - 4.06716418e-03, - 4.09203980e-03, - 4.11691542e-03, - 4.14179104e-03, - 4.16666667e-03, - 4.19154229e-03, - 4.21641791e-03, - 4.24129353e-03, - 4.26616915e-03, - 4.29104478e-03, - 4.31592040e-03, - 4.34079602e-03, - 4.36567164e-03, - 4.39054726e-03, - 4.41542289e-03, - 4.44029851e-03, - 4.46517413e-03, - 4.49004975e-03, - 4.51492537e-03, - 4.53980100e-03, - 4.56467662e-03, - 4.58955224e-03, - 4.61442786e-03, - 4.63930348e-03, - 4.66417910e-03, - 4.68905473e-03, - 4.71393035e-03, - 4.73880597e-03, - 4.76368159e-03, - 4.78855721e-03, - 4.81343284e-03, - 4.83830846e-03, - 4.86318408e-03, - 4.88805970e-03, - 4.91293532e-03, - 4.93781095e-03, - 4.96268657e-03, - 4.98756219e-03, - ], - unit='s', - ) - }, -) - -wavelength = sc.DataArray( - data=sc.array( - dims=['wavelength'], - values=[ - 2.47910521e-02, - 3.15394565e-02, - 2.46673832e-02, - 4.24700432e-02, - 6.22005065e-02, - 6.41430908e-02, - 5.63970675e-02, - 4.79885540e-02, - 4.28010531e-02, - 4.17366412e-02, - 4.51519899e-02, - 5.35348380e-02, - 6.07649341e-02, - 5.33537571e-02, - 4.52415054e-02, - 3.85305979e-02, - 3.29632299e-02, - 2.82808267e-02, - 2.43136951e-02, - 2.09961870e-02, - 1.81474939e-02, - 1.57517036e-02, - 1.36839501e-02, - 1.19127479e-02, - 1.04094236e-02, - 9.09390901e-03, - 7.97376111e-03, - 7.03987735e-03, - 6.19641665e-03, - 5.48516928e-03, - 4.84954606e-03, - 4.31560357e-03, - 3.84683395e-03, - 3.43486380e-03, - 3.07473495e-03, - 2.75943204e-03, - 2.48705338e-03, - 2.24190868e-03, - 2.02792878e-03, - 1.83647522e-03, - 1.66751194e-03, - 1.52037927e-03, - 1.38568580e-03, - 1.26625902e-03, - 1.15985092e-03, - 1.06326666e-03, - 9.77402425e-04, - 8.97152751e-04, - 8.28473264e-04, - 7.65517629e-04, - 7.06753047e-04, - 6.53870845e-04, - 6.05626744e-04, - 5.61775168e-04, - 5.21407086e-04, - 4.83706064e-04, - 4.50096590e-04, - 4.18595081e-04, - 3.89942860e-04, - 3.63082390e-04, - 3.38196875e-04, - 3.16116766e-04, - 2.94433976e-04, - 2.74611217e-04, - 2.56804279e-04, - 2.39866263e-04, - 2.24103151e-04, - 2.09428428e-04, - 1.96024411e-04, - 1.83244400e-04, - 1.71452766e-04, - 1.60372237e-04, - 1.50247463e-04, - 1.40620774e-04, - 1.31338349e-04, - 1.23209023e-04, - 1.15219833e-04, - 1.07938245e-04, - 1.01217712e-04, - 9.47807301e-05, - 8.87686984e-05, - 8.31031332e-05, - 7.78786212e-05, - 7.29971694e-05, - 6.83232132e-05, - 6.40614634e-05, - 6.00522902e-05, - 5.63009205e-05, - 5.26437650e-05, - 4.93458816e-05, - 4.62942251e-05, - 4.33867986e-05, - 4.06646952e-05, - 3.80409296e-05, - 3.57000207e-05, - 3.34181377e-05, - 3.13558896e-05, - 2.93784366e-05, - 2.75483925e-05, - 2.57593945e-05, - 2.41535573e-05, - ], - ), - coords={ - 'wavelength': sc.array( - dims=['wavelength'], - values=[ - 1.98514851e-01, - 3.95544554e-01, - 5.92574257e-01, - 7.89603960e-01, - 9.86633663e-01, - 1.18366337e00, - 1.38069307e00, - 1.57772277e00, - 1.77475248e00, - 1.97178218e00, - 2.16881188e00, - 2.36584158e00, - 2.56287129e00, - 2.75990099e00, - 2.95693069e00, - 3.15396040e00, - 3.35099010e00, - 3.54801980e00, - 3.74504950e00, - 3.94207921e00, - 4.13910891e00, - 4.33613861e00, - 4.53316832e00, - 4.73019802e00, - 4.92722772e00, - 5.12425743e00, - 5.32128713e00, - 5.51831683e00, - 5.71534653e00, - 5.91237624e00, - 6.10940594e00, - 6.30643564e00, - 6.50346535e00, - 6.70049505e00, - 6.89752475e00, - 7.09455446e00, - 7.29158416e00, - 7.48861386e00, - 7.68564356e00, - 7.88267327e00, - 8.07970297e00, - 8.27673267e00, - 8.47376238e00, - 8.67079208e00, - 8.86782178e00, - 9.06485149e00, - 9.26188119e00, - 9.45891089e00, - 9.65594059e00, - 9.85297030e00, - 1.00500000e01, - 1.02470297e01, - 1.04440594e01, - 1.06410891e01, - 1.08381188e01, - 1.10351485e01, - 1.12321782e01, - 1.14292079e01, - 1.16262376e01, - 1.18232673e01, - 1.20202970e01, - 1.22173267e01, - 1.24143564e01, - 1.26113861e01, - 1.28084158e01, - 1.30054455e01, - 1.32024752e01, - 1.33995050e01, - 1.35965347e01, - 1.37935644e01, - 1.39905941e01, - 1.41876238e01, - 1.43846535e01, - 1.45816832e01, - 1.47787129e01, - 1.49757426e01, - 1.51727723e01, - 1.53698020e01, - 1.55668317e01, - 1.57638614e01, - 1.59608911e01, - 1.61579208e01, - 1.63549505e01, - 1.65519802e01, - 1.67490099e01, - 1.69460396e01, - 1.71430693e01, - 1.73400990e01, - 1.75371287e01, - 1.77341584e01, - 1.79311881e01, - 1.81282178e01, - 1.83252475e01, - 1.85222772e01, - 1.87193069e01, - 1.89163366e01, - 1.91133663e01, - 1.93103960e01, - 1.95074257e01, - 1.97044554e01, - 1.99014851e01, - ], - unit='angstrom', - ) - }, -) - -frequency = 14.0 * sc.Unit('Hz') diff --git a/src/tof/facilities/ess_pulse/__init__.py b/src/tof/facilities/ess_pulse/__init__.py new file mode 100644 index 0000000..8db86ae --- /dev/null +++ b/src/tof/facilities/ess_pulse/__init__.py @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2025 Scipp contributors (https://github.com/scipp) + +""" +This module exists only to provide retro-compatibility for `essreduce` and +potentially other downstream packages that used to import pulse parameters from the old +`tof.facilities.ess_pulse` module. + +This should be phased out in future releases. +""" + +from ..ess.pulse_profile import pulse + +frequency = pulse.frequency +birth_time = pulse.birth_time +wavelength = pulse.wavelength + +__all__ = ["birth_time", "frequency", "wavelength"] + +del pulse diff --git a/src/tof/source.py b/src/tof/source.py index af7d848..33cdfe8 100644 --- a/src/tof/source.py +++ b/src/tof/source.py @@ -205,16 +205,16 @@ def __init__( wmax: sc.Variable | None = None, seed: int | None = None, ): - self.facility = facility + self.facility = facility.lower() if facility is not None else None self.neutrons = int(neutrons) self.pulses = int(pulses) self.data = None - if facility is not None: + if self.facility is not None: try: - facility_params = import_module(f"tof.facilities.{facility}_pulse") + facility_params = import_module(f"tof.facilities.{self.facility}").pulse except ModuleNotFoundError as e: - raise ValueError(f"Facility '{facility}' not found.") from e + raise ValueError(f"Facility '{self.facility}' not found.") from e self.frequency = facility_params.frequency pulse_params = _make_pulses( neutrons=self.neutrons, diff --git a/src/tof/utils.py b/src/tof/utils.py index c1e51f5..45eaf46 100644 --- a/src/tof/utils.py +++ b/src/tof/utils.py @@ -91,3 +91,23 @@ def one_mask( class Plot: ax: plt.Axes fig: plt.Figure + + +@dataclass +class PulseProfile: + """ + Parameters of a neutron pulse (typically from a neutron facility). + + Parameters + ---------- + frequency: + Frequency of the pulse in Hz. + birth_time: + Probability distribution of neutrons in time within a single pulse. + wavelength: + Probability distribution of neutrons in wavelength within a single pulse. + """ + + frequency: sc.Variable + birth_time: sc.DataArray + wavelength: sc.DataArray diff --git a/tests/source_test.py b/tests/source_test.py index 5c8d23c..2d6fb7a 100644 --- a/tests/source_test.py +++ b/tests/source_test.py @@ -22,6 +22,15 @@ def test_ess_pulse(): assert (wavs[:150].sum() > 1.5 * wavs[150:].sum()).value +@pytest.mark.parametrize('facility', ['ESS', 'Ess']) +def test_ess_pulse_uppercase(facility): + source = tof.Source(facility=facility, neutrons=100_000) + assert source.neutrons == 100_000 + assert sc.identical(source.frequency, sc.scalar(14.0, unit="Hz")) + assert source.pulses == 1 + assert source.facility == 'ess' + + def test_creation_from_neutrons(): birth_times = sc.array(dims=['event'], values=[1000.0, 1500.0, 2000.0], unit='us') wavelengths = sc.array(dims=['event'], values=[1.0, 5.0, 10.0], unit='angstrom')