{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Union tutorial on masks\n", "There are some geometries that are impossible to build using only the priority based system geometry system, for example making part of a cylinder thinner, which is needed for a cryostat window. In many such cases, masks can be used to solve the problem." ] }, { "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": [ "### Setting up an example without masks\n", "First we set up an example with a thick and hollow Al cylinder and a logger to view the spatial distribution of scattering." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "Al_inc = instrument.add_component(\"Al_inc\", \"Incoherent_process\")\n", "Al_inc.sigma = 0.0082\n", "Al_inc.unit_cell_volume = 66.4\n", "\n", "Al_pow = instrument.add_component(\"Al_pow\", \"Powder_process\")\n", "Al_pow.reflections = '\"Al.laz\"'\n", "\n", "Al = instrument.add_component(\"Al\", \"Union_make_material\")\n", "Al.process_string = '\"Al_inc,Al_pow\"'\n", "Al.my_absorption = 100*0.231/66.4 # barns [m^2 E-28]*Å^3 [m^3 E-30]=[m E-2], factor 100\n", "\n", "src = instrument.add_component(\"source\", \"Source_div\")\n", "\n", "src.xwidth = 0.2\n", "src.yheight = 0.035\n", "src.focus_aw = 0.01\n", "src.focus_ah = 0.01\n", "\n", "\n", "src.lambda0 = instrument.add_parameter(\"wavelength\", value=5.0,\n", " comment=\"Wavelength in [Ang]\")\n", "src.dlambda = \"0.01*wavelength\"\n", "src.flux = 1E13\n", "\n", "wall = instrument.add_component(\"wall\", \"Union_cylinder\")\n", "wall.set_AT([0,0,1], RELATIVE=src)\n", "wall.yheight = 0.15\n", "wall.radius = 0.1\n", "wall.material_string='\"Al\"' \n", "wall.priority = 10\n", "\n", "wall_vac = instrument.add_component(\"wall_vacuum\", \"Union_cylinder\")\n", "wall_vac.set_AT([0,0,0], RELATIVE=wall)\n", "wall_vac.yheight = 0.15 + 0.01\n", "wall_vac.radius = 0.1 - 0.02\n", "wall_vac.material_string='\"Vacuum\"' \n", "wall_vac.priority = 50\n", "\n", "logger_zx = instrument.add_component(\"logger_space_zx\", \"Union_logger_2D_space\")\n", "logger_zx.set_RELATIVE(wall)\n", "logger_zx.D_direction_1 = '\"z\"'\n", "logger_zx.D1_min = -0.12\n", "logger_zx.D1_max = 0.12\n", "logger_zx.n1 = 300\n", "logger_zx.D_direction_2 = '\"x\"'\n", "logger_zx.D2_min = -0.12\n", "logger_zx.D2_max = 0.12\n", "logger_zx.n2 = 300\n", "logger_zx.filename = '\"logger_zx.dat\"'\n", "\n", "master = instrument.add_component(\"master\", \"Union_master\")\n", "\n", "instrument.show_diagram()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the above diagram it is clear that the wall is made of Al and the master simulates the wall and the wall_vacuum." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "scroll-output" ] }, "outputs": [], "source": [ "instrument.settings(ncount=2E6, output_path=\"data_folder/union_masks\")\n", "\n", "data = instrument.backengine()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ms.make_sub_plot(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Masks\n", "All Union geometries can act as a mask for a list of other already defined geometries. The geometries affected by a mask will only exist inside the mask, while the parts outside will not have any effect on this simulation. This provides some interesting geometrical capabilities, for example by defining two spheres with some overlap and making one a mask of the other, a classical lens shape can be created.\n", "\n", "The relevant parameters of all geometry components are:\n", "- mask_string : comma separated list of geometry names the mask should be applied to\n", "- mask_setting : selects between \"ANY\" or \"ALL\" mode. Default mode is \"ALL\".\n", "\n", "The mask mode is only important if several masks affect the same geometry, per default just having any of the masks overlap the target geometry allow it to exists, which correspond to the \"ANY\" mode. If the \"ALL\" mode is selected, the target geometry will only exists in regions where all the masks and itself overlap.\n", "\n", "Note that a unique priority is still necessary, but it is not used." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding a window using masks\n", "Here we add a window to one side of the cylinder by inserting a larger vacuum cylinder, but mask it so that it is only active in the area around the window. In this way we get a nice curved window. We chose a box shape for the mask, but we could also have chosen a cylinder to get a round window." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "window = instrument.add_component(\"window\", \"Union_cylinder\", before=\"master\")\n", "window.set_AT([0,0,0], RELATIVE=wall)\n", "window.yheight = 0.15 + 0.02\n", "window.radius = 0.1 - 0.01\n", "window.material_string='\"Vacuum\"' \n", "window.priority = 25\n", "\n", "mask = instrument.add_component(\"mask\", \"Union_box\", before=\"master\")\n", "mask.xwidth = 0.1\n", "mask.yheight = 0.2\n", "mask.zdepth = 0.09\n", "mask.priority = 1\n", "mask.mask_string='\"window\"'\n", "mask.set_AT([0,0,-0.1], RELATIVE=wall)\n", "\n", "instrument.show_diagram()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The windows was added to the diagram and is connected to the master as expected. The mask shows up as a component that only acts on the window geometry, as the mask itself is not simulated, it just modifies the window." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "scroll-output" ] }, "outputs": [], "source": [ "data = instrument.backengine()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ms.make_sub_plot(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding an external window using a mask\n", "It is also possible to create a thinner section where the material is reduced from the outside. Here we need to add both a vacuum and an aluminium geometry, both of which need to have a priority lower than the original inner vacuum. One mask can handle several geometries, just include both names in the *mask_string* parameter." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "o_window = instrument.add_component(\"outer_window\", \"Union_cylinder\", before=\"master\")\n", "o_window.set_AT([0,0,0], RELATIVE=wall)\n", "o_window.yheight = 0.15 + 0.03\n", "o_window.radius = 0.1 + 0.01\n", "o_window.material_string='\"Vacuum\"' \n", "o_window.priority = 30\n", "\n", "o_window_al = instrument.add_component(\"outer_window_Al\", \"Union_cylinder\", before=\"master\")\n", "o_window_al.set_AT([0,0,0], RELATIVE=wall)\n", "o_window_al.yheight = 0.15 + 0.04\n", "o_window_al.radius = 0.1 - 0.01\n", "o_window_al.material_string='\"Al\"' \n", "o_window_al.priority = 31\n", "\n", "mask = instrument.add_component(\"mask_outer\", \"Union_box\", before=\"master\")\n", "mask.xwidth = 0.12\n", "mask.yheight = 0.2\n", "mask.zdepth = 0.09\n", "mask.priority = 2\n", "mask.mask_string='\"outer_window,outer_window_Al\"'\n", "mask.set_AT([0,0,0.1], RELATIVE=wall)\n", "\n", "instrument.show_diagram()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the outer window the mask_outer acts on two geometries, outer_window and outer_window_Al. Notice that both the arrow for Al and mask_outer go to outer_window_Al as they both impact that component, one as a material and the other as a mask." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "scroll-output" ] }, "outputs": [], "source": [ "data = instrument.backengine()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ms.make_sub_plot(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Masks are flexible\n", "Masks can be used to create many interesting shapes with few geometries. Below we create a octagon with rounded corners using just three geometries, two of these being masks. Using masks expands the space of possible geometries greatly, and in many cases can also be a performance advantage when they reduce the number of geometries needed to describe the desired geometry." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "instrument = ms.McStas_instr(\"python_tutorial\", input_path=\"run_folder\")\n", "\n", "Al_inc = instrument.add_component(\"Al_inc\", \"Incoherent_process\")\n", "Al_inc.sigma = 0.0082\n", "Al_inc.unit_cell_volume = 66.4\n", "\n", "Al_pow = instrument.add_component(\"Al_pow\", \"Powder_process\")\n", "Al_pow.reflections = '\"Al.laz\"'\n", "\n", "Al = instrument.add_component(\"Al\", \"Union_make_material\")\n", "Al.process_string = '\"Al_inc,Al_pow\"'\n", "Al.my_absorption = 100*0.231/66.4 # barns [m^2 E-28]*Å^3 [m^3 E-30]=[m E-2], factor 100\n", "\n", "src = instrument.add_component(\"source\", \"Source_div\")\n", "\n", "src.xwidth = 0.2\n", "src.yheight = 0.035\n", "src.focus_aw = 0.01\n", "src.focus_ah = 0.01\n", "\n", "instrument.add_parameter(\"wavelength\", value=5.0, comment=\"Wavelength in [Ang]\")\n", "src.lambda0=\"wavelength\"\n", "src.dlambda=\"0.01*wavelength\"\n", "src.flux = 1E13\n", "\n", "box = instrument.add_component(\"box\", \"Union_box\")\n", "box.set_AT([0,0,1], RELATIVE=src)\n", "box.xwidth = 0.2\n", "box.yheight = 0.1\n", "box.zdepth = 0.2\n", "box.material_string='\"Al\"' \n", "box.priority = 10\n", "\n", "# Cut the corners by using an identical box rotated 45 deg around y\n", "box_mask = instrument.add_component(\"box_mask\", \"Union_box\")\n", "box_mask.set_AT([0,0,0], RELATIVE=box)\n", "box_mask.set_ROTATED([0,45,0], RELATIVE=box)\n", "box_mask.xwidth = 0.2\n", "box_mask.yheight = 0.11 # Have to increase yheight to avoid perfect overlap\n", "box_mask.zdepth = 0.2\n", "box_mask.mask_string='\"box\"' \n", "box_mask.priority = 50\n", "\n", "# Round the corners with a cylinder mask\n", "cyl_mask = instrument.add_component(\"cylinder_mask\", \"Union_cylinder\")\n", "cyl_mask.set_AT([0,0,0], RELATIVE=box)\n", "cyl_mask.radius = 0.105\n", "cyl_mask.yheight = 0.12\n", "cyl_mask.mask_string='\"box\"' \n", "cyl_mask.priority = 51\n", "\n", "logger_zx = instrument.add_component(\"logger_space_zx\", \"Union_logger_2D_space\")\n", "logger_zx.set_RELATIVE(box)\n", "logger_zx.D_direction_1 = '\"z\"'\n", "logger_zx.D1_min = -0.12\n", "logger_zx.D1_max = 0.12\n", "logger_zx.n1 = 300\n", "logger_zx.D_direction_2 = '\"x\"'\n", "logger_zx.D2_min = -0.12\n", "logger_zx.D2_max = 0.12\n", "logger_zx.n2 = 300\n", "logger_zx.filename = '\"logger_zx.dat\"'\n", "\n", "master = instrument.add_component(\"master\", \"Union_master\")\n", "\n", "\n", "instrument.show_diagram()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [ "scroll-output" ] }, "outputs": [], "source": [ "data = instrument.backengine()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ms.make_sub_plot(data)" ] }, { "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 }