From 2489a3e147a5c52afc98c531d80b680a1d285cdf Mon Sep 17 00:00:00 2001 From: Gabriel Date: Thu, 14 Aug 2025 14:03:28 -0300 Subject: [PATCH 01/15] start bumps_scans in commisslib --- apsuite/commisslib/bumps_scans.py | 359 ++++++++++++++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 apsuite/commisslib/bumps_scans.py diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py new file mode 100644 index 00000000..8d914400 --- /dev/null +++ b/apsuite/commisslib/bumps_scans.py @@ -0,0 +1,359 @@ +""".""" + +import time as _time +import numpy as _np +from siriuspy.devices import SOFB, CurrInfoSI +from siriuspy.clientconfigdb import ConfigDBClient as _ConfigDBClient +import matplotlib.pyplot as _mplt +import matplotlib.gridspec as _mgs +from apsuite.orbcorr.si_bumps import SiCalcBumps + +from ..utils import ( + ThreadedMeasBaseClass as _BaseClass, + ParamsBaseClass as _ParamsBaseClass, +) + + +class BumpParams(_ParamsBaseClass): + """.""" + + def __init__(self): + """.""" + _ParamsBaseClass().__init__() + self.posx_min = -100 # [um] + self.posx_max = 100 # [um] + self.nr_steps_x = 11 + self.posy_min = -100 # [um] + self.posy_max = 100 # [um] + self.nr_steps_y = 7 + + self.angx_min = -100 # [um] + self.angx_max = 100 # [um] + self.nr_steps_angx = 11 + self.angy_min = -100 # [um] + self.angy_max = 100 # [um] + self.nr_steps_angy = 7 + + self.n_bpms_out = 3 + self.minsingval = 0.2 + self.buffer_sloworb = 20 + self.bump_residue = 5 + self.bump_max_residue = 10 + + self.wait_meas = 2 # [s] + self.orbcorr_nr_iters = 5 + self.orbcorr_residue = 5 # [um] + enbl_bump = _np.ones(160, dtype=int) + enbl_bump[1:7] = 0 + enbl_bump[-7:-1] = 0 + self.orbcorr_bpm_enbl = enbl_bump + + def __str__(self): + """.""" + dtmp = '{0:20s} = {1:9d}\n'.format + ftmp = '{0:20s} = {1:9.4f} {2:s}\n'.format + stmp = '{0:20s} = {1:9s} {2:s}\n'.format + stg = ftmp('posx_min', self.posx_min, '[um]') + stg += ftmp('posx_max', self.posx_max, '[um]') + stg += dtmp('nr_stepsx', self.nr_steps_x) + stg += ftmp('posy_min', self.posy_min, '[um]') + stg += ftmp('posy_max', self.posy_max, '[um]') + stg += dtmp('nr_stepsy', self.nr_steps_y) + stg += dtmp('buffer_sloworb', self.buffer_sloworb) + + stg += ftmp('wait_meas', self.wait_meas, '[s]') + stg += dtmp('orbcorr_nr_iters', self.orbcorr_nr_iters) + stg += ftmp('orbcorr_residue', self.orbcorr_residue, '[um]') + stg += stmp('orbcorr_bpm_enbl', '\n' + str(self.orbcorr_bpm_enbl), '') + return stg + + +class Bump(_BaseClass): + """.""" + + def __init__(self, isonline=True): + """.""" + _BaseClass.__init__( + self, params=BumpParams(), target=self._do_scan, isonline=isonline + ) + self.data = dict() + self.reforbx = None + self.reforby = None + self.bumptools = SiCalcBumps() + if self.isonline: + self.devices['sofb'] = SOFB(SOFB.DEVICES.SI) + self.devices['currinfo'] = CurrInfoSI() + self._configdb = _ConfigDBClient(config_type='si_orbit') + self.update_reforb() + self.configure_sofb() + + def config_sofb(self): + """.""" + sofb = self.devices['sofb'] + sofb.nr_points = self.params.buffer_sloworb + sofb.cmd_change_opmode_to_sloworb() + sofb.cmd_reset() + # NOTE: the factor of 8 is because the current version of SOFB is too + # slow to update multi-turn orbits. + sofb.wait_buffer(timeout=sofb.nr_points * 0.5 * 8) + sofb.cmd_turn_off_autocorr() + self._bpmxenbl = self.devices['sofb'].bpmxenbl + self._bpmyenbl = self.devices['sofb'].bpmyenbl + + def get_measurement_data(self): + """.""" + bbbh, bbbv = self.devices['bbbh'], self.devices['bbbv'] + trajx, trajy = self.get_trajectory_sofb() + data = { + 'timestamp': _time.time(), + 'stored_current': bbbh.dcct.current, + 'rf_frequency': bbbh.rfcav.dev_rfgen.frequency, + 'spech_mag': bbbh.sram.spec_mag, + 'spech_freq': bbbh.sram.spec_freq, + 'bbbh_data_mean': bbbh.sram.data_mean, + 'spech_mk1_mag': bbbh.sram.spec_marker1_mag, + 'spech_mk1_tune': bbbh.sram.spec_marker1_tune, + 'trajx': trajx, + 'specv_mag': bbbv.sram.spec_mag, + 'specv_freq': bbbv.sram.spec_freq, + 'bbbv_data_mean': bbbv.sram.data_mean, + 'specv_mk1_mag': bbbv.sram.spec_marker1_mag, + 'specv_mk1_tune': bbbv.sram.spec_marker1_tune, + 'trajy': trajy, + } + return data + + def restore_sofb_reforb(self): + sofb = self.devices['sofb'] + clt = self._configdb + ref_orb = clt.get_config_value('ref_orb') + refx = _np.array(ref_orb['x']) + refy = _np.array(ref_orb['y']) + sofb.refx = refx + sofb.refy = refy + sofb.bpmxenbl = _np.ones(refx.size, dtype=bool) + sofb.bpmyenbl = _np.ones(refx.size, dtype=bool) + + @staticmethod + def subsec_2_sectype_nr(subsec): + section_nr = int(subsec[:2]) + if not 1 <= section_nr <= 20: + raise ValueError('Section must be between 01..20.') + section_type = subsec[2:] + return section_type, section_nr + + def remove_sofb_bpms(self, section_type, section_nr, n_bpms_out): + sofb = self.devices['sofb'] + idcs_out = self.bumptools.get_closest_bpms_indices( + section_type=section_type, + sidx=section_nr - 1, + n_bpms_out=n_bpms_out, + ) + sofb.bpmxenbl[idcs_out[: n_bpms_out * 2]] = False + sofb.bpmyenbl[idcs_out[n_bpms_out * 2 :]] = False + _time.sleep(0.5) # NOTE: For some reason We have to wait here. + + def get_orbrms(self, refx, refy, idx): + dorbx = self.devices['sofb'].orbx - refx + dorby = self.devices['sofb'].orby - refy + dorbx = dorbx[idx] + dorby = dorby[idx] + return _np.hstack([dorbx, dorby]).std() + + def implement_bump( + self, refx=None, refy=None, agx=0, agy=0, psx=0, psy=0, subsec=None + ): + """.""" + sofb = self.devices['sofb'] + refx = refx or self.reforbx + refy = refy or self.reforby + n_bpms_out = self.params.n_bpms_out + minsingval = self.params.minsingval + nr_iters = self.params.orbcorr_nr_iters + residue = self.params.orbcorr_residue + bump_residue = self.params.bump_residue + bump_max_residue = self.bump_max_residue + + orbx, orby = sofb.si_calculate_bumps( + refx, + refy, + subsec, + agx=agx, + agy=agy, + psx=psx, + psy=psy, + n_bpms_out=n_bpms_out, + minsingval=minsingval, + ) + section_type, section_nr = self.subsec_2_sectype_nr(subsec) + + self.remove_sofb_bpms(section_type, section_nr) + + idcs_bpm = self.bumptools.get_bpm_indices( + section_type=section_type, sidx=section_nr - 1 + ) + + # Set orbit + sofb.refx = orbx + sofb.refy = orby + + # Verify orbit correction + rms_residue = bump_residue + 1 + print('Waiting orbit...') + while rms_residue > bump_residue: + _ = sofb.correct_orbit_manually(nr_iters=nr_iters, residue=residue) + rms_residue = self.get_orbrms(orbx, orby, idcs_bpm) + print(f' rms_residue = {rms_residue:.3f} um') + bump_residue *= 1.2 + if bump_residue > bump_max_residue: + raise ValueError('Could not correct orbit.') + + print('Done!') + + def update_reforb(self): + """.""" + self.reforb = self._configdb.get_config_value('ref_orb') + self.reforbx = _np.array(self.reforb['x']) + self.reforby = _np.array(self.reforb['y']) + + def calc_bump_span(self): + """.""" + prms = self.params + posx_span = _np.linspace(prms.posx_min, prms.posx_max, prms.nr_stepsx) + posy_span = _np.linspace(prms.posy_min, prms.posy_max, prms.nr_stepsy) + return posx_span, posy_span + + def _do_scan(self): + print( + 'NOTE:\n' + + 'Remember to turn off the septa and their orbit feedforward.\n' + ) + prms = self.params + posx_span, posy_span = self.calc_bump_span() + + # zig-zag type of scan in the y plane + idy, idx = _np.meshgrid(range(prms.nr_stepsy), range(prms.nr_stepsx)) + idy[1::2] = _np.flip(idy[1::2]) + idx, idy = idx.ravel(), idy.ravel() + + data = list() + for iter in range(idx.size): + posx = posx_span[idx[iter]] + posy = posy_span[idy[iter]] + + self.config_sofb_sloworb() + self.implement_bump(psx=posx, psy=posy) + self.config_sofb_multiturn() + _time.sleep(prms.wait_meas) + data.append(self.get_measurement_data()) + ampx = data[-1]['spech_mk1_mag'] + ampy = data[-1]['specv_mk1_mag'] + fstr = f'(x, y) = ({posx:6.1f}, {posy:6.1f}) um --> ' + fstr += f'(ampx, ampy) = ({ampx:.1f}, {ampy:.1f}) dB' + print(fstr) + data[-1]['bump'] = (posx, posy) + self.data = data + if self._stopevt.is_set(): + print('Stopping...') + break + + # return to initial reference orbit + self.config_sofb_sloworb() + print('Returning to ref_orb...') + self.devices['sofb'].refx = self.reforbx + self.devices['sofb'].refy = self.reforby + self.devices['sofb'].correct_orbit_manually( + nr_iters=self.params.orbcorr_nr_iters, + residue=self.params.orbcorr_residue, + ) + print('Finished!') + + def process_data(self): + """.""" + posx, posy, magx, magy = [], [], [], [] + trajx, trajy = [], [] + data_index = [] + for idx, datai in enumerate(self.data): + trjx = datai['trajx'].copy() + trjx -= trjx.mean(axis=0)[None, :] + trjy = datai['trajy'].copy() + trjy -= trjy.mean(axis=0)[None, :] + trajx.append(trjx.std()) + trajy.append(trjy.std()) + posx.append(datai['bump'][0]) + posy.append(datai['bump'][1]) + magx.append(datai['spech_mk1_mag']) + magy.append(datai['specv_mk1_mag']) + data_index.append(idx) + + nsteps = self.params.nr_stepsx + anly = dict() + anly['posx_bump'] = self._reshape_vec(posx, axis0_size=nsteps) + anly['posy_bump'] = self._reshape_vec(posy, axis0_size=nsteps) + anly['bbbh_mag'] = self._reshape_vec(magx, axis0_size=nsteps) + anly['bbbv_mag'] = self._reshape_vec(magy, axis0_size=nsteps) + anly['tbt_trajx_std'] = self._reshape_vec(trajx, axis0_size=nsteps) + anly['tbt_trajy_std'] = self._reshape_vec(trajy, axis0_size=nsteps) + anly['data_index'] = self._reshape_vec(data_index, axis0_size=nsteps) + self.analysis = anly + + def plot_bbb_mag(self, plane='HV', shading='auto', cmap='jet'): + """.""" + fig = _mplt.figure(figsize=(8, 6)) + gs = _mgs.GridSpec(1, 1) + ax1 = _mplt.subplot(gs[0, 0]) + anly = self.analysis + posx, posy = anly['posx_bump'], anly['posy_bump'] + + if plane.upper() == 'HV': + _mx, _my = anly['bbbh_mag'], anly['bbbv_mag'] + _mx, _my = 10 ** (_mx / 20), 10 ** (_my / 20) + mag = _np.log(_mx**2 + _my**2) + elif plane.upper() == 'H': + mag = 10 ** (anly['bbbh_mag'] / 20) + elif plane.upper() == 'V': + mag = 10 ** (anly['bbbv_mag'] / 20) + else: + raise Exception('plane argument must be HV, H or V.') + + pcm = ax1.pcolormesh(posx, posy, mag, shading=shading, cmap=cmap) + ax1.set_xlabel(r'Bump x [$\mu$m]') + ax1.set_ylabel(r'Bump y [$\mu$m]') + cbar = fig.colorbar(pcm, ax=ax1) + cbar.set_label(plane + ' BbB Mag') + ax1.set_title('') + fig.tight_layout() + return fig, ax1 + + def plot_tbt_traj(self, plane='HV', shading='auto', cmap='jet'): + """.""" + fig = _mplt.figure(figsize=(8, 6)) + gs = _mgs.GridSpec(1, 1) + ax1 = _mplt.subplot(gs[0, 0]) + anly = self.analysis + posx, posy = anly['posx_bump'], anly['posy_bump'] + + if plane.upper() == 'HV': + _tx, _ty = anly['tbt_trajx_std'], anly['tbt_trajy_std'] + traj = _np.sqrt(_tx**2 + _ty**2) + elif plane.upper() == 'H': + traj = anly['tbt_trajx_std'] + elif plane.upper() == 'V': + traj = anly['tbt_trajy_std'] + else: + raise Exception('plane argument must be HV, H or V.') + + pcm = ax1.pcolormesh(posx, posy, traj, shading=shading, cmap=cmap) + ax1.set_xlabel(r'Bump x [$\mu$m]') + ax1.set_ylabel(r'Bump y [$\mu$m]') + cbar = fig.colorbar(pcm, ax=ax1) + cbar.set_label(plane + r'. TbT std distortion [$\mu$m]') + ax1.set_title('') + fig.tight_layout() + return fig, ax1 + + @staticmethod + def _reshape_vec(vec, axis0_size): + new_vec = _np.array(vec).reshape((axis0_size, -1)) + new_vec[1::2, :] = new_vec[1::2, ::-1] + return new_vec From 6a99c1287ff341b954b7c782e466ca66fa2896df Mon Sep 17 00:00:00 2001 From: Gabriel Date: Thu, 14 Aug 2025 15:59:18 -0300 Subject: [PATCH 02/15] first version with first scan --- apsuite/commisslib/bumps_scans.py | 261 +++++++++--------------------- 1 file changed, 75 insertions(+), 186 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index 8d914400..ad65f93d 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -2,10 +2,8 @@ import time as _time import numpy as _np -from siriuspy.devices import SOFB, CurrInfoSI +from siriuspy.devices import SOFB, HLFOFB, CurrInfoSI from siriuspy.clientconfigdb import ConfigDBClient as _ConfigDBClient -import matplotlib.pyplot as _mplt -import matplotlib.gridspec as _mgs from apsuite.orbcorr.si_bumps import SiCalcBumps from ..utils import ( @@ -20,58 +18,56 @@ class BumpParams(_ParamsBaseClass): def __init__(self): """.""" _ParamsBaseClass().__init__() - self.posx_min = -100 # [um] - self.posx_max = 100 # [um] - self.nr_steps_x = 11 - self.posy_min = -100 # [um] - self.posy_max = 100 # [um] - self.nr_steps_y = 7 - - self.angx_min = -100 # [um] - self.angx_max = 100 # [um] - self.nr_steps_angx = 11 - self.angy_min = -100 # [um] - self.angy_max = 100 # [um] - self.nr_steps_angy = 7 + self.x_min = 0 # [um] or [urad] + self.x_max = 0 # [um] or [urad] + self.nr_steps_x = 0 + self.y_min = 0 # [um] or [urad] + self.y_max = 0 # [um] or [urad] + self.nr_steps_y = 0 + self.subsec = '01C1' + self.do_angular_bumps = True self.n_bpms_out = 3 self.minsingval = 0.2 - self.buffer_sloworb = 20 self.bump_residue = 5 self.bump_max_residue = 10 + self.buffer_sloworb = 20 self.wait_meas = 2 # [s] self.orbcorr_nr_iters = 5 self.orbcorr_residue = 5 # [um] - enbl_bump = _np.ones(160, dtype=int) - enbl_bump[1:7] = 0 - enbl_bump[-7:-1] = 0 - self.orbcorr_bpm_enbl = enbl_bump + + self.use_fofb = False def __str__(self): """.""" dtmp = '{0:20s} = {1:9d}\n'.format ftmp = '{0:20s} = {1:9.4f} {2:s}\n'.format - stmp = '{0:20s} = {1:9s} {2:s}\n'.format - stg = ftmp('posx_min', self.posx_min, '[um]') - stg += ftmp('posx_max', self.posx_max, '[um]') - stg += dtmp('nr_stepsx', self.nr_steps_x) - stg += ftmp('posy_min', self.posy_min, '[um]') - stg += ftmp('posy_max', self.posy_max, '[um]') - stg += dtmp('nr_stepsy', self.nr_steps_y) - stg += dtmp('buffer_sloworb', self.buffer_sloworb) - + stg = ftmp('x_min', self.x_min, '[um] or [urad]') + stg += ftmp('x_max', self.x_max, '[um] or [urad]') + stg += dtmp('nr_steps_x', self.nr_steps_x) + stg += ftmp('y_min', self.y_min, '[um] or [urad]') + stg += ftmp('y_max', self.y_max, '[um] or [urad]') + stg += dtmp('nr_steps_y', self.nr_steps_y) + stg += dtmp('subsec', self.subsec) + stg += dtmp('do_angular_bumps', self.do_angular_bumps) + + stg += dtmp('n_bpms_out', self.n_bpms_out, '') + stg += ftmp('minsingval', self.minsingval, '') + stg += ftmp('bump_residue', self.bump_residue, '') + stg += ftmp('bump_max_residue', self.bump_max_residue, '') + + stg += dtmp('buffer_sloworb', self.buffer_sloworb, '') stg += ftmp('wait_meas', self.wait_meas, '[s]') stg += dtmp('orbcorr_nr_iters', self.orbcorr_nr_iters) stg += ftmp('orbcorr_residue', self.orbcorr_residue, '[um]') - stg += stmp('orbcorr_bpm_enbl', '\n' + str(self.orbcorr_bpm_enbl), '') return stg class Bump(_BaseClass): """.""" - def __init__(self, isonline=True): + def __init__(self, measurement_func=None, isonline=True): """.""" _BaseClass.__init__( self, params=BumpParams(), target=self._do_scan, isonline=isonline @@ -79,13 +75,35 @@ def __init__(self, isonline=True): self.data = dict() self.reforbx = None self.reforby = None + if measurement_func is None: + self.measurement_func = self.dummy_meas + else: + self.measurement_func = measurement_func self.bumptools = SiCalcBumps() if self.isonline: self.devices['sofb'] = SOFB(SOFB.DEVICES.SI) + self.devices['fofb'] = HLFOFB(HLFOFB.DEVICES.SI) self.devices['currinfo'] = CurrInfoSI() self._configdb = _ConfigDBClient(config_type='si_orbit') - self.update_reforb() - self.configure_sofb() + + @staticmethod + def dummy_meas(): + print('Not a measurement!') + + @staticmethod + def subsec_2_sectype_nr(subsec): + section_nr = int(subsec[:2]) + if not 1 <= section_nr <= 20: + raise ValueError('Section must be between 01..20.') + section_type = subsec[2:] + return section_type, section_nr + + def get_measurement_data(self): + """.""" + currinfo = self.devices['currinfo'] + data = {'timestamp': _time.time(), 'current': currinfo.current} + data['measurements:'] = self.measurement_func() + return data def config_sofb(self): """.""" @@ -100,28 +118,11 @@ def config_sofb(self): self._bpmxenbl = self.devices['sofb'].bpmxenbl self._bpmyenbl = self.devices['sofb'].bpmyenbl - def get_measurement_data(self): - """.""" - bbbh, bbbv = self.devices['bbbh'], self.devices['bbbv'] - trajx, trajy = self.get_trajectory_sofb() - data = { - 'timestamp': _time.time(), - 'stored_current': bbbh.dcct.current, - 'rf_frequency': bbbh.rfcav.dev_rfgen.frequency, - 'spech_mag': bbbh.sram.spec_mag, - 'spech_freq': bbbh.sram.spec_freq, - 'bbbh_data_mean': bbbh.sram.data_mean, - 'spech_mk1_mag': bbbh.sram.spec_marker1_mag, - 'spech_mk1_tune': bbbh.sram.spec_marker1_tune, - 'trajx': trajx, - 'specv_mag': bbbv.sram.spec_mag, - 'specv_freq': bbbv.sram.spec_freq, - 'bbbv_data_mean': bbbv.sram.data_mean, - 'specv_mk1_mag': bbbv.sram.spec_marker1_mag, - 'specv_mk1_tune': bbbv.sram.spec_marker1_tune, - 'trajy': trajy, - } - return data + def is_beam_alive(self): + if self.devices['currinfo'].storedbeam: + return True + print('Beam is dead!') + self.restore_sofb_reforb() def restore_sofb_reforb(self): sofb = self.devices['sofb'] @@ -134,14 +135,6 @@ def restore_sofb_reforb(self): sofb.bpmxenbl = _np.ones(refx.size, dtype=bool) sofb.bpmyenbl = _np.ones(refx.size, dtype=bool) - @staticmethod - def subsec_2_sectype_nr(subsec): - section_nr = int(subsec[:2]) - if not 1 <= section_nr <= 20: - raise ValueError('Section must be between 01..20.') - section_type = subsec[2:] - return section_type, section_nr - def remove_sofb_bpms(self, section_type, section_nr, n_bpms_out): sofb = self.devices['sofb'] idcs_out = self.bumptools.get_closest_bpms_indices( @@ -167,6 +160,7 @@ def implement_bump( sofb = self.devices['sofb'] refx = refx or self.reforbx refy = refy or self.reforby + subsec = subsec or self.subsec n_bpms_out = self.params.n_bpms_out minsingval = self.params.minsingval nr_iters = self.params.orbcorr_nr_iters @@ -210,26 +204,16 @@ def implement_bump( print('Done!') - def update_reforb(self): - """.""" - self.reforb = self._configdb.get_config_value('ref_orb') - self.reforbx = _np.array(self.reforb['x']) - self.reforby = _np.array(self.reforb['y']) - def calc_bump_span(self): """.""" prms = self.params - posx_span = _np.linspace(prms.posx_min, prms.posx_max, prms.nr_stepsx) - posy_span = _np.linspace(prms.posy_min, prms.posy_max, prms.nr_stepsy) - return posx_span, posy_span + x_span = _np.linspace(prms.x_min, prms.x_max, prms.nr_stepsx) + y_span = _np.linspace(prms.y_min, prms.y_max, prms.nr_stepsy) + return x_span, y_span def _do_scan(self): - print( - 'NOTE:\n' - + 'Remember to turn off the septa and their orbit feedforward.\n' - ) prms = self.params - posx_span, posy_span = self.calc_bump_span() + x_span, y_span = self.calc_bump_span() # zig-zag type of scan in the y plane idy, idx = _np.meshgrid(range(prms.nr_stepsy), range(prms.nr_stepsx)) @@ -238,122 +222,27 @@ def _do_scan(self): data = list() for iter in range(idx.size): - posx = posx_span[idx[iter]] - posy = posy_span[idy[iter]] - - self.config_sofb_sloworb() - self.implement_bump(psx=posx, psy=posy) - self.config_sofb_multiturn() + x = x_span[idx[iter]] + y = y_span[idy[iter]] + + self.config_sofb() + if prms.do_angular_bumps: + self.implement_bump(agx=x * 1e-6, agy=y * 1e-6) + unit = 'urad' + else: + self.implement_bump(psx=x * 1e-6, psy=y * 1e-6) + unit = 'um' _time.sleep(prms.wait_meas) data.append(self.get_measurement_data()) - ampx = data[-1]['spech_mk1_mag'] - ampy = data[-1]['specv_mk1_mag'] - fstr = f'(x, y) = ({posx:6.1f}, {posy:6.1f}) um --> ' - fstr += f'(ampx, ampy) = ({ampx:.1f}, {ampy:.1f}) dB' + fstr = f'(x, y) = ({x:6.1f}, {y:6.1f}) ' + unit print(fstr) - data[-1]['bump'] = (posx, posy) + data[-1]['bump'] = (x, y) self.data = data if self._stopevt.is_set(): print('Stopping...') break # return to initial reference orbit - self.config_sofb_sloworb() print('Returning to ref_orb...') - self.devices['sofb'].refx = self.reforbx - self.devices['sofb'].refy = self.reforby - self.devices['sofb'].correct_orbit_manually( - nr_iters=self.params.orbcorr_nr_iters, - residue=self.params.orbcorr_residue, - ) + self.restore_sofb_reforb() print('Finished!') - - def process_data(self): - """.""" - posx, posy, magx, magy = [], [], [], [] - trajx, trajy = [], [] - data_index = [] - for idx, datai in enumerate(self.data): - trjx = datai['trajx'].copy() - trjx -= trjx.mean(axis=0)[None, :] - trjy = datai['trajy'].copy() - trjy -= trjy.mean(axis=0)[None, :] - trajx.append(trjx.std()) - trajy.append(trjy.std()) - posx.append(datai['bump'][0]) - posy.append(datai['bump'][1]) - magx.append(datai['spech_mk1_mag']) - magy.append(datai['specv_mk1_mag']) - data_index.append(idx) - - nsteps = self.params.nr_stepsx - anly = dict() - anly['posx_bump'] = self._reshape_vec(posx, axis0_size=nsteps) - anly['posy_bump'] = self._reshape_vec(posy, axis0_size=nsteps) - anly['bbbh_mag'] = self._reshape_vec(magx, axis0_size=nsteps) - anly['bbbv_mag'] = self._reshape_vec(magy, axis0_size=nsteps) - anly['tbt_trajx_std'] = self._reshape_vec(trajx, axis0_size=nsteps) - anly['tbt_trajy_std'] = self._reshape_vec(trajy, axis0_size=nsteps) - anly['data_index'] = self._reshape_vec(data_index, axis0_size=nsteps) - self.analysis = anly - - def plot_bbb_mag(self, plane='HV', shading='auto', cmap='jet'): - """.""" - fig = _mplt.figure(figsize=(8, 6)) - gs = _mgs.GridSpec(1, 1) - ax1 = _mplt.subplot(gs[0, 0]) - anly = self.analysis - posx, posy = anly['posx_bump'], anly['posy_bump'] - - if plane.upper() == 'HV': - _mx, _my = anly['bbbh_mag'], anly['bbbv_mag'] - _mx, _my = 10 ** (_mx / 20), 10 ** (_my / 20) - mag = _np.log(_mx**2 + _my**2) - elif plane.upper() == 'H': - mag = 10 ** (anly['bbbh_mag'] / 20) - elif plane.upper() == 'V': - mag = 10 ** (anly['bbbv_mag'] / 20) - else: - raise Exception('plane argument must be HV, H or V.') - - pcm = ax1.pcolormesh(posx, posy, mag, shading=shading, cmap=cmap) - ax1.set_xlabel(r'Bump x [$\mu$m]') - ax1.set_ylabel(r'Bump y [$\mu$m]') - cbar = fig.colorbar(pcm, ax=ax1) - cbar.set_label(plane + ' BbB Mag') - ax1.set_title('') - fig.tight_layout() - return fig, ax1 - - def plot_tbt_traj(self, plane='HV', shading='auto', cmap='jet'): - """.""" - fig = _mplt.figure(figsize=(8, 6)) - gs = _mgs.GridSpec(1, 1) - ax1 = _mplt.subplot(gs[0, 0]) - anly = self.analysis - posx, posy = anly['posx_bump'], anly['posy_bump'] - - if plane.upper() == 'HV': - _tx, _ty = anly['tbt_trajx_std'], anly['tbt_trajy_std'] - traj = _np.sqrt(_tx**2 + _ty**2) - elif plane.upper() == 'H': - traj = anly['tbt_trajx_std'] - elif plane.upper() == 'V': - traj = anly['tbt_trajy_std'] - else: - raise Exception('plane argument must be HV, H or V.') - - pcm = ax1.pcolormesh(posx, posy, traj, shading=shading, cmap=cmap) - ax1.set_xlabel(r'Bump x [$\mu$m]') - ax1.set_ylabel(r'Bump y [$\mu$m]') - cbar = fig.colorbar(pcm, ax=ax1) - cbar.set_label(plane + r'. TbT std distortion [$\mu$m]') - ax1.set_title('') - fig.tight_layout() - return fig, ax1 - - @staticmethod - def _reshape_vec(vec, axis0_size): - new_vec = _np.array(vec).reshape((axis0_size, -1)) - new_vec[1::2, :] = new_vec[1::2, ::-1] - return new_vec From 2217726f82d12701fbb3f9ea44404002d9793f9d Mon Sep 17 00:00:00 2001 From: Gabriel Date: Thu, 14 Aug 2025 16:51:43 -0300 Subject: [PATCH 03/15] add bumps with fofb functionality --- apsuite/commisslib/bumps_scans.py | 52 ++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index ad65f93d..719ed3cc 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -31,6 +31,7 @@ def __init__(self): self.minsingval = 0.2 self.bump_residue = 5 self.bump_max_residue = 10 + self.fofb_max_kick = 4 # [urad] self.buffer_sloworb = 20 self.wait_meas = 2 # [s] @@ -56,6 +57,7 @@ def __str__(self): stg += ftmp('minsingval', self.minsingval, '') stg += ftmp('bump_residue', self.bump_residue, '') stg += ftmp('bump_max_residue', self.bump_max_residue, '') + stg += ftmp('fofb_max_kick', self.fofb_max_kick, '[urad]') stg += dtmp('buffer_sloworb', self.buffer_sloworb, '') stg += ftmp('wait_meas', self.wait_meas, '[s]') @@ -108,13 +110,18 @@ def get_measurement_data(self): def config_sofb(self): """.""" sofb = self.devices['sofb'] + fofb = self.devices['fofb'] sofb.nr_points = self.params.buffer_sloworb sofb.cmd_change_opmode_to_sloworb() sofb.cmd_reset() # NOTE: the factor of 8 is because the current version of SOFB is too # slow to update multi-turn orbits. sofb.wait_buffer(timeout=sofb.nr_points * 0.5 * 8) - sofb.cmd_turn_off_autocorr() + if self.use_fofb: + sofb.cmd_turn_on_autocorr() + fofb.cmd_turn_on_loop_state() + else: + sofb.cmd_turn_off_autocorr() self._bpmxenbl = self.devices['sofb'].bpmxenbl self._bpmyenbl = self.devices['sofb'].bpmyenbl @@ -135,7 +142,7 @@ def restore_sofb_reforb(self): sofb.bpmxenbl = _np.ones(refx.size, dtype=bool) sofb.bpmyenbl = _np.ones(refx.size, dtype=bool) - def remove_sofb_bpms(self, section_type, section_nr, n_bpms_out): + def remove_bpms(self, section_type, section_nr, n_bpms_out): sofb = self.devices['sofb'] idcs_out = self.bumptools.get_closest_bpms_indices( section_type=section_type, @@ -144,6 +151,10 @@ def remove_sofb_bpms(self, section_type, section_nr, n_bpms_out): ) sofb.bpmxenbl[idcs_out[: n_bpms_out * 2]] = False sofb.bpmyenbl[idcs_out[n_bpms_out * 2 :]] = False + if self.params.use_fofb: + fofb = self.devices['fofb'] + fofb.bpmxenbl[idcs_out[: n_bpms_out * 2]] = False + fofb.bpmyenbl[idcs_out[n_bpms_out * 2 :]] = False _time.sleep(0.5) # NOTE: For some reason We have to wait here. def get_orbrms(self, refx, refy, idx): @@ -153,6 +164,15 @@ def get_orbrms(self, refx, refy, idx): dorby = dorby[idx] return _np.hstack([dorbx, dorby]).std() + def update_reforb(self, orbx, orby): + if self.use_fofb: + fofb = self.devices['fofb'] + fofb.orbx = orbx + fofb.orby = orby + sofb = self.devices['sofb'] + sofb.orbx = orbx + sofb.orby = orby + def implement_bump( self, refx=None, refy=None, agx=0, agy=0, psx=0, psy=0, subsec=None ): @@ -167,6 +187,7 @@ def implement_bump( residue = self.params.orbcorr_residue bump_residue = self.params.bump_residue bump_max_residue = self.bump_max_residue + fofb_max_kick = self.fofb_max_kick orbx, orby = sofb.si_calculate_bumps( refx, @@ -181,23 +202,36 @@ def implement_bump( ) section_type, section_nr = self.subsec_2_sectype_nr(subsec) - self.remove_sofb_bpms(section_type, section_nr) + self.remove_bpms(section_type, section_nr) idcs_bpm = self.bumptools.get_bpm_indices( section_type=section_type, sidx=section_nr - 1 ) # Set orbit - sofb.refx = orbx - sofb.refy = orby + self.update_reforb(orbx, orby) # Verify orbit correction rms_residue = bump_residue + 1 + kick = fofb_max_kick - 1 + if self.use_fofb: + fofb = self.devices['fofb'] print('Waiting orbit...') - while rms_residue > bump_residue: - _ = sofb.correct_orbit_manually(nr_iters=nr_iters, residue=residue) + while rms_residue > bump_residue or kick > fofb_max_kick: rms_residue = self.get_orbrms(orbx, orby, idcs_bpm) - print(f' rms_residue = {rms_residue:.3f} um') + if self.use_fofb: + kick = _np.max(( + _np.abs(fofb.kickch_acc), + _np.abs(fofb.kickcv_acc), + )) + else: + _ = sofb.correct_orbit_manually( + nr_iters=nr_iters, residue=residue + ) + print( + f' orb_rms = {rms_residue:.3f} um, ' + f'maxkick = {kick:.3f} urad' + ) bump_residue *= 1.2 if bump_residue > bump_max_residue: raise ValueError('Could not correct orbit.') @@ -222,6 +256,8 @@ def _do_scan(self): data = list() for iter in range(idx.size): + if not self.is_beam_alive(): + break x = x_span[idx[iter]] y = y_span[idy[iter]] From a84699364f0f8a1983aee525f9fafa8aa9868350 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Fri, 15 Aug 2025 09:31:51 -0300 Subject: [PATCH 04/15] create update_reforb function --- apsuite/commisslib/bumps_scans.py | 120 ++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 40 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index 719ed3cc..1dfa8839 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -18,19 +18,17 @@ class BumpParams(_ParamsBaseClass): def __init__(self): """.""" _ParamsBaseClass().__init__() - self.x_min = 0 # [um] or [urad] - self.x_max = 0 # [um] or [urad] - self.nr_steps_x = 0 - self.y_min = 0 # [um] or [urad] - self.y_max = 0 # [um] or [urad] - self.nr_steps_y = 0 + self.pts_x = 0 # 1d numpy array [um] + self.pts_y = 0 # 1d numpy array [um] + self.pts_px = 0 # 1d numpy array [urad] + self.pts_py = 0 # 1d numpy array [urad] self.subsec = '01C1' self.do_angular_bumps = True self.n_bpms_out = 3 self.minsingval = 0.2 - self.bump_residue = 5 - self.bump_max_residue = 10 + self.bump_residue = 5 # [um] + self.bump_max_residue = 10 # [um] self.fofb_max_kick = 4 # [urad] self.buffer_sloworb = 20 @@ -44,25 +42,25 @@ def __str__(self): """.""" dtmp = '{0:20s} = {1:9d}\n'.format ftmp = '{0:20s} = {1:9.4f} {2:s}\n'.format - stg = ftmp('x_min', self.x_min, '[um] or [urad]') - stg += ftmp('x_max', self.x_max, '[um] or [urad]') - stg += dtmp('nr_steps_x', self.nr_steps_x) - stg += ftmp('y_min', self.y_min, '[um] or [urad]') - stg += ftmp('y_max', self.y_max, '[um] or [urad]') - stg += dtmp('nr_steps_y', self.nr_steps_y) - stg += dtmp('subsec', self.subsec) + stmp = '{0:20s} = {1:9s} {2:s}\n'.format + stg = dtmp('subsec', self.subsec) stg += dtmp('do_angular_bumps', self.do_angular_bumps) stg += dtmp('n_bpms_out', self.n_bpms_out, '') stg += ftmp('minsingval', self.minsingval, '') - stg += ftmp('bump_residue', self.bump_residue, '') - stg += ftmp('bump_max_residue', self.bump_max_residue, '') + stg += ftmp('bump_residue', self.bump_residue, '[um]') + stg += ftmp('bump_max_residue', self.bump_max_residue, '[um]') stg += ftmp('fofb_max_kick', self.fofb_max_kick, '[urad]') stg += dtmp('buffer_sloworb', self.buffer_sloworb, '') stg += ftmp('wait_meas', self.wait_meas, '[s]') stg += dtmp('orbcorr_nr_iters', self.orbcorr_nr_iters) stg += ftmp('orbcorr_residue', self.orbcorr_residue, '[um]') + + stg += stmp('pts_x', self.pts_x, '[um]') + stg += stmp('pts_y', self.pts_y, '[um]') + stg += stmp('pts_px', self.pts_px, '[um] or [urad]') + stg += stmp('pts_py', self.pts_py, '[um] or [urad]') return stg @@ -78,7 +76,7 @@ def __init__(self, measurement_func=None, isonline=True): self.reforbx = None self.reforby = None if measurement_func is None: - self.measurement_func = self.dummy_meas + self.measurement_func = self._dummy_meas else: self.measurement_func = measurement_func self.bumptools = SiCalcBumps() @@ -89,17 +87,44 @@ def __init__(self, measurement_func=None, isonline=True): self._configdb = _ConfigDBClient(config_type='si_orbit') @staticmethod - def dummy_meas(): + def _dummy_meas(): print('Not a measurement!') + def _is_beam_alive(self): + if self.devices['currinfo'].storedbeam: + return True + print('Beam is dead!') + self.restore_sofb_reforb() + @staticmethod def subsec_2_sectype_nr(subsec): + """Convert subsec string to subsec type and number. + + Args: + subsec (str): Subsection name. example "02C1" + + Raises: + ValueError: Must be a valid section + + Returns: + str, int: section type and section number + """ section_nr = int(subsec[:2]) if not 1 <= section_nr <= 20: raise ValueError('Section must be between 01..20.') section_type = subsec[2:] return section_type, section_nr + def update_reforb(self, refx, refy): + """Update reforb. + + Args: + refx (1d numpy array): Horizontal orbit + refy (1d numpy array): Vertical orbit + """ + self.reforbx = refx + self.reforby = refy + def get_measurement_data(self): """.""" currinfo = self.devices['currinfo'] @@ -125,13 +150,8 @@ def config_sofb(self): self._bpmxenbl = self.devices['sofb'].bpmxenbl self._bpmyenbl = self.devices['sofb'].bpmyenbl - def is_beam_alive(self): - if self.devices['currinfo'].storedbeam: - return True - print('Beam is dead!') - self.restore_sofb_reforb() - def restore_sofb_reforb(self): + """.""" sofb = self.devices['sofb'] clt = self._configdb ref_orb = clt.get_config_value('ref_orb') @@ -143,6 +163,14 @@ def restore_sofb_reforb(self): sofb.bpmyenbl = _np.ones(refx.size, dtype=bool) def remove_bpms(self, section_type, section_nr, n_bpms_out): + """Remove BPMs from correction system. + + Args: + section_type (str): Section type. Ex: "C1", "C2" + section_nr (int): Number of section + n_bpms_out (int): Number of BPMs to remove from each + side of the bump. + """ sofb = self.devices['sofb'] idcs_out = self.bumptools.get_closest_bpms_indices( section_type=section_type, @@ -158,13 +186,29 @@ def remove_bpms(self, section_type, section_nr, n_bpms_out): _time.sleep(0.5) # NOTE: For some reason We have to wait here. def get_orbrms(self, refx, refy, idx): + """Calculate rms of orbit distortion. + + Args: + refx (1d numpy array): Horizontal ref. orb + refy (1d numpy array): Vertical ref. orb + idx (1d numpy array): Indices of BPMs used in bump. + + Returns: + float: rms of orbit distortion + """ dorbx = self.devices['sofb'].orbx - refx dorby = self.devices['sofb'].orby - refy dorbx = dorbx[idx] dorby = dorby[idx] return _np.hstack([dorbx, dorby]).std() - def update_reforb(self, orbx, orby): + def set_orb(self, orbx, orby): + """Update orbit of corr. sytems. + + Args: + orbx (1d numpy array): Horizontal orbit + orby (1d numpy array): Vertical orbit + """ if self.use_fofb: fofb = self.devices['fofb'] fofb.orbx = orbx @@ -209,7 +253,7 @@ def implement_bump( ) # Set orbit - self.update_reforb(orbx, orby) + self.set_orb(orbx, orby) # Verify orbit correction rms_residue = bump_residue + 1 @@ -238,28 +282,24 @@ def implement_bump( print('Done!') - def calc_bump_span(self): - """.""" - prms = self.params - x_span = _np.linspace(prms.x_min, prms.x_max, prms.nr_stepsx) - y_span = _np.linspace(prms.y_min, prms.y_max, prms.nr_stepsy) - return x_span, y_span - def _do_scan(self): prms = self.params - x_span, y_span = self.calc_bump_span() + if prms.do_angular_bumps: + x_span, y_span = prms.pts_px, prms.pts_py + else: + x_span, y_span = prms.pts_x, prms.pts_y # zig-zag type of scan in the y plane - idy, idx = _np.meshgrid(range(prms.nr_stepsy), range(prms.nr_stepsx)) + idy, idx = _np.meshgrid(range(len(x_span)), range(len(y_span))) idy[1::2] = _np.flip(idy[1::2]) idx, idy = idx.ravel(), idy.ravel() data = list() - for iter in range(idx.size): - if not self.is_beam_alive(): + for i in range(idx.size): + if not self._is_beam_alive(): break - x = x_span[idx[iter]] - y = y_span[idy[iter]] + x = x_span[idx[i]] + y = y_span[idy[i]] self.config_sofb() if prms.do_angular_bumps: From 6a688183d573bd2c616df2f7a30fa727696e5bef Mon Sep 17 00:00:00 2001 From: Gabriel Date: Tue, 19 Aug 2025 08:29:46 -0300 Subject: [PATCH 05/15] remove meas function from comisslib bmps_scan --- apsuite/commisslib/bumps_scans.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index 1dfa8839..64dfe172 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -67,7 +67,7 @@ def __str__(self): class Bump(_BaseClass): """.""" - def __init__(self, measurement_func=None, isonline=True): + def __init__(self, isonline=True): """.""" _BaseClass.__init__( self, params=BumpParams(), target=self._do_scan, isonline=isonline @@ -75,10 +75,6 @@ def __init__(self, measurement_func=None, isonline=True): self.data = dict() self.reforbx = None self.reforby = None - if measurement_func is None: - self.measurement_func = self._dummy_meas - else: - self.measurement_func = measurement_func self.bumptools = SiCalcBumps() if self.isonline: self.devices['sofb'] = SOFB(SOFB.DEVICES.SI) @@ -87,7 +83,7 @@ def __init__(self, measurement_func=None, isonline=True): self._configdb = _ConfigDBClient(config_type='si_orbit') @staticmethod - def _dummy_meas(): + def do_measurement(): print('Not a measurement!') def _is_beam_alive(self): @@ -129,7 +125,7 @@ def get_measurement_data(self): """.""" currinfo = self.devices['currinfo'] data = {'timestamp': _time.time(), 'current': currinfo.current} - data['measurements:'] = self.measurement_func() + data['measurements:'] = self.do_measurement() return data def config_sofb(self): From 902fac18fad3c07ea714f4183e1bf9ff00dd5249 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Tue, 26 Aug 2025 10:49:14 -0300 Subject: [PATCH 06/15] fix errors script --- apsuite/commisslib/bumps_scans.py | 39 ++++++++++++++++++------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index 64dfe172..822cf623 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -42,8 +42,8 @@ def __str__(self): """.""" dtmp = '{0:20s} = {1:9d}\n'.format ftmp = '{0:20s} = {1:9.4f} {2:s}\n'.format - stmp = '{0:20s} = {1:9s} {2:s}\n'.format - stg = dtmp('subsec', self.subsec) + # stmp = '{0:20s} = {1:9.4f} {1:9.4f} {2:s}\n'.format + stg = 'subsec = {} \n'.format(self.subsec) stg += dtmp('do_angular_bumps', self.do_angular_bumps) stg += dtmp('n_bpms_out', self.n_bpms_out, '') @@ -57,20 +57,27 @@ def __str__(self): stg += dtmp('orbcorr_nr_iters', self.orbcorr_nr_iters) stg += ftmp('orbcorr_residue', self.orbcorr_residue, '[um]') - stg += stmp('pts_x', self.pts_x, '[um]') - stg += stmp('pts_y', self.pts_y, '[um]') - stg += stmp('pts_px', self.pts_px, '[um] or [urad]') - stg += stmp('pts_py', self.pts_py, '[um] or [urad]') + # stg += stmp('pts_x', self.pts_x[0], self.pts_x[-1], '[um]') + # stg += stmp('pts_y', self.pts_y[0], self.pts_y[-1], '[um]') + # stg += stmp( + # 'pts_px', self.pts_px[0], self.pts_px[-1], '[um] or [urad]' + # ) + # stg += stmp( + # 'pts_py', self.pts_py[0], self.pts_py[-1], '[um] or [urad]' + # ) return stg class Bump(_BaseClass): """.""" - def __init__(self, isonline=True): + def __init__(self, params, isonline=True): """.""" _BaseClass.__init__( - self, params=BumpParams(), target=self._do_scan, isonline=isonline + self, + params=params if params is not None else BumpParams(), + target=self._do_scan, + isonline=isonline, ) self.data = dict() self.reforbx = None @@ -174,11 +181,11 @@ def remove_bpms(self, section_type, section_nr, n_bpms_out): n_bpms_out=n_bpms_out, ) sofb.bpmxenbl[idcs_out[: n_bpms_out * 2]] = False - sofb.bpmyenbl[idcs_out[n_bpms_out * 2 :]] = False + sofb.bpmyenbl[idcs_out[n_bpms_out * 2 :] - 160] = False if self.params.use_fofb: fofb = self.devices['fofb'] fofb.bpmxenbl[idcs_out[: n_bpms_out * 2]] = False - fofb.bpmyenbl[idcs_out[n_bpms_out * 2 :]] = False + fofb.bpmyenbl[idcs_out[n_bpms_out * 2 :] - 160] = False _time.sleep(0.5) # NOTE: For some reason We have to wait here. def get_orbrms(self, refx, refy, idx): @@ -220,14 +227,14 @@ def implement_bump( sofb = self.devices['sofb'] refx = refx or self.reforbx refy = refy or self.reforby - subsec = subsec or self.subsec + subsec = subsec or self.params.subsec n_bpms_out = self.params.n_bpms_out minsingval = self.params.minsingval nr_iters = self.params.orbcorr_nr_iters residue = self.params.orbcorr_residue bump_residue = self.params.bump_residue - bump_max_residue = self.bump_max_residue - fofb_max_kick = self.fofb_max_kick + bump_max_residue = self.params.bump_max_residue + fofb_max_kick = self.params.fofb_max_kick orbx, orby = sofb.si_calculate_bumps( refx, @@ -242,7 +249,7 @@ def implement_bump( ) section_type, section_nr = self.subsec_2_sectype_nr(subsec) - self.remove_bpms(section_type, section_nr) + self.remove_bpms(section_type, section_nr, n_bpms_out) idcs_bpm = self.bumptools.get_bpm_indices( section_type=section_type, sidx=section_nr - 1 @@ -254,12 +261,12 @@ def implement_bump( # Verify orbit correction rms_residue = bump_residue + 1 kick = fofb_max_kick - 1 - if self.use_fofb: + if self.params.use_fofb: fofb = self.devices['fofb'] print('Waiting orbit...') while rms_residue > bump_residue or kick > fofb_max_kick: rms_residue = self.get_orbrms(orbx, orby, idcs_bpm) - if self.use_fofb: + if self.params.use_fofb: kick = _np.max(( _np.abs(fofb.kickch_acc), _np.abs(fofb.kickcv_acc), From 1670755932dda50bcb62d55953633d34bd159b22 Mon Sep 17 00:00:00 2001 From: ximenes Date: Tue, 26 Aug 2025 16:10:33 -0300 Subject: [PATCH 07/15] fix bugs --- apsuite/commisslib/bumps_scans.py | 92 ++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 32 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index 822cf623..72c13ff8 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -18,10 +18,10 @@ class BumpParams(_ParamsBaseClass): def __init__(self): """.""" _ParamsBaseClass().__init__() - self.pts_x = 0 # 1d numpy array [um] - self.pts_y = 0 # 1d numpy array [um] - self.pts_px = 0 # 1d numpy array [urad] - self.pts_py = 0 # 1d numpy array [urad] + self.pts_psx = 0 # 1d numpy array [um] + self.pts_psy = 0 # 1d numpy array [um] + self.pts_agx = 0 # 1d numpy array [urad] + self.pts_agy = 0 # 1d numpy array [urad] self.subsec = '01C1' self.do_angular_bumps = True @@ -37,6 +37,7 @@ def __init__(self): self.orbcorr_residue = 5 # [um] self.use_fofb = False + self.timeout_fofb_ramp = 15 def __str__(self): """.""" @@ -57,13 +58,13 @@ def __str__(self): stg += dtmp('orbcorr_nr_iters', self.orbcorr_nr_iters) stg += ftmp('orbcorr_residue', self.orbcorr_residue, '[um]') - # stg += stmp('pts_x', self.pts_x[0], self.pts_x[-1], '[um]') - # stg += stmp('pts_y', self.pts_y[0], self.pts_y[-1], '[um]') + # stg += stmp('pts_psx', self.pts_psx[0], self.pts_psx[-1], '[um]') + # stg += stmp('pts_psy', self.pts_psy[0], self.pts_psy[-1], '[um]') # stg += stmp( - # 'pts_px', self.pts_px[0], self.pts_px[-1], '[um] or [urad]' + # 'pts_agx', self.pts_agx[0], self.pts_agx[-1], '[um] or [urad]' # ) # stg += stmp( - # 'pts_py', self.pts_py[0], self.pts_py[-1], '[um] or [urad]' + # 'pts_agy', self.pts_agy[0], self.pts_agy[-1], '[um] or [urad]' # ) return stg @@ -88,6 +89,9 @@ def __init__(self, params, isonline=True): self.devices['fofb'] = HLFOFB(HLFOFB.DEVICES.SI) self.devices['currinfo'] = CurrInfoSI() self._configdb = _ConfigDBClient(config_type='si_orbit') + fofb = self.devices['fofb'] + self._orig_fofb_bpmxenbl = fofb.bpmxenbl + self._orig_fofb_bpmyenbl = fofb.bpmyenbl @staticmethod def do_measurement(): @@ -139,23 +143,28 @@ def config_sofb(self): """.""" sofb = self.devices['sofb'] fofb = self.devices['fofb'] + if sofb.autocorrsts or fofb.loop_state: + fofb.cmd_turn_off_loop_state(timeout=self.params.timeout_fofb_ramp) + sofb.cmd_turn_off_autocorr() sofb.nr_points = self.params.buffer_sloworb sofb.cmd_change_opmode_to_sloworb() sofb.cmd_reset() # NOTE: the factor of 8 is because the current version of SOFB is too # slow to update multi-turn orbits. sofb.wait_buffer(timeout=sofb.nr_points * 0.5 * 8) - if self.use_fofb: + if self.params.use_fofb: sofb.cmd_turn_on_autocorr() fofb.cmd_turn_on_loop_state() else: sofb.cmd_turn_off_autocorr() + fofb.cmd_turn_off_loop_state() self._bpmxenbl = self.devices['sofb'].bpmxenbl self._bpmyenbl = self.devices['sofb'].bpmyenbl def restore_sofb_reforb(self): """.""" sofb = self.devices['sofb'] + fofb = self.devices['fofb'] clt = self._configdb ref_orb = clt.get_config_value('ref_orb') refx = _np.array(ref_orb['x']) @@ -164,6 +173,9 @@ def restore_sofb_reforb(self): sofb.refy = refy sofb.bpmxenbl = _np.ones(refx.size, dtype=bool) sofb.bpmyenbl = _np.ones(refx.size, dtype=bool) + if self.params.use_fofb: + fofb.bpmxenbl = self._orig_fofb_bpmxenbl + fofb.bpmyenbl = self._orig_fofb_bpmyenbl def remove_bpms(self, section_type, section_nr, n_bpms_out): """Remove BPMs from correction system. @@ -180,12 +192,23 @@ def remove_bpms(self, section_type, section_nr, n_bpms_out): sidx=section_nr - 1, n_bpms_out=n_bpms_out, ) + enblx = _np.ones(self.reforbx.size, dtype=bool) + enbly = _np.ones(self.reforby.size, dtype=bool) + enblx[idcs_out[: n_bpms_out * 2]] = False + enbly[idcs_out[n_bpms_out * 2:] - 160] = False + + sofb.bpmxenbl = enblx + sofb.bpmyenbl = enbly sofb.bpmxenbl[idcs_out[: n_bpms_out * 2]] = False - sofb.bpmyenbl[idcs_out[n_bpms_out * 2 :] - 160] = False + sofb.bpmyenbl[idcs_out[n_bpms_out * 2:] - 160] = False if self.params.use_fofb: fofb = self.devices['fofb'] - fofb.bpmxenbl[idcs_out[: n_bpms_out * 2]] = False - fofb.bpmyenbl[idcs_out[n_bpms_out * 2 :] - 160] = False + enblx = _np.copy(self._orig_fofb_bpmxenbl) + enbly = _np.copy(self._orig_fofb_bpmyenbl) + enblx[idcs_out[: n_bpms_out * 2]] = False + enbly[idcs_out[n_bpms_out * 2:] - 160] = False + fofb.bpmxenbl = enblx + fofb.bpmyenbl = enbly _time.sleep(0.5) # NOTE: For some reason We have to wait here. def get_orbrms(self, refx, refy, idx): @@ -199,11 +222,13 @@ def get_orbrms(self, refx, refy, idx): Returns: float: rms of orbit distortion """ + idcx = idx[:2] + idcy = idx[2:] - 160 dorbx = self.devices['sofb'].orbx - refx dorby = self.devices['sofb'].orby - refy - dorbx = dorbx[idx] - dorby = dorby[idx] - return _np.hstack([dorbx, dorby]).std() + dorbx = dorbx[idcx] + dorby = dorby[idcy] + return _np.sqrt(_np.sum(_np.hstack([dorbx, dorby]))) def set_orb(self, orbx, orby): """Update orbit of corr. sytems. @@ -212,21 +237,21 @@ def set_orb(self, orbx, orby): orbx (1d numpy array): Horizontal orbit orby (1d numpy array): Vertical orbit """ - if self.use_fofb: + if self.params.use_fofb: fofb = self.devices['fofb'] - fofb.orbx = orbx - fofb.orby = orby + fofb.refx = orbx + fofb.refy = orby sofb = self.devices['sofb'] - sofb.orbx = orbx - sofb.orby = orby + sofb.refx = orbx + sofb.refy = orby def implement_bump( self, refx=None, refy=None, agx=0, agy=0, psx=0, psy=0, subsec=None ): """.""" sofb = self.devices['sofb'] - refx = refx or self.reforbx - refy = refy or self.reforby + refx0 = refx or self.reforbx + refy0 = refy or self.reforby subsec = subsec or self.params.subsec n_bpms_out = self.params.n_bpms_out minsingval = self.params.minsingval @@ -236,9 +261,9 @@ def implement_bump( bump_max_residue = self.params.bump_max_residue fofb_max_kick = self.params.fofb_max_kick - orbx, orby = sofb.si_calculate_bumps( - refx, - refy, + refx, refy = sofb.si_calculate_bumps( + refx0, + refy0, subsec, agx=agx, agy=agy, @@ -249,14 +274,13 @@ def implement_bump( ) section_type, section_nr = self.subsec_2_sectype_nr(subsec) - self.remove_bpms(section_type, section_nr, n_bpms_out) idcs_bpm = self.bumptools.get_bpm_indices( section_type=section_type, sidx=section_nr - 1 ) # Set orbit - self.set_orb(orbx, orby) + self.set_orb(refx, refy) # Verify orbit correction rms_residue = bump_residue + 1 @@ -265,7 +289,7 @@ def implement_bump( fofb = self.devices['fofb'] print('Waiting orbit...') while rms_residue > bump_residue or kick > fofb_max_kick: - rms_residue = self.get_orbrms(orbx, orby, idcs_bpm) + rms_residue = self.get_orbrms(refx, refy, idcs_bpm) if self.params.use_fofb: kick = _np.max(( _np.abs(fofb.kickch_acc), @@ -286,11 +310,15 @@ def implement_bump( print('Done!') def _do_scan(self): + subsec = self.params.subsec + n_bpms_out = self.params.n_bpms_out + section_type, section_nr = self.subsec_2_sectype_nr(subsec) + self.remove_bpms(section_type, section_nr, n_bpms_out) prms = self.params if prms.do_angular_bumps: - x_span, y_span = prms.pts_px, prms.pts_py + x_span, y_span = prms.pts_agx, prms.pts_agy else: - x_span, y_span = prms.pts_x, prms.pts_y + x_span, y_span = prms.pts_psx, prms.pts_psy # zig-zag type of scan in the y plane idy, idx = _np.meshgrid(range(len(x_span)), range(len(y_span))) @@ -306,10 +334,10 @@ def _do_scan(self): self.config_sofb() if prms.do_angular_bumps: - self.implement_bump(agx=x * 1e-6, agy=y * 1e-6) + self.implement_bump(agx=x, agy=y) unit = 'urad' else: - self.implement_bump(psx=x * 1e-6, psy=y * 1e-6) + self.implement_bump(psx=x, psy=y) unit = 'um' _time.sleep(prms.wait_meas) data.append(self.get_measurement_data()) From 260aaa05506c6831daa32e377ff272af4f165e8b Mon Sep 17 00:00:00 2001 From: ximenes Date: Mon, 19 Jan 2026 13:14:28 -0300 Subject: [PATCH 08/15] Allow remove_bpms to be run with n_bpms_out=0 --- apsuite/commisslib/bumps_scans.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index 72c13ff8..ff91f379 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -194,19 +194,22 @@ def remove_bpms(self, section_type, section_nr, n_bpms_out): ) enblx = _np.ones(self.reforbx.size, dtype=bool) enbly = _np.ones(self.reforby.size, dtype=bool) - enblx[idcs_out[: n_bpms_out * 2]] = False - enbly[idcs_out[n_bpms_out * 2:] - 160] = False + if n_bpms_out != 0: + enblx[idcs_out[: n_bpms_out * 2]] = False + enbly[idcs_out[n_bpms_out * 2:] - 160] = False sofb.bpmxenbl = enblx sofb.bpmyenbl = enbly - sofb.bpmxenbl[idcs_out[: n_bpms_out * 2]] = False - sofb.bpmyenbl[idcs_out[n_bpms_out * 2:] - 160] = False + if n_bpms_out != 0: + sofb.bpmxenbl[idcs_out[: n_bpms_out * 2]] = False + sofb.bpmyenbl[idcs_out[n_bpms_out * 2:] - 160] = False if self.params.use_fofb: fofb = self.devices['fofb'] enblx = _np.copy(self._orig_fofb_bpmxenbl) enbly = _np.copy(self._orig_fofb_bpmyenbl) - enblx[idcs_out[: n_bpms_out * 2]] = False - enbly[idcs_out[n_bpms_out * 2:] - 160] = False + if n_bpms_out != 0: + enblx[idcs_out[: n_bpms_out * 2]] = False + enbly[idcs_out[n_bpms_out * 2:] - 160] = False fofb.bpmxenbl = enblx fofb.bpmyenbl = enbly _time.sleep(0.5) # NOTE: For some reason We have to wait here. From 0dad7e39b342c913210e04b92c7f73d45f6708f1 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Fri, 6 Feb 2026 14:29:27 -0300 Subject: [PATCH 09/15] Fix bumps_scans --- apsuite/commisslib/bumps_scans.py | 139 +++++++++++++++++++----------- 1 file changed, 87 insertions(+), 52 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index ff91f379..f9e3d783 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -18,10 +18,10 @@ class BumpParams(_ParamsBaseClass): def __init__(self): """.""" _ParamsBaseClass().__init__() - self.pts_psx = 0 # 1d numpy array [um] - self.pts_psy = 0 # 1d numpy array [um] - self.pts_agx = 0 # 1d numpy array [urad] - self.pts_agy = 0 # 1d numpy array [urad] + self.pts_psx = _np.array([0]) # 1d numpy array [um] + self.pts_psy = _np.array([0]) # 1d numpy array [um] + self.pts_agx = _np.array([0]) # 1d numpy array [urad] + self.pts_agy = _np.array([0]) # 1d numpy array [urad] self.subsec = '01C1' self.do_angular_bumps = True @@ -39,11 +39,18 @@ def __init__(self): self.use_fofb = False self.timeout_fofb_ramp = 15 + clt = _ConfigDBClient(config_type='si_orbit') + ref_orb = clt.get_config_value('ref_orb') + refx = _np.array(ref_orb['x']) + refy = _np.array(ref_orb['y']) + self.reforbx = refx + self.reforby = refy + def __str__(self): """.""" dtmp = '{0:20s} = {1:9d}\n'.format ftmp = '{0:20s} = {1:9.4f} {2:s}\n'.format - # stmp = '{0:20s} = {1:9.4f} {1:9.4f} {2:s}\n'.format + stmp = '{0:20s} = {1:9.2f} - {2:9.2f} {3:s} with {4:1.0f} pts\n'.format stg = 'subsec = {} \n'.format(self.subsec) stg += dtmp('do_angular_bumps', self.do_angular_bumps) @@ -58,14 +65,34 @@ def __str__(self): stg += dtmp('orbcorr_nr_iters', self.orbcorr_nr_iters) stg += ftmp('orbcorr_residue', self.orbcorr_residue, '[um]') - # stg += stmp('pts_psx', self.pts_psx[0], self.pts_psx[-1], '[um]') - # stg += stmp('pts_psy', self.pts_psy[0], self.pts_psy[-1], '[um]') - # stg += stmp( - # 'pts_agx', self.pts_agx[0], self.pts_agx[-1], '[um] or [urad]' - # ) - # stg += stmp( - # 'pts_agy', self.pts_agy[0], self.pts_agy[-1], '[um] or [urad]' - # ) + stg += stmp( + 'pts_psx', + self.pts_psx[0], + self.pts_psx[-1], + '[um]', + len(self.pts_psx), + ) + stg += stmp( + 'pts_psy', + self.pts_psy[0], + self.pts_psy[-1], + '[um]', + len(self.pts_psy), + ) + stg += stmp( + 'pts_agx', + self.pts_agx[0], + self.pts_agx[-1], + '[urad]', + len(self.pts_agx), + ) + stg += stmp( + 'pts_agy', + self.pts_agy[0], + self.pts_agy[-1], + '[urad]', + len(self.pts_agy), + ) return stg @@ -81,17 +108,13 @@ def __init__(self, params, isonline=True): isonline=isonline, ) self.data = dict() - self.reforbx = None - self.reforby = None self.bumptools = SiCalcBumps() if self.isonline: self.devices['sofb'] = SOFB(SOFB.DEVICES.SI) self.devices['fofb'] = HLFOFB(HLFOFB.DEVICES.SI) self.devices['currinfo'] = CurrInfoSI() - self._configdb = _ConfigDBClient(config_type='si_orbit') - fofb = self.devices['fofb'] - self._orig_fofb_bpmxenbl = fofb.bpmxenbl - self._orig_fofb_bpmyenbl = fofb.bpmyenbl + self.get_sofb_bpm_enbl() + self.get_fofb_bpm_enbl() @staticmethod def do_measurement(): @@ -143,6 +166,7 @@ def config_sofb(self): """.""" sofb = self.devices['sofb'] fofb = self.devices['fofb'] + print('Configuring sofb...') if sofb.autocorrsts or fofb.loop_state: fofb.cmd_turn_off_loop_state(timeout=self.params.timeout_fofb_ramp) sofb.cmd_turn_off_autocorr() @@ -152,30 +176,41 @@ def config_sofb(self): # NOTE: the factor of 8 is because the current version of SOFB is too # slow to update multi-turn orbits. sofb.wait_buffer(timeout=sofb.nr_points * 0.5 * 8) + print('Done!') if self.params.use_fofb: sofb.cmd_turn_on_autocorr() fofb.cmd_turn_on_loop_state() else: sofb.cmd_turn_off_autocorr() fofb.cmd_turn_off_loop_state() - self._bpmxenbl = self.devices['sofb'].bpmxenbl - self._bpmyenbl = self.devices['sofb'].bpmyenbl + + def get_sofb_bpm_enbl(self): + """.""" + self._bpmxenbl = _np.copy(self.devices['sofb'].bpmxenbl) + self._bpmyenbl = _np.copy(self.devices['sofb'].bpmyenbl) + + def get_fofb_bpm_enbl(self): + """.""" + self._fofb_bpmxenbl = _np.copy(self.devices['fofb'].bpmxenbl) + self._fofb_bpmyenbl = _np.copy(self.devices['fofb'].bpmyenbl) + + def _generate_bpm_enbl(self, n_bpms_out, enblx, enbly, idcs_out): + if n_bpms_out != 0: + enblx[idcs_out[: n_bpms_out * 2]] = False + enbly[idcs_out[n_bpms_out * 2 :] - 160] = False + return enblx, enbly def restore_sofb_reforb(self): """.""" sofb = self.devices['sofb'] fofb = self.devices['fofb'] - clt = self._configdb - ref_orb = clt.get_config_value('ref_orb') - refx = _np.array(ref_orb['x']) - refy = _np.array(ref_orb['y']) - sofb.refx = refx - sofb.refy = refy - sofb.bpmxenbl = _np.ones(refx.size, dtype=bool) - sofb.bpmyenbl = _np.ones(refx.size, dtype=bool) + sofb.refx = self.params.reforbx + sofb.refy = self.params.reforby + sofb.bpmxenbl = self._bpmxenbl + sofb.bpmyenbl = self._bpmyenbl if self.params.use_fofb: - fofb.bpmxenbl = self._orig_fofb_bpmxenbl - fofb.bpmyenbl = self._orig_fofb_bpmyenbl + fofb.bpmxenbl = self._fofb_bpmxenbl + fofb.bpmyenbl = self._fofb_bpmyenbl def remove_bpms(self, section_type, section_nr, n_bpms_out): """Remove BPMs from correction system. @@ -192,24 +227,21 @@ def remove_bpms(self, section_type, section_nr, n_bpms_out): sidx=section_nr - 1, n_bpms_out=n_bpms_out, ) - enblx = _np.ones(self.reforbx.size, dtype=bool) - enbly = _np.ones(self.reforby.size, dtype=bool) - if n_bpms_out != 0: - enblx[idcs_out[: n_bpms_out * 2]] = False - enbly[idcs_out[n_bpms_out * 2:] - 160] = False - + enblx = self._bpmxenbl + enbly = self._bpmyenbl + enblx, enbly = self._generate_bpm_enbl( + n_bpms_out, enblx, enbly, idcs_out + ) sofb.bpmxenbl = enblx sofb.bpmyenbl = enbly - if n_bpms_out != 0: - sofb.bpmxenbl[idcs_out[: n_bpms_out * 2]] = False - sofb.bpmyenbl[idcs_out[n_bpms_out * 2:] - 160] = False + if self.params.use_fofb: fofb = self.devices['fofb'] - enblx = _np.copy(self._orig_fofb_bpmxenbl) - enbly = _np.copy(self._orig_fofb_bpmyenbl) - if n_bpms_out != 0: - enblx[idcs_out[: n_bpms_out * 2]] = False - enbly[idcs_out[n_bpms_out * 2:] - 160] = False + enblx = self._fofb_bpmxenbl + enbly = self._fofb_bpmyenbl + enblx, enbly = self._generate_bpm_enbl( + n_bpms_out, enblx, enbly, idcs_out + ) fofb.bpmxenbl = enblx fofb.bpmyenbl = enbly _time.sleep(0.5) # NOTE: For some reason We have to wait here. @@ -231,7 +263,7 @@ def get_orbrms(self, refx, refy, idx): dorby = self.devices['sofb'].orby - refy dorbx = dorbx[idcx] dorby = dorby[idcy] - return _np.sqrt(_np.sum(_np.hstack([dorbx, dorby]))) + return _np.sqrt(_np.sum(_np.hstack([dorbx, dorby] ** 2))) def set_orb(self, orbx, orby): """Update orbit of corr. sytems. @@ -248,13 +280,11 @@ def set_orb(self, orbx, orby): sofb.refx = orbx sofb.refy = orby - def implement_bump( - self, refx=None, refy=None, agx=0, agy=0, psx=0, psy=0, subsec=None - ): + def implement_bump(self, agx=0, agy=0, psx=0, psy=0, subsec=None): """.""" sofb = self.devices['sofb'] - refx0 = refx or self.reforbx - refy0 = refy or self.reforby + refx0 = self.params.reforbx + refy0 = self.params.reforby subsec = subsec or self.params.subsec n_bpms_out = self.params.n_bpms_out minsingval = self.params.minsingval @@ -277,7 +307,6 @@ def implement_bump( ) section_type, section_nr = self.subsec_2_sectype_nr(subsec) - idcs_bpm = self.bumptools.get_bpm_indices( section_type=section_type, sidx=section_nr - 1 ) @@ -347,6 +376,8 @@ def _do_scan(self): fstr = f'(x, y) = ({x:6.1f}, {y:6.1f}) ' + unit print(fstr) data[-1]['bump'] = (x, y) + data[-1]['orbx'] = self.devices['sofb'].orbx + data[-1]['orby'] = self.devices['sofb'].orby self.data = data if self._stopevt.is_set(): print('Stopping...') @@ -356,3 +387,7 @@ def _do_scan(self): print('Returning to ref_orb...') self.restore_sofb_reforb() print('Finished!') + + def save_data(self, fname, overwrite=False, compress=False): + """.""" + return super().save_data(fname, overwrite, compress) From ff8e2d128b8b4f8cde1e514d18dafa2afb9ef611 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Fri, 6 Feb 2026 14:57:32 -0300 Subject: [PATCH 10/15] update do_measurement function at bumps_scans.py --- apsuite/commisslib/bumps_scans.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index f9e3d783..b001fb29 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -118,7 +118,14 @@ def __init__(self, params, isonline=True): @staticmethod def do_measurement(): + """Measurement function. + + Change this function by an external + function that does the measurement + you want to perform at each bump point. + """ print('Not a measurement!') + return {} def _is_beam_alive(self): if self.devices['currinfo'].storedbeam: From 070702cfc1702b010955bd7ffd387cc00a416ac8 Mon Sep 17 00:00:00 2001 From: ximenes Date: Mon, 9 Feb 2026 17:33:52 -0300 Subject: [PATCH 11/15] fix bugs --- apsuite/commisslib/bumps_scans.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index b001fb29..c6834190 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -204,7 +204,7 @@ def get_fofb_bpm_enbl(self): def _generate_bpm_enbl(self, n_bpms_out, enblx, enbly, idcs_out): if n_bpms_out != 0: enblx[idcs_out[: n_bpms_out * 2]] = False - enbly[idcs_out[n_bpms_out * 2 :] - 160] = False + enbly[idcs_out[n_bpms_out * 2:] - 160] = False return enblx, enbly def restore_sofb_reforb(self): @@ -264,13 +264,14 @@ def get_orbrms(self, refx, refy, idx): Returns: float: rms of orbit distortion """ - idcx = idx[:2] - idcy = idx[2:] - 160 + idcs_bool = _np.zeros(160, dtype=bool) + idcs_bool[idx[0:2]] = True dorbx = self.devices['sofb'].orbx - refx dorby = self.devices['sofb'].orby - refy - dorbx = dorbx[idcx] - dorby = dorby[idcy] - return _np.sqrt(_np.sum(_np.hstack([dorbx, dorby] ** 2))) + dorbx = dorbx[idcs_bool] + dorby = dorby[idcs_bool] + # return _np.sqrt(_np.sum(_np.hstack([dorbx, dorby]) ** 2)) + return _np.hstack([dorbx, dorby]).std() def set_orb(self, orbx, orby): """Update orbit of corr. sytems. @@ -328,6 +329,8 @@ def implement_bump(self, agx=0, agy=0, psx=0, psy=0, subsec=None): fofb = self.devices['fofb'] print('Waiting orbit...') while rms_residue > bump_residue or kick > fofb_max_kick: + sofb.cmd_reset() + sofb.wait_buffer() rms_residue = self.get_orbrms(refx, refy, idcs_bpm) if self.params.use_fofb: kick = _np.max(( @@ -340,6 +343,7 @@ def implement_bump(self, agx=0, agy=0, psx=0, psy=0, subsec=None): ) print( f' orb_rms = {rms_residue:.3f} um, ' + f' bump_rms = {bump_residue:.3f} um, ' f'maxkick = {kick:.3f} urad' ) bump_residue *= 1.2 @@ -360,9 +364,9 @@ def _do_scan(self): x_span, y_span = prms.pts_psx, prms.pts_psy # zig-zag type of scan in the y plane - idy, idx = _np.meshgrid(range(len(x_span)), range(len(y_span))) - idy[1::2] = _np.flip(idy[1::2]) - idx, idy = idx.ravel(), idy.ravel() + idx, idy = _np.meshgrid(range(len(x_span)), range(len(y_span))) + idx[1::2] = _np.flip(idx[1::2]) + idy, idx = idy.ravel(), idx.ravel() data = list() for i in range(idx.size): From bf98235db5d16620f3e856b41f2a192133d79284 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Fri, 13 Feb 2026 14:02:57 -0300 Subject: [PATCH 12/15] Improve bumps_scans with Fernando suggestions --- apsuite/commisslib/bumps_scans.py | 59 ++++++++++++++----------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index b001fb29..5e202777 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -36,9 +36,11 @@ def __init__(self): self.orbcorr_nr_iters = 5 self.orbcorr_residue = 5 # [um] - self.use_fofb = False + self.closed_loops = False self.timeout_fofb_ramp = 15 + self.sleep_time = 0.5 # [s] + clt = _ConfigDBClient(config_type='si_orbit') ref_orb = clt.get_config_value('ref_orb') refx = _np.array(ref_orb['x']) @@ -65,6 +67,10 @@ def __str__(self): stg += dtmp('orbcorr_nr_iters', self.orbcorr_nr_iters) stg += ftmp('orbcorr_residue', self.orbcorr_residue, '[um]') + stg += dtmp('closed_loops', self.closed_loops) + stg += dtmp('timeout_fofb_ramp', self.timeout_fofb_ramp, '') + stg += dtmp('sleep_time', self.sleep_time, '') + stg += stmp( 'pts_psx', self.pts_psx[0], @@ -152,16 +158,6 @@ def subsec_2_sectype_nr(subsec): section_type = subsec[2:] return section_type, section_nr - def update_reforb(self, refx, refy): - """Update reforb. - - Args: - refx (1d numpy array): Horizontal orbit - refy (1d numpy array): Vertical orbit - """ - self.reforbx = refx - self.reforby = refy - def get_measurement_data(self): """.""" currinfo = self.devices['currinfo'] @@ -184,7 +180,7 @@ def config_sofb(self): # slow to update multi-turn orbits. sofb.wait_buffer(timeout=sofb.nr_points * 0.5 * 8) print('Done!') - if self.params.use_fofb: + if self.params.closed_loops: sofb.cmd_turn_on_autocorr() fofb.cmd_turn_on_loop_state() else: @@ -215,7 +211,7 @@ def restore_sofb_reforb(self): sofb.refy = self.params.reforby sofb.bpmxenbl = self._bpmxenbl sofb.bpmyenbl = self._bpmyenbl - if self.params.use_fofb: + if self.params.closed_loops: fofb.bpmxenbl = self._fofb_bpmxenbl fofb.bpmyenbl = self._fofb_bpmyenbl @@ -242,7 +238,7 @@ def remove_bpms(self, section_type, section_nr, n_bpms_out): sofb.bpmxenbl = enblx sofb.bpmyenbl = enbly - if self.params.use_fofb: + if self.params.closed_loops: fofb = self.devices['fofb'] enblx = self._fofb_bpmxenbl enbly = self._fofb_bpmyenbl @@ -251,7 +247,9 @@ def remove_bpms(self, section_type, section_nr, n_bpms_out): ) fofb.bpmxenbl = enblx fofb.bpmyenbl = enbly - _time.sleep(0.5) # NOTE: For some reason We have to wait here. + _time.sleep( + self.params.sleep_time + ) # NOTE: For some reason We have to wait here. def get_orbrms(self, refx, refy, idx): """Calculate rms of orbit distortion. @@ -264,22 +262,20 @@ def get_orbrms(self, refx, refy, idx): Returns: float: rms of orbit distortion """ - idcx = idx[:2] - idcy = idx[2:] - 160 - dorbx = self.devices['sofb'].orbx - refx - dorby = self.devices['sofb'].orby - refy - dorbx = dorbx[idcx] - dorby = dorby[idcy] - return _np.sqrt(_np.sum(_np.hstack([dorbx, dorby] ** 2))) - - def set_orb(self, orbx, orby): + sofb = self.devices['sofb'] + ref = _np.r_[refx, refy] + orb = _np.r_[sofb.orbx, sofb.orby] + dorb = (orb - ref)[idx] ** 2 + return _np.sqrt(dorb.sum()) + + def set_reforb(self, orbx, orby): """Update orbit of corr. sytems. Args: orbx (1d numpy array): Horizontal orbit orby (1d numpy array): Vertical orbit """ - if self.params.use_fofb: + if self.params.closed_loops: fofb = self.devices['fofb'] fofb.refx = orbx fofb.refy = orby @@ -319,17 +315,17 @@ def implement_bump(self, agx=0, agy=0, psx=0, psy=0, subsec=None): ) # Set orbit - self.set_orb(refx, refy) + self.set_reforb(refx, refy) # Verify orbit correction rms_residue = bump_residue + 1 kick = fofb_max_kick - 1 - if self.params.use_fofb: + if self.params.closed_loops: fofb = self.devices['fofb'] print('Waiting orbit...') while rms_residue > bump_residue or kick > fofb_max_kick: rms_residue = self.get_orbrms(refx, refy, idcs_bpm) - if self.params.use_fofb: + if self.params.closed_loops: kick = _np.max(( _np.abs(fofb.kickch_acc), _np.abs(fofb.kickcv_acc), @@ -348,7 +344,8 @@ def implement_bump(self, agx=0, agy=0, psx=0, psy=0, subsec=None): print('Done!') - def _do_scan(self): + def do_scan(self): + """Start bumps scan.""" subsec = self.params.subsec n_bpms_out = self.params.n_bpms_out section_type, section_nr = self.subsec_2_sectype_nr(subsec) @@ -394,7 +391,3 @@ def _do_scan(self): print('Returning to ref_orb...') self.restore_sofb_reforb() print('Finished!') - - def save_data(self, fname, overwrite=False, compress=False): - """.""" - return super().save_data(fname, overwrite, compress) From fca1f17b3354074a487fa8a508f02febf4a3a6d0 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Fri, 13 Feb 2026 14:43:43 -0300 Subject: [PATCH 13/15] create _check_rms_conditions function in bumps_scans --- apsuite/commisslib/bumps_scans.py | 45 +++++++++++++++++++------------ 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index e0c97bbd..0a2905aa 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -27,7 +27,7 @@ def __init__(self): self.n_bpms_out = 3 self.minsingval = 0.2 - self.bump_residue = 5 # [um] + self.bump_residue = 3 # [um] self.bump_max_residue = 10 # [um] self.fofb_max_kick = 4 # [urad] @@ -283,6 +283,22 @@ def set_reforb(self, orbx, orby): sofb.refx = orbx sofb.refy = orby + def _check_rms_conditions( + self, rms_residue, bump_residue, refx, refy, idcs_bpm + ): + sofb = self.devices['sofb'] + bump_max_residue = self.params.bump_max_residue + sofb.cmd_reset() + sofb.wait_buffer() + rms_residue = self.get_orbrms(refx, refy, idcs_bpm) + print( + f' orb_rms = {rms_residue:.3f} um, ' + f' bump_rms = {bump_residue:.3f} um, ' + ) + bump_residue *= 1.2 + if bump_residue > bump_max_residue: + raise ValueError('Could not correct orbit.') + def implement_bump(self, agx=0, agy=0, psx=0, psy=0, subsec=None): """.""" sofb = self.devices['sofb'] @@ -294,7 +310,6 @@ def implement_bump(self, agx=0, agy=0, psx=0, psy=0, subsec=None): nr_iters = self.params.orbcorr_nr_iters residue = self.params.orbcorr_residue bump_residue = self.params.bump_residue - bump_max_residue = self.params.bump_max_residue fofb_max_kick = self.params.fofb_max_kick refx, refy = sofb.si_calculate_bumps( @@ -323,28 +338,24 @@ def implement_bump(self, agx=0, agy=0, psx=0, psy=0, subsec=None): if self.params.closed_loops: fofb = self.devices['fofb'] print('Waiting orbit...') - while rms_residue > bump_residue or kick > fofb_max_kick: - sofb.cmd_reset() - sofb.wait_buffer() - rms_residue = self.get_orbrms(refx, refy, idcs_bpm) - if self.params.closed_loops: + if self.params.closed_loops: + while rms_residue > bump_residue and kick > fofb_max_kick: kick = _np.max(( _np.abs(fofb.kickch_acc), _np.abs(fofb.kickcv_acc), )) - else: + self._check_rms_conditions( + rms_residue, bump_residue, refx, refy, idcs_bpm + ) + print(f' kick fofb = {kick:.3f} urad, ') + else: + while rms_residue > bump_residue: _ = sofb.correct_orbit_manually( nr_iters=nr_iters, residue=residue ) - print( - f' orb_rms = {rms_residue:.3f} um, ' - f' bump_rms = {bump_residue:.3f} um, ' - f'maxkick = {kick:.3f} urad' - ) - bump_residue *= 1.2 - if bump_residue > bump_max_residue: - raise ValueError('Could not correct orbit.') - + self._check_rms_conditions( + rms_residue, bump_residue, refx, refy, idcs_bpm + ) print('Done!') def do_scan(self): From c09ee13cb6a91c4d3dad67e7d054e602917140ad Mon Sep 17 00:00:00 2001 From: Gabriel Date: Mon, 2 Mar 2026 17:32:14 -0300 Subject: [PATCH 14/15] add get_initial_state method to bumps_scans.py --- apsuite/commisslib/bumps_scans.py | 34 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index 0a2905aa..43723a2f 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -41,13 +41,6 @@ def __init__(self): self.sleep_time = 0.5 # [s] - clt = _ConfigDBClient(config_type='si_orbit') - ref_orb = clt.get_config_value('ref_orb') - refx = _np.array(ref_orb['x']) - refy = _np.array(ref_orb['y']) - self.reforbx = refx - self.reforby = refy - def __str__(self): """.""" dtmp = '{0:20s} = {1:9d}\n'.format @@ -119,8 +112,6 @@ def __init__(self, params, isonline=True): self.devices['sofb'] = SOFB(SOFB.DEVICES.SI) self.devices['fofb'] = HLFOFB(HLFOFB.DEVICES.SI) self.devices['currinfo'] = CurrInfoSI() - self.get_sofb_bpm_enbl() - self.get_fofb_bpm_enbl() @staticmethod def do_measurement(): @@ -133,11 +124,22 @@ def do_measurement(): print('Not a measurement!') return {} + def get_initial_state(self): + """Get initial state of the SOFB and FOFB.""" + clt = _ConfigDBClient(config_type='si_orbit') + ref_orb = clt.get_config_value('ref_orb') + refx = _np.array(ref_orb['x']) + refy = _np.array(ref_orb['y']) + self.reforbx = refx + self.reforby = refy + self.get_sofb_bpm_enbl() + self.get_fofb_bpm_enbl() + def _is_beam_alive(self): if self.devices['currinfo'].storedbeam: return True print('Beam is dead!') - self.restore_sofb_reforb() + self.restore_initial_state() @staticmethod def subsec_2_sectype_nr(subsec): @@ -203,12 +205,12 @@ def _generate_bpm_enbl(self, n_bpms_out, enblx, enbly, idcs_out): enbly[idcs_out[n_bpms_out * 2 :] - 160] = False return enblx, enbly - def restore_sofb_reforb(self): + def restore_initial_state(self): """.""" sofb = self.devices['sofb'] fofb = self.devices['fofb'] - sofb.refx = self.params.reforbx - sofb.refy = self.params.reforby + sofb.refx = self.reforbx + sofb.refy = self.reforby sofb.bpmxenbl = self._bpmxenbl sofb.bpmyenbl = self._bpmyenbl if self.params.closed_loops: @@ -302,8 +304,8 @@ def _check_rms_conditions( def implement_bump(self, agx=0, agy=0, psx=0, psy=0, subsec=None): """.""" sofb = self.devices['sofb'] - refx0 = self.params.reforbx - refy0 = self.params.reforby + refx0 = self.reforbx + refy0 = self.reforby subsec = subsec or self.params.subsec n_bpms_out = self.params.n_bpms_out minsingval = self.params.minsingval @@ -403,5 +405,5 @@ def do_scan(self): # return to initial reference orbit print('Returning to ref_orb...') - self.restore_sofb_reforb() + self.restore_initial_state() print('Finished!') From 5cd16dd87bbb5984b2bf6fd9d5b2120a4ec9161d Mon Sep 17 00:00:00 2001 From: Gabriel Date: Mon, 2 Mar 2026 17:48:09 -0300 Subject: [PATCH 15/15] updare remove_bpms function at bumps_scans.py --- apsuite/commisslib/bumps_scans.py | 45 +++++++++++++------------------ 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py index 43723a2f..9501a72b 100644 --- a/apsuite/commisslib/bumps_scans.py +++ b/apsuite/commisslib/bumps_scans.py @@ -134,6 +134,14 @@ def get_initial_state(self): self.reforby = refy self.get_sofb_bpm_enbl() self.get_fofb_bpm_enbl() + self._initial_state = {} + self._initial_state['refx'] = refx + self._initial_state['refy'] = refy + self._initial_state['bpmxenbl'] = self._bpmxenbl + self._initial_state['bpmyenbl'] = self._bpmyenbl + self._initial_state['fofb_bpmxenbl'] = self._fofb_bpmxenbl + self._initial_state['fofb_bpmyenbl'] = self._fofb_bpmyenbl + self.data['initial_state'] = self._initial_state def _is_beam_alive(self): if self.devices['currinfo'].storedbeam: @@ -217,15 +225,12 @@ def restore_initial_state(self): fofb.bpmxenbl = self._fofb_bpmxenbl fofb.bpmyenbl = self._fofb_bpmyenbl - def remove_bpms(self, section_type, section_nr, n_bpms_out): - """Remove BPMs from correction system. + def remove_bpms(self): + """Remove BPMs from correction system.""" + subsec = self.params.subsec + n_bpms_out = self.params.n_bpms_out + section_type, section_nr = self.subsec_2_sectype_nr(subsec) - Args: - section_type (str): Section type. Ex: "C1", "C2" - section_nr (int): Number of section - n_bpms_out (int): Number of BPMs to remove from each - side of the bump. - """ sofb = self.devices['sofb'] idcs_out = self.bumptools.get_closest_bpms_indices( section_type=section_type, @@ -285,14 +290,11 @@ def set_reforb(self, orbx, orby): sofb.refx = orbx sofb.refy = orby - def _check_rms_conditions( - self, rms_residue, bump_residue, refx, refy, idcs_bpm - ): + def _check_rms_conditions(self, rms_residue, bump_residue): sofb = self.devices['sofb'] bump_max_residue = self.params.bump_max_residue sofb.cmd_reset() sofb.wait_buffer() - rms_residue = self.get_orbrms(refx, refy, idcs_bpm) print( f' orb_rms = {rms_residue:.3f} um, ' f' bump_rms = {bump_residue:.3f} um, ' @@ -346,26 +348,20 @@ def implement_bump(self, agx=0, agy=0, psx=0, psy=0, subsec=None): _np.abs(fofb.kickch_acc), _np.abs(fofb.kickcv_acc), )) - self._check_rms_conditions( - rms_residue, bump_residue, refx, refy, idcs_bpm - ) + rms_residue = self.get_orbrms(refx, refy, idcs_bpm) + self._check_rms_conditions(rms_residue, bump_residue) print(f' kick fofb = {kick:.3f} urad, ') else: while rms_residue > bump_residue: _ = sofb.correct_orbit_manually( nr_iters=nr_iters, residue=residue ) - self._check_rms_conditions( - rms_residue, bump_residue, refx, refy, idcs_bpm - ) + rms_residue = self.get_orbrms(refx, refy, idcs_bpm) + self._check_rms_conditions(rms_residue, bump_residue) print('Done!') def do_scan(self): """Start bumps scan.""" - subsec = self.params.subsec - n_bpms_out = self.params.n_bpms_out - section_type, section_nr = self.subsec_2_sectype_nr(subsec) - self.remove_bpms(section_type, section_nr, n_bpms_out) prms = self.params if prms.do_angular_bumps: x_span, y_span = prms.pts_agx, prms.pts_agy @@ -398,12 +394,9 @@ def do_scan(self): data[-1]['bump'] = (x, y) data[-1]['orbx'] = self.devices['sofb'].orbx data[-1]['orby'] = self.devices['sofb'].orby - self.data = data + self.data['bumps_data'] = data if self._stopevt.is_set(): print('Stopping...') break - # return to initial reference orbit - print('Returning to ref_orb...') - self.restore_initial_state() print('Finished!')