diff --git a/docs/_static/thumbnails/estia_advanced_mcstas_reduction_dark.svg b/docs/_static/thumbnails/estia_advanced_mcstas_reduction_dark.svg new file mode 100644 index 00000000..1afc3dc3 --- /dev/null +++ b/docs/_static/thumbnails/estia_advanced_mcstas_reduction_dark.svg @@ -0,0 +1,775 @@ + + + + + + + + 2025-06-10T13:10:47.066434 + image/svg+xml + + + Matplotlib v3.10.1, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_static/thumbnails/estia_advanced_mcstas_reduction_light.svg b/docs/_static/thumbnails/estia_advanced_mcstas_reduction_light.svg new file mode 100644 index 00000000..facd4d33 --- /dev/null +++ b/docs/_static/thumbnails/estia_advanced_mcstas_reduction_light.svg @@ -0,0 +1,775 @@ + + + + + + + + 2025-06-10T13:10:44.276942 + image/svg+xml + + + Matplotlib v3.10.1, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_static/thumbnails/estia_mcstas_reduction_dark.svg b/docs/_static/thumbnails/estia_mcstas_reduction_dark.svg new file mode 100644 index 00000000..5ce44ea6 --- /dev/null +++ b/docs/_static/thumbnails/estia_mcstas_reduction_dark.svg @@ -0,0 +1,1482 @@ + + + + + + + + 2025-06-10T13:10:40.148754 + image/svg+xml + + + Matplotlib v3.10.1, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_static/thumbnails/estia_mcstas_reduction_light.svg b/docs/_static/thumbnails/estia_mcstas_reduction_light.svg new file mode 100644 index 00000000..96b9f0f4 --- /dev/null +++ b/docs/_static/thumbnails/estia_mcstas_reduction_light.svg @@ -0,0 +1,1482 @@ + + + + + + + + 2025-06-10T13:10:39.360461 + image/svg+xml + + + Matplotlib v3.10.1, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/index.md b/docs/index.md index a5b34110..8a43b807 100644 --- a/docs/index.md +++ b/docs/index.md @@ -28,23 +28,26 @@

