Exemplo n.º 1
0
def _calculate_normalised_dispersion_2d(meas_input, input_files, beta, header):
    # TODO there are no errors from orbit
    order = 2 if meas_input.second_order_dispersion else 1
    plane = "X"
    model = meas_input.accelerator.model
    df_orbit = _get_merged_df(meas_input, input_files, plane, ['CO', 'CORMS', f"AMP{plane}"])
    df_orbit[f"ND{plane}{MDL}"] = df_orbit.loc[:, f"D{plane}{MDL}"] / np.sqrt(
        df_orbit.loc[:, f"BET{plane}{MDL}"])
    if order > 1:
        df_orbit[f"ND2{plane}{MDL}"] = df_orbit.loc[:, f"D2{plane}{MDL}"] / np.sqrt(
            df_orbit.loc[:, f"BET{plane}{MDL}"])
    df_orbit = pd.merge(df_orbit, beta.loc[:, [f"BET{plane}", f"{ERR}BET{plane}"]], how='inner',
                        left_index=True, right_index=True)
    dpps = input_files.dpps(plane)
    if np.max(dpps) - np.min(dpps) == 0.0:
        return  # temporary solution
        # raise ValueError('Cannot calculate dispersion, only a single dpoverp')
    fit = np.polyfit(dpps, input_files.get_data(df_orbit, 'CO').T, order, cov=True)
    if order > 1:
        df_orbit['ND2X_unscaled'] = fit[0][-3, :].T / stats.weighted_mean(input_files.get_data(df_orbit, f"AMP{plane}"), axis=1)
        df_orbit['STDND2X_unscaled'] = np.sqrt(fit[1][-3, -3, :].T) / stats.weighted_mean(input_files.get_data(df_orbit, f"AMP{plane}"), axis=1)
    df_orbit['NDX_unscaled'] = fit[0][-2, :].T / stats.weighted_mean(input_files.get_data(df_orbit, f"AMP{plane}"), axis=1)  # TODO there is no error from AMPX
    df_orbit['STDNDX_unscaled'] = np.sqrt(fit[1][-2, -2, :].T) / stats.weighted_mean(input_files.get_data(df_orbit, f"AMP{plane}"), axis=1)
    mask = meas_input.accelerator.get_element_types_mask(df_orbit.index, ["arc_bpm"])
    global_factor = np.sum(df_orbit.loc[mask, f"ND{plane}{MDL}"].values) / np.sum(df_orbit.loc[mask, 'NDX_unscaled'].values)
    if order > 1:
        df_orbit[f"ND2{plane}"] = global_factor * df_orbit.loc[:, 'ND2X_unscaled']
        df_orbit[f"{ERR}ND2{plane}"] = global_factor * df_orbit.loc[:, 'STDND2X_unscaled']
    df_orbit[f"ND{plane}"] = global_factor * df_orbit.loc[:, 'NDX_unscaled']
    df_orbit[f"{ERR}ND{plane}"] = global_factor * df_orbit.loc[:, 'STDNDX_unscaled']
    df_orbit = _calculate_from_norm_disp(df_orbit, model, plane)
    output_df = df_orbit.loc[:, _get_output_columns(plane, df_orbit)]
    tfs.write(join(meas_input.outputdir, f"{NORM_DISP_NAME}{plane.lower()}{EXT}"), output_df, header, save_index='NAME')
    return output_df
Exemplo n.º 2
0
def calculate(measure_input, input_files):
    tune_d = TuneDict()
    accelerator = measure_input.accelerator
    for plane in PLANES:
        tune_d[plane]["QM"] = accelerator.model.headers[
            f"Q{PLANE_TO_NUM[plane]}"]
        tune_list = [
            df.headers[f"Q{PLANE_TO_NUM[plane]}"]
            for df in input_files.dpp_frames(plane, 0)
        ]
        tune_rms_list = [
            df.headers[f"Q{PLANE_TO_NUM[plane]}RMS"]
            for df in input_files.dpp_frames(plane, 0)
        ]
        measured_tune = stats.weighted_mean(np.array(tune_list),
                                            errors=np.array(tune_rms_list))
        tune_d[plane]["Q"], tune_d[plane]["QF"] = measured_tune, measured_tune
        tune_d[plane]["QFM"] = accelerator.nat_tunes[PLANE_TO_NUM[plane] - 1]
        if accelerator.excitation:
            tune_d[plane]["QM"] = accelerator.drv_tunes[PLANE_TO_NUM[plane] -
                                                        1]
            tune_d[plane]["QF"] = tune_d[plane]["Q"] - tune_d[plane][
                "QM"] + tune_d[plane]["QFM"]
            if measure_input.compensation == "equation":
                tune_d[plane]["ac2bpm"] = tune_d.phase_ac2bpm(
                    input_files.joined_frame(plane, [f"MU{plane}"],
                                             dpp_value=0,
                                             how='inner'), plane,
                    measure_input.accelerator)
    return tune_d
