|
1 | 1 | from . import _version |
2 | 2 | __version__ = _version.get_versions()['version'] |
3 | 3 |
|
4 | | -from pyemittance.pyemittance import PyEmittance |
| 4 | +from pyemittance.observer import Observer |
| 5 | +from pyemittance.data_handler import adapt_range, check_symmetry, find_inflection_pnt, add_measurements_btwn_pnts |
| 6 | +from pyemittance.emittance_calc import EmitCalc |
| 7 | +from pyemittance.emittance_calc_multiwire import MultiWireCalc |
| 8 | +from pyemittance.load_json_configs import load_configs |
5 | 9 |
|
| 10 | +# For setting quad back after scan is done |
| 11 | +from pyemittance.beam_io import MachineIO |
| 12 | + |
| 13 | +# TODO: set up unit testing for PyEmittance, EmitCalc, MultiWireCalc, Observer |
| 14 | +class PyEmittance: |
| 15 | + |
| 16 | + def __init__(self, |
| 17 | + emit_calc_type='quadscan', # 'quadscan' or 'multiwire' |
| 18 | + config_name='LCLS_OTR2', # make sure config corresponds to calc type |
| 19 | + config_dict=None, # supersedes json configs |
| 20 | + meas_type='OTRS', # only relevant if emit_calc_type=='quadscan' |
| 21 | + use_model=False, |
| 22 | + online=False |
| 23 | + ): |
| 24 | + |
| 25 | + self.emit_calc_type = emit_calc_type |
| 26 | + self.meas_type = meas_type |
| 27 | + |
| 28 | + # if config is not provided, use LCLS-OTR2 as default |
| 29 | + self.config_name = config_name |
| 30 | + self.config_dict = config_dict if config_dict else load_configs(self.config_name) |
| 31 | + |
| 32 | + # if running on machine, use_model=False |
| 33 | + self.use_model = use_model |
| 34 | + # only True if setting PVs |
| 35 | + self.online = online |
| 36 | + self.verbose = True |
| 37 | + |
| 38 | + # injector settings (SOL, CQ, SQ) if optimizing |
| 39 | + # TODO: remove injector settings options from all modules (optimizer needs to be separate) |
| 40 | + self.inj_config = None |
| 41 | + if self.emit_calc_type == 'quadscan': |
| 42 | + # initial rough quad scan |
| 43 | + self.quad_init = [-6, -4, -2, 0] |
| 44 | + |
| 45 | + # pyemittance method options |
| 46 | + self.save_runs = False |
| 47 | + self.calc_bmag = False |
| 48 | + self.show_plots = True |
| 49 | + if self.emit_calc_type == 'quadscan': |
| 50 | + self.adapt_ranges = True |
| 51 | + self.num_points = 7 |
| 52 | + self.check_sym = True |
| 53 | + self.infl_check = True |
| 54 | + self.add_pnts = True |
| 55 | + self.use_prev_meas = True |
| 56 | + self.quad_tol = 0.05 |
| 57 | + |
| 58 | + # simulation/model options |
| 59 | + # beamsize function from model |
| 60 | + self.get_bs_model = None |
| 61 | + self.add_noise = False |
| 62 | + |
| 63 | + # to save total number of points queried |
| 64 | + self.return_num_points = False |
| 65 | + |
| 66 | + def measure_emittance(self): |
| 67 | + if self.emit_calc_type == 'quadscan': |
| 68 | + self.measure_emittance_quad_scan() |
| 69 | + elif self.emit_calc_type == 'multiwire': |
| 70 | + self.measure_emittance_multiwire() |
| 71 | + else: |
| 72 | + raise Exception("Cannot perform measurement. 'emit_calc_type' needs to be 'quadscan' or 'multiwire'.") |
| 73 | + |
| 74 | + def measure_emittance_quad_scan(self): |
| 75 | + # get initial points from the observer |
| 76 | + o = Observer([], {'x': [], 'y': []}, {'x': [], 'y': []}) |
| 77 | + o.use_model = self.use_model |
| 78 | + o.inj_config = self.inj_config |
| 79 | + o.online = self.online |
| 80 | + o.meas_type = self.meas_type |
| 81 | + o.use_prev_meas = self.use_prev_meas |
| 82 | + o.tolerance = self.quad_tol |
| 83 | + o.config_name = self.config_name |
| 84 | + o.config_dict = self.config_dict |
| 85 | + |
| 86 | + # print warning |
| 87 | + if self.online and self.verbose: |
| 88 | + print("Running online!") |
| 89 | + else: |
| 90 | + print("Running offline.") |
| 91 | + |
| 92 | + # if using sim |
| 93 | + # set beamsize fn |
| 94 | + o.get_beamsizes_model = self.get_bs_model |
| 95 | + o.add_noise = self.add_noise |
| 96 | + |
| 97 | + energy = o.config_dict['beamline_info']['energy'] |
| 98 | + l_quad = o.config_dict['beamline_info']['l'] |
| 99 | + |
| 100 | + # Save init quad value |
| 101 | + io = MachineIO(self.config_name, self.config_dict, self.meas_type) |
| 102 | + io.online = self.online |
| 103 | + quad_init = io.getquad() |
| 104 | + |
| 105 | + # Get initial beamsizes (rough scan) |
| 106 | + bs_x_list, bs_y_list, bs_x_list_err, bs_y_list_err = o.measure_beam(self.quad_init) |
| 107 | + |
| 108 | + quad_range_x = self.quad_init |
| 109 | + quad_range_y = self.quad_init |
| 110 | + |
| 111 | + if self.adapt_ranges: |
| 112 | + quad_range_x = adapt_range(quad_range_x, |
| 113 | + bs_x_list, |
| 114 | + 'x', |
| 115 | + w=bs_x_list_err, |
| 116 | + energy=energy, |
| 117 | + l_eff=l_quad, |
| 118 | + num_points=self.num_points |
| 119 | + ) |
| 120 | + quad_range_y = adapt_range(quad_range_y, |
| 121 | + bs_y_list, |
| 122 | + 'y', |
| 123 | + w=bs_y_list_err, |
| 124 | + energy=energy, |
| 125 | + l_eff=l_quad, |
| 126 | + num_points=self.num_points |
| 127 | + ) |
| 128 | + |
| 129 | + new_beamsize_x = o.measure_beam(quad_range_x) |
| 130 | + bs_x_list, bs_x_list_err = new_beamsize_x[0], new_beamsize_x[2] |
| 131 | + new_beamsize_y = o.measure_beam(quad_range_y) |
| 132 | + bs_y_list, bs_y_list_err = new_beamsize_y[1], new_beamsize_y[3] |
| 133 | + |
| 134 | + if self.check_sym: |
| 135 | + add_points_x = check_symmetry(quad_range_x, bs_x_list, bs_x_list_err, 'x', |
| 136 | + bs_fn=o.measure_beam, add_meas=True) |
| 137 | + add_points_y = check_symmetry(quad_range_y, bs_y_list, bs_y_list_err, 'y', |
| 138 | + bs_fn=o.measure_beam, add_meas=True) |
| 139 | + |
| 140 | + if add_points_x is not None: |
| 141 | + quad_range_x = add_points_x[0] |
| 142 | + bs_x_list = add_points_x[1] |
| 143 | + bs_x_list_err = add_points_x[2] |
| 144 | + |
| 145 | + if add_points_y is not None: |
| 146 | + quad_range_y = add_points_y[0] |
| 147 | + bs_y_list = add_points_y[1] |
| 148 | + bs_y_list_err = add_points_y[2] |
| 149 | + |
| 150 | + if self.infl_check: |
| 151 | + left_x, right_x = find_inflection_pnt(quad_range_x, |
| 152 | + bs_x_list, |
| 153 | + show_plots=self.show_plots |
| 154 | + ) |
| 155 | + left_y, right_y = find_inflection_pnt(quad_range_y, |
| 156 | + bs_y_list, |
| 157 | + show_plots=self.show_plots |
| 158 | + ) |
| 159 | + |
| 160 | + # truncate data |
| 161 | + quad_range_x = quad_range_x[left_x:right_x] |
| 162 | + bs_x_list = bs_x_list[left_x:right_x] |
| 163 | + bs_x_list_err = bs_x_list_err[left_x:right_x] |
| 164 | + |
| 165 | + quad_range_y = quad_range_y[left_y:right_y] |
| 166 | + bs_y_list = bs_y_list[left_y:right_y] |
| 167 | + bs_y_list_err = bs_y_list_err[left_y:right_y] |
| 168 | + |
| 169 | + if self.add_pnts: |
| 170 | + quad_range_x, bs_x_list, bs_x_list_err = add_measurements_btwn_pnts(quad_range_x, |
| 171 | + bs_x_list, |
| 172 | + bs_x_list_err, |
| 173 | + self.num_points, |
| 174 | + 'x', |
| 175 | + bs_fn=o.measure_beam |
| 176 | + ) |
| 177 | + quad_range_y, bs_y_list, bs_y_list_err = add_measurements_btwn_pnts(quad_range_y, |
| 178 | + bs_y_list, |
| 179 | + bs_y_list_err, |
| 180 | + self.num_points, |
| 181 | + 'y', |
| 182 | + bs_fn=o.measure_beam |
| 183 | + ) |
| 184 | + |
| 185 | + # Put quad back |
| 186 | + io.online = self.online |
| 187 | + io.setquad(quad_init) |
| 188 | + |
| 189 | + # Finally get emittance |
| 190 | + ef = EmitCalc({'x': quad_range_x, 'y': quad_range_y}, |
| 191 | + {'x': bs_x_list, 'y': bs_y_list}, |
| 192 | + {'x': bs_x_list_err, 'y': bs_y_list_err}, |
| 193 | + config_dict=o.config_dict, |
| 194 | + config_name=o.config_name |
| 195 | + ) |
| 196 | + ef.plot = self.show_plots |
| 197 | + ef.save_runs = self.save_runs |
| 198 | + ef.calc_bmag = self.calc_bmag |
| 199 | + |
| 200 | + # get normalized transverse emittance |
| 201 | + ef.get_emit() |
| 202 | + |
| 203 | + # save total number of points queried |
| 204 | + if self.return_num_points: |
| 205 | + ef.out_dict["total_points_measured"] = len(o.quad_meas) |
| 206 | + |
| 207 | + return ef.out_dict |
| 208 | + |
| 209 | + def measure_emittance_multiwire(self): |
| 210 | + # get wire measurements from the observer |
| 211 | + o = Observer() |
| 212 | + o.inj_config = self.inj_config |
| 213 | + o.online = self.online |
| 214 | + o.meas_type = 'WIRE' |
| 215 | + o.emit_calc_type = self.emit_calc_type |
| 216 | + o.config_name = self.config_name |
| 217 | + o.config_dict = self.config_dict |
| 218 | + |
| 219 | + # print warning |
| 220 | + if self.online and self.verbose: |
| 221 | + print("Running online!") |
| 222 | + else: |
| 223 | + print("Running offline.") |
| 224 | + |
| 225 | + # Get beamsizes |
| 226 | + # This returns lists now: xrms, yrms, xrms_err, yrms_err |
| 227 | + # TODO: implement error dict to be returned to track multiwire success |
| 228 | + bs_x_list, bs_y_list, bs_x_list_err, bs_y_list_err = o.multiwire_measure_beam() |
| 229 | + |
| 230 | + # Finally get emittance |
| 231 | + ef = MultiWireCalc(beam_vals={'x': bs_x_list, 'y': bs_y_list}, |
| 232 | + beam_vals_err={'x': bs_x_list_err, 'y': bs_y_list_err}, |
| 233 | + config_dict=o.config_dict, |
| 234 | + config_name=o.config_name |
| 235 | + ) |
| 236 | + ef.plot = self.show_plots |
| 237 | + ef.save_runs = self.save_runs |
| 238 | + ef.calc_bmag = self.calc_bmag |
| 239 | + |
| 240 | + # get normalized transverse emittance |
| 241 | + ef.get_emit() |
| 242 | + |
| 243 | + return ef.out_dict |
0 commit comments