From 24f3022f4e4b8b32245960cbd8579699105ccc85 Mon Sep 17 00:00:00 2001 From: jmcchesn <jmcchesn@aps.anl.gov> Date: Tue, 12 Jul 2022 11:59:20 -0500 Subject: [PATCH] fixed all imports paths --- build/lib/iexcode/__init__.py | 1 + .../lib/iexcode}/instruments/AD_utilites.py | 4 +- build/lib/iexcode/instruments/AD_utilities.py | 347 ++ build/lib/iexcode/instruments/ARPES.py | 769 +++++ build/lib/iexcode/instruments/FMB_mirrors.py | 174 + build/lib/iexcode/instruments/IEX_VPU.py | 528 ++++ .../iexcode/instruments/IEX_endstations.py | 131 + build/lib/iexcode/instruments/Kappa.py | 1127 +++++++ build/lib/iexcode/instruments/Kappa_Euler.py | 120 + build/lib/iexcode/instruments/Kappa_det.py | 77 + .../lib/iexcode/instruments/Lakeshore_335.py | 448 +++ build/lib/iexcode/instruments/Motors.py | 232 ++ build/lib/iexcode/instruments/Scienta.py | 578 ++++ build/lib/iexcode/instruments/VLS_PGM.py | 739 +++++ build/lib/iexcode/instruments/__init__.py | 42 + build/lib/iexcode/instruments/bakeout.py | 193 ++ build/lib/iexcode/instruments/beamline.py | 94 + build/lib/iexcode/instruments/cameras.py | 52 + .../instruments/conversions_constants.py | 109 + .../iexcode/instruments/current_amplifiers.py | 367 +++ build/lib/iexcode/instruments/diagnostics.py | 225 ++ .../iexcode/instruments/electron_analyzer.py | 797 +++++ build/lib/iexcode/instruments/encoders.py | 62 + .../iexcode/instruments/files_and_folders.py | 268 ++ build/lib/iexcode/instruments/gate_valves.py | 35 + build/lib/iexcode/instruments/hxp_mirrors.py | 64 + build/lib/iexcode/instruments/logfile.py | 169 + build/lib/iexcode/instruments/m3r.py | 126 + build/lib/iexcode/instruments/mpa.py | 440 +++ .../iexcode/instruments/remote_controlers.py | 356 +++ build/lib/iexcode/instruments/resolution.py | 106 + .../lib/iexcode/instruments/s29_temp_cntl.py | 44 + build/lib/iexcode/instruments/scalers.py | 31 + build/lib/iexcode/instruments/scanRecord.py | 688 ++++ build/lib/iexcode/instruments/scratch.py | 44 + build/lib/iexcode/instruments/shutters.py | 112 + build/lib/iexcode/instruments/slits.py | 509 +++ build/lib/iexcode/instruments/spec_stuff.py | 14 + build/lib/iexcode/instruments/staff.py | 82 + build/lib/iexcode/instruments/storage_ring.py | 23 + build/lib/iexcode/instruments/userCalcs.py | 177 ++ build/lib/iexcode/instruments/utilities.py | 213 ++ build/lib/iexcode/instruments/vortexs29.py | 91 + build/lib/iexcode/instruments/xrays.py | 700 +++++ build/lib/iexcode/macros/ARPES_macros.py | 96 + build/lib/iexcode/macros/BL_shutdown.py | 47 + .../lib/iexcode/macros/Kappa_optimization.py | 100 + .../lib/iexcode/macros/ScanFunctions_plot.py | 2787 +++++++++++++++++ build/lib/iexcode/macros/__init__.py | 1 + build/lib/iexcode/macros/commissioning.py | 944 ++++++ build/lib/iexcode/macros/start_of_the_week.py | 303 ++ iexcode.egg-info/PKG-INFO | 12 + iexcode.egg-info/SOURCES.txt | 57 + iexcode.egg-info/dependency_links.txt | 1 + iexcode.egg-info/requires.txt | 5 + iexcode.egg-info/top_level.txt | 1 + iexcode/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 147 bytes iexcode/instruments/AD_utilities.py | 347 ++ iexcode/instruments/ARPES.py | 43 +- iexcode/instruments/FMB_mirrors.py | 6 +- iexcode/instruments/IEX_VPU.py | 8 +- iexcode/instruments/IEX_endstations.py | 2 + iexcode/instruments/Kappa.py | 43 +- iexcode/instruments/Kappa_Euler.py | 6 +- iexcode/instruments/Kappa_det.py | 2 +- iexcode/instruments/Lakeshore_335.py | 4 +- iexcode/instruments/Motors.py | 2 +- iexcode/instruments/VLS_PGM.py | 4 +- iexcode/instruments/__init__.py | 42 + .../__pycache__/AD_utilites.cpython-37.pyc | Bin 0 -> 11481 bytes .../__pycache__/AD_utilities.cpython-37.pyc | Bin 0 -> 11482 bytes .../__pycache__/ARPES.cpython-37.pyc | Bin 0 -> 22012 bytes .../__pycache__/FMB_mirrors.cpython-37.pyc | Bin 0 -> 5479 bytes .../__pycache__/IEX_VPU.cpython-37.pyc | Bin 0 -> 13342 bytes .../IEX_endstations.cpython-37.pyc | Bin 0 -> 3164 bytes .../__pycache__/Kappa.cpython-37.pyc | Bin 0 -> 30320 bytes .../__pycache__/Kappa_Euler.cpython-37.pyc | Bin 0 -> 3978 bytes .../__pycache__/Kappa_det.cpython-37.pyc | Bin 0 -> 2325 bytes .../__pycache__/Lakeshore_335.cpython-37.pyc | Bin 0 -> 11833 bytes .../__pycache__/Motors.cpython-37.pyc | Bin 0 -> 6871 bytes .../__pycache__/Scienta.cpython-37.pyc | Bin 0 -> 16246 bytes .../__pycache__/VLS_PGM.cpython-37.pyc | Bin 0 -> 19637 bytes .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 1954 bytes .../__pycache__/bakeout.cpython-37.pyc | Bin 0 -> 4491 bytes .../__pycache__/beamline.cpython-37.pyc | Bin 0 -> 2570 bytes .../__pycache__/cameras.cpython-37.pyc | Bin 0 -> 1804 bytes .../conversions_constants.cpython-37.pyc | Bin 0 -> 3048 bytes .../current_amplifiers.cpython-37.pyc | Bin 0 -> 10455 bytes .../__pycache__/diagnostics.cpython-37.pyc | Bin 0 -> 6664 bytes .../electron_analyzer.cpython-37.pyc | Bin 0 -> 23058 bytes .../__pycache__/encoders.cpython-37.pyc | Bin 0 -> 1790 bytes .../files_and_folders.cpython-37.pyc | Bin 0 -> 7213 bytes .../__pycache__/gate_valves.cpython-37.pyc | Bin 0 -> 1193 bytes .../__pycache__/hxp_mirrors.cpython-37.pyc | Bin 0 -> 1740 bytes .../__pycache__/logfile.cpython-37.pyc | Bin 0 -> 4639 bytes .../__pycache__/m3r.cpython-37.pyc | Bin 0 -> 3609 bytes .../__pycache__/mpa.cpython-37.pyc | Bin 0 -> 11343 bytes .../remote_controlers.cpython-37.pyc | Bin 0 -> 9051 bytes .../__pycache__/resolution.cpython-37.pyc | Bin 0 -> 3831 bytes .../__pycache__/s29_temp_cntl.cpython-37.pyc | Bin 0 -> 1487 bytes .../__pycache__/scalers.cpython-37.pyc | Bin 0 -> 1015 bytes .../__pycache__/scanRecord.cpython-37.pyc | Bin 0 -> 19907 bytes .../__pycache__/shutters.cpython-37.pyc | Bin 0 -> 2889 bytes .../__pycache__/slits.cpython-37.pyc | Bin 0 -> 13354 bytes .../__pycache__/spec_stuff.cpython-37.pyc | Bin 0 -> 599 bytes .../__pycache__/staff.cpython-37.pyc | Bin 0 -> 2083 bytes .../__pycache__/storage_ring.cpython-37.pyc | Bin 0 -> 719 bytes .../__pycache__/userCalcs.cpython-37.pyc | Bin 0 -> 4471 bytes .../__pycache__/utilities.cpython-37.pyc | Bin 0 -> 6980 bytes .../__pycache__/vortexs29.cpython-37.pyc | Bin 0 -> 3759 bytes .../__pycache__/xrays.cpython-37.pyc | Bin 0 -> 18992 bytes iexcode/instruments/backups.py | 339 -- iexcode/instruments/bakeout.py | 4 +- iexcode/instruments/beamline.py | 9 +- iexcode/instruments/cameras.py | 6 +- iexcode/instruments/current_amplifiers.py | 4 +- iexcode/instruments/diagnostics.py | 2 +- iexcode/instruments/electron_analyzer.py | 22 +- iexcode/instruments/encoders.py | 2 +- iexcode/instruments/files_and_folders.py | 54 - iexcode/instruments/hxp_mirrors.py | 4 +- iexcode/instruments/logfile.py | 10 +- iexcode/instruments/m3r.py | 4 +- iexcode/instruments/mpa.py | 10 +- iexcode/instruments/resolution.py | 2 +- iexcode/instruments/scalers.py | 2 +- iexcode/instruments/scanRecord.py | 6 +- iexcode/instruments/scratch.py | 44 + iexcode/instruments/shutters.py | 2 +- iexcode/instruments/slits.py | 8 +- iexcode/instruments/staff.py | 36 + iexcode/instruments/storage_ring.py | 2 +- iexcode/instruments/xrays.py | 38 +- 133 files changed, 16456 insertions(+), 525 deletions(-) create mode 100644 build/lib/iexcode/__init__.py rename {iexcode => build/lib/iexcode}/instruments/AD_utilites.py (98%) create mode 100644 build/lib/iexcode/instruments/AD_utilities.py create mode 100644 build/lib/iexcode/instruments/ARPES.py create mode 100644 build/lib/iexcode/instruments/FMB_mirrors.py create mode 100644 build/lib/iexcode/instruments/IEX_VPU.py create mode 100644 build/lib/iexcode/instruments/IEX_endstations.py create mode 100644 build/lib/iexcode/instruments/Kappa.py create mode 100644 build/lib/iexcode/instruments/Kappa_Euler.py create mode 100644 build/lib/iexcode/instruments/Kappa_det.py create mode 100644 build/lib/iexcode/instruments/Lakeshore_335.py create mode 100644 build/lib/iexcode/instruments/Motors.py create mode 100644 build/lib/iexcode/instruments/Scienta.py create mode 100644 build/lib/iexcode/instruments/VLS_PGM.py create mode 100644 build/lib/iexcode/instruments/__init__.py create mode 100644 build/lib/iexcode/instruments/bakeout.py create mode 100644 build/lib/iexcode/instruments/beamline.py create mode 100644 build/lib/iexcode/instruments/cameras.py create mode 100644 build/lib/iexcode/instruments/conversions_constants.py create mode 100644 build/lib/iexcode/instruments/current_amplifiers.py create mode 100644 build/lib/iexcode/instruments/diagnostics.py create mode 100644 build/lib/iexcode/instruments/electron_analyzer.py create mode 100644 build/lib/iexcode/instruments/encoders.py create mode 100644 build/lib/iexcode/instruments/files_and_folders.py create mode 100644 build/lib/iexcode/instruments/gate_valves.py create mode 100644 build/lib/iexcode/instruments/hxp_mirrors.py create mode 100644 build/lib/iexcode/instruments/logfile.py create mode 100644 build/lib/iexcode/instruments/m3r.py create mode 100644 build/lib/iexcode/instruments/mpa.py create mode 100644 build/lib/iexcode/instruments/remote_controlers.py create mode 100644 build/lib/iexcode/instruments/resolution.py create mode 100644 build/lib/iexcode/instruments/s29_temp_cntl.py create mode 100644 build/lib/iexcode/instruments/scalers.py create mode 100644 build/lib/iexcode/instruments/scanRecord.py create mode 100644 build/lib/iexcode/instruments/scratch.py create mode 100644 build/lib/iexcode/instruments/shutters.py create mode 100644 build/lib/iexcode/instruments/slits.py create mode 100644 build/lib/iexcode/instruments/spec_stuff.py create mode 100644 build/lib/iexcode/instruments/staff.py create mode 100644 build/lib/iexcode/instruments/storage_ring.py create mode 100644 build/lib/iexcode/instruments/userCalcs.py create mode 100644 build/lib/iexcode/instruments/utilities.py create mode 100644 build/lib/iexcode/instruments/vortexs29.py create mode 100644 build/lib/iexcode/instruments/xrays.py create mode 100644 build/lib/iexcode/macros/ARPES_macros.py create mode 100644 build/lib/iexcode/macros/BL_shutdown.py create mode 100644 build/lib/iexcode/macros/Kappa_optimization.py create mode 100644 build/lib/iexcode/macros/ScanFunctions_plot.py create mode 100644 build/lib/iexcode/macros/__init__.py create mode 100644 build/lib/iexcode/macros/commissioning.py create mode 100644 build/lib/iexcode/macros/start_of_the_week.py create mode 100644 iexcode.egg-info/PKG-INFO create mode 100644 iexcode.egg-info/SOURCES.txt create mode 100644 iexcode.egg-info/dependency_links.txt create mode 100644 iexcode.egg-info/requires.txt create mode 100644 iexcode.egg-info/top_level.txt create mode 100644 iexcode/__pycache__/__init__.cpython-37.pyc create mode 100644 iexcode/instruments/AD_utilities.py create mode 100644 iexcode/instruments/__init__.py create mode 100644 iexcode/instruments/__pycache__/AD_utilites.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/AD_utilities.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/ARPES.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/FMB_mirrors.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/IEX_VPU.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/IEX_endstations.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/Kappa.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/Kappa_Euler.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/Kappa_det.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/Lakeshore_335.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/Motors.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/Scienta.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/VLS_PGM.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/__init__.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/bakeout.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/beamline.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/cameras.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/conversions_constants.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/current_amplifiers.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/diagnostics.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/electron_analyzer.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/encoders.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/files_and_folders.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/gate_valves.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/hxp_mirrors.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/logfile.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/m3r.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/mpa.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/remote_controlers.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/resolution.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/s29_temp_cntl.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/scalers.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/scanRecord.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/shutters.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/slits.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/spec_stuff.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/staff.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/storage_ring.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/userCalcs.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/utilities.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/vortexs29.cpython-37.pyc create mode 100644 iexcode/instruments/__pycache__/xrays.cpython-37.pyc delete mode 100644 iexcode/instruments/backups.py create mode 100644 iexcode/instruments/scratch.py diff --git a/build/lib/iexcode/__init__.py b/build/lib/iexcode/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/build/lib/iexcode/__init__.py @@ -0,0 +1 @@ + diff --git a/iexcode/instruments/AD_utilites.py b/build/lib/iexcode/instruments/AD_utilites.py similarity index 98% rename from iexcode/instruments/AD_utilites.py rename to build/lib/iexcode/instruments/AD_utilites.py index 19de023..f8d43d2 100644 --- a/iexcode/instruments/AD_utilites.py +++ b/build/lib/iexcode/instruments/AD_utilites.py @@ -13,8 +13,8 @@ from os.path import join, isfile, exists, dirname from time import sleep from epics import caget, caput -from .IEX_endstations import * -from .files_and_folders import get_next_fileNumber +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.files_and_folders import get_next_fileNumber def AD_CurrentDirectory(ADplugin): diff --git a/build/lib/iexcode/instruments/AD_utilities.py b/build/lib/iexcode/instruments/AD_utilities.py new file mode 100644 index 0000000..f8d43d2 --- /dev/null +++ b/build/lib/iexcode/instruments/AD_utilities.py @@ -0,0 +1,347 @@ +""" +General functions for dealing with Area Detectors and Cameras at 29ID + +work in progress need to redo +""" +############################################################################################################## +############################## General Area Detector ############################## +############################################################################################################## +import datetime +import re +from os import listdir,mkdir,chown,system,chmod +from os.path import join, isfile, exists, dirname +from time import sleep + +from epics import caget, caput +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.files_and_folders import get_next_fileNumber + + +def AD_CurrentDirectory(ADplugin): + """ + returns the current directory for area detector SavePlugin + handles both Winodws and linux IOCs + ADplugin = "29idc_ps1:TIFF1:"; $(P)$(SavePlugin) + """ + SubDir=caget(ADplugin+"FilePath",as_string=True) + if SubDir[0] == 'X': + Dir='/net/s29data/export/data_29idb/' + SubDir=SubDir.split('\\')[1:] + elif SubDir[0] == 'Y': + Dir='/net/s29data/export/data_29idc/' + SubDir=SubDir.split('\\')[1:] + elif SubDir[0] == 'Z': + Dir='/net/s29data/export/data_29idd/' + SubDir=SubDir.split('\\')[1:] + else: + Dir = SubDir + SubDir=[] + FilePath=join(Dir,*SubDir,'') + return FilePath + +def AD_prefix(ADplugin): + """ + returns the prefix for AreaDetector plugin based on ADplugin + """ + prefix = caget(ADplugin+"FileName_RBV",as_string=True) + return prefix + +def AD_EnableStats(ADplugin): + """ + Enabling the statistics in an AreaDector + ADplugin = "29idc_ps1:Stats1:"; (ADplugin=$(P)$(StatsPlugin)) + """ + caput(ADplugin+"EnableCallbacks","Enable") + caput(ADplugin+"ComputeStatistics","Yes") + caput(ADplugin+"ComputeCentroid","Yes") + + +def AD_SaveFileSetup(ADplugin,mda,**kwargs): + """ + ADplugin = "29id_ps1:TIFF1:" which IOC and which filesaving plugin + (ADplugin=$(P)$(SavePlugin)) + uses to get the current MDA directory and then set the path to one up + /dtype + MDA_CurrentDirectory(scanIOC=None) + + **kwargs (defaults) + scanIOC = BL_ioc() + userpath = extracted from ScanRecord unless specified + subfolder = taken from ADplugin unless specified + filepath = userpath/subfolder + + prefix = default same as subfolder + ext = file extension is extracted for ADplugin unless specified + (TIFF -> tif, HDF -> h5, ...) + FileTemplate="%s%s_%4.4d."+ext; format for filename first %s = filepath, second %s = prefix + + """ + kwargs.setdefault("userpath",dirname(dirname(mda.filepath))) + kwargs.setdefault("subfolder",ADplugin.split(":")[-2][:-1]) + + kwargs.setdefault("prefix",ADplugin.split(":")[-2][:-1]) + extDict={"TIFF":".tif","HDF":"h5"} + kwargs.setdefault("ext",ADplugin.split(":")[-2][:-1]) + kwargs.setdefault("FileTemplate","%s%s_%4.4d."+kwargs["ext"]) + + kwargs.setdefault("debug",False) + + if kwargs['debug']: + print("kwargs: ",kwargs) + + fpath=join(kwargs['userpath'],kwargs['subfolder'],'') + print("\nFolder: " + fpath) + if not (exists(fpath)): + fileNumber=1 + else: + fileNumber=get_next_fileNumber(fpath,kwargs["prefix"]) + print("NextFile: "+str(fileNumber)) + caput(ADplugin+"CreateDirectory",-1) #allows IOC to create directories + caput(ADplugin+"FilePath",fpath) + caput(ADplugin+"FileName",kwargs["prefix"]) + caput(ADplugin+"FileNumber",fileNumber) + + #setup AD + caput(ADplugin+"FileTemplate",kwargs["FileTemplate"]) + caput(ADplugin+"AutoIncrement","Yes") + caput(ADplugin+"AutoSave","Yes") + +def AD_CurrentPrefix(ADplugin): + """ + returns the prefix (without _) for area detector SavePlugin + ADplugin = "29id_ps1:TIFF1:"; $(P)$(SavePlugin) + """ + Prefix=caget(ADplugin+'FileName',as_string=True) + return Prefix + +def AD_CurrentRun(ADplugin): + """ + returns the curent run specified in the filepath for area detector SavePlugin + ADplugin = "29id_ps1:TIFF1:"; $(P)$(SavePlugin) + """ + fpath=caget(ADplugin+"FilePath",as_string=True) + current_run=re.findall("\d\d\d\d_\d", fpath)[0] + return current_run + +def AD_CurrentUser(ADplugin): + """ + returns the curent user specified in the filepath for area detector SavePlugin + ADplugin = "29id_ps1:TIFF1:"; $(P)$(SavePlugin) + """ + folder_name = ADplugin.split(":")[1] + folder_name[:-1] + SubDir=caget(ADplugin+":FilePath",as_string=True) + if SubDir[0] == 'X': + current_user='Staff' + elif SubDir[0] == 'Y': + m=SubDir.find(AD_CurrentRun(ADplugin)) + n=SubDir.find(folder_name) + current_user=SubDir[m+7:n] + else: current_user=None + return current_user + +def AD_DoneSingleSave(ADplugin,**kwargs): + """ + sets and AD up ready to save images + Acquire -> Done + ImageMode -> Single + Save -> Enable + e.g. ADplugin="29id_ps2:TIFF1:" + + **kwargs: + P=first part of ADplugin if not specified + R="cam1:"; other AD have "det1:" + """ + kwargs.setdefault("P",ADplugin.split(":")[0]+":") + kwargs.setdefault("R","cam1:") + caput(kwargs["P"]+kwargs["R"]+"Acquire","Done",wait=True,timeout=5*60);sleep(.1) + caput(kwargs["P"]+kwargs["R"]+"ImageMode","Single");sleep(.5) + caput(ADplugin+"EnableCallbacks","Enable");sleep(.1) + +def AD_FreeRun(ADplugin,**kwargs): + """ + sets and AD to disable saving and free run + Saving -> Disable + Acquire -> Done + ImageMode -> Single + + e.g. ADplugin="29id_ps2:TIFF1:" + + **kwargs: + P=first part of ADplugin if not specified + R="cam1:"; other AD have "det1:" + """ + kwargs.setdefault("P",ADplugin.split(":")[0]+":") + kwargs.setdefault("R","cam1:") + caput(kwargs["P"]+kwargs["R"]+"Acquire","Done",wait=True,timeout=5*60) + caput(ADplugin+"EnableCallbacks","Disable");sleep(.1) + caput(kwargs["P"]+kwargs["R"]+"ImageMode","Continuous");sleep(.1) + caput(kwargs["P"]+kwargs["R"]+"Acquire","Acquire");sleep(.1) + + +def AD_snap(ADplugin,**kwargs): + """ + takes an image and save the image for ADplugin + e.g. ADplugin="29id_ps2:TIFF1:" + + use AD_SaveFileSetup to set filepath, prefix etc. + use AD_CurrentDirectory to see current directory + use AD_prefix to see current prefix + + **kwargs: + P=first part of ADplugin if not specified + R="cam1:"; other AD have "det1:" + + ExposureTime: changes both the exposure time and the acquire time for the snapshot + resets after acquisition + FreeRun: True => disable setting and go back to continuous acquision + False => leave saving enabled and camera in single acquision + """ + kwargs.setdefault("P",ADplugin.split(":")[0]+":") + kwargs.setdefault("R","cam1:") + kwargs.setdefault("FreeRun",True) + + expT=caget(kwargs["P"]+kwargs["R"]+"AcquireTime_RBV") + acqT=caget(kwargs["P"]+kwargs["R"]+"AcquirePeriod_RBV") + + AD_DoneSingleSave(ADplugin,**kwargs) + + if "ExposureTime" in kwargs: + caput(kwargs["P"]+kwargs["R"]+"AcquireTime",kwargs["ExposureTime"]) + caput(kwargs["P"]+kwargs["R"]+"AcquireTime",kwargs["ExposureTime"]+.01) + + caput(kwargs["P"]+kwargs["R"]+"Acquire","Acquire",wait=True,timeout=5*60) + + if "ExposureTime" in kwargs: + caput(kwargs["P"]+kwargs["R"]+"AcquireTime",expT) + caput(kwargs["P"]+kwargs["R"]+"AcquireTime",acqT) + + if kwargs["FreeRun"]: + sleep(.1) + AD_FreeRun(ADplugin,**kwargs) + + + +def AD_ScanTrigger(ADplugin,mda,**kwargs): + """ + Add Triggering of AreaDetector to scanIOC + ADplugin = "29idc_ps1:TIFF1:" (ADplugin=$(P)$(SavePlugin)) + + **kwargs + scanIOC = "29id"+BL_ioc() if not specified + scanDIM = 1 + P=first part of ADplugin if not specified + R="cam1:"; other AD have "det1" + detTrig = 2; detectorTrigger number + """ + kwargs.setdefault("scanDIM",1) + kwargs.setdefault("P",ADplugin.split(":")[0]+":") + kwargs.setdefault("R","cam1:") + kwargs.setdefault("detTrig",2) + + scanPV=mda.ioc+"scan"+str(kwargs["scanDIM"]) + trigger=".T"+str(kwargs["detTrig"])+"PV" + caput(scanPV+trigger,kwargs["P"]+kwargs["R"]+"Acquire",wait=True,timeout=5*60) + +def ADplugin_ScanSetup(ADplugin,mda, **kwargs): + """ + stop the acquisition, puts in ImageMode=Single + enables saving + add to detector trigger + Does not press go + + ADplugin = "29idc_ps1:TIFF1:"; (ADplugin=$(P)$(SavePlugin)) + **kwargs + # AD_ScanTrigger + scanIOC = "29id"+BL_ioc() if not specified + scanDIM = 1 + P=first part of ADplugin if not specified + R="cam1:"; other AD have "det1:" + detTrig = 2; detectorTrigger number + + # AD_SaveFileSetup + filepath=userpath (from BL_ioc scanRecord)+"/dtype" + (e.g. filepath="/net/s29data/export/data_29id"+folder+"/"+run+"/"+userName+"/"+df) + dtype = taken from ADplugin + FileTemplate="%s%s_%4.4d."+dtype; format for filename first %s = filepath, second %s = prefix + prefix = dtype by default + + """ + #from AD_ScanTrigger + kwargs.setdefault("P",ADplugin.split(":")[0]+":") + kwargs.setdefault("R","cam1:") + + AD_DoneSingleSave(ADplugin,**kwargs) + + AD_SaveFileSetup(ADplugin,**kwargs) + AD_ScanTrigger(ADplugin, **kwargs) + trigger=".T"+str(kwargs["detTrig"])+"PV" + scanPV=mda.ioc+"scan"+str(kwargs["scanDIM"]) + print("WARNING: you need to need to disable saving and clear the trigger by hand after the scan") + print("\tAD_FreeRun("+ADplugin+"); caput("+scanPV+trigger+",'')") + +def AD_ROI_setup(AD,ROInum,xcenter=500,ycenter=500,xsize=50,ysize=50,binX=1,binY=1): + """ + AD = "29id_ps4" + AD = "29iddMPA" + """ + # roiNUM=1 MPA_ROI_SetUp(535,539,50,50) center of MCP + + ADplugin=AD+':ROI'+str(ROInum)+':' + xstart=xcenter-xsize/2.0 + ystart=ycenter-ysize/2.0 + caput(ADplugin+'MinX',xstart) + caput(ADplugin+'MinY',ystart) + caput(ADplugin+'SizeX',xsize) + caput(ADplugin+'SizeY',ysize) + caput(ADplugin+'BinX',binX) + caput(ADplugin+'BinY',binY) + caput(ADplugin+'EnableCallbacks','Enable') + print(ADplugin+' - '+caget(ADplugin+'EnableCallbacks_RBV',as_string=True)) + #MPA_ROI_Stats(roiNUM) + +def AD_OVER_SetUp(AD,ROInum,OVERnum,linewidth=5,shape='Rectangle'): + """ + AD = "29id_ps4" + AD = "29iddMPA" + shape= 'Cross', 'Rectangle', 'Ellipse','Text' + """ + OVER1=AD+":Over1:"+str(OVERnum)+":" + ROI=AD+":ROI"+str(ROInum)+":" + + caput(ROI+'EnableCallbacks','Enable') + caput(OVER1+"Name","ROI"+str(ROInum)) + caput(OVER1+"Shape",shape) + caput(OVER1+"Red",0) + caput(OVER1+"Green",255) + caput(OVER1+"Blue",0) + caput(OVER1+'WidthX',linewidth) + caput(OVER1+'WidthY',linewidth) + + caput(OVER1+"PositionXLink.DOL",ROI+"MinX_RBV CP") + caput(OVER1+"SizeXLink.DOL",ROI+"SizeX_RBV CP") + caput(OVER1+"PositionYLink.DOL",ROI+"MinY_RBV CP") + caput(OVER1+"SizeYLink.DOL",ROI+"SizeY_RBV CP") + + caput(OVER1+"Use","Yes") + + +def AD_OverLayCenter(x,y,AD,OverLay=1,Num=1): + """ + Sets CenterX and CenterY for AD:Over(OverLay):Num: + eg. 29id_ps2:Over1:1: AD='29id_ps2',Overlay=1, Num=1 + """ + caput(AD+":Over"+str(OverLay)+":"+str(Num)+":CenterX",x) + caput(AD+":Over"+str(OverLay)+":"+str(Num)+":CenterY",y) + +def AD_OverLayCenter_get(AD,OverLay=1,Num=1): + """ + Sets CenterX and CenterY for AD:Over(OverLay):Num: + eg. 29id_ps2:Over1:1: AD='29id_ps2',Overlay=1, Num=1 + """ + print('x = '+str(caget(AD+":Over"+str(OverLay)+":"+str(Num)+":CenterX"))) + print('y = '+str(caget(AD+":Over"+str(OverLay)+":"+str(Num)+":CenterY"))) + + + + + diff --git a/build/lib/iexcode/instruments/ARPES.py b/build/lib/iexcode/instruments/ARPES.py new file mode 100644 index 0000000..90f825f --- /dev/null +++ b/build/lib/iexcode/instruments/ARPES.py @@ -0,0 +1,769 @@ +import numpy as np +from time import sleep + +from epics import caget,caput,PV + +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.staff import staff_detector_dictionary + +from iexcode.instruments.files_and_folders import check_run,make_user_folders,folder_mda +from iexcode.instruments.staff import staff_detector_dictionary +from iexcode.instruments.logfile import logfile_name_set,logfile_header + +from iexcode.instruments.conversions_constants import * +from iexcode.instruments.utilities import * +from iexcode.instruments.userCalcs import userStringSeq_clear, userStringSeq_pvs + +from iexcode.instruments.scanRecord import * +from iexcode.instruments.Motors import * +from iexcode.instruments.xrays import * +from iexcode.instruments.current_amplifiers import * +from iexcode.instruments.gate_valves import valve_close, branch_valves +from iexcode.instruments.shutters import branch_shutter_close +from iexcode.instruments.slits import slit3C_get + +from iexcode.instruments.Lakeshore_335 import Lakeshore_reset +from iexcode.instruments.electron_analyzer import folders_EA,EA + + +############################################################################# +def ARPES_init(set_folders=True,reset=True,**kwargs): + """ + kwargs: + set_folders: sets the mda and EA folders; default => False + xrays: sets global variable; default => True + """ + kwargs.setdefault('scan_ioc','29idARPES:') + kwargs.setdefault('xrays',True) + kwargs.setdefault('BL_mode','user') + + #scan + if kwargs['BL_mode']=='staff': + detector_dictionary = staff_detector_dictionary() + else: + detector_dictionary = ARPES_detector_dictionary() + mda_scanRecord = ScanRecord(kwargs['scan_ioc'],detector_dictionary, + ARPES_trigger_dictionary(),ARPES_scan_before_sequence(),ARPES_scan_after_sequence()) + + #endstation + global BL + BL=Endstation('ARPES',kwargs['scan_ioc'],kwargs['xrays'],kwargs['BL_mode'],mda_scanRecord) + + #EA + try: + EA.get() + except: + print("\n\n NOTE: Scienta IOC is not running - start IOC and %run Macros_29id/ScanFunctions_EA.py\n\n") + + #global detectors + global tey,ca15 + tey = Keithley('c',1) + ca15 = Keithley('b',15) + + #setting folders + if 'set_folders': + if BL.mode == 'staff': + user_name = 'staff' + else: + user_name = input('user name: ') + folders_ARPES(user_name,**kwargs) + + #resetting + if 'reset': + ARPES_reset() + + #motors + global ARPES_Motors + physical_motors = ['x','y','z','th','chi','phi'] + psuedo_motors = ['focus'] + ARPES_Motors = Motors('ARPES',ARPES_motor_dictionary(),physical_motors,psuedo_motors) + + print ('ARPES_init') +############################################################################################################## +############################## ARPES detectors and motors ############################## +############################################################################################################## +def ARPES_detector_list(): + """ + list of detectors to trigger + + Previously: part of Detector_List + """ + ca_list=[["c",1],["b",15],["b",4],["b",13]] + return ca_list + +def ARPES_detector_dictionary(xrays=True): + """ + returns a dictionary of the default detectors for the scan record + + Previously: Detector_Default + """ + det_dict={ + 14:"29idc:ca2:read", + 16:"29idc:ca1:read", + 17:EA._statsPlugin+"Total_RBV", + 18:"29idARPES:LS335:TC1:IN1", + 19:"29idARPES:LS335:TC1:IN2", + } + if xrays: + det_dict.update(xrays_detector_dictionary()) + return det_dict + +def ARPES_motor_dictionary(): + """ + returns a dictionary with {name:[rbv,val,spmg,pv]} + """ + + motor_nums = {'x': 1, + 'y':2, + 'z':3, + 'th':4, + 'chi':5, + 'phi':6, + } + + motor_dictionary={} + for name in motor_nums.keys(): + pv = '29idc:m'+str(motor_nums[name]) + motor_dictionary.update({name:[pv+'.RBV',pv+'.VAL',pv+'.SPMG',pv]}) + + return motor_dictionary + +def ARPES_extra_pvs(): + """ + used to get the PV associated with a given pnuemonic + + """ + d={ + "SES_slit":"29idc:m8.RBV", + "TA":"29idARPES:LS335:TC1:IN1", + "TB":"29idARPES:LS335:TC1:IN2", + "tey1":"29idc:ca1:read", + "tey2":"29idc:ca2:read", + } + return d + +############################################################################################################## +############################## setting folder ############################## +############################################################################################################## +def folders_ARPES(user_name,**kwargs): + """ + Create and sets (if create_only=False) all the folders the current run and ARPES user + Sets the FileName for logging to be "UserName/YYYYMMDD_log.txt using logname_Set()" + + **kwargs: + set_folders = True (default); set the mda and EA scanRecords + = False; only makes the folders (was create_only) + run: run cycle e.g. 2022_1 (default => check_run() ) + ftp = True / False (default); print what needs to be added to the ftps crontab and create additional ftp folders + mda_ioc: will overwrite the default ioc for mda scans + debug + + """ + kwargs.setdefault('set_folders',True) + kwargs.setdefault('run',check_run()) + kwargs.setdefault('ftp',False) + kwargs.setdefault('debug',False) + + run = kwargs['run'] + + if kwargs['debug']: + print("run,folder,user_name,ioc,ftp: ",run,BL.folder,user_name,BL.ioc,kwargs['ftp']) + + # Create User Folder: + make_user_folders(run,BL.folder,user_name,BL.endstation,ftp=kwargs['ftp']) + sleep(5) + + if kwargs["set_folders"]: + # Set up MDA folder: + folder_mda(run,BL.folder,user_name,BL.prefix,BL.ioc) + logfile_name_set(BL.endstation) + logfile_header(BL.endstation,BL.ioc,ARPES_log_header()) + + + #Set up Scienta folders: + try: + userPath = "/net/s29data/export/data_29id"+BL.folder+"/"+run+"/"+user_name+"/" + folders_EA(userPath,filePrefix="EA") + except: + print_warning_message("EA ioc is not running, cannot set folder") + +def ARPES_reset(): + """ + resets scanRecord, current amplifiers, mono limits and lakeshore + """ + #resetting the scanRecord + BL.mda.reset() + + #resetting the current amplifiers + if BL.xray: + ca_reset_all() + else: + tey.reset() + + #resetting mono and anyother beamline stuff + if BL.xrays: + xrays_reset() + + #reseting the ARPES Lakeshore + pv = "29idARPES:LS335:" + LS_355_defaults = { + "TC1:read.SCAN":".5 second", + "TC1:OUT1:Cntrl":"A", + "TC1:OUT2:Cntrl":"B", + "TC1:OUT1:Mode":"Closed Loop" + } + Lakeshore_reset(pv,LS_355_defaults) + +############################################################################################################## +############################## get all ############################## +############################################################################################################## +def ARPES_get_all(verbose=True): + """ + returns a dictionary with the current status of the ARPES endstation and exit slit + """ + vals={} + + #sample position + motor_dictionary = ARPES_motor_dictionary() + for motor in motor_dictionary.keys(): + vals[motor]=ARPES_Motors.get(motor,verbose=False) + + #endstation pvs + extra_pvs = ARPES_extra_pvs() + for key in extra_pvs.keys(): + vals.update(key,caget(extra_pvs[key])) + vals.update('exit_slit',slit3C_get()) + + #beamline info + if BL.xray: + beamline_info = xrays_get_all() + vals.update(beamline_info) + + if verbose: + for key in vals: + print(key+" = "+vals[key]) + return vals + + + +############################################################################################################## +############################## logging ############################## +############################################################################################################## +def ARPES_log_header(): + header_list={ + 'EA': "scan,x,y,z,th,chi,phi,T,scan_mode,E1,E2,step,i,f,PE,lens_mode,SES slit #,hv,exit_slit,GRT,ID_SP,ID_RBV,ID_Mode,ID_QP,TEY1,TEY2,time,comment", + 'ARPES':"scan,motor,start,stop,step,x,y,z,th,chi,phi,T,hv,exit_slit,GRT,ID_SP,ID_RBV,ID_Mode,ID_QP,TEY_1,TEY_2,time,comment" + } + return header_list + +def ARPES_log_entries(): + """ + endstation info for log file + + Previously: scanlog + """ + vals = ARPES_get_all(verbose=False) + x = vals['x'] + y = vals['y'] + z = vals['z'] + th = vals['th'] + chi = chi['chi'] + phi = vals['phi'] + TA = vals['TA'] + TB = vals['TB'] + tey1 = vals['tey1'] + tey2 = vals['tey2'] + + entry_list = ["x","y","z","th","chi","phi","TA","TB","tey1","tey2"] + pv_list = [ x, y, z, th, chi, phi, TA, TB, tey1,tey2] + format_list = [".2f",".2f",".2f",".2f",".2f",".2f",".2f",".2f","1.2e","1.2e"] + + return entry_list,pv_list,format_list + +############################################################################################################## +############################## ARPES scanRecord ############################## +############################################################################################################## +def ARPES_scan_before_sequence(**kwargs): + """ + writes the user string sequence to happen at the beginning of a scan + returns before_scan_pv = pv for userStringSeq for before scan + + **kwargs + seq_num: userStringSeq number in ioc => 9 (default) + + Previously: BeforeScan_StrSeq + """ + kwargs.setdefault(seq_num,9) + seq_num=kwargs['seq_num'] + + before_scan_pv,before_scan_proc = userStringSeq_pvs(BL.ioc, seq_num) + + #clear and write the before scan user sequence + userStringSeq_clear(BL.ioc,seq_num) + caput(before_scan_pv+".DESC","Before Scan") + + #sequence put CAs in passive + ca_list = ARPES_detector_list() + for (i,ca) in enumerate(ca_list): + ca_pv = Keithley_pv(ca[0], ca[1])+':read.SCAN PP NMS' + caput(before_scan_pv+".LNK" +str(i+1),ca_pv) + caput(before_scan_pv+".STR" +str(i+1),"Passive") + + return before_scan_proc + +def ARPES_ca_live_sequence(**kwargs): + """ + """ + kwargs.setdefault('seq_num',7) + ca_live_sequence_proc = ca_live_sequence(BL.ioc,kwargs['seq_num'],ARPES_detector_list()) + return ca_live_sequence_proc + +def ARPES_scan_after_sequence(scan_dim, **kwargs): + """ + writes the user string sequence to happen at the end of a scan + returns after_scan_pv = pv for userStringSeq for after scan + + **kwargs + seq_num: userStringSeq number in ioc => 10 (default) + snake: for snake scanning => False (default) + Previously: AfterScan_StrSeq + """ + kwargs.setdefault(seq_num,10) + kwargs.setdefault('snake',False) + seq_num=kwargs['seq_num'] + + after_scan_pv,after_scan_proc = userStringSeq_pvs(BL.ioc, seq_num) + + #clear and write the after scan user sequence + userStringSeq_clear(BL.ioc,seq_num) + caput(after_scan_pv+".DESC","After Scan") + + ## Put All relevant CA back in live mode + ca_live_sequence_proc = ARPES_ca_live_sequence(**kwargs) + caput(after_scan_pv+".LNK1",ca_live_sequence_proc+" PP NMS") + caput(after_scan_pv+".DO1",1) + + scan_pv = BL.ioc+"scan"+str(scan_dim) + ## Put scan record back in absolute mode + caput(after_scan_pv+".LNK2",scan_pv+".P1AR") + caput(after_scan_pv+".STR2","ABSOLUTE") + + ## Put Positionner Settling time to 0.1s + caput(after_scan_pv+".LNK3",scan_pv+".PDLY NPP NMS") + caput(after_scan_pv+".DO3",0.1) + + ## Clear DetTriggers 2: + caput(after_scan_pv+".LNK4",scan_pv+".T2PV NPP NMS") #remove trigger 2 (used for EA) after scan + + if kwargs['snake']: + snake_dim = scan_dim - 1 + #snake_proc= ARPES_snake_pv(snake_dim,enable=True) + #caput(after_scan_pv+".LNK10",ARPES_snake_pv()+"PP NMS") + #caput(after_scan_pv+".D10",1) + + return after_scan_proc + + +def ARPES_detector_triggers_sequence(**kwargs): # do we need to add 29idb:ca5 ??? + """ + """ + kwargs.setdefault(seq_num,8) + seq_num=kwargs['seq_num'] + + detector_triggers_pv,detector_triggers_proc = userStringSeq_pvs(BL.ioc, seq_num) + + #clear the userStringSeq + userStringSeq_clear(BL.ioc,seq_num=kwargs['seq_num']) + caput(detector_triggers_pv+".DESC","ARPES_Trigger1") + + ca_list = ARPES_detector_list + last = len(ca_list) + for i,ca in enumerate(ca_list): + ca_pv = Keithley_pv(ca[0], ca[1])+':read.PROC CA NMS' + caput(detector_triggers_pv+".LNK" +str(i+1),ca_pv) + caput(detector_triggers_pv+".WAIT"+str(i+1),"After"+str(last)) + + return detector_triggers_proc + +def ARPES_trigger_dictionary(): + """ + need to do something + """ + trigger_dictionary = { + 1:ARPES_detector_triggers_sequence(), + } + return trigger_dictionary + + +############################################################################################################## +############################## ARPES Motor Scan Set Up ############################## +############################################################################################################## +def ARPES_motor_encoder_sync(): + ioc = "29idc:" + for motor in [1,2,3,4]: + pv = ioc+"m"+str(motor)+".SYNC" + caput(pv,1) + print("synch encoder: "+pv) + + + + +#################################################################################################### +################ ARPES Positions (LEED,transfer, measure) ##################################### +##################################################################################################### +def _ARPES_DefaultPosition(destination): + """ + Default ARPES positions in DIAL units + """ + DefaultPosition={ + 'measure':(0.0,0.0,-13.5,0.0,0.0,0.0), + 'LEED':(-0.0, 0.0, -141.5, 89.5, 0.0, 0.0), + 'transfer':(-0,0.0,-141.5,180.0,0.0,0.0), + } + if destination in DefaultPosition: + pos=DefaultPosition[destination] + else: + pos=(None,None,None,None,None,None) + return pos + +def _ARPES_MoveSequence(destination): + """ + Moves the ARPES manipulator x,y,z,th to a given destination (does not change chi or phi) + Resets the limits to help protect from crashes + such as: phi/chi motor body into the strong back, manipulator into the LEED, manipulator into the wobble stick + + DO NOT MAKE BIG MOVE WITH OUT WATCHING THE MANIPULATOR!!!! + if the theta encoder stops reading the manipulator will CRASH!!!! + """ + (x,y,z,th,chi,phi)=_ARPES_DefaultPosition(destination) + motor_dictionary = ARPES_motor_dictionary() + if x is None: + print("Not a valid destination") + else: + #reset limits to default values + _ARPES_limits_set(None) + #print destination + print(("Moving to "+destination+": "+str(round(x,1))+","+str(round(y,1))+","+str(round(z,1))+","+str(round(th,1)))+' Time:'+time.strftime("%H:%M")) + # move x and y to zero for all moves (helps protect the motors from crashing into the strong back) + caput(motor_dictionary['x'][3]+".DVAL",0,wait=True,timeout=18000) + caput(motor_dictionary['y'][3]+".DVAL",0,wait=True,timeout=18000) + # moves z and th simultaneously and monitors the .DMOV + caput(motor_dictionary['z'][3]+".DVAL",z,wait=False,timeout=18000) + caput(motor_dictionary['th'][3]+".DVAL",th,wait=False,timeout=18000) + while True: + sleep(5) + Test=caget(motor_dictionary['z'][3]+".DMOV")*caget(motor_dictionary['th'][3]+".DMOV") + if(Test==1): + break + # move x and y to final position + caput(motor_dictionary['x'][3]+".DVAL",x,wait=True,timeout=18000) + caput(motor_dictionary['y'][3]+".DVAL",y,wait=True,timeout=18000) + print(("Arrived at "+destination+": "+str(round(x,1))+","+str(round(y,1))+","+str(round(z,1))+","+str(round(th,1)))+' Time:'+time.strftime("%H:%M")) + #set the limits for this position + _ARPES_limits_set(destination) + +def _ARPES_limits_set(destination): + """ + Resets the ARPES motor limits in Dial cooridinates(remember that z is negative) + destination: 'measures' or 'LEED', + else: sets to the full range + ARPES_MoveSequence(destination) minimizes that chance of a crash and sets limits + + Previously: ARPES_LimitsSet + """ + motor_dictionary = ARPES_motor_dictionary() + if destination == 'measure': + (x,y,z,th,chi,phi)=_ARPES_DefaultPosition("measure") + limits={'x':[5.5,-7],'y':[7,-5],'z':[-6,-310],'th':[th+35,th-25],'chi':[45,-15],'phi':[120,-120]} + elif destination == 'LEED': + (x,y,z,th,chi,phi)=_ARPES_DefaultPosition("LEED") + limits={'x':[5.5,-7],'y':[6,-1],'z':[z+5,z-5],'th':[th+2,th-2],'chi':[45,-15],'phi':[120,-120]} + else: + limits={'x':[5.5,-7],'y':[7,-5],'z':[-6,-310],'th':[240,-70],'chi':[45,-15],'phi':[120,-120]} + #setting the limits for each motor in Dial + for m in limits: + caput(motor_dictionary[m][3]+'.DHLM',limits[m][0]) + caput(motor_dictionary[m][3]+'.DLLM',limits[m][1]) + if destination is not None: + print("Limits have been reset for "+destination+" position") + else: + print("Limits have been reset for full range") +#################################################################################################### +def ARPES_transfer(chi=0,phi=0,**kwargs): + """ + Moves the ARPES manipulator to the default transfer position + kwargs: + EA_HV_Off=True; Turns off the EA HV + Close_CBranch=True; closes the C-shutter and the C-valve (main chamber to BL) + """ + ARPESgo2("transfer",**kwargs) + +def ARPES_measure(**kwargs): + """ + Moves to ARPES motors x,y,z,th to the default measurement position + kwargs + chi=None # specify a value to move chi + phi=None # specifiy a value to move phi + """ + ARPESgo2("measure",**kwargs) + +def ARPES_LEED(**kwargs): + """ + Moves to ARPES motors x,y,z,th to the default LEED position + kwargs: + EA_HV_Off=True; Turns off the EA HV + Close_CBranch=True; closes the C-shutter and the C-valve (main chamber to BL) + + chi=None # specify a value to move chi + phi=None # specifiy a value to move phi + """ + ARPESgo2("LEED",**kwargs) +#################################################################################################### +def ARPESgo2(destination,**kwargs): + """ + Moves the ARPES manipulator to the default position: + destination: "transfer", "measure", "LEED" + + kwargs: + EA_off = True; turns off EA HV + shutter_close = True; closes the C-shutter + valve_close = True; closes the C-valve + + chi=None => doesn't move, otherwise specify a value to move chi + phi=None => doesn't move, otherwise specifiy a value to move phi + + """ + + + kwargs.setdefault("EA_off",True) + kwargs.setdefault("shutter_close",True) + kwargs.setdefault("valve_close",True) + + kwargs.setdefault("chi",None) + kwargs.setdefault("phi",None) + + ARPES_safe_state(**kwargs) + + #Move x,y,z,th + _ARPES_MoveSequence(destination) + #Move chi and phi back to 0 + motor_dictionary = ARPES_motor_dictionary() + if kwargs["chi"] is not None: + caput(motor_dictionary['chi'][1],kwargs["chi"]) + if kwargs["phi"] is not None: + caput(motor_dictionary['phi'][1],kwargs["phi"]) + + +############################################################################################################## +############################## ARPES pvs for GUIs (caQtDM) ############################## +############################################################################################################## +def ARPES_StringCalc_trigger2_active(): + """ + setup a string calc to be used by caQtDM to indicate that the trigger 2 is filled + """ + ioc="29idARPES:" + pv=ioc+"userStringCalc2" + caput(pv+".DESC","scan1 trigger2 calc") + caput(pv+".INAA",ioc+"scan1.T2PV CP NMS") + caput(pv+".CALC$","AA") + + caput(ioc+"userStringCalcEnable.VAL","Enable") + + + +############################################################################################################## +############################## ARPES safestate ############################## +############################################################################################################## +def ARPES_safe_state(**kwargs): + """ + puts the C-branch in a safe state, + **kwargs + EA_off = True; turns off EA HV + shutter_close = True; closes the C-shutter + valve_close = True; closes the C-valve + """ + kwargs.setdefault("EA_off",True) + kwargs.setdefault("shutter_close",True) + kwargs.setdefault("valve_close",True) + + if kwargs["EA_off"]: + try: + EA.off(quiet=False) + except: + print('EA is not running, visually confirm HV is off') + + if kwargs['valve_close']: + valve_close(branch_valves('c'), verbose=True) + + if kwargs['shutter_close']: + branch_shutter_close('c') + +############################################################################################################## +############################## ARPES motors ############################## +############################################################################################################## +### x ### +def mvx(val,**kwargs): + """ moves x motor in the endstation""" + ARPES_Motors.move('x',val,**kwargs) + +def dmvx(val): + """ relative x motor in the endstation""" + ARPES_Motors.move('x',val,relative=True) + +def scanx(start,stop,step,**kwargs): + """ scans the x motor in the endstation""" + ARPES_Motors.scan("x",start,stop,step,**kwargs) + +def dscanx(start,stop,step): + """relative scans of the x motor in the endstation """ + ARPES_Motors.scan("x",start,stop,step,relative=True) + +### y ### +def mvy(val,**kwargs): + """ moves y motor in the endstation""" + ARPES_Motors.move('y',val,**kwargs) + +def dmvy(val): + """ relative y motor in the endstation""" + ARPES_Motors.move('y',val,relative=True) + +def scany(start,stop,step,**kwargs): + """ scans the y motor in the endstation""" + ARPES_Motors.scan("y",start,stop,step,**kwargs) + +def dscany(start,stop,step): + """relative scans of the y motor in the endstation """ + ARPES_Motors.scan("y",start,stop,step,relative=True) + +### z ### +def mvz(val,**kwargs): + """ moves z motor in the endstation""" + ARPES_Motors.move('z',val,**kwargs) + +def dmvz(val): + """ relative z motor in the endstation""" + ARPES_Motors.move('z',val,relative=True) + +def scanz(start,stop,step,**kwargs): + """ scans the z motor in the endstation""" + ARPES_Motors.scan("z",start,stop,step,**kwargs) + +def dscanz(start,stop,step): + """relative scans of the z motor in the endstation """ + ARPES_Motors.scan("z",start,stop,step,relative=True) + +### th ### +def mvth(val,**kwargs): + """ moves th motor in the endstation""" + ARPES_Motors.move('th',val,**kwargs) + +def dmvth(val): + """ relative th motor in the endstation""" + ARPES_Motors.move('th',val,relative=True) + +def scanth(start,stop,step,**kwargs): + """ scans the th motor in the endstation""" + ARPES_Motors.scan("th",start,stop,step,**kwargs) + +def dscanth(start,stop,step): + """relative scans of the th motor in the endstation """ + ARPES_Motors.scan("th",start,stop,step,relative=True) + +### chi ### +def mvchi(val,**kwargs): + """ moves chi motor in the endstation""" + ARPES_Motors.move('chi',val,**kwargs) + +def dmvchi(val): + """ relative chi motor in the endstation""" + ARPES_Motors.move('chi',val,relative=True) + +def scanchi(start,stop,step,**kwargs): + """ scans the chi motor in the endstation""" + ARPES_Motors.scan("chi",start,stop,step,**kwargs) + +def dscanchi(start,stop,step): + """relative scans of the chi motor in the endstation """ + ARPES_Motors.scan("chi",start,stop,step,relative=True) + +### phi ### +def mvphi(val,**kwargs): + """ moves phi motor in the endstation""" + ARPES_Motors.move('phi',val,**kwargs) + +def dmvphi(val): + """ relative phi motor in the endstation""" + ARPES_Motors.move('phi',val,relative=True) + +def scanphi(start,stop,step,**kwargs): + """ scans the phi motor in the endstation""" + ARPES_Motors.scan("phi",start,stop,step,**kwargs) + +def dscanphi(start,stop,step): + """relative scans of the phi motor in the endstation """ + ARPES_Motors.scan("phi",start,stop,step,relative=True) + + +def mvfocus(x_val): + """ + Moves APPES x and compensates y motor so that the beam stays in the same sample position but the focus is moved + """ + x_delta = x_val - ARPES_Motors.get('x') + y_delta = x_delta * np.tan(55/180*np.pi) + y_val = ARPES_Motors.get('y') + y_delta + + ARPES_Motors.move("x",x_val) + ARPES_Motors.move("y",y_val) + + return x_val,y_val + +def mvy_fixed_focus(y_val): + """ + Moves APPES x and compensates y motor so that the beam stays in the same sample position but the focus is moved + """ + y_delta = y_val - ARPES_Motors.get('y') + x_delta = y_delta / np.tan(55/180*np.pi) + x_val = ARPES_Motors.get('x') + x_delta + + ARPES_Motors.move("x",x_val) + ARPES_Motors.move("y",y_val) + return x_val,y_val + +def scanfocus(x_start,x_stop,x_step,**kwargs): + """ + Scans APPES x and compensates y motor so that the beam stays in the same sample position + y=x*tan(55) + + kwargs: + execute: True/False to start the scan => True (default) + """ + kwargs.setdefault('execute',True) + + + +def ARPES_sample_map2D(step_y=0.5,step_z=0.5): + """ + 2D map of sample area in ARPES chamber + + Previously: Map_ARPES_Sample + """ + print("Scan_ARPES_2Dmotor(\"y\",0,4,"+str(step_y)+",\"z\",12,16,"+str(step_z)+")") + ARPES_Motors.scan2D(["y",0,4,step_y],["z",12,16,step_z]) + + +######## mprint and mvsample ################### +def mprint(): + """ + prints current position of the physical motors + """ + ARPES_Motors.mprint() + +def mvsample(position_list): + """ + moves the sample to the position sepcified by position_list + position_list = ['description',x,y,z,th,chi,phi] + + Previously: sample + """ + ARPES_Motors.mvsample(position_list) + diff --git a/build/lib/iexcode/instruments/FMB_mirrors.py b/build/lib/iexcode/instruments/FMB_mirrors.py new file mode 100644 index 0000000..888d9b9 --- /dev/null +++ b/build/lib/iexcode/instruments/FMB_mirrors.py @@ -0,0 +1,174 @@ +from time import sleep + +from epics import caget, caput + +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.utilities import read_dict, print_warning_message +from iexcode.instruments.m3r import m3r_branch + +M0M1_fpath="/home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/Dict_IDCal.txt" + + +def FMB_mirror_ioc(mirror_num): + """ + returns the ioc name for the given mirror number: + miror_num = 0 / 1 / 3 for M0 / M1 /M3R respectively + """ + ioc = '29id_'+['M0','M1','','M3R'][mirror_num]+":" + return ioc + +def FMB_mirror_status(mirror_num): + """ + returns the ioc name for the given mirror number: + miror_num = 0 / 1 / 3 for M0 / M1 /M3R respectively + + status =1 when positioned + """ + ioc = '29id_'+['M0','M1','','M3R'][mirror_num]+":" + pv = FMB_mirror_ioc(mirror_num) + status=caget(pv+'SYSTEM_STS') + + return status + +def FMB_mirror_axis_position(mirror_num,axis, verbose=True): + """ + returns the readback and set point of an FMB mirror + """ + pv = FMB_mirror_ioc(mirror_num) + + rbv = round(caget(pv+axis+"_MON"),3) + sp = round(caget(pv+axis+"_POS_SP"),3) + + if verbose: + print (pv+axis+": "+str(rbv)) + + return rbv,sp + +def FMB_mirror_get(mirror_num,verbose=True): + """ + get and returns the current mirror position + """ + axis_labels=['Tx','Ty','Tz','Rx','Ry','Rz'] + vals=[] + message = "\nM"+str(mirror_num)+" @ " + for axis in axis_labels: + rbv,sp = FMB_mirror_axis_position(mirror_num,axis, verbose=False) + vals.append(rbv) + message =+ "%.3f"+"/" % rbv + if verbose: + print(message) + if mirror_num == 3: + mirror_branch = m3r_branch() + print(" => In "+mirror_branch+" branch") + return vals + +def FMB_mirror_move(mirror_num,axis,val,verbose=True): + """ + miror_num = 0 / 1 / 3 for M0 / M1 /M3R respectively + and axis: + TX = lateral RX = Roll + TY = vertical RY = Pitch + TZ = longitudinal RZ = Yaw + + "Previously: Move_M0M1 + """ + pv = FMB_mirror_ioc(mirror_num) + axes = ['TX','TY','TZ','RX','RY','RZ'] + if axis in axes: + caput(pv+axis+"_SP.PREC",3) + caput(pv+axis+"_POS_SP") + caput(pv+"MOVE_CMD.PROC",1,wait=True,timeout=18000) + while True: + if FMB_mirror_status != 1: + sleep(.5) + else: + break + FMB_mirror_axis_position(mirror_num,axis,verbose=verbose) + + else: + print_warning_message(axis+' is not a valid axis chose '+axes) + +def FMB_mirror_move_all(mirror_num,position_list,verbose=True): + """ + miror_num = 0 / 1 / 3 for M0 / M1 /M3R respectively + position_list = [TX,TY,TZ,RX,RY,RZ] + + "Previously: Move_M0M1 + """ + pv = FMB_mirror_ioc(mirror_num) + for axis in ['TX','TY','TZ','RX','RY','RZ']: + caput(pv+axis+"_SP.PREC",3) + caput(pv+axis+"_POS_SP") + caput(pv+"MOVE_CMD.PROC",1,wait=True,timeout=18000) + while True: + if FMB_mirror_status != 1: + sleep(.5) + else: + break + FMB_mirror_axis_position(mirror_num,axis,verbose=verbose) + + +def FMB_mirror_tweak(mirror_num,axis,val,verbose=False): + """ + miror_num = 0 / 1 / 3 for M0 / M1 /M3R respectively + and axis: + TX = lateral RX = Roll + TY = vertical RY = Pitch + TZ = longitudinal RZ = Yaw + + "Previously: Move_M0M1 + """ + pv = FMB_mirror_ioc(mirror_num) + previous_position = FMB_mirror_axis_position(mirror_num,axis,verbose=False) + new_position = previous_position[0] + val + FMB_mirror_move(mirror_num,axis,new_position,verbose=False) + + if verbose: + print(pv+" "+str(previous_position[0])+" -> "+str(new_position[0])) + + +def FMB_mirror_scan(mirror_num,start,stop,step): + """ + e.g. Scan_FMB_mirror("m1:TX",-5,5,.25) + + TX = lateral RX = Yaw + TY = vertical RY = Pitch + TZ = longitudinal RZ = Roll + """ + pv = FMB_mirror_ioc(mirror_num) + + # database sets .PREC==0. We want more digits than that. + caput(pv+"_SP.PREC",3) + mda.fillin(pv+"_SP",pv+"_MON",start,stop,step) + + + + +def M0M1_table(run,mirror): + """ + Prints the positions TX / TY / TZ / RX / RY / RZ for either Mirror = 0 or 1 (M0 or M1) for the specified Run + Run='default' give a reasonable starting position after homing + M0M1_SP() will put those values as the set points, you will need to push the Move button + """ + M0M1_Pos=read_dict(M0M1_fpath) + return M0M1_Pos[run][mirror[-1]] + +def M0M1_SP(run,mirror,Go=False): + """ + Gets the values from the mirror position from a previous run and + put values as defined by M0M1_Table as the set points + Go = True / False (default); True to moves to that position + """ + mirror_pos=M0M1_table(run,mirror).split('/') + motor=['TX','TY','TZ','RX','RY','RZ'] + mirror=mirror[-1] + for i in range(len(motor)): + PV="29id_m"+str(mirror)+":"+motor[i]+"_POS_SP" + Val=mirror_pos[i] #float(MirrorPos[i]) + print(PV+" = "+Val) + caput(PV,Val) + sleep(0.5) + if Go: + caput('29id_m'+str(mirror)+':MOVE_CMD.PROC',0,wait=True,timeout=18000) + else: + print(" caput(\'29id_m"+str(mirror)+":MOVE_CMD.PROC\',0)") diff --git a/build/lib/iexcode/instruments/IEX_VPU.py b/build/lib/iexcode/instruments/IEX_VPU.py new file mode 100644 index 0000000..6678eb1 --- /dev/null +++ b/build/lib/iexcode/instruments/IEX_VPU.py @@ -0,0 +1,528 @@ + + +from math import * +from time import sleep +import numpy.polynomial.polynomial as poly + +from epics import caget, caput + +from iexcode.instruments.utilities import dateandtime, print_warning_message, read_dict +from iexcode.instruments.shutters import main_shutter_check_open +from iexcode.instruments.VLS_PGM import mono_grating_get +from iexcode.instruments.userCalcs import userCalcOut_clear + +IDcal_fpath="/home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/Dict_IDCal.txt" + +############################################################################################################## +################################ ID limits and calibration ############################## +############################################################################################################## + +def ID_calc_SP(mono_grating,ID_mode,hv_eV): # Mode = state (0=RCP,1=LCP,2=V,3=H) + """Calculate the ID SP for a given polarization mode and energy; + with Mode = 0 (RCP),1 (LCP), 2 (V), 3 (H) + + Previously: ID_Calc + """ + + + if type(ID_mode)== str: + ID_state=ID_state_mode()[ID_mode] + try: + K=ID_Coef(mono_grating,ID_mode,hv_eV) + ID=poly.polyval(hv_eV,K) + except KeyError: + message_string='Not a valid ID mode!'+"\nValid Modes: "+str(ID_mode_list) + print_warning_message(message_string) + ID=caget("ID29:EnergySeteV") + return round(ID,1) + +############################################################################################################## +################################ ID Functions ############################## +############################################################################################################## + + +def ID_wait_for_permission(): + """ + Monitors the ID permissions and waits for the ID to be in User Mode and then breaks + Checks the status every 30 seconds + + Previously: WaitForPermission + """ + while True: + ID_Access=caget("ID29:AccessSecurity.VAL") + if (ID_Access!=0): + print("Checking ID permission, please wait..."+dateandtime()) + sleep(30) + else: + print("ID now in user mode -"+dateandtime()) + break + +############################################################################################################## +def ID_get_all(verbose=False): + """ + returns dictionart with: ID_Mode, ID_QP_ratio, ID_SP, ID_RBV + + Previously: Get_energy + """ + vals={ + "ID_mode":ID_mode_get(verbose=False), + "ID_QP_ratio":ID_QP_ratio_get(verbose=False), + "ID_sp":ID_SP_get_eV(verbose=False), + "ID_rbv":ID_rbv_get_eV(verbose=False) + } + + if verbose: + print(" ID SP : "+"%.2f" % vals["ID_sp"] , "eV ID mode : "+vals["ID_mode"]) + print(" ID RBV : "+"%.2f" % vals['ID_rbv'], "eV QP mode : "+str(vals['ID_QP_ratio']) +" %") + return vals + +def ID_energy_range(ID_mode=None): + """ + Returns the ID_min_SP, ID_max_SP for a given ID mode + + Previously: ID_Range + """ + if ID_mode == None: + ID_state=caget("ID29:ActualMode") + else: + ID_state = ID_state_mode(ID_mode) + # RCP,LCP, V , H , HN + ID_min_SP = [400,400,440,250,250] + ID_max_SP = 3800 + #hv_min=[390,390,430,245,245] + + return ID_min_SP[ID_state],ID_max_SP + +def ID_mode_list(): + """ + returns the ID_mode_List + current mode = + """ + return ["RCP", "LCP", "V", "H", "HN"] + + +def ID_state_mode(ID_mode): + """ + Returns the state number if mode is a string + Returns the mode string if mode is an int or float + + Previously: ID_State2Mode(which,mode) + """ + try: + ID_mode = ID_mode.upper() + ID_state =ID_mode_list()[ID_mode].index(ID_mode) + return ID_state + + except: + message_string='Not a valid ID mode!'+"\nValid Modes: "+str(ID_mode_list) + print_warning_message(message_string) + +def ID_state_get(): + """ + Returns the current ID state + """ + ID_state = caget("ID29:ActualMode") + +def ID_mode_get(verbose=True): + """ + Returns the current ID mode + """ + ID_state = caget("ID29:ActualMode") + ID_mode = ID_mode_list()[ID_state] + + if verbose: + print('ID mode: '+ID_mode) + return ID_mode + +def ID_mode_set(ID_mode): + """ + writes the desired mode to the correct ID pv + """ + caput("ID29:DesiredMode.VAL",ID_mode,wait=True,timeout=18000) # RCP + + +def ID_ready(verbose=True): + """ + check both the read and busy pv every 2 seconds + + Previously: ID_Ready + """ + while True: + RBV=caget("ID29:Energy.VAL") + checkready=caget("ID29:feedback.VAL") + checkbusy=caget("ID29:BusyRecord") + if (checkready!="Ready") or (RBV < 3.7): + sleep(2) + elif ((checkready=="Ready") and (RBV > 3.7)) and (checkbusy=="Busy"): + caput("ID29:Busy.VAL",0) + else: + break + if verbose: + print("ID Ready") + +def ID_stop(verbose=True): + """ + waits for the ID to be in user mode and then turns it off + + Previously: ID_Stop + """ + ID_wait_for_permission() + caput("ID29:Main_on_off.VAL",1,wait=True,timeout=18000) + + sleep(5) + if verbose: + print("ID is now off") + + +def ID_off(): + """ + calls ID_stop + """ + ID_stop() + + +def ID_start(ID_mode='RCP'): + """ + waits for ID permission and then + starts ID with a specific polarization + mode = \"H\", \"V\", \"RCP\" or \"LCP\" + + """ + + ID_wait_for_permission() + #turning on the ID; default mode = RCP + print("Starting ID - "+dateandtime()) + caput("ID29:EnergySet.VAL",3.8) + caput("ID29:Main_on_off.VAL",0,wait=True,timeout=18000) + ID_ready() + ID_mode_set(ID_mode) + + ID_wait_for_permission() + main_shutter_check_open() + + print('ID is now on, please set your energy') + +def ID_switch_mode(ID_mode): + """ + Change ID polarization; which = 'H', 'V', 'RCP' or 'LCP' + if ID_mode = current mode then does nothing + + WARNING: Does not set the energy + + Previously Switch_IDMode + """ + ID_wait_for_permission() + main_shutter_check_open() + ID_state = ID_state_get() + + try: + if ID_state_mode(ID_mode) != ID_state: + print("Turning ID off...") + ID_stop(verbose=True) + sleep(10) + + print("Switching ID mode, please wait...") + ID_mode_set(ID_mode) + ID_ready() + print("ID Mode:",ID_mode) + except: + print_warning_message("Not a valid ID mode") + +def ID_SP_get(verbose=False): + """ + returns the ID setpoint in keV + """ + ID_SP = caget("ID29:EnergyScanSet.VAL") + if verbose: + print("ID_SP: ", ID_SP) + return ID_SP + +def ID_SP_get_eV(verbose=False): + """ + returns the ID setpoint in eV + """ + ID_SP = ID_SP_get(verbose=False) + if verbose: + print("ID_SP: ", ID_SP) + return ID_SP + +def ID_rbv_get(verbose=False): + """ + returns the readback value for the + """ + ID_RBV = caget("ID29:EnergyRBV") + if verbose: + print("ID_RBV: ", ID_RBV) + return ID_RBV + +def ID_rbv_get_eV(verbose=False): + """ + returns the readback value for the + """ + ID_RBV = ID_rbv_get(verbose=False) + if verbose: + print("ID_RBV: ", ID_RBV) + return ID_RBV + +def ID_restart(): + """ + turns off the ID and turns it back on with the same set point + + Previously: ID_Restart + """ + ID_mode=ID_mode_list[ID_state_get()] + hv=ID_SP_get_eV(verbose=False) + + print("Restarting ID", dateandtime()) + ID_stop(verbose=False) + ID_ready(verbose=False) + + ID_start(verbose=False) + + print("ID is back ON", dateandtime()) + ID_SP_set(hv) + +def ID_SP_set(hv_eV,verbose=True): + """ + "Sets the ID set point to a specific value (hv(eV)) which will not likely be optimum" + + Previously: SetID_Raw + """ + ID_wait_for_permission() + main_shutter_check_open() + + ID_max,ID_min = ID_energy_range() + ID_SP=min(max(hv_eV,ID_min),ID_max)*1.0 + + if hv_eV < ID_min or hv_eV > ID_max: + message_string="Set point out of BL energy range \nPlease select a different energy." + print_warning_message(message_string) + else: + ID_SP_RBV=round(caget("ID29:EnergySet.VAL"),3)*1000 + if ID_SP == ID_SP_RBV: # checks if ID is already at the SP energy + ID_RBV=caget("ID29:EnergyRBV") + if verbose: + print("ID SET : "+"%.1f" % ID_SP, "eV") + print("ID RBV : "+"%.1f" % ID_RBV, "eV") + print(caget('ID29:TableDirection',as_string=True)) + else: + caput("ID29:EnergyScanSet.VAL",ID_SP/1000,wait=True,timeout=18000) + sleep(0.5) + caput("ID29:EnergyScanSet.VAL",(ID_SP+0.001)/1000,wait=True,timeout=18000) + caput('ID29:StartRamp.VAL',1) + + sleep(5) + ID_ready(verbose=False) + + ID_RBV=caget("ID29:EnergyRBV") + print("\nID SET : "+"%.1f" % ID_SP, "eV") + print("ID RBV : "+"%.1f" % ID_RBV, "eV") + print(caget('ID29:TableDirection',as_string=True)) + + ID_diff = abs(ID_RBV-ID_SP) + ID_bw = ID_SP*0.095 + if ID_diff > ID_bw: + sleep(20) + ID_RBV=caget("ID29:EnergyRBV") + ID_diff = abs(ID_RBV-ID_SP) + print("\nID RBV : "+"%.1f" % ID_RBV, "eV") + if ID_diff > ID_bw: + ID_restart + +def ID_energy_set(hv_eV): + """ + Sets optimum ID set point for hv(eV) (max intensity) + and opens the main shutter + + Note that QP is generally not calibrated + + Previously: SetID + """ + ID_mode = ID_mode_list[ID_state_get()] + mono_grating = mono_grating_get() + ID_SP = ID_calc_SP(mono_grating,ID_mode,hv_eV) + + ID_SP(hv_eV) + + +def ID_QP_ratio_get(verbose=True): + """ + gets the read back for the QP ratio + calculate the QP ratio + + + """ + Byq=caget("ID29:ByqRdbk") + Vcoil=caget("ID29:ByRdbk.VAL") + ratio_calc=Byq/Vcoil + ratio_RBV=caget("ID29:QuasiRatio.RVAL") + if verbose: + print("QP ratio =", round(ratio_RBV,3)*100,"%") + print("QP RBV =", ratio_RBV,"%") + + if abs(ratio_RBV-ratio_calc)>1: + message_string="QP RBV and QP calc do not agree \nCheck Interlocks" + print_warning_message(message_string) + return ratio_RBV, ratio_calc + +def ID_QP_mode_set(ID_mode,QP_ratio): + """ + switched to QP mode, if not currently in + sets the QP ratio (QP ration min is 70) and polarization (ID mode) + + does not set the energy, need to use ID_SP + + Previously: Switch_IDQP + """ + QP_min = 70 + if QP_ratio < QP_min: + message_string="QP ratio is too small, setting it to minimum allowed value ("+str(QP_min)+")" + print_warning_message(message_string) + QP_ratio=max(70,QP_ratio) + + ratio_RBV, ratio_calc=ID_QP_ratio_get() + if ratio_RBV != QP_ratio: + ID_stop() + caput("ID29:QuasiRatioIn.C",QP_ratio) + + ID_start(ID_mode) + sleep(15) + + Byq=caget("ID29:ByqRdbk") + Vcoil=caget("ID29:ByRdbk.VAL") + ratio=Byq/Vcoil + ratio_RBV=caget("ID29:QuasiRatio.RVAL") + print("QP ratio =", round(ratio,3)*100,"%") + print("QP RBV =", ratio_RBV,"%") + sleep(15) + +def ID_Coef(grt,ID_mode,hv_eV): # Mode = state (0=RCP,1=LCP,2=V,3=H); + + """Return the ID coeff for a given polarization mode and energy; + with Mode = 0 (RCP),1 (LCP), 2 (V), 3 (H). + Current coefficient dictionary: + /home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/Dict_IDCal.txt + + Previously: ID_Coef + """ + + def ListRange(grt,ID_mode,IDdict): # extract the list of break pts for a given mode/grt + tmp_list=[] + for item in (IDdict[grt][ID_mode]): + tmp_list.append(item[0]) + return tmp_list + + + def FindRange(hv_eV,range_list): # returns the index for the corresponding range + B = [x - hv_eV for x in range_list] + #print(B) + index = [i for (i, x) in enumerate(B) if x > 0] + #print(index) + return(index[0]) + + try: + ID_function=read_dict(IDcal_fpath) + + except KeyError: + print("Unable to read dictionary") + + try: + Lrange = ListRange(grt,ID_mode,ID_function) + Erange = FindRange(hv_eV,Lrange) + K = ID_function[grt][ID_mode][Erange][1] + return K + + except KeyError: + print("WARNING: PLease select one of the following:") + print(" mode 0 = RCP") + print(" mode 1 = LCP") + print(" mode 2 = V") + print(" mode 3 = H") + +def ID_scan_pvs(): + """ + returns the rbv and val for scanning + """ + val_pv="ID29:EnergyScanSeteV" + rbv_pv="ID29:EnergySetRBV" + return val_pv, rbv_pv + +def ID_scan_fillin(mda,scan_dim,start,stop,step,**kwargs): + """ + fills in the scanRecord for scanning the ID set point + + **kwargs => scanRecord.fillin kwargs + """ + #Setting up the ScanRecord for ID in Table mode + val_pv, rbv_pv = ID_scan_pvs() + mda.fillin(scan_dim,val_pv,rbv_pv,start,stop,step,**kwargs) + + +def ID_scan_fillin_table(mda,scan_dim,ID_array,**kwargs): + """ + fills in the scanRecord for scanning the ID set point + + **kwargs => scanRecord.fillin kwargs + """ + #Setting up the ScanRecord for ID in Table mode + val_pv, rbv_pv = ID_scan_pvs() + mda.fillin.table(scan_dim,val_pv,rbv_pv,ID_array,**kwargs) + +############################################################################################################## +############################## ID direction table ############################## +############################################################################################################## + +def ID_Table(): + """ + + Previously: ID_Table + """ + table = caget("ID29:TableDirection") # up = 1 , down = 0 + By = caget("ID29:ByPolaritySet") # pos = 1, neg = 0 + + mode = ID_mode_get() + + if By > 0: + print("\nBy > 0") + if table == 1: # By=1, table = 1 => -1 => A=B + print("table = up") + ID_direction = -1 + elif table == 0: # By=1, table = 0 => +1 => A#B + print("table = down") + ID_direction = 1 + elif By <= 0: + print("\nBy < 0") + if table == 1: # By=0, table = 1 => +1 => A=B + print("table = up") + ID_direction = 1 + elif table == 0: # By=0, table = 0 => -1 => A#B + print("table = down") + ID_direction = -1 + + + if mode == "H" and mode == "RCP": + if By > 0 and table == 0: + print_warning_message("will do a long hysteresis if decreasing energy !!!") +# if Mode == "HN" and Mode == "LCP": +# if By = 0 and table == 1: +# print "WARNING: will do a long hysteresis if decreasing energy !!!" + print("ID direction", ID_direction) + return ID_direction + +def ID_table_userCalcOut(): + """ + # Work in progress + + Previously:ID_Table_CalcOut + """ + n=4 + userCalcOut_clear("b",n) + pvstr="29idb:userCalcOut"+str(n) + caput(pvstr+".DESC","ID_Table") + table="ID29:TableDirection" # up = 1 , down = 0 + By="ID29:ByPolaritySet" # pos = 1, neg = 0 + caput(pvstr+".INPA",table+" CP NMS") + caput(pvstr+".INPB",By+" CP NMS") + caput(pvstr+".CALC$","A#B") + caput(pvstr+".OOPT","On Change") + caput(pvstr+".DOPT","Use CALC") \ No newline at end of file diff --git a/build/lib/iexcode/instruments/IEX_endstations.py b/build/lib/iexcode/instruments/IEX_endstations.py new file mode 100644 index 0000000..94ee6e1 --- /dev/null +++ b/build/lib/iexcode/instruments/IEX_endstations.py @@ -0,0 +1,131 @@ +from math import floor +import time + +from epics import caput +""" +Endstation class is used to contain information about ioc, branch, mode + +it makes the default stuff work + +this will prompt for the endstation and will set the default parameters, look at the +init kwargs to see which defaults you can change. +""" +global BL +BL = None + +class Endstation: + """ + used for defining which endstation in which you are running + for short names and ioc info + + BL = endstation('ARPES') + BL.ioc = "ARPES" / "Kappa" / "Test" + BL.mode = "User" / "Staff" / "No Xray" + BL.branch = 'c' / 'd' + BL.mda => mda scanRecord + + """ + + def __init__(self,endstation_name,scan_ioc,xrays,BL_mode,mda_scanRecord): + """ + intializes the several beamline variables + + endstation_name = 'ARPES' / 'Kappa' + + BL = Endstation() + BL.endstation => endstation_name + BL.xrays => True/False + BL.mode => previously: BL_Mode_Set + BL.folder => 'b','c','d' + BL.prefix => 'ARPES_','Kappa_' + BL.ioc => previously: BL_ioc() + BL.mda_filepath + + """ + global BL + endstations_list = ['ARPES','Kappa'] + BL_mode_list = ['user','staff'] + + if self.endstation_name in endstations_list: + self.endstation=endstation_name + else: + print('Not a valid Endstation choice') + print('Endstations: '+endstations_list) + return + + if BL_mode in BL_mode_list: + self.mode = BL_mode + if BL_mode.lower == 'user': + if endstation_name == 'ARPES': + self.folder = 'c' + self.prefix = 'ARPES_' + elif endstation_name == 'Kappa': + self.folder = 'd' + self.prefix = 'Kappa_' + #elif endstation_name == 'Octupole': + #self.folder = 'e' + elif BL_mode.lower == 'staff': + self.folder = 'b' #overwrite folder for staff mode + else: + print('Not a valid BL_mode choice') + print('BL_modes: '+BL_mode_list) + return + + self.xrays = xrays + self.ioc = scan_ioc + self.mda = mda_scanRecord + + + def set_logfile_path(): + """ + sets the default logfile path + """ + +############################################################################################################## +############################## BL commands ############################## +############################################################################################################## +def BL_ioc(): + """ + returns the branch from the Endstation instance + """ + return BL.ioc + +def BL_mode(): + """ + returns the beamline mode, User / Staff / No_Xray + """ + return BL.ioc + +def BL_mda_prefix(): + """ + returns the mda file prefix + """ + return BL.prefix + +def BL_mda_filepath(): + """ + returns the mda file prefix + """ + return BL.filepath + +def scalar_cts(self,integration_time=0.1,verbose=True,**kwargs): + """ + Sets the integration time for the scalers + kwargs: + mcp = True/False to sum mpa + Previously: cts, Kappa counts + """ + + if BL.endstation == 'ARPES': + pass + elif BL.endstation == 'Kappa': + Kappa_scalar_pv = "29idMZ0:scaler1.TP" + mpa_Proc1_pv = "29iddMPA:Proc1:" + + caput(Kappa_scalar_pv,integration_time) + + if kwargs["mpa"]: + caput(mpa_Proc1_pv+'NumFilter',floor(time)) + + if verbose: + print("Integration time set to:", str(time)) \ No newline at end of file diff --git a/build/lib/iexcode/instruments/Kappa.py b/build/lib/iexcode/instruments/Kappa.py new file mode 100644 index 0000000..18c9810 --- /dev/null +++ b/build/lib/iexcode/instruments/Kappa.py @@ -0,0 +1,1127 @@ +import numpy as np +from time import sleep +from math import floor + +from epics import caget, caput,PV + +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.staff import staff_detector_dictionary + +from iexcode.instruments.files_and_folders import check_run,make_user_folders,folder_mda +from iexcode.instruments.staff import staff_detector_dictionary +from iexcode.instruments.logfile import logfile_name_set,logfile_header + +from iexcode.instruments.conversions_constants import * +from iexcode.instruments.utilities import * +from iexcode.instruments.userCalcs import userStringSeq_clear, userStringSeq_pvs + +from iexcode.instruments.scanRecord import * +from iexcode.instruments.Motors import * +from iexcode.instruments.xrays import * +from iexcode.instruments.current_amplifiers import * +from iexcode.instruments.gate_valves import valve_close, branch_valves +from iexcode.instruments.shutters import branch_shutter_close +from iexcode.instruments.slits import slit3D_get + +from iexcode.instruments.Kappa_det import * +from iexcode.instruments.spec_stuff import folders_spec + + + +############################################################################# +def Kappa_init(set_folders=False,reset=False,**kwargs): + """ + + set_folders: sets the mda and EA folders; default => False + reset: resets the scanRecord (detectors,triggers...) + **kwargs: + xrays: sets global variable; default => True + BL_mode: 'user' / 'staff' => used for saving, detectors... + """ + kwargs.setdefault('scan_ioc','29idKappa:') + kwargs.setdefault('xrays',True) + kwargs.setdefault('BL_mode','user') + + #scan + if kwargs['BL_mode']=='staff': + detector_dictionary = staff_detector_dictionary() + else: + detector_dictionary = Kappa_detector_dictionary() + + mda_scanRecord = ScanRecord(kwargs['scan_ioc'],detector_dictionary, + Kappa_trigger_dictionary(),Kappa_scan_before_sequence(),Kappa_scan_after_sequence()) + + #endstation + global BL + BL=Endstation('Kappa',kwargs['scan_ioc'],kwargs['xrays'],kwargs['BL_mode'],mda_scanRecord) + + #global detectors + global tey, d3, d4, mesh, Kappa_scaler_pv + tey = SRS("29idMZ0:scaler1.S2", '29idd:A1') + d3 = SRS("29idMZ0:scaler1.S3", '29idd:A2') + d4 = SRS("29idMZ0:scaler1.S4", '29idd:A3') + mesh = SRS("29idMZ0:scaler1.S14", '29idd:A4') + Kappa_scaler_pv = '29idMZ0:scaler1.CNT' + + global tthdet + tthdet = Kappa_Detector() + + #setting folders + if 'set_folders': + if BL.mode == 'staff': + user_name = 'staff' + else: + user_name = input('user name: ') + folders_Kappa(user_name,**kwargs) + + #resetting + if 'reset': + Kappa_reset() + + #motors + global Kappa_Motors + physical_motors = ['x','y','z','tth','kth','kap','kphi'] + psuedo_motors = ['th','chi','phi'] + Kappa_Motors = Motors('Kappa',Kappa_motor_dictionary(),physical_motors,psuedo_motors) + +############################################################################################################## +############################## detectors and motors ############################## +############################################################################################################## +def Kappa_detector_list(): + """ + list of detectors to trigger + + Previously: part of Detector_List + """ + ca_list=[] + return ca_list + + +def Kappa_detector_dictionary(**kwargs): + """ + returns a dictionary of the default detectors + + **kwargs + add_vortex: to specifiy to add the vortex detectors to the dictionary (True/False) + + Previously: Detector_Default + """ + kwargs.setdefault('add_vortex',False) + det_dict={} + vortex={ + 15:"", + 16:"", + 17:"", + } + sample_temp={ + 23:"29idd:LS331:TC1:SampleA", + 24:"29idd:LS331:TC1:SampleB", + } + m3r={ + 25:"29id_ps6:Stats1:CentroidX_RBV", + 26:"29id_ps6:Stats1:SigmaX_RBV", + 27:"29id_ps6:Stats1:CentroidTotal_RBV", + } + scalers={ + 31:"29idMZ0:scaler1.S14", + 32:"29idMZ0:scaler1.S2", + 33:"29idMZ0:scaler1.S3", + 34:"29idMZ0:scaler1.S4", + 35:"29idMZ0:scaler1.S5", + 36:"29idMZ0:scaler1_calc1.B", + 37:"29idMZ0:scaler1_calc1.C", + 38:"29idMZ0:scaler1_calc1.D", + 39:"29idMZ0:scaler1_calc1.E", + } + mpa={ + 30:"29iddMPA:det1:TotalRate_RBV", + 41:"29iddMPA:Stats1:Total_RBV", + 42:"29iddMPA:Stats2:Total_RBV", + 43:"29iddMPA:Stats3:Total_RBV", + 44:"29iddMPA:Stats4:Total_RBV", + 45:"29iddMPA:Stats5:Total_RBV", + } + hkl={ + 46:'<H>', + 47:'<K>', + 48:'<L>' + } + motors={ + 51:"29idKappa:m8.RBV", + 52:"29idKappa:m7.RBV", + 53:"29idKappa:m1.RBV", + 54:"29idKappa:m9.RBV", + 55:"29idKappa:Euler_ThetaRBV", + 56:"29idKappa:Euler_ChiRBV", + 57:"29idKappa:Euler_PhiRBV", + } + #hkl are listed just a place holders, they are filled in by thier scanning functions + det_dict.update(sample_temp) + det_dict.update(scalers) + det_dict.update(mpa) + det_dict.update(motors) + if kwargs['add_vortex']: + det_dict.update(vortex) + + return det_dict + + +def Kappa_motor_dictionary(name): + """ + motor_dictionary = {name:[rbv,val,spmg,pv]} for physical and psuedo/Euler motors + usage: + KappaS_PVmotor('x') => ['29idKappa:m2.RBV', '29idKappa:m2.VAL', '29idKapp:m2.SPMG','29idKappa:m2'] + """ + motor_nums={ + 'x':2, + 'y':3, + 'z':4, + 'tth':9, + 'kth':8, + 'kap':7, + 'kphi':1, + } + Euler_motors={ + 'th':'29idKappa:Euler_Theta', + 'chi':'29idKappa:Euler_Chi', + 'phi':'29idKappa:Euler_Phi' + } + motor_dictionary = {} + for name in motor_nums.keys(): + pv = '29idKappa:m'+str(motor_nums[name]) + motor_dictionary.update({name:[pv+'.VAL',pv+'.SPMG',pv]}) + + for name in Euler_motors.keys(): + pv = Euler_motors[name] + motor_dictionary.update({name:[pv+'RBV',pv,'',pv]}) + + return motor_dictionary + +def Kappa_extra_pvs(): + """ + used to get the PV associated with a given pnuemonic + + """ + d={ + "TA":"29idd:LS331:TC1:Control", + "TB":"29idd:LS331:TC1:SampleB", + "det_nam":'29idKappa:userStringSeq6.STR1', + "HV":'29idKappa:userCalcOut10.OVAL', + "centroid":'29id_ps6:Stats1:CentroidX_RBV' + } + return d + +def Kappa_kth_offset_get(): + caget("29idKappa:userCalcOut1.G") + +def Kappa_kth_offset_set(val): + caput("29idKappa:userCalcOut1.G",val) + + +############################################################################################################# +############################## setting folder ############################## +############################################################################################################## +def folders_Kappa(user_name,**kwargs): + """ + Create and sets (if create_only=False) all the folders the current run and ARPES user + Sets the FileName for logging to be "UserName/YYYYMMDD_log.txt using logname_Set()" + + **kwargs: + set_folders = True (default); set the mda and EA scanRecords + = False; only makes the folders (was create_only) + run: run cycle e.g. 2022_1 (default => check_run() ) + ftp = True / False (default); print what needs to be added to the ftps crontab and create additional ftp folders + mda_ioc: will overwrite the default ioc for mda scans + debug + """ + kwargs.setdefault('set_folders',False) + kwargs.setdefault('run',check_run()) + kwargs.setdefault('ftp',False) + kwargs.setdefault('debug',False) + + run = kwargs['run'] + + if kwargs['debug']: + print("run,folder,user_name,ioc,ftp: ",run,BL.folder,user_name,BL.ioc,kwargs['ftp']) + + # Create User Folder: + make_user_folders(run,BL.folder,user_name,BL.endstation,ftp=kwargs['ftp']) + sleep(5) + + if kwargs["set_folders"]: + # Set up MDA folder: + folder_mda(run,BL.folder,user_name,BL.prefix,BL.ioc) + logfile_name_set(BL.endstation) + logfile_header(BL.endstation,BL.ioc,Kappa_log_header()) + + + # Set up SPEC folder: + folders_spec(run,BL.folder,user_name) + + # Set up MPA folder: + #Folder_MPA(run,BL.folder,user_name) + + #resetting + if 'reset': + Kappa_reset() + +def Kappa_reset(): + """ + resets scanRecord, current amplifiers, mono limits and lakeshore + """ + #resetting the scanRecord + BL.mda.reset() + + #resetting the current amplifiers + if BL.xray: + ca_reset_all() + + #resetting mono and anyother beamline stuff + if BL.xrays: + xrays_reset() + + #reseting the Kappa Lakeshore + + #motors home and sync + SmarAct_motors_home() + PI_motors_sync() + Euler_motors_sync() + +def Kappa_reminder_list(ioc): + """ + resets scanRecord, current amplifiers, mono limits and synchs motors + """ + mda.reset() + if BL.xray: + ca_reset_all() + + + + + +############################################################################################################## +############################## get all ############################## +############################################################################################################## +def Kappa_get_all(verbose=True): + """ + returns a dictionary with the current status of the Kappa endstation and exit slit + """ + vals = {} + + #sample postion + motor_dictionary = Kappa_motor_dictionary() + for motor in motor_dictionary.keys(): + vals[motor]=Kappa_Motors.get(motor,verbose=False) + + #endstation pvs + extra_pvs = Kappa_extra_pvs() + for key in extra_pvs.keys(): + vals.update(key,caget(extra_pvs[key])) + + #beamline info + if BL.xray: + beamline_info = xrays_get_all() + vals.update(beamline_info) + + mesh.get() + vals.update({'mesh':mesh.current}) + + if verbose: + for key in vals: + print(key+" = "+vals[key]) + return vals + +############################################################################################################## +############################## logging ############################## +############################################################################################################## +def Kappa_log_header(): + """ + header for the log file + """ + h = "scan,motor,start,stop,step,x,y,z,tth,kth,kap,kphi,TA,TB," + h += "hv,exit_slit,GRT,ID_SP,ID_RBV,ID_Mode,ID_QP,TEY,mesh,det_name,mpa_HV,m3r_centroid,time,comment" + header_list = {'Kappa':h} + return header_list + +def Kappa_log_entries(): + """ + enstation info for log file + + Previously: scanlog + """ + vals = Kappa_get_all(verbose=False) + x = vals['x'] + y = vals['y'] + z = vals['z'] + tth = vals['tth'] + kth = vals['kth'] + kap = vals['kap'] + kphi = vals['kphi'] + + TA = vals['TA'] + TB = vals['TB'] + mesh.get() + tey_current = tey.current + mesh_current = mesh.current + det_name = tthdet.name + mpa_HV = mpa_HV_get() + m3r_centroid = vals['kphi'] + + entry_list = ["x","y","z","tth","kth","kap","kphi","TA","TB","TEY","mesh","det_name","mpa_HV","m3r_centroid"] + pv_list = [ x, y, z, tth, kth, kap,kphi, TA, TB,tey_current,mesh_current,det_name,mpa_HV,m3r_centroid] + format_list = [".2f",".2f",".2f",".2f",".2f",".2f",".2f",".2f",".2f","1.2e","1.2e","s",".0f",".0f"] + + return entry_list,pv_list,format_list + +############################################################################################################## +############################## Kappa scanRecord ############################## +############################################################################################################## +def Kappa_scan_before_sequence(**kwargs): + """ + writes the user string sequence to happen at the beginning of a scan + returns before_scan_pv = pv for userStringSeq for before scan + + **kwargs + seq_num: userStringSeq number in ioc => 9 (default) + + Previously: BeforeScan_StrSeq + """ + kwargs.setdefault(seq_num,9) + seq_num=kwargs['seq_num'] + + before_scan_pv,before_scan_proc = userStringSeq_pvs(BL.ioc, seq_num) + + #clear and write the before scan user sequence + userStringSeq_clear(BL.ioc,seq_num) + caput(before_scan_pv+".DESC","Before Scan") + + #This is where you'd do something if need (CA -> 'Passive', etc) + pv='' + cmd='' + caput(before_scan_pv+".LNK" +str(1),pv) + caput(before_scan_pv+".STR" +str(1),cmd) + + return before_scan_proc + +def Kappa_scan_after_sequence(scan_dim, **kwargs): + """ + writes the user string sequence to happen at the end of a scan + returns after_scan_pv = pv for userStringSeq for after scan + + **kwargs + seq_num: userStringSeq number in ioc => 10 (default) + snake: for snake scanning => False (default) + + Previously: AfterScan_StrSeq + """ + kwargs.setdefault(seq_num,10) + kwargs.setdefault('snake',False) + seq_num=kwargs['seq_num'] + + after_scan_pv,after_scan_proc = userStringSeq_pvs(BL.ioc, seq_num) + + #clear and write the after scan user sequence + userStringSeq_clear(BL.ioc,seq_num) + caput(after_scan_pv+".DESC","After Scan") + + scan_pv = BL.ioc+"scan"+str(scan_dim) + ## Put scan record back in absolute mode + caput(after_scan_pv+".LNK2",scan_pv+".P1AR") + caput(after_scan_pv+".STR2","ABSOLUTE") + + ## Put Positionner Settling time to 0.1s + caput(after_scan_pv+".LNK3",scan_pv+".PDLY NPP NMS") + caput(after_scan_pv+".DO3",0.1) + + ## Clear DetTriggers 2 to 4: + caput(after_scan_pv+".LNK4",scan_pv+".T2PV NPP NMS") #remove trigger 2 (used for EA) after scan + + if kwargs['snake']: + snake_dim = scan_dim - 1 + #snake_proc= Kappa_snake_pv(snake_dim,enable=True) + #caput(after_scan_pv+".LNK10",Kappa_snake_pv()+"PP NMS") + #caput(after_scan_pv+".D10",1) + + return after_scan_proc + +def Kappa_detector_triggers_sequence(**kwargs): # do we need to add 29idb:ca5 ??? + """ + """ + kwargs.setdefault(seq_num,8) + seq_num=kwargs['seq_num'] + + detector_triggers_pv,detector_triggers_proc = userStringSeq_pvs(BL.ioc, seq_num) + + #clear the userStringSeq + userStringSeq_clear(BL.ioc,seq_num=kwargs['seq_num']) + caput(detector_triggers_pv+".DESC","Kappa_Trigger1") + + scaler_pv = Kappa_scaler_pv + + caput(detector_triggers_pv+".LNK" +str(1),scaler_pv) + caput(detector_triggers_pv+".WAIT"+str(1),"After"+str(last)) + + ca_list = Kappa_detector_list + last = len(ca_list) + for i,ca in enumerate(ca_list): + ca_pv = Keithley_pv(ca[0], ca[1])+':read.PROC CA NMS' + caput(detector_triggers_pv+".LNK" +str(i+2),ca_pv) + caput(detector_triggers_pv+".WAIT"+str(i+2),"After"+str(last)) + + return detector_triggers_proc + +def Kappa_trigger_dictionary(): + """ + need to do something + """ + trigger_dictionary = { + 1:Kappa_detector_triggers_sequence(), + } + return trigger_dictionary + + + + +############################################################################################################## +############################## Kappa Motor Scan Set Up ############################## +############################################################################################################## + + +def Kappa_sample_Euler_list(): + """ + returns list of motor names used by mvsample + """ + motor_list = ['th','chi','phi'] + return motor_list + + +def Kappa_4c_mprint(): + """ + returns the dictionary of the current sample position in 4c units + + motors=['th','chi','phi'] + + Previously: diffracto? + """ + positions={} + motors=Kappa_sample_Euler_list() + for motor in motors: + positions.update(motor,Kappa_motor_dictionary()(motor)[0]) + return positions + +def Kappa_4c_move(th_chi_phi_list): + """ + moves the sample in 4c (Euler) units + Previously: mv4C + """ + motor_list=Kappa_sample_Euler_list() + for motor,i in enumerate(motor_list): + Kappa_Motors.move(motor,motor_list[i],wait=True,verbose=False) + + for motor,i in enumerate(motor_list): + Kappa_Motors.get(motor,verbose=True) + + + +def SmarAct_motor_list(): + """ + returns a list of the motor names for the SmarAct motors in the Kappa + """ + return ['x','y','z'] + +def SmarAct_motors_home(): + """ + Homes the piezo (x,y,z). Home position is middle of travel + + Previously: Home_SmarAct_Motor + """ + motor_dictionary = Kappa_motor_dictionary() + for motor in SmarAct_motor_list(): + pv = motor_dictionary[motor][3] + caput(pv+'.HOMF',1) + sleep(10) + print('SamrAct motors VAL homed') + +def SmarAct_enable(): + motor_dictionary = Kappa_motor_dictionary() + for motor in SmarAct_motor_list(): + spmg = motor_dictionary[motor][2] + caput(spmg,3) # 3=Go + +def SmarAct_disable(): + motor_dictionary = Kappa_motor_dictionary() + for motor in SmarAct_motor_list(): + spmg = motor_dictionary[motor][2] + caput(spmg,0) # 1=Stop + +def PI_motor_list(): + """ + returns a list of the motor names for the SmarAct motors in the Kappa + """ + return ['kap','kth','tth'] + +def PI_motors_sync(): + motor_dictionary = Kappa_motor_dictionary() + for motor in PI_motor_list(): + val_pv = motor_dictionary[motor][1] + rbv_pv = motor_dictionary[motor][0] + current_rbv=caget(rbv_pv) + caput(val_pv,current_rbv) + print('PI motors VAL synced to RBV') + +def Euler_motors_sync(): + """ + Syncs the Euler motores + + Previously: Sync_Euler_Motor + """ + caput('29idKappa:Kappa_sync.PROC',1) + sleep(1) + caput('29idKappa:Kappa_sync.PROC',1) + print('Euler motors VAL/RBV synced to physical motors') + +def Kappa_kphi_reset_zero(val): + """ + resets the zero for the kphi motor + + """ + kphi_pv = Kappa_motor_dictionary()['kphi'][3] + caput(kphi_pv+".SET",1) # 1 = Set + sleep(0.5) + caput("kphi_pv.VAL",val) + sleep(0.5) + caput(kphi_pv+".SET",0) # 0 = Use + print("\nkphi has been reset to " +str(val)) + +def Kappa_tth_reset_zero(): + """ + resets the zero for the tth motor + """ + tthdet.tth0_set() + + + +def Kappa_th2th_scan_sensitivity(th_table,gain_num_table,gain_unit_table,detector=tey,**kwargs): + """ + Scans th2th with variable gain on SRS# (srs_num). + Gain is specified for a given th2th range by building tables as follow: + + th_table = np.array([]) + gain_num_table = np.array([]) + gain_unit_table = np.array([]) + + Example: + for i in RangeUp(10,20,0.5): + th_table = np.append(th_table, i) + gn_table = np.append(gn_table, gain_dict(2)) + gu_table = np.append(gu_table, unit_dict('mA')) + for i in RangeUp(20.5,40,0.5): + th_table = np.append(th_table, i) + gn_table = np.append(gn_table, gain_dict(1)) + gu_table = np.append(gu_table, unit_dict('uA')) + for i in RangeUp(40.5,60,0.5): + th_table = np.append(th_table, i) + gn_table = np.append(gn_table, gain_dict(10)) + gu_table = np.append(gu_table, unit_dict('pA')) + + kwargs: + scan_dim: 1 (default) + cts: integration time for scalers and mpa/mcp => 0.1 (default) + execute: True/False to start the scan => True (default) + + Previoulsy: Scan_th2th_sensitivity + """ + kwargs.setdefault('cts',0.1) + kwargs.setdefault('execute',True) + + kth_offset = Kappa_kth_offset_get() + + motor_dictionary = Kappa_motor_dictionary() + kth_val,kth_rbv,kth_spmg,kth_pv = motor_dictionary['kth'] + tth_val,tth_rbv,tth_spmg,tth_pv = motor_dictionary['tth'] + + detector_pv = detector._srs_pv + gain_num_pv = detector_pv + "sens_num.VAL" + gain_unit_pv= detector_pv + "sens_unit.VAL" + + kth_table = th_table + kth_offset + tth_table = 2*th_table + + kwargs.update("positioner_num",1) + mda.fillin_table(kth_val,kth_rbv,kth_table,**kwargs) + + kwargs.update("positioner_num",2) + mda.fillin_table(tth_val,tth_rbv,tth_table,**kwargs) + + kwargs.update("positioner_num",3) + mda.fillin_table(gain_num_pv,"",gain_num_table,**kwargs) + + kwargs.update("positioner_num",4) + mda.fillin_table(gain_unit_pv,"",gain_unit_table,**kwargs) + + print("\nDon't forget to clear extra positionners at the end of the scan if you were to abort the script using the function:") + print(" Clear_Scan_Positioners('Kappa',1)") + + scaler_cts(kwargs['cts'],verbose=False) + if kwargs['execute']: + mda.go(**kwargs) + + #clean up after scan + mda.table_reset_after(**kwargs) + scaler_cts(verbose=False) + +def Kappa_scan_th2th(tth_start,tth_stop,tth_step,th_offset,**kwargs): + """ + Used for a linear (not table) scan where th = tth /2 + th_offset + **kwargs + scan_dim: 1 (default) + cts: integration time for scalers and mpa/mcp => 0.1 (default) + execute: True/False to start the scan => True (default) + + Previously: scanth2th + """ + kwargs.setdefault('scan_dim',1) + kwargs.setdefault('cts',0.1) + kwargs.setdefault('execute',True) + + th_start = (tth_start)/2+th_offset + th_stop = (tth_stop)/2+th_offset + th_step = int(tth_step/2) + + print('tth: '+str(tth_start)+"/"+str(tth_stop)+"/"+str(tth_step)) + print('th: '+str(th_start)+"/"+str(th_stop)+"/"+str(tth_step/2.0)) + + #write to the scanRecord + th_val,th_rbv,th_spmg,th_pv = Kappa_motor_dictionary['th'] + tth_val,tth_rbv,tth_spmg,tth_pv = Kappa_motor_dictionary['tth'] + + kwargs.update("positioner_num",2) + BL.mda.fillin(th_val,th_rbv,th_start,th_stop,th_step,**kwargs) + + kwargs.update("positioner_num",1) + BL.mda.fillin(tth_val,tth_rbv,tth_start,tth_stop,tth_step,**kwargs) + + scaler_cts(kwargs['cts'],verbose=False) + if kwargs['execute']: + BL.mda.go(**kwargs) + + #clean up after scan + scaler_cts(verbose=False) + + +def scan_th2th_table(tth_table,th0,**kwargs): + """ + Create a table for tth, e.g.: + mytable_tth=[] + for q in range(2,41,1): + mytable_tth.append(q) + print(mytable_tth) + + th0 = th value at grazing incidence + cts = counting time (default is 1s) + + **kwargs + scan_dim: 1 (default) + cts: integration time for scalers and mpa/mcp => 0.1 (default) + execute: True/False to start the scan => True (default) + + Previously: scanth2th_table + + """ + kwargs.setdefault('cts',0.1) + kwargs.setdefault('execute',True) + + tth_table=np.asarray(tth_table) + th_table=tth_table/2.0+th0 + + th_val,th_rbv,th_spmg,th_pv = Kappa_motor_dictionary['th'] + tth_val,tth_rbv,tth_spmg,tth_pv = Kappa_motor_dictionary['tth'] + + #write to the scanRecord + kwargs.update("positioner_num",1) + BL.mda.fillin_table(tth_val,tth_rbv,tth_table,**kwargs) + + kwargs.update("positioner_num",2) + BL.mda.fillin_table(th_val,th_rbv,th_table,**kwargs) + BL.mda.positioner_after_scan(after="STAY") + + + scaler_cts(kwargs['cts'],verbose=False,**kwargs) + if kwargs['execute']: + BL.mda.go(**kwargs) + + #clean up after scan + BL.mda.table_reset_after() + scaler_cts(verbose=False) + BL.mda.positioner_after_scan(after="PRIOR POS",**kwargs) + + + + +############################################################################################################# +############################## Preset Positions ############################## +############################################################################################################## +def KappaTransfer_StrSeq(): + #User= [ DESC, x, y, z, tth, kth, kap, kphi] + User = ["Kappa Transfer",0, -2650, -650, 0, 57, 0, -88] + n=4 + KappaPreset_StrSeq(n,User) + + +def KappaGrazing_StrSeq(): #Need to determine positions and then add to the Kappa graphic + #Dial= [ DESC, x, y, z, tth, kth, kap, kphi] + User = ["Kappa Grazing",0, 0, 0, 0, 57.045, 134.76,57.045] + n=3 + KappaPreset_StrSeq(n,User) + +def Kappa_ResetPreset(): + KappaGrazing_StrSeq() + KappaTransfer_StrSeq() + + + + + + + + + + +############################################################################################################## +############################## Kappa light ############################## +############################################################################################################## +def Kappa_light(ON_OFF): + """ + Previously:light + """ + light_pv = '29idd:Unidig1Bo0' + if ON_OFF.lower() == 'on': + light=0 + elif ON_OFF.lower() == 'off': + light=1 + caput(light_pv,light) + print(("Turning light "+ON_OFF+".")) + + + + +def Kappa_detector_triggers_strSeq(**kwargs): # do we need to add 29idb:ca5 ??? + """ + """ + kwargs.setdefault(seq_num,8) + seq_num=kwargs['seq_num'] + + detector_triggers_pv,detector_triggers_proc = userStringSeq_pvs(BL.ioc, seq_num) + + #clear the userStringSeq + userStringSeq_clear(BL.ioc,seq_num=kwargs['seq_num']) + caput(detector_triggers_pv+".DESC","Kappa_Trigger1") + + #no triggers see ARPES_detector_triggers_sequence for example + + return detector_triggers_proc + + + + + + +def KappaPreset_StrSeq(n,User): + scanIOC="Kappa" + motorIOC="29idKappa:" + motor = ["m2","m3","m4","m9","m8","m7","m1"] + strSeq_pv = userStringSeq_clear(mda,n) + + if User[0] == "Kappa Grazing": phi0= 0 + if User[0] == "Kappa Transfer": phi0= 57 + caput(strSeq_pv+".DESC",User[0]) + caput(strSeq_pv+".LNK1", "29idKappa:userCalcOut9.A CA NMS") # MPA HV pV + caput(strSeq_pv+".DO1",0) # MPA HV = 0 + caput(strSeq_pv+".WAIT1","Wait") # Wait for completion + caput(strSeq_pv+".LNK2", "29idKappa:m1.VAL CA NMS") # phi = phi0 + caput(strSeq_pv+".DO2",phi0) + caput(strSeq_pv+".WAIT2","Wait") # Wait for completion + for i in range(3,10): + caput(strSeq_pv+".LNK"+str(i),motorIOC+motor[i-3]+".VAL CA NMS") + caput(strSeq_pv+".DO"+str(i),User[i-2]) + if i < 9: + caput(strSeq_pv+".WAIT"+str(i),"After8") + + + + + + + + +def Bragg_Angle_CalcOut(d,eV,l): + n=7 + userCalcOut_pv = userCalcOut_clear(mda,n) + + h=4.135667516e-15 + c=299792458 + f=h*c*1e9*10 + caput(userCalcOut_pv+"DESC","Bragg_Angle") + caput(userCalcOut_pv+"A",d) + caput(userCalcOut_pv+"B",l) + caput(userCalcOut_pv+"C",eV) + caput(userCalcOut_pv+"D",np.pi) + caput(userCalcOut_pv+"E",f) + caput(userCalcOut_pv+".CALC$","ASIN(B*E/(C*"+str(2)+"*A))*"+str(180)+"/D") + +############################################################################################################## +######################## Scan Temp and Pressure ############################## +############################################################################################################## +def Kappa_temperature_pressure_scan(scan_dim=1): + """ + starts scan to monitor temperature and pressure in the kappa chamber + + Previously: Kappa_ScanTempPres + """ + + pv="29id"+BL.mda.ioc+":scan"+str(scan_dim) + #Clear all scan pvs + caput(pv+".CMND",6) + #Set detectors + caput(pv+".D01PV","29idd:tc1:getVal_A.VAL") + caput(pv+".D02PV","29idd:tc1:getVal_B.VAL") + caput(pv+".D03PV","29idb:VS11D.VAL") + #time scan + BL.mda.time_go() + + + +############################################################################################################## +############################## Kappa safestate ############################## +############################################################################################################## +def Kappa_safe_state(**kwargs): + """ + puts the C-branch in a safe state, + **kwargs + MPA_off = True; turns off EA HV + shutter_close = True; closes the D-shutter + valve_close = True; closes the D-valve + """ + kwargs.setdefault("MPA_off",True) + kwargs.setdefault("shutter_close",True) + kwargs.setdefault("valve_close",True) + + if kwargs["EA_off"]: + try: + mpa_HV_off() + except: + print('MPA is not running') + + if kwargs['valve_close']: + valve_close(branch_valves('d'), verbose=True) + + if kwargs['shutter_close']: + branch_shutter_close('d') + +############################################################################################################## +############################## Kappa motors ############################## +############################################################################################################## +### x ### +def mvx(val,**kwargs): + """ moves x motor in the endstation""" + Kappa_Motors.move('x',val,**kwargs) + +def dmvx(val): + """ relative x motor in the endstation""" + Kappa_Motors.move('x',val,relative=True) + +def scanx(start,stop,step,**kwargs): + """ scans the x motor in the endstation""" + Kappa_Motors.scan("x",start,stop,step,**kwargs) + +def dscanx(start,stop,step): + """relative scans of the x motor in the endstation """ + Kappa_Motors.scan("x",start,stop,step,relative=True) + +### y ### +def mvy(val,**kwargs): + """ moves y motor in the endstation""" + Kappa_Motors.move('y',val,**kwargs) + +def dmvy(val): + """ relative y motor in the endstation""" + Kappa_Motors.move('y',val,relative=True) + +def scany(start,stop,step,**kwargs): + """ scans the y motor in the endstation""" + Kappa_Motors.scan("y",start,stop,step,**kwargs) + +def dscany(start,stop,step): + """relative scans of the y motor in the endstation """ + Kappa_Motors.scan("y",start,stop,step,relative=True) + +### z ### +def mvz(val,**kwargs): + """ moves z motor in the endstation""" + Kappa_Motors.move('z',val,**kwargs) + +def dmvz(val): + """ relative z motor in the endstation""" + Kappa_Motors.move('z',val,relative=True) + +def scanz(start,stop,step,**kwargs): + """ scans the z motor in the endstation""" + Kappa_Motors.scan("z",start,stop,step,**kwargs) + +def dscanz(start,stop,step): + """relative scans of the z motor in the endstation """ + Kappa_Motors.scan("z",start,stop,step,relative=True) + +### kth ### +def mvkth(val,**kwargs): + """ moves kth motor in the endstation""" + Kappa_Motors.move('kth',val,**kwargs) + +def dmvkth(val): + """ relative kth motor in the endstation""" + Kappa_Motors.move('kth',val,relative=True) + +def scankth(start,stop,step,**kwargs): + """ scans the kth motor in the endstation""" + Kappa_Motors.scan("kth",start,stop,step,**kwargs) + +def dscankth(start,stop,step): + """relative scans of the kth motor in the endstation """ + Kappa_Motors.scan("kth",start,stop,step,relative=True) + +### kphi ### +def mvkphi(val,**kwargs): + """ moves kphi motor in the endstation""" + Kappa_Motors.move('kphi',val,**kwargs) + +def dmvkphi(val): + """ relative kphi motor in the endstation""" + Kappa_Motors.move('kphi',val,relative=True) + +def scankphi(start,stop,step,**kwargs): + """ scans the kphi motor in the endstation""" + Kappa_Motors.scan("kphi",start,stop,step,**kwargs) + +def dscankphi(start,stop,step): + """relative scans of the kphi motor in the endstation """ + Kappa_Motors.scan("kphi",start,stop,step,relative=True) + +### kap ### +def mvkap(val,**kwargs): + """ moves kap motor in the endstation""" + Kappa_Motors.move('kap',val,**kwargs) + +def dmvkap(val): + """ relative kap motor in the endstation""" + Kappa_Motors.move('kap',val,relative=True) + +def scankap(start,stop,step,**kwargs): + """ scans the kap motor in the endstation""" + Kappa_Motors.scan("kap",start,stop,step,**kwargs) + +def dscankap(start,stop,step): + """relative scans of the kap motor in the endstation """ + Kappa_Motors.scan("kap",start,stop,step,relative=True) + +### tth ### +def mvtth(val,**kwargs): + """ moves tth motor in the endstation""" + Kappa_Motors.move('tth',val,**kwargs) + +def dmvtth(val): + """ relative tth motor in the endstation""" + Kappa_Motors.move('tth',val,relative=True) + +def scantth(start,stop,step,**kwargs): + """ scans the tth motor in the endstation""" + Kappa_Motors.scan("tth",start,stop,step,**kwargs) + +def dscantth(start,stop,step): + """relative scans of the tth motor in the endstation """ + Kappa_Motors.scan("tth",start,stop,step,relative=True) + +### th ### +def mvth(val,**kwargs): + """ moves th motor in the endstation""" + Kappa_Motors.move('th',val,**kwargs) + +def dmvth(val): + """ relative th motor in the endstation""" + Kappa_Motors.move('th',val,relative=True) + +def scanth(start,stop,step,**kwargs): + """ scans the th motor in the endstation""" + Kappa_Motors.scan("th",start,stop,step,**kwargs) + +def dscanth(start,stop,step): + """relative scans of the th motor in the endstation """ + Kappa_Motors.scan("th",start,stop,step,relative=True) + +### chi ### +def mvchi(val,**kwargs): + """ moves chi motor in the endstation""" + Kappa_Motors.move('chi',val,**kwargs) + +def dmvchi(val): + """ relative chi motor in the endstation""" + Kappa_Motors.move('chi',val,relative=True) + +def scanchi(start,stop,step,**kwargs): + """ scans the chi motor in the endstation""" + Kappa_Motors.scan("chi",start,stop,step,**kwargs) + +def dscanchi(start,stop,step): + """relative scans of the chi motor in the endstation """ + Kappa_Motors.scan("chi",start,stop,step,relative=True) + +### phi ### +def mvphi(val,**kwargs): + """ moves phi motor in the endstation""" + Kappa_Motors.move('phi',val,**kwargs) + +def dmvphi(val): + """ relative phi motor in the endstation""" + Kappa_Motors.move('phi',val,relative=True) + +def scanphi(start,stop,step,**kwargs): + """ scans the phi motor in the endstation""" + Kappa_Motors.scan("phi",start,stop,step,**kwargs) + +def dscanphi(start,stop,step): + """relative scans of the phi motor in the endstation """ + Kappa_Motors.scan("phi",start,stop,step,relative=True) + +### uan moves tth and th simultaneously +def uan(tth,th): + """ Moves tth and th motors simultaneously in the in the Kappa chamber + """ + #move tth and th + mvth(th,wait=False) + mvtth(tth,wait=False) + sleep(0.2) + + while True: + status = Kappa_Motors.status() + if status == 0: + sleep(0.2) + else: + tth_RBV=round(Kappa_Motors.get('tth',verbose=False),3) + th_RBV=round(Kappa_Motors.get('th',verbose=False),3) + print('tth='+str(tth_RBV)+' th='+str(th_RBV)) + break + +######## mprint and mvsample ################### +def mprint(): + """ + prints current position of the physical motors + """ + Kappa_Motors.mprint() + +def mvsample(position_list): + """ + moves the sample to the position sepcified by position_list + position_list = ['description',x,y,z,th,chi,phi] + + Previously: sample + """ + Kappa_Motors.mvsample(position_list) \ No newline at end of file diff --git a/build/lib/iexcode/instruments/Kappa_Euler.py b/build/lib/iexcode/instruments/Kappa_Euler.py new file mode 100644 index 0000000..42dfe2f --- /dev/null +++ b/build/lib/iexcode/instruments/Kappa_Euler.py @@ -0,0 +1,120 @@ +import numpy as np +from epics import caget, caput + +from iexcode.instruments.userCalcs import userCalcOut_clear +from iexcode.instruments.IEX_endstations import mda +from iexcode.instruments.userCalcs import userCalcOut_clear + +#### Obsolete? + +def Bragg_Index_CalcOut(d,eV,th): + n=8 + userCalcOut_pv = userCalcOut_clear(mda,n) + + h=4.135667516e-15 + c=299792458 + f=h*c*1e9*10 + + caput(userCalcOut_pv+".DESC","Bragg_Index") + caput(userCalcOut_pv+".A",d) + caput(userCalcOut_pv+".B",th) + caput(userCalcOut_pv+".C",eV) + caput(userCalcOut_pv+".D",np.pi) + caput(userCalcOut_pv+".E",f) + caput(userCalcOut_pv+".CALC$","SIN(B*D/"+str(180)+")*"+str(2)+"*A*C/E") + + + +def KtoE_th_CalcOut(): + n=1 + userCalcOut_pv = userCalcOut_clear(mda,n) + + caput(userCalcOut_pv+".DESC","KtoE_th") + caput(userCalcOut_pv+".INPA","29idKappa:m8.RBV CP NMS") #A=kth + caput(userCalcOut_pv+".INPB","29idKappa:m7.RBV CP NMS") #B=kap + caput(userCalcOut_pv+".INPC","29idKappa:m1.RBV CP NMS") #C=kphi + caput(userCalcOut_pv+".D",3.141592653589793) + caput(userCalcOut_pv+".E",180) + caput(userCalcOut_pv+".F",50) + caput(userCalcOut_pv+".H",57.045) + caput(userCalcOut_pv+".CALC$","((A-(G-H))*D/E-ATAN(TAN(B*D/E/2.0)*COS(F*D/E)))*E/D") + caput(userCalcOut_pv+".DOPT",0) # Use CAL + +def KtoE_chi_CalcOut(): + n=2 + userCalcOut_pv = userCalcOut_clear(mda,n) + + caput(userCalcOut_pv+".DESC","KtoE_chi") + caput(userCalcOut_pv+".INPA","29idKappa:m8.RBV CP NMS") #A=kth + caput(userCalcOut_pv+".INPB","29idKappa:m7.RBV CP NMS") #B=kap + caput(userCalcOut_pv+".INPC","29idKappa:m1.RBV CP NMS") #C=kphi + caput(userCalcOut_pv+".D",3.141592653589793) + caput(userCalcOut_pv+".E",180) + caput(userCalcOut_pv+".F",50) + caput(userCalcOut_pv+".CALC$","(2*ASIN(SIN(B*D/E/2.0) * SIN(F*D/E)))*E/D") + caput(userCalcOut_pv+".DOPT",0) # Use CAL + +def KtoE_phi_CalcOut(): + n=3 + userCalcOut_pv = userCalcOut_clear(mda,n) + + caput(userCalcOut_pv+".DESC","KtoE_phi") + caput(userCalcOut_pv+".INPA","29idKappa:m8.RBV CP NMS") #A=kth + caput(userCalcOut_pv+".INPB","29idKappa:m7.RBV CP NMS") #B=kap + caput(userCalcOut_pv+".INPC","29idKappa:m1.RBV CP NMS") #C=kphi + caput(userCalcOut_pv+".D",3.141592653589793) + caput(userCalcOut_pv+".E",180) + caput(userCalcOut_pv+".F",50) + caput(userCalcOut_pv+".CALC$","(C*D/E-ATAN(TAN(B*D/E/2.0)*COS(F*D/E)))*E/D") + caput(userCalcOut_pv+".DOPT",0) # Use CAL + + + + +def EtoK_kth_CalcOut(): + n=4 + userCalcOut_pv = userCalcOut_clear(mda,n) + + caput(userCalcOut_pv+".DESC","EtoK_kth") + caput(userCalcOut_pv+".INPA","29idKappa:userCalcOut1.VAL CP NMS") #A=th + caput(userCalcOut_pv+".INPB","29idKappa:userCalcOut2.VAL CP NMS") #B=chi + caput(userCalcOut_pv+".INPC","29idKappa:userCalcOut3.VAL CP NMS") #C=phi + caput(userCalcOut_pv+".D",3.141592653589793) + caput(userCalcOut_pv+".E",180) + caput(userCalcOut_pv+".F",50) + caput(userCalcOut_pv+".CALC$","(A*D/E-ASIN(-TAN(B*D/E/2.0)/TAN(F*D/E)))*E/D") + caput(userCalcOut_pv+".DOPT",0) # Use CAL + +def EtoK_kap_CalcOut(): + n=5 + userCalcOut_pv = userCalcOut_clear(mda,n) + + caput(userCalcOut_pv+".DESC","EtoK_kap") + caput(userCalcOut_pv+".INPA","29idKappa:userCalcOut1.VAL CP NMS") #A=th + caput(userCalcOut_pv+".INPB","29idKappa:userCalcOut2.VAL CP NMS") #B=chi + caput(userCalcOut_pv+".INPC","29idKappa:userCalcOut3.VAL CP NMS") #C=phi + caput(userCalcOut_pv+".D",3.141592653589793) + caput(userCalcOut_pv+".E",180) + caput(userCalcOut_pv+".F",50) + caput(userCalcOut_pv+".CALC$","((2*ASIN(SIN(B*D/E/2.0) / SIN(F*D/E))))*E/D") + caput(userCalcOut_pv+".DOPT",0) # Use CAL + +def EtoK_kphi_CalcOut(): + n=6 + userCalcOut_pv = userCalcOut_clear(mda,n) + + caput(userCalcOut_pv+".DESC","EtoK_kphi") + caput(userCalcOut_pv+".INPA","29idKappa:userCalcOut1.VAL CP NMS") #A=th + caput(userCalcOut_pv+".INPB","29idKappa:userCalcOut2.VAL CP NMS") #B=chi + caput(userCalcOut_pv+".INPC","29idKappa:userCalcOut3.VAL CP NMS") #C=phi + caput(userCalcOut_pv+".D",3.141592653589793) + caput(userCalcOut_pv+".E",180) + caput(userCalcOut_pv+".F",50) + caput(userCalcOut_pv+".CALC$","(C*D/E-ASIN(-TAN(B*D/E/2.0)/TAN(F*D/E)))*E/D") + caput(userCalcOut_pv+".DOPT",0) # Use CAL + + + + + + diff --git a/build/lib/iexcode/instruments/Kappa_det.py b/build/lib/iexcode/instruments/Kappa_det.py new file mode 100644 index 0000000..9a1a190 --- /dev/null +++ b/build/lib/iexcode/instruments/Kappa_det.py @@ -0,0 +1,77 @@ +from time import sleep +from epics import caget, caput + +from iexcode.instruments.IEX_endstations import Motors + + +############################################################################################################## +################################ Kappa detector class ############################## +############################################################################################################## +det_set_pv = '29idKappa:det:set' +det_list = 'd3', 'd4', 'mcp', 'apd', 'yag' + +class Kappa_Detector: + """ + class for Kappa detector + """ + + def __init__(self): + self.get() + + def get(self): + """ + gets the current det_name and position + + det_name, tth_val + """ + #det_name = caget('29idKappa:userStringSeq6.STR1') + det_name = caget(det_set_pv) + self.name = det_name + tth_val = Motors.get('tth') + return self.name, tth_val + + def set(self,det_name,move=True): + """ + sets the tth detector + and moves the motor to tth value for the new detecotr + + det_names + d3: large diode + d4: small diode + mcp: for the mpa + apd: avalanche photodiode (not currently installed) + yag: yag 'fluorescence screen' + """ + #get current value for tth + tth_val = Motors.get('tth') + + #change det + if det_name in det_list: + caput(det_set_pv,det_name) + if move: + Motors.move('tth',tth_val,wait=True,verbose=False) + + def tth0_set(move): + """ + resetting the tth motor zero to correspond to direct beam + + only works with d4 + """ + current_det=caget(det_set_pv,as_string=True) + tth_pv = Motors._motor_dictionary['th'][3] + + if current_det != 'd4': + print('tth0 can only be redefined with d4') + else: + foo=input('Are you sure you want to reset tth0 (y or n)? >') + if foo == 'Y' or foo == 'y' or foo == 'yes'or foo == 'YES': + caput(tth_pv+'.SET','Set') + sleep(0.5) + caput(tth_pv+'.VAL',0) + caput(tth_pv+'.DVAL',0) + sleep(0.5) + caput(tth_pv+'.SET','Use') + print("tth position reset to 0") + else: + print("That's ok, everybody can get cold feet in tough situation...") + diff --git a/build/lib/iexcode/instruments/Lakeshore_335.py b/build/lib/iexcode/instruments/Lakeshore_335.py new file mode 100644 index 0000000..5acc6b9 --- /dev/null +++ b/build/lib/iexcode/instruments/Lakeshore_335.py @@ -0,0 +1,448 @@ +""" +work in progress need to redo +""" +from time import sleep, strftime, localtime + +import numpy as np +from epics import caget, caput + + +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.utilities import read_dict + +def Lakeshore_reset(pv,d): + """ + resets the lake short to the default paramters defind in the dictionary d + """ + for key in d.keys(): + caput(pv+key,d[key]) + + +############################################################################################################# +############################## Lakeshore 335 Diode Curves ############################## +############################################################################################################## +#---Settings as of 12/04/2018--- +#DiodeCurve_Write(22,"SI435") +#DiodeCurve_SetInput("A",22,400) +#DiodeCurve_Write(23,"SI410") +#DiodeCurve_SetInput("B",23,450) + + + +# ============================================================================= +# def DiodeCurves(DiodeModel): +# DiodeCalibration={} +# DiodeCalibration["SI435"]=[0.00000,1.66751,1.64531,1.61210,1.57373,1.53247,1.49094,1.45196,1.41723,1.38705,1.36089,1.33785,1.31699,1.29756,1.27912,1.26154,1.24489,1.22917,1.21406,1.19855,1.18193,1.16246,1.13841,1.12425,1.11828,1.11480,1.11217,1.10996,1.10828,1.10643,1.10465,1.10295,1.10124,1.09952,1.09791,1.09629,1.09468,1.09306,1.09145,1.08983,1.08821,1.08659,1.08497,1.08334,1.08171,1.08008,1.07844,1.07681,1.07517,1.07353,1.07188,1.07023,1.06858,1.06693,1.06528,1.06362,1.06196,1.06029,1.05863,1.05696,1.05528,1.05360,1.05192,1.05024,1.04856,1.04687,1.04518,1.04349,1.04179,1.04010,1.03839,1.03669,1.03498,1.03327,1.03156,1.02985,1.02814,1.02642,1.02471,1.02299,1.02127,1.01957,1.01785,1.01612,1.01439,1.01267,1.01093,1.00918,1.00744,1.00569,1.00393,1.00218,1.00042,0.99865,0.99687,0.99510,0.99332,0.99153,0.98975,0.98795,0.98615,0.98434,0.98254,0.98073,0.97891,0.97710,0.97527,0.97344,0.97161,0.96978,0.96794,0.96609,0.96424,0.96239,0.96053,0.95867,0.95680,0.95492,0.95304,0.95116,0.94927,0.94738,0.94549,0.94358,0.94167,0.93976,0.93785,0.93592,0.93398,0.93203,0.93008,0.92812,0.92616,0.92418,0.92221,0.92022,0.91823,0.91623,0.91423,0.91222,0.91021,0.90819,0.90617,0.90414,0.90212,0.90008,0.89805,0.89601,0.89397,0.89293,0.88988,0.88783,0.88578,0.88372,0.88166,0.87959,0.87752,0.87545,0.87337,0.87129,0.86921,0.86712,0.86503,0.86294,0.86084,0.85874,0.85664,0.85453,0.85242,0.85030,0.84818,0.84606,0.84393,0.84180,0.83967,0.83754,0.83540,0.83325,0.83111,0.82896,0.82680,0.82465,0.82249,0.82032,0.81816,0.81599,0.81381,0.81163,0.80945,0.80727,0.80508,0.80290,0.80071,0.79851,0.79632,0.79412,0.79192,0.78972,0.78752,0.78532,0.78311,0.78090,0.77869,0.77648,0.77426,0.77205,0.76983,0.76761,0.76539,0.76317,0.76094,0.75871,0.75648,0.75425,0.75202,0.74978,0.74755,0.74532,0.74308,0.74085,0.73861,0.73638,0.73414,0.73191,0.72967,0.72743,0.72520,0.72296,0.72072,0.71848,0.71624,0.71400,0.71176,0.70951,0.70727,0.70503,0.70278,0.70054,0.69829,0.69604,0.69379,0.69154,0.68929,0.68704,0.68479,0.68253,0.68028,0.67802,0.67576,0.67351,0.67124,0.66898,0.66672,0.66445,0.66219,0.65992,0.65765,0.65538,0.65310,0.65083,0.64855,0.64628,0.64400,0.64172,0.63944,0.63716,0.63487,0.63259,0.63030,0.62802,0.62573,0.62344,0.62115,0.61885,0.61656,0.61427,0.61197,0.60968,0.60738,0.60509,0.60279,0.60050,0.59820,0.59590,0.59360,0.59131,0.58901,0.58671,0.58441,0.58211,0.57980,0.57750,0.57520,0.57289,0.57059,0.56828,0.56598,0.56367,0.56136,0.55905,0.55674,0.55443,0.55211,0.54980,0.54748,0.54516,0.54285,0.54053,0.53820,0.53588,0.53356,0.53123,0.52891,0.52658,0.52425,0.52192,0.51958,0.51725,0.51492,0.51258,0.51024,0.50790,0.50556,0.50322,0.50088,0.49854,0.49620,0.49385,0.49151,0.48916,0.48681,0.48446,0.48212,0.47977,0.47741,0.47506,0.47271,0.47036,0.46801,0.46565,0.46330,0.46095,0.45860,0.45624,0.45389,0.45154,0.44918,0.44683,0.44447,0.44212,0.43976,0.43741,0.43505,0.43270,0.43034,0.42799,0.42563,0.42327,0.42092,0.41856,0.41620,0.41384,0.41149,0.40913,0.40677,0.40442,0.40206,0.39970,0.39735,0.39499,0.39263,0.39027,0.38792,0.38556,0.38320,0.38085,0.37849,0.37613,0.37378,0.37142,0.36906,0.36670,0.36435,0.36199,0.35963,0.35728,0.35492,0.35256,0.35021,0.34785,0.34549,0.34313,0.34078,0.33842,0.33606,0.33371,0.33135,0.32899,0.32667,0.32428,0.32192] +# DiodeCalibration["SI410"]=[0.0000,1.7191,1.7086,1.6852,1.6530,1.6124,1.5659,1.5179,1.4723,1.4309,1.3956,1.3656,1.3385,1.3142,1.2918,1.2712,1.2517,1.2333,1.2151,1.1963,1.1759,1.1524,1.1293,1.1192,1.1146,1.1114,1.1090,1.1069,1.1049,1.1031,1.1014,1.0997,1.0980,1.0964,1.0949,1.0933,1.0917,1.0902,1.0886,1.0871,1.0855,1.0839,1.0824,1.0808,1.0792,1.0776,1.0760,1.0744,1.0728,1.0712,1.0696,1.0679,1.0663,1.0646,1.0630,1.0613,1.0597,1.0580,1.0563,1.0547,1.0530,1.0513,1.0497,1.0480,1.0463,1.0446,1.0429,1.0412,1.0395,1.0378,1.0361,1.0344,1.0327,1.0310,1.0293,1.0276,1.0259,1.0242,1.0224,1.0207,1.0190,1.0172,1.0155,1.0137,1.0120,1.0102,1.0085,1.0067,1.0049,1.0032,1.0014,0.9996,0.9978,0.9960,0.9942,0.9924,0.9905,0.9887,0.9869,0.9851,0.9832,0.9814,0.9795,0.9777,0.9758,0.9740,0.9721,0.9703,0.9684,0.9665,0.9646,0.9628,0.9609,0.9590,0.9571,0.9552,0.9533,0.9514,0.9495,0.9476,0.9457,0.9437,0.9418,0.9398,0.9379,0.9359,0.9340,0.9320,0.9300,0.9281,0.9261,0.9241,0.9222,0.9202,0.9182,0.9162,0.9142,0.9122,0.9102,0.9082,0.9062,0.9042,0.9022,0.9002,0.8982,0.8962,0.8942,0.8921,0.8901,0.8881,0.8860,0.8840,0.8820,0.8799,0.8779,0.8758,0.8738,0.8717,0.8696,0.8676,0.8655,0.8634,0.8613,0.8593,0.8572,0.8551,0.8530,0.8509,0.8488,0.8467,0.8446,0.8425,0.8404,0.8383,0.8362,0.8341,0.8320,0.8299,0.8278,0.8257,0.8235,0.8214,0.8193,0.8171,0.8150,0.8129,0.8107,0.8086,0.8064,0.8043,0.8021,0.8000,0.7979,0.7957,0.7935,0.7914,0.7892,0.7871,0.7849,0.7828,0.7806,0.7784,0.7763,0.7741,0.7719,0.7698,0.7676,0.7654,0.7632,0.7610,0.7589,0.7567,0.7545,0.7523,0.7501,0.7479,0.7457,0.7435,0.7413,0.7391,0.7369,0.7347,0.7325,0.7303,0.7281,0.7259,0.7237,0.7215,0.7193,0.7170,0.7148,0.7126,0.7104,0.7082,0.7060,0.7037,0.7015,0.6993,0.6971,0.6948,0.6926,0.6904,0.6881,0.6859,0.6837,0.6814,0.6792,0.6770,0.6747,0.6725,0.6702,0.6680,0.6657,0.6635,0.6612,0.6590,0.6567,0.6545,0.6522,0.6500,0.6477,0.6455,0.6432,0.6410,0.6387,0.6365,0.6342,0.6319,0.6297,0.6274,0.6251,0.6229,0.6206,0.6183,0.6161,0.6138,0.6115,0.6092,0.6070,0.6047,0.6024,0.6001,0.5979,0.5956,0.5933,0.5910,0.5887,0.5865,0.5842,0.5819,0.5796,0.5773,0.5750,0.5727,0.5705,0.5682,0.5659,0.5636,0.5613,0.5590,0.5567,0.5544,0.5521,0.5498,0.5475,0.5452,0.5429,0.5406,0.5383,0.5360,0.5337,0.5314,0.5291,0.5268,0.5245,0.5222,0.5199,0.5176,0.5153,0.5130,0.5107,0.5084,0.5061,0.5038,0.5015,0.4992,0.4968,0.4945,0.4922,0.4899,0.4876,0.4853,0.4830,0.4806,0.4783,0.4760,0.4737,0.4714,0.4690,0.4667,0.4644,0.4621,0.4597,0.4574,0.4551,0.4528,0.4504,0.4481,0.4458,0.4435,0.4411,0.4388,0.4365,0.4341,0.4318,0.4295,0.4271,0.4248,0.4225,0.4201,0.4178,0.4154,0.4131,0.4108,0.4084,0.4061,0.4037,0.4014,0.3991,0.3967,0.3944,0.4154,0.4131,0.4108,0.4084,0.4061,0.4037,0.4014,0.3991,0.3967,0.3709,0.3685,0.3662,0.3638,0.3615,0.3591,0.3567,0.3544,0.3520,0.3497,0.3473,0.3449,0.3426,0.3402,0.3379,0.3355,0.3331,0.3308,0.3284,0.3260,0.3237,0.3213,0.3189,0.3165,0.3142,0.3118,0.3094,0.3071,0.3047,0.3023,0.2999,0.2976,0.2952,0.2928,0.2904,0.2880,0.2857,0.2833,0.2809,0.2785,0.2761,0.2738,0.2714,0.2690,0.2666,0.2642,0.2618,0.2594,0.2570,0.2547,0.2523,0.2499,0.2475,0.2451,0.2427,0.2403,0.2379,0.2355,0.2331,0.2307,0.2283,0.2259,0.2235,0.2211,0.2187,0.2163,0.2139,0.2115,0.2091,0.2067,0.2043] +# return DiodeModel,DiodeCalibration[DiodeModel] +# ============================================================================= + +def DiodeCurve_Write_backup(CRVNum,DiodeModel): + """ + Writes a Diode curve to the Lakeshore 335 temperature controller + Diode curves are saved in the fname=Dict_TempDiode.txt + usage: DiodeCurve_Write(22,"SI435") to write to curve 22 the dictionary entry "SI435" + DiodeCurve_SetInput("A",22,400) + """ + fname="Dict_TempDiode.txt" + PV="29idARPES:LS335:TC1:serial.AOUT" + + t=1 + index=1 + #Curve Header + DiodeDict=read_dict(fname) + CRVHDR,CRVList=DiodeModel,DiodeDict[DiodeModel] + + cmd="CRVHDR "+str(CRVNum)+","+CRVHDR+",SN,2,"+str(len(CRVList)-1)+",1" + print(cmd) + caput(PV,cmd,wait=True,timeout=1800) + while t <len(CRVList): + if(t>=0 and t<30): + countby=1 + elif(t>=30 and t<230): + countby=2 + elif(t>=230): + countby=5 + cmd="CRVPT "+str(CRVNum)+","+str(index)+","+str(CRVList[t])+","+str(t) + #print cmd + caput(PV,cmd,wait=True,timeout=1800) + #sleep(1) + t=t+countby + index+=1 + #write 0,0 to indicate list is done + cmd="CRVPT "+str(CRVNum)+","+str(index)+","+str(CRVList[0])+","+str(0) + caput(PV,cmd,wait=True,timeout=1800) + print("last point = "+str(index)) + +def DiodeCurve_Write(CRVNum,DiodeModel,run=True): #reversing order since it seems that the curve needs to go from high to low based on the built-in + """ + Writes a Diode curve to the Lakeshore 335 temperature controller + Diode curves are saved in the fname=Dict_TempDiode.txt + + run=True to write to the Lakeshoe + run=False to print only + + usage: DiodeCurve_Write(22,"SI435") to write to curve 22 the dictionary entry "SI435" + DiodeCurve_SetInput("A",22,400) + """ + fname="Dict_TempDiode.txt" + PV="29idARPES:LS335:TC1:serial.AOUT" + + + #Curve Header + CRVList=read_dict(fname)[DiodeModel] + cmd="CRVHDR "+str(CRVNum)+","+DiodeModel+",SN,2,"+str(len(CRVList)-1)+",1" + if run == True: + caput(PV,cmd,wait=True,timeout=1800) + else: + print(cmd) + + #Writing the individual terms (model 335 only supports index:1-200) + T1=50 + T2=230 + T3=len(CRVList) + + T=np.arange(0,T1,1) + V=np.array(CRVList[0:T1]) + + T=np.append(T,np.arange(T1,T2,2)) + V=np.append(V,np.array(CRVList[T1:T2:2])) + + T=np.append(T,np.arange(T2,T3,5)) + V=np.append(V,np.array(CRVList[T2:T3:5])) + + #reverse order + T=T[::-1] + V=V[::-1] + + for i,t in enumerate(T): + cmd="CRVPT "+str(CRVNum)+","+str(i+1)+","+str(V[i])+","+str(t) + #cmd="index,v,t"+str(i+1)#+","+str(V[i])+","+str(t) + if run == True: + caput(PV,cmd,wait=True,timeout=1800) + else: + print(cmd) + +def DiodeCurve_SetInput(Channel,Curve,Tmax): + """ + Sets the diode curve for a given channel on the Lakeshore 335 temperature controller + DiodeCurve_SetInput("A",22,400) + Channel: A or B + Curve: set by DiodeCurve_Write(22,"SI435") + Tmax: max temperature for that diode + (pg 118 of manual) + """ + + PV="29idARPES:LS335:TC1:serial.AOUT" + #Set curve info + cmd="INTYPE "+Channel+","+"1,0,0,0,1" + caput(PV,cmd,wait=True,timeout=1800) + #Select curve + cmd="INCRV "+Channel+","+str(Curve) + caput(PV,cmd,wait=True,timeout=1800) + #Set temperature Limit + cmd="TLIMIT "+Channel+","+str(Tmax) + caput(PV,cmd,wait=True,timeout=1800) + +############################################################################################################## +############################## PID Settings ############################## +############################################################################################################## + + +#Setting the PID +# Set P=10, I=0, D=0; Set T and range, +# if T oscilate above setpoint, P/=2 +# if T doesn't reach setpoint, P*=2 +# if T swings above setpoint but then settle below use this P +# Set I=10; smaller I, more reactive (approximately time(sec) for one full oscillation period +# Set D=I/4 +#Lakeshore 355 I = time in seconds for one oscilation / 1000; D=50-100 for starters + + +def PID_dict(which,T): #Dictionary of PID setting to try for the ARPES chamber + """Dictionary for common PID setting for the ARPES system. + Since the PID is dependent on the cooling power, this will change with the cryogen and the flow rate: + - Flow = "LHe,LN" => LHe = needle valve = "on" and flow = 70 + """ + PID={} +# PID[which,T]=[P,I,D,Range] Range=HIGH,MEDIUM,LOW,OFF + PID['RT', 378.0]=[200.0,0.0,0.0,'MEDIUM'] + PID['RT', 375.0]=[200.0,0.0,0.0,'MEDIUM'] + PID['RT', 338.0]=[100.0,0.0,0.0,'MEDIUM'] + PID['RT', 298.0]=[20.0,0.0,0.0,'LOW'] + PID['GHe', 378.0]=[210.0,0.0,0.0,'MEDIUM'] + PID['GHe', 375.0]=[210.0,0.0,0.0,'MEDIUM'] + PID['GHe', 338.0]=[130.0,0.0,0.0,'MEDIUM'] + PID['GHe', 298.0]=[60.0,0.0,0.0,'MEDIUM'] + PID['GHe', 250.0]=[10.0,0.0,0.0,'LOW'] + PID['LHe',298.0]=[20.0,5.0,1.0,'HIGH'] # +/- 1.1 deg, over 10 min, heater power 53% + PID['LHe',200.0]=[10.0,5.0,1.0,'HIGH'] # +/- 1.1 deg, over 10 min, heater power 53% + PID['LHe',150.0]=[200.0,0.1,0,'HIGH'] # Stablized at 153, heater power 43% + PID['LHe',180.0]=[200.0,0.1,0,'HIGH'] + PID['LHe',230.0]=[200.0,0.1,0,'HIGH'] + PID['LHe',300.0]=[200.0,0.1,0,'HIGH'] + PID['LHe',40.0]=[200.0,0.1,0,'HIGH'] # + PID['LN2',230]=[10.0,0.6,100,'HIGH'] #stable 237.83, needle valve=ON; flow 6 half turns from max) + PID['LN2',180]=[50.0,4,100,'MEDIUM'] + return PID[which,T] + +def PID_set(which,T,heater='ON'): + """ + Uses preset PID settings as defined in PID_dict() + To cool down: heater='OFF' + + """ + P,I,D,Range=PID_dict(which,T) + caput("29idARPES:LS335:TC1:P1",P) + caput("29idARPES:LS335:TC1:I1",I) + caput("29idARPES:LS335:TC1:D1",D) + if heater == 'ON': + caput("29idARPES:LS335:TC1:HTR1:Range",Range) + elif heater == 'OFF': + caput("29idARPES:LS335:TC1:HTR1:Range",'OFF') + print('\nP = ',PID_dict(which,T)[0]) + print('I = ',PID_dict(which,T)[1]) + print('D = ',PID_dict(which,T)[2]) + print('Range = ',PID_dict(which,T)[3]) + + +def SetT_Sample(which,T,precision=5,minutes=15,offset=6): + """ + Sets PID settings for a given set point as defined in PID_dict(). + Available set points: + which = 'RT' : T = 298, 338, 375 + which = 'LHe': T = 150, 200 + which = 'GHe': T = 250, 298, 338, 370 + which = 'LN2': T = 230, 180 + precision is temperature in K + """ + current_T=caget("29idARPES:LS335:TC1:IN1") + print('\nSet T to '+ str(T)+' K @ '+ dateandtime()) + if T>current_T: + PID_set(which,T,'ON') + Range=PID_dict(which,T)[3] + SetT_Up(T,offset,Range,precision,minutes) + else: + PID_set(which,T,'OFF') + Range=PID_dict(which,T)[3] + SetT_Down(T,offset,Range,precision,minutes) + caput("29idARPES:LS335:TC1:OUT1:SP",T) + + +def SetT_Up(T,offset,Range,precision=5,minutes=15): + if Range=='LOW': Range=1 + if Range=='MEDIUM': Range=2 + if Range=='HIGH': Range=3 + t=15 + u=0 + caput('29idARPES:LS335:TC1:OUT1:SP',T) # set the set point to T + while True: # while TA < T + TA=caget('29idARPES:LS335:TC1:IN1') # get current temperature at the sample + TB=caget('29idARPES:LS335:TC1:IN2') # get current temperature (B=cold finger) + if TA<T-precision: # if it hasn't reach SP + caput("29idARPES:LS335:TC1:HTR1:Range",min(Range,3)) # increase heater range to Range +1 + while True: # while TB < T+offset: + TB=caget('29idARPES:LS335:TC1:IN2') # get current temperature at the cold finger + if (t%120)==0: + print('\nTA = ',TA,' TB = ',TB, ' @ ',dateandtime()) + if TB<(T+offset) and t<=minutes*60: #if it hasn't reach the SP+offser + sleep(15) + t+=15 + #print t, TA, TB + elif TB<(T+offset) and t>minutes*60: + heater_power=caget('29idARPES:LS335:TC1:HTR1') + heater_range=caget('29idARPES:LS335:TC1:HTR1:Range') + if heater_power > 90: + caput("29idARPES:LS335:TC1:HTR1:Range",min(heater_range+1,3)) + print('Change Range to',caget('29idARPES:LS335:TC1:HTR1:Range'),' @ ',dateandtime()) + elif heater_range==3 or heater_power<=90: + P=caget("29idARPES:LS335:TC1:P1") + caput("29idARPES:LS335:TC1:P1",P*1.5) + print('Change P to',caget("29idARPES:LS335:TC1:P1"),' @ ',dateandtime()) + t=0 + + else: #else + break # break + caput("29idARPES:LS335:TC1:HTR1:Range",'OFF') # turn off the heater + elif TA>T-precision: # if it has reach the set point + break # break + print('TA = ',TA,' TB = ',TB, ' @ ',dateandtime()) # print temperatures + caput("29idARPES:LS335:TC1:HTR1:Range",Range) # set the heater range to preset value + + + + + + +def SetT_Down(T,offset,Range,precision=5,minutes=15): + t=0 + caput('29idARPES:LS335:TC1:OUT1:SP',T) # set the set point to T + while True: # while TA < T + TA=caget('29idARPES:LS335:TC1:IN1') + TB=caget('29idARPES:LS335:TC1:IN2') + if (t%120)==0: + print('\nTA = ',TA,' TB = ',TB, ' @ ',dateandtime()) + if TA>T+precision: + sleep(15) + t+=15 + elif t>minutes*60: + P=caget("29idARPES:LS335:TC1:P1") + caput("29idARPES:LS335:TC1:P1",P/1.5) + t=0 + else: + break + caput("29idARPES:LS335:TC1:HTR1:Range",Range) + print('TA = ',TA,' TB = ',TB, ' @ ',dateandtime()) + + +def Get_PID(which='LHe'): + T=caget("29idARPES:LS335:TC1:IN1") + SP=caget("29idARPES:LS335:TC1:OUT1:SP") + P=caget("29idARPES:LS335:TC1:P1") + I=caget("29idARPES:LS335:TC1:I1") + D=caget("29idARPES:LS335:TC1:D1") + Range=caget("29idARPES:LS335:TC1:HTR1:Range",as_string=True) +# print SP,P,I,D,Range + print("Current T:", T,'K') + print("PID[\'"+which+"\',"+str(SP)+"]=["+str(P)+","+str(I)+","+str(D)+",\'"+Range+"\']") + + + +############################################################################################################## +############################## SI9700 Kludge ############################## + +############################################################################################################## +def ResetPID(): + caput('29idd:tc1:SetPID_1.INPA','') + caput('29idd:tc1:SetPID_1.INPB','') + caput('29idd:tc1:SetPID_1.INPC','') + +def GetCurrentPID(): + T=caget('29idd:tc1:getVal_A.VAL') + P=caget('29idd:tc1:SetPID_1.A') + I=caget('29idd:tc1:SetPID_1.B') + D=caget('29idd:tc1:SetPID_1.C') + return T,P,I,D + +def SetTempSetting(T,P,I,D): + #Change the sample temperature T with the specified PID values + caput('29idd:tc1:getVal_A.VAL',T) + caput('29idd:tc1:SetPID_1.A',P) + caput('29idd:tc1:SetPID_1.B',I) + caput('29idd:tc1:SetPID_1.C',D) + +# GetCurrentPID => (60.0121, 8.0, 50.0, 12.0) ## Temperature overshoots no more than 5.5 degrees +#it takes about 3 min to stablize at any temp +#cooling is about 10 degrees/min + + +#functions use from ScanFunctions_IEX import BL_ioc +def PVLakeshores(which): + """ + which = RSXS / ARPES /Hydra / DetPool + usage: P, Sample, ColdFinger, SetPoint = PVLakeshores[which] + """ + # key: + + d={"Kappa":("29idd:LS331:TC1:","SampleA","SampleB","wr_SP"), + "ARPES":("29idARPES:LS335:TC1:","IN1","IN2","OUT1:SP"), + "Hydra":("29idc:LS340:TC2:","Sample","Control","wr_SP1"), + "DetPool":("29idc:LS340:TC1:","Sample","Control","wr_SP1") + } + return d[which] + +def dateandtime(): + return strftime("%a %d %b %Y %H:%M:%S",localtime()) + +def tempr(which=None): + """ + reads the temperature in the current branch unless otherwise specificed + """ + if which == None: + which=BL_ioc() + PVs=PVLakeshores(which) + sample=PVs[0]+PVs[1] + print(which+" Sample Temperature: {} K".format(caget(sample))) + +def temps(tempset,which=None): + """ + Set sthe sample temperature set point and waits for it to stablelize for the current branch + unless specified by which = Kappa / ARPES /Hydra set by PVLakeshores + """ + if which == None: + which=BL_ioc() + + P, Sample, ColdFinger, SetPoint = PVLakeshores(which) + + print("Initial Sample Temperature:",caget(P+Sample)," K",dateandtime()) + caput(P+SetPoint,1.0*tempset) + stop_var=0 + b=0 + sleep(0) + while b == 0: + delta=abs(caget(P+Sample)-1.0*tempset) + if abs(delta) < 1: + c=0 + while c < 10: + sleep(1) + delta=delta+(caget(P+Sample)-1.0*tempset) + c=c+1 + if abs(delta/10) < 1: + print("Stable Sample Temperature:",caget(P+Sample)," K",dateandtime()) + b=1 + else: + temp1=caget(P+Sample) + sleep(10) + temp2=caget(P+Sample) + if abs(temp1-temp2) < 0.1: + sleep(30) + temp2=caget(P+Sample) + if abs(temp1-temp2) < .5: + print("UNABLE TO STABLIZE TEMPERATURE! Stopped at T=:",caget(P+Sample)," K",dateandtime()) + b=1 + +def ARPES_warming(Tmax,PowerMax=50,Tdiff=10): + """ + Ramps the temperature up for ARPES using open loop and manual power at PowerMax with + the heater on High until Tmax - Tdif is reached and then switches to + closed loop with the heater on Medium + + PowerMax=50,Tdiff=10 + PowerMax=55,Tdiff=25 + """ + + P="29idARPES:LS335:TC1:" + #Needs modified in controller changed + Tsample=P+"IN1" + Tcontrol=P+"IN2" + Tsetpoint=P+"OUT1:SP" + HeaterRange=P+"HTR1:Range" + ControlMode=P+"OUT1:Mode" + ManualPower=P+"OUT1:MOUT" + + caput(Tsetpoint,Tmax) + caput(ManualPower,PowerMax) + caput(ControlMode,"Open Loop") + caput(HeaterRange,'HIGH') + + print("T", caget(Tsample)) + print("Started warming:", dateandtime()) + + while True: + TA=caget(Tsample) + TB=caget(Tsample) + if TB >= Tmax: + print ("Control is to high",TB) + break + if TA < Tmax - Tdiff: + sleep(60) + else: + break + print("T", caget(Tsample)) + print("Switch to closed loop", dateandtime()) + + caput(HeaterRange,'MEDIUM');sleep(1) + caput(ControlMode,"Closed Loop");sleep(1) + caput(HeaterRange,'OFF');sleep(1) + caput(HeaterRange,'MEDIUM');sleep(1) + temps(Tmax,which='ARPES') diff --git a/build/lib/iexcode/instruments/Motors.py b/build/lib/iexcode/instruments/Motors.py new file mode 100644 index 0000000..119b51b --- /dev/null +++ b/build/lib/iexcode/instruments/Motors.py @@ -0,0 +1,232 @@ +import time +from math import floor + +from epics import caget, caput + +from iexcode.instruments.IEX_endstations import * + + +class Motors: + """ + short hand to move motors in endstation + usage motors = Motor('ARPES','ARPES_motor_dictionary') + """ + + def __init__(self,endstation_name,motor_dictionary,physical_motors, + psuedo_motors): + self.endstation_name = endstation_name + self._motor_dictionary = motor_dictionary + self.physical_motors = physical_motors + self.pseudo_motors = psuedo_motors + + def info(self): + print("physical motors:",self.physical_motors) + print("pseudo motors:",self.pseudo_motors) + + def get(self,name,verbose=False): + """ + get current position of motor name = "x","y"... + """ + + try: + rbv = caget(self._motor_dictionary(name)[0]) + if verbose: + print('current position: '+name+" = "+str(rbv)) + return rbv + except: + print("Not a valid motor name") + + def reset(self,name): + """ + Reset motor if stuck in 'moving' + """ + pv=self._motor_dictionary(name)[2] + caput(pv,'Stop') + time.sleep(1) + caput(pv,'Go') + + def status(self): + """ + if status == 0 then moving + """ + status = 0 + for motor in self._motor_dictionary.keys(): + status *= caget(self._motor_dictionary[motor][3]+".DMOV") + return status + + def move(self,name,val,**kwargs): + """ + Moves a motor in the endstation based on common name, not PV name + + name: motor name => 'x','y','z','th','chi','phi' ... + val: absolute position if relative=False, delta if relative=True + + **kwargs: + relative: move mode => True/False; default = False (absolute) + wait: wait for move completion before moving on => True/False; default=True + verbose: print old and new motor position => True/False; default=False + + + Previously: Move_ARPES_Motor or Move_ARPES_Motor or motor_move_vs_Branch + """ + kwargs.setdefault('relative',False) + kwargs.setdefault('wait',True) + kwargs.setdefault('verbose',False) + + try: + rbv_pv,val_pv,sgm_pv,pv = self._motor_dictionary[name] + old_position = caget(rbv_pv) + if kwargs['relative']: + val = old_position + val + + caput(val_pv,val,wait=kwargs['wait'],timeout=18000) + + new_position = caget(rbv_pv) + if kwargs['verbose']: + print('old: '+name+" = "+str(old_position)) + print('new: '+name+" = "+str(new_position)) + except: + print(name+" is not a valid motor name") + + + + def dmove(self,name,val,**kwargs): + """ + relative move of a motor in the endstation based on common name, not PV name + name = x,y,z,th,chi,phi ... + + **kwargs: + wait: wait for move completion before moving on => True/False; default=True + verbose: print old and new motor position => True/False; default=False + + Previously: motor_umove_vs_Branch + """ + kwargs.setdefault('wait',True) + kwargs.setdefault('verbose',False) + + self.move(name,val,relative=True,wait=kwargs['wait'],verbose=kwargs['verbose']) + + def mprint(self): + """ + prints current position of the physical motors + """ + position_list = [] + for motor in self.physical_motors(): + position_list.append(self.get(motor,verbose=False)) + return position_list + + def mvsample(self,position_list,verbose=True): + """ + moves the sample to the position sepcified by position_list + position_list = ['description',x,y,z,th,chi,phi] + + Previously: sample + """ + if not isinstance(position_list[0],str): + sample_name = '' + else: + sample_name = position_list[0] + position_list = position_list[1:] + + motor_list = self.physical_motors() + for motor,i in enumerate(motor_list): + self.move(motor,position_list[i]) + + if verbose: + print("Sample now @ "+sample_name) + + def scan(self,name,start,stop,step, **kwargs): + """ + scans a motor + + **kwargs: + relative: move mode => True/False; default = False (absolute) + scan_dim: 1 (default) + execute: True/False to start the scan => True (default) + + for Kappa only: + cts: scaler integration time in seconds (default = 0.1 s) + mpa: True/False to sum mpa acquisitions + + Previously Scan_ARPES_motor / Scan_Kappa_motor + """ + kwargs.setdefault('relative',False) + kwargs.setdefault('scan_dim',1) + kwargs.setdefault('execute',True) + #for kappa only + kwargs.setdefault('cts',0.1) + kwargs.setdefault('mpa',False) + + rbv_pv,val_pv,sgm_pv,pv =self._motor_dictionary()[name] + + if kwargs['mode'] == "relative": + current_value = caget(rbv_pv) + abs_start = round(current_value + start,3) + abs_stop = round(current_value + stop,3) + print("start, stop, step = "+str(abs_start)+", "+str(abs_stop) + +", "+str(step)) + else: + abs_start = start + abs_stop = stop + + self._scalar_cts(kwargs['cts'],verbose=True,**kwargs) + BL.mda.fillin(val_pv,rbv_pv,abs_start,abs_stop,step,**kwargs) + + if kwargs['execute']: + BL.mda.go(**kwargs) + + + def scan_2D(self,inner_loop_list,outer_loop_list,**kwargs): + """ + Fills in a the Scan Record for a 2D scan (Mesh), does NOT press go + InnerMotorList=[name1,start1,stop1,step1] #scanDIM=2 + OuterMotorList=[name2,start2,stop2,step2] #scanDIM=3 + name = 'x'/'y'/'z'/'th'... + + **kwargs: + relative: scab => True/False; default = False (absolute) + outer_scan_dim: 2 (default) + execute: True/False to start the scan => True (default) + Snake; coming soon + + + for Kappa only: + cts: scaler integration time in seconds (default = 0.1 s) + mpa: True/False to sum mpa acquisitions + + """ + kwargs.setdefault('mode','absolute') + kwargs.setdefault('outer_scan_dim',2) + kwargs.setdefault('snake',False) + kwargs.setdefault('execute',True) + #for kappa only + kwargs.setdefault('cts',0.1) + kwargs.setdefault('mpa',False) + + rbv_pv,val_pv,sgm_pv,pv = self._motor_dictionary()[inner_loop_list[0]] + inner_loop_list.insert(0,rbv_pv) + inner_loop_list.insert(0,val_pv) + + rbv_pv,val_pv,sgm_pv,pv = self._motor_dictionary()[outer_loop_list[0]] + outer_loop_list.insert(0,rbv_pv) + outer_loop_list.insert(0,val_pv) + + if kwargs["mode"] == "relative": + current_value1 = caget(inner_loop_list[1]) + inner_loop_list[2]=round(current_value1+inner_loop_list[2],3) + inner_loop_list[3]=round(current_value1+inner_loop_list[3],3) + + current_value2 = caget(outer_loop_list[1]) + outer_loop_list[2]=round(current_value1+outer_loop_list[2],3) + outer_loop_list[3]=round(current_value1+outer_loop_list[3],3) + + scalar_cts(kwargs['cts'],verbose=True,**kwargs) + BL.mda.fillin_2D(inner_loop_list,outer_loop_list, + outer_scan_dim=kwargs['outer_scan_dim'],**kwargs) + + if kwargs['execute']: + BL.mda.go(**kwargs) + + + + diff --git a/build/lib/iexcode/instruments/Scienta.py b/build/lib/iexcode/instruments/Scienta.py new file mode 100644 index 0000000..f493664 --- /dev/null +++ b/build/lib/iexcode/instruments/Scienta.py @@ -0,0 +1,578 @@ +import time +import numpy as np +from epics import caget,caput, PV + + + + +#----------------------------------------------- +#--- Beamline dependent PVs and definitions --- +#----------------------------------------------- +P="29idcScienta:" +PHV=P+'HV:' +Pcam =P+'basler:cam1:' +savePlugin = P+"HDF1:" +statsPlugin = P+"Stats4:" +dtype = "h5" +#energy scaling +KEpix=0.0000845#*PE +KEwidth=0.1085#*PE + + +def AllowedEnergyRange(*args): + """ + KEmin, KEmax for a given PassEnergy, LensMode combination + *args + = PassEnergy, LensMode => returns KEmin,KEmax + = empty => returns entire table + + """ + Table={} + Table['Transmission']={1:(0,76),2:(0,107),5:(1,237),10:(1,453),20:(3,968),50:(7,6041),100:(21,6000),200:(30,6206),500:(283,6504)} + Table['Angular']={1:(None,None),2:(1,52),5:(2,131),10:(2,262),20:(10,523),50:(24,1309),100:(34,6105),200:(230,6206),500:(None,None)} + Table['Angular_01']={1:(None,None),2:(None,None),5:(None,None),10:(None,None),20:(None,None),50:(126,2947),100:(252,1401),200:(844,1261),500:(None,None)} + Table['Angular_05']={1:(None,None),2:(None,None),5:(None,None),10:(None,None),20:(None,None),50:(None,None),100:(252,1323),200:(1043,1270),500:(None,None)} + + if len(args)<1: + return Table + + if len(args)==2: + try: + PassEnergy,LensMode=args + KEmin,KEmax=Table[LensMode][PassEnergy] + return KEmin,KEmax + except: + return None + else: + print('Not a valid argument') + +############################################################# +########### Scienta python control functions ################ +############################################################# + +class Scienta: + """ + Scienta only talks in 'KE' anything related to the binding energy happens outside of these + controls + Usage: + EA=Scienta() + EA.get(); cagets all Scienta parameters and returns vars(EA); dictionary of variables + EA.PE(),EA.KE(),EA.LensMode() + """ + + def __init__(self): + #High Voltage Setting + self.PHV = PHV + self._Pcam=Pcam + self._savePlugin=savePlugin + self._statsPlugin=statsPlugin + + self.dtype=dtype + self.wk = None + + self.ElementSet = None #HV for XPS, LV for UPS + + self.LensMode = None + self.PassEnergy = None + self.KineticEnergy = None + self.SpectraMode = None + + #Camera Settings + self.Frames = None + + self.get() + return + + def get(self,q=1): + """ + Gets the current Scienta parameters + returns the dictionary for q!=1 + """ + for key in vars(self): + if key not in ["PHV","_Pcam","_savePlugin","_statsPlugin","wk","Frames","dtype"]: + vars(self)[key]=caget(self.PHV+key+".VAL") + if key in ["LensMode","SpectraMode"]: + vars(self)[key]=caget(self.PHV+key+".VAL",as_string=True) + if key == "Frames": + vars(self)[key]=caget(self.PHV.split(':')[0]+':'+"ExpFrames.VAL") + if key == "wk": + vars(self)[key]=caget(self.PHV+"WorkFunction") + if q !=1: + for key in vars(self): + print(key+" = "+str(vars(self)[key])) + + return vars(self) + + def counts(self): + """ + returns the current intensity on the Scienta using EA. + """ + return caget(self._savePlugin+"Total_RBV") + + def _errorCheck(self,KineticEnergy,PassEnergy,LensMode,Frames,**kwargs): + """ + check the parameters to see if they are allowed + + returns list of errors + **kwargs: + debug=False + """ + kwargs.setdefault('debug',False) + if kwargs['debug']: + print("\n _errorCheck") + error=[] + + if LensMode not in AllowedEnergyRange().keys(): + error.append("LensMode = "+LensMode+" is not a valid") + elif PassEnergy not in AllowedEnergyRange()[LensMode]: + error.append("PassEnergy = "+str(PassEnergy)+" is not a valid") + try: + KEmin,KEmax=AllowedEnergyRange(PassEnergy,LensMode) + if KEmin>KineticEnergy or KineticEnergy>KEmax: + error.append("Kinetic Energy: "+str(KineticEnergy)+" is outside allowed range for this Lens Mode,Pass Energy combination") + except: + error.append("Undefined PassEnergy/LensMode Combination "+str(PassEnergy + )+"/"+LensMode) + if Frames < 1: + error.append("Frames must be 1 or greater") + return error + + def put(self, KE=None, PE=None, LensMode="Angular",Frames=1,**kwargs): + """ + Used to set all the SES parameters at once + KE = kinetic energy (None keeps the current KE) + PE = pass energy (None keeps the current PE) + LensMode = "Angular"/"Transmission" + + **kwargs + debug + """ + + kwargs.setdefault('debug',False) + parms=self.get(q=1) + if kwargs['debug']: + print("\n EA.put") + print('KE,PE,LensMode,Frames = ',KE,PE,LensMode,Frames) + time.sleep(.1) + if KE != None: + parms.update({"KineticEnergy":KE}) + else: + parms.update({"KineticEnergy":self.KineticEnergy}) + if PE != None: + parms.update({"PassEnergy":PE}) + else: + parms.update({"PassEnergy":self.PassEnergy}) + parms.update({"LensMode":LensMode}) + parms.update({"Frames":Frames}) + + if kwargs['debug']: + print("parms:",parms) + + time.sleep(.5) + self._setHV(parms) + + def _setHV(self,parms,**kwargs): + """ + Error checks and sets the Scienta high voltage supplies + parms={'LensMode':LensMode, + 'PassEnergy':PassEnergy + 'KineticEnergy':KineticEnergy, + 'Frames':Frames} + + kwargs: + q: quiet (default = 1); to print the dictionary q!=1 + """ + kwargs.setdefault('q',1) + kwargs.setdefault('debug',False) + + if kwargs['debug']: + print('\n _setHV') + print(parms) + + #checking for errors + error=self._errorCheck(parms['KineticEnergy'],parms['PassEnergy'],parms['LensMode'],parms['Frames']) + if kwargs['debug']: + print(error) + + #setting Frames + self._frames(parms['Frames']) + + if len(error) == 0: + caput(self.PHV+"LensMode",parms["LensMode"]) + time.sleep(.25) + caput(self.PHV+"PassEnergy_Set",str(int(parms["PassEnergy"]))) + time.sleep(0.25) + caput(self.PHV.split(':')[0]+':'+"ExpFrames",parms["Frames"]) + time.sleep(.25) + caput(self.PHV+"KineticEnergy",parms["KineticEnergy"]+0.01) + time.sleep(1) + caput(self.PHV+"KineticEnergy",parms["KineticEnergy"]) + time.sleep(1) + + self.get() + if kwargs['q'] !=1: + for key in vars(self):print(key+" = "+str(vars(self)[key])) + + else: + for e in error: + #print(e) #JM need to modify + x=5 + return + + def off(self,quiet=True): + """ + Zeros the high voltage supplies + """ + self.zeroSupplies() + if quiet == False: + print ("EA HV is zeroed, visually confirm") + + def zeroSupplies(self): + """ + Zeros the Scienta HV supplies (safe-state) + """ + caput(self.PHV+"ZeroSupplies.RVAL",1) + caput(self.PHV+"ZeroSuppliesSeq.PROC",1) + + + def KE(self,val): + """ + Sets the kinetic energy of the Scienta + KE should be grearter than the PassEnergy + """ + self.put(KE=val) + + + def _setwk(self,val): + """ + Sets the wk to the value specified + """ + caput(self.PHV+"WorkFunction",val) + + def PE(self,val): + """ + Sets the pass energy of the Scienta + PassEnergy: 1, 2, 5, 10, 20, 50, 100, 200, 500 + """ + self.put(PE=val) + + def lensMode(self,val): + """ + Sets the pass energy of the Scienta + LensMode: 'Transmission','Angular','Angular_01','Angular_05' + """ + self.put(LensMode=val) + + def _frames(self,val,q=1): + """ + Sets the number of frames + Frames >=1 + """ + caput(self.PHV.split(':')[0]+':'+"ExpFrames.VAL",val) + + + def _ElementSet(self,mode,q=1): + """ + Used to switch between + 0: High Energy (XPS) and 1:Low Energy (UPS) mode + Must be done in conjunction with changing the cabling on the front of the HV supply + mode = 0 or 1 + """ + caput(self.PHV+"ElementSet",mode) + self.get() + if q !=1: + for key in vars(self):print(key+" = "+str(vars(self)[key])) + return + + + def _BurstSupplies(self,val): + """ + BurstSupplies:'Fast','Slow' + """ + caput(self.PHV+"BurstSupplies",val,as_string=True) + + def _AcqCalc(self,val): + """ + AcqCalc:'Off','Live','Scan' + """ + caput(self.PHV+"AcqCalc",val,as_string=True) + + def _AcquisitionMode(self,val): + """ + AcquisitionMode: 'Off','Scan','Spectra' + """ + caput(self.PHV+"AcquisitionMode",val) + + def _ScientaMode(self,val): + """ + ScientaMode: 'BetaScan','XYScan','Fixed','BabySweep','SweepOverlapping','SweepNo' + """ + caput(self.PHV+"ScientaMode",val,as_string=True) + + + def _babySweepSteps(self,val): + """ + sets the number of steps in baby sweep + """ + caput(self.PHV+"babySweepSteps.VAL",val) + + def _spectraStatus(self): + """ + checking the spectra status + returns 1=busy or 0=idle + """ + status=caget(self.PHV+'ScanTrigger') + return status + + def _spectraProgress(self): + """ + Monitors the spectra status every 30 seconds + returns 0 when the spectra scan is idle + """ + while True: + status=self._spectraStatus() + if (status==1): + time.sleep(30) + else: + break + return 0 + + def _SpectraMode(self,Mode): + """ + Checks that Mode exists and sets the mode + + """ + DefinedModes=["Fixed","Baby-Sweep","Sweep"] + if Mode in DefinedModes: + caput(self.PHV+"SpectraMode", "Mode") + else: + print("Not a defined SpectraMode") + + def _spectraInfo(self): + """ + returns the pertainent information for a given spectra type + modeNum=2; Swept=[start,stop,step] + """ + modeNum=caget(self.PHV+"SpectraMode") + scanType=caget(self.PHV+"SpectraMode",as_string=True) + info=[] + if modeNum==2: #swept + info.append(caget(self.PHV+"sweepStartEnergy")) + info.append(caget(self.PHV+"sweepStopEnergy")) + info.append(caget(self.PHV+"sweepStepEnergy")) + elif modeNum==1: #baby-swept + info.append(caget(self.PHV+"babySweepCenter.VAL")) + elif modeNum==0: #fixed + info.append(caget(self.PHV+"fixedEnergy.VAL")) + return scanType,info + + def live(self,KE=None,PE=None,LensMode="Angular"): + """ + puts the Scienta back in live mode (no savinga and Fixed Mode) with PE and KE + if PE or KE is None then uses the current value + """ + if self.spectraProgress() != 1: + parms=self.get() + caput('29idcScienta:HV:LiveSeq',1) + self.put(KE,PE,LensMode) + else: + print("Scienta spectra in progress") + + def _updateAttributes(self,filepath=None): + """ + filepath is the full path to the xml file + filepath = None; to update after making changes + filepath='/xorApps/epics/synApps_6_1/ioc/29idcScienta/iocBoot/ioc29idcScienta/HDF5Attributes.xml' + + from 29idcScienta:HDF1: screen + More (under process plugin) + NDPluginBase Full + """ + if filepath is None: + print(caget(self._savePlugin+"NDAttributesFile",as_string=True)) + else: + caput(self._savePlugin+"NDAttributesFile",filepath) + time.sleep(1) + print(caget(self._savePlugin+"NDAttributesStatus",as_string=True)) + + + def _spectraErrorCheck(self,KElist,PassEnergy,Frames,LensMode,**kwargs): + """ + error checking to see if parameters are allowed + """ + error=[] + parms={} + for KE in list(KElist[0:-1]): + error=self._errorCheck(KE,PassEnergy,LensMode,Frames,**kwargs) + + if len(error) > 0: + print(error) + return error + + def _spectra_Swept(self,KEstart,KEstop,KEstep,PE): + """ + setting up for a swept spectra, does not set the LensMode, PassEnergy or Frames + """ + print("setting SpectraMode") + caput(self.PHV+"SpectraMode", "Sweep") + time.sleep(1) + #input pass energy setpoint (doesn't change util KE is changed but needed for calcs) + caput(self.PHV+"PassEnergy_Set",PE) + time.sleep(0.25) + #input spectra parameters + caput(self.PHV+"sweepStartEnergy.VAL",KEstart);time.sleep(0.25) #added 20220317 - sweep calcs need time to process or you get weird stuff + caput(self.PHV+"sweepStopEnergy.VAL",KEstop);time.sleep(0.25) #added 20220317 - sweep calcs need time to process or you get weird stuff + caput(self.PHV+"sweepStepEnergy.VAL",KEstep) + time.sleep(1) + #Re-Calc + #print('Yes Re-Calc') + #caput(self.PHV+"SweepReCalc.PROC",1) + #time.sleep(1) + #Load spectra parameters + caput(self.PHV+"SweepLoadFanout.PROC",1) + time.sleep(1) + + + def _spectra_BabySwept(self, KEcenter): + """ + setting up for a Baby-Swept + """ + #setting SpectraMode + caput(self.PHV+"SpectraMode", "Baby-Sweep") + time.sleep(1) + #input spectra parameters + caput(self.PHV+"babySweepCenter.VAL",KEcenter) + time.sleep(1) + #Load spectra parameters + caput(self.PHV+"SweepLoadFanout.PROC",1) + time.sleep(1) + #settling time + #caput(self.PHV+"scan2.PDLY",0.25) + #caput(self.PHV+"scan2.DDLY",0.1) + + def _spectra_Fixed(self,KEcenter): + """ + setting up for Fixed + """ + #setting SpectraMode + caput(self.PHV+"SpectraMode", "Fixed") + caput("29idcScienta:HV:SpectraMode", "Fixed") + time.sleep(1) + #input spectra parameters + caput(self.PHV+"fixedEnergy.VAL",KEcenter) + time.sleep(1) + + def _spectraSetup(self,EAlist,**kwargs): + """ + Writes EAlist to the appropriate PVs and does error checking + and return scanType, scanPV, KElist, parms + **kwargs + run = True; will write the PVs (False just does error checking) + LensMode = "Angular" + debug = False + + """ + kwargs.setdefault("debug",False) + kwargs.setdefault("run",True) + kwargs.setdefault("LensMode","Angular") + + if kwargs['debug']: + print("\n\n _spectraSetup") + print("EAlist",EAlist) + + scanType="spectra" + if EAlist[-1] == "BS": + BS=True + EAlist=EAlist[0:-1] + else: + BS=False + Sweeps=EAlist[-1] + Frames=EAlist[-2] + PassEnergy=EAlist[-3] + EnergyMode=EAlist[0] + KElist=np.array(EAlist[1:-3]) + LensMode=kwargs['LensMode'] + + + if kwargs['debug']: + print("KElist: ",KElist) + + parms={'LensMode':LensMode,'PassEnergy':PassEnergy,'KineticEnergy':KElist[0],'Frames':Frames} + + #checking parameters are valid + error=self._spectraErrorCheck(KElist,PassEnergy,Frames,LensMode) + if len(error) == 0: #passed check + #Checking Progress + if self._spectraStatus() == 1: + print("Spectra in Progess") + + # writing scan parameters + else: + if len(EAlist)==7: #Swept Mode + scanType="Sweep" + self._spectra_Swept(KElist[0],KElist[1],KElist[2],PassEnergy) + parms={'LensMode':LensMode,'PassEnergy':PassEnergy,'KineticEnergy':KElist[0]-.01,'Frames':Frames} + elif len(EAlist)==5: #fixed or baby sweep + if BS is True: + scanType="Baby-Sweep" + self._spectra_BabySwept(KElist[0]) + parms={'LensMode':LensMode,'PassEnergy':PassEnergy,'KineticEnergy':KElist[0]-.01,'Frames':Frames} + else: + scanType="Fixed" + self._spectra_Fixed(KElist[0]) + parms={'LensMode':LensMode,'PassEnergy':PassEnergy,'KineticEnergy':KElist[0]-.01,'Frames':Frames} + else: + print("not a valid number of parameters") + #PV to initate scan" + scanPV=self.PHV+"ScanTrigger" + return scanType, scanPV, KElist, parms + + def _spectraMessage(self,scanType, scanPV, KElist): + """ + prints KE range for spectra + """ + message=str(scanType)+" scan KE: " + for KE in KElist: + message+=str(KE)+" | " + return message[0:-2] + + + + def spectra(self,EAlist,**kwargs): + """ + takes a Scienta spectra based on the number or parameters in EAlist + Sweeps are handled by scanRecord outside of this modual (see scanEA) + + Fixed Mode:["KE",CenterEnergy,PassEnergy,Frames,Sweeps] (5) + Swept Mode:["KE",StartEnergy,StopEnergy,StepEnergy,PassEnergy,Frames,Sweeps] (7) + Baby Sweep (dither):["KE",CenterEnergy,PassEnergy,Frames,Sweeps,"BS"] (6) + + **kwargs: + debug=False + run=True (executes the scan); False for error checking only + LensMode = Angular (default) + """ + kwargs.setdefault("debug",False) + kwargs.setdefault("run",True) + + #Sending Parmeters to _spectraSetup + scanType, scanPV, KElist, parms =self._spectraSetup(EAlist,**kwargs) + + if kwargs['debug']: + print(scanType,scanPV, KElist) + else: + print(scanType, "KE: ", KElist) + #Getting Filename info + fpath=caget(self._savePlugin+"FileName_RBV",as_string=True)+"_ " + fnum=caget(self._savePlugin+"FileNumber",as_string=True) + + + if kwargs['run'] is True: + dateandtime=time.strftime("%a %d %b %Y %H:%M:%S",time.localtime()) + print("\t"+fpath+fnum+" started at ",dateandtime,"\r") + self.put(parms['KineticEnergy'],parms['PassEnergy'],LensMode=parms['LensMode'],Frames=parms['Frames']) + caput(scanPV,1,wait=True,timeout=129600)#timeout=36 hours + dateandtime=time.strftime("%a %d %b %Y %H:%M:%S",time.localtime()) + print("\t"+fpath+fnum+" finished at ",dateandtime,"\r") + + diff --git a/build/lib/iexcode/instruments/VLS_PGM.py b/build/lib/iexcode/instruments/VLS_PGM.py new file mode 100644 index 0000000..8f7bfdc --- /dev/null +++ b/build/lib/iexcode/instruments/VLS_PGM.py @@ -0,0 +1,739 @@ +""" +functions used for the mono +""" + +import time +import numpy as np + + +from epics import caget,caput +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.utilities import print_warning_message,read_dict + + +############################################################################################################## +################################ mono ############################## +############################################################################################################## +def mono_grating_names(): + """ + List of grating names + + """ + ext = ['C', 'D', 'E', 'F', 'G', 'H', 'I', "J", "K", "L" ] + GRT_names = ["MEG_Imp","HEG","MEG","Dummy", "not used", "not used", "not used","not used","not used","not used"] + GRT_names=GRT_names[0:2] # only use the first 3 slots + return GRT_names + +def mono_mirror_names(): + """ + List of grating names + + """ + ext = ['C', 'D', 'E', 'F', 'G', 'H', 'I', "J", "K", "L" ] + MIR_names = ["Au", "Silicon", "Carbon","not used","not used","not used","not used","not used","not used","not used"] + MIR_names=MIR_names[0:2] # only use the first 3 slots + return MIR_names + +def mono_extensions(): + """ + extension used for the mono mirror and grating locations + """ + ext = ['C', 'D', 'E', 'F', 'G', 'H', 'I', "J", "K", "L" ] + return ext + +def mono_energy_range(GRT=None): + """ + returns hv_min,hv_max, the minimum and maxium energy achievable the the specified grating + """ + if GRT == None: + GRT = mono_grating_get() + + if GRT == 'HEG': + hv_min=120 + hv_max=2200 + elif GRT == 'MEG': + hv_min=120 + hv_max=3000 + return hv_min,hv_max + +def mono_get_all(verbose=False): + """ + gets hv, hv_sp,grating,mirror + + Previously: Mono_Optics + """ + d = mono_get_all_extended(verbose=False) + vals = { + 'hv':d['ENERGY_MON'], + 'hv_sp':d['ENERGY_SP'], + 'grating':d['GRT'], + 'mirror':d['MIR'], + } + if verbose: + print(" hv: "+"%.2f" % vals['hv'], "eV, hv_sp: " % vals['hv_sp']) + print(" grating : "+vals['grating']," mirror : "+vals['mirror']) + return vals + +def mono_grating_num(): + """returns the current grating num""" + return caget('29idmonoGRT_TYPE_MON') + +def mono_mirror_num(): + """returns the current mirror num""" + return caget('29idmonoMIR_TYPE_MON') + +def mono_pvs(grt_num=None,mir_num=None): + """ + returns a dictionary with all the mono pvs + """ + if grt_num is None: + grt_num = mono_grating_num() + if mir_num is None: + mir_num = mono_mirror_num() + + ext=mono_extensions() + + d={ + 'energy':"29idmono:ENERGY_MON", + 'energy_sp':"29idmono:ENERGY_SP", + 'grt_density':"29idmono:GRT_DENSITY", + 'grt_offset':'29idmonoGRT:P_OFFSETS.'+ext['grt_num'], + 'grt_b2':'29idmonoGRT:B2_CALC.'+ext['grt_num'], + 'grt_pos':'29idmonoGRT:X_DEF_POS.'+ext['grt_num'], + 'grt_LD':'29idmonoGRT:TYPE_CALC.'+ext['grt_num'], + 'grt_tun0':'29idmonoGRT:TUN0_CALC.'+ext['grt_num'], + 'grt_tun1':'29idmonoGRT:TUN1_CALC.'+ext['grt_num'], + 'grt_tun2':'29idmonoGRT:TUN2_CALC.'+ext['grt_num'], + 'grt_tun3':'29idmonoGRT:TUN3_CALC.'+ext['grt_num'], + 'grt_type_sp':"29idmonoGRT_TYPE_SP", + 'grt_move':"29idb:'gr:move", + 'grt_P_status':"29idmonoGRT:P_AXIS_STS", + 'mir_P_status':"29idmonoMIR:P_AXIS_STS", + 'grt_X_status':"29idmonoGRT:X_AXIS_STS", + 'mir_X_status':"29idmonoMIR:X_AXIS_STS", + 'mir_offset':'29idmonoMIR:P_OFFSETS.'+ext['mir_num'], + 'mir_pos':'29idmonoMIR:X_DEF_POS.'+ext['mir_num'], + 'cff':'29idmono:CC_MON', + 'arm':'29idmono:PARAMETER.G' + + } + +def mono_get_all_extended(verbose=False): + """ + gets the mono parameters for the current grating and mirror + returns a dictionary with those values + + Previously: Mono_Optics + """ + grt_num = mono_grating_num() + mir_num = mono_mirror_num() + grt_names = mono_grating_names() + mir_names = mono_mirror_names() + + ext=mono_extensions() + + pvs = mono_pvs(grt_num=None,mir_num=None) + vals={ + 'grt_num':grt_num, + 'mir_num':mir_num, + 'grt':grt_names[grt_num], + 'mir':mir_names[mir_num], + 'energy':caget(pvs['energy']), + 'energy_sp':caget(pvs['energy_sp']), + 'grt_density':caget(pvs['energy_sp']), + 'grt_offset':caget(pvs['grt_offset']), + 'grt_b2':caget(pvs['grt_offset']), + 'grt_pos':caget(pvs['grt_pos']), + 'grt_LD':caget(pvs['grt_LD']), + 'grt_tun0':caget(pvs['grt_tun0']), + 'grt_tun1':caget(pvs['grt_tun1']), + 'grt_tun2':caget(pvs['grt_tun2']), + 'grt_tun3':caget(pvs['grt_tun3']), + 'grt_P_status':caget(pvs['grt_P_status']), + 'mir_P_status':caget(pvs['mir_P_status']), + 'grt_X_status':caget(pvs['grt_X_status']), + 'mir_X_status':caget(pvs['mir_X_status']), + 'mir_offset':caget(pvs['mir_offset']), + 'mir_pos':caget(pvs['mir_pos']), + 'CFF':caget('29idmono:CC_MON'), + 'arm':caget('29idmono:PARAMETER.G') + } + + if verbose: + for key in vals.keys(): + print(key,vals[key]) + + return vals + +def mono_grating_get(): + """ + returns the grating density and the string name for the grating + """ + return mono_get_all_extended()['grt'] + +def mono_energy_get(): + """ + returns the grating density and the string name for the grating + """ + return mono_get_all_extended()['energy'] + +def mono_grating_density_get(): + """ + returns the grating density and the string name for the grating + """ + return mono_get_all_extended()['grt_density'] + +def mono_energy_set(hv_eV,verbose=True): + """ + sets the mono energy + + Previously: SetMono + """ + hv_min, hv_max = mono_energy_range() + pv = mono_pvs()['energy_sp'] + if hv_min <= hv_eV <= hv_max: + caput(pv,hv_eV,wait=True,timeout=60) + time.sleep(2.5) + + mono_status = mono_status_get() + while True: + if mono_status() > 1: + mono_resest_pitch() + caput(pv,hv_eV,wait=True,timeout=60) + time.sleep(2.5) + else: + break + if verbose: + print("Mono set to",str(hv_eV),"eV") + +def mono_scan_pvs(): + """ + returns the pvs to be used in scanning + """ + val_pv = mono_pvs()['energy_sp'] + rbv_pv = mono_pvs()['energy'] + return val_pv, rbv_pv + +def mono_status_get(): + """ + returns the status of the mirror (GRT * MIR) + 1 = ready + + + Previously Mono_Status + """ + pvs = mono_pvs()['energy'] + mir_P_status = caget(pvs['mir_P_status']) + grt_P_status = caget(pvs['grt_P_status']) + mir_X_status = caget(pvs['mir_P_status']) + grt_X_status = caget(pvs['grt_P_status']) + mirror_status = mir_P_status * mir_X_status + grating_status = grt_P_status * grt_X_status + return mirror_status*grating_status + + +def mono_grating_translate(grating,verbose=True): + """ + Translate between the different grating positions + + Warning: this does not close the shutters use XX + + Previously: Move_GRT + """ + current_grating=mono_grating_get() + hv_eV = mono_energy_get() + + try: + grating_state = mono_grating_names().index(grating) + + except: + print_warning_message(grating+' not a valid grating') + + if current_grating != grating: + pvs=mono_pvs() + caput(pvs['grt_type_sp'],grating_state,wait=True,timeout=18000) + caput(pvs['grt_move'],1,wait=True,timeout=18000) + while True: + if mono_status_get() > 1: + time.sleep(5) + else: + break + mono_energy_set(hv_eV) + + else: + if verbose: + print("grating is already "+grating) + + if verbose: + print("Mono Grating:",grating) + + +def mono_resest_pitch(): + """ + resets the MIR and GRT pitch interlocks after a following error or limit + Previoulsy part of SetMono + """ + caput("29idmonoMIR:P.STOP",1) + time.sleep(1) + caput("29idmonoGRT:P.STOP",1) + time.sleep(1) + print('Mono pitch was reset') + +def mono_kill(): + """ + kills the mono pitch motors + + Previously: Kill_Mono + """ + caput("29idmonoMIR:P_KILL_CMD.PROC",1) + caput("29idmonoGRT:P_KILL_CMD.PROC",1) +# caput("29idmonoGRT:X_KILL_CMD.PROC",1) + time.sleep(5) + caput("29idmono:STOP_CMD.PROC",1) +# caput("29idmonoGRT:X_HOME_CMD.PROC",1) + + +def mono_stop(): + """ + presses the stop button on the mono motors + + Previously: Stop_Mono + """ + caput("29idmono:STOP_CMD.PROC",1) + time.sleep(5) + +def mono_enable(): + """ + re-enable the mono motors + + Previously: Enable_Mono + """ + caput("29idmonoGRT:X_ENA_CMD.PROC",1) + caput("29idmonoGRT:P_ENA_CMD.PROC",1) + caput("29idmonoMIR:X_ENA_CMD.PROC",1) + caput("29idmonoMIR:P_ENA_CMD.PROC",1) + +def mono_limits_reset(): + """ + resest the mono low limit to 200 + was changed in the pmac to the wrong value so we need to do this + + Previously: Reset_Mono_Limits + """ + # caput("29idmono_GRT_TYPE_SP.ONST", 'MEG_PA') + # caput("29idmono_GRT_TYPE_SP.TWST", 'HEG_JY') + # caput("29idmono_GRT_TYPE_SP.THST", 'MEG_JY') + caput("29idmono:ENERGY_SP.DRVL",200) + caput("29idmono:ENERGY_SP.LOW",200) + caput("29idmono:ENERGY_SP.LOLO",200) + caput("29idmono:ENERGY_SP.LOPR",200) + print("Mono limits have been reset.") + +def mono_cff_print(): + """ + print the cff tuning parameters and arm for the current grating + + Previously: Get_CFF + """ + d = mono_get_all_extended + cff = d['cff'] + tun0 = d['grt_tun0'] + tun1 = d['grt_tun1'] + tun2 = d['grt_tun2'] + tun3 = d['grt_tun3'] + arm = d['arm'] + print(" cff : "+"%.4f" % cff , " exit arm: "+"%.1f" % arm,"mm") + print(" tun0 : "+"%.4e" % tun0 , " tun1: "+"%.4e" % tun1) + print(" tun2 : "+"%.4e" % tun2 , " tun3: "+"%.4e" % tun3) + +def mono_parameters_pv(grt_num=None,mir_num=None): + """ + returns dictionary with mono_parameter for current grating/mirror + """ + if grt_num is None: + grt_num = mono_grating_num()() + if mir_num is None: + mir_num = mono_mirror_num()() + + ext = mono_extensions() + + d={ + 'mir_offset':"29idmonoMIR:P_OFFSETS."+ext[mir_num], + 'mir_pos':"29idmonoMIR:X_DEF_POS."+ext[mir_num], + 'grt_offset':"29idmonoGRT:P_OFFSETS."+ext[grt_num], + 'grt_pos':"29idmonoGRT:X_DEF_POS."+ext[grt_num], + 'grt_density':"29idmonoGRT:TYPE_CALC."+ext[grt_num], + 'grt_b2':"29idmonoGRT:B2_CALC."+ext[grt_num] + } + return d + + +def mono_parameters_get(): + """ + Gets the mono parameters and prints them for the History + + Previously: Mono_Parameters_Get + """ + date=time.strftime("%Y%m%d",time.localtime()) + mirList = mono_mirror_names() + grtList = mono_grating_names() + pvList = mono_extensions() + + #MIR + txt="MonoParm[\'"+date+"\']= {\n\t" + for i in range(0,len(mirList)): + d = mono_parameters_pv(mir_num=i) + mir = mirList[i] + offset = caget(d['mir_offset']) + position = caget(d['mir_pos']) + txt+="\'"+mir+"\':["+str(offset)+","+str(position)+"]," + + #GRT + txt+="\n\t" + for i in range(0,len(grtList)): + d = mono_parameters_pv(grt_num=i) + grt = grtList[i] + offset = caget(d['grt_offset']) + position = caget(d['grt_pos']) + density = caget(d['grt_density']) + b2 = caget(d['grt_b2']) + txt+="\'"+grt+"\':["+str(offset)+","+str(position)+","+str(density)+","+str(b2)+"]," + + txt+="}" + print(txt) + +def mono_parameters_history_read(date): + """ + Dictionary of Mono parameters used in Mono_Parameters(date) which sets the parameters + #MonoParm['date']={'Au':[],Si:[],'Carbon':[],'MEG_Imp':[],'HEG_JY':[],'MEG_JY':[]} + Mono_Parameters_Set(date) writes the parameters from the Dictionary + + Previously: Mono_Parameters_History + """ + #MonoParm['date']={'Au':[],Si:[],'Carbon':[],'MEG_Imp':[],'HEG_JY':[],'MEG_JY':[]} + + MonoParm=read_dict('mono_parameters.txt') + return MonoParm[date] + + +def mono_parameters_history_write(date): + """ + + Previously: Mono_Parameters_Set + """ + hv_eV=caget("29idmono:ENERGY_SP") + MonoParm=mono_parameters_history_read(date) + pvList=['C','D','E'] + mirList=["Au","Si","Carbon"] + for i in range(0,len(mirList)): + mir=mirList[i] + caput("29idmonoMIR:P_OFFSETS."+pvList[i],MonoParm[mir][0]) + caput("29idmonoMIR:X_DEF_POS."+pvList[i],MonoParm[mir][1]) + grtList=["MEG_Imp", "HEG_JY", "MEG_JY"] + for i in range(0,len(grtList)): + grt=grtList[i] + caput("29idmonoGRT:P_OFFSETS."+pvList[i],MonoParm[grt][0]) + caput("29idmonoGRT:X_DEF_POS."+pvList[i],MonoParm[grt][1]) + caput("29idmonoGRT:TYPE_CALC."+pvList[i],MonoParm[grt][2]) + caput("29idmonoGRT:B2_CALC."+pvList[i],MonoParm[grt][3]) + time.sleep (1) + mono_energy_set(hv_eV,verbose=True) + + + +def mono_temperature_interlock(): + """ + userCalcOut to monitor the temperatures on the Mono if the temperature gets too high then it closes the main shutter + #29idb:userCalcOut10.VAL =0 => OK; =1 => Too hot + + Previously: Mono_TemperatureInterlock + """ + pv='29idb:userCalcOut10' + caput(pv+'.DESC', 'Mono Temperature Interlock') + caput(pv+'.INPA','29idmono:TC1_MON CP NMS') + caput(pv+'.INPB','29idmono:TC2_MON CP NMS') + caput(pv+'.INPC','29idmono:TC3_MON CP NMS') + caput(pv+'.INPD','29idmono:TC4_MON CP NMS') + caput(pv+'.INPE','29idmono:TC5_MON CP NMS') + caput(pv+'.INPF','29idmono:TC6_MON CP NMS') + + caput(pv+'.H','31') + + caput(pv+'.CALC','A>H | B>H | C>H |D>H') + caput(pv+'OOPT','Transition To Non-zero') + caput(pv+'.OUT','PC:29ID:FES_CLOSE_REQUEST.VAL PP NMS') + + + +def mono_scan_fillin(hv_start,hv_stop,hv_step,**kwargs): + """ + fills in the scanRecord for scanning the mono + + puts the positioner in stay after scan + + **kwargs => scanRecord.fillin kwargs + positioner_settling_time: increased because busy record is too fast => 0.2 (default) + """ + kwargs.setdefault('positioner_settling_time',0.2) + + + #Setting up the ScanRecord for Mono in Table mode + val_pv, rbv_pv = mono_scan_pvs() + BL.mda.fillin(val_pv,rbv_pv,hv_start,hv_stop,hv_step,**kwargs) + + #mono needs to stay and have a longer settling time + BL.mda.positioner_after_scan("STAY") + + +def mono_scan_fillin_table(hv_array,**kwargs): + """ + fills in the scanRecord for scanning the mono + + puts the positioner in stay after scan + + **kwargs => scanRecord.fillin kwargs + positioner_settling_time: increased because busy record is too fast => 0.2 (default) + + """ + kwargs.setdefault('positioner_settling_time',0.2) + + #Setting up the ScanRecord for Mono in Table mode + val_pv, rbv_pv = mono_scan_pvs() + BL.mda.fillin.table(val_pv,rbv_pv,hv_array,**kwargs) + + #mono needs to stay and have a longer settling time + BL.mda.positioner_settling_time(kwargs['positioner_settling_time']) + BL.mda.positioner_after_scan("STAY") + +def mono_scan_after(**kwargs): + """ + resets mda after scanning the mono + """ + after_scan_pv = BL.mda.default_after_scan_seq + caput(after_scan_pv+".PROC",1) + BL.mda.positioner_after_scan("PRIOR POS") + + +############################################################################################################## +################################ aligmnent and commissioning ############################## +############################################################################################################## +def mono_motor_move(motor,value): + """ + for pitch motor => MIR:P or GRT:P + for translation motor => MIR:X or GRT:X + + Previously: Move_FMBMono + """ + val_pv,rbv_pv = mono_motor_scan_pvs() + caput(val_pv+".PREC",3) + caput(val_pv,value,wait=True,timeout=18000) + +def mono_motor_scan_pvs(motor): + """ + returns the rbv and val for scanning + """ + val_pv = "29idmono"+motor+"_SP" + rbv_pv = "29idmono"+motor+"_MON" + return val_pv,rbv_pv + +def mono_motor_scan_fillin(motor,start,stop,step,**kwargs): + """ + for pitch motor => MIR:P or GRT:P + for translation motor => MIR:X or GRT:X + + Previously: Scan_FMBMono + """ + val_pv,rbv_pv = mono_motor_scan_pvs(motor) + caput(val_pv+".PREC",3) # database sets .PREC==0. We want more digits than that. + + BL.mda.fillin(val_pv,rbv_pv,start,stop,step,**kwargs) + +def mono_zero_order(angle): + """ + puts the beam in zero order => grating/mirror are parallel + range ~1 - 5 degrees + + Previously: Mono_zero + """ + angle=angle*1.0 + caput("29idmonoMIR:P_POS_SP",angle,wait=True,timeout=18000) + caput("29idmonoMIR:P_MOVE_CMD.PROC",1,wait=True,timeout=18000) + caput("29idmonoGRT:P_POS_SP",angle,wait=True,timeout=18000) + caput("29idmonoGRT:P_MOVE_CMD.PROC",1,wait=True,timeout=18000) + print("Mono set to zero order: MIR_pitch = "+str(angle)+", GRT_pitch = "+str(angle)) + +def mono_angles_set(alpha,beta): #JM modified to monitor the ready, moving sequentialy ended up in crash sometimes + """ + Sets the mirror pitch (alpha) and grating pitch (beta) angles + + Previously: Mono_angle + """ + alpha=alpha*1.0 + beta=beta*1.0 + #Putting Setpoints Go + caput("29idmonoGRT:P_SP",alpha) + caput("29idmonoMIR:P_SP",beta) + ready=0 + while ready != 1: + time.sleep(0.1) + ready=caget('29idmono:ERDY_STS') + print("Mono set to zero order: MIR_pitch = "+str(alpha)+", GRT_pitch = "+str(beta)) + +def mono_pink_beam(): + """ + moves grating and mirror out of beam + x-rays will hit the pink beamstop + + Previously: Mono_pinkbeam + """ + caput("29idmonoMIR:P_POS_SP",0,0) + caput("29idmonoMIR:P_MOVE_CMD.PROC",0) + time.sleep(3) + caput("29idmonoGRT:P_POS_SP",0.0) + caput("29idmonoGRT:P_MOVE_CMD.PROC",0) + time.sleep(3) + caput("29idmonoMIR:X_POS_SP",-52) + caput("29idmonoMIR:X_MOVE_CMD.PROC",0) + time.sleep(3) + caput("29idmonoGRT:X_POS_SP",210) + caput("29idmonoGRT:X_MOVE_CMD.PROC",0,wait=True,timeout=18000) + +def mono_CC(): + """ + ##used for commissioning + + Previously: Mono_CC + """ + caput("29idmonoMIR_TYPE_SP",3) + caput("29idmonoMIR:X_DCPL_CALC.PROC",0) + caput("29idmonoGRT_TYPE_SP", 10) + caput("29idmonoGRT:X_DCPL_CALC.PROC",0) + + caput("29idmono:ENERGY_SP", 440) + +def mono_grating_offset_set(val,grt_num=None): + """ + sets the grating offset for the grating + + Previously: Mono_Set_GRT0 + """ + pvs=mono_pvs(grt_num,None) + caput(pvs['grt_offset'],val) + mono_get_all_extended(verbose=True) + +def mono_mirror_offset_set(val,mir_num=None): + """ + sets the mirror offset for the grating + + Previously: Mono_Set_MIR0 + """ + pvs=mono_pvs(None,mir_num) + caput(pvs['mir_offset'],val) + mono_get_all_extended(verbose=True) + +def mono_grating_mirror_offsets_set(mirror_offset): + """ + changes the mirror and grating offsets to maintain parallelism + After determining parallelism (zero order a=b) + Get the correct take-off angle delta, equivalent to scan ExitSlit_Vcenter + + Previously: Mono_Set_MIR0_GRT0,MIR_GRT_Offset,Mono_Set_MIR0_GRT0_all + """ + current_vals = mono_get_all_extended(verbose=False) + energy_sp = current_vals['energy_sp'] + mirror_offset_old = current_vals['mir_offset'] + + #get current grating offsets + grating_offsets=[] + for grt_num in range(0,3): + grating_offsets.append(mono_parameters_pv(grt_num=grt_num,mir_num=None)['grt_offset']) + + #calc mirror_offset - grating_grating + mir_grt_offsets = mirror_offset_old-np.array(grating_offsets) # list of MIR-GRT for all 3 gratings + + #set mirror_offset + mono_mirror_offset_set(mirror_offset) + + #set grating_offsets + for grt_num in range(0,3): + mono_grating_offset_set(mirror_offset - mir_grt_offsets[grt_num],grt_num) + + time.sleep (1) + mono_energy_set(energy_sp) + mono_get_all_extended(verbose=True) + + +def mono_grating_b2_set(val,grt_num=None): + """ + sets the grating offset for the grating + Previously: Mono_Set_b2 + """ + hv = mono_energy_get() + pvs = mono_pvs(grt_num,None) + caput(pvs['grt_b2t'],val) + time.sleep(1) + + mono_energy_set(hv) + mono_get_all_extended(verbose=True) + +def mono_cff_set(val,tune_num): + """ + sets the tuning parameters for the cff calculation + tune_num = order + + Previously: Mono_Set_cff + """ + hv = mono_energy_get() + pvs = mono_pvs() + + #get current info + current_vals = mono_get_all_extended(verbose=False) + grt_pitch = current_vals['grt_pitch'] + mir_pitch = current_vals['mir_pitch'] + print ('Pitch grating, mirror: ',grt_pitch,mir_pitch) + + #set the tuning coeffient + tun='tun'+str(tune_num) + caput(pvs[tun],val) + time.sleep(1) + + #set then energy + mono_energy_set(hv) + + #print differences in pitch + mono_get_all_extended(verbose=False) + grt_dif = grt_pitch-current_vals['grt_pitch'] + mir_dif = mir_pitch-current_vals['mir_pitch'] + + print('Pitch grating, mirror: ',current_vals['grt_pitch'],current_vals['mir_pitch']) + print('Differences : ',grt_dif,mir_dif) + +def mono_arm_set(distance_mm): + """ + sets the exit arm for the grating + + Previously: Mono_Set_ExitArm + """ + hv = mono_energy_get() + pvs = mono_pvs() + + #get current info + current_vals = mono_get_all_extended(verbose=False) + grt_pitch = current_vals['grt_pitch'] + mir_pitch = current_vals['mir_pitch'] + print ('Pitch grating, mirror: ',grt_pitch,mir_pitch) + + #set the exit arm + caput("29idmono:PARAMETER.G",distance_mm) + time.sleep(1) + + #set then energy + mono_energy_set(hv) + + #print differences in pitch + mono_get_all_extended(verbose=False) + grt_dif = grt_pitch-current_vals['grt_pitch'] + mir_dif = mir_pitch-current_vals['mir_pitch'] + + print('Pitch grating, mirror: ',current_vals['grt_pitch'],current_vals['mir_pitch']) + print('Differences : ',grt_dif,mir_dif) \ No newline at end of file diff --git a/build/lib/iexcode/instruments/__init__.py b/build/lib/iexcode/instruments/__init__.py new file mode 100644 index 0000000..e244a3c --- /dev/null +++ b/build/lib/iexcode/instruments/__init__.py @@ -0,0 +1,42 @@ + +""" from iexcode.instruments.AD_utilites import * +from iexcode.instruments.ARPES import * +from iexcode.instruments.bakeout import * +from iexcode.instruments.beamline import * +from iexcode.instruments.cameras import * +from iexcode.instruments.conversions_constants import * +from iexcode.instruments.current_amplifiers import * +from iexcode.instruments.diagnostics import * +from iexcode.instruments.electron_analyzer import * +from iexcode.instruments.encoders import * +from iexcode.instruments.files_and_folders import * +from iexcode.instruments.FMB_mirrors import * +from iexcode.instruments.gate_valves import * +from iexcode.instruments.hxp_mirrors import * +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.IEX_VPU import * +from iexcode.instruments.Kappa import * +from iexcode.instruments.Kappa_det import * +from iexcode.instruments.Kappa_Euler import * +from iexcode.instruments.Lakeshore_335 import * +from iexcode.instruments.logfile import * +from iexcode.instruments.m3r import * +from iexcode.instruments.Motors import * +from iexcode.instruments.mpa import * +from iexcode.instruments.remote_controlers import * +from iexcode.instruments.resolution import * +from iexcode.instruments.s29_temp_cntl import * +from iexcode.instruments.scalers import * +from iexcode.instruments.Scienta import * +from iexcode.instruments.shutters import * +from iexcode.instruments.slits import * +from iexcode.instruments.spec_stuff import * +from iexcode.instruments.staff import * +from iexcode.instruments.storage_ring import * +from iexcode.instruments.userCalcs import * +from iexcode.instruments.utilities import * +from iexcode.instruments.VLS_PGM import * +from iexcode.instruments.vortexs29 import * +from iexcode.instruments.xrays import * + + """ diff --git a/build/lib/iexcode/instruments/bakeout.py b/build/lib/iexcode/instruments/bakeout.py new file mode 100644 index 0000000..94dfd1a --- /dev/null +++ b/build/lib/iexcode/instruments/bakeout.py @@ -0,0 +1,193 @@ +from time import sleep +from os import mkdir +from os.path import exists + +from epics import caget,caput + +from iexcode.instruments.files_and_folders import get_next_fileNumber, check_run +from iexcode.instruments.scanRecord import * + +############################################################################################################## +############################## Scan Bakeout ############################## +############################################################################################################## + +def bakout_scan(scan_ioc_name,chamber): + """ + sets up the scan record to record temperature and pressures + + scan_ioc_name = 'ARPES', 'Kappa','Test' + chamber = "Beamline"; Yokawgawa + all beamline ion pumps and vacuum gauges + chamber = "Mono"; Beamline + mono thermocouples + chamber = "ARPES"; Beamline + ARPES pressures and temperatures + chamber = "Kappa"; Beamline + Kappa pressures and temperatures + chamber = "Stinger"; Stinger temperatures + ARPES and Kappa temperatures and pressures + + Previously: Scan_Bakeout + """ + #setting the folders and save datat + run = check_run() + folder='/net/s29data/export/data_29idb/Bakeout/' + subfolder='/'+chamber+'/'+run + prefix='mda_' + + scan_ioc = '29id'+scan_ioc_name+":" + + caput(scan_ioc+'saveData_fileSystem',folder) + caput(scan_ioc+'saveData_subDir',subfolder) + caput(scan_ioc+'saveData_baseName',prefix) + + MyPath="/net/s29data/export/data_29idb/Bakeout/"+chamber+"/"+run + print("\nBakeout folder: " + MyPath) + if not (exists(MyPath)): + mkdir(MyPath) + sleep(1) + FileNumber=1 + else: + FileNumber=get_next_fileNumber(MyPath,prefix[:-1]) + caput(scan_ioc+'saveData_scanNumber',FileNumber) + + sleep(5) + SaveStatus=caget(scan_ioc+'saveData_status',as_string=True) + SaveMessage=caget(scan_ioc+'saveData_message',as_string=True) + print("\nSave Status "+scan_ioc+" "+SaveStatus+" - "+SaveMessage) + + #setting up scan record + detector_dictionary = bakeout_detector_dictionary(chamber) + + #caput("29idb:ca1:read.SCAN",0) + #trigger_dictionary = {1:"29idb:ca1:read"} + trigger_dictionary = {1:""} + + before_scan_pv = "" + after_scan_pv = "" + + mda_scan = ScanRecord(scan_ioc,detector_dictionary,trigger_dictionary,before_scan_pv,after_scan_pv) + + mda_scan.time_scan(99999,60) + + + +def bakeout_detector_dictionary(chamber): + """ + returns a dictionary of the default detectors + + """ + thermocouples={ + 1:"29iddau1:dau1:001:ADC", + 2:"29iddau1:dau1:002:ADC", + 3:"29iddau1:dau1:003:ADC", + 4:"29iddau1:dau1:004:ADC", + 6:"29iddau1:dau1:006:ADC", + 7:"29iddau1:dau1:007:ADC", + 8:"29iddau1:dau1:008:ADC", + 9:"29iddau1:dau1:009:ADC", + 10:"29iddau1:dau1:010:ADC", + } + ion_gauges={ + 21:"29idb:VS1A.VAL", + 22:"29idb:VS2A.VAL", + 23:"29idb:VS3AB.VAL", + 24:"29idb:VS4B.VAL", + 25:"29idb:VS5B.VAL", + 26:"29idb:VS6B.VAL", + 27:"29idb:VS7B.VAL", + 28:"29idb:VS8C.VAL", + 29:"29idb:VS8CSP.VAL", + 30:"29idb:VS9C.VAL", + 31:"29idb:VS10C.VAL", + 32:"29idb:VS8D.VAL", + 33:"29idb:VS9D.VAL", + 34:"29idb:VS10D.VAL", + } + ion_pumps={ + 41:"29idb:IP1A.VAL", + 42:"29idb:IP2A.VAL", + 43:"29idb:IP3A.VAL", + 44:"29idb:IP3B.VAL", + 45:"29idb:IP4B.VAL", + 46:"29idb:IP5B.VAL", + 47:"29idb:IP6B.VAL", + 48:"29idb:IP7B.VAL", + 49:"29idb:IP7C.VAL", + 51:"29idb:IP8C1.VAL", + 52:"29idb:IP8C2.VAL", + 53:"29idb:IP9C.VAL", + 54:"29idb:IP10C1.VAL", + 55:"29idb:IP10C2.VAL", + 56:"29idb:IP7D.VAL", + 57:"29idb:IP8D1.VAL", + 58:"29idb:IP8D2.VAL", + 59:"29idb:IP9D.VAL", + 60:"29idb:IP10D1.VAL", + 61:"29idb:IP10D2.VAL", + } + + mono = { + 11:"29idmono:TC1_MON", + 12:"29idmono:TC2_MON", + 13:"29idmono:TC3_MON", + 14:"29idmono:TC4_MON", + 15:"29idmono:TC5_MON", + 16:"29idmono:TC6_MON", + } + + ARPES = { + 11:"29idc:VS11C.VAL", + 12:"29idc:VSCUBE.VAL", + 13:"29idc:IP11C1.VAL", + 14:"29idc:IP11C2.VAL", + 15:"29idc:IPCUBE.VAL", + 16:"29idARPES:LS335:TC1:IN1", + 17:"29idARPES:LS335:TC1:IN2", + } + + Kappa = { + 11:"29idb:VS11D.VAL", + 12:"29idb:VS12D.VAL", + 13:"29idd:LS331:TC1:SampleA", + 14:"29idd:LS331:TC1:SampleB", + } + + Stinger = { + 1:"29idc:LS340:TC2:Control", + 2:"29idc:LS340:TC2:Sample", + + 3:"29idARPES:LS335:TC1:IN1", + 4:"29idARPES:LS335:TC1:IN2", + + 5:"29idd:LS331:TC1:SampleA", + 6:"29idd:LS331:TC1:SampleB", + + 7:"29idc:VS11C.VAL", + 8:"29idc:IP11C1.VAL", + 9:"29idc:IP11C2.VAL", + + 10:"29idb:VS11D.VAL", + } + d={} + if chamber.lower() == "Beamline".lower(): + d.update(thermocouples) + d.update(ion_gauges) + d.update(ion_pumps) + print("ScanRecord ready") + + if chamber.lower() == "Mono".lower(): + d.update(thermocouples) + d.update(ion_gauges) + d.update(ion_pumps) + d.update(mono) + print('Setting up Mono temperatures') + + if chamber.lower() == "ARPES".lower(): + d.update(ARPES) + print('Setting up ARPES temperatures') + + if chamber.lower() == "Kappa".lower(): + d.update(Kappa) + print('Setting up Kappa temperatures') + + if chamber.lower() == "Stinger".lower(): + d.update(Stinger) + print('Setting up Stinger temperatures') + + return d diff --git a/build/lib/iexcode/instruments/beamline.py b/build/lib/iexcode/instruments/beamline.py new file mode 100644 index 0000000..6817162 --- /dev/null +++ b/build/lib/iexcode/instruments/beamline.py @@ -0,0 +1,94 @@ + +""" +short name for functions used with or without x-rays + +""" + +import numpy as np +from time import sleep + +from epics import PV +from iexcode.instruments.IEX_endstations import * + + +############################################################################################################## +########################### Scan stuff ###################### +############################################################################################################## +def scan_fillin(VAL,RBV,start,stop,steps_points,**kwargs): + """ + fills in the scan record for the curretn beamline ioc + """ + BL.mda.fillin(VAL,RBV,start,stop,steps_points,**kwargs) + +def scan_fillin_table(VAL,RBV,my_table,**kwargs): + """ + fills in the scan record for the curretn beamline ioc + """ + BL.mda.fillin_table(VAL,RBV,my_table,**kwargs) + +def scan_go(**kwargs): + """ + Starts a scan + by default: scanDIM=1 + Logging is automatic: use **kwargs or the optional logging arguments see scanlog() for details + kwargs: + X-ray = True (default), does shutter checks + = False no shutter checks + """ + BL.mda.go(verbose=True,**kwargs) + + +def last_mda(): + """ + returns the last mda file number in the ioc defined by BL_ioc + Previously: LastMDA + """ + filenum = BL.mda.lastFileNum() + return filenum + + + +############################################################################################################## +############################## Beeper ############################## +############################################################################################################## + + + +def print_beeper(scanDIM=1): + """ + Prints pv to copy/paste into the beeper + + Previously: Print_Beeper + """ + branch=BL.branch + if branch == "c": + print("29idcEA:det1:Acquire") + pv=BL.ioc()+":scan"+str(scanDIM)+".FAZE" + print(pv) + print("ID29:BusyRecord") + + +def branch_cams_enable(branch=BL.branch): + """ + """ + cam_dict={'c':[0,1,2],'c':[3,4,6]} # index of cam_list + pvcam1=PV("29id_ps1:cam1:Acquire") + pvcam2=PV("29id_ps2:cam1:Acquire") + pvcam3=PV("29id_ps3:cam1:Acquire") + pvcam4=PV("29id_ps4:cam1:Acquire") + pvcam6=PV("29id_ps6:cam1:Acquire") + pvcam7=PV("29id_ps7:cam1:Acquire") + cam_list=[pvcam1,pvcam2,pvcam3,pvcam4,pvcam6,pvcam7] + sleep(0.1) + for pvcam in cam_list: # turn OFF all cam + try: + if pvcam.connected: pvcam.put(0) + except: + pass + for i in cam_dict[branch]: # turn ON relevant cam + try: + if cam_list[i].connected: cam_list[i].put(1) + else: print(cam_list[i].pvname+' not connected') + except: + pass + diff --git a/build/lib/iexcode/instruments/cameras.py b/build/lib/iexcode/instruments/cameras.py new file mode 100644 index 0000000..1436d9b --- /dev/null +++ b/build/lib/iexcode/instruments/cameras.py @@ -0,0 +1,52 @@ + +from epics import caget, caput + +from iexcode.instruments.userCalcs import userStringSeq_clear +from iexcode.instruments.AD_utilities import * +from iexcode.instruments.IEX_endstations import * + + +def cam_pv_dictionary(cam_num): + """ + dictionary of pv names for the beamline cameras + """ + d={ + 1:"29id_ps1:", + 2:"29id_ps2:", + 3:"29id_ps3:", + 4:"29id_ps4:", + 5:"29idarv5:", + 6:"29id_ps6:", + } + return d[cam_num] + +def cam_scan_setup(cam_num,ADtype='TIFF',**kwargs): + """ + sets up the BL scanRecord to trigger the camera (trigger - 2) + """ + ADplugin = cam_pv_dictionary(cam_num)+ADtype+":" + ADplugin_ScanSetup(ADplugin,BL.mda, **kwargs) + +def cam_snap(cam_num,ADtype='TIFF',**kwargs): + """ + takes a camera image and saves it in the user folder + + **kwargs: + ExposureTime: changes both the exposure time and the acquire time for the snapshot + resets after acquisition + FreeRun: True => disable setting and go back to continuous acquision + False => leave saving enabled and camera in single acquision + + """ + ADplugin = cam_pv_dictionary(cam_num)+ADtype+":" + AD_SaveFileSetup(ADplugin,BL.mda,**kwargs) + AD_snap(ADplugin,**kwargs) + +def cam_live(cam_num,ADtype='TIFF',**kwargs): + """ + puts camera in no save and continuous + a.k.a free run + + """ + ADplugin = cam_pv_dictionary(cam_num)+ADtype+":" + AD_FreeRun(ADplugin,**kwargs) diff --git a/build/lib/iexcode/instruments/conversions_constants.py b/build/lib/iexcode/instruments/conversions_constants.py new file mode 100644 index 0000000..45f1aea --- /dev/null +++ b/build/lib/iexcode/instruments/conversions_constants.py @@ -0,0 +1,109 @@ +import numpy as np + +from epics import caget + +############################################################################################################## +############################## conversions ############################## +############################################################################################################## +h=4.135667516e-15 +c=299792458 + +def deg2rad(angle_deg): + angle_rad=angle_deg*np.pi/180 + return angle_rad + +def rad2deg(angle_rad): + angle_deg=angle_rad*180/np.pi + return angle_deg + +def eV2Lambda(eV): + """ + Converts energy (eV) into wavelenght (Angstrom) + """ + + Angstrom = h*c/eV*1e9*10 + return Angstrom + +def Lambda2eV(Angstrom): + """ + Converts wavelenghth (Angstrom) into energy (eV) + """ + + eV = h*c/Angstrom*1e9*10 + return eV + +############################################################################################################## +############################## Kappa to Euler Conversion ############################## +############################################################################################################## +def Kappa2Fourc(ang1,ang2,ang3,conversion,k_arm=50): + + a,b,c=0,0,0 + if conversion=='Kappa': + a,b,c=EtoK(ang1,ang2,ang3,k_arm) + print(("\n"+"th = "+str(ang1))) + print(("chi = "+str(ang2))) + print(("phi = "+str(ang3))) + print("~~~~~~~~") + print(("kth = "+str(a))) + print(("kap = "+str(b))) + print(("kphi = "+str(c)+"\n")) + elif conversion=='fourc': + a,b,c=KtoE(ang1,ang2,ang3,k_arm) + print(("\n"+"kth = "+str(ang1))) + print(("kap = "+str(ang2))) + print(("kphi = "+str(ang3))) + print("~~~~~~~~") + print(("th = "+str(a))) + print(("chi = "+str(b))) + print(("phi = "+str(c)+"\n")) + else: + print('2nd argument invalid; please choose one of the following:') + print('"Kappa" or "fourc"') + return a,b,c + + + +def EtoK(e_theta,e_chi,e_phi,k_arm=50): + conv = np.pi/180.0 + kth_offset=caget('29idKappa:userCalcOut1.G') + if(abs(e_chi)> 2.0*k_arm): + print("Chi should be no more than twice the Kappa arm offset angle.") + kth,kap,kphi=0,0,0 + else: + print(("Calculating Kappa angles using kth0 = "+str(kth_offset))) + k_ang = k_arm*conv + delta = np.asin( - np.tan(e_chi*conv/2.0) / np.tan(k_ang) ) + k_theta = e_theta*conv - delta + k_kappa = 2.0 * np.asin( np.sin(e_chi*conv/2.0) / np.sin(k_ang)) + k_phi = e_phi*conv - delta + #print k_theta, k_kappa,k_phi + kth = rounder(k_theta)-(57.045-kth_offset) + kap = rounder(k_kappa) + kphi = rounder(k_phi) + #print delta + return (kth,kap,kphi) + + + + +def KtoE(k_theta,k_kappa,k_phi,k_arm=50): + conv = np.pi/180.0 + kth_offset=caget('29idKappa:userCalcOut1.G') + print(("Calculating Euler angles using kth0 = "+str(kth_offset))) + k_ang = k_arm*conv + delta = np.atan( np.tan(k_kappa*conv/2.0) * np.cos(k_ang) ) + e_theta = k_theta*conv - delta + e_chi = 2.0 * np.asin( np.sin(k_kappa*conv/2.0) * np.sin(k_ang) ) + e_phi = k_phi*conv - delta + #print round(e_theta,1),round(e_phi,1),round(e_chi,1) + theta = rounder(e_theta)+(57.045-kth_offset) + chi = rounder(e_chi) # convert from rad to deg + phi = rounder(e_phi) + #print round(delta,1) + return (theta,chi,phi) + + +def rounder(val): # convert from rad to deg + conv = np.pi/180.0 + roundVal=round(1000.0*val/conv)/1000.0 + return roundVal diff --git a/build/lib/iexcode/instruments/current_amplifiers.py b/build/lib/iexcode/instruments/current_amplifiers.py new file mode 100644 index 0000000..5df2d30 --- /dev/null +++ b/build/lib/iexcode/instruments/current_amplifiers.py @@ -0,0 +1,367 @@ +import numpy as np +from time import sleep + +from epics import caget, caput +from iexcode.instruments.userCalcs import userStringSeq_pvs, userStringSeq_clear +from iexcode.instruments.VLS_PGM import mono_energy_get + + +def ca_detector_list(branch): + """ + returns the list of keithley current amplifiers based on key + key: 'ARPES' / 'Staff' + """ + ca_list = list(ca_dictionary()[branch].keys()) + + return ca_list + +def ca_dictionary(): + """ + dictionary of connected Kiethlys and thier names + + Previously: CA_Name + """ + ca={} + ca["b"] = { + 1: 'W-mesh', + 2: 'H-wire', + 3: 'V-wire', + 4: 'Slit1A', + 5: 'Slit1A', + 9: 'D2B', + 10: 'B-branch', + 12: '', + 13: 'Slit3C', + 14: 'MeshD', + 15: 'DiodeC', + } + + ca["c"] = { + 1:'TEY', + } + return ca + +############################################################################################################## +################################ Keithley ############################## +############################################################################################################## +def Keithley_pv(ca_ioc, ca_num): + return "29id"+ca_ioc+":ca"+str(ca_num)+":" + +class Keithley: + """ + class for Keithley current amplifiers + """ + def __init__(self,ca_ioc, ca_num): + self._pv = Keithley_pv(ca_ioc, ca_num) + try: + self.name = ca_dictionary()[ca_ioc][ca_num] + except: + self.name = 'CA'+ca_ioc+str(ca_num) + + self.current + self.rate + self.filter_num + self.range + + def get(self): + """ + reads the current SRS and corresponding scaler values + """ + self.current = caget(self.pv+"read") + self.range = 'Autoscale' if caget(self.pv+"rangeAuto",as_string=True)=='On' else caget(self.pv+"range",as_string=True) + self.rate = caget(self.pv+"rate") + digital_filter = (self.pv+'digitalFilter') + self.filter_num = 1 if digital_filter == 'On' else (self.pv+'digitalFilterCount') + + + def reset(self,rate="Slow"): + """ + + Previously: Reset_CA + """ + + pv = self.pv + caput(pv+":reset.PROC",1) + caput(pv+":digitalFilterSet","Off") + caput(pv+":medianFilterSet","Off") + caput(pv+":zeroCheckSet",0) + caput(pv+":rangeAuto",1) + caput(pv+":rateSet",rate) + caput(pv+":rangeAutoUlimit","20mA") + caput(pv+":read.SCAN",".5 second") + + def autoscale(self,On_Off='On',gain=7): + """ + ca_ioc = 'b' / 'c' + ca_num = 2 + + On_Off= 'On' => Turns On the Autoscale; gain is irrelevant. + On_Off= 'Off' => Turns Off the Autoscale with gain below: + 0 = 2nA + 1 = 20nA + 2 = 200nA + 3 = 2uA + 4 = 20uA + 5 = 200uA + 6 = 2mA + 7 = 20mA + + Previously: CA_Autoscale + """ + pv = self.pv + caput(pv+":rangeAutoSet",On_Off) + sleep(0.5) + caput(pv+":rangeSet",gain) + print(pv,"Autoscale",On_Off) + + if On_Off == 'Off': + sleep(1) + print("Gain set to:",caget(pv+":range",as_string=True)) + + + + def avg(self,num_averages,rate='Slow',verbose=True): + """ + Turns on the filtering for the Keithlys for average during a scan + + rate: Keithley sampling rate + 'Slow' / 'Medium' / 'Fast' + + num_averages: number of points to average + + returns the settling_time + + Previously: CA_Filter + """ + + pv = self.pv + name=self.name + t=0.1 + if rate == "Slow": + t=6/60.0 + elif rate == "Medium": + t=1/60.0 + elif rate == "Fast": + t=0.1/60.0 + + settling_time=round(max(0.15,num_averages*t+0.1),2) + + if num_averages <= 1: # no averaging + self.reset(rate) + settling_time = None + if verbose: + print("Average disabled: "+name+" - "+pv) + + else: + caput(pv+"read.SCAN","Passive",wait=True,timeout=500) + caput(pv+"rateSet",rate) + sleep(1) + caput(pv+"digitalFilterCountSet",num_averages,wait=True,timeout=500) + caput(pv+"digitalFilterControlSet","Repeat",wait=True,timeout=500) + caput(pv+"digitalFilterSet","On",wait=True,timeout=500) + + if verbose: + print("Average enabled: "+name+" - "+pv) + print("Detector settling time: "+str(settling_time)+"s") + + return settling_time + + + +def ca_reset_all(rate="Slow"): + """" + + Previously: Reset_CA_all + """ + ca_dict = ca_dictionary() + for ca_ioc in ca_dict.keys(): + for ca_num in ca_dict[ca_ioc].keys(): + CA = Keithley(ca_ioc,ca_num) + CA.reset() + + caput("29idb:ca5:read.SCAN","Passive") # CA5 in passive + print("\nAll the current amplifiers have been reset; ca5 set to passive.") + +def ca_live_sequence(ioc,seq_num,detector_list): + """ + creates a string sequence to put keithleys back in live mode + + ca_list = list of ca to go in live + + Previously: CA_Live_StrSeq + """ + userStringSeq_pv, userStringSeq_proc = userStringSeq_pvs(ioc, seq_num) # do we need to add 29idb:ca5 ??? + + userStringSeq_clear(ioc,seq_num) + + caput(userStringSeq_pv+".DESC","CA Live "+ioc) + + for (i,ca) in enumerate(detector_list): + pv = "29id"+ca[0]+":ca"+str(ca[1])+":" + ca_read_pv = pv+'read.SCAN CA NMS' + ca_avg_pv = pv+'digitalFilterSet PP NMS' + + caput(userStringSeq_pv+".LNK"+str(i+1),ca_avg_pv) + caput(userStringSeq_pv+".STR" +str(i+1),"Off") + + n=len(detector_list) + if n+1+i < 10: + caput(userStringSeq_pv+".LNK" +str(n+1+i),ca_read_pv) + caput(userStringSeq_pv+".STR" +str(n+1+i),".5 second") + caput(userStringSeq_pv+".WAIT"+str(n+1+i),"After"+str(n)) + elif n+1+i == 10: + caput(userStringSeq_pv+".LNKA",ca_read_pv) + caput(userStringSeq_pv+".STRA",".5 second") + caput(userStringSeq_pv+".WAITA","After"+str(n)) + + return userStringSeq_proc + +def ca_average(avg_pts,ca_list,rate="Slow",verbose=True): + """ + Average reading of the relevant current amplifiers for the current scanIOC/branch. + + Previously: CA_average + """ + print("\nAverage set to: "+str(max(avg_pts,1))) + for i in range(0,len(ca_list)): + ca = Keithley(ca_list[i][0],ca_list[i][1]) + ca.avg(avg_pts,rate=rate,verbose=verbose) + + +def load_responsivity_curve(): + """ + Loads the responsivity curve for the AUX100 photo diode, used to convert + current to flux at a given photon energy + + Previously: loadResponsivityCurve + """ + FilePath='/home/beams/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/' + FileName="DiodeResponsivityCurve" + data = np.loadtxt(FilePath+FileName, delimiter=' ', skiprows=1) + return data + +def current2flux(current,hv=None,verbose=True): + """ + Converts the current to flux for of an AUX 100 diode using the responisity curves + + hv = None; gets the current mono energy; otherwise you will need to specify + + Previously CA2flux + """ + try: + curve=load_responsivity_curve() + except: + print('responsivity curve not loaded') + return + responsivity=curve[:,0] + energy=curve[:,1] + charge = 1.602e-19 + if hv is None: + try: + hv = mono_energy_get() + except: + print('specify hv') + return + eff=np.interp(hv,energy,responsivity) + flux = current/(eff*hv*charge) + if verbose: + print("\nCalculating flux for:") + print(" hv = %.1f eV" % hv) + print(" current = %.3e Amp" % current) + print("Flux = %.3e ph/s\n" % flux) + return flux + + +def flux2current(flux,hv=None,verbose=True): + """ + Converts the specified flux to a current for of an AUX 100 diode using the responisity curves + + hv = None; gets the current mono energy; otherwise you will need to specify + """ + try: + curve=load_responsivity_curve() + except: + print('responsivity curve not loaded') + return + responsivity=curve[:,0] + energy=curve[:,1] + charge = 1.602e-19 + if hv is None: + try: + hv = mono_energy_get() + except: + print('specify hv') + return + eff=np.interp(hv,energy,responsivity) + current = flux*(eff*hv*charge) + if verbose: + print("\nCalculating current for:") + print(" hv = %.1f eV" % hv) + print(" flux = %.3e ph/s" % flux) + print("Current = %.3e Amp/n" % current) + return current + + + +############################################################################################################## +################################ SRS ############################## +############################################################################################################## +class SRS: + """ + SRS current amplifier and corresponding scalers + """ + + def __init__(self,scaler_pv,srs_pv): + self._scaler_pv = scaler_pv + self._srs_pv = srs_pv + + self.scaler_value + self.gain + self.unit + self.current + self.get() + + def get(self): + """ + reads the current SRS and corresponding scaler values + """ + self.scaler_value = caget(self._scaler_pv) + self.gain = float(caget(self._scaler_pv+'sens_num.VAL',as_string=True)) + self.unit = caget(self._scaler_pv+'sens_unit.VAL',as_string=True) + CA = {'pA':1e-12, 'nA':1e-9, 'uA':1e-6, 'mA':1e-3} + self.current = self.scaler_value * self.gain * CA[self.unit] + + def setgain(self,gain,unit): + """ + gain = 1,2,5,10,20,50,100,200,500 + unit = 'pA, 'nA', 'uA', 'mA' + """ + caput(self._srs_pv+'sens_num.VAL',str(gain)) + caput(self._srs_pv+'sens_unit.VAL',str(unit)) + + + def srs_print_all(self, long=False): + """ + prints SRS setting + """ + + invert=caget(self._srs_pv+'invert_on.VAL',as_string=True) + currentUnit=caget(self._srs_pv+'sens_unit.VAL',as_string=True) + currentValue=caget(self._srs_pv+'sens_num.VAL',as_string=True) + offsetValue=caget(self._srs_pv+"offset_num.VAL",as_string=True) + offsetUnit=caget(self._srs_pv+"offset_unit.VAL",as_string=True) + offsetSign=caget(self._srs_pv+"offset_sign.VAL",as_string=True) + offsetFactor=caget(self._srs_pv+"off_u_put.VAL",as_string=True) + print('Gain: '+currentValue+' '+currentUnit+' (invert '+invert+')') + print('Baseline: '+offsetSign+' '+offsetFactor+' x '+offsetValue+" "+offsetUnit) + if long: + filterType=caget(self._srs_pv+'filter_type.VAL',as_string=True) + filterLow=caget(self._srs_pv+'low_freq.VAL',as_string=True) + filterHigh=caget(self._srs_pv+'high_freq.VAL',as_string=True) + blank=caget(self._srs_pv+'blank_on.VAL',as_string=True) + biasOnOff=caget(self._srs_pv+'bias_on.VAL',as_string=True) + biasValue=caget(self._srs_pv+'bias_put.VAL',as_string=True) + print('Filter: '+filterType+' - Low/High: '+filterLow+' -'+filterHigh) + print('Bias: '+biasOnOff+'- '+biasValue) + print('Blank: '+blank) + diff --git a/build/lib/iexcode/instruments/diagnostics.py b/build/lib/iexcode/instruments/diagnostics.py new file mode 100644 index 0000000..50814ae --- /dev/null +++ b/build/lib/iexcode/instruments/diagnostics.py @@ -0,0 +1,225 @@ +from numpy import nan + +from epics import caput, caget +from iexcode.instruments.IEX_endstations import * + +############################################################################################################## +################################ default positions ############################## +############################################################################################################## + +###### We should make pvs in the 29idb ioc to hold these values: +def diagnostics_dict(): + """ + Dictionary of Diagnostic positions In and Out by either motor number or name + WARNING: When updating motor values, also update the following screens: + - 29id_BL_Layout.ui (for MeshD and DiodeC) + - 29id_Diagnostic.ui + - 29idd_graphic + usage: + diagnostics_dict()['name'] returns dictionary motor:name + diagnostics_dict()['motor'] returns dictionary name:motor + diagnostics_dict()['In'] returns dictionary motor:In position (where val can be a list for multiple position) + diagnostics_dict()['Out'] returns dictionary motor:In position + motor=diagnostics_dict()['motor']['gas-cell'] + pos_in=diagnostics_dict()['In'][motor] + + WARNING: When updating MeshD (D5D) value: update value in the dictionnary + caQtdM (29id_BL_Diag.ui + 29idd_graphic.ui + Diagnostic.ui) + + Previously: AllDiag_dict + """ + diag={} + diag["In"] = { 5:-55, 6:-46, 17:-56, 20:-30, 25:-56, 28:[-3,-3]} + + diag["Out"] = {1:-4, 2:-10, 3:-4, 4:-4, 5:-20, 6:-20, 7:-20, 17:-20, 20:-21, 25:-20, 28:-3} + diag["name"]= {1:"H-wire", 2:"V-wire", 3:"H-Diagon", 4:"V-Diagon", 5:"W-mesh", + 6:"D2B", 7:"D3B", 17:"D4C/pre-slit", 20:"gas-cell", 25:"D4D/pre-slit", 28:"D5D/pre-RSXS"} + diag["motor"]= {"H-wire":1, "V-wire":2, "H-Diagon":3, "V-Diagon":4,"W-mesh":5, + "D2B":6, "D3B":7, "D4C":17, "gas-cell":20,"D4D":25,"D5D":28} + return diag + +def diagnostic_CA_dict(): + CA_dict={ + 'diode_c':'29idb:ca15:' + } + return CA_dict + + +def diagnostic(diagnostic_name,in_out): + "Inserts/retracts a diagnostic(motor number or name) either = \"In\" or \"Out\"" + diag=diagnostics_dict() + if type(diagnostic_name) is int: + motor=diagnostic_name + name=diag['name'][motor] + else: + name=diagnostic_name + motor=diag["motor"][name] + position=diag[in_out][motor] + + caput("29idb:m"+str(motor)+".VAL",position,wait=True,timeout=18000) + print("\n"+name+": "+ in_out) + +def diagnostics_all_out(diode_stay_in=False): + """ + Retracts all diagnostic + diode_to_stay_in = True / False (checks beamline) + + Previously: AllDiagOut + """ + diag=diagnostics_dict() + text="" + + #which motor is Diode of interest + if diode_stay_in: + branch = BL.branch + + if branch == 'c': + diode_motor=diag["motor"]["gas-cell"] + elif branch == 'd': + diode_motor=diag["motor"]["D5D"] + else: + diode_motor=None + + #Taking out the diagnostic + for motor in list(diag["Out"].keys()): + if motor is diode_motor: + text=' except Diode-'+BL.branch + #putting Diode In if not already in -JM + position=diag["In"][motor] + caput("29idb:m"+str(motor)+".VAL",position,wait=True,timeout=18000) + else: + position=diag["Out"][motor] + caput("29idb:m"+str(motor)+".VAL",position,wait=True,timeout=18000) + text="All diagnostics out"+text + print("\n",text) + +def diagnostics_all_in(): + """ + Inserts all diagnostic (meshes and diodes) for pinhole scans + + Previously: AllDiagIn and AllMeshIn() + """ + diag=diagnostics_dict() + for motor in list(diag["In"].keys()): + position=diag["In"][motor] + if type(position) == list: + position=position[0] + caput("29idb:m"+str(motor)+".VAL",position,wait=True,timeout=18000) + print('m'+str(motor)+' = '+str(position)) + print("All diagnostics in (meshes and diodes) for pinhole scans") + +def mesh_W(In_Out): + """ + Inserts/retracts RSXS mesh (post-slit); arg = \"In\" or \"Out\" + + Previously MeshW + """ + diag=diagnostics_dict() + motor=5; position=diag[In_Out][motor] + caput("29idb:m"+str(motor)+".VAL",position,wait=True,timeout=18000) + print("\nD1A mesh_W: "+ In_Out) + + +def diodeC(In_Out): + """ + Inserts/retracts ARPES (gas-cell) diode; arg = \"In\" or \"Out\" + + Previously: DiodeC + """ + diag=diagnostics_dict() + motor=20; position=diag[In_Out][motor] + caput("29idb:m"+str(motor)+".VAL",position,wait=True,timeout=18000) + print("\nARPES Diode: "+ In_Out) + + +def diodeD(In_Out): + """ + Inserts/retracts RSXS diode; arg = \"In\" or \"Out\" + + Previously:DiodeD + """ + diag=diagnostics_dict() + motor=28; position=position=diag[In_Out][motor] + if type(position) == list: + position=position[1] + caput("29idb:m"+str(motor)+".VAL",position,wait=True,timeout=18000) + print("\nRSXS Diode: "+ In_Out) + + + +def meshC(In_Out,which="postSlit"): + """ + Inserts/retracts ARPES mesh (preSlit or postSlit); arg = \"In\" or \"Out\" + + Previously: MeshC + """ + diag=diagnostics_dict() + if which == "postSlit": + motor=20; position=-31 + elif which == "preSlit": + motor=17; position=diag[In_Out][motor] + + caput("29idb:m"+str(motor)+".VAL",position,wait=True,timeout=18000) + print("\nD4C Au-Mesh: "+ In_Out) + +def meshD(In_Out): + """ + Inserts/retracts RSXS mesh (post-slit); arg = \"In\" or \"Out\" + + Previoulsy: MeshD + """ + diag=diagnostics_dict() + motor=28; position=position=diag[In_Out][motor] + if type(position) == list: + position=position[0] + caput("29idb:m"+str(motor)+".VAL",position,wait=True,timeout=18000) + print("\nD5D Au-Mesh: "+ In_Out) + +def diagnostic_read(diode_d, quiet=True): + """ + reads the current amplifier and returns the value + quiet = False to pring + """ + try: + diode_pv = diagnostic_CA_dict['diode_d'] + val=caget(diode_pv+'read') + except: + diode_pv="not connected" + val = nan + print('pv not defined') + + if not quiet: + print('Diode-D ',val, "(pv = "+diode_pv+")") + return val + +def diodeC_read(quiet=True): + """ + reads the current amplifier and returns the value + quiet = False to pring + """ + val = diagnostic_read('diode_d',quiet) + return val + +def diodeD_read(quiet=True): + """ + reads the current amplifier and returns the value + quiet = False to pring + """ + val = diagnostic_read('diode_d',quiet) + return val + +def meshD_read(quiet=True): + """ + reads the current amplifier and returns the value + quiet = False to pring + """ + val = diagnostic_read('diode_d',quiet) + return val + + +############################################################################################### +####################################### FLUX CONVERSION ####################################### +############################################################################################### + + + + diff --git a/build/lib/iexcode/instruments/electron_analyzer.py b/build/lib/iexcode/instruments/electron_analyzer.py new file mode 100644 index 0000000..8cfbb77 --- /dev/null +++ b/build/lib/iexcode/instruments/electron_analyzer.py @@ -0,0 +1,797 @@ +############################################################# +###################### Imports ############################## +############################################################# + +from os.path import join, isfile, exists, dirname +from os import mkdir +from operator import itemgetter +import time +from datetime import timedelta +from math import ceil,floor,modf + +import numpy as np +from scipy.interpolate import interp1d + +from epics import caput,caget +from iexcode.instruments.IEX_endstations import * + +from iexcode.instruments.scanRecord import * +from iexcode.instruments.conversions_constants import * +from iexcode.instruments.xrays import energy, scanXAS_BL, BL_energy_tables +from iexcode.instruments.shutters import branch_shutter_close +from iexcode.instruments.VLS_PGM import mono_energy_get +from iexcode.instruments.files_and_folders import get_next_fileNumber +from iexcode.instruments.logfile import * +from iexcode.instruments.ARPES import ARPES_motor_dictionary, ARPES_motor_scan, ARPES_mvsample,ARPES_scan_2D +from iexcode.instruments.Scienta import * + +def __main__(): + global EA + EA = Scienta() + +mda = BL.mda +########################################################################### + +def EA_ioc_init(**kwargs): + """ + run after restarting the 29idcScienta IOC + kwargs: + Close_CShutter="Close" default, if something else then it doesn't close the shutter + """ + kwargs.setdefault("close_shutter",True) + if kwargs['close_shutter']==True: + branch_shutter_close('c') + #Addding HDF5 atttributes + filepath='/xorApps/epics/synApps_6_1/ioc/29idcScienta/iocBoot/ioc29idcScienta/HDF5Attributes.xml' + EA._updateAttributes(filepath) + #Allow EA to make directories + caput('29idcScienta:HDF1:CreateDirectory',-1) + #Enabling User CalcOut, needed for BE + caput("29idcScienta:userCalcOutEnable.VAL","Enable") + #Enabling User StringCalc for caQtDM + ioc="29idcScienta:" + pv=ioc+"userStringCalc2" + caput(pv+".DESC","scan1 trigger2 calc") + caput(pv+".INAA",ioc+"scan1.T2PV CP NMS") + caput(pv+".BB",ioc+"HV:ScanTrigger") + caput(pv+".CALC$","AA==BB") + caput(ioc+"userStringCalcEnable.VAL","Enable") + + #clipping and other processing + caput("29idcScienta:Proc1:EnableLowClip","Enable") + caput("29idcScienta:Proc1:LowClip",1) + caput("29idcScienta:Proc1:EnableHighClip",'Disable') + caput("29idcScienta:Proc1:EnableOffsetScale","Enable") + caput("29idcScienta:Proc1:Scale",10) + caput("29idcScienta:Proc1:Offset",-1) + + #Global Detector Setting + caput('29idcScienta:HV:AngularROImin.VAL',290) + caput('29idcScienta:HV:AngularROImax.VAL',830) + caput('29idcScienta:HV:AngularBinning','Off') + + ### Add here + #Setting ExpFrames + caput("29idcScienta:ExpFrames.VAL",1) + #setting save default lens values + EA._AcquisitionMode('Spectra') + time.sleep(0.25) + try: + KE = mono_energy_get() + except: + KE = 2000 + EA.put(KE, PE=50, LensMode="Angular") + caput(EA.PHV+"KineticEnergy:TWV.VAL",1) + caput(EA._Pcam+"Acquire","Acquire") + + print("C-Shutter is closed for initialization ") + +def getSESslit(): + SES=caget("29idc:m8.RBV") + return SES + +def folders_EA(userPath,filePrefix="EA",**kwargs): + """ + For Staff: folder='b', userName='Staff' + For ARPES: folder ='c' + + setFolders = True; creates the data folder and sets scanRecords/AreaDetectors + = False: only creates the data folders; does NOT set + """ + kwargs.setdefault('set_folders',True) + kwargs.setdefault('debug',False) + try: + ADplugin=EA._savePlugin + dtype=EA.dtype + if dtype == "nc": + df="netCDF" + else: + df=EA.dtype + except: + df="h5" + + fpath=join(userPath,df) + print("\nFolder: " + fpath) + + if exists(fpath): + if kwargs['debug']==True: + print("exists") + try: + fileNumber=get_next_fileNumber(fpath,filePrefix, debug=False) + except: + kwargs['create_only']=True + print("fileNumber not set, EA not connected, create_only=True") + else: + if kwargs['debug']==True: + print("making") + mkdir(fpath) + fileNumber=1 + try: + if kwargs['set_folders']: + caput(ADplugin+"FilePath",fpath) + caput(ADplugin+"FileName",filePrefix) + caput(ADplugin+"FileNumber",fileNumber) + + #setup AD + caput(ADplugin+"FileTemplate","%s%s_%4.4d."+dtype) + caput(ADplugin+"AutoIncrement","Yes") + caput(ADplugin+"AutoSave","Yes") + + print("EA path: "+fpath) + print("Next "+filePrefix+" file: "+str(fileNumber)) + except: + df='h5' + fpath=join(userPath,df) + +def EA_log_update(): + """ + spectra entries for the log file + """ + spectra_info = { + 'PE':[EA.PassEnergy,".0f"], + 'lens_mode':[EA.LensMode,"s"], + "scan_mode":[EA.scan_mode,"s"], + "KE":[str(EA.KE),"s"], + "sweeps":[EA.Sweeps,".0f"], + "frames":[EA.ExpFrames,".0f"] + } + + try: + entry_list=[] + pv_list=[] + format_list=[] + for key in spectra_info: + entry_list.append(key) + pv_list.append(spectra_info[key][0]) + format_list.append(spectra_info[key][1]) + logfile_update("ARPES",BL.ioc,entry_list,pv_list,format_list) + except: + print("EAlog did not write to file, check for errors.") + +def log_headerEA():##JM - need to update so that we get the keys from log_EA + s="\nscan x y z th chi phi T scan_mode E1 E2 step i f PE lens_mode SES slit # ID_mode hv exit_slit GRT TEY1 TEY2 time\n" + kwargs={'comment': s} + logfile_print("ARPES",BL.ioc,comment='') + +def EAsweptTime_estimate(EAlist,overhead=[60,.22]): + """ + estimates the time for spectra with the current analyzer settings + overhead[0] = intiating scan (seconds) + overhead[1] = per point (seconds) + """ + E=1392#image width + HVscanDIM=2 + + B=EAlist[4]*0.000078# PixelEnergy=PassEnergy*0.000078 + + C=EAlist[1]# Estart + A=ceil(EAlist[3]/B)*B # Estep + D=C+ceil((EAlist[2]-C)/A)*A # Estop + + E0=ceil((D-C)/(ceil(A/B)*B)) + E1=E/min(floor(ceil(A/B)+0.001),E) + E2=(E/min(floor(ceil(A/B)+0.001),E))%2 + numPnts=E0+E1-E2+1 + print(numPnts) + + PDLY=caget(EA.PHV+"scan"+str(HVscanDIM)+".PDLY") + DDLY=caget(EA.PHV+"scan"+str(HVscanDIM)+".PDLY") + time_seconds=overhead[0]+numPnts*(1/17+PDLY+DDLY+overhead[1]) + print(C,D,B, EAlist[4]) + return str(timedelta(seconds=time_seconds)) + + +def _scanEATrigger(EAlist,before_after,**kwargs): + """ + before_after="before" sets up scanIOC scanRecord for EA scan and sets prefix to "MDAscan0000" + before_after="after" clears up scanIOC scanRecord of EA scan and resets prefix to "EA" + Trigger EA + Det20 = EA scanNum + + set the EA._savePlugin Prefix to be 'MDAscan0045_' + **kwargs: + scan_dim=1 + detTrig=2 + """ + kwargs.setdefault("scan_dim",1) + kwargs.setdefault("detTrig",2) + kwargs.setdefault("dtype",EA.dtype) + kwargs.setdefault("detNum",20) + kwargs.setdefault("prefix","EA")# if not None then over rides the auto + kwargs.setdefault("debug",False) + + scanPV=BL.ioc+"scan"+str(kwargs["scan_dim"]) + triggerPV=scanPV+".T"+str(kwargs["detTrig"])+"PV" + + if kwargs["debug"]: + print("scanPV: "+scanPV) + print("before_after: "+before_after) + + #setting EA._savePlugin FilePath, FileName,FileNumber + if before_after == "before": + _scanEAPrefix("mda",**kwargs) + scantype, HVscanPV, KElist, parms =EA._spectraSetup(EAlist,**kwargs) + caput(triggerPV,HVscanPV) + + if before_after == "after": + _scanEAPrefix(kwargs["prefix"],**kwargs) + caput(triggerPV,"") + + if kwargs["debug"]: + print(triggerPV,caget(triggerPV, as_string=True)) + return + + +def _scanEAPrefix(ptype,**kwargs): + """ + sets the EA file prefix based on + ptype = "mda" -> for "MDAscan"+current MDA file + else prefix = ptype + kwargs: + debug = False (default) + prefix + """ + kwargs.setdefault("debug",False) + kwargs.setdefault("nzeros",4) + kwargs.setdefault("debug",False) + + prefix="" + if kwargs["debug"]: + print(ptype) + + if ptype == "mda": + fpath = mda.filepath[0:-3]+EA.dtype + nextMDA = mda.fileNum + prefix = "MDAscan"+str.zfill(str(nextMDA),kwargs["nzeros"]) + else: + prefix = ptype + + if kwargs["debug"]==kwargs["debug"]: + print("_scanEAPrefix prefix: ",prefix) + + + #setting the file path for the EA saving + fpath=caget(EA._savePlugin +"FilePath",as_string=True) + caput(EA._savePlugin+"FileName",prefix) + nextNum=get_next_fileNumber(fpath,prefix,**kwargs) + caput(EA._savePlugin+"FileNumber",nextNum) + time.sleep(.5) + if kwargs["debug"]: + print("FilePath: ",caget(EA._savePlugin +"FilePath", as_string=True)) + print("FileName: ",caget(EA._savePlugin +"FileName", as_string=True)) + print("FileNumber: ",caget(EA._savePlugin +"FileNumber", as_string=True)) + +def _BE2KE_setupCalc(BE,DESC,CalcOutNum,OutputPV): + """ + used by scanEA for talking in BE + """ + pvCalcOut="29idcScienta:userCalcOut"+str(CalcOutNum) + if len(OutputPV)==0: + caput(pvCalcOut+"Enable","D") + else: + caput(pvCalcOut+"Enable","E") + caput(pvCalcOut+".DESC", DESC) + caput(pvCalcOut+".A",BE) + caput(pvCalcOut+".INPB","29idmono:ENERGY_MON CP NMS") + caput(pvCalcOut+".INPC","29idcScienta:HV:WorkFunction CP NMS") + caput(pvCalcOut+".CALC$","B-A-C") + caput(pvCalcOut+".OUT",OutputPV+" PP") + return pvCalcOut + +def scanEA_reset(**kwargs): + """resets the IOC after a forced stop + """ + kwargs.setdefault("scan_dim",1) + + _scanEATrigger([],"after",**kwargs) + mda.positioners_clear(kwargs["scan_dim"]) + +def scanEA(EAlist,**kwargs): + """ + Uses the scanRecord (mda) to take multiple Scienta spectra + EAlist= + Fixed Mode:["KE"/"BE",CenterEnergy,PassEnergy,Frames,Sweeps] (5) + Swept Mode:["KE"/"BE",StartEnergy,StopEnergy,StepEnergy,PassEnergy,Frames,Sweeps] (7) + Baby Sweep (dither):["KE"/"BE",CenterEnergy,PassEnergy,Frames,Sweeps,"BS"] (6) + + (+) BE is positive below Ef + (-) BE is negative above Ef + + **kwargs + scanIOC=BL_ioc() + scan_dim=1 + execute: True/False to start the scan => True (default) + debug=False + + """ + + kwargs.setdefault('scanIOC',BL.ioc()) + kwargs.setdefault('scan_dim',1) + kwargs.setdefault('execute',True) + kwargs.setdefault("debug",False) + + if EAlist[0]=="KE" or EAlist[0]=="BE": + pass + else: + print("need to specify BE or KE") + + if EAlist[-1]=='BS': + sweeps=EAlist[-2] + else: + sweeps=EAlist[-1] + + if kwargs['debug']: + print("sweeps: "+str(sweeps)) + + if EAlist[0]=="BE": + if len(EAlist)==5: #Fixed + pvCalcOut1=_BE2KE_setupCalc(EAlist[1],"BE_center",10,"29idcScienta:HV:fixedEnergy.VAL") + EAlist[1]=caget(pvCalcOut1+'.VAL') + arrayP1=list(np.full(sweeps, EAlist[1])) + mda.fillin_table(pvCalcOut1+'.PROC',"",kwargs["scanIOC"],kwargs["scan_dim"],arrayP1,1) + EAlist[1]=arrayP1[0] + if kwargs['debug']: + print('\npvCalcOut1: ',pvCalcOut1) + print('Pos1 table:',arrayP1) + elif len(EAlist)==6: #Baby-Swept + pvCalcOut1=_BE2KE_setupCalc(EAlist[1],"BE_center",10,"29idcScienta:HV:babySweepCenter.VAL") + EAlist[1]=caget(pvCalcOut1+'.VAL') + arrayP1=list(np.full(sweeps, EAlist[1])) + mda.fillin_table(pvCalcOut1+'PROC',"",kwargs["scanIOC"],kwargs["scan_dim"],arrayP1,1) + print('\npvCalcOut1: ',pvCalcOut1) + print('Pos1 table:',arrayP1) + elif len(EAlist)==7: #Sweep + pvCalcOut1=_BE2KE_setupCalc(EAlist[1],"BE_start",9,"29idcScienta:HV:sweepStartEnergy.VAL") + pvCalcOut2=_BE2KE_setupCalc(EAlist[2],"BE_stop",10,"29idcScienta:HV:sweepStopEnergy.VAL") + EAlist[1]=caget(pvCalcOut1+'.VAL') + EAlist[2]=caget(pvCalcOut2+'.VAL') + arrayP1=list(np.full(sweeps, EAlist[1])) + arrayP2=list(np.full(sweeps, EAlist[2])) + mda.fillin_table(pvCalcOut1+'.PROC',"",kwargs["scanIOC"],kwargs["scan_dim"],arrayP1,1) + mda.fillin_table(pvCalcOut2+'.PROC',"",kwargs["scanIOC"],kwargs["scan_dim"],arrayP2,2) + if kwargs['debug']: + print("\npvCalcOut1",pvCalcOut1) + print("\npvCalcOut2",pvCalcOut2) + print('Pos1 table:',arrayP1) + print('Pos2 table:',arrayP2) + EAlist[0]=='KE' + else: + _BE2KE_setupCalc(0,"",9,"") + _BE2KE_setupCalc(0,"",10,"") + + if kwargs['debug']: + print('/n EAlist => ',EAlist) + + #set up name and add HV trigger and FileNum as det scan1 (sweeps) + _scanEATrigger(EAlist,"before",**kwargs) + + if kwargs['debug']: + print("Clearing scan positioners and filling in sweeps") + #Fill in Sweeps scan + mda.positioners_clear(**kwargs) + VAL="" + RBV="" + mda.fillin(VAL,RBV,1,sweeps,1,**kwargs) + if kwargs['debug']: + scanPV="29id"+kwargs["scanIOC"]+":scan"+str(kwargs["scan_dim"]) + print("scanPV: "+scanPV) + + + + #Writing EA parameters + EAscanType, EAscanPV, KElist, EAparms =EA._spectraSetup(EAlist,**kwargs) + + if kwargs['debug']: + print("EA._spectraSetup: ") + print(EAscanType,EAscanPV, KElist) + + print(EA._spectraMessage(EAscanType, EAscanPV, KElist)) + #print(EA._EAspectraTime()) + + #executing the scan + if kwargs['execute']: + EA.put(EAparms['KineticEnergy'],EAparms['PassEnergy'],LensMode="Angular",Frames=EAparms['Frames'],**kwargs) + time.sleep(10) + EA.put(EAlist[1]-.05,EAlist[-3],LensMode="Angular") + time.sleep(2) + mda.go(**kwargs) + #After scan + EA_log_update() + scanEA_reset(**kwargs) + mda.table_reset_after(**kwargs) + else: + return EAparms + + + +def scanFM(RoughPositions,thList,EAlist,**kwargs): + """ + New FermiMap using ScanRecord table scans to move motors + RoughPositions is a List rough positions from which to interpolate (use RoughPositions_Find()) + thList=[th_start,th_stop,th_step] + EAlist to be finish only one scan at the moment[can be a single list if you are only taking a single scan or a list of lists to take multiple scans] + **kwargs + scanIOC = BL_ioc() + scan_dim = 1 + + logfile(**kwargs) + """ + kwargs.setdefault("scan_dim",2) + kwargs.setdefault("debug",False) + kwargs.setdefault('execute',True) + + # Making Tables and Filling positioners + x,y,z,th,chi,phi=interpRoughPositions(RoughPositions,thList[0],thList[1],thList[2]) + if kwargs['debug']: + print(x,y,z,th,chi,phi) + + mda.fillin_table(ARPES_motor_dictionary("th")[1],ARPES_motor_dictionary("th")[0],th,positioner_num=1) + mda.fillin_table(ARPES_motor_dictionary("x")[1],ARPES_motor_dictionary("x")[0],x,positioner_num=2) + mda.fillin_table(ARPES_motor_dictionary("y")[1],ARPES_motor_dictionary("y")[0],y,positioner_num=3) + mda.fillin_table(ARPES_motor_dictionary("z")[1],ARPES_motor_dictionary("z")[0],z,positioner_num=4) + + #setting up EA + EAkwargs={ + 'execute':False, + 'scan_dim':1 + } + EAparms=scanEA(EAlist,**EAkwargs) + + #executing the scan + if kwargs["execute"]==True: + print(EAparms) + EA.put(EAparms['KineticEnergy'],EAparms['PassEnergy'],LensMode="Angular",Frames=EAparms['Frames'],**kwargs) + mda.go(**kwargs) + EA_log_update() + scanEA_reset(**kwargs) + + +def interpRoughPositions(RoughPositions,thStart,thStop,thStep,**kwargs): + """ + Interpolate sample position as a function of theta, based on RoughPosition, + a list of motor position lists and returns x,y,z,th,chi,phi + + **kwargs: + kind="cubic" by default, interpolation type ("linear","cubic","quadratic") + + Usage: + x,y,z,th,chi,phi=interpRoughPositions(RoughPositions,3,-8,0.5) + + (direction to minimize backlash) + RoughPositions=[ + [x,y,z,th,chi,phi], + [x,y,z,th,chi,phi] + ] + """ + kwargs.setdefault('kind','cubic') + kwargs.setdefault('debug',False) + + #Puts Rough Position in increasing theta order + RoughPositions=sorted(RoughPositions, key=itemgetter(3))[::-1] + RoughPositions=np.array(RoughPositions) + if kwargs['debug']: + print('RoughPositions: ',RoughPositions) + + #thlist + if kwargs['debug']: + print('ths: ',thStart,thStop,thStep) + th=np.arange(np.max([thStart,thStop]),np.min([thStart,thStop])-1.0*abs(thStep),-1.0*abs(thStep),dtype=float) + if kwargs['debug']: + print('th: ',th) + + #interpolating + def func(th,th_col,m_col,**kwargs): + f=interp1d(th_col,m_col) + m=f(th) + return m + x=func(th,RoughPositions[:,3], RoughPositions[:,0]) + y=func(th,RoughPositions[:,3], RoughPositions[:,1]) + z=func(th,RoughPositions[:,3], RoughPositions[:,2]) + chi=func(th,RoughPositions[:,3], RoughPositions[:,4]) + phi=func(th,RoughPositions[:,3], RoughPositions[:,5]) + + return x,y,z,th,chi,phi + +def mvth_interp(RoughPositions, thVal,**kwargs): + """ + Moves to the interpolated position for a give theta and RoughPosition list + uses interpRoughPositions + """ + x,y,z,th,chi,phi=interpRoughPositions(RoughPositions,thVal,thVal-.1,1,**kwargs) + Pos=[x[0],y[0],z[0],th[0],chi[0],phi[0]] + #print("Pos = ",Pos) + ARPES_mvsample(Pos) + +def scanEA_hv(hv_start_stop_step_lists,EAlist=[],**kwargs): + """ + triggers and EAscan for each photon energy in *hvs + + hv_start_stop_step_lists, listoflist ... see scanXAS for more info + + EAlist = + Fixed Mode: EAlist=["KE/BE",CenterEnergy,PassEnergy,Frames,Sweeps] (5) + Swept Mode: EAlist=["KE/BE",StartEnergy,StopEnergy,StepEnergy,PassEnergy,Frames,Sweeps] (7) + Baby Sweep (dither): EAlist=["KE"/"BE",CenterEnergy,PassEnergy,Frames,Sweeps,"BS"] (6) + KE => Auger + BE => Core levels + (+) BE is positive below Ef + (-) BE is negative above Ef + + usage: + scanEA_hv([400,500,1200],EAlist=["BE",-5,200,17*60,1]) + NOTE that EAlist = needs to be written explicitly + + **kwargs + execute: True/False to start the scan => True (default) + debug: default => False + + (Note: scan_dim=2 is hardcoded for photon energy) + + """ + kwargs.setdefault("average",None) + kwargs.setdefault("execute",True) + kwargs.setdefault("debug",False) + + if(kwargs['debug']): + print('EAlist: ',EAlist) + + #setting up EA + EAkwargs={ + 'execute':False, + 'scan_dim':1 + } + EAparms=scanEA(EAlist,**EAkwargs) + + scanGo = kwargs['execute'] + #Setting up the ScanRecord for Mono and ID in Table mode + kwargs["scan_dim"]=2 + kwargs["execute"]=False + mono_array,ID_array = BL_energy_tables(hv_start_stop_step_lists) + scanXAS_BL(hv_start_stop_step_lists,**kwargs) + + if scanGo == True: + #Setting the beamline energy to the first point, and EA at first KE + energy(mono_array[0]) + #Scanning + EA.put(mono_array[0]-EAlist[1],EAparms['PassEnergy'],LensMode="Angular",Frames=EAparms['Frames'],**kwargs) + mda.go(**kwargs) + #After scan + scanEA_reset(**kwargs) + mda.table_reset_after(**kwargs) + +def scanEA_y(EAlist, start,stop,step,mode='absolute',**kwargs): + scanEA_motor(EAlist,'y',start,stop,step,mode=mode,**kwargs) + +def scanEA_z(EAlist, start,stop,step,mode='absolute',**kwargs): + scanEA_motor(EAlist,'z',start,stop,step,mode=mode,**kwargs) + + +def scanEA_motor(EAlist, motor,start,stop,step,mode='absolute',**kwargs): + """ + scans an ARPES motor (scan_dim=2) while triggering the EA (scan_dim=1) + + EAlist = + Fixed Mode:["BE",CenterEnergy,PassEnergy,Frames,Sweeps] (5) + Swept Mode:["BE",StartEnergy,StopEnergy,StepEnergy,PassEnergy,Frames,Sweeps] (7) + Baby Sweep (dither):["KE"/"BE",CenterEnergy,PassEnergy,Frames,Sweeps,"BS"] (6) + + (+) BE is positive below Ef + (-) BE is negative above Ef + + **kwargs + execute: True/False to start the scan => True (default) + debug=False + (Note: scan_dim=2 is hardcoded) + """ + kwargs.setdefault("execute",True) + kwargs.setdefault("debug",False) + + #setting up EA + EAkwargs={ + 'execute':False, + 'scan_dim':1 + } + EAparms=scanEA(EAlist,**EAkwargs) + + #Setting up the ScanRecord for motor scans + scan_dim=2 #hard coded + kwargs.update({'scan_dim':scan_dim}) + + ARPES_motor_scan(motor,start,stop,step,**kwargs) + + if kwargs['debug']: + print("ScanGo scan_dim = ",scan_dim) + + if kwargs["execute"]==True: + #Scanning + EA.put(EAlist[1],EAparms['PassEnergy'],LensMode="Angular",Frames=EAparms['Frames'],**kwargs) + BL.mda.go(kwargs["scanIOC"],scan_dim) + EA_log_update() + #After scan + scanEA_reset(**kwargs) + +def scanEA_Mesh(EAlist,y_start_stop_step,z_start_stop_step,**kwargs): + """ + 2D scan mesh (y/z) while triggering the EA: scan_dim=1 + y_start_stop_step=[start1,stop1,step1] #y: scan_dim=2 + z_start_stop_step=[start2,stop2,step2] #z: scan_dim=3 + + + EAlist = + Fixed Mode:["BE",CenterEnergy,PassEnergy,Frames,Sweeps] (5) + Swept Mode:["BE",StartEnergy,StopEnergy,StepEnergy,PassEnergy,Frames,Sweeps] (7) + Baby Sweep (dither):["KE"/"BE",CenterEnergy,PassEnergy,Frames,Sweeps,"BS"] (6) +1205 + (+) BE is positive below Ef + (-) BE is negative above Ef + + **kwargs + execute: True/False to start the scan => True (default) + debug=False + (Note: scan_dims are hardcoded) + """ + kwargs.setdefault("execute",True) + kwargs.setdefault("debug",False) + + #setting up EA + EAkwargs={ + 'execute':False, + 'scan_dim':1 + } + EAparms=scanEA(EAlist,**EAkwargs) + + #Setting up the ScanRecord for motor scans + outer_scan_dim=3 #hard coded + inner_loop_list = y_start_stop_step.insert(0,"y") + outer_loop_list = z_start_stop_step.insert(0,"z") + ARPES_scan_2D(inner_loop_list,outer_loop_list,outer_scan_dim,**kwargs) + + if kwargs['debug']: + print("ScanGo scan_dim = ",outer_scan_dim) + + if kwargs['execute']: + #Scanning + EA.put(EAlist[1],EAparms['PassEnergy'],LensMode="Angular",Frames=EAparms['Frames'],**kwargs) + mda.go(scan_dim=outer_scan_dim) + EA_log_update() + + #After scan + scanEA_reset(**kwargs) + + + + + +def hv2kz(lattice,V0,hv): + """ + Converts a hv value for the nth zone had returns corresponding kz values + [0]for zone boundary and [1] for zone center in inverse angstroms + lattice = c; assuming kz orthoganal to a-b plane (i.e. 2np.pi/c = GZ distance) + and at zone center (kx,ky)=0 /cos(th=0)=1 + V0 = the inner potential + """ + work_fct=EA.wk + Ek=hv-work_fct + k_z=0.5124*np.sqrt(Ek+V0) # Kz at (kx,ky)=0 i.e. cos(th)=1 + c_star=2*np.pi/lattice # 2pi/c = GZ distance + GZ_n=round((k_z/c_star*2),2) + G_n=round((k_z/c_star),2) + print(" kz = "+str(round(k_z,2))+" A^(-1) = " +str(GZ_n)+" * pi/c = " +str(G_n)+" * 2pi/c") + return GZ_n,G_n + +def kz2hv(lattice,V0,n): + """ + Converts a kz value for the nth zone had returns corresponding hv + lattice = c; assuming kz orthoganal to a-b plane (i.e. 2pi/c = GZ distance) + and at zone center (kx,ky)=0 /cos(th=0)=1 + V0 = the inner potential + """ + work_fct=EA.wk + c_star=2*np.pi/lattice # 2pi/c = GZ distance + Ek=(n*c_star/0.5124)**2-V0 # Ek at (kx,ky)=0 i.e. cos(th)=1 + hv=Ek+work_fct + mono=round(hv,1) + print("\n") + print(" hv = Ek + Phi = "+str(round(hv,2))+" eV") + print(" kz = n*2pi/c with n = "+str(n)) + return mono + + +def Print_Gamma_n(lattice,V0,n1,n2): + work_fct=EA.wk + c_star=2*np.pi/lattice # 2pi/c = GZ distance + for n in range(n1,n2+1,1): + Ek_Gn=(n*c_star/0.5124)**2-V0 # Ek at G + Ek_Zn=((n+0.5)*c_star/0.5124)**2-V0 # Ek at Z + hv_Gn=round(Ek_Gn+work_fct,2) + hv_Zn=round(Ek_Zn+work_fct,2) + print("\n G["+str(n)+"]: hv = Ek + Phi = "+str(round(hv_Gn,2))+" eV ") + print(" Z["+str(n)+"]: hv = Ek + Phi = "+str(round(hv_Zn,2))+" eV") + +def Print_Gamma_hv(lattice,V0,hv1,hv2): + work_fct=EA.wk + c_star=2*np.pi/lattice # 2pi/c = GZ distance + Ek1=hv1-work_fct + Ek2=hv2-work_fct + k_z1=0.5124*np.sqrt(Ek1+V0) # Kz at (kx,ky)=0 i.e. cos(th)=1 + k_z2=0.5124*np.sqrt(Ek2+V0) # Kz at (kx,ky)=0 i.e. cos(th)=1 + GZ_n1=round((k_z1/c_star*2),1) + G_n1=round((k_z1/c_star),1) + GZ_n2=round((k_z2/c_star*2),1) + G_n2=round((k_z2/c_star),1) + if modf(G_n1)[0]>=0.5: + n1=modf(G_n1)[1]+1,0 + else: + n1=modf(G_n1)[1] + n2=modf(G_n2)[1] + print("\n hv1 = "+str(hv1)+" eV: " +str(GZ_n1)+" * pi/c = " +str(G_n1)+" * 2pi/c") + if type(n1) == tuple: n1 = n1[0] + if type(n2) == tuple: n2 = n2[0] + Print_Gamma_n(lattice,V0,n1,n2) + print("\n hv2 = "+str(hv2)+" eV: " +str(GZ_n2)+" * pi/c = " +str(G_n2)+" * 2pi/c") + return n1,n2 + +def kx2deg(lattice,hv): + a=np.pi/lattice + b=0.5124*np.sqrt(hv) + c=a/b + theta_rad=np.asin(c) + theta_deg=rad2deg(theta_rad) + print(" 1/2-BZ (GX) = "+str(round(theta_deg,1))+" deg") + +def resolution_EA(PE,slit_SES): # updated 10/30/17: straight slits scaled to slit width not area + SES_Table={} + SES_Table[2] = {1: 1.6, 2:2, 3:2, 4:4, 5:4, 6:6, 7:12, 8:20, 9:32} + SES_Table[5] = {1: 2.7, 2:4, 3:4, 4:7, 5:7, 6:11, 7:20, 8:34, 9:54} + SES_Table[10] = {1: 5.7, 2:9, 3:9, 4:14, 5:14, 6:23, 7:43, 8:71, 9:114} + SES_Table[20] = {1:10.8, 2:16, 3:16, 4:27, 5:27, 6:43, 7:81, 8:135, 9:216} + SES_Table[50] = {1:34.6, 2:52, 3:52, 4:87, 5:87, 6:138, 7:260, 8:433, 9:692} + SES_Table[100] = {1:49.5, 2:74, 3:74, 4:124, 5:124, 6:198, 7:371, 8:619, 9:990} + SES_Table[200] = {1:88.9, 2:133, 3:133, 4:222, 5:222, 6:356, 7:667, 8:1111, 9:1778} + SES_Table[500] = {1:250, 2:375, 3:375, 4:625, 5:625, 6:1000, 7:1875, 8:3125, 9:5000} + try: + SES=SES_Table[PE][slit_SES] + except KeyError: + print("WARNING: Not a valid PE/Slit combination") + SES=0 + return SES + + +def SES_slit(val): + """ + Set the Scienta Slit + val w_mm l_mm Shape + 1 0.2 25 Curved + 2 0.3 30 Straight + 3 0.3 25 Curved + 4 0.5 30 Straight + 5 0.5 25 Curved + 6 0.8 25 Curved + 7 1.5 30 Straight + 8 2.5 30 Straight + 8.5 open apperture + 9 4.0 30 Straight + """ + caput("29idc:m8.VAL",val,wait=True,timeout=18000) + + diff --git a/build/lib/iexcode/instruments/encoders.py b/build/lib/iexcode/instruments/encoders.py new file mode 100644 index 0000000..ef7cb08 --- /dev/null +++ b/build/lib/iexcode/instruments/encoders.py @@ -0,0 +1,62 @@ +from epics import caput,caget + +def encoder_dictionary_entry(name): + """ + returns a dictionary with the encoder pvs + encoder_dictionary_entry(name) = encoder_ioc, motor_ioc, encoder_list + + """ + d={ + 'slit2B':("29idMini1:",[13,14,15,16]), + 'slit3D':("29idMini2:",[26,27]), + 'ARPES':("ARPES:",[1,2,3,4]), + } + return d[name] + +def encoder_sync(name): + """ + sync all the encoders for name + """ + encoder_ioc, motor_ioc, encoder_list = encoder_dictionary_entry(name) + for encoder_num in encoder_list: + pv = motor_ioc+":m"+str(encoder_num)+".SYNC" + caput(pv,1) + +def encoders_reset(name,Hcenter,Vcenter): + """ + Resetting Slit 2B encoders to 0 for a given position (Hcenter,Vcenter). + Slit size need to be set to 0. + + Previously: Reset_Slit2B_Encoders + """ + encoder_sync('name') + encoder_ioc, motor_ioc, encoder_list = encoder_dictionary_entry(name) + + print('\nCurrent Position:') + for e_num in encoder_list: + pv = encoder_ioc+'e'+(e_num)+'Pos' + print('e'+(e_num)+' = '+str(caget(pv))) + + print('\nSetting all Offsets to 0:') + for e_num in encoder_list: + pv = encoder_ioc+'e'+(e_num)+'Pos.EOFF' + caput(pv,0) + + print('\nCurrent Position:') + for e_num in encoder_list: + pv = encoder_ioc+'e'+(e_num)+'Pos' + print('e'+(e_num)+' = '+str(caget(pv))) + + print('\nSetting back Offsets:') + for e_num in encoder_list: + pos_pv = encoder_ioc+'e'+(e_num)+'Pos' + offset_pv = encoder_ioc+'e'+(e_num)+'Pos.EOFF' + caput(offset_pv,-caget(pos_pv)) + + print('\nCurrent Position:') + for e_num in encoder_list: + pv = encoder_ioc+'e'+(e_num)+'Pos' + print('e'+(e_num)+' = '+str(caget(pv))) + + encoder_sync(name) + print('sync '+name) diff --git a/build/lib/iexcode/instruments/files_and_folders.py b/build/lib/iexcode/instruments/files_and_folders.py new file mode 100644 index 0000000..9710c1b --- /dev/null +++ b/build/lib/iexcode/instruments/files_and_folders.py @@ -0,0 +1,268 @@ +from os import listdir,mkdir,chmod +from os.path import join, isfile, exists +import datetime +from time import sleep +import re + +from epics import caget, caput + + +############################################################################################################## +################################ Standard Paths ############################## +############################################################################################################## + + +path_User_Folders='/home/beams/29IDUSER/Documents/User_Folders/' + +def path_dserv(folder,run,user_name): + """ + Returns the path to a user folder + dataFolder='/net/s29data/export/data_29id'+folder+'/'+run+'/'+user_name + + previously: _userDataFolder + """ + + dataFolder='/net/s29data/export/data_29id'+folder+'/'+run+'/'+user_name + return dataFolder + + + + + + + +############################################################################################################## +################################ Folders ############################## +############################################################################################################## +def check_run(): + """ + gets the current date and determines the run number from that + run = year_cycle + cycle 1 => Jan - April + cycle 2 => May - September + cycle 1 => October - December + + previously: Check_run + """ + todays_date = datetime.date.today() + + date1 = ( 1, 1) # start date run_1 + date2 = ( 5, 1) # start date run_2 + date3 = (10, 1) # start date run_3 + + datex = (todays_date.month,todays_date.day) + + if date1 <= datex < date2: + run=str(todays_date.year)+'_1' + elif date2 <= datex < date3: + run=str(todays_date.year)+'_2' + else: + run=str(todays_date.year)+'_3' + + return(run) + +def make_ftp(folder,run,user_name): + """ + Creates the folders on kip (ftp server) and prints what is needed for the cronjob + + folder = 'c' /'d' + + Previously: was part of Make_DataFolder + """ + crontime={ + 'mda2ascii':'0,30 * * * * ', + 'chmod':'1,31 * * * * ', + 'data_other':'2,32 * * * * ', + 'notebook':'*/3 * * * * ', + } + + #making the crontab text + print('-------------------------------------------------------------') + #mda2ascii + MyPath_kip_run='/net/kip/sftp/pub/29id'+folder+'ftp/files/'+run+'/' + MyPath_kip='/net/kip/sftp/pub/29id'+folder+'ftp/files/'+run+'/'+user_name+'/' + cmd_mda2ascii=crontime['mda2ascii']+' /net/s29dserv/APSshare/bin/mda2ascii -d '+MyPath_kip+'ascii '+MyPath_kip+'mda/*.mda' + print(cmd_mda2ascii) + #chmode + cmd_chmod=crontime['chmod']+' chmod 664 '+MyPath_kip+'ascii/*.asc' + print(cmd_chmod) + #notebooks + cmd_notebook=crontime['notebook']+' /usr/bin/rsync -av --exclude=core /home/beams22/29IDUSER/Documents/User_Folders/'+user_name+'/* kip:'+MyPath_kip+'notebook > /home/beams22/29ID/cronfiles/cptoftp-currrun-d-User.log 2>&1' + print(cmd_notebook) + print('-------------------------------------------------------------\n\n') + + #making folders + print("\n\n") + print(MyPath_kip) + print(MyPath_kip+"ascii") + if not (exists(MyPath_kip_run)): + mkdir(MyPath_kip_run) + chmod(MyPath_kip_run, 0o775) + if not (exists(MyPath_kip)): + mkdir(MyPath_kip) + chmod(MyPath_kip, 0o775) + if not (exists(MyPath_kip+"ascii")): + mkdir(MyPath_kip+'ascii') + chmod(MyPath_kip+'ascii', 0o775) + if not (exists(MyPath_kip+"notebook")): + mkdir(MyPath_kip+"notebook") + chmod(MyPath_kip+"notebook", 0o775) + +def make_user_folders(run,folder,user_name,endstation_name,ftp=False): + """ + if user_name is not a folder in path_User_Folders, then it creates a new folder + + if ftp = True / False (default) creates the folders on kip (ftp server) and modifies the cronjob + + previously: Make_DataFolder + """ + + if (folder == 'c'or folder == 'd'): + if ftp: + make_ftp(folder,run,user_name) + else: + print("To create ftp folders & update contrab, you need to run the following as 29id:") + print("\tFolder_"+str(endstation_name)+"('"+str(run)+"','"+str(user_name)+"',ftp=True)") + + #create user folder for saving log files + path_user=join(path_User_Folders,user_name) + user_name = "/"+user_name + if not (exists(path_user)): + mkdir(path_user) + + +def folder_mda(run,folder,user_name,file_prefix,ioc,verbose=True): + """ + sets the folder, prefix and file number for the scan record + ioc is the full ioc, e.g. '29idb', or '29idARPES' + + For Staff: folder='b', user_name='' + For ARPES: folder ='c' + For Kappa: folder = 'd' + + mda_path="/net/s29data/export/data_29id"+folder+"/"+run+"/"+user_name+"mda" + """ + + if user_name == 'Staff': + user_name="" + else: + user_name=user_name+"/" + + mda_path="/net/s29data/export/data_29id"+folder+"/"+run+"/"+user_name+"/mda" + print("\nMDA folder: " + mda_path) + if not (exists(mda_path)): + mkdir(mda_path) + FileNumber=1 + else: + FileNumber=get_next_fileNumber(mda_path,file_prefix) + + if ioc in ['29idb','29idc','29idd']: #VME iocs + caput(ioc+"saveData_fileSystem","//s29data/export/data_29id"+folder+"/"+run) + sleep(0.25) + caput(ioc+"saveData_subDir",user_name+"mda") + + else: #soft iocs + caput(ioc+"saveData_fileSystem","/net/s29data/export/data_29id"+folder+"/"+run) + sleep(0.25) #needed so that it has time to write + caput(ioc+"saveData_subDir","/"+user_name+"mda") + + caput(ioc+"saveData_baseName",file_prefix+"_") + caput(ioc+"saveData_scanNumber",FileNumber) + + print("\nioc set to:", ioc) + sleep(5) + + if verbose: + SaveStatus=caget(ioc+'saveData_status',as_string=True) + SaveMessage=caget(ioc+'saveData_message',as_string=True) + print("\nSave Status "+ioc+": "+SaveStatus+" - "+SaveMessage) + + +def folder_SPEC(run,folder,user_name): + if folder == "b": + user_name = "Staff" + else: + user_name = user_name+"/" + MyPath="/home/beams22/29IDUSER/spec/data/"+run+"/"+user_name + print("\nSPEC folder: " + MyPath) + print("You will need to create folder and set up the path manually in SPEC:") + print(" cd "+MyPath) + print(" newfile FileName") + print("To start SPEC fresh: ./bin/kappa29ID -f") + #if not (exists(MyPath)): + # mkdir(MyPath) + + +def folder_MPA(run,folder,user_name,file_prefix="mpa"): + if folder == "b": + windows_ioc = "X" + user_name = "" + windows_path = windows_ioc+':\\'+run+"\\mpa" + else: + windowsIOC = "Z" + user_name = user_name+"/" + windows_path=windows_ioc+':\\'+run+"\\"+user_name[:-1]+"\\mpa" + mpa_path="/net/s29data/export/data_29id"+folder+"/"+run+"/"+user_name+"mpa" + print("\nMPA folder: " + mpa_path) + if not (exists(mpa_path)): + mkdir(mpa_path) + FileNumber=1 + else: + FileNumber=get_next_fileNumber(mpa_path,file_prefix) + caput("29iddMPA:det1:MPAFilename",windows_ioc+":/"+run+"/"+user_name+"mpa/mpa_") + + save_plugin='TIFF1' + caput("29iddMPA:"+save_plugin+":FilePath",windows_path) + print("\nMPA folder on Crabby: "+windows_path) + caput("29iddMPA:"+save_plugin+":FileName",file_prefix) + caput("29iddMPA:"+save_plugin+":FileNumber",FileNumber) + +def _filename_key(filename): + return (len(filename), filename) + +def get_next_fileNumber(data_dir, file_prefix,**kwargs): + """ + gets the next file number for the pattern + data_dir/file_prefix_filenum + + kwargs: + debug = False (default); if True then print lo + q = True (default); if False then prints next file number + + Previously: getNextFileNumber + """ + kwargs.setdefault("debug",False) + kwargs.setdefault("q",True) + + onlyfiles = [f for f in listdir(data_dir) if isfile(join(data_dir, f)) and f[:len(file_prefix)] == file_prefix] + sortedfiles = sorted(onlyfiles, key=_filename_key) + pattern = re.compile('(.*)_(.*)\.(.*)') + try: + lastFile = sortedfiles[-1] + except IndexError as errObj: + nextFileNumber = 1 + if kwargs["debug"]: + print("Data directory = ", data_dir) + print("File prefix =", file_prefix) + print("File number =", None) + print("File extension =", "TBD") + print("Next File number =", nextFileNumber) + else: + matchObj = pattern.match(lastFile) + nextFileNumber = int(matchObj.group(2)) + 1 + if kwargs["debug"]: + print("Data directory = ", data_dir) + print("File prefix =", matchObj.group(1)) + print("File number =", matchObj.group(2)) + print("File extension =", matchObj.group(3)) + print("Next File number =", nextFileNumber) + if kwargs["q"] == False: + print("Next File Number: ",nextFileNumber) + return nextFileNumber + + + + + + + diff --git a/build/lib/iexcode/instruments/gate_valves.py b/build/lib/iexcode/instruments/gate_valves.py new file mode 100644 index 0000000..1e122c5 --- /dev/null +++ b/build/lib/iexcode/instruments/gate_valves.py @@ -0,0 +1,35 @@ +from epics import caget, caput + +def branch_valves(): + """ + returns a dictionary of the gate valves for a given branch + + used by close_valve and safe_state + """ + + GVs={ + 'c':('GV10'), + 'd':('GV14'), + 'e':() + } + return GVs + +def valve_close(GV, verbose=True): + """ + closes the gate valve ('GV10', 'GV14'), EPS nomenclature + + Previously: Close_DValve,Close_DValve + """ + caput("29id:BLEPS:"+GV+":CLOSE.VAL",1,wait=True,timeout=18000) + if verbose: + print("Closing gate valve: "+GV+"...") + +def valve_open(GV, verbose=True): + """ + closes the gate valve ('GV10', 'GV14'), EPS nomenclature + + Previously: Close_DValve,Close_DValve + """ + caput("29id:BLEPS:"+GV+":OPEN.VAL",1,wait=True,timeout=18000) + if verbose: + print("Opening gate valve: "+GV+"...") \ No newline at end of file diff --git a/build/lib/iexcode/instruments/hxp_mirrors.py b/build/lib/iexcode/instruments/hxp_mirrors.py new file mode 100644 index 0000000..0eb9050 --- /dev/null +++ b/build/lib/iexcode/instruments/hxp_mirrors.py @@ -0,0 +1,64 @@ +from re import M +from epics import caput, caget +from iexcode.instruments.utilities import print_warning_message +from iexcode.instruments.m3r import m3r_branch + +def hxp_ioc(mirror_name): + """ + returns the ioc for a given + mirror_name: 'm3a' / 'm4a' / 'm4r' + """ + hxp_namelist={ + 'm4r':'hxp2', + 'm3a':'hxp1', + 'm4a':'hxp3', + } + if mirror_name not in hxp_namelist: + print("not a valid mirror name ("+list(hxp_namelist.keys())+")") + else: + ioc = '29idHXP:'+hxp_namelist[mirror_name]+":" + return ioc + + +def HXP_synch(mirror_name): + """ + synchs the rbv and sp for all axes + + mirror_name: 'm3a' / 'm4a' / 'm4r' + + Previously: Sync_m4r + """ + pv = hxp_ioc(mirror_name) + try: + for m in range(1,7): + caput(pv+'m'+str(m)+'.SYNC',1) + except: + print_warning_message("Check if ioc is running") + + +def hxp_print(mirror_name): + """ + + Previously: Get_HXP + """ + pv = hxp_ioc(mirror_name) + + Tx=caget(pv+"m1.RBV") + Ty=caget(pv+"m2.RBV") + Tz=caget(pv+"m3.RBV") + Rx=caget(pv+"m4.RBV") + Ry=caget(pv+"m5.RBV") + Rz=caget(pv+"m6.RBV") + print("\n"+mirror_name+" @ "+"%.3f" % Tx, "/","%.3f" % Ty, "/","%.3f" % Tz, "/","%.3f" % Rx, "/","%.3f" % Ry, "/","%.3f" % Rz) + +def hxp_get(): + """ + + Previously: Get_HXP + """ + branch = m3r_branch() + if branch == 'c': + hxp_print('m3a') + hxp_print('m4a') + else: + hxp_print('m4r') \ No newline at end of file diff --git a/build/lib/iexcode/instruments/logfile.py b/build/lib/iexcode/instruments/logfile.py new file mode 100644 index 0000000..dec2ce2 --- /dev/null +++ b/build/lib/iexcode/instruments/logfile.py @@ -0,0 +1,169 @@ +from os.path import join,isfile + +from epics import caget, caput +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.utilities import today +from iexcode.instruments.ARPES import ARPES_log_entries +from iexcode.instruments.Kappa import Kappa_log_entries + + +############################################################################################################## +############################## logging ############################## +############################################################################################################## +def log_print(**kwargs): + """ + prints a comment to the logfile + """ + logfile_print(BL.endstation_name,BL.ioc,kwargs['comment']) + +def log_update(): + """ + updates the log file with the last scan info + """ + if BL.endstation_name == 'ARPES': + entry_list,pv_list, format_list = ARPES_log_entries() + elif BL.endstation_name == 'Kappa': + entry_list,pv_list, format_list = Kappa_log_entries() + logfile_update(BL.endstation_name,BL.ioc,entry_list,pv_list,format_list) + + +############################################################################################################## +############################## general logfile functions ############################## +############################################################################################################## + + +def logfile_name_pv(endstation_name): + """ + Dictionary to get the PV in which the FileName for logging is stored + + endstation: 'ARPES' / 'Kappa' / 'Test + + Previously: logname_pv + """ + pv_dict={ + 'Test':'29idb:userStringSeq10.STR1', + 'ARPES':'29idb:userStringSeq10.STR2', + 'Kappa':'29idb:userStringSeq10.STR3', + 'RSoXS':'29idb:userStringSeq10.STR4', + } + if endstation_name in pv_dict.keys(): + pv=pv_dict[endstation_name] + else: + pv='29idb:userStringSeq10.STR10' + return pv + +def logfile_name_set(endstation_name,**kwargs): + """ + Sets the string used for the FileName in scanlog and EAlog + uses logname_PV to reference PV associated with a particular ioc + **kwargs: + filename = to set a specific filename, default => YYYYMMDD_log.txt + + Previously: logname_set + """ + kwargs.setdefault('filename_suffix','_log') + kwargs.setdefault('filename',today()+kwargs['filename_suffix']+'.txt') + + filename = kwargs['filename'] + + try: + caput(logfile_name_pv(endstation_name),filename) + print("\nLog filename = \'"+filename+"\' @ "+logfile_name_pv(endstation_name)) + print('To change filename, use logname_set(endstation_name,new_name)') + except: + print("Error: was not able to set the filename, check that 29idb ioc is running") + + +def logfile_name_get(endstation_name): + """ + Gets the string used for the FileName in scanlog and EAlog for the given scanIOC. + + Previously: logname + """ + + return caget(logfile_name_pv(endstation_name)) + +def logfile_fpath(endstation_name,mda_ioc): + """ + returns the full path to the logfile based on the current user in the mda scanRecord + + + Previously: logname, logname_generate + """ + + try: + filename = logfile_name_get(endstation_name) + user_name = BL.mda.scanRecord_user(mda_ioc) + fpath_with_subfolder = join(user_name,filename) + except: + fpath_with_subfolder = "logfile.txt" + print("Couldn't read log file path, using: "+fpath_with_subfolder) + + return fpath_with_subfolder + + +def logfile_print(endstation_name,mda_ioc,comment=''): + """ + Writes comments to the logfile on a new line + comment is a string of what you want to add to the file + e.g. comment = "------- New Sample--------" + + Previously: logprint + """ + + logfile_name = logfile_name_get(endstation_name) + + try: + fpath_with_subfolder=logfile_fpath(endstation_name,mda_ioc) + if not isfile(fpath_with_subfolder): + logfile_header(fpath_with_subfolder,entry=None) + with open(fpath_with_subfolder,'a') as myfile: + myfile.write(comment) + myfile.write('\n') + #print(comment,FileNameWithSubFolder) + except: + print("Logprint failed") + + +def logfile_header(endstation_name,mda_ioc,header_list): + """ + file path = path to where file_name lives + file_name = name of the logfile + entry is a list + + Previously: SaveFile_Header + """ + version = '1.4' + + fpath_with_subfolder=logfile_fpath(endstation_name,mda_ioc) + with open(fpath_with_subfolder, "w+") as f: + f.write('@Log version: '+ version+'\n\n') + file_path = BL.mda.scanRecord_filepath(mda_ioc) + f.write('FilePath '+endstation_name+': '+file_path+'\n') + + for key in header_list.keys(): + f.write(key+' Header: '+ header_list[key]+'\n\n') + + + +def logfile_update(endstation_name,mda_ioc,entry_list,pv_list,format_list): + """ + To be used for scanlog and scanEA functions. + Update SaveFile_Header version number when changing the structure of the file (indexing). + + Previously: SaveFile + """ + fpath_with_subfolder=logfile_fpath(endstation_name,mda_ioc) + + if not isfile(fpath_with_subfolder): + logfile_header(endstation_name,mda_ioc,entry_list) + + with open(fpath_with_subfolder, "a+") as f: + for i in range(len(format_list)): + pv_format = "{0:"+format_list[i]+"}," + f.write(pv_format.format(pv_list[i])) + last_entry=len(format_list)-1 + pv_format="{0:"+format_list[last_entry]+"}\n" + f.write(pv_format.format(pv_list[last_entry])) + + diff --git a/build/lib/iexcode/instruments/m3r.py b/build/lib/iexcode/instruments/m3r.py new file mode 100644 index 0000000..54bb5ec --- /dev/null +++ b/build/lib/iexcode/instruments/m3r.py @@ -0,0 +1,126 @@ +from cgi import print_arguments + +from epics import caget, caput + +from iexcode.instruments.utilities import print_warning_message +from iexcode.instruments.FMB_mirrors import * + +############################################################################################################## +################################ M3R ############################## +############################################################################################################## +M3R_RY_POS_SP_initial = -16.52 +M3R_camera = '29id_ps6:' +M3R_cam1 = M3R_camera+"cam1:" +M3R_stats = M3R_camera+"Stats1:" +M3R_pitch = "29id_m3r:RY_MON" +M3R_align_pv = "29idKappa:align_m3r:" + +############################################################################################################## +################################ M3R default positions ############################## +############################################################################################################## +def m3r_get(verbose=True): + position = FMB_mirror_get(3,verbose) + return position + +def m3r_RY_pos_sp(): + return M3R_RY_POS_SP_initial + +def m3r_RY_pos_sp_get(): + RY_SP = caget('29id_m3r:RY_POS_SP') + return RY_SP + +def m3R_tweak (axis,val): + """ + \"TX\" = lateral \"RX\" = Yaw + \"TY\" = vertical \"RY\" = Pitch + \"TZ\" = longitudinal \"RZ\" = Roll + + Previously: Tweak_M3R + """ + mirror_num = 3 + FMB_mirror_tweak(mirror_num,axis,val,verbose=True) + +def m3r_tweak_pitch(sign,val=0.005): + """ + Previously: Tweak_M3R_Pitch + """ + axis="RY" + if sign == "+": + m3R_tweak(axis,val) + elif sign == "-": + m3R_tweak(axis,-val) + + +def m3r_table(branch): + """ + Defines the positions used by switch_branch() form M3R + WARNING: branch_pv uses: D => (Tx <= 0) and (Ry < 0) - Make sure this remains true or change it + """ + branch = branch.lower() + positions={ + "c":[10,0,0,0,0,0], + "d":[-2.5,0,0,-13.955,-16.450,-5.15], + "e":[-2.000,0,0,-13.960,-16.614,-7.500] + } + # d => optimized for MEG @ 500 eV on 2/29/def start + # e => 2018_3-- JM changed for RSoXS alignment max diode current + + try: + position = positions[branch] + except KeyError: + print_warning_message("not a valid branch") + position = FMB_mirror_get(3,verbose=False) + return position + +def m3r_branch(): + """ + get which branch based on the mirror position and returns the branch + """ + PV="29id_m3r:" + TX=caget(PV+"TX_MON") + if TX > 6: + branch = 'c' + elif TX < -1: + branch = 'd' + else: + print('M3R is not in a default position') + return branch + +def m3r_switch_branch(branch): + """ + switches the mirror to the position defined in m3r_table + """ + position = m3r_table(branch) + FMB_mirror_move_all(3,position,verbose=False) + # Relax bellows by doing large Z translation: + FMB_mirror_move_all(3,"TY",5,verbose=False) + FMB_mirror_move_all(3,"TY",5,verbose=False) + + FMB_mirror_get(3,verbose=True) + +############################################################################################################## +################################ M3R alignment ############################## +############################################################################################################## + +def centroid(t=None,q=1): + ''' + Return position of centroid, sigma, m3r:RY (mirror pitch) + Optional argument t to set camera intergration time. + ''' + if t is not None: + caput(M3R_cam1+'AcquireTime',t) + else: + t=caget(M3R_cam1+'AcquireTime') + position = round(caget(M3R_stats+'CentroidX_RBV'),2) + sigma = round(caget(M3R_stats+'SigmaX_RBV'),2) + intensity = round(caget(M3R_stats+'CentroidTotal_RBV'),2) + m3rRY = round(caget(M3R_pitch),4) + if q != None: + print('(position, sigma, total intensity, integration time (s), mirror pitch):') + return position,sigma,intensity,t,m3rRY + +def m3r_align(pxl=None): + if pxl == None: + pxl = caget(M3R_align_pv+'desired_pixel') + caput(M3R_align_pv+'desired_pixel',pxl) + caput(M3R_align_pv+'startAlign',1,wait=True,timeout=180) diff --git a/build/lib/iexcode/instruments/mpa.py b/build/lib/iexcode/instruments/mpa.py new file mode 100644 index 0000000..6408b46 --- /dev/null +++ b/build/lib/iexcode/instruments/mpa.py @@ -0,0 +1,440 @@ +from time import sleep +from os.path import dirname, join +import socket + +from epics import caget, caput +from iexcode.instruments.userCalcs import userCalcOut_clear,userStringSeq_clear +from iexcode.instruments.Kappa import Kappa_motor_dictionary,Kappa_cts, mda +from iexcode.instruments.AD_utilities import AD_ROI_setup +from iexcode.instruments.scalers import scaler_cts +from iexcode.instruments.scanRecord import * + + +""" +To do, get busy record in the mpa ioc, get user calcs in the mpa ioc + + +""" +############################################################################################################## +############################## PVs ############################## +############################################################################################################## +mpa_pv = "29iddMPA:" +mpaDet_pv = mpa_pv + "det1:" +mpa_busy = "29idcScienta:mybusy2" +userCalc_pv = "29idTest" +DAC_pv = '29iddau1:dau1:011:DAC' +max_HV = 2990 +ratio = 500 +tth_dLLM=13.73 +tth_dHLM=23.73 + + +def mpa_reset_pv(): + """ + returns the pv used for resetting the mpa + "writing a 1 resets" + """ + return mpa_pv+'C1O' + +def mpa_on_off_pv(): + """ + returns the pv used for turning on/off the mpa + on => caput 1 + off => caput0 + """ + return mpa_pv+'C0O' + +def mpa_HV_pvs(): + """ + returns the pvs for the userCalcout which hold the pvs for the mpa high voltage + """ + val_pv = "29idKappa:userCalcOut9.A" + rbv_pv ="29idKappa:userCalcOut10.OVAL" + return val_pv, rbv_pv + +############################################################################################################## +############################## HV Control ############################## +############################################################################################################## + +def mpa_HV_set(volt,verbose=True): + """ + sets the high voltage for the mpa + + Previously: MPA_HV_Set + """ + val_pv, rbv_pv = mpa_HV_pvs() + volt=min(volt,2990) + + caput(val_pv,volt,wait=True,timeout=18000) + sleep(1) + HV_rbv=caget(rbv_pv) + + if verbose: + print("HV = "+str(HV_rbv)+" V") + + +def mpa_HV_get(): + """ + sets the high voltage for the mpa + """ + val_pv, rbv_pv = mpa_HV_pvs() + + return caget(rbv_pv) + + +def mpa_HV_on(): + """ + turns the mpa high voltage on + + Previously: MPA_HV_ON + """ + n_on=1 + tth_pv = Kappa_motor_dictionary('tth')[3] + tth_dial = caget(tth_pv+'.DRBV') + if 13.73<= tth_dial <=23.73: + print('MPA OFF: detector in direct beam (-5 < tth for mcp < 5); move away before turning HV ON.') + else: + caput(mpa_reset_pv,1,wait=True,timeout=18000) + caput(mpa_reset_pv,0,wait=True,timeout=18000) + caput(mpa_on_off_pv,n_on,wait=True,timeout=18000) + print("MPA - HV On") + +def mpa_HV_off(): + """ + turns the mpa high voltage off + + Previously: MPA_HV_OFF + """ + n_off=0 + caput(mpa_on_off_pv,wait=True,timeout=18000) + print("MPA - HV Off") + + +def mpa_HV_reset(): + """ + resets the mpa high voltage + + Previously: MPA_HV_Reset + """ + caput(mpa_reset_pv,1) + print("MPA - Reset") + + +def mpa_HV_scan(start=2400,stop=2990,step=10,**kwargs): + """ + + Previously: MPA_HV_scan + """ + kwargs.setdefault('positioner_settling_time',1) + Kappa_cts(1) + val_pv, rbv_pv = mpa_HV_pvs() + mda.fillin(val_pv, rbv_pv,start,stop,step,**kwargs) + mda.go(**kwargs) + +############################################################################################################## +############################## MCP Scripts ############################## +############################################################################################################## +def mpa_ROI_setup(ROI_num=1,xcenter=535,ycenter=539,xsize=50,ysize=50,binX=1,binY=1): + """ + usage: + center of MCP, roiNum = 1 => MPA_ROI_SetUp(1,535,539,50,50) + to set up all use: mpa_ROI_setup_all(xcenter,ycenter) + """ + AD_ROI_setup('29iddMPA',ROI_num,xcenter,ycenter,xsize,ysize,binX,binY) + ROI_pv = mpa_pv+"ROI"+str(ROI_num)+':' + mpa_ROI_stats(ROI_num) + +def mpa_ROI_setup_all(xcenter=535,ycenter=539): + """ + setup up ROI + 1 => size = 50 x 50 + 2 => size = 100 x 100 + 3 => size = 150 x 150 + 4 => size = 200 x 200 + + """ + mpa_ROI_setup(1,xcenter,ycenter,xsize=50,ysize=50) + mpa_ROI_setup(2,xcenter,ycenter,xsize=100,ysize=100) + mpa_ROI_setup(3,xcenter,ycenter,xsize=150,ysize=150) + mpa_ROI_setup(4,xcenter,ycenter,xsize=200,ysize=200) + + +def mpa_ROI_stats(ROI_num): + """ + sequence to enable stats for mpa ROI + """ + ROI_pv=mpa_pv+"ROI"+str(ROI_num)+':' + stats_pv=mpa_pv+"Stats"+str(ROI_num)+':' + caput(stats_pv+'NDArrayPort','ROI'+str(ROI_num)) + caput(stats_pv+'EnableCallbacks','Enable') + caput(stats_pv+'ArrayCallbacks','Enable') + caput(stats_pv+'ComputeStatistics','Yes') + caput(stats_pv+'ComputeCentroid','Yes') + caput(stats_pv+'ComputeProfiles','Yes') + +def _mpa_trigger_calcOut(**kwargs): + """ + writes strSeq and calcOut for MPA this should go into the IOC + + **kwargs: + ADplugin = 'TIFF1:' (default) + """ + + kwargs.setdefault("ADplugin","TIFF1:") + kwargs.setdefault("save_image",False) + + ADplugin = mpa_pv + kwargs["ADplugin"] + Proc1 = mpa_pv + "Proc1:" + + #All this should moved into the MPA IOC + Calcs_mda = ScanRecord("29idTest:") + + desc = "MPA busy" + n=9 + busy_CalcOut = userCalcOut_clear(Calcs_mda,n) + caput(busy_CalcOut+".DESC",desc) + caput(busy_CalcOut+".INPA",mpa_busy+" CP") + caput(busy_CalcOut+".OOPT",'Transition to non-zero') + caput(busy_CalcOut+".OUT",start_strSeq+".PROC PP") + + desc = "MPA start" + n=1 + start_strSeq = userStringSeq_clear(Calcs_mda,n) + caput(start_strSeq+".DESC",desc) + caput(start_strSeq+".LNK1", Proc1+"ResetFilter PP") + caput(start_strSeq+".STR1","Yes") + + desc = "MPA wait" + n=10 + wait_CalcOut = userCalcOut_clear(Calcs_mda,n) + caput(wait_CalcOut+".DESC",desc) + caput(wait_CalcOut+".INPA",Proc1+"NumFilter_RBV") + caput(wait_CalcOut+".INPB",Proc1+"NumFiltered_RBV") + caput(wait_CalcOut+".OOPT",'Transition to non-zero') + caput(wait_CalcOut+".OUT",done_strSeq+".PROC PP") + + desc = "MPA writeDone" + n=1 + done_strSeq = userStringSeq_clear(Calcs_mda,n) + caput(done_strSeq+".DESC","MPA writeDone") + caput(done_strSeq+".LNK1", ADplugin+"WriteFile PP") + caput(done_strSeq+".STR1","Write") + caput(done_strSeq+".LNK1", mpa_busy+" PP") + caput(done_strSeq+".STR1","Done") + + + +def _mpa_trigger_callback(trigger,saveImg=False,**kwargs): + """ + used for triggering the MPA in the scanRecord, reset Proc1 and waits for finish + + trigger: adds/removes trigger to mda (True/False) + + **kwargs: + save_image = False used for appropriate trigger of ROIs + = True also saves an image based on ADplugin type + ADplugin = 'TIFF1:' (default + + by default ADplugin = 29iddMPA:TIFF1: + can use 29iddMPA:HDF1: + """ + + kwargs.setdefault("ADplugin","TIFF1:") + kwargs.setdefault("save_image",False) + + ADplugin = mpa_pv + kwargs['ADplugin'] + + _mpa_trigger_calcOut(**kwargs) + + if trigger==True: + caput(ADplugin+"AutoResetFilter","Yes") + if saveImg: + caput(ADplugin+"AutoSave", "No") + caput(ADplugin+"EnableCallbacks", "Enable") + + if trigger==False: + caput(ADplugin+"AutoResetFilter","No") + if saveImg: + caput(ADplugin+"EnableCallbacks", "Disable") + caput(ADplugin+"AutoSave", "Yes") + + return mpa_busy + +def _mpa_prefix(**kwargs): + """ + """ + kwargs.setdefault("debug",False) + + fpath=join(dirname(dirname(mda.filepath)),"mpa",'') + nextMDA = mda.fileNum + prefix="mda"+str.zfill(str(nextMDA),4)+"_mpa" + return prefix + +def mpa_trigger(trigger, **kwargs): + """ + Sets up / Cleans up the ScanRecord to trigger the MPA + + trigger: adds/removes trigger to mda (True/False) + + + **kwargs: + save_image = False used for appropriate trigger of ROIs + = True also saves an image based on ADplugin type + detTrigNum = 2 (default) + scan_dim = 1 (default) + ADplugin = "TIFF1" / "HDF1" + + Previously: MPA_Trigger + """ + kwargs.setdefault("detTrigNum",2) + kwargs.setdefault("save_image",False) + kwargs.setdefault('scan_dim',1) + kwargs.setdefault("ADplugin","TIFF1:") + + mpa_busy = _mpa_trigger_callback(trigger, **kwargs) + + #adding the MPA to the scanRecord trigger + if trigger == True: + _mpa_prefix(**kwargs) + mda.triggers_set(kwargs['scan_dim'],{2:mpa_busy}) + + if trigger == False: + mda.triggers_set(kwargs['scan_dim'],{2:''}) + + + +def mpa_save_strSeq(**kwargs): + """ + **kwargs: + ADplugin: "TIFF1:" + """ + kwargs.setdefault("ADplugin","TIFF1:") + + ADplugin = mpa_pv + kwargs["ADplugin"] + + desc = "MCP datamode" + n=2 + strSeq_pv = userStringSeq_clear(mda,n) + caput(strSeq_pv+".DESC",desc) + caput(strSeq_pv+".LNK1",mpaDet_pv+"Acquire CA NMS") + caput(strSeq_pv+".STR1","Done") + caput(strSeq_pv+".WAIT1","Wait") + caput(strSeq_pv+".LNK2",mpaDet_pv+"RunTimeEnable PP NMS") + caput(strSeq_pv+".STR2","1") + caput(strSeq_pv+".WAIT2","Wait") + caput(strSeq_pv+".LNK3",ADplugin+"EnableCallbacks PP NMS") + caput(strSeq_pv+".STR3","1") + +def mpa_freerun_strSeq(): + """ + """ + desc = "MCP freerun" + n=1 + strSeq_pv = userStringSeq_clear(mda,n) + caput(strSeq_pv+".DESC",desc) + caput(strSeq_pv+".LNK1",mpaDet_pv+"Acquire PP NMS") + caput(strSeq_pv+".WAIT1","Wait") + caput(strSeq_pv+".STR1","Done") + caput(strSeq_pv+".LNK2","29iddMPA:TIFF1:EnableCallbacks PP NMS") + caput(strSeq_pv+".STR2","0") + caput(strSeq_pv+".WAIT2","Wait") + caput(strSeq_pv+".LNK3",mpaDet_pv+"RunTimeEnable PP NMS") + caput(strSeq_pv+".STR3","0") + caput(strSeq_pv+".WAIT3","Wait") + caput(strSeq_pv+".LNK4",mpaDet_pv+"Acquire PP NMS") + caput(strSeq_pv+".STR4","Acquire") + + +def mpa_HV_sp_calcOut(): + """ + """ + + desc = "MPA HV SP" + n=9 + calcOut_pv = userCalcOut_clear(mda,n) + + caput(calcOut_pv+".DESC",desc) + caput(calcOut_pv+".A",0) + caput(calcOut_pv+".B",ratio) + caput(calcOut_pv+".C",max_HV) + caput(calcOut_pv+".CALC$","A") + caput(calcOut_pv+".OCAL$","MIN(A/B,C/B)") + caput(calcOut_pv+".OOPT",1) # On Change + caput(calcOut_pv+".DOPT",1) # Use 0CALC + caput(calcOut_pv+".IVOA",0) # Continue Normally + caput(calcOut_pv+".OUT",DAC_pv+" PP NMS") + +def mpa_HV_rbv_calcOut(): + """ + """ + desc = "MPA HV RBV" + n=10 + calcOut_pv = userCalcOut_clear(mda,n) + + caput(calcOut_pv+".DESC",desc) + caput(calcOut_pv+".INPA",DAC_pv+' CP NMS') + caput(calcOut_pv+".B",ratio) + caput(calcOut_pv+".CALC$","A*B") + +def mpa_interlock_mpa(): + """ + """ + desc = "MPA Interlock mpa" + n=7 + calcOut_pv = userCalcOut_clear(mda,n) + + caput(calcOut_pv+".DESC",desc) + tth_pv = Kappa_motor_dictionary('tth')[3] + caput(calcOut_pv+".INPA",tth_pv+".DRBV CP NMS") + caput(calcOut_pv+".B",1) + caput(calcOut_pv+".CALC$","ABS((("+str(tth_dLLM)+"<A && A<"+str(tth_dHLM)+") && (B>0))-1)") + caput(calcOut_pv+".OCAL$",'A') + caput(calcOut_pv+".OOPT",2) # When zero + caput(calcOut_pv+".DOPT",0) # Use CALC + caput(calcOut_pv+".IVOA",0) # Continue Normally + caput(calcOut_pv+".OUT",mpa_on_off_pv()+"PP NMS") + + +def mpa_interlock_DAC(): + """ + """ + desc = "MPA Interlock DAC" + n=8 + calcOut_pv = userCalcOut_clear(mda,n) + + caput(calcOut_pv+".DESC",desc) + tth_pv = Kappa_motor_dictionary('tth')[3] + caput(calcOut_pv+".INPA",tth_pv+".DRBV CP NMS") + caput(calcOut_pv+".B",1) + caput(calcOut_pv+".CALC$","ABS((("+str(tth_dLLM)+"<A && A<"+str(tth_dHLM)+") && (B>0))-1)") + caput(calcOut_pv+".OCAL$",'A') + caput(calcOut_pv+".OOPT",2) # When zero + caput(calcOut_pv+".DOPT",0) # Use CALC + caput(calcOut_pv+".IVOA",0) # Continue Normally + caput(calcOut_pv+".OUT",DAC_pv+"_Se PP NMS") + +def reset_mpa_HV(): + """ + resetting the SRS which controls the MPA HV + Model: SRS PS350 + ip = "164.54.118.57" + """ + ip = "164.54.118.57" + # Open TCP connect to poet 1234 of GPIB-ETHERNET + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) + sock.settimeout(0.1) + sock.connect((ip, 1234)) + + # Set mode as CONTROLLER + sock.send(b'++mode 1\n') + + # specify GPIB address of device being controlled + addr = "14" + str1="++addr"+addr+'\n' + sock.send(bytes(str1,'utf-8')) + + # reset SRS PS350 + #sock.send("*RST\n") + + # turn the high voltage on, clearing any current or voltage trips + sock.send(b"HVON\n") \ No newline at end of file diff --git a/build/lib/iexcode/instruments/remote_controlers.py b/build/lib/iexcode/instruments/remote_controlers.py new file mode 100644 index 0000000..2c65239 --- /dev/null +++ b/build/lib/iexcode/instruments/remote_controlers.py @@ -0,0 +1,356 @@ +from epics import caget,caput + +############################################################################################################## +############################## RSXS Remote control ############################## +############################################################################################################## + +# To start ioc for ARPES(RSXS) Surly(Sneezy) +# cd /net/s29dserv/xorApps/epics/synApps_5_8/ioc/29idSurlySoft/iocboot/iocLinux +# 29idSurlySoft start + +# to bring up epics screen +# cd /net/s29dserv/xorApps/epics/synApps_5_8/ioc/29idSurlySoft/ +# start_epics_29idSurlySoft + +def ARPES_Controller(): + """ + Button={} with Button[][0]=PV name and Button[][1]=value (for tweaks or other procs value=1) + ".TWF -> (+)tweak" + ".TWR -> (-)tweak" + """ + Controller="29idSurlySoft" + ioc="29idc:" + + Triggers={} #['LTSeq','RTSeq','LBSeq','RBSeq'] + Triggers[0]=ioc+"allstop.VAL",1 + Triggers[1]=ioc+"allstop.VAL",1 + Triggers[2]=ioc+"allstop.VAL",1 + Triggers[3]=ioc+"allstop.VAL",1 + + DPad={} #['UpSeq','DownSeq','LeftSeq','RightSeq'] + DPad[0]=ioc+"m1.TWF",1 #x + DPad[1]=ioc+"m1.TWR",1 + DPad[2]=ioc+"m4.TWR",1 #th + DPad[3]=ioc+"m4.TWF",1 + + Buttons={} #['YSeq','ASeq','XSeq','BSeq'] + Buttons[0]=ioc+"m3.TWF",1 #z + Buttons[1]=ioc+"m3.TWR",1 + Buttons[2]=ioc+"m2.TWR",1 #y + Buttons[3]=ioc+"m2.TWF",1 + + Ljoy={} #['LSUpSeq','LSDownSeq','LSLeftSeq','LSRightSeq'] + Ljoy[0]="",0 + Ljoy[1]="",0 + Ljoy[2]=ioc+"m6.TWR",1 #phi + Ljoy[3]=ioc+"m6.TWF",1 #phi + Lclick={} #['L3Seq'] + Lclick[0]=ioc+"m1.TWV",.25 #x + Lclick[1]=ioc+"m2.TWV",.25 #y + Lclick[2]=ioc+"m3.TWV",.25 #z + Lclick[3]=ioc+"m4.TWV",1 #th + Lclick[4]=ioc+"m5.TWV",1 #chi + Lclick[5]=ioc+"m6.TWV",1 #phi + Lclick[6]="",0 + Lclick[7]="",0 + Lclick[8]="",0 + + Rjoy={} #['RSUpSeq','RSDownSeq','RSLeftSeq','RSRightSeq'] + Rjoy[0]="",0 + Rjoy[1]="",0 + Rjoy[2]=ioc+"m5.TWR",1 #chi + Rjoy[3]=ioc+"m5.TWF",1 #chi + Rclick={} #['R3Seq'] + Rclick[0]=ioc+"m1.TWV",.5 #x + Rclick[1]=ioc+"m2.TWV",.5 #y + Rclick[2]=ioc+"m3.TWV",.5 #z + Rclick[3]=ioc+"m4.TWV",5 #th + Rclick[4]=ioc+"m5.TWV",5 #chi + Rclick[5]=ioc+"m6.TWV",5 #phi + Rclick[6]="",0 + Rclick[7]="",0 + Rclick[8]="",0 + + Controller_setup(Controller,Triggers,DPad,Buttons,Ljoy,Lclick,Rjoy,Rclick) + + +def RSXS_Kappa_Controller_backup(): #Keep JM modified below when kth broke + """ + Button={} with Button[][0]=PV name and Button[][1]=value (for tweaks or other procs value=1) + ".TWF -> (+)tweak" + ".TWR -> (-)tweak" + """ + Controller="29idSneezySoft" + ioc="29idKappa:" + + Triggers={} #['LTSeq','RTSeq','LBSeq','RBSeq'] + Triggers[0]=ioc+"m9.TWR",1 #tth + Triggers[1]=ioc+"m9.TWF",1 + Triggers[2]=ioc+"allstop.VAL",1 + Triggers[3]=ioc+"allstop.VAL",1 + + DPad={} #['UpSeq','DownSeq','LeftSeq','RightSeq'] + DPad[0]=ioc+"m4.TWF",1 #z + DPad[1]=ioc+"m4.TWR",1 + DPad[2]=ioc+"m1.TWR",1 #phi + DPad[3]=ioc+"m1.TWF",1 + + Buttons={} #['YSeq','ASeq','XSeq','BSeq'] + Buttons[0]=ioc+"m3.TWF",1 #y + Buttons[1]=ioc+"m3.TWR",1 + Buttons[2]=ioc+"m2.TWR",1 #x + Buttons[3]=ioc+"m2.TWF",1 + + Ljoy={} #['LSUpSeq','LSDownSeq','LSLeftSeq','LSRightSeq'] + Ljoy[0]="",0 + Ljoy[1]="",0 + Ljoy[2]=ioc+"m8.TWR",1 #th + Ljoy[3]=ioc+"m8.TWF",1 + Lclick={} #['L3Seq'] + Lclick[0]=ioc+"m2.TWV",50 #x + Lclick[1]=ioc+"m3.TWV",50 #y + Lclick[2]=ioc+"m4.TWV",50 #z + Lclick[3]=ioc+"m1.TWV",0.5 #phi (0.5 deg) + Lclick[4]=ioc+"m7.TWV",0.5 #kappa + Lclick[5]=ioc+"m8.TWV",0.5 #th + Lclick[6]=ioc+"m9.TWV",0.5 #tth + Lclick[7]="",0 + Lclick[8]="",0 + + Rjoy={} #['RSUpSeq','RSDownSeq','RSLeftSeq','RSRightSeq'] + Rjoy[0]="",0 + Rjoy[1]="",0 + Rjoy[2]=ioc+"m7.TWR",1 #kappa + Rjoy[3]=ioc+"m7.TWF",1 + Rclick={} #['R3Seq'] + Rclick[0]=ioc+"m2.TWV",250 #x + Rclick[1]=ioc+"m3.TWV",250 #y + Rclick[2]=ioc+"m4.TWV",250 #z + Rclick[3]=ioc+"m1.TWV",5 #phi (45 deg) + Rclick[4]=ioc+"m7.TWV",5 #kappa + Rclick[5]=ioc+"m8.TWV",5 #th + Rclick[6]=ioc+"m9.TWV",5 #tth + Rclick[7]="",0 + Rclick[8]="",0 + + Controller_setup(Controller,Triggers,DPad,Buttons,Ljoy,Lclick,Rjoy,Rclick) + +def RSXS_Kappa_Controller(): + """ + Button={} with Button[][0]=PV name and Button[][1]=value (for tweaks or other procs value=1) + ".TWF -> (+)tweak" + ".TWR -> (-)tweak" + """ + Controller="29idSneezySoft" + ioc="29idKappa:" + + Triggers={} #['LTSeq','RTSeq','LBSeq','RBSeq'] + Triggers[0]=ioc+"m9.TWR",1 #tth + Triggers[1]=ioc+"m9.TWF",1 + Triggers[2]=ioc+"allstop.VAL",1 + Triggers[3]=ioc+"allstop.VAL",1 + + DPad={} #['UpSeq','DownSeq','LeftSeq','RightSeq'] + DPad[0]=ioc+"m4.TWF",1 #z + DPad[1]=ioc+"m4.TWR",1 + DPad[2]=ioc+"m1.TWR",1 #phi + DPad[3]=ioc+"m1.TWF",1 + + Buttons={} #['YSeq','ASeq','XSeq','BSeq'] + Buttons[0]=ioc+"m3.TWF",1 #y + Buttons[1]=ioc+"m3.TWR",1 + Buttons[2]=ioc+"m2.TWR",1 #x + Buttons[3]=ioc+"m2.TWF",1 + + Ljoy={} #['LSUpSeq','LSDownSeq','LSLeftSeq','LSRightSeq'] + Ljoy[0]="29idd:Unidig1Bo0",0 #light on + Ljoy[1]="29idd:Unidig1Bo0",1 #light off + Ljoy[2]=ioc+"",0 #th + Ljoy[3]=ioc+"",0 + Lclick={} #['L3Seq'] + Lclick[0]=ioc+"m2.TWV",250 #x + Lclick[1]=ioc+"m3.TWV",250 #y + Lclick[2]=ioc+"m4.TWV",250 #z + Lclick[3]=ioc+"m1.TWV",0.5 #phi (0.5 deg) + Lclick[4]=ioc+"m7.TWV",0.5 #kappa + Lclick[5]=ioc+"m8.TWV",0.5 #th + Lclick[6]=ioc+"m9.TWV",0.5 #tth + Lclick[7]="",0 + Lclick[8]="",0 + + Rjoy={} #['RSUpSeq','RSDownSeq','RSLeftSeq','RSRightSeq'] + Rjoy[0]="",0 + Rjoy[1]="",0 + Rjoy[2]=ioc+"",0 #kappa + Rjoy[3]=ioc+"",0 + Rclick={} #['R3Seq'] + Rclick[0]=ioc+"m2.TWV",500 #x + Rclick[1]=ioc+"m3.TWV",500 #y + Rclick[2]=ioc+"m4.TWV",500 #z + Rclick[3]=ioc+"m1.TWV",5 #phi (45 deg) + Rclick[4]=ioc+"m7.TWV",5 #kappa + Rclick[5]=ioc+"m8.TWV",5 #th + Rclick[6]=ioc+"m9.TWV",5 #tth + Rclick[7]="",0 + Rclick[8]="",0 + + Controller_setup(Controller,Triggers,DPad,Buttons,Ljoy,Lclick,Rjoy,Rclick) + + +def RSoXS_Controller(): + """ + Button={} with Button[][0]=PV name and Button[][1]=value (for tweaks or other procs value=1) + ".TWF -> (+)tweak" + ".TWR -> (-)tweak" + """ + Controller="29idSneezySoft" + ioc="29idRSoXS:" + + Triggers={} #['LTSeq','RTSeq','LBSeq','RBSeq'] + Triggers[0]=ioc+"allstop.VAL",1 + Triggers[1]=ioc+"allstop.VAL",1 + Triggers[2]=ioc+"allstop.VAL",1 + Triggers[3]=ioc+"allstop.VAL",1 + + DPad={} #D-Pad:['UpSeq','DownSeq','LeftSeq','RightSeq'] + DPad[0]=ioc+"m10.TWF",1 #Det-V + DPad[1]=ioc+"m10.TWR",1 + DPad[2]=ioc+"m11.TWR",1 #Det-Q + DPad[3]=ioc+"m11.TWF",1 + + Buttons={} #Buttons:['YSeq','ASeq','XSeq','BSeq'] + Buttons[0]=ioc+"m2.TWF",1 #y + Buttons[1]=ioc+"m2.TWR",1 + Buttons[2]=ioc+"m1.TWR",1 #x + Buttons[3]=ioc+"m1.TWF",1 + + Ljoy={} #Left Joystick:['LSUpSeq','LSDownSeq','LSLeftSeq','LSRightSeq'] + Ljoy[0]=ioc+"m5.TWF",1 #phi + Ljoy[1]=ioc+"m5.TWR",1 + Ljoy[2]=ioc+"m9.TWR",1 #tth + Ljoy[3]=ioc+"m9.TWF",1 + Lclick={} #['L3Seq'] + Lclick[0]="",0 + Lclick[1]="",0 + Lclick[2]="",0 + Lclick[3]="",0 + Lclick[4]="",0 + Lclick[5]="",0 + Lclick[6]="",0 + Lclick[7]="",0 + Lclick[8]="",0 + + Rjoy={} #Right Joystick:['RSUpSeq','RSDownSeq','RSLeftSeq','RSRightSeq'] + Rjoy[0]=ioc+"m3.TWF",1 #z + Rjoy[1]=ioc+"m3.TWF",1 + Rjoy[2]=ioc+"m8.TWR",1 #th + Rjoy[3]=ioc+"m8.TWF",1 + Rclick={} #['R3Seq'] + Rclick[0]=ioc+"m2.TWV",1 #x + Rclick[1]=ioc+"m3.TWV",1 #y + Rclick[2]=ioc+"m4.TWV",1 #z + Rclick[3]=ioc+"m1.TWV",1 #th + Rclick[4]=ioc+"m7.TWV",1 #phi + Rclick[5]=ioc+"m8.TWV",1 #tth + Rclick[6]=ioc+"m9.TWV",1 #Det-V + Rclick[7]=ioc+"m9.TWV",1 #Det-Q + Rclick[8]="",0 + + Controller_setup(Controller,Triggers,DPad,Buttons,Ljoy,Lclick,Rjoy,Rclick) + +def Controller_setup(Controller,Triggers,DPad,Buttons,Ljoy,Lclick,Rjoy,Rclick): + """ -------- Standard set up for the Logitech controllers -------------- + Controller -> controller ioc name + + Button={} + Button[][0]=PV name + Button[][1]=value (for tweaks or other procs value=1) + ".TWF -> (+)tweak" + ".TWR -> (-)tweak" + + Trigger[0,3] -> PVs for link 1 of the 4 buttons (bottoms are typically the allstop) + DPad[0,3] -> PVs for link 1 of the 4 buttons (order: Top/Bottom/Left/Right) + Buttons[0,3] -> PVs for link 1 of the 4 buttons (order: Top/Bottom/Left/Right) + Ljoy[0,3] -> PVs for link 1 of the 4 buttons (order: Top/Bottom/Left/Right) + Lclick[0,8] -> PVs for Link 1-9 of the center click (typically coarse tweak values) + RJoy[0,3] -> PVs for link 1 of the 4 buttons (order: Top/Bottom/Left/Right) + Rclick[0,8] -> PVs for Link 1-9 of the center click (typically fine tweak values) + """ + + #Triggers + Ctrl_button(Controller,"LTSeq",1,Triggers[0][0],Triggers[0][1]) + Ctrl_button(Controller,"RTSeq",1,Triggers[1][0],Triggers[1][1]) + Ctrl_button(Controller,"LBSeq",1,Triggers[2][0],Triggers[2][1])#All Stop + Ctrl_button(Controller,"RBSeq",1,Triggers[3][0],Triggers[3][1])#All Stop + + # D-Pad + Ctrl_button(Controller,"UpSeq",1, DPad[0][0],DPad[0][1]) + Ctrl_button(Controller,"DownSeq",1, DPad[1][0],DPad[1][1]) + Ctrl_button(Controller,"LeftSeq",1, DPad[2][0],DPad[2][1]) + Ctrl_button(Controller,"RightSeq",1,DPad[3][0],DPad[3][1]) + + + #Buttons + Ctrl_button(Controller,"YSeq",1,Buttons[0][0],Buttons[0][1]) + Ctrl_button(Controller,"ASeq",1,Buttons[1][0],Buttons[1][1]) + Ctrl_button(Controller,"XSeq",1,Buttons[2][0],Buttons[2][1]) + Ctrl_button(Controller,"BSeq",1,Buttons[3][0],Buttons[3][1]) + + + #Left Joystick + Ctrl_button(Controller,"LSUpSeq",1, Ljoy[0][0],Ljoy[0][1]) + Ctrl_button(Controller,"LSDownSeq",1, Ljoy[1][0],Ljoy[1][1]) + Ctrl_button(Controller,"LSLeftSeq",1, Ljoy[2][0],Ljoy[2][1]) + Ctrl_button(Controller,"LSRightSeq",1,Ljoy[3][0],Ljoy[3][1]) + #Left Click Coarse Tweak Values + Ctrl_button(Controller,"L3Seq",1,Lclick[0][0],Lclick[0][1]) + Ctrl_button(Controller,"L3Seq",2,Lclick[1][0],Lclick[1][1]) + Ctrl_button(Controller,"L3Seq",3,Lclick[2][0],Lclick[2][1]) + Ctrl_button(Controller,"L3Seq",4,Lclick[3][0],Lclick[3][1]) + Ctrl_button(Controller,"L3Seq",5,Lclick[4][0],Lclick[4][1]) + Ctrl_button(Controller,"L3Seq",6,Lclick[5][0],Lclick[5][1]) + Ctrl_button(Controller,"L3Seq",7,Lclick[6][0],Lclick[6][1]) + Ctrl_button(Controller,"L3Seq",8,Lclick[7][0],Lclick[7][1]) + Ctrl_button(Controller,"L3Seq",9,Lclick[8][0],Lclick[8][1]) + + #Right Joystick + Ctrl_button(Controller,"RSUpSeq",1, Rjoy[0][0],Rjoy[0][1]) + Ctrl_button(Controller,"RSDownSeq",1, Rjoy[1][0],Rjoy[1][1]) + Ctrl_button(Controller,"RSLeftSeq",1, Rjoy[2][0],Rjoy[2][1]) + Ctrl_button(Controller,"RSRightSeq",1,Rjoy[3][0],Rjoy[3][1]) + #Right Click Fine Tweak Values + Ctrl_button(Controller,"R3Seq",1,Rclick[0][0],Rclick[0][1]) + Ctrl_button(Controller,"R3Seq",2,Rclick[1][0],Rclick[1][1]) + Ctrl_button(Controller,"R3Seq",3,Rclick[2][0],Rclick[2][1]) + Ctrl_button(Controller,"R3Seq",4,Rclick[3][0],Rclick[3][1]) + Ctrl_button(Controller,"R3Seq",5,Rclick[4][0],Rclick[4][1]) + Ctrl_button(Controller,"R3Seq",6,Rclick[5][0],Rclick[5][1]) + Ctrl_button(Controller,"R3Seq",7,Rclick[6][0],Rclick[6][1]) + Ctrl_button(Controller,"R3Seq",8,Rclick[7][0],Rclick[7][1]) + Ctrl_button(Controller,"R3Seq",9,Rclick[8][0],Rclick[8][1]) + + +def Ctrl_reset(Controller): + PV="" + val=0 + CtrlButtonList = ['LTSeq','RTSeq','LBSeq','RBSeq','UpSeq','DownSeq','LeftSeq','RightSeq','YSeq','ASeq','XSeq','BSeq'] + CtrlButtonList.extend(['LSUpSeq','LSDownSeq','LSLeftSeq','LSRightSeq','L3Seq','RSUpSeq','RSDownSeq','RSLeftSeq','RSRightSeq','R3Seq']) + for button in CtrlButtonList: + for link in range(1,10): + Ctrl_button(Controller,button,link,PV,val) + +def Ctrl_button(Controller,button,link,PV,val):#writes to specified link (controller has 1 to 9) + ctlpv=Controller+":ctl1:"+button + caput(ctlpv+".LNK"+str(link),PV+" NPP NMS") + caput(ctlpv+".DO"+str(link),val) + +def Ctrl_print(Controller): + CtrlButtonList = ['LTSeq','RTSeq','LBSeq','RBSeq','UpSeq','DownSeq','LeftSeq','RightSeq','YSeq','ASeq','XSeq','BSeq'] + CtrlButtonList.extend(['LSUpSeq','LSDownSeq','LSLeftSeq','LSRightSeq','L3Seq','RSUpSeq','RSDownSeq','RSLeftSeq','RSRightSeq','R3Seq']) + for button in CtrlButtonList: + print("---"+button+"---") + for link in range(1,10): + ctlpv=Controller+":ctl1:"+button + PV=caget(ctlpv+".LNK"+str(link)) + val=caget(ctlpv+".DO"+str(link)) + print(" "+PV+", "+str(val)) diff --git a/build/lib/iexcode/instruments/resolution.py b/build/lib/iexcode/instruments/resolution.py new file mode 100644 index 0000000..a34f891 --- /dev/null +++ b/build/lib/iexcode/instruments/resolution.py @@ -0,0 +1,106 @@ +import numpy as np + +from iexcode.instruments.electron_analyzer import resolution_EA +############################################################################################################# +############################## Resolution ############################## +############################################################################################################# +def resolution_calculate_beamline(grt,hv_eV,slit_size): + """ + + Previously: Calc_BL_Resolution + """ + res_function={} + res_function[10] = {"MEG":(-0.000800494,2.56761e-05,1.83724e-08,-1.69223e-12,0), "HEG":(-0.000414482,1.07456e-05,8.79034e-09,-8.42361e-13,0)} + res_function[20] = {"MEG":(-0.00156643, 3.16894e-05,2.72121e-08,-2.63642e-12,0), "HEG":(-0.00054591, 1.35647e-05,2.01775e-08,-4.30789e-12,5.70112e-16)} + res_function[50] = {"MEG":(-0.00251543, 4.89022e-05,7.70055e-08,-1.66358e-11,2.21272e-15),"HEG":(-0.00173081, 2.97625e-05,4.90646e-08,-1.0706e-11, 1.43011e-15)} + res_function[100] = {"MEG":(-0.00545563, 9.14928e-05,1.51335e-07,-3.30332e-11,4.41314e-15),"HEG":(-0.00360316, 5.83265e-05,9.76881e-08,-2.13767e-11,2.85877e-15)} + res_function[200] = {"MEG":(-0.0111658, 0.000179785,3.01277e-07,-6.59309e-11,8.81755e-15),"HEG":(-0.00728107, 0.000116055,1.95149e-07,-4.27331e-11,5.71638e-15)} + try: + a,b,c,d,e = res_function[slit_size][grt] + resolution = a + b*hv_eV + c*hv_eV**2 + d*hv_eV**3 + e*hv_eV**4 + + except KeyError: + print("WARNING: Slit size not listed, please choose one of the following:") + print(" 10, 20, 50, 100, 200 um.") + resolution=0 + + return resolution*1000 + + +def resolution_mono_noise(grt,hv_eV): + """ + jitter/noise in the mono contribution to the resolution + + Previoulsy: Resolution_Mono + """ + if grt == "MEG": + K0=-2.3836 + K1=0.02083 + K2=7.8886e-06 + if grt == "HEG": + K0=-2.0984 + K1=0.011938 + K2=3.6397e-06 + noise = K0 + K1*hv_eV + K2*hv_eV**2 + return noise + + +def resolution_beamline(grt,hv_eV,slit_size,verbose=True): + """ + resolution of the beamline optics + + Resolution_BL + """ + mono_noise = resolution_mono_noise(grt,hv_eV) + theortical = resolution_calculate_beamline(grt,hv_eV,slit_size) + resolution=np.sqrt(theortical**2 + mono_noise**2) + if verbose: + print("BL : "+"%.0f" % theortical, "meV") + print("Mono : "+"%.0f" % mono_noise, "meV") + print("Total: "+"%.0f" % resolution, "meV") + return resolution + + +def resolution_ARPES(grt,hv_eV,slit_size,PE,slit_SES,T,verbose=True): + """ + resolution of the ARPES + """ + bl = resolution_beamline(grt,hv_eV,slit_size,verbose=False) + KbT = T*25/300 + SES = resolution_EA(PE,slit_SES) + + resolution = np.sqrt(bl**2 + KbT**2 + SES**2) + + if verbose: + print("BL + mono : "+"%.0f" % bl, "meV") + print("SES : "+"%.0f" % SES, "meV") + print("KbT : "+"%.0f" % KbT, "meV") + print("Total: "+"%.0f" % resolution, "meV") + + return resolution + +def Resolution_BL_eff(grating,hv,slit_3C,PE,slit_SES,T,Ef): + M = resolution_mono_noise(grating,hv) + KbT = T*25/300 + BL = resolution_calculate_beamline(grating,hv,slit_3C) + SES = resolution_EA(PE,slit_SES) + resolution = np.sqrt(BL**2+SES**2+KbT**2+M**2) + effective = np.sqrt(Ef**2-SES**2-KbT**2-M**2) + print("BL_th : "+"%.0f" % BL, "meV SES : "+"%.0f" % SES, "meV") + print("Mono : "+"%.0f" % M, "meV KbT : "+"%.0f" % KbT, "meV") + print("Total : "+"%.0f" % resolution, "meV Fermi : "+"%.0f" % Ef, "meV") + print("BL_eff: "+"%.0f" % effective, "meV") + + +def Resolution_BL_eff2(grating,hv,slit_3C,PE,slit_SES,T,Ef,Dirty_Au): + M = resolution_mono_noise(grating,hv) + KbT = T*25/300 + BL = resolution_calculate_beamline(grating,hv,slit_3C) + SES = resolution_EA(PE,slit_SES) + resolution = np.sqrt(BL**2+SES**2+KbT**2+M**2+Dirty_Au**2) + effective = np.sqrt(Ef**2-SES**2-KbT**2-M**2-Dirty_Au**2) + print("BL_th : "+"%.0f" % BL, "meV SES : "+"%.0f" % SES, "meV") + print("Mono : "+"%.0f" % M, "meV KbT : "+"%.0f" % KbT, "meV") + print("Total : "+"%.0f" % resolution, "meV Fermi : "+"%.0f" % Ef, "meV") + print("BL_eff: "+"%.0f" % effective, "meV Sample : "+"%.0f" % Dirty_Au, "meV") + diff --git a/build/lib/iexcode/instruments/s29_temp_cntl.py b/build/lib/iexcode/instruments/s29_temp_cntl.py new file mode 100644 index 0000000..4bffcda --- /dev/null +++ b/build/lib/iexcode/instruments/s29_temp_cntl.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +from epics import caget, caput +from time import sleep, gmtime, strftime, localtime + +#Define caput with wait function +def caputw(PVname,PVinput): + return caput(PVname,PVinput,wait=True, timeout = 100000) + +def dateandtime(): + return strftime("%a %d %b %Y %H:%M:%S",localtime()) + +def tempr(): + print("RSXS Sample Temperature: {} K".format(caget("29idd:LS331:TC1:SampleA"))) + +def temps(tempset): + print("Initial Sample Temperature:",caget("29idd:LS331:TC1:SampleA")," K",dateandtime()) + caput("29idd:LS331:TC1:wr_SP",1.0*tempset) + stop_var=0 + b=0 + sleep(10) + while b == 0: + delta=abs(caget("29idd:LS331:TC1:SampleA")-1.0*tempset) + if abs(delta) < 1: + c=0 + while c < 10: + sleep(1) + delta=delta+(caget("29idd:LS331:TC1:SampleA")-1.0*tempset) + c=c+1 + if abs(delta/10) < 1: + print("Stable Sample Temperature:",caget("29idd:LS331:TC1:SampleA")," K",dateandtime()) + b=1 + else: + temp1=caget("29idd:LS331:TC1:SampleA") + sleep(10) + temp2=caget("29idd:LS331:TC1:SampleA") + if abs(temp1-temp2) < 0.1: + sleep(30) + temp2=caget("29idd:LS331:TC1:SampleA") + if abs(temp1-temp2) < .5: + print("UNABLE TO STABLIZE TEMPERATURE! Stopped at T=:",caget("29idd:LS331:TC1:SampleA")," K",dateandtime()) + b=1 + + diff --git a/build/lib/iexcode/instruments/scalers.py b/build/lib/iexcode/instruments/scalers.py new file mode 100644 index 0000000..89be3db --- /dev/null +++ b/build/lib/iexcode/instruments/scalers.py @@ -0,0 +1,31 @@ +from math import floor + +from epics import caput,PV + +from iexcode.instruments.IEX_endstations import BL + +def scaler_cts(time_seconds=0.1,verbose=True): + """ + sets the scalers counting for the endstation defined in BL + """ + if BL.ioc == 'Kappa': + Kappa_scaler(time_seconds,verbose=verbose) + + +def Kappa_scaler(time_seconds=0.1,verbose=True): + """ + Sets the integration time for the scalers in the Kappa chamber + and mpa filter num if mpa is running + """ + scaler_pv="29idMZ0:scaler1.TP" + mpa_NumFilter = '29iddMPA:Proc1:NumFilter' + + caput(pv,time_seconds) + + pv=PV(mpa_NumFilter) + if pv.connected(): + caput(mpa_NumFilter,floor(time_seconds)) + + if verbose: + print("Integration time set to:", str(time_seconds)) + diff --git a/build/lib/iexcode/instruments/scanRecord.py b/build/lib/iexcode/instruments/scanRecord.py new file mode 100644 index 0000000..2e01a97 --- /dev/null +++ b/build/lib/iexcode/instruments/scanRecord.py @@ -0,0 +1,688 @@ + +from os.path import join +import time + +from epics import caget, caput, PV + +from iexcode.instruments.utilities import dateandtime, print_warning_message +from iexcode.instruments.logfile import log_update +from iexcode.instruments.userCalcs import userStringSeq_pvs,userStringSeq_clear + + +def saveData_get_all(ioc_pv): + """ + returns saveData info: + """ + saveData_info = { + 'fileSystem':caget(ioc_pv+":saveData_fileSystem",as_string=True), + 'subDir':caget(ioc_pv+":saveData_subDir",as_string=True), + 'scanNumber':caget(ioc_pv+":saveData_scanNumber"), + 'baseName':caget(ioc_pv+":saveData_baseName",as_string=True), + } + + filepath = join(saveData_info['fileSystem'],saveData_info['subDir']) + filepath=filepath.replace('//','/') + + +default_positioner_settling_time = 0.05 +default_detector_settling_time = 0.1 + +class ScanRecord: + """ + used for short hand scanning and to get info related to a scanRecord + """ + + def __init__(self,scan_ioc,detector_dictionary,trigger_dictionary,before_scan_pv,after_scan_pv,**kwargs): + """ + scan_ioc: full pv for the ioc, "29idARPES:", "29idc:" + detector_dictionary: dictionary corresponding to detector num and pv to proc => {det_num:'det_pv'} + trigger_dictionary: dictionary corresponding to trigger num and trigger pv => {trigger_num:'trigger_pv'} + before_scan_pv: pv to be proc'ed be for the start of a scan + after_scan_pv: pv to be proc'ed be for the start of a scan + + **kwargs + reset: resets the scanRecord (True / False) => default = True + + """ + kwargs.setdefault('reset',True) + self.ioc = scan_ioc + + if kwargs['reset']: + self.reset_all(detector_dictionary,trigger_dictionary,before_scan_pv,after_scan_pv) + + pass + +############################################################################################################## +################################ MDA (ScanRecord) files and folders ############################## +############################################################################################################## + + def fileNum(self): + """ + returns the current fileNum in the SaveData of ioc is scanning + or next if idle + + """ + + return saveData_get_all(self.ioc)['scanNumber'] + + def lastFileNum(self): + """ + returns the last saved fileNum in the SaveData of ioc + + previously: MDA_GetLastFileNum + """ + + fileNum = self.fileNum(self) + return fileNum - 1 + + + def filepath(self): + """ + returns the full filepath for the SaveData of ioc + + previously: MDA_CurrentDirectory + """ + return saveData_get_all(self.ioc)['filepath'] + + def prefix(self): + """ + returns the prefix for the SaveData of ioc + + previously: MDA_CurrentPrefix + """ + return saveData_get_all(self.ioc)['baseName'] + + def scanRecord_run(self): + """ + returns the current run by parsing the filepath in the SaveData for ioc + + previously: MDA_CurrentRun + """ + filepath = self.filepath() + m=filepath.find('data_29id')+len('data_29id')+2 + current_run=filepath[m:m+6] + return current_run + + def scanRecord_user(self): + """ + returns the current user by parsing the filepath in the SaveData for ioc + + previously: MDA_CurrentUser + """ + subdir=caget(self.ioc+":saveData_subDir",as_string=True) + m=subdir.find('/mda') + if m == 0 : + current_user='Staff' + elif m > 0: + current_user=subdir[1:m] + else: + current_user="" + print_warning_message("mda_user is empty string") + return current_user + + +############################################################################################################## +############################# filling in the scan record ############################## +############################################################################################################## + + + ### before and after scans + def _before_after_scan(self,**kwargs): + """ + Fills in the before and after user sequences into the scan record + **kwargs + before_scan_pv => '' + after_scan_pv => self.default_after_scan_seq() + """ + kwargs.setdefault('scan_dim',1) + kwargs.setdefault('before_scan_pv','') + kwargs.setdefault('after_scan_pv',self._default_after_scan_seq()) + + scan_pv = self.ioc+"scan"+str(kwargs['scan_dim']) + + #Clearing all Before/Afters in all dims + for dim in [1,2,3,4]: + caput(self.ioc+"scan"+str(dim)+".BSPV","") + caput(self.ioc+"scan"+str(dim)+".ASPV","") + + caput(scan_pv+".BSPV",kwargs['before_scan_pv']) + caput(scan_pv+".BSCD",1) + caput(scan_pv+".BSWAIT","Wait") + + caput(scan_pv+".ASPV",kwargs['after_scan_pv']) + caput(scan_pv+".ASCD",1) + caput(scan_pv+".ASWAIT","Wait") + + def default_after_scan_seq(self,**kwargs): + """ + writes the user string sequence to happen at the end of a scan + returns after_scan_pv = pv for userStringSeq for after scan + + uses links 1-4, link 10 is reserved for snake scans + + **kwargs + seq_num: userStringSeq number in ioc => 10 (default) + positioner_settling_time + detector_settling_time + + + Previously: AfterScan_StrSeq + """ + kwargs.setdefault(seq_num,10) + kwargs.setdefault('positioner_settling_time',default_positioner_settling_time) + kwargs.setdefault('detector_settling_time',default_detector_settling_time) + + scan_dim = 1 + scan_pv = self.ioc+"scan"+str(scan_dim) + + seq_num=kwargs['seq_num'] + after_scan_pv = userStringSeq_pvs(self.ioc, seq_num) + + #clear and write the after scan user sequence + userStringSeq_clear(self.ioc,seq_num) + caput(after_scan_pv+".DESC","After Scan") + + #link 1 - clears positioners + caput(after_scan_pv+".LNK1",scan_pv+'.P1AR'+" PP NMS") + caput(after_scan_pv+".DO1","Clear pos&rdbk PV's, etc") + + #link 2 - sets positioner mode to to abosolute + caput(after_scan_pv+".LNK2",scan_pv+'.CMND'+" PP NMS") + caput(after_scan_pv+".DO2","ABSOLUTE") + + #link 3 - sets positioner delay time to default + settling_time = kwargs['positioner_settling_time'] + caput(after_scan_pv+".LNK3",scan_pv+'.PDLY'+" PP NMS") + caput(after_scan_pv+".DO3",settling_time) + + #link 4 - sets detector delay time to default + settling_time = kwargs['detector_settling_time'] + caput(after_scan_pv+".LNK4",scan_pv+'.DDLY'+" PP NMS") + caput(after_scan_pv+".DO4",settling_time) + + return after_scan_pv + + ### Resetting: + def reset_scan_dim(self,scan_dim,detector_dictionary,trigger_dictionary,before_scan_pv,after_scan_pv): + """ + Resets all the scanRecords (scanDIM=1,2,3,4) for a given IOC + uses Reset_Scan() + **kwargs + scaler='y', only for Kappa ioc + + Previously: Reset_ScanAll + """ + self.detectors_clear(scan_dim) + self.triggers_clear(scan_dim) + self.positioners_clear(scan_dim) + + self.detectors_set(scan_dim,detector_dictionary) + self.triggers_set(scan_dim,trigger_dictionary) + self.before_after_sequences_set(scan_dim,before_scan_pv,after_scan_pv) + + self.detector_settling_time(scan_dim) + self.positioner_settling_time(scan_dim) + + def reset_all(self,detector_dictionary,trigger_dictionary,before_scan_pv,after_scan_pv): + """ + Resets all the scanRecords (scanDIM=1,2,3,4) for a given IOC + uses Reset_Scan() + **kwargs + scaler='y', only for Kappa ioc + + Previously: Reset_ScanAll + """ + + for scan_dim in range(4,1): + self.reset_scan_dim(scan_dim,{},{},'','') + self.reset_scan_dim(1,detector_dictionary,trigger_dictionary,before_scan_pv,after_scan_pv) + + ### default setting + def settings_defaults(self,scan_dim,verbose=True,**kwargs): + """ + Reset scan settings to default: ABSOLUTE, STAY, 0.05/0.1 positioner/detector settling time + + Previous: Reset_Scan + """ + scan_pv = self.ioc+"scan"+str(scan_dim) + + self.positioner_absolute_mode(absolute=True,**kwargs) # Absolute position + self.positioner_after_scan(after="PRIOR POS",**kwargs) # After scan: prior position + self.positioner_settling_time(**kwargs)# Positioner Settling time => default + self.detector_settling_time(**kwargs)# Detector Settling time => default + self.triggers_reset(verbose=False,**kwargs) + + if verbose: + print("Scan record "+self.ioc+" reset to default settings.") + + ### positioners + def positioners_clear(self,scan_dim,verbose=True): + """ + Clear all extra scan positioners + + Previously: Clear_Scan_Positioners + """ + scan_pv = self.ioc+"scan"+str(scan_dim) + caput(scan_pv+".CMND",3) # Clear all Positionners + + if verbose: + print("\nAll extra positionners cleared") + + def positioner_settling_time(self,**kwargs): + """ + sets the positioner settling time + """ + kwargs.setdefault('scan_dim',1) + kwargs.setdefault('positioner_settling_time',self.default_positioner_settling_time()) + + scan_pv = self.ioc+"scan"+str(kwargs['scan_dim']) + + caput(scan_pv+".PDLY",kwargs['positioner_settling_time']) # Positioner Settling time + return scan_pv+".PDLY",kwargs['positioner_settling_time'] + + def positioner_after_scan(self,after="PRIOR POS",**kwargs): + """ + sets the positioner after scan + after = "PRIOR POS" + "STAY" + + """ + kwargs.setdefault('scan_dim',1) + scan_pv = self.ioc+"scan"+str(kwargs['scan_dim']) + caput(scan_pv+".PASM",after) + + def positioner_absolute_mode(self,absolute=True,**kwargs): + """ + absolute = True + = False => relative + + """ + kwargs.setdefault('scan_dim',1) + + scan_pv = self.ioc+"scan"+str(kwargs['scan_dim']) + if absolute: + ab_state = 0 + else: + ab_state = 1 + + caput(scan_pv+".P1AR",ab_state) # Absolute position + + def fillin(self,val_pv,rbv_pv,start,stop,steps_points,**kwargs): + """ + Fills in the scanRecord for the positionner + dim: scan dim + val_pv: drive pv + rbv_pv: readback pv + start: first point + stop: last point + step: step size (only used for positioner_num = 1, otherwise ignored) + kwargs: + num_points: False (default) => steps_points is the step size + True => steps_points is the number of points + positioner_num: default => 1 + positioner_settling_time: default => 0.1 + detector_settling_time: default => 0.1 + + Previously: Scan_FillIn + """ + kwargs.setdefault('scan_dim',1) + kwargs.setdefault("positioner_num",1) + kwargs.setdefault('detector_settling_time',default_detector_settling_time) + kwargs.setdefault('positioner_settling_time',default_positioner_settling_time) + + scan_dim = kwargs['scan_dim'] + self.progress(scan_dim) + + scan_pv = self.ioc+"scan"+str(scan_dim) + posNum = kwargs['positioner_num'] + + caput(scan_pv+".P"+str(posNum)+"PV",val_pv) + caput(scan_pv+".R"+str(posNum)+"PV",rbv_pv) + caput(scan_pv+".P"+str(posNum)+"SP",start*1.0) + caput(scan_pv+".P"+str(posNum)+"EP",stop*1.0) + if kwargs['positioner_num'] == 1: + if kwargs['num_points'] : + caput(scan_pv+".NPTS",steps_points) + else: + caput(scan_pv+".P1SI",steps_points*1.0) + + self.detector_settling_time(scan_dim,kwargs['detector_settling_time']) + self.positioner_settling_time(scan_dim,kwargs['positioner_settling_time']) + + #checking that PVs and positioner limits are good + self._check(scan_dim) + + + ### detectors and detector settling time + def detectors_clear(self,scan_dim,verbose=True): + """ + Clear all scan detectors + + Previously: Clear_Scan_Detectors + """ + scan_pv = self.ioc+"scan"+str(scan_dim) + for i in range(1,10): + caput(scan_pv+".D0"+str(i)+"PV","") + for i in range(10,71): + caput(scan_pv+".D"+str(i)+"PV","") + if verbose: + print("\nAll detectors cleared") + + def detectors_set(self,scan_dim,detector_dictionary): + """ + sets the detectors + detector_dictionary = {detNum:pv} + """ + scan_pv = self.ioc+"scan"+str(scan_dim) + for det_num in detector_dictionary.keys(): + det_pv=caget(scan_pv+".D"+det_num+"PV") + caput(det_pv,detector_dictionary[det_num]) + + def detector_settling_time(self,**kwargs): + """ + set the detector settling time + """ + kwargs.setdefault('scan_dim',1) + kwargs.setdefault('detector_settling_time',self.default_detector_settling_time()) + + scan_pv = self.ioc+"scan"+str(kwargs['scan_dim']) + + caput(scan_pv+".DDLY",kwargs['detector_settling_time']) + return scan_pv+".DDLY",kwargs['detector_settling_time'] + + ### triggers + def triggers_set(self,scan_dim,trigger_dictionary): + """ + Clear all scan detectors triggers + + trigger_dictionary = {trigger_num:pv} + Previously: Clear_Scan_Triggers + """ + scan_pv = self.ioc+"scan"+str(scan_dim) + if len(trigger_dictionary.keys())>0: + for tigger_num in trigger_dictionary.keys(): + caput(scan_pv+".T"+str(tigger_num)+"PV",trigger_dictionary[tigger_num]) + + def triggers_clear(self,scan_dim,trigger_dictionary,verbose=True): + """ + Clear all scan detectors triggers + + Previously: Clear_Scan_Triggers + """ + scan_pv = self.ioc+"scan"+str(scan_dim) + inner_scan_trigger = self.ioc+"scan"+str(scan_dim-1)+".EXSC" + + if len(trigger_dictionary.keys())>0: + for dim in range(1,5): + if dim==1 and scan_dim>1: + caput(scan_pv+".T1PV",inner_scan_trigger) + else: + caput(scan_pv+'.T'+str(dim)+'PV',"") + + def triggers_reset(self,verbose=True,**kwargs): + """ + sets the trigger1 to be the lower dim execute, does not change the other triggers + does nothing if scan_dim = 1 + """ + kwargs.setdefault('scan_dim',1) + scan_pv = self.ioc+"scan"+str(kwargs['scan_dim']) + + inner_scan_trigger = self.ioc+"scan"+str(kwargs['scan_dim']-1)+".EXSC" + if kwargs['scan_dim'] > 1: + caput(scan_pv+".T1PV",inner_scan_trigger) #Resets the higher dimensional scans to trigger the inner scan + + if verbose: + print("Scan record "+self.ioc+"Trigger 1 is reset") + + + ### checks + def check(self,scan_dim): + """ + checks pvs connects and that positioners are within limits + """ + self._check_pvs(scan_dim) + if not self._check_limits(scan_dim): + print("check positioner limits") + + def _check_pvs(self,scan_dim): + """ + Check if any of the detectors or positions pvs are not connected + + Previously: Scan_Check + """ + print('Checking if all detectors & positioners are connected...') + scan_pv = self.ioc+"scan"+str(scan_dim) + + #Detectors + for i in range(1,71): + det_num=(str(i).zfill(2)) + det_pv=caget(scan_pv+".D"+det_num+"PV") + if det_pv !='': + det=PV(det_pv); time.sleep(0.1)#smallest sleep to allow for PV traffic + if not det.connected: + print("Detector "+det_num+" has a bad PV: "+det.pvname+" not connected") + #Positioners + for i in range(1,5): + pos_num=str(i) + pos_pv=caget(scan_pv+".P"+pos_num+"PV") + if pos_pv != '': + pos=PV(pos_pv) + if not pos.connected: + print("Positioner "+pos_num+" has a BAD PV: "+pos.pvname+" not connected") + + def _check_limits(self,scan_dim): + """ + checks if the scan parameters are within the limits + returns limits_ok (True / False) + """ + scan_pv = self.ioc+"scan"+str(scan_dim) + caput(scan_pv+'.CMND',1) + time.sleep(.5) + SMSG = caget(scan_pv+'SMSG',as_string=True) + if len(SMSG) > 0: + print_warning_message('Positioner out of limits \n '+SMSG) + limits_ok = False + else: + limits_ok = True + + + ############################################################################################################## + ############################# table scans ############################## + ############################################################################################################## + def fillin_table(self,scan_dim,val_pv,rbv_pv,myarray,**kwargs): + """ + Fills in the scan record for table scans given positioner=posNum + myarray can be generated by myarray=Scan_MakeTable(StartStopStepLists) + + kwargs: + positioner_num: default => 1 + detector_settling_time: + positioner_settling_time + + Previously: Scan_FillIn_Table + """ + kwargs.setdefault("positioner_num",1) + kwargs.setdefault('detector_settling_time',default_detector_settling_time) + kwargs.setdefault('positioner_settling_time',default_positioner_settling_time) + + self.progress(scan_dim) + + scan_pv = self.ioc+"scan"+str(scan_dim) + posNum = kwargs['positioner_num'] + + caput(scan_pv+".P"+str(posNum)+"SM","TABLE") + caput(scan_pv+".P"+str(posNum)+"PV",val_pv) #Drive + caput(scan_pv+".R"+str(posNum)+"PV",rbv_pv) #Read + caput(scan_pv+".P"+str(posNum)+"PA",myarray) + caput(scan_pv+'.NPTS',len(myarray)) + + self.detector_settling_time(scan_dim,kwargs['detector_settling_time']) + self.positioner_settling_time(scan_dim,kwargs['positioner_settling_time']) + + #checking that PVs and positioner limits are good + self.check(scan_dim) + + def table_reset_after(self,scan_dim): + """ + resets positioner settling time 0.1 + sets all positionitonser to linear + clears positioners + + Previously: Scan_Reset_AfterTable + """ + #Setting everything back + scan_pv = self.ioc+"scan"+str(scan_dim) + + self.detector_settling_time(self.ioc,scan_dim) + + for i in range(1,5): + caput(scan_pv+".P"+str(i)+"SM","LINEAR") + + self.positioners_clear(self.ioc,scan_dim) + + ############################################################################################################## + ############################# progress, go and abort ############################## + ############################################################################################################## + def progress(self,scan_dim,verbose=True): + """ + Checks if a scan is in progress, and sleeps until it is done + + pv now includes the full 29idARPES: + Previously: Scan_Progress + """ + pv = self.ioc+"scan"+str(scan_dim) + check=caget(pv+".FAZE") + while (check!=0): + if verbose: + print(pv+" in progress - please wait...") + time.sleep(30) + check=caget(pv+".FAZE") + + def abort(self): + """ + aborts a scan for the command line + + Previously: Scan_Abort + """ + caput(self.ioc+"AbortScans.PROC",1) + + def go(self,verbose=True,**kwargs): + """ + checks limits and then starts a scan for a given ioc and scan_dim + + Previously: part of Scan_Go + """ + kwargs.setdefault('scan_dim',1) + scan_dim = kwargs['scan_dim'] + + self.progress(scan_dim) + + if verbose: + for i in range(1,scan_dim+1): + drive = caget(self.ioc+"scan"+str(i)+".P1PV") + start = caget(self.ioc+"scan"+str(i)+".P1SP") + stop = caget(self.ioc+"scan"+str(i)+".P1EP") + step = caget(self.ioc+"scan"+str(i)+".P1SI") + print('Scan'+str(i)+': '+drive+'= '+str(start)+' / '+str(stop)+' / '+str(step)) + + if self._check_limits(scan_dim): + filename = self.prefix(self.ioc) + fileNum = self.fileNum(self.ioc) + print(filename+str(fileNum)+" started at ", dateandtime()) + scan_pv = self.ioc+"scan"+str(scan_dim) + caput(scan_pv+".EXSC",1,wait=True,timeout=900000) #pushes scan button + log_update() # writes the log file + print(filename+str(fileNum)+" finished at ", dateandtime()) + print('\n') + + + ############################################################################################################## + ############################# empty and time scans ############################## + ############################################################################################################## + + + def empty_scan(self,**kwargs): + """ + starts a scan with out a drive + + Previously: Scan_Empty_Go + """ + kwargs.setdefault('execute',True) + self.fillin("","",0,1,1,**kwargs) + if kwargs['execute']: + self.go(**kwargs) + + def time_scan(self,duration_min,step_sec,**kwargs): + """ + starts a scan with the readback as time in seconds + + **kwargs + trigger_dictionary = {1:'29idb:ca5:read'} (default) + + Previously: Scan_Time_Go + """ + + stop=duration_min*60.0/step_sec + self.fillin("","time",1,stop,1,**kwargs) + + print("Time scan - Settling time : "+str(step_sec)) + if kwargs['execute']: + self.go(**kwargs) + + ############################################################################################################## + ############################# 2D scans typewriter/snake scans ############################## + ############################################################################################################## + def fillin_2D(self,inner_loop_list,outer_loop_list,outer_scan_dim=2,**kwargs): + """ + Fills in a the Scan Record for a 2D scan (Mesh), + InnerMotorList=[val_pv1,rbv_pv1,start1,stop1,step1] #scanDIM=1 + OuterMotorList=[val_pv2,rbv_pv2,start2,stop2,step2] #scanDIM=2 + kwargs: + num_points: False (default) => steps_points is the step size + True => steps_points is the number of points + positioner_num: default => 1 + detector_settling_time: default => 0.1 + snake: coming soon + """ + kwargs.setdefault("snake",False) + + #fillin inner loop + kwargs.update({'scan_dim':outer_scan_dim-1}) + self.fillin(inner_loop_list[0],inner_loop_list[1],inner_loop_list[2],inner_loop_list[3],inner_loop_list[4],**kwargs) + #fillin outer loop + kwargs.update({'scan_dim':outer_scan_dim}) + self.fillin(outer_loop_list[0],outer_loop_list[1],outer_loop_list[2],outer_loop_list[3],outer_loop_list[1],**kwargs) + + + + def snake_pv(self,snake_dim,enable=True): + """ + used for snake scans (as opposed to typewriter scans) + """ + snake_proc = self.snake_scan_userCalcOut(self.ioc,snake_dim=snake_dim,calc_num=1,enable=enable) + return snake_proc + + + def snake_scan_userCalcOut(self,snake_dim=1,calc_num=1,enable=False): + """ + for snake scans you need to add this userCalcOut to the after scan + it multiplies the step of the inner scan by -1 + + return the userCalcOut.PROC pv + """ + snake_calc_pv = self.ioc+"userCalcOut"+str(calc_num) #Snake userCalc + caput(self.ioc+"userCalcOutEnable.VAL",1) #global enable for userCalcs + + step_pv = self.ioc+"scan"+str(snake_dim)+".P1SI" + ### Setting up the user + caput(snake_calc_pv+".DESC","Snake Calc") + caput(snake_calc_pv+".INPA",step_pv+" NPP NMS") + caput(snake_calc_pv+".CALC$","A*-1*B") + caput(snake_calc_pv+".OUT",step_pv+" PP NMS") + + if enable: + caput(snake_calc_pv+".B",1) + else: + caput(snake_calc_pv+".B",0) + + return snake_calc_pv+".PROC" \ No newline at end of file diff --git a/build/lib/iexcode/instruments/scratch.py b/build/lib/iexcode/instruments/scratch.py new file mode 100644 index 0000000..ded5e79 --- /dev/null +++ b/build/lib/iexcode/instruments/scratch.py @@ -0,0 +1,44 @@ +############################################################################################################## +############################## setting folder from ARPES ############################## +############################################################################################################## +def folders_ARPES(user_name,**kwargs): + """ + Create and sets (if create_only=False) all the folders the current run and ARPES user + Sets the FileName for logging to be "UserName/YYYYMMDD_log.txt using logname_Set()" + + **kwargs: + set_folders = True (default); set the mda and EA scanRecords + = False; only makes the folders (was create_only) + run: run cycle e.g. 2022_1 (default => check_run() ) + ftp = True / False (default); print what needs to be added to the ftps crontab and create additional ftp folders + mda_ioc: will overwrite the default ioc for mda scans + debug + + """ + kwargs.setdefault('set_folders',True) + kwargs.setdefault('run',check_run()) + kwargs.setdefault('ftp',False) + kwargs.setdefault('debug',False) + + run = kwargs['run'] + + if kwargs['debug']: + print("run,folder,user_name,ioc,ftp: ",run,BL.folder,user_name,BL.ioc,kwargs['ftp']) + + # Create User Folder: + make_user_folders(run,BL.folder,user_name,BL.endstation,ftp=kwargs['ftp']) + sleep(5) + + if kwargs["set_folders"]: + # Set up MDA folder: + folder_mda(run,BL.folder,user_name,BL.prefix,BL.ioc) + logfile_name_set(BL.endstation) + logfile_header(BL.endstation,BL.ioc,ARPES_log_header()) + + + #Set up Scienta folders: + try: + userPath = "/net/s29data/export/data_29id"+BL.folder+"/"+run+"/"+user_name+"/" + folders_EA(userPath,filePrefix="EA") + except: + print_warning_message("EA ioc is not running, cannot set folder") \ No newline at end of file diff --git a/build/lib/iexcode/instruments/shutters.py b/build/lib/iexcode/instruments/shutters.py new file mode 100644 index 0000000..2afd842 --- /dev/null +++ b/build/lib/iexcode/instruments/shutters.py @@ -0,0 +1,112 @@ +""" +main shutter and branch shutter functions +""" +from time import sleep +from epics import caget, caput + +from iexcode.instruments.utilities import dateandtime, print_warning_message + + +############################################################################################################## +################################ main shutter ############################## +############################################################################################################## + +def main_shutter_open(): + """ + opens the main shutter + + Previously: Open_MainShutter + """ + caput("PC:29ID:FES_OPEN_REQUEST.VAL",1, wait=True,timeout=180000) + print("Opening Main Shutter...") + +def main_shutter_close(): + """ + closes the main shutter + + Previously: Close_MainShutter + """ + caput("PC:29ID:FES_CLOSE_REQUEST.VAL",1,wait=True,timeout=18000) + print("Closing Main Shutter...") + +def main_shutter_status(): + """ + checks on the status of the main shutter + and returns shutter_open = True / False + + """ + SS1=caget("EPS:29:ID:SS1:POSITION") + SS2=caget("EPS:29:ID:SS2:POSITION") + PS2=caget("EPS:29:ID:PS2:POSITION") + check=SS1*SS2*PS2 + + if check == 8: + shutter_open = True + else: + shutter_open = False + + return shutter_open + + +def main_shutter_check_open(): + """ + Checks main shutter is open, if not opens it" + + Previously: Check_MainShutter + """ + while True: + shutter_open = main_shutter_status() + if shutter_open == False: + print("MAIN SHUTTER CLOSED !!!" , dateandtime()) + main_shutter_open() + sleep(10) + else: + print("Shutter is now open" , dateandtime()) + break +############################################################################################################## +################################ branch shutters ############################## +############################################################################################################## + +def branch_shutter_status(branch,verbose=False): + """ + checks on the status of the main shutter + and returns shutter_open = True / False + """ + pvA="PA:29ID:S"+branch+"S_BLOCKING_BEAM.VAL" + pvB="PB:29ID:S"+branch+"S_BLOCKING_BEAM.VAL" + #"ON" = 1 => shutter open + status=caget(pvA)+caget(pvA) + if status == 2: + shutter_open=True + else: + shutter_open=False + + if verbose: + status = 'Open' if shutter_open else 'Closed' + print(branch+"-shutter is "+status) + return shutter_open + +def branch_shutter_close(branch): + """ + closes current branch shutter + + Previously: Close_BranchShutter + """ + caput("PC:29ID:S"+branch+"S_CLOSE_REQUEST.VAL",1,wait=True,timeout=18000) + print("Closing "+branch+"-Shutter...") + + +def branch_shutter_open(branch): + """ + Opens current branch shutter + + Previoulsy: Open_BranchShutter + + """ + shutter_status = branch_shutter_status + if shutter_status: + print(branch+"-Shutter already open...") + else: + caput("PC:29ID:S"+branch+"S_OPEN_REQUEST.VAL",1,wait=True,timeout=18000) + print("Opening "+branch+"-Shutter...") + \ No newline at end of file diff --git a/build/lib/iexcode/instruments/slits.py b/build/lib/iexcode/instruments/slits.py new file mode 100644 index 0000000..1b323e3 --- /dev/null +++ b/build/lib/iexcode/instruments/slits.py @@ -0,0 +1,509 @@ +from numpy import inf,nan + +from epics import caget, caput +from .IEX_endstations import * + + +from iexcode.instruments.shutters import main_shutter_close +from iexcode.instruments.utilities import print_warning_message, read_dict +from iexcode.instruments.VLS_PGM import mono_get_all +from iexcode.instruments.encoders import encoders_reset + +slit_ioc="29idb:" + +def slits_dictionary(): + """ + dictionary of slit pv names for two and four blade slit assemblies + + slit_name = slit1A, slit2B, slit3D + """ + #'slit3C' : (None, "uses a flexure stage so is different"), + + d = { + 'slit1A' : ('Slit1H','Slit1V'), + 'slit2B' : ('Slit2H','Slit2V'), + 'slit3D' : ('Slit4V') + } + return d + +def slits_pvs(slit_name): + """ + returns the rbv and drive for the size,center + (rbv_size,val_size),(rvb_center,val_center) + """ + slit_pv = slit_ioc + slit_name + size_rbv = slit_pv +'t2.D' + size_val = slit_pv +'center.VAL' + center_rbv = slit_pv +'t2.C' + center_val = slit_pv +'center.VAL' + + return (size_rbv,size_val),(center_rbv,center_val) + +def slits_synch(slit_name): + """ + synch the motor position and the slit table for all the beamline slits + """ + slit_pv = slit_ioc + slit_name+'sync.PROC' + caput(slit_pv,1) + +def slits_sync_all(): + """ + synch the motor position and the slit table for all the beamline slits + Slit-1A, slit-2B, slit-3C, slit-3D + + Previously: SyncAllSlits + """ + slit_list = slits_dictionary() + for slit_name in slit_list.keys(): + slits_synch(slit_name) + +def slits_print_all(): + """ + gets the current position of slit-1A, slit-2B, slit-3C, slit-3D + + Previously Get_Slits + """ + slits_sync_all() + slit1A_get(verbose=True) + slit2B_get(verbose=True) + + slit3C_get(verbose=True) + slit3D_get(verbose=True) + +def slits_get_all(verbose=False): + """ + returns a dictionary with the current slit status + """ + vals = {} + vals['slit1A_size'], vals['slit1A_center'] = slit1A_get(verbose=False) + vals['slit2B_size'], vals['slit2B_center'] = slit2B_get(verbose=False) + vals['slit3C_size'] = slit3C_get(verbose=False) + vals['slit3D_size'],vals['slit3D_center'] = slit3D_get(verbose=False) + + if verbose: + slits_print_all() + return vals + +def slits_set(slit_name,size,center,verbose=True): + """ + synchs the slit motors and the slit table and then sets the center and size + + slit_name is key in slits_dictionary + size = (H_size, V_size) + center = (H_center, V_center) + + Previously: SetSlit + """ + d = slits_dictionary() + #syncing + slits_synch(slit_name) + + for i,slit_HV in enumerate(d[slit_name]): #H and V + (size_rbv,size_val),(center_rbv,center_val) = slits_pvs(slit_HV) + caput(size_val, size[i],timeout=180) + caput(center_val, center[i],timeout=180) + + if verbose: + if (d[slit_name])>1: + print(slit_name + " = ("+str(round(size[0],3))+"x"+str(round(size[1],3))+") @ ("+str(center[0])+","+str(center[1])+")") + else: + print(slit_name + " = ("+str(round(size[0],3))+") @ ("+str(center[0])+")") + + return size,center + + +def slits_get(slit_name,verbose=True): + """ + returns the current H_size,V_size,H_center,V_center + + """ + d=slits_dictionary() + size = [] + center = [] + + #syncing + slits_synch(slit_name) + + for slit_HV in d[slit_name]: #H and V + (size_rbv,size_val),(center_rbv,center_val) = slits_pvs(slit_HV) + size.append(caget(size_rbv)) + center.append(caget(center_rbv)) + + if verbose: + if (d[slit_name])>1: + print(slit_name + " = ("+str(round(size[0],3))+"x"+str(round(size[1],3))+") @ ("+str(center[0])+","+str(center[1])+")") + else: + print(slit_name + " = ("+str(round(size[0],3))+") @ ("+str(center[0])+")") + + return tuple(size),tuple(center) + +def slits_set_size(slit_name,size): + """ + sets the slit size with the current slit center + + size = (H,V) for slit1A, slit2B + size = V for slit3D + """ + size_current,center = slits_get(slit_name,verbose=False) + slits_set(size,center) + +def slits_set_center(slit_name,center): + """ + sets the slit size with the current slit center + + center = (H,V) for slit1A, slit2B + center = V for slit3D + """ + size,center_current = slits_get(slit_name,verbose=False) + slits_set(size,center) + +def slits_scan_size(slit_name,direction,start,stop,step,**kwargs): + """ + slit_name = 'slit1A','slit2B','slit3D' + direction = 'H', 'V' + **kwargs + center: slits center, if not specified then uses current center position + """ + kwargs.setdefault('center',slits_get(slit_name,verbose=False)[1]) + kwargs.setdefault('execute',True) + + slit_H,slit_V = slits_dictionary()[slit_name] + + if direction == 'H': + (size_rbv,size_val),(center_rbv,center_val) = slits_pvs(slit_H) + if direction == 'V': + (size_rbv,size_val),(center_rbv,center_val) = slits_pvs(slit_V) + + #set slit center + slits_set_center(slit_name,kwargs['center']) + #scan + BL.mda.fillin(size_rbv,size_val,start,stop,step,**kwargs) + if kwargs['execute']: + BL.mda.go(**kwargs) + +def slits_scan_center(slit_name,direction,start,stop,step,**kwargs): + """ + slit_name = 'slit1A','slit2B','slit3D' + direction = 'H', 'V' + **kwargs + size: slits size, if not specified then uses current size + """ + kwargs.setdefault('size',slits_get(slit_name,verbose=False)[0]) + kwargs.setdefault('execute',True) + + slit_H,slit_V = slits_dictionary()[slit_name] + + if direction == 'H': + (size_rbv,size_val),(center_rbv,center_val) = slits_pvs(slit_H) + if direction == 'V': + (size_rbv,size_val),(center_rbv,center_val) = slits_pvs(slit_V) + + #set slit center + slits_set_size(slit_name,kwargs['size']) + #scan + BL.mda.fillin(center_rbv,center_val,start,stop,step,**kwargs) + if kwargs['execute']: + BL.mda.go(**kwargs) + +def slit1A_scribe_marks(): + """ + moves slit1A to the position which corresponds to the scribe marks + used to check if we have lost steps + + Dial values should be 0 on when on the scribe marks + User values are set to correspond to the center of the beam + + Previously: GoToSlit1AScribe + """ + main_shutter_close() + for m in range(9,13): + caput('29idb:m'+str(m)+'.DVAL',0) + +def slit1A_set(H_size,V_size,verbose=True,**kwargs): + """ + sets the first aperture / slit to the specificed size + if Hsize / Vsize = inf then goes to maximim 4.5 / 4.5 + **kwargs + center = slit center, by default => (0,0) + + Previously: SetSlit1A + """ + kwargs.setdefault('center',(0,0)) + slit_name = 'slit1A' + + if H_size in [inf,nan,None]: + H_size=4.5 + if V_size in [inf,nan,None]: + V_size=4.5 + + size = (H_size,V_size) + center = kwargs['center'] + slits_set(slit_name,size,center, verbose=verbose) + +def slit1A_get(verbose=True): + """ + returns the current (H_size,V_size),(H_center,V_center) + """ + slit_name = "slit1A" + + return slits_get(slit_name,verbose=verbose) + +def slit2B_set(H_size,V_size,verbose=True,**kwargs): + """ + sets the clean up aperture / slit downstream of the mono to the specificed size + if Hsize / Vsize = inf then goes to maximim 6 / 8 + + **kwargs + center = slit center, by default => (0,0) + + Previously: SetSlit2B + """ + kwargs.setdefault('center',(0,0)) + slit_name = "slit2B" + + if H_size in [inf,nan,None]: + H_size=6 + if V_size in [inf,nan,None]: + V_size=8 + + size = (H_size,V_size) + center = kwargs['center'] + slits_set(slit_name,size,center, verbose=verbose) + +def slit2B_set_small(Hsize,Vsize,coef,verbose=True,**kwargs): + """ + Used to scale the aperature size by coef + Hsize = Hsize * coef + Vsize = Vsize * coef + """ + Hsize = Hsize * coef + Vsize = Vsize * coef + print_warning_message("closing Slit-2B down by a factor of", coef, "!!!") + slit2B_set(Hsize,Vsize,verbose=True,**kwargs) + +def slit2B_get(verbose=True): + """ + returns the current (H_size,V_size),(H_center,V_center) + """ + slit_name = "slit2B" + return slits_get(slit_name,verbose=verbose) + +def slit2B_encoders_reset(Hcenter,Vcenter): + """ + Resetting Slit 2B encoders to 0 for a given position (Hcenter,Vcenter). + Slit size need to be set to 0. + + Previously: Reset_Slit2B_Encoders + """ + encoders_reset('slit2B',Hcenter,Vcenter) + slits_synch('slit2B') + + +def slit3C_set(size,verbose=True): + """ + sets slit-3C (ARPES resolution defining slit) + + Previously SetSlit3C + """ + position=round(slit_3C_fit(size),1) + caput("29idb:Slit3CFit.A",size,wait=True,timeout=18000) + + if verbose: + print("Slit-3C =",size,"um - ( m24 =",position,")") + +def slit3C_get(size,verbose=True): + """ + gets slit-3C (ARPES resolution defining slit) + returns size, position + """ + size, position = slit_3C_rbv() + + if verbose: + print("Slit-3C =",size,"um - ( m24 =",position,")") + + return size, position + +def slit3D_set(V_size,verbose=True,**kwargs): + """ + sets the clean up aperture / slit downstream of the mono to the specificed size + if Hsize / Vsize = inf then goes to maximim 6 / 8 + + **kwargs + center = slit center, by default => current value + + Previously: SetSlit3D + """ + kwargs.setdefault('center',slit3D_get(verbose=False)[1]) + slit_name = "slit3D" + + if V_size in [inf,nan,None]: + V_size=2000 + + size = (V_size) + center = kwargs['center'] + slits_set(slit_name,size,center,verbose=verbose) + + +def slit3D_get(verbose=True): + """ + returns the current V_size,V_center + """ + slit_name = "slit1A" + (V_size),(V_center) = slits_get(slit_name) + V_size = round(V_size,3) + V_center = round(V_center,3) + + return V_size,V_center + +def slit3D_encoders_reset(Hcenter,Vcenter): + """ + Resetting Slit 2B encoders to 0 for a given position (Hcenter,Vcenter). + Slit size need to be set to 0. + + Previously: Reset_Slit2B_Encoders + """ + encoders_reset('slit3D',Hcenter,Vcenter) + slits_synch('slit3D') + +def exit_slit(branch, size, verbose=True): + """ + verbose used to supress printing + + Previously: SetExitSlit + """ + if branch == "c": + slit3C_set(size, verbose) + elif branch == "d": + slit3D_set(size, verbose) + elif branch == "e": + slit3D_set(size, verbose) + + + + + +############################################################################################################## +################################ slit fits ############################## +############################################################################################################## + + +def aperture_fit(hv,slit_num): + """ + used close the beamline apertures/slits to only take the center of the beam, + i.e. no heat bump (determined emperically by looking at the the shift in energy vs slit position) + + Previously Aperture_Fit + """ + K=slit_Coef(slit_num)[1] + sizeH=K[0]+K[1]*hv+K[2]*hv*hv + sizeV=K[3]+K[4]*hv+K[5]*hv*hv + return [round(sizeH,3),round(sizeV,3)] + +def slit_Coef(slit_num): + """ + 3rd order polynomials coeffients for closing slit1A / slit2B to minimize heat bump effects + slit_num = 1 / 2 for slit1A / slit2B + """ + if slit_num == 1: + pv='29id:k_slit1A' + #Redshifted x (H): + H0=2.3325 + H1=-.000936 + H2=2.4e-7 + #Redshifted z (V): + V0=2.3935 + V1=-.0013442 + V2=3.18e-7 + if slit_num == 2: + pv='29id:k_slit2B' + #Redshifted x (H): + H0=3.61 + H1=-0.00186 + H2=5.2e-7 + #Redshifted z (V): + V0=6.8075 + V1=-0.003929 + V2=9.5e-7 + K=H0,H1,H2,V0,V1,V2 + return pv,K + + +def slit_3C_fit(size): + """ + used to convert slit size to motor position for slit 3C - flexure stage + + empirically determine offline with a camera + + Previously: Slit3C_Fit + """ + K0=-36.383 + K1=0.16473 + K2=-0.00070276 + K3=8.4346e-06 + K4=-5.6215e-08 + K5=1.8223e-10 + K6=-2.2635e-13 + motor=K0+K1*size+K2*size**2+K3*size**3+K4*size**4+K5*size**5+K6*size**6 + return motor + +def slit_3C_rbv(): + """ + used to convert slit motor position to size + + Previously: Slit3C_RBV + """ + position=caget("29idb:m24.RBV") + K0=299.66 + K1=16.404 + K2=1.5572 + K3=0.14102 + K4=0.0064767 + K5=0.00014501 + K6=1.2617e-06 + size=round(K0+K1*position+K2*position**2+K3*position**3+K4*position**4+K5*position**5+K6*position**6,0) + return size, position + +############################################################################################################## +################################ beamline slits ############################## +############################################################################################################## + +def slits_set_BL(c_2B=1,c_1A=1,verbose=True): + """ + Sets slit-1A and slit-2B for the current + grating and photon energy to remove the heat bump + + c_1A and c_2B are used to take a different ratio of the beam + (1 = normal ops, < 1 to reduce flux) + + Previously: SetSlit_BL + """ + mono_vals=mono_get_all() + hv_rbv = mono_vals['ENERGY_MON'] + grt = mono_vals['GRT'] + + # slit were defined for the range: 500 - 2000 + hv = max(hv_rbv,500) + hv = min(hv_rbv,2000) + c = 4.2/2.2 # to account for the magnification difference between gratings + + if grt == 'MEG': + V=0.65 # set to 65% of RR calculation for both grt => cf 2016_2_summary + elif grt=='HEG': + V=0.65*c # set to 65% of RR calculation (no longer 80%) => cf 2016_2_summary + + try: + slit_position = read_dict(FileName='Dict_Slit.txt') + except KeyError: + print_warning_message("Unable to read dictionary") + return + + V2_center= slit_position[grt]['S2V'] + H2_center= slit_position[grt]['S2H'] + V1_center= slit_position[grt]['S1V'] + H1_center= slit_position[grt]['S1H'] + + Size1A=( aperture_fit(hv,1)[0]*c_1A, aperture_fit(hv,1)[1]*c_1A ) + Size2B=( aperture_fit(hv,2)[0]*c_2B, round(aperture_fit(hv,2)[1]*c_2B*V,3)) + slit1A_set(Size1A[0],Size1A[1],H1_center,V1_center,verbose) # standard operating + slit1A_set(Size2B[0],Size2B[1],H2_center,V2_center,verbose) + diff --git a/build/lib/iexcode/instruments/spec_stuff.py b/build/lib/iexcode/instruments/spec_stuff.py new file mode 100644 index 0000000..61cc859 --- /dev/null +++ b/build/lib/iexcode/instruments/spec_stuff.py @@ -0,0 +1,14 @@ + +def folders_spec(run,folder,UserName): + if folder == "b": + UserName = "Staff" + else: + UserName = UserName+"/" + MyPath="/home/beams22/29IDUSER/spec/data/"+run+"/"+UserName + print("\nSPEC folder: " + MyPath) + print("You will need to create folder and set up the path manually in SPEC:") + print(" cd "+MyPath) + print(" newfile FileName") + print("To start SPEC fresh: ./bin/kappa29ID -f") + #if not (exists(MyPath)): + # mkdir(MyPath) diff --git a/build/lib/iexcode/instruments/staff.py b/build/lib/iexcode/instruments/staff.py new file mode 100644 index 0000000..b9d6eef --- /dev/null +++ b/build/lib/iexcode/instruments/staff.py @@ -0,0 +1,82 @@ +from os import listdir,mkdir,chmod +from os.path import join, isfile, exists + +#from .ARPES import folders_ARPES +#from .Kappa import folders_Kappa + + +def folders_startup(run): + """ + Creates the run directories for the following: + data_29idb + data_29idc + data_29idd + + calls folder_ARPES('Staff');folder_Kappa('Staff') + + print text required to modify the rsynch crontab + + previously: Folders_Startup + """ + data_directories=['data_29idb','data_29idc','data_29idd'] + dserv="/net/s29data/export/" + for dataDir in data_directories: + path=join(dserv,dataDir,run) + print(path) + if not (exists(path)): + mkdir(path) + + #folders_ARPES('Staff',create_only=True) + #folders_Kappa('Staff',create_only=True) + + txt="\n\n\nupdate the rsync portion the 29id@nerdy crontab\n" + txt+="*/1 * * * * /usr/bin/rsync -av --exclude=core /net/s29data/export/data_29idd/"+run+" kip:/net/kip/sftp/pub/29iddftp/files > /home/beams22/29ID/cronfiles/cptoftp-currrun-d.log 2>&1" + txt+=("\n") + txt+="*/1 * * * * /usr/bin/rsync -av --exclude=core /net/s29data/export/data_29idd/"+run+" kip:/net/kip/sftp/pub/29iddftp/files > /home/beams22/29ID/cronfiles/cptoftp-currrun-d.log 2>&1" + print(txt) + +############################################################################################################## +############################## staff detectors ############################## +############################################################################################################## + + +def staff_detector_dictionary(xrays=True): + """ + returns a dictionary of the default detectors + + Previously: Detector_Default + """ + det_dict={} + diodes={ + 15:"29idb:ca15:read" + } + slits_apertures={ + 58:"29idb:Slit1Ht2.C", + 59:"29idb:Slit1Ht2.D", + 60:"29idb:Slit1Vt2.C", + 61:"29idb:Slit1Vt2.D", + 62:"29idb:Slit2Ht2.C", + 63:"29idb:Slit2Ht2.D", + 64:"29idb:Slit2Vt2.C", + 65:"29idb:Slit2Vt2.D", + 66:"29idb:Slit3CRBV", + 67:"29idb:Slit4Vt2.C" + } + vacuum_shutters={ + 61:"29idb:VS1A.VAL", + 62:"29idb:VS2A.VAL", + 63:"29idb:VS3AB.VAL", + 64:"29idb:VS4B.VAL", + 65:"29idb:IP4B.VAL", + 66:"PA:29ID:SCS_BLOCKING_BEAM.VAL", + 67:"PA:29ID:SDS_BLOCKING_BEAM.VAL" + } + mono_details={ + 68:"29idmono:ENERGY_SP", + 69:"29idmonoMIR:P.RBV", + 70:"29idmonoGRT:P.RBV" + } + det_dict.update(diodes) + det_dict.update(slits_apertures) + det_dict.update(mono_details) + return det_dict \ No newline at end of file diff --git a/build/lib/iexcode/instruments/storage_ring.py b/build/lib/iexcode/instruments/storage_ring.py new file mode 100644 index 0000000..a2269b8 --- /dev/null +++ b/build/lib/iexcode/instruments/storage_ring.py @@ -0,0 +1,23 @@ + + +""" +Functions dealing with the storage ring pvs + +""" +from epics import caget +from time import sleep + +from iexcode.instruments.utilities import dateandtime + +def wait_for_beam(): + """ + Monitors the storage ring current and breaks when the ring current is above 60 mA + Checks the status every 30 seconds + """ + while True: + SR=caget("S:SRcurrentAI.VAL") + if (SR<80): + sleep(30) + else: + print("Beam is back -"+dateandtime()) + break \ No newline at end of file diff --git a/build/lib/iexcode/instruments/userCalcs.py b/build/lib/iexcode/instruments/userCalcs.py new file mode 100644 index 0000000..367ceec --- /dev/null +++ b/build/lib/iexcode/instruments/userCalcs.py @@ -0,0 +1,177 @@ +from epics import caget, caput +############################################################################################################## +########################### String Sequences ###################### +############################################################################################################## +def userStringSeq_pvs(ioc, seq_num): + """ + returns the proc pv for a given userStringSeq + userStringSeq, proc_pv + + Previously: BeforeScan_StrSeq, AfterScan_StrSeq + """ + userStringSeq_pv = ioc+"userStringSeq"+str(seq_num) + userStringSeq_proc = userStringSeq_pv+".PROC" + return userStringSeq_pv, userStringSeq_proc + + +def userStringSeq_clear(ioc,seq_num): + """ + clears the nth suserStringSeq in the specified ioc + + Previously: ClearStringSeq + """ + pv=ioc+"userStringSeq"+str(seq_num) + caput(pv+".DESC","") + for i in range(1,10): + caput(pv+".STR"+str(i),"") + caput(pv+".LNK"+str(i),"") + caput(pv+".DOL"+str(i),"") + caput(pv+".DO"+str(i),0.0) + caput(pv+".DLY"+str(i),0.0) + caput(pv+".WAIT"+str(i),"NoWait") + caput(pv+".STRA","") + caput(pv+".LNKA","") + caput(pv+".DOLA","") + caput(pv+".DOA",0.0) + caput(pv+".DLYA",0.0) + caput(pv+".WAITA","NoWait") + caput(pv+".FLNK","") + return pv + +############################################################################################################## +########################### User CalcsOut ###################### +############################################################################################################## +def userCalcOut_clear(ioc,calcOut_num): + """ + clears the nth userCalcOut in the specified ioc + + Previously:ClearCalcOut + """ + pv=ioc+"userCalcOut"+str(calcOut_num) + caput(pv+".DESC","") + + for i in ["A","B","C","D","E","F","G","H","I","J","K","L"]: + caput(pv+".INP"+i,"") + caput(pv+"."+i,0) + + caput(pv+".CALC$","A") + caput(pv+".OCAL$","A") + caput(pv+".OUT","") + caput(pv+".OOPT","On Change") + return pv + + +############################################################################################################## +########################### User Calcs, User Average User Average ###################### +############################################################################################################## +def userAvg_clear(ioc,userAvg_num): + """ + clears the nth userAvg in the specified ioc + + Previously: ClearUserAvg + """ + pv = ioc+"userAve"+str(userAvg_num) + caput(pv+".DESC","") + caput(pv+".INPB","") + return pv + +def userAvg_pvs(ioc, userAve_num): + """ + returns the proc pv for a given userStringSeq + userStringSeq, proc_pv + + """ + userAve_pv = ioc+"userAve"+str(userAve_num) + userAve_proc = userAve_pv+".PROC" + return userAve_pv, userAve_proc + + +def userAvg_trigger_strSeq(ioc,num,userAvg_num=8): + """ + num = number of userAvg + """ + userAvg_num = 8 + userAvg_clear(ioc,userAvg_num) + str_pv="29id"+ioc+":userStringSeq"+str(userAvg_num) + caput(str_pv+".DESC","Average Triggers_"+ioc) + if num < 10: + str_num = str(num) + else: + str_num = "A" + for userAve_num in range(1,userAve_num+1,1): + if userAve_num < 10: + userAve_num = str(userAve_num) + else: + userAve_num = "A" + userAve_pv, userAve_proc = userAvg_pvs(ioc, userAve_num) + caput(str_pv+".LNK"+userAve_num,userAve_proc+" CA NMS") + caput(str_pv+".WAIT"+userAve_num,"After"+str_num) + + +def UserAvg_setup(ioc,userAvg_num,det_num,average_pts,UserAvg_trigger_strSeq_pv,scan_dim=1): + """ + kappa + userAv_num 1->10 + det_num 61 -> 70 + Previously:UserAvg + """ + userAvg_pv = ioc+"userAve"+str(userAvg_num) + scan_pv = ioc+"scan"+str(scan_dim) + det_pv = scan_pv+".D"+str(det_num)+"PV" + + trigger_strSeq_pv,trigger_strSeq_proc = userAvg_trigger_strSeq(ioc) + + if average_pts > 0: + caput(scan_pv+".T2PV",trigger_strSeq_proc) #set all Avg as Det + caput(userAvg_pv+".A",average_pts) + caput(det_pv,userAvg_pv+".VAL") + print("User Average enabled") + elif average_pts == 0: + caput(scan_pv+".T2PV","") + caput(det_pv,"") + print("User Average disabled") + + +def userAvg_setup(ioc,pv,userAvg_num,name,average_pts=10,mode="ONE-SHOT"): + """ + + Previously: UserAvg_PV + """ + trigger_strSeq_pv,trigger_strSeq_proc = userAvg_trigger_strSeq(ioc) + + # Fill User Average: + userAvg_clear(ioc,userAvg_num) + userAve_pv, userAve_proc = userAvg_pvs(ioc, userAvg_num) + + caput(userAve_pv+".SCAN","Passive") + caput(userAve_pv+".INPB",trigger_strSeq_pv+" CP NMS") + caput(userAve_pv+".A",average_pts) + caput(userAve_pv+".PREC",9) + caput(userAve_pv+"_mode.VAL",mode) + caput(userAve_pv+"_algorithm.VAL","AVERAGE") + caput(userAve_pv+".DESC",name) + + +def userAvg_trigger_strSeq(ioc,num, seq_num=8): + """ + + Previously: UserAvg_Trigger_StrSeq + """ + userStringSeq_clear(ioc,seq_num) + trigger_strSeq_pv,trigger_strSeq_proc = userStringSeq_pvs(ioc, seq_num) + caput(trigger_strSeq_pv+".DESC",ioc+" Average Triggers") + + if num < 10: + str_num=str(num) + else: + str_num="A" + for i in range(1,num+1,1): + if i < 10: + i=str(i) + else: + i="A" + avg_pv="29id"+ioc+":userAve"+i + caput(trigger_strSeq_pv+".LNK"+i,avg_pv+"_acquire.PROC CA NMS") + caput(trigger_strSeq_pv+".WAIT"+i,"After"+str_num) + + return trigger_strSeq_pv,trigger_strSeq_proc \ No newline at end of file diff --git a/build/lib/iexcode/instruments/utilities.py b/build/lib/iexcode/instruments/utilities.py new file mode 100644 index 0000000..3e4b739 --- /dev/null +++ b/build/lib/iexcode/instruments/utilities.py @@ -0,0 +1,213 @@ +""" +useful functions + +""" +import time +import datetime +import select +import sys +import ast +import os +import numpy as np +from numpy import inf + +from epics import caget, caput + +def dateandtime(): + return time.strftime("%a %d %b %Y %H:%M:%S",time.localtime()) + +def today(which='default'): + dt=datetime.date.today() + today_year = dt.year + today_month = dt.month + today_day = dt.day + if which == 'slash': + today_str=("{:02d}/{:02d}/{:04d}".format(today_month,today_day,today_year)) + elif which == 'int': + today_str=int(("{:04d}{:02d}{:02d}".format(today_year,today_month,today_day))) + else: # 'default': + today_str=("{:04d}{:02d}{:02d}".format(today_year,today_month,today_day)) + return today_str + + +def print_warning_message(message_string): + print('\n==================================') + print("WARNING: " +message_string+ " !!!!! ") + print('==================================\n') + + +def wait_for_it(D,H,M): + """ + D = how many days from now + H,M = what time that day in 24h clock + + e.g.: if today is Wed Nov 21 at 14:00 + WaitForIt(2,9,0) => Fri Nov 23 at 9:00 + """ + t = datetime.datetime.now()() + day = datetime.timedelta(days=D) + future = t + day + returnTime = datetime.datetime(future.year, future.month, future.day, int(H), int(M)) + timeToWait = returnTime - t + s=round(timeToWait.total_seconds(),1) + m=round(timeToWait.total_seconds()/60.0,1) + h=round(timeToWait.total_seconds()/3600.0,1) + print("Now is: "+str(t)) + print("Target date: "+str(returnTime)) + print("Sleeping for "+str(s)+" s = "+str(m)+" m = "+str(h)+" h") + print("Waaaaaaaait for it...") + time.sleep(timeToWait.total_seconds()) + print(dateandtime()) + + +def input_timeout(question,t): + print("You have "+str(t)+" seconds to answer!") + i, o, e = select.select( [sys.stdin], [], [], t ) + if (i): + print("You said", sys.stdin.readline().strip()) + else: + print("You said nothing!") + +def playsound(sound='FF'): + """ + plays a sound when run + 'FF' Final Fantasy victory sound + 'ding' a subtle ding noise + 'hallelujah' hallelujah chorus + """ + if sound == 'FF': + sounds = '/home/beams/29IDUSER/Documents/User_Macros/Macros_29id/Sound_Files/VictoryFF.wav' + elif sound == 'ding': + sounds = '/home/beams/29IDUSER/Documents/User_Macros/Macros_29id/Sound_Files/ding.wav' + elif sound == 'hallelujah': + sounds = '/home/beams/29IDUSER/Documents/User_Macros/Macros_29id/Sound_Files/hallelujah.wav' + os.system('aplay ' + sounds) + +def RangeUp(start,end,step): + while start <= end: + yield start + start += abs(step) + +def RangeDown(start,end,step): + while start >= end: + yield start + start -= abs(step) + +def EgForLoop(): + for hv in RangeDown(2000,500,-500): + print(hv) + for hv in RangeUp(500,2000,500): + print(hv) + +def take_closest_value(my_list,my_number): + """ + Given a list of integers, I want to find which number is the closest to a number x. + + Previously: TakeClosest + """ + return min(my_list, key=lambda x:abs(x-my_number)) + +def read_dict(filename,**kwargs): + """ + filename = name with extension of dictionary to read + **kwargs: + path = path to folder containing dictionary file (default: .IEX_dictionaries) + """ + if 'path' in kwargs: + path = kwargs['path'] + else: + path = os.path.join(os.path.dirname(__file__),'IEX_dictionaries') + fpath = os.path.join(path,filename) + with open(fpath) as f: + for c,line in enumerate(f.readlines()): + if line[0] == '=': + lastdate=line[8:16] + lastline=line + mydict=ast.literal_eval(lastline) + return mydict + + +def make_table(start_stop_step_lists): + """ + Creates and returns a np.array with values based on StartStopStepList + StartStopStepList is a list of lists defining regions for a table array + StartStopStepList[[start1,stop1,step1],[start1,stop1,step1],...] + Automatically removes duplicates and sorts into ascending order + if you want descending + myarray=XAS_Table(StartStopStepLists)[::-1] + + Previously: Scan_MakeTable + """ + table_array=np.array([]) + if type(start_stop_step_lists) is not list: + start = start_stop_step_lists[0] + stop = start_stop_step_lists[1] + step = start_stop_step_lists[2] + j = start + while j<=stop: + table_array=np.append(table_array, j) + j+=step + else: + for i in range(0,len(start_stop_step_lists)): + start=start_stop_step_lists[i][0] + stop=start_stop_step_lists[i][1] + step=start_stop_step_lists[i][2] + j=start + while j<=stop: + table_array=np.append(table_array, j) + j+=step + table_array=np.unique(table_array)#removing duplicate + table_array=np.sort(table_array) #sort into ascending order + return table_array + + + +def SendText(msg,which='user'): + """ + + which = 'user' or 'staff' + + Edit the following file with the correct email/phone number: + [sleepy /home/beams22/29ID/bin] pvMailSomething # for staff + [sleepy /home/beams22/29IDUSER/bin] pvMailUser # for user + + Use the following website to figure out carriers email: + https://20somethingfinance.com/how-to-send-text-messages-sms-via-email-for-free/ + + In a terminal, run the screen session: + pvMailUser (or pvMailSomething) + + To kill the screen session: + screen -ls #show list of screen session + screen -r screen# #reattached to a given screen session + screen -XS screen# kill #kill a given screen session + + + """ + if which == 'staff':n='6' + elif which == 'user':n='7' + caput('29idb:userCalcOut'+n+'.DESC',msg) + caput('29idb:userCalcOut'+n+'.A',0) + time.sleep(1) + caput('29idb:userCalcOut'+n+'.A',1) + print('Sending text/email') + + +def AbortScript(): + """ + Example: + while True: + try: + print 'do something' + sleep(5) + except KeyboardInterrupt: + if AbortScript() == 'y': + break + """ + try: + print('\n\nWARNING: Do you want to abort?') + print('Type y to ABORT, anything else to CONTINUE >') + foo = input() + return foo + except KeyboardInterrupt as e: + raise e diff --git a/build/lib/iexcode/instruments/vortexs29.py b/build/lib/iexcode/instruments/vortexs29.py new file mode 100644 index 0000000..81deb73 --- /dev/null +++ b/build/lib/iexcode/instruments/vortexs29.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python + +from epics import caget, caput +from time import sleep, gmtime, strftime, localtime + +#Define caput with wait function +def caputw(PVname,PVinput): + return caput(PVname,PVinput,wait=True, timeout = 100000) + +def dateandtime(): + return strftime("%a %d %b %Y %H:%M:%S",localtime()) + +def vortex(energy,time=1): + cen, wid = vortex_Ecal(energy) + set_roi1(cen,wid) + +def set_roi1(cen,wid): + caput('29idVORTEX:mca1.R0LO',round(cen-wid/2)) + caput('29idVORTEX:mca1.R0HI',round(cen+wid/2)) + +def set_roi2(cen,wid): + caput('29idVORTEX:mca1.R1LO',round(cen-wid/2)) + caput('29idVORTEX:mca1.R1HI',round(cen+wid/2)) + +def vortex_Ecal(energy): + cen = 12.8 + 0.72*energy + wid = 120 + return cen, wid + +def mcacounttime(time): + caput('29idVORTEX:mca1.PRTM',time) + +def runmca(time): + caput('29idVORTEX:mca1.PRTM',time) + sleep(0.1) + caputw('29idVORTEX:mca1EraseStart',1) + +def mcaoff(): + caput('29idd:Unidig1Bo1',1) + +def mcaon(): + caput('29idd:Unidig1Bo1',0) + +def savemca(): + caput('29idKappa:scan1.T2PV','29idVORTEX:scanH.EXSC') + caput('29idKappa:scan1.D16PV','29idVORTEX:mca1.R0') + caput('29idKappa:scan1.D17PV','29idVORTEX:mca1.R1') + caput('29idKappa:scan1.D49PV','29idd:userTran1.D') + caput('29idKappa:scan1.D50PV','29idd:userTran1.E') + sleep(1) + +def nosavemca(): + caput('29idKappa:scan1.T2PV','29idVORTEX:mca1EraseStart') + caput('29idKappa:scan1.D16PV','29idVORTEX:mca1.R0') + caput('29idKappa:scan1.D17PV','29idVORTEX:mca1.R1') + caput('29idKappa:scan1.D49PV','29idd:userTran1.D') + caput('29idKappa:scan1.D50PV','29idd:userTran1.E') + sleep(1) + +def nomca(): + caput('29idKappa:scan1.T2PV','') + caput('29idKappa:scan1.D16PV','') + caput('29idKappa:scan1.D17PV','') + caput('29idKappa:scan1.D49PV','') + caput('29idKappa:scan1.D50PV','') + sleep(1) + + +def scan_num(): + scan = caget('29idKappa:saveData_message') + loc = scan.find('.') + if (loc==-1): + scannum=0 + else: + scannum =int(scan[loc-4:loc]) + return scannum + +def mcafileinit(): + # mainpath = '/net/s4data/export/sector4/4idc/mda' + scanrecpath = caget('29idKappa:saveData_subDir') + scan = scan_num()+1 + caput('4idcVORTEX:saveData_subDir', '/'+ scanrecpath+'/S'+str(scan)) + caput('4idcVORTEX:saveData_scanNumber',1) + + +def chkmcasave(): + if (caget('29idKappa:scan1.T2PV')=='29idVORTEX:scanH.EXSC'): + print('Saving mca files') + mcafileinit() + + diff --git a/build/lib/iexcode/instruments/xrays.py b/build/lib/iexcode/instruments/xrays.py new file mode 100644 index 0000000..aa02940 --- /dev/null +++ b/build/lib/iexcode/instruments/xrays.py @@ -0,0 +1,700 @@ +""" +functions related to using x-rays + +""" +import numpy as np +from time import sleep + +from epics import caget,caput +from iexcode.instruments.IEX_endstations import BL + +from iexcode.instruments.resolution import * +from iexcode.instruments.IEX_VPU import * +from iexcode.instruments.VLS_PGM import * +from iexcode.instruments.slits import * +from iexcode.instruments.shutters import * +from iexcode.instruments.gate_valves import * +from iexcode.instruments.diagnostics import * +from iexcode.instruments.m3r import * +from iexcode.instruments.logfile import * +from iexcode.instruments.utilities import print_warning_message,make_table, take_closest_value +from iexcode.instruments.mpa import * +from iexcode.instruments.current_amplifiers import ca_average +from iexcode.instruments.beamline import branch_cams_enable + +from iexcode.instruments.ARPES import ARPES_mprint,ARPES_extra_pvs +from iexcode.instruments.Kappa import Kappa_mprint +from iexcode.instruments.electron_analyzer import getSESslit, EA +############################################################################################################## +############################## resets and detector lists ############################## +############################################################################################################## +def test_function(): + print('hello world') + +def xrays_reset(): + mono_limits_reset() + xray_motor_encoder_sync() + + +def xrays_detector_list(): + """ + defualt detectors for the APRES endstation + used by: + CA_Live_StrSeq() + Detector_Triggers_StrSeq() + BeforeScan_StrSeq() => puts everybody in passive + CA_Average() + WARNING: can't have more than 5 otherwise CA_Live_StrSeq gets angry. + + Previously: part of Detector_List + """ + ca_list=[["b",4],["b",13]] + + return ca_list + + +def xrays_detector_dictionary(): + """ + returns a dictionary of the default detectors + + Previously: Detector_Default + """ + det_dict={} + sr_main_shutter={ + 1:"S:SRcurrentAI.VAL", + 2:"EPS:29:ID:SS1:POSITION" + } + bl_energy={ + 3:"29idmono:ENERGY_MON", + 4:"ID29:EnergySet.VAL", + 5:"ID29:Energy.VAL" + } + bl_keithleys={ + 6:"29idb:ca1:read", + 7:"29idb:ca2:read", + 8:"29idb:ca3:read", + 9:"29idb:ca4:read", + 10:"29idb:ca5:read", + 11:"29idb:ca9:read", + 12:"29idb:ca12:read", + 13:"29idb:ca13:read" + } + det_dict.update(sr_main_shutter) + det_dict.update(bl_energy) + det_dict.update(bl_keithleys) + return det_dict + +############################################################################################################## +############################## get beamline info ############################## +############################################################################################################## + +def xrays_get_all(verbose=False): + """ + gets info about current beamline setting + returns a dictionary + + """ + vals={} + print("\n===========================================================") + vals.update(ID_get_all(verbose=True)) + vals.update(mono_get_all(verbose=True)) + print("-----------------------------------------------------------") + vals.update(slits_get_all(verbose=True)) + vals.update(FMB_mirror_get(0,verbose=True)) + vals.update(FMB_mirror_get(1,verbose=True)) + vals.update(FMB_mirror_get(3,verbose=True)) + print("-----------------------------------------------------------") + vals.update({'ARPES':ARPES_mprint()}) + print("ARPES = ",ARPES_mprint()) + vals.update({'Kappa':Kappa_mprint()}) + print("Kappa = ",Kappa_mprint()) + print("===========================================================") + return vals + +def xrays_log_entries(**kwargs): + """ + Writes CSV file for the MDA scans with the following default parameters: + FilePath='/home/beams/29IDUSER/Documents/User_Folders/UserName/' + Filename is set by logname_set(FileName=None), default:YYYYMMDD_log.txt, where YYYYMMDD is when folders were set + (FileName=None,FileSuffix=None) is they are specified then it will use those values instead + + comment="Additional comments, last column" + + Previously: scanlog + """ + #default parameters + kwargs.setdefault('comment','') + + ioc = BL.ioc + + #scan Num + scan = BL.mda.prefix(ioc)+str(BL.mda.fileNum(ioc)) + entry_list=["scan"] + pv_list=[scan] + format_list=["s" ] + #scan info + drive = caget(ioc+"scan1.P1PV") + start = caget(ioc+"scan1.P1SP") + stop = caget(ioc+"scan1.P1EP") + step = caget(ioc+"scan1.P1SI") + entry_list.append('drive','start','stop' ,'step') + pv_list.append(drive,start,stop ,step) + format_list.append("s",".3f",".3f",".3f") + + #endstation info + if BL.endstation_name == 'ARPES': + endstation_entry, endstation_pv, endstation_format = ARPES_log_entries(**kwargs) + entry_list.append(endstation_entry) + pv_list.append(endstation_pv) + format_list.append(endstation_format) + elif BL.endstation_name == 'Kappa': + endstation_entry, endstation_pv, endstation_format = Kappa_log_entries(**kwargs) + entry_list.append(endstation_entry[:-7]) + pv_list.append(endstation_pv[:-7]) + format_list.append(endstation_format[:-7]) + + #beamline info + ID_mode = ID_mode_get(verbose=False) if BL.xrays else "no_xrays" + ID_QP_ratio = ID_QP_ratio_get (verbose=False) if BL.xrays else "no_xrays" + ID_sp = round(ID_SP_get_eV()) if BL.xrays else 0 + ID_rbv = round(ID_rbv_get_eV,4) if BL.xrays else 0 + hv = mono_energy_get() if BL.xrays else 0 + grt = mono_grating_get() if BL.xrays else 0 + slit_size = slit_get()[0] if BL.xrays else 0 + entry_list.append("hv","exit_slit","GRT", "ID_SP", "ID_RBV", "ID_Mode", "ID_QP") + pv_list.append(hv,slit_size,grt, ID_sp, ID_rbv,ID_mode, ID_QP_ratio) + format_list.append(".2f",".0f","s",".1f",".1f","s", ".0f") + + #endstation info 2: Fanny can I change to order to get rid of this complication? + if BL.endstation_name == 'Kappa': + endstation_entry, endstation_pv, endstation_format = Kappa_log_entries(**kwargs) + entry_list.append(endstation_entry[-7:]) + pv_list.append(endstation_pv[-7:]) + format_list.append(endstation_format[-7:]) + + #timestamp and comments + t = time.strftime("%D-%H:%M:%S") + comment = kwargs["comment"] + entry_list.append('time','comment') + pv_list.append(t,comment) + format_list.append("s","s") + + try: + logfile_update(BL.endstation,BL.ioc,entry_list,pv_list,format_list) + except: + print("scanlog did not write to file, check for errors.") + +############################################################################################################## +############################## beamline encoders ############################## +############################################################################################################## + +def xray_motor_encoder_sync(): + ioc = "29idb:" + for motor in [13,14,15,16,26,27]: + pv = ioc+"m"+str(motor)+".SYNC" + caput(pv,1) + print("synch encoder: "+pv) + +############################################################################################################## +############################## energy : ID + mono ############################## +############################################################################################################## +def energy_get_all(verbose=True): + """ + returns ID_mode,ID_QP_ratio,ID_sp,ID_rbv,hv,grt + + """ + d = ID_get_all(verbose=False) + d.update(mono_get_all(verbose=False)) + keylist = ['ID_Mode', 'ID_QP_ratio', 'ID_SP', 'ID_RBV','hv','grating'] + values = [] + for key in keylist: + values.append(d[key]) + if verbose: + print(key,d[key]) + + return tuple(values) + +def energy_get(): + hv = getE() + return hv + +def energy(hv_eV,slit_coeff=1,m3r=True,verbose=True): + """ + sets the ID for optimal flux based on calibration + sets the mono + sets the beamline aperture/slits + + slit_coeff < 1 is used to kill flux by closing the aperature after the mono (slit-2B) + + m3r => if True optimizes the mirror for the d-branch only + Previously: Set_BL, energy + """ + if BL.xrays: + if hv_eV != energy_range_check(hv_eV): + message_string = 'request photon energy '+str(hv_eV)+' not with the allowed range' + message_string = '\n closest allowed energy is '+str(energy_range_check(hv_eV)) + print_warning_message(message_string) + + + ID_energy_set(hv_eV,verbose=verbose) + mono_energy_set(hv_eV,verbose=verbose) + slits_set_BL(c_2B=slit_coeff,c_1A=1,verbose=verbose) + + if m3r == True: + if BL.branch() == 'd': + print('\nalign_m3r()') + try: + m3r_align() + sleep(1) + if m3r_RY_pos_sp_get() == m3r_RY_pos_sp(): + m3r_align() + except: + print('Unable to align; check camera settings.') + else: + message_string = 'BL.xrays = False, energy is not set' + print_warning_message(message_string) + +def energy_range_min_max(): + """ + returns the min,max energies for the current grating/ID setting + """ + ID_min, ID_max = ID_energy_range() + mono_min, mono_max = mono_energy_range() + hv_max = np.min(ID_max, mono_max) + hv_min = np.max(ID_min, mono_min) + return hv_min,hv_max + +def energy_range_check(hv): + """ + checks if the energy is within the allowed range for both the mono + and the ID. If outside the ranges set it to the closest allowed value + + Previously: SetRange() + """ + hv_min,hv_max = energy_range_min_max() + hv = np.min(hv_max,hv) + hv = np.max(hv_min,hv) + return hv + +def mvid(val): + """ + Sets the ID absolute set point (not optimized , mono & apertures stay fixed). + + to go to most flux use energy + """ + ID_SP_set() + +def scan_ID(ID_sp_start,ID_sp_stop,ID_sp_step,**kwargs): + """ + scan the ID set point + """ + val_pv,rbv_pv = ID_scan_pvs() + mda.fillin(val_pv,rbv_pv,ID_sp_start,ID_sp_stop,ID_sp_step,**kwargs) + + +def mvmono(val): + """ + Sets the mono energy (insertion device & apertures stay fixed). + """ + mono_energy_set(val) + +def getE(): + """ + Returns current mono set point. + """ + return mono_energy_get() + +def switch_gratings(grating): + """ + used to switch between gratings + grating = 'HEG' / 'MEG' + + check current grating + closes the main shutter + sets the mono energy + + Previously: Switch_Grating + """ + current_grt = mono_grating_get() + if grating != current_grt: + shutter_open = main_shutter_status() + main_shutter_close() + print("Switching grating, please wait...") + mono_grating_translate(grating,quiet=True) + slits_set_BL() + + if shutter_open: + main_shutter_open() + + print(dateandtime()," grating: "+grating) + else: + print("grating: "+grating) + +def mvgrating(grating): + """ + Change mono grating: + HEG = high resolution, low flux + MEG = medium resolution, high flux + """ + switch_gratings(grating) + +def polarization(ID_mode): + """ + Change beam polarization: ID_mode = 'H', 'V', 'RCP' or 'LCP' + """ + ID_switch_mode(ID_mode) + +def polarization_get(): + """ + gets the current polarization + """ + ID_mode_get() + +def scanhv(start,stop,step,average_pnts=1,**kwargs): + """ + scans the mono at the current ID value + + """ + ca_average(average_pnts) + + mono_scan_fillin(mda,start,stop,step,**kwargs) + + mono_energy_set(start) + mda.go(**kwargs) + + mono_scan_after(mda) + + +def scanE(start,stop,step,ID_offset=0,mesh='stay',average_pnts=1,scan_dim=1,**kwargs): + """ + sets the ID and then scans the mono + + hv_start,hv_stop,hv_step: are the mono start, stop, step energies + ID_offset: used to position the ID + None => the ID does not move + otherwise ID_energy_set = (stop-start)/2 + ID_offset + mesh: used to specifiy mesh status (for normalization) + => mesh='y': drops in the mesh for normalization (D branch only); retracts it at the end. + => mesh='stay': to leave the mesh in after the scan is finished + => mesh='n': no mesh. + **kwargs + scan_dim + positioner_settling_time = 0.2 + """ + mda = BL.mda + ca_average(average_pnts) + + if ID_offset != None: + ID_energy_set = (start+stop)/2.0 + ID_offset + + slits_set_BL() + + mono_scan_fillin(mda,scan_dim,start,stop,step,**kwargs) + + mono_energy_set(start) + BL.mda.go(scan_dim) + + mono_scan_after(mda,scan_dim) + + +def scanXAS(ID_eV,start_stop_step_lists,**kwargs): + """ + Sets the beamline to ID_eV and then scans the mono for XAS scans + + start_stop_step_lists is a list of lists for the different scan ranges + start_stop_step_lists = [[start1,stop1,step1], [start1,stop1,step1],...] + Note duplicates are removed and the resulting array is sorted in ascending order + + **kwargs: + scan_dim = 1 (default) + average_pnts: if using Keithlys this set the averaging; None for no averaging + m3r: if True the mirror is optimized before the start of the scan + mcp + execute: True/False to start the scan => True (default) + + Normalization: + - If in the D-branch the Mesh is put in but is not removed + - If in the C-branch we use the slit blades for normalization (ca13) + """ + kwargs.setdefault("scan_dim",1) + kwargs.setdefault("average_pnts",1) + kwargs.setdefault("m3r",True) + kwargs.setdefault("mcp",True) + kwargs.setdefault('execute',True) + + scan_dim=kwargs['scan_dim'] + + #Setting up the ScanRecord for Mono in Table mode + hv_array = make_table(start_stop_step_lists) + mono_scan_fillin_table(BL.mda,scan_dim,hv_array,**kwargs) + + #Averaging and Normalization + ca_average(kwargs['average_pnts']) + if BL.branch=="d": + meshD("In") + + #Setting the beamline energy + energy(ID_eV,m3r=kwargs["m3r"]) + + #mpa + if BL.branch == "d" and kwargs["mcp"]: + mpa_HV_on() + + #Scanning + if kwargs['execute']: + mono_energy_set(hv_array[0]) + BL.mda.go(scan_dim) + + #Setting everything back + mono_energy_set(ID_eV) + mono_scan_after(mda,scan_dim) + mda.table_reset_after(scan_dim) + + if BL.branch == "d": + if kwargs["mcp"]: + mpa_HV_off() + print("WARNING: Mesh"+BL.branch+" is still In") + +def scanXAS_BL(start_stop_step_lists,**kwargs): + """ + scans the mono and the ID for XAS scans + Note: this is slow, but required in if the c-branch + + start_stop_step_lists is a list of lists for the different scan ranges + start_stop_step_lists = [[start1,stop1,step1], [start1,stop1,step1],...] + Note duplicates are removed and the resulting array is sorted in ascending order + + **kwargs: + scan_dim = 1 (default) + average_pnts: if using Keithlys this set the averaging; None for no averaging + m3r: if True the mirror is optimized before the start of the scan + mcp + execute: True/False to start the scan => True (default) + + Normalization: + - If in the D-branch the Mesh is put in but is not removed + - If in the C-branch we use the slit blades for normalization (ca13) + """ + kwargs.setdefault("scan_dim",1) + kwargs.setdefault("average_pnts",1) + kwargs.setdefault("m3r",True) + kwargs.setdefault("mcp",True) + kwargs.setdefault('execute',True) + + scan_dim=kwargs['scan_dim'] + + #Setting up the ScanRecord for Mono and ID in Table mode + mono_array,ID_array = BL_energy_tables(start_stop_step_lists) + + kwargs.update('positioner_num',1) + mono_scan_fillin_table(mda,scan_dim,mono_array,**kwargs) + + kwargs.update('positioner_num',2) + ID_scan_fillin_table(mda,scan_dim,ID_array,**kwargs) + + #Averaging and Normalization + ca_average(kwargs['average_pnts']) + if BL.branch=="d": + meshD("In") + + #Setting the beamline energy + slits_set_BL(mono_array[0],m3r=kwargs["m3r"]) + + #mpa + if BL.branch == "d" and kwargs["mcp"]: + mpa_HV_on() + + #Scanning + if kwargs['execute']: + mono_energy_set(mono_array[0]) + ID_SP_set(ID_array[0]) + BL.mda.go(scan_dim) + + #Setting everything back + mono_energy_set(mono_array[0]) + mono_scan_after(mda,scan_dim) + mda.table_reset_after(scan_dim) + + if BL.branch == "d": + if kwargs["mcp"]: + mpa_HV_off() + print("WARNING: Mesh"+BL.branch+" is still In") + +def BL_energy_tables(start_stop_step_lists): + """ + returns mono_array and ID_array for BL energy scans + *energies: + start,stop,step + ListofLists, e.g.{[400,420,1],[420,425,0.25]...} + + Previously: Tables_BLenergy + """ + mono_array = make_table(start_stop_step_lists) + ID_array=np.array([]) + ID_mode,ID_QP_ratio,ID_sp,ID_rbv,hv,grt = energy_get_all(verbose=False) + for hv_eV in mono_array: + ID_array=np.append(ID_array,ID_calc_SP(grt,ID_mode,hv_eV)) + return mono_array,ID_array + + +############################################################################################################## +########################### branch shutter, valves, safe_state ###################### +############################################################################################################## +def get_branch(verbose=True): + """ + gets the branch based on the position of m3r + """ + branch = m3r_branch(verbose) + return branch + + +def open_branch(branch=None,valve=False): + """ + Opens the branch shutter, if branch is none, then uses the position of m3r to determine branch + + Previously: Check_BranchShutter + """ + branch = get_branch(verbose=False) + + if branch_shutter_status(branch): + print(dateandtime(), +branch+" shutter already open..." ,) + else: + while True: + if branch_shutter_status(branch) == False : + print(dateandtime(), "Opening "+branch+" shutter ..." ,) + branch_shutter_open(branch,verbose=False) + sleep(30) + else: + print(dateandtime(), +branch+" shutter is now open..." ,) + break + if valve: + GVs = branch_valves() + for GV in GVs: + valve_open(GV) + +def close_shutter(branch=None): + """ + Closes the branch shutter, if branch is none, then uses the position of m3r to determine branch + + Previously: Close_CShutter,Close_DShutter + """ + if branch is None: + branch = get_branch() + caput("PC:29ID:S"+branch+"S_CLOSE_REQUEST.VAL",1,wait=True,timeout=18000) + print("Closing "+branch+"-Shutter...") + +def switch_branch(branch, force=False, shutter=True,scan_reset=True,enable_cams=True): + """Switch beam into which = \"c\" or \"d\" branch (by retracting/inserting deflecting mirror) + Optionnal keyword argument: + force: change m3r even if the beamline think it is already there (default=False) + shutter: open the branch shutter, does not open the valve (default=False) + reset: resets the scanRecord and the global BL for the selected branch + enable_cams: to turn on/off branch camera + + Previously: Switch_Branch + """ + branch = branch.lower() + if branch in ["c","d","e"]: + + # Check current branch: + if get_branch() == branch and not force: + m3r_position = FMB_mirror_get(3,verbose=False) + if np.sum(np.array(m3r_position)): + print("\nMirror homed...") + print("...Try again using: Switch_Branch(which, forced=True)") + else: + print("\nWell, looks like the beam is already in this branch...") + print("...if you want to force the mirror motion, use the argument: forced=True") + else: + # Close both shutters: + print("\n") + print("Switching branch, please wait...") + if shutter: + branch_shutter_close('c') + branch_shutter_close('d') + + #Move M3R: + m3r_switch_branch(branch) + print(dateandtime()," - In "+branch+"-Branch") + sleep(2) + + # Open branch shutters: + if shutter: + open_branch(branch,valve=False) + + if scan_reset: + BL.mda.reset() + + if enable_cams: + branch_cams_enable(branch) + + else: + print_warning_message(branch+' is not a valid branch selection') +# if not nocam: + +############################################################################################################## +########################### mirrors ###################### +############################################################################################################## +def get_mirrors(): + for mirror_num in [0,1,3]: + FMB_mirror_get(mirror_num,verbose=True) + + +############################################################################################################## +########################### slits and resolution ###################### +############################################################################################################## +def slit(size): + """ + sets the exit slit based on BL.endstation + + ARPES = 0 < x < 300 um + Kappa = 0 < x < 1000 um + """ + branch = BL.branch + if branch == "c": + slit3C_set(size, quiet=False) + elif branch == "d": + slit3D_set(size, quiet=False) + + +def slit_get(verbose=True): + """ + sets the exit slit based on BL.endstation + + ARPES = 0 < x < 300 um + Kappa = 0 < x < 1000 um + """ + branch = BL.branch + if branch == "c": + slit_size = slit3C_get(verbose=False) + slit_center = np.nan + elif branch == "d": + slit_size,slit_center = slit3D_get(verbose=False) + + if verbose: + message = BL.branch+' exit slit: '+str(slit_size) + message += ", @ "+str(slit_center) if BL.branch is not 'c' else '' + print(message) + return slit_size,slit_center + +def resolution(): + """ + Calculate the theoretical resolution for the current beamline settings: + ARPES: total resolution i.e. sqrt(kbT^2 + analyzer^2 + BL^2); default SES slit = 5. + Kappa: beamline contribution only + """ + branch = get_branch() + grt = mono_grating_get() + hv_eV = getE() + slit_size = take_closest_value([10,20,50,100,200],round(slit_get(),0)) + + if branch == "c": + slit_SES = getSESslit() + PE = int(EA.PassEnergy) + T = caget(ARPES_extra_pvs['TA']) + resolution_ARPES(grt,hv_eV,slit_size,PE,slit_SES,T,verbose=True) + else: + resolution_beamline(grt,hv_eV,slit_size,verbose=True) + + + diff --git a/build/lib/iexcode/macros/ARPES_macros.py b/build/lib/iexcode/macros/ARPES_macros.py new file mode 100644 index 0000000..240e6d8 --- /dev/null +++ b/build/lib/iexcode/macros/ARPES_macros.py @@ -0,0 +1,96 @@ +import numpy as np + +from epics import caget,caput + +from .ScanFunctions_plot import mda_1D,fit_gauss + +from ..instruments.electron_analyzer import scanEA +from ..instruments.slits import slit3C_set +from ..instruments.xrays import energy +from ..instruments.ARPES import mvth,scanx,mvx,mprint + + +############################################################################################################## +############################## SES Work Function ############################## +############################################################################################################## + +def WorkFunction_Measure(PE,KE=409,**kwargs): + ''' + Takes data with 1st and 2nd order light of core level with KE at @ hv=500eV + **kwargs + + ''' + frames=round(17*60*200/PE,0) + energy(500) + + #Fermi Level + slit3C_set(150) + kwargs.update({'comment': 'EF1'}) + EAlist=['KE',495,PE,frames,1] + scanEA(EAlist,**kwargs) + + #Core level first order + slit3C_set(50) + kwargs.update({'comment': 'KE1'}) + EAlist=['KE',KE,PE,frames/10,1] + scanEA(EAlist,**kwargs) + + #Core level second order + slit3C_set(150) + kwargs.update({'comment': 'KE2'}) + EAlist=['KE',KE+500,PE,frames*10,1] + scanEA(EAlist,**kwargs) + print("""Fit the data : + - EF1 = EF in first order light (scan #1) + - KE1 = Au_3/2 in first order light (scan #2) + - KE2 = Au_3/2 for 2nd order light (scan #3) + => wk = KE2-2*KE1-(EF1-KE1)` + Details of the math is page 16 of commissionning book IV + Now if you know what you are doing you can: + caput('29idcEA:det1:ENERGY_OFFSET',new_wk_value)""") + +def WorkFunction_Calc(EF1,KE1,KE2): + """Fit the data : + - EF1 = EF in first order light (scan #1) + - KE1 = Au_3/2 in first order light (scan #2) + - KE2 = Au_3/2 for 2nd order light (scan #3) + => wk = KE2-2*KE1-(EF1-KE1)` + Details of the math is page 16 of commissionning book IV""" + wk = KE2-2*KE1-(EF1-KE1) + print("wk = KE2-2*KE1-(EF1-KE1) = ",wk) + print("Now if you know what you are doing you can:") + print("EA._setwk("+str(round(wk,2))+")") + +def WorkFunction(KE1,KE2,Ef): + Phi=round(KE2-2*KE1-(Ef-KE1),3) + print(('Phi = '+str(Phi)+' eV')) + return Phi + +def RoughPositions_Find(th_start,th_stop, th_step,**kwargs): + """automatically scans and creates the RoughPosition list used in FermiMap for + relatively uniform samples by: + 1) scanx(-0.5,0.5,.05,mode='relative') + 2) does a gaussian fits the mda EAV intesnity to determine center of the fit x_center + 3) mv(x_center) + 4) mprint() and appends current position to list + 5) moves to the next theta value and repeats + + Note: before starting move to the first theta position and adjust the focus leaving the EA is Live Mode + + **kwargs + scanDet=17 + """ + kwargs.setdefault("scanDet",17) + if caget("29idcEA:det1:Acquire") !=1: + print("Turn on the EA!!! EALive(PE,KE,Lens=\'Transmission\')") + else: + RefinedPositions=[] + for th in (th_start,th_stop+th_step,th_step): + mvth(th) + scanx(-0.5,0.5,.05,mode='relative') + scanNum=caget("29idARPES:saveData_scanNumber")-1 + x,y,x_name,y_name=mda_1D(scanNum,kwargs["scanDet"]) + results=fit_gauss(np.array(x),np.array(y)) + mvx(results[2][1]) + RefinedPositions.append(mprint()) + print("RefinedPositions=",RefinedPositions) diff --git a/build/lib/iexcode/macros/BL_shutdown.py b/build/lib/iexcode/macros/BL_shutdown.py new file mode 100644 index 0000000..800bf0c --- /dev/null +++ b/build/lib/iexcode/macros/BL_shutdown.py @@ -0,0 +1,47 @@ +from epics import caget,caput + +from ..instruments.electron_analyzer import EA +from ..instruments.shutters import main_shutter_close,branch_shutter_close +from ..instruments.diagnostics import diagnostics_all_out + +############################################################################################################## +############################################ Shut down ################################################# +############################################################################################################## + +def BL_Shutdown(): + BL_CloseAllShutters() + BL_Valve_All(state="CLOSE") + diagnostics_all_out() + try: + EA.off() + except: + print('EA is not running, visually confirm HV is off') + + caput("29iddau1:dau1:011:DAC_Set",0) #RSXS HV = "OFF" + +def BL_CloseAllShutters(): + main_shutter_close() + branch_shutter_close('c') + branch_shutter_close('d') + +def BL_Valve_All(state="CLOSE"): + ValveList=["V1A","V2A","V3B","V4B","V5B","V6B","V7C","V8C","V9C","V10C","V7D","V8D","V9D","V10D"] + for Vname in ValveList: + pv=BL_Valve2pv(Vname)+state+".VAL" + caput(pv,1) + +def BL_Valve(Vname,state="CLOSE"): + pv=BL_Valve2pv(Vname)+state+".VAL" + caput(pv,1) + +def BL_Valve2pv(Vname): + Valve={ + "V1A":"GV01","V2A":"GV02", #A-Hutch + "V3B":"GV03","V4B":"GV04","V5B":"GV05","V6B":"GV15", #B-Branch + "V7C":"GV06","V8C":"GV08","V9C":"GV09","V10C":"GV10", #C-Branch + "C-Turbo":"GV16", #ARPES + "V7D":"GV11","V8D":"GV12","V9D":"GV13","V10D":"GV14", #D-Branch + "D-Turbo":"GV17","D-TES":"GV18" #RSXS + } + pv="29id:BLEPS:"+Valve[Vname]+":" + return pv \ No newline at end of file diff --git a/build/lib/iexcode/macros/Kappa_optimization.py b/build/lib/iexcode/macros/Kappa_optimization.py new file mode 100644 index 0000000..378f9c5 --- /dev/null +++ b/build/lib/iexcode/macros/Kappa_optimization.py @@ -0,0 +1,100 @@ +import matplotlib.pyplot as plt + +from epics import caget,caput +from ..instruments.Kappa import * +from ..instruments.Motors import * + +from .ScanFunctions_plot import fit_mda, mda_1D + +##### Kappa Optimization scans ##### +def Opt_d4(iteration,moveZ,graph='y',srs=None,start=-0.5,stop=0.5,step=0.04): + tth_w=0.1 + if srs==None: det=21 + else: det=34 + z=caget('29idKappa:m4.RBV') + current_det=caget('29idKappa:userStringSeq6.STR1',as_string=True) + if current_det != 'd4': + print('Switching detector to d4') + tthdet.set('d4',move=False) + mvtth(0) + if moveZ is not None: + mvz(z-1000) + for i in range(iteration): + scantth(start,stop,step,'relative') + scannum = mda.lastFileNum() + tth0=fit_mda(scannum,det,tth_w,'gauss',graph=graph) + mvtth(tth0) + mvz(z) + return tth0,scannum + +def Opt_z0(iteration,movetth,movekth,det='d4',srs=None,graph='y'): + z_w=400 + current_det=caget('29idKappa:userStringSeq6.STR1',as_string=True) + if current_det != det: + print('Switching detector to '+det) + tthdet.set(det,move=False) + if det=='d3' and srs==None: det=20 + if det=='d3' and srs!=None: det=33 + if det=='d4' and srs==None: det=21 + if det=='d4' and srs!=None: det=34 + if movetth is not None: + mvtth(movetth) + if movekth is not None: + mvkth(movekth) + if iteration>1: + scanz(-2000,2000,250,'relative') + scannum=mda.fileNum() + z0=fit_mda(scannum,det,z_w,'erf',graph=graph) + mvz(z0) + scanz(-700,700,50,'relative') + scannum = mda.lastFileNum() + z0=fit_mda(scannum,det,z_w,'erf',graph=graph) + mvz(z0) + return z0,scannum + +def Opt_kth(kth_0,theta,det='d4',srs=None,graph='y'): + current_det=caget('29idKappa:userStringSeq6.STR1',as_string=True) + if current_det != det: + print('Switching detector to '+det) + tthdet.set(det,move=False) + if det == 'd4': + if srs==None: det=21 + else: det=34 + i=1 + elif det == 'd3': + if srs==None: det=20 + else: det=33 + i=5 + mvtth(theta*2) + mvkth(kth_0+theta) + scankth(-0.5*i,0.5*i,0.05*i,'relative') + new_kth=fit_mda(mda.lastFileNum(),det,0.1,'gauss',graph=graph) + mvkth(new_kth) + scankth(-0.2*i,0.2*i,0.02*i,'relative') + new_kth=fit_mda(mda.lastFileNum(),det,0.1,'gauss',graph=graph) + mvkth(new_kth) + scannum=mda.lastFileNum + kth0=round(new_kth-theta,3) + print('\nkth0 = ',kth0) + print('To plot use:') + print(' fit_mda('+str(scannum)+',21,0.2,"gauss",mytitle=\'theta ='+str(theta)+'\')') + print('If you are happy with that, you can set this value as kth0 using:') + print(' kth0_set('+str(kth0)+')') + + return kth0,scannum + +def plot_opt(opt_scan_list,energy_list,det,mytitle=''): + fig,(ax1)=plt.subplots(1,1) + fig.set_size_inches(5,4) + + for i,j in zip(opt_scan_list,energy_list): + x,y,x_name,y_name=mda_1D(i,det,1,0) + xdata = np.array(x) + ydata = np.array(y) + Imax=np.max(ydata) + ax1.plot(x,y/Imax,label=str(j)+" eV") + ax1.set(ylabel='Intensity') + ax1.set(xlabel=x_name) + ax1.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + ax1.grid(color='lightgray', linestyle='-', linewidth=0.5) + ax1.legend(bbox_to_anchor=[1.2, 1],ncol=2,shadow=True, title=mytitle, fancybox=True) diff --git a/build/lib/iexcode/macros/ScanFunctions_plot.py b/build/lib/iexcode/macros/ScanFunctions_plot.py new file mode 100644 index 0000000..c4ca216 --- /dev/null +++ b/build/lib/iexcode/macros/ScanFunctions_plot.py @@ -0,0 +1,2787 @@ +""" +#ScanFunctions_plot.py +how to copy tabs in stabs and not spaces jupyter + +For Fanny: Converting spaces to tabs from the terminal +cp ScanFunctions_IEX.py ScanFunctions_IEXbackup.py +sed -e 's/ /\t/g' ScanFunctions_IEXbackup.py > ScanFunctions_IEX.py +""" + +#### Other utilities: +import csv +from os import listdir +from os.path import join, isfile, dirname +from scipy.optimize import curve_fit +from scipy.special import erf +import numpy.polynomial.polynomial as poly +import ast + +### Data analysis: +from inspect import CO_ASYNC_GENERATOR +from sre_constants import CATEGORY_DIGIT +import matplotlib.pyplot as plt +import matplotlib.image as mpimg +import numpy as np +from math import * + +from netCDF4 import Dataset + + +##### APS / 29ID-IEX: +from IEX_plotting_and_analysis.mda import readMDA,scanDim +from IEX_plotting_and_analysis.IEX_nData import * + +try: + from epics import caget + from ..instruments.IEX_endstations import BL + from ..instruments.utilities import read_dict,today + from ..instruments.slits import slits_set_BL + from IEXcode.iexcode.current_amplifiers import current2flux +except: + print("EPICS package and dependent functions are not installed") + + + +############################################################################################################## +############################## JM Curves and Curve Fitting ############################## +############################################################################################################## + +def gauss(x, *p): + """ + Function for a guassian where p=[A,x0,sigma] + f(x)=A*numpy.exp(-(x-x0)**2/(2.*sigma**2) + fwhm=2.355*sigma + """ + A, x0, sigma = p + return A*np.exp(-(x-x0)**2/(2.*sigma**2)) + +def gauss_p0(y,x): + """ + Inital guesses for guassian fit + x and y are np.arrays + returns p0=[A,x0,sigma] + """ + A=max(y) + x0=x[np.where(y == max(y))][0] + sigma=0.2 + return [A,x0,sigma] + +def fit_gauss(x,y,xrange=None): + """ + x,y=EA_Spectrum(ScanNum, EnergyAxis,FilePath,Prefix) + x,y,x_name,y_name=mda_1D(ScanNum,DetectorNum) + + xrange = None to fit the full range + = [x1,x2] to fit a subrange + returns input_x,fit_y,coeff,var_matrix + where input_x and fit_y are the sub range x and the fitted data respectivley + coeff=[A,x0,sigma] + var_matrix is the fit variance + """ + if xrange is None: + input_y=y + input_x=x + else: + index1=np.where(x == x.flat[np.abs(x - xrange[0]).argmin()])[0][0] + index2=np.where(x == x.flat[np.abs(x - xrange[1]).argmin()])[0][0] + input_x=x[index1:index2] + input_y=y[index1:index2] + coeff, var_matrix = curve_fit(gauss, input_x, input_y, p0=gauss_p0(input_y,input_x)) + fit_y= gauss(input_x, *coeff) + return input_x,fit_y,coeff,var_matrix + + +############################################################################################################## +############################## Plot Tiff,JPEG,PNG ############################## +############################################################################################################## + +# filepath='/home/beams/29IDUSER/Documents/User_Folders/Topp/S089.tif' +def plot_image(filepath,h=20,v=10,**kwargs): + """ + filepath = '/home/beams/29IDUSER/Documents/User_Folders/UserName/TifFile.tif' + ** kwargs are imshow kwargs: + cmap = colormap; examples => 'gray','BuPu','Inferno' + vmin,vmax, adjust max and min of colormap + """ + + image = mpimg.imread(filepath) + plt.figure(figsize=(h,v)) + plt.imshow(image,**kwargs) + plt.axis('off') + plt.show() + + + +def plot_image2(Filepath1,Filepath2,h=20,v=10): + """ + filepath = '/home/beams/29IDUSER/Documents/User_Folders/UserName/TifFile.tif' + """ + print(Filepath1) + print(Filepath2) + image1 = mpimg.imread(Filepath1) + image2 = mpimg.imread(Filepath2) + plt.figure(figsize=(h,v)) + plt.subplot(1,2,1), plt.imshow(image1,cmap='gray') + plt.axis('off') + plt.subplot(1,2,2), plt.imshow(image2,cmap='gray') + plt.axis('off') +# fig.subplots_adjust(wspace=0.5,hspace=0.5,left=0.125,right=0.9,top=0.85,bottom=0.15) + plt.tight_layout() + plt.show() + + +############################################################################################################## +############################## Extract mda Data ############################## +############################################################################################################## + +## mda_1D(1691,44,1,0,'/net/s29data/export/data_29idb/2018_1/mda_b')??? + +def mda_unpack(ScanNum,filepath=None,prefix=None): + """ Return data file + dictionary D##:("pv",index##) + filepath: by default plot scans for the current data folder (as defined in BL_ioc() ScanRecord SaveData) + or specified folder path: + e.g. filepath='/net/s29data/export/data_29idb/2018_1/mda_b/' + prefix: by default, uses prefix as defined in ScanRecord + "mda_" for users, "Kappa_" or "ARPES_" for staff (sometimes) + """ + try: + if filepath is None: + filepath = BL.mda.filepath + if prefix is None: + prefix = BL.mda.prefix + except: + print('Please specify filepath and prefix, BL is not defined') + + mdaFile=filepath + prefix+'{:04}.mda'.format(ScanNum) + data_file = readMDA(mdaFile) + try: + D={} + n=len(data_file)-1 + for i in range(0,data_file[n].nd): + detector=data_file[n].d[i].fieldName + D[int(detector[1:])]=(data_file[n].d[i].name,i) + return (data_file,D) + except: + pass + +def mda_1D(ScanNum,DetectorNum,coeff=1,bckg=0,filepath=None,prefix=None): + """ Return x=positionner and y=detector(DetectorNum) + for a given detector number DYY (as shown in dview). """ + try: + (data_file,det)=mda_unpack(ScanNum,filepath,prefix) + index=det[DetectorNum][1] + x = data_file[1].p[0].data + y = data_file[1].d[index].data + x_name = data_file[1].p[0].name + y_name = data_file[1].d[index].name + if type(x_name) == bytes: + x_name=x_name.decode("utf-8") + if type(y_name) == bytes: + y_name=y_name.decode("utf-8") + n=list(zip(x,y)) + d=[n[i] for i, e in enumerate(n) if e != (0.0,0.0)] + if len(d)<len(n): + x=x[:len(d)] + y=y[:len(d)] + y=[(y[i]+bckg)*coeff for i, e in enumerate(y)] + #y=[(y[i]*coeff)+bckg for i, e in enumerate(y)] + return x,y,x_name,y_name + except: + print('error') + pass + + +def mda_1D_unscaled(ScanNum,DetectorNum,filepath=None,prefix=None): + """ Return x=positionner and y=detector(DetectorNum) + for a given detector number DYY (as shown in dview). """ + try: + (data_file,det)=mda_unpack(ScanNum,filepath,prefix) + if (data_file,det) == (None,None): + return(None) + else: + index=det[DetectorNum][1] + x = data_file[1].p[0].data + y = data_file[1].d[index].data + x_name = data_file[1].p[0].name + y_name = data_file[1].d[index].name + if type(x_name) == bytes: + x_name=x_name.decode("utf-8") + if type(y_name) == bytes: + y_name=y_name.decode("utf-8") + n=list(zip(x,y)) + d=[n[i] for i, e in enumerate(n) if e != (0.0,0.0)] + if len(d)<len(n): + x=x[:len(d)] + y=y[:len(d)] + bckg=min(y) + coeff=max(y)-min(y) + y=[(y[i]-bckg)/coeff for i, e in enumerate(y)] + return x,y,x_name,y_name + except: + pass + +def mda_1D_Xindex(ScanNum,DetectorNum,coeff=1,bckg=0,filepath=None,prefix=None): + """ Return x=index and y=detector(DetectorNum) + for a given detector number DYY (as shown in dview). """ + try: + (data_file,det)=mda_unpack(ScanNum,filepath,prefix) + index=det[DetectorNum][1] + x = data_file[1].d[0].data + y = data_file[1].d[index].data + x_name = data_file[1].d[0].name + y_name = data_file[1].d[index].name + if type(x_name) == bytes: + x_name=x_name.decode("utf-8") + if type(y_name) == bytes: + y_name=y_name.decode("utf-8") + n=list(zip(x,y)) + d=[n[i] for i, e in enumerate(n) if e != (0.0,0.0)] + if len(d)<len(n): + y=y[:len(d)] + x=list(range(1,len(y)+1)) + y=[(y[i]*coeff)+bckg for i, e in enumerate(y)] + return x,y,x_name,y_name + except: + pass + +def mda_1D_vsDet(ScanNum,DetectorNum,DetectorNum2,coeff=1,bckg=0,filepath=None,prefix=None): + """ Return x=index and y=detector(DetectorNum) + for a given detector number DYY (as shown in dview). """ + try: + #print(ScanNum,filepath,prefix,scanIOC) + (data_file,det)=mda_unpack(ScanNum,filepath,prefix) + index=det[DetectorNum][1] + index2=det[DetectorNum2][1] + x = data_file[1].d[0].data + x2 = data_file[1].d[index2].data + y = data_file[1].d[index].data + x_name = data_file[1].d[0].name + x2_name = data_file[1].d[index2].name + y_name = data_file[1].d[index].name + x = data_file[1].p[0].data + x2= data_file[1].d[index2].data + y= data_file[1].d[index].data + if type(x_name) == bytes: + x_name=x_name.decode("utf-8") + if type(y_name) == bytes: + y_name=y_name.decode("utf-8") + if type(x2_name) == bytes: + x2_name=x2_name.decode("utf-8") + n=list(zip(x,y)) + d=[n[i] for i, e in enumerate(n) if e != (0.0,0.0)] + if len(d)<len(n): + y=y[:len(d)] + x2=x2[:len(d)] + y=[(y[i]*coeff)+bckg for i, e in enumerate(y)] + return x2,y,x2_name,y_name + except: + pass + +def mda_Flux(ScanNum,DetectorNum,EnergyNum,filepath=None,prefix=None): + """ Return x=positionner and y=Flux(DetectorNum) + for a given diode recorded as detector number DYY (see ## in dview). + EnergyNum is the detector number for the mono RBV. + + """ + try: + (data_file,det)=mda_unpack(ScanNum,filepath,prefix) + index=det[DetectorNum][1] + Eindex=det[EnergyNum][1] + x = data_file[1].p[0].data + y = data_file[1].d[index].data + energy = data_file[1].d[Eindex].data + x_name = data_file[1].p[0].name + y_name = data_file[1].d[index].name + if type(x_name) == bytes: + x_name=x_name.decode("utf-8") + if type(y_name) == bytes: + y_name=y_name.decode("utf-8") + n=list(zip(x,y)) + d=[n[i] for i, e in enumerate(n) if e != (0.0,0.0)] + if len(d)<len(n): + x=x[:len(d)] + y=y[:len(d)] + y=[current2flux(y[i],energy[i],p=None) for (i, e) in enumerate(y)] + return x,y,x_name,y_name + except: + pass + + + +def mda_NormDet(ScanNum,DetectorNum,NormNum,coeff=1,bckg=0,filepath=None,prefix=None): + """ Return x=positionner and y=detector(DetectorNum) + for a given detector number DYY (as shown in dview). """ + try: + (data_file,det)=mda_unpack(ScanNum,filepath,prefix) + index=det[DetectorNum][1] + index_Norm=det[NormNum][1] + x = data_file[1].p[0].data + y= data_file[1].d[index].data + y_Norm=data_file[1].d[index_Norm].data + x_name = data_file[1].p[0].name + y_name = data_file[1].d[index].name#+"_norm:"+str(NormNum) + if type(x_name) == bytes: + x_name=x_name.decode("utf-8") + if type(y_name) == bytes: + y_name=y_name.decode("utf-8") + + n=list(zip(x,y)) + d=[n[i] for i, e in enumerate(n) if e != (0.0,0.0)] + if len(d)<len(n): + x=x[:len(d)] + y=y[:len(d)] + y=[y[i]/y_Norm[i] for i, e in enumerate(y)] + return x,y,x_name,y_name + except: + pass + +def mda_DivScan(ScanNum1,DetectorNum1,ScanNum2,DetectorNum2,coeff=1,bckg=0,filepath=None,prefix=None): + """ Return x=positionner and y=detector(DetectorNum) + for a given detector number DYY (as shown in dview). """ + try: + (data_file1,det1)=mda_unpack(ScanNum1,filepath,prefix) + index1=det1[DetectorNum1][1] + (data_file2,det2)=mda_unpack(ScanNum2,filepath,prefix) + index2=det2[DetectorNum2][1] + x1 = data_file1[1].p[0].data + y1= data_file1[1].d[index1].data + y2= data_file2[1].d[index2].data + x_name = data_file1[1].p[0].name + y_name = data_file1[1].d[index1].name+"_norm:"+str(ScanNum2) + if type(x_name) == bytes: + x_name=x_name.decode("utf-8") + if type(y_name) == bytes: + y_name=y_name.decode("utf-8") + + n=list(zip(x1,y1)) + d=[n[i] for i, e in enumerate(n) if e != (0.0,0.0)] + if len(d)<len(n): + x1=x1[:len(d)] + y1=y1[:len(d)] + y=[y1[i]/y2[i] for i, e in enumerate(y1)] + return x1,y,x_name,y_name + except: + pass + + + +def mda_2D(ScanNum,DetectorNum,filepath=None,prefix=None): + """ Return x=positionner and y=detector(DetectorNum) + for a given detector number DYY (as shown in dview). """ + try: + (data_file,det)=mda_unpack(ScanNum,filepath,prefix) + index=det[DetectorNum][1] + x_temp = data_file[2].p[0].data + y_temp = data_file[1].p[0].data + z_temp = data_file[2].d[index].data + x_name = data_file[2].p[0].name + y_name = data_file[1].p[0].name + z_name = data_file[2].d[index].name + if type(x_name) == bytes: + x_name=x_name.decode("utf-8") + if type(y_name) == bytes: + y_name=y_name.decode("utf-8") + if type(z_name) == bytes: + z_name=z_name.decode("utf-8") + + n=list(zip(x_temp,y_temp,z_temp)) + d=[n[i] for i, e in enumerate(n) if e != (0.0,0.0,0.0)] + if len(d)<len(n): + x_temp=x_temp[:len(d)] + y_temp=y_temp[:len(d)] + z_temp=z_temp[:len(d)] + x = x_temp[0] + y = y_temp + z = np.asarray(z_temp) #2-D array + return x,y,z,x_name,y_name,z_name + except: + pass + + +############################################################################################### +#################################### PLOT MDA ################################### +############################################################################################### + + + + +def plot_mda2D(ScanNum,DetectorNum,title=None,color=None,filepath=None,prefix=None): + try: + x,y,z,xName,yName,zName=mda_2D(ScanNum,DetectorNum,filepath,prefix) + fig, ax0 = plt.subplots() + if color is None: + color='gnuplot' + img = ax0.imshow(z, cmap=color, interpolation = 'nearest', extent = [min(x), max(x), max(y), min(y)], aspect = 'auto') + fig.colorbar(img) + if title is None: + plt.title(zName) + else: + plt.title(title) + ax0.set_xlabel(xName) + ax0.set_ylabel(yName) + # ax0.set_ylim(y.max(),y.min()) + plt.show() + except: + pass + + + +def plot_mda_series(*ScanDet,**kwArg): + """plot_mda_series(1217, 1226, 1, 39 ,0.025, **kwArg) + (first,last,countby,det,offset,**kwArg) + Optional data analysis keywords arguments: + Flux conversion (for diode only): flux= 3(User) or 25(Staff). + Norm option: norm = 'yes' normalizes all the scans to 1 (default: None) + NormDet= 1 for SR current, 14 for Mesh (D-branch); Normalize by the mesh does not work with norm='yes' + Optional graphical keywords arguments: + sizeH = 1,1.5,... increase horizontal figure size + sizeV = 1,1.5,... increase vertical figure size + marker = 'x','+','o','v','^','D',... (default:None) + markersize = 1,2,3,... (default: 5) + linewidth = 0.5,1,2... (default: 1) + linestyle = '-','--','-.',':' (default: solid '-') + color = 'r','b','m','c','g','y'... (default: jupyter colors series) + legend = 'best',Supper left', 'lower right'... (default: None) + log = 'log' (default: None = linear) + xrange = [x1,x2] (default: None = Autoscale) + yrange = [y1,y2] (default: None = Autoscale) + xlabel = 'mxLabel' (default: pv name) + ylabel = 'myLabel' (default: pv name) + ytickstyle = 'sci' for y axes (default: 'plain') + xtickstyle = 'sci' for y axes (default: 'plain') + filepath: by default plot scans for the current data folder (as defined in BL_ioc() ScanRecord SaveData) + or specified folder path: + e.g. user : filepath='/net/s29data/export/data_29idc/2018_2/UserName/mda/' + e.g. staff: filepath='/net/s29data/export/data_29idb/2018_2/mda/' + prefix: by default, uses prefix as defined in ScanRecord ("mda_") + scanIOC: by default, uses the IOC for the current branch as define in BL_IOC() + """ + + if type(ScanDet[0]) is not tuple: + ScanDet=(tuple(ScanDet),) + m=1 + else: m= len(ScanDet) + + scanlist="" + j=0 + offset=0 + for n in range(m): + print(n) + print(m) + print(ScanDet) + det=ScanDet[n][3] + if len(ScanDet)>4 and isinstance(ScanDet[n][3],str)== False: + offset=ScanDet[n][4] + for scanNum in range(ScanDet[n][0],ScanDet[n][1]+ScanDet[n][2],ScanDet[n][2]): + scanlist+=str(scanNum)+',(det,1,'+str(offset)+'),' + j+=offset + cmd="plot_mda("+scanlist + if kwArg is not None: + for key, value in list(kwArg.items()): + if type(value) == str: + cmd=cmd+(key+'=\"'+value+'\",') + else: + cmd=cmd+(key+'='+str(value)+',') + if cmd[-1]==",": + cmd=cmd[:-1] + cmd=cmd+")" + if kwArg is not None: + for key, value in list(kwArg.items()): + if key=='q': + print('det=',det) + print(cmd) + exec(cmd) + + + +def plot_mda(*ScanDet,**kwArg): + + """ + Plot mda scans: *ScanDet = (scan,det,scan,det...),(scan,det,scan,det...),title=(subplot_title1,subplot_title2) + = subplot1, subplot2 + Optional data analysis keywords arguments: + Flux conversion (for diode only): flux= 3(D## for mono rbv, typically 3). + Norm option: norm = 'yes' normalizes all the scans to 1 (default: None) + NormDet = 1 for SR current, 14 for Mesh (D-branch); Normalize by the mesh does not work with norm='yes' + DivScan = ? + Optional graphical keywords arguments: + sizeH = 1,1.5,... increase horizontal figure size + sizeV = 1,1.5,... increase vertical figure size + marker = 'x','+','o','v','^','D',... (default:None) + markersize = 1,2,3,... (default: 5) + linewidth = 0.5,1,2... (default: 1) + linestyle = '-','--','-.',':' (default: solid '-') + color = 'r','b','m','c','g','y'... (default: jupyter colors series) + legend = 'best',Supper left', 'lower right'... (default: None) + log = 'log' (default: None = linear) + xrange = [x1,x2] (default: None = Autoscale) + yrange = [y1,y2] (default: None = Autoscale) + xlabel = 'mxLabel' (default: pv name) + ylabel = 'myLabel' (default: pv name) + ytickstyle = 'sci' for y axes (default: 'plain') + xtickstyle = 'sci' for y axes (default: 'plain') + filepath: by default plot scans for the current data folder (as defined in BL_ioc() ScanRecord SaveData) + or specified folder path: + e.g. user : filepath='/net/s29data/export/data_29idc/2018_2/UserName/mda/' + e.g. staff: filepath='/net/s29data/export/data_29idb/2018_2/mda/' + prefix: by default, uses prefix as defined in ScanRecord ("mda_") + scanIOC: by default, uses the IOC for the current branch as define in BL_IOC() + """ + + args={ + 'marker':None, + 'markersize':5, + 'linewidth':1, + 'linestyle':'-', + 'color':None, + 'nticks':None, + 'sizeH':1, + 'sizeV':1, + 'title':'', + 'filepath':None, + 'prefix':None, + 'norm':None, + 'flux':None, + 'NormDet':None, + 'scanIOC':None, + 'legend':None, + 'vs_index':None, + 'vs_det':None, + 'xrange':[None,None], + 'yrange':[None,None], + 'save':True + } + + args.update(kwArg) + + mkr=args['marker'] + ms=args['markersize'] + lw=args['linewidth'] + ls=args['linestyle'] + c=args['color'] + path=args['filepath'] + prefix=args['prefix'] + scanIOC=args['scanIOC'] + save=args['save'] + + if 'legend' in args: + if args['legend'] == 'center left': + hsize=7 + + if type(ScanDet[0]) is not tuple: + ScanDet=(tuple(ScanDet),) + m=1 + else: m= len(ScanDet) + + def SubplotsLayout(m): + if m >1: + ncols=2 + else: + ncols=1 + nrows=max(sum(divmod(m,2)),1) + hsize=ncols*5*args['sizeH'] + vsize=nrows*4*args['sizeV'] + if nrows==1: vsize=nrows*3.5*args['sizeV'] + return nrows,ncols,hsize,vsize + + try: + nrows,ncols,hsize,vsize=SubplotsLayout(m) + + fig, axes = plt.subplots(nrows,ncols,figsize=(hsize,vsize)) # HxV + axes=np.array(axes) + + for (n,ax) in zip(list(range(m)),axes.flat): + for (i,j) in zip(ScanDet[n][0::2],ScanDet[n][1::2]): + if type(j) is tuple: + p,k,l=j + x,y,x_name,y_name=mda_1D(i,p,k,l,path,prefix) + elif args['flux'] is not None: + x,y,x_name,y_name=mda_Flux(i,j,args['flux'],path,prefix,scanIOC) + elif args['norm'] is not None: + x,y,x_name,y_name=mda_1D_unscaled(i,j,path,prefix,scanIOC) + elif args['NormDet'] is not None: + x,y,x_name,y_name=mda_NormDet(i,j,args['NormDet'],1,0,path,prefix,scanIOC) + elif args['vs_index'] is not None: + x,y,x_name,y_name=mda_1D_Xindex(i,j,1,0,path,prefix) + elif args['vs_det'] is not None: + x,y,x_name,y_name=mda_1D_vsDet(i,j,args['vs_det'],1,0,path,prefix) + #elif DivScan is not None: + # x,y,x_name,y_name=mda_DivScan(i,j,DivScan,DivDet,1,0,path,prefix,scanIOC) + else: + x,y,x_name,y_name=mda_1D(i,j,1,0,path,prefix,scanIOC) + ax.plot(x,y,label="mda_"+str(i),color=c,marker=mkr,markersize=ms,linewidth=lw,linestyle=ls) + ax.grid(color='lightgray', linestyle='-', linewidth=0.5) + + #modifying graph + if args['legend'] != None: + if args['legend'] == 'center left': + box = ax.get_position() + ax.set_position([box.x0, box.y0, box.width * 0.6, box.height]) + ax.legend(loc='center left', bbox_to_anchor=(1, 0.5)) + myleft=0.1 + myright=0.55 + else: + ax.legend(args['legend'], frameon=True) + if 'ytickstyle' in args: + ax.ticklabel_format(style=args['ytickstyle'], axis='y', scilimits=(0,0)) + if 'xtickstyle' in args: + ax.ticklabel_format(style=args['xtickstyle'], axis='x', scilimits=(0,0)) + if 'log' in args: + ax.set_yscale('log') + if args['xrange'] != [None,None]: + ax.set_xlim(args['xrange'][0],args['xrange'][1]) + if args['yrange'] != [None,None]: + ax.set_ylim(args['yrange'][0],args['yrange'][1]) + if 'xlabel' in args: + x_name=args['xlabel'] + if 'ylabel' in args: + y_name=args['ylabel'] + if args['nticks'] != None: + tck=args['nticks']-1 + ax.locator_params(tight=None,nbins=tck) + + + if 'title' in args: + mytitle=args['title'] + if type(mytitle) is not tuple: + ax.set(xlabel=x_name,ylabel=y_name,title=mytitle) + else: + p=len(mytitle) + if n == p: + ax.set(xlabel=x_name,ylabel=y_name,title='') + else: + ax.set(xlabel=x_name,ylabel=y_name,title=mytitle[n]) + # ax.text(0.5, 1.025,mytitle,horizontalalignment='center',fontsize=14,transform = ax.transAxes) + ax.grid(color='lightgray', linestyle='-', linewidth=0.5) + + if ncols==1 and nrows==1 and kwArg.get('legend')!='center left': + fig.subplots_adjust(wspace=0.5,hspace=0.5,left=0.25,right=0.88,top=0.85,bottom=0.22) + elif ncols==1 and kwArg.get('legend')=='center left': + fig.subplots_adjust(wspace=0.5,hspace=0.5,left=myleft,right=myright,top=0.85,bottom=0.22) + else: + fig.subplots_adjust(wspace=0.5,hspace=0.5,left=0.125,right=0.9,top=0.85,bottom=0.15) + + plt.tight_layout() + if save: + try: + fname=join('/home/beams/29IDUSER/Documents/User_Folders/','lastfigure.png') + print(fname) + plt.savefig(fname) + except: + pass + plt.show() + except: + pass + + + +def plot_mda_lists(*ScanDet,**kwArg): + """ + Plot mda scans: *ScanDet = (scanNum_list,detNum_list),(scanNum_list,detNum_list) + = subplot1, subplot2 + Optional data analysis keywords arguments: + Flux conversion (for diode only): flux= 3(User) or 25(Staff). + Norm option: norm = 'yes' normalizes all the scans to 1 (default: None) + NormDet= 1 for SR current, 14 for Mesh (D-branch); Normalize by the mesh does not work with norm='yes' + Optional graphical keywords argudef plot_mdaments: + sizeH = 1,1.5,... increase horizontal figure size + sizeV = 1,1.5,... increase vertical figure size + marker = 'x','+','o','v','^','D',... (default:None) + markersize = 1,2,3,... (default: 5) + linewidth = 0.5,1,2... (default: 1) + linestyle = '-','--','-.',':' (default: solid '-') + color = 'r','b','m','c','g','y'... (default: jupyter colors F) + legend = 'best',Supper left', 'lower right'... (default: None) + log = 'log' (default: None = linear) + xrange = [x1,x2] (default: None = Autoscale) + yrange = [y1,y2] (default: None = Autoscale) + xlabel = 'mxLabel' (default: pv name) + ylabel = 'myLabel' (default: pv name) + ytickstyle = 'sci' for y axes (default: 'plain') + xtickstyle = 'sci' for y axes (default: 'plain') + filepath: by default plot scans for the current data folder (as defined in BL_ioc() ScanRecord SaveData) + or specified folder path: + e.g. user : filepath='/net/s29data/export/data_29idc/2018_2/UserName/mda/' + e.g. staff: filepath='/net/s29data/export/data_29idb/2018_2/mda/' + prefix: by default, uses prefix as defined in ScanRecord ("mda_") + scanIOC: by default, uses the IOC for the current branch as define in BL_IOC() + """ + + args={ + 'marker':None, + 'markersize':5, + 'linewidth':1, + 'linestyle':'-', + 'color':None, + 'nticks':None, + 'sizeH':1, + 'sizeV':1, + 'title':'', + 'filepath':None, + 'prefix':None, + 'norm':None, + 'flux':None, + 'NormDet':None, + 'scanIOC':None, + 'legend':None, + 'vs_index':None, + 'xrange':[None,None], + 'yrange':[None,None] + } + + args.update(kwArg) + + mkr=args['marker'] + ms=args['markersize'] + lw=args['linewidth'] + ls=args['linestyle'] + c=args['color'] + path=args['filepath'] + prefix=args['prefix'] + scanIOC=args['scanIOC'] + + + + if 'legend' in args: + if args['legend'] == 'center left': + hsize=7 + + #setting up the subplot + if type(ScanDet[0]) is not tuple: + ScanDet=(tuple(ScanDet),) + m=1 + else: m= len(ScanDet) + + def SubplotsLayout(m): + if m >1: + ncols=2 + else: + ncols=1 + nrows=max(sum(divmod(m,2)),1) + hsize=ncols*5*args['sizeH'] + vsize=nrows*4*args['sizeV'] + if nrows==1: vsize=nrows*3.5*args['sizeV'] + return nrows,ncols,hsize,vsize + + try: + nrows,ncols,hsize,vsize=SubplotsLayout(m) + fig, axes = plt.subplots(nrows,ncols,figsize=(hsize,vsize)) # HxV + axes=np.array(axes) + + + for (n,ax) in zip(list(range(m)),axes.flat): #n=subplot tuple + scanNum_list=ScanDet[n][0] + detNum_list=ScanDet[n][1] + + if type(scanNum_list) is int: + scanNum_list=[scanNum_list] + if type(detNum_list) is int: + detNum_list=[detNum_list] + for i in range(1,len(scanNum_list)): + detNum_list.append(detNum_list[0]) + if type(args['filepath']) is not list: + filepath_list=[args['filepath']] + for i in range(1,len(scanNum_list)): + filepath_list.append(filepath_list[0]) + else: filepath_list=args['filepath'] + if type(args['prefix']) is not list: + prefix_list=[args['prefix']] + for i in range(1,len(scanNum_list)): + prefix_list.append(prefix_list[0]) + else: prefix_list=args['prefix'] + if type(args['scanIOC']) is not list: + scanIOC_list=[args['scanIOC']] + for i in range(1,len(scanNum_list)): + scanIOC_list.append(scanIOC_list[0]) + else: scanIOC_list=args['scanIOC'] + #loading the data + for index in range(0,len(scanNum_list)): + i=scanNum_list[index] + j=detNum_list[index] + path=filepath_list[index] + prefix=prefix_list[index] + scanIOC=scanIOC_list[index] + #print(i) + if type(j) is tuple: + p,k,l=j + x,y,x_name,y_name=mda_1D(i,p,k,l,path,prefix) + elif args['flux'] is not None: + x,y,x_name,y_name=mda_Flux(i,j,args['flux'],path,prefix,scanIOC) + elif args['norm'] is not None: + x,y,x_name,y_name=mda_1D_unscaled(i,j,path,prefix,scanIOC) + elif args['NormDet'] is not None: + x,y,x_name,y_name=mda_NormDet(i,j, args['NormDet'],1,0,path,prefix,scanIOC) + else: + x,y,x_name,y_name=mda_1D(i,j,1,0,path,prefix,scanIOC) + #adding to graph + ax.grid(color='lightgray', linestyle='-', linewidth=0.5) + ax.plot(x,y,label="mda_"+str(i),color=c,marker=mkr,markersize=ms,linewidth=lw,linestyle=ls) + + #modifying graph + if args['legend'] != None: + if args['legend'] == 'center left': + box = ax.get_position() + ax.set_position([box.x0, box.y0, box.width * 0.6, box.height]) + ax.legend(loc='center left', bbox_to_anchor=(1, 0.5)) + myleft=0.1 + myright=0.55 + else: + ax.legend(args['legend'], frameon=True) + if 'ytickstyle' in args: + ax.ticklabel_format(style=args['ytickstyle'], axis='y', scilimits=(0,0)) + if 'xtickstyle' in args: + ax.ticklabel_format(style=args['xtickstyle'], axis='x', scilimits=(0,0)) + if 'log' in args: + ax.set_yscale('log') + if args['xrange'] != [None,None]: + ax.set_xlim(args['xrange'][0],args['xrange'][1]) + if args['yrange'] != [None,None]: + ax.set_ylim(args['yrange'][0],args['yrange'][1]) + if 'xlabel' in args: + x_name=args['xlabel'] + if 'ylabel' in args: + y_name=args['ylabel'] + if args['nticks'] != None: + tck=args['nticks']-1 + ax.locator_params(tight=None,nbins=tck) + + if 'title' in args: + if type(args['title']) is not str: + mytitle=args['title'][n] + else: + mytitle=args['title'] + ax.set(xlabel=x_name,ylabel=y_name,title=mytitle) + #adjusting subplots + if ncols==1 and nrows==1 and kwArg.get('legend')!='center left': + fig.subplots_adjust(wspace=0.5,hspace=0.5,left=0.25,right=0.88,top=0.85,bottom=0.22) + elif ncols==1 and kwArg.get('legend')=='center left': + fig.subplots_adjust(wspace=0.5,hspace=0.5,left=myleft,right=myright,top=0.85,bottom=0.22) + else: + fig.subplots_adjust(wspace=0.5,hspace=0.5,left=0.125,right=0.9,top=0.85,bottom=0.15) + #show plot + plt.tight_layout() + plt.show() + + except: + pass + + + + +############################################################################################### +#################################### PLOT netCDF ################################### +############################################################################################### + + + + +def nc_unpack(ScanNum,FilePath=None,Prefix=None): + """ + Returns the full netCDF data file + meta data (Attributes/Exta PVs) + c.variables['Attr_EnergyStep_Swept'][:][0] + data array is accessed + nc.variables['array_data'][:][0] + + FilePath: by default plot scans for the current data folder (as shown on detector panel) + or specified folder path ending with '/': + e.g. user : FilePath='/net/s29data/export/data_29idc/2018_2/UserName/netCDF/' + e.g. staff: FilePath='/net/s29data/export/data_29idb/2018_2/netCDF/' + Prefix: by default, uses prefix as shown on detector panel ("EA_") + """ + def GetFileName(): + SubFolder= caget('29idcEA:netCDF1:FilePath',as_string=True) + if SubFolder[0]=='X': drive='b' + elif SubFolder[0]=='Y': drive='c' + FilePath='/net/s29data/export/data_29id'+drive+SubFolder[2:]+'/' + Prefix = caget("29idcEA:netCDF1:FileName_RBV",as_string=True) + return FilePath, Prefix + + if FilePath is None: + FilePath=GetFileName()[0] + if Prefix is None: + Prefix= GetFileName()[1] + myFile=FilePath+Prefix+'{:04}.nc'.format(ScanNum) + nc = Dataset(myFile,mode='r') + return nc + +def EA_data(nc): + """ Returns: x,xname,ycrop,yname,img,ActualPhotonEnergy,WorkFunction,PE + """ + + LowEnergy=nc.variables['Attr_LowEnergy'][:][0] + HighEnergy=nc.variables['Attr_HighEnergy'][:][0] + ActualPhotonEnergy=nc.variables['Attr_ActualPhotonEnergy'][:][0] + EnergyStep_Swept=nc.variables['Attr_EnergyStep_Swept'][:][0] + EnergyStep_Swept_RBV=nc.variables['Attr_EnergyStep_Swept_RBV'][:][0] + EperChannel=nc.variables['Attr_EnergyStep_Fixed_RBV'][:][0] + GratingPitch=nc.variables['Attr_GratingPitch'][:][0] + MirrorPitch=nc.variables['Attr_MirrorPitch'][:][0] + + WorkFunction=nc.variables['Attr_Energy Offset'][:][0] + + DetMode=nc.variables['Attr_DetectorMode'][:][0] # BE=0,KE=1 + AcqMode= nc.variables['Attr_AcquisitionMode'][:][0] # Swept=0, Fixed=1, BS=2 + LensMode=nc.variables['Attr_LensMode'][:][0] + + PassEnergyMode=nc.variables['Attr_PassEnergy'][:][0] + PEdict={0:1,1:2,2:5,3:10,4:20,5:50,6:100,7:200,8:500} + PE=PassEnergyMode + + #ActualPhotonEnergy,WorkFunction,PE=EA_metadata(nc)[0:3] + data = nc.variables['array_data'][:][0] + + def SelectValue(which,x1,x2): + if which == 0: value=x1 + if which == 1: value=x2 + return value + + ### Energy Scaling: + Edelta = SelectValue(DetMode,-EnergyStep_Swept,EnergyStep_Swept) + if AcqMode == 0: # Swept + Ehv=ActualPhotonEnergy + Estart = SelectValue(DetMode, Ehv-LowEnergy, LowEnergy) + #Estop = SelectValue(DetMode, Ehv-HighEnergy, HighEnergy) + if AcqMode >= 1: # Fixed or Baby Swept + Ecenter=nc.variables['Attr_CentreEnergy_RBV'][:][0] + #print nc.variables#JM was here + #print Ecenter,Edelta#JM was here + Estart=Ecenter-(data.shape[1]/2.0)*Edelta + Eunits=SelectValue(DetMode,"Binding Energy (eV)","Kinetic Energy (eV)") + + x=[Estart+Edelta*i for i,e in enumerate(data[0,:])] + xname=Eunits + + ### Angular Scaling: + if LensMode>-1: # Angular Modes RE changed from >0 (Angular) to >-1 (all mode) + CenterChannel=571 + FirstChannel=0 + Ddelta =0.0292717 + Dstart = (FirstChannel-CenterChannel)*Ddelta + y=[Dstart+Ddelta*i for i,e in enumerate(data[:,0])] + #getting rid of edges with no data + data=nc.variables['array_data'] + #x1=338;x2=819 #old + x1=338-100;x2=819-10 + datacrop=data[:,x1:x2] + ycrop=y[x1:x2] + yname='Degrees' + else: + ycrop,yname=None,'mm' + return x,xname,ycrop,yname,datacrop,ActualPhotonEnergy,WorkFunction,PE + + + + +def EA_Image(ScanNum,EnergyAxis='KE',FilePath=None,Prefix=None): + """ + Returns + x = KE or BE energy scale; BE is calculated based on the wk in the SES and the mono energy + y = Integrated intensity + + FilePath: by default plot scans for the current data folder (as shown on detector panel) + or specified folder path ending with '/': + e.g. user : FilePath='/net/s29data/export/data_29idc/2018_2/UserName/netCDF/' + e.g. staff: FilePath='/net/s29data/export/data_29idb/2018_2/netCDF/' + Prefix: by default, uses prefix as shown on detector panel ("EA_") + + x,y,img=EA_Image(1) + plt.imshow(img,extent = [min(x), max(x), min(y), max(y)], aspect = 'auto') + plt.show()) + """ + nc=nc_unpack(ScanNum,FilePath,Prefix) + x,xname,y,yname,img,hv,wk,PE=EA_data(nc) + y=np.array(y) + img=img[0] + if EnergyAxis == 'KE': + x=np.array(x) + else: + x=hv-wk-np.array(x) + return x, y, img + +def EA_Spectrum(ScanNum,EnergyAxis='KE',FilePath=None,Prefix=None): + """ + Returns + x = KE or BE energy scale; BE is calculated based on the wk in the SES and the mono energy + y = Integrated intensity + FilePath: by default plot scans for the current data folder (as shown on detector panel) + or specified folder path ending with '/': + e.g. user : FilePath='/net/s29data/export/data_29idc/2018_2/UserName/netCDF/' + e.g. staff: FilePath='/net/s29data/export/data_29idb/2018_2/netCDF/' + Prefix: by default, uses prefix as shown on detector panel ("EA_") + + Simple plot: x,y=EA_Spectrum(ScanNum);plt.plot(x,y);plt.xlim(min(x),xmax(x));plt.show() +""" + x, ang, img = EA_Image(ScanNum, EnergyAxis,FilePath,Prefix) + y = np.asarray([sum(img[:,i]) for i in range(img.shape[1])]) + return x, y + +def EA_metadata(ScanNum,FilePath=None,Prefix=None): + """ Returns: ActualPhotonEnergy,WorkFunction,GratingPitch,MirrorPitch + """ + nc=nc_unpack(ScanNum,FilePath,Prefix) + # SES parameters + LowEnergy=nc.variables['Attr_LowEnergy'][:][0] + HighEnergy=nc.variables['Attr_HighEnergy'][:][0] + EnergyStep_Swept=nc.variables['Attr_EnergyStep_Swept'][:][0] + EnergyStep_Swept_RBV=nc.variables['Attr_EnergyStep_Swept_RBV'][:][0] + EperChannel=nc.variables['Attr_EnergyStep_Fixed_RBV'][:][0] + WorkFunction=nc.variables['Attr_Energy Offset'][:][0] + DetMode=nc.variables['Attr_DetectorMode'][:][0] # BE=0,KE=1 + AcqMode= nc.variables['Attr_AcquisitionMode'][:][0] # Swept=0, Fixed=1, BS=2 + LensMode=nc.variables['Attr_LensMode'][:][0] + PassEnergyMode=nc.variables['Attr_PassEnergy'][:][0] + PEdict={0:1,1:2,2:5,3:10,4:20,5:50,6:100,7:200,8:500} + PE=PassEnergyMode + + TEY=nc.variables['Attr_TEY'][:][0] + + # Mono parameters + ActualPhotonEnergy=nc.variables['Attr_ActualPhotonEnergy'][:][0] + GratingPitch=nc.variables['Attr_GratingPitch'][:][0] + MirrorPitch=nc.variables['Attr_MirrorPitch'][:][0] + Grating_Density=nc.variables['Attr_Grating_Density'][:][0] + Grating_Slot=nc.variables['Attr_Grating_Slot'][:][0] + GRT_Offset_1=nc.variables['Attr_GRT_Offset_1'][:][0] + GRT_Offset_2=nc.variables['Attr_GRT_Offset_2'][:][0] + GRT_Offset_3=nc.variables['Attr_GRT_Offset_3'][:][0] + MIR_Offset_1=nc.variables['Attr_MIR_Offset_1'][:][0] + b2_GRT1=nc.variables['Attr_b2-GRT1'][:][0] + b2_GRT2=nc.variables['Attr_b2-GRT2'][:][0] + b2_GRT3=nc.variables['Attr_b2-GRT3'][:][0] + + offset=[MIR_Offset_1,GRT_Offset_1,GRT_Offset_2,GRT_Offset_3] + b2=[0,b2_GRT1,b2_GRT2,b2_GRT3] + + return WorkFunction,ActualPhotonEnergy,MirrorPitch,GratingPitch,Grating_Density,Grating_Slot,offset,b2 + + +def Get_EDCmax(ScanNum,EnergyAxis='KE',FilePath=None,Prefix=None): + x,y=EA_Spectrum(ScanNum, EnergyAxis,FilePath,Prefix) + maxY= max(y) + maxX=round(x[np.where(y == max(y))][0],3) + return maxX,maxY # energy position, intensity of the peak + + + +def EDC_Series(first,last,countby, EnergyAxis='BE',title="",norm=None,FilePath=None,Prefix=None): + """ + Plots a seriew of EA_Spectrum + """ + if title == "": + title="Scans: "+str(first)+"/"+str(last)+"/"+str(countby) + fig = plt.figure(figsize=(6,6)) + a1 = fig.add_axes([0,0,1,1]) + for ScanNum in range(first,last+countby,countby): + x,y=EA_Spectrum(ScanNum, EnergyAxis,FilePath,Prefix) + if norm is not None: maxvalue=max(y) + else: maxvalue=1 + plt.plot(x,y/maxvalue,label='#'+str(ScanNum)) + plt.legend(ncol=2, shadow=True, title=title, fancybox=True) + plt.grid(color='lightgray', linestyle='-', linewidth=0.5) + a1.plot + if EnergyAxis == 'BE': + a1.set_xlim(max(x),min(x)) + plt.show() + + + + +def plot_nc(*ScanNum,**kwgraph): + """ + ScanNum = Scan number to be plotted: single scan, or range (first,last,countby) to average. + kwgraph = EDC / FilePath / Prefix + - Transmission mode: angle integrated EDC. + - Angular mode: + default: band map only + EDC = 'y' : angle integrated EDC only + EDC = 'both': angle integrated EDC + band map + EnergyAxis = KE (default) or BE (BE uses work function from SES) + FilePath: by default plot scans for the current data folder (as shown on detector panel) + or specified folder path ending with '/': + e.g. user : FilePath='/net/s29data/export/data_29idc/2018_2/UserName/netCDF/' + e.g. staff: FilePath='/net/s29data/export/data_29idb/2018_2/netCDF/' + Prefix: by default, uses prefix as shown on detector panel ("EA_") + + """ + FilePath,Prefix,EDC,EnergyAxis,avg=None,None,None,'KE',None + if kwgraph is not None: + for key, value in list(kwgraph.items()): + if key=='FilePath': FilePath=value + if key=='Prefix': Prefix=value + if key=='EDC': EDC=value + if key=='EnergyAxis': EnergyAxis=value + if key=='avg': avg=1 + #Get lens mode + nc=nc_unpack(ScanNum[0],FilePath,Prefix) + LensMode=nc.variables['Attr_LensMode'][:][0] + #Loading Scans () + first=ScanNum[0] + if len(ScanNum)==1: + last =ScanNum[0] + countby=1 + else: + last=ScanNum[1] + countby=ScanNum[2] + for n in range(first,last+countby,countby): + x,intensity=EA_Spectrum(n,EnergyAxis,FilePath,Prefix) + x,y,img =EA_Image(n,EnergyAxis,FilePath,Prefix) + if n == first: + Spectra=intensity + Img=img + else: + if avg == 1: #Summing the Scans + print('averaging') + Spectra=np.add(Spectra, intensity) + Img=np.add(Img,img) + + #Getting plot size + if LensMode == 0 or EDC != None and EDC != 'both': #Integrated Angle only + hsize,vsize=6,3.5 + elif LensMode >0 and EDC == None: + hsize,vsize=6,4 + elif LensMode >0 and EDC == 'both': + hsize,vsize=6,8 + if kwgraph is not None: + for key, value in list(kwgraph.items()): + if key=='hsize': hsize=value + if key=='vsize': vsize=value + #plotting\ + if LensMode == 0 or EDC != None and EDC != 'both': #Integrated Angle only + #print('p-DOS only') + fig, ax = plt.subplots(figsize=(hsize,vsize)) # HxV + ax.plot(x,Spectra) + if EnergyAxis == "BE": + ax.set_xlim(max(x),min(x)) + else: + ax.set_xlim(min(x),max(x)) + ax.set(xlabel=EnergyAxis,ylabel='Intensity') + ax.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + ax.grid(color='lightgray', linestyle='-', linewidth=0.5) + elif LensMode >0 and EDC is None: #Imgage Only + #print('Image Only') + fig, ax = plt.subplots(figsize=(hsize,vsize)) # HxV + if EnergyAxis == 'BE': + fig=ax.imshow(Img,extent = [max(x), min(x), min(y), max(y)], aspect = 'auto') + else: + fig=ax.imshow(Img,extent = [min(x), max(x), min(y), max(y)], aspect = 'auto') + ax.set(xlabel=EnergyAxis,ylabel="Angle") + elif LensMode >0 and EDC == 'both': + #print('both') + fig, axes = plt.subplots(2,1,figsize=(hsize,vsize)) # HxV + axes=np.array(axes) + for (n,ax) in zip(list(range(2)),axes.flat): + if n == 0: + ax.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + ax.grid(color='lightgray', linestyle='-', linewidth=0.5) + ax.plot(x,Spectra) + if EnergyAxis == "BE": + ax.set_xlim(max(x),min(x)) + else: + ax.set_xlim(min(x),max(x)) + ax.set(xlabel=EnergyAxis,ylabel='Intensity') + if n == 1: + if EnergyAxis == 'BE': + ax.imshow(Img,extent = [max(x), min(x), min(y), max(y)], aspect = 'auto') + else: + ax.imshow(Img,extent = [min(x), max(x), min(y), max(y)], aspect = 'auto') + ax.set(xlabel=EnergyAxis,ylabel='Angle') + plt.tight_layout() + plt.show() + + +def plot_nc_Sum(first,last,**kwgraph): + + FilePath,Prefix=None,None + if kwgraph is not None: + for key, value in list(kwgraph.items()): + if key=='FilePath': FilePath=value + if key=='Prefix': Prefix=value + for n in range(first,last+1): + print(n) + nc=nc_unpack(n,FilePath,Prefix) + x,xname,ycrop,yname,img,hv,wk,PE=EA_data(nc) + LensMode=nc.variables['Attr_LensMode'][:][0] + if n == first: + datasum = nc.variables['array_data'] + x,xname,ycrop,yname,img,hv,wk,PE=EA_data(nc) + else: + data = nc.variables['array_data'] + tmp=datasum + datasum=np.add(tmp,data) + crop_data=data[:,338:819] + fig, ax = plt.subplots(figsize=(6,4)) # HxV + fig=ax.imshow(crop_data.squeeze(),extent = [min(x), max(x), min(ycrop), max(ycrop)], aspect = 'auto') + plt.show() + + + + + + + + + + + +############################################################################################### +##################################### FR curves fitting ####################################### +############################################################################################### + + +def fit_mda(scannum,det,FWHM_or_PolyOrder,fct,hkl_positionner=False,xrange=None,title='',marker='x',graph=True, filepath=None,prefix=None): + """ + fct= 'gauss','lorz','erf','poly','box' + hkl_positionner = 'H','K','L','tth','th','chi','phi' + """ + + if hkl_positionner == False: + x,y,x_name,y_name=mda_1D(scannum,det,1,0,filepath,prefix) + if hkl_positionner: + d={'h':46,'k':47,'l':48,'tth':54,'th':55,'chi':56,'phi':57} + x,y,x_name,y_name=mda_1D_vsDet(scannum,det,d[hkl_positionner.lower()],1,0,filepath,prefix) + + + + if BL.endstation == 'Kappa' and fct != 'poly': + try: + title=title + ' centroid = '+str(centroid_avg(scannum)) + except: + pass + + def closest(lst, K): + return lst[min(range(len(lst)), key = lambda i: abs(lst[i]-K))] + + def gaussian(x,*p): + amp, cen, wid, bkgd = p + return bkgd+amp*np.exp(-np.power(x - cen, 2.) / (2 * np.power(wid, 2.))) + + def lorentzian( x, *p): + amp, x0, gam, bkgd =p + return bkgd+amp * gam**2 / ( gam**2 + ( x - x0 )**2) + + def step(z,amp=1,bkgd=0,z0=0,width=1): + return (amp*erf((z-z0)/width))+bkgd + + def box(x, *p): + height, center, width ,bkgd = p + return bkgd+height*(center-width/2 < x)*(x < center+width/2) + + + + if xrange is not None: + x1=closest(x,xrange[0]) + x2=closest(x,xrange[1]) + xrange=[x.index(x1),x.index(x2)] + xmin=min(xrange) + xmax=max(xrange) + xcrop=x[xmin:xmax] + ycrop=y[xmin:xmax] + xdata = np.array(xcrop) + ydata = np.array(ycrop) + else: + xdata = np.array(x) + ydata = np.array(y) + + + Imax=np.max(ydata) + #print(xdata) + #print(ydata) + x0=xdata[np.where(ydata==Imax)][0] + A0=xdata[0] + + nPt_fit=200 + xfit = np.linspace(min(x),max(x), nPt_fit) + + + if fct == 'gauss': + FWHM=FWHM_or_PolyOrder + best_vals, covar = curve_fit(gaussian, xdata, ydata, p0=[Imax, x0, FWHM, A0]) #5 p0=[amp,cen,wid] + yfit=gaussian(xfit,*best_vals) + FWHM=2.355*best_vals[2] + center=best_vals[1] + print('Amplitude: ',best_vals[0]) + elif fct == 'lorz': + FWHM=FWHM_or_PolyOrder + best_vals, covar = curve_fit(lorentzian, xdata, ydata, p0=[Imax, x0, FWHM, A0]) #5 p0=[amp,cen,wid] + yfit=lorentzian(xfit,*best_vals) + FWHM=2.355*best_vals[2] + center=best_vals[1] + print('Amplitude: ',best_vals[0]) + elif fct == 'erf' or fct == 'box': + FWHM=FWHM_or_PolyOrder + xmax=np.max(xdata) + xmin=np.min(xdata) + amp=np.mean(ydata) + x0=np.mean(xdata) + ymin=np.min(ydata) + if fct == 'erf': + yfirst=ydata[np.where(xdata == xmin)][0] + ylast =ydata[np.where(xdata == xmax)][0] + if yfirst-ylast >0: amp=-abs(amp) #amp <0 step down + else: amp = abs(amp) #amp >0 step up + p0=[amp, ymin, x0, FWHM] + popt,pcor = curve_fit(step, xdata, ydata, p0=p0) + yfit=step(xfit,*popt) + FWHM=popt[3] + center=popt[2] + elif fct == 'box': + p0=[amp, x0, FWHM, A0] + popt,pcor = curve_fit(box, xdata, ydata, p0=p0) + yfit=box(xfit,*popt) + FWHM=popt[2] + center=popt[1] + elif fct == 'poly': + PolyRank=FWHM_or_PolyOrder + coefs = poly.polyfit(xdata, ydata, PolyRank) + yfit = poly.polyval(xfit, coefs) + result='' + for i in list(range(len(coefs))): + result=result+'a'+str(i)+' = '+str(round(coefs[i],3))+'\n' + print(result) + center=coefs + + if fct != 'poly': + print('Center: ',center) + print('FWHM: ',FWHM) + print('\n') + + + if graph == True: + center=round(center,4) + fig,(ax1)=plt.subplots(1,1) + fig.set_size_inches(5,4) + ax1.plot(xdata,ydata,label='mda #'+str(scannum),marker=marker) + ax1.plot(xfit,yfit,label='fit @ '+str(center)[0:6]) + ax1.set(ylabel=y_name) + ax1.set(xlabel=x_name) + ax1.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + ax1.grid(color='lightgray', linestyle='-', linewidth=0.5) + ax1.legend(shadow=True, title=title, fancybox=True) + + return center + + +def fit_centroid(n): + fit_mda(n,25,0,'poly',graph=False) + +def centroid_avg(scannum,det=25): # det = detector number for centroid position + avg=round(fit_mda_poly(scannum,det,0,graph=None)[0],2) + return avg + + +def fit_mda_gauss(scannum,det,FWHM,xrange=None,title='',marker='x',graph=1, filepath=None,prefix=None,scanIOC=None): + fct='gauss' + fit_mda(scannum,det,FWHM,fct,xrange,title,marker,graph, filepath,prefix,scanIOC) + + +def fit_mda_lorz(scannum,det,FWHM,xrange=None,title='',marker='x',graph=1, filepath=None,prefix=None,scanIOC=None): + fct='lorz' + fit_mda(scannum,det,FWHM,fct,xrange,title,marker,graph, filepath,prefix,scanIOC) + + +def fit_mda_erf(scannum,det,FWHM,xrange=None,title='',marker='x',graph=1, filepath=None,prefix=None,scanIOC=None): + fct='erf' + fit_mda(scannum,det,FWHM,fct,xrange,title,marker,graph, filepath,prefix,scanIOC) + +def fit_mda_poly(scannum,det,PolyRank,xrange=None,title='',marker='x',graph=1, filepath=None,prefix=None,scanIOC=None): + fct='poly' + fit_mda(scannum,det,PolyRank,fct,xrange,title,marker,graph, filepath,prefix,scanIOC) + + +def fit_mda_box(scannum,det,FWHM,xrange=None,title='',marker='x',graph=1, filepath=None,prefix=None,scanIOC=None): + fct='box' + fit_mda(scannum,det,FWHM,fct,xrange,title,marker,graph, filepath,prefix,scanIOC) + + + + + + + + +############################################################################################### +######################################### hkl ################################### +############################################################################################### + + + +def plot_hkl(mydata,n,x,y,title='',nlabel=''): + """mydata = instance of mdaFile object + n = scan number or [scan number1, ..., scan number N] + x = h, k or l index (D##) + d = detector index (D##) + """ + if type(n) == int: + maxpts=mydata.dim1[n].curr_pt + x_index=mydata.dim1[n].kappaDet()[x][0] + y_index=mydata.dim1[n].kappaDet()[y][0] + x_name =mydata.dim1[n].d[x_index].desc + y_name =mydata.dim1[n].kappaDet()[y][2] + plt.plot(mydata.dim1[n].d[x_index].data[:maxpts],mydata.dim1[n].d[y_index].data[:maxpts],label='scan #'+str(n)+' '+nlabel) + elif type(n) == list: + for i,j in enumerate(n): + maxpts=mydata.dim1[j].curr_pt + x_index=mydata.dim1[j].kappaDet()[x][0] + y_index=mydata.dim1[j].kappaDet()[y][0] + x_name =mydata.dim1[j].d[x_index].desc + y_name =mydata.dim1[j].kappaDet()[y][2] + try: + curve_label='scan #'+str(j)+' '+nlabel[i] + except: + curve_label='scan #'+str(j) + plt.plot(mydata.dim1[j].d[x_index].data[:maxpts],mydata.dim1[j].d[y_index].data[:maxpts],label=curve_label) + else: + print('n must be a single scan (int) or a list of scan') + return + plt.xlabel(x_name) + plt.ylabel(y_name) + plt.locator_params(tight=None,nbins=5,axis='x') + plt.legend(ncol=1, shadow=True, title=title, fancybox=True,loc='center left', bbox_to_anchor=(1, 0.5)) + plt.grid(color='lightgray', linestyle='-', linewidth=0.5) + plt.show() + + +def fit_hkl(mydata,n,pos,det,FWHM,fct='gauss',xrange=None,title='',marker='x',graph=1): + """ + mydata = instance of the class mdaFile + n = scan number + pos = D## for positioner + det = D## for detector + FWHM = width estimate + fct = 'gauss' or 'lorz' + xrange = [x1,x2] subrange for fit + """ + + + x_index=mydata.dim1[n].kappaDet()[pos][0] + y_index=mydata.dim1[n].kappaDet()[det][0] + x_name =mydata.dim1[n].d[x_index].desc + y_name =mydata.dim1[n].kappaDet()[det][2] + + x=mydata.dim1[n].d[x_index].data + y=mydata.dim1[n].d[y_index].data + + def closest(lst, K): + return lst[min(range(len(lst)), key = lambda i: abs(lst[i]-K))] + + if xrange is not None: + x1=closest(x,xrange[0]) + x2=closest(x,xrange[1]) + xrange=[np.where(x==x1),np.where(x==x2)] + xmin=min(xrange) + xmax=max(xrange) + xmin=min(xrange[0][0]) + xmax=max(xrange[1][0]) + xcrop=x[xmin:xmax] + ycrop=y[xmin:xmax] + xdata = np.array(xcrop) + ydata = np.array(ycrop) + else: + xdata = np.array(x) + ydata = np.array(y) + + Imax=np.max(ydata) + x0=xdata[np.where(ydata==Imax)][0] + + nPt_gaus=200 + xfit = np.linspace(min(x),max(x), nPt_gaus) + + + def lorentzian( x, amp, x0, gam ): + return amp * gam**2 / ( gam**2 + ( x - x0 )**2) + + def gaussian(x, amp, cen, wid): + return amp*np.exp(-np.power(x - cen, 2.) / (2 * np.power(wid, 2.))) + + if fct == 'gauss': + best_vals, covar = curve_fit(gaussian, xdata, ydata, p0=[Imax, x0, FWHM]) #5 p0=[amp,cen,wid] + yfit=gaussian(xfit,*best_vals) + elif fct == 'lorz': + best_vals, covar = curve_fit(lorentzian, xdata, ydata, p0=[Imax, x0, FWHM]) #5 p0=[amp,cen,wid] + yfit=lorentzian(xfit,*best_vals) + else: + print('Not a valid function: fct = "gauss" or fct = "lorz"') + return + + FWHM=2.355*best_vals[2] + center=best_vals[1] + print('Amplitude: ',best_vals[0]) + print('Center: ',center) + print('FWHM: ',FWHM) + + if graph is not None: + fig,(ax1)=plt.subplots(1,1) + fig.set_size_inches(5,4) + ax1.plot(xdata,ydata,label='data',marker=marker) + ax1.plot(xfit,yfit,label='fit @ '+str(center)[0:6]) + ax1.set(ylabel=y_name) + ax1.set(xlabel=x_name) + ax1.locator_params(tight=None,nbins=5,axis='x') + ax1.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + ax1.grid(color='lightgray', linestyle='-', linewidth=0.5) + ax1.legend(shadow=True, title=title, fancybox=True) + + return center + + +def fit_d4(n=BL.mda.lastFileNum()): + d4=fit_mda(n,34,0.1,'gauss',title='mda_'+str(n).zfill(4)) + return round(d4,3) + +def fit_d3(n=BL.mda.lastFileNum()): + d3=fit_mda(n,33,3,'gauss',title='mda_'+str(n).zfill(4)) + return round(d3,3) + +def fit_z(n=BL.mda.lastFileNum(),d=33): + z=fit_mda(n,d,500,'erf',title='mda_'+str(n).zfill(4)) + return round(z,0) + +def plotd(): + dets=[34,33,35] + n=BL.mda.lastFileNum() + d=dets[caget('29idKappa:det:set')] + plot_mda(n,d,title='mda_'+str(n).zfill(4)) + + + +def Find_kth_zero(th_0,th_180): + """ + th_0 is the motor position for specular near zero + th_180 is motor position for spectular near 180 + """ + + Offset =0.5*(180 - th_180 - th_0) + + print("with motor at 0, the actual value is ",Offset) + + +# def read_hkl_old(n,user=None,run=None): +# h=[] +# k=[] +# l=[] +# if user is None: user=MDA_CurrentUser() +# if run is None: run=MDA_CurrentRun() +# with open('/home/beams/29IDUSER/Documents/User_Folders/'+user+'/hkl/scan_'+str(n)+'.txt') as csv_file: +# csv_f=csv.reader(csv_file,delimiter=',') +# for row in csv_f: +# if not row[0]=='h': +# h.append(float(row[0])) +# k.append(float(row[1])) +# l.append(float(row[2])) +# return np.array(h), np.array(k), np.array(l) + +# def plot_hkl_old(mydata,n1,n2=None,which='h',det=39,cropMin=None,cropMax=None,title=''): +# D=mydata.kappaDet(n1) +# d=D[det][1] +# if which == 'h': +# m = 0 +# if which == 'k': +# m = 1 +# if which == 'l': +# m = 2 + +# plt.figure(num=None, figsize=(6, 6), dpi=80, facecolor='w', edgecolor='k') +# if n2 == None: +# n=n1 +# #dif=len(read_hkl(n)[m])-mydata.dim1[n].curr_pt +# if cropMax is not None: +# x=read_hkl(n)[m][cropMin:-cropMax] +# else: +# x=read_hkl(n)[m][cropMin:] + +# plt.plot(x,mydata.dim1[n].d[d].data,label='scan #'+str(n),marker='+') +# else: +# for n in range(n1,n2+1): +# #dif=len(read_hkl(n)[m])-mydata.dim1[n].curr_pt +# if cropMax is not None: +# x=read_hkl(n)[m][cropMin:-cropMax] +# else: +# x=read_hkl(n)[m][cropMin:] + +# plt.plot(x,mydata.dim1[n].d[d].data,label='scan #'+str(n),marker='+') +# plt.xlabel(which) +# plt.ylabel(D[det][-1]) +# plt.legend(ncol=1, shadow=True, title=title, fancybox=True,loc='center left', bbox_to_anchor=(1, 0.5)) +# #plt.title(title) +# plt.grid(color='lightgray', linestyle='-', linewidth=0.5) +# plt.show() + + + +# def fit_hkl_gauss_old(scannum,det,FWHM,which='l',cropMin=None,cropMax=None,xrange=None,title='',marker='x',graph=1): +# motor,y,motor_name,y_name=mda_1D(scannum,det,1,0) +# if which == 'h': +# m = 0 +# if which == 'k': +# m = 1 +# if which == 'l': +# m = 2 +# if cropMax is not None: +# x=read_hkl(scannum)[m][cropMin:-cropMax] +# else: +# x=read_hkl(scannum)[m][cropMin:] +# def closest(lst, K): +# return lst[min(range(len(lst)), key = lambda i: abs(lst[i]-K))] + +# if xrange is not None: +# x1=closest(x,xrange[0]) +# x2=closest(x,xrange[1]) +# xrange=[np.where(x==x1),np.where(x==x2)] +# xmin=min(xrange) +# xmax=max(xrange) +# xmin=min(xrange[0][0]) +# xmax=max(xrange[1][0]) +# xcrop=x[xmin:xmax] +# ycrop=y[xmin:xmax] +# xdata = np.array(xcrop) +# ydata = np.array(ycrop) +# else: +# xdata = np.array(x) +# ydata = np.array(y) + +# Imax=np.max(ydata) +# x0=xdata[np.where(ydata==Imax)][0] + +# nPt_gaus=200 +# xfit = np.linspace(min(x),max(x), nPt_gaus) + +# def gaussian(x, amp, cen, wid): +# return amp*np.exp(-np.power(x - cen, 2.) / (2 * np.power(wid, 2.))) + +# best_vals, covar = curve_fit(gaussian, xdata, ydata, p0=[Imax, x0, FWHM]) #5 p0=[amp,cen,wid] +# yfit=gaussian(xfit,*best_vals) +# FWHM=2.355*best_vals[2] +# center=best_vals[1] +# print('Amplitude: ',best_vals[0]) +# print('Center: ',center) +# print('FWHM: ',FWHM) + +# if graph is not None: +# fig,(ax1)=plt.subplots(1,1) +# fig.set_size_inches(5,4) +# ax1.plot(xdata,ydata,label='data',marker=marker) +# ax1.plot(xfit,yfit,label='fit @ '+str(center)[0:6]) +# ax1.set(ylabel=y_name) +# ax1.set(xlabel=which) +# ax1.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) +# ax1.grid(color='lightgray', linestyle='-', linewidth=0.5) +# ax1.legend(shadow=True, title=title, fancybox=True) + +# return center + + +############################################################################################### +######################################### Start Of The Week ################################### +############################################################################################### + +def plot_StartOfTheWeek_DetDict(branch): + """ + returns the detectors for a given branch + + """ + DetDict={'c':(9,7,8,15),'d':(9,7,8,14)} + return DetDict[branch] + +def StartOfTheWeek_plot(branch,FirstScanNum,**kwargs): + """ + Plots the data from StartOfTheWeek + + branch is used to set the detectors + detCA4,detH,detV,detDiode=plot_StartOfTheWeek_Det(branch) + + FirstScanNum is based on slit1A + slit1A-H = FirstScanNum + slit1A-V = FirstScanNum + 1 + wire1-H = FirstScanNum + 2 + wire1-V = FirstScanNum + 3 + monoVslit = FirstScanNum + 4/5/6/7 + flux = FirstScanNum + 8 + + Slit 1A and Wire scans: determine ID steering and Slit1A position + Scan_MonoVsSlit: determine the steering from M0/M1 + by default the full range is plotted (pnt_first=0, pnt_last=inf) + refine the plot via + plot_StartOfTheWeek_mono(branch,FirstScanNum,pnt_first,pnt_last) + + kwargs: + filepath = None, uses current mda filepath unless specified + e.g. user : filepath='/net/s29data/export/data_29idc/2018_2/UserName/mda/' + e.g. staff: filepath='/net/s29data/export/data_29idb/2018_2/mda/' + prefix = None, uses current mda prefix unless specified + scanIOC = None, uses BL_ioc() unless specified + + scanType = ['slit1','wire','flux','monoVslit'], full set by default + + ref_firstScanNum to plot reference spectra + ref_branch = branch, unless specified + ref_filepath = filepath, unless specified + ref_prefix = prefix, unless specified + + steering out => moves beam more positive (20 urad ~ 0.5 mm) + steering up => moves beam more positive (20 urad ~ 0.5 mm) + +figure, axes = plt.subplots(nrows=2, ncols=2) +axes[0, 0].plot(x, y) + """ + + kwargs.setdefault('filepath',BL.mda.filepath()) + kwargs.setdefault('prefix',BL.mda.prefix()) + + kwargs.setdefault('scanType',['slit1','wire','flux','monoVslit']) + + kwargs.setdefault('ref_firstScanNum',None) + kwargs.setdefault('ref_fpath',None) + kwargs.setdefault('ref_branch',branch) + kwargs.setdefault('ref_filepath',BL.mda.filepath()) + kwargs.setdefault('ref_prefix',BL.mda.prefix()) + + kwargs.setdefault('debug',False) + + + scanNum=FirstScanNum + ref_firstScanNum=kwargs['ref_firstScanNum'] + detCA4,detH,detV,detDiode=plot_StartOfTheWeek_DetDict(branch) + + + if 'slit1' in kwargs['scanType']: + if kwargs['debug']: + print('slit1') + plot_StartOfTheWeek_slit1A(branch,scanNum,**kwargs) + scanNum+=2 + if kwargs['ref_firstScanNum'] is not None: + kwargs.update({'ref_firstScanNum':ref_firstScanNum+2}) + + + if 'wire' in kwargs['scanType']: + if kwargs['debug']: + print('wire') + plot_StartOfTheWeek_wire(branch,scanNum,**kwargs) + scanNum+=2 + if kwargs['ref_firstScanNum'] is not None: + ref_firstScanNum+=2 + kwargs.update({'ref_firstScanNum':ref_firstScanNum+2}) + + if 'monoVslit' in kwargs['scanType']: + d=IEXdata(scanNum,path=kwargs['filepath'],prefix=kwargs['prefix'],q=1) + if d.mda[scanNum].header.all['rank']<2: #quick + print(scanNum, detDiode) + V2=fit_mda(scanNum,detDiode,1,'gauss',filepath=kwargs['filepath'],prefix=kwargs['prefix'],scanIOC=kwargs['scanIOC'],title='2V - mda_'+str(scanNum)) + H2=fit_mda(scanNum+1,detDiode,1,'gauss',filepath=kwargs['filepath'],prefix=kwargs['prefix'],scanIOC=kwargs['scanIOC'],title='2H - mda_'+str(scanNum+1)) + V1=fit_mda(scanNum+2,detDiode,1,'gauss',filepath=kwargs['filepath'],prefix=kwargs['prefix'],scanIOC=kwargs['scanIOC'],title='1V - mda_'+str(scanNum+2)) + H1=fit_mda(scanNum+3,detDiode,1,'gauss',filepath=kwargs['filepath'],prefix=kwargs['prefix'],scanIOC=kwargs['scanIOC'],title='1H - mda_'+str(scanNum+3)) + plt.show() + print('\nFit positions:') + print(f"V2={round(V2,3)}") + print(f"H2={round(H2,3)}") + print(f"V1={round(V1,3)}") + print(f"H1={round(H1,3)}") + + else: #MonoVsSlit + print('\n# To plot the Mono vs Slit data use:\n') + print('\tplot_MonoVsSlit(\"'+branch+'\",'+str(scanNum)+','+str(detDiode)+',0,inf)'+'\t#2V') + print('\tplot_MonoVsSlit(\"'+branch+'\",'+str(scanNum+1)+','+str(detDiode)+',0,inf)'+'\t#2H') + print('\tplot_MonoVsSlit(\"'+branch+'\",'+str(scanNum+2)+','+str(detDiode)+',0,inf)'+'\t#1V') + print('\tplot_MonoVsSlit(\"'+branch+'\",'+str(scanNum+3)+','+str(detDiode)+',0,inf)'+'\t#1H') + print('\n# (pnt_first,pnt_last0=(0,inf) => plots all') + print('# select specific first/last to refine.') + + print('\nREMEMBER to update slit center using: \tupdate_slit_dict()' ) + scanNum+=4 + if kwargs['ref_firstScanNum'] is not None: + kwargs.update({'ref_firstScanNum':ref_firstScanNum+4}) + + if 'flux' in kwargs['scanType']: + if kwargs['debug']: + print('flux') + plot_StartOfTheWeek_flux(branch,scanNum,**kwargs) + +def plot_StartOfTheWeek_slit1A(branch,scanNum,**kwargs): + """ + plots the slit1A scans + scanNum = slit1A-H scanNum + """ + kwargs.setdefault('filepath',BL.mda.filepath()) + kwargs.setdefault('prefix',BL.mda.prefix()) + kwargs.setdefault('plotType',['slit1','wire','flux','monoVslit']) + kwargs.setdefault('ref_firstScanNum',None) + kwargs.setdefault('ref_fpath',None) + kwargs.setdefault('ref_branch',branch) + kwargs.setdefault('ref_filepath',BL.mda.filepath()) + kwargs.setdefault('ref_prefix',BL.mda.prefix()) + kwargs.setdefault('debug',False) + + detCA4,detH,detV,detDiode=plot_StartOfTheWeek_DetDict(branch) + ref_detCA4,ref_detH,ref_detV,ref_detDiode=plot_StartOfTheWeek_DetDict(kwargs['ref_branch']) + + figure, axes = plt.subplots(nrows=1, ncols=2,figsize=(10,3)) + for i,t in enumerate(['1H center scan','1V center scan']): + d=IEXdata(scanNum,path=kwargs['filepath'],prefix=kwargs['prefix'],q=1) + axes[i].plot(d.mda[scanNum].det[detCA4].scale['x'], d.mda[scanNum].det[detCA4].data,marker='x',label=str(scanNum)) + scanNum+=1 + if kwargs['ref_firstScanNum'] is not None: + ref_scanNum=kwargs['ref_firstScanNum']+i + ref_d=IEXdata(ref_scanNum,path=kwargs['ref_filepath'],prefix=kwargs['ref_prefix'],q=1) + axes[i].plot(ref_d.mda[ref_scanNum].det[ref_detCA4].scale['x'], ref_d.mda[ref_scanNum].det[ref_detCA4].data,marker='x',label=str(ref_scanNum)) + axes[i].grid(color='lightgray', linestyle='-', linewidth=0.5) + axes[i].title.set_text(t) + axes[i].legend() + plt.show() + print("\nsteering out => move beam more positive (10 urad ~ 0.25 mm)") + print("steering up => move beam more positive (10 urad ~ 0.25 mm)") + print("\nTo fit beam position use:\n") + print("detCA4,detH,detV,detDiode=plot_StartOfTheWeek_DetDict('"+kwargs['ref_branch']+"')") + print("fit_mda("+str(scanNum-2)+",detCA4,1,'gauss',xrange=(-1,1))") + print("fit_mda("+str(scanNum-1)+",detCA4,1,'gauss',xrange=(-1,1))\n") + + +def plot_StartOfTheWeek_wire(branch,scanNum,**kwargs): + """ + plots the wire scans + scanNum = wire-H scanNum + """ + kwargs.setdefault('filepath',BL.mda.filepath()) + kwargs.setdefault('prefix',BL.mda.prefix()) + kwargs.setdefault('plotType',['slit1','wire','flux','monoVslit']) + kwargs.setdefault('ref_firstScanNum',None) + kwargs.setdefault('ref_fpath',None) + kwargs.setdefault('ref_branch',branch) + kwargs.setdefault('ref_filepath',BL.mda.filepath()) + kwargs.setdefault('ref_prefix',BL.mda.prefix()) + kwargs.setdefault('debug',False) + + detCA4,detH,detV,detDiode=plot_StartOfTheWeek_DetDict(branch) + ref_detCA4,ref_detH,ref_detV,ref_detDiode=plot_StartOfTheWeek_DetDict(kwargs['ref_branch']) + + figure, axes = plt.subplots(nrows=1, ncols=2,figsize=(10,3)) + for i,t in enumerate(['H-wire','V-wire']): + d=IEXdata(scanNum,path=kwargs['filepath'],prefix=kwargs['prefix'],q=1) + #niceplot(d.mda[scanNum].det[DetDict[branch][1+i]],marker='x',label=str(scanNum)) + detNum=plot_StartOfTheWeek_DetDict(branch)[1+i] + axes[i].plot(d.mda[scanNum].det[detNum].scale['x'], d.mda[scanNum].det[detNum].data,marker='x',label=str(scanNum)) + scanNum+=1 + if kwargs['ref_firstScanNum'] is not None: + ref_scanNum=kwargs['ref_firstScanNum']+i + ref_d=IEXdata(ref_scanNum,path=kwargs['ref_filepath'],prefix=kwargs['ref_prefix'],q=1) + #niceplot(ref_d.mda[ref_scanNum].det[DetDict[kwargs["ref_branch"]][1+i]],marker='x',label=str(ref_scanNum)) + detNum=plot_StartOfTheWeek_DetDict(kwargs["ref_branch"])[1+i] + axes[i].plot(ref_d.mda[ref_scanNum].det[detNum].scale['x'], ref_d.mda[ref_scanNum].det[detNum].data,marker='x',label=str(ref_scanNum)) + axes[i].grid(color='lightgray', linestyle='-', linewidth=0.5) + axes[i].title.set_text(t) + axes[i].legend() + plt.show() + +def plot_StartOfTheWeek_flux(branch,scanNum,**kwargs): + """ + plots the wire scans + scanNum = wire-H scanNum + """ + kwargs.setdefault('filepath',BL.mda.filepath()) + kwargs.setdefault('prefix',BL.mda.prefix()) + kwargs.setdefault('plotType',['slit1','wire','flux','monoVslit']) + kwargs.setdefault('ref_firstScanNum',None) + kwargs.setdefault('ref_fpath',None) + kwargs.setdefault('ref_branch',branch) + kwargs.setdefault('ref_filepath',BL.mda.filepath()) + kwargs.setdefault('ref_prefix',BL.mda.prefix()) + kwargs.setdefault('debug',False) + + detCA4,detH,detV,detDiode=plot_StartOfTheWeek_DetDict(branch) + ref_detCA4,ref_detH,ref_detV,ref_detDiode=plot_StartOfTheWeek_DetDict(kwargs['ref_branch']) + + for t in ['ID peak @ 500eV']: + d=IEXdata(scanNum,path=kwargs['filepath'],prefix=kwargs['prefix'],q=1) + niceplot(d.mda[scanNum].det[detDiode],marker='x',label=str(scanNum)) + scanNum+=1 + if kwargs['ref_firstScanNum'] is not None: + ref_scanNum=kwargs['ref_firstScanNum']+2 + ref_d=IEXdata(ref_scanNum,path=kwargs['ref_filepath'],prefix=kwargs['ref_prefix'],q=1) + niceplot(ref_d.mda[ref_scanNum].det[ref_detDiode],marker='x',label=str(ref_scanNum)) + ref_scanNum+=1 + plt.grid(color='lightgray', linestyle='-', linewidth=0.5) + plt.title(t) + plt.legend() + plt.show() + +def plot_MonoVsSlit(branch,ScanNum,detDiode,pnt_first,pnt_last,norm=True,filepath=None,prefix=None,scanIOC=None): + """ + Plots Scan_MonoVsSlit to determine the steering from M0/M1 + To plot the full range (pnt_first=0, pnt_last=inf) + plot_StartofWeek_mono(branch,FirstScanNum+4,pnt_first,pnt_last) + + filepath: by default plot scans for the current data folder (as defined in BL_ioc() ScanRecord SaveData) + or specified folder path ending with '/': + e.g. user : filepath='/net/s29data/export/data_29idc/2018_2/UserName/mda/' + e.g. staff: filepath='/net/s29data/export/data_29idb/2018_2/mda/' + prefix: by default, uses prefix as defined in ScanRecord ("mda_") + """ + x,y,z,x_name,y_name,z_name=mda_2D(ScanNum,detDiode,filepath,prefix,scanIOC) + Which=str(y_name)[10:12] + if pnt_last is inf: + pnt_last=len(z)-1 + for i in range(pnt_first,pnt_last+1): + maxvalue=max(z[i]) + if norm == True: + plt.plot(x,z[i]/maxvalue,label='#'+str(i)+': '+str(round(y[i],2))) + else: + plt.plot(x,z[i],label='#'+str(i)+': '+str(round(y[i],2))) + plt.legend(bbox_to_anchor=(1, 0), loc='lower left', ncol=2,shadow=True, title="ScanNum: "+str(ScanNum)+"\nSlit-"+Which+" position", fancybox=True) + #plt.legend(loc='lower left',ncol=2, shadow=True, title="ScanNum: "+str(ScanNum)+"\nSlit-"+Which+" position", fancybox=True) + plt.grid(color='lightgray', linestyle='-', linewidth=0.5) + plt.show() + + +############################################################################################### +######################################### IDCalibration New ################################### +############################################################################################### + + + +def id_calibration_fit(first_scan,last_scan,det,PolyRank,**kwargs): + + + #id_calibration_fit(FirstLast[0],FirstLast[1],det,poly_order,mytitle=mytitle,countby=countby,plotMin=plt_min,plotMax=plt_max,plotType=plotType,filepath=filepath,prefix=prefix) + """ + Fits calibration curves fpr each GRTs and modes included in the data set. Creates 3 dictionnaries: + + coefs={[GRT][mode][[breakpoint1,[coefs]],[breakpoint2,[coefs]...} + xdata= {[GRT][mode][[breakpoint1,[flux curve x axis]],[breakpoint2,[flux curve x axis]...} + fdata= {[GRT][mode][[breakpoint1,[flux curve y axis]],[breakpoint2,[flux curve y axis]...} + + kwargs: + countby = 1 by default + mytitle = '' by default + plotMin = min x range for plotting (default 250) + plotMax = max x range for plotting (default 3000) + plotType = ['dif,'fit,'flux'], full set by default + filepath = None, uses current mda filepath unless specified + e.g. user : filepath='/net/s29data/export/data_29idc/2018_2/UserName/mda/' + e.g. staff: filepath='/net/s29data/export/data_29idb/2018_2/mda/' + prefix = None, uses current mda prefix unless specified + scanIOC = None, uses BL_ioc() unless specified + """ + + kwargs.setdefault('countby',1) + kwargs.setdefault('mytitle','') + #kwargs.setdefault('plotMin',2000) + kwargs.setdefault('plotMin',225) + kwargs.setdefault('plotMax',3000) + kwargs.setdefault('plotType',['fit','dif','flux']) + kwargs.setdefault('filepath',BL.mda.filepath()) + kwargs.setdefault('prefix',BL.mda.prefix()) + + countby=kwargs['countby'] + mytitle=kwargs['mytitle'] + plotMin=kwargs['plotMin'] + plotMax=kwargs['plotMax'] + filepath=kwargs['filepath'] + prefix=kwargs['prefix'] + + + def calibration_curve(first,last,det,countby,filepath,prefix): + mono_list=[] + ID_list=[] + max_list=[] + flux_list=[] + print("filepath = ",filepath) + print("prefix = ",prefix) + print("First scan = ",first) + print("Last scan = ",last) + for i in range(first,last,countby): + x,y,x_name,y_name=mda_1D(i,det,1,0,filepath,prefix) #mda_1D(ScanNum,DetectorNum,coeff=1,bckg=0,filepath=None,prefix=None,scanIOC=None): + v,w,v_name,w_name=mda_1D(i, 4 ,1,0,filepath,prefix) + if y != []: + n=y.index(max(y)) # finds the peak max intensity index + e=round(x[n],2) # finds the corresponding mono energy + sp=round(w[2]*1000,0) # reads the ID set point + mono_list.append(e) + max_list.append(max(y)) + ID_list.append(sp) + flux_list.append(current2flux(max(y),e,p=None)) + return mono_list,ID_list,max_list,flux_list + + Mono_max,ID_SP,int_max,flux=calibration_curve(first_scan,last_scan+1,det,countby,filepath,prefix) + nPt_gaus=200 + x_HR = np.linspace(Mono_max[0], Mono_max[-1], nPt_gaus) + + # Data + xdata = np.array(Mono_max) + ydata = np.array(ID_SP) + zdata = np.array(int_max) + fdata = np.array(flux) + # Fitting + coefs = poly.polyfit(xdata, ydata, PolyRank) + ffit_HR = poly.polyval(x_HR, coefs) + ffit_Coarse = poly.polyval(xdata, coefs) + # Residual + Dif=np.array(ID_SP)-np.array(ffit_Coarse) + ratio=(np.array(ID_SP)-np.array(ffit_Coarse))/(np.array(ID_SP)/100) + # Plot differences + #print('plotMin = ',str(plotMin)) + if 'dif' in kwargs['plotType']: + fig = plt.figure(figsize=(12,6)) + plt.plot(Mono_max,ID_SP,marker='x',markersize=5,color='g',linewidth=0,label='data') + plt.plot(x_HR,ffit_HR,color='b',label='SP-fit') + plt.plot(xdata,Dif*100,marker='x',color='r',label='Dif x100') + plt.plot(xdata,ratio*1000,marker='x',color='g',label='Difx1000/ID') + plt.ylabel('ID SP') + plt.xlabel('Mono') + plt.xlim(plotMin,plotMax) + plt.legend(ncol=2, shadow=True, title=mytitle, fancybox=True) + plt.grid(linestyle='-', linewidth='0.5', color='grey') + plt.show() + # Plot raw data + fit + if 'fit' in kwargs['plotType']: + fig = plt.figure(figsize=(12,6)) + a1 = fig.add_axes([0,0,1,1]) + a1.plot(xdata+Dif,zdata,marker='*',markersize=10,color='r',linewidth=0,label='Interpolated ID SP') + for i in range(first_scan,last_scan+1): + x,y,x_name,y_name=mda_1D(i,det,1,0,filepath,prefix) + a1.plot(x,y,color='b') + a1.set_xlim(plotMin,plotMax) + a1.set(xlabel='Mono') + a1.set(ylabel='ID SP') + a1.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + a1.grid(linestyle='-', linewidth='0.5', color='grey') + plt.legend(ncol=2, shadow=True, title=mytitle, fancybox=True) + plt.show() + # Plot flux curves: + fdata_x=xdata+Dif + if 'flux' in kwargs['plotType']: + fig = plt.figure(figsize=(12,6)) + a1 = fig.add_axes([0,0,1,1]) + a1.plot(fdata_x,fdata,color='r',linewidth=1,label='Flux curves') + a1.set_xlim(plotMin,plotMax) + a1.set(xlabel='Mono') + a1.set(ylabel='ID SP') + a1.set_yscale('log') + #a1.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + a1.grid(linestyle='-', linewidth='0.5', color='grey') + plt.legend(ncol=2, shadow=True, title=mytitle, fancybox=True) + plt.show() + return coefs, fdata_x,fdata + + + + + + +def read_id_files(first,last,filepath=None,prefix=None,q=True): + """ + Reads extra PVs + Return a list of [[ScanNum,ID_SP,grt,mode],...] for mda files between first and last. + """ + if filepath == None: + filepath=BL.mda.filepath() + if prefix == None: + prefix=BL.mda.prefix()[:-1] + elif prefix[-1] == "_": + prefix=prefix[:-1] + mydata=mdaFile(first,last,filepath=filepath,prefix=prefix,q=q) + value=[] + modeDict={'CW, RCP':'RCP','CCW, LCP':'LCP','H':'H','V':'V'} + grtDict={1200:'MEG',2400:'HEG'} + for mdaNum in mydata.scanList: + if first<=mdaNum<=last: + extraPVs=mydata.header[mdaNum].all + try: + ID=round(extraPVs['ID29:EnergySet.VAL'][2][0]*1000,2) + mode=modeDict[extraPVs['ID29:ActualMode'][2]] # extraPVs return 'CW, RCP' + grt=grtDict[extraPVs['29idmono:GRT_DENSITY'][2][0]] # extraPVs return 1200 + except KeyError: + ID=round(extraPVs[b'ID29:EnergySet.VAL'][2][0]*1000,2) + mode=modeDict[str(extraPVs[b'ID29:ActualMode'][2])[2:-1]] + grt=grtDict[extraPVs[b'29idmono:GRT_DENSITY'][2][0]] # extraPVs return 1200 + if len(value)>0 and value[-1][1:] == [ID,grt,mode]: + pass + else: + value.append([mdaNum,ID,grt,mode]) + return value + + + +def id2num(ID,grt,mode,first=0,last=inf,ignore=[],filepath=None,prefix=None,q=True): # Not efficient - requires to read all 600 files everytime + """ + Return ScanNum corresponding to a given ID_SP from ExtraPVs (using mdaFile) + """ + if filepath == None: + filepath=BL.mda.filepath() + if prefix == None: + prefix=BL.mda.prefix()[:-1] + elif prefix[-1] == "_": + prefix=prefix[:-1] + ScanNum = 0 + data_list = read_id_files(first,last,filepath,prefix,q) + data_short=[x for x in data_list if x[2:]==[grt,mode] and x[0] not in ignore] + step=data_short[1][1]-data_short[0][1] + precision=int(step/2) + ID1=ID-precision + ID2=ID+precision + ScanNum=[x[0] for x in data_short if ID1<= x[1]<= ID2] + if len(ScanNum)==0: result=None + else: result=ScanNum[0] + return result + + + +def num2id(ScanNum,grt,mode,first=0,last=inf,ignore=[],filepath=None,prefix=None,q=True): # Not efficient - requires to read all 600 files everytime + """ + Return ID SP corresponding to a given ScanNum from ExtraPVs (using mdaFile) + """ + if filepath == None: + filepath=BL.mda.filepath() + if prefix == None: + prefix=BL.mda.prefix()[:-1] + elif prefix[-1] == "_": + prefix=prefix[:-1] + ID = 0 + data_short=[] + data_list = read_id_files(first,last,filepath,prefix,q) + data_short=[x for x in data_list if x[2:]==[grt,mode] and x[0] not in ignore] + ID=[x[1] for x in data_short if x[0] == ScanNum] + return ID[0] + + + +def extract_id(first,last,ignore=[],breakpts={'RCP':[600],'H':[400,600],'V':[600],'LCP':[600],'HEG':[],'MEG':[2200,2475]},filepath=None,prefix=None,q=True): + """ + Breaksdown the info from a calibration files into grt & mode with the corresponding breakpoints (hardcoded): + [[first, last, 'HEG', 'RCP', [600]], + [first, last, 'HEG', 'H', [400, 600]]...] + """ + if filepath == None: + filepath=BL.mda.filepath() + if prefix == None: + prefix=BL.mda.prefix()[:-1] + elif prefix[-1] == "_": + prefix=prefix[:-1] + IDlog=read_id_files(first,last,filepath,prefix,q) + #breakpts={'RCP':[600],'H':[400,600],'V':[600],'LCP':[600],'HEG':[],'MEG':[2200]} + #breakpts={'RCP':[600],'H':[400,600],'V':[600],'LCP':[600],'HEG':[],'MEG':[2200]} + #breakpts={'RCP':[600],'H':[600],'V':[600],'LCP':[600],'HEG':[],'MEG':[2200,2475]} # FR changed H breakpoints, missing low energy scans 06/14/2021 + data=[] + for grt in ['HEG','MEG']: + for mode in ['RCP','H','V','LCP']: + tmp=[x for x in IDlog if x[2:]==[grt,mode] and x[0] not in ignore] + #print(tmp) + if len(tmp)>0: + tmp=[tmp[0][0],tmp[-1][0],grt,mode,breakpts[mode]+breakpts[grt]] + data.append(tmp) + return data + + + +def update_id_dict(first,last,det,update_file,ignore,**kwargs): + + + + """ + Calculate new calibration curve for a full set of data.' + But what if the set is not complete? To be addressed by Future Me + If update_file == True (ONLY): + \tupdate the ID dictionary '/home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/Dict_IDCal.txt' + """ + + if update_file == True: + foo=input('\nAre you sure you want to update the ID calibration dictionary?\n>') + if foo == 'Y' or foo == 'y' or foo == 'yes'or foo == 'YES' or foo == 'Yes': + foo = 'yes' + print('\nWill save new dictionary to: \'/home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/Dict_IDCal.txt\'\n') + else: + print('\nCalibration curves will not be saved.\n') + else: + print('\nCalibration curves will not be saved.\n') + + kwargs.setdefault('countby',1) + kwargs.setdefault('mytitle','') + kwargs.setdefault('plotMin',225) + kwargs.setdefault('plotMax',3000) + kwargs.setdefault('plotType',['fit','dif','flux']) + kwargs.setdefault('filepath',BL.mda.filepath()) + kwargs.setdefault('prefix',BL.mda.prefix()) + kwargs.setdefault('q',True) + kwargs.setdefault('breakpts',{'RCP':[600],'H':[400,600],'V':[600],'LCP':[600],'HEG':[],'MEG':[2200,2475]}) + + + breakpts=kwargs['breakpts'] + plotType=kwargs['plotType'] + filepath=kwargs['filepath'] + prefix=kwargs['prefix'] + q=kwargs['q'] + + ID_data=extract_id(first,last,ignore,breakpts,filepath,prefix,q) + + ##### Extract parameters: + + mode_dict ={'RCP':0,'LCP':1,'V':2,'H':3} + id_coef={} + new_id_function={} + id_flux={} + flux_dict={} + id_energy={} + energy_dict={} + for mylist in ID_data: + if len(mylist) > 0: # in case of incomplete data set + tmp0,tmp1,tmp2=[],[],[] + first_scan=mylist[0] + last_scan=mylist[1] + scan_list=[[first_scan,last_scan]] + countby=1 + #det=15 + #det=33 + poly_order=4 + grt=mylist[2] + mode=mylist[3] + breakpts_energy=mylist[4] + breakpts_scan=[] + mytitle=mode+' - '+grt + + ## generating list of scans depending on breakpoints: + if breakpts_energy != []: + scan_list=[] + for x in breakpts_energy: + breakpts_scan.append(id2num(x,grt,mode,first,last,ignore,filepath,prefix,q)) + breakpts_scan = [i for i in breakpts_scan if i] + print(breakpts_scan) + for c,x in enumerate(breakpts_scan): # can get the number by extracting step size then /2 + if c == 0: + if x == first_scan: pass + else: scan_list.append([first_scan,x]) + if 0 < c < len(breakpts_scan)-1: + scan_list.append([breakpts_scan[c-1],x]) + scan_list.append([x,breakpts_scan[c+1]]) + if c == len(breakpts_scan)-1 and (c-1) == 0: + scan_list.append([breakpts_scan[c-1],x]) + if c == len(breakpts_scan)-1: + scan_list.append([x,last_scan]) + final_list = [i for n, i in enumerate(scan_list) if i not in scan_list[:n]] # remove doubles + energy_list=[] + for x in final_list: + ID1=num2id(x[0],grt,mode,first,last,ignore,filepath,prefix,q) + ID2=num2id(x[1],grt,mode,first,last,ignore,filepath,prefix,q) + energy_list.append([ID1,ID2]) + energy_list_2=energy_list # we need the final value for plot purposes (max value) + if grt == 'HEG': + energy_list_2[-1][-1]=2500 # but we don't want it to be a cutoff for ID_Calc in dict + for (FirstLast,ID,cutoff) in zip(final_list,energy_list,energy_list_2): + plt_min=ID[0]-200 + plt_max=ID[1]+100 + if grt=='MEG' and ID[1]>2900: + poly_order=5 + plt_min=ID[0]-200 + print('det =',det) + print('poly_order =',poly_order) + + subkwargs=kwargs # kwargs contains plotType, filepath, prefix and q + newkwargs={'mytitle':mytitle,'countby':countby,"plotMin":plt_min,'plotMax':plt_max} + subkwargs.update(newkwargs) + #print(newkwargs) + #print(FirstLast[0],FirstLast[1]) + #result,energy_data,flux_data=id_calibration_fit(FirstLast[0],FirstLast[1],det,poly_order,subkwargs) + result,energy_data,flux_data=id_calibration_fit(FirstLast[0],FirstLast[1],det,poly_order,mytitle=mytitle,countby=countby,plotMin=plt_min,plotMax=plt_max,plotType=['fit','dif'],filepath=filepath,prefix=prefix) + tmp0.append([cutoff[1],result.tolist()]) + tmp1.append([cutoff[1],energy_data]) + tmp2.append([cutoff[1],flux_data]) + + if 2900<tmp0[-1][0]<3000: tmp0[-1][0]=3000 + id_coef[mode_dict[mode]]=tmp0 # dictionary that + new_id_function[grt]=id_coef # dictionary containing all the calibration curves forthe data set + id_energy[mode_dict[mode]]=tmp1 # dictionary that + energy_dict[grt]=id_energy + id_flux[mode_dict[mode]]=tmp2 # dictionary that + flux_dict[grt]=id_flux + + ##### Read & update old dictionary: + + try: + id_function=read_dict('Dict_IDCal.txt') + print(id_function) + id_function.update(new_id_function) + + except KeyError: + print("Unable to read previous dictionary") + + + if update_file == True and foo == 'yes': + filepath = "/home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/" + filename = 'Dict_IDCal.txt' + + with open(join(filepath, filename), "a+") as f: + f.write('\n======= '+today()+': \n') + f.write(str(id_function)) + f.write('\n') + print('\nWriting dictionary to:',join(filepath, filename)) + + if update_file == True and foo == 'yes': + filepath = "/home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/" + filename = 'Flux_Curves.txt' + + with open(join(filepath, filename), "a+") as f: + f.write('\n======= '+today()+': \n') + f.write('\n----- flux_x:\n') + f.write(str(energy_dict)) + f.write('\n----- flux_y:\n') + f.write(str(flux_dict)) + f.write('\n') + print('\nWriting flux curves to:',join(filepath, filename)) + + return id_function,energy_dict,flux_dict + + +def update_slit_dict(**kwargs): + """ + Interactive function to update the slit position dictionary (Dict_Slit.txt) + + **kwargs + readOnly == False, if true just pring the current slit dictionary + """ + filepath = "/home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/" + filename ='Dict_Slit.txt' + + kwargs.setdefault("readOnly",False) + + print('\n\nWARNING: 2V slit should always be centered at zero ie steer M1 center beam on the grating') + print('Increasing the roll moves the beam more positive: +0.05 pitch => ~ +0.04 slit position; Use:\n') + print("Get_Mirror(1);Move_M1('RZ',x)") + print("energy(500);mono(505);slit(200)") + print("Scan_NarrowSlit_Go(which='2V',slit_parameters=[0.25, -2, 2, 0.25]) #for MEG; slit_parameters=[0.5, -2, 2, 0.25] for HEG") + print("Scan_NarrowSlit_Go(which='2V',slit_parameters=[0.50, -2, 2, 0.25]) #for HEG") + print('Once steering is completed, check 2H position:') + print("Scan_NarrowSlit_Go(which='2H',slit_parameters=[0.25, -6, 6, 0.25]) ") + + try: + slit_position=read_dict(filename) + print('\nCurrent dictionary:\n') + print(slit_position) + + except KeyError: + print("Unable to read previous dictionary") + return + + if kwargs['readOnly']== True: + return slit_position + + else: + grt=input('\nWhich grating do you want to update (HEG or MEG) >') + s2v=input('New Slit 2B-V center >') + s2h=input('New Slit 2B-H center >') + s1v=input('New Slit 1A-V center >') + s1h=input('New Slit 1A-H center >') + + if grt == '' or s1h == '' or s1v == '' or s2h == '' or s2v == '': + print('\nMissing input. No changes made in file.') + return + + if grt[0] == '"' or grt[0]=="'": grt = grt[1:4] + + # new_slit_position={grt.upper():{'S1H':float(s1h),'S1V':float(s1v),'S2H':float(s2h),'S2V':float(s2v)}} # change order to match scan/fit order + new_slit_position={grt.upper():{'S2V':float(s2v),'S2H':float(s2h),'S1V':float(s1v),'S1H':float(s1h)}} + slit_position.update(new_slit_position) + + + with open(join(filepath, filename), "a+") as f: + f.write('\n======= '+today()+': \n') + f.write(str(slit_position)) + f.write('\n') + print('\nWriting dictionary to:',join(filepath, filename)) + + slits_set_BL() + return slit_position + + + + + + +def read_flux(FileName='Flux_Curves.txt',FilePath="/home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/"): + print('test') + with open(join(FilePath, FileName)) as f: + for c,line in enumerate(f.readlines()): + if line[0] == '=': + lastdate=line[8:16] + print(lastdate) + if line[0] == '-' and line[-2] == 'x': + axis='x' + print(axis) + line_x=line + if line[0] == '-' and line[-2] == 'y': + axis='y' + print(axis) + line_y=line + mydict_x=ast.literal_eval(line_x) + mydict_y=ast.literal_eval(line_y) + return mydict_x,mydict_y + + + + + + +############################################################################################### +######################################### Object Oriented ##################################### +############################################################################################### + +class _mdaData(scanDim): + + def __init__(self): + scanDim.__init__(self) + self.poslist = None + self.detlist = None + + + def _getDetIndex(self,d): + """ + d = det Num + """ + D=self.detlist + if D==[]: + print('Data contains no detectors. Try higher dimensions: mydata.dim2[n]') + index=[None] + else: + index=[x for x, y in enumerate(D) if y[1] == 'D'+str(d).zfill(2)] + if index == []: + print('Detector '+str(d)+' not found.') + index=[None] + return index[0] + + + + def plt(self,d): + d=self._getDetIndex(d) + if d== None: + return + x=self.p[0] + y=self.d[d] + if self.dim == 2: + print('This is a 2D scan; use method mydata.img(n,d)') + for i in range(len(y.data)): + plt.plot(x.data[i], y.data[i],label=y.fieldName,marker='+') # crop aborted scans (curr_pt<npts) + else: plt.plot(x.data[:self.curr_pt], y.data[:self.curr_pt],label=y.fieldName,marker='+') # crop aborted scans (curr_pt<npts) + plt.xlabel(x.name) + plt.ylabel(y.name) + plt.legend() + plt.grid(color='lightgray', linestyle='-', linewidth=0.5) + + + + def kappaDet(self,q=None): + if q is not None: + print('\nUse detIndex for plot: mydata.dim1[n].d[detIndex].data\nIf D=mydata.dim1[n].kappaDet => detIndex=D[detNum][1]\n') + print('key = (detIndex, detNum, description, pv)') + det={} + D=self.detlist + for (i,j) in zip([32,33,34,35,31,36,37,38,39,41,42,43,44,45,46,47,48,54,55,56,57],['TEY','D3','D4','MCP','mesh','TEY / mesh','D3 / mesh','D4 / mesh','MCP / mesh','ROI1','ROI2','ROI3','ROI4','ROI5','<H>','<K>','<L>','tth','th','chi','phi']): + d=self._getDetIndex(i) + if d != None: + det[i]=(d,D[d][1],j,D[d][2]) + else: + det[i]=None + return det + + + + + +class _mdaHeader: + def __init__(self): + self.all = None + self.sample = None + self.mono = None + self.ID = None + self.energy = None + self.det = None + self.motor = None + self.mirror = None + self.centroid = None + self.slit=None + self.comment=None + + + + +class mdaFile: + + + '''mydata=mdaFile(first=0,last=None,name=datasetName,filepath=None,prefix=None) + + /net/s29data/export/data_29idb/2020_3/mda/Kappa_0107.mda is a 1-D file; 1 dimensions read in. + + mydata.header[n] = dictionary of 163 scan-environment PVs + + usage: mydata.header[n]['sampleEntry'] -> ('description', 'unit string', 'value', 'EPICS_type', 'count') + + mydata.dim1[n] = 1D data from '29idKappa:scan1' acquired on Oct 20, 2020 19:06:23: + 33/33 pts; 1 positioners, 65 detectors + + usage: mydata.dim1[n].p[2].data -> 1D array of positioner 1 data + + Each scan dimension (i.e., dim1, dim2, ...) has the following fields: + time - date & time at which scan was started: Oct 20, 2020 19:06:23 + name - name of scan record that acquired this dimension: 29idKappa:scan1 + curr_pt - number of data points actually acquired: 33 + npts - number of data points requested: 33 + nd - number of detectors for this scan dimension: 65 + d[] - list of detector-data structures + np - number of positioners for this scan dimension: 1 + p[] - list of positioner-data structures + nt - number of detector triggers for this scan dimension: 1 + t[] - list of trigger-info structures + + Each detector-data structure (e.g., dim[1].d[0]) has the following fields: + desc - description of this detector + data - data list + unit - engineering units associated with this detector + fieldName - scan-record field (e.g., 'D01') + + Each positioner-data structure (e.g., dim[1].p[0]) has the following fields: + desc - description of this positioner + data - data list + step_mode - scan mode (e.g., Linear, Table, On-The-Fly) + unit - engineering units associated with this positioner + fieldName - scan-record field (e.g., 'P1') + name - name of EPICS PV (e.g., 'xxx:m1.VAL') + readback_desc - description of this positioner + readback_unit - engineering units associated with this positioner + readback_name - name of EPICS PV (e.g., 'xxx:m1.VAL') + ''' + + def __init__(self,first=1,last=None,name='mydata',filepath=None,prefix=None,q=False): + if filepath == None: + filepath = BL.mda.filepath() + self.path = filepath + self._name = name + self._first = first + self._last = last + if prefix != None and prefix[-1]=='_': + self._prefix= prefix[:-1] + else: + self._prefix= prefix + + self._allFiles = None + self._allPrefix = None + self.loadedFiles = None + self.scanList = None + self.dim1 = None + self.dim2 = None + self.dim3 = None + self.header = None + + self._extractFiles() + self._extractData(q) + + + #def __str__(self): + + + + def _extractFiles(self): + allFiles = [f for f in listdir(self.path) if isfile(join(self.path, f))] + loadedFiles= [x for (i,x) in enumerate(allFiles) if allFiles[i].split('.')[-1]=='mda'] + allPrefix = [loadedFiles[i][:loadedFiles[i].find('_')] for (i,x) in enumerate(loadedFiles)] + scanList = [int(loadedFiles[i][loadedFiles[i].find('_')+1:loadedFiles[i].find('_')+5]) for (i,x) in enumerate(loadedFiles)] + if self._prefix != None: + allPrefix=[s for s in allPrefix if s == self._prefix] + scanList = [int(loadedFiles[i][loadedFiles[i].find('_')+1:loadedFiles[i].find('_')+5]) for (i,x) in enumerate(loadedFiles) if loadedFiles[i][:loadedFiles[i].find('_')] == self._prefix] + loadedFiles = [s for s in loadedFiles if s[:s.find('_')] == self._prefix] + else: + self._prefix=allPrefix[-1] + if allPrefix[0]!=allPrefix[-1]: + print('\nWARNING: Found more than one file prefix: {}, {}.\n\nData with the same scan number will be overwriten in the order they are loaded. \nPlease specify the one you want to load with arg prefix="which".\n\n'.format(allPrefix[0],allPrefix[-1])) + if self._last == None: + self._last = self._first + shortlist = [i for (i,x) in enumerate(scanList) if self._first<=x<=self._last] + self._allFiles = allFiles + self.loadedFiles = [loadedFiles[i] for i in shortlist] + self.scanList = [scanList[i] for i in shortlist] + self._allPrefix=[allPrefix[i] for i in shortlist] + + + def _extractData(self,q): + + allheader = {} + alldata1 = {} + alldata2 = {} + alldata3 = {} + + for (i,mda) in enumerate(self.loadedFiles): + + ##### File info: + + filename=mda + filepath=self.path + #print(filepath) + num=self.scanList[i] + #print(num) + fullpath=join(filepath,filename) + #print(fullpath) + data=readMDA(fullpath,useNumpy=True) # data = scanDim object of mda module + + ###### Extract header: + + D0 = _mdaHeader() # initiate D0 = mdaHeader object + D1 = _mdaData() + D2 = _mdaData() + D3 = _mdaData() + + D=[] + + for d in range(0,4): + if d in range(0,len(data)): D.append(data[d]) + else: D.append(None) + + # D[0]=data[0] + # D[1]=data[1] + # D[2]=None if 1D data, data[2] if 2D data + # D[3]=None if 2D data, data[3] if 3D data + + + D0.all=D[0] + + + if filename[:5] == 'Kappa': + try: + D0.sample={**{key:value[:3] for key, value in D[0].items() if '29idKappa:m' in key},**{key:value[:3] for key, value in D[0].items() if '29idKappa:Euler' in key},**{key:value[:3] for key, value in D[0].items() if 'LS331' in key}} + D0.mirror = {key:value[:3] for key, value in D[0].items() if '29id_m3r' in key} + D0.centroid={key:value[:3] for key, value in D[0].items() if 'ps6' in key.lower()} + D0.det = {key:value[:3] for key, value in D[0].items() if '29idd:A' in key} + detkeys=['29idMZ0:scaler1.TP','29idKappa:m9.RBV','29idKappa:userCalcOut10.OVAL','29iddMPA:C0O','29idKappa:userStringSeq6.STR1','29idd:Unidig1Bo0'] + for k in detkeys: + if k in D[0]: D0.det[k]=D[0][k][:3] + D0.ID={key:value[:3] for key, value in D[0].items() if 'ID29' in key} + D0.UB={key:value[:3] for key, value in D[0].items() if 'UB' in key} + D0.mono={key:value[:3] for key, value in D[0].items() if 'mono' in key} + D0.energy={key:value[:3] for key, value in D[0].items() if 'energy' in key.lower()} + D0.motor = {key:value[:3] for key, value in D[0].items() if '29idb:m' in key} + D0.slit={key:value[:3] for key, value in D[0].items() if 'slit3d' in key.lower()} + except: + pass + if filename[:5] == 'ARPES': + try: + #D0.sample={**{key:value[:3] for key, value in D[0].items() if '29idKappa:m' in key},**{key:value[:3] for key, value in D[0].items() if '29idKappa:Euler' in key},**{key:value[:3] for key, value in D[0].items() if 'LS331' in key}} + #D0.mirror = {key:value[:3] for key, value in D[0].items() if '29id_m3r' in key} + #D0.centroid={key:value[:3] for key, value in D[0].items() if 'ps6' in key.lower()} + #D0.det = {key:value[:3] for key, value in D[0].items() if '29idd:A' in key} + #detkeys=['29idMZ0:scaler1.TP','29idKappa:m9.RBV','29idKappa:userCalcOut10.OVAL','29iddMPA:C0O','29idKappa:userStringSeq6.STR1','29idd:Unidig1Bo0'] + #for k in detkeys: + # if k in D[0]: D0.det[k]=D[0][k][:3] + D0.ID={key:value[:3] for key, value in D[0].items() if 'ID29' in key} + #D0.UB={key:value[:3] for key, value in D[0].items() if 'UB' in key} + D0.mono={key:value[:3] for key, value in D[0].items() if 'mono' in key} + D0.energy={key:value[:3] for key, value in D[0].items() if 'energy' in key.lower()} + D0.motor = {key:value[:3] for key, value in D[0].items() if '29idb:m' in key} + D0.slit={key:value[:3] for key, value in D[0].items() if 'slit3c' in key.lower()} + except: + pass + + try: + cmt1=D[0]['29id'+self._prefix+':saveData_comment1'][2] + cmt2=D[0]['29id'+self._prefix+':saveData_comment2'][2] + if cmt2 != '': D0.comment = cmt1+' - '+cmt2 + else: D0.comment = cmt1 + except: + D0.comment = '' + + + ###### Extract data: + + DIMS=[D1,D2,D3] + + for counter, value in enumerate(DIMS): + c=counter+1 + if D[c] is not None: + value.rank=D[c].rank + value.dim=D[c].dim + value.npts=D[c].npts + value.curr_pt=D[c].curr_pt + value.plower_scans=D[c].plower_scans + value.name=D[c].name # + value.time=D[c].time + value.np=D[c].np + value.p=D[c].p + value.nd=D[c].nd + value.d=D[c].d + value.nt=D[c].nt + value.t=D[c].t + value.detlist=[(i,D[c].d[i].fieldName,D[c].d[i].name,D[c].d[i].desc) for i in range(0,D[c].nd)] + value.poslist=[(i,D[c].p[i].fieldName,D[c].p[i].name,D[c].p[i].desc) for i in range(0,D[c].np)] + else: + value=None + + allheader[num] = D0 + alldata1[num] = D1 + alldata2[num] = D2 + alldata3[num] = D3 + + d=D.index(None)-1 + if q is False: + print('Loading {} as {}.dim{}[{}]:\n\t\t...{}D data, {}/{} pts; {} positioners, {} detectors'.format( + filename,self._name,d,self.scanList[i],D[d].dim,D[d].curr_pt, D[d].npts, D[d].np, D[d].nd)) + + self.header=allheader + self.dim1=alldata1 + self.dim2=alldata2 + self.dim3=alldata3 + + + + + + def updateFiles(self,first=0,last=inf,name=None,filepath=None,prefix=None): + new=mdaFile(first,last,name,filepath,prefix) + self.loadedFiles=list(dict.fromkeys(self.loadedFiles+new.loadedFiles)) + self._allFiles=list(dict.fromkeys(self._allFiles+new._allFiles)) # merging the 2 list and removing duplicates + self.scanList=list(dict.fromkeys(self.scanList+new.scanList)) + self._allPrefix=list(dict.fromkeys(self._allPrefix+new._allPrefix)) + self.dim1.update(new.dim1) + self.dim2.update(new.dim2) + self.dim3.update(new.dim3) + self.header.update(new.header) + return self + + + + def plt(self,*argv): + if self.dim2[argv[0]].dim == 0: #1D scan + for index,arg in enumerate(argv): + if index %2 !=0: + pass + else: + n=arg + d=argv[index+1] + d=self.dim1[n]._getDetIndex(d) + x=self.dim1[n].p[0] + y=self.dim1[n].d[d] + plt.plot(x.data[:self.dim1[n].curr_pt], y.data[:self.dim1[n].curr_pt],label='mda #'+str(n)+' - '+y.fieldName,marker='+') + plt.xlabel(x.name) + plt.ylabel(y.name+' - ('+y.fieldName+')') + plt.legend() + plt.grid(color='lightgray', linestyle='-', linewidth=0.5) + plt.show() + elif self.dim2[argv[0]].dim == 2: # 2D scan + for index,arg in enumerate(argv): + if index %2 !=0: + pass + else: + n=arg + d=argv[index+1] + d=self.dim2[n]._getDetIndex(d) + if d == None: + return + x=self.dim2[n].p[0] + y=self.dim1[n].p[0] + z=self.dim2[n].d[d] + zlim=self.dim2[n].curr_pt + fig, ax0 = plt.subplots() + img = ax0.imshow(z.data[:zlim],cmap='gnuplot', interpolation = 'nearest', extent = [min(x.data[0]), max(x.data[0]), min(y.data),max(y.data)], aspect = 'auto') + fig.colorbar(img) + plt.title(z.name+' - ('+z.fieldName+')') + ax0.set_xlabel(x.name) + ax0.set_ylabel(y.name) + plt.show() + + + + + + + diff --git a/build/lib/iexcode/macros/__init__.py b/build/lib/iexcode/macros/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/build/lib/iexcode/macros/__init__.py @@ -0,0 +1 @@ + diff --git a/build/lib/iexcode/macros/commissioning.py b/build/lib/iexcode/macros/commissioning.py new file mode 100644 index 0000000..675889f --- /dev/null +++ b/build/lib/iexcode/macros/commissioning.py @@ -0,0 +1,944 @@ +""" +Functions for commissioning, alignment and start of the week + +""" +from os.path import join, isfile, exists, dirname +from time import sleep + +from epics import caget, caput + + +from ..instruments.files_and_folders import check_run, folder_mda +from ..instruments.scanRecord import scanRecord_run,scanRecord_filepath +from ..instruments.xrays import energy +from ..instruments.m3r import M3R_branch + +from ..instruments.IEX_VPU import ID_switch_mode +from ..instruments.diagnostics import diagnostics_all_out, diagnostics_all_in,diodeC,diodeD +from ..instruments.current_amplifiers import current2flux +from ..instruments.slits import set_exit_slit +from ..instruments.logfile import * +from ..instruments.cameras import * + +from ..instruments.ARPES import * +from ..instruments.electron_analyzer import * +from ..instruments.Kappa import * + + +from .ScanFunctions_plot import fit_mda, mda_1D + +############################################################################################################## +################################ setting the mda folder ############################## +############################################################################################################## + +def staff_init(endstation_name=None,set_folders=True,reset=True): + """ + endstation_name : sets the scan record and BL + set_folders: sets the mda and EA folders; default => False + reset: resets the scanRecord (detectors,triggers...) + + **kwargs: + xrays: sets global variable; default => True + BL_mode: 'user' / 'staff' => used for saving, detectors + + Previously: folders_staff + """ + if endstation_name is None: + endstation_name = BL.endstation + + if endstation_name == 'ARPES': + ARPES_init(set_folders,reset,BL_mode='staff') + if endstation_name == 'Kappa': + Kappa_init(set_folders,reset,BL_mode='staff') + + + +def check_staff_directory(BL, **kwargs): + """ + Switchs to the staff directory + + **kwargs for folder_ARPES + + Previously: Check_Staff_Directory + """ + + ioc = BL.ioc() + run = check_run() + + directory = scanRecord_filepath(ioc) + current_run = scanRecord_run(ioc) + + if directory.find('data_29idb') < 1 or current_run != run: + print('You are not currently saving in the Staff directory and/or the desired run.') + # foo=input('\nAre you ready to switch to the '+run+' Staff directory (y or n)? >') + # if foo.lower() == 'y' or foo.lower() == 'yes': + if not (exists(directory)): + print('Staff directory does not exist for this run.') + print('Open ipython as 29id to create a new run directory using:\n\tFolder_'+ioc+'(run,"Staff")') + foo=input('\nPress enter when done >') + if foo == '': pass + print('Switching directory...') + if ioc=='ARPES': + folders_ARPES('Staff',mdaOnly=True,**kwargs) + elif ioc=='Kappa': + folders_Kappa('Staff',create_only=False) + else: + print('\nFolder not set.') + else: + print('Staff directory OK.') + directory = scanRecord_filepath(ioc) + print('\nCurrent directory: '+directory) + + + +############################################################################################################## +################################ checking alignment ############################## +############################################################################################################## + +def check_flux(hv=500,ID_mode='RCP',stay=False): + """ + puts the diode in + measures flux at energy ID_mode specified + stay: diode position after the scan + + Previously: CheckFlux + """ + ID_switch_mode(ID_mode) + energy(hv) + diagnostics_all_out(diode_to_stay_in=m3r_branch()) + SR=round(caget("S:SRcurrentAI.VAL"),2) + if m3r_branch() == "c": + current_slit=caget('29idb:Slit3CFit.A') + diodeC('In') + set_exit_slit(50) + sleep(10) + diode=diodeC_read() + elif m3r_branch() == "d": + current_slit=caget('29idb:Slit4Vsize.VAL') + sleep(10) + diodeD("In") + set_exit_slit(50) + diode=caget('29idb:ca14:read') + flux=current2flux(diode) + print("\n----- Current on diode : %.3e" % diode, "A") + print("----- Corresponding flux: %.3e" % flux, "ph/s \n") + print("----- Storage ring current: %.2f" % SR, "mA") + print("\nFlux at hv=500 as off Feb 2019 for RCP and HEG: ~3.3e-06 A = ~1.5e+11 ph/s") + + if not stay: + diagnostics_all_out() + set_exit_slit(current_slit) + +def check_m0m1(hv=500,stay=True,wire=True,**kwargs): + """ + Prints Flux in C-branch + stay = 'yes' => Leaves diagnostics in the beam + wire ='yes'=> Does wire scans to determine M0M1 alignment + Logging is automatic: use **kwargs or the optional logging arguments see scanlog() for details + + Previously: CheckM0M1 + """ + + switch_branch('c') + switch_gratings('HEG') + print("\nFlux at hv=500 as off Feb 2019: ~3.3e-06 A = ~1.5e+11 ph/s") + branch_shutter_open() + check_flux(hv=hv,stay=stay) + if wire is not None: + scan_wire('H',**kwargs) + scan_wire('V',**kwargs) + +def scan_wire(direction,all_diag=True,**kwargs): + """ + Scans the wires located just downstream of M0/M1, + which = 'H' for the horizontal, typically CA2 + which = 'V' for the vertical, typically CA3 + all_diag =True -> AllDiagIn(), otherwise you have to put any diagnostics in by hand + Logging is automatic: use **kwargs or the optional logging arguments see scanlog() for details + **kwargs: + execute + + Previously: WireScan + """ + kwargs.setdefault('execute',True) + if all_diag: + diagnostics_all_in() + + + if direction == 'H': + name = "H-wire" + + elif direction == 'V': + name = "V-wire" + + diag = diagnostics_dict() + pv = "29idb:m"+str(diag["motor"][name]) + print("\n================== "+name+" scan (29idb:ca2):") + BL.mda.fillin(pv+".VAL",pv+".RBV",1,-17,-30,-0.25,**kwargs) + + if kwargs['execute']: + BL.mda.go(**kwargs) + + if all_diag: + diagnostics_all_out() + + +############################################################################################################## +########################### Beam Profile ###################### +############################################################################################################## +def scan_narrow_slit(slit='2V',slit_parameters=[0.25,-2,2,0.5],**kwargs): + """ + slit = '1V','1H','2V','2H' + slit_parameters = [SlitSize,start,stop,step] + + Typical slit sizes/start/stop/step are (for full range): + 1H/1V : [0.50, -4.5, 4.5, 0.2] + 2H : [0.25, -3.0, 3.0, 0.2] + 2V-MEG: [0.25, -4.0, 4.0, 0.2] + 2V-HEG: [0.50, -8.0, 8.0, 0.2] + + **kwargs: + scan_dim = 1 + execute = True + + Previously: Scan_NarrowSlit + """ + kwargs.setdefault('scan_dim',1) + kwargs.setdefault('execute',True) + + size,start,stop,step = slit_parameters + + direction = slit[1] + if direction == "V": + size = (inf, size) + center = (0, 0) + elif direction == "H": + size = (size, inf) + center = (0, 0) + + if slit[0] == '1': + slit_name = "slit1A" + slit1A_set(size,center) + slit2B_set((inf,inf),(0,0)) + + elif slit[0] == '2': + slit_name = "slit2B" + slit1A_set((3,3),(0,0)) + slit2B_set(size,center) + + slits_scan_center(slit_name,direction,start,stop,step,**kwargs) + if kwargs['execute']: + BL.mda.go(kwargs['scan_dim']) + + +def scan_mono_vs_slit(slit='2V',slit_parameters=[0.25,-2,2,0.5],energy_parameters=[470,530,2],**kwargs): + """ + This can be used to find the center of the resonant beam i.e. the slit value for the most blue shifted curve: + slit='1V','1H','2V','2H' + slit_parameters = [SlitSize,start,stop,step] + energy_parameters = [eVstart,eVstop,eVstep]= [470,530,2] + + Typical slit sizes/start/stop/step are (for full range): + 1H/1V : [0.50, -4.5, 4.5, 0.2] + 2H : [0.25, -3.0, 3.0, 0.2] + 2V-MEG: [0.25, -4.0, 4.0, 0.2] + 2V-HEG: [0.50, -8.0, 8.0, 0.2] + + Previously: Scan_MonoVsSlit + """ + hv_start,hv_stop,hv_step = energy_parameters + # Filling Scans: + mono_scan_fillin(hv_start,hv_stop,hv_step) + scan_narrow_slit(slit='2V',slit_parameters=[0.25,-2,2,0.5],**kwargs) + + # Resetting everybody to normal: + mono_energy_set((hv_start+hv_stop)/2.0) + slits_set_BL() + + +############################################################################################################## +################################ mono alignment ############################## +############################################################################################################## +def mono_MIR_GRT_find_offset(grating,slit_list,**kwargs): + """ + Find MIR-GRT offset by scanning 0 order through exit slit + **kwargs: + detNum = 15 + + Previously: MIR_GRT_Offset + """ + kwargs.setdefault("detNum",15) + + switch_gratings(grating) + exit_slit(50) + diodeC('In') + slit2B_set(2,0.5,0,0) + + #set zero order, scan grating pitch + for ang in RangeUp(1.5,5,0.5): + print("\r") + mono_zero_order(ang) + print("Mirror pitch: "+str(ang)+"\r") + for s in slit_list: + exit_slit(s) + print("\r") + + mono_motor_scan_fillin("GRT:P",ang-0.0005*s, ang+0.0005*s ,0.00002*s) + BL.mda.go(**kwargs) + + sleep(1) + print("\r") + grating_ang = fit_mda(mda.fileNum(),kwargs['detNum'],.1,'gauss') + print("Peak: ",grating_ang) + print("\r") + print("-------------------------") + +##################### mono slit scans => Energy calibration ##################### +def exit_slit_vs_hv(hv,c=3): + ''' + Adjust slit size to keep reasonable count vs hv + c=3 for nominal slit2B size + c=10 for apertured slit2B (e.g 0.25 or 0.5) + + Previously: Mono_Set_Slitvshv + ''' + slit(hv/100.0*c) + + +def scan_energy_along_grating(hv,peakBE=84,pts=11,r=0.75,i=1,**kwargs): + """ + Takes a Slit-2V map for a range of photon energies + peakBE=84; fixed mode + pts = number of pts in slit positions, default: 11 + r = % of range in slit size, default: 0.75 + i = scanEA time in minute (default:1 for MEG, 2 for HEG?) + Takes 2.5h with default parameters (i=1) + + Previously: Mono_Scan_Slit2BV + """ + grt_density = mono_grating_density_get() + c=grt_density/1200 # c = 1 for MEG, 2 for HEG + + # Getting EA parameters vs hv: + PE=200 + + #Getting Slit-2B parameters + Vsize=0.25*c + n=(4.5-hv/500) + Vstart=-((floor((2*n)/Vsize/2)+1)*Vsize*c)*r + Vstep=-round(Vstart*2/pts,2) + + #Setting energy: + energy(hv) + exit_slit_vs_hv(hv,c=10) + + #Aperturing slit 2V (note that we have the nominal size horizontally) + (size_rbv,size_val),(center_rbv,center_val) = slits_pvs('slit2B') + caput(size_val,Vsize) + + print("\n--------------- Starting core level vs Slit-2B(V) map ---------------") + + #Take XPS for each Slit-2V position + for V_center in RangeUp(Vstart,-Vstart,Vstep): + caput(center_val,V_center,wait=True,timeout=18000) + kwargs.update({'comment': "2-V center ="+str(V_center)[0:6]}) + EAlist=["BE",peakBE,PE,17*60*i,1] + scanEA(EAlist,**kwargs) + + #reset the slits + slits_set_BL() + + + +def scan_mono_energy_drift(start,stop,step,peakBE=84,EF=None,**kwargs): + ''' + Measure core level (by default Au 4f) in fixed mode vs energy while maintaining resonable count rate + If EF='y' (by default None) takes the Fermi edge as well. + kwargs: + c=3 is the default slit2B size + + Used for grating calibration (changing grating_offset and b2 effects this - use grating_offset since b2 was measured) + + Previously: Mono_Scan_hv_drift + ''' + kwargs.setdefault('c','3') + + for hv in RangeUp(start,stop,step): + energy(hv) + c=int(kwargs['c']) + exit_slit_vs_hv(hv,c) + kwargs.update({'comment': "Core level @ hv = "+str(hv)}) + scanEA(["BE",peakBE,200,17*60*1,1],**kwargs) + if EF is not None: + kwargs.update({'comment': "EF @ hv = "+str(hv)}) + scanEA(["BE",0,200,17*60*10,1],**kwargs) + + + +############################################################################################################## +########################### Pinhole Scripts ###################### +############################################################################################################## +def scan_pinhole_full(): + """ + Does a pinhole with .1 mm step with AllDiagIn and then with only branch diode in + + Previously: FullPinhole + """ + diagnostics_all_in() + scan_pinhole([-3, 3, .5, .1], [-3, 3, .1, .5]) + diagnostics_all_out(diode_stay_in=True) + scan_pinhole([-3, 3, .5, .1], [-3, 3, .1, .5]) + + +def scan_pinhole(H_list,V_list,**kwargs): + """ + Sets up the pinhole scan for slit1A + Make sure that you have the appropriate Diagnostics in can use AllDiagIn() + + H_list = [Hstart, Hstop, Hstep, Hsize] => [-3, 3, .1, .5] + V_list = [Vstart, Vstop, Vstep, Vsize] => [-3, 3, .1, .5] + + *kwargs: + H_scan_dim => 1 + V_scan_dim => 2 + execute => True + + Previously: Scan_Pinhole_Go, Scan_Pinhole + """ + kwargs.setdefault('H_scan_dim',1) + kwargs.setdefault('V_scan_dim',2) + kwargs.setdefault('execute',True) + + #Synching slits + slits_synch("slit1A") + + #Getting initial slit size + (Hsize0,Vsize0),(Hcenter0,Vcenter0) = slit1A_get(verbose=False) + + #Filling in the ScanRecord + slits_scan_center("slit1A","H",H_list[0],H_list[1],H_list[2],scan_dim=kwargs['H_scan_dim'],execute=False) + slits_scan_center("slit1A","V",V_list[0],V_list[1],V_list[2],scan_dim=kwargs['V_scan_dim'],execute=False) + + #Set the slit size + slit1A_set((H_list[2],V_list[2]),(0,0),verbose=True) + + if kwargs['execute']: + print("\rPinhole scan: ("+str(kwargs['H_scan_dim'])+","+str(kwargs['V_scan_dim'])+") " + +str(H_list[0])+"/"+str(H_list[1])+"/"+str(H_list[2])+"/"+str(H_list[3])+"/" + +str(V_list[0])+"/"+str(V_list[1])+"/"+str(V_list[2])+"/"+str(V_list[3])) + + BL.mda.go(max(kwargs['H_scan_dim'],kwargs['V_scan_dim'])) + print("\rNow setting the slits back to:") + slits_set_BL() + + print("\r") + print_warning_message("Don't forget to pull all of the diagnostics out.") + + + +############################################################################################################## +################## Slit Calibration & Beam Steering scripts ################## +############################################################################################################## + + +def Notes_from_20191212(): + print(""" looking at diode 3 in scattering chamber, open 3D all the way (2000) + WARNING: 10% energy detune is enough to see the 2 lobes with 2B but not for 1A => 20% + Red shifted (485eV) = edges give center of the grating + Blue shifted (510eV) = gaussian center gives center of the beam + => we want the slit centered on the grating center and the beam centered on the slits/grating + => 5 urad down = 1 mm negative motion on 2B-V with HEG""") + + +def procedure_alignment_M1(): + print("""\n-Start with a very red shifted line cut to localize the edges of the grating: + Set_IDraw(510) + mono(485) + Scan_NarrowSlit('2V',[0.25,-8,8,0.5]) + Scan_NarrowSlit('2H',[0.25,-4,4,0.5]) + => Verticaly: The edges/center of the grating define the center of 2V + => Horizontaly: The edges are defined by the aperture upstream. + As long as the beam makes it through the pipe to the next mirror, we are good. + Each M1 pitch will have its own slit-2H zero + It can be a good idea to check that GRT or M2 translation does not change anything. +\n-Once the slits are centered on the grating, center resonant beam on the grating (zero slit): + Get_Mirror(1) # to get current values + for roll in RangeUp(3,4,0.2): + print('\\n') + Move_M1('RZ',roll) + sleep(5) + scan_mono_vs_slit('2V',[0.25,-0.5,0.5,0.25],[490,530,2]) + for pitch in RangeUp(8.5,8.7,0.05): + print('\\nMove_M1('RY',pitch)') + Move_M1('RY',pitch) + sleep(5) + scan_mono_vs_slit('2H',[0.25,-0.5,0.5,0.25],[490,530,2]) + => The resonant beam is the slit value for the most blue shifted curve. + + WARNING: It is more accurate (and easier) to compare the leading edge of the ID peak + than trying to look at the perpendicular direction (slit line cut) because of the + asymetry in the beam intensity (uneven carbon strip along the grating?) +""") + + + # 2017_3 - HEG after opitmization: mba_b_0241-0244 + # 2018_1 - HEG before opitmization: mba_b_0005-0008 + + +def beamsteering_2B(v_position): + vroll=round(v_position*0.05/0.04,3) + if v_position<0: v_direction=' +' + else: v_direction=' -' + v_steering=v_direction+str(vroll)+' urad' + return v_steering + +def beamsteering_1A(h_position,v_position): + hrad=round(h_position*10/0.25,0) + vrad=round(v_position*10/0.25,0) + if h_position<0: h_direction=' urad outboard' + else: h_direction=' urad inboard' + if v_position<0: v_direction=' urad up' + else: v_direction=' urad down' + h_steering=str(hrad)+h_direction + v_steering=str(vrad)+v_direction + return h_steering,v_steering + + + + + +##################################################################################### +### Slit-1A Procedures +##################################################################################### + +def check_slit1A(step=0.1): + """ + Checks that Slit1A is centered on the fixed aperature/theoretical optic axis + top='m9' -> CA5 + inboard='m10' -> CA4 + + Scans the top blade - CA4 sees an step function + top_center = midpoint of motor position for zero current and when current saturates + Scans the inner blade - CA4 sees box fuction convoluted with gaussian beam position + inner_center - midpoint bewteen box edges + + SetSlit1A(0,0,inner_center,top_center) + for m in range(9,13): + Reset_Motor_User(m,'b',0) + SyncAllSlits() + + If data makes no sense then Reset_Slit1A_Procedure() + + """ + #scanning top-blade + slit1A_set(8,8,0,0) # aperture wide open + caput('29idb:m10.VAL',0) # Inboard blade centered-ish in the beam + m=9 + VAL='29idb:m'+str(m)+'.VAL' + RBV='29idb:m'+str(m)+'.RBV' + BL.mda.fillin(VAL,RBV,-4,4.0,step) + BL.mda.go() + FileNum1 = BL.mda.lastFileNum() + + #scanning inboard-blade + slit1A_set(8,8,0,0) # aperture wide open + caput('29idb:m9.VAL',0) # top blade centered-ish in the beam + m=10 + VAL='29idb:m'+str(m)+'.VAL' + RBV='29idb:m'+str(m)+'.RBV' + BL.mda.fillin(VAL,RBV,-4,4.0,step) + BL.mda.go() + FileNum2 = BL.mda.lastFileNum() + + return FileNum1, FileNum2 + + +def procedure_reset_slit1A(): + """ + Prints the procedure for Resetting Slit1A + """ + print("\n#------- Checking and Resetting the Dial -------") + print(" GoToSlit1AScribe()") + print("# if not on the scribe marks tweek the position until the scribe marks are aligned") + print(" for m in [9,10,11,12]: Reset_Motor_Dial(m,\'b\',0);SyncAllSlits()") + print("\n#------- Resetting the User -------") + print("# SetSlit1A by hand such that you have some beam coming through") + print(" Scan_SlitSize(\"1H\",start,stop,step) #looking at CA4") + print(" Scan_SlitSize(\"1V\",start,stop,step) #looking at CA4") + print("# Then SetSlit1A(1Hsize,1Vsize,0,0); where 1Hsize and 1Vsize are the size where CA4 = 0") + print(" for m in [9,10,11,12]: Reset_Motor_User(m,'b',0);SyncAllSlits()") + + +def check_ID_steering(hv=2000): + """ + Scans Slit1A center (set to a (0.25,0.25) pinhole) while looking at the back blade: + - slit center vs fixed aperture: given by the position of the edges + - beam center vs fixed aperture: given by the position of the bump in the middle + """ + slit1A_set(0.25,0.25,0,0) + ID_SP_set(hv) + scan_slit_center('1H',-3,3,0.1) + scan_slit_center('1V',-3,3,0.1) + +def scan_slit_center(slit,start,stop,step,size=0.25): + """ + slit = '1H','1V','2H' or '2V' + + Previously: Scan_SlitCenter + """ + if slit[0] == '1': + slit_name = "slit1A" + elif slit[0] == '2': + slit_name = "slit2B" + direction = slit[1] + slits_scan_center(slit_name,direction,start,stop,step,size=size) + + +def scan_slit_size(slit,start,stop,step,center=(0,0)): + """ + slit = '1H','1V','2H' or '2V' + + Previously: Scan_SlitSize + """ + if slit[0] == '1': + slit_name = "slit1A" + elif slit[0] == '2': + slit_name = "slit2B" + direction = slit[1] + slits_scan_center(slit_name,direction,start,stop,step,center=center) + + + +##################################################################################### +# Checking the beam steering uses the gas-cell to see if the beam is centered on the grating +# and that the slits are centered +##################################################################################### + +def ID_beam_profile(grt,slit_list,c_slit=1,c_energy=1,**kwargs): + """ + Makes a nice 2D image of the energy distribution of the beam across the grating at ID=500 + Does NOT put the diagnostics into the beam you need to run the following if you haven't already (AllDiagOut(); DiodeCIn()) + SlitList=["2H","2V","1H","1V"] + c_slit = scaling of step size (c=1 Slit-1: step = 0.25. Slit-2: step = 0.5) + c_energy = scaling of mono step size ( c=1 eV step = 2) + with c_slit=1 and c_energy = 1 Each slit ~ 1:10 min + + Previously + """ + switch_gratings(grt) + grt_density = mono_grating_density_get() + c=grt_density/1200 + ID=500 + eVstart,eVstop,eVstep=460,540,4 + for slit in slit_list: + if slit=="1H": + #Hsize,Vsize,Hstart,Hstop,Hstep = 0.50,2,-2,2,0.25*c_slit # => 35 min + #MonoVsSlit1AH_Go(ID,eVstart,eVstop,eVstep*c_energy,Hstart,Hstop,Hstep,Hsize,Vsize,scanIOC,**kwargs) + scan_mono_vs_slit('1H',[0.5,-2,2,0.25*c_slit],[eVstart,eVstop,eVstep],comment='Mono/Slit - 1H') + elif slit == "1V": + #Hsize,Vsize,Vstart,Vstop,Vstep = 2,0.50,-2,2,0.25*c_slit + #MonoVsSlit1AV_Go(ID,eVstart,eVstop,eVstep*c_energy,Vstart,Vstop,Vstep,Hsize,Vsize,scanIOC,**kwargs) + scan_mono_vs_slit('1V',[0.5,-2,2,0.25*c_slit],[eVstart,eVstop,eVstep],comment='Mono/Slit - 1V') + elif slit =="2H": + #Hsize,Vsize,Hstart,Hstop,Hstep = 0.50,8,-3,3,0.5*c_slit + #MonoVsSlit2BH_Go(ID,eVstart,eVstop,eVstep*c_energy,Hstart,Hstop,Hstep,Hsize,Vsize,scanIOC,**kwargs) + scan_mono_vs_slit('2H',[0.5,-3,3,0.5*c_slit],[eVstart,eVstop,eVstep],comment='Mono/Slit - 2H') + elif slit =="2V": + #Hsize,Vsize,Vstart,Vstop,Vstep = 6,0.25*c,-4*c,4*c,0.5*c_slit + #MonoVsSlit2BV_Go(ID,eVstart,eVstop,eVstep*c_energy,Vstart,Vstop,Vstep,Hsize,Vsize,scanIOC,**kwargs) + scan_mono_vs_slit('2V',[0.5,-4,4,0.5*c_slit],[eVstart,eVstop,eVstep],comment='Mono/Slit - 2V') + + +def CheckAllSlits_long(hv_list=[500],slit_list=["1H","1V","2H","2V"],**kwargs): + """ + For each photon energy in hvList, takes 3 slit curves @ mono below / resonance / above + For given SlitList + For both gratings + """ + for grt in ["HEG","MEG"]: # One full loop for a given grating and 3 energies takes 5h for average 20 + switch_gratings(grt) + for hv in hv_list: + slits_set_BL(hv) + step=hv/100.0 + start=hv-step + stop=hv+step + for slit in slit_list: + for hv in range(start,stop,step): + print("\r") + mono_energy_set(hv) + check_ID_beam_steering(slit) + + +def CheckSlitCalibration(slit_list,BL=500,hvList=[485,510],scanIOC=None,**kwargs): + """ + Slit scan for red shifted and blue shifted mono values + used to determine in the beam is centered on the grating (gaussian) + and if the slits are centered on the grating (humped) + hv=500; hvList=[485,510] + hv=1500;hvList=[hv*0.97,hv*1.01] + Note: sets the exit slit to 50 + """ + if scanIOC is None: + scanIOC=BL_ioc() + exit_slit(50) + slits_set_BL(BL) + for hv in hvList: + mono_energy_set(hv) + for slit in slit_list: + check_ID_beam_steering(slit) + print("\n") + +def check_ID_beam_steering(slit): #here + """ + checks the ID beam steering by scanning the slit with the ID detuned + if there is carbon on the mirror you might need to do the full ID_beam_profile to + find the red shifted beam + + slit = '1V','1H','2V','2H' + + Previously: CheckBeamPosition + """ + + grt_density = mono_grating_density_get() # can be plotted directly on dview using the buffer. That should + c=grt_density/1200 # be fairly quick and not require any data loading/analysis + + slit1A_set((3.5,3.5),(0,0),verbose=False) + slit2B_set((6.0,8.0),(0,0),verbose=False) # Open up all the slits to make sure we scan the full beam + + if slit == "1H": + size,start,stop,step = 0.50,-4.5,4.5,0.2 + elif slit == "1V": + size,start,stop,step = 0.50,-4.5,4.5,0.2 + elif slit == "2H": + size,start,stop,step = 0.25,-8.0,8.0,0.2 + elif slit == "2V": + size,start,stop,step = 0.25*c,-4*c,4*c,0.2*c + + scan_slit_center(slit,start,stop,step,size=size) + slits_set_BL() + + +############################################################################################################## +################## Commissioning paper curves ################## +############################################################################################################## +def FermiEdges(Energy,Slit): + EF_Table={} + EF_Table[500] = {10:20, 20:20, 50:20, 100:20, 200:20} + EF_Table[1000] = {10:50, 20:50, 50:50, 100:50, 200:50} + EF_Table[1500] = {10:100, 20:100, 50:100, 100:100, 200:100} + PE = EF_Table[Energy][Slit] + return PE + + + + +def QP_curves(ID_energy,ID_mode_list,hv_start,hv_stop,hv_step): + """ + """ + exit_slit(200) + #Switch_Grating("MEG") + #print "\r" + #print "**************** QP OFF ****************" + #for mode in list_mode: + # Switch_IDMode(mode) + # SetID_Raw(hv) + # SetSlit_BL() + # SetMono(250) + # Scan_Mono(1,start,stop,step) + # Scan_Go("b",1) + #print "\r" + #print "**************** QP ON ****************" + #Switch_IDQP("on") + for ID_mode in ID_mode_list: + polarization(ID_mode) + ID_SP_set(ID_energy) + slits_set_BL()() + mono_energy_set(250) + mono_scan_fillin(hv_start,hv_stop,hv_step) + BL.mda.go() + #Switch_IDQP("off") + #Switch_Grating("HEG") + #Switch_IDMode("RCP") + + +############################################################################################################## +################################ ID calibration scripts ############################## +############################################################################################################## + +###### Energy range follows ID ######## + +def ID_calibration_scan(ID_start,ID_stop,ID_step,bandwidth=10,QP=None,Harm=1,scanIOC=None): + """ + Sets the ID_SP and scans the mono: + start,stop,step are for ID + if QP is not None adjusts the mono range to offset accordingly + !!! DOES NOT CHANGE QP VALUE !!!! + !!! Doesn't put diodes in !!! + """ + + print(""" + !!! DOES NOT CHANGE QP VALUE !!!! + !!! Doesn't put diodes in !!!""") + + logfile_name_set(BL.endstation,filename_suffix='IDCalibration') + + #Getting mono max range based on grating + grt_density = mono_grating_density_get() + c=grt_density/1200 # c = 1 for MEG, 2 for HEG + if c == 1: + maxhv = 3000 + elif c == 2: + maxhv = 2000 + + log_print(comment="====== Starting Mono Scan vs ID ======") + + for ID_energy in RangeUp(ID_start,ID_stop,ID_step): + print("\n------------------ ID-SP @ "+str(ID_energy)+" ------------------") + mono_energy_set(ID_energy) + ID_SP_set(ID_energy) + slits_set_BL() + branch = m3r_branch() + if branch == 'd': + m3r_align() + if QP is None: + start_hv=min(ID_energy-ID_energy/(bandwidth*1.0)*Harm,maxhv) # FR update range 11/20/2019 + stop_hv=min(ID_energy+ID_energy/(bandwidth*2.0)*Harm,maxhv) + step_hv=round(ID_energy/300.0,1) +# start_hv=min(ID_energy-ID_energy/(bandwidth*2.0)*Harm,maxhv) # shift the window by doing -BW*2/+BW*1 instead of -BW*1'/'+BW*2 +# stop_hv=min(ID_energy+ID_energy/(bandwidth*1.0)*Harm,maxhv) +# step_hv=round(ID_energy/500.0,1) + else: + start_hv=min(ID_energy-ID_energy/(bandwidth*2.5)*Harm,maxhv) + stop_hv=min(ID_energy+ID_energy/(bandwidth*0.7)*Harm,maxhv) + step_hv=round(ID_energy/300.0,1) + + mono_scan_fillin(start_hv,stop_hv,step_hv) + BL.mda.go() + sleep(1) + + FileNum = BL.mda.lastFileNum + if ID_energy == ID_start: + FirstFileNum = FileNum # Record First Scan Number + + LastFileNum = FileNum # Record Last Scan Number + + print("Use: update_id_dict("+str(FirstFileNum)+","+str(LastFileNum)+"ignore=[] ,update_file,path=None,prefix='ARPES',q=True)") + + + + +def ID_calibration_mode(ID_mode,start=None,stop=None,step=25,Harm=1): + ''' + Runs full calibration with a 25 eV step (by default) for a given mode: + which = 'RCP','LCP', 'V','H' + Better to run each mode in a separated cell (print statement at the end helps with calibration) + ''' + branch = m3r_branch() + diagnostics_all_out(diode_stay_in=True) + + polarization(ID_mode) + + grt = mono_grating_get() + + QP_ratio = ID_QP_ratio_get() + + hv_min,hv_max = energy_range_min_max() + if start == None: + start = hv_min + if stop == None: + stop=hv_max-50 + + if QP_ratio <100: + stop=min(stop,1500) + QP_ratio=1 + else: + QP_ratio=None + + ID_calibration_scan(start,stop,step,QP=QP_ratio,Harm=Harm) + + +############################################################################################################## +############################## Beam Motion ############################## +############################################################################################################## +def scan_cam_motor(motor_name,start,stop,step,cam_num,ExposureTime): + """ + motor_name = 'x','y','z'... + cam_num = 1,2,3... uses cam_pv to get the pv + + Previously: Pixel_Calibration + """ + cam_scan_setup(cam_num,ADtype='TIFF') + + if BL.branch == 'ARPES': + ARPES_Motors.scan(motor_name,start,stop,step) + if BL.branch == 'Kappa': + Kappa_Motors.scan(motor_name,start,stop,step) + + cam_live() + + + +def scan_cam_hv(start,stop,step,cam_num,**kwargs): + """ + takes and image as a function of photon energy + **kwargs: + ExposureTime + Previously: BeamMotion_Vs_BL_Go + """ + cam_scan_setup(cam_num,ADtype='TIFF') + scanhv(start,stop,step,**kwargs) + + cam_live() + + + +def scan_cam_mono_zero_order(start,stop,step,cam_num,iterations=1,**kwargs): + """ + Previously: BeamMotion_Vs_MonoZero_Go + """ + i = 0 + while i < iterations: + for angle in range(start,stop+step,step): + mono_zero_order(angle) + sleep(5) + cam_snap(cam_num,ADtype='TIFF',**kwargs) + i+=1 + + cam_live() + +def scan_cam_mono_pitch(motor,angle,delta,cam_num,iterations=1,**kwargs): + """ + takes image at zero order (alpha = beta = angle) + takes image at zeror order with delta + + motor = 'grating' or 'mirror + if 'grating': + alpha = angle + beta = angle + delta + if 'mirror': + alpha = angle + delta + beta = angle + + Previously:BeamMotion_Vs_MirPitch_Go + """ + if motor == 'grating': + alpha = angle + beta = angle + delta + if motor == 'mirror': + alpha = angle + delta + beta = angle + i=0 + while i<iterations: + mono_angles_set(angle,angle) + sleep(5) + cam_snap(cam_num,ADtype='TIFF',**kwargs) + + mono_angles_set(alpha,beta) + sleep(5) + cam_snap(cam_num,ADtype='TIFF',**kwargs) + i+=1 + + cam_live() + + + diff --git a/build/lib/iexcode/macros/start_of_the_week.py b/build/lib/iexcode/macros/start_of_the_week.py new file mode 100644 index 0000000..ce69923 --- /dev/null +++ b/build/lib/iexcode/macros/start_of_the_week.py @@ -0,0 +1,303 @@ +import matplotlib.pyplot as plt + +from ..instruments.diagnostics import diodeC +from ..instruments.files_and_folders import check_run +from ..instruments.utilities import wait_for_it +from ..instruments.current_amplifiers import Keithley +from ..instruments.IEX_VPU import * +from ..instruments.VLS_PGM import * +from ..instruments.xrays import * +from ..instruments.shutters import * +from ..instruments.slits import * + +from ..instruments.Kappa import * +from ..instruments.ARPES import * + +from .commissioning import * +from .ScanFunctions_plot import * + +def start_of_the_week(grt,branch,wait=False,**kwargs): + """ + This should be run every at the start of every week; 50 min total. + Switch to C-branch,. + If wait=True, wait for next day a 8:05. + + kwargs defaults: + run = None: used today's date to set run appropriatetly + repeat = False: sets everything: switches branch, sets BL_Mode to Staff, resets scan/CAs/mono + scanType = ['slit1','wire','flux','monoVslit'] + sound = False; no sounds + = True => plays sound after Slit1A scans and when script is complete + } + ID steering: (slit1A) + steering out => move beam more positive (10 urad ~ 0.25 mm) + steering up => move beam more positive (10 urad ~ 0.25 mm) + + M1 steering: (monoVslit-2V and 2H) + Roll: Rz more positive => 2V more positive (0.25 => .25 mm) + Pitch: Ry more positive => 2H more positive (0.03 => .5 mm + + Previously: StartOfTheWeek + """ + kwargs.setdefault('repeat',False) + kwargs.setdefault('run',check_run()) + kwargs.setdefault('quick',True) + kwargs.setdefault('interactive',True) + kwargs.setdefault('scanType',['slit1','wire','flux','monoVslit']) + kwargs.setdefault('sound',True) + kwargs.setdefault('extended_range',False) + + for scan in kwargs['scanType']: + if scan not in ['slit1','wire','flux','monoVslit']: + print(scan+" is not a valid scan scanType=['slit1','wire','flux','monoVslit']") + return + + + ### checking diode settings: + if branch == 'c': + diode = Keithley('b',15) + diode.autoscale() + diodeC('In') + else: + foo=input('Do you have a diode in direct beam (y or n)? >') + if foo.lower() == 'y' or foo.lower() == 'yes': + print('Resuming...') + + + ### checking QP ratio: + QP_ratio = ID_QP_ratio_get() + if QP_ratio != 100: + foo=input('QP on!!! Continue (y or n)? >') + if foo.lower() == 'y' or foo.lower() == 'yes': + print('Resuming...') + + ### Stuff that doesn't need beam & does not need to be run if repeating SoTW: + print("\n\n================== Sets Beamline & Scans:") + switch_gratings(grt) + + if not kwargs['repeat']: + switch_branch(branch) + + print("\n\n================== Sets Directory:") + + if branch=='c': + ARPES_init(BL_mode='staff') + + fname='StartOfTheWeek_log.txt' + logfile_name_set(BL.endstation,filename=fname) + + + ### checking branch shutter: + if branch_shutter_status() == False: + branch_shutter_open() + + + ### Wait for next 8AM: + if wait: + t = datetime.today() + if 0 <= t.hour <= 8: + wait_for_it(0,8,5) + else: + wait_for_it(1,8,5) + + ### checking ID: + print("\n\n================== Start ID:") + polarization('RCP') + + + ### Ready to start: + FirstScan=BL.mda.fileNum+1 + + + if not kwargs['repeat']: + log_print("\n\n================== Start Of The Week @ "+today('slash')+':\n') + + + def interactive_fct(comment='scans'): + foo=input('\nRepeat '+comment+' (y or n)? >') + if foo.lower() == 'y' or foo.lower() == 'yes': + print('\nRepeating...\n');flag=True + + else: + print('\nResuming...\n'); flag=False + return flag + + DetDict={'c':(9,7,8,15),'d':(9,7,8,14)} + detCA4,detH,detV,detDiode=DetDict[branch] + + ###### Scan front-end slit center: + if 'slit1' in kwargs['scanType']: + print("\n\n================== Slit 1A scans:") + ID_SP_set(2000) + flag=True + while flag: + + #Scan_SlitCenter('1H',-3,3,0.1,comment='Slit center - 1H') + #Scan_SlitCenter('1V',-3,3,0.1,comment='Slit center - 1V') + scan_narrow_slit('1H',[0.25,-3,3,0.1],comment='Slit center - 1H') + scan_narrow_slit('1V',[0.25,-3,3,0.1],comment='Slit center - 1V') + #SetSlit1A(4.5,4.5,0,0) + slit1A_set((4.5,4.5),(0,0)) + m=BL.mda.lastFileNum() + print('\nsteering out => move beam more positive (10 urad ~ 0.25 mm)') + print('steering up => move beam more positive (10 urad ~ 0.25 mm)') + try: + h_position=fit_mda(m-1,detCA4,1,'gauss',xrange=[-1,1]);plt.show() + v_position=fit_mda(m ,detCA4,1,'gauss',xrange=[-1,1]);plt.show() + print('\nBeam position:') + print(' - horizontal: '+str(h_position)) + print(' - vertical: '+str(v_position)) + h_steering,v_steering=beamsteering_1A(h_position,v_position) + print('\nBeam steering:') + print(' - horizontal: '+h_steering) + print(' - vertical: '+v_steering) + print('\n') + except: + print('Unable to fit position; try to tweak xrange or FWHM:') + print('fit_mda('+str(m)+','+str(detCA4)+',1,"gauss",xrange=[-1,1])') + print('fit_mda('+str(m+1)+','+str(detCA4)+',1,"gauss",xrange=[-1,1])') + print('\n') + if kwargs['sound']:playsound() + if kwargs['interactive']: flag=interactive_fct() + else: break + + ###### Wire scans: + if 'wire' in kwargs['scanType']: + ID_SP_set(2000) + slit1A_set(4.5,4.5,0,0) + scan_wire('H',comment='Wire scan - H',all_diag=False) # by default puts all meshes in + DiodeC + scan_wire('V',comment='Wire scan - V',all_diag=False) + m=BL.mda.lastFileNum() + try: + plot_mda(m-1,detH,m,detV,title='wire-H (blue) & wire-V (orange)');plt.show() + except: + print('Unable to plot.') + if kwargs['sound']:playsound() + + + ###### Mono/slit scans: + list_position=[] + if 'monoVslit' in kwargs['scanType']: + if grt=="HEG": + slit(300) + c=2 + else: + slit(200) + c=1 + print("\n\n================== Mono/slit scans:") + if 'quick' in kwargs: + energy(500) + mono_energy_set(505) + print('\n---------- Scanning slit 2B:\n') + flag=True + while flag: + scan_narrow_slit('2V',[0.25*c, -8, 8, 0.25]) + sleep(1) + scan_narrow_slit('2H',[0.25, -6, 6, 0.25]) + sleep(1) + m=BL.mda.lastFileNum() + try: + V2=fit_mda(m-1,detDiode,1*c,'gauss') + plt.show() + H2=fit_mda(m ,detDiode,1,'gauss') + plt.show() + list_position.append(V2) + list_position.append(H2) + #print(f"V2 = {round(V2,3)}") + #print(f"H2 = {round(H2,3)}") + + print('\nBeam position:') + print(f" - vertical: V1 = {round(V2,3)}") + print(f" - horizontal: H1 = {round(H2,3)}") + v_steering=beamsteering_2B(V2) + print('\nM1 roll (RZ) steering:') + print(' - vertical: ~ '+v_steering) + print(' - horizontal: reset slit 2BH center to ',str(H2)) + print('\n') + + print('\n\nWARNING: 2V slit should always be centered at zero') + print('Increasing M1 roll moves the beam more positive on 2V:') + print('\t+0.05 pitch => ~ +0.04 slit position') + print('To steer M1, use:') + print("\tGet_Mirror(1);Move_M1('RZ',x)") + except: + print('Unable to fit position.') + if kwargs['sound']:playsound() + if kwargs['interactive']: flag=interactive_fct('slit 2B') + else: break + print('\n---------- Scanning slit 1A:\n') + flag=True + while flag: + scan_narrow_slit('1V',[0.25, -4, 4, 0.1]) + sleep(1) + scan_narrow_slit('1H',[0.25, -3, 3, 0.1]) + sleep(1) + slits_set_BL() + m=BL.mda.lastFileNum() + try: + V1=fit_mda(m-1,detDiode,1,'gauss');plt.show() + H1=fit_mda(m ,detDiode,1,'gauss');plt.show() + list_position.append(V1) + list_position.append(H1) + print('\nBeam position:') + print(f" - vertical: V1 = {round(V1,3)}") + print(f" - horizontal: H1 = {round(H1,3)}") + except: + print('Unable to fit position.') + if kwargs['sound']:playsound() + if kwargs['interactive']: flag=interactive_fct('slit 1A') + else: break + print('\nBeam center fit @ [2V,2H,1V,1H]:',list_position) + print('\nTo update slit center using: update_slit_dict()\n') + else: # longer full slit scans + ID_SP_set(500) + flag=True + while flag: + mono_energy_set(500) + slits_set_BL() + n=2 if 'extended_range' in kwargs else 1 + scan_mono_vs_slit('2V',[0.25*c,-2*c*n,2*c*n,0.5],[475,515,2],comment='Mono/Slit - 2V') #10/20 min for MEG/HEG + scan_mono_vs_slit('2H',[0.25,-2*n,2*n,0.5],[475,515,2],comment='Mono/Slit - 2H') #10min + scan_mono_vs_slit('1V',[0.5,-0.75*n,0.75*n,0.25*c],[475,515,2],comment='Mono/Slit - 1V') #10min + scan_mono_vs_slit('1H',[0.5,-0.75*n,0.75*n,0.25],[475,515,2],comment='Mono/Slit - 1H') #10min + #interactive + if kwargs['interactive']: flag=interactive_fct() + else: break + + ###### Check flux: + if 'flux' in kwargs['scanType']: + if m3r_branch == 'c': + diodeC('In') + print("\n\n================== Check Flux:") + flag=True + while flag: + check_flux(stay=True) + exit_slit(50) + energy(500) + scanhv(475,525,1,comment='Mono Scan @ 500eV') + try: + m=BL.mda.lastFileNum() + plot_mda(m,detDiode,flux=3);plt.show() + except: + print('Unable to fit position.') + ### interactive + if kwargs['interactive']: flag=interactive_fct() + else: break + + diagnostics_all_out() + + print("\n\n================== All done:") + print("\nDon't forget to put back the user folder !!!!") + + ###### Plotting instructions and return first scan number + if FirstScan is not None: + log_print(comment="\nFirstScan = "+str(FirstScan)) + print('StartOfTheWeek_plot("'+branch+'",'+str(FirstScan)+')' ) + print('Beam center fit @ [2V,2H,1V,1H]:',list_position) + print('\nREMEMBER to update slit center using: update_slit_dict()') + + if kwargs['sound']: + playsound() + + return list_position + diff --git a/iexcode.egg-info/PKG-INFO b/iexcode.egg-info/PKG-INFO new file mode 100644 index 0000000..c7569a8 --- /dev/null +++ b/iexcode.egg-info/PKG-INFO @@ -0,0 +1,12 @@ +Metadata-Version: 2.1 +Name: iexcode +Version: 0.0.1 +Summary: python scripts to run 29id of the APS +Home-page: https://github.com/xxx +Maintainer: Jessica McChesney +Maintainer-email: jmcchesn@anl.gov +License: UNKNOWN +Platform: UNKNOWN + +UNKNOWN + diff --git a/iexcode.egg-info/SOURCES.txt b/iexcode.egg-info/SOURCES.txt new file mode 100644 index 0000000..e22a33a --- /dev/null +++ b/iexcode.egg-info/SOURCES.txt @@ -0,0 +1,57 @@ +README.md +setup.py +iexcode/__init__.py +iexcode.egg-info/PKG-INFO +iexcode.egg-info/SOURCES.txt +iexcode.egg-info/dependency_links.txt +iexcode.egg-info/requires.txt +iexcode.egg-info/top_level.txt +iexcode/instruments/AD_utilities.py +iexcode/instruments/ARPES.py +iexcode/instruments/FMB_mirrors.py +iexcode/instruments/IEX_VPU.py +iexcode/instruments/IEX_endstations.py +iexcode/instruments/Kappa.py +iexcode/instruments/Kappa_Euler.py +iexcode/instruments/Kappa_det.py +iexcode/instruments/Lakeshore_335.py +iexcode/instruments/Motors.py +iexcode/instruments/Scienta.py +iexcode/instruments/VLS_PGM.py +iexcode/instruments/__init__.py +iexcode/instruments/bakeout.py +iexcode/instruments/beamline.py +iexcode/instruments/cameras.py +iexcode/instruments/conversions_constants.py +iexcode/instruments/current_amplifiers.py +iexcode/instruments/diagnostics.py +iexcode/instruments/electron_analyzer.py +iexcode/instruments/encoders.py +iexcode/instruments/files_and_folders.py +iexcode/instruments/gate_valves.py +iexcode/instruments/hxp_mirrors.py +iexcode/instruments/logfile.py +iexcode/instruments/m3r.py +iexcode/instruments/mpa.py +iexcode/instruments/remote_controlers.py +iexcode/instruments/resolution.py +iexcode/instruments/s29_temp_cntl.py +iexcode/instruments/scalers.py +iexcode/instruments/scanRecord.py +iexcode/instruments/scratch.py +iexcode/instruments/shutters.py +iexcode/instruments/slits.py +iexcode/instruments/spec_stuff.py +iexcode/instruments/staff.py +iexcode/instruments/storage_ring.py +iexcode/instruments/userCalcs.py +iexcode/instruments/utilities.py +iexcode/instruments/vortexs29.py +iexcode/instruments/xrays.py +iexcode/macros/ARPES_macros.py +iexcode/macros/BL_shutdown.py +iexcode/macros/Kappa_optimization.py +iexcode/macros/ScanFunctions_plot.py +iexcode/macros/__init__.py +iexcode/macros/commissioning.py +iexcode/macros/start_of_the_week.py \ No newline at end of file diff --git a/iexcode.egg-info/dependency_links.txt b/iexcode.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/iexcode.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/iexcode.egg-info/requires.txt b/iexcode.egg-info/requires.txt new file mode 100644 index 0000000..5255694 --- /dev/null +++ b/iexcode.egg-info/requires.txt @@ -0,0 +1,5 @@ +matplotlib +numpy +scipy +h5py +netCDF4 diff --git a/iexcode.egg-info/top_level.txt b/iexcode.egg-info/top_level.txt new file mode 100644 index 0000000..3dc2072 --- /dev/null +++ b/iexcode.egg-info/top_level.txt @@ -0,0 +1 @@ +iexcode diff --git a/iexcode/__pycache__/__init__.cpython-37.pyc b/iexcode/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e2d0eaad8087d6fedb2ad12bc304ce79bc95a05c GIT binary patch literal 147 zcmZ?b<>g`kf`|!ck{E&XV-N=hKmraxT+9L_QW%06G#UL?G8BP?5yUSW{m|mnqGJ85 z+~nkp)Z#q-g36MN{Ji+`{G#mQg2d!heNWej<ouLW{mj$~FdZMCnU`4-AFo$Xd5gm) RH$SB`C)EyQ>SrKk003mmBt`%L literal 0 HcmV?d00001 diff --git a/iexcode/instruments/AD_utilities.py b/iexcode/instruments/AD_utilities.py new file mode 100644 index 0000000..f8d43d2 --- /dev/null +++ b/iexcode/instruments/AD_utilities.py @@ -0,0 +1,347 @@ +""" +General functions for dealing with Area Detectors and Cameras at 29ID + +work in progress need to redo +""" +############################################################################################################## +############################## General Area Detector ############################## +############################################################################################################## +import datetime +import re +from os import listdir,mkdir,chown,system,chmod +from os.path import join, isfile, exists, dirname +from time import sleep + +from epics import caget, caput +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.files_and_folders import get_next_fileNumber + + +def AD_CurrentDirectory(ADplugin): + """ + returns the current directory for area detector SavePlugin + handles both Winodws and linux IOCs + ADplugin = "29idc_ps1:TIFF1:"; $(P)$(SavePlugin) + """ + SubDir=caget(ADplugin+"FilePath",as_string=True) + if SubDir[0] == 'X': + Dir='/net/s29data/export/data_29idb/' + SubDir=SubDir.split('\\')[1:] + elif SubDir[0] == 'Y': + Dir='/net/s29data/export/data_29idc/' + SubDir=SubDir.split('\\')[1:] + elif SubDir[0] == 'Z': + Dir='/net/s29data/export/data_29idd/' + SubDir=SubDir.split('\\')[1:] + else: + Dir = SubDir + SubDir=[] + FilePath=join(Dir,*SubDir,'') + return FilePath + +def AD_prefix(ADplugin): + """ + returns the prefix for AreaDetector plugin based on ADplugin + """ + prefix = caget(ADplugin+"FileName_RBV",as_string=True) + return prefix + +def AD_EnableStats(ADplugin): + """ + Enabling the statistics in an AreaDector + ADplugin = "29idc_ps1:Stats1:"; (ADplugin=$(P)$(StatsPlugin)) + """ + caput(ADplugin+"EnableCallbacks","Enable") + caput(ADplugin+"ComputeStatistics","Yes") + caput(ADplugin+"ComputeCentroid","Yes") + + +def AD_SaveFileSetup(ADplugin,mda,**kwargs): + """ + ADplugin = "29id_ps1:TIFF1:" which IOC and which filesaving plugin + (ADplugin=$(P)$(SavePlugin)) + uses to get the current MDA directory and then set the path to one up + /dtype + MDA_CurrentDirectory(scanIOC=None) + + **kwargs (defaults) + scanIOC = BL_ioc() + userpath = extracted from ScanRecord unless specified + subfolder = taken from ADplugin unless specified + filepath = userpath/subfolder + + prefix = default same as subfolder + ext = file extension is extracted for ADplugin unless specified + (TIFF -> tif, HDF -> h5, ...) + FileTemplate="%s%s_%4.4d."+ext; format for filename first %s = filepath, second %s = prefix + + """ + kwargs.setdefault("userpath",dirname(dirname(mda.filepath))) + kwargs.setdefault("subfolder",ADplugin.split(":")[-2][:-1]) + + kwargs.setdefault("prefix",ADplugin.split(":")[-2][:-1]) + extDict={"TIFF":".tif","HDF":"h5"} + kwargs.setdefault("ext",ADplugin.split(":")[-2][:-1]) + kwargs.setdefault("FileTemplate","%s%s_%4.4d."+kwargs["ext"]) + + kwargs.setdefault("debug",False) + + if kwargs['debug']: + print("kwargs: ",kwargs) + + fpath=join(kwargs['userpath'],kwargs['subfolder'],'') + print("\nFolder: " + fpath) + if not (exists(fpath)): + fileNumber=1 + else: + fileNumber=get_next_fileNumber(fpath,kwargs["prefix"]) + print("NextFile: "+str(fileNumber)) + caput(ADplugin+"CreateDirectory",-1) #allows IOC to create directories + caput(ADplugin+"FilePath",fpath) + caput(ADplugin+"FileName",kwargs["prefix"]) + caput(ADplugin+"FileNumber",fileNumber) + + #setup AD + caput(ADplugin+"FileTemplate",kwargs["FileTemplate"]) + caput(ADplugin+"AutoIncrement","Yes") + caput(ADplugin+"AutoSave","Yes") + +def AD_CurrentPrefix(ADplugin): + """ + returns the prefix (without _) for area detector SavePlugin + ADplugin = "29id_ps1:TIFF1:"; $(P)$(SavePlugin) + """ + Prefix=caget(ADplugin+'FileName',as_string=True) + return Prefix + +def AD_CurrentRun(ADplugin): + """ + returns the curent run specified in the filepath for area detector SavePlugin + ADplugin = "29id_ps1:TIFF1:"; $(P)$(SavePlugin) + """ + fpath=caget(ADplugin+"FilePath",as_string=True) + current_run=re.findall("\d\d\d\d_\d", fpath)[0] + return current_run + +def AD_CurrentUser(ADplugin): + """ + returns the curent user specified in the filepath for area detector SavePlugin + ADplugin = "29id_ps1:TIFF1:"; $(P)$(SavePlugin) + """ + folder_name = ADplugin.split(":")[1] + folder_name[:-1] + SubDir=caget(ADplugin+":FilePath",as_string=True) + if SubDir[0] == 'X': + current_user='Staff' + elif SubDir[0] == 'Y': + m=SubDir.find(AD_CurrentRun(ADplugin)) + n=SubDir.find(folder_name) + current_user=SubDir[m+7:n] + else: current_user=None + return current_user + +def AD_DoneSingleSave(ADplugin,**kwargs): + """ + sets and AD up ready to save images + Acquire -> Done + ImageMode -> Single + Save -> Enable + e.g. ADplugin="29id_ps2:TIFF1:" + + **kwargs: + P=first part of ADplugin if not specified + R="cam1:"; other AD have "det1:" + """ + kwargs.setdefault("P",ADplugin.split(":")[0]+":") + kwargs.setdefault("R","cam1:") + caput(kwargs["P"]+kwargs["R"]+"Acquire","Done",wait=True,timeout=5*60);sleep(.1) + caput(kwargs["P"]+kwargs["R"]+"ImageMode","Single");sleep(.5) + caput(ADplugin+"EnableCallbacks","Enable");sleep(.1) + +def AD_FreeRun(ADplugin,**kwargs): + """ + sets and AD to disable saving and free run + Saving -> Disable + Acquire -> Done + ImageMode -> Single + + e.g. ADplugin="29id_ps2:TIFF1:" + + **kwargs: + P=first part of ADplugin if not specified + R="cam1:"; other AD have "det1:" + """ + kwargs.setdefault("P",ADplugin.split(":")[0]+":") + kwargs.setdefault("R","cam1:") + caput(kwargs["P"]+kwargs["R"]+"Acquire","Done",wait=True,timeout=5*60) + caput(ADplugin+"EnableCallbacks","Disable");sleep(.1) + caput(kwargs["P"]+kwargs["R"]+"ImageMode","Continuous");sleep(.1) + caput(kwargs["P"]+kwargs["R"]+"Acquire","Acquire");sleep(.1) + + +def AD_snap(ADplugin,**kwargs): + """ + takes an image and save the image for ADplugin + e.g. ADplugin="29id_ps2:TIFF1:" + + use AD_SaveFileSetup to set filepath, prefix etc. + use AD_CurrentDirectory to see current directory + use AD_prefix to see current prefix + + **kwargs: + P=first part of ADplugin if not specified + R="cam1:"; other AD have "det1:" + + ExposureTime: changes both the exposure time and the acquire time for the snapshot + resets after acquisition + FreeRun: True => disable setting and go back to continuous acquision + False => leave saving enabled and camera in single acquision + """ + kwargs.setdefault("P",ADplugin.split(":")[0]+":") + kwargs.setdefault("R","cam1:") + kwargs.setdefault("FreeRun",True) + + expT=caget(kwargs["P"]+kwargs["R"]+"AcquireTime_RBV") + acqT=caget(kwargs["P"]+kwargs["R"]+"AcquirePeriod_RBV") + + AD_DoneSingleSave(ADplugin,**kwargs) + + if "ExposureTime" in kwargs: + caput(kwargs["P"]+kwargs["R"]+"AcquireTime",kwargs["ExposureTime"]) + caput(kwargs["P"]+kwargs["R"]+"AcquireTime",kwargs["ExposureTime"]+.01) + + caput(kwargs["P"]+kwargs["R"]+"Acquire","Acquire",wait=True,timeout=5*60) + + if "ExposureTime" in kwargs: + caput(kwargs["P"]+kwargs["R"]+"AcquireTime",expT) + caput(kwargs["P"]+kwargs["R"]+"AcquireTime",acqT) + + if kwargs["FreeRun"]: + sleep(.1) + AD_FreeRun(ADplugin,**kwargs) + + + +def AD_ScanTrigger(ADplugin,mda,**kwargs): + """ + Add Triggering of AreaDetector to scanIOC + ADplugin = "29idc_ps1:TIFF1:" (ADplugin=$(P)$(SavePlugin)) + + **kwargs + scanIOC = "29id"+BL_ioc() if not specified + scanDIM = 1 + P=first part of ADplugin if not specified + R="cam1:"; other AD have "det1" + detTrig = 2; detectorTrigger number + """ + kwargs.setdefault("scanDIM",1) + kwargs.setdefault("P",ADplugin.split(":")[0]+":") + kwargs.setdefault("R","cam1:") + kwargs.setdefault("detTrig",2) + + scanPV=mda.ioc+"scan"+str(kwargs["scanDIM"]) + trigger=".T"+str(kwargs["detTrig"])+"PV" + caput(scanPV+trigger,kwargs["P"]+kwargs["R"]+"Acquire",wait=True,timeout=5*60) + +def ADplugin_ScanSetup(ADplugin,mda, **kwargs): + """ + stop the acquisition, puts in ImageMode=Single + enables saving + add to detector trigger + Does not press go + + ADplugin = "29idc_ps1:TIFF1:"; (ADplugin=$(P)$(SavePlugin)) + **kwargs + # AD_ScanTrigger + scanIOC = "29id"+BL_ioc() if not specified + scanDIM = 1 + P=first part of ADplugin if not specified + R="cam1:"; other AD have "det1:" + detTrig = 2; detectorTrigger number + + # AD_SaveFileSetup + filepath=userpath (from BL_ioc scanRecord)+"/dtype" + (e.g. filepath="/net/s29data/export/data_29id"+folder+"/"+run+"/"+userName+"/"+df) + dtype = taken from ADplugin + FileTemplate="%s%s_%4.4d."+dtype; format for filename first %s = filepath, second %s = prefix + prefix = dtype by default + + """ + #from AD_ScanTrigger + kwargs.setdefault("P",ADplugin.split(":")[0]+":") + kwargs.setdefault("R","cam1:") + + AD_DoneSingleSave(ADplugin,**kwargs) + + AD_SaveFileSetup(ADplugin,**kwargs) + AD_ScanTrigger(ADplugin, **kwargs) + trigger=".T"+str(kwargs["detTrig"])+"PV" + scanPV=mda.ioc+"scan"+str(kwargs["scanDIM"]) + print("WARNING: you need to need to disable saving and clear the trigger by hand after the scan") + print("\tAD_FreeRun("+ADplugin+"); caput("+scanPV+trigger+",'')") + +def AD_ROI_setup(AD,ROInum,xcenter=500,ycenter=500,xsize=50,ysize=50,binX=1,binY=1): + """ + AD = "29id_ps4" + AD = "29iddMPA" + """ + # roiNUM=1 MPA_ROI_SetUp(535,539,50,50) center of MCP + + ADplugin=AD+':ROI'+str(ROInum)+':' + xstart=xcenter-xsize/2.0 + ystart=ycenter-ysize/2.0 + caput(ADplugin+'MinX',xstart) + caput(ADplugin+'MinY',ystart) + caput(ADplugin+'SizeX',xsize) + caput(ADplugin+'SizeY',ysize) + caput(ADplugin+'BinX',binX) + caput(ADplugin+'BinY',binY) + caput(ADplugin+'EnableCallbacks','Enable') + print(ADplugin+' - '+caget(ADplugin+'EnableCallbacks_RBV',as_string=True)) + #MPA_ROI_Stats(roiNUM) + +def AD_OVER_SetUp(AD,ROInum,OVERnum,linewidth=5,shape='Rectangle'): + """ + AD = "29id_ps4" + AD = "29iddMPA" + shape= 'Cross', 'Rectangle', 'Ellipse','Text' + """ + OVER1=AD+":Over1:"+str(OVERnum)+":" + ROI=AD+":ROI"+str(ROInum)+":" + + caput(ROI+'EnableCallbacks','Enable') + caput(OVER1+"Name","ROI"+str(ROInum)) + caput(OVER1+"Shape",shape) + caput(OVER1+"Red",0) + caput(OVER1+"Green",255) + caput(OVER1+"Blue",0) + caput(OVER1+'WidthX',linewidth) + caput(OVER1+'WidthY',linewidth) + + caput(OVER1+"PositionXLink.DOL",ROI+"MinX_RBV CP") + caput(OVER1+"SizeXLink.DOL",ROI+"SizeX_RBV CP") + caput(OVER1+"PositionYLink.DOL",ROI+"MinY_RBV CP") + caput(OVER1+"SizeYLink.DOL",ROI+"SizeY_RBV CP") + + caput(OVER1+"Use","Yes") + + +def AD_OverLayCenter(x,y,AD,OverLay=1,Num=1): + """ + Sets CenterX and CenterY for AD:Over(OverLay):Num: + eg. 29id_ps2:Over1:1: AD='29id_ps2',Overlay=1, Num=1 + """ + caput(AD+":Over"+str(OverLay)+":"+str(Num)+":CenterX",x) + caput(AD+":Over"+str(OverLay)+":"+str(Num)+":CenterY",y) + +def AD_OverLayCenter_get(AD,OverLay=1,Num=1): + """ + Sets CenterX and CenterY for AD:Over(OverLay):Num: + eg. 29id_ps2:Over1:1: AD='29id_ps2',Overlay=1, Num=1 + """ + print('x = '+str(caget(AD+":Over"+str(OverLay)+":"+str(Num)+":CenterX"))) + print('y = '+str(caget(AD+":Over"+str(OverLay)+":"+str(Num)+":CenterY"))) + + + + + diff --git a/iexcode/instruments/ARPES.py b/iexcode/instruments/ARPES.py index b07a8c4..90f825f 100644 --- a/iexcode/instruments/ARPES.py +++ b/iexcode/instruments/ARPES.py @@ -2,27 +2,28 @@ import numpy as np from time import sleep from epics import caget,caput,PV -from .IEX_endstations import * -from .staff import staff_detector_dictionary - -from .files_and_folders import check_run,make_user_folders,folder_mda -from .staff import staff_detector_dictionary -from .logfile import logfile_name_set,logfile_header - -from .conversions_constants import * -from .utilities import * -from .userCalcs import userStringSeq_clear, userStringSeq_pvs - -from .scanRecord import * -from .Motors import * -from .xrays import * -from .current_amplifiers import * -from .gate_valves import valve_close, branch_valves -from .shutters import branch_shutter_close -from .slits import slit3C_get - -from .Lakeshore_335 import Lakeshore_reset -from .electron_analyzer import folders_EA,EA + +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.staff import staff_detector_dictionary + +from iexcode.instruments.files_and_folders import check_run,make_user_folders,folder_mda +from iexcode.instruments.staff import staff_detector_dictionary +from iexcode.instruments.logfile import logfile_name_set,logfile_header + +from iexcode.instruments.conversions_constants import * +from iexcode.instruments.utilities import * +from iexcode.instruments.userCalcs import userStringSeq_clear, userStringSeq_pvs + +from iexcode.instruments.scanRecord import * +from iexcode.instruments.Motors import * +from iexcode.instruments.xrays import * +from iexcode.instruments.current_amplifiers import * +from iexcode.instruments.gate_valves import valve_close, branch_valves +from iexcode.instruments.shutters import branch_shutter_close +from iexcode.instruments.slits import slit3C_get + +from iexcode.instruments.Lakeshore_335 import Lakeshore_reset +from iexcode.instruments.electron_analyzer import folders_EA,EA ############################################################################# diff --git a/iexcode/instruments/FMB_mirrors.py b/iexcode/instruments/FMB_mirrors.py index c7c0069..888d9b9 100644 --- a/iexcode/instruments/FMB_mirrors.py +++ b/iexcode/instruments/FMB_mirrors.py @@ -2,9 +2,9 @@ from time import sleep from epics import caget, caput -from .IEX_endstations import * -from .utilities import read_dict, print_warning_message -from .m3r import m3r_branch +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.utilities import read_dict, print_warning_message +from iexcode.instruments.m3r import m3r_branch M0M1_fpath="/home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/Dict_IDCal.txt" diff --git a/iexcode/instruments/IEX_VPU.py b/iexcode/instruments/IEX_VPU.py index 10274c0..6678eb1 100644 --- a/iexcode/instruments/IEX_VPU.py +++ b/iexcode/instruments/IEX_VPU.py @@ -6,10 +6,10 @@ import numpy.polynomial.polynomial as poly from epics import caget, caput -from .utilities import dateandtime, print_warning_message, read_dict -from .shutters import main_shutter_check_open -from .VLS_PGM import mono_grating_get -from .userCalcs import userCalcOut_clear +from iexcode.instruments.utilities import dateandtime, print_warning_message, read_dict +from iexcode.instruments.shutters import main_shutter_check_open +from iexcode.instruments.VLS_PGM import mono_grating_get +from iexcode.instruments.userCalcs import userCalcOut_clear IDcal_fpath="/home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/Dict_IDCal.txt" diff --git a/iexcode/instruments/IEX_endstations.py b/iexcode/instruments/IEX_endstations.py index 5c3fd18..94ee6e1 100644 --- a/iexcode/instruments/IEX_endstations.py +++ b/iexcode/instruments/IEX_endstations.py @@ -10,6 +10,8 @@ it makes the default stuff work this will prompt for the endstation and will set the default parameters, look at the init kwargs to see which defaults you can change. """ +global BL +BL = None class Endstation: """ diff --git a/iexcode/instruments/Kappa.py b/iexcode/instruments/Kappa.py index 6fcb40d..18c9810 100644 --- a/iexcode/instruments/Kappa.py +++ b/iexcode/instruments/Kappa.py @@ -3,27 +3,28 @@ from time import sleep from math import floor from epics import caget, caput,PV -from .IEX_endstations import * -from .staff import staff_detector_dictionary - -from .files_and_folders import check_run,make_user_folders,folder_mda -from .staff import staff_detector_dictionary -from .logfile import logfile_name_set,logfile_header - -from .conversions_constants import * -from .utilities import * -from .userCalcs import userStringSeq_clear, userStringSeq_pvs - -from .scanRecord import * -from .Motors import * -from .xrays import * -from .current_amplifiers import * -from .gate_valves import valve_close, branch_valves -from .shutters import branch_shutter_close -from .slits import slit3D_get - -from .Kappa_det import * -from .spec_stuff import folders_spec + +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.staff import staff_detector_dictionary + +from iexcode.instruments.files_and_folders import check_run,make_user_folders,folder_mda +from iexcode.instruments.staff import staff_detector_dictionary +from iexcode.instruments.logfile import logfile_name_set,logfile_header + +from iexcode.instruments.conversions_constants import * +from iexcode.instruments.utilities import * +from iexcode.instruments.userCalcs import userStringSeq_clear, userStringSeq_pvs + +from iexcode.instruments.scanRecord import * +from iexcode.instruments.Motors import * +from iexcode.instruments.xrays import * +from iexcode.instruments.current_amplifiers import * +from iexcode.instruments.gate_valves import valve_close, branch_valves +from iexcode.instruments.shutters import branch_shutter_close +from iexcode.instruments.slits import slit3D_get + +from iexcode.instruments.Kappa_det import * +from iexcode.instruments.spec_stuff import folders_spec diff --git a/iexcode/instruments/Kappa_Euler.py b/iexcode/instruments/Kappa_Euler.py index cd2dbfb..42dfe2f 100644 --- a/iexcode/instruments/Kappa_Euler.py +++ b/iexcode/instruments/Kappa_Euler.py @@ -1,9 +1,9 @@ import numpy as np from epics import caget, caput -from .userCalcs import userCalcOut_clear -from .IEX_endstations import mda -from .userCalcs import userCalcOut_clear +from iexcode.instruments.userCalcs import userCalcOut_clear +from iexcode.instruments.IEX_endstations import mda +from iexcode.instruments.userCalcs import userCalcOut_clear #### Obsolete? diff --git a/iexcode/instruments/Kappa_det.py b/iexcode/instruments/Kappa_det.py index 14b2f04..9a1a190 100644 --- a/iexcode/instruments/Kappa_det.py +++ b/iexcode/instruments/Kappa_det.py @@ -1,7 +1,7 @@ from time import sleep from epics import caget, caput -from .IEX_endstations import Motors +from iexcode.instruments.IEX_endstations import Motors ############################################################################################################## diff --git a/iexcode/instruments/Lakeshore_335.py b/iexcode/instruments/Lakeshore_335.py index 080c578..5acc6b9 100644 --- a/iexcode/instruments/Lakeshore_335.py +++ b/iexcode/instruments/Lakeshore_335.py @@ -7,8 +7,8 @@ import numpy as np from epics import caget, caput -from .IEX_endstations import * -from .utilities import read_dict +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.utilities import read_dict def Lakeshore_reset(pv,d): """ diff --git a/iexcode/instruments/Motors.py b/iexcode/instruments/Motors.py index 24085c5..119b51b 100644 --- a/iexcode/instruments/Motors.py +++ b/iexcode/instruments/Motors.py @@ -3,7 +3,7 @@ from math import floor from epics import caget, caput -from IEX_endstations import * +from iexcode.instruments.IEX_endstations import * class Motors: diff --git a/iexcode/instruments/VLS_PGM.py b/iexcode/instruments/VLS_PGM.py index 4ca6dd8..8f7bfdc 100644 --- a/iexcode/instruments/VLS_PGM.py +++ b/iexcode/instruments/VLS_PGM.py @@ -7,8 +7,8 @@ import numpy as np from epics import caget,caput -from .IEX_endstations import * -from .utilities import print_warning_message,read_dict +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.utilities import print_warning_message,read_dict ############################################################################################################## diff --git a/iexcode/instruments/__init__.py b/iexcode/instruments/__init__.py new file mode 100644 index 0000000..e244a3c --- /dev/null +++ b/iexcode/instruments/__init__.py @@ -0,0 +1,42 @@ + +""" from iexcode.instruments.AD_utilites import * +from iexcode.instruments.ARPES import * +from iexcode.instruments.bakeout import * +from iexcode.instruments.beamline import * +from iexcode.instruments.cameras import * +from iexcode.instruments.conversions_constants import * +from iexcode.instruments.current_amplifiers import * +from iexcode.instruments.diagnostics import * +from iexcode.instruments.electron_analyzer import * +from iexcode.instruments.encoders import * +from iexcode.instruments.files_and_folders import * +from iexcode.instruments.FMB_mirrors import * +from iexcode.instruments.gate_valves import * +from iexcode.instruments.hxp_mirrors import * +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.IEX_VPU import * +from iexcode.instruments.Kappa import * +from iexcode.instruments.Kappa_det import * +from iexcode.instruments.Kappa_Euler import * +from iexcode.instruments.Lakeshore_335 import * +from iexcode.instruments.logfile import * +from iexcode.instruments.m3r import * +from iexcode.instruments.Motors import * +from iexcode.instruments.mpa import * +from iexcode.instruments.remote_controlers import * +from iexcode.instruments.resolution import * +from iexcode.instruments.s29_temp_cntl import * +from iexcode.instruments.scalers import * +from iexcode.instruments.Scienta import * +from iexcode.instruments.shutters import * +from iexcode.instruments.slits import * +from iexcode.instruments.spec_stuff import * +from iexcode.instruments.staff import * +from iexcode.instruments.storage_ring import * +from iexcode.instruments.userCalcs import * +from iexcode.instruments.utilities import * +from iexcode.instruments.VLS_PGM import * +from iexcode.instruments.vortexs29 import * +from iexcode.instruments.xrays import * + + """ diff --git a/iexcode/instruments/__pycache__/AD_utilites.cpython-37.pyc b/iexcode/instruments/__pycache__/AD_utilites.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a370ff2b8c85e414b0f9ab7954b00e977d81b09 GIT binary patch literal 11481 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 Ng#Qc0iQ-hT@V}DV0}ucJ literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/AD_utilities.cpython-37.pyc b/iexcode/instruments/__pycache__/AD_utilities.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..763286c8ddcdce49fed5d93a0cf42c0560f773e6 GIT binary patch literal 11482 zcmd5?-ESLLcIOOdNQ$C9Eq_RMoQZ6$#8{DHBU?0bl&m7zUZaVuwUxxu1|UYfLuq1? z!`>Oj5=)_Ioqg#x!J<IBA6OI)(3b^@eQSX}_qqSTed!i`@_YM|ho--C?#z&uWIMY^ zw=02n?)Th#&bjCO&bjmM&`>dhzkmJnf7|)hLMHQ{R7n1E$b1jK&@wU^k=f1&Lzs1A z+u*0UZQ_}&XB*bG)v&kiMs7RT$ZzKxh3$eteXaUHqqtpc3~moLO53Hz(Do45+x6kb z$o2^5bM?{2*!Ease0!YB`TD6%8e6DOG)`}yM%{oYewNujBL+nY&$D7k4C8rDjEGS@ zC⁣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 IQ^msn0*urtegFUf literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/ARPES.cpython-37.pyc b/iexcode/instruments/__pycache__/ARPES.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..062ed0acd605454bc28bed0f3204edf64921a4eb GIT binary patch literal 22012 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 P^gZXj7}=BW&Sm~T@Z~(e literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/FMB_mirrors.cpython-37.pyc b/iexcode/instruments/__pycache__/FMB_mirrors.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc28ad732f71f5749fe7379b7c1c41463fa49469 GIT binary patch literal 5479 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 DV|)e} literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/IEX_VPU.cpython-37.pyc b/iexcode/instruments/__pycache__/IEX_VPU.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..48fdfabc4675e10f548048eafc93655f24cb372c GIT binary patch literal 13342 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 F{oi>tC;b2b literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/IEX_endstations.cpython-37.pyc b/iexcode/instruments/__pycache__/IEX_endstations.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4ef735bafb06232202d431f08609321d27de902 GIT binary patch literal 3164 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/Kappa.cpython-37.pyc b/iexcode/instruments/__pycache__/Kappa.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b280eebee6bb33b0a5a9ee70a1958a91420d40e9 GIT binary patch literal 30320 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 BsY3t& literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/Kappa_Euler.cpython-37.pyc b/iexcode/instruments/__pycache__/Kappa_Euler.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d7e5066528a6d569653c3be9a202fe97ce5d86f4 GIT binary patch literal 3978 zcmdT{&2!sC6xZs*k(2l<N$Zx<A(?WJoW`Q*0m^hBvf|P-!O4)2>3Ep&2(9Y4^@nC9 zL*j{Nm<gQW#DN3Di5c$v1>Exw=)i@Ok8t1s7v7U1*-le(pdAu<p5FfUt>oSP{q%OX zluAu<aIr6rKfRITxZe;&7Y~IMFn1qrl%u4{RS9^$$+v{6&=RX6!Fr)7wG!1tP!^jL zEx9V6s+VZuORk!v6I2F2Nt1LE{1ly{)8MCRil)J*XohCNr)iGPfX`5}&8f2k_;;?N z^1fu5b=yPdbUl^$xvpz7!)#icU9V;}ZIeNz(4wX#;3II!U=o-C=KcsbO8SDwdE`(y z;`<`y`w|uUi9_iyF~?C6Oq%2R6CQsk(nOx46GuGZ0wm|TLoP;1KzxiQ0g)I@<vCAy zD#mykqQm#3;H_y-qM0KCr7X0SXf7zv<b(H94#!*>VAt#K|NiZF*MIoqe&f;puemzZ zaQ5-BK0wNPQgOAsZB(a9%&gaIYi(-p_xPd?Rsu7?R>8`>MA6VUj5mAo_F6?L6;`#u z&-gg676wRgFHz77hE`S+Oa#bqx){!%&$k_)cN)IndQ6plLVe!e^?7g4Cv87HI`__J zK7m@xCr^Ij*cEvAuXXKXAjj37wk&JUcH5eB;O%wVwa+{3nd_LAt*w<GTODd^4SPTI zZTO#V%WiwFwr)C(Su1y&HY+*@{_MyYYbS)`9Jtru#u1TB@*>=`e42#6EO?fOn1v1S z!lB}*0wsJ81yBMPC;?`@Pk>`Wd0+%|5im}u2uO<2s2T_bHPB=n1ODk$jDT;NicuO+ zCPu(FupRvaFivM<GzaJkqCf#xD+2<(j98vn_d4a8w+DEwvZeR(%l8^In5B1H?-V~O z?amup^OX;`!+2>d{`PZmV=R7WG!Ck%Ut3z;`FkO`g7RUgLnu5LEQf`M^*@zQzW(K_ zjg?qgcNIlnQa)IEsHz~Z@{+!zR}?f<TUlE!zNHq7&28lYLKPa7wN)J0=GG3wL8`(j z>8U)+LOp|o6v`>k6uS!0&z#Ieq=VO?azqD_0*0Qw1})Ck!8Z`ONID3ZpR2GW^ebXs z4#sQkHU2;9*>&tslt)x$xu9b%iSkLv@O)t&D_#o6*=WoW$&1hd7wHc__93}piQRz6 zrJ{I%xoDhX=i*Sjs2H#QLk>xXQAZ@-gDLqpl5bu*l5s(eNiKVx_1d#x3SSte@R7Cj zPH|V?IF-!LZn^wiOQUyo%e$j3Uzp&>zw+CP9!dZ|>ZS2yt|5FmA9^$?kp>=}qk%Ur zod$3}bxH%K6Xo)=`MM|?SX5q+>b0>{|KHa0Y$yTKi6rp&90@F3Ite63B#;b;JCf`# zwgzrTOTjD61G#usA{9J2M+LVau4a9y-EBDs3=adoWIGMZWw_H14=^`Rjuyqn@Muwt za;+QeIjgtMY6C};n%$<ZXL^lJ+YQe|c$^6KlEH3+;cCP%9WfLn!~TT3R6PES>{sRc ftqz5Q%zL=g4VE_<4l2_Gexf`hXXLb;155t{!G28R literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/Kappa_det.cpython-37.pyc b/iexcode/instruments/__pycache__/Kappa_det.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64a16ee6fa9eb6ab0e825aeb9dc61f7afccee3c6 GIT binary patch literal 2325 zcmZuyOK%iM5bmDWj@N60Nx(wNp~D4MLN)>vDHei2AQ4L9i=8OUWi*~{d&i!gnRNHs z+0}B1xpK}S7q~>qH9u#hNRe{lFXWW3dcA8N8L7K#`cd^&^;gx8R#v(U-(SD|x_7d~ z*gy0$|9l8vpqWSLB$K>kL!NL>vR8UTKk<h_65!vLK^YFCB!V0!9T_F@*GzU~{DR5& z)JvA+(hlo)-#`!R^ENc4Qcd4$!#q1smWF0z-|*RL^j&RhZ6<5CKQH7rS<_@&QrRt| zY(Gpqd8hT{U0BYW#LpU;_~Yy#C&lw`0sA&)6ic{dIK~rSdeVQvl0XJB#2Ct+gUFLe zF3B!tI&v8>SK3u)HGQnC$^m6^tt;^5WoC@nueET?0-)UTY>~jzN27=KC7S7>o3dBz zHKrwlZoUooefW$aFsj`DgXv{#`uFMP_eN>6c{t4Tfil%*Gq!`eN>6HiWST5jn@^wo znAcKm7V78OSQ)EFLseO`Il~J;8_l?l)3m4xo2Hit0%2i%vi`118}mK+95+Rcn!|k! zf@PMU^5=ZYr!4iX|0<CDHGl3+*`YV}_C0vxO?I5~6dm|vgdHf6kF*9T3Qt;PLnX3G zil#P&E$XVf*gs#sDXbl&$62{pX?@J}LrrMfF73nBr_FI2kUAY<T7#;r?`z_28_!j> zoikM~q_d80!0o;a!NllTC*~3F^2w$5LIMwVb23U_fJT;FI6i}5*(>e>;7_^rjv{^A z`Zk#IQ;)N$hd*{0N<MWYlfg^MiVz`+vPX#U!Z&y_ixQA72qYHE6slo;tY&+Lln27r z6d{2Kjg(^_1-Viuv*WeZi_;5X9uTvbp&OESwnUlf10`euBE7qIcT1RIR+j&>hD_U9 z7#=p+I|q@0TOtFvtO9REGXQ3%Ufif^J10Xq76s@E{Yv)F-6d4W-Yx3H_5E^GYh`j( z<w}@bD^*=z*wU-GgWa!CGlG+>ux;m9>Al*h?S6F5h#3{i0*OKQr!Syu0}5zAa9q)A zG<}6SN{;t<()zfbPcd&?O<d{vF?bSu$P{c_<P903=7{nq1j-x9rT5Z%4x$|T(!a`N zaFro*xD1bcebWXqI^~>Izt~WAkYhe*6CZhI7vs{|*gYGUk39Xmji#RLtuwhobh90~ zO6T3|1<YP_v$SVz@%Q!~I(f<M3FJr4atsU~lO!*Lq*o>f=Ydx6&KA{yOJ-u;jD{1X zLBv|*HPYKOHJFM&S!mQiv8S@(d1}`c?%NeEaZ(V^?=H0OqV6qaCdCcGMC;kX8_GD{ zK{vSuYqvz6Rl;f5Q$j1L_KQl%xuVJDL#@QP9tkr#6HYP|8$ban1%XuD7z-p$)qf!F zxBOYl$9N=^N&II|b|%5b&Xe8N-%<7eHu3%a0CPV){8m#XoP-;XAtyen@noI6pI5HA z{#x9c+}j;w_PP=E(M_R1#PMD&#|}7Z2qIKUv9BN%6@ZNn1_H%zlu`ZL*x1<a$F8(F zb~^4RYpIKIDvO*pWqRC(4FZIhv8eDe&e%+q96ToWCd+44Hbtr@5pbeDCjNe1zfb9y z0zw6pYMA*19gBK=)dRE91YhMb+N|$-A3PKD(|ERa`!PK&RD;vB?WVviM<vO<H2rCm zm1irRG?jIprg{aoT<O*8Bnj&1t$V9;)tQPi?R8lf`+*nxaj$z>)BJY7+Xl8k*(dMY zP&GwvX5pixYcJqcOjRXOTPSj7RtqTaH5GiP<1Ks6JQ;1{do%d=>DHa)9>3`IqJRh9 Ef4^5icmMzZ literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/Lakeshore_335.cpython-37.pyc b/iexcode/instruments/__pycache__/Lakeshore_335.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8fe68e9b0219fe031f8768107e920399efdadc2c GIT binary patch literal 11833 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 Zopg`5Z@Y)w1CH&aoe^i$F<sX^{l6o`p#J~> literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/Motors.cpython-37.pyc b/iexcode/instruments/__pycache__/Motors.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3ada008630f4d6119799aa0b66f9b538493f799 GIT binary patch literal 6871 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 ND2s83{?2M;{Xg<brv(52 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/Scienta.cpython-37.pyc b/iexcode/instruments/__pycache__/Scienta.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9008e02435a9fc2c2c5df84e805f720754feb2a6 GIT binary patch literal 16246 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/VLS_PGM.cpython-37.pyc b/iexcode/instruments/__pycache__/VLS_PGM.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b24f7623fd135e0ec917cb7d239a015d5dc153ef GIT binary patch literal 19637 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 E2g7!XeE<Le literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/__init__.cpython-37.pyc b/iexcode/instruments/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c3c008b246ea4aaf58e2d55d10549b1f5dd589b GIT binary patch literal 1954 zcmb7F&1w`u5T4CJ!$RI+uE}8sTs#OO8eM-dD~n5lcqw{&YG&H>AJskUZXU%q@L_y` zTzv#RiHJ4vZ!ZaDLk~Uh_19JP)z9(4!MNb{<HM)b_gh8rdHDIWMtpe4JMHtQF5VXP zXjvTYsjmzy8w##8xY41;iSa2@@6hKodGrMOY^=$cO5<XnTpo?DN55XomIB;Lonz?p zUu1bb<qDOv#-jwQl|xeh484I?2fs#2Cinz=PFb<HqBnXXjtnRlt!WH%NN&|ewLYY5 zv<QwCb*A7!d1bd3NRl_u59-P6X^h3h>@_sON-sT~AA>VQLH0(gGQwKf^-k*Cph&lg z|HJ-b_6E?`EL<K$lmuwFa<({?KwqjDl^6&$igNji6=uDa33|d+lDa?$)9GD_F*dY= zuo9@7igIcmvQTX&ix;5_oEBVioZlP=k#K}5SZ+hXOI%BL?m<QuLHle)7qpUEvebr) zUz8cC>vI-5n7F};B9S6$NV#tskz-j&fq_(uK*qNc;yxigR#w)`e$5Xf>jGydOISRc zON?0aFv1P1Edg&xZFeI)E?*svE^h<WK?8g&Xpg7j)#uyu>Xh9})jOwkhsjrQn>+5s s?*pBus5DmGNQPEh{T`Ur&cLhyj5irz61SK8_g$#D+8-P-`4`^BPe8!MX8-^I literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/bakeout.cpython-37.pyc b/iexcode/instruments/__pycache__/bakeout.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed7fa33afb66085ab5615af80990bfae05fdd2fe GIT binary patch literal 4491 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/beamline.cpython-37.pyc b/iexcode/instruments/__pycache__/beamline.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb16662015007d4b73929f7a716b9331a54537d8 GIT binary patch literal 2570 zcmb_eOK%iM5bmDYnSFTujKPqH)Ka8~MZ^vUfmnqQZwv{FaS#w7D@CK(>E89&vok|? zkMXX?$^p)~hD15QG5;X>5%~jiOin&Wx#SR~s%H%b`IsJcS9SGc>+x0fcBZEr1lr%< z{oeZvxIb}n*lb`vg|4<>;G|19<yJ(y7En8~hm~%HqO1}*L$~W1-ifOB2zPn)De2aD zjn{$Jd4o>@ZSW?a20F#x;<J42DeX4J^!7}j@_D`hvsrH4C9TEr<%Zfyq-L={6s(^} z*3aTVhe@nhrUYk?L%qd7j5NtK+xt-ZBh_defSP2pMGHqoLZrZ0x9`J9&jt=IL31bl zOQ7mq7&O_Y+g46;np?S@SNcxwd{5C7P{kTwYcOd2Fp3llW2UzRQ-L2dDS||Dqa4bD zOiH0+))W3P3S+^-Brqp8TXtb}HVS*l{ldk2!noxY_Wd=G-Ray1Rkbg5QBgWci>A^d zRbHBeu~vn<^VpXI^$U?Rpy)qetbDD6R4d!VAlMQrUP(t_e(XI?<c>=HK&;%n{xC?m zSP8{mNn@pDHWYB#%Asb<>8O}O6TDKHd31yY1F6V47y}GCw#uX%Po09vQBU>l-2Q*P zBr!q~!&5X%%{=Y*BGIbI8m`n~=+=g#QYxoWhHfc`S>THKBW9J`mOy-H+yd|cbkit| zXrM9fZD2~{`Uc@4&B+gzG0z4NRL1|BP~OErDdv{|Frwawanbj)NVko2{pPK!=NU6= z8_8f0#se0@kxVB;Uxz^(@ypJhEdz@XM(`vlV%v{cG&urMFQdkkDDeg>XIchrTxdUx zl=+GBnzrE)rVlX>*j08<W`doWXx2K%cp?;2Tbb5EGQdu-Q=Z2&rd?$>{0KCQljF7( z)m<Tbi4yk!%}3&74U!f;HWF16i9zxnc((L^U7i5`HUA^%KEn+dM)d!4>jl{-IRT&3 zv1hD;q{<|uJ-R9Km1dAen2n%nHtc~llbnQf!$?5paRx!@Y<PzWcw36yFv(OjYO@Wv z;nw<^x%o9qrtt0?aP4L`Y|(BNe*u2rO@P&Kn+GbiP;JByaT-)2$_@}w0S?&gNjK(o zV1nlD)0}d;W66`+;?@&NLHgW&LAbp{bR{QG=n~<TC9-d6D=*hA?%+P|f=jC7uZ-fi zB^C#k?lPUQAW26nDQE>1Cx$utsRyM`$i`&zE9=ccuT!pK*b5qr7gw)@Jh;Br23(zQ zuLX~?P>QkB#sH3;<r{0=>*Lv*>#JAVolK4Hn5wtgatsOzw<rBL*eaY9bt-J|d0|7z zftqI!0O=qxxe6=YebbjsbH*!`y#kw569(eCv<BZP=#s(wrAwz^QiX0@`Uf!h7IAxE z^9pyk3$*&PwNEwSwI>u@NO&En4YUEY0(1(f1GEX$1v(A1S{D7>$=$p<aQADP@)>mb z-f~`BC*OXsUmq>z^=(Uz^ZIj}(-*MzfIQ-q;P(x151~IGdz<ipkkbUtboLvgkMdgH zI8ET(uO$a(Ej$}9K&Z<#htDoS(9M0%Uf%Z%?tAut@Hq(i{Mae80C6R;1|MM393Z?7 z5Pk<(FI$#84a5xN6Bth~Rp;A*KYSI?L-FeI;tR)%FCH(xbiDZTYvRFAKVtjs+IR-a ziUy>{u?V!_n=MzO0TN9q>M!Mmon~6TgF-jm#izeDWzf^Dfhisj1KouMG;2avC-lOE zUYyWN6MDHk8HGxB9dojv;hQ*IC^788y~-0YJ~^L*4(c2X#6D@zh={~*4tj%H)Fm_2 z1#SlT2AQKLVJ5LAKsm%HwdRVd=kX-)yuyJx0#B|Lr$t4FLs6RTh%^k8@k8<c<hfoB zUp?2$@RWl#9R3`b|2^humiUC3I%-Oe$&a!eW0DyKj#y_NNAoV$J{~4Ki^M1R#HvLY TDprj)ttPFx=4;mG8Z-X^$M1@R literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/cameras.cpython-37.pyc b/iexcode/instruments/__pycache__/cameras.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..314916561858f01cd5b932833b4edd3f4acfa988 GIT binary patch literal 1804 zcmb7EOOM+&5EdmrwBDCDeQZ;-1%dVuFA%%kK7bf)(0#-~&|nYowg`MNXpy!ROO!%V zPIlp(nsfUH`Um<qdhZ|5wI~0DoH|4Kv1ttyrNALILk?#?e#6t<-I@bU{rczNkFw+Z zg+I&Y!Q^}B`X><F;Uscm(j&3ka|!;tkr$VGB|G<`@+IoLr~+e&mtQ-*D&ON(?!P8I zpYQW6UW3^duk&rb1G5_6?K+KvIrw%OZe9-9NEjT_%rr>;PNs$G8WqY>S3K_rkzfkc z<R~Dx1DXrH3jGYa{tyHw+~pn~&MUn7Tjdp*k(qPbfn)sZdo>D;hk*$b$<!51hBTd0 z$zq}DFj3Tu1sw<$N1+rn0JUP;?z!GNz8CU-s=sKjrpN2)m+R?Qiz!pnuh-gd+KtK_ zgPvo=b3}DsLIHR?^9qRH5r8V3*_T<I6RvhZX~mm|tsemg-Fg-W!B}Y7O0Uc~k^L8m zn&^}TqV?#((;(rZ6^fU|D78^pETqw`MH<cYDnCH?>9oH_U<(e>dmx;$OO~JSsyb-$ z(&eL0C%}NQVYnFg576~zAdE9}eg=3C0rW%Xm1oGzGww5Q=+4|<2r$A0(7o%27C<eG zrdetcK7CAez~mDVB#Kj$P`KgINEGlF;nF)x-CcUzupZu!_C=#qM0V@sJdLtZDEnP- z)D<R6bNBRd?!}zJnq7!4ubiBlt5oEb$qS}NI`@}**2t=TT)2m*U_gBcGD{CJKTOss z)rq<d<7Nmx2g`p!U`jJ@NN&QxVqk&I$FK>ojxA_L!x*?vndDTnDHuXSLuui{A^>(q zTqwKIild_<t+xGp@G?zwro?3!i#83$Opd_CATeX>K`hMFfZd*fL}$VCEL5UUF4?ao zOZ7N0)-6`c&8CFCMm97Mk=?FCEG4_Rql9>p$u_-InV{d@hdR@25D5x<3{<c^ca+cp z3nqB!K_Wq&B^iK#k!Wy4|8;DKMcR6U+Mj|iHbqxLqGxVhE(il)X)wWgD{nRHP2GZv zx@eRO=G0Hl`(5zV2_s>7quvFxdJjZi1smqSV32wrEl3CT0g6>Y7PRp#ZD9E)VANQ` z&UT#uWj=!0X39{oY@*ED6uCaLal;d+SLBdXCYBgQ$}8yXO4gh-865)tRAut5tQVDB zgUlB9^FNY{!l}3mAs5w9fz&Y7iw02#F#coM&dVYV1Fi7AQ20>vK3+UW&2ar3HIdC{ zEP{ntV?DZQ4MdxnFamA~oqxJ%1w43tA-OgT`KRsO+FG^f+c<CBm%TY#>;AWK!m~)+ V$0+T?S9f=aPkhhct5s|Je*@+r#-soM literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/conversions_constants.cpython-37.pyc b/iexcode/instruments/__pycache__/conversions_constants.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2eaf10078a2d3ae7767b79bdbca730a979c03442 GIT binary patch literal 3048 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/current_amplifiers.cpython-37.pyc b/iexcode/instruments/__pycache__/current_amplifiers.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17f97f1549d6930c4f0d1af030b664f4227c3664 GIT binary patch literal 10455 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 D{1ngV literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/diagnostics.cpython-37.pyc b/iexcode/instruments/__pycache__/diagnostics.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc05a7a8e2024e0b4a86b65d0f8a0ca36bdad5f6 GIT binary patch literal 6664 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/electron_analyzer.cpython-37.pyc b/iexcode/instruments/__pycache__/electron_analyzer.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91ff70dc60fd8a66b8eb2c045939a252083917e3 GIT binary patch literal 23058 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 Rj%F?*buiPNIflH!{|$7eKsf*a literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/encoders.cpython-37.pyc b/iexcode/instruments/__pycache__/encoders.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b073fe79b3370736b5f54c0b7df04e2a5372cc9 GIT binary patch literal 1790 zcmbtU&5j#I5bmBId+c31OR~gCkfK(E&>F4mU~&K}M6jEU!hy{y76e8h%RAln4zXt@ z-EC(*8gnrZz=;dI058ECa7Vs!@+)wns>k+j7KsZZx%$V|UsZkeb5N<234FiL{y4o+ zB;+qVtUn7jpTbY=gW!bIAsJDhjx67zgj?MHk@z-uxC^Vp3%m%c%e@Y%mzMB?)UAaZ z^pdg0Wgv7w@fJP=24mz9e(C`TP7w~F22S5XDDDj`*p+yhZ}DwjG56J9?HSEzMs5=@ zaxML#%;1wkk7cA-kMS_jVI1}3l)Vo18PjKii71G<kSv)fqrEzJFLb*i(sH^N^+ux3 zK3PBLhH<dZMzM}%zFj{YhDw(WR9bF*9P%&1DEy$ktUwscDvE0;c0gSG-)b-K;O#qj zTlnnx$x)}hL^hU4#nMLMfT-JkK@CIQI>^@#AN#J+{td}=(8UGi63rO#_tWN=N=ViG zeiQ^}LPgDFs?Xx6`#P2{RniMY^Yf#xk$`3>&hs@=T8>8$vudtFQjKJ~xV4V&DlzDs z@<5Pcm6qwQC9lJNlgk1e&~=l`N3hW(BXjyKWNK#3?DO|C`!V_M-pt7?PHRNd%$dW= zts0rR+R9vR&!L=N?Y<z~fvmdeo*`FFqk#2>!^=rjtRG8;Ead!K?NRDBI^P^0O044J zx@~-0*bt&bX6prs*;s6?gM*|lN}HrEw%7NVRDYutjYkVBnOyKJ-$Im)Hu{DM&S`NB zEmYoun*iCu5B))E3@t-QwT2>}Q;G!S^w={83E&pqo`00NK-2wMA+xz%BUu3`a)6>D zXxQfD0(rJ}vf><YYJdTxz59Z|{{@5ao<X?#213ywxEZvQA-Qk#|BuC=4MNFSd}u5_ zc*7$4>|KfhJt%26D}m?UoNf{0$Dfz{Xd9H)zfk@&@}KCdyMSRz4LG%UIU_kId<%+b zJN?B}$aA5D)?qYY9q207I#_qNVmfB`O*!@0Ae@MZC9w)|da%8x0d$9u`(Nd2y<xBn zf(lc?A|W`wKNSq%4N@bY2q#iZ!g#EP(>61<yZEYg&^^khP(Ff8O{Uk&hhr(h5<6L0 zZZD|tZ8)Vi%%Sv7xwCQ}+u&Kh55{u8+o=ayjiYBzo=BV`>CKJmX)k!W)@`S*>7rw3 z11!$GIow2sFz{~R1y*a`C}rc6UtGoR7gxc{T~v7+#Z3^tE4t7fvWEKtP}WT*eknG- z!S-s$R9F3_x)L*S8(ygv2vYQD)$(AR>`=B^uby{c)Z`lXV3jyJArNpN2?J%E&fUjp gl!u2Zmpm|2(loC3a1`_LP&`7i6t?A#<yE}WKasJgV*mgE literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/files_and_folders.cpython-37.pyc b/iexcode/instruments/__pycache__/files_and_folders.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d07f6e1e79cca22aff31195d4481cfe94d9ffbaa GIT binary patch literal 7213 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/gate_valves.cpython-37.pyc b/iexcode/instruments/__pycache__/gate_valves.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b226d256630f6017a49ca7160868608128964e54 GIT binary patch literal 1193 zcmds$&2G~`5XX0ICvlwyid3mcT=rHhMM*)u5FrFDp+ZD$q?QQf5;@*&V(^!C*D0~e ziC#H#fEVDEEOFx0SK!3VhKh&-55Ooh`{CK~`uCq4v|1hk`~KzgbYK(m0~a?2Yw{Fa z?Lpv#)0iZ5OcQHtQNk^5Uy-rRJznGWD>`;~lQ*~vvl`zxCI03$oFcwe)xuyVG!A*8 zL)=88;12j9xY~liDVF5USNj9GBv7DM#vTKcLKiYsEMPneb(E!nT(az(>A7IDKnu1A z;)PJ`Jd=<!ixwhfQyHY;+%xA3B{-WdSr}(ZOiVHhQqELxE+$Gt1(P;*jz$mmOx*uQ ze7mAyMR`Sq#B2I=Y#)u3nVazQRrj?LQgz=ZVK^5m?dD58&(g_7Cf}(%2u1g0|4o>2 z(T&9W)tD+Ri$tVab@Ae>yQogStXiuJ|K72Yi3@?a7PY8r$qksFg%)ms;aCSZ>`)vH z^e)K`hyo4_QIby%C6`pTHRTQS%-ta$DF}z+cx7Tf+AvXnp=9@W5yfts;kdu+w^@I9 z%F+z39>xKvAy#CEQY@mZQ1P<I4h`MsBh;e3zA*18n-3pFymv5wLcP-K9S%-T`<>CV z!8P{5Su}iMTE)2tB3(6fl!&a*<u=xd(%G8KJyzPCPUqOSBvPtsxr|Z`U1lAPs>VXd zDTw?>C5aTPrV)K&KK2f5D1=Aqcd0`qw%&gOcB6MP>>ndniGS-GuCfUWxe1}%KFLM; zPo%O1d4Ts8p1F+y`+pS-O>adrgF^S<$vRAgd+gIQ{MD<P$fHn6>{cU|>0$$1V&Ym| Y^9EDgeVk;xh{Y4^3sZW_a&LLwFA!}UF#rGn literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/hxp_mirrors.cpython-37.pyc b/iexcode/instruments/__pycache__/hxp_mirrors.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8747a50a2eed658297f8b7cb72d64d78f027bda7 GIT binary patch literal 1740 zcma)7TW=dh6rP#AczsEma*35nR4Wh!OO>t0p%M{7Ra0(4jjE*3)Dp5bJCk^ccWq|Y zX}q!}r0^4X<N+S}G5mpj;Dx9Bg*@@iu|oq?f?dry-^|XA&wl4S#}8LlY7Ez-?|(b| z-DB(zJGr_9zyl0(03(^?LzeO$=N5~hNS&UOx;>ZX&d^K!o=?~vR??ss0DIE^f%U4g zA_MrEtjZdEUDjm-z9CokS?k6HPG>D%^2aSvdT}(K<klUl9E)#`b&}=%7m?1A?6{vQ zWAFqIYH3&Z4|SBqgP7YBa5)(G2)2V^K82B73h7AqCuh!QY{pM2x}77eoX_~r;um(# zn@svmc+RpnWMzh3>kMl_lg-6U>@!(Kh_&KzEySf&KGB&8^MMMJQ5+tPbQp!l$*Iah z+K?t%k90qaQnej!q}^yE?7(kbdA&i~dhXzCyh-*R*>1}zopejXbR&zRVm%w>IQ%pk zCNf-{8`^Wj_b+*?2sS@V<mU%ZwlDeiE?)MCrqUfICNJF+H8ohQUkj(#z)w<Ow8cUA zzcPIb99@3d`N}A5I^U*oJWwX<jHmfvl=WYX^obcqvFd#B$w54ls*|X*h0jc`C#lME z)3L`ce#f-O)3RdcINY}>xB<g_!37UQz;BCHu3yK>-|}TQQof!7<ZOmq%=vWl>~79& z_Rhs4_RXES$i<9Hr^#|>CMatzvc%5?XWy+oW6zL1&g`AL(zDro&N6X;+#-9GA|uw# z6k1s%PamF!Q6@t(UgU5%45Kq;YX3KhwC{;lr^#qyhSTkEA7}TGKzeXt^>|T%DS29W z?fvJwJ4JJ6pyHD-IU?mGCe)M6N_w~D5h<kyEgviG*w-BJtF4k-j=pIZ#Q0RN0Z_&k z<~6%sg$DZcVNFa;1BL|w2w&xIbD!(mnEyq0fXNC5(fuR972UQRBuW9)3&Mi1Ll~rE z-6agtfvJ3?N7$!zDxrBL7bu}+`N--_tXYhzpE+v_?=HQ!^#0OU*4TL+{i2R^HHtfn zJY47ZGnM!8sYGw#r}x@>55I=rT>9?PZ!P@?OMl<`lGh3+d=M7yyY24LCGVhMh;r?M z18q5L1w^x62QJ0aGmI%lQHnj_J>b3KU({QrQ`v3cEwc*4yg)Sg%Ui#Lwb(wemsN#r zsYL%>fSjGPITSDXiO?bR4rN2Z&^h&^0LZmSdZ5MsFIq|-Ynx$RwZ^J%w<WYKFA4)K z(_gg9e|`v+EV}J|Y&G<6;Q2hjT@(5}I%cI5T4@`5>8WuN8&WCJSH87u3hm@&Q)o}} zWSHcMG9==%`KmqW-;3yMOCX_Aob;2|LbNSPlh%We(vh4D)yI}d>Mae(9Bhl=R<Ih} F{1aG=kdOcX literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/logfile.cpython-37.pyc b/iexcode/instruments/__pycache__/logfile.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b49542070a25057382ecc6276ee31b7fe9f44a17 GIT binary patch literal 4639 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/m3r.cpython-37.pyc b/iexcode/instruments/__pycache__/m3r.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc9f6764e739a8d6050c03f89d8ed03dc58736b8 GIT binary patch literal 3609 zcmZ`*&2JmW72nwpa!FC2mi(2(o~Vf{*0SZ+MbHR#THA?@z%Z>+Y$7cR5G&4#+G@GW z%q;c6Qa}MGy%{~XXpt7^r9jbB59uWkdhb85x1xtuNUy#4(*E8o$w*GRi=FS;dGGgr z@6EeEIaxOFeD<@yZGApt82_No=&{iF0Y3Fd6v7ZJGCIs-jM`>ob}Y~8*q*KXR^)UF zUO~6*sMs)s<GG^Xm4cEeUNF2eY8Ul{D0vl8_9n$QL`6(~%)BWvC8j?%ylF8bW>L?G zIdKm4taw8#i1S!6CoYJK7&#{{iOZ<x#Vg`f)C=NGaaC}vI4`b=*To`Q7sPApM)jK~ zkl3iQY`QDMIBoi}-RlH#s;Xw@wET9EQq}FHShA2W+4p4}#_eV&Pznn$!M@f0<clAE zcIA^>$NkcG-VQ~xtKM4eV`JyrtNr47>Zb~gX+71sEm!NC%_mQ5{dpRA?038Vsvm{z zxJh%379)}HnD_|U2l&(!iWJBWjA!N%8!*Uj)=p>|0!%%8a=+OLrA%ZKB187#ULdy; z6=ZHVQDGV;@gEF1fz>Y_to%p?Qmy=?(`xMmDqiUxraMX8+)w1L>iVr<<^A_IT8Ri& z!r&mUW0lGrl@&;MvwN5oQPW8aB$|!FU}YvJQMafwdPoy3JuOKM$joX4<dfU=CNNlg zy53x0Ylbld^dl|asiJ~C*(4o=rO=|Ox|cAd2o{6MX_Vu`=pY)`htWoY>KQvS26U)d z>(8CWK0mk0q=l7L<~cZEeeLC=XU`l>+P;eALv39q;RHrY3xfvzsen%la1jl`j?B~; zm;+<Lgek1&#C{vXIQ_qr`S4k6Y%KD-Jo3{(`q5}~Trbx3^rpXmYR4w+fOXTb_0=tl zbv?ZnrmdY*dpvzg61T&&C&KvbzQwwpt|w7c)_c}uuoosh6&<ef#(v=MHeoM4Ba6_i z0pLogVM%Z16!B78TD3CUKL}N3?fH>c9&$47bzVld)By+d7LI@AsJb?j=g|A-Pv`&i z=<ogX`)A-Kh&8;o&>(mR2rj#(yr^Mj&rD{-^BA)*ww#Ymz*)wyzxn@_YkmosXVy1g zu<I|_4d|z(A(a%oQ_kUBIZFjOQeJ41P7_nKF3m0>e;w0GM@!pgF00^^#EG$IlPWGg zt&T)vVEoqjopA)Uz?I?KE(lAvZQ+OldPUuHMM;#=oA|YL#11P1M1)Jmk@=w!FWE-Q zcFQti2EC~%D$f{dOH5Mad~P0@n?`JIfR*6kO#k;9kavUaFb)(?cLF|&6^i#%Ao$iH zSNo(;b4&VhYiFs-w-ednBRYI|uU>n<_Gp#o)6MQ4?Nh7#U4Hi+zSKD2ckc3ARqn@v zFVzqE9qQcRPyF40tDX#SW~jIfI(`^K0ND$8B6(}akJ|wcQ!U;J;V8+!+xYO<$L;Em zfAQD<{OkI0`!+r5)^Yn+zocjWc(}$mZvUI&@fTRL|M{nvSAK&v`SvTvuqSI}Ok^zR z&&5f~eGaaMP**FdIsS0S@v5CUQL-P%%zYdjzDEI8I{1+!>)}U*d{0&yBPX@IFA)S} z2SNO;D0mC|)I5pexaJg_V)Kp(f=q!Tb4<RD@fJ<eLnNW^0Y3FT6yQAK8nOO@f!Kyi z09?1_6=5Hl@p<%|T|+L50(x%BM3IcIP3`t;8cT#QzP}T;c803*EuYMs#M=0|uZ}sO zttNxCC&@M-N3N>=(eRdoR;ym2u|b#F2}M778O6ze@yJ)u>Jw8qOiU<;G55I$w*6j| zjt{AtItFKszFGmOx%Po)Ha6r{`rAt=mI=jNipQ8%6o3tQ82ML$J`;X+4P#?Jqv$-2 z$`8>Y_y{_{L-0{tw%{S?3G%XGoc>DR#5}z5IR%p<e8=tI*Er<fF?60Gera;Xl}Kgy zmmvMeEYO?J5DiLio)EJdwW_6g;GH`aTRX{K(Db7y=bf3u^L0$qm)04l-Na9a!N_G( z<_w!L<r2nfC*)M)2um>0`I>D%M*}=#yCsUQfCG%9!~+qwlA3v+Zr9X8&lVQ(QP@aK z%;AVL9<hcah4F~3M;z=ZVuu4Wuw6?Yqy^#Tj2&158`q|@?Evahf1uH-YbK6~!;@{^ zLY^j*P%LwV=Z?Qj_#(C9OCu&z=&I`7PrH-?;ockNcRb}Vl!8MbNyFs}^WY9{OP|*9 zH0%U7^~PE@aj*4JFO)$8W8UP0;h7uF`uz`1h+kfLole&S-t1_;k)(d4*HG%yf3!3T z5MyCeTA`1Pk)Rze>*i?z_>!tZC!N)CeXmqkt3^rw%Un2ClhVtf`^cmhi>ySWin7hT zsW-_tu%TMWSemhqbacubjh9y<o%P|K1L*3TFFTw&LO8@PMo|m=sUTL-OHm6h_g&16 zU5-dKcDYq_z#{`U^1S8|(~%2T6S>%kQzJ&<y3<#kPL(KcE7p+x2MXXLqYe)OBoxq2 zruQgWmR0<`aq?%B$an31KTNYC87@JxZsoXC2#`Fr?(S*ikh56bgVVXDzQJ{X)(iXi zpQ7k)Ks5@?@@+bSQPx9o8_A?TnKIdnp4DoHl30*AK{sqEeQa)`*M{lyP26px^!a8l z4I?DGfzr2#clGPmeChT|Qr7R4hsFj?<qZtT6)GqTl5{QTv|FdcIwjRPi%vIm7|{Vy zJG{1&*5;I*b%@mvxOX~<=taRhWI=TeMa3jjyN=B$i*ZrfPT6)X*LEk$6YhdL<<696 F{|BB%T<rh= literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/mpa.cpython-37.pyc b/iexcode/instruments/__pycache__/mpa.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..706901ac024918e144fc9eece5186afcac389293 GIT binary patch literal 11343 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 C!VNS4 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/remote_controlers.cpython-37.pyc b/iexcode/instruments/__pycache__/remote_controlers.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6af0c322f5a4b610f11108737fbae1bc8057e3ec GIT binary patch literal 9051 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/resolution.cpython-37.pyc b/iexcode/instruments/__pycache__/resolution.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff69f443a3bdb693d5a4a10a67e75ff819044f0a GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/s29_temp_cntl.cpython-37.pyc b/iexcode/instruments/__pycache__/s29_temp_cntl.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb69f7e781be3eb592bbd3fe33c50cba8a604c99 GIT binary patch literal 1487 zcmZ`(&2Jk;6rY*>@Wytc9MTV{K+C~Md#Op<ORcsctt>SnF;UiO%U-PY&NSU-zdAc1 zsj*K++>okXD&SI%{0T^iKV&5&Bu<eK;>?Nn#!2d;FsqsO=FPtMe(z)E-r{1N!1(Ql z*TX+eLjJ(b*<iqY3bWjTg%eH_lG1>tEMOG(EHP3uFjFhAQaiBIT2KSI!OgEp;BbrE zKo@w8J3#Avf!BdHc*7@N^8_psk5yI_?hA#aI8>flS!E(bQQ7;cic?WJrIJrHw~*vf zm>?S=EF4H}nD=3p%djXiCS%GeXWaPK1UF`{AHVsAq9gMtj8#=b137r2(f|dI1@_Lv zEKEgE+j$sgfZ-=1F(LnKxA#gR%l5N0ik^xxYZoun(>xm-<?^5`!br3?yN{!si*_uI zXEXz_hpEU^*)G>^4U|ZWL6oWFdhw#Nb$~}TgsSmV*#tg<c|H)4js;H?q+8J`nKFoj z^+qeL&~0(IHFR5FxUC1B)^?}mdrV>iB%0N*=avS@ss<002s4ghV_0Xt7lD7rw*><2 z`!X=-`wdl$vnl<NPAL`}P#C?@2fO}b-}S?^NQB#m0EG<Ip%fkWtBL#B=*rrynDfq- zf8)k#r+<I7Gy8MHGb*c)ai%K!NiNe+NzB0<Si~kZVhhtI5#JeBtZYPK!GV#UWBnMU zvpgYEZYT!%GAU8eg4`OD704c7w<i=PQzoxrEXRM18MhDEODbJuDd-l^)qh_lQ#vt} zH8w!v91!^{=$Xf#j6oB2ufT$X7RJWAT35)#fPWmI=5zjtpaovL2G+EdUjXC6sm3`* z?o8P8n=|ReI>UClx50unwh<4ngXf!Y-ayY6oHzA3;foi0cr>fsQg3u^GmBLmCKr|O ze=9d)P}WQTE$fjS_&fW$Ams0LiN!g=5}qJ8df!*!5c=bSHrfCFI~+f+pL_^^o?7^Q zeRO@Vw{dT)>-Il){XTG;0r1`Jo$l^Ne{Z+@k?X6xC<J#y<@WD%JX<b-{;Z3zSM+U` z@P3iX2#2KyFA?@pDO3PYNL~!S3}r=!mBmG(!iq+f?z7cEleM`{eA|TjNh#OBCZQ{I ziJI^^v<Z9@xH|C2EyHRv2X+qdvvq~a_du)XH7iRLaa7KL;{C2nyowsB#6F(8zj*6) g<4f##ZD9`A+Z*ECPIG>kh`VU7MENqkYB>7)7wut7@Bjb+ literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/scalers.cpython-37.pyc b/iexcode/instruments/__pycache__/scalers.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66dfe7809dd855ff40fe37bc83daf9b7275f17a3 GIT binary patch literal 1015 zcmZ8g&2AGh5VpO$`Pn9=;(#hnmN?~-wA_d)egYI#v{9rMg!U3`ZKv6G*SpwGp;0O! zl`9VbCphv5JOnSWCnOGtSK!3h`6*%~dpw@mna|(M_L|KQf&6&){W$ax`e}`&fQ=r) z(stvB;}m5$!q}ckN-{6<GC%U2?WI9hi7Iej;}u?gkE1GY@EWhfsm8-Y)N1~QEU1Oc zV3g*$YLPNvaWOF^8N7sB(%b*_>7$iz4~`hIu^?9aupYzGw_!8r6mxt=)Gb41XoflY zK%l4x1^nrgkb-DoG&N&EHH%ZBG-dfjnna$^QLdarNUlw6l3Y?QMu`-hCX)8{-REWS zJT8hDAXU$WI?lD|w>%X<?b1tfRyLe_I8P3*XqY4uLoHYiSyc_<z9Q8C_2*gVg@*c_ z(~PmP(6Uopm~k$LZ*p~}i<pVd-qTks=c1E{w{tDEQIkwaqdN=q?c$;g=jS0ax(bQ4 zWd!*lR_mZwTv>=Ku6DuT_>ypP=@~ThXXpxeGI-`);Y)w!e<oj$+5(SfJn&)bonh58 zKKD$pyx~6Z8cbh1Vh)$QCenx#HRsPhe#PsOa3It2?}(yo9B0QuxfHSFG%I2{N>U>f zm6MDnBX^T%swPrGmt5fV=G}V<KZv%v^PJo5qrr63-tmLMqwYZEY`fc^WV<fWbaU^2 zNH7G{<lTO&qU;zeOH5gZ9!bHB;K~kG859cMQhGpoD^P1T&i1dYFZeFbqb9%|t_qc; zICs6BZ%zA!{eN!2(i^a$z{frbNe$QVI`Ogd_FF_%z<0FL_0rE`GhUz&MZ)wo%;?Ty w9NNkCIJ99@hSzhe-9WnjyAuC^vZJRgva+_5<$RKg`xq>3U)CTSq(Q>kUrUw+&;S4c literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/scanRecord.cpython-37.pyc b/iexcode/instruments/__pycache__/scanRecord.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a2f063245c768dd7a2f4c18e550b51ae6fa01c5 GIT binary patch literal 19907 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/shutters.cpython-37.pyc b/iexcode/instruments/__pycache__/shutters.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..63ecf2bc4e4cee714175f614eaa967f2b1e6e58c GIT binary patch literal 2889 zcmb`J-EJF27=~y6*N&4WaT*tBr42_|w6+Km5>Y|Ku1zCS$J8!Ad(f4$vu&2zYcn%$ znw2dfxu;wRXWRi-zzysvM>*jFIO6+e*N&4ER3X;#{O|0{Gw-}Jwbj);f#=Vken0-T zK*%4MnLipNzJNcEAfUu0RH15L8K|zRU|Q>I1KrgJhHJ>Y-Zux9YYlAIhP*+|*Tl_H zi`o$9X^!S0UZE?r0C9odq-*rXYsFoq>vRK3*61cJLVSa6bx3(T{xCo2g^|Ng#zF+l z=|$8zX1&NiSrk1RN4^M$5zn8)07+R%4BiidQCUq4zxO;47>&jP@+-6_0_Z}7gCMy% zVqqk_(;kb$=(#rtIEU6g_A!4<NYvraZ$lvD#HmJgYW%D#L@3mpDKJF~25QIkyaUg0 z6hzz+CxNp(?7XaNv*2Yo9P|F!o^u54yhkvzPBt;wBEH?O@9lni@L;dm=y*r%M$3EL z`1(ns)2)27e|WCID7vBZn@V6V`e`o|iG@=cjzwI;9$_91cIsrkRw|WNS!FuBml$%o zzYw+pIT`%*u=<1tj8}gc`2I=2qw45PoD3uHbjV)tQO^&m2aWIiAq}cw@G6ZXF4%Yw zM1ohdCE=CPS+aq%^|BHVA*{hyIDVLz>k3<i_&<|<Aldl+ArIaLZ5<mfvrW<7Sw>qw zJnA%-*)j`8*bUoF5Rl4j$gms)aS40-7px5T%>~jl1v`i&o0pKt(U+k7f(a5@Ffkyb z!e6Rw4-%-hnKH##g;;@DgIJxAiLyl|>K2)56VT!dmF-evsz*9Cr}{((En2ac5<3a} z7u*>}Qjc8p#29kVuB#J;iyMmtV$49k%j@WT=5*OOaH>wT*XKd1<aoQ$?toDDK&hS1 zM|<s~&O!I!sCBt~_l@Q4&PDk-g6=lU3ezA_He3yQ!(i9dpdD;noI+Aq9?%kO$+>um zb2dxpjjuyMOk2TULA4cA)m63$Wee!IpYX#HdJPh<?h7&_Q!)Yg0QN3yaaE`@G6Sa* z+KfP`<1#4ZM-}Syd0iXw--6cxd^3J3;n!36IJh}aCj|VkHSRj$Gbb8~%q&9jzI;Ol z%N`-SxF+OgTza&B&~iEtpLDy8$BvZp17~Mv=Nz>d7dx4wz}KSTsr*Q*Y{;3XYDKNd z9m!B}0Dl7u7Pi17*3u=cavkKp{utV3XjR2Tv=HXig2J|-rs-ps_@H$z*gk~}RVJha zVlI)XO0`8=6Cmap$Qwhb0|`q}e*&U5q^OOF!rY0$n$(I^YEN|mJ<(i=O!N}LwU{9H ze8ydl^#2w;Zjd<F-cP-`1B|`e;ZgmogVvW`t+D?I-5Kg@S^YUe=r&y)J=ry-A<?*S zFOxCq089qy$OS2ziJ6{hiS;sI$6#NsHhQ^_K`k*;NxyNCZl<p-j5lox9!5B>1~Cet z{B>9))V>Z&1y=kHl>4ZBd@x=da(5wvwliOw^r)j|cGy|6+)kO(oMe8O8ck}={+Ka1 z8Lphq*U!Gxplk9J&%`Rb0fO1-Ckel`N859cj#utw{Vg4QDPo0f!|bk#eCrB7>C)dr z!_1{`DePUyEpXwwE^yT#Bgj+%d2UNdf_zbq(Fa1M;L{*>4gPRjpeD64h_);T@yeS+ z9w4s7DMZu?cizm<xdgG#=NE{}3{!}=<_w%(p9MX7CLI8PEaTn0e!KXm6VS$i;R$$! zt|pa~ItrfiplC$wu4O9sGTmPBmEslTXox<%$K?go(iL4<Q-S+?P}V9d&GLF;c^)15 zo|ou&*^#D|7{Ms?dAf(>_2y1?MX7`rSCq<Fg#A#20cX<laOqOx!?QPaMBFFoOmL;; mXO~nW2QU55zCRe!aX<JRx2@b=azCl-mYsuN(JtnTcK&Z&Lc6s9 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/slits.cpython-37.pyc b/iexcode/instruments/__pycache__/slits.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d3c0b6595d26512aaace68743436f4a86bc9707 GIT binary patch literal 13354 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 DfQ5o3 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/spec_stuff.cpython-37.pyc b/iexcode/instruments/__pycache__/spec_stuff.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..233507689e4b15634113b3e21e2bbb3cc81c6e5a GIT binary patch literal 599 zcmYjO&59F25bmD*jUl^;2zxD0l7rijn?Xd}jj)1YF)nB>p)*~>jG5^fx_gKjW>5MK z9()2{X~5Gyg10?cokZAz@2jtVs4lACo}3IB*`HrO*LR$;J0JYFfW#Yu9T6#}_z$*Z zDp;^_C+bkjTbJPV1;H+f8m{;zFsCZG4RqK9s&mF}1J!NV8S}acDDF6>dJRS2IcKE% zM?ZLw3y--+-YZ704(BajFMA6o<M^J7`k6?I9K{-CZYL8ld3kktvzUDpw!}y%>7=Mn zhl}gk1;j<B&`hDe{8Ch~O|uL%q5@YyWKcRhc7W6hEIO!4a0x;wT>`n(mCUjoq#FFR zX*~e4NJ0J73ys@2%@E!Z&1H`D^G^j>Cym>;Lu}9{Q#cpvREv!)OX)X-7x9?4z0#!G zjk|5fRJ!dS_FRTH7L6aZ?0?u@(+<Cx>C<iR-wR){;%gp730f`6ol6S6+7@PGOBtcK znthH6g(Ago2d}L&RgT)({asepRdIY??%L7eF}w2T@?UMA5HTL|eh~7<d_>B_gp{3+ Y4}QPK0~%1R4PWJjsxo}-r~D<be~m4g!2kdN literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/staff.cpython-37.pyc b/iexcode/instruments/__pycache__/staff.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c323fc27e5c18ca6a135a8417e28121eee33a507 GIT binary patch literal 2083 zcmZuyPjk~o6qhW?HX<Q`8UoC8MxD+8rjw{~=yWiU;>3n#AZaG1Y0*qaRkZ6^pnvSH zg6*+S@G&@)V?Rv4fUZ5|+*?n5Z)K9WK^nc?_j`Z#Y4v`qjrn;~f$R3izYexx_BSe@ zE)6D+;o<8bh(gp*iBw08N=_-#98E>66zY-T7*W|NM-`_6m`3#Xic=*QNSRdLtIh>d z?J9Ni7;-3eEw6+D7bIZ05xtg!-;WXk2K`kM#CbX3y&$A{nGV2@`znUPRfflaXA>Si z06|nzBHE!gQHee&4Za<#ZRNK+6HO@N(pVcS@3e_7)Up0fRTWXfk=KM)=x=JjD8!f; zV}q2(%927VOUn0()T`5R28TQb<{=mGO-BEi7F?T*dV+FG^r^+N*n*d#zDQUASTA7` z?<HZFyba>RrVJsT@Z84zfE>)K{;Wz&srz0SVyci(=C<~BJKa0CyTa@BZr2}7(Pv(o zdM9WRma-rgmY@S+G5RKhs)!{L3pP3E4U1~|F!uYF&yrYp2cM$oQIKRj95$^da!KyO zVoYSIEXFZUi$62>&&<iFX2(?6ym23WZ8}I3ChXCgX_{FI5ItSpLQ{~$62TpL98*Sy zCpC?>@7k-@-RW&-oY@CKY%{FfTK0~t<z+hX!;H{Je!{5rxrI*(Cidue>vfPer58+W z-V>>vW(PJdO;Ew6<JP)m+5IG<_5t-G-e`b*t8L>(NxSW*A_4DZKVuBO`7&7vlS8Yq zerq*X&7Hc=utjr2egam3IYSQB%ee;L&Z4a7OrJOg;glUY6*RPg6ME{2ey)ju_)}r? z5c+S&{ux}j{VMYPKIO5U4n;qS-M0yQ%~Q{(_EzUtKOxi(U_;~>bHTER#)8`%+hrvk z<_kr`a2F8#rd7zzuY*wLO;uM-bxy6qJ!k0ZB8-bFTL9aO8TwrWMAEMhlRyFD0R;nf z0yHE#0V<ISLE4+-BAFxeAIynHE|CSYNG_8r<O@<GS3guHI=M!c$aQk#g9e0C{{Xsv z1Zv5db}}=H8PM(K=%s8xMn%SAZh1g7A6v&`Lo4aY1|qcQWudSL6_SFy=;>W{+SBc+ z$!!<DGUqYA<Y+-Mvgv!P-!*~LWVBcS-7pZVKZ?f6<~gK2gY2L4L1f0KaW0QUW_`}( zX%u;;1!6sGn|mAkXBWRGgWz~=!mAuVK=Ba8BNTXUAOC>jF^U$74HWnpN0*A%-0!Zo zR`y%ZPxVHjPnX^AuC+F19q!HOTf0;J%iUHJ3DfLucHNEVKW#qS+Ii}3bXwao{M6k3 zjJaN6HK>YRMR5(q5(?~5b_2yt5Th$tbCkqMv$NCLd-~Gt?v5^h!nU{en!78wK_}4D zy%z;k*N)qe;JAb035dFymy1&g=xGd?H{^L08py@=a>^tCE^%jp!=`iBODV91A#mjR zSr)mxp9ulPbmp)c7XrN?<dU<6xCCqDHzWbkD*ICAE?V~+F!OsLl(Jr`Y9;mm_bgUu z)shKRHC6T%#O>787j-i)C7hK&l_w3;VU|}CzJdpdJdGIsIg7IhyIqE!DH9JFCTp!e VgkMA!(slgha*V2}wQ}|A{{WDAH3a|w literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/storage_ring.cpython-37.pyc b/iexcode/instruments/__pycache__/storage_ring.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..efa0198e6e77c11ece2df7d1e27b632650dc0c14 GIT binary patch literal 719 zcmZXSO>Yx15Qc4MHwjBpi4zw<zUIJgB@hRMD$)w1N~NkA@u9t}Hulgp*)MD-X(Bj4 z_%r+%{=i;2<;ay2<4r1%n8>s5SfiQem2*5C1_Z<Z@?-jo67tg(H^ERm$73F2k|ZXQ zN|w`t#f&=L$vcG?d#(0zzv#wY)V}O~BXLjmCS=rKhr!EvDXgkW!zHA-DzErb*^Ju^ zxUrQ^uYhZJTQ5uyY;hAA(Z&~eZ=EnXK#k(IOf96Pw5kB%x?CVJ>EJoR(^6iC7RfgB zGx<a$n-P7_BIkxu@{w64J=@ukyTsKOc=Hx%k+Qq~8gPtPRjH83{Ld!lT0?0$?&nht z>CEtD24&mW!rwF~!_#TC0RH5V7vpy7Nd^M@cYaelHyjqASNzc-Hz2A~nzn6yXYzD% zxt}vW4d0B<wijq?`vBAW;26?^<Me4NX8hs#$kXn1v@_rMH8NQ3p&rqgO)kF^-N)kh zS@ha~HqnPdhzv{_)hnA-WwNaF%+#rX==8-qQAvmtT<^Z6v3g#hR}<}DCvgVDdesb< zsj|uYN+(l<8Q=or67d=B<1b(bRJ%XZ`KaIYl0;S_Ng5w&B}@z3c<$|X$oKaPL-luI oIJYWSR)J})w6j_t|D;<uzv(|KDml;LsEd_xPCYuH12$lP0GvF&O#lD@ literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/userCalcs.cpython-37.pyc b/iexcode/instruments/__pycache__/userCalcs.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..580ff6a9df83ae20d6aa1f0833e6156dee94d33f GIT binary patch literal 4471 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/utilities.cpython-37.pyc b/iexcode/instruments/__pycache__/utilities.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d801bc7a5477c18181ad534cbc761ccf908fe29 GIT binary patch literal 6980 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/vortexs29.cpython-37.pyc b/iexcode/instruments/__pycache__/vortexs29.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac73f08f263bedd80815a13b5b4a6dae8cf422fb GIT binary patch literal 3759 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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/__pycache__/xrays.cpython-37.pyc b/iexcode/instruments/__pycache__/xrays.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d7ea418488f9414d9abefdb6532e7066cd55956 GIT binary patch literal 18992 zcmeHv>u(&{m0wr≶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 literal 0 HcmV?d00001 diff --git a/iexcode/instruments/backups.py b/iexcode/instruments/backups.py deleted file mode 100644 index 05d3c3a..0000000 --- a/iexcode/instruments/backups.py +++ /dev/null @@ -1,339 +0,0 @@ -def ARPES_scan_before_sequence(**kwargs): - """ - writes the user string sequence to happen at the beginning of a scan - returns before_scan_pv = pv for userStringSeq for before scan - - **kwargs - seq_num: userStringSeq number in ioc => 9 (default) - - Previously: BeforeScan_StrSeq - """ - kwargs.setdefault(seq_num,9) - seq_num=kwargs['seq_num'] - - before_scan_pv,before_scan_proc = userStringSeq_pvs(BL.ioc, seq_num) - - #clear and write the before scan user sequence - userStringSeq_clear(BL.ioc,seq_num) - caput(before_scan_pv+".DESC","Before Scan") - - #sequence put CAs in passive - ca_list = ARPES_detector_list() - for (i,ca) in enumerate(ca_list): - ca_pv = Keithley_pv(ca[0], ca[1])+':read.SCAN PP NMS' - caput(before_scan_pv+".LNK" +str(i+1),ca_pv) - caput(before_scan_pv+".STR" +str(i+1),"Passive") - - return before_scan_proc - - -def ARPES_after_scan_clear_sequence(**kwargs): - """ - writes the user string sequence to clean up after a scan - returns clear_scan_pv = pv for userStringSeq for before scan - - **kwargs - ioc: ioc where the string sequence lives => ARPES_ioc (default) - seq_num: userStringSeq number in ioc => 6 (default) - - Previously: ARPES_Scan_Clear_StringSeq - """ - - kwargs.setdefault(seq_num,6) - seq_num=kwargs['seq_num'] - - - #clear the userStringSeq - userStringSeq_clear(BL.ioc,seq_num=kwargs['seq_num']) - - clear_scan_userStringSeq = BL.ioc+"userStringSeq"+str(seq_num) - - caput(clear_scan_userStringSeq+".DESC","ARPES_Scan_Clear") - caput(clear_scan_userStringSeq+".LNK1",BL.ioc+"scan1.T2PV PP") - caput(clear_scan_userStringSeq+".STR1","") - - ca_live_sequence_pv = ARPES_ca_live_sequence(**kwargs) - caput(clear_scan_userStringSeq+".LNK2",ca_live_sequence_pv+" PP") - - caput(clear_scan_userStringSeq+".STR2","1") - caput(clear_scan_userStringSeq+".LNK3",BL.ioc+"scan1.CMND PP") - caput(clear_scan_userStringSeq+".STR3","6") - caput(clear_scan_userStringSeq+".LNK4",BL.ioc+"scan2.CMND PP") - caput(clear_scan_userStringSeq+".STR4","6") - caput(clear_scan_userStringSeq+".LNK5",BL.ioc+"scan3.CMND PP") - caput(clear_scan_userStringSeq+".STR5","6") - caput(clear_scan_userStringSeq+".LNK6",BL.ioc+"scan4.CMND PP") - caput(clear_scan_userStringSeq+".STR6","6") - - clear_scan_userStringSeq_pv = clear_scan_userStringSeq+".PROC" - return clear_scan_userStringSeq_pv - - from IEX_beamline.IEX_beamline.instruments.IEX_VPU import ID_SP - -def scan2D(InnerMotorList,OuterMotorList,mode="absolute",settling_time=0.1,**kwargs): - """ Scans two motors.using the motor name (e.g. x,y,z,th). - InnerMotorList=[name1,start1,stop1,step1](x in the plot) - OuterMotorList=[name2,start2,stop2,step2](y in the plot) - name = 'x'/'y'/'z'/'th'... - Logging is automatic: use **kwargs or the optional logging arguments see scanlog() for details - """ - mybranch=CheckBranch() - if mybranch == "c": - Scan_ARPES_2D_Go(InnerMotorList,OuterMotorList,**kwargs) - elif mybranch == "d": - Scan_Kappa_2D_Go(InnerMotorList,OuterMotorList,**kwargs) - else: - print("Not yet implemented") - -def scan3D(list1,list2,list3,mode="absolute",settling_time=0.1,**kwargs): - """ Scans two motors.using the motor name (e.g. x,y,z,th). - listn=[name,start,stop,step] - list1 is for the inner loop -> fast (x in the plot) - list2 is for the middle loop -> slow (y in the plot) - list3 is for the outer loop -> slow (y in the plot) - """ - scanIOC=BL_ioc() - mybranch=CheckBranch() - if mybranch == "c": - m1_RBV=ARPES_PVmotor(list1[0])[0] - m1_VAL=ARPES_PVmotor(list1[0])[1] - m2_RBV=ARPES_PVmotor(list2[0])[0] - m2_VAL=ARPES_PVmotor(list2[0])[1] - m3_RBV=ARPES_PVmotor(list3[0])[0] - m3_VAL=ARPES_PVmotor(list3[0])[1] - elif mybranch == "d": - m1_RBV=Kappa_PVmotor(list1[0])[0] - m1_VAL=Kappa_PVmotor(list1[0])[1] - m2_RBV=Kappa_PVmotor(list2[0])[0] - m2_VAL=Kappa_PVmotor(list2[0])[1] - m3_RBV=Kappa_PVmotor(list3[0])[0] - m3_VAL=Kappa_PVmotor(list3[0])[1] - else: - print("Not yet implemented") - if mode == "relative": - current_value1=caget(m1_RBV) - abs_start1=round(current_value1+list1[1],3) - abs_stop1 =round(current_value1+list1[2],3) - current_value2=caget(m2_RBV) - abs_start2=round(current_value2+list2[1],3) - abs_stop2 =round(current_value2+list2[2],3) - current_value3=caget(m3_RBV) - abs_start3=round(current_value3+list3[1],3) - abs_stop3 =round(current_value3+list3[2],3) - else: - abs_start1=list1[1] - abs_stop1 =list1[2] - abs_start2=list2[1] - abs_stop2 =list2[2] - abs_start3=list3[1] - abs_stop3 =list3[2] - step1=list1[3] - step2=list2[3] - step3=list3[3] - Scan_FillIn(m1_VAL, m1_RBV, scanIOC, 1, abs_start1, abs_stop1, step1, point=None) - Scan_FillIn(m2_VAL, m2_RBV, scanIOC, 2, abs_start2, abs_stop2, step2, point=None) - Scan_FillIn(m3_VAL, m3_RBV, scanIOC, 3, abs_start3, abs_stop3, step3, point=None) - - Scan_Go(scanIOC,scanDIM=3,**kwargs) - - - - - -############################################################################################################## -############################## Move Sample ############################## -############################################################################################################## - - -def Print_Motor(): - mybranch=CheckBranch() - if mybranch == "c": - x=round(caget(ARPES_PVmotor('x')[0]),2) - y=round(caget(ARPES_PVmotor('y')[0]),2) - z=round(caget(ARPES_PVmotor('z')[0]),2) - th=round(caget(ARPES_PVmotor('th')[0]),2) - chi=round(caget(ARPES_PVmotor('chi')[0]),2) - phi=round(caget(ARPES_PVmotor('phi')[0]),2) - return [x,y,z,th,chi,phi] - # print "x="+str(x), " y="+str(y)," z="+str(z), " theta="+str(th) - print("\nx,y,z,th = ["+str(x)+","+str(y)+","+str(z)+","+str(th)+","+str(chi)+","+str(phi)+"]") - elif mybranch == "d": - x=round(caget("29idKappa:m2.RBV"),0) - y=round(caget("29idKappa:m3.RBV"),0) - z=round(caget("29idKappa:m4.RBV"),0) - tth= round(caget("29idKappa:m9.RBV"),2) - kth= round(caget("29idKappa:m8.RBV"),2) - kap= round(caget("29idKappa:m7.RBV"),2) - kphi=round(caget("29idKappa:m1.RBV"),2) - th= round(caget("29idKappa:Euler_ThetaRBV"),3) - chi= round(caget("29idKappa:Euler_ChiRBV"),3) - phi=round(caget("29idKappa:Euler_PhiRBV"),3) - #(th,chi,phi)=KtoE(kth,kap,kphi) - print("\nx,y,z,tth,th,chi,phi = ["+str(x)+","+str(y)+","+str(z)+","+str(tth)+","+str(th)+","+str(chi)+","+str(phi)+"]") - print("x,y,z,tth,kth,kap,kphi = ["+str(x)+","+str(y)+","+str(z)+","+str(tth)+","+str(kth)+","+str(kap)+","+str(kphi)+"]") - #print "\ntth,th,chi,phi = ["+str(round(tth,1))+","+str(round(th,1))+","+str((round(chi,1)))+","+str((round(phi,1)))+"]" - pos=[x,y,z,tth,kth,kap,kphi] - return pos - #elif mybranch == "e": - # print(Get_mRSoXS()[1]) - - - - - -def Move_Kappa_Sample(ListPosition): - """ListPosition = ["Sample Name", x, y, z, tth, kth, kap, kphi] - keeps tth fixes - """ - if not isinstance(ListPosition[0],str): - ListPosition.insert(0,"") - #tth=round(caget("29idHydra:m1.RBV"),2) - name,x,y,z,tth,kth,kap,kphi=ListPosition - print("\nx="+str(x), " y="+str(y)," z="+str(z), " tth="+str(tth), " kth="+str(kth), " kap="+str(kap), " kphi="+str(kphi),"\n") - caput("29idKappa:m2.VAL",x,wait=True,timeout=18000) - caput("29idKappa:m3.VAL",y,wait=True,timeout=18000) - caput("29idKappa:m4.VAL",z,wait=True,timeout=18000) - caput("29idKappa:m8.VAL",kth,wait=True,timeout=18000) - caput("29idKappa:m7.VAL",kap,wait=True,timeout=18000) - caput("29idKappa:m1.VAL",kphi,wait=True,timeout=18000) - print("Sample now @:",name) - -cts: integration time for scalers and mpa/mcp => 0.1 (default) -kwargs.setdefault('cts',0.1) -Kappa_cts(kwargs['cts'],verbose=False) - -kwargs: - mode: "absolute" (default) / "relative" - scan_dim: 1 (default) - cts: integration time for scalers and mpa/mcp => 0.1 (default) - - kwargs.setdefault('mode',"absolute") - kwargs.setdefault('scan_dim',1) - kwargs.setdefault('cts',0.1) - scan_dim = kwargs['scan_dim'] - - if kwargs['mode'] == "relative": - - - -kwargs.setdefault('run',True) - -if kwarg['run']: - BL.mda.go(kwargs['scan_dim']) - -run: True/False to start the scan => True (default) - - - - - kth_val,kth_rbv,kth_spmg,kth_pv = motor_dictionary['kth'] - tth_val,tth_rbv,tth_spmg,tth_pv = motor_dictionary['tth'] - - - kwargs.update("positioner_num",1) - BL.mda.fillin_table(scan_dim,kth_val,kth_rbv,kth_table,**kwargs) - - kwargs.update("positioner_num",2) - BL.mda.fillin_table(scan_dim,tth_val,tth_rbv,tth_table,**kwargs) - - tthdet - - - -def scan_reset(scan_dim=1,**kwargs): - """ - kwargs: - scaler='y', for Kappa IOC only, ARPES ignors this keyword - detTrig=2, for ARPES/Kappa IOC, used to clear SES/MPA trigger - - Previously: Reset_Scan - """ - print("\nResetting "+BL.ioc) - - #Clear Scan Settings - for dim in range(1,5): - scanRecord.clear_reset(BL.ioc,dim,verbose=True) - - #Setting Detectors - detector_dictionary = ARPES_detector_list() - scanRecord.detectors_set(BL.ioc,scan_dim,detector_dictionary) - ARPES_detectors=ARPES_detector_dictionary() - if BL.xrays: - ARPES_detectors.update(xray_detector_dictionary()) - scanRecord.detectors_set(BL.ioc,scan_dim,ARPES_detectors) - - #Set the Detector Triggers - - - if scanIOC == 'Kappa': - caput('29idKappa:UBmatrix',[0,0,0,0,0,0,0,0,0]) - caput('29idKappa:UBsample','') - caput('29idKappa:UBlattice',[0,0,0,0,0,0]) - caput('29idKappa:UBenergy',0) - caput('29idKappa:UBlambda',0) - caput(pv+".T1PV",'') - caput(pv+".T2PV",'') - caput(pv+".T3PV",'') - caput(pv+".T4PV",'') - caput("29id"+scanIOC+":scan1.CMND",7) - caput("29id"+scanIOC+":scan2.CMND",7) - caput("29id"+scanIOC+":scan3.CMND",7) - caput("29id"+scanIOC+":scan4.CMND",7) - if scaler == 'y': - caput(pv+".T1PV",'29idMZ0:scaler1.CNT') - caput('29idMZ0:scaler1.TP',0.1) - print('Kappa scalers are triggered. Counting time set to 0.1s') - #Reset_CA_all(); Reset_MonoMPA_ROI_Trigger() - else: - caput(pv+".T1PV",Detector_Triggers_StrSeq(scanIOC)) - print('Kappa scalers are NOT triggered. Triggering StrSeq Kappa #8 instead (empty, to be filled).') - #print("\nDetector triggered:", Detector_List(scanIOC)) -# if scanIOC=='Kappa' and scaler is not None: -# print('\nDo you want to use the scalers as detector trigger? (Note to self: to be modified (FR))>') -# foo = input() -# if foo in ['yes','y','Y','YES']: -# caput(pv+".T1PV",'29idMZ0:scaler1.CNT') -# caput('29idMZ0:scaler1.TP',0.1) -# print('Scalers added as trigger 2') -# print('Counting time set to 0.1s') -# else: -# print('\nScalers not triggered. To trigger use: \n\n Reset_Scan(\'Kappa\',scaler=\'y\')') - caput(pv+".BSPV",BeforeScan_StrSeq(scanIOC)) - caput(pv+".ASPV",AfterScan_StrSeq(scanIOC,scanDIM)) - caput(pv+".BSCD",1) - caput(pv+".BSWAIT","Wait") - caput(pv+".ASCD",1) - caput(pv+".ASWAIT","Wait") - #SaveData - caput("29id"+scanIOC+":saveData_realTime1D",1) - #Check that detector and positioner PVs are good - sleep(10) - Scan_Check(scanIOC,scanDIM) -############################################################################################################## -############################## ARPES & Kappa scan ############################## -############################################################################################################## - -def scanMesh(InnerMotorList,OuterMotorList,**kwargs): - """ - InnerMotorList=[startH,stopH,stepH](x in the plot) - OuterMotorList=[startV,stopV,stepV](y in the plot) - - Raster/maps the sample with outer/inner loop being the vertical/horizontal direction: - - yz scan for ARPES - - yx scan for Kappa (sample is assumed to be normal to the beam i.e kth/ksap/kphi = 147/134.7/57) - - **kwargs defaults - mode='Absolute' - settling_time=0.1, - scanIOC=BL_ioc - Snake; coming soon - """ - branch = BL.branch - startH,stopH,stepH=InnerMotorList - startV,stopV,stepV=OuterMotorList - if branch == "c": - ARPES_scan_2D(["y",startH,stopH,stepH],["z",startV,stopV,stepV],**kwargs) - elif branch == "d": - Kappa_scan_2D(["y",startH,stopH,stepH],["x",startV,stopV,stepV],**kwargs) - else: - print("Not yet implemented") \ No newline at end of file diff --git a/iexcode/instruments/bakeout.py b/iexcode/instruments/bakeout.py index 979393c..94dfd1a 100644 --- a/iexcode/instruments/bakeout.py +++ b/iexcode/instruments/bakeout.py @@ -4,8 +4,8 @@ from os.path import exists from epics import caget,caput -from .files_and_folders import get_next_fileNumber, check_run -from .scanRecord import * +from iexcode.instruments.files_and_folders import get_next_fileNumber, check_run +from iexcode.instruments.scanRecord import * ############################################################################################################## ############################## Scan Bakeout ############################## diff --git a/iexcode/instruments/beamline.py b/iexcode/instruments/beamline.py index c4d045a..6817162 100644 --- a/iexcode/instruments/beamline.py +++ b/iexcode/instruments/beamline.py @@ -8,14 +8,7 @@ import numpy as np from time import sleep from epics import PV -from .IEX_endstations import * -#from .logfile import * -#from .ARPES import ARPES_log_entries -#from .Kappa import Kappa_log_entries -#from .IEX_VPU import * -#from .VLS_PGM import * -#from .slits import * -#from .m3r import * +from iexcode.instruments.IEX_endstations import * ############################################################################################################## diff --git a/iexcode/instruments/cameras.py b/iexcode/instruments/cameras.py index 56ac0ce..1436d9b 100644 --- a/iexcode/instruments/cameras.py +++ b/iexcode/instruments/cameras.py @@ -1,9 +1,9 @@ from epics import caget, caput -from .userCalcs import userStringSeq_clear -from .AD_utilites import * -from .IEX_endstations import * +from iexcode.instruments.userCalcs import userStringSeq_clear +from iexcode.instruments.AD_utilities import * +from iexcode.instruments.IEX_endstations import * def cam_pv_dictionary(cam_num): diff --git a/iexcode/instruments/current_amplifiers.py b/iexcode/instruments/current_amplifiers.py index 6a72551..5df2d30 100644 --- a/iexcode/instruments/current_amplifiers.py +++ b/iexcode/instruments/current_amplifiers.py @@ -2,8 +2,8 @@ import numpy as np from time import sleep from epics import caget, caput -from .userCalcs import userStringSeq_pvs, userStringSeq_clear -from .VLS_PGM import mono_energy_get +from iexcode.instruments.userCalcs import userStringSeq_pvs, userStringSeq_clear +from iexcode.instruments.VLS_PGM import mono_energy_get def ca_detector_list(branch): diff --git a/iexcode/instruments/diagnostics.py b/iexcode/instruments/diagnostics.py index d22bca8..50814ae 100644 --- a/iexcode/instruments/diagnostics.py +++ b/iexcode/instruments/diagnostics.py @@ -1,7 +1,7 @@ from numpy import nan from epics import caput, caget -from IEX_endstations import * +from iexcode.instruments.IEX_endstations import * ############################################################################################################## ################################ default positions ############################## diff --git a/iexcode/instruments/electron_analyzer.py b/iexcode/instruments/electron_analyzer.py index 4427551..8cfbb77 100644 --- a/iexcode/instruments/electron_analyzer.py +++ b/iexcode/instruments/electron_analyzer.py @@ -13,17 +13,17 @@ import numpy as np from scipy.interpolate import interp1d from epics import caput,caget -from .IEX_endstations import * - -from .scanRecord import * -from .conversions_constants import * -from .xrays import energy, scanXAS_BL, BL_energy_tables -from .shutters import branch_shutter_close -from .VLS_PGM import mono_energy_get -from .files_and_folders import get_next_fileNumber -from .logfile import * -from .ARPES import ARPES_motor_dictionary, ARPES_motor_scan, ARPES_mvsample,ARPES_scan_2D -from .Scienta import * +from iexcode.instruments.IEX_endstations import * + +from iexcode.instruments.scanRecord import * +from iexcode.instruments.conversions_constants import * +from iexcode.instruments.xrays import energy, scanXAS_BL, BL_energy_tables +from iexcode.instruments.shutters import branch_shutter_close +from iexcode.instruments.VLS_PGM import mono_energy_get +from iexcode.instruments.files_and_folders import get_next_fileNumber +from iexcode.instruments.logfile import * +from iexcode.instruments.ARPES import ARPES_motor_dictionary, ARPES_motor_scan, ARPES_mvsample,ARPES_scan_2D +from iexcode.instruments.Scienta import * def __main__(): global EA diff --git a/iexcode/instruments/encoders.py b/iexcode/instruments/encoders.py index e45ab7f..ef7cb08 100644 --- a/iexcode/instruments/encoders.py +++ b/iexcode/instruments/encoders.py @@ -9,7 +9,7 @@ def encoder_dictionary_entry(name): d={ 'slit2B':("29idMini1:",[13,14,15,16]), 'slit3D':("29idMini2:",[26,27]), - 'ARPES':("ARPES:"),[1,2,3,4]) + 'ARPES':("ARPES:",[1,2,3,4]), } return d[name] diff --git a/iexcode/instruments/files_and_folders.py b/iexcode/instruments/files_and_folders.py index 6f1e48d..9710c1b 100644 --- a/iexcode/instruments/files_and_folders.py +++ b/iexcode/instruments/files_and_folders.py @@ -1,26 +1,3 @@ -""" -Utilities to set beamline folders - -# to caget path: caget("29idc:saveData_fileSystem",as_string=True) - -# Data folder structure: -# -# STAFF: data_29idb / 2017_2 / mda -## h5 -# mpa -# g5 -# SES -# tif -# mpa -# -# USERS (C): data_29idc / 2017_2 / user_name / mda -# h5 -# -# -# USERS (D): data_29idd / 2017_2 / user_name / mda -# - -""" from os import listdir,mkdir,chmod from os.path import join, isfile, exists import datetime @@ -29,9 +6,6 @@ import re from epics import caget, caput -from .utilities import dateandtime -from .ARPES import folders_ARPES -from .Kappa import folders_Kappa ############################################################################################################## ################################ Standard Paths ############################## @@ -51,35 +25,7 @@ def path_dserv(folder,run,user_name): dataFolder='/net/s29data/export/data_29id'+folder+'/'+run+'/'+user_name return dataFolder -def folders_startup(run): - """ - Creates the run directories for the following: - data_29idb - data_29idc - data_29idd - - calls folder_ARPES('Staff');folder_Kappa('Staff') - print text required to modify the rsynch crontab - - previously: Folders_Startup - """ - data_directories=['data_29idb','data_29idc','data_29idd'] - dserv="/net/s29data/export/" - for dataDir in data_directories: - path=join(dserv,dataDir,run) - print(path) - if not (exists(path)): - mkdir(path) - - folders_ARPES('Staff',create_only=True) - folders_Kappa('Staff',create_only=True) - - txt="\n\n\nupdate the rsync portion the 29id@nerdy crontab\n" - txt+="*/1 * * * * /usr/bin/rsync -av --exclude=core /net/s29data/export/data_29idd/"+run+" kip:/net/kip/sftp/pub/29iddftp/files > /home/beams22/29ID/cronfiles/cptoftp-currrun-d.log 2>&1" - txt+=("\n") - txt+="*/1 * * * * /usr/bin/rsync -av --exclude=core /net/s29data/export/data_29idd/"+run+" kip:/net/kip/sftp/pub/29iddftp/files > /home/beams22/29ID/cronfiles/cptoftp-currrun-d.log 2>&1" - print(txt) diff --git a/iexcode/instruments/hxp_mirrors.py b/iexcode/instruments/hxp_mirrors.py index a757fc5..0eb9050 100644 --- a/iexcode/instruments/hxp_mirrors.py +++ b/iexcode/instruments/hxp_mirrors.py @@ -1,7 +1,7 @@ from re import M from epics import caput, caget -from utilities import print_warning_message -from m3r import m3r_branch +from iexcode.instruments.utilities import print_warning_message +from iexcode.instruments.m3r import m3r_branch def hxp_ioc(mirror_name): """ diff --git a/iexcode/instruments/logfile.py b/iexcode/instruments/logfile.py index 2f158f6..dec2ce2 100644 --- a/iexcode/instruments/logfile.py +++ b/iexcode/instruments/logfile.py @@ -1,10 +1,10 @@ -from os.path import join,open,isfile +from os.path import join,isfile from epics import caget, caput -from .IEX_endstations import * -from .utilities import today -from .ARPES import ARPES_log_entries -from .Kappa import Kappa_log_entries +from iexcode.instruments.IEX_endstations import * +from iexcode.instruments.utilities import today +from iexcode.instruments.ARPES import ARPES_log_entries +from iexcode.instruments.Kappa import Kappa_log_entries ############################################################################################################## diff --git a/iexcode/instruments/m3r.py b/iexcode/instruments/m3r.py index 903ed76..54bb5ec 100644 --- a/iexcode/instruments/m3r.py +++ b/iexcode/instruments/m3r.py @@ -2,8 +2,8 @@ from cgi import print_arguments from epics import caget, caput -from .utilities import print_warning_message -from .FMB_mirrors import * +from iexcode.instruments.utilities import print_warning_message +from iexcode.instruments.FMB_mirrors import * ############################################################################################################## ################################ M3R ############################## diff --git a/iexcode/instruments/mpa.py b/iexcode/instruments/mpa.py index f941431..6408b46 100644 --- a/iexcode/instruments/mpa.py +++ b/iexcode/instruments/mpa.py @@ -3,11 +3,11 @@ from os.path import dirname, join import socket from epics import caget, caput -from .userCalcs import userCalcOut_clear,userStringSeq_clear -from .Kappa import Kappa_motor_dictionary,Kappa_cts, mda -from .AD_utilites import AD_ROI_setup -from .scalers import scaler_cts -from scanRecord import * +from iexcode.instruments.userCalcs import userCalcOut_clear,userStringSeq_clear +from iexcode.instruments.Kappa import Kappa_motor_dictionary,Kappa_cts, mda +from iexcode.instruments.AD_utilities import AD_ROI_setup +from iexcode.instruments.scalers import scaler_cts +from iexcode.instruments.scanRecord import * """ diff --git a/iexcode/instruments/resolution.py b/iexcode/instruments/resolution.py index 9b2e864..a34f891 100644 --- a/iexcode/instruments/resolution.py +++ b/iexcode/instruments/resolution.py @@ -1,6 +1,6 @@ import numpy as np -from electron_analyzer import resolution_EA +from iexcode.instruments.electron_analyzer import resolution_EA ############################################################################################################# ############################## Resolution ############################## ############################################################################################################# diff --git a/iexcode/instruments/scalers.py b/iexcode/instruments/scalers.py index 33da16e..89be3db 100644 --- a/iexcode/instruments/scalers.py +++ b/iexcode/instruments/scalers.py @@ -2,7 +2,7 @@ from math import floor from epics import caput,PV -from .IEX_endstations import BL +from iexcode.instruments.IEX_endstations import BL def scaler_cts(time_seconds=0.1,verbose=True): """ diff --git a/iexcode/instruments/scanRecord.py b/iexcode/instruments/scanRecord.py index c2719c4..2e01a97 100644 --- a/iexcode/instruments/scanRecord.py +++ b/iexcode/instruments/scanRecord.py @@ -4,9 +4,9 @@ import time from epics import caget, caput, PV -from .utilities import dateandtime, print_warning_message -from .logfile import log_update -from .userCalcs import userStringSeq_pvs,userStringSeq_clear +from iexcode.instruments.utilities import dateandtime, print_warning_message +from iexcode.instruments.logfile import log_update +from iexcode.instruments.userCalcs import userStringSeq_pvs,userStringSeq_clear def saveData_get_all(ioc_pv): diff --git a/iexcode/instruments/scratch.py b/iexcode/instruments/scratch.py new file mode 100644 index 0000000..ded5e79 --- /dev/null +++ b/iexcode/instruments/scratch.py @@ -0,0 +1,44 @@ +############################################################################################################## +############################## setting folder from ARPES ############################## +############################################################################################################## +def folders_ARPES(user_name,**kwargs): + """ + Create and sets (if create_only=False) all the folders the current run and ARPES user + Sets the FileName for logging to be "UserName/YYYYMMDD_log.txt using logname_Set()" + + **kwargs: + set_folders = True (default); set the mda and EA scanRecords + = False; only makes the folders (was create_only) + run: run cycle e.g. 2022_1 (default => check_run() ) + ftp = True / False (default); print what needs to be added to the ftps crontab and create additional ftp folders + mda_ioc: will overwrite the default ioc for mda scans + debug + + """ + kwargs.setdefault('set_folders',True) + kwargs.setdefault('run',check_run()) + kwargs.setdefault('ftp',False) + kwargs.setdefault('debug',False) + + run = kwargs['run'] + + if kwargs['debug']: + print("run,folder,user_name,ioc,ftp: ",run,BL.folder,user_name,BL.ioc,kwargs['ftp']) + + # Create User Folder: + make_user_folders(run,BL.folder,user_name,BL.endstation,ftp=kwargs['ftp']) + sleep(5) + + if kwargs["set_folders"]: + # Set up MDA folder: + folder_mda(run,BL.folder,user_name,BL.prefix,BL.ioc) + logfile_name_set(BL.endstation) + logfile_header(BL.endstation,BL.ioc,ARPES_log_header()) + + + #Set up Scienta folders: + try: + userPath = "/net/s29data/export/data_29id"+BL.folder+"/"+run+"/"+user_name+"/" + folders_EA(userPath,filePrefix="EA") + except: + print_warning_message("EA ioc is not running, cannot set folder") \ No newline at end of file diff --git a/iexcode/instruments/shutters.py b/iexcode/instruments/shutters.py index 909009f..2afd842 100644 --- a/iexcode/instruments/shutters.py +++ b/iexcode/instruments/shutters.py @@ -4,7 +4,7 @@ main shutter and branch shutter functions from time import sleep from epics import caget, caput -from .utilities import dateandtime, print_warning_message +from iexcode.instruments.utilities import dateandtime, print_warning_message ############################################################################################################## diff --git a/iexcode/instruments/slits.py b/iexcode/instruments/slits.py index 5a1ead3..1b323e3 100644 --- a/iexcode/instruments/slits.py +++ b/iexcode/instruments/slits.py @@ -4,10 +4,10 @@ from epics import caget, caput from .IEX_endstations import * -from .shutters import main_shutter_close -from .utilities import print_warning_message, read_dict -from .VLS_PGM import mono_get_all -from .encoders import encoders_reset +from iexcode.instruments.shutters import main_shutter_close +from iexcode.instruments.utilities import print_warning_message, read_dict +from iexcode.instruments.VLS_PGM import mono_get_all +from iexcode.instruments.encoders import encoders_reset slit_ioc="29idb:" diff --git a/iexcode/instruments/staff.py b/iexcode/instruments/staff.py index 8ff6206..b9d6eef 100644 --- a/iexcode/instruments/staff.py +++ b/iexcode/instruments/staff.py @@ -1,3 +1,39 @@ +from os import listdir,mkdir,chmod +from os.path import join, isfile, exists + +#from .ARPES import folders_ARPES +#from .Kappa import folders_Kappa + + +def folders_startup(run): + """ + Creates the run directories for the following: + data_29idb + data_29idc + data_29idd + + calls folder_ARPES('Staff');folder_Kappa('Staff') + + print text required to modify the rsynch crontab + + previously: Folders_Startup + """ + data_directories=['data_29idb','data_29idc','data_29idd'] + dserv="/net/s29data/export/" + for dataDir in data_directories: + path=join(dserv,dataDir,run) + print(path) + if not (exists(path)): + mkdir(path) + + #folders_ARPES('Staff',create_only=True) + #folders_Kappa('Staff',create_only=True) + + txt="\n\n\nupdate the rsync portion the 29id@nerdy crontab\n" + txt+="*/1 * * * * /usr/bin/rsync -av --exclude=core /net/s29data/export/data_29idd/"+run+" kip:/net/kip/sftp/pub/29iddftp/files > /home/beams22/29ID/cronfiles/cptoftp-currrun-d.log 2>&1" + txt+=("\n") + txt+="*/1 * * * * /usr/bin/rsync -av --exclude=core /net/s29data/export/data_29idd/"+run+" kip:/net/kip/sftp/pub/29iddftp/files > /home/beams22/29ID/cronfiles/cptoftp-currrun-d.log 2>&1" + print(txt) ############################################################################################################## ############################## staff detectors ############################## diff --git a/iexcode/instruments/storage_ring.py b/iexcode/instruments/storage_ring.py index 3e71edd..a2269b8 100644 --- a/iexcode/instruments/storage_ring.py +++ b/iexcode/instruments/storage_ring.py @@ -7,7 +7,7 @@ Functions dealing with the storage ring pvs from epics import caget from time import sleep -from .utilities import dateandtime +from iexcode.instruments.utilities import dateandtime def wait_for_beam(): """ diff --git a/iexcode/instruments/xrays.py b/iexcode/instruments/xrays.py index fdf2e7f..aa02940 100644 --- a/iexcode/instruments/xrays.py +++ b/iexcode/instruments/xrays.py @@ -6,25 +6,25 @@ import numpy as np from time import sleep from epics import caget,caput -from .IEX_endstations import BL - -from .resolution import * -from .IEX_VPU import * -from .VLS_PGM import * -from .slits import * -from .shutters import * -from .gate_valves import * -from .diagnostics import * -from .m3r import * -from .logfile import * -from .utilities import print_warning_message,make_table, take_closest_value -from .mpa import * -from .current_amplifiers import ca_average -from .beamline import branch_cams_enable - -from .ARPES import ARPES_mprint,ARPES_extra_pvs -from .Kappa import Kappa_mprint -from .electron_analyzer import getSESslit, EA +from iexcode.instruments.IEX_endstations import BL + +from iexcode.instruments.resolution import * +from iexcode.instruments.IEX_VPU import * +from iexcode.instruments.VLS_PGM import * +from iexcode.instruments.slits import * +from iexcode.instruments.shutters import * +from iexcode.instruments.gate_valves import * +from iexcode.instruments.diagnostics import * +from iexcode.instruments.m3r import * +from iexcode.instruments.logfile import * +from iexcode.instruments.utilities import print_warning_message,make_table, take_closest_value +from iexcode.instruments.mpa import * +from iexcode.instruments.current_amplifiers import ca_average +from iexcode.instruments.beamline import branch_cams_enable + +from iexcode.instruments.ARPES import ARPES_mprint,ARPES_extra_pvs +from iexcode.instruments.Kappa import Kappa_mprint +from iexcode.instruments.electron_analyzer import getSESslit, EA ############################################################################################################## ############################## resets and detector lists ############################## ############################################################################################################## -- GitLab