{ "cells": [ { "cell_type": "markdown", "id": "swedish-cathedral", "metadata": {}, "source": [ "# Dynamic instrument cuts with MCPL bridges\n", "\n", "There are some instances where it is beneficial to write to McStas instruments to simulate something by transfering the beam from one to the other through an MCPL file. This is most commonly done to save on computational time, imagine for example an instrument with a long complicated guide with high resolution choppers, only allowing few neutrons through. If one wants to simulate a number of different samples on such an instrument, it would be nice to run the simulation up until the end of the guide once, and then once for each sample starting after the guide.\n", "\n", "That can be done in McStas using the MCPL output and MCPL_input component, which saves and loads the beam to a file respectively. Then one would have two instrument files, the first ending with an MCPL_output, and the next starting with an MCPL_input. \n", "\n", "\n", "In McStasScript it is possible to do the same thing dynamically through the instrument object. The instrument can be executed in segments, and the MCPL_output / MCPL_input components are added automatically. The instrument object will also keep track of the generated MCPL files. This tutorial will show an example of how this can be done, and importantly show that there are some limitations on how the instrument is written to allow for this." ] }, { "cell_type": "markdown", "id": "devoted-liberal", "metadata": {}, "source": [ "## A simple example instrument\n", "The instrument below does not benefit greatly from being segmented, but is sufficient to show how the system is used." ] }, { "cell_type": "code", "execution_count": null, "id": "motivated-factory", "metadata": {}, "outputs": [], "source": [ "import mcstasscript as ms\n", "\n", "instr = ms.McStas_instr(\"bridge_demo\", input_path=\"run_folder\")\n", "\n", "src = instr.add_component(\"Source\", \"Source_simple\")\n", "src.E0 = instr.add_parameter(\"energy\", value=10)\n", "src.set_parameters(xwidth=0.1, yheight=0.1, dE=2,\n", " focus_xw=0.03, focus_yh=0.03, dist=2)\n", "\n", "guide_1 = instr.add_component(\"guide_1\", \"Guide_gravity\")\n", "guide_1.set_parameters(w1=0.03, h1=0.03, m=3, l=10)\n", "guide_1.set_AT(2, RELATIVE=src)\n", "\n", "guide_2 = instr.add_component(\"guide_2\", \"Guide_gravity\")\n", "guide_2.set_parameters(w1=0.03, h1=0.03, m=3, l=10)\n", "guide_2.set_AT(guide_1.l + 0.01, RELATIVE=guide_1)\n", "\n", "guide_end = instr.add_component(\"Guide_end\", \"Arm\")\n", "guide_end.set_AT(guide_2.l, RELATIVE=guide_2)\n", "\n", "sample_position = instr.add_component(\"Sample_position\", \"Arm\")\n", "sample_position.set_AT(22.5, RELATIVE=src)\n", "\n", "sample = instr.add_component(\"sample\", \"PowderN\", RELATIVE=sample_position)\n", "sample.reflections = instr.add_parameter(\"string\", \"data\", value='\"Cu.laz\"')\n", "sample.set_parameters(radius=0.005, yheight=0.05)\n", "\n", "banana = instr.add_component(\"banana\", \"Monitor_nD\", RELATIVE=sample)\n", "banana.xwidth = 2.0\n", "banana.yheight = 0.3\n", "banana.restore_neutron = 1\n", "banana.filename = '\"banana.dat\"'\n", "banana.options = '\"theta limits=[5 175] bins=150, energy limits=[5,15] bins=100, banana\"'" ] }, { "cell_type": "code", "execution_count": null, "id": "framed-burlington", "metadata": {}, "outputs": [], "source": [ "instr.show_diagram()" ] }, { "cell_type": "markdown", "id": "packed-darkness", "metadata": {}, "source": [ "## Requirements for the instrument to be segmented\n", "In order to cut the instrument into two sections, the positions and rotations in the latter part are not allowed to refer to any component in the first part. In the above example it would not be possible to make a cut at any component from *guide_1* to *Guide_end*, as the sample position would not be able to be set relative to something in another instrument file. It would however be possible to create a cut at the *Source* (not very useful) or at the *Sample_position* and at any component after.\n", "\n", "In order to set the end of an instrument segment, one use the *run_to* method on the instrument object, providing the name of the component or component object of the component that should act as the transfer point. This will check if its allowed to segment the instrument at that component, so lets try at the Guide_end which wouldn't be possible." ] }, { "cell_type": "code", "execution_count": null, "id": "mechanical-sierra", "metadata": {}, "outputs": [], "source": [ "try:\n", " instr.run_to(guide_end)\n", "except Exception as e:\n", " print(e)" ] }, { "cell_type": "markdown", "id": "specific-analyst", "metadata": {}, "source": [ "## Instruments suitable for being segmented\n", "Instruments can be written in a way where there are more options to segment the instrument, we could just specify the position of the sample relative to the end of the guide, then this instrument could be cut anywhere." ] }, { "cell_type": "code", "execution_count": null, "id": "precise-poster", "metadata": {}, "outputs": [], "source": [ "sample_position.set_AT(0.5, RELATIVE=guide_end)\n", "instr.show_diagram()" ] }, { "cell_type": "markdown", "id": "super-visibility", "metadata": {}, "source": [ "There is no need to write all instruments with only references to the previous component in order to allow segmentation of the instrument at every component, but it is worthwhile to keep this in mind for points in your instrument where segmentation is natural, such as the end of a guide.\n", "\n", "## Running the first part of an instrument\n", "To run the instrument up to a certain point and dump the beam to disk, one need to use the *run_to* method to set the end point, and then run the instrument as one normally would with *backengine*. One can always use *show_run_subset* to see what part of the instrument is currently selected for execution." ] }, { "cell_type": "code", "execution_count": null, "id": "electoral-zimbabwe", "metadata": {}, "outputs": [], "source": [ "instr.run_to(guide_end)\n", "instr.show_run_subset()" ] }, { "cell_type": "markdown", "id": "outside-speed", "metadata": {}, "source": [ "### Metadata for beamdump\n", "It is possible to set the name of the beam dump and to add a comment, which is good practice so it easier to keep track of the beam dumps." ] }, { "cell_type": "code", "execution_count": null, "id": "controversial-ballet", "metadata": {}, "outputs": [], "source": [ "instr.run_to(guide_end, \"10 meV\", comment=\"Run with spread of +/- 2 meV\")" ] }, { "cell_type": "markdown", "id": "confused-spain", "metadata": {}, "source": [ "### Instrument information\n", "This information is also reflected in the instrument diagram and show components. Here it is visible that the *Guide_end* component has been substituted with a *MCPL_Guide_end* component that has been given its exact position." ] }, { "cell_type": "code", "execution_count": null, "id": "intensive-serve", "metadata": {}, "outputs": [], "source": [ "instr.show_components()\n", "instr.show_diagram()" ] }, { "cell_type": "markdown", "id": "fuzzy-hydrogen", "metadata": {}, "source": [ "This part of the instrument can now be executed as normal." ] }, { "cell_type": "code", "execution_count": null, "id": "remarkable-issue", "metadata": {}, "outputs": [], "source": [ "instr.settings(suppress_output=True)\n", "instr.backengine()" ] }, { "cell_type": "markdown", "id": "fancy-memphis", "metadata": {}, "source": [ "## The dump database\n", "The instrument object keeps track of the generated MCPL files in a small database written to disk next to the instrument file. These can be shown with the *show_dumps* method, and will persist through sessions as the data is saved on disk. This database doesn't copy the MCPL data, but keeps track on where it is located on the disk, so moving or deleting the generated data folders can cause trouble." ] }, { "cell_type": "code", "execution_count": null, "id": "other-provincial", "metadata": {}, "outputs": [], "source": [ "instr.show_dumps()" ] }, { "cell_type": "markdown", "id": "handled-rwanda", "metadata": {}, "source": [ "### Information on a specific beam dump\n", "It is possible to get further information on one beam dump with the *show_dump* method. This method require the component name where the dump is located, and the most recent version is then displayed. It is possible to add *run_name* and *tag* to show a specific beam dump." ] }, { "cell_type": "code", "execution_count": null, "id": "young-float", "metadata": {}, "outputs": [], "source": [ "instr.show_dump(guide_end)" ] }, { "cell_type": "code", "execution_count": null, "id": "breeding-chocolate", "metadata": {}, "outputs": [], "source": [ "instr.show_dump(guide_end, run_name=\"10 meV\", tag=0)" ] }, { "cell_type": "markdown", "id": "involved-morocco", "metadata": {}, "source": [ "### Clearing the database\n", "Clearing this database can be done by removing the database folder from the disk, it is a folder that has the same name as the instrument and ends with \"_db\". There is no method in McStasScript to do so as there is risk of deleting important data on accident. It is also possible to change the name of the instrument, as the database is tied to the name." ] }, { "cell_type": "markdown", "id": "judicial-alignment", "metadata": {}, "source": [ "## Running from a beam dump\n", "Now that we have a beam dump in the database, we can use it to run from. It is however important to remember run_to is still set to *guide_end*, so asking the instrument to run_from *guide_end* would not be allowed." ] }, { "cell_type": "code", "execution_count": null, "id": "hybrid-classic", "metadata": {}, "outputs": [], "source": [ "try:\n", " instr.run_from(guide_end)\n", "except Exception as e:\n", " print(e)" ] }, { "cell_type": "markdown", "id": "above-westminster", "metadata": {}, "source": [ "### Clearing run_to or run_from\n", "It is possible to clear run_to or run_from by giving the input *None*, or both can be cleared with the method *reset_run_points*. This allows us to run from the *guide_end* beam dump. The *run_from* method would result in an error if no dumps where found at that point in the beam dump database." ] }, { "cell_type": "code", "execution_count": null, "id": "spanish-seattle", "metadata": {}, "outputs": [], "source": [ "instr.reset_run_points()\n", "instr.run_from(guide_end)\n", "\n", "instr.show_components()\n", "instr.show_diagram()" ] }, { "cell_type": "markdown", "id": "accessible-distribution", "metadata": {}, "source": [ "Now we see the part of the instrument after the *Guide_end* component as expected." ] }, { "cell_type": "markdown", "id": "unable-penguin", "metadata": {}, "source": [ "### Selecting a certain beam dump\n", "Just running *run_from* will select the most recent beam dump at that position, and is often the appropriate choice. It is however possible to select any beam dump in the database, provided it is at the specified component using the *run_name* and *tag* keyword arguments." ] }, { "cell_type": "code", "execution_count": null, "id": "based-brooklyn", "metadata": {}, "outputs": [], "source": [ "instr.run_from(guide_end, run_name=\"10 meV\", tag=0)" ] }, { "cell_type": "markdown", "id": "pending-calcium", "metadata": {}, "source": [ "### Settings to the MCPL_input and MCPL_output components\n", "Both the *run_from* and *run_to* methods are able to pass parameters to the MCPL components used, so the *run_to* method accepts parameters for the MCPL_output component, while run_from accept parameters from the MCPL_input component. Here we show the help of the MCPL_input component." ] }, { "cell_type": "code", "execution_count": null, "id": "strange-estate", "metadata": {}, "outputs": [], "source": [ "instr.component_help(\"MCPL_input\")" ] }, { "cell_type": "markdown", "id": "elementary-acting", "metadata": {}, "source": [ "When loading a beam from disk, it is possible to run each neutron several times with small differences in its position, direction and energy. This can have similar problematic effects on the statistics as SPLIT if used without care, so think about the justification to use repeats and smear for your application. Using smear of direction from a source with an almost isotropic distribution of directions would be sensible, but using it on a tightly collimated beam could introduce inaccuracies." ] }, { "cell_type": "code", "execution_count": null, "id": "amazing-bracelet", "metadata": {}, "outputs": [], "source": [ "instr.run_from(guide_end, repeat_count=3, E_smear=0.01, dir_smear=0.1)" ] }, { "cell_type": "markdown", "id": "manual-holder", "metadata": {}, "source": [ "## Running a segment of the simulation\n", "As a demonstration we choose to run from the guide_end to (but not including) the detector to show that one can use both *run_from* and *run_to* at the same time, and thus segment an instrument into an arbitrary number of segments." ] }, { "cell_type": "code", "execution_count": null, "id": "unique-simpson", "metadata": {}, "outputs": [], "source": [ "instr.run_to(banana, run_name=\"Cu sample\")" ] }, { "cell_type": "code", "execution_count": null, "id": "fitting-decimal", "metadata": {}, "outputs": [], "source": [ "instr.show_components()\n", "instr.show_diagram()" ] }, { "cell_type": "markdown", "id": "creative-dining", "metadata": {}, "source": [ "### Executing the simulation\n", "Now that we have one run for a 10 meV beam at the sample position, we can quickly simulate a range of different samples using the same beam. We use the following powder descriptions included in McStas to get 4 different powder patterns.\n", "\n", "- Cu.laz\n", "- Al.laz\n", "- Au.laz\n", "- Na2Ca3Al2F14.laz\n", "\n", "Since the underlying instrument doesn't change, we can save some time by disabling *force_compile* when running these simulations." ] }, { "cell_type": "code", "execution_count": null, "id": "intensive-webcam", "metadata": {}, "outputs": [], "source": [ "instr.backengine()\n", "\n", "instr.settings(force_compile=False)\n", "\n", "instr.set_parameters(data='\"Al.laz\"')\n", "instr.run_to(banana, run_name=\"Al sample\")\n", "instr.backengine()\n", "\n", "instr.set_parameters(data='\"Au.laz\"')\n", "instr.run_to(banana, run_name=\"Au sample\")\n", "instr.backengine()\n", "\n", "instr.set_parameters(data='\"Na2Ca3Al2F14.laz\"')\n", "instr.run_to(banana, run_name=\"Calibration\")\n", "instr.backengine()\n", "\n", "instr.settings(force_compile=True)" ] }, { "cell_type": "code", "execution_count": null, "id": "serial-pakistan", "metadata": {}, "outputs": [], "source": [ "instr.show_dumps()" ] }, { "cell_type": "markdown", "id": "pressed-fluid", "metadata": {}, "source": [ "## Running to the end of the instrument\n", "We now wish to just run the detector for the four different beam dumps made just before the detector. To do so we set *run_to* to None, letting the instrument know we intend to run to the end of the instrument, and setting *run_from* to banana with specifications on the dump corresponding to the desired sample. " ] }, { "cell_type": "code", "execution_count": null, "id": "developing-fossil", "metadata": {}, "outputs": [], "source": [ "instr.run_to(None)" ] }, { "cell_type": "code", "execution_count": null, "id": "cognitive-spirituality", "metadata": {}, "outputs": [], "source": [ "instr.run_from(banana, run_name=\"Cu sample\")\n", "data1 = instr.backengine()\n", "\n", "instr.settings(force_compile=False) # Avoid recompiling\n", "instr.run_from(banana, run_name=\"Al sample\")\n", "data2 = instr.backengine()\n", "\n", "instr.run_from(banana, run_name=\"Au sample\")\n", "data3 = instr.backengine()\n", "\n", "instr.run_from(banana, run_name=\"Calibration\")\n", "data4 = instr.backengine()\n", "instr.settings(force_compile=True)" ] }, { "cell_type": "markdown", "id": "amateur-carroll", "metadata": {}, "source": [ "### Plotting the resulting data\n", "We can now plot the data and see we indeed get four different sets of powder data." ] }, { "cell_type": "code", "execution_count": null, "id": "improved-brisbane", "metadata": {}, "outputs": [], "source": [ "ms.make_sub_plot(data1 + data2 + data3 + data4)" ] }, { "cell_type": "code", "execution_count": null, "id": "fundamental-albany", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "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": 5 }