"""This is the instrument implementation for the Larmor beamline."""
from logging import info
from .Instrument import ScanningInstrument
from .Util import dae_setter
from .genie import gen
[docs]def sleep(seconds):
"""Override the sleep function to use genie.
We need this override to ensure that simularted runs are forced to
wait for real sleeps."""
return gen.waitfor(seconds=seconds)
[docs]class Larmor(ScanningInstrument): # pylint: disable=too-many-public-methods
"""This class handles the Larmor beamline"""
_poslist = ['AB', 'BB', 'CB', 'DB', 'EB', 'FB', 'GB', 'HB', 'IB', 'JB',
'KB', 'LB', 'MB', 'NB', 'OB', 'PB', 'QB', 'RB', 'SB', 'TB',
'AT', 'BT', 'CT', 'DT', 'ET', 'FT', 'GT', 'HT', 'IT', 'JT',
'KT', 'LT', 'MT', 'NT', 'OT', 'PT', 'QT', 'RT', 'ST', 'TT',
'1CB', '2CB', '3CB', '4CB', '5CB', '6CB', '7CB',
'8CB', '9CB', '10CB', '11CB', '12CB', '13CB', '14CB',
'1CT', '2CT', '3CT', '4CT', '5CT', '6CT', '7CT',
'8CT', '9CT', '10CT', '11CT', '12CT', '13CT', '14CT',
'1WB', '2WB', '3WB', '4WB', '5WB', '6WB', '7WB',
'8WB', '9WB', '10WB', '11WB', '12WB', '13WB', '14WB',
'1WT', '2WT', '3WT', '4WT', '5WT', '6WT', '7WT',
'8WT', '9WT', '10WT', '11WT', '12WT', '13WT', '14WT']
step = 100.0
lrange = "0.9-13.25"
@property
def TIMINGS(self):
if self._dae_mode == "sesans":
return self._TIMINGS + ["u", "d"]
return self._TIMINGS
[docs] def set_measurement_type(self, value):
gen.set_pv("IN:LARMOR:PARS:SAMPLE:MEAS:TYPE", value)
[docs] def set_measurement_label(self, value):
gen.set_pv("IN:LARMOR:PARS:SAMPLE:MEAS:LABEL", value)
[docs] def set_measurement_id(self, value):
gen.set_pv("IN:LARMOR:PARS:SAMPLE:MEAS:ID", value)
[docs] def get_lrange(self):
"""Return the current wavelength range"""
return self.lrange
[docs] def set_lrange(self, lrange):
"""Set the current wavelength range"""
self._dae_mode = ""
self.lrange = lrange
[docs] def get_tof_step(self):
"""Get the current TOF step for the tcb"""
return self.step
[docs] def set_tof_step(self, step):
"""Set the current TOF step for the tcb"""
self._dae_mode = ""
self.step = step
@staticmethod
def _generic_scan( # pylint: disable=dangerous-default-value
detector=r"C:\Instrument\Settings\Tables\detector.dat",
spectra=r"C:\Instrument\Settings\Tables\spectra_1To1.dat",
wiring=r"C:\Instrument\Settings\Tables\wiring.dat",
tcbs=[]):
ScanningInstrument._generic_scan(detector, spectra, wiring, tcbs)
@staticmethod
def _set_choppers(lrange):
# now set the chopper phasing to the defaults
# T0 phase checked for November 2015 cycle
# Running at 5Hz and centering the dip from the T0 at 50ms by
# setting phase to 48.4ms does not stop the fast flash
# Setting the T0 phase to 0 (50ms) does
if lrange == "0.9-13.25":
# This is for 0.9-13.25
gen.cset(T0Phase=0)
gen.cset(TargetDiskPhase=2750)
gen.cset(InstrumentDiskPhase=2450)
elif lrange == "0.65-12.95":
# This is for 0.65-12.95
gen.cset(TargetDiskPhase=1900)
gen.cset(InstrumentDiskPhase=1600)
else:
raise RuntimeError(
"The only known lranges for the chopper "
"are '0.9-13.25' and '0.65-12.95'")
[docs] @dae_setter("SCAN", "scan")
def setup_dae_scanning(self):
Larmor._generic_scan(
spectra=r"C:\Instrument\Settings\Tables\spectra_scanning_80.dat",
tcbs=[{"low": 5.0, "high": 100000.0, "step": 100.0,
"trange": 1, "log": 0}])
[docs] @dae_setter("SCAN", "scan")
def setup_dae_nr(self):
Larmor._generic_scan(
spectra=r"C:\Instrument\Settings\Tables\spectra_nrscanning.dat",
tcbs=[{"low": 5.0, "high": 100000.0, "step": 100.0,
"trange": 1, "log": 0}])
[docs] @dae_setter("SCAN", "scan")
def setup_dae_nrscanning(self):
Larmor._generic_scan(
spectra=r"U:\Users\Masks\spectra_scanning_auto.dat",
tcbs=[{"low": 5.0, "high": 100000.0, "step": 100.0,
"trange": 1, "log": 0}])
[docs] @dae_setter("SANS", "sans")
def setup_dae_event(self):
# Normal event mode with full detector binning
Larmor._generic_scan(
wiring=r"C:\Instrument\Settings\Tables\wiring_event.dat",
tcbs=[{"low": 5.0, "high": 100000.0, "step": self.step,
"trange": 1, "log": 0},
{"low": 0.0, "high": 0.0, "step": 0.0,
"trange": 2, "log": 0},
{"low": 5.0, "high": 100000.0, "step": 2.0, "trange": 1,
"log": 0, "regime": 2}])
self._set_choppers(self.lrange)
[docs] @dae_setter("SANS", "sans")
def setup_dae_event_fastsave(self):
"""Event mode with reduced detector histogram binning to decrease
filesize."""
# Event mode with reduced detector histogram binning to
# decrease filesize
# This currently breaks mantid nexus read
Larmor._generic_scan(
wiring=r"C:\Instrument\Settings\Tables\wiring_event_fastsave.dat",
# change to log binning to reduce number of detector bins
# by a factor of 10 to decrease write time
tcbs=[{"low": 5.0, "high": 100000.0, "step": 0.1,
"trange": 1, "log": 1},
{"low": 0.0, "high": 0.0, "step": 0.0,
"trange": 2, "log": 0},
{"low": 5.0, "high": 100000.0, "step": 2.0, "trange": 1,
"log": 0, "regime": 2},
# 3rd time regime for monitors to allow flexible
# binning of detector to reduce file size and
# decrease file write time
{"low": 5.0, "high": 100000.0, "step": self.step,
"trange": 1, "log": 0, "regime": 3},
{"low": 0.0, "high": 0.0, "step": 0.0, "trange": 2,
"log": 0, "regime": 3}])
self._set_choppers(self.lrange)
[docs] @dae_setter("SANS", "sans")
def setup_dae_histogram(self):
gen.change_sync('isis')
Larmor._generic_scan(
tcbs=[{"low": 5.0, "high": 100000.0, "step": 100.0,
"trange": 1, "log": 0},
{"low": 0.0, "high": 0.0, "step": 0.0,
"trange": 2, "log": 0}])
self._set_choppers(self.lrange)
[docs] @dae_setter("TRANS", "transmission")
def setup_dae_transmission(self):
gen.set_pv("IN:LARMOR:PARS:SAMPLE:MEAS:TYPE", "transmission")
gen.change_sync('isis')
Larmor._generic_scan(
r"C:\Instrument\Settings\Tables\detector_monitors_only.dat",
r"C:\Instrument\Settings\Tables\spectra_monitors_only.dat",
r"C:\Instrument\Settings\Tables\wiring_monitors_only.dat",
[{"low": 5.0, "high": 100000.0, "step": 100.0,
"trange": 1, "log": 0},
{"low": 0.0, "high": 0.0, "step": 0.0,
"trange": 2, "log": 0}])
self._set_choppers(self.lrange)
[docs] @staticmethod
@dae_setter("TRANS", "transmission")
def setup_dae_monotest():
"""Setup with a mono test?"""
Larmor._generic_scan(
tcbs=[{"low": 5.0, "high": 100000.0, "step": 100.0,
"trange": 1, "log": 0},
{"low": 0.0, "high": 0.0, "step": 0.0,
"trange": 2, "log": 0}])
gen.cset(T0Phase=0)
gen.set_pv("IN: LARMOR: MK3CHOPR_01: CH2: DIR: SP", "CW")
gen.cset(TargetDiskPhase=8200)
gen.set_pv("IN: LARMOR: MK3CHOPR_01: CH3: DIR: SP", "CCW")
gen.cset(InstrumentDiskPhase=77650)
[docs] @staticmethod
@dae_setter("SANS", "sans")
def setup_dae_tshift(tlowdet=5.0, thighdet=100000.0, tlowmon=5.0,
thighmon=100000.0):
"""Allow m1 to count as normal but to shift the rest of the detectors
in order to allow counting over the frame.
"""
Larmor._generic_scan(
wiring=r"C:\Instrument\Settings\Tables\wiring_tshift.dat",
tcbs=[{"low": tlowdet, "high": thighdet, "step": 100.0,
"trange": 1, "log": 0},
{"low": 0.0, "high": 0.0, "step": 0.0,
"trange": 2, "log": 0},
{"low": tlowmon, "high": thighmon, "step": 20.0, "trange": 1,
"log": 0, "regime": 3}])
[docs] @staticmethod
@dae_setter("SANS", "sans")
def setup_dae_diffraction():
"""Set the wiring tables for a diffraction measurement"""
Larmor._generic_scan(
tcbs=[{"low": 5.0, "high": 100000.0, "step": 0.01,
"trange": 1, "log": 1},
{"low": 0.0, "high": 0.0, "step": 0.0,
"trange": 2, "log": 0}])
[docs] @staticmethod
@dae_setter("SANS", "sans")
def setup_dae_polarised():
"""Set the wiring tables for a polarisation measurement."""
Larmor._generic_scan(
tcbs=[{"low": 5.0, "high": 100000.0, "step": 100.0, "trange": 1},
{"low": 0.0, "high": 0.0, "step": 0.0,
"trange": 2, "log": 0}])
[docs] @dae_setter("SANS", "sans")
def setup_dae_bsalignment(self):
Larmor._generic_scan(
tcbs=[{"low": 1000.0, "high": 100000.0, "step": 99000.0,
"trange": 1, "log": 0},
{"low": 0.0, "high": 0.0, "step": 0.0,
"trange": 2, "log": 0}])
[docs] @staticmethod
@dae_setter("TRANS", "transmission")
def setup_dae_monitorsonly():
"""Set the wiring tables to record only the monitors."""
Larmor._generic_scan(
spectra=r"C:\Instrument\Settings\Tables\spectra_phase1.dat",
tcbs=[{"low": 5.0, "high": 100000.0, "step": 20.0,
"trange": 1, "log": 0},
{"low": 0.0, "high": 0.0, "step": 0.0,
"trange": 2, "log": 0}])
[docs] @staticmethod
@dae_setter("SANS", "sans")
def setup_dae_resonantimaging():
"""Set the wiring table for resonant imaging"""
Larmor._generic_scan(
r"C:\Instrument\Settings\Tables\detector_monitors_only.dat",
r"C:\Instrument\Settings\Tables\spectra_monitors_only.dat",
r"C:\Instrument\Settings\Tables\wiring_monitors_only.dat",
[{"low": 5.0, "high": 1500.0, "step": 0.256,
"trange": 1, "log": 0},
{"low": 1500.0, "high": 100000.0, "step": 100.0,
"trange": 2, "log": 0}])
[docs] @staticmethod
@dae_setter("SANS", "sans")
def setup_dae_resonantimaging_choppers(): # pylint: disable=invalid-name
"""Set the wiring thable for resonant imaging choppers"""
info("Setting Chopper phases")
gen.cset(T0Phase=49200)
gen.cset(TargetDiskPhase=0)
gen.cset(InstrumentDiskPhase=0)
[docs] @staticmethod
@dae_setter("SANS", "sans")
def setup_dae_4periods():
"""Setup the instrument with four periods."""
Larmor._generic_scan(
r"C:\Instrument\Settings\Tables\detector.dat",
r"C:\Instrument\Settings\Tables\spectra_4To1.dat",
r"C:\Instrument\Settings\Tables\wiring.dat",
[{"low": 5.0, "high": 100000.0, "step": 100.0,
"trange": 1, "log": 0},
{"low": 0.0, "high": 0.0, "step": 0.0, "trange": 2, "log": 0}])
[docs] @dae_setter("SESANS", "sesans")
def setup_dae_sesans(self):
"""Setup the instrument for SESANS measurements."""
self.setup_dae_event()
@staticmethod
def _begin_sesans():
"""Initialise a SESANS run"""
gen.change(nperiods=2)
gen.begin(paused=1)
@staticmethod
def _waitfor_sesans(u=1000, d=1000,
**kwargs): # pylint: disable=invalid-name
"""Perform a SESANS run"""
if "uamps" in kwargs:
get_total = gen.get_uamps
key = "uamps"
else:
get_total = gen.get_frames
key = "frames"
gfrm = gen.get_frames()
gtotal = get_total()
while gtotal < kwargs[key]:
gen.change(period=1)
info("Flipper On")
gen.flipper1(1)
gfrm = gen.get_frames()
gen.resume()
gen.waitfor(frames=gfrm+u)
gen.pause()
gen.change(period=2)
info("Flipper Off")
gen.flipper1(1)
gfrm = gen.get_frames()
gen.resume()
gen.waitfor(frames=gfrm+d)
gen.pause()
gtotal = get_total()
@staticmethod
def set_aperature(size):
if size.upper() == "MEDIUM":
gen.cset(a1hgap=20.0, a1vgap=20.0, s1hgap=14.0, s1vgap=14.0)
def _configure_sans_custom(self):
# move the transmission monitor out
gen.cset(m4trans=200.0)
def _configure_trans_custom(self):
# move the transmission monitor in
gen.cset(m4trans=0.0)
@staticmethod
def _detector_is_on():
"""Is the detector currently on?"""
voltage_status = all([
gen.get_pv(
"IN:LARMOR:CAEN:hv0:0:{}:status".format(x)).lower() == "on"
for x in [8, 9, 10, 11]])
return voltage_status
@staticmethod
def _detector_turn_on(delay=True):
gen.set_pv("IN:LARMOR:CAEN:hv0:0:8:pwonoff", "On")
gen.set_pv("IN:LARMOR:CAEN:hv0:0:9:pwonoff", "On")
gen.set_pv("IN:LARMOR:CAEN:hv0:0:10:pwonoff", "On")
gen.set_pv("IN:LARMOR:CAEN:hv0:0:11:pwonoff", "On")
if delay:
info("Waiting For Detector To Power Up (180s)")
sleep(180)
@staticmethod
def _detector_turn_off(delay=True):
gen.set_pv("IN:LARMOR:CAEN:hv0:0:8:pwonoff", "Off")
gen.set_pv("IN:LARMOR:CAEN:hv0:0:9:pwonoff", "Off")
gen.set_pv("IN:LARMOR:CAEN:hv0:0:10:pwonoff", "Off")
gen.set_pv("IN:LARMOR:CAEN:hv0:0:11:pwonoff", "Off")
if delay:
info("Waiting For Detector To Power Down (60s)")
sleep(60)
# Instrument Specific Scripts
[docs] @staticmethod
def FOMin(): # pylint: disable=invalid-name
"""Put the frame overload mirror into the beam."""
# gen.cset(pol_trans=0, pol_arc=-1.6)
# Convert to angle instead of mm
gen.cset(pol_trans=0, pol_arc=-0.084)
[docs] @staticmethod
def ShortPolariserin(): # pylint: disable=invalid-name
"""Put the short polariser for long wavelength into the beam."""
# gen.cset(pol_trans=-100, pol_arc=-1.3)
# Convert to angle instead of mm
gen.cset(pol_trans=-100, pol_arc=-0.069)
[docs] @staticmethod
def LongPolariserin(): # pylint: disable=invalid-name
"""Put the long polariser for short wavelengths into the beam."""
# gen.cset(pol_trans=100, pol_arc=-1.3)
# Convert to angle instead of mm
gen.cset(pol_trans=100, pol_arc=-0.069)
[docs] @staticmethod
def BSInOut(In=True): # pylint: disable=invalid-name
"""Move the Beam Stop in and out of the beam.
Parameters
----------
In : bool
Whether to move the beam stop in or out
"""
# move beamstop in or out. The default is to move in
if In:
gen.cset(BSY=88.5, BSZ=353.0)
else:
gen.cset(BSY=200.0, BSZ=0.0)
@staticmethod
def _generic_home_slit(slit):
# home north and west
gen.set_pv(slit + "JN: MTR.HOMR", "1")
gen.set_pv(slit + "JW: MTR.HOMR", "1")
gen.waitfor_move()
gen.set_pv(slit + "JN: MTR.VAL", "20")
gen.set_pv(slit + "JW: MTR.VAL", "20")
# home south and east
gen.set_pv(slit + "JS: MTR.HOMR", "1")
gen.set_pv(slit + "JE: MTR.HOMR", "1")
gen.waitfor_move()
gen.set_pv(slit + "JS: MTR.VAL", "20")
gen.set_pv(slit + "JE: MTR.VAL", "20")
gen.waitfor_move()
[docs] @staticmethod
def homecoarsejaws():
"""Rehome coarse jaws."""
info("Homing Coarse Jaws")
gen.cset(cjhgap=40, cjvgap=40)
gen.waitfor_move()
Larmor._generic_home_slit("IN: LARMOR: MOT: JAWS1: ")
[docs] @staticmethod
def homea1():
"""Rehome aperature 1."""
info("Homing a1")
gen.cset(a1hgap=40, a1vgap=40)
Larmor._generic_home_slit("IN: LARMOR: MOT: JAWS2: ")
gen.waitfor_move()
[docs] @staticmethod
def homes1():
"""Rehome slit1."""
info("Homing s1")
gen.cset(s1hgap=40, s1vgap=40)
gen.waitfor_move()
Larmor._generic_home_slit("IN: LARMOR: MOT: JAWS3: ")
[docs] @staticmethod
def homes2():
"""Rehome slit2. This is currentl a no-op."""
info("Homing s2")
[docs] def movebench(self, angle=0.0, delaydet=True):
"""Safely move the downstream arm"""
info("Turning Detector Off")
self.detector_on(False, delay=delaydet)
self.rotatebench(angle)
# turn the detector back on
info("Turning Detector Back on")
self.detector_on(True, delay=delaydet)
[docs] def rotatebench(self, angle=0.0):
"""Move the downstream arm"""
if self.detector_on():
info("The detector is not turned off")
info("Not attempting Move")
return
else:
info("The detector is off")
if angle >= -0.5:
gen.cset(benchlift=1)
info("Lifting Bench (20s)")
sleep(20)
if gen.get_pv("IN: LARMOR: BENCH: STATUS") == 1:
info("Rotating Bench")
gen.cset(bench_rot=angle)
gen.waitfor_move()
info("Lowering Bench (20s)")
gen.cset(benchlift=0)
sleep(20)
else:
info("Bench failed to lift")
info("Move not attempted")
[docs] @staticmethod
def setup_pi_rotation():
"""Initialise the pi flipper."""
script = ["*IDN?", "ERR?", "SVO 1 1", "RON 1 1",
"VEL 1 180", "ACC 1 90", "DEC 1 90"]
gen.set_pv("IN: LARMOR: SDTEST_01: P2: COMM", script[0])
for line in script[1:]:
sleep(1)
gen.set_pv("IN: LARMOR: SDTEST_01: P2: COMM", line)
[docs] @staticmethod
def home_pi_rotation():
"""Calibrate the pi flipper."""
gen.set_pv("IN: LARMOR: SDTEST_01: P2: COMM", "FRF 1")