In this section, we describe some additional ways to build instruments using groups, code extension, conditions, loops and duplication of components.
As a summary, the nearly complete grammar definition for component instances within the instrument TRACE section is:
1{REMOVABLE} {CPU} {SPLIT} COMPONENT name = comp(parameters) {WHEN condition} 2 AT (...) [RELATIVE [reference|PREVIOUS] | ABSOLUTE] 3 {ROTATED {RELATIVE [reference|PREVIOUS] | ABSOLUTE} } 4 {GROUP group_name} 5 {EXTEND C_code} 6 {JUMP [reference|PREVIOUS|MYSELF|NEXT] [ITERATE number_of_times | WHEN condition] } 7 {METADATA type name C_code} 8
The REMOVABLE keyword is documented in section 4.4.1 below, CPU in section 4.5.2, and METADATA in section 4.4.7.
The %include insertion mechanism may be used within the TRACE section, in order to concatenate instruments together. This way, each DECLARE, INITIALIZE, SAVE, and FINALLY C blocks, as well as instrument parameters from each part are concatenated. The TRACE section is made of inserted COMPONENTS from each part. In principle, it is then possible to write an instrument as:
1DEFINE concatenated() 2TRACE 3 4%include "part1.instr" 5%include "part2.instr" 6 7END 8
where each inserted instrument is a valid full instrument. In order to avoid some components to be duplicated - e.g. Sources from each part - a special syntax in the TRACE section
1REMOVABLE COMPONENT a=... 2
marks the component a as removable when inserted. In principle, inserted instruments may themselves use %include.
It is sometimes desirable to slightly modify an existing component of the McStas library. One would usually make a copy of the component, and extend the code of its TRACE section. McStas provides an easy way to change the behavior of existing components in an instrument definition without duplicating files, using the EXTEND modifier
1EXTEND 2%{ 3// C code executed after the component TRACE section... 4%} 5
The embedded C code is appended to the component TRACE section, and all its internal variables (as well as all the DECLARE instrument variables, except instrument parameters) may be used. To use instrument parameters, you should copy them into global variables in the DECLARE instrument section, and refer to these latter. This component declaration modifier is of course optional. You will find numerous usage examples, and in particular in the Sources section of the Component manual.
In some peculiar configurations it is necessary to position one or more groups of components, nested, in parallel, or overlapping. One example is a multiple crystal monochromator. One would then like the neutron ray to interact with one of the components of the group and then continue.
In order to handle such arrangements without removing neutrons, groups are defined by the GROUP modifier (after the AT-ROTATED positioning):
1GROUP name 2
to all involved component declarations. All components of the same named group are tested one after the other, until one of them interacts (uses the SCATTER macro). The selected component acts on the neutron ray, and the rest of the group is skipped. Such groups are thus exclusive (only one of the elements is active).
Within a GROUP, all EXTEND sections of the group are executed. In order to discriminate components that are active from those that are skipped, one may use the SCATTERED flag, which is set to zero when entering each component or group, and incremented when the neutron is SCATTERed, as in the following example
1COMPONENT name0 = comp ( 2 p_1 = e_1, 3 p_2 = e_2, 4 ...) 5AT (0,0,0) ABSOLUTE 6 7COMPONENT name1 = comp... 8AT (...) ROTATED (...) 9 10GROUP GroupName EXTEND 11%{ 12 if (SCATTERED) printf("I scatter"); else printf("I do not scatter"); 13%} 14 15COMPONENT} name2 = comp ... 16AT (...) ROTATED (...) 17GROUP GroupName 18
Components name1 and name2 are at the same position. If the first one intercepts the neutron (and has a SCATTER within its TRACE section), the SCATTERED variable becomes true, the code extension will result in printing "I scatter", and the second component will be skipped. Thus, we recommend to make use of the SCATTER keyword each time a component ’uses’ the neutron (scatters, detects, …) within component definitions (see section 4.5). Also, the components to be grouped should be consecutive in the TRACE section of the instrument, and the GROUPed section should not contain components which are not part of the group.
A usage example of the GROUP keyword can be found in the
Neutron site/ILL/ILL_H15_IN6 instrument from the mcgui, to model 3 monochromators.
Combining EXTEND, GROUP and WHEN can result in unexpected behavior. Please read the related warning at the end of section 4.4.4.
Often, one has a set of similar component instances in an instrument. These could be e.g. a set of identical monochromator blades, or a set of detectors or guide elements. Together with JUMPs (see below), there is a way to copy a component instance, duplicating parameter set.
Position (AT) and rotation (ROTATED) specification must be explicitly entered in order to avoid component overlapping, and it is futher recommended to explicitly set any EXTEND, GROUP, JUMP and WHEN keyword to each mother or copied instance. But BEWARE, advanced combinations of (many) keywords may introduce unexpected behaviour.
The syntax for instance copy is
1COMPONENT name = COPY (instance_name) 2
where instance_name is the name of a preceding component instance in the instrument. It may be ’PREVIOUS’ as well.
If you would like to change only some of the parameters in the instance copy, you may write, e.g.:
1COMPONENT name = COPY (instance_name)(par1=0, par2=1) 2
which will override the original instance parameter values. This possibility to override parameters is very useful in case of describing e.g. sample environments using the Isotropic_Sqw and PowderN components, which allow concentric geometry (first instance must have concentric = 1 and the second concentric = 0). In case EXTEND, GROUP, JUMP and WHEN keywords are defined for the copied instance, these will override the settings from the copied instance.
In the case where there are many duplicated components all originating from the same instance, there is a mechanism for automating copied instance names:
1COMPONENT COPY(root_name) = COPY(instance_name) 2
will concatenate a unique number to root_name, avoiding name conflicts. As a side effect, referring to this component instance (for e.g. further positioning) is not straight forward as the name is determined by mcstas and does not depend completely on the user’s choice, even though the PREVIOUS keyword may still be used. We thus recommend to use this naming mechanism only for components which should not be referred to in the instrument.
This automatic naming may be used anywhere in the TRACE section of the instrument, so that all components which do not need further referring may be labeled as COPY(Origin).
As an example, we show how to build a guide made of equivalent elements. Only the first instance of the Guide component is defined, whereas following instances are copies of that definition. The instance name of Guide components is set automatically.
1COMPONENT CG_In = Arm() AT (...) 2 3COMPONENT CG_1 = Guide_gravity(l=L/n, m=1, ...) 4 AT (0,0,0) RELATIVE PREVIOUS 5 6COMPONENT COPY(CG_1) = COPY(CG_1) 7 AT (0,0,L/n+d) RELATIVE PREVIOUS 8 ROTATED (0, (L/n+d)/R*180/PI, 0) RELATIVE PREVIOUS 9 10COMPONENT COPY(CG_1) = COPY(CG_1) 11 AT (0,0,L/n+d) RELATIVE PREVIOUS 12 ROTATED (0, (L/n+d)/R*180/PI, 0) RELATIVE PREVIOUS 13... 14COMPONENT CG_Out = Arm() AT (0,0,L/n) RELATIVE PREVIOUS 15
One of the most useful features of the extended McStas syntax is the conditional WHEN modifier. This optional keyword comes before the AT-ROTATED positioning. It basically enables the component only when a given condition is true (non null).
1 COMPONENT name = comp (p_1 = e_1, p_2 = e_2, ...) 2 WHEN condition 3
The condition has the same scope as the EXTEND modifier, i.e. may use component internal variables as well as all the DECLARE instrument variables.
Usage examples could be to have specific monitors only sensitive to selected processes, or to have components which are only present under given circumstances (e.g. removable guide or radial collimator), or to select a sample among a set of choices.
In the following example, an EXTEND block sets a condition when a scattering event is encountered, and the following monitor is then activated.
1COMPONENT Sample = V_sample(...) AT ... 2 EXTEND 3 %{ 4 if (SCATTERED) flag=1; else flag=0; 5 %} 6 7COMPONENT MyMon = Monitor(...) WHEN (flag==1) 8 AT ... 9
The WHEN keyword only applies to the TRACE section and related EXTEND blocks of instruments/components. Other sections (INITIALIZE, SAVE, MCDISPLAY, FINALLY) are executed independently of the condition. As a side effect, the 3D view of the instrument (mcdisplay) will show all components as if all conditions were true.
Also, the WHEN keyword is a condition for GROUP. This means that when the WHEN is false, the component instance is not active in the GROUP it belongs to.
A usage example of the WHEN keyword can be found in the
Neutron site/ILL/ILL_TOF_Env instrument from the mcgui, to monitor neutrons depending
on their fate.
WARNING: Combining WHEN, EXTEND and GROUP can result in unexpected behavior, please use with caution! Let for instance a GROUP of components all have the same WHEN condition, i.e. if the WHEN condition is false, none of the elements SCATTER, meaning that all neutrons will be ABSORBed. As a solution to this problem, we propose to include an EXTENDed Arm component in the GROUP, but with the opposite WHEN condition and a SCATTER keyword in the EXTEND section. This means that when none of the other GROUP elements are present, the Arm will be present and SCATTER.
There are situations for which one would like to repeat a given component many times, or under a given condition. The JUMP modifier is meant for that and should be placed after the positioning, GROUP and EXTEND. This breaks the sequential propagation along components in the instrument description. There may be more than one JUMP per component instance.
The jump may depend on a condition:
1COMPONENT name = comp(p_1 = e_1, p_2 = e_2, ...) 2 AT (...) 3 JUMP reference WHEN condition 4
in which case the instrument TRACE will jump to the reference when condition is true.
The reference may be an instance name, as well as PREVIOUS, PREVIOUS(\(n\)), MYSELF, NEXT, and NEXT(\(n\)), where \(n\) is the index gap to the target either backward (PREVIOUS) or forward (NEXT), so that PREVIOUS(1) is PREVIOUS and NEXT(1) is NEXT. MYSELF means that the component will be iterated as long as the condition is true. This may be a way to handle multiple scattering, if the component has been designed for that.
The jump arrives directly inside the target component, in the local coordinate system (i.e. without applying the AT and ROTATED keywords). In order to control better the target positions, it is required that, except for looping MYSELF, the target component type should be an Arm.
There is a more general way to iterate components, which consists in repeating the loop for a given number of times.
1 JUMP reference ITERATE number_of_times 2
This method is specially suited for very long curved guides of similar components, but in order to take into account rotation and translation between guide sections, the iterations are performed between Arm’s.
In the following example for a curved guide made on \(n=500\) elements of length \(L\) on a curvature radius \(R\), with gaps \(d\) between elements, we simply write:
1COMPONENT CG_In = Arm() AT (...) 2 3COMPONENT CG_1 = Guide_gravity(l=L/n, m=1, ...) 4 AT (0,0,0) RELATIVE PREVIOUS 5 6COMPONENT CG_2_Position = Arm() 7 AT (0,0,L/n+d) RELATIVE PREVIOUS 8 ROTATED (0, (L/n+d)/R*180/PI, 0) RELATIVE PREVIOUS 9 10COMPONENT CG_2 = Guide_gravity(l=L/n, m=1, ...) 11 AT (0,0,0) RELATIVE PREVIOUS 12 ROTATED (0, (L/n+d)/R*180/PI, 0) RELATIVE PREVIOUS 13 JUMP CG_2_Position ITERATE n 14... 15COMPONENT CG_Out = Arm() AT (0,0,L/n) RELATIVE PREVIOUS 16
Similarly to the WHEN modifier (see section 4.4.4), JUMP only applies within the TRACE section of the instrument definition. Other sections (INITIALIZE, SAVE, MCDISPLAY, FINALLY) are executed independently of the jump. As a side effect, the 3D view of the instrument (mcdisplay) will show components as if there was no jump. This means that in the following example, the very long guide 3D view only shows a single guide element.
It is not recommended to use the JUMP inside GROUPs, as the JUMP condition/counter applies to the component instance within its group.
We would like to emphasize the potential errors originating from such jumps. Indeed, imbricating many jumps may lead to situations were it is difficult to understand the flow of the simulation. We thus recommend the usage of JUMPs only for experienced and cautious users.
The following method applies when the incoming neutron event distribution is considered to be representative of the real beam, but neutrons are lost in the course of propagation (with low efficiency processes, absorption, etc). Then, one may think that it’s a pity to have so few events reaching the ’interesting’ part of the instrument (usually close to the end of the instrument description). If some components make extensive use of random numbers (MC choices), they shuffle this way the distributions, so that identical incoming events will not produce the same outgoing event. In this case, you may use the SPLIT keyword with the syntax
1 SPLIT r COMPONENT name = comp(...) 2
where the optional number \(r\) specifies the number of repetitions for each event. Default is \(r=10\). Each neutron event reaching component name will be repeated \(r\) times with a weight divided by \(r\), so that in practice the number of events for the remaining part of the simulation (down to the END), will potentially have more statistics. This is only true if following components (and preferably component name) use random numbers. You may use this method as many times as you wish in the same instrument, e.g. at the monochromator and sample position. This keyword can also be used within a GROUP. The efficiency is roughly \(r\) raised to the number of occurrences in the instrument, so that enhancing two components with the default \(r=10\) will produce at the end an enhancement effect of 100 in the number of events. The execution time will usually get slightly longer. This technique is known as the stratified sampling (see Appendix 2). If the instrument makes use of global variables - e.g. in conjunction with a WHEN or User Variable monitoring (see Monitor_nD) - you should take care that these variables are set properly for each SPLIT loop, which usually means that they must be reset inside the SPLITed section and assigned/used further on.
A usage example of the SPLIT keyword can be found in the
Neutron site/ILL/ILL_H15_IN6 instrument from the mcgui, to enhance statistics for neutrons
scattering on monochromators and sample.
The METADATA keyword attaches an arbitrary, named, typed block of text to a component definition, a component instance, or an instrument. It has no effect whatsoever on the simulated physics; it exists so that instrument/component authors can embed structured side-information (an IDF fragment, a JSON blob of geometry metadata, a CIF snippet, a free-form note, …) that downstream tools can retrieve without having to parse the generated C code. This is how, e.g., mcrun –format=NeXus –IDF (section 3.4) locates instrument-definition-file fragments attached to detector components.
The syntax is
1METADATA type name 2%{ 3 ... arbitrary text ... 4%} 5
placed after a component instance’s positioning/EXTEND/JUMP clauses in the TRACE section, or inside a component definition (any number of METADATA blocks may be given). type and name are either bare identifiers or quoted strings (the latter allowing e.g. MIME-style types such as "application/json"); name identifies this particular metadata block within its owning component/instrument.
Metadata attached at the component-definition level is inherited by every instance of that component (and, per the usual INHERIT rules, by any component that INHERITs from it); metadata attached to a specific instance in the TRACE section applies only to that instance, and instance-level METADATA with the same name overrides definition-level METADATA of that name for that instance.
Metadata blocks are inspected from the command line via mcrun: –meta-list prints all component:name pairs carrying metadata in the instrument, –meta-defined=comp[:name] indicates whether a given metadata name is defined (or lists all defined names for a component), –meta-type=comp:name prints its declared type, and –meta-data=comp:name prints its content – see Table 3.2 in the User Manual.