Source code for src.instruments.heliox

"""A software representation of the Oxford Heliox 3He insert."""

from ConfigParser import ConfigParser
from threading import Lock
from time import sleep, clock

from src.core import instrument as inst
from src.core.action import Action, ActionScan, ActionSpec, ParameterSpec
from src.instruments.noauto.itc503 import ITC503
from src.instruments.noauto.oxford_common import (waitForStableTemperature,
                                                  expandRange)
from src.instruments.noauto.ps120 import PS120
from src.tools import path_tools as pt

MODE_DIRECT = 0
MODE_THROUGH_MONITOR = 1

[docs]class Heliox(inst.Instrument): """A Heliox 3He insert.""" def __init__(self, experiment, name='Heliox', spec=None): super(Heliox, self).__init__(experiment, name, spec) self._info = ('Instrument: ' + self.getName() + '\n' + 'Oxford Instruments Heliox 3He Insert') config = ConfigParser() config.read(pt.unrel('etc', 'heliox.conf')) # Power supply settings psProtocol = str(config.get('ps_address', 'protocol')).lower() psGpibaddress = config.get('ps_address', 'gpib_address') psIsobusaddress = config.get('ps_address', 'isobus_address') psSerial = None if psProtocol == 'serial' or psProtocol == 'isobus': psSerial = {'baud_rate': config.get('ps_address', 'serial_baud_rate'), 'parity': config.get('ps_address', 'serial_parity'), 'data_bits': config.get('ps_address', 'serial_data_bits'), 'stop_bits': config.get('ps_address', 'serial_stop_bits')} self._powerSupply = PS120('Magnet', psProtocol, psIsobusaddress, psGpibaddress, psSerial) # Temperature controller settings tcProtocol = str(config.get('tc_address', 'protocol')).lower() tcGpibaddress = config.get('tc_address', 'gpib_address') tcIsobusaddress = config.get('tc_address', 'isobus_address') tcSerial = None if tcProtocol == 'serial' or tcProtocol == 'isobus': tcSerial = {'baud_rate': config.get('tc_address', 'serial_baud_rate'), 'parity': config.get('tc_address', 'serial_parity'), 'data_bits': config.get('tc_address', 'serial_data_bits'), 'stop_bits': config.get('tc_address', 'serial_stop_bits')} self._tempController = ITC503('Temperature Controller', tcProtocol, tcIsobusaddress, tcGpibaddress, tcSerial) self._standardPID = {'low': eval(config.get('pid', 'low')), 'high': eval(config.get('pid', 'high')), 'condense': eval(config.get('pid', 'condense'))} self._cutoffTemperature = config.getfloat('smart_temp', 'cutoff') self._tempStepArray = eval(config.get('smart_temp', 'step_array')) # These are set at initialization time. self._temperatures = None self._activeSensor = None self._pid = [0, 0, 0] self._field = 0 self._fieldSetpoint = 0 self._fieldRampRate = config.getfloat('field', 'default_ramp') self._mode = MODE_DIRECT self._lock = Lock() #=========================================================================== # General #===========================================================================
[docs] def getInformation(self): """Return the Heliox's information string.""" return self._info
[docs] def initialize(self): """Initialize the Oxford Heliox.""" self._tempController.initialize() self._powerSupply.initialize() #FIXME: write finalize methods
[docs] def finalize(self): """Finalize the Oxford Heliox.""" self._tempController.closeCommunication() self._powerSupply.closeCommunication()
[docs] def setMode(self, newMode): """Set the Heliox reading mode. Parameters ---------- newMode : int An integer (MODE_DIRECT or MODE_THROUGH_MONITOR) to specify the reading mode. If it is MODE_DIRECT, all data come directly from the temperature controller. If it is MODE_THROUGH_MONITOR, only the temperature monitor triggers direct readings from the controller, and other requests from data receive the most recent readings so triggered. """ self._mode = newMode #=========================================================================== # Magnetic field #===========================================================================
[docs] def setField(self, field, block='yes'): """Set the magnetic field. Parameters ---------- field : float The magnetic field in Tesla. wait : str A string of either 'wait' or 'proceed' to determine whether to wait for the field to reach the target. """ self.setFieldNoWait(field) if block.lower() == 'yes': while abs(field - self._field) < 0.0001: self.directGetField() sleep(0.2) if self._expt.isPaused(): self.setFieldNoWait(self._field) while self._expt.isPaused(): sleep(0.2) self.setFieldNoWait(field) self.directGetField()
[docs] def setFieldNoWait(self, field): """Set the target magnetic field and return. Parameters ---------- field : float The target magnetic field in Tesla. """ with self._lock: self._powerSupply.setField(field) self._fieldSetpoint = field
[docs] def directGetField(self): """Read the magnetic field from the power supply. Returns ------- float The magnetic field in Tesla. """ with self._lock: self._field = self._powerSupply.getField() return self._field
[docs] def getField(self): """Get the magnetic field. Returns ------- float The magnetic field in Tesla. """ if self._mode == MODE_DIRECT: return self.directGetField() return self._field
[docs] def directGetFieldSetpoint(self): """Read the field setpoint from the power supply. Returns ------- float The magnetic field setpoint in Tesla. """ with self._lock: self._fieldSetpoint = self._powerSupply.getFieldSetpoint() return self._fieldSetpoint
[docs] def getFieldSetpoint(self): """Get the magnetic field setpoint. Returns ------- float The magnetic field setpoint in Tesla. """ if self._mode == MODE_DIRECT: return self.directGetFieldSetpoint() return self._fieldSetpoint
[docs] def setFieldRampRate(self, rampRate): """Set the magnetic field ramp rate. Parameters ---------- rampRate : float The desired magnetic field ramp rate in Tesla/min. """ with self._lock: self._powerSupply.setSweepRate(rampRate) self._fieldRampRate = rampRate
[docs] def directGetFieldRampRate(self): """Read the magnetic field sweep rate directly from the power supply. Returns ------- float The magnetic field ramp rate in Tesla/min. """ with self._lock: self._fieldRampRate = self._powerSupply.getSweepRate() return self._fieldRampRate
[docs] def getFieldRampRate(self): """Get the magnetic field sweep rate. Returns ------- float The magnetic field ramp rate in Tesla/min. """ if self._mode == MODE_DIRECT: return self.directGetFieldRampRate() return self._fieldRampRate #=========================================================================== # Temperature #===========================================================================
[docs] def setTemperatureSorb(self, temperature): """Set the target for the sorb-temperature sensor. Parameters ---------- temperature : float The target temperature in Kelvin. """ with self._lock: if self._activeSensor != 1: self._tempController.setHeaterSensor('1') self._activeSensor = 1 self._tempController.setTemperature(temperature)
[docs] def directGetTemperatureSorb(self): """Read the sorb temperature from the temperature controller. Returns ------- float The sorb temperature in Kelvin. """ with self._lock: self._temperatures[0] = self._tempController.getTemperature('1') return self._temperatures[0]
[docs] def getTemperatureSorb(self): """Get the sorb temperature. Returns ------- float The sorb temperature in Kelvin. """ if self._mode == MODE_DIRECT: return self.directGetTemperatureSorb() return self._temperatures[0]
[docs] def setTemperatureLow(self, temperature): """Set the target for the low-temperature sensor. Parameters ---------- temperature : float The target temperature in Kelvin. """ with self._lock: if self._activeSensor != 2: self._tempController.setHeaterSensor('2') self._activeSensor = 2 self._tempController.setTemperature(temperature)
[docs] def directGetTemperatureLow(self): """Read the sample-low temperature from the temperature controller. Returns ------- float The sample-low temperature in Kelvin. """ with self._lock: self._temperatures[1] = self._tempController.getTemperature('2') return self._temperatures[1]
[docs] def getTemperatureLow(self): """Get the sample-low temperature. Returns ------- float The sorb temperature in Kelvin. """ if self._mode == MODE_DIRECT: return self.directGetTemperatureLow() return self._temperatures[1]
[docs] def setTemperatureHigh(self, temperature): """Set the target for the high-temperature sensor. Parameters ---------- temperature : float The target temperature in Kelvin. """ with self._lock: if self._activeSensor != 3: self._tempController.setHeaterSensor('3') self._activeSensor = 3 self._tempController.setTemperature(temperature)
[docs] def directGetTemperatureHigh(self): """Read the sample-high temperature from the temperature controller. Returns ------- float The sample-high temperature in Kelvin. """ with self._lock: self._temperatures[2] = self._tempController.getTemperature('3') return self._temperatures[2]
[docs] def getTemperatureHigh(self): """Get the sample-high temperature. Returns ------- float The sample-high temperature in Kelvin. """ if self._mode == MODE_DIRECT: return self.directGetTemperatureHigh() return self._temperatures[2]
[docs] def directGetTemperatures(self): """Read the temperatures measured on all three sensors. Returns ------- tuple of float A tuple consisting of the sorb temperature, the sample-low temperature, and the sample-high temperature expressed as floats. """ with self._lock: self._temperatures = list(self._tempController.getTemperatures()) return tuple(self._temperatures)
[docs] def getTemperatures(self): """Read the temperatures measured on all three sensors. Returns ------- tuple of float A tuple consisting of the sorb temperature, the sample-low temperature, and the sample-high temperature expressed as floats. """ if self._mode == MODE_DIRECT: return self.directGetTemperatures() return tuple(self._temperatures)
[docs] def directGetSampleTemperatures(self): """Read the sample temperatures from the temperature controller. Returns ------- tuple of float A 2-tuple consisting of the low and high sample temperatures (i.e. all temperatures except that of the sorb). """ with self._lock: self._temperatures = [self._temperatures[0], self._tempController.getTemperature('2'), self._tempController.getTemperature('3')] return tuple(self._temperatures[1:3])
[docs] def getSampleTemperatures(self): """Get the sample temperatures. Returns ------- tuple of float A 2-tuple consisting of the low and high sample temperatures (i.e. all temperatures except that of the sorb). """ if self._mode == MODE_DIRECT: return self.directGetSampleTemperatures() return tuple(self._temperatures[1:3])
[docs] def setTemperature(self, target): """Set the temperature intelligently. Parameters ---------- target : float The temperature setpoint. """ if target > self._cutoffTemperature: sensor = 3 else: sensor = 2 temps = self.directGetTemperatures() goingUp = target > temps[sensor-1] if goingUp: currTemp = min([temps[1], temps[2]]) else: currTemp = max([temps[1], temps[2]]) if (currTemp < self._cutoffTemperature and target < self._cutoffTemperature): self._auxSetTemp(target, False) elif (currTemp < self._cutoffTemperature and target > self._cutoffTemperature): self._auxSetTemp(self._cutoffTemperature, False) self._auxSetTemp(target, True) elif (currTemp > self._cutoffTemperature and target < self._cutoffTemperature): self._auxSetTemp(self._cutoffTemperature, True) self._auxSetTemp(target, False) else: self._auxSetTemp(target, True) if target > self._cutoffTemperature: waitForStableTemperature(target, self.directGetTemperatureHigh, 5) else: waitForStableTemperature(target, self.directGetTemperatureLow, 5)
def _auxSetTemp(self, target, aboveCutoff=True, timeout=30.0): """Help set the temperature. Help set the temperature by breaking up the full range into a series of smaller, user-defined steps. Set the temperature to a step value, wait until the temperature passes that value, then move on to the next step. Continue until the target temperature has been passed. Parameters ---------- target : float The desired temperature in Kelvin. aboveCutoff : bool `True` if the **starting** temperature is above the low sensor/high sensor cutoff temperature. timeout : float The maximum time (in seconds) to wait after setting the temperature setpoint at each step before proceeding to the next step. """ if aboveCutoff: getTemp = self.directGetTemperatureHigh setTemp = self.setTemperatureLow else: getTemp = self.directGetTemperatureLow setTemp = self.setTemperatureLow if target > getTemp(): def compareTemp(tempA, tempB): """Return tempA < tempB. """ return tempA < tempB else: def compareTemp(tempA, tempB): """Return tempB < tempA""" return tempB < tempA temp = getTemp() for step in expandRange(temp, target, self._tempStepArray): setTemp(step) startTime = clock() while compareTemp(getTemp(), step): sleep(0.5) if clock() - startTime > timeout: break
[docs] def getTemperature(self): """Get the temperature (a weighted average of the sample temperatures). Returns ------- float The sample temperature. """ if self._mode == MODE_DIRECT: self.directGetTemperatures() tempLowC = 1.6 tempHighC = 2.5 tempLow = self._temperatures[1] tempHigh = self._temperatures[2] if tempLow <= tempLowC: return tempLow if tempHigh >= tempHighC: return tempHigh return ((tempLow*(tempHighC-tempHigh) + tempHigh*(tempLow-tempLowC))/ ((tempHighC-tempHigh) + (tempLow-tempLowC)))
[docs] def setPID(self, newP, newI, newD=0.0): """Set the PID values for the temperature controller. Parameters ---------- newP : float The proportional band in Kelvin, to a resolution of 0.001 K. newI : float The integral action time in minutes. Values between 0 and 140 minutes (inclusive), in steps of 0.1 minutes, are accepted. newD : float The derivative action time in minutes. The allowed range is 0 to 273 minutes. The default is 0.0. """ self._lock.acquire() self._tempController.setPID(newP, newI, newD) self._pid = (newP, newI, newD) self._lock.release()
[docs] def directGetPID(self): """Read the PID values from the temperature controller. Returns ------- tuple of float The proportional, integral, and derivative constants for the temperature controller as floats in a tuple. """ self._lock.acquire() self._pid = self._tempController.getPID() self._lock.release() return self._pid
[docs] def getPID(self): """Get the PID values. Returns ------- tuple of float The proportional, integral, and derivative constants for the temperature controller as floats in a tuple. """ if self._mode == MODE_DIRECT: return self.directGetPID() return self._pid
[docs] def getActions(self): """Return the list of supported actions.""" return [ ActionSpec('get_field', Action, {'experiment': self._expt, 'instrument': self, 'description': 'Get field', 'outputs': [ ParameterSpec('field', {'experiment': self._expt, 'description': 'Magnetic field', 'formatString': '%.4f', 'binName': 'Field', 'binType': 'column'}) ], 'string': 'Read the magnetic field.', 'method': self.getField} ), ActionSpec('set_field', Action, {'experiment': self._expt, 'instrument': self, 'description': 'Set field', 'inputs': [ ParameterSpec('field', {'experiment': self._expt, 'description': 'Magnetic field', 'formatString': '%.4f', 'binName': 'Field', 'binType': 'column'} ), ParameterSpec('wait', {'experiment': self._expt, 'description': 'Following action', 'formatString': '%s', 'binName': None, 'binType': None, 'allowed': ['wait', 'proceed'], 'value': 'wait'} ) ], 'string': 'Set the magnetic field to $field T and $wait.', 'method': self.setField} ), ActionSpec('scan_field', ActionScan, {'experiment': self._expt, 'instrument': self, 'description': 'Scan field', 'inputs': [ ParameterSpec('field', {'experiment': self._expt, 'description': 'Magnetic field', 'formatString': '%.4f[]', 'binName': 'Field', 'binType': 'column', 'value': [(0.0, 0.0, 0.0)]} ) ], 'string': 'Scan the magnetic field', 'method': self.setField} ) ] #=========================================================================== # Class methods #===========================================================================
@classmethod def getRequiredParameters(cls): return [] @classmethod
[docs] def isSingleton(cls): """Return whether at most one instance of the instrument may exist. Returns ------- bool Whether only zero or one instance of the instrument may exist. """ return True
[docs]class HelioxDummy(object): """A dummy class for testing Heliox updating""" def __init__(self): """Create a new heliox dummy."""
[docs] def directGetField(self): """Dummy...read field from file.""" tempfile = 'C:/Users/thomas.DellWin-PC/Desktop/field.txt' with open(tempfile) as temp: ans = float(temp.readline().strip()) return ans
[docs] def getFieldSetpoint(self): """Dummy...read field setpoint from file.""" tempfile = 'C:/Users/thomas.DellWin-PC/Desktop/setpoint.txt' with open(tempfile) as temp: ans = float(temp.readline().strip()) return ans
[docs] def getFieldRampRate(self): """Dummy...read ramp rate from file.""" tempfile = 'C:/Users/thomas.DellWin-PC/Desktop/ramp.txt' with open(tempfile) as temp: ans = float(temp.readline().strip()) return ans
[docs] def getPID(self): """Dummy...read PID from file.""" tempfile = 'C:/Users/thomas.DellWin-PC/Desktop/pid.txt' with open(tempfile) as temp: val1 = float(temp.readline().strip()) val2 = float(temp.readline().strip()) val3 = float(temp.readline().strip()) return [val1, val2, val3]
[docs] def directGetTemperatures(self): """Dummy...read temperature from file.""" tempfile = 'C:/Users/thomas.DellWin-PC/Desktop/temp.txt' with open(tempfile) as temp: val1 = float(temp.readline().strip()) val2 = float(temp.readline().strip()) val3 = float(temp.readline().strip()) return [val1, val2, val3]
[docs] def getTemperature(self): """Get the temperature.""" vals = self.directGetTemperatures() tempLowC = 1.6 tempHighC = 2.5 tempLow = vals[1] tempHigh = vals[2] if tempLow <= tempLowC: return tempLow if tempHigh >= tempHighC: return tempHigh return ((tempLow*(tempHighC-tempHigh) + tempHigh*(tempLow-tempLowC))/ ((tempHighC-tempHigh) + (tempLow-tempLowC)))
[docs] def setField(self, field, action='wait'): """Set the magnetic field.""" pass
[docs] def setFieldRampRate(self, rampRate): """Set the magnetic field ramp rate.""" pass
[docs] def setPID(self, newP, newI, newD): """Set the PID values for the temperature controller.""" pass
[docs] def setTemperatureSorb(self, temperature): """Set the sorb temperature.""" pass
[docs] def setTemperatureSampleLow(self, temperature): """Set the sample-low temperature.""" pass
[docs] def setTemperatureSampleHigh(self, temperature): """Set the sample-high temperature.""" pass
[docs] def setTemperature(self, temperature): """Set the sample temperature using the automatic algorithm.""" pass