Source code for autodeer.hardware.dummy_xepr

# -*- coding: utf-8 -*-
"""
This is a set of scripts that pretend to be an xepr spectromter, this allows for the further development of 
this code without having to be sat in front of an actual spectrometer:

Manufactuer: Hugo Karas (me)
Model: DUMMY
Features: The most relibable Bruker EPR spectromter in the world (possibly?)
"""

import re
import numpy as np
import deerlab as dl
import time
import os
import random as rand
from .XeprAPI_link import XeprAPILink,dataset

import logging

[docs] hw_log = logging.getLogger('hardware.dummy')
rand.seed(1212)
[docs] hardware_meta = {# This dictionary should be moved into a config file eventually "Type": "Complete Spectrometer", "Manufactuer": "Hugo Karas", "Model": "Dummy", "Local name": "Dummy", "Acq. Resolution": 2, "Pulse Resolution": 2, "AWG": False, "Min Freq": 33, "Max Freq": 35, }
[docs] class dummy_api(XeprAPILink): def __init__(self) -> None:
[docs] self._tmp_dir = None
[docs] self.meta = hardware_meta # This will become more neuanced eventually. Currentlty this is sufficent.
pass
[docs] def find_Xepr(self): self.Xepr = dummy_xepr() pass
[docs] def is_exp_running(self): # Since all experiments are instantaneous this has to be always false return False
[docs] def acquire_scan(self): return self.acquire_dataset()
[docs] def acquire_scan_at(self, scan_num: int): return self.acquire_dataset()
[docs] def acquire_scan_2d(self): return self.acquire_dataset()
[docs] def compile_PulseSpel_def(self): pass
[docs] def compile_PulseSpel_prg(self): pass
[docs] def set_PulseSpel_var(self, variable: str, value: int): # This should be expanded into a dictionary that can then effect the simulation. In an ideal world pass
[docs] def run_exp(self): hw_log.info('Experiment started') pass
[docs] def stop_exp(self): hw_log.info('Experiment stopped') pass
[docs] def abort_exp(self): hw_log.info('Experiment aborted') pass
[docs] class dummy_cur_exp: def __init__(self): # if experiment == "DEER_quick": # print("Experiment set to: DEER_quick") # self.NbScansToDo = dummy_param(0,10000,int,112) # self.SweepsPExp = dummy_param(0,10000,int,112) # This is the same as scans in a 1D experiment # self.ShotsPLoop = dummy_param(0,10000,int,4) # This is the same as shots per point # self.XSpecRes = dummy_param(0,10000,int,256) # This is the length of the X direction # self.NbScansDone = dummy_param(0,self.NbScansToDo,int,0) # Intially zero scans have been done # self.FTAcqModeSlct = dummy_param(None,None,str,'Run from PulseSPEL')# Setting teh acquistion mode # self.PlsSPELPrgPaF = dummy_param(None,None,str,'rand_path') # Pulse Spel Experiment Path # self.PlsSPELGlbPaF = dummy_param(None,None,str,'rand_path') # Pulse Spel Definitions Path # self.PlsSPELLISTSlct = dummy_param(None,None,str,'Phase Cycle') # Selecting the phase cycling # self.PlsSPELEXPSlct = dummy_param(None,None,str,'Experiment') # Selecting the Experiment # self.ReplaceMode = dummy_param(None,None,bool,False) # Setting the replace mode state # elif experiment == "DEER_std":#TODO # print("Experiment set to: DEER_std") # self.NbScansToDo = dummy_param(0,10000,int,2000) # self.SweepsPExp = dummy_param(0,10000,int,112) # elif experiment == "2D_DEC":#TODO # print("Experiment set to 2D Dec")
[docs] self.ShotRepTime = dummy_param(0,100000,int,6000)
[docs] self.NbScansToDo = dummy_param(0,10000,int,112)
[docs] self.SweepsPExp = dummy_param(0,10000,int,112) # This is the same as scans in a 1D experiment
[docs] self.ShotsPLoop = dummy_param(0,10000,int,4) # This is the same as shots per point
[docs] self.XSpecRes = dummy_param(0,10000,int,256) # This is the length of the X direction
[docs] self.NbScansDone = dummy_param(0,self.NbScansToDo,int,0) # Intially zero scans have been done
[docs] self.FTAcqModeSlct = dummy_param(None,None,str,'Run from PulseSPEL')# Setting teh acquistion mode
[docs] self.PlsSPELPrgPaF = dummy_param(None,None,str,'rand_path') # Pulse Spel Experiment Path
[docs] self.PlsSPELGlbPaF = dummy_param(None,None,str,'rand_path') # Pulse Spel Definitions Path
[docs] self.PlsSPELLISTSlct = dummy_param(None,None,str,'Phase Cycle') # Selecting the phase cycling
[docs] self.PlsSPELEXPSlct = dummy_param(None,None,str,'Experiment') # Selecting the Experiment
[docs] self.ReplaceMode = dummy_param(None,None,bool,False) # Setting the replace mode state
[docs] self.CenterField = dummy_param(0,15000,int)
[docs] self.FrequencyMon = dummy_param(33,35,float)
[docs] self.SweepWidth = dummy_param(0,10000,int,300)
[docs] def __getitem__(self, name): """This method returns a given parameter class for the name provided :param name: The name of parameter needed :type name: string :return: Instance of dummy_param class corresponding to the searched name :rtype: dummy_param class """ if '.' in name: # This removes the category allowing the paramter class to be found name = name.split('.')[1] return(getattr(self,name))
[docs] def getParam(self,name:str): """Extracts a paremeter from the current exeperiment :param name: The name of the parameter to be extracted :type name: string :return: An instance of the parameter class for the given paramter name :rtype: parameter Class """ return getattr(self,name)
[docs] def aqExpRun(self): #Dummy function, for running the experiment print('Dummy Experiment running') pass
[docs] class dummy_param: def __init__(self,min,max,type=int,par=None):
[docs] self.aqGetParMaxValue = max
[docs] self.aqGetParMinValue = min
[docs] self.val = par
if (par == None) & (type == int): # If no value is given a random one is picked self.val = rand.randint(self.aqGetParMinValue,self.aqGetParMaxValue) @property
[docs] def value(self): return self.val
@value.setter def value(self, par): self.val = par
[docs] class dummy_xepr: def __init__(self) -> None:
[docs] self.cur_exp = dummy_cur_exp()
[docs] self.hidden = dummy_hidden()
pass
[docs] def XeprExperiment(self,hidden=None): if hidden == None: # return dummy_cur_exp("DEER_quick") return self.cur_exp elif hidden == "AcqHidden": return self.hidden
[docs] def XeprDataset(self): # Returns the current dataset cur_exp = self.cur_exp hidden = self.hidden # Generates simulated DEER data def generateDEER(num_points,max_time): """ Modified from an example in DEERlab. Copyright 2019-2021, Luis Fábregas Ibáñez, Stefan Stoll, and others. """ t = np.linspace(0,max_time/1000,num_points) # time axis, µs r = np.linspace(2,5,200) # distance axis, nm param = [3, 0.1, 0.2, 3.5, 0.1, 0.65, 3.8, 0.05, 0.15] # parameters for three-Gaussian model P = dl.dd_gauss3(r,param) # model distance distribution lam = 0.3 # modulation depth B = dl.bg_hom3d(t,300,lam) # background decay K = dl.dipolarkernel(t,r,mod=lam,bg=B) # kernel matrix Vexp = K@P + dl.whitegaussnoise(t,0.01,seed=0) # DEER signal with added noise return t, Vexp def generateCarrPurcell(num_points,max_time): t = np.linspace(0,4000,256) V = np.exp(-((t*3e-3)/(4.2))**4) noise = np.random.normal(0, .05, V.shape) *np.array([1+1j]) Vexp = V + noise return t, Vexp def generate2D(numpoints,max_time): t = np.linspace(0,max_time,numpoints) X, Y = np.meshgrid(t, t) def fun_2d(x,y): return (np.exp(-((x*2.8e-3)/(4.2))**4) + np.exp(-((y*2.8e-3)/(4.2))**4)) Z = fun_2d(X,Y) noise = np.random.normal(0, .05, Z.shape) *np.array([1+1j]) return [t,t],Z+noise def generate_hahn_echo_transient(numpoints,phase,phase_offset=0.2): def gaussian(t,a,b,c,phase): return a * np.exp(-(t-b)**2/c) * np.exp(-1j*(phase+phase_offset)) t = np.arange(0,numpoints,1) noise = np.random.normal(loc=0.0,scale=0.1,size=t.shape) + 1j * np.random.normal(loc=0.0,scale=0.03,size=t.shape) V = gaussian(t,1,numpoints/2,numpoints*2,phase) + noise return t,V def generate_phase_experiment(numpoints,pg,phase,phase_offset=0.2): vals = np.zeros(numpoints) vals = vals + 1j* vals t = range(0,numpoints) for i in t: ta,V = generate_hahn_echo_transient(pg,phase,phase_offset) vals[i,] = np.trapz(V,x=ta) return t, vals if cur_exp.PlsSPELEXPSlct.value == 'DEER': t,V = generateDEER(250,4000) return dummy_dataset(t,V) elif cur_exp.PlsSPELEXPSlct.value =='2D Dec. 64': t,V = generate2D(64,4000) return dummy_dataset(t,V) elif cur_exp.PlsSPELEXPSlct.value =='Carr Purcell exp': t,V = generateCarrPurcell(250,4000) return dummy_dataset(t,V) elif cur_exp.PlsSPELEXPSlct.value =='tau 2 scan': t,V = generateCarrPurcell(250,4000) return dummy_dataset(t,V) elif cur_exp.PlsSPELEXPSlct.value == 'Hahn Echo': def phases(x,range): m = 6/range *np.pi c = -3*np.pi return m*x + c if cur_exp.PlsSPELLISTSlct.value == 'MainPhase': phase_setting = hidden['SignalPhase'].value phase = phases(phase_setting,hidden['SignalPhase'].aqGetParMaxValue) elif cur_exp.PlsSPELLISTSlct.value in ['BrXPhase','BrYPhase','MinBrXPhase','MinBrYPhase']: phase_setting = hidden[cur_exp.PlsSPELLISTSlct.value].value phase = phases(phase_setting,hidden[cur_exp.PlsSPELLISTSlct.value].aqGetParMaxValue) else: print("Sorry, This can not be simulated yet, returning Main Phase") phase_setting = hidden['SignalPhase'] phase = phases(phase_setting,hidden['SignalPhase'].aqGetParMaxValue()) t,V = generate_phase_experiment(10,512,phase) return dummy_dataset(t,V) else: print("Sorry, This can not be simulated yet, returning white noise") t = np.linspace(0,2500,250) noise = np.random.normal(0, .1, t.shape) *np.array([1+1j]) return dummy_dataset(t,noise)
[docs] class XeprCmds:
[docs] def aqPgShowPrg(): # Opens the Pulse Spel Panel, obvs does nothing pass
[docs] def aqPgCompile(): # Compiles Pulse Spel, obvs does nothing pass
[docs] def aqExpSelect(exp): # Would normally select the experiment, dummy does nothing pass
[docs] def aqPgLoad(filepath): # Loads the pulse spel program, does nothing pass
[docs] def aqPgDefLoad(filepath): # Loas the pulse spel definitions file, does nothing pass
[docs] def aqPgDefSaveAs(path): pass
[docs] def aqPgSaveAs(path): pass
[docs] def vpSave(viewpoint:str,title:str,path:str): pass
[docs] class dummy_hidden: # This is a dummy version of the hidden class containing the extra current experiment features def __init__(self) -> None:
[docs] self.SignalPhase = dummy_param(0,4095,type=int,par=rand.randint(0,4096))
[docs] self.BrXPhase = dummy_param(0,100,type=float,par=rand.random())
[docs] self.BrYPhase = dummy_param(0,100,type=float,par=rand.random())
[docs] self.MinBrXPhase = dummy_param(0,100,type=float,par=rand.random())
[docs] self.MinBrYPhase = dummy_param(0,100,type=float,par=rand.random())
[docs] self.BrXAmp = dummy_param(0,100,type=float,par=rand.random())
[docs] self.BrYAmp = dummy_param(0,100,type=float,par=rand.random())
[docs] self.MinBrXAmp = dummy_param(0,100,type=float,par=rand.random())
[docs] self.MinBrYAmp = dummy_param(0,100,type=float,par=rand.random())
pass
[docs] def __getitem__(self, name): """This method returns a given parameter class for the name provided :param name: The name of parameter needed :type name: string :return: Instance of dummy_param class corresponding to the searched name :rtype: dummy_param class """ if '.' in name: # This removes the category allowing the paramter class to be found name = name.split('.')[1] return(getattr(self,name))
[docs] def getParam(self,name:str): """Extracts a paremeter from the current exeperiment :param name: The name of the parameter to be extracted :type name: string :return: An instance of the parameter class for the given paramter name :rtype: parameter Class """ return getattr(self,name)
[docs] class dummy_dataset: def __init__(self,t,V) -> None:
[docs] self.O = V
[docs] self.size = np.shape(V)
if len(self.size) == 1: self.X = t elif len(self.size) == 2: self.X = t[0] self.Y = t[1]