Files
sf-op/script/Tools/BunchLengthScan.py
gobbo_a 8beeb7dbc7
2023-08-17 15:16:51 +02:00

262 lines
11 KiB
Python

import ch.psi.pshell.epics.Positioner as Positioner
from mathutils import fit_polynomial
#Parameters
dry_run = False
do_elog = True
USE_SCREEN_PANEL = True
MO_FREQ = 142.8e6 # MO = 142.8 MHz
is_panel = get_exec_pars().source != CommandSource.ui # run from panel
if is_panel:
start = args[0]
stop = args[1] + 0.001 # needed to get the last point
step = args[2]
nb = int(args[3])
lat = args[4]
thr = args[5]
tds = args[6]
bunch = args[7]
plt11 = args[8]
plt12 = args[9]
plt21 = args[10]
plt22 = args[11]
else:
start = -0.1
stop = 0.1 + 0.001
step = 0.05
nb = 5
lat = 2.0
thr = 250
tds = "S30CB14"
bunch = "Bunch1"
plt11 = plot(None, title="Output")[0]
plt12 = plot(None, title="Output")[0]
plt21 = plot(None, title="Output")[0]
plt22 = plot(None, title="Output")[0]
run("Tools/BunchLengthTDSdata.py")
#Record callback: update of output plot
def update_plot(record, scan, plt):
com_mean, com_stdev = record[0].mean, record[0].stdev
rms_mean, rms_stdev = record[1].mean, record[1].stdev
fw_mean, fw_stdev = record[2].mean, record[2].stdev
fit_mean, fit_stdev = record[3].mean, record[3].stdev
phase = record.positions[0]
plt.getSeries(0).appendData(phase, com_mean, com_stdev)
plt.getSeries(1).appendData(phase, rms_mean, rms_stdev)
plt.getSeries(2).appendData(phase, fw_mean, fw_stdev)
plt.getSeries(3).appendData(phase, fit_mean, fit_stdev)
def update_plot_1(record, scan):
global plt11
update_plot(record, scan, plt11)
def update_plot_2(record, scan):
global plt12
update_plot(record, scan, plt12)
#Plot setup
def init_plots(plt1, plt2):
plt1.clear()
plt1.removeMarker(None)
plt1.setStyle(plt1.Style.ErrorY)
plt1.addSeries(LinePlotErrorSeries("Bunch center of mass", Color.red))
plt1.addSeries(LinePlotErrorSeries("Bunch length rms", Color.yellow, 2))
plt1.addSeries(LinePlotErrorSeries("Bunch length fw", Color.green, 2))
plt1.addSeries(LinePlotErrorSeries("Bunch length gauss fit", Color.blue, 2))
plt1.getAxis(plt1.AxisId.X).setLabel("TDS Phase (deg)")
plt1.getAxis(plt1.AxisId.Y).setLabel("Bunch center of mass (um)")
plt1.getAxis(plt1.AxisId.Y2).setLabel("Bunch length (um)")
plt1.setLegendVisible(True)
plt2.clear()
plt2.removeMarker(None)
plt2.setStyle(plt2.Style.Normal)
plt2.getAxis(plt2.AxisId.X).setLabel("Time (fs)")
plt2.getAxis(plt2.AxisId.Y).setLabel("Current (kA)")
plt2.setLegendVisible(False)
init_plots(plt11, plt21)
init_plots(plt12, plt22)
#Creating Phase positioner
if dry_run:
phase = DummyPositioner("TDS Phase")
camera_name = "simulation"
else:
phase = Positioner("TDS Phase", tds + "-RSYS:SET-BEAM-PHASE", tds + "-RSYS:SET-BEAM-PHASE")
camera_name = tds_data[tds]["screen"]
phase.config.minValue = -90.0
phase.config.maxValue = 270.0
phase.config.precision = 4
phase.config.resolution = 0.05
phase.config.rotation = True
phase.config.save()
phase.initialize()
phase0 = phase.read() % 360
#Camera setup
if USE_SCREEN_PANEL:
cam_server.start(camera_name+"_sp1", True) # shared pipeline
else:
cam_server.start(camera_name) # generic pipeline
cam_server.setFunction("beam_full_width")
cam_server.setInstanceConfigValue("fw_threshold", 0.3)
cam_server.setThreshold(thr)
cam_server.setBackgroundSubtraction(False)
cam_server.setGoodRegion(0.1, 3.0)
print "Waiting for y_fw and y_fit"
timeout = 1.0
start_time = time.time()
while (True):
wait_cam_server_message()
y_fw = cam_server.stream.getChild("y_fw")
y_fit = cam_server.stream.getChild("gr_y_fit_standard_deviation")
if (y_fw is not None) and (y_fit is not None):
break
if time.time() - start_time > timeout:
if y_fw is None:
raise Exception("y_fw not in camera stream")
if y_fit is None:
raise Exception("y_fit not in camera stream")
y_center_of_mass = cam_server.stream.getChild("y_center_of_mass")
y_rms = cam_server.stream.getChild("y_rms")
y_profile = cam_server.stream.getChild("y_profile")
y_axis = cam_server.stream.getChild("y_axis")
#Creating averagers
com_averager = create_averager(y_center_of_mass, nb, -1) # -1 event based, waits for the next value
rms_averager = create_averager(y_rms, nb, -1)
fw_averager = create_averager(y_fw, nb, -1)
fit_averager = create_averager(y_fit, nb, -1)
rms_averager.monitored = True # not blocking, will return last nb values
fw_averager.monitored = True
fit_averager.monitored = True
#switch BLMs off
blm = tds_data[tds]["BLM"]
strg = ":B1_ROI_ACTIVE_OP" if bunch == "Bunch1" else ":B2_ROI_ACTIVE_OP"
for i in range(len(blm)):
if dry_run:
print(blm[i] + strg, 0)
else:
caput(blm[i] + strg, 0)
#The scan loop
try:
bunch1_charge = caget("SINEG01-DBPM340:Q1")
bunch2_charge = caget("SINEG01-DBPM340:Q2")
bunch_charge = bunch1_charge if bunch == "Bunch1" else bunch2_charge
sensor_list = [com_averager, rms_averager, fw_averager, fit_averager, y_axis, y_profile, y_center_of_mass]
phase.write(start)
time.sleep(1.0)
# get bs phase-jitter / screen position data for SATMA02 calib
r1 = lscan(phase, sensor_list, start, stop, step , latency=lat, after_read = update_plot_1)
start, stop = start + 180.0, stop + 180.0
phase.write(start)
time.sleep(10.0)
r2 = lscan(phase, sensor_list, start, stop, step , latency=lat, after_read = update_plot_2)
finally:
phase.write(phase0)
phase.close()
cam_server.stop() # stops cam_server but does not close it cam_server is a global object
# stop the beam
if dry_run:
print("SIN-TIMAST-TMA:Beam-RF-OnDelay-Sel", 1)
print("SIN-TIMAST-TMA:Beam-Apply-Cmd.PROC", 1)
else:
caput("SIN-TIMAST-TMA:Beam-RF-OnDelay-Sel", 1)
caput("SIN-TIMAST-TMA:Beam-Apply-Cmd.PROC", 1)
#switch BLMs on
for i in range(len(blm)):
if dry_run:
print(blm[i] + strg, 1)
else:
caput(blm[i] + strg, 1)
#output
def write_output(r, plt):
phase_pos = r.getPositions(0)
rf_period = 1 / MO_FREQ / tds_data[tds]["harm"]
time_pos = [val / 360.0 * rf_period * 1e15 for val in phase_pos] # in fs
bunch_center = [val.mean for val in r.getReadable(0)]
bunch_center_stdev = [val.stdev for val in r.getReadable(0)]
a0, a1 = fit_polynomial(time_pos, bunch_center, 1)
calib = abs(a1) # in fs/um
#calib = 1
bunch_length_rms = [val.mean * calib for val in r.getReadable(1)]
bunch_length_rms_average = sum(bunch_length_rms) / len(bunch_length_rms)
bunch_length_fw = [val.mean * calib for val in r.getReadable(2)]
bunch_length_fw_average = sum(bunch_length_fw) / len(bunch_length_fw)
bunch_length_fit = [val.mean * calib for val in r.getReadable(3)]
bunch_length_fit_average = sum(bunch_length_fit) / len(bunch_length_fit)
profiles_um = r[y_axis]
profiles_dig = r[y_profile]
profiles_com = r[y_center_of_mass]
com = sum(profiles_com) / len(profiles_com)
profiles_fs = []
profiles_kA = []
for i in range(len(r[y_axis])):
delta_um = profiles_com[i] - com
profiles_fs.append([(val - delta_um) * calib for val in profiles_um[i]])
step_fs = (max(profiles_fs[i]) - min(profiles_fs[i])) / (len(profiles_fs[i]) - 1)
total_dig = float(sum(profiles_dig[i]))
profiles_kA.append([val / total_dig * bunch_charge / step_fs for val in profiles_dig[i]]) # pC / fs = kA
for i in range(len(profiles_um)):
plt.addSeries(LinePlotSeries("Bunch current " + str(i), Color.red))
plt.getSeries(i).setData(profiles_fs[i], profiles_kA[i])
return bunch_length_rms_average, bunch_length_fw_average, bunch_length_fit_average, calib
bunch_length_rms_average1, bunch_length_fw_average1, bunch_length_fit_average1, calib1 = write_output(r1, plt21)
bunch_length_rms_average2, bunch_length_fw_average2, bunch_length_fit_average2, calib2 = write_output(r2, plt22)
bunch_length_rms = (bunch_length_rms_average1 + bunch_length_rms_average2) / 2
bunch_length_fw = (bunch_length_fw_average1 + bunch_length_fw_average2) / 2
bunch_length_fit = (bunch_length_fit_average1 + bunch_length_fit_average2) / 2
calib = (calib1 + calib2) / 2
#archiver channels
if bunch == "Bunch1":
caput(tds + "-RTDS100:BD-BUNCH1-DURATION", bunch_length_fw)
caput(tds + "-RTDS100:BD-BUNCH1-CALIBRATION", 1 / calib)
else:
caput(tds + "-RTDS100:BD-BUNCH2-DURATION", bunch_length_fw)
caput(tds + "-RTDS100:BD-BUNCH2-CALIBRATION", 1 / calib)
#Elog entry
if do_elog:
if get_option("Generated data file:\n" + get_exec_pars().path +"\n\n" + "Save to ELOG?", "YesNo") == "Yes":
title = "Bunch length Scan"
log_msg = "Data file: " + get_exec_pars().path + "\n\n"
log_msg = log_msg + "TDS: " + tds + "\n"
log_msg = log_msg + "Bunch: " + bunch + "\n\n"
log_msg = log_msg + "#### 0 deg ####\n"
log_msg = log_msg + "Bunch length rms: %0.1f" % bunch_length_rms_average1 + " fs\n"
log_msg = log_msg + "Bunch length fw: %0.1f" % bunch_length_fw_average1 + " fs\n"
log_msg = log_msg + "Bunch length fit: %0.1f" % bunch_length_fit_average1 + " fs\n"
log_msg = log_msg + "Calibration: %0.4f" % calib1 + " fs/um\n\n"
log_msg = log_msg + "#### 180 deg ####\n"
log_msg = log_msg + "Bunch length rms: %0.1f" % bunch_length_rms_average2 + " fs\n"
log_msg = log_msg + "Bunch length fw: %0.1f" % bunch_length_fw_average2 + " fs\n"
log_msg = log_msg + "Bunch length fit: %0.1f" % bunch_length_fit_average2 + " fs\n"
log_msg = log_msg + "Calibration: %0.4f" % calib2 + " fs/um\n\n"
log_msg = log_msg + "#### mean ####\n"
log_msg = log_msg + "Bunch length rms: %0.1f" % bunch_length_rms + " fs\n"
log_msg = log_msg + "Bunch length fw: %0.1f" % bunch_length_fw + " fs\n"
log_msg = log_msg + "Bunch length fit: %0.1f" % bunch_length_fit + " fs\n"
log_msg = log_msg + "Calibration: %0.4f" % calib + " fs/um"
sleep(0.1) #Give some time to plot to be finished - it is not sync with acquisition
file_name1 = os.path.abspath(get_context().setup.getContextPath() + "/BunchLengthScanPlot1.png")
plt11.saveSnapshot(file_name1, "png")
file_name2 = os.path.abspath(get_context().setup.getContextPath() + "/BunchLengthCurrent1.png")
plt21.saveSnapshot(file_name2, "png")
file_name3 = os.path.abspath(get_context().setup.getContextPath() + "/BunchLengthScanPlot2.png")
plt12.saveSnapshot(file_name3, "png")
file_name4 = os.path.abspath(get_context().setup.getContextPath() + "/BunchLengthCurrent2.png")
plt22.saveSnapshot(file_name4, "png")
elog(title, log_msg, [file_name1, file_name2, file_name3, file_name4])
set_return([bunch_length_rms_average1, bunch_length_fw_average1, bunch_length_fit_average1, calib1,
bunch_length_rms_average2, bunch_length_fw_average2, bunch_length_fit_average2, calib2,
bunch_length_rms, bunch_length_fw, bunch_length_fit, calib])