{ "cells": [ { "cell_type": "markdown", "id": "valid-array", "metadata": {}, "source": [ "# Component object\n", "McStas components are the essential building blocks used to make McStas simulations. This sections focuses on how to work with [component objects](../_autosummary/mcstasscript.helper.mcstas_objects.Component.rst) in McStasScript.\n", "\n", "Since a component is always part of an instrument, new components are created in the instrument object so that they may immediately be registered and controlled by that instrument object." ] }, { "cell_type": "code", "execution_count": null, "id": "widespread-maple", "metadata": {}, "outputs": [], "source": [ "import mcstasscript as ms\n", "\n", "instrument = ms.McStas_instr(\"component_examples\")" ] }, { "cell_type": "markdown", "id": "piano-anderson", "metadata": {}, "source": [ "## Creating a component object\n", "A [component object](../_autosummary/mcstasscript.helper.mcstas_objects.Component.rst) is returned from the *add_component* and *copy_component* instrument object methods. When creating a [component object](../_autosummary/mcstasscript.helper.mcstas_objects.Component.rst), it needs an instance name and the name of the component in the library. When copying a component, the same library component is used." ] }, { "cell_type": "code", "execution_count": null, "id": "documentary-import", "metadata": {}, "outputs": [], "source": [ "source = instrument.add_component(\"source\", \"Source_div\")\n", "print(source)" ] }, { "cell_type": "markdown", "id": "confidential-active", "metadata": {}, "source": [ "## Setting component parameters\n", "Since the underlying McStas component is read, McStasScript is aware of the parameters, including which are required from the user. These can be set directly as attributes or with the *set_parameters* method." ] }, { "cell_type": "code", "execution_count": null, "id": "dynamic-holmes", "metadata": {}, "outputs": [], "source": [ "source.xwidth = 0.1\n", "source.set_parameters(yheight=0.03)\n", "print(source)" ] }, { "cell_type": "markdown", "id": "naked-harbor", "metadata": {}, "source": [ "Trying to set a parameter that does not exist would lead to an error. The full list of parameters in a component can be shown with *show_parameters*. This also show the current state of the component, including default values and values set by the user." ] }, { "cell_type": "code", "execution_count": null, "id": "respective-spectrum", "metadata": {}, "outputs": [], "source": [ "source.show_parameters()" ] }, { "cell_type": "markdown", "id": "liable-spokesman", "metadata": {}, "source": [ "## Setting the component position and rotation\n", "The default location of a component is at the origin of the absolute coordinate system used in McStas. The placement of components in space is very important, and McStas provides easy coordinate transfers when placing each component, which are accessible from McStasScript. In McStas one specifies the position with **AT** and orientation with **ROTATED**. Each of these can be relative to a reference, which is set with **RELATIVE**. This behavior is adopted in McStasScript where *set_AT* and *set_ROTATED* methods are available.\n", "\n", "If for example we want to shift our source a bit horizontally, we would use a list as a vector. " ] }, { "cell_type": "code", "execution_count": null, "id": "respiratory-springer", "metadata": {}, "outputs": [], "source": [ "source.set_AT([0.02, 0, 0])\n", "print(source)" ] }, { "cell_type": "markdown", "id": "twelve-roommate", "metadata": {}, "source": [ "In McStas convention the beam direction is along z, and it is common to place components some distance down beam, and so if a number is given instead of a list, it is assumed to be along z." ] }, { "cell_type": "code", "execution_count": null, "id": "compatible-romance", "metadata": {}, "outputs": [], "source": [ "source.set_AT(0.01)\n", "print(source)" ] }, { "cell_type": "markdown", "id": "wrong-garbage", "metadata": {}, "source": [ "The position and rotation can also be specified when creating a new component with the *AT* and *RELATIVE* keywords. With the **RELATIVE** option we are now in the coordinate system of the reference component." ] }, { "cell_type": "code", "execution_count": null, "id": "familiar-joshua", "metadata": {}, "outputs": [], "source": [ "monitor = instrument.add_component(\"PSD\", \"PSD_monitor\", AT=[0, 0, 2], RELATIVE=source)\n", "print(monitor)" ] }, { "cell_type": "markdown", "id": "expressed-supplement", "metadata": {}, "source": [ "Setting the rotation is similar with the *set_ROTATED* method. The rotation is specified with euler angles in degrees." ] }, { "cell_type": "code", "execution_count": null, "id": "recreational-overhead", "metadata": {}, "outputs": [], "source": [ "monitor.set_ROTATED([0, 4, 0])\n", "print(monitor)" ] }, { "cell_type": "markdown", "id": "prepared-harrison", "metadata": {}, "source": [ "Here McStasScript assumed that the rotation should be relative to the source, but it is possible to have another reference for the rotation as shown here." ] }, { "cell_type": "code", "execution_count": null, "id": "automotive-racing", "metadata": {}, "outputs": [], "source": [ "monitor.set_ROTATED([0, 4, 0], RELATIVE=\"ABSOLUTE\")\n", "print(monitor)" ] }, { "cell_type": "markdown", "id": "tired-breakfast", "metadata": {}, "source": [ "When setting both position and rotation at initialization one can distinguish between the reference for position and rotation with the *AT_RELATIVE* and *ROTATED_RELATIVE* keywords." ] }, { "cell_type": "code", "execution_count": null, "id": "beautiful-marking", "metadata": {}, "outputs": [], "source": [ "guide = instrument.add_component(\"guide\", \"Guide\",\n", " AT=2, AT_RELATIVE=source, \n", " ROTATED=[0,0,0], ROTATED_RELATIVE=monitor)\n", "print(guide)" ] }, { "cell_type": "markdown", "id": "opened-athletics", "metadata": {}, "source": [ "## Using parameters in a component\n", "It is common to use defined [parameters](../_autosummary/mcstasscript.helper.mcstas_objects.ParameterVariable.rst) and [declare variables](../_autosummary/mcstasscript.helper.mcstas_objects.DeclareVariable.rst) in a component, and they can even be defined directly where needed. The parameter section of the documentation explain the details of setting up parameters, but here it is shown how they are used in components.\n", "\n", "After creating a parameter, it can be used when assigning component parameters. Just use a string with the same name, and it can even contain basic math." ] }, { "cell_type": "code", "execution_count": null, "id": "better-massachusetts", "metadata": {}, "outputs": [], "source": [ "instrument.add_parameter(\"guide_width\")\n", "guide.w1 = \"guide_width\"\n", "guide.h1 = \"1.5*guide_width\"\n", "print(guide)" ] }, { "cell_type": "markdown", "id": "capable-touch", "metadata": {}, "source": [ "When creating a [parameter](../_autosummary/mcstasscript.helper.mcstas_objects.ParameterVariable.rst) or [declare variable](../_autosummary/mcstasscript.helper.mcstas_objects.DeclareVariable.rst), an object is returned which can be provided to the component." ] }, { "cell_type": "code", "execution_count": null, "id": "accomplished-currency", "metadata": {}, "outputs": [], "source": [ "guide_length = instrument.add_declare_var(\"double\", \"guide_length\")\n", "guide.l = guide_length\n", "\n", "guide.m = instrument.add_parameter(\"guide_m_value\")\n", "\n", "print(guide)" ] }, { "cell_type": "markdown", "id": "large-auditor", "metadata": {}, "source": [ "These features cover the majority of use-cases for McStas components." ] }, { "cell_type": "code", "execution_count": null, "id": "discrete-celebrity", "metadata": {}, "outputs": [], "source": [ "guide.set_comment(\"This guide points in the same direction as the PSD monitor\")\n", "print(guide)" ] }, { "cell_type": "markdown", "id": "removable-national", "metadata": {}, "source": [ "## Advanced features\n", "In McStas / McXtrace there are a number of advanced features which can be used to control component behavior in the simulation. In the tutorial section these keywords are explained and demonstrated, here it is shown how they are applied. Most of these can be set when creating a component or through component methods." ] }, { "cell_type": "markdown", "id": "technological-garden", "metadata": {}, "source": [ "### SPLIT\n", "The split keyword instructs the simulation to split the ray into multiple smaller rays to improve statistics. This is done before the component that has the SPLIT keyword applied. The split value must be an integer. Use the SPLIT keyword to set split while creating a new component." ] }, { "cell_type": "code", "execution_count": null, "id": "equal-tactics", "metadata": {}, "outputs": [], "source": [ "guide.set_SPLIT(150)\n", "print(guide)" ] }, { "cell_type": "code", "execution_count": null, "id": "adequate-small", "metadata": {}, "outputs": [], "source": [ "print(instrument.add_component(\"Splitter\", \"Arm\", SPLIT=180))" ] }, { "cell_type": "markdown", "id": "registered-roommate", "metadata": {}, "source": [ "The split keyword can be removed from a component by setting split to 0." ] }, { "cell_type": "code", "execution_count": null, "id": "french-bangladesh", "metadata": {}, "outputs": [], "source": [ "guide.set_SPLIT(0)\n", "print(guide)" ] }, { "cell_type": "markdown", "id": "opened-stewart", "metadata": {}, "source": [ "### EXTEND\n", "A component can be extended with additional C code that has a special scope. It can access both parameters and variable from the component, and the instrument. Lines of extend code can be added with the *append_EXTEND* method. Beware that this code is executed for each neutron that reach the component and during the ray-tracing simulation." ] }, { "cell_type": "code", "execution_count": null, "id": "academic-outside", "metadata": { "scrolled": true }, "outputs": [], "source": [ "instrument.add_declare_var(\"int\", \"flag\")\n", "guide.append_EXTEND(\" if (x>0) flag = 1;\")\n", "guide.append_EXTEND(\" else flag = 0;\")\n", "print(guide)" ] }, { "cell_type": "markdown", "id": "initial-federation", "metadata": {}, "source": [ "### WHEN\n", "The WHEN keyword can be used to set a condition for when a component should be applied to the ray. The input for a WHEN condition is a logical c expression that can use variables in the instrument scope. Since just the expression is needed, there is no terminating semicolon.\n", "\n", "Below a monitor is created which checks the flag defined during the extend example. This monitor will thus only record rays that had a positive x coordinate after the guide. WHEN and EXTEND are often used in conjunction with each other in this way, this is also explored in the tutorial." ] }, { "cell_type": "code", "execution_count": null, "id": "flying-olympus", "metadata": {}, "outputs": [], "source": [ "left_PSD = instrument.add_component(\"Left_side_PSD\", \"PSD_monitor\")\n", "left_PSD.set_WHEN(\"flag == 1\")\n", "print(left_PSD)" ] }, { "cell_type": "markdown", "id": "honey-notebook", "metadata": {}, "source": [ "The WHEN keyword can also be applied when adding a component." ] }, { "cell_type": "code", "execution_count": null, "id": "immediate-ivory", "metadata": {}, "outputs": [], "source": [ "right_PSD = instrument.add_component(\"Right_side_PSD\", \"PSD_monitor\", WHEN=\"flag == 0\")\n", "print(right_PSD)" ] }, { "cell_type": "markdown", "id": "cutting-spring", "metadata": {}, "source": [ "### JUMP\n", "The JUMP keyword allows the user to modify the order in which components are executed on a per neutron level. Using JUMP is complex with several pitfalls, but the syntax is simple. The system is explored in the tutorial on this site, but for full documentation refer to the McStas / McXtrace documentation.\n", "\n", "Here the jump keyword is used to jump from the guide component to the target_arm in case the y component of the velocity is positive. That would skip the monitors!" ] }, { "cell_type": "code", "execution_count": null, "id": "seven-kingdom", "metadata": {}, "outputs": [], "source": [ "instrument.add_component(\"target_arm\", \"Arm\", RELATIVE=guide)\n", "guide.set_JUMP(\"target_arm WHEN (vy>0)\")\n", "print(guide)" ] }, { "cell_type": "markdown", "id": "valid-latitude", "metadata": {}, "source": [ "### GROUP\n", "The GROUP keyword allows the user to set a number of components in parallel, but only allows a ray to interact with one of them. When two components have the same group name, this behavior is applied." ] }, { "cell_type": "code", "execution_count": null, "id": "consecutive-request", "metadata": {}, "outputs": [], "source": [ "crystal_1 = instrument.add_component(\"mono_1\", \"Monochromator_flat\")\n", "crystal_1.set_GROUP(\"monochromator\")\n", "\n", "crystal_2 = instrument.add_component(\"mono_2\", \"Monochromator_flat\", GROUP=\"monochromator\")\n", "\n", "# Place two crystals in parallel\n", "crystal_1.set_AT([0.01, 0, 5]) \n", "crystal_2.set_AT([-0.01, 0, 5])\n", "\n", "print(crystal_1)\n", "print(crystal_2)" ] }, { "cell_type": "markdown", "id": "educated-commons", "metadata": {}, "source": [ "### Set c code\n", "It is possible to add C code between components in the instrument trace section. In McStasScript this is attached to component objects as one can insert code before or after a component." ] }, { "cell_type": "code", "execution_count": null, "id": "assigned-content", "metadata": { "scrolled": true }, "outputs": [], "source": [ "code_arm = instrument.add_component(\"code_arm\", \"Arm\", c_code_before=\"// code before\")\n", "code_arm.set_c_code_after(\"// code after\")\n", "print(code_arm)" ] }, { "cell_type": "markdown", "id": "verbal-gibson", "metadata": {}, "source": [ "Here this is used to set comments, but there is also the option of setting a comment directly with the comment keyword." ] }, { "cell_type": "code", "execution_count": null, "id": "nuclear-bottle", "metadata": { "scrolled": false }, "outputs": [], "source": [ "commented_component = instrument.add_component(\"commented_component\", \"Arm\", comment=\"This is a comment!\")\n", "print(commented_component)" ] }, { "cell_type": "markdown", "id": "better-chance", "metadata": {}, "source": [ "### SEARCH\n", "It is also possible to set a number of SEARCH statements for a component. McStas will not include the component if it is not found in that search statement. These work just as for instruments and are documented [here](instrument_object.ipynb). Note that McStasScript does not yet search for components in these locations, so for now this syntax can only be used to use different versions of existing components with the same input parameters." ] }, { "cell_type": "code", "execution_count": null, "id": "stable-blond", "metadata": {}, "outputs": [], "source": [ "arm = instrument.add_component(\"code_arm\", \"Arm\")\n", "arm.add_search(\"/search/for/component/here\")\n", "print(arm)" ] }, { "cell_type": "code", "execution_count": null, "id": "focused-short", "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" }, "metadata": { "execution": { "timeout": 100 } } }, "nbformat": 4, "nbformat_minor": 5 }