diff --git a/docs/user-guide/dream/dream-visualize-absorption.ipynb b/docs/user-guide/dream/dream-visualize-absorption.ipynb new file mode 100644 index 0000000..fe2b275 --- /dev/null +++ b/docs/user-guide/dream/dream-visualize-absorption.ipynb @@ -0,0 +1,227 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Visualize effect of absorption\n", + "\n", + "This notebook is an example of how to use the `compute_transmission_map` function to visualize the effect of (sample) absorption on the intensity in the detector.\n", + "\n", + "Note that the `compute_transmission_map` function only accounts for the effect of absorption. The measured intensity in the detector can also be impacted by other effects, such as the effect of the solid angle being different for different detector pixels.\n", + "\n", + "For more details, see https://scipp.github.io/scippneutron/user-guide/absorption-correction.html." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "import scipp as sc\n", + "\n", + "from plopp.graphics import Camera\n", + "\n", + "from ess import dream\n", + "import ess.dream.data # noqa: F401\n", + "\n", + "from scippneutron.absorption import compute_transmission_map\n", + "from scippneutron.absorption.cylinder import Cylinder\n", + "from scippneutron.absorption.material import Material\n", + "from scippneutron.atoms import ScatteringParams" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Load the data\n", + "\n", + "We load a dataset from a Geant4 simulation only to get access to the detector geometry." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "dg = dream.io.load_geant4_csv(dream.data.get_path(\"data_dream0_new_hkl_Si_pwd.csv.zip\"))[\"instrument\"]\n", + "dg = sc.DataGroup({key: detector[\"events\"] for key, detector in dg.items()})\n", + "dg.keys()" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "## Inhomogeneity from absorption in mantle detector\n", + "\n", + "If the sample absorbs or scatters a large fraction of the incoming neutrons, the intensity in the detector will vary even if the scattering is inhomogeneous.\n" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "### Rod-like sample oriented along x-axis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "# The material determines the rate of absorption and scattering\n", + "vanadium = Material(\n", + " scattering_params=ScatteringParams.for_isotope('V'),\n", + " effective_sample_number_density=sc.scalar(0.07192, unit='1/angstrom**3')\n", + ")\n", + "# The shape determines the shape and the orientation of the sample\n", + "rod_shape = Cylinder(\n", + " radius=sc.scalar(1, unit='cm'),\n", + " height=(height := sc.scalar(5., unit='cm')),\n", + " # Cylinder is oriented along the x-axis\n", + " symmetry_line=(symmetry_line := sc.vector([1, 0, 0])),\n", + " center_of_base=-height * symmetry_line / 2,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "transmission_fraction_mantle = compute_transmission_map(\n", + " rod_shape,\n", + " vanadium,\n", + " beam_direction=sc.vector([0, 0, 1]),\n", + " wavelength=sc.linspace('wavelength', 4, 8, 20, unit='angstrom'),\n", + " # To make it faster, don't compute the transmission fraction for every detector pixel, it's not necessary for the visualization.\n", + " detector_position=dg['mantle'].coords['position']['strip', ::4]['wire', ::2].copy(),\n", + " quadrature_kind='cheap',\n", + ")\n", + "\n", + "# The visualization expects the `position` coord to denote detector positions, and `wavelength` to be the last dimension.\n", + "transmission_fraction_mantle.coords['position'] = transmission_fraction_mantle.coords.pop('detector_position')\n", + "dream.instrument_view(transmission_fraction_mantle.transpose((*set(transmission_fraction_mantle.dims) - {'wavelength'}, 'wavelength')), dim='wavelength', camera=Camera(position=sc.vector((-2, 2, -2), unit='m'), look_at=sc.vector((1, -1/3, 1/2), unit='m')))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "transmission_fraction_mantle" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "## Disk-like sample oriented along x-axis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "disk_shape = Cylinder(\n", + " radius=sc.scalar(5, unit='cm'),\n", + " height=(height := sc.scalar(1., unit='cm')),\n", + " # Cylinder is oriented along the x-axis\n", + " symmetry_line=(symmetry_line := sc.vector([1, 0, 0])),\n", + " center_of_base=-height * symmetry_line / 2,\n", + ")\n", + "\n", + "transmission_fraction_mantle = compute_transmission_map(\n", + " disk_shape,\n", + " vanadium,\n", + " beam_direction=sc.vector([0, 0, 1]),\n", + " wavelength=sc.linspace('wavelength', 4, 8, 20, unit='angstrom'),\n", + " # To make it faster, don't compute the transmission fraction for every detector pixel, it's not necessary for the visualization.\n", + " detector_position=dg['mantle'].coords['position']['strip', ::4]['wire', ::2].copy(),\n", + " quadrature_kind='cheap',\n", + ")\n", + "\n", + "# The visualization expects the `position` coord to denote detector positions, and `wavelength` to be the last dimension.\n", + "transmission_fraction_mantle.coords['position'] = transmission_fraction_mantle.coords.pop('detector_position')\n", + "dream.instrument_view(transmission_fraction_mantle.transpose((*set(transmission_fraction_mantle.dims) - {'wavelength'}, 'wavelength')), dim='wavelength', camera=Camera(position=sc.vector((-2, 2, -2), unit='m'), look_at=sc.vector((1, -1/3, 1/2), unit='m')))" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Does absorption influence the intensity in the endcap detector?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "transmission_fraction_endcap = compute_transmission_map(\n", + " rod_shape,\n", + " vanadium,\n", + " beam_direction=sc.vector([0, 0, 1]),\n", + " wavelength=sc.linspace('wavelength', 4, 8, 20, unit='angstrom'),\n", + " detector_position=dg['endcap_backward'].coords['position']['strip', 0].copy(),\n", + " quadrature_kind='cheap',\n", + ")\n", + "\n", + "transmission_fraction_endcap.coords['position'] = transmission_fraction_endcap.coords.pop('detector_position')\n", + "dream.instrument_view(transmission_fraction_endcap.transpose((*set(transmission_fraction_endcap.dims) - {'wavelength'}, 'wavelength')), dim='wavelength', camera=Camera(position=sc.vector((-1, 1, -3), unit='m'), look_at=sc.vector((0.3, -0.3, 1), unit='m')))" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "As expected the impact of absorption on on the intensity in the endcap detector is close to uniform." + ] + } + ], + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/user-guide/dream/index.md b/docs/user-guide/dream/index.md index 5a5281f..24fe156 100644 --- a/docs/user-guide/dream/index.md +++ b/docs/user-guide/dream/index.md @@ -9,4 +9,5 @@ dream-data-reduction dream-instrument-view workflow-widget-dream dream-detector-diagnostics +dream-visualize-absorption ```