Beispiel #1
0
def onclick(click_event):
    """
    Process mouse click events in the 2D cross-correlation map

    :param click_event: mouse click event
    """
    global angles, q_range, ccf, current_q, ax0, ax1, my_cmap, ymin, ymax

    if click_event.inaxes == ax0:  # click in the 2D cross-correlation map
        current_q = util.find_nearest(
            reference_array=q_range, test_values=click_event.ydata
        )
        ymin = ccf[current_q, indices].min()
        ymax = 1.2 * ccf[current_q, indices].max()
        ax1.cla()
        ax1.plot(
            angles,
            ccf[current_q, :],
            linestyle="None",
            marker=".",
            markerfacecolor="blue",
        )
        ax1.set_xlim(0, 180)
        ax1.set_ylim(ymin, ymax)
        ax1.set_xlabel("Angle (deg)")
        ax1.set_ylabel("Cross-correlation (A.U.)")
        ax1.set_xticks(np.arange(0, 181, 30))
        ax1.set_title("Cross-correlation at q={:.3f}".format(q_range[current_q]))
        plt.draw()
Beispiel #2
0
def onclick(click_event):
    """
    Process mouse click events in the interactive line plot

    :param click_event: mouse click event
    """
    global sum_roi, vline, ax1, ax2, index_peak, motor_positions, data, my_cmap, sum_int
    global figure, scale, motor_text, max_text

    if click_event.inaxes == ax1:  # click in the line plot
        index_peak = util.find_nearest(motor_positions, click_event.xdata)
        vline.remove()
        if scale == "linear":
            vline = ax1.vlines(
                x=motor_positions[index_peak],
                ymin=sum_int.min(),
                ymax=sum_int[index_peak],
                colors="r",
                linestyle="dotted",
            )
        else:  # 'log'
            vline = ax1.vlines(
                x=motor_positions[index_peak],
                ymin=np.log10(sum_int.min()),
                ymax=np.log10(sum_int[index_peak]),
                colors="r",
                linestyle="dotted",
            )
        ax2.cla()
        ax2.imshow(
            np.log10(
                data[index_peak, sum_roi[0] : sum_roi[1], sum_roi[2] : sum_roi[3]]
            ),
            cmap=my_cmap,
            vmin=0,
        )
        ax2.axis("scaled")
        ax2.set_title("ROI at line")
        motor_text.remove()
        motor_text = figure.text(
            0.70,
            0.75,
            motor_name + " = {:.2f}".format(motor_positions[index_peak]),
            size=10,
        )
        max_text.remove()
        max_text = figure.text(
            0.70,
            0.70,
            "ROI max at line = "
            + "{:.0f}".format(
                data[index_peak, sum_roi[0] : sum_roi[1], sum_roi[2] : sum_roi[3]].max()
            ),
            size=10,
        )
        plt.draw()