Exemplo n.º 3
0
def calculate_orbit(meas_input, input_files, header, plane):
    """
    Calculates orbit.
    Args:
        meas_input: Optics_input object
        input_files: Stores the input files tfs
        header: OrderedDict containing information about the analysis
        plane: "X" or "Y"

    Returns:
        TfsDataFrame corresponding to output file
    """
    df_orbit = _get_merged_df(meas_input, input_files, plane, ['CO', 'CORMS'])
    df_orbit[plane] = stats.weighted_mean(input_files.get_data(df_orbit, 'CO'),
                                          axis=1)
    df_orbit[f"{ERR}{plane}"] = stats.weighted_error(input_files.get_data(
        df_orbit, 'CO'),
                                                     axis=1)
    df_orbit = _get_delta_columns(df_orbit, plane)
    output_df = df_orbit.loc[:, _get_output_columns(plane, df_orbit)]
    tfs.write(join(meas_input.outputdir, f"{ORBIT_NAME}{plane.lower()}{EXT}"),
              output_df,
              header,
              save_index='NAME')
    return output_df
Exemplo n.º 4
0
def test_nanhandling():
    vector = np.array([355., 0., 5., np.nan])
    assert stats.circular_nanmean(vector) == stats.circular_mean(vector[:-1])
    assert stats.weighted_nanmean(vector) == stats.weighted_mean(vector[:-1])
    assert stats.weighted_nanrms(vector) == stats.weighted_rms(vector[:-1])
    vector = np.array([[355., 0., 5., 0.], [355., 0., 5., 0.],
                       [355., 0., 5., np.nan]])
    assert np.all(
        stats.circular_nanerror(vector, axis=1) == stats.circular_error(
            vector[:, :-1], axis=1))
