first idea for polarization parallel phases
This commit is contained in:
0
phases/__init__.py
Normal file
0
phases/__init__.py
Normal file
67
phases/dataconverter.py
Executable file
67
phases/dataconverter.py
Executable file
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("finput", help="name of excel file to load")
|
||||||
|
parser.add_argument("foutput", help="name of json file to write")
|
||||||
|
clargs = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
import pandas as pd
|
||||||
|
from slic.utils import json_save
|
||||||
|
|
||||||
|
|
||||||
|
def load(fn, nparams=6, skip_cols=3):
|
||||||
|
engine = "xlrd" if fn.endswith(".xls") else "openpyxl"
|
||||||
|
df = pd.read_excel(fn, engine=engine)
|
||||||
|
|
||||||
|
header = df.columns
|
||||||
|
data = df.values
|
||||||
|
|
||||||
|
nrows, ncols = data.shape
|
||||||
|
assert nrows % nparams == 0
|
||||||
|
nunds = nrows // nparams
|
||||||
|
|
||||||
|
res = defaultdict(dict)
|
||||||
|
for i in range(nunds):
|
||||||
|
start = i * nparams
|
||||||
|
stop = start + nparams
|
||||||
|
|
||||||
|
und_name = data[start, 1]
|
||||||
|
idata = data[start:stop, 1:]
|
||||||
|
|
||||||
|
for j in range(skip_cols, ncols):
|
||||||
|
param_name = header[j]
|
||||||
|
jdata = idata[:, j-1].astype(float)
|
||||||
|
res[und_name][param_name] = list(jdata)
|
||||||
|
|
||||||
|
return dict(**res)
|
||||||
|
|
||||||
|
|
||||||
|
def print_overview(data):
|
||||||
|
unds = data.keys()
|
||||||
|
unds = sorted(unds)
|
||||||
|
|
||||||
|
params = data[unds[0]].keys()
|
||||||
|
params = sorted(params)
|
||||||
|
|
||||||
|
nunds = len(unds)
|
||||||
|
nparams = len(params)
|
||||||
|
|
||||||
|
print(f"read {nparams} parameters for {nunds} undulators")
|
||||||
|
print("\nUndulators:", ", ".join(unds))
|
||||||
|
print("\nParameters:", ", ".join(params))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
data = load(clargs.finput)
|
||||||
|
print_overview(data)
|
||||||
|
json_save(data, clargs.foutput)
|
||||||
|
|
||||||
|
|
||||||
|
|
27
phases/model.py
Normal file
27
phases/model.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import numpy as np
|
||||||
|
from scipy.optimize import fsolve
|
||||||
|
|
||||||
|
|
||||||
|
def parallel2gap(K, phi, undudict):
|
||||||
|
gLH = K2gap(K, undudict['K-value_LH'])
|
||||||
|
if phi >= 0.0:
|
||||||
|
gLV = K2gap(K, undudict['K-value_LV+'])
|
||||||
|
gC = K2gap(K, undudict['K-value_C+'])
|
||||||
|
dgLV = gLV - gLH
|
||||||
|
dgC = gC - gLH - dgLV/2
|
||||||
|
else:
|
||||||
|
gLV = K2gap(K, undudict['K-value_LV-'])
|
||||||
|
gC = K2gap(K, undudict['K-value_C-'])
|
||||||
|
dgLV = gLV - gLH
|
||||||
|
dgC = gC - gLH - dgLV/2
|
||||||
|
return gLH + dgLV * np.sin(0.5 * phi)**2 + dgC * np.sin(phi)**2
|
||||||
|
|
||||||
|
|
||||||
|
def K2gap(Kval, fitparam):
|
||||||
|
g2K_func = np.poly1d(fitparam[::-1])
|
||||||
|
tau_init = 1.0
|
||||||
|
k_log = float(np.log(Kval))
|
||||||
|
return float(fsolve(k_log - g2K_func, tau_init))
|
||||||
|
|
||||||
|
|
||||||
|
|
143
phases/parallel.py
Normal file
143
phases/parallel.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
from pyepics import PV
|
||||||
|
from slic.core.adjustable import Adjustable, PVAdjustable
|
||||||
|
from slic.utils import json_load
|
||||||
|
from model import parallel2gap
|
||||||
|
|
||||||
|
|
||||||
|
UND_PERIOD = 38.0
|
||||||
|
|
||||||
|
|
||||||
|
def check_phase(phase):
|
||||||
|
assert -180 <= phase <= 180 #TODO: modulo to be in the correct range?
|
||||||
|
|
||||||
|
def convert_phase_to_shift(phase):
|
||||||
|
ratio = UND_PERIOD / 360.0
|
||||||
|
return phase * ratio / 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class UndPhases(Adjustable):
|
||||||
|
"""
|
||||||
|
Set of several UndPhase objects
|
||||||
|
allows to set the same phase to all undulators
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, ID, params, und_names=None, **kwargs):
|
||||||
|
super().__init__(ID, **kwargs)
|
||||||
|
self.params = params
|
||||||
|
|
||||||
|
if und_names is None:
|
||||||
|
und_names = params.keys()
|
||||||
|
|
||||||
|
SUFFIX = "-UIND030:"
|
||||||
|
self.phases = [UndPhase(i + SUFFIX, params[i]) for i in und_names]
|
||||||
|
|
||||||
|
|
||||||
|
def set_target_value(self, value):
|
||||||
|
tasks = [p.set_target_value(value) for p in self.phases]
|
||||||
|
for t in tasks:
|
||||||
|
t.wait()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class UndPhase(Adjustable):
|
||||||
|
"""
|
||||||
|
Combination of UndShift, UndRadial and UndTotalK
|
||||||
|
allows to set the phase of one undulator
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, ID, params, **kwargs):
|
||||||
|
super().__init__(ID, **kwargs)
|
||||||
|
self.params = params
|
||||||
|
self.shift = UndShift(ID)
|
||||||
|
self.radial = UndRadial(ID)
|
||||||
|
self.totalk = UndTotalK(ID)
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_value(self):
|
||||||
|
raise NotImplementedError #TODO: how to do that?
|
||||||
|
|
||||||
|
|
||||||
|
def set_target_value(self, value):
|
||||||
|
phase = value
|
||||||
|
shift = convert_phase_to_shift(phase)
|
||||||
|
|
||||||
|
k = self.totalk.get()
|
||||||
|
radial = parallel2gap(k, phase, self.params)
|
||||||
|
radial = round(radial, 4) #TODO: why?
|
||||||
|
|
||||||
|
self.shift.set_target_value(shift).wait()
|
||||||
|
self.radial.set_target_value(radial).wait()
|
||||||
|
|
||||||
|
|
||||||
|
def is_moving(self):
|
||||||
|
return self.shift.is_moving() or self.radial.is_moving()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class UndShiftRadialBase(PVAdjustable): #TODO: better name?
|
||||||
|
"""
|
||||||
|
Base class with functionality shared between UndShift and UndRadial
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, ID, accuracy=0.001):
|
||||||
|
super().__init__(ID + "-SET", ID + "-TL", accuracy=accuracy)
|
||||||
|
self.pv_on = PV(ID + "-ON")
|
||||||
|
self.pv_go = PV(ID + "-GO")
|
||||||
|
|
||||||
|
def set_target_value(self, value):
|
||||||
|
t = super().set_target_value(value)
|
||||||
|
sleep(0.3)
|
||||||
|
self.pv_on.put(1)
|
||||||
|
sleep(0.3)
|
||||||
|
self.pv_go.put(1)
|
||||||
|
t.wait()
|
||||||
|
|
||||||
|
|
||||||
|
class UndShift(UndShiftRadialBase):
|
||||||
|
|
||||||
|
def __init__(self, ID):
|
||||||
|
super().__init__(ID + "SHIFT")
|
||||||
|
|
||||||
|
|
||||||
|
class UndRadial(UndShiftRadialBase):
|
||||||
|
|
||||||
|
def __init__(self, ID):
|
||||||
|
super().__init__(ID + "RADIAL")
|
||||||
|
|
||||||
|
|
||||||
|
class UndTotalK:
|
||||||
|
"""
|
||||||
|
Helper class to get the total K from set value and taper
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, ID):
|
||||||
|
self.pv_kset = PV(ID + "K_SET")
|
||||||
|
self.pv_ktaperset = PV(ID + "K_TAPER_SET")
|
||||||
|
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
k = self.pv_kset.get()
|
||||||
|
ktaper = self.pv_ktaperset.get()
|
||||||
|
|
||||||
|
#TODO why?
|
||||||
|
k = round(k, 4)
|
||||||
|
ktaper = round(ktaper, 4)
|
||||||
|
|
||||||
|
return k + ktaper
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
phase = 123
|
||||||
|
check_phase(phase)
|
||||||
|
|
||||||
|
params = json_load("UE38_all_parallel_parameters.json")
|
||||||
|
|
||||||
|
ups = UndPhases("SATUN-PHASES", params)
|
||||||
|
ups.set_target_value(phase).wait()
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user