def _do_clean(main_input, clean_input, bpm_datas, file_date, model_tfs): usvs, all_bad_bpms, bpm_ress = {}, {}, {} clean_writer = output_handler.CleanedAsciiWritter(main_input, file_date) for plane in ("x", "y"): bpm_data = bpm_datas[plane] bpm_data, bpms_not_in_model = _get_only_model_bpms(bpm_data, model_tfs) if bpm_data.empty: raise AssertionError("Check BPMs names! None of the BPMs was found in the model!") bad_bpms = [] with timeit(lambda spanned: LOGGER.debug("Time for filtering: %s", spanned)): bpm_data, bad_bpms_clean = clean.clean(bpm_data, clean_input, file_date,) with timeit(lambda spanned: LOGGER.debug("Time for SVD clean: %s", spanned)): bpm_data, bpm_res, bad_bpms_svd, usv = clean.svd_clean(bpm_data, clean_input,) bpm_ress[plane] = bpm_res bad_bpms.extend(bpms_not_in_model) bad_bpms.extend(bad_bpms_clean) bad_bpms.extend(bad_bpms_svd) all_bad_bpms[plane] = bad_bpms setattr(clean_writer, "samples_matrix_" + plane, bpm_data) if plane == "x": dpp = _calc_dp_over_p(main_input, bpm_data) bpm_datas[plane] = bpm_data usvs[plane] = usv if clean_input.write_clean: clean_writer.dpp = dpp clean_writer.write() return usvs, all_bad_bpms, bpm_ress, dpp
def _do_harpy(main_input, harpy_input, bpm_datas, usvs, model_tfs, bpm_ress, dpp, all_bad_bpms): lin_frames = {} for plane in ("x", "y"): bpm_data, usv = bpm_datas[plane], usvs[plane] lin_frames[plane] = pd.DataFrame( index=bpm_data.index, data=OrderedDict([ ("NAME", bpm_data.index), ("S", model_tfs.set_index("NAME").loc[bpm_data.index, "S"]) ])) with timeit(lambda spanned: LOGGER.debug("Time for orbit_analysis: %s", spanned)): lin_frames[plane] = _get_orbit_data(lin_frames[plane], bpm_data, bpm_ress[plane]) bpm_datas[plane], usvs[plane] = _prepare_data_for_harpy(bpm_data, usv) with timeit(lambda spanned: LOGGER.debug("Time for harmonic_analysis: %s", spanned)): harpy_iterator = harpy.harpy( harpy_input, bpm_datas["x"], usvs["x"], bpm_datas["y"], usvs["y"], ) dpp_amp = None lin = {} for plane in ("x", "y"): harpy_results, spectr, bad_bpms_summaries = harpy_iterator.next() lin_frame = lin_frames[plane] lin_frame = lin_frame.loc[harpy_results.index].join(harpy_results) if harpy_input.is_free_kick: bpm_data = bpm_datas[plane] lin_frame = _kick_phase_correction(bpm_data, lin_frame, plane) if harpy_input.tunez > 0.0 and plane == "x": dpp_amp, _, _ = chroma.get_dpoverp_amp(model_tfs, lin_frame, bpm_datas["x"]) lin_frame = _sync_phase(lin_frame, plane) lin_frame = _rescale_amps_to_main_line(lin_frame, plane) lin_frame = _add_resonances_noise(lin_frame, plane) lin_frame = lin_frame.sort_values('S', axis=0, ascending=True) headers = _compute_headers(lin_frame, plane, dpp, dpp_amp) output_handler.write_harpy_output(main_input, lin_frame, headers, spectr, plane) all_bad_bpms[plane].extend(bad_bpms_summaries) lin[plane] = tfs.TfsDataFrame(lin_frame, headers=headers) return all_bad_bpms, lin
def _calc_beta_response(self): """ Response Matrix for delta beta. Eq. A35 -> Eq. B45 in [#FranchiAnalyticformulasrapid2017]_ """ LOG.debug("Calculate Beta Response Matrix") with timeit(lambda t: LOG.debug(" Time needed: {:f}s".format(t))): tw = self._twiss adv = self._phase_advances el_out = self._elements_out k1_el = self._elements_in["K1L"] dbeta = dict.fromkeys(["X", "Y"]) for plane in ["X", "Y"]: col_beta = "BET" + plane q = tw.Q1 if plane == "X" else tw.Q2 coeff_sign = -1 if plane == "X" else 1 pi2tau = 2 * np.pi * tau(adv[plane].loc[k1_el, el_out], q) dbeta[plane] = tfs.TfsDataFrame( tw.loc[el_out, col_beta].values[None, :] * tw.loc[k1_el, col_beta].values[:, None] * np.cos(2 * pi2tau.values) * (coeff_sign / (2 * np.sin(2 * np.pi * q))), index=k1_el, columns=el_out).transpose() return dict_mul(self._direction, dbeta)
def run_all(main_input, clean_input, harpy_input, optics_input, to_log): with timeit( lambda spanned: LOGGER.info("Total time for file: %s", spanned)): if (not main_input.write_raw and clean_input is None and harpy_input is None): LOGGER.error("No file has been choosen to be writen!") return _setup_file_log_handler(main_input) LOGGER.debug(to_log) tbt_files = [ turn_by_turn_reader.read_tbt_file(input_file.strip()) for input_file in main_input.file.strip("\"").split(",") ] lins = [] for tbt_file in tbt_files: lins.extend([ run_all_for_file(bunchfile, this_main_input, clean_input, harpy_input) for this_main_input, bunchfile in output_handler.handle_multibunch(main_input, tbt_file) ]) if optics_input is not None: inputs = measure_optics.InputFiles(lins) iotools.create_dirs(optics_input.outputdir) calibrations = measure_optics._copy_calibration_files( optics_input.outputdir, optics_input.calibrationdir) inputs.calibrate(calibrations) measure_optics.measure_optics(inputs, optics_input)
def run_all(main_input, clean_input, harpy_input, optics_input, to_log): with timeit(lambda spanned: LOGGER.info("Total time for file: %s", spanned)): if (not main_input.write_raw and clean_input is None and harpy_input is None): LOGGER.error("No file has been choosen to be writen!") return _setup_file_log_handler(main_input) LOGGER.debug(to_log) tbt_files = [turn_by_turn_reader.read_tbt_file(input_file.strip()) for input_file in main_input.file.strip("\"").split(",")] lins = [] for tbt_file in tbt_files: lins.extend( [run_all_for_file(bunchfile, this_main_input, clean_input, harpy_input) for this_main_input, bunchfile in output_handler.handle_multibunch(main_input, tbt_file)] ) if optics_input is not None: inputs = measure_optics.InputFiles(lins) iotools.create_dirs(optics_input.outputdir) calibrations = measure_optics._copy_calibration_files(optics_input.outputdir, optics_input.calibrationdir) inputs.calibrate(calibrations) measure_optics.measure_optics(inputs, optics_input)
def _calc_tune_response(self): """ Response vectors for Tune. Eq. 7 in [#TomasReviewlinearoptics2017]_ """ LOG.debug("Calculate Tune Response Matrix") with timeit(lambda t: LOG.debug(" Time needed: {:f}s".format(t))): tw = self._twiss k1_el = self._elements_in["K1L"] if len(k1_el) > 0: dtune = dict.fromkeys(["X", "Y"]) dtune["X"] = 1 / (4 * np.pi) * tw.loc[k1_el, ["BETX"]].transpose() dtune["X"].index = ["DQX"] dtune["Y"] = -1 / (4 * np.pi) * tw.loc[k1_el, ["BETY"]].transpose() dtune["Y"].index = ["DQY"] else: LOG.debug( " No 'K1L' variables found. Tune Response will be empty.") dtune = { "X": tfs.TfsDataFrame(None, index=["DQX"]), "Y": tfs.TfsDataFrame(None, index=["DQY"]) } return dict_mul(self._direction, dtune)
def _calc_chromatic_term(self): """ Calculates the chromatic term which is common to all chromatic equations """ LOG.debug("Calculating Chromatic Term.") self._check_k_columns(["K1L", "K2L", "K2SL"]) res = self._results_df tw = self.twiss_df with timeit(lambda t: LOG.debug(" Time needed: {:f}".format(t))): mask = (tw['K1L'] != 0) | (tw['K2L'] != 0) | (tw['K2SL'] != 0) if "DX" in tw and "DY" in tw: LOG.debug( "Dispersion values found in model. Used for chromatic calculations" ) sum_term = tw.loc[mask, 'K1L'] - \ (tw.loc[mask, 'K2L'] * tw.loc[mask, 'DX']) + \ (tw.loc[mask, 'K2SL'] * tw.loc[mask, 'DY']) else: LOG.info( "Dispersion values NOT found in model. Using analytic values." ) if "DX" not in res or "DY" not in res: self.calc_linear_dispersion() sum_term = tw.loc[mask, 'K1L'] - \ (tw.loc[mask, 'K2L'] * res.loc[mask, 'DX']) + \ (tw.loc[mask, 'K2SL'] * res.loc[mask, 'DY']) res['CHROMX'] = sum_term * tw.loc[mask, 'BETX'] res['CHROMY'] = sum_term * tw.loc[mask, 'BETY'] LOG.debug("Chromatic Term Calculated.")
def calc_chromatic_beating(self): """ Calculate the Chromatic Beating Eq. 36 in [#FranchiAnalyticformulasrapid2017]_ """ tw = self.twiss_df res = self._results_df phs_adv = self.get_phase_adv() if 'CHROMX' not in res: self._calc_chromatic_term() LOG.debug("Calculating Chromatic Beating") with timeit(lambda t: LOG.debug(" Time needed: {:f}".format(t))): chromx = res['CHROMX'].dropna() chromy = res['CHROMY'].dropna() res['DBEATX'] = self._chromatic_beating( chromx, tau(phs_adv['X'].loc[chromx.index, :], tw.Q1), tw.Q1).transpose() - 1 res['DBEATY'] = -self._chromatic_beating( chromy, tau(phs_adv['Y'].loc[chromy.index, :], tw.Q2), tw.Q2).transpose() - 1 LOG.debug(" Pk2Pk chromatic beating DBEATX: {:g}".format( res['DBEATX'].max() - res['DBEATX'].min())) LOG.debug(" Pk2Pk chromatic beating DBEATY: {:g}".format( res['DBEATY'].max() - res['DBEATY'].min())) self._log_added('DBEATX', 'DBEATY')
def __init__(self, accel_inst, variable_categories, varmap_or_path, at_elements='bpms'): LOG.debug("Initializing TwissResponse.") with timeit(lambda t: LOG.debug(" Time initializing TwissResponse: {:f}s".format(t))): # Get input self._twiss = self._get_model_twiss(accel_inst) self._variables = accel_inst.get_variables(classes=variable_categories) self._var_to_el = self._get_variable_mapping(varmap_or_path) self._elements_in = self._get_input_elements() self._elements_out = self._get_output_elements(at_elements) self._direction = self._get_direction(accel_inst) # calculate all phase advances self._phase_advances = get_phase_advances(self._twiss) # All responses are calcluated as needed, see getters below! # slots for response matrices self._beta = None self._dispersion = None self._phase = None self._phase_adv = None self._tune = None self._coupling = None self._beta_beat = None self._norm_dispersion = None # slots for mapped response matrices self._coupling_mapped = None self._beta_mapped = None self._dispersion_mapped = None self._phase_mapped = None self._phase_adv_mapped = None self._tune_mapped = None self._beta_beat_mapped = None self._norm_dispersion_mapped = None
def _calc_coupling_response(self): """ Response Matrix for coupling. Eq. 10 in [#FranchiAnalyticformulasrapid2017]_ """ LOG.debug("Calculate Coupling Matrix") with timeit(lambda t: LOG.debug(" Time needed: {:f}s".format(t))): tw = self._twiss adv = self._phase_advances el_out = self._elements_out k1s_el = self._elements_in["K1SL"] dcoupl = dict.fromkeys(["1001", "1010"]) i2pi = 2j * np.pi phx = dphi(adv['X'].loc[k1s_el, el_out], tw.Q1).values phy = dphi(adv['Y'].loc[k1s_el, el_out], tw.Q2).values bet_term = np.sqrt(tw.loc[k1s_el, "BETX"].values * tw.loc[k1s_el, "BETY"].values) for plane in ["1001", "1010"]: phs_sign = -1 if plane == "1001" else 1 dcoupl[plane] = tfs.TfsDataFrame( bet_term[:, None] * np.exp(i2pi * (phx + phs_sign * phy)) / (4 * (1 - np.exp(i2pi * (tw.Q1 + phs_sign * tw.Q2)))), index=k1s_el, columns=el_out).transpose() return dict_mul(self._direction, dcoupl)
def _do_harpy(main_input, harpy_input, bpm_datas, usvs, model_tfs, bpm_ress, dpp, all_bad_bpms): lin_frames = {} for plane in ("x", "y"): bpm_data, usv = bpm_datas[plane], usvs[plane] lin_frames[plane] = pd.DataFrame( index=bpm_data.index, data=OrderedDict([ ("NAME", bpm_data.index), ("S", model_tfs.set_index("NAME").loc[bpm_data.index, "S"]) ]) ) with timeit(lambda spanned: LOGGER.debug("Time for orbit_analysis: %s", spanned)): lin_frames[plane] = _get_orbit_data(lin_frames[plane], bpm_data, bpm_ress[plane]) bpm_datas[plane], usvs[plane] = _prepare_data_for_harpy(bpm_data, usv) with timeit(lambda spanned: LOGGER.debug("Time for harmonic_analysis: %s", spanned)): harpy_iterator = harpy.harpy( harpy_input, bpm_datas["x"], usvs["x"], bpm_datas["y"], usvs["y"], ) dpp_amp = None lin = {} for plane in ("x", "y"): harpy_results, spectr, bad_bpms_summaries = harpy_iterator.next() lin_frame = lin_frames[plane] lin_frame = lin_frame.loc[harpy_results.index].join(harpy_results) if harpy_input.is_free_kick: bpm_data = bpm_datas[plane] lin_frame = _kick_phase_correction(bpm_data, lin_frame, plane) if harpy_input.tunez > 0.0 and plane == "x": dpp_amp, _, _ = chroma.get_dpoverp_amp(model_tfs, lin_frame, bpm_datas["x"]) lin_frame = _sync_phase(lin_frame, plane) lin_frame = _rescale_amps_to_main_line(lin_frame, plane) lin_frame = _add_resonances_noise(lin_frame, plane) lin_frame = lin_frame.sort_values('S', axis=0, ascending=True) headers = _compute_headers(lin_frame, plane, dpp, dpp_amp) output_handler.write_harpy_output( main_input, lin_frame, headers, spectr, plane ) all_bad_bpms[plane].extend(bad_bpms_summaries) lin[plane] = tfs.TfsDataFrame(lin_frame, headers=headers) return all_bad_bpms, lin
def _calc_dispersion_response(self): """ Response Matrix for delta normalized dispersion Eq. 25-27 in [#FranchiAnalyticformulasrapid2017]_ But w/o the assumtion :math:`\delta K_1 = 0` from Appendix B.1 """ LOG.debug("Calculate Dispersion Response Matrix") with timeit(lambda t: LOG.debug(" Time needed: {:f}".format(t))): tw = self._twiss adv = self._phase_advances el_out = self._elements_out els_in = self._elements_in sign_map = { "X": {"K0L": 1, "K1L": -1, "K1SL": 1, }, "Y": {"K0SL": -1, "K1L": 1, "K1SL": 1, }, } col_disp_map = { "X": {"K1L": "DX", "K1SL": "DY", }, "Y": {"K1L": "DY", "K1SL": "DX", }, } q_map = {"X": tw.Q1, "Y": tw.Q2} disp_resp = dict.fromkeys(["{p:s}_{t:s}".format(p=p, t=t) for p in sign_map for t in sign_map[p]]) for plane in sign_map: q = q_map[plane] col_beta = "BET{}".format(plane) el_types = sign_map[plane].keys() els_per_type = [els_in[el_type] for el_type in el_types] coeff = np.sqrt(tw.loc[el_out, col_beta].values) / (2 * np.sin(np.pi * q)) for el_in, el_type in zip(els_per_type, el_types): coeff_sign = sign_map[plane][el_type] out_str = "{p:s}_{t:s}".format(p=plane, t=el_type) if len(el_in): pi2tau = 2 * np.pi * tau(adv[plane].loc[el_in, el_out], q) bet_term = np.sqrt(tw.loc[el_in, col_beta]) try: col_disp = col_disp_map[plane][el_type] except KeyError: pass else: bet_term *= tw.loc[el_in, col_disp] disp_resp[out_str] = (coeff_sign * coeff[None, :] * bet_term[:, None] * np.cos(pi2tau) ).transpose() else: LOG.debug( " No '{:s}' variables found. ".format(el_type) + "Dispersion Response '{:s}' will be empty.".format(out_str)) disp_resp[out_str] = tfs.TfsDataFrame(None, index=el_out) return dict_mul(self._direction, disp_resp)
def calc_cmatrix(self): """ Calculates C matrix and Coupling and Gamma from it. See [#CalagaBetatroncouplingMerging2005]_ """ tw = self.twiss_df res = self._results_df LOG.debug("Calculating CMatrix.") with timeit(lambda t: LOG.debug(" CMatrix calculated in {:f}s".format(t))): j = np.array([[0., 1.], [-1., 0.]]) rs = np.reshape(tw.as_matrix(columns=["R11", "R12", "R21", "R22"]), (len(tw), 2, 2)) cs = np.einsum("ij,kjn,no->kio", -j, np.transpose(rs, axes=(0, 2, 1)), j) cs = np.einsum("k,kij->kij", (1 / np.sqrt(1 + np.linalg.det(rs))), cs) g11a = 1 / np.sqrt(tw.loc[:, "BETX"]) g12a = np.zeros(len(tw)) g21a = tw.loc[:, "ALFX"] / np.sqrt(tw.loc[:, "BETX"]) g22a = np.sqrt(tw.loc[:, "BETX"]) gas = np.reshape(np.array([g11a, g12a, g21a, g22a]).T, (len(tw), 2, 2)) ig11b = np.sqrt(tw.loc[:, "BETY"]) ig12b = np.zeros(len(tw)) ig21b = -tw.loc[:, "ALFY"] / np.sqrt(tw.loc[:, "BETY"]) ig22b = 1. / np.sqrt(tw.loc[:, "BETY"]) igbs = np.reshape(np.array([ig11b, ig12b, ig21b, ig22b]).T, (len(tw), 2, 2)) cs = np.einsum("kij,kjl,kln->kin", gas, cs, igbs) gammas = np.sqrt(1 - np.linalg.det(cs)) res.loc[:, "GAMMA_C"] = gammas res.loc[:, "F1001_C"] = ((cs[:, 0, 0] + cs[:, 1, 1]) * 1j + (cs[:, 0, 1] - cs[:, 1, 0])) / 4 / gammas res.loc[:, "F1010_C"] = ((cs[:, 0, 0] - cs[:, 1, 1]) * 1j + (-cs[:, 0, 1]) - cs[:, 1, 0]) / 4 / gammas res.loc[:, "C11"] = cs[:, 0, 0] res.loc[:, "C12"] = cs[:, 0, 1] res.loc[:, "C21"] = cs[:, 1, 0] res.loc[:, "C22"] = cs[:, 1, 1] LOG.debug(" Average coupling amplitude |F1001|: {:g}".format(np.mean( np.abs(res.loc[:, "F1001_C"])))) LOG.debug(" Average coupling amplitude |F1010|: {:g}".format(np.mean( np.abs(res.loc[:, "F1010_C"])))) LOG.debug(" Average gamma: {:g}".format(np.mean( np.abs(res.loc[:, "GAMMA_C"])))) self._log_added('GAMMA_C', 'F1001_C', 'F1010_C', 'C11', 'C12', 'C21', 'C22')
def _add_coupling(dict_of_tfs): """ Adds coupling to the tfs. QUICK FIX VIA LOOP!""" with timeit(lambda t: LOG.debug(" Time adding coupling: {:f}s".format(t))): for var in dict_of_tfs: twopt = TwissOptics(dict_of_tfs[var]) cpl = twopt.get_coupling("cmatrix") dict_of_tfs[var]["1001"] = cpl["F1001"] dict_of_tfs[var]["1010"] = cpl["F1010"] return dict_of_tfs
def _add_coupling(dict_of_tfs): """ Adds coupling to the tfs. QUICK FIX VIA LOOP!""" with timeit( lambda t: LOG.debug(" Time adding coupling: {:f}s".format(t))): for var in dict_of_tfs: twopt = TwissOptics(dict_of_tfs[var], keep_all_elem=True) cpl = twopt.get_coupling("cmatrix") dict_of_tfs[var]["1001"] = cpl["F1001"] dict_of_tfs[var]["1010"] = cpl["F1010"] return dict_of_tfs
def _calc_phase_response(self): """ Response Matrix for delta DPhi. Eq. 28 in [#FranchiAnalyticformulasrapid2017]_ Reduced to only delta phase. --> w = 0: DPhi(z,j) = DPhi(x, 0->j) This calculation could also be achieved by applying np.cumsum to the DataFrames of _calc_phase_adv_response() (tested!), but _calc_phase_response() is about 4x faster. """ LOG.debug("Calculate Phase Response Matrix") with timeit(lambda t: LOG.debug(" Time needed: {:f}s".format(t))): tw = self._twiss adv = self._phase_advances k1_el = self._elements_in["K1L"] el_out = self._elements_out if len(k1_el) > 0: dmu = dict.fromkeys(["X", "Y"]) pi = tfs.TfsDataFrame( tw['S'][:, None] < tw['S'][None, :], # pi(i,j) = s(i) < s(j) index=tw.index, columns=tw.index, dtype=int) pi_term = pi.loc[k1_el, el_out].values for plane in ["X", "Y"]: col_beta = "BET" + plane q = tw.Q1 if plane == "X" else tw.Q2 coeff_sign = 1 if plane == "X" else -1 pi2tau = 2 * np.pi * tau( adv[plane].loc[k1_el, [DUMMY_ID] + el_out], q) brackets = (2 * pi_term + ( (np.sin(2 * pi2tau.loc[:, el_out].values) - np.sin(2 * pi2tau.loc[:, DUMMY_ID].values[:, None])) / np.sin(2 * np.pi * q))) dmu[plane] = tfs.TfsDataFrame( tw.loc[k1_el, col_beta].values[:, None] * brackets * (coeff_sign / (8 * np.pi)), index=k1_el, columns=el_out).transpose() else: LOG.debug( " No 'K1L' variables found. Phase Response will be empty." ) dmu = { "X": tfs.TfsDataFrame(None, index=el_out), "Y": tfs.TfsDataFrame(None, index=el_out) } return dict_mul(self._direction, dmu)
def get_response_for(self, obs=None): """ Calculates and returns only desired response matrices """ # calling functions for the getters to call functions only if needed def caller(func, plane): return func()[plane] def disp_caller(func, plane): disp = func() return response_add( *[disp[k] for k in disp.keys() if k.startswith(plane)]) def tune_caller(func, _unused): tune = func() res = tune["X"].append(tune["Y"]) res.index = ["Q1", "Q2"] return res def couple_caller(func, plane): # apply() converts empty DataFrames to Series! Cast them back. # Also: take care of minus-sign convention! sign = -1 if plane[-1] == "R" else 1 part_func = np.real if plane[-1] == "R" else np.imag return sign * tfs.TfsDataFrame( func()[plane[:-1]].apply(part_func).astype(np.float64)) # to avoid if-elif-elif-... obs_map = { 'Q': (tune_caller, self.get_tune, None), 'BETX': (caller, self.get_beta, "X"), 'BETY': (caller, self.get_beta, "Y"), 'BBX': (caller, self.get_beta_beat, "X"), 'BBY': (caller, self.get_beta_beat, "Y"), 'MUX': (caller, self.get_phase, "X"), 'MUY': (caller, self.get_phase, "Y"), 'DX': (disp_caller, self.get_dispersion, "X"), 'DY': (disp_caller, self.get_dispersion, "Y"), 'NDX': (disp_caller, self.get_norm_dispersion, "X"), 'NDY': (disp_caller, self.get_norm_dispersion, "Y"), 'F1001R': (couple_caller, self.get_coupling, "1001R"), 'F1001I': (couple_caller, self.get_coupling, "1001I"), 'F1010R': (couple_caller, self.get_coupling, "1010R"), 'F1010I': (couple_caller, self.get_coupling, "1010I"), } if obs is None: obs = obs_map.keys() LOG.debug("Calculating responses for {:s}.".format(obs)) with timeit(lambda t: LOG.debug("Total time getting responses: {:f}s". format(t))): response = dict.fromkeys(obs) for key in obs: response[key] = obs_map[key][0](*obs_map[key][1:3]) return response
def _do_clean(main_input, clean_input, bpm_datas, file_date, model_tfs): usvs, all_bad_bpms, bpm_ress = {}, {}, {} clean_writer = output_handler.CleanedAsciiWritter(main_input, file_date) for plane in ("x", "y"): bpm_data = bpm_datas[plane] bpm_data, bpms_not_in_model = _get_only_model_bpms(bpm_data, model_tfs) if bpm_data.empty: raise AssertionError( "Check BPMs names! None of the BPMs was found in the model!") bad_bpms = [] usv = None with timeit(lambda spanned: LOGGER.debug("Time for filtering: %s", spanned)): bpm_data, bad_bpms_clean = clean.clean( bpm_data, clean_input, file_date, ) with timeit(lambda spanned: LOGGER.debug("Time for SVD clean: %s", spanned)): bpm_data, bpm_res, bad_bpms_svd, usv = clean.svd_clean( bpm_data, clean_input, ) bpm_ress[plane] = bpm_res bad_bpms.extend(bpms_not_in_model) bad_bpms.extend(bad_bpms_clean) bad_bpms.extend(bad_bpms_svd) all_bad_bpms[plane] = bad_bpms setattr(clean_writer, "samples_matrix_" + plane, bpm_data) if plane == "x": dpp = _calc_dp_over_p(main_input, bpm_data) bpm_datas[plane] = bpm_data usvs[plane] = usv if clean_input.write_clean: clean_writer.dpp = dpp clean_writer.write() return usvs, all_bad_bpms, bpm_ress, dpp
def _remove_nonnecessaries(self): """ Removies unnecessary entries from model Load model into twiss_df first! """ LOG.debug("Removing unnecessary entries:") LOG.debug(" Entries total: {:d}".format(self.twiss_df.shape[0])) with timeit(lambda t: LOG.debug(" Removed in {:f}s".format(t))): cleaned = self.twiss_df.loc[ regex_in(r"\A(M|BPM)", self.twiss_df.index), :] LOG.debug(" Entries left: {:d}".format(cleaned.shape[0])) return cleaned
def run_all(main_input, clean_input, harpy_input, to_log): with timeit( lambda spanned: LOGGER.info("Total time for file: %s", spanned)): if (not main_input.write_raw and clean_input is None and harpy_input is None): LOGGER.error("No file has been choosen to be writen!") return _setup_file_log_handler(main_input) LOGGER.debug(to_log) tbt_files = turn_by_turn_reader.read_tbt_file(main_input.file) for tbt_file in tbt_files: run_all_for_file(tbt_file, main_input, clean_input, harpy_input)
def _calc_phase_advance_response(self): """ Response Matrix for delta DPhi. Eq. 28 in [#FranchiAnalyticformulasrapid2017]_ Reduced to only phase advances between consecutive elements, as the 3D-Matrix of all elements exceeds memory space (~11000^3 = 1331 Giga Elements) --> w = j-1: DPhi(z,j) = DPhi(x, (j-1)->j) """ LOG.debug("Calculate Phase Advance Response Matrix") with timeit(lambda t: LOG.debug(" Time needed: {:f}s".format(t))): tw = self._twiss adv = self._phase_advances k1_el = self._elements_in["K1L"] el_out_all = [DUMMY_ID] + self._elements_out # Add MU[XY] = 0.0 to the start el_out = el_out_all[1:] # in these we are actually interested el_out_mm = el_out_all[0:-1] # elements-- if len(k1_el) > 0: dmu = dict.fromkeys(["X", "Y"]) pi = tfs.TfsDataFrame(tw['S'][:, None] < tw['S'][None, :], # pi(i,j) = s(i) < s(j) index=tw.index, columns=tw.index, dtype=int) pi_term = (pi.loc[k1_el, el_out].values - pi.loc[k1_el, el_out_mm].values + np.diag(pi.loc[el_out, el_out_mm].values)[None, :]) for plane in ["X", "Y"]: col_beta = "BET" + plane q = tw.Q1 if plane == "X" else tw.Q2 coeff_sign = 1 if plane == "X" else -1 pi2tau = 2 * np.pi * tau(adv[plane].loc[k1_el, el_out_all], q) brackets = (2 * pi_term + ((np.sin(2 * pi2tau.loc[:, el_out].values) - np.sin(2 * pi2tau.loc[:, el_out_mm].values)) / np.sin(2 * np.pi * q) )) dmu[plane] = tfs.TfsDataFrame( tw.loc[k1_el, col_beta].values[:, None] * brackets * (coeff_sign / (8 * np.pi)), index=k1_el, columns=el_out).transpose() else: LOG.debug(" No 'K1L' variables found. Phase Response will be empty.") dmu = {"X": tfs.TfsDataFrame(None, index=el_out), "Y": tfs.TfsDataFrame(None, index=el_out)} return dict_mul(self._direction, dmu)
def evaluate_for_variables(accel_inst, variable_categories, order=4, num_proc=multiprocessing.cpu_count(), temp_dir=None): """ Generate a dictionary containing response matrices for beta, phase, dispersion, tune and coupling and saves it to a file. Args: accel_inst : Accelerator Instance. variable_categories (list): Categories of the variables/knobs to use. (from .json) order (int or tuple): Max or [min, max] of K-value order to use. num_proc (int): Number of processes to use in parallel. temp_dir (str): temporary directory. If ``None``, uses model_dir. """ LOG.debug("Generating Fullresponse via Mad-X.") with timeit(lambda t: LOG.debug( " Total time generating fullresponse: {:f}s".format(t))): if not temp_dir: temp_dir = accel_inst.model_dir create_dirs(temp_dir) variables = accel_inst.get_variables(classes=variable_categories) if len(variables) == 0: raise ValueError( "No variables found! Make sure your categories are valid!") # try: # variables = variables.tolist() # except AttributeError: # pass num_proc = num_proc if len(variables) > num_proc else len(variables) process_pool = multiprocessing.Pool(processes=num_proc) k_values = _get_orders(order) try: _generate_madx_jobs(accel_inst, variables, k_values, num_proc, temp_dir) _call_madx(process_pool, temp_dir, num_proc) mapping = _load_madx_results(variables, k_values, process_pool, temp_dir) except IOError: raise IOError("MADX was unable to compute the mapping.") finally: _clean_up(variables, temp_dir, num_proc) return mapping
def _calc_dispersion_response(self): """ Response Matrix for delta dispersion Eq. 25-27 in [#FranchiAnalyticformulasrapid2017]_ """ LOG.debug("Calculate Dispersion Response Matrix") with timeit(lambda t: LOG.debug(" Time needed: {:f}".format(t))): tw = self._twiss adv = self._phase_advances el_out = self._elements_out els_in = self._elements_in disp_resp = dict.fromkeys(["X_K0L", "X_K1SL", "Y_K0SL", "Y_K1SL"]) for plane in ["X", "Y"]: q = tw.Q1 if plane == "X" else tw.Q2 type_plane = ("K0L" if plane == "X" else "K0SL", "K1SL") el_in_plane = (els_in[type_plane[0]], els_in[type_plane[1]]) col_beta = "BET" + plane col_disp = "DY" if plane == "X" else "DX" if any((len(el_in_plane[0]), len(el_in_plane[1]))): coeff = np.sqrt(tw.loc[el_out, col_beta].values) / ( 2 * np.sin(np.pi * q)) for el_in, el_type in zip(el_in_plane, type_plane): coeff_sign = -1 if el_type == "K0SL" else 1 out_str = "{p:s}_{t:s}".format(p=plane, t=el_type) if len(el_in): pi2tau = 2 * np.pi * tau(adv[plane].loc[el_in, el_out], q) bet_term = np.sqrt(tw.loc[el_in, col_beta].values) if el_type == "K1SL": bet_term *= tw.loc[el_in, col_disp].values disp_resp[out_str] = tfs.TfsDataFrame( coeff_sign * coeff[None, :] * bet_term[:, None] * np.cos(pi2tau), index=el_in, columns=el_out).transpose() else: LOG.debug( " No '{:s}' variables found. ".format(el_type) + "Dispersion Response '{:s}' will be empty.".format( out_str)) disp_resp[out_str] = tfs.TfsDataFrame(None, index=el_out) return dict_mul(self._direction, disp_resp)
def create_response(accel_inst, vars_categories, optics_params): """ Wrapper to create response via TwissResponse """ LOG.debug("Creating response via TwissResponse.") varmap_path = check_varmap_file(accel_inst, vars_categories) with timeit(lambda t: LOG.debug("Total time getting TwissResponse: {:f}s".format(t))): tr = TwissResponse(accel_inst, vars_categories, varmap_path) response = tr.get_response_for(optics_params) if not any([resp.size for resp in response.values()]): raise ValueError("Responses are all empty. " + "Are variables {:s} ".format(tr.get_variable_names()) + "correct for '{:s}'?".format(optics_params) ) return response
def _calc_chromatic_term(self): """ Calculates the chromatic term which is common to all chromatic equations """ LOG.debug("Calculating Chromatic Term.") res = self._results_df tw = self.twiss_df #TODO: Decide wether DX, DY from Model or from calc with timeit(lambda t: LOG.debug(" Time needed: {:f}".format(t))): mask = (tw['K1L'] != 0) | (tw['K2L'] != 0) | (tw['K2SL'] != 0) sum_term = tw.loc[mask, 'K1L'] - \ (tw.loc[mask, 'K2L'] * tw.loc[mask, 'DX']) + \ (tw.loc[mask, 'K2SL'] * tw.loc[mask, 'DY']) res['CHROMX'] = sum_term * tw.loc[mask, 'BETX'] res['CHROMY'] = sum_term * tw.loc[mask, 'BETY'] LOG.debug("Chromatic Term Calculated.")
def generate_fullresponse(accel_inst, variable_categories, delta_k=0.00002, num_proc=multiprocessing.cpu_count(), temp_dir=None): """ Generate a dictionary containing response matrices for beta, phase, dispersion, tune and coupling and saves it to a file. Args: accel_inst : Accelerator Instance. variable_categories (list): Categories of the variables/knobs to use. (from .json) delta_k (float): delta K1L to be applied to quads for sensitivity matrix num_proc (int): Number of processes to use in parallel. temp_dir (str): temporary directory. If ``None``, uses folder of original_jobfile. """ LOG.debug("Generating Fullresponse via Mad-X.") with timeit(lambda t: LOG.debug( " Total time generating fullresponse: {:f}s".format(t))): if not temp_dir: temp_dir = accel_inst.model_dir create_dirs(temp_dir) variables = accel_inst.get_variables(classes=variable_categories) if len(variables) == 0: raise ValueError( "No variables found! Make sure your categories are valid!") # try: # variables = variables.tolist() # except AttributeError: # pass num_proc = num_proc if len(variables) > num_proc else len(variables) process_pool = multiprocessing.Pool(processes=num_proc) incr_dict = _generate_madx_jobs(accel_inst, variables, delta_k, num_proc, temp_dir) _call_madx(process_pool, temp_dir, num_proc) _clean_up(temp_dir, num_proc) var_to_twiss = _load_madx_results(variables, process_pool, incr_dict, temp_dir) fullresponse = _create_fullresponse_from_dict(var_to_twiss) return fullresponse
def calc_linear_chromaticity(self): """ Calculate the Linear Chromaticity Eq. 31 in [#FranchiAnalyticformulasrapid2017]_ """ res = self._results_df if 'CHROMX' not in res: self._calc_chromatic_term() LOG.debug("Calculating Linear Chromaticity") with timeit(lambda t: LOG.debug(" Time needed: {:f}".format(t))): DQ1 = -1 / (4 * np.pi) * res['CHROMX'].dropna().sum(axis="index") DQ2 = 1 / (4 * np.pi) * res['CHROMY'].dropna().sum(axis="index") self._results_df.DQ1 = DQ1 self._results_df.DQ2 = DQ2 LOG.debug(" Q'x: {:f}".format(DQ1)) LOG.debug(" Q'y: {:f}".format(DQ2)) self._log_added('DQ1', 'DQ2')
def generate_fullresponse(accel_inst, variable_categories, delta_k=0.00002, num_proc=multiprocessing.cpu_count(), temp_dir=None): """ Generate a dictionary containing response matrices for beta, phase, dispersion, tune and coupling and saves it to a file. Args: accel_inst : Accelerator Instance. variable_categories (list): Categories of the variables/knobs to use. (from .json) delta_k (float): delta K1L to be applied to quads for sensitivity matrix num_proc (int): Number of processes to use in parallel. temp_dir (str): temporary directory. If ``None``, uses folder of original_jobfile. """ LOG.debug("Generating Fullresponse via Mad-X.") with timeit(lambda t: LOG.debug(" Total time generating fullresponse: {:f}s".format(t))): if not temp_dir: temp_dir = accel_inst.model_dir create_dirs(temp_dir) variables = accel_inst.get_variables(classes=variable_categories) if len(variables) == 0: raise ValueError("No variables found! Make sure your categories are valid!") # try: # variables = variables.tolist() # except AttributeError: # pass num_proc = num_proc if len(variables) > num_proc else len(variables) process_pool = multiprocessing.Pool(processes=num_proc) incr_dict = _generate_madx_jobs(accel_inst, variables, delta_k, num_proc, temp_dir) _call_madx(process_pool, temp_dir, num_proc) _clean_up(temp_dir, num_proc) var_to_twiss = _load_madx_results(variables, process_pool, incr_dict, temp_dir) fullresponse = _create_fullresponse_from_dict(var_to_twiss) return fullresponse
def evaluate_for_variables(accel_inst, variable_categories, order=4, num_proc=multiprocessing.cpu_count(), temp_dir=None): """ Generate a dictionary containing response matrices for beta, phase, dispersion, tune and coupling and saves it to a file. Args: accel_inst : Accelerator Instance. variable_categories (list): Categories of the variables/knobs to use. (from .json) order (int or tuple): Max or [min, max] of K-value order to use. num_proc (int): Number of processes to use in parallel. temp_dir (str): temporary directory. If ``None``, uses model_dir. """ LOG.debug("Generating Fullresponse via Mad-X.") with timeit(lambda t: LOG.debug(" Total time generating fullresponse: {:f}s".format(t))): if not temp_dir: temp_dir = accel_inst.model_dir create_dirs(temp_dir) variables = accel_inst.get_variables(classes=variable_categories) if len(variables) == 0: raise ValueError("No variables found! Make sure your categories are valid!") # try: # variables = variables.tolist() # except AttributeError: # pass num_proc = num_proc if len(variables) > num_proc else len(variables) process_pool = multiprocessing.Pool(processes=num_proc) k_values = _get_orders(order) try: _generate_madx_jobs(accel_inst, variables, k_values, num_proc, temp_dir) _call_madx(process_pool, temp_dir, num_proc) mapping = _load_madx_results(variables, k_values, process_pool, temp_dir) finally: _clean_up(variables, temp_dir, num_proc) return mapping
def create_response(accel_inst, vars_categories, optics_params): """ Wrapper to create response via TwissResponse """ LOG.debug("Creating response via TwissResponse.") vars_list = accel_inst.get_variables(classes=vars_categories) if len(vars_list) == 0: raise ValueError( "No variables found! Make sure your categories are valid!") varmap_path = check_varmap_file(accel_inst, vars_categories) with timeit(lambda t: LOG.debug("Total time getting TwissResponse: {:f}s". format(t))): sign = 1 if accel_inst.get_beam() == 1 else -1 tr = TwissResponse(varmap_path, accel_inst.get_elements_tfs(), vars_list, sign) response = tr.get_response_for(optics_params) if not any([resp.size for resp in response.values()]): raise ValueError("Responses are all empty. " + "Are variables {:s} ".format(vars_list) + "correct for '{:s}'?".format(optics_params)) return response
def get_phase_advances(twiss_df): """ Calculate phase advances between all elements :return: Matrices similar to DPhi(i,j) = Phi(j) - Phi(i) """ LOG.debug("Calculating Phase Advances:") phase_advance_dict = dict.fromkeys(['X', 'Y']) with timeit(lambda t: LOG.debug(" Phase Advances calculated in {:f}s". format(t))): for plane in ['X', 'Y']: colmn_phase = "MU" + plane phases_mdl = twiss_df.loc[twiss_df.index, colmn_phase] # Same convention as in [1]: DAdv(i,j) = Phi(j) - Phi(i) phase_advances = pd.DataFrame( (phases_mdl[None, :] - phases_mdl[:, None]), index=twiss_df.index, columns=twiss_df.index) # Do not calculate dphi and tau here. # only slices of phase_advances as otherwise super slow phase_advance_dict[plane] = phase_advances return phase_advance_dict
def _sort_element_types(self): """ Sorts Elements by types Load model into twiss_df first! """ LOG.debug("Sorting Elements into types:") tw = self.twiss_df with timeit(lambda t: LOG.debug(" Sorted in {:f}s".format(t))): return { 'BPM': regex_in(r'\ABPM', tw.index), 'MB': regex_in(r'\AMB[^S]', tw.index), 'MBS': regex_in(r'\AMBS', tw.index), 'MQ': regex_in(r'\AMQ[^ST]', tw.index), 'MQS': regex_in(r'\AMQS', tw.index), 'MQT': regex_in(r'\AMQT', tw.index), 'MS': regex_in(r'\AMS[^S]', tw.index), 'MSS': regex_in(r'\AMSS', tw.index), 'MO': regex_in(r'\AMO[^S]', tw.index), 'MOS': regex_in(r'\AMOS', tw.index), 'MCB': regex_in(r'\AMCB', tw.index), 'MCS': regex_in(r'\AMCS', tw.index), 'MCO': regex_in(r'\AMCO', tw.index), 'MCD': regex_in(r'\AMCD', tw.index), }
def calc_ac_dipole_driving_terms(self, order_or_terms, spectral_line, plane, ac_tunes, acd_name): """ Calculates the Hamiltonian Terms under Forced Motion. Args: order_or_terms: int, string or list of strings If an int is given all Resonance Driving Terms up to this order will be calculated. The strings are assumed to be the desired driving term names, e.g. "F1001" spectral_line: tuple Needed to determine what phase advance is needed before and after AC dipole location, depends on detal+ and delta-. Sample input: (2,-1) plane: string Either 'H' or 'V' to determine phase term of AC dipole before and after ACD location. ac_tunes: tuple Contains horizontal and vertical AC dipole tunes, i.e. (0.302, 0.33) """ if isinstance(order_or_terms, int): rdt_list = get_all_rdts(order_or_terms) elif not isinstance(order_or_terms, list): rdt_list = [order_or_terms] else: rdt_list = order_or_terms LOG.debug("Calculating RDTs: {:s}.".format(str(rdt_list)[1:-1])) with timeit(lambda t: LOG.debug(" RDTs calculated in {:f}s".format(t))): i2pi = 2j * np.pi tw = self.twiss_df LOG.debug('STARTING phase advance calculation...') phs_adv = self.get_phase_adv() LOG.debug('phase advance calculation done...') res = self._results_df for rdt in rdt_list: assertion(len(rdt) == 5 and rdt[0].upper() == 'F', ValueError("'{:s}' does not seem to be a valid RDT name.".format(rdt))) conj_rdt = ''.join(['F', rdt[2], rdt[1], rdt[4], rdt[3]]) if conj_rdt in self._results_df: res[rdt.upper()] = np.conjugate(self._results_df[conj_rdt]) else: j, k, l, m = int(rdt[1]), int(rdt[2]), int(rdt[3]), int(rdt[4]) n = j + k + l + m assertion(n >= 2, ValueError( "The RDT-order has to be >1 but was {:d} for {:s}".format(n, rdt))) if (l + m) % 2 == 0: src = 'K' + str(n-1) + 'L' sign = -(1j ** (l+m)) else: src = 'K' + str(n-1) + 'SL' sign = -(1j ** (l+m+1)) if spectral_line[1] < 0: c, d = 0, abs(spectral_line[1]) elif spectral_line[1] > 0: c, d = abs(spectral_line[1]), 0 elif spectral_line[1] == 0: c, d = 0, 0 qx_min = ac_tunes[0]-tw.Q1 qy_min = ac_tunes[1]-tw.Q2 qx_plus = ac_tunes[0]+tw.Q1 qy_plus = ac_tunes[1]+tw.Q2 if plane == 'H': if spectral_line[0] == (k-j+1): a, b = 0, 0 elif spectral_line[0] == -(k-j+1): a, b = j-1, k else: LOG.warning("Line of different order than main driving term") if spectral_line[1] == (m-l): c, d = 0, 0 elif spectral_line[1] == -(m-l): c, d = l, m else: LOG.warning("Line of different order than main driving term") acd_ph = np.exp(i2pi * ( (k - j + 1 + a - b) * qx_min + (b - a) * qx_plus + (m - l + c - d) * qy_min + (d - c) * qy_plus )) # acd_ph = np.exp(i2pi*( (k-j+1)*Qx_min + (m-l)*Qy_min )) denom1 = 1./(factorial(j) * factorial(k) * factorial(l) * factorial(m) * 2**n) denom2 = 1./(1. - np.exp(-i2pi * (-tw.Q1 + spectral_line[0] * ac_tunes[0] + spectral_line[1] * ac_tunes[1])) ) # denom2 = 1./(1. - np.exp(i2pi * ((j-k)*tw.Q1 + (l-m)*tw.Q2 ))) elif plane == 'V': if spectral_line[0] == (k-j): a, b = 0, 0 elif spectral_line[0] == -(k-j): a, b = j-1, k else: LOG.warning("Line of different order than main driving term") if spectral_line[1] == (m-l+1): a, b = 0, 0 elif spectral_line[1] == -(m-l+1): a, b = j-1, k else: LOG.warning("Line of different order than main driving term") acd_ph = np.exp(i2pi * ( (k - j + a - b) * qx_min + (b - a) * qx_plus + (m - l + 1 + c - d) * qy_min + (d - c) * qy_plus )) denom1 = 1./(factorial(j) * factorial(k) * factorial(l) * factorial(m) * 2**n) denom2 = 1./(1. - np.exp(i2pi * (-tw.Q2 + spectral_line[0] * ac_tunes[0] + spectral_line[1] * ac_tunes[1])) ) try: mask_in = (tw[src] != 0) | (tw.index == acd_name) if sum(mask_in) == 0: raise KeyError except KeyError: # either src is not in tw or all k's are zero. LOG.warning(" All {:s} == 0. RDT '{:s}' will be zero.".format(src, rdt)) res.loc[:, rdt.upper()] = 0 else: # the next three lines determine the main order of speed, hence # - mask as much as possible # - additions are faster than multiplications (-> applymap last) phx = dphi(phs_adv['X'].loc[mask_in, :], tw.Q1) phy = dphi(phs_adv['Y'].loc[mask_in, :], tw.Q2) phs_acd = pd.DataFrame(columns=phs_adv['X'].loc[mask_in, :].columns, index=phs_adv['X'].loc[mask_in, :].index) phs_acd[:] = acd_ph mk_acd = phs_adv['X'].loc[mask_in, :] > 0 phs_acd.where(mk_acd, 1., inplace=True) phase_term = ((j-k) * phx + (l-m) * phy).applymap(lambda p: np.exp(i2pi*p)) total_phase_term = phase_term.multiply(phs_acd) beta_term = tw.loc[mask_in, src] * \ tw.loc[mask_in, 'BETX'] ** ((j+k) / 2.) * \ tw.loc[mask_in, 'BETY'] ** ((l+m) / 2.) res.loc[:, rdt.upper().replace('F','HAC')] = sign * total_phase_term.multiply( beta_term, axis="index").sum(axis=0).transpose() * denom1 res.loc[:, rdt.upper().replace('F','FAC')] = sign * total_phase_term.multiply( beta_term, axis="index").sum(axis=0).transpose() * denom1 * denom2 LOG.debug(" Average RDT amplitude |{:s}|: {:g}".format(rdt, np.mean( np.abs(res.loc[:, rdt.upper()])))) self._log_added(*rdt_list)
def calc_linear_dispersion(self, bpms_only=False): """ Calculate the Linear Disperion. Eq. 24 in [#FranchiAnalyticformulasrapid2017]_ """ tw = self.twiss_df phs_adv = self.get_phase_adv() res = self._results_df coeff_fun = self._linear_dispersion_coeff sum_fun = self._linear_dispersion_sum # Calculate LOG.debug("Calculate Linear Dispersion") with timeit(lambda t: LOG.debug(" Time needed: {:f}".format(t))): # sources k0_mask = tw['K0L'] != 0 k0s_mask = tw['K0SL'] != 0 k1s_mask = tw['K1SL'] != 0 mx_mask = k0_mask | k1s_mask # magnets contributing to Dx,j (-> Dy,m) my_mask = k0s_mask | k1s_mask # magnets contributing to Dy,j (-> Dx,m) if not any(mx_mask | my_mask): LOG.warning( " No linear dispersion contributions found. Values will be NaN." ) res['DX'] = np.nan res['DY'] = np.nan return # results if bpms_only: res_mask = self._elements["BPM"] else: res_mask = np.ones(res.shape[0], dtype=bool) # create temporary DataFrame for magnets with coefficients already in place df = tfs.TfsDataFrame(index=tw.index).join( coeff_fun(tw.loc[:, 'BETX'], tw.Q1)).join(coeff_fun(tw.loc[:, 'BETY'], tw.Q2)) df.columns = ['COEFFX', 'COEFFY'] LOG.debug(" Calculate uncoupled linear dispersion") df.loc[my_mask, 'DX'] = df.loc[my_mask, 'COEFFX'] * \ sum_fun(tw.loc[mx_mask, 'K0L'], 0, 0, tw.loc[mx_mask, 'BETX'], tau(phs_adv['X'].loc[mx_mask, my_mask], tw.Q1) ).transpose() df.loc[mx_mask, 'DY'] = df.loc[mx_mask, 'COEFFY'] * \ sum_fun(-tw.loc[my_mask, 'K0SL'], # MINUS! 0, 0, tw.loc[my_mask, 'BETY'], tau(phs_adv['Y'].loc[my_mask, mx_mask], tw.Q2) ).transpose() LOG.debug(" Calculate full linear dispersion values") res.loc[res_mask, 'DX'] = df.loc[res_mask, 'COEFFX'] * \ sum_fun(tw.loc[mx_mask, 'K0L'], tw.loc[mx_mask, 'K1SL'], df.loc[mx_mask, 'DY'], tw.loc[mx_mask, 'BETX'], tau(phs_adv['X'].loc[mx_mask, res_mask], tw.Q1) ).transpose() res.loc[res_mask, 'DY'] = df.loc[res_mask, 'COEFFY'] * \ sum_fun(-tw.loc[my_mask, 'K0SL'], # MINUS! tw.loc[my_mask, 'K1SL'], df.loc[my_mask, 'DX'], tw.loc[my_mask, 'BETY'], tau(phs_adv['Y'].loc[my_mask, res_mask], tw.Q2) ).transpose() LOG.debug(" Average linear dispersion Dx: {:g}".format( np.mean(res['DX']))) LOG.debug(" Average linear dispersion Dy: {:g}".format( np.mean(res['DY']))) self._log_added('DX', 'DY')
def calc_rdts(self, order): """ Calculates the Resonance Driving Terms. Eq. A8 in [#FranchiAnalyticformulasrapid2017]_ Args: order: int, string or list of strings If an int is given all Resonance Driving Terms up to this order will be calculated. The strings are assumed to be the desired driving term names, e.g. "F1001" """ if isinstance(order, int): order = get_all_rdts(order) elif not isinstance(order, list): order = [order] LOG.debug("Calculating RDTs: {:s}.".format(str(order)[1:-1])) with timeit( lambda t: LOG.debug(" RDTs calculated in {:f}s".format(t))): i2pi = 2j * np.pi tw = self.twiss_df phs_adv = self.get_phase_adv() res = self._results_df for rdt in order: assertion( len(rdt) == 5 and rdt[0].upper() == 'F', ValueError( "'{:s}' does not seem to be a valid RDT name.".format( rdt))) conj_rdt = ''.join(['F', rdt[2], rdt[1], rdt[4], rdt[3]]) if conj_rdt in self._results_df: res[rdt.upper()] = np.conjugate(self._results_df[conj_rdt]) else: j, k, l, m = int(rdt[1]), int(rdt[2]), int(rdt[3]), int( rdt[4]) n = j + k + l + m assertion( n >= 2, ValueError( "The RDT-order has to be >1 but was {:d} for {:s}". format(n, rdt))) denom = 1. / (factorial(j) * factorial(k) * factorial(l) * factorial(m) * 2**n * (1. - np.exp(i2pi * ((j - k) * tw.Q1 + (l - m) * tw.Q2)))) if (l + m) % 2 == 0: src = 'K' + str(n - 1) + 'L' sign = -(1j**(l + m)) else: src = 'K' + str(n - 1) + 'SL' sign = -(1j**(l + m + 1)) k_mask = tw[src] != 0 el_mask = self._elements_mapped[src] | k_mask if sum(k_mask) > 0: # the next three lines determine the main order of speed, hence # - mask as much as possible # - additions are faster than multiplications (-> applymap last) phx = dphi(phs_adv['X'].loc[k_mask, el_mask], tw.Q1) phy = dphi(phs_adv['Y'].loc[k_mask, el_mask], tw.Q2) phase_term = ( (j - k) * phx + (l - m) * phy).applymap(lambda p: np.exp(i2pi * p)) beta_term = tw.loc[k_mask, src] * \ tw.loc[k_mask, 'BETX'] ** ((j+k) / 2.) * \ tw.loc[k_mask, 'BETY'] ** ((l+m) / 2.) res.loc[el_mask, rdt.upper()] = sign * phase_term.multiply( beta_term, axis="index").sum( axis=0).transpose() * denom LOG.debug( " Average RDT amplitude |{:s}|: {:g}".format( rdt, np.mean(np.abs(res.loc[el_mask, rdt.upper()])))) else: LOG.debug( " All {:s} == 0. RDT '{:s}' will be zero.".format( src, rdt)) res.loc[el_mask, rdt.upper()] = 0 self._log_added(*order)