Exemplo n.º 5
0
def calculate_coupling(
    meas_input: dict,
    input_files: dict,
    phase_dict: Dict[str, Tuple[Dict[str, tfs.TfsDataFrame],
                                Sequence[tfs.TfsDataFrame]]],
    tune_dict: Dict[str, float],
    header_dict: OrderedDict,
) -> None:
    """
    Calculates the coupling RDTs f1001 and f1010, as well as the closest tune approach Cminus (|C-|).
    This represents the "2 BPM method" in https://cds.cern.ch/record/1264111/files/CERN-BE-Note-2010-016.pdf
    (a more up-to-date reference will come in the near future).

    Two formulae are used to calculate the Cminus, taken from the following reference:
    https://cds.cern.ch/record/2135848/files/PhysRevSTAB.17.051004.pdf
    The first one (Eq(1)) is an approximation using only the amplitudes of the RDTs, while the second one
    (Eq(2) in the same paper) is more exact but needs also the phase of the RDT.

    The results are written down in the optics_measurements outputs as **f1001.tfs** and **f1010.tfs** files.

    Args:
        meas_input (dict): `OpticsInput` object containing analysis settings from the command-line.
        input_files (dict): `InputFiles` (dict) object containing frequency spectra files (linx/y) for
            each transverse plane (as keys).
        phase_dict (Dict[str, Tuple[Dict[str, tfs.TfsDataFrame], tfs.TfsDataFrame]]): dictionary containing
            the measured phase advances, with an entry for each transverse plane. In said entry is a
            dictionary with the measured phase advances for 'free' and 'uncompensated' cases, as well as
            the location of the output ``TfsDataFrames`` for the phases.
        tune_dict (Dict[str, float]): `TuneDict` object containing measured tunes. There is an entry
            calculated for the 'Q', 'QF', 'QM', 'QFM' and 'ac2bpm' modes, each value being a float.
        header_dict (OrderedDict): header dictionary of common items for coupling output files,
            will be attached as the header to the **f1001.tfs** and **f1010.tfs** files..
    """
    LOGGER.info("Calculating coupling")
    bd = meas_input.accelerator.beam_direction
    compensation = "uncompensated" if meas_input.compensation == "model" else "free"

    # We need vertical and horizontal spectra, so we have to intersect first all inputs with X and Y phase
    # output furthermore the output has to be rearranged in the order of the model (important for e.g. LHC
    # beam 2) and thus we need to intersect the *model* index with all the above. Since the first index of
    # the .intersect chain dictates the order, we have to start with the model index.
    LOGGER.debug("Intersecting measurements, starting with model")
    joined: tfs.TfsDataFrame = _joined_frames(
        input_files)  # merge transverse input frames
    joined_index: pd.Index = (meas_input.accelerator.model.index.intersection(
        joined.index).intersection(
            phase_dict["X"][compensation]["MEAS"].index).intersection(
                phase_dict["Y"][compensation]["MEAS"].index))
    joined = joined.loc[joined_index].copy()

    phases_x: tfs.TfsDataFrame = phase_dict["X"][compensation]["MEAS"].loc[
        joined_index].copy()
    phases_y: tfs.TfsDataFrame = phase_dict["Y"][compensation]["MEAS"].loc[
        joined_index].copy()

    LOGGER.debug("Averaging (arithmetic mean) amplitude columns")
    for col in [SECONDARY_AMPLITUDE_X, SECONDARY_AMPLITUDE_Y]:
        arithmetically_averaved_columns = [
            c for c in joined.columns if c.startswith(col)
        ]
        joined[col] = stats.weighted_mean(
            joined[arithmetically_averaved_columns], axis=1)

    LOGGER.debug("Averaging (circular mean) frequency columns"
                 )  # make sure to use period=1 here
    for col in [SECONDARY_FREQUENCY_X, SECONDARY_FREQUENCY_Y]:
        circularly_averaved_columns = [
            x for x in joined.columns if x.startswith(col)
        ]
        joined[col] = bd * stats.circular_mean(
            joined[circularly_averaved_columns], axis=1, period=1)

    LOGGER.debug("Finding BPM pairs for momentum reconstruction")
    bpm_pairs_x, deltas_x = _find_pair(phases_x, meas_input.coupling_pairing)
    bpm_pairs_y, deltas_y = _find_pair(phases_y, meas_input.coupling_pairing)

    LOGGER.debug("Computing complex lines from spectra")
    A01: np.ndarray = 0.5 * _get_complex_line(
        joined[SECONDARY_AMPLITUDE_X] *
        exp(joined[SECONDARY_FREQUENCY_X] * PI2I), deltas_x, bpm_pairs_x)
    B10: np.ndarray = 0.5 * _get_complex_line(
        joined[SECONDARY_AMPLITUDE_Y] *
        exp(joined[SECONDARY_FREQUENCY_Y] * PI2I), deltas_y, bpm_pairs_y)
    A0_1: np.ndarray = 0.5 * _get_complex_line(
        joined[SECONDARY_AMPLITUDE_X] *
        exp(-joined[SECONDARY_FREQUENCY_X] * PI2I), deltas_x, bpm_pairs_x)
    B_10: np.ndarray = 0.5 * _get_complex_line(
        joined[SECONDARY_AMPLITUDE_Y] *
        exp(-joined[SECONDARY_FREQUENCY_Y] * PI2I), deltas_y, bpm_pairs_y)

    q1001_from_A = -np.angle(A01) + (bd * joined[f"{COL_MU}Y"].to_numpy() -
                                     0.25) * PI2
    q1001_from_B = np.angle(B10) - (bd * joined[f"{COL_MU}X"].to_numpy() -
                                    0.25) * PI2
    eq_1001 = exp(1.0j * q1001_from_A) + exp(1.0j * q1001_from_B)

    q1010_from_A = -np.angle(A0_1) - (bd * joined[f"{COL_MU}Y"].to_numpy() -
                                      0.25) * PI2
    q1010_from_B = -np.angle(B_10) - (bd * joined[f"{COL_MU}X"].to_numpy() -
                                      0.25) * PI2
    eq_1010 = exp(1.0j * q1010_from_A) + exp(1.0j * q1010_from_B)

    LOGGER.debug("Computing average of coupling RDTs")
    f1001: np.ndarray = -0.5 * sqrt(np.abs(A01 * B10)) * eq_1001 / abs(eq_1001)
    f1010: np.ndarray = 0.5 * sqrt(np.abs(
        A0_1 * B_10)) * eq_1010 / abs(eq_1010)

    LOGGER.debug("Getting tune separation from measurements")
    tune_separation = np.abs(tune_dict["X"]["QFM"] % 1.0 -
                             tune_dict["Y"]["QFM"] % 1.0)

    LOGGER.debug("Calculating approximated Cminus")
    C_approx = 4.0 * tune_separation * np.mean(np.abs(f1001))
    header_dict["Cminus_approx"] = C_approx
    LOGGER.info(
        f"|C-| (approx) = {C_approx:.5f}, tune_sep = {tune_separation:.3f}, from Eq.1 in PRSTAB 17,051004"
    )

    LOGGER.debug("Calculating exact Cminus")
    C_exact = np.abs(
        4.0 * tune_separation *
        np.mean(f1001 * exp(1.0j *
                            (joined[f"{COL_MU}X"] - joined[f"{COL_MU}Y"]))))
    header_dict["Cminus_exact"] = C_exact
    LOGGER.info(
        f"|C-| (exact)  = {C_exact:.5f}, from Eq.2 w/o i*s*Delta/R in PRSTAB 17,051004"
    )

    if meas_input.compensation == "model":
        LOGGER.debug("Compensating coupling RDT values by model")
        f1001, f1010 = compensate_rdts_by_model(f1001, f1010, tune_dict)

    LOGGER.debug("Adding model values and deltas")
    model_coupling = coupling_via_cmatrix(
        meas_input.accelerator.model).loc[joined_index]

    f1001_df = _rdt_to_output_df(f1001, model_coupling[F1001],
                                 meas_input.accelerator.model, joined_index)
    f1010_df = _rdt_to_output_df(f1010, model_coupling[F1010],
                                 meas_input.accelerator.model, joined_index)

    tfs.write(
        Path(meas_input.outputdir) / f"{F1001.lower()}{EXT}", f1001_df,
        header_dict)
    tfs.write(
        Path(meas_input.outputdir) / f"{F1010.lower()}{EXT}", f1010_df,
        header_dict)