Esempio n. 1
0
    def fit(self,
            fitter,
            data_arrays: list,
            *args,
            dask: str = 'forbidden',
            fit_kwargs: dict = None,
            fn_kwargs: dict = None,
            vectorized: bool = False,
            **kwargs) -> List[FitResults]:
        """
        Perform a fit on one or more DataArrays. This fit utilises a given fitter from `easyCore.Fitting.Fitter`, though
        there are a few differences to a standard easyCore fit. In particular, key-word arguments to control the
        optimisation algorithm go in the `fit_kwargs` dictionary, fit function key-word arguments go in the `fn_kwargs`
        and given key-word arguments control the `xarray.apply_ufunc` function.

        :param fitter: Fitting object which controls the fitting
        :type fitter: easyCore.Fitting.Fitter
        :param args: Arguments to go to the fit function
        :type args: Any
        :param dask: Dask control string. See `xarray.apply_ufunc` documentation
        :type dask: str
        :param fit_kwargs: Dictionary of key-word arguments to be supplied to the Fitting control
        :type fit_kwargs: dict
        :param fn_kwargs: Dictionary of key-words to be supplied to the fit function
        :type fn_kwargs: dict
        :param vectorized: Should the fit function be given dependents in a single object or split
        :type vectorized: bool
        :param kwargs: Key-word arguments for `xarray.apply_ufunc`. See `xarray.apply_ufunc` documentation
        :type kwargs: Any
        :return: Results of the fit
        :rtype: List[FitResults]
        """

        if fn_kwargs is None:
            fn_kwargs = {}
        if fit_kwargs is None:
            fit_kwargs = {}
        if not isinstance(data_arrays, (list, tuple)):
            data_arrays = [data_arrays]

        # In this case we are only fitting 1 dataset
        if len(data_arrays) == 1:
            variable_label = data_arrays[0]
            dataset = self._obj[variable_label]
            if self.__error_mapper.get(variable_label, False):
                # Pull out any sigmas and send them to the fitter.
                temp = self._obj[self.__error_mapper[variable_label]]
                temp[xr.ufuncs.isnan(temp)] = 1e5
                fit_kwargs['weights'] = temp
            # Perform a standard DataArray fit.
            return dataset.easyCore.fit(fitter,
                                        *args,
                                        fit_kwargs=fit_kwargs,
                                        fn_kwargs=fn_kwargs,
                                        dask=dask,
                                        vectorize=vectorized,
                                        **kwargs)
        else:
            # In this case we are fitting multiple datasets to the same fn!
            bdim_f = [
                self._obj[p].easyCore.fit_prep(fitter.fit_function)
                for p in data_arrays
            ]
            dim_names = [
                list(self._obj[p].dims.keys()) if isinstance(
                    self._obj[p].dims, dict) else self._obj[p].dims
                for p in data_arrays
            ]
            bdims = [bdim[0] for bdim in bdim_f]
            fs = [bdim[1] for bdim in bdim_f]
            old_fit_func = fitter.fit_function

            fn_array = []
            y_list = []
            for _idx, d in enumerate(bdims):
                dims = self._obj[data_arrays[_idx]].dims
                if isinstance(dims, dict):
                    dims = list(dims.keys())

                def local_fit_func(x, *args, idx=None, **kwargs):
                    kwargs['vectorize'] = vectorized
                    res = xr.apply_ufunc(fs[idx],
                                         *bdims[idx],
                                         *args,
                                         dask=dask,
                                         kwargs=fn_kwargs,
                                         **kwargs)
                    if dask != 'forbidden':
                        res.compute()
                    return res.stack(all_x=dim_names[idx])

                y_list.append(self._obj[data_arrays[_idx]].stack(all_x=dims))
                fn_array.append(local_fit_func)

            def fit_func(x, *args, **kwargs):
                res = []
                for idx in range(len(fn_array)):
                    res.append(fn_array[idx](x, *args, idx=idx, **kwargs))
                return xr.DataArray(np.concatenate(res, axis=0),
                                    coords={'all_x': x},
                                    dims='all_x')

            fitter.initialize(fitter.fit_object, fit_func)
            try:
                if fit_kwargs.get('weights', None) is not None:
                    del fit_kwargs['weights']
                x = xr.DataArray(np.arange(np.sum([y.size for y in y_list])),
                                 dims='all_x')
                y = xr.DataArray(np.concatenate(y_list, axis=0),
                                 coords={'all_x': x},
                                 dims='all_x')
                f_res = fitter.fit(x, y, **fit_kwargs)
                f_res = check_sanity_multiple(
                    f_res, [self._obj[p] for p in data_arrays])
            finally:
                fitter.fit_function = old_fit_func
            return f_res