-## Overview +## Quick links -This documentation is under construction. -See the [Amor data reduction](examples/amor) example for a quick start. +::::{grid} 3 -## Table of contents +:::{grid-item-card} ESTIA +:link: user-guide/estia/index.md -```{toctree} ---- -maxdepth: 2 ---- +::: -user-guide/index -api-reference/index -developer/index -about/index -``` +:::{grid-item-card} Amor +:link: user-guide/amor/index.md + +::: + +::::{grid-item-card} Offspec +:link: user-guide/offspec/index.md + +::: + +:::: :::{include} user-guide/installation.md :heading-offset: 1 @@ -54,3 +57,14 @@ about/index - If you have questions that are not answered by these documentation pages, ask on [discussions](https://github.com/scipp/essreflectometry/discussions). Please include a self-contained reproducible example if possible. - Report bugs (including unclear, missing, or wrong documentation!), suggest features or view the source code [on GitHub](https://github.com/scipp/essreflectometry). + +```{toctree} +--- +hidden: +--- + +user-guide/index +api-reference/index +developer/index +about/index +``` diff --git a/docs/user-guide/amor/index.md b/docs/user-guide/amor/index.md index e379a54a..2c0d7023 100644 --- a/docs/user-guide/amor/index.md +++ b/docs/user-guide/amor/index.md @@ -6,4 +6,5 @@ maxdepth: 1 --- amor-reduction compare-to-eos +workflow-widget ``` diff --git a/docs/user-guide/amor/gui.ipynb b/docs/user-guide/amor/workflow-widget.ipynb similarity index 89% rename from docs/user-guide/amor/gui.ipynb rename to docs/user-guide/amor/workflow-widget.ipynb index c3566f08..696313b6 100644 --- a/docs/user-guide/amor/gui.ipynb +++ b/docs/user-guide/amor/workflow-widget.ipynb @@ -1,9 +1,17 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Workflow widget" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "0", + "id": "1", "metadata": {}, "outputs": [], "source": [ @@ -14,7 +22,7 @@ { "cell_type": "code", "execution_count": null, - "id": "1", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -24,7 +32,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -34,12 +42,12 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "4", "metadata": {}, "outputs": [], "source": [ "gui = AmorBatchReductionGUI()\n", - "gui.widget\n" + "gui.widget" ] } ], diff --git a/docs/user-guide/estia/estia-advanced-mcstas-reduction.ipynb b/docs/user-guide/estia/estia-advanced-mcstas-reduction.ipynb new file mode 100644 index 00000000..f1103e44 --- /dev/null +++ b/docs/user-guide/estia/estia-advanced-mcstas-reduction.ipynb @@ -0,0 +1,520 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# ESTIA advanced data reduction\n", + "\n", + "- Audience: Instrument (data) scientists, instrument users\n", + "- Prerequisites: Basic knowledge of [Scipp](https://scipp.github.io/), [Sciline](https://scipp.github.io/sciline/)\n", + "\n", + "This notebook builds on the [basic ESTIA reduction guide](./estia-mcstas-reduction.rst); you should read that first.\n", + "This advanced guide demonstrates how the workflow can be used to make diagnostics plots and options for processing multiple files together.\n", + "e the default loader with the `load_mcstas_events` provider.\n", + "\n", + "The data is available through the ESSreflectometry package but accessing it requires the pooch package.\n", + "If you get an error about a missing module pooch, you can install it with `!pip install pooch`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import NewType\n", + "\n", + "import pandas as pd\n", + "import sciline\n", + "import scipp as sc\n", + "\n", + "from ess.estia.data import estia_mcstas_example, estia_mcstas_groundtruth\n", + "from ess.estia import EstiaMcStasWorkflow\n", + "from ess.reflectometry.types import *\n", + "from ess.reflectometry.figures import wavelength_z_figure, wavelength_theta_figure, q_theta_figure" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Process multiple files\n", + "\n", + "To apply the ESTIA workflow to multiple files, we could simply write a for-loop like this:\n", + "```python\n", + "results = []\n", + "for filename in estia_mcstas_example('Ni/Ti-multilayer'):\n", + " wf[Filename[SampleRun]] = filename\n", + " results.append(wf.compute(ReflectivityOverQ))\n", + "```\n", + "However, this would re-load and re-process the reference measurement for every sample file.\n", + "Instead, we will use Sciline's [map-reduce](https://scipp.github.io/sciline/user-guide/parameter-tables.html) feature to map the workflow over several files and merge the individual results.\n", + "\n", + "First, we set up the workflow in almost the same way as in the [basic ESTIA reduction guide](./estia-mcstas-reduction.rst), except that we do *not* set a filename for the sample run." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "wf = EstiaMcStasWorkflow()\n", + "wf[Filename[ReferenceRun]] = estia_mcstas_example('reference')\n", + "\n", + "wf[YIndexLimits] = sc.scalar(35), sc.scalar(64)\n", + "wf[ZIndexLimits] = sc.scalar(0), sc.scalar(48 * 32)\n", + "wf[BeamDivergenceLimits] = sc.scalar(-0.75, unit='deg'), sc.scalar(0.75, unit='deg')\n", + "\n", + "wf[WavelengthBins] = sc.geomspace('wavelength', 3.5, 12, 2001, unit='angstrom')\n", + "wf[QBins] = 1000\n", + "\n", + "# There is no proton current data in the McStas files, here we just add some fake proton current\n", + "# data to make the workflow run.\n", + "wf[ProtonCurrent[SampleRun]] = sc.DataArray(\n", + " sc.array(dims=('time',), values=[]),\n", + " coords={'time': sc.array(dims=('time',), values=[], unit='s')})\n", + "wf[ProtonCurrent[ReferenceRun]] = sc.DataArray(\n", + " sc.array(dims=('time',), values=[]),\n", + " coords={'time': sc.array(dims=('time',), values=[], unit='s')})" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "## Reflectivity vs. Q\n", + "\n", + "In this section, we compute the reflectivity as a function of momentum transfer for a number of files with the same sample at different sample rotations.\n", + "We will `map` the workflow over the ames for each file and `reduce` the results into a single data group.\n", + "\n", + "Starting with a parameter table of filenames, we `map` the workflow:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "param_table = pd.DataFrame({\n", + " Filename[SampleRun]: estia_mcstas_example('Ni/Ti-multilayer')\n", + "}).rename_axis(index='sample_rotation')\n", + "\n", + "# Make a copy to preserve the original `wf`.\n", + "multi_file_workflow = wf.copy()\n", + "mapped = multi_file_workflow[ReflectivityOverQ].map(param_table)" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "Then we merge the reflectivity data for all files into a single data group.\n", + "We use the sample rotation as key because in this example, each file contains data at a different rotation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "def combine_measurements(*measurements: sc.DataArray) -> sc.DataGroup[sc.DataArray]:\n", + " return sc.DataGroup({\n", + " f\"{da.coords['sample_rotation']:c}\": da for da in measurements\n", + " })\n", + "\n", + "multi_file_workflow[ReflectivityOverQ] = mapped.reduce(\n", + " func=combine_measurements\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "The graph now indicates that everything that depends on `SampleRun` data has been mapped: (Names shown in boxes as opposed to rectangles have been mapped.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "multi_file_workflow.visualize(\n", + " ReflectivityOverQ,\n", + " graph_attr={\"rankdir\": \"LR\"},\n", + " compact=True,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "10", + "metadata": {}, + "source": [ + "We compute the reflectivity for each file and get a data group:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "reflectivities = multi_file_workflow.compute(ReflectivityOverQ)\n", + "reflectivities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "reflectivities.hist().plot(norm='log', vmin=1e-8)" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "### Reflectivity vs. Q with custom mask\n", + "\n", + "The data is very noisy in some $Q$ bins.\n", + "We can clean up the plot by removing or masking those bins.\n", + "The variance of the reference measurement is a good measure for how noisy the final data will be, so we use that to insert a mask into the reflectivity curve.\n", + "This means that we need to request `Reference` to construct the mask.\n", + "But since the value of `Reference` is different for each sample run, we need to insert a new step into the workflow and include it in the `map`-operation.\n", + "See also [Replacing providers](https://scipp.github.io/sciline/recipes/replacing-providers.html) for a more general guide.\n", + "\n", + "We define a custom type (`MaskedReflectivityOverQ`) and a provider to apply the mask:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "MaskedReflectivityOverQ = NewType('MaskedReflectivityOverQ', sc.DataArray)\n", + "\n", + "def mask_noisy_reference(\n", + " reflectivity: ReflectivityOverQ,\n", + " reference: Reference,\n", + ") -> MaskedReflectivityOverQ:\n", + " ref = reference.hist(Q=reflectivity.coords['Q'])\n", + " return reflectivity.assign_masks(\n", + " noisy_reference= sc.stddevs(ref).data > 0.3 * ref.data\n", + " )\n", + "\n", + "wf.insert(mask_noisy_reference)" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "Note that the code above inserts into `wf`, i.e., the unmapped workflow.\n", + "Now map-reduce the workflow again but in the new `MaskedReflectivityOverQ` type:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "multi_file_workflow = wf.copy()\n", + "param_table = pd.DataFrame({\n", + " Filename[SampleRun]: estia_mcstas_example('Ni/Ti-multilayer')\n", + "}).rename_axis(index='run')\n", + "mapped = multi_file_workflow[MaskedReflectivityOverQ].map(param_table)\n", + "multi_file_workflow[MaskedReflectivityOverQ] = mapped.reduce(\n", + " func=combine_measurements\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "The graph is the same as before except that only `MaskedReflectivityOverQ` is available as a final, unmapped type:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "multi_file_workflow.visualize(\n", + " MaskedReflectivityOverQ,\n", + " graph_attr={\"rankdir\": \"LR\"},\n", + " compact=True,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "Compute the results:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "reflectivities = multi_file_workflow.compute(MaskedReflectivityOverQ)\n", + "reflectivities" + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, + "source": [ + "To plot, we histogram and set values in masked bins to NaN to make the plot easier to read:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "histograms = reflectivities.hist().apply(lambda R: R.assign(\n", + " sc.where(\n", + " R.masks['noisy_reference'],\n", + " sc.scalar(float('nan'), unit=R.unit),\n", + " R.data\n", + " )\n", + "))" + ] + }, + { + "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "Since this data is from a McStas simulation, we know the true reflectivity of the sample.\n", + "We plot it alongside the 'measured' reflectivity for comparison:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "ground_truth = estia_mcstas_groundtruth('Ni/Ti-multilayer')\n", + "sc.plot({'ground_truth': ground_truth} | dict(histograms), norm='log', vmin=1e-8, c={'ground_truth': 'k'})" + ] + }, + { + "cell_type": "markdown", + "id": "25", + "metadata": {}, + "source": [ + "### Other projections\n", + "\n", + "ESSreflectometry provides functions for plotting the intensity as a function of different variables.\n", + "Note that these plots can also be produced by the workflow directly, see [Draw diagnostics plots with the workflow](#Draw-diagnostics-plots-with-the-workflow).\n", + "But here, we make them manually to more easily combine data from all files.\n", + "\n", + "The plots consume data that is provided by the `Sample` key in the workflow.\n", + "We could map-reduce the workflow in that key instead of in `MaskedReflectivityOverQ` to compute `Sample` for each file.\n", + "But for simplicity, we use [sciline.compute_mapped](https://scipp.github.io/sciline/generated/functions/sciline.compute_mapped.html#sciline.compute_mapped) to compute the results for all files:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26", + "metadata": {}, + "outputs": [], + "source": [ + "results = list(sciline.compute_mapped(multi_file_workflow, Sample))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27", + "metadata": {}, + "outputs": [], + "source": [ + "wavelength_z_figure(results[3], wavelength_bins=400)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28", + "metadata": {}, + "outputs": [], + "source": [ + "wavelength_theta_figure(\n", + " results,\n", + " wavelength_bins=200,\n", + " theta_bins=200,\n", + " q_edges_to_display=[sc.scalar(0.016, unit='1/angstrom'), sc.scalar(0.19, unit='1/angstrom')]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29", + "metadata": {}, + "outputs": [], + "source": [ + "q_theta_figure(results, q_bins=200, theta_bins=200)" + ] + }, + { + "cell_type": "markdown", + "id": "30", + "metadata": {}, + "source": [ + "## Draw diagnostics plots with the workflow\n", + "\n", + "The ESTIA workflow can produce a number of plots directly to quickly visualize the data in different forms.\n", + "Those plots can be combined into a single `ReflectivityDiagnosticsView` or constructed separately.\n", + "Here, we use the combined `ReflectivityDiagnosticsView` but you can also request one or more of the individual views (see also the graph below):\n", + "- `ReflectivityOverQ`\n", + "- `ReflectivityOverZW`\n", + "- `WavelengthThetaFigure`\n", + "- `WavelengthZIndexFigure`\n", + "\n", + "These plots correspond to the figures in the [Other projections](#Other-projections) sections above but show only data from a single file.\n", + "\n", + "We construct the workflow as in the [basic ESTIA reduction guide](./estia-mcstas-reduction.rst) but also add `ThetaBins` for the sample run to control the plots:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31", + "metadata": {}, + "outputs": [], + "source": [ + "wf = EstiaMcStasWorkflow()\n", + "wf[Filename[SampleRun]] = estia_mcstas_example('Ni/Ti-multilayer')[3]\n", + "wf[Filename[ReferenceRun]] = estia_mcstas_example('reference')\n", + "\n", + "wf[YIndexLimits] = sc.scalar(35), sc.scalar(64)\n", + "wf[ZIndexLimits] = sc.scalar(0), sc.scalar(48 * 32)\n", + "wf[BeamDivergenceLimits] = sc.scalar(-0.75, unit='deg'), sc.scalar(0.75, unit='deg')\n", + "\n", + "wf[WavelengthBins] = sc.geomspace('wavelength', 3.5, 12, 2001, unit='angstrom')\n", + "wf[ThetaBins[SampleRun]] = 200\n", + "wf[QBins] = 1000\n", + "\n", + "# There is no proton current data in the McStas files, here we just add some fake proton current\n", + "# data to make the workflow run.\n", + "wf[ProtonCurrent[SampleRun]] = sc.DataArray(\n", + " sc.array(dims=('time',), values=[]),\n", + " coords={'time': sc.array(dims=('time',), values=[], unit='s')})\n", + "wf[ProtonCurrent[ReferenceRun]] = sc.DataArray(\n", + " sc.array(dims=('time',), values=[]),\n", + " coords={'time': sc.array(dims=('time',), values=[], unit='s')})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32", + "metadata": {}, + "outputs": [], + "source": [ + "wf.visualize(ReflectivityDiagnosticsView, graph_attr={'rankdir':'LR'})" + ] + }, + { + "cell_type": "markdown", + "id": "33", + "metadata": {}, + "source": [ + "We request the figure like any other data in the workflow:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34", + "metadata": {}, + "outputs": [], + "source": [ + "view = wf.compute(ReflectivityDiagnosticsView)" + ] + }, + { + "cell_type": "markdown", + "id": "35", + "metadata": {}, + "source": [ + "And finally, we draw it: (this `view` object can be rendered in a Jupyter notebook.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36", + "metadata": {}, + "outputs": [], + "source": [ + "view" + ] + } + ], + "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/estia/estia-mcstas-reduction.ipynb b/docs/user-guide/estia/estia-mcstas-reduction.ipynb index 8e670b42..a8838ff9 100644 --- a/docs/user-guide/estia/estia-mcstas-reduction.ipynb +++ b/docs/user-guide/estia/estia-mcstas-reduction.ipynb @@ -5,60 +5,83 @@ "id": "0", "metadata": {}, "source": [ - "# Reduction of ESTIA McStas data" + "# Reduction of ESTIA McStas data\n", + "\n", + "- Audience: Instrument users, beginners\n", + "- Prerequisites: Basic knowledge of [Scipp](https://scipp.github.io/)\n", + "\n", + "This notebook demonstrates the basic reflectometry data reduction workflow for ESTIA with simulated data.\n", + "A workflow for data recorded at ESS would be very similar but is not yet available.\n", + "The workflow:\n", + "\n", + "1. Converts the data to momentum transfer $Q$.\n", + "2. Normalizes by a reference measurement.\n", + "3. Packages the results to be saved to and [ORSO ORT](https://www.reflectometry.org/advanced_and_expert_level/file_format) file.\n", + "\n", + "The data is available through the ESSreflectometry package but accessing it requires the pooch package.\n", + "If you get an error about a missing module pooch, you can install it with `!pip install pooch`." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "1", "metadata": {}, + "outputs": [], "source": [ - "This notebook demonstrates how to run the data reduction on the output of McStas simulations of the instrument.\n", + "import scipp as sc\n", "\n", - "Essentially this looks very similar to how one would do data reduction on real data files from the physical instrument,\n", - "but we replace the default loader with the `load_mcstas_events` provider." + "from ess.estia.data import estia_mcstas_example\n", + "from ess.estia import EstiaMcStasWorkflow\n", + "from ess.reflectometry.types import *" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Create and configure the workflow\n", + "\n", + "We begin by creating the ESTIA (McStas) workflow object which is a skeleton for reducing ESTIA data with pre-configured steps:" ] }, { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ - "#%matplotlib widget\n", - "import scipp as sc\n", - "\n", - "from ess.estia.load import load_mcstas_events\n", - "from ess.estia.data import estia_mcstas_example, estia_mcstas_groundtruth\n", - "from ess.estia import EstiaMcStasWorkflow\n", - "from ess.reflectometry.types import *\n", - "from ess.reflectometry.figures import wavelength_z_figure, wavelength_theta_figure, q_theta_figure" + "wf = EstiaMcStasWorkflow()" ] }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ - "The Estia reduction workflow is created and we set parameters such as region of interest, wavelengthbins, and q-bins." + "We then need to set the missing parameters which are specific to each experiment (the keys are types defined in [ess.reflectometry.types](../../generated/modules/ess.reflectometry.types.rst))." ] }, { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ - "\n", - "wf = EstiaMcStasWorkflow()\n", - "wf.insert(load_mcstas_events)\n", + "# Specify input data files:\n", + "# (The choice of 'Ni/Ti-multilayer' file number 3 is arbitrary.)\n", + "wf[Filename[SampleRun]] = estia_mcstas_example('Ni/Ti-multilayer')[3]\n", "wf[Filename[ReferenceRun]] = estia_mcstas_example('reference')\n", "\n", - "wf[YIndexLimits] = sc.scalar(35), sc.scalar(64)\n", + "# Select a region of interest:\n", + "wf[YIndexLimits] = sc.scalar(35), sc.scalar(64)\n", "wf[ZIndexLimits] = sc.scalar(0), sc.scalar(48 * 32)\n", "wf[BeamDivergenceLimits] = sc.scalar(-0.75, unit='deg'), sc.scalar(0.75, unit='deg')\n", + "\n", + "# Configure the binning of intermediate and final results:\n", "wf[WavelengthBins] = sc.geomspace('wavelength', 3.5, 12, 2001, unit='angstrom')\n", "wf[QBins] = 1000\n", "\n", @@ -69,29 +92,7 @@ " coords={'time': sc.array(dims=('time',), values=[], unit='s')})\n", "wf[ProtonCurrent[ReferenceRun]] = sc.DataArray(\n", " sc.array(dims=('time',), values=[]),\n", - " coords={'time': sc.array(dims=('time',), values=[], unit='s')})\n", - "\n", - "\n", - "def compute_reflectivity_curve_for_mcstas_data(wf, results):\n", - " R, ref, da = w.compute((ReflectivityOverQ, Reference, ReducibleData[SampleRun])).values()\n", - " # In the McStas simulation the reference has quite low intensity.\n", - " # To make the reflectivity curve a bit more clean\n", - " # we filter out the Q-points where the reference has too large uncertainties.\n", - " ref = ref.hist(Q=R.coords['Q'])\n", - " too_large_uncertainty_in_reference = sc.stddevs(ref).data > 0.3 * ref.data\n", - " R = R.hist()\n", - " R.data = sc.where(too_large_uncertainty_in_reference, sc.scalar(float('nan'), unit=R.unit), R.data)\n", - " results[f\"{da.coords['sample_rotation'].value} {da.coords['sample_rotation'].unit}\"] = R\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5", - "metadata": {}, - "outputs": [], - "source": [ - "wf.visualize(graph_attr={'rankdir':\"LR\"})" + " coords={'time': sc.array(dims=('time',), values=[], unit='s')})" ] }, { @@ -99,10 +100,9 @@ "id": "6", "metadata": {}, "source": [ - "## Ni/Ti multilayer sample\n", - "\n", - "Below is a comparison between the reflectivity curve obtained using the reduction workflow and the ground truth reflectivity curve that was used in the McStas simulation.\n", - "The sample was simulated at different sample rotation settings, each settings produces a separate reflectivity curve covering a higher Q-range, and that is the angle in the legend of the figure." + "We can visualize the workflow as a graph.\n", + "This can help us understand how the data will be reduced.\n", + "(`ReflectivityOverQ` is the key of the output data.)" ] }, { @@ -112,15 +112,7 @@ "metadata": {}, "outputs": [], "source": [ - "results = {}\n", - "for path in estia_mcstas_example('Ni/Ti-multilayer'):\n", - " w = wf.copy()\n", - " w[Filename[SampleRun]] = path\n", - " compute_reflectivity_curve_for_mcstas_data(w, results)\n", - "\n", - "ground_truth = estia_mcstas_groundtruth('Ni/Ti-multilayer')\n", - "\n", - "sc.plot({'ground_truth': ground_truth} | results, norm='log', vmin=1e-8)" + "wf.visualize(ReflectivityOverQ, graph_attr={'rankdir': \"LR\"})" ] }, { @@ -128,7 +120,12 @@ "id": "8", "metadata": {}, "source": [ - "Below are a number of figures displaying different projections of the measured intensity distribution." + "The workflow is a [Sciline](https://scipp.github.io/sciline/) pipeline.\n", + "See the documentation of Sciline for more information and how to modify and extend the workflow.\n", + "\n", + "## Use the reduction workflow\n", + "\n", + "We call [wf.compute(targets)](https://scipp.github.io/sciline/generated/classes/sciline.Pipeline.html#sciline.Pipeline.compute) to compute the result:" ] }, { @@ -138,21 +135,16 @@ "metadata": {}, "outputs": [], "source": [ - "results = []\n", - "for path in estia_mcstas_example('Ni/Ti-multilayer'):\n", - " w = wf.copy()\n", - " w[Filename[SampleRun]] = path\n", - " results.append(w.compute(Sample))" + "reflectivity = wf.compute(ReflectivityOverQ)" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "10", "metadata": {}, - "outputs": [], "source": [ - "wavelength_z_figure(results[3], wavelength_bins=400)" + "The reflectivity is still event data.\n", + "We can histogram it and plot it:" ] }, { @@ -162,25 +154,31 @@ "metadata": {}, "outputs": [], "source": [ - "wavelength_theta_figure(results, wavelength_bins=400, theta_bins=200, q_edges_to_display=[sc.scalar(0.016, unit='1/angstrom'), sc.scalar(0.19, unit='1/angstrom')])" + "reflectivity.hist().plot(norm='log', vmin=1e-8)" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "12", "metadata": {}, - "outputs": [], "source": [ - "q_theta_figure(results, q_bins=300, theta_bins=200)" + "## Saving to ORSO ORT file\n", + "\n", + "Ultimately, we want to save the reduced data to a file.\n", + "The workflow supports building an [ORSO dataset](https://www.reflectometry.org/orsopy/orsopy.fileio.html#orsopy.fileio.OrsoDataset) from the reduced data: `OrsoIofQDataset`.\n", + "We require some additional imports:" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "13", "metadata": {}, + "outputs": [], "source": [ - "## Ni on Silicon" + "from orsopy import fileio\n", + "\n", + "from ess.reflectometry import orso" ] }, { @@ -190,28 +188,17 @@ "metadata": {}, "outputs": [], "source": [ - "results = {}\n", - "for path in estia_mcstas_example('Ni-film on silicon'):\n", - " w = wf.copy()\n", - " w[Filename[SampleRun]] = path\n", - " compute_reflectivity_curve_for_mcstas_data(w, results)\n", - "\n", - "ground_truth = estia_mcstas_groundtruth('Ni-film on silicon')\n", - "sc.plot({'ground_truth': ground_truth} | results, norm='log', vmin=1e-9)" + "wf.visualize(orso.OrsoIofQDataset, graph_attr={'rankdir': \"LR\"})" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "15", "metadata": {}, - "outputs": [], "source": [ - "results = []\n", - "for path in estia_mcstas_example('Ni-film on silicon'):\n", - " w = wf.copy()\n", - " w[Filename[SampleRun]] = path\n", - " results.append(w.compute(ReducibleData[SampleRun]))" + "We can see from the graph that some nodes are missing (red boxes).\n", + "These need to be manually provided as they cannot be deduced from the input.\n", + "Further, McStas files do not contain all required metadata, so that needs to be provided manually as well." ] }, { @@ -221,17 +208,30 @@ "metadata": {}, "outputs": [], "source": [ - "wavelength_z_figure(results[3], wavelength_bins=400)" + "wf[orso.Beamline] = orso.Beamline(name='ESTIA', facility='ESS')\n", + "wf[orso.Measurement] = orso.Measurement(\n", + " title='McStas Simulation, Ni/Ti-multilayer',\n", + ")\n", + "wf[orso.OrsoSample] = orso.OrsoSample(\n", + " fileio.data_source.Sample(name='Ni/Ti multilayer')\n", + ")\n", + "wf[orso.OrsoCreator] = orso.OrsoCreator(\n", + " fileio.base.Person(\n", + " name='Max Mustermann',\n", + " affiliation='European Spallation Source ERIC',\n", + " contact='max.mustermann@ess.eu',\n", + " )\n", + ")\n", + "wf[orso.OrsoOwner] = wf[orso.OrsoCreator]" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "17", "metadata": {}, - "outputs": [], "source": [ - "wavelength_theta_figure(results, wavelength_bins=400, theta_bins=200, q_edges_to_display=[sc.scalar(0.016, unit='1/angstrom'), sc.scalar(0.19, unit='1/angstrom')])" + "Now that we have these additional parameters, we can construct the ORSO dataset:\n", + "(Note that this will re-run the reduction procedure to ensure that all data is consistent.)" ] }, { @@ -241,7 +241,7 @@ "metadata": {}, "outputs": [], "source": [ - "q_theta_figure(results, q_bins=300, theta_bins=200)" + "dataset = wf.compute(orso.OrsoIofQDataset)" ] }, { @@ -249,7 +249,8 @@ "id": "19", "metadata": {}, "source": [ - "## SiO2 on Silicon" + "The `dataset` has not been written to disk yet.\n", + "For that, simply call `save`:" ] }, { @@ -259,58 +260,17 @@ "metadata": {}, "outputs": [], "source": [ - "results = {}\n", - "for path in estia_mcstas_example('Natural SiO2 on silicon'):\n", - " w = wf.copy()\n", - " w[Filename[SampleRun]] = path\n", - " compute_reflectivity_curve_for_mcstas_data(w, results)\n", - "\n", - "ground_truth = estia_mcstas_groundtruth('Natural SiO2 on silicon')\n", - "sc.plot({'ground_truth': ground_truth} | results, norm='log', vmin=1e-9)" + "dataset.save('estia_reduced_iofq.ort')" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "21", "metadata": {}, - "outputs": [], "source": [ - "results = []\n", - "for path in estia_mcstas_example('Natural SiO2 on silicon'):\n", - " w = wf.copy()\n", - " w[Filename[SampleRun]] = path\n", - " results.append(w.compute(ReducibleData[SampleRun]))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "22", - "metadata": {}, - "outputs": [], - "source": [ - "wavelength_z_figure(results[3], wavelength_bins=400)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "23", - "metadata": {}, - "outputs": [], - "source": [ - "wavelength_theta_figure(results, wavelength_bins=400, theta_bins=200, q_edges_to_display=[sc.scalar(0.016, unit='1/angstrom')])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "24", - "metadata": {}, - "outputs": [], - "source": [ - "q_theta_figure(results, q_bins=300, theta_bins=200)" + "---\n", + "\n", + "Next, consider reading the [ESTIA advanced McStas reduction](./estia-advanced-mcstas-reduction.rst) guide which demonstrates how multiple files can be reduced together and how to make different types of plots." ] } ], diff --git a/docs/user-guide/estia/index.md b/docs/user-guide/estia/index.md index 268892a1..8e102096 100644 --- a/docs/user-guide/estia/index.md +++ b/docs/user-guide/estia/index.md @@ -1,8 +1,44 @@ -# Estia +# ESTIA + +## Reduction Workflows + +::::{grid} 3 + +:::{grid-item-card} Reduction of McStas Data +:link: estia-mcstas-reduction.ipynb +:text-align: center + +```{image} ../../_static/thumbnails/estia_mcstas_reduction_light.svg +:class: only-light +:width: 100% +``` +```{image} ../../_static/thumbnails/estia_mcstas_reduction_dark.svg +:class: only-dark +:width: 100% +``` +::: + +:::{grid-item-card} Advanced reduction of McStas Data +:link: estia-advanced-mcstas-reduction.ipynb +:text-align: center + +```{image} ../../_static/thumbnails/estia_advanced_mcstas_reduction_light.svg +:class: only-light +:width: 100% +``` +```{image} ../../_static/thumbnails/estia_advanced_mcstas_reduction_dark.svg +:class: only-dark +:width: 100% +``` +::: + +:::: ```{toctree} --- -maxdepth: 1 +hidden: --- + estia-mcstas-reduction +estia-advanced-mcstas-reduction ``` diff --git a/src/ess/estia/workflow.py b/src/ess/estia/workflow.py index 99aa93e6..84001d1c 100644 --- a/src/ess/estia/workflow.py +++ b/src/ess/estia/workflow.py @@ -27,6 +27,7 @@ mcstas_providers = ( *_general_providers, *load.providers, + load.load_mcstas_events, ) """List of providers for setting up a Sciline pipeline for McStas data. diff --git a/tools/docs/estia-thumbnails.ipynb b/tools/docs/estia-thumbnails.ipynb new file mode 100644 index 00000000..4b456e66 --- /dev/null +++ b/tools/docs/estia-thumbnails.ipynb @@ -0,0 +1,283 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# ESTIA thumbnails\n", + "\n", + "This notebook generates the thumbnails used in the ESTIA user guide." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import NewType\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "import sciline\n", + "import scipp as sc\n", + "\n", + "from ess.estia.data import estia_mcstas_example\n", + "from ess.estia import EstiaMcStasWorkflow\n", + "from ess.reflectometry.types import *\n", + "from ess.reflectometry.figures import q_theta_figure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "wf = EstiaMcStasWorkflow()\n", + "wf[Filename[SampleRun]] = estia_mcstas_example('Ni/Ti-multilayer')[3]\n", + "wf[Filename[ReferenceRun]] = estia_mcstas_example('reference')\n", + "\n", + "wf[YIndexLimits] = sc.scalar(35), sc.scalar(64)\n", + "wf[ZIndexLimits] = sc.scalar(0), sc.scalar(48 * 32)\n", + "wf[BeamDivergenceLimits] = sc.scalar(-0.75, unit='deg'), sc.scalar(0.75, unit='deg')\n", + "\n", + "wf[WavelengthBins] = sc.geomspace('wavelength', 3.5, 12, 2001, unit='angstrom')\n", + "wf[QBins] = 1000\n", + "\n", + "# There is no proton current data in the McStas files, here we just add some fake proton current\n", + "# data to make the workflow run.\n", + "wf[ProtonCurrent[SampleRun]] = sc.DataArray(\n", + " sc.array(dims=('time',), values=[]),\n", + " coords={'time': sc.array(dims=('time',), values=[], unit='s')})\n", + "wf[ProtonCurrent[ReferenceRun]] = sc.DataArray(\n", + " sc.array(dims=('time',), values=[]),\n", + " coords={'time': sc.array(dims=('time',), values=[], unit='s')})" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Basic McStas workflow" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "reflectivity = wf.compute(ReflectivityOverQ)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "def basic_estia_plot(style: str):\n", + " with plt.style.context(style):\n", + " fig, ax = plt.subplots(layout='constrained', figsize=(3, 2.5))\n", + " _ = reflectivity.hist(Q=150).plot(ax=ax, norm='log')\n", + " ax.set_xlim((0.13, 0.53))\n", + " ax.set_xlabel(r'$Q$ [1/Å]')\n", + " ax.set_ylabel(r'$R(Q)$')\n", + " return fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "fig = basic_estia_plot('default')\n", + "fig.savefig(\n", + " \"../../docs/_static/thumbnails/estia_mcstas_reduction_light.svg\",\n", + " transparent=True,\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "fig = basic_estia_plot('dark_background')\n", + "fig.savefig(\n", + " \"../../docs/_static/thumbnails/estia_mcstas_reduction_dark.svg\",\n", + " transparent=True,\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "## Advanced McStas workflow" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "MaskedReflectivityOverQ = NewType('MaskedReflectivityOverQ', sc.DataArray)\n", + "\n", + "def mask_noisy_reference(\n", + " reflectivity: ReflectivityOverQ,\n", + " reference: Reference,\n", + ") -> MaskedReflectivityOverQ:\n", + " ref = reference.hist(Q=reflectivity.coords['Q'])\n", + " return reflectivity.assign_masks(\n", + " noisy_reference= sc.stddevs(ref).data > 0.3 * ref.data\n", + " )\n", + "\n", + "wf.insert(mask_noisy_reference)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "param_table = pd.DataFrame({\n", + " Filename[SampleRun]: estia_mcstas_example('Ni/Ti-multilayer')\n", + "}).rename_axis(index='sample_rotation')\n", + "\n", + "# Make a copy to preserve the original `wf`.\n", + "multi_file_workflow = wf.copy()\n", + "mapped = multi_file_workflow[MaskedReflectivityOverQ].map(param_table)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "def combine_measurements(*measurements: sc.DataArray) -> sc.DataGroup[sc.DataArray]:\n", + " return sc.DataGroup({\n", + " f\"{da.coords['sample_rotation']:c}\": da for da in measurements\n", + " })\n", + "\n", + "multi_file_workflow[MaskedReflectivityOverQ] = mapped.reduce(\n", + " func=combine_measurements\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "multi_file_workflow.visualize(\n", + " MaskedReflectivityOverQ,\n", + " graph_attr={\"rankdir\": \"LR\"},\n", + " compact=True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "samples = list(sciline.compute_mapped(multi_file_workflow, Sample))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "def advanced_estia_plot(style: str):\n", + " with plt.style.context(style):\n", + " fig, ax = plt.subplots(layout='constrained', figsize=(3, 2.5))\n", + " _ = q_theta_figure(samples, q_bins=100, theta_bins=100, ax=ax)\n", + " ax.set_xlim((0.0, 0.53))\n", + " ax.set_ylim((0.0, 0.15))\n", + " ax.set_xlabel(r'$Q$ [1/Å]')\n", + " ax.set_ylabel(r'$\\theta$ [rad]')\n", + " fig.axes[-1].set_ylabel(None)\n", + " return fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "fig = advanced_estia_plot('default')\n", + "fig.savefig(\n", + " \"../../docs/_static/thumbnails/estia_advanced_mcstas_reduction_light.svg\",\n", + " transparent=True,\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "fig = advanced_estia_plot('dark_background')\n", + "fig.savefig(\n", + " \"../../docs/_static/thumbnails/estia_advanced_mcstas_reduction_dark.svg\",\n", + " transparent=True,\n", + ")\n", + "fig" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}