Beispiel #3
0
def calc_ccf_polar(point, q1_name, q2_name, bin_values, polar_azi_int):
    """
    Calculate for the cross-correlation of point with all other points at the second q value and sort the result.

    :param point: the reference point
    :param q1_name: key for the first q value in the dictionnary polar_azi_int
    :param q2_name: key for the second q value in the dictionnary polar_azi_int
    :param bin_values: in radians, angular bin values where to calculate the cross-correlation
    :param polar_azi_int: a dictionnary with fields 'q1', 'q2', ... Each field contains three 1D
     arrays: polar angle, azimuthal angle and intensity values for each point
    :return: the sorted cross-correlation values, angular bins indices and number of points contributing to the angular
     bins
    """
    # calculate the angle between the current point and all points from the second q value (delta in [0 pi])
    delta_val = np.arccos(
        np.sin(polar_azi_int[q1_name][point, 0]) *
        np.sin(polar_azi_int[q2_name][:, 0]) *
        np.cos(polar_azi_int[q2_name][:, 1] -
               polar_azi_int[q1_name][point, 1]) +
        np.cos(polar_azi_int[q1_name][point, 0]) *
        np.cos(polar_azi_int[q2_name][:, 0]))

    # It can happen that the value in the arccos is outside [-1, 1] because of the limited floating precision of Python,
    # which result in delta_val = nan. These points would contribute to the 0 and 180 degrees CCF, and can be neglected.

    # find the nearest angular bin value for each value of the array delta
    nearest_indices = util.find_nearest(test_values=delta_val,
                                        reference_array=bin_values,
                                        width=bin_values[1] - bin_values[0])

    # update the counter of bin indices
    counter_indices, counter_val = np.unique(
        nearest_indices, return_counts=True)  # counter_indices are sorted

    # filter out -1 indices which correspond to no neighbour in the range defined by width in find_nearest()
    counter_val = np.delete(counter_val, np.argwhere(counter_indices == -1))
    counter_indices = np.delete(counter_indices,
                                np.argwhere(counter_indices == -1))

    # calculate the contribution to the cross-correlation for bins in counter_indices
    ccf_uniq_val = np.zeros(len(counter_indices))
    for idx in range(len(counter_indices)):
        ccf_uniq_val[idx] = (
            polar_azi_int[q1_name][point, 2] *
            polar_azi_int[q2_name][nearest_indices == counter_indices[idx],
                                   2]).sum()

    return ccf_uniq_val, counter_val, counter_indices
Beispiel #4
0
hist, bin_edges = np.histogram(amp[amp > cutoff_amp].flatten(), bins=50)
bin_step = (bin_edges[1] - bin_edges[0]) / 2
bin_axis = bin_edges + bin_step
bin_axis = bin_axis[0:len(hist)]

# interpolate the histogram
newbin_axis = np.linspace(bin_axis.min(), bin_axis.max(), 500)
interp_hist = interp1d(bin_axis, hist, kind='cubic')
newhist = interp_hist(newbin_axis)

