{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# The Union components\n", "This tutorial is the first in a series showing how the Union components are used. This notebook focuses on setting up material definitions that are used to provide scattering physics to geometries. There are several kinds of Union components, and they need to be used in conjunction with one another to function.\n", "- Process components: Describe individual scattering phenomena, such as incoherent, powder, single crystal scattering\n", "- Make_material component: Joins several processes into a material definition\n", "- Geometry components: Describe geometry, each is assigned a material definition\n", "- Union logger components: Records information for each scattering event and plots it\n", "- Union abs logger components: Records information for each absorption event and plots it\n", "- Union conditional components: Modifies a logger or abs logger so it only records when certain final condition met\n", "- Union master component: Performs simulation described by previous Union components\n", "\n", "In this notebook we will focus on setting up materials using process components and the *Union_make_material* component, but the Union components can not work individually, so it will also be necessary to add a geometry and the *Union_master*. First we import McStasScript and create a new instrument object.\n", "\n", "In case of any issues with running the tutorial notebooks there is troubleshooting at the end of this notebook." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import mcstasscript as ms" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "instrument = ms.McStas_instr(\"python_tutorial\", input_path=\"run_folder\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Differences between McStas versions\n", "The Union components can be used in both McStas 2.X and McStas 3.X, but when used in McStas 3.X it is necessary to add an *Union_init* component named init before any other Union component and a *Union_stop* component after all other Union components. To make the notebooks compatible with both, the init and stop component can be added inside an if-statement that checks what version of McStas is used." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if instrument.mccode_version == 3:\n", " init = instrument.add_component(\"init\", \"Union_init\") # This component has to be called init" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Process components\n", "In this notebook we will focus on exploring how to build different physical descriptions of materials, and checking that they behave as expected. We start by looking at the process component for incoherent scattering, Incoherent_process." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "instrument.available_components(\"union\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "instrument.component_help(\"Incoherent_process\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The process components in general have few parameters as they just describe a single physical phenomena. The incoherent process here is described adequately by just the cross section *sigma* and volume of the unit cell, *unit_cell_volume*.\n", "\n", "Two parameters are available for all processes, *packing_factor* and *interact_fraction*. The packing factor describes how dense the material is, and can make it easier to mix for example different powders. It is implemented as a simple factor on the scattering strength. The interact fraction is used to balance many processes when they are used in one material. Normally processes are sampled according to they natural probability for scattering, but this can be overwritten using the *interact_fraction*, which provides the sampling probability directly, they just have to sum to 1 within a material." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "incoherent = instrument.add_component(\"incoherent\", \"Incoherent_process\")\n", "incoherent.sigma = 2.5\n", "incoherent.unit_cell_volume = 13.8" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Making a material\n", "In order to collect processes into a material, one uses the *Union_make_material* component. Here are the parameters." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "instrument.component_help(\"Union_make_material\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A material definition thus consists of a number of processes given with the *process_string* parameter, and a description of the absorption in the material given with the inverse penetration depth at the standard neutron speed of 2200 m/s. For our first test material, lets just set absorption to zero and set our process_string to incoherent, referring to the process we created above.\n", "\n", "The name of the material is now inc_material, which will be used in the future to refer to this material." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "inc_material = instrument.add_component(\"inc_material\", \"Union_make_material\")\n", "inc_material.my_absorption = 0.0\n", "inc_material.process_string = '\"incoherent\"'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the material contains no physical processes, it is necessary to set the *absorber* parameter to 1, as it will just have an absorption description. Here we make a material called abs_material. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "absorber = instrument.add_component(\"abs_material\", \"Union_make_material\")\n", "absorber.absorber = 1\n", "absorber.my_absorption = 3.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The primary reason for having both process components and a make_material component is that it is possible to add as many processes in one material as necessary. Here we create a powder process, and then make a material using the powder and previously defined incoherent processes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "powder = instrument.add_component(\"powder\", \"Powder_process\")\n", "powder.reflections = '\"Cu.laz\"'\n", "\n", "inc_material = instrument.add_component(\"powder_material\", \"Union_make_material\")\n", "inc_material.my_absorption = 1.2\n", "inc_material.process_string = '\"incoherent,powder\"'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "At this point we have three materials defined\n", "\n", "| Material name | Description |\n", "|-----------------|------------------------------------------------------------------|\n", "| inc_material | Has one incoherent process and no absorption |\n", "| abs_material | Only has absorption |\n", "| powder_material | Has both incoherent and powder process in addition to absorption |\n", "\n", "The instrument diagram can show the components and their underlying connections. Connections between Union components are shown on the right side. From the diagram it is clear the same incoherent process is used in both inc_material and powder_material. In most cases a user would make individual incoherent processes for each material to set independent incoherent cross sections. It is also visible that powder_material contains two processes and abs_material contains none." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "instrument.show_diagram()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us defined a quick test instrument to see these materials are behaving as expected. First we add a source." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "src = instrument.add_component(\"source\", \"Source_div\")\n", "\n", "source_width = instrument.add_parameter(\"source_width\", value=0.15,\n", " comment=\"Width of source in [m]\")\n", "src.xwidth = source_width\n", "src.yheight = 0.03\n", "src.focus_aw = 0.01\n", "src.focus_ah = 0.01\n", "\n", "src.lambda0 = instrument.add_parameter(\"wavelength\", value=5.0,\n", " comment=\"Wavelength in [Ang]\")\n", "src.dlambda = \"0.001*wavelength\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding geometries that use the material definitions\n", "Here we add three boxes, each using a different material definition and placed next to one another. The *material_string* parameter is used to specify the material name. The *priority* parameter will be explained later, as it is only important when geometries overlap, here they are spatially separated, yet the priorities must still be unique.\n", "\n", "It is important to note that these three boxes will be simulated simultaneously in the McStas simulation flow, so no need for GROUP statements to have these in parallel. Because they are simulated simultaneously, a ray can go from one to another, which would not be possible with a standard GROUP." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "box_inc = instrument.add_component(\"box_inc\", \"Union_box\", AT=[0.04,0,1], RELATIVE=src)\n", "box_inc.xwidth = 0.03\n", "box_inc.yheight = 0.03\n", "box_inc.zdepth = 0.03\n", "box_inc.material_string = '\"inc_material\"'\n", "box_inc.priority = 10\n", "\n", "box_inc = instrument.add_component(\"box_powder\", \"Union_box\", AT=[0,0,1], RELATIVE=src)\n", "box_inc.xwidth = 0.03\n", "box_inc.yheight = 0.03\n", "box_inc.zdepth = 0.01\n", "box_inc.material_string = '\"powder_material\"'\n", "box_inc.priority = 11\n", "\n", "box_inc = instrument.add_component(\"box_abs\", \"Union_box\", AT=[-0.04,0,1], RELATIVE=src)\n", "box_inc.xwidth = 0.03\n", "box_inc.yheight = 0.03\n", "box_inc.zdepth = 0.03\n", "box_inc.material_string = '\"abs_material\"'\n", "box_inc.priority = 12" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us take another look at how this changed the instrument diagram. The three geometries have been added, and each have a line to their material component. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "instrument.show_diagram()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding loggers that show scattering and absorption\n", "In order to check the three materials behave as expected, we add spatial loggers for scattering and absorption. These are called loggers and abs_loggers, here is the parameters for a logger." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "instrument.component_help(\"Union_logger_2D_space\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The parameters for the abs_logger are very similar, so the two are added here." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "logger = instrument.add_component(\"logger_space\", \"Union_logger_2D_space\")\n", "logger.set_RELATIVE(\"box_powder\")\n", "logger.D_direction_1 = '\"z\"'\n", "logger.D1_min = -0.04\n", "logger.D1_max = 0.04\n", "logger.n1 = 250\n", "logger.D_direction_2 = '\"x\"'\n", "logger.D2_min = -0.075\n", "logger.D2_max = 0.075\n", "logger.n2 = 400\n", "logger.filename = '\"logger.dat\"'\n", "\n", "logger = instrument.add_component(\"abs_logger_space\", \"Union_abs_logger_2D_space\")\n", "logger.set_RELATIVE(\"box_powder\")\n", "logger.D_direction_1 = '\"z\"'\n", "logger.D1_min = -0.04\n", "logger.D1_max = 0.04\n", "logger.n1 = 250\n", "logger.D_direction_2 = '\"x\"'\n", "logger.D2_min = -0.075\n", "logger.D2_max = 0.075\n", "logger.n2 = 400\n", "logger.filename = '\"abs_logger.dat\"'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding the Union master component\n", "The Union master component is what actually executes the simulation, and so it takes information from all Union components defined before and performs the described simulation. This is the component that matters in terms of order of execution within the sequence of McStas components. As all the previous components have described the what the master component should simulate, it has no required parameters. It also does not matter where it is located in space, as it will grab the locations described by all previous Union components that need a spatial location, such as the geometries and loggers." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "instrument.component_help(\"Union_master\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "master = instrument.add_component(\"master\", \"Union_master\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### McStas 3.X compatability\n", "As mentioned at the start of the notebook it is necessary to add a *Union_stop* component after all Union coponents in the instrument, this is typically after the last master." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if instrument.mccode_version == 3:\n", " stop = instrument.add_component(\"stop\", \"Union_stop\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Although the Union_master component is not told specifically what geometry components to simulate, it picks up the relevant information from the components added before it. The instrument diagram shows these hidden connections." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "instrument.show_diagram()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Running the simulation\n", "Here the McStas simulation is executed as normal." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "scroll-output" ] }, "outputs": [], "source": [ "instrument.set_parameters(wavelength=8.0)\n", "instrument.settings(ncount=3E6, output_path=\"data_folder/union_materials\")\n", "instrument.show_settings()\n", "\n", "data = instrument.backengine()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Interpreting the results\n", "The first logger shows scattering, and since the top box has incoherent, and the middle both powder and incoherent, we expect those to show up. We can see the beam attenuation, as the beam originates from the left side.\n", "\n", "The second logger shows absorption, and here the top box is absent as it has no absorption cross section. The bottom box is however visible now, as it has absorption but no scattering. As the absorber is quite strong, we see the attenuation here as well." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ms.make_sub_plot(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Alternative run to show powder properties\n", "In order to see the scattering from the powder sample, we restrict the source size to only illuminate the center box with a powder material. A wavelength with powder lines close to 90 deg is selected to ensure the scattering from the center box hits the surrounding boxes.\n", "\n", "We choose to show the data with logarithmic colorscale using the *name_plot_options* method on functions." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "scroll-output" ] }, "outputs": [], "source": [ "instrument.set_parameters(wavelength=2.8, source_width=0.03)\n", "instrument.settings(ncount=5E6, output_path=\"data_folder/union_materials\")\n", "instrument.show_settings()\n", "\n", "data = instrument.backengine()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ms.name_plot_options(\"logger_space\", data, log=True)\n", "ms.name_plot_options(\"abs_logger_space\", data, log=True)\n", "ms.make_sub_plot(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Interpretation of the data\n", "Now that the direct beam only hits the center box, all rays that enter the surrounding boxes are scattered from that center box. Since the center box contains a powder, the scattered beam is not homogeneous and most of it is in the form of Bragg peaks with certain scattering angles, and we can see two of these intersecting the surrounding geometries." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Troubleshooting\n", "In case of issues with the notebooks concerning the Union components or McStasScript it is recommended to:\n", "- Update McStasScript with python -m pip install --upgrade mcstasscript\n", "- Get newest version of Union components (Both library files and components themselves)\n", "\n", "Since the Union components need to collaborate, it is important to have the same version of the libraries and components. The newest version of the components can be found here: https://github.com/McStasMcXtrace/McCode/tree/master/mcstas-comps/contrib/union\n", "All libraries for McStas are found here: https://github.com/McStasMcXtrace/McCode/tree/master/mcstas-comps/share but only a few are needed for the Union components:\n", "- Union_initialization.c\n", "- Union_functions.c\n", "- Geometry_functions.c\n", "- Union_last_functions.c (if on McStas 3.X)\n", "\n", "If using McStas 3.X, it could be that the Union_init component and Union_stop component was not added." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "celltoolbar": "Tags", "kernelspec": { "display_name": "Python 3", "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.8.8" } }, "nbformat": 4, "nbformat_minor": 2 }