Esempio n. 1
0
    def _plot_SPP_if_valid(self, ax=None, **kwargs):
        """
        Mark SPPs on the plot if they are valid.
        """
        if ax is None:
            ax = self.plt
        if isinstance(self.positions, numbers.Number):
            if is_inside(self.positions, self.x):
                x_closest, idx = find_nearest(self.x, self.positions)
                try:
                    ax.plot(x_closest, self.y_norm[idx], **kwargs)
                except (ValueError, TypeError):
                    ax.plot(x_closest, self.y[idx], **kwargs)

        if isinstance(self.positions, np.ndarray) or isinstance(
                self.positions, Iterable):
            if np.array(self.positions).ndim == 0:
                self.positions = np.atleast_1d(self.positions)
            # iterate over 0-d array: need to cast np.atleast_1d
            for i, val in enumerate(self.positions):
                if is_inside(self.positions[i], self.x):
                    x_closest, idx = find_nearest(self.x, self.positions[i])
                    try:
                        ax.plot(x_closest, self.y_norm[idx], **kwargs)
                    except (ValueError, TypeError):
                        ax.plot(x_closest, self.y[idx], **kwargs)
Esempio n. 2
0
def _split_on_SPP(a: np.ndarray, val: Union[List,
                                            np.ndarray]) -> List[np.ndarray]:
    """
    Split up an array based on value(s).
    """
    if isinstance(val, numbers.Number):
        if is_inside(val, a):
            v, _ = find_nearest(a, val)
            logger.info(f"split value was set to {v} instead of {val}.")
        else:
            logger.info(f"{val} is outside of array range, skipping.")
            return [a]
        idx = np.where(a != v)[0]
        return np.split(a[idx], np.where(np.diff(idx) != 1)[0] + 1)
    elif isinstance(val, (list, np.ndarray)):
        real_callbacks = []
        for i, v in enumerate(val):
            if not np.any(a == v):
                if is_inside(v, a):
                    value, _ = find_nearest(a, v)
                    real_callbacks.append(value)
                    logger.info(f"{v} was replaced with {value}.")
                else:
                    logger.info(f"{v} was thrown away, not in range..")
            else:
                real_callbacks.append(v)

        idx = np.in1d(a, real_callbacks)
        split_at = a.searchsorted(real_callbacks) - np.arange(
            0, np.count_nonzero(idx))
        return np.split(a[~idx], split_at)
Esempio n. 3
0
def cut_data(x, y, ref, sam, start=None, stop=None):
    x, y = _handle_input(x, y, ref, sam)
    if start is None:
        start = np.min(x)
    if stop is None:
        stop = np.max(x)
    if start < stop:
        low_item, _ = find_nearest(x, start)
        high_item, _ = find_nearest(x, stop)
        mask = np.where((x >= low_item) & (x <= high_item))
        return x[mask], y[mask]
    elif stop < start:
        raise ValueError("Start must not exceed stop value.")
    else:
        return np.array([]), np.array([])
Esempio n. 4
0
    def flip_around(self, value, side="left"):
        """
        Flip the phase's y values.

        Parameters
        ----------
        value : float
            The x value where to perform flipping.
        side : str, optional
            The side where to flip the y values. Default is "left".
        """
        if side == "left":
            idx = np.where(self.x <= value)[0]
        elif side == "right":
            idx = np.where(self.x >= value)[0]
        else:
            idx = np.array([])

        x_to_flip, y_to_flip = self.x[idx], self.y[idx]

        _, cls_idx = find_nearest(x_to_flip, value)
        logger.info(f"Using {self.x[cls_idx]} instead {value} as flip center.")
        value_base = self.y[cls_idx]
        y_to_flip *= -1
        y_to_flip += 2 * value_base
        self.y[idx] = y_to_flip
Esempio n. 5
0
    def _fit(self, reference_point, order):
        """
        This is meant to be used privately, when the pprint_disp
        is handled by another function. The `fit` method is for
        public use.
        """
        if self.is_coeff or self.is_dispersion_array:
            warnings.warn("No need to fit another curve.")
            return
        else:
            if self.GD_mode:
                order -= 1
            self.fitorder = order

            _function = _fit_config[order]

            x, y = np.copy(self.x), np.copy(self.y)
            x -= reference_point

            if _has_lmfit:

                fitmodel = Model(_function)
                pars = fitmodel.make_params(
                    **{f"b{i}": 1
                       for i in range(order + 1)})
                result = fitmodel.fit(y, x=x, params=pars)
            else:
                popt, pcov = curve_fit(_function, x, y, maxfev=8000)

            if _has_lmfit:
                dispersion, dispersion_std = transform_lmfit_params_to_dispersion(
                    *_unpack_lmfit(result.params.items()),
                    drop_first=True,
                    dof=1)
                fit_report = result.fit_report()
                self.fitted_curve = result.best_fit
            else:
                dispersion, dispersion_std = transform_cf_params_to_dispersion(
                    popt, drop_first=True, dof=1)
                fit_report = (
                    "To display detailed results you must have `lmfit` installed."
                )

                self.fitted_curve = _function(x, *popt)

            if self.GD_mode:
                _, idx = find_nearest(self.x, reference_point)
                dispersion = np.insert(dispersion, 0, self.fitted_curve[idx])
                dispersion_std = np.insert(dispersion_std, 0, 0)

                # The below line must have 7 elements, so slice these redundant coeffs..
                dispersion, dispersion_std = dispersion[:
                                                        -1], dispersion_std[:
                                                                            -1]

            self.coef_array = self.coef_temp(*dispersion)
            self.coef_std_array = self.coef_std_temp(*dispersion_std)

            return dispersion, dispersion_std, fit_report