##############################################
# fit the peak with a pseudovoigt line shape #
##############################################
if fit:
    # find indices of the histogram points belonging to the range of interest
    ind_min, ind_max = util.find_nearest(
        newbin_axis, [min(fit_range), max(fit_range)])
    fit_axis = newbin_axis[np.arange(ind_min, ind_max + 1, 1)]
    fit_hist = newhist[np.arange(ind_min, ind_max + 1, 1)]
    # offset_hist = min(fit_hist)

    # define the initial parameters
    fit_params = Parameters()
    if lineshape == 'pseudovoigt':
        cen = newbin_axis[np.unravel_index(newhist.argmax(), newhist.shape)]
        fit_params.add('amp_1', value=50000, min=100, max=1000000)
        fit_params.add('cen_1', value=cen, min=cen - 0.2, max=cen + 0.2)
        fit_params.add('sig_1', value=0.1, min=0.01, max=0.5)
        fit_params.add('ratio_1', value=0.5, min=0, max=1)

    # run the fit
    result = minimize(util.objective_lmfit,
Beispiel #5
0
np.savez_compressed(root_folder + 'q+angular_avg.npz',
                    q=q_axis,
                    avg=y_mean_masked,
                    median=y_median_masked)
if save_txt:
    file = open(root_folder + 'q+angular_avg.txt', "w")
    file.write('{:8s}'.format('q') + '\t' + '{:10s}'.format('avg') + '\n')
    for idx in range(len(q_axis)):
        file.write('{:8.6f}'.format(q_axis[idx]) + '\t' +
                   '{:10.1f}'.format(y_mean_masked[idx]) + '\n')
    file.close()

#############
# plot data #
#############
q_axvline = util.find_nearest(q_axis, vertical_lines)

fig, ax0 = plt.subplots(1, 1)
plt0 = ax0.plot(q_axis, np.log10(y_mean_masked), 'r')
plt.xlabel('q (1/nm)')
plt.ylabel('Angular average (A.U.)')
if xlim is not None:
    plt.xlim(xlim[0], xlim[1])
if ylim is not None:
    plt.ylim(ylim[0], ylim[1])
ymax = np.log10(y_mean_masked.max())
for counter, value in enumerate(vertical_lines):
    ax0.axvline(x=value,
                ymax=np.log10(y_mean_masked[q_axvline[counter]]) / ymax,
                linestyle='--')
plt.savefig(root_folder + 'angular_avg_labels.png')
Beispiel #6
0
    else:
        ylim = [0, np.log10(average[~np.isnan(average)].max()) + 1]
ax.set_xlim(xlim[0], xlim[1])
ax.set_ylim(ylim[0], ylim[1])

##################################################
# combine ranges of interest in a single dataset #
##################################################
nb_ranges = len(fit_range)
nb_points = np.zeros(nb_ranges, dtype=int)
fit_range = np.asarray(fit_range)

for idx in range(nb_ranges):
    # find indices of distances belonging to ranges of interest
    myrange = fit_range[idx]
    ind_min, ind_max = util.find_nearest(
        distances, [myrange.min(), myrange.max()])
    nb_points[idx] = ind_max - ind_min + 1

# check if the number of points in ranges in the same, interpolate otherwise
max_points = nb_points.max()
combined_xaxis = []
combined_data = []
for idx in range(nb_ranges):
    # find indices of distances belonging to ranges of interest
    myrange = fit_range[idx]
    ind_min, ind_max = util.find_nearest(
        distances, [myrange.min(), myrange.max()])
    indices = np.arange(ind_min, ind_max + 1, 1)
    if (ind_max - ind_min + 1) != max_points:
        interp = interp1d(distances[indices],
                          average[indices],
Beispiel #7
0
def bcc_lattice(q_values,
                unitcell_param,
                pivot,
                euler_angles=(0, 0, 0),
                offset_indices=False,
                verbose=False):
    """
    Calculate Bragg peaks positions using experimental parameters for a BCC unit cell.

    :param q_values: tuple of 1D arrays (qx, qz, qy), q_values range where to look for Bragg peaks
    :param unitcell_param: the unit cell parameter of the FCC lattice
    :param pivot:  tuple, the pivot point position in pixels for the rotation
    :param euler_angles: tuple of angles for rotating the unit cell around (qx, qz, qy)
    :param offset_indices: if True, return the non rotated lattice with the origin of indices corresponding to the
    length of padded q values
    :param verbose: True to have printed comments
    :return: offsets after padding, the list of Bragg peaks positions in pixels, and the corresponding list of hlk.
    """
    lattice_list = [
    ]  # position of the pixels corresponding to hkl reflections
    peaks_list = []  # list of hkl fitting the data range

    recipr_param = 2 * np.pi / unitcell_param  # reciprocal lattice is simple cubic of parameter 2*pi/unitcell_param
    if verbose:
        print('fcc unit cell of parameter a =', unitcell_param, 'nm')
        print('reciprocal unit cell of parameter 2*pi/a =',
              str('{:.4f}'.format(recipr_param)), '1/nm')

    qx = q_values[0]  # along z downstream in CXI convention
    qz = q_values[1]  # along y vertical up in CXI convention
    qy = q_values[2]  # along x outboard in CXI convention
    q_max = np.sqrt(abs(qx).max()**2 + abs(qz).max()**2 + abs(qy).max()**2)
    numz, numy, numx = len(qx), len(qz), len(qy)

    # calculate the maximum Miller indices which fit into q_max
    h_max = int(np.floor(q_max * unitcell_param / (2 * np.pi)))
    hkl = np.arange(start=-h_max, stop=h_max + 1, step=1)

    # pad q arrays in order to find the position in pixels of each hkl within the array
    # otherwise it finds the first or last index but this can be far from the real peak position
    leftpad_z, leftpad_y, leftpad_x = numz, numy, numx  # offset of indices to the left
    pad_qx = qx[0] - leftpad_z * (qx[1] - qx[0]) + np.arange(
        3 * numz) * (qx[1] - qx[0])
    pad_qz = qz[0] - leftpad_y * (qz[1] - qz[0]) + np.arange(
        3 * numy) * (qz[1] - qz[0])
    pad_qy = qy[0] - leftpad_x * (qy[1] - qy[0]) + np.arange(
        3 * numx) * (qy[1] - qy[0])

    # calculate peaks position for the non rotated lattice
    for h in hkl:  # h downstream along qx
        for k in hkl:  # k outboard along qy
            for l in hkl:  # l vertical up along qz
                # simple cubic unit cell with two point basis (0,0,0), (0.5,0.5,0.5)
                struct_factor = np.real(1 + np.exp(1j * np.pi * (h + k + l)))
                if struct_factor != 0:  # find the position of the pixel nearest to q_bragg
                    pix_h = util.find_nearest(original_array=pad_qx,
                                              array_values=h * recipr_param)
                    pix_k = util.find_nearest(original_array=pad_qy,
                                              array_values=k * recipr_param)
                    pix_l = util.find_nearest(original_array=pad_qz,
                                              array_values=l * recipr_param)

                    lattice_list.append([pix_h, pix_l, pix_k])
                    peaks_list.append([h, l, k])

    if offset_indices:
        # non rotated lattice, the origin of indices will correspond to the length of padded q values
        return (leftpad_z, leftpad_y, leftpad_x), lattice_list, peaks_list
    else:
        # rotate previously calculated peaks, the origin of indices will correspond to the length of original q values
        lattice_pos, peaks = rotate_lattice(lattice_list=lattice_list,
                                            peaks_list=peaks_list,
                                            original_shape=(numz, numy, numx),
                                            pad_offset=(leftpad_z, leftpad_y,
                                                        leftpad_x),
                                            pivot=pivot,
                                            euler_angles=euler_angles)
        return (leftpad_z, leftpad_y, leftpad_x), lattice_pos, peaks
    for key, value in result.items():  # loop over linecuts
        # value is a dictionary {'distance': 1D array, 'cut': 1D array}
        tmp_str = f"{key}"
        print(f'\n{"#" * len(tmp_str)}\n' + tmp_str + "\n" +
              f'{"#" * len(tmp_str)}')
        for idx_roi, roi in enumerate(
                fit_roi[idx_point]
        ):  # loop over the ROIs, roi is a tuple of two number
            # define the fit initial center
            tmp_str = f"{roi}"
            indent = 2
            print(f'\n{" " * indent}{"-" * len(tmp_str)}\n' +
                  f'{" " * indent}' + tmp_str + "\n" +
                  f'{" " * indent}{"-" * len(tmp_str)}')
            # find linecut indices falling into the roi
            ind_start, ind_stop = util.find_nearest(value["distance"], roi)

            # fit a RectangleModel from lmfit to the peaks
            midpoint = (roi[0] + roi[1]) / 2
            offset = (roi[1] - roi[0]) / 8
            # initialize fit parameters (guess does not perform well)
            rect_mod = RectangleModel(form="erf")
            rect_params = rect_mod.make_params()
            rect_params["amplitude"].set(0.75, min=0.5, max=1)
            rect_params["center1"].set(midpoint - offset,
                                       min=roi[0],
                                       max=roi[1])
            rect_params["sigma1"].set(1, min=0.001, max=10)
            rect_params["center2"].set(midpoint + offset,
                                       min=roi[0],
                                       max=roi[1])
Beispiel #9
0
plt.ylabel('Angular average (A.U.)')
plt.title("Click to select background points\nx to pause/resume for pan/zoom\n"
          "a restart ; p plot background ; q quit")
if xlim is not None:
    plt.xlim(xlim[0], xlim[1])
if ylim is not None:
    plt.ylim(ylim[0], ylim[1])
plt.connect('key_press_event', press_key)
plt.connect('button_press_event', on_click)
plt.show()

#########################################################
# fit background and interpolate it to mach data points #
#########################################################
xy_array = np.asarray(xy)
indices = util.find_nearest(distances, xy_array[:, 0])
if scale == 'linear':
    interpolation = interp1d(distances[indices], data[indices], kind='linear', bounds_error=False,
                             fill_value=np.nan)
    background = interpolation(distances)
    background[np.isnan(background)] = 0
    data_back = data - background
    data_back[data_back <= 0] = 0
else:  # fit direcly log values, less artefactsdistances.max
    interpolation = interp1d(distances[indices], np.log10(data[indices]), kind='linear', bounds_error=False,
                             fill_value=np.nan)
    background = interpolation(distances)
    background = 10**background
    background[np.isnan(background)] = 0
    data_back = data - background
    data_back[data_back <= 1] = 1  # will appear as 0 in log plot
    for key, value in result.items():  # loop over linecuts
        # value is a dictionary {'distance': 1D array, 'cut': 1D array}
        tmp_str = f'{key}'
        print(f'\n{"#" * len(tmp_str)}\n' + tmp_str + '\n' +
              f'{"#" * len(tmp_str)}')
        for idx_roi, roi in enumerate(
                fit_roi[idx_point]
        ):  # loop over the ROIs, roi is a tuple of two number
            # define the fit initial center
            tmp_str = f'{roi}'
            indent = 2
            print(f'\n{" " * indent}{"-" * len(tmp_str)}\n' +
                  f'{" " * indent}' + tmp_str + '\n' +
                  f'{" " * indent}{"-" * len(tmp_str)}')
            # find linecut indices falling into the roi
            ind_start, ind_stop = util.find_nearest(value['distance'], roi)

            # fit a RectangleModel from lmfit to the peaks
            midpoint = (roi[0] + roi[1]) / 2
            offset = (roi[1] - roi[0]) / 8
            # initialize fit parameters (guess does not perform well)
            rect_mod = RectangleModel(form='erf')
            rect_params = rect_mod.make_params()
            rect_params['amplitude'].set(0.75, min=0.5, max=1)
            rect_params['center1'].set(midpoint - offset,
                                       min=roi[0],
                                       max=roi[1])
            rect_params['sigma1'].set(1, min=0.001, max=10)
            rect_params['center2'].set(midpoint + offset,
                                       min=roi[0],
                                       max=roi[1])
Beispiel #11
0
except MemoryError:  # switch to the for loop, not enough memory to calculate the CCF using vectorization
    print('Not enough memory, switching to the iterative calculation')
    start = time.time()
    for idx in range(
            nb_points[0]):  # loop over the points of the first q value
        # calculate the angle between the current point and all points from the second q value (delta in [0 pi])
        delta = np.arccos(
            np.sin(theta_phi_int['q1'][idx, 0]) *
            np.sin(theta_phi_int[key_q2][:, 0]) *
            np.cos(theta_phi_int[key_q2][:, 1] - theta_phi_int['q1'][idx, 1]) +
            np.cos(theta_phi_int['q1'][idx, 0]) *
            np.cos(theta_phi_int[key_q2][:, 0]))

        # find the nearest angular bin value for each value of the array delta
        indices = util.find_nearest(test_values=delta,
                                    reference_array=ang_corr_count[:, 0])

        # update the cross-correlation function with correlations for the current point. Nan values are already removed.
        ang_corr_count[indices, 1] = theta_phi_int['q1'][
            idx, 2] * theta_phi_int[key_q2][indices, 2]

        # update the counter of bin indices
        index, counts = np.unique(indices, return_counts=True)
        ang_corr_count[index, 2] = ang_corr_count[index, 2] + counts

        del index, counts, indices, delta
        gc.collect()
    end = time.time()
    print('Time ellapsed for the calculation of the CCF:', int(end - start),
          's')