first idea for polarization parallel phases

This commit is contained in:
2022-10-07 11:47:30 +02:00
parent 1bbe9e87bc
commit b3d9d0097d
4 changed files with 237 additions and 0 deletions

0
phases/__init__.py Normal file
View File

67
phases/dataconverter.py Executable file
View 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
View 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
View 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()