Esempio n. 6
0
 def set_initial_region(self, percent):
     """ Determines the initial region to fit"""
     self._init_set = True
     if percent < 0 or percent > 100:
         raise ValueError("percent must satisfy percent > 0 or percent < 100.")
     _, idx = find_nearest(self.x, 0)
     self._upper_bound = np.floor(idx + (percent / 2) * (len(self.x) + 1))
     self._lower_bound = np.floor(idx - (percent / 2) * (len(self.x) + 1))
     self._upper_bound = self._upper_bound.astype(int)
     self._lower_bound = self._lower_bound.astype(int)
     if self._lower_bound < 0:
         self._lower_bound = 0
     if self._upper_bound > len(self.x):
         self._upper_bound = len(self.x)
     self._x_curr = self.x[self._lower_bound:self._upper_bound]
     self._y_curr = self._y_norm[self._lower_bound:self._upper_bound]
Esempio n. 7
0
 def _prepare_SPP_data(self):
     pos_x, pos_y = [], []
     if self.positions is not None:
         position = np.array(self.positions, dtype=np.float64).flatten()
         for i, val in enumerate(position):
             if is_inside(position[i], self.x):
                 x_closest, idx = find_nearest(self.x, position[i])
                 try:
                     pos_x.append(x_closest)
                     pos_y.append(self.y_norm[idx])
                 except (ValueError, TypeError):
                     pos_x.append(x_closest)
                     pos_y.append(self.y[idx])
         pos_x = np.array(pos_x)
         pos_y = np.array(pos_y)
     return pos_x, pos_y
Esempio n. 8
0
 def test_find_nearest(self):
     x = np.array([1, 2, 4, 9])
     el, idx = find_nearest(x, 10)
     assert el == 9
     assert idx == 3
Esempio n. 9
0
def min_max_method(
    x,
    y,
    ref,
    sam,
    ref_point,
    maxx=None,
    minx=None,
    SPP_callbacks=None,
):
    """
    Build the phase from extremal positions and SPP_callbacks.
    """
    x, y = _handle_input(x, y, ref, sam)
    if maxx is None:
        max_ind = argrelextrema(y, np.greater)
        maxx = x[max_ind]
    if minx is None:
        min_ind = argrelextrema(y, np.less)
        minx = x[min_ind]

    _, ref_index = find_nearest(x, ref_point)
    ref_point = x[ref_index]
    logger.info(f"refpoint set to {x[ref_index]} instead of {ref_point}.")

    # subtract the reference point from x axis at extremals
    max_freq = x[ref_index] - maxx
    min_freq = x[ref_index] - minx

    if SPP_callbacks is not None:
        if isinstance(SPP_callbacks, numbers.Number):
            SPP_callbacks -= ref_point
        elif isinstance(SPP_callbacks, (list, np.ndarray)):
            try:
                SPP_callbacks = np.asarray(SPP_callbacks) - ref_point
            except TypeError:
                pass
        else:
            raise TypeError("SPP_callbacks must be list-like, or number.")
        logger.info(
            f"SPP_callbacks are now {SPP_callbacks}, with ref_point {ref_point}."
        )

    # find which extremal point is where (relative to reference_point) and order them
    # as they need to be multiplied together with the corresponding order `m`
    neg_freq = np.sort(
        np.append(max_freq[max_freq < 0], min_freq[min_freq < 0]))[::-1]
    pos_freq = np.sort(
        np.append(max_freq[max_freq >= 0], min_freq[min_freq >= 0]))

    pos_data_x, pos_data_y = _build_single_phase_data(
        -pos_freq, SPP_callbacks=SPP_callbacks)

    # if we fail, the whole negative half is empty
    try:
        if np.diff(pos_data_y)[-1] < 0:
            flip = True
            logger.info(
                "Positive side was flipped because the other side is decreasing."
            )
        else:
            flip = False
    except IndexError:
        flip = False

    neq_data_x, neq_data_y = _build_single_phase_data(
        -neg_freq, SPP_callbacks=SPP_callbacks, flip=flip)

    x_s = np.insert(neq_data_x, np.searchsorted(neq_data_x, pos_data_x),
                    pos_data_x)
    y_s = np.insert(neq_data_y, np.searchsorted(neq_data_x, pos_data_x),
                    pos_data_y)

    return x_s + ref_point, -y_s + ref_point
Esempio n. 10
0
    def GD_lookup(self,
                  reference_point=None,
                  engine="cwt",
                  silent=False,
                  **kwargs):
        """
        Quick GD lookup: it finds extremal points near the
        `reference_point` and returns an average value of 2*pi
        divided by distances between consecutive minimal or maximal values.
        Since it's relying on peak detection, the results may be irrelevant
        in some cases. If the parent class is `~pysprint.CosFitMethod`, then
        it will set the predicted value as initial parameter for fitting.

        Parameters
        ----------
        reference_point : float
            The reference point for the algorithm.
        engine : str, optional
            The backend to use. Must be "cwt", "normal" or "fft".
            "cwt" will use `scipy.signal.find_peaks_cwt` function to
            detect peaks, "normal" will use `scipy.signal.find_peaks`
            to detect peaks. The "fft" engine uses Fourier-transform and
            looks for the outer peak to guess delay value. It's not
            reliable when working with low delay values.
        silent : bool, optional
            Whether to print the results immediately. Default in `False`.
        kwargs : dict, optional
            Additional keyword arguments to pass for peak detection
            algorithms. These are:
            pmin, pmax, threshold, width, floor_thres, etc..
            Most of them are described in the `find_peaks` and
            `find_peaks_cwt` docs.
        """
        precision = _get_config_value("precision")

        if engine not in ("cwt", "normal", "fft"):
            raise ValueError("Engine must be `cwt`, `fft` or `normal`.")

        if reference_point is None and engine != "fft":
            warnings.warn(
                f"Engine `{engine}` isn't available without reference point, falling back to FFT based prediction.",
                PySprintWarning)
            engine = "fft"

        if engine == "fft":
            pred, _ = find_center(*ifft_method(self.x, self.y))
            if pred is None:
                if not silent:
                    print("Prediction failed, skipping.")
                return
            print(f"The predicted GD is ± {pred:.{precision}f} fs.")

            if hasattr(self, "params"):
                self.params[3] = pred
            return

        if engine == "cwt":
            widths = kwargs.pop("widths", np.arange(1, 20))
            floor_thres = kwargs.pop("floor_thres", 0.05)
            x_min, _, x_max, _ = self.detect_peak_cwt(widths=widths,
                                                      floor_thres=floor_thres)

            # just validation
            _ = kwargs.pop("pmin", 0.1)
            _ = kwargs.pop("pmax", 0.1)
            _ = kwargs.pop("threshold", 0.35)

        else:
            pmin = kwargs.pop("pmin", 0.1)
            pmax = kwargs.pop("pmax", 0.1)
            threshold = kwargs.pop("threshold", 0.35)
            x_min, _, x_max, _ = self.detect_peak(pmin=pmin,
                                                  pmax=pmax,
                                                  threshold=threshold)

            # just validation
            _ = kwargs.pop("widths", np.arange(1, 10))
            _ = kwargs.pop("floor_thres", 0.05)

        if kwargs:
            raise TypeError(f"Invalid argument:{kwargs}")

        try:
            closest_val, idx1 = find_nearest(x_min, reference_point)
            m_closest_val, m_idx1 = find_nearest(x_max, reference_point)
        except (ValueError, IndexError):
            if not silent:
                print("Prediction failed, skipping.. ")
            return
        try:
            truncated = np.delete(x_min, idx1)
            second_closest_val, _ = find_nearest(truncated, reference_point)
        except (IndexError, ValueError):
            if not silent:
                print("Prediction failed, skipping.. ")
            return
        try:
            m_truncated = np.delete(x_max, m_idx1)
            m_second_closest_val, _ = find_nearest(m_truncated,
                                                   reference_point)
        except (IndexError, ValueError):
            if not silent:
                print("Prediction failed, skipping.. ")
            return

        lowguess = 2 * np.pi / np.abs(closest_val - second_closest_val)
        highguess = 2 * np.pi / np.abs(m_closest_val - m_second_closest_val)

        #  estimate the GD with that
        if hasattr(self, "params"):
            self.params[3] = (lowguess + highguess) / 2

        if not silent:
            print(
                f"The predicted GD is ± {((lowguess + highguess) / 2):.{precision}f} fs"
                f" based on reference point of {reference_point:.{precision}f}."
            )