diff --git a/ophyd_devices/epics/proxies/InsertionDevice.py b/ophyd_devices/epics/proxies/InsertionDevice.py index ef0bb99..dbba07e 100644 --- a/ophyd_devices/epics/proxies/InsertionDevice.py +++ b/ophyd_devices/epics/proxies/InsertionDevice.py @@ -4,11 +4,11 @@ from ophyd import PVPositioner, Component, EpicsSignal, EpicsSignalRO, Kind class InsertionDevice(PVPositioner): """Python wrapper for the CSAXS insertion device control - This wrapper provides a positioner interface for the ID control. - is completely custom XBPM with templates directly in the - VME repo. Thus it needs a custom ophyd template as well... + This wrapper provides a positioner interface for the ID control. + is completely custom XBPM with templates directly in the + VME repo. Thus it needs a custom ophyd template as well... - WARN: The x and y are not updated by the IOC + WARN: The x and y are not updated by the IOC """ status = Component(EpicsSignalRO, "-USER:STATUS", auto_monitor=True) errorSource = Component(EpicsSignalRO, "-USER:ERROR-SOURCE", auto_monitor=True) @@ -23,4 +23,4 @@ class InsertionDevice(PVPositioner): # Automatically start simulation if directly invoked # (NA for important devices) if __name__ == "__main__": - pass + pass diff --git a/ophyd_devices/epics/proxies/SpmBase.py b/ophyd_devices/epics/proxies/SpmBase.py index 88ca98b..d96985e 100644 --- a/ophyd_devices/epics/proxies/SpmBase.py +++ b/ophyd_devices/epics/proxies/SpmBase.py @@ -6,12 +6,12 @@ import matplotlib.pyplot as plt class SpmBase(Device): """Python wrapper for the Staggered Blade Pair Monitors - SPM's consist of a set of four horizontal tungsten blades and are - used to monitor the beam height (only Y) for the bending magnet - beamlines of SLS. + SPM's consist of a set of four horizontal tungsten blades and are + used to monitor the beam height (only Y) for the bending magnet + beamlines of SLS. - Note: EPICS provided signals are read only, but the user can - change the beam position offset. + Note: EPICS provided signals are read only, but the user can + change the beam position offset. """ # Motor interface s1 = Component(EpicsSignalRO, "Current1", auto_monitor=True) @@ -27,12 +27,12 @@ class SpmBase(Device): class SpmSim(SpmBase): """Python wrapper for simulated Staggered Blade Pair Monitors - SPM's consist of a set of four horizontal tungsten blades and are - used to monitor the beam height (only Y) for the bending magnet - beamlines of SLS. + SPM's consist of a set of four horizontal tungsten blades and are + used to monitor the beam height (only Y) for the bending magnet + beamlines of SLS. - This simulation device extends the basic proxy with a script that - fills signals with quasi-randomized values. + This simulation device extends the basic proxy with a script that + fills signals with quasi-randomized values. """ # Motor interface s1w = Component(EpicsSignal, "Current1:RAW.VAL", auto_monitor=False) @@ -53,13 +53,13 @@ class SpmSim(SpmBase): def _simFrame(self): """Generator to simulate a jumping gaussian""" - # define normalized 2D gaussian + # Define normalized 2D gaussian def gaus2d(x=0, y=0, mx=0, my=0, sx=1, sy=1): return np.exp(-((x - mx)**2. / (2. * sx**2.) + (y - my)**2. / (2. * sy**2.))) - #Generator for dynamic values - self._MX = 0.75 * self._MX + 0.25 * (10.0 * np.random.random()-5.0) - self._MY = 0.75 * self._MY + 0.25 * (10.0 * np.random.random()-5.0) + # Generator for dynamic values + self._MX = 0.75 * self._MX + 0.25 * (10.0 * np.random.random() - 5.0) + self._MY = 0.75 * self._MY + 0.25 * (10.0 * np.random.random() - 5.0) self._I0 = 0.75 * self._I0 + 0.25 * (255.0 * np.random.random()) arr = self._I0 * gaus2d(self._x, self._y, self._MX, self._MY) @@ -68,12 +68,12 @@ class SpmSim(SpmBase): def sim(self): # Get next frame beam = self._simFrame() - total = np.sum(beam) - np.sum(beam[24:48,:]) - rnge = np.floor(np.log10(total) - 0.0 ) - s1 = np.sum(beam[0:16,:]) / 10**rnge - s2 = np.sum(beam[16:24,:]) / 10**rnge - s3 = np.sum(beam[40:48,:]) / 10**rnge - s4 = np.sum(beam[48:64,:]) / 10**rnge + total = np.sum(beam) - np.sum(beam[24:48, :]) + rnge = np.floor(np.log10(total) - 0.0) + s1 = np.sum(beam[0:16, :]) / 10**rnge + s2 = np.sum(beam[16:24, :]) / 10**rnge + s3 = np.sum(beam[40:48, :]) / 10**rnge + s4 = np.sum(beam[48:64, :]) / 10**rnge self.s1w.set(s1).wait() self.s2w.set(s2).wait() @@ -82,23 +82,23 @@ class SpmSim(SpmBase): self.rangew.set(rnge).wait() # Print debug info print(f"Raw signals: R={rnge}\t{s1}\t{s2}\t{s3}\t{s4}") - #plt.imshow(beam) - #plt.show(block=False) + # plt.imshow(beam) + # plt.show(block=False) plt.pause(0.5) # Automatically start simulation if directly invoked if __name__ == "__main__": - spm1 = SpmSim("X06D-FE-BM1:", name="spm1") - spm2 = SpmSim("X06D-FE-BM2:", name="spm2") + spm1 = SpmSim("X06D-FE-BM1:", name="spm1") + spm2 = SpmSim("X06D-FE-BM2:", name="spm2") - spm1.wait_for_connection(timeout=5) - spm2.wait_for_connection(timeout=5) + spm1.wait_for_connection(timeout=5) + spm2.wait_for_connection(timeout=5) - spm1.rangew.set(1).wait() - spm2.rangew.set(1).wait() + spm1.rangew.set(1).wait() + spm2.rangew.set(1).wait() - while True: - print("---") - spm1.sim() - spm2.sim() + while True: + print("---") + spm1.sim() + spm2.sim() diff --git a/ophyd_devices/epics/proxies/XbpmBase.py b/ophyd_devices/epics/proxies/XbpmBase.py index 5f6d13b..344ea28 100644 --- a/ophyd_devices/epics/proxies/XbpmBase.py +++ b/ophyd_devices/epics/proxies/XbpmBase.py @@ -6,10 +6,10 @@ import matplotlib.pyplot as plt class XbpmCsaxsOp(Device): """Python wrapper for custom XBPMs in the cSAXS optics hutch - This is completely custom XBPM with templates directly in the - VME repo. Thus it needs a custom ophyd template as well... + This is completely custom XBPM with templates directly in the + VME repo. Thus it needs a custom ophyd template as well... - WARN: The x and y are not updated by the IOC + WARN: The x and y are not updated by the IOC """ sum = Component(EpicsSignalRO, "SUM", auto_monitor=True) x = Component(EpicsSignalRO, "POSH", auto_monitor=True) @@ -23,14 +23,14 @@ class XbpmCsaxsOp(Device): class XbpmBase(Device): """Python wrapper for X-ray Beam Position Monitors - XBPM's consist of a metal-coated diamond window that ejects - photoelectrons from the incoming X-ray beam. These electons - are collected and their current is measured. Effectively - they act as four quadrant photodiodes and are used as BPMs - at the undulator beamlines of SLS. + XBPM's consist of a metal-coated diamond window that ejects + photoelectrons from the incoming X-ray beam. These electons + are collected and their current is measured. Effectively + they act as four quadrant photodiodes and are used as BPMs + at the undulator beamlines of SLS. - Note: EPICS provided signals are read only, but the user can - change the beam position offset. + Note: EPICS provided signals are read only, but the user can + change the beam position offset. """ # Motor interface s1 = Component(EpicsSignalRO, "Current1", auto_monitor=True) @@ -51,17 +51,17 @@ class XbpmBase(Device): class XbpmSim(XbpmBase): """Python wrapper for simulated X-ray Beam Position Monitors - XBPM's consist of a metal-coated diamond window that ejects - photoelectrons from the incoming X-ray beam. These electons - are collected and their current is measured. Effectively - they act as four quadrant photodiodes and are used as BPMs - at the undulator beamlines of SLS. + XBPM's consist of a metal-coated diamond window that ejects + photoelectrons from the incoming X-ray beam. These electons + are collected and their current is measured. Effectively + they act as four quadrant photodiodes and are used as BPMs + at the undulator beamlines of SLS. - Note: EPICS provided signals are read only, but the user can + Note: EPICS provided signals are read only, but the user can change the beam position offset. - This simulation device extends the basic proxy with a script that - fills signals with quasi-randomized values. + This simulation device extends the basic proxy with a script that + fills signals with quasi-randomized values. """ # Motor interface s1w = Component(EpicsSignal, "Current1:RAW.VAL", auto_monitor=False) @@ -118,16 +118,16 @@ class XbpmSim(XbpmBase): # Automatically start simulation if directly invoked if __name__ == "__main__": - xbpm1 = XbpmSim("X01DA-FE-XBPM1:", name="xbpm1") - xbpm2 = XbpmSim("X01DA-FE-XBPM2:", name="xbpm2") + xbpm1 = XbpmSim("X01DA-FE-XBPM1:", name="xbpm1") + xbpm2 = XbpmSim("X01DA-FE-XBPM2:", name="xbpm2") - xbpm1.wait_for_connection(timeout=5) - xbpm2.wait_for_connection(timeout=5) + xbpm1.wait_for_connection(timeout=5) + xbpm2.wait_for_connection(timeout=5) - xbpm1.rangew.set(1).wait() - xbpm2.rangew.set(1).wait() + xbpm1.rangew.set(1).wait() + xbpm2.rangew.set(1).wait() - while True: - print("---") - xbpm1.sim() - xbpm2.sim() + while True: + print("---") + xbpm1.sim() + xbpm2.sim() diff --git a/ophyd_devices/epics/proxies/mono_dccm.py b/ophyd_devices/epics/proxies/mono_dccm.py index 4adeded..a53b9a5 100644 --- a/ophyd_devices/epics/proxies/mono_dccm.py +++ b/ophyd_devices/epics/proxies/mono_dccm.py @@ -9,14 +9,15 @@ IMPORTANT: Virtual monochromator axes should be implemented already in EPICS!!! import numpy as np from math import isclose -from ophyd import EpicsSignal, EpicsSignalRO, EpicsMotor, PseudoPositioner, PseudoSingle, Device, Component, Kind +from ophyd import (EpicsSignal, EpicsSignalRO, EpicsMotor, PseudoPositioner, + PseudoSingle, Device, Component, Kind) from ophyd.pseudopos import pseudo_position_argument, real_position_argument from ophyd.sim import SynAxis, Syn2DGauss LN_CORR = 2e-4 -def a2e(angle, hkl=[1,1,1], lnc=False, bent=False, deg=False): +def a2e(angle, hkl=[1, 1, 1], lnc=False, bent=False, deg=False): """Convert between angle and energy for Si monchromators ATTENTION: 'angle' must be in radians, not degrees! """ @@ -24,7 +25,7 @@ def a2e(angle, hkl=[1,1,1], lnc=False, bent=False, deg=False): angle = angle*np.pi/180 if deg else angle # Lattice constant along direction - d0 = 5.43102 * (1.0-lncorr) / np.linalg.norm(hkl) + d0 = 5.43102 * (1.0 - lncorr) / np.linalg.norm(hkl) energy = 12.39842 / (2.0 * d0 * np.sin(angle)) return energy @@ -41,22 +42,22 @@ def w2e(wwl): return 12398.42 * 0.1 / wwl -def e2a(energy, hkl=[1,1,1], lnc=False, bent=False): +def e2a(energy, hkl=[1, 1, 1], lnc=False, bent=False): """Convert between energy and angle for Si monchromators ATTENTION: 'angle' must be in radians, not degrees! """ lncorr = LN_CORR if lnc else 0.0 # Lattice constant along direction - d0 = 2*5.43102 * (1.0-lncorr) / np.linalg.norm(hkl) - angle = np.arcsin(12.39842/d0/energy) + d0 = 2 * 5.43102 * (1.0 - lncorr) / np.linalg.norm(hkl) + angle = np.arcsin(12.39842 / d0 / energy) # Rfine for bent mirror if bent: rho = 2 * 19.65 * 8.35 / 28 * np.sin(angle) dt = 0.2e-3 / rho * 0.279 - d0 = 2 * 5.43102 * (1.0+dt) / np.linalg.norm(hkl) - angle = np.arcsin(12.39842/d0/energy) + d0 = 2 * 5.43102 * (1.0 + dt) / np.linalg.norm(hkl) + angle = np.arcsin(12.39842 / d0 / energy) return angle @@ -64,8 +65,8 @@ def e2a(energy, hkl=[1,1,1], lnc=False, bent=False): class MonoMotor(PseudoPositioner): """Monochromator axis - Small wrapper to combine a real angular axis with the corresponding energy. - ATTENTION: 'angle' is in degrees, at least for PXIII + Small wrapper to combine a real angular axis with the corresponding energy. + ATTENTION: 'angle' is in degrees, at least for PXIII """ # Real axis (in degrees) angle = Component(EpicsMotor, "", name='angle') @@ -86,10 +87,10 @@ class MonoMotor(PseudoPositioner): class MonoDccm(PseudoPositioner): """Combined DCCM monochromator - The first crystal selects the energy, the second one is only following. - DCCMs are quite simple in terms that they can't crash and we don't - have a beam offset. - ATTENTION: 'angle' is in degrees, at least for PXIII + The first crystal selects the energy, the second one is only following. + DCCMs are quite simple in terms that they can't crash and we don't + have a beam offset. + ATTENTION: 'angle' is in degrees, at least for PXIII """ # Real axis (in degrees) @@ -102,22 +103,30 @@ class MonoDccm(PseudoPositioner): energy = Component(PseudoSingle, name='energy', kind=Kind.hinted) # Other parameters - #feedback = Component(EpicsSignal, "MONOBEAM", name="feedback") - #enc1 = Component(EpicsSignalRO, "1:EXC1", name="enc1") - #enc2 = Component(EpicsSignalRO, "1:EXC2", name="enc2") + # feedback = Component(EpicsSignal, "MONOBEAM", name="feedback") + # enc1 = Component(EpicsSignalRO, "1:EXC1", name="enc1") + # enc2 = Component(EpicsSignalRO, "1:EXC2", name="enc2") @pseudo_position_argument def forward(self, pseudo_pos): """WARNING: We have an overdefined system! Not sure if common crystal movement is reliable without retuning""" if abs(pseudo_pos.energy-self.energy.position) > 0.0001 and abs(pseudo_pos.en1-self.en1.position) < 0.0001 and abs(pseudo_pos.en2-self.en2.position) < 0.0001: # Probably the common energy was changed - return self.RealPosition(th1=-180.0*e2a(pseudo_pos.energy)/3.141592, th2=180.0*e2a(pseudo_pos.energy)/3.141592) + return self.RealPosition( + th1=-180.0 * e2a(pseudo_pos.energy) / 3.141592, + th2=180.0 * e2a(pseudo_pos.energy) / 3.141592 + ) else: # Probably the individual axes was changes - return self.RealPosition(th1=-180.0*e2a(pseudo_pos.en1)/3.141592, th2=180.0*e2a(pseudo_pos.en2)/3.141592) + return self.RealPosition( + th1=-180.0 * e2a(pseudo_pos.en1 / 3.141592, + th2=180.0 * e2a(pseudo_pos.en2) / 3.141592 + ) @real_position_argument def inverse(self, real_pos): - return self.PseudoPosition(en1=-a2e(3.141592*real_pos.th1/180.0), - en2=a2e(3.141592*real_pos.th2/180.0), - energy=-a2e(3.141592*real_pos.th1/180.0)) + return self.PseudoPosition( + en1=-a2e(3.141592 * real_pos.th1 / 180.0), + en2=a2e(3.141592 * real_pos.th2 / 180.0), + energy=-a2e(3.141592 * real_pos.th1 / 180.0) + ) diff --git a/ophyd_devices/epics/proxies/slits.py b/ophyd_devices/epics/proxies/slits.py index 0e8a14d..4c5f5db 100644 --- a/ophyd_devices/epics/proxies/slits.py +++ b/ophyd_devices/epics/proxies/slits.py @@ -1,15 +1,15 @@ from ophyd import Device, Component, EpicsMotor, PseudoPositioner, PseudoSingle -from ophyd.pseudopos import pseudo_position_argument,real_position_argument +from ophyd.pseudopos import pseudo_position_argument, real_position_argument class SlitH(PseudoPositioner): """Python wrapper for virtual slits - These devices should be implemented as an EPICS SoftMotor IOC, - but thats not the case for all slits. So here is a pure ophyd - implementation. Uses standard naming convention! + These devices should be implemented as an EPICS SoftMotor IOC, + but thats not the case for all slits. So here is a pure ophyd + implementation. Uses standard naming convention! - NOTE: The real and virtual axes are wrapped together. + NOTE: The real and virtual axes are wrapped together. """ # Motor interface x1 = Component(EpicsMotor, "TRX1") @@ -20,13 +20,13 @@ class SlitH(PseudoPositioner): @pseudo_position_argument def forward(self, pseudo_pos): - '''Run a forward (pseudo -> real) calculation''' + """Run a forward (pseudo -> real) calculation""" return self.RealPosition(x1=pseudo_pos.cenx-pseudo_pos.gapx/2, x2=pseudo_pos.cenx+pseudo_pos.gapx/2) @real_position_argument def inverse(self, real_pos): - '''Run an inverse (real -> pseudo) calculation''' + """Run an inverse (real -> pseudo) calculation""" return self.PseudoPosition(cenx=(real_pos.x1+real_pos.x2)/2, gapx=real_pos.x2-real_pos.x1) @@ -34,11 +34,11 @@ class SlitH(PseudoPositioner): class SlitV(PseudoPositioner): """Python wrapper for virtual slits - These devices should be implemented as an EPICS SoftMotor IOC, - but thats not the case for all slits. So here is a pure ophyd - implementation. Uses standard naming convention! + These devices should be implemented as an EPICS SoftMotor IOC, + but thats not the case for all slits. So here is a pure ophyd + implementation. Uses standard naming convention! - NOTE: The real and virtual axes are wrapped together. + NOTE: The real and virtual axes are wrapped together. """ # Motor interface y1 = Component(EpicsMotor, "TRY1")