diff --git a/apsuite/commisslib/bumps_scans.py b/apsuite/commisslib/bumps_scans.py new file mode 100644 index 00000000..34939fd4 --- /dev/null +++ b/apsuite/commisslib/bumps_scans.py @@ -0,0 +1,426 @@ +""".""" + +import time as _time +import numpy as _np +from siriuspy.devices import SOFB, HLFOFB, CurrInfoSI +from apsuite.orbcorr.si_bumps import SiCalcBumps +from siriuspy.clientconfigdb import ConfigDBClient as _ConfigDBClient + +from ..utils import ( + ThreadedMeasBaseClass as _BaseClass, + ParamsBaseClass as _ParamsBaseClass, +) + + +class BumpParams(_ParamsBaseClass): + """.""" + + def __init__(self): + """.""" + _ParamsBaseClass().__init__() + 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 + + self.n_bpms_outx = 3 + self.n_bpms_outy = 3 + self.minsingval = 0.2 + self.bump_residue = 3 # [um] + self.bump_max_residue = 10 # [um] + self.fofb_min_kick = 2 # [urad] + + self.buffer_sloworb = 20 + self.wait_meas = 2 # [s] + self.orbcorr_nr_iters = 5 + self.orbcorr_residue = 5 # [um] + self.nr_orbit_verification_closed_loop = 5 # [um] + + self.closed_loops = False + self.timeout_fofb_ramp = 15.0 # [s] + + self.sleep_time = 0.5 # [s] + + self.meas_func = None + self.args = None + self.kwargs = None + + 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.2f} - {2:9.2f} {3:s} with {4:1.0f} pts\n'.format + stg = '{0:20s} = {1:>9s}\n'.format('subsec', self.subsec) + stg += dtmp('do_angular_bumps', self.do_angular_bumps) + + stg += dtmp('n_bpms_out_x', self.n_bpms_outx, '') + stg += dtmp('n_bpms_out_y', self.n_bpms_outy, '') + stg += ftmp('minsingval', self.minsingval, '') + stg += ftmp('bump_residue', self.bump_residue, '[um]') + stg += ftmp('bump_max_residue', self.bump_max_residue, '[um]') + stg += ftmp('fofb_min_kick', self.fofb_min_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 += dtmp('closed_loops', self.closed_loops) + stg += ftmp('timeout_fofb_ramp', self.timeout_fofb_ramp, '') + stg += ftmp('sleep_time', self.sleep_time, '') + + 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 + + +class Bump(_BaseClass): + """.""" + + def __init__(self, params, isonline=True): + """.""" + _BaseClass.__init__( + self, + params=params if params is not None else BumpParams(), + target=self.do_scan, + isonline=isonline, + ) + self.data = dict() + self.bumptools = SiCalcBumps() + self.meas_func = self.params.meas_func + self.args = self.params.args + self.kwargs = self.params.kwargs + if self.isonline: + self.devices['sofb'] = SOFB(SOFB.DEVICES.SI) + self.devices['fofb'] = HLFOFB(HLFOFB.DEVICES.SI) + self.devices['currinfo'] = CurrInfoSI() + + def do_measurement(self): + """Measurement function.""" + if self.meas_func is None: + print('Not a measurement....') + return self.meas_func(*self.args, **self.kwargs) + + def get_initial_state(self, use_ioc_reforb=True): + """Get initial state of the SOFB and FOFB.""" + if use_ioc_reforb: + refx = self.devices['sofb'].refx + refy = self.devices['sofb'].refy + else: + clt = _ConfigDBClient(config_type='si_orbit') + ref_orb = clt.get_config_value('ref_orb') + refx = ref_orb[:160] + refy = ref_orb[160:] + self.reforbx = refx + 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.copy() + self._initial_state['bpmyenbl'] = self._bpmyenbl.copy() + self._initial_state['fofb_bpmxenbl'] = self._fofb_bpmxenbl.copy() + self._initial_state['fofb_bpmyenbl'] = self._fofb_bpmyenbl.copy() + self.data['initial_state'] = self._initial_state + + def _is_beam_alive(self): + if self.devices['currinfo'].storedbeam: + return True + print('Beam is dead!') + self.restore_initial_state() + + @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 get_measurement_data(self): + """.""" + currinfo = self.devices['currinfo'] + data = {'timestamp': _time.time(), 'current': currinfo.current} + data['measurements:'] = self.do_measurement() + return data + + 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() + 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) + print('Done!') + if self.params.closed_loops: + sofb.cmd_turn_on_autocorr() + fofb.cmd_turn_on_loop_state() + else: + sofb.cmd_turn_off_autocorr() + fofb.cmd_turn_off_loop_state() + + 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_outx, n_bpms_outy, enblx, enbly, idcs_out): + if n_bpms_outx != 0: + enblx[idcs_out[: n_bpms_outx * 2]] = False + if n_bpms_outy != 0: + enbly[idcs_out[n_bpms_outy * 2 :] - 160] = False + return enblx, enbly + + def restore_initial_state(self): + """.""" + sofb = self.devices['sofb'] + fofb = self.devices['fofb'] + sofb.refx = self.reforbx + sofb.refy = self.reforby + sofb.bpmxenbl = self._bpmxenbl + sofb.bpmyenbl = self._bpmyenbl + if self.params.closed_loops: + fofb.bpmxenbl = self._fofb_bpmxenbl + fofb.bpmyenbl = self._fofb_bpmyenbl + + def remove_bpms(self): + """Remove BPMs from correction system.""" + subsec = self.params.subsec + n_bpms_outx = self.params.n_bpms_outx + n_bpms_outy = self.params.n_bpms_outy + section_type, section_nr = self.subsec_2_sectype_nr(subsec) + + sofb = self.devices['sofb'] + idcs_out = self.bumptools.get_closest_bpms_indices( + section_type=section_type, + sidx=section_nr - 1, + n_bpms_outx=n_bpms_outx, + n_bpms_outy=n_bpms_outy, + ) + enblx = self._bpmxenbl + enbly = self._bpmyenbl + enblx, enbly = self._generate_bpm_enbl( + n_bpms_outx, n_bpms_outy, enblx, enbly, idcs_out + ) + sofb.bpmxenbl = enblx + sofb.bpmyenbl = enbly + + if self.params.closed_loops: + fofb = self.devices['fofb'] + enblx = self._fofb_bpmxenbl + enbly = self._fofb_bpmyenbl + enblx, enbly = self._generate_bpm_enbl( + n_bpms_outx, n_bpms_outy, enblx, enbly, idcs_out + ) + fofb.bpmxenbl = enblx + fofb.bpmyenbl = enbly + _time.sleep( + self.params.sleep_time + ) # NOTE: For some reason We have to wait here. + + def get_orbrms(self, 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 + """ + sofb = self.devices['sofb'] + refx = sofb.refx + refy = sofb.refy + 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.closed_loops: + fofb = self.devices['fofb'] + fofb.refx = orbx + fofb.refy = orby + sofb = self.devices['sofb'] + sofb.refx = orbx + sofb.refy = orby + + 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() + print( + f' orb_rms = {rms_residue:.3f} um, ' + f' bump_rms = {bump_residue:.3f} um, ' + ) + 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'] + refx0 = self.reforbx + refy0 = self.reforby + subsec = subsec or self.params.subsec + n_bpms_outx = self.params.n_bpms_outx + n_bpms_outy = self.params.n_bpms_outy + minsingval = self.params.minsingval + nr_iters = self.params.orbcorr_nr_iters + residue = self.params.orbcorr_residue + bump_residue = self.params.bump_residue + fofb_min_kick = self.params.fofb_min_kick + nr_orbit_verification_closed_loop = ( + self.params.nr_orbit_verification_closed_loop + ) + + refx, refy = sofb.si_calculate_bumps( + refx0, + refy0, + subsec, + agx=agx, + agy=agy, + psx=psx, + psy=psy, + n_bpms_outx=n_bpms_outx, + n_bpms_outy=n_bpms_outy, + minsingval=minsingval, + ) + 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 + ) + + # Set orbit + self.set_reforb(refx, refy) + + # Verify orbit correction + rms_residue = bump_residue + 1 + kick = fofb_min_kick - 1 + if self.params.closed_loops: + fofb = self.devices['fofb'] + print('Waiting orbit...') + if self.params.closed_loops: + while rms_residue > bump_residue and kick > fofb_min_kick: + kick = _np.max(( + _np.abs(fofb.kickch_acc), + _np.abs(fofb.kickcv_acc), + )) + for _ in _np.arange(nr_orbit_verification_closed_loop): + rms_residue = self.get_orbrms(idcs_bpm) + self._check_rms_conditions(rms_residue, bump_residue) + bump_residue *= 1.2 + print(f' kick fofb = {kick:.3f} urad, ') + else: + while rms_residue > bump_residue: + _ = sofb.correct_orbit_manually( + nr_iters=nr_iters, residue=residue + ) + + rms_residue = self.get_orbrms(idcs_bpm) + self._check_rms_conditions(rms_residue, bump_residue) + bump_residue *= 1.2 + print('Done!') + + def do_scan(self): + """Start bumps scan.""" + prms = self.params + if prms.do_angular_bumps: + x_span, y_span = prms.pts_agx, prms.pts_agy + else: + x_span, y_span = prms.pts_psx, prms.pts_psy + + # zig-zag type of scan in the y plane + 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): + if not self._is_beam_alive(): + break + x = x_span[idx[i]] + y = y_span[idy[i]] + + self.config_sofb() + if prms.do_angular_bumps: + self.implement_bump(agx=x, agy=y) + unit = 'urad' + else: + self.implement_bump(psx=x, psy=y) + unit = 'um' + _time.sleep(prms.wait_meas) + data.append(self.get_measurement_data()) + 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['bumps_data'] = data + if self._stopevt.is_set(): + print('Stopping...') + break + + print('Finished!') diff --git a/apsuite/orbcorr/si_bumps.py b/apsuite/orbcorr/si_bumps.py index 1897d733..05030c92 100644 --- a/apsuite/orbcorr/si_bumps.py +++ b/apsuite/orbcorr/si_bumps.py @@ -40,12 +40,13 @@ class SiCalcBumps: SECTION_TYPES = list(MARKER_NAMES.keys()) def __init__( - self, model=None, section_type=None, section_nr=None, n_bpms_out=None + self, model=None, section_type=None, section_nr=None, n_bpms_outx=None, n_bpms_outy=None ): """.""" self._section_type = section_type self._section_nr = section_nr - self._n_bpms_out = n_bpms_out + self._n_bpms_outx = n_bpms_outx + self._n_bpms_outy = n_bpms_outy self._model = model self._mat_i2s = None self._mat_i2r = None @@ -78,18 +79,32 @@ def section_nr(self, value): self._section_nr = value @property - def n_bpms_out(self): - """Number of BPMs to remove from each side. + def n_bpms_outx(self): + """Number of horizontal BPMs to remove from each side. Returns: int: Number of BPMs to be removed from each side of the BPMs used in the bump. """ - return self._n_bpms_out + return self._n_bpms_outx - @n_bpms_out.setter - def n_bpms_out(self, value): - self._n_bpms_out = value + @n_bpms_outx.setter + def n_bpms_outx(self, value): + self._n_bpms_outx = value + + @property + def n_bpms_outy(self): + """Number of vertical BPMs to remove from each side. + + Returns: + int: Number of BPMs to be removed from each side + of the BPMs used in the bump. + """ + return self._n_bpms_outy + + @n_bpms_outy.setter + def n_bpms_outy(self, value): + self._n_bpms_outy = value @property def model(self): @@ -148,12 +163,14 @@ def mat_s2r(self, value): @staticmethod def _get_matrix_ss_section(leng): - return _np.array([ - [1, -leng / 2, 0, 0], - [1, leng / 2, 0, 0], - [0, 0, 1, -leng / 2], - [0, 0, 1, leng / 2], - ]) + return _np.array( + [ + [1, -leng / 2, 0, 0], + [1, leng / 2, 0, 0], + [0, 0, 1, -leng / 2], + [0, 0, 1, leng / 2], + ] + ) def _get_sec_bpm_indices(self, section_type=None): if section_type is None: @@ -182,6 +199,7 @@ def get_bpm_indices(self, section_type=None, sidx=None): Returns: 1d numpy array: Indices of the BPMs where orbit bump will be applied. + """ if section_type is None: section_type = self.section_type @@ -200,7 +218,7 @@ def get_bpm_indices(self, section_type=None, sidx=None): return idcs def get_closest_bpms_indices( - self, section_type=None, sidx=None, n_bpms_out=None + self, section_type=None, sidx=None, n_bpms_outx=None, n_bpms_outy=None ): """Get closest BPMs indices to remove from orbit correction. @@ -218,19 +236,26 @@ def get_closest_bpms_indices( section_type = self.section_type if sidx is None: sidx = self.section_nr - 1 - if n_bpms_out is None: - n_bpms_out = self.n_bpms_out + if n_bpms_outx is None: + n_bpms_outx = self.n_bpms_outx + if n_bpms_outy is None: + n_bpms_outy = self.n_bpms_outy bpm1_sec_index, bpm2_sec_index = self._get_sec_bpm_indices( section_type ) idlist = _np.arange(0, 160, 1) - idcs_ignore = list() - for i in _np.arange(n_bpms_out): - idcs_ignore.append(idlist[bpm1_sec_index + 8 * sidx - (i + 1)]) - idcs_ignore.append(idlist[bpm2_sec_index + 8 * sidx + (i + 1)]) - idcs_ignore = _np.array(idcs_ignore) - idcs_ignore = _np.tile(idcs_ignore, 2) - idcs_ignore[n_bpms_out * 2 :] += 160 + idcs_ignorex = list() + for i in _np.arange(n_bpms_outx): + idcs_ignorex.append(idlist[bpm1_sec_index + 8 * sidx - (i + 1)]) + idcs_ignorex.append(idlist[bpm2_sec_index + 8 * sidx + (i + 1)]) + idcs_ignorex = _np.array(idcs_ignorex) + idcs_ignorey = list() + for i in _np.arange(n_bpms_outy): + idcs_ignorey.append(idlist[bpm1_sec_index + 8 * sidx - (i + 1)]) + idcs_ignorey.append(idlist[bpm2_sec_index + 8 * sidx + (i + 1)]) + idcs_ignorey = _np.array(idcs_ignorey) + idcs_ignorey += 160 + idcs_ignore = _np.concatenate((idcs_ignorex, idcs_ignorey)) return idcs_ignore def get_btwbpm_corrs_indices(self, section_type=None, sidx=None): @@ -340,7 +365,7 @@ def remove_corrs_btwbpm(self, orbcorr, section_type=None, sidx=None): return orbcorr def remove_closest_bpms( - self, orbcorr, section_type=None, sidx=None, n_bpms_out=None + self, orbcorr, section_type=None, sidx=None, n_bpms_outx=None, n_bpms_outy=None ): """Remove closest BPMs form orbit correction. @@ -359,10 +384,12 @@ def remove_closest_bpms( section_type = self.section_type if sidx is None: sidx = self.section_nr - 1 - if n_bpms_out is None: - n_bpms_out = self.n_bpms_out + if n_bpms_outx is None: + n_bpms_outx = self.n_bpms_outx + if n_bpms_outy is None: + n_bpms_outy = self.n_bpms_outy idcs_ignore = self.get_closest_bpms_indices( - section_type, sidx, n_bpms_out + section_type, sidx, n_bpms_outx, n_bpms_outy ) if idcs_ignore.size != 0: orbcorr.params.enbllistbpm[idcs_ignore] = False @@ -372,7 +399,8 @@ def calc_matrices( self, section_type=None, section_nr=None, - n_bpms_out=None, + n_bpms_outx=None, + n_bpms_outy=None, use_ss_tfm=False, minsingval=0.2, deltax=10e-6, @@ -404,8 +432,10 @@ def calc_matrices( section_type = self.section_type if section_nr is None: section_nr = self.section_nr - if n_bpms_out is None: - n_bpms_out = self.n_bpms_out + if n_bpms_outx is None: + n_bpms_outx = self.n_bpms_outx + if n_bpms_outy is None: + n_bpms_outy = self.n_bpms_outy if 'S' in section_type and use_ss_tfm: length = self.SS_LENGTHS[section_type] @@ -417,7 +447,9 @@ def calc_matrices( if self.model is None: mod = si.create_accelerator() mod.cavity_on = True - elif self.model.cavity_on is False: + else: + mod = self.model + if mod.cavity_on is False: raise ValueError('Model cavity must be turned on!') orbcorr = OrbitCorr(mod, 'SI', use6dtrack=True) orbcorr.params.enblrf = True @@ -442,7 +474,7 @@ def calc_matrices( # remove closest BPMS orbcorr = self.remove_closest_bpms( - orbcorr, section_type, sidx, n_bpms_out + orbcorr, section_type, sidx, n_bpms_outx, n_bpms_outy ) mat_i2s = _np.zeros((4, 4), dtype=float) @@ -509,7 +541,8 @@ def test_matrices( minsingval=svals, section_nr=section_nr, section_type=section_type, - n_bpms_out=n_bpms, + n_bpms_outx=n_bpms, + n_bpms_outy=n_bpms, ) ms_i2s.append(m_i2s) ms_i2r.append(m_i2r) @@ -523,7 +556,8 @@ def test_matrices( minsingval=svals, section_nr=section_nr, section_type=section_type, - n_bpms_out=n_bpms, + n_bpms_outx=n_bpms, + n_bpms_outy=n_bpms, ) ms_i2s.append(m_i2s) ms_i2r.append(m_i2r) @@ -545,7 +579,8 @@ def calculate_bumps( self, section_type=None, section_nr=None, - n_bpms_out=None, + n_bpms_outx=None, + n_bpms_outy=None, m_s2r=None, use_ss_tfm=False, posx=0, @@ -580,8 +615,10 @@ def calculate_bumps( section_type = self.section_type if section_nr is None: section_nr = self.section_nr - if n_bpms_out is None: - n_bpms_out = self.n_bpms_out + if n_bpms_outx is None: + n_bpms_outx = self.n_bpms_outx + if n_bpms_outy is None: + n_bpms_outy = self.n_bpms_outy # Get bump matrices if m_s2r is None: @@ -589,7 +626,8 @@ def calculate_bumps( section_type=section_type, minsingval=0.2, section_nr=section_nr, - n_bpms_out=n_bpms_out, + n_bpms_outx=n_bpms_outx, + n_bpms_outy=n_bpms_outy, use_ss_tfm=use_ss_tfm, ) sidx = max(min(section_nr, 20), 1) @@ -600,7 +638,9 @@ def calculate_bumps( if self.model is None: mod = si.create_accelerator() mod.cavity_on = True - elif self.model.cavity_on is False: + else: + mod = self.model + if mod.cavity_on is False: raise ValueError('Model cavity must be turned on!') orbcorr = OrbitCorr(mod, 'SI', use6dtrack=True) orbcorr.params.enblrf = True @@ -615,7 +655,7 @@ def calculate_bumps( # remove closest BPMS orbcorr = self.remove_closest_bpms( - orbcorr, section_type, sidx, n_bpms_out + orbcorr, section_type, sidx, n_bpms_outx, n_bpms_outy ) gorb = orbcorr.get_orbit() @@ -628,7 +668,8 @@ def test_bumps( self, section_type=None, section_nr=None, - n_bpms_out=None, + n_bpms_outx=None, + n_bpms_outy=None, m_s2r=None, use_ss_tfm=False, plot_results=True, @@ -663,8 +704,10 @@ def test_bumps( section_type = self.section_type if section_nr is None: section_nr = self.section_nr - if n_bpms_out is None: - n_bpms_out = self.n_bpms_out + if n_bpms_outx is None: + n_bpms_outx = self.n_bpms_outx + if n_bpms_outy is None: + n_bpms_outy = self.n_bpms_outy sidx = max(min(section_nr, 20), 1) sidx -= 1 @@ -672,7 +715,8 @@ def test_bumps( gorb, orbcorr = self.calculate_bumps( section_type, section_nr, - n_bpms_out, + n_bpms_outx, + n_bpms_outy, m_s2r, use_ss_tfm, posx, @@ -701,7 +745,10 @@ def test_bumps( ax.plot(1e6 * xres, '-o', label='Resultant bump') ax.set_ylabel('Pos [um] / angle [urad]') ax.set_xlabel('Bump vector index') - ax.set_ylim(_np.min(1e6 * xres) * 0.9, _np.max(1e6 * xres) * 1.1) + ax.set_ylim( + _np.min(1e6 * _np.concatenate((xres, vec))) * 0.9, + _np.max(1e6 * _np.concatenate((xres, vec))) * 1.1, + ) ax.legend() ay.plot(kicks)