Esempio n. 2
0
    def do_calc_setup(self, scale, this_x_array):
        if len(self.pattern.backgrounds) == 0:
            bg = np.zeros_like(this_x_array)
        else:
            bg = self.pattern.backgrounds[0].calculate(this_x_array)

        num_crys = len(self.current_crystal.keys())

        if num_crys == 0:
            return bg

        crystals = [self.storage[key] for key in self.current_crystal.keys()]
        phase_scales = [
            self.storage[str(key) + "_scale"]
            for key in self.current_crystal.keys()
        ]
        phase_lists = []
        profiles = []
        peak_dat = []
        for crystal in crystals:
            phasesL = cryspy.PhaseL()
            idx = [
                idx for idx, item in enumerate(self.phases.items)
                if item.label == crystal.data_name
            ][0]
            phasesL.items.append(self.phases.items[idx])
            phase_lists.append(phasesL)
            profile, peak = _do_run(self.model, self.polarized, this_x_array,
                                    crystal, phasesL)
            profiles.append(profile)
            peak_dat.append(peak)
        # pool = mp.ProcessPool(num_crys)
        # print("\n\nPOOL = " + str(pool))
        # result = pool.amap(functools.partial(_do_run, self.model, self.polarized, this_x_array), crystals,
        # phase_lists)
        # while not result.ready():
        #     time.sleep(0.01)
        # obtained = result.get()
        # profiles, peak_dat = zip(*obtained)
        # else:
        #     raise ArithmeticError

        # Do this for now
        x_str = "ttheta"
        if self.type == "powder1DTOF":
            x_str = "time"
        if self.polarized:
            # TODO *REPLACE PLACEHOLDER FN*
            dependents, additional_data = self.polarized_update(
                lambda up, down: up + down,
                crystals,
                profiles,
                peak_dat,
                phase_scales,
                x_str,
            )
        else:
            dependents, additional_data = self.nonPolarized_update(
                crystals, profiles, peak_dat, phase_scales, x_str)
        self.additional_data["phases"].update(additional_data)
        self.additional_data["global_scale"] = scale
        self.additional_data["background"] = bg
        self.additional_data["ivar_run"] = this_x_array
        self.additional_data["phase_names"] = list(additional_data.keys())
        self.additional_data["type"] = self.type

        # just the sum of all phases
        dependent_output = scale * np.sum(dependents, axis=0) + bg

        scaled_dependents = [scale * dep for dep in dependents]
        self.additional_data["components"] = scaled_dependents
        self.additional_data["components"] = scaled_dependents

        if borg.debug:
            print(f"y_calc: {dependent_output}")
        return (np.sum(
            [s["profile"] for s in self.additional_data["phases"].values()],
            axis=0) + self.additional_data["background"])
Esempio n. 3
0
 def get_total_y_for_phases(self) -> Tuple[np.ndarray, np.ndarray]:
     x_values = self.additional_data["ivar_run"]
     y_values = (np.sum(
         [s["profile"] for s in self.additional_data["phases"].values()],
         axis=0) + self.additional_data["background"])
     return x_values, y_values
Esempio n. 4
0
    def calculate(self, x_array: np.ndarray) -> np.ndarray:
        """
        For a given x calculate the corresponding y
        :param x_array: array of data points to be calculated
        :type x_array: np.ndarray
        :return: points calculated at `x`
        :rtype: np.ndarray
        """
        if self.filename is None:
            raise AttributeError

        if self.pattern is None:
            scale = 1.0
            offset = 0
        else:
            scale = self.pattern.scale.raw_value
            offset = self.pattern.zero_shift.raw_value

        this_x_array = x_array + offset

        # Experiment/Instrument/Simulation parameters
        x_min = this_x_array[0]
        x_max = this_x_array[-1]
        num_points = np.prod(x_array.shape)
        x_step = (x_max - x_min) / (num_points - 1)

        if len(self.pattern.backgrounds) == 0:
            bg = np.zeros_like(this_x_array)
        else:
            bg = self.pattern.backgrounds[0].calculate(this_x_array)

        dependents = []

        # Sample parameters
        # We assume that the phases items has the same indexing as the knownphases item
        cifs = self.grab_cifs()
        if len(cifs) == 0:
            raise ValueError("No phases found for calculation")

        for idx, file in enumerate(cifs):
            cif_file = CFML_api.CIFFile(file)
            cell = cif_file.cell
            space_group = cif_file.space_group
            atom_list = cif_file.atom_list
            job_info = cif_file.job_info

            job_info.range_2theta = (x_min, x_max)
            job_info.theta_step = x_step
            job_info.u_resolution = self.conditions["u_resolution"]
            job_info.v_resolution = self.conditions["v_resolution"]
            job_info.w_resolution = self.conditions["w_resolution"]
            job_info.x_resolution = self.conditions["x_resolution"]
            job_info.y_resolution = self.conditions["y_resolution"]
            job_info.lambdas = (self.conditions["lamb"],
                                self.conditions["lamb"])
            job_info.bkg = 0.0

            # Calculations
            try:
                reflection_list = CFML_api.ReflectionList(
                    cell, space_group, True, job_info)

                reflection_list.compute_structure_factors(
                    space_group, atom_list, job_info)

                diffraction_pattern = CFML_api.DiffractionPattern(
                    job_info, reflection_list, cell.reciprocal_cell_vol)
            except Exception as e:
                for cif in cifs:
                    os.remove(cif)
                raise ArithmeticError

            item = list(self.known_phases.items())[idx]
            key = list(self.known_phases.keys())[idx]
            phase_scale = self.getPhaseScale(key)

            dependent, additional_data = self.nonPolarized_update(
                item,
                diffraction_pattern,
                reflection_list,
                job_info,
                scales=phase_scale)
            dependents.append(dependent)
            self.additional_data["phases"].update(additional_data)
        for cif in cifs:
            os.remove(cif)
        self.additional_data["global_scale"] = scale
        self.additional_data["background"] = bg
        self.additional_data["ivar_run"] = this_x_array
        self.additional_data["ivar"] = x_array
        self.additional_data["components"] = [
            scale * dep + bg for dep in dependents
        ]
        self.additional_data["phase_names"] = list(self.known_phases.items())
        self.additional_data["type"] = "powder1DCW"

        dependent_output = scale * np.sum(dependents, axis=0) + bg

        if borg.debug:
            print(f"y_calc: {dependent_output}")
        return (np.sum(
            [s["profile"] for s in self.additional_data["phases"].values()],
            axis=0) + self.additional_data["background"])