From daa01d995a89e8b384301b1ff01a83f1254e4d82 Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Tue, 26 Sep 2023 11:33:46 +0200 Subject: [PATCH 01/24] tune_scan function description added --- pySC/correction/tune.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index 817f061..ad5ccb4 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -17,8 +17,34 @@ LOGGER = logging_tools.get_logger(__name__) -def tune_scan(SC, quad_ords, rel_quad_changes, target=1, n_points=60, do_plot=False, nParticles=None, nTurns=None, - full_scan=False): +def tune_scan(SC, quad_ords, rel_quad_changes, target=1, n_points=60, do_plot=False, nParticles=None, nTurns=None, full_scan=False): + """ + Varies two quadrupole families to improve beam transmission, on a grid of relative setpoints specified in `rel_quad_changes` in a spiral-like pattern to increase the beam transmission. + + Args: + SC: SimulatedCommissioning instance + quad_ords: [1x2] array of quadrupole ordinates {`[1 x NQ1],[1 x NQ2]`} + rel_quad_changes: [1x2]` cell array of quadrupole setpoints {`[SP1_1,...,SP1_N1],[SP2_1,...,SP2_N2]`} with `N2=N1` + target(int, optional): Transmission target at `nTurns` + n_points(int, optional): Number of points for the scan + nParticles(int, optional): Number of particles used for tracking (for convenience, otherwise `SC.INJ.nParticles` is used) + nTurns(int, optional): Number of turns used for tracking (for convenience, otherwise `SC.INJ.nTurns` is used) + + full_scan( bool , optional): If false, the scan finishes as soon as the target is reached + do_plot( bool , optional): If true, beam transmission is plotted at every step + + Returns: + Updated SC structure with applied setpoints for maximised beam transmission + Relative setpoints which satisfied the target condition if reached, or the values which resulted in best transmission + Number of achieved turns + Turn-by-turn particle loss + Error: + `0`: Beam transmission target reached. + `1`: Beam transmission or number of turns increased, target not reached. + `2`: Unable to increase beam transmission. + + see also: *bpm_reading*, *generate_bunches* + """ if nParticles is None: nParticles = SC.INJ.nParticles if nTurns is None: From 2cd91c1b9fdb47542de40ab47f52a048b6bf344a Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Tue, 26 Sep 2023 14:52:13 +0200 Subject: [PATCH 02/24] corrected --- pySC/correction/tune.py | 108 ++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index ad5ccb4..b02aa12 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -45,60 +45,60 @@ def tune_scan(SC, quad_ords, rel_quad_changes, target=1, n_points=60, do_plot=Fa see also: *bpm_reading*, *generate_bunches* """ - if nParticles is None: - nParticles = SC.INJ.nParticles - if nTurns is None: - nTurns = SC.INJ.nTurns - nq = np.array([len(quad_ords[0]), len(quad_ords[1])], dtype=int) - nqsp = np.array([len(rel_quad_changes[0]), len(rel_quad_changes[1])], dtype=int) - max_turns = np.full((nqsp[0], nqsp[1]), np.nan) - transmission = np.full((nqsp[0], nqsp[1], nTurns), np.nan) - inds = _golden_donut_inds(np.floor_divide(nqsp, 2), n_points=n_points) - first_quads = [SC.RING[quad_ords[0][0]].FamName, SC.RING[quad_ords[1][0]].FamName] - ords = np.hstack(quad_ords) - for q1, q2 in inds.T: - q_setpoints = np.hstack((np.ones(nq[0]) * rel_quad_changes[0][q1], np.ones(nq[1]) * rel_quad_changes[1][q2])) - SC = set_magnet_setpoints(SC, ords, q_setpoints, False, 1, method='rel') - max_turns[q1, q2], surviving_fraction = beam_transmission(SC, nParticles=nParticles, nTurns=nTurns) - transmission[q1, q2, :] = 1 * surviving_fraction - SC = set_magnet_setpoints(SC, ords, 1 / q_setpoints, False, 1, method='rel') - - if do_plot: - f, ax = plot_scan(transmission[:, :, -1], max_turns, first_quads, rel_quad_changes) - ax[2] = plot_transmission(ax[2], surviving_fraction, nTurns, SC.INJ.beamLostAt) - f.tight_layout() - f.show() - - if not full_scan and transmission[q1, q2, -1] >= target: - setpoints = [rel_quad_changes[0][q1], rel_quad_changes[1][q2]] - LOGGER.info(f'Transmission target reached with:\n' - f' {first_quads[0]} SetPoint: {setpoints[0]:.4f}\n' - f' {first_quads[1]} SetPoint: {setpoints[1]:.4f}') - SC = set_magnet_setpoints(SC, ords, q_setpoints, False, 1, method='rel') - return SC, setpoints, max_turns, transmission - - testTrans = np.zeros(n_points) - testTurns = np.zeros(n_points) - for i in range(n_points): - testTrans[i] = transmission[inds[0, i], inds[1, i], -1] - testTurns[i] = max_turns[inds[0, i], inds[1, i]] - if np.max(testTurns) == 0: - raise RuntimeError('Fail, no transmission at all.\n') - max_ind = np.argmax(testTurns if np.max(testTrans) == 0 else testTrans) - if max_ind == 0: - LOGGER.warning('No improvement possible.\n') - setpoints = [rel_quad_changes[0][inds[0, max_ind]], rel_quad_changes[1][inds[1, max_ind]]] - if (max_transmission := np.max(testTrans)) == 0: - LOGGER.warning(f'No transmission at final turn at all. Maximum of {testTurns[max_ind]} turns reached with:\n' - f' {first_quads[0]} SetPoint: {setpoints[0]:.4f}\n' - f' {first_quads[1]} SetPoint: {setpoints[0]:.4f}') - else: - LOGGER.warning(f'Transmission target not reached. Best value ({max_transmission:.4f}) reached with:\n' - f' {first_quads[0]} SetPoint: {setpoints[0]:.4f}\n' - f' {first_quads[1]} SetPoint: {setpoints[0]:.4f}') - q_setpoints = np.hstack((setpoints[0] * np.ones(nq[0]), setpoints[1] * np.ones(nq[1]))) - SC = set_magnet_setpoints(SC, ords, q_setpoints, False, 1, method='rel') - return SC, setpoints, max_turns, transmission + if nParticles is None: + nParticles = SC.INJ.nParticles + if nTurns is None: + nTurns = SC.INJ.nTurns + nq = np.array([len(quad_ords[0]), len(quad_ords[1])], dtype=int) + nqsp = np.array([len(rel_quad_changes[0]), len(rel_quad_changes[1])], dtype=int) + max_turns = np.full((nqsp[0], nqsp[1]), np.nan) + transmission = np.full((nqsp[0], nqsp[1], nTurns), np.nan) + inds = _golden_donut_inds(np.floor_divide(nqsp, 2), n_points=n_points) + first_quads = [SC.RING[quad_ords[0][0]].FamName, SC.RING[quad_ords[1][0]].FamName] + ords = np.hstack(quad_ords) + for q1, q2 in inds.T: + q_setpoints = np.hstack((np.ones(nq[0]) * rel_quad_changes[0][q1], np.ones(nq[1]) * rel_quad_changes[1][q2])) + SC = set_magnet_setpoints(SC, ords, q_setpoints, False, 1, method='rel') + max_turns[q1, q2], surviving_fraction = beam_transmission(SC, nParticles=nParticles, nTurns=nTurns) + transmission[q1, q2, :] = 1 * surviving_fraction + SC = set_magnet_setpoints(SC, ords, 1 / q_setpoints, False, 1, method='rel') + + if do_plot: + f, ax = plot_scan(transmission[:, :, -1], max_turns, first_quads, rel_quad_changes) + ax[2] = plot_transmission(ax[2], surviving_fraction, nTurns, SC.INJ.beamLostAt) + f.tight_layout() + f.show() + + if not full_scan and transmission[q1, q2, -1] >= target: + setpoints = [rel_quad_changes[0][q1], rel_quad_changes[1][q2]] + LOGGER.info(f'Transmission target reached with:\n' + f' {first_quads[0]} SetPoint: {setpoints[0]:.4f}\n' + f' {first_quads[1]} SetPoint: {setpoints[1]:.4f}') + SC = set_magnet_setpoints(SC, ords, q_setpoints, False, 1, method='rel') + return SC, setpoints, max_turns, transmission + + testTrans = np.zeros(n_points) + testTurns = np.zeros(n_points) + for i in range(n_points): + testTrans[i] = transmission[inds[0, i], inds[1, i], -1] + testTurns[i] = max_turns[inds[0, i], inds[1, i]] + if np.max(testTurns) == 0: + raise RuntimeError('Fail, no transmission at all.\n') + max_ind = np.argmax(testTurns if np.max(testTrans) == 0 else testTrans) + if max_ind == 0: + LOGGER.warning('No improvement possible.\n') + setpoints = [rel_quad_changes[0][inds[0, max_ind]], rel_quad_changes[1][inds[1, max_ind]]] + if (max_transmission := np.max(testTrans)) == 0: + LOGGER.warning(f'No transmission at final turn at all. Maximum of {testTurns[max_ind]} turns reached with:\n' + f' {first_quads[0]} SetPoint: {setpoints[0]:.4f}\n' + f' {first_quads[1]} SetPoint: {setpoints[0]:.4f}') + else: + LOGGER.warning(f'Transmission target not reached. Best value ({max_transmission:.4f}) reached with:\n' + f' {first_quads[0]} SetPoint: {setpoints[0]:.4f}\n' + f' {first_quads[1]} SetPoint: {setpoints[0]:.4f}') + q_setpoints = np.hstack((setpoints[0] * np.ones(nq[0]), setpoints[1] * np.ones(nq[1]))) + SC = set_magnet_setpoints(SC, ords, q_setpoints, False, 1, method='rel') + return SC, setpoints, max_turns, transmission def _golden_donut_inds(r, n_points): From 09a77c848132ecf817d892ff1c1e1b0b71a9ccd7 Mon Sep 17 00:00:00 2001 From: liuzzo Date: Tue, 26 Sep 2023 14:56:26 +0200 Subject: [PATCH 03/24] indents --- pySC/correction/tune.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index b02aa12..66048f8 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -18,8 +18,8 @@ def tune_scan(SC, quad_ords, rel_quad_changes, target=1, n_points=60, do_plot=False, nParticles=None, nTurns=None, full_scan=False): - """ - Varies two quadrupole families to improve beam transmission, on a grid of relative setpoints specified in `rel_quad_changes` in a spiral-like pattern to increase the beam transmission. + """ + Varies two quadrupole families to improve beam transmission, on a grid of relative setpoints specified in `rel_quad_changes` in a spiral-like pattern to increase the beam transmission. Args: SC: SimulatedCommissioning instance @@ -29,15 +29,15 @@ def tune_scan(SC, quad_ords, rel_quad_changes, target=1, n_points=60, do_plot=Fa n_points(int, optional): Number of points for the scan nParticles(int, optional): Number of particles used for tracking (for convenience, otherwise `SC.INJ.nParticles` is used) nTurns(int, optional): Number of turns used for tracking (for convenience, otherwise `SC.INJ.nTurns` is used) - + full_scan( bool , optional): If false, the scan finishes as soon as the target is reached do_plot( bool , optional): If true, beam transmission is plotted at every step - Returns: + Returns: Updated SC structure with applied setpoints for maximised beam transmission Relative setpoints which satisfied the target condition if reached, or the values which resulted in best transmission Number of achieved turns - Turn-by-turn particle loss + Turn-by-turn particle loss Error: `0`: Beam transmission target reached. `1`: Beam transmission or number of turns increased, target not reached. @@ -45,6 +45,7 @@ def tune_scan(SC, quad_ords, rel_quad_changes, target=1, n_points=60, do_plot=Fa see also: *bpm_reading*, *generate_bunches* """ + if nParticles is None: nParticles = SC.INJ.nParticles if nTurns is None: From 7becaafa518e00029c3ff9f4d0a55a7de5ca9ec8 Mon Sep 17 00:00:00 2001 From: liuzzo Date: Tue, 26 Sep 2023 15:02:42 +0200 Subject: [PATCH 04/24] minors for help --- pySC/correction/tune.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index 66048f8..0dec3ce 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -19,12 +19,13 @@ def tune_scan(SC, quad_ords, rel_quad_changes, target=1, n_points=60, do_plot=False, nParticles=None, nTurns=None, full_scan=False): """ - Varies two quadrupole families to improve beam transmission, on a grid of relative setpoints specified in `rel_quad_changes` in a spiral-like pattern to increase the beam transmission. + Varies two quadrupole families to improve beam transmission, on a grid of relative setpoints specified in + `rel_quad_changes` in a spiral-like pattern to increase the beam transmission. Args: SC: SimulatedCommissioning instance quad_ords: [1x2] array of quadrupole ordinates {`[1 x NQ1],[1 x NQ2]`} - rel_quad_changes: [1x2]` cell array of quadrupole setpoints {`[SP1_1,...,SP1_N1],[SP2_1,...,SP2_N2]`} with `N2=N1` + rel_quad_changes: [1x2] cell array of quadrupole setpoints {`[SP1_1,...,SP1_N1],[SP2_1,...,SP2_N2]`} with `N2=N1` target(int, optional): Transmission target at `nTurns` n_points(int, optional): Number of points for the scan nParticles(int, optional): Number of particles used for tracking (for convenience, otherwise `SC.INJ.nParticles` is used) From 1ed4e9f92d89f0e2c23984d16e549b7032359d99 Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Tue, 26 Sep 2023 16:56:05 +0200 Subject: [PATCH 05/24] remove error message. Fix conflict. --- pySC/correction/tune.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index 0dec3ce..5e1d5de 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -39,10 +39,6 @@ def tune_scan(SC, quad_ords, rel_quad_changes, target=1, n_points=60, do_plot=Fa Relative setpoints which satisfied the target condition if reached, or the values which resulted in best transmission Number of achieved turns Turn-by-turn particle loss - Error: - `0`: Beam transmission target reached. - `1`: Beam transmission or number of turns increased, target not reached. - `2`: Unable to increase beam transmission. see also: *bpm_reading*, *generate_bunches* """ From 1a5db6375270dc63204036428e7755fae37da7bb Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Tue, 26 Sep 2023 16:57:54 +0200 Subject: [PATCH 06/24] added doc. Removed unexplained inputs of quadrupoles. --- pySC/correction/chroma.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/pySC/correction/chroma.py b/pySC/correction/chroma.py index dc6088d..25d9ad3 100644 --- a/pySC/correction/chroma.py +++ b/pySC/correction/chroma.py @@ -10,15 +10,27 @@ def fit_chroma(SC, s_ords, target_chroma=None, init_step_size=np.array([2, 2]), xtol=1E-4, ftol=1E-3, tune_knobs_ords=None, tune_knobs_delta_k=None): + """ + Applies a chromaticity correction using two sextupole families. + Args: + SC: SimulatedCommissioning instance + s_ords: [1x2] array of sextupole ordinates {`[1 x NSF1],[1 x NSD2]`} + target_chroma (optional): Target chromaticity for correction. Default: chromaticity of 'SC.IDEALRING' + init_step_size ([1x2] array, optional): Initial step size for the solver. Default: [2,2] + xtol(int, optional): Step tolerance for solver. Default: 1e-4 + ftol(int, optional): Merit tolerance for solver. Default: 1e-4 + + Returns: + SC: SimulatedCommissioning instance with corrected chromaticity. + Example: + SC = fit_chroma(SC, s_ords=SCgetOrds(sc.RING, 'SF|SD'), target_chroma = [1 1]) + """ if target_chroma is None: _, _, target_chroma = atlinopt(SC.IDEALRING, 0, []) if np.sum(np.isnan(target_chroma)): LOGGER.error('Target chromaticity must not contain NaN. Aborting.') return SC - if tune_knobs_ords is not None and tune_knobs_delta_k is not None: - for nFam in range(len(tune_knobs_ords)): - SC = set_magnet_setpoints(SC, tune_knobs_ords[nFam], tune_knobs_delta_k[nFam], False, 1, - method='add') # TODO quads here? + LOGGER.debug(f'Fitting chromaticities from {atlinopt(SC.RING, 0, [])[2]} to {target_chroma}.') # first two elements SP0 = np.zeros((len(s_ords), len(s_ords[0]))) # TODO can the lengts vary for nFam in range(len(s_ords)): From c596445048ca3560c0aa37c92cac15dd3589acc1 Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Tue, 26 Sep 2023 17:12:51 +0200 Subject: [PATCH 07/24] revert changes --- pySC/correction/chroma.py | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/pySC/correction/chroma.py b/pySC/correction/chroma.py index 25d9ad3..dc6088d 100644 --- a/pySC/correction/chroma.py +++ b/pySC/correction/chroma.py @@ -10,27 +10,15 @@ def fit_chroma(SC, s_ords, target_chroma=None, init_step_size=np.array([2, 2]), xtol=1E-4, ftol=1E-3, tune_knobs_ords=None, tune_knobs_delta_k=None): - """ - Applies a chromaticity correction using two sextupole families. - Args: - SC: SimulatedCommissioning instance - s_ords: [1x2] array of sextupole ordinates {`[1 x NSF1],[1 x NSD2]`} - target_chroma (optional): Target chromaticity for correction. Default: chromaticity of 'SC.IDEALRING' - init_step_size ([1x2] array, optional): Initial step size for the solver. Default: [2,2] - xtol(int, optional): Step tolerance for solver. Default: 1e-4 - ftol(int, optional): Merit tolerance for solver. Default: 1e-4 - - Returns: - SC: SimulatedCommissioning instance with corrected chromaticity. - Example: - SC = fit_chroma(SC, s_ords=SCgetOrds(sc.RING, 'SF|SD'), target_chroma = [1 1]) - """ if target_chroma is None: _, _, target_chroma = atlinopt(SC.IDEALRING, 0, []) if np.sum(np.isnan(target_chroma)): LOGGER.error('Target chromaticity must not contain NaN. Aborting.') return SC - + if tune_knobs_ords is not None and tune_knobs_delta_k is not None: + for nFam in range(len(tune_knobs_ords)): + SC = set_magnet_setpoints(SC, tune_knobs_ords[nFam], tune_knobs_delta_k[nFam], False, 1, + method='add') # TODO quads here? LOGGER.debug(f'Fitting chromaticities from {atlinopt(SC.RING, 0, [])[2]} to {target_chroma}.') # first two elements SP0 = np.zeros((len(s_ords), len(s_ords[0]))) # TODO can the lengts vary for nFam in range(len(s_ords)): From 025d3def36b0039d124da8691f2ad0739410b078 Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Tue, 26 Sep 2023 17:13:03 +0200 Subject: [PATCH 08/24] revert changes --- pySC/correction/tune.py | 141 +++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 83 deletions(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index 5e1d5de..b42a816 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -10,93 +10,68 @@ from scipy.optimize import fmin from pySC.core.beam import beam_transmission, plot_transmission -from pySC.core.lattice_setting import set_magnet_setpoints from pySC.utils import logging_tools from pySC.utils.at_wrapper import atlinopt LOGGER = logging_tools.get_logger(__name__) -def tune_scan(SC, quad_ords, rel_quad_changes, target=1, n_points=60, do_plot=False, nParticles=None, nTurns=None, full_scan=False): - """ - Varies two quadrupole families to improve beam transmission, on a grid of relative setpoints specified in - `rel_quad_changes` in a spiral-like pattern to increase the beam transmission. - - Args: - SC: SimulatedCommissioning instance - quad_ords: [1x2] array of quadrupole ordinates {`[1 x NQ1],[1 x NQ2]`} - rel_quad_changes: [1x2] cell array of quadrupole setpoints {`[SP1_1,...,SP1_N1],[SP2_1,...,SP2_N2]`} with `N2=N1` - target(int, optional): Transmission target at `nTurns` - n_points(int, optional): Number of points for the scan - nParticles(int, optional): Number of particles used for tracking (for convenience, otherwise `SC.INJ.nParticles` is used) - nTurns(int, optional): Number of turns used for tracking (for convenience, otherwise `SC.INJ.nTurns` is used) - - full_scan( bool , optional): If false, the scan finishes as soon as the target is reached - do_plot( bool , optional): If true, beam transmission is plotted at every step - - Returns: - Updated SC structure with applied setpoints for maximised beam transmission - Relative setpoints which satisfied the target condition if reached, or the values which resulted in best transmission - Number of achieved turns - Turn-by-turn particle loss - - see also: *bpm_reading*, *generate_bunches* - """ - - if nParticles is None: - nParticles = SC.INJ.nParticles - if nTurns is None: - nTurns = SC.INJ.nTurns - nq = np.array([len(quad_ords[0]), len(quad_ords[1])], dtype=int) - nqsp = np.array([len(rel_quad_changes[0]), len(rel_quad_changes[1])], dtype=int) - max_turns = np.full((nqsp[0], nqsp[1]), np.nan) - transmission = np.full((nqsp[0], nqsp[1], nTurns), np.nan) - inds = _golden_donut_inds(np.floor_divide(nqsp, 2), n_points=n_points) - first_quads = [SC.RING[quad_ords[0][0]].FamName, SC.RING[quad_ords[1][0]].FamName] - ords = np.hstack(quad_ords) - for q1, q2 in inds.T: - q_setpoints = np.hstack((np.ones(nq[0]) * rel_quad_changes[0][q1], np.ones(nq[1]) * rel_quad_changes[1][q2])) - SC = set_magnet_setpoints(SC, ords, q_setpoints, False, 1, method='rel') - max_turns[q1, q2], surviving_fraction = beam_transmission(SC, nParticles=nParticles, nTurns=nTurns) - transmission[q1, q2, :] = 1 * surviving_fraction - SC = set_magnet_setpoints(SC, ords, 1 / q_setpoints, False, 1, method='rel') - - if do_plot: - f, ax = plot_scan(transmission[:, :, -1], max_turns, first_quads, rel_quad_changes) - ax[2] = plot_transmission(ax[2], surviving_fraction, nTurns, SC.INJ.beamLostAt) - f.tight_layout() - f.show() - - if not full_scan and transmission[q1, q2, -1] >= target: - setpoints = [rel_quad_changes[0][q1], rel_quad_changes[1][q2]] - LOGGER.info(f'Transmission target reached with:\n' - f' {first_quads[0]} SetPoint: {setpoints[0]:.4f}\n' - f' {first_quads[1]} SetPoint: {setpoints[1]:.4f}') - SC = set_magnet_setpoints(SC, ords, q_setpoints, False, 1, method='rel') - return SC, setpoints, max_turns, transmission - - testTrans = np.zeros(n_points) - testTurns = np.zeros(n_points) - for i in range(n_points): - testTrans[i] = transmission[inds[0, i], inds[1, i], -1] - testTurns[i] = max_turns[inds[0, i], inds[1, i]] - if np.max(testTurns) == 0: - raise RuntimeError('Fail, no transmission at all.\n') - max_ind = np.argmax(testTurns if np.max(testTrans) == 0 else testTrans) - if max_ind == 0: - LOGGER.warning('No improvement possible.\n') - setpoints = [rel_quad_changes[0][inds[0, max_ind]], rel_quad_changes[1][inds[1, max_ind]]] - if (max_transmission := np.max(testTrans)) == 0: - LOGGER.warning(f'No transmission at final turn at all. Maximum of {testTurns[max_ind]} turns reached with:\n' - f' {first_quads[0]} SetPoint: {setpoints[0]:.4f}\n' - f' {first_quads[1]} SetPoint: {setpoints[0]:.4f}') - else: - LOGGER.warning(f'Transmission target not reached. Best value ({max_transmission:.4f}) reached with:\n' - f' {first_quads[0]} SetPoint: {setpoints[0]:.4f}\n' - f' {first_quads[1]} SetPoint: {setpoints[0]:.4f}') - q_setpoints = np.hstack((setpoints[0] * np.ones(nq[0]), setpoints[1] * np.ones(nq[1]))) - SC = set_magnet_setpoints(SC, ords, q_setpoints, False, 1, method='rel') - return SC, setpoints, max_turns, transmission +def tune_scan(SC, quad_ords, rel_quad_changes, target=1, n_points=60, do_plot=False, nParticles=None, nTurns=None, + full_scan=False): + if nParticles is None: + nParticles = SC.INJ.nParticles + if nTurns is None: + nTurns = SC.INJ.nTurns + nq = np.array([len(quad_ords[0]), len(quad_ords[1])], dtype=int) + nqsp = np.array([len(rel_quad_changes[0]), len(rel_quad_changes[1])], dtype=int) + max_turns = np.full((nqsp[0], nqsp[1]), np.nan) + transmission = np.full((nqsp[0], nqsp[1], nTurns), np.nan) + inds = _golden_donut_inds(np.floor_divide(nqsp, 2), n_points=n_points) + first_quads = [SC.RING[quad_ords[0][0]].FamName, SC.RING[quad_ords[1][0]].FamName] + ords = np.hstack(quad_ords) + for q1, q2 in inds.T: + q_setpoints = np.hstack((np.ones(nq[0]) * rel_quad_changes[0][q1], np.ones(nq[1]) * rel_quad_changes[1][q2])) + SC.set_magnet_setpoints(ords, q_setpoints, False, 1, method='rel') + max_turns[q1, q2], surviving_fraction = beam_transmission(SC, nParticles=nParticles, nTurns=nTurns) + transmission[q1, q2, :] = 1 * surviving_fraction + SC.set_magnet_setpoints(ords, 1 / q_setpoints, False, 1, method='rel') + + if do_plot: + f, ax = plot_scan(transmission[:, :, -1], max_turns, first_quads, rel_quad_changes) + ax[2] = plot_transmission(ax[2], surviving_fraction, nTurns, SC.INJ.beamLostAt) + f.tight_layout() + f.show() + + if not full_scan and transmission[q1, q2, -1] >= target: + setpoints = [rel_quad_changes[0][q1], rel_quad_changes[1][q2]] + LOGGER.info(f'Transmission target reached with:\n' + f' {first_quads[0]} SetPoint: {setpoints[0]:.4f}\n' + f' {first_quads[1]} SetPoint: {setpoints[1]:.4f}') + SC.set_magnet_setpoints(ords, q_setpoints, False, 1, method='rel') + return SC, setpoints, max_turns, transmission + + testTrans = np.zeros(n_points) + testTurns = np.zeros(n_points) + for i in range(n_points): + testTrans[i] = transmission[inds[0, i], inds[1, i], -1] + testTurns[i] = max_turns[inds[0, i], inds[1, i]] + if np.max(testTurns) == 0: + raise RuntimeError('Fail, no transmission at all.\n') + max_ind = np.argmax(testTurns if np.max(testTrans) == 0 else testTrans) + if max_ind == 0: + LOGGER.warning('No improvement possible.\n') + setpoints = [rel_quad_changes[0][inds[0, max_ind]], rel_quad_changes[1][inds[1, max_ind]]] + if (max_transmission := np.max(testTrans)) == 0: + LOGGER.warning(f'No transmission at final turn at all. Maximum of {testTurns[max_ind]} turns reached with:\n' + f' {first_quads[0]} SetPoint: {setpoints[0]:.4f}\n' + f' {first_quads[1]} SetPoint: {setpoints[0]:.4f}') + else: + LOGGER.warning(f'Transmission target not reached. Best value ({max_transmission:.4f}) reached with:\n' + f' {first_quads[0]} SetPoint: {setpoints[0]:.4f}\n' + f' {first_quads[1]} SetPoint: {setpoints[0]:.4f}') + q_setpoints = np.hstack((setpoints[0] * np.ones(nq[0]), setpoints[1] * np.ones(nq[1]))) + SC.set_magnet_setpoints(ords, q_setpoints, False, 1, method='rel') + return SC, setpoints, max_turns, transmission def _golden_donut_inds(r, n_points): @@ -142,13 +117,13 @@ def fit_tune(SC, q_ords, target_tune=None, xtol=1E-4, ftol=1E-3, fit_integer=Tru SP0[nFam][n] = SC.RING[q_ords[nFam][n]].SetPointB[1] fun = lambda x: _fit_tune_fun(SC, q_ords, x, SP0, target_tune, fit_integer) sol = fmin(fun, xtol=xtol, ftol=ftol) - SC = set_magnet_setpoints(SC, q_ords, sol + SP0, False, 1, method='abs', dipole_compensation=True) + SC.set_magnet_setpoints(q_ords, sol + SP0, False, 1, method='abs', dipole_compensation=True) LOGGER.debug(f' Final tune: [{tune(SC, fit_integer)}]\n Setpoints change: [{sol}]') return SC def _fit_tune_fun(SC, q_ords, setpoints, init_setpoints, target, fit_integer): - SC = set_magnet_setpoints(SC, q_ords, setpoints + init_setpoints, False, 1, method='abs', dipole_compensation=True) + SC.set_magnet_setpoints(q_ords, setpoints + init_setpoints, False, 1, method='abs', dipole_compensation=True) nu = tune(SC, fit_integer) return np.sqrt(np.mean((nu - target) ** 2)) From 21640de83a024c82ba12898c09c9dd38cf464393 Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Tue, 26 Sep 2023 17:17:45 +0200 Subject: [PATCH 09/24] corrected conflicts --- pySC/correction/tune.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index b42a816..cb38b9f 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -18,6 +18,29 @@ def tune_scan(SC, quad_ords, rel_quad_changes, target=1, n_points=60, do_plot=False, nParticles=None, nTurns=None, full_scan=False): + """ + Varies two quadrupole families to improve beam transmission, on a grid of relative setpoints specified in + `rel_quad_changes` in a spiral-like pattern to increase the beam transmission. + + Args: + SC: SimulatedCommissioning instance + quad_ords: [1x2] array of quadrupole ordinates {`[1 x NQ1],[1 x NQ2]`} + rel_quad_changes: [1x2] cell array of quadrupole setpoints {`[SP1_1,...,SP1_N1],[SP2_1,...,SP2_N2]`} with `N2=N1` + target(int, optional): Transmission target at `nTurns` + n_points(int, optional): Number of points for the scan + nParticles(int, optional): Number of particles used for tracking (for convenience, otherwise `SC.INJ.nParticles` is used) + nTurns(int, optional): Number of turns used for tracking (for convenience, otherwise `SC.INJ.nTurns` is used) + + full_scan( bool , optional): If false, the scan finishes as soon as the target is reached + do_plot( bool , optional): If true, beam transmission is plotted at every step + Returns: + Updated SC structure with applied setpoints for maximised beam transmission + Relative setpoints which satisfied the target condition if reached, or the values which resulted in best transmission + Number of achieved turns + Turn-by-turn particle loss + + see also: *bpm_reading*, *generate_bunches* + """ if nParticles is None: nParticles = SC.INJ.nParticles if nTurns is None: From 55013c64d518b5d1aeb13b7e491f0a22726cdb87 Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Thu, 28 Sep 2023 13:48:20 +0200 Subject: [PATCH 10/24] fit_tune documentation --- pySC/correction/tune.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index cb38b9f..8ba8460 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -130,6 +130,23 @@ def plot_scan(fin_trans, max_turns, first_quads, rel_quad_changes): def fit_tune(SC, q_ords, target_tune=None, xtol=1E-4, ftol=1E-3, fit_integer=True): + """ + Applies a tune correction using two quadrupole families. + Note: this is not beam based but assumes the tunes can be measured reasonably well. + + Args: + SC: SimulatedCommissioning instance + q_ords: [2xN] array or list [[1 x NQF],[1 x NQD]] of quadrupole ordinates + target_tune (optional, [1x2] array): Target tunes for correction. Default: tunes of 'SC.IDEALRING' + xtol(float, optional): Step tolerance for solver. Default: 1e-4 + ftol(float, optional): Merit tolerance for solver. Default: 1e-4 + fit_integer(bool, optional): Flag specifying if the integer part should be fitted as well. + + Returns: + SC: SimulatedCommissioning instance with corrected chromaticity. + Example: + SC = fit_chroma(SC, s_ords=[SCgetOrds(sc.RING, 'SF'), SCgetOrds(sc.RING, 'SD')], target_chroma=numpy.array([1,1])) + """ # TODO check if experimantally feasible if target_tune is None: target_tune = tune(SC, fit_integer, ideal=True) From b41ef802dfc8cbc35609d7b07d89da4eb243c49f Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Thu, 28 Sep 2023 16:04:16 +0200 Subject: [PATCH 11/24] added description of fit_tune --- pySC/correction/tune.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index 8ba8460..3391cbf 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -140,31 +140,32 @@ def fit_tune(SC, q_ords, target_tune=None, xtol=1E-4, ftol=1E-3, fit_integer=Tru target_tune (optional, [1x2] array): Target tunes for correction. Default: tunes of 'SC.IDEALRING' xtol(float, optional): Step tolerance for solver. Default: 1e-4 ftol(float, optional): Merit tolerance for solver. Default: 1e-4 - fit_integer(bool, optional): Flag specifying if the integer part should be fitted as well. + fit_integer(bool, optional): Flag specifying if the integer part should be fitted as well. Default: True. Returns: - SC: SimulatedCommissioning instance with corrected chromaticity. + SC: SimulatedCommissioning instance with corrected tunes. Example: - SC = fit_chroma(SC, s_ords=[SCgetOrds(sc.RING, 'SF'), SCgetOrds(sc.RING, 'SD')], target_chroma=numpy.array([1,1])) + SC = fit_tune(SC, q_ords=[SCgetOrds(sc.RING, 'QF'), SCgetOrds(sc.RING, 'QD')], target_tune=numpy.array([0.16,0.21])) """ # TODO check if experimantally feasible if target_tune is None: target_tune = tune(SC, fit_integer, ideal=True) - LOGGER.debug(f'Fitting tunes from [{tune(SC, fit_integer)}] to [{target_tune}].') - SP0 = np.zeros((len(q_ords), len(q_ords[0]))) # TODO can the lengts vary + LOGGER.debug(f'Fitting tunes from [{SC.RING.get_tune(get_integer=fit_integer)}] to [{target_tune}].') + SP0 = [0*q_ords[0], 0*q_ords[1]] #working with a list of two arrays for nFam in range(len(q_ords)): for n in range(len(q_ords[nFam])): SP0[nFam][n] = SC.RING[q_ords[nFam][n]].SetPointB[1] fun = lambda x: _fit_tune_fun(SC, q_ords, x, SP0, target_tune, fit_integer) sol = fmin(fun, xtol=xtol, ftol=ftol) - SC.set_magnet_setpoints(q_ords, sol + SP0, False, 1, method='abs', dipole_compensation=True) - LOGGER.debug(f' Final tune: [{tune(SC, fit_integer)}]\n Setpoints change: [{sol}]') + LOGGER.debug(f' Final tune: [{SC.RING.get_tune(get_integer=fit_integer)}]\n Setpoints change: [{sol}]') return SC def _fit_tune_fun(SC, q_ords, setpoints, init_setpoints, target, fit_integer): - SC.set_magnet_setpoints(q_ords, setpoints + init_setpoints, False, 1, method='abs', dipole_compensation=True) - nu = tune(SC, fit_integer) + SC.set_magnet_setpoints(q_ords[0], setpoints[0] + init_setpoints[0], False, 1, method='abs', dipole_compensation=True) + SC.set_magnet_setpoints(q_ords[1], setpoints[1] + init_setpoints[1], False, 1, method='abs', dipole_compensation=True) + nu = SC.RING.get_tune(get_integer=fit_integer) + nu = nu[0:2] return np.sqrt(np.mean((nu - target) ** 2)) From 71410bb81d247329a4726d0ea27edcb6cbd720cc Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Thu, 28 Sep 2023 17:06:37 +0200 Subject: [PATCH 12/24] changes in fit_tune --- pySC/correction/tune.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index 3391cbf..5f8a516 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -129,7 +129,7 @@ def plot_scan(fin_trans, max_turns, first_quads, rel_quad_changes): return f, [ax1, ax2, ax3] -def fit_tune(SC, q_ords, target_tune=None, xtol=1E-4, ftol=1E-3, fit_integer=True): +def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([1, 1]),xtol=1E-4, ftol=1E-3, fit_integer=True): """ Applies a tune correction using two quadrupole families. Note: this is not beam based but assumes the tunes can be measured reasonably well. @@ -138,6 +138,7 @@ def fit_tune(SC, q_ords, target_tune=None, xtol=1E-4, ftol=1E-3, fit_integer=Tru SC: SimulatedCommissioning instance q_ords: [2xN] array or list [[1 x NQF],[1 x NQD]] of quadrupole ordinates target_tune (optional, [1x2] array): Target tunes for correction. Default: tunes of 'SC.IDEALRING' + init_step_size ([1x2] array, optional): Initial step size for the solver. Default: [1,1] xtol(float, optional): Step tolerance for solver. Default: 1e-4 ftol(float, optional): Merit tolerance for solver. Default: 1e-4 fit_integer(bool, optional): Flag specifying if the integer part should be fitted as well. Default: True. @@ -156,7 +157,7 @@ def fit_tune(SC, q_ords, target_tune=None, xtol=1E-4, ftol=1E-3, fit_integer=Tru for n in range(len(q_ords[nFam])): SP0[nFam][n] = SC.RING[q_ords[nFam][n]].SetPointB[1] fun = lambda x: _fit_tune_fun(SC, q_ords, x, SP0, target_tune, fit_integer) - sol = fmin(fun, xtol=xtol, ftol=ftol) + sol = fmin(fun, init_step_size, xtol=xtol, ftol=ftol) LOGGER.debug(f' Final tune: [{SC.RING.get_tune(get_integer=fit_integer)}]\n Setpoints change: [{sol}]') return SC From 87f4b270c9b26b00cb86b3ba8c73dde430255f80 Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Fri, 29 Sep 2023 10:22:33 +0200 Subject: [PATCH 13/24] testing in progress --- pySC/correction/tune.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index 5f8a516..eb5305b 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -129,7 +129,7 @@ def plot_scan(fin_trans, max_turns, first_quads, rel_quad_changes): return f, [ax1, ax2, ax3] -def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([1, 1]),xtol=1E-4, ftol=1E-3, fit_integer=True): +def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.001, 0.001]),xtol=1E-4, ftol=1E-3, fit_integer=True): """ Applies a tune correction using two quadrupole families. Note: this is not beam based but assumes the tunes can be measured reasonably well. @@ -163,8 +163,16 @@ def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([1, 1]),xtol= def _fit_tune_fun(SC, q_ords, setpoints, init_setpoints, target, fit_integer): + print(SC.RING.get_tune(get_integer=fit_integer)) SC.set_magnet_setpoints(q_ords[0], setpoints[0] + init_setpoints[0], False, 1, method='abs', dipole_compensation=True) + SP0 = [0 * q_ords[0], 0 * q_ords[1]] # working with a list of two arrays + for nFam in range(len(q_ords)): + for n in range(len(q_ords[nFam])): + SP0[nFam][n] = SC.RING[q_ords[nFam][n]].SetPointB[1] + print(SP0) + print(SC.RING.get_tune(get_integer=fit_integer)) SC.set_magnet_setpoints(q_ords[1], setpoints[1] + init_setpoints[1], False, 1, method='abs', dipole_compensation=True) + print(SC.RING.get_tune(get_integer=fit_integer)) nu = SC.RING.get_tune(get_integer=fit_integer) nu = nu[0:2] return np.sqrt(np.mean((nu - target) ** 2)) From db8f62d1aad72bbf41ac3df16df6baecc655913d Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Thu, 5 Oct 2023 10:29:09 +0200 Subject: [PATCH 14/24] quadrupole input now allows N number of families, each with different length array --- pySC/correction/tune.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index eb5305b..2e63bd3 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -148,11 +148,12 @@ def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.001, 0.001 Example: SC = fit_tune(SC, q_ords=[SCgetOrds(sc.RING, 'QF'), SCgetOrds(sc.RING, 'QD')], target_tune=numpy.array([0.16,0.21])) """ - # TODO check if experimantally feasible if target_tune is None: target_tune = tune(SC, fit_integer, ideal=True) LOGGER.debug(f'Fitting tunes from [{SC.RING.get_tune(get_integer=fit_integer)}] to [{target_tune}].') - SP0 = [0*q_ords[0], 0*q_ords[1]] #working with a list of two arrays + SP0 = [] + for n in range(len(q_ords)) + SP0.append(np.zeros_like(q_ords[n])) #working with a list of two arrays for nFam in range(len(q_ords)): for n in range(len(q_ords[nFam])): SP0[nFam][n] = SC.RING[q_ords[nFam][n]].SetPointB[1] @@ -163,21 +164,12 @@ def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.001, 0.001 def _fit_tune_fun(SC, q_ords, setpoints, init_setpoints, target, fit_integer): - print(SC.RING.get_tune(get_integer=fit_integer)) - SC.set_magnet_setpoints(q_ords[0], setpoints[0] + init_setpoints[0], False, 1, method='abs', dipole_compensation=True) - SP0 = [0 * q_ords[0], 0 * q_ords[1]] # working with a list of two arrays for nFam in range(len(q_ords)): - for n in range(len(q_ords[nFam])): - SP0[nFam][n] = SC.RING[q_ords[nFam][n]].SetPointB[1] - print(SP0) - print(SC.RING.get_tune(get_integer=fit_integer)) - SC.set_magnet_setpoints(q_ords[1], setpoints[1] + init_setpoints[1], False, 1, method='abs', dipole_compensation=True) - print(SC.RING.get_tune(get_integer=fit_integer)) + SC.set_magnet_setpoints(q_ords[nFam], setpoints[nFam] + init_setpoints[nFam], False, 1, method='abs', dipole_compensation=True) nu = SC.RING.get_tune(get_integer=fit_integer) nu = nu[0:2] return np.sqrt(np.mean((nu - target) ** 2)) - def tune(SC, fit_integer: bool = False, ideal: bool = False): ring = SC.IDEALRING if ideal else SC.RING if fit_integer: From 12ce744ee258413bbc4fdab1f32a55b5f9e12424 Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Thu, 5 Oct 2023 10:56:21 +0200 Subject: [PATCH 15/24] test functions for tune and chroma correction --- tests/test_tune_chroma.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/test_tune_chroma.py diff --git a/tests/test_tune_chroma.py b/tests/test_tune_chroma.py new file mode 100644 index 0000000..9c1f24b --- /dev/null +++ b/tests/test_tune_chroma.py @@ -0,0 +1,19 @@ +import copy +import pytest +import numpy as np +from numpy.testing import assert_allclose, assert_equal +import at +import pySC +from pySC.correction.tune import tune_scan, fit_tune +from pySC.correction.chroma import fit_chroma +from pySC.core.simulated_commissioning import SimulatedCommissioning +from pySC.utils.sc_tools import SCgetOrds + + +def test_fit_tune(sc): + sc = fit_tune(sc, q_ords=[SCgetOrds(sc.RING, 'QF1'), SCgetOrds(sc.RING, 'QD')], target_tune=numpy.array([0.16, 0.21])) + return sc + +def test_fit_chroma(sc): + sc = fit_chroma() + return sc \ No newline at end of file From 1faa6b4b6112dc3fe6e0f6e124f5c47d64a09493 Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Thu, 5 Oct 2023 11:07:33 +0200 Subject: [PATCH 16/24] typo --- pySC/correction/tune.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index 2e63bd3..3c20443 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -152,7 +152,7 @@ def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.001, 0.001 target_tune = tune(SC, fit_integer, ideal=True) LOGGER.debug(f'Fitting tunes from [{SC.RING.get_tune(get_integer=fit_integer)}] to [{target_tune}].') SP0 = [] - for n in range(len(q_ords)) + for n in range(len(q_ords)): SP0.append(np.zeros_like(q_ords[n])) #working with a list of two arrays for nFam in range(len(q_ords)): for n in range(len(q_ords[nFam])): From 6c3935d3333f25412c15b27a2134aab4e1d4328a Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Thu, 5 Oct 2023 11:07:48 +0200 Subject: [PATCH 17/24] succesful test --- tests/test_tune_chroma.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/tests/test_tune_chroma.py b/tests/test_tune_chroma.py index 9c1f24b..3889c4b 100644 --- a/tests/test_tune_chroma.py +++ b/tests/test_tune_chroma.py @@ -1,19 +1,29 @@ import copy +import at import pytest import numpy as np -from numpy.testing import assert_allclose, assert_equal -import at -import pySC +from numpy.testing import assert_equal, assert_allclose +from tests.test_at_wrapper import at_lattice from pySC.correction.tune import tune_scan, fit_tune from pySC.correction.chroma import fit_chroma from pySC.core.simulated_commissioning import SimulatedCommissioning from pySC.utils.sc_tools import SCgetOrds -def test_fit_tune(sc): - sc = fit_tune(sc, q_ords=[SCgetOrds(sc.RING, 'QF1'), SCgetOrds(sc.RING, 'QD')], target_tune=numpy.array([0.16, 0.21])) - return sc - -def test_fit_chroma(sc): - sc = fit_chroma() +def test_fit_tune(at_lattice): + sc = SimulatedCommissioning(at_lattice) + sc.register_cavities(SCgetOrds(sc.RING, 'RFCav'), + FrequencyOffset=5E3, + VoltageOffset=5E3, + TimeLagOffset=0.5) + sc.register_magnets(SCgetOrds(sc.RING, 'QF'), HCM=1E-3, + CalErrorB=np.array([5E-2, 1E-3]), + MagnetOffset=200E-6 * np.array([1, 1, 0]), + MagnetRoll=200E-6 * np.array([1, 0, 0])) + sc.register_magnets(SCgetOrds(sc.RING, 'QD'), VCM=1E-3, + CalErrorA=np.array([5E-2, 0]), + CalErrorB=np.array([0, 1E-3]), + MagnetOffset=200E-6 * np.array([1, 1, 0]), + MagnetRoll=200E-6 * np.array([1, 0, 0])) + sc = fit_tune(sc, q_ords=[SCgetOrds(sc.RING, 'QF'), SCgetOrds(sc.RING, 'QD')], target_tune=np.array([0.16, 0.21])) return sc \ No newline at end of file From 2a4eb29456acb898e805fdc71da1c7e6113850a2 Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Thu, 5 Oct 2023 13:02:30 +0200 Subject: [PATCH 18/24] updated documentation --- pySC/correction/tune.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index 3c20443..a18e9da 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -136,7 +136,7 @@ def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.001, 0.001 Args: SC: SimulatedCommissioning instance - q_ords: [2xN] array or list [[1 x NQF],[1 x NQD]] of quadrupole ordinates + q_ords: [2xN] array or list [[1 x NQ1],[1 x NQ2], [1 x NQ3], ...] of quadrupole ordinates target_tune (optional, [1x2] array): Target tunes for correction. Default: tunes of 'SC.IDEALRING' init_step_size ([1x2] array, optional): Initial step size for the solver. Default: [1,1] xtol(float, optional): Step tolerance for solver. Default: 1e-4 From 3eb4a1b7f1a0690d195431e729bb186afaf83949 Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Thu, 5 Oct 2023 16:46:56 +0200 Subject: [PATCH 19/24] removed the function tune --- pySC/correction/tune.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index a18e9da..9bc69db 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -149,7 +149,7 @@ def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.001, 0.001 SC = fit_tune(SC, q_ords=[SCgetOrds(sc.RING, 'QF'), SCgetOrds(sc.RING, 'QD')], target_tune=numpy.array([0.16,0.21])) """ if target_tune is None: - target_tune = tune(SC, fit_integer, ideal=True) + target_tune = SC.IDEALRING.get_tune(get_integer=fit_integer) LOGGER.debug(f'Fitting tunes from [{SC.RING.get_tune(get_integer=fit_integer)}] to [{target_tune}].') SP0 = [] for n in range(len(q_ords)): @@ -168,12 +168,4 @@ def _fit_tune_fun(SC, q_ords, setpoints, init_setpoints, target, fit_integer): SC.set_magnet_setpoints(q_ords[nFam], setpoints[nFam] + init_setpoints[nFam], False, 1, method='abs', dipole_compensation=True) nu = SC.RING.get_tune(get_integer=fit_integer) nu = nu[0:2] - return np.sqrt(np.mean((nu - target) ** 2)) - -def tune(SC, fit_integer: bool = False, ideal: bool = False): - ring = SC.IDEALRING if ideal else SC.RING - if fit_integer: - ld, _, _ = atlinopt(ring, 0, range(len(ring) + 1)) - return ld[-1].mu / 2 / np.pi - _, nu, _ = atlinopt(ring, 0) - return nu + return np.sqrt(np.mean((nu - target) ** 2)) \ No newline at end of file From 1fc73bf61763b0627f10c98306b8dc1259a15b58 Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Wed, 11 Oct 2023 10:59:32 +0200 Subject: [PATCH 20/24] minor corrections --- pySC/correction/tune.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index 9bc69db..d23284d 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -129,7 +129,7 @@ def plot_scan(fin_trans, max_turns, first_quads, rel_quad_changes): return f, [ax1, ax2, ax3] -def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.001, 0.001]),xtol=1E-4, ftol=1E-3, fit_integer=True): +def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.1, 0.1]),xtol=1E-4, ftol=1E-3, fit_integer=True): """ Applies a tune correction using two quadrupole families. Note: this is not beam based but assumes the tunes can be measured reasonably well. @@ -138,7 +138,7 @@ def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.001, 0.001 SC: SimulatedCommissioning instance q_ords: [2xN] array or list [[1 x NQ1],[1 x NQ2], [1 x NQ3], ...] of quadrupole ordinates target_tune (optional, [1x2] array): Target tunes for correction. Default: tunes of 'SC.IDEALRING' - init_step_size ([1x2] array, optional): Initial step size for the solver. Default: [1,1] + init_step_size ([1x2] array, optional): Initial step size for the solver. Default: [0.1,0.1] xtol(float, optional): Step tolerance for solver. Default: 1e-4 ftol(float, optional): Merit tolerance for solver. Default: 1e-4 fit_integer(bool, optional): Flag specifying if the integer part should be fitted as well. Default: True. @@ -166,6 +166,5 @@ def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.001, 0.001 def _fit_tune_fun(SC, q_ords, setpoints, init_setpoints, target, fit_integer): for nFam in range(len(q_ords)): SC.set_magnet_setpoints(q_ords[nFam], setpoints[nFam] + init_setpoints[nFam], False, 1, method='abs', dipole_compensation=True) - nu = SC.RING.get_tune(get_integer=fit_integer) - nu = nu[0:2] + nu = SC.RING.get_tune(get_integer=fit_integer)[:2] return np.sqrt(np.mean((nu - target) ** 2)) \ No newline at end of file From 9e349c599d9508b3599ffb51f0913d2e119c229b Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Wed, 11 Oct 2023 11:16:34 +0200 Subject: [PATCH 21/24] added the assess line --- tests/test_tune_chroma.py | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/tests/test_tune_chroma.py b/tests/test_tune_chroma.py index 3889c4b..89753b0 100644 --- a/tests/test_tune_chroma.py +++ b/tests/test_tune_chroma.py @@ -1,29 +1,19 @@ -import copy -import at -import pytest import numpy as np -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose from tests.test_at_wrapper import at_lattice -from pySC.correction.tune import tune_scan, fit_tune -from pySC.correction.chroma import fit_chroma from pySC.core.simulated_commissioning import SimulatedCommissioning +from pySC.correction.tune import fit_tune from pySC.utils.sc_tools import SCgetOrds def test_fit_tune(at_lattice): - sc = SimulatedCommissioning(at_lattice) - sc.register_cavities(SCgetOrds(sc.RING, 'RFCav'), - FrequencyOffset=5E3, - VoltageOffset=5E3, - TimeLagOffset=0.5) - sc.register_magnets(SCgetOrds(sc.RING, 'QF'), HCM=1E-3, - CalErrorB=np.array([5E-2, 1E-3]), - MagnetOffset=200E-6 * np.array([1, 1, 0]), - MagnetRoll=200E-6 * np.array([1, 0, 0])) - sc.register_magnets(SCgetOrds(sc.RING, 'QD'), VCM=1E-3, - CalErrorA=np.array([5E-2, 0]), - CalErrorB=np.array([0, 1E-3]), - MagnetOffset=200E-6 * np.array([1, 1, 0]), - MagnetRoll=200E-6 * np.array([1, 0, 0])) - sc = fit_tune(sc, q_ords=[SCgetOrds(sc.RING, 'QF'), SCgetOrds(sc.RING, 'QD')], target_tune=np.array([0.16, 0.21])) - return sc \ No newline at end of file + SC = sc(at_lattice) + SC = fit_tune(SC, q_ords=[SCgetOrds(SC.RING, 'QF'), SCgetOrds(SC.RING, 'QD')], target_tune=SC.RING.get_tune()[:2]+[0.005,-0.005]) + assert_allclose(actual=SC.RING.get_tune()[:2], desired=SC.RING.get_tune()[:2]+[0.1,0.1], rtol=1e-2) + return sc + +def sc(at_lattice): + SC = SimulatedCommissioning(at_lattice) + SC.register_magnets(SCgetOrds(SC.RING, 'QF')) + SC.register_magnets(SCgetOrds(SC.RING, 'QD')) + return SC \ No newline at end of file From f10b0f1873aa2b56401e56877ffc3afcbad81dbd Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Wed, 11 Oct 2023 11:56:17 +0200 Subject: [PATCH 22/24] simplified function and updated default values --- pySC/correction/tune.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index d23284d..fe937d4 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -129,7 +129,7 @@ def plot_scan(fin_trans, max_turns, first_quads, rel_quad_changes): return f, [ax1, ax2, ax3] -def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.1, 0.1]),xtol=1E-4, ftol=1E-3, fit_integer=True): +def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.0001, 0.0001]),xtol=1E-3, ftol=1E-3, fit_integer=True): """ Applies a tune correction using two quadrupole families. Note: this is not beam based but assumes the tunes can be measured reasonably well. @@ -138,9 +138,9 @@ def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.1, 0.1]),x SC: SimulatedCommissioning instance q_ords: [2xN] array or list [[1 x NQ1],[1 x NQ2], [1 x NQ3], ...] of quadrupole ordinates target_tune (optional, [1x2] array): Target tunes for correction. Default: tunes of 'SC.IDEALRING' - init_step_size ([1x2] array, optional): Initial step size for the solver. Default: [0.1,0.1] - xtol(float, optional): Step tolerance for solver. Default: 1e-4 - ftol(float, optional): Merit tolerance for solver. Default: 1e-4 + init_step_size ([1x2] array, optional): Initial step size for the solver. Default: [0.0001,0.0001] + xtol(float, optional): Step tolerance for solver. Default: 1e-3 + ftol(float, optional): Merit tolerance for solver. Default: 1e-3 fit_integer(bool, optional): Flag specifying if the integer part should be fitted as well. Default: True. Returns: @@ -151,20 +151,14 @@ def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.1, 0.1]),x if target_tune is None: target_tune = SC.IDEALRING.get_tune(get_integer=fit_integer) LOGGER.debug(f'Fitting tunes from [{SC.RING.get_tune(get_integer=fit_integer)}] to [{target_tune}].') - SP0 = [] - for n in range(len(q_ords)): - SP0.append(np.zeros_like(q_ords[n])) #working with a list of two arrays - for nFam in range(len(q_ords)): - for n in range(len(q_ords[nFam])): - SP0[nFam][n] = SC.RING[q_ords[nFam][n]].SetPointB[1] - fun = lambda x: _fit_tune_fun(SC, q_ords, x, SP0, target_tune, fit_integer) + fun = lambda x: _fit_tune_fun(SC, q_ords, x,target_tune, fit_integer) sol = fmin(fun, init_step_size, xtol=xtol, ftol=ftol) LOGGER.debug(f' Final tune: [{SC.RING.get_tune(get_integer=fit_integer)}]\n Setpoints change: [{sol}]') return SC -def _fit_tune_fun(SC, q_ords, setpoints, init_setpoints, target, fit_integer): +def _fit_tune_fun(SC, q_ords, setpoints, target, fit_integer): for nFam in range(len(q_ords)): - SC.set_magnet_setpoints(q_ords[nFam], setpoints[nFam] + init_setpoints[nFam], False, 1, method='abs', dipole_compensation=True) + SC.set_magnet_setpoints(q_ords[nFam], setpoints[nFam], False, 1, method='add', dipole_compensation=True) nu = SC.RING.get_tune(get_integer=fit_integer)[:2] return np.sqrt(np.mean((nu - target) ** 2)) \ No newline at end of file From 9b58b0206dc53ab2e29ab12e3f8d8799a6f1412a Mon Sep 17 00:00:00 2001 From: Lina Hoummi Date: Wed, 11 Oct 2023 11:56:57 +0200 Subject: [PATCH 23/24] assert test function added, test succesful --- tests/test_tune_chroma.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_tune_chroma.py b/tests/test_tune_chroma.py index 89753b0..1629693 100644 --- a/tests/test_tune_chroma.py +++ b/tests/test_tune_chroma.py @@ -8,8 +8,8 @@ def test_fit_tune(at_lattice): SC = sc(at_lattice) - SC = fit_tune(SC, q_ords=[SCgetOrds(SC.RING, 'QF'), SCgetOrds(SC.RING, 'QD')], target_tune=SC.RING.get_tune()[:2]+[0.005,-0.005]) - assert_allclose(actual=SC.RING.get_tune()[:2], desired=SC.RING.get_tune()[:2]+[0.1,0.1], rtol=1e-2) + SC = fit_tune(SC, q_ords=[SCgetOrds(SC.RING, 'QF'), SCgetOrds(SC.RING, 'QD')], target_tune=SC.RING.get_tune(get_integer=True)[:2]+[0.005,-0.005], fit_integer=True) + assert_allclose(actual=SC.RING.get_tune()[:2], desired=SC.RING.get_tune()[:2]+[0.005,-0.005], rtol=1e-2) return sc def sc(at_lattice): From 962b6ba25c92b4ba579d5189d952425d2632d522 Mon Sep 17 00:00:00 2001 From: malina Date: Wed, 24 Jan 2024 18:56:51 +0100 Subject: [PATCH 24/24] Little fixes --- pySC/correction/tune.py | 9 +++++---- tests/test_tune.py | 24 ++++++++++++++++++++++++ tests/test_tune_chroma.py | 19 ------------------- 3 files changed, 29 insertions(+), 23 deletions(-) create mode 100644 tests/test_tune.py delete mode 100644 tests/test_tune_chroma.py diff --git a/pySC/correction/tune.py b/pySC/correction/tune.py index fe937d4..e2443f7 100644 --- a/pySC/correction/tune.py +++ b/pySC/correction/tune.py @@ -11,7 +11,7 @@ from pySC.core.beam import beam_transmission, plot_transmission from pySC.utils import logging_tools -from pySC.utils.at_wrapper import atlinopt + LOGGER = logging_tools.get_logger(__name__) @@ -149,9 +149,9 @@ def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.0001, 0.00 SC = fit_tune(SC, q_ords=[SCgetOrds(sc.RING, 'QF'), SCgetOrds(sc.RING, 'QD')], target_tune=numpy.array([0.16,0.21])) """ if target_tune is None: - target_tune = SC.IDEALRING.get_tune(get_integer=fit_integer) + target_tune = SC.IDEALRING.get_tune(get_integer=fit_integer)[:2] LOGGER.debug(f'Fitting tunes from [{SC.RING.get_tune(get_integer=fit_integer)}] to [{target_tune}].') - fun = lambda x: _fit_tune_fun(SC, q_ords, x,target_tune, fit_integer) + fun = lambda x: _fit_tune_fun(SC, q_ords, x, target_tune, fit_integer) sol = fmin(fun, init_step_size, xtol=xtol, ftol=ftol) LOGGER.debug(f' Final tune: [{SC.RING.get_tune(get_integer=fit_integer)}]\n Setpoints change: [{sol}]') return SC @@ -159,6 +159,7 @@ def fit_tune(SC, q_ords, target_tune=None, init_step_size=np.array([0.0001, 0.00 def _fit_tune_fun(SC, q_ords, setpoints, target, fit_integer): for nFam in range(len(q_ords)): + # TODO check the add method here SC.set_magnet_setpoints(q_ords[nFam], setpoints[nFam], False, 1, method='add', dipole_compensation=True) nu = SC.RING.get_tune(get_integer=fit_integer)[:2] - return np.sqrt(np.mean((nu - target) ** 2)) \ No newline at end of file + return np.sqrt(np.mean((nu - target) ** 2)) diff --git a/tests/test_tune.py b/tests/test_tune.py new file mode 100644 index 0000000..a936e2c --- /dev/null +++ b/tests/test_tune.py @@ -0,0 +1,24 @@ +import pytest +import numpy as np +from numpy.testing import assert_allclose +from tests.test_at_wrapper import at_lattice +from pySC.core.simulated_commissioning import SimulatedCommissioning +from pySC.correction.tune import fit_tune +from pySC.utils import sc_tools + + +def test_fit_tune(sc): + sc = fit_tune(sc, q_ords=[sc_tools.ords_from_regex(sc.RING, 'QF'), + sc_tools.ords_from_regex(sc.RING, 'QD')], + target_tune=sc.RING.get_tune(get_integer=True)[:2] + [0.005, -0.005], fit_integer=True) + # TODO with this tolerance it is not testing much + assert_allclose(actual=sc.RING.get_tune()[:2], desired=sc.RING.get_tune()[:2] + [0.005, -0.005], rtol=1e-2) + return sc + + +@pytest.fixture +def sc(at_lattice): + SC = SimulatedCommissioning(at_lattice) + SC.register_magnets(sc_tools.ords_from_regex(SC.RING, 'QF')) + SC.register_magnets(sc_tools.ords_from_regex(SC.RING, 'QD')) + return SC diff --git a/tests/test_tune_chroma.py b/tests/test_tune_chroma.py deleted file mode 100644 index 1629693..0000000 --- a/tests/test_tune_chroma.py +++ /dev/null @@ -1,19 +0,0 @@ -import numpy as np -from numpy.testing import assert_allclose -from tests.test_at_wrapper import at_lattice -from pySC.core.simulated_commissioning import SimulatedCommissioning -from pySC.correction.tune import fit_tune -from pySC.utils.sc_tools import SCgetOrds - - -def test_fit_tune(at_lattice): - SC = sc(at_lattice) - SC = fit_tune(SC, q_ords=[SCgetOrds(SC.RING, 'QF'), SCgetOrds(SC.RING, 'QD')], target_tune=SC.RING.get_tune(get_integer=True)[:2]+[0.005,-0.005], fit_integer=True) - assert_allclose(actual=SC.RING.get_tune()[:2], desired=SC.RING.get_tune()[:2]+[0.005,-0.005], rtol=1e-2) - return sc - -def sc(at_lattice): - SC = SimulatedCommissioning(at_lattice) - SC.register_magnets(SCgetOrds(SC.RING, 'QF')) - SC.register_magnets(SCgetOrds(SC.RING, 'QD')) - return SC \ No newline at end of file