Skip to content

Reflectometry Composite Driving Layer

John Holt edited this page Nov 26, 2019 · 32 revisions

Wiki > The Backend System > Specific Device IOC > Miscellaneous motion control > Reflectometry IOC > Composite Driving Layer

The composite driving layer sets the PV values which will make the moves happen.

The layer consists of drivers which take setpoint values from components and push these values into a PV wrapper, and take readback values and push them into the components. The types we currently have are:

  • DisplacementDrivers: Set the position of the motor based on the displacement, this includes moving to a parked value if the component is out of the beam
  • AngleDriver: Set the position of the motor based on the angle.

both are derived from a common base IocDriver.

There are various PV wrapper types which handle the mapping of parameters to the correct PVs based on different conventions. These are:

  • PVWrapper: base wrapper that has all the utility functions (e.g. monitors and change listeners) but will not point at a real PV
  • MotorPVWrapper: read and write to a motor PV
  • AxisPVWrapper: read and write to an axis
  • VerticalJawsPVWrapper: read and write to a jaw set without a height stage (height defined by vertical centre)

TODO: I think we need a parameter driver and then we can more easily separate SlitGapParameter from their PV wrappers.

Synchronisation

To enable synchronisation of axes this layer:

  • can report the minimum time taken for individual IOC driver to finish a move
  • can be told to make a move in a given duration.

When calculating the time for each axis to finish a move, the backlash distance and speed is taken into account to try to be accurate even if the backlash distance makes up a considerable part of the total move duration for the axis (ticket). There are two limitations with the system:

  • The acceleration time of the motors is not taken into account, so calculations will be slightly inaccurate
  • When starting a move from within backlash range the speed cannot be controlled, so unless the backlash speed is very slow and is the limiting factor on the entire move, this motor will finish its move earlier than the others.

The user can turn this off in the configuration file. In which case the time reported to finish a move is 0, and the axis's speed is not changed on move.

Synchronisation in the configuration files defaults to true but can be set with:

DisplacementDriver( ... , synchronised=False)

or

AngleDriver( ... , synchronised=False)

Engineering Offset

Engineering offsets correct the value sent to a PV because of inaccuracies in the engineering. For instance, if we set theta to 0.3 we will be setting the height of the jaw so that the jaws centre is in the middle of the beam. However, because of needing to tilt the jaws and the centre of rotation not being in the middle of the jaws, we need to add a correction to the geometry of 0.1mm. The best place to do this is at the point at which the value is sent to the driver. The form of the corrections can multiple but we will start by catering for:

  1. pure function based on the value and values of other components
  2. an interpolated table based on a set point

Note that in this case, the zero motor position is no longer necessarily zero, while this does not affect the maths, users will probably want to ensure that in the straight-through case the motor is zero.

The configuration for this is to add an engineering offset object to the IOCDriver. The engineering offset object will do the following:

  1. Convert readbacks from the IOC PVWrapper to the uncorrected value
  2. Convert set-points from the component to the correct value that get sent to the PV
  3. On initialisation to convert PV to set-point value to be initialised
    1. This is hard because to calculate the value you need a beamline parameter value which is not yet set because it is being calculated. To avoid this we introduce the constraint that engineering corrections may only be functions of an autosaved beamline parameter or the motor position/PV on which the driver is based.

Types of Engineering Corrections

No Correction

Doesn't perform any correction. Is useful when you want to define a correction but have it do nothing.

Constant Correction

Add a constant on to the value, probably better to redefine zero on the axis. Add this into the configuration file to the driver:

add_driver(DisplacementDriver(
    component, 
    PVWrapper, 
    engineering_correction=ConstantCorrection(value))

where value is the amount you want to add onto the axis when setting a set point.

Interpolated Data Correction

This will perform a correction based on a table loaded from disc. It will perform linear interpolation between the points in the datafile to derive the correction. Outside of the table of data no correction is set. To use this add to the configuration:

theta_param_angle = add_parameter(AngleParameter("THETA", theta_comp))

...

add_driver(DisplacementDriver(
    component, 
    PVWrapper, 
    engineering_correction=InterpolateGridDataCorrection(filename, theta_param_angle))

In this example, theta_param_angle is being used as the interpolation variable. The filename in the name of a file in the configuration directory (C:\Instrument\Settings\config\<instrument>\configurations\refl\) which contains the points. The format is:

Theta, correction
1.0, 1.2
1.01, 3.4
...
2.5, 4.6

Where the header Theta, correction matches with the name of the beamline parameter (first argument in XXXParameter). If you want to use the value sent to the displacement driver instead use DRIVER in the header.

If you want to do multi-dimension interpolation then your InterpolateGridDataCorrection object should have all beamline values in it that are needed, e.g.:

InterpolateGridDataCorrection(filename, theta_param_angle, sm_angle_param))

and the data file should have a similar header and data:

Theta, smangle, correction
-10, 10, 1
10, 10, 10
-10, -10, 2
10, -10, 20

The data points do not have to form a square.

If we take the above example we mark these four points on a graph:

4 points for a 2D example map

At point:

  • A (0,0) = 8.25. Point is equidistant from all points so is the average of all points.
  • B (10,0) = 15. Point is halfway between the two right-hand points so is average of right-hand points.
  • C (12,3) =0. Point is outside of the area made by the points and so its correction is 0.

The algorithm used is the linear version of griddata from scipy.

User Function Engineering Correction

To apply a user function engineering correction yo an axis we must first define the function in the configuration file. In this example case, we have a function which depends on a single beamline parameter, theta:

def my_correction(value, theta):
   ...
   return calulated_offset

When called by the reflectometry IOC this function will be given:

-value which is the position that would be sent to the axis if the correction was 0 -theta which is the value of the first beamline parameter set in UserFunctionCorrection

If there are more beamline parameters set in UserFunctionCorrection these would be additional arguments. The function should return a correction which will be added onto the value before being sent to the PV. The next step is to add the engineering correction to the IOC driver. We show the definition of the theta beamline parameter to make it clear what the arguments are:

theta_param_angle = add_parameter(AngleParameter("THETA", theta_comp))

...

add_driver(DisplacementDriver(
    component, 
    PVWrapper, 
    engineering_correction=UserFunctionCorrection(my_correction, theta_param_angle))

The UserFunctionCorrection takes:

  • user_correction_function: reference to the function to use
  • beamline parameters: 0 or more beamline parameters whose set point readback values should be passed to the user_correction_function

User Specified

If you wish to write your own engineering_correction you must:

  1. either inherit from SymmetricEngineeringCorrection: this used when the same correction is added when setting a value on an axis as is subtracted when getting a value from an axis. To set the correction override correction(self, setpoint) which returns the correction to add/subtract based on the setpoint.
  2. or inherit from EngineeringCorrection: this allows different correction to be used when setting and getting value to and from the axis. The following must be overridden:
    • to_axis(self, setpoint): given a setpoint return the correct value to send to the axis
    • from_axis(self, value, setpoint): given the value read from the axis and setpoint for the axis return the value without the correction
Clone this wiki locally