Instrument definition files =========================== Instrument layouts are specified in RAMP using instrument definition files written in Javascript Object Notation (JSON). JSON is a widely adopted format for describing objects as a heirarchical list of key-value pairs, and there are a `number of readily available learning resources `_ online which describe the format better than I could here. The hierarchy of objects in the RAMP instrument definition file goes *execution block* -> *component* -> *kernels* -> *attributes*, each of which are described in turn below. Execution blocks ---------------- Top level objects in RAMP instrument definition files are execution blocks. These blocks are used to separate sections of the instrument into linear and non-linear execution. The name of execution block objects is arbitrary. In linear execution blocks, each successive component is executed in turn a single time. This is useful when simulating, for example, guide sections, where each neutron is expected to proceed in an orderly fashion from one guide component to the next. In non-linear execution blocks, the geometry kernel of every component is executed and the earliest intersected component for each neutron is stored. Then, only the scattering kernel for the earliest intersected component acts upon the neutron. This is useful when simulating, for example, sample environments, where there is no guarantee that neutrons will intersect components in the order they are specified in the instrument definition file. The following is an example of a truncated instrument definition file containing two blocks: a linearly executed guide section and a non-linearly executed sample environment .. code-block:: JSON { "guides" : { "linear" : true, ... }, "sample_env" : { "linear" : false ... } } Components ---------- Below the level of execution blocks come component definitions. An execution block contains several components. The position and rotation of a component are specified using the ``position`` and ``rotation`` attributes of the component object. Both attributes are specified as an array of three floating point values. For ``position`` these are the Cartesian coordinates of the component in meters and for ``rotation`` these are the rotation angles (in radians) about the Cartesian axes with the component at the origin, in the order z, y, x. The position and rotation of a component can also be specified relative to a component declared earlier in the instrument definition file using the ``relative`` attribute. This is best demonstrated by example - the following is a truncated instrument definition file demonstrating a basic monochromator setup using relative rotations .. code-block:: JSON { "all" : { "linear" : true, "neutron_source" : { "source": true, "position" : [0.0, 0.0, 0.0], ... }, "Mono_arm" : { "position" : [0.0, 0.0, 1.0], "rotation" : [0.0, 1.0, 0.0], "relative" : "neutron_source" ... }, "Mono_out" : { "position" : [0.0, 0.0, 0.0], "rotation" : [0.0, 1.0, 0.0], "relative" : "Mono_arm", ... }, "mono" : { "position" : [0.0, 0.0, 0.0], "relative" : "Mono_arm", ... }, "sample" : { "position" : [0.0, 0.0, 1.0], "relative" : "Mono_out", ... } } } Another important component level attribute is the ``restore_neutron`` attribute. In linear execution mode, if a neutron does not intersect with the geometry of a given component during its execution step, the neutron is 'terminated', i.e. it will no longer interact with later components in the instrument. The ``restore_neutron`` flag prevents neutrons from being terminated in this case. This is useful in situations where the path of neutrons through the instrument 'splits', for example if an energy monitor is placed such that it monitors the transmitted beam through a monochromator. When set to ``true``, the ``restore_neutron`` flag will ensure that the reflected neutrons are not terminated while the transmitted beam is being detected. Kernels ------- All of the calculations in RAMP are handled by OpenCL kernels - programs which run on OpenCL capable devices. There are three classes of kernel in a RAMP simulation: - Moderator kernels - Geometry kernels - Scattering kernels Moderator kernels ~~~~~~~~~~~~~~~~~ Typically an instrument will contain a single component which executes a moderator kernel, to generate the neutrons at the beginning of the simulation. There is a special component level attribute which must be specified for neutron sources: the ``source`` attribute should be set to ``true``. For example, the following component defines an ISIS style moderator using the ``MISIS`` moderator kernel .. code-block:: JSON "mod" : { "source": true, "position" : [0.0, 0.0, 0.0], "moderator_kernel": { "name": "MISIS", ... } } Geometry and scattering kernels ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The kernels which dictate how a component interacts with neutrons are specified below the level of component objects in the kernel objects ``geom_kernel`` and ``scat_kernel``. Each component which is not a source should contain a ``geom_kernel`` and ``scat_kernel`` object. The ``name`` attribute of these objects specify which kernel the component should use. The other attributes of the kernel objects are used to specify the parameters of the scattering kernel. For example, one would specify the radius of a spherical geometry kernel, or the lattice spacing of a monochromator scattering kernel. The following is an example of the definition of a flat monochromator .. code-block:: JSON "mono" : { "position" : [0.0, 0.0, 0.0], "geom_kernel" : { "name": "GPlane", "width": 0.10, "height": 0.10, "orientation": "yz" }, "scat_kernel": { "name": "SMonochromator", "slab_width" : 0.1, "slab_height" : 0.1, "mosaic_horizontal" : 40, "mosaic_vertical" : 40, "r0" : 0.9, "d_spacing" : 3.53, "radius_vertical" : 0.0 } } Variables --------- It is often inconvenient to edit the instrument definition file every time a component parameter such as the angle of a monochromator must be adjusted. To remedy this, RAMP supports a non-standard notation for its JSON files to allow variable to be set directly from the Python script. Variable names surrounded by \$ signs inside instrument definition files will be substituted for by keyword arguments provided when the instrument is instantiated in Python. For example, if the following component was specified inside an instrument definition file ``inst.json`` .. code-block:: JSON "mod" : { "source": true, "position" : [0.0, 0.0, 0.0], "moderator_kernel": { "name": "MISIS", "spec_file": "Let_Base.mcstas", "mod_dim": [0.04, 0.09], "target_dim": [0.04, 0.09], "target_dist": 1.7, "E_min": $emin$, "E_max": $emax$ } }, The moderator attributes ``E_min`` and ``E_max`` could be set when the instrument is instantiated to 1.0 and 9.0, respectively, in Python via .. code-block:: Python inst = Instrument('inst.json', ctx, queue, emin=1.0, emax=9.0) The variable syntax also supports basic arithmetic. After the variable names have been substituted for the values specified, the resulting expression within the \$ signs is evaluated as a Python expression. For example, if the instrument definition file ``inst.json`` were to contain two choppers with the same constant phase offset but different initial phases, this could be specified as follows .. code-block:: JSON "Chopper1" : { "position" : [0.0, 0.0, 5.0], "geom_kernel" : { "name" : "GPlane", "width" : 0.5, "height" : 0.5 }, "scat_kernel" : { "name" : "SChopper", "radius": 0.5, "freq" : 314.1, "n_slits" : 6, "jitter" : 7e-7, "slit_width" : 0.04, "phase" : $initial_pha_chop1 + pha_offset$ } }, "Chopper2" : { "position" : [0.0, 0.0, 10.0], "geom_kernel" : { "name" : "GPlane", "width" : 0.5, "height" : 0.5 }, "scat_kernel" : { "name" : "SChopper", "radius": 0.5, "freq" : -314.1, "n_slits" : 6, "jitter" : 7e-7, "slit_width" : 0.04, "phase" : $initial_pha_chop2 + pha_offset$ } } and in the Python script .. code-block:: Python inst = Instrument( 'inst.json', ctx, queue, initial_pha_chop1 = 0.1, initial_pha_chop2 = 0.7, pha_offset = 55.0e-3 ) NOTE: once variables have been added to an instrument definition file it is no longer a strictly valid JSON file, and many programs that interpret JSON files will no longer properly load the instrument definition file. Comments -------- RAMP supports C-style single line comments in instrument definition files. Any text on a line following two forward slashes (``//``) will be ignored. For example, the following .. code-block:: JSON "mod" : { "source": true, // This is a comment! "position" : [0.0, 0.0, 0.0], "moderator_kernel": { "name": "MISIS", // This is another comment! "spec_file": "Let_Base.mcstas", "mod_dim": [0.04, 0.09], "target_dim": [0.04, 0.09], "target_dist": 1.7, "E_min": $emin$, "E_max": $emax$ } }, is equivalent to .. code-block:: JSON "mod" : { "source": true, "position" : [0.0, 0.0, 0.0], "moderator_kernel": { "name": "MISIS", "spec_file": "Let_Base.mcstas", "mod_dim": [0.04, 0.09], "target_dim": [0.04, 0.09], "target_dist": 1.7, "E_min": $emin$, "E_max": $emax$ } }, Complete example ---------------- The following is a complete example of an instrument definition file modelling the LET spectrometer at ISIS, incorporating all of the concepts discussed above .. code-block:: JSON { "all" : { "linear" : true, "mod" : { "source": true, "position" : [0.0, 0.0, 0.0], "moderator_kernel": { "name": "MISIS", "spec_file": "Let_Base.mcstas", "mod_dim": [0.04, 0.09], "target_dim": [0.04, 0.09], "target_dist": 1.7, "E_min": 1.1, "E_max": 9.0 } }, "moderator_Emon" : { "position" : [0.0, 0.0, 0.01], "geom_kernel" : { "name" : "GPlane", "width" : 0.1, "height" : 0.1 }, "scat_kernel" : { "name" : "SDetector1D", "binning" : [1.1, 0.05, 9.0], "var" : "energy", "restore_neutron" : true } }, "guide1" : { "position" : [0.0, 0.0, 1.680], "geom_kernel" : { "name": "GPlane", "width": 0.04, "height": 0.09 }, "scat_kernel" : { "name" : "SGuide", "w1" : 0.04, "h1" : 0.09, "w2" : 0.04, "h2" : 0.09, "l" : 1.98, "R0" : 1.0, "Qc" : 0.0218, "alpha" : 4.38, "m" : 2, "W" : 0.003 } }, "guide2" : { "position" : [0.0, 0.0, 3.740], "geom_kernel" : { "name": "GPlane", "width": 0.04, "height": 0.09 }, "scat_kernel" : { "name" : "SGuide", "w1" : 0.04, "h1" : 0.09, "w2" : 0.04, "h2" : 0.09, "l" : 2.50, "R0" : 1.0, "Qc" : 0.0218, "alpha" : 4.38, "m" : 2, "W" : 0.003 } }, "guide3" : { "position" : [0.0, 0.0, 6.30], "geom_kernel" : { "name": "GPlane", "width": 0.04, "height": 0.09 }, "scat_kernel" : { "name" : "SGuide", "w1" : 0.04, "h1" : 0.09, "w2" : 0.04, "h2" : 0.09, "l" : 1.514, "R0" : 1.0, "Qc" : 0.0218, "alpha" : 4.38, "m" : 2, "W" : 0.003 } }, "Res1" : { "position" : [0.0, 0.0, 7.83], "geom_kernel" : { "name" : "GPlane", "width" : 0.6, "height" : 0.6 }, "scat_kernel" : { "name" : "SChopper", "radius": 0.279, "freq" : 314.1, "n_slits" : 6, "jitter" : 7e-7, "slit_width" : 0.04, "phase" : $7.83 / v_foc + pha_offset$ } }, "Res1_counter" : { "position" : [0.0, 0.0, 7.830002], "geom_kernel" : { "name" : "GPlane", "width" : 0.6, "height" : 0.6 }, "scat_kernel" : { "name" : "SChopper", "radius": 0.279, "freq" : -314.1, "n_slits" : 6, "jitter" : 7e-7, "slit_width" : 0.04, "phase" : -$7.83 / v_foc + pha_offset$ } }, "guide4" : { "position" : [0.0, 0.0, 7.852], "geom_kernel" : { "name": "GPlane", "width": 0.04, "height": 0.09 }, "scat_kernel" : { "name" : "SGuide", "w1" : 0.04, "h1" : 0.09, "w2" : 0.04, "h2" : 0.09, "l" : 0.312, "R0" : 1.0, "Qc" : 0.0218, "alpha" : 4.38, "m" : 2, "W" : 0.003 } }, "guide5" : { "position" : [0.0, 0.0, 8.236], "geom_kernel" : { "name": "GPlane", "width": 0.04, "height": 0.09 }, "scat_kernel" : { "name" : "SGuide", "w1" : 0.04, "h1" : 0.09, "w2" : 0.04, "h2" : 0.09, "l" : 3.499, "R0" : 1.0, "Qc" : 0.0218, "alpha" : 4.38, "m" : 2, "W" : 0.003 } }, "PR" : { "position" : [0.0, 0.0, 11.75], "geom_kernel" : { "name" : "GPlane", "width" : 0.6, "height" : 0.6 }, "scat_kernel" : { "name" : "SChopper", "radius": 0.29, "freq" : 628.3, "n_slits" : 2, "jitter" : 7e-7, "slit_width" : 0.058, "phase" : $11.75 / v_foc + pha_offset$ } }, "guide6" : { "position" : [0.0, 0.0, 11.765], "geom_kernel" : { "name": "GPlane", "width": 0.04, "height": 0.09 }, "scat_kernel" : { "name" : "SGuide", "w1" : 0.04, "h1" : 0.09, "w2" : 0.04, "h2" : 0.09, "l" : 3.886, "R0" : 1.0, "Qc" : 0.0218, "alpha" : 4.38, "m" : 2, "W" : 0.003 } }, "CR" : { "position" : [0.0, 0.0, 15.66], "geom_kernel" : { "name" : "GPlane", "width" : 0.6, "height" : 0.6 }, "scat_kernel" : { "name" : "SChopper", "radius": 0.29, "freq" : 314.1, "n_slits" : 6, "jitter" : 7e-7, "slit_width" : 0.054, "phase" : $15.66 / v_foc + pha_offset$ } }, "guide7" : { "position" : [0.0, 0.0, 15.681], "geom_kernel" : { "name": "GPlane", "width": 0.04, "height": 0.09 }, "scat_kernel" : { "name" : "SGuide", "w1" : 0.04, "h1" : 0.09, "w2" : 0.04, "h2" : 0.0639, "l" : 5.807, "R0" : 1.0, "Qc" : 0.0218, "alpha" : 4.38, "m" : 2, "W" : 0.003 } }, "guide8" : { "position" : [0.0, 0.0, 21.489], "geom_kernel" : { "name": "GPlane", "width": 0.04, "height": 0.0639 }, "scat_kernel" : { "name" : "SGuide", "w1" : 0.04, "h1" : 0.0639, "w2" : 0.031, "h2" : 0.06, "l" : 0.7823, "R0" : 1.0, "Qc" : 0.0218, "alpha" : 4.38, "m" : 4, "W" : 0.003 } }, "funnel" : { "position" : [0.0, 0.0, 22.373], "geom_kernel" : { "name": "GPlane", "width": 0.031, "height": 0.05711 }, "scat_kernel" : { "name" : "SGuide", "w1" : 0.031, "h1" : 0.05711, "w2" : 0.02, "h2" : 0.04868, "l" : 1.117, "R0" : 1.0, "Qc" : 0.0218, "alpha" : 4.38, "m" : 4, "W" : 0.003 } }, "endguide" : { "position" : [0.0, 0.0, 23.52], "geom_kernel" : { "name": "GPlane", "width": 0.02, "height": 0.0484 }, "scat_kernel" : { "name" : "SGuide", "w1" : 0.02, "h1" : 0.0484, "w2" : 0.02, "h2" : 0.04, "l" : 1.1, "R0" : 1.0, "Qc" : 0.0218, "alpha" : 4.38, "m" : 4, "W" : 0.003 } }, "Emon" : { "position" : [0.0, 0.0, 25.0], "geom_kernel" : { "name": "GPlane", "width": 1.0, "height": 1.0 }, "scat_kernel" : { "name" : "SDetector1D", "binning": [0.0, 0.01, 10.0], "var" : "energy", "restore_neutron": true } }, "samplepos_divpos" : { "position" : [0.0, 0.0, 25.0], "geom_kernel": { "name": "GPlane", "width": 0.1, "height": 0.1 }, "scat_kernel": { "name": "SDetector2D", "axis1_binning": [-0.05, 0.001, 0.05], "axis2_binning": [-3.0, 0.01, 3.0], "axis1_var": "x", "axis2_var": "divX", "restore_neutron" : true } }, "sample" : { "position" : [0.0, 0.0, 25.0], "rotation" : [0.0, 0.0, 0.0], "geom_kernel" : { "name": "GSphere", "radius": 0.03 }, "scat_kernel": { "name": "SPowder1", "d_spacing": 14.2, "pack": 1.0, "vc": 85.0, "sigma_abs": 0.0, "multiplicity": 1, "DW": 1.0, "F2": 60.0 } }, "det" : { "position" : [0.0, 0.0, 25.0], "rotation" : [0.0, 0.0, 0.0], "geom_kernel": { "name": "GBanana", "radius": 0.5, "height": 0.1, "mintheta" : -80.0, "maxtheta" : 80.0 }, "scat_kernel": { "name": "SDetector2D", "axis1_binning": [-40.0, 1.0, 140.0], "axis2_binning": [22000, 50, 50000], "axis1_var": "theta", "axis2_var": "tof", "logscale" : true } } } }