From d2cd6aabf923a831794ebe0429fac8dedad111c6 Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Fri, 23 Jul 2021 11:57:27 +0200 Subject: [PATCH 01/18] Update pyproject.toml --- pyproject.toml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2ff424b2..959917ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,15 +16,21 @@ documentation = "https://github.com/easyScience/easyDiffractionLib" include = ["CHANGELOG.md"] packages = [ { include = "easyDiffractionLib" } ] +[[tool.poetry.source]] +name = "easyScience" +url = "https://easyscience.github.io/pypi/" +secondary = true + [tool.poetry.dependencies] python = "^3.7, <3.8" #cryspy = "^0.5" cryspy = { git = 'https://github.com/ikibalin/cryspy.git', rev = 'bravis_type_fix' } matplotlib = "^3.4" # easyScience -libsDarwin = { git = 'https://github.com/easyScience/libsDarwin.git', rev = 'develop', platform = 'darwin' } -libsLinux = { git = 'https://github.com/easyScience/libsLinux.git', rev = 'develop', platform = 'linux' } -libsWin32 = { git = 'https://github.com/easyScience/libsWin32.git', rev = 'develop', platform = 'win32' } +# libsDarwin = { git = 'https://github.com/easyScience/libsDarwin.git', rev = 'develop', platform = 'darwin' } +# libsLinux = { git = 'https://github.com/easyScience/libsLinux.git', rev = 'develop', platform = 'linux' } +# libsWin32 = { git = 'https://github.com/easyScience/libsWin32.git', rev = 'develop', platform = 'win32' } +CFML = '^0.0.1' easyScienceCore = { git = 'https://github.com/easyScience/easyCore.git', rev = 'develop' } [tool.poetry.dev-dependencies] From 3300b0dc7e4d94e589d624cb2f81142d8098d83d Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Fri, 23 Jul 2021 11:58:42 +0200 Subject: [PATCH 02/18] Update __init__.py --- easyDiffractionLib/Calculators/__init__.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/easyDiffractionLib/Calculators/__init__.py b/easyDiffractionLib/Calculators/__init__.py index c4a4ad0a..db73d94a 100644 --- a/easyDiffractionLib/Calculators/__init__.py +++ b/easyDiffractionLib/Calculators/__init__.py @@ -1,21 +1,3 @@ __author__ = 'github.com/wardsimon' __version__ = '0.0.1' -import os, sys - -if 'darwin' in sys.platform: - import libsDarwin - libs_path = list(libsDarwin.__path__)[0] -elif 'linux' in sys.platform: - import libsLinux - libs_path = list(libsLinux.__path__)[0] -elif 'win32' in sys.platform: - import libsWin32 - libs_path = list(libsWin32.__path__)[0] -else: - raise NotImplementedError(f"Platform '{sys.platform}' is not supported") - -gsasii_path = os.path.join(libs_path, "GSASII") - -sys.path.append(libs_path) -sys.path.append(gsasii_path) From 7b0fdc0c50c9f4072e7eb5dcca20c436bf198ef0 Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Fri, 23 Jul 2021 12:02:21 +0200 Subject: [PATCH 03/18] Update test.yml --- .github/workflows/test.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eca056da..66e204be 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,9 +31,9 @@ jobs: # with: # ssh-private-key: ${{ secrets.GH_WEBFACTORY_KEY }} - - name: Set up dependences - if: runner.os == 'Linux' - run: sudo apt-get install libgfortran4 +# - name: Set up dependences +# if: runner.os == 'Linux' +# run: sudo apt-get install libgfortran4 - name: Set up Python environment uses: actions/setup-python@v2 @@ -52,8 +52,8 @@ jobs: - name: Create venv and install dependences run: poetry update - - name: Relink CrysFML from default Python dylib - run: poetry run python tools/Scripts/RelinkCrysfml.py $pythonLocation +# - name: Relink CrysFML from default Python dylib +# run: poetry run python tools/Scripts/RelinkCrysfml.py $pythonLocation - name: Run main.py run: poetry run easyDiffractionLib From bd53c9bcf422b8ad1b1d1dc8901dc4bc4bdf2a4b Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Mon, 26 Jul 2021 16:32:01 +0200 Subject: [PATCH 04/18] Update pyproject.toml --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 959917ae..0c9f8393 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ matplotlib = "^3.4" # libsLinux = { git = 'https://github.com/easyScience/libsLinux.git', rev = 'develop', platform = 'linux' } # libsWin32 = { git = 'https://github.com/easyScience/libsWin32.git', rev = 'develop', platform = 'win32' } CFML = '^0.0.1' +GSASII = '^0.0.1' easyScienceCore = { git = 'https://github.com/easyScience/easyCore.git', rev = 'develop' } [tool.poetry.dev-dependencies] From 479a62081aa0ca169b44ca8c3975898a2231f3b6 Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Mon, 26 Jul 2021 16:36:31 +0200 Subject: [PATCH 05/18] Update GSASII.py --- easyDiffractionLib/Calculators/GSASII.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/easyDiffractionLib/Calculators/GSASII.py b/easyDiffractionLib/Calculators/GSASII.py index 59a3a153..578ead58 100644 --- a/easyDiffractionLib/Calculators/GSASII.py +++ b/easyDiffractionLib/Calculators/GSASII.py @@ -3,7 +3,7 @@ import os, pathlib from easyCore import borg -import GSASIIscriptable as G2sc +from GSASII import GSASIIscriptable as G2sc from easyCore import np @@ -161,4 +161,4 @@ def get_hkl(self, tth: np.array = None) -> dict: hkl_dict = self.hkl_dict if tth is not None: pass - return hkl_dict \ No newline at end of file + return hkl_dict From 2fd651063aa4f9d77d757d715948476f1c461abc Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Tue, 27 Jul 2021 20:39:09 +0200 Subject: [PATCH 06/18] ReflectionList nrefs to nref --- easyDiffractionLib/Calculators/CFML.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easyDiffractionLib/Calculators/CFML.py b/easyDiffractionLib/Calculators/CFML.py index a770ddc7..194e2cfb 100644 --- a/easyDiffractionLib/Calculators/CFML.py +++ b/easyDiffractionLib/Calculators/CFML.py @@ -122,7 +122,7 @@ def calculate(self, x_array: np.ndarray) -> np.ndarray: start_time = timeit.default_timer() - hkltth = np.array([[*reflection_list[i].hkl, reflection_list[i].stl] for i in range(reflection_list.nrefs)]) + hkltth = np.array([[*reflection_list[i].hkl, reflection_list[i].stl] for i in range(reflection_list.nref)]) self.hkl_dict['ttheta'] = np.rad2deg(np.arcsin(hkltth[:, 3] * job_info.lambdas[0])) * 2 self.hkl_dict['h'] = hkltth[:, 0] From 4cdd1946e96aee111ef5412c2f9182edd89348d2 Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Fri, 30 Jul 2021 14:03:25 +0200 Subject: [PATCH 07/18] Moving to a more module based scheme --- easyDiffractionLib/Calculators/cryspy.py | 28 +- .../Elements/Experiments/Experiment.py | 9 +- easyDiffractionLib/Interfaces/cryspy.py | 11 +- easyDiffractionLib/Jobs.py | 63 +++++ easyDiffractionLib/Profiles/P1D.py | 248 ++++++++++++++++++ easyDiffractionLib/Profiles/__init__.py | 2 + easyDiffractionLib/Profiles/common.py | 148 +++++++++++ easyDiffractionLib/Runner.py | 49 ++++ pyproject.toml | 5 +- tests/fit_script.py | 15 +- 10 files changed, 556 insertions(+), 22 deletions(-) create mode 100644 easyDiffractionLib/Jobs.py create mode 100644 easyDiffractionLib/Profiles/P1D.py create mode 100644 easyDiffractionLib/Profiles/__init__.py create mode 100644 easyDiffractionLib/Profiles/common.py create mode 100644 easyDiffractionLib/Runner.py diff --git a/easyDiffractionLib/Calculators/cryspy.py b/easyDiffractionLib/Calculators/cryspy.py index 5446402c..34ac06b5 100644 --- a/easyDiffractionLib/Calculators/cryspy.py +++ b/easyDiffractionLib/Calculators/cryspy.py @@ -32,6 +32,7 @@ def __init__(self): self.storage = {} self.current_crystal = {} self.model = None + self.type = 'powder1D' @property def cif_str(self): @@ -42,8 +43,16 @@ def cif_str(self): def cif_str(self, value): self.createCrystal_fromCifStr(value) - def createModel(self, model_id, model_type=None): - self.model = cryspy.Pd(background=cryspy.PdBackgroundL(), phase=cryspy.PhaseL()) + def createModel(self, model_id, model_type=''): + model = { + 'background': cryspy.PdBackgroundL(), + 'phase': cryspy.PhaseL() + } + cls = cryspy.Pd + if model_type == 'tof': + cls = cryspy.Tof + model['background'] = cryspy.TOFBackground() + self.model = cls(**model) def createPhase(self, crystal_name, key='phase'): phase = cryspy.Phase(label=crystal_name, scale=1, igsize=0) @@ -187,7 +196,7 @@ def updateResolution(self, key, **kwargs): for r_key in kwargs.keys(): setattr(resolution, r_key, kwargs[key]) - def calculate(self, x_array: np.ndarray) -> np.ndarray: + def powder_1d_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 @@ -234,6 +243,19 @@ def calculate(self, x_array: np.ndarray) -> np.ndarray: print(f"y_calc: {res}") return res + 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 + """ + res = np.zeros_like(x_array) + if self.type == 'powder1D': + return self.powder_1d_calculate(x_array) + return res + def get_hkl(self, tth: np.array = None) -> dict: hkl_dict = self.hkl_dict diff --git a/easyDiffractionLib/Elements/Experiments/Experiment.py b/easyDiffractionLib/Elements/Experiments/Experiment.py index feffb550..8ca424d3 100644 --- a/easyDiffractionLib/Elements/Experiments/Experiment.py +++ b/easyDiffractionLib/Elements/Experiments/Experiment.py @@ -14,16 +14,15 @@ class Pars1D(BaseObj): 'wavelength': { '@module': 'easyCore.Objects.Base', '@class': 'Parameter', - '@version': '0.0.1', 'name': 'wavelength', 'units': 'angstrom', 'value': 1.54056, - 'fixed': True + 'fixed': True, + 'min': 0 }, 'resolution_u': { '@module': 'easyCore.Objects.Base', '@class': 'Parameter', - '@version': '0.0.1', 'name': 'resolution_u', 'value': 0.0002, 'fixed': True @@ -31,7 +30,6 @@ class Pars1D(BaseObj): 'resolution_v': { '@module': 'easyCore.Objects.Base', '@class': 'Parameter', - '@version': '0.0.1', 'name': 'resolution_v', 'value': -0.0002, 'fixed': True @@ -40,7 +38,6 @@ class Pars1D(BaseObj): 'resolution_w': { '@module': 'easyCore.Objects.Base', '@class': 'Parameter', - '@version': '0.0.1', 'name': 'resolution_w', 'value': 0.012, 'fixed': True @@ -49,7 +46,6 @@ class Pars1D(BaseObj): 'resolution_x': { '@module': 'easyCore.Objects.Base', '@class': 'Parameter', - '@version': '0.0.1', 'name': 'resolution_x', 'value': 0.0, 'fixed': True @@ -57,7 +53,6 @@ class Pars1D(BaseObj): 'resolution_y': { '@module': 'easyCore.Objects.Base', '@class': 'Parameter', - '@version': '0.0.1', 'name': 'resolution_y', 'value': 0.0, 'fixed': True diff --git a/easyDiffractionLib/Interfaces/cryspy.py b/easyDiffractionLib/Interfaces/cryspy.py index 7881c81e..0e9f6dab 100644 --- a/easyDiffractionLib/Interfaces/cryspy.py +++ b/easyDiffractionLib/Interfaces/cryspy.py @@ -5,7 +5,7 @@ from easyDiffractionLib.Interfaces.interfaceTemplate import InterfaceTemplate from easyCore.Objects.Inferface import ItemContainer from easyDiffractionLib.Calculators.cryspy import Cryspy as Cryspy_calc -from easyDiffractionLib.Elements.Experiments.Experiment import Pars1D +from easyDiffractionLib.Profiles.P1D import Instrument1DParameters, Powder1DParameters from easyDiffractionLib.Elements.Experiments.Pattern import Pattern1D from easyDiffractionLib import Lattice, SpaceGroup, Site, Phase, Phases @@ -58,7 +58,7 @@ def create(self, model): r_list = [] t_ = type(model) model_key = self.__identify(model) - if issubclass(t_, Pars1D): + if issubclass(t_, Instrument1DParameters): # These parameters are linked to the Resolution and Setup cryspy objects res_key = self.calculator.createResolution() setup_key = self.calculator.createSetup() @@ -74,7 +74,7 @@ def create(self, model): self.calculator.genericReturn, self.calculator.genericUpdate) ) - elif issubclass(t_, Pattern1D): + elif issubclass(t_, Powder1DParameters): # These parameters do not link directly to cryspy objects. self.calculator.pattern = model elif issubclass(t_, Lattice): @@ -108,10 +108,13 @@ def create(self, model): for atom in model.atoms: self.calculator.assignAtom_toCrystal(self.__identify(atom), model_key) elif issubclass(t_, Phases): - self.calculator.createModel(model_key) + # self.calculator.createModel(model_key, 'powder1D') for phase in model: ident = str(self.__identify(phase)) + '_phase' self.calculator.assignPhase(model_key, ident) + elif t_.__name__ == 'Powder1D': + # #TODO Check to see if parameters and pattern should be initialized here. + self.calculator.createModel(model_key, 'powder1D') else: if self._borg.debug: print(f"I'm a: {type(model)}") diff --git a/easyDiffractionLib/Jobs.py b/easyDiffractionLib/Jobs.py new file mode 100644 index 00000000..3f0d1f08 --- /dev/null +++ b/easyDiffractionLib/Jobs.py @@ -0,0 +1,63 @@ +__author__ = 'github.com/wardsimon' +__version__ = '0.1.1' + +from easyCore.Datasets.xarray import xr, np +from easyDiffractionLib.Profiles.common import _PowderBase +from easyDiffractionLib.interface import InterfaceFactory +from easyCore.Fitting.Fitting import Fitter + + +class Powder1D(_PowderBase): + def __init__(self, name: str, datastore: xr.Dataset, phases=None, parameters=None, pattern=None): + from easyDiffractionLib.Profiles.P1D import Unpolarized1DClasses + interface = InterfaceFactory() + super(Powder1D, self).__init__(name, Unpolarized1DClasses, datastore, phases, parameters, pattern, interface=interface) + self._x_axis_name = 'tth' + self._y_axis_prefix = 'Intensity_' + + def create_simulation(self, tth, simulation_name=None): + if not isinstance(tth, xr.DataArray): + coord_name = self.datastore._simulations._simulation_prefix + self.name + '_tth' + self.datastore.add_coordinate(coord_name, tth) + self.datastore.store[coord_name].name = self._x_axis_name + else: + coord_name = tth.name + x, f = self.datastore.store[coord_name].easyCore.fit_prep(self.interface.fit_func, + bdims=xr.broadcast(self.datastore.store[coord_name].transpose())) + y = xr.apply_ufunc(f, *x) + y.name = self._y_axis_prefix + self.name + '_sim' + if simulation_name is None: + simulation_name = self.name + self.datastore._simulations.add_simulation(simulation_name, y) + return y + + def plot_simulation(self, simulation_name=None): + if simulation_name is None: + sim_name = self.datastore._simulations._simulation_prefix + self.name + else: + sim_name = self.datastore._simulations._simulation_prefix + self.name + '_' + simulation_name + return self.datastore.store[sim_name].plot() + + def add_experiment(self, experiment_name, file_path): + data_x, data_y, data_e = np.loadtxt(file_path, unpack=True) + coord_name = self.name + '_' + experiment_name + '_tth' + + self.datastore.store.easyCore.add_coordinate(coord_name, data_x) + self.datastore.store.easyCore.add_variable(self.name + '_' + experiment_name + '_I', [coord_name], data_y) + self.datastore.store.easyCore.sigma_attach(self.name + '_' + experiment_name + '_I', data_e) + # self._experiments[] + + def simulate_experiment(self, experiment_name=None): + tth_name = self.name + '_' + experiment_name + '_tth' + tth = self.datastore.store[tth_name] + return self.create_simulation(tth, simulation_name=self.name + '_' + experiment_name) + + def plot_experiment(self, experiment_name=None): + dataarray_name = self.name + '_' + experiment_name + '_I' + return self.datastore.store[dataarray_name].plot() + + def fit_experiment(self, experiment_name, fitter=None): + dataarray_name = self.name + '_' + experiment_name + '_I' + if fitter is None: + fitter = Fitter(self, self.interface.fit_func) + return self.datastore.store[dataarray_name].easyCore.fit(fitter) \ No newline at end of file diff --git a/easyDiffractionLib/Profiles/P1D.py b/easyDiffractionLib/Profiles/P1D.py new file mode 100644 index 00000000..d1b98b73 --- /dev/null +++ b/easyDiffractionLib/Profiles/P1D.py @@ -0,0 +1,248 @@ +__author__ = 'github.com/wardsimon' +__version__ = '0.0.1' + +from typing import TypeVar, List + +from easyCore.Datasets.xarray import xr +from easyCore.Objects.Base import BaseObj, Parameter +from copy import deepcopy +from easyCore.Utils.json import MontyDecoder +from easyDiffractionLib.Elements.Backgrounds.Background import BackgroundContainer +from easyDiffractionLib.Profiles.common import JobSetup, _DataClassBase + +_decoder = MontyDecoder() +T = TypeVar('T') + + +class Powder1DSim(_DataClassBase): + def __init__(self, dataset): + super(Powder1DSim, self).__init__(dataset) + self._simulation_prefix = 'sim_' + self.name = '' + + def add_simulation(self, simulation_name, simulation): + self._dataset[self._simulation_prefix + simulation_name] = simulation + + # @property + # def simulations(self) -> xr.Dataset: + # temp_dataset = xr.Dataset() + # for sim in self.simulation_names: + # temp_dataset[sim] = self._dataset[sim] + # return temp_dataset + # + # @property + # def simulation_names(self) -> List[str]: + # sims = [a for a in self._dataset.variables.keys() if a.startswith(self._simulation_prefix)] + # return sims + + +class Powder1DExp(_DataClassBase): + def __init__(self, dataset, simulation_prefix): + super(Powder1DExp, self).__init__(dataset) + self.simulation_prefix = simulation_prefix + + @property + def experiments(self) -> xr.Dataset: + temp_dataset = xr.Dataset() + for exp in self.experiment_names: + temp_dataset[exp] = self._dataset[exp] + return temp_dataset + + @property + def experiment_names(self) -> List[str]: + exps = [a for a in self._dataset.variables.keys() + if not a.startswith(self.simulation_prefix) and + not a in self._dataset.dims] + return exps + + +class Powder1DPolSim(Powder1DSim): + def __init__(self, dataset): + super(Powder1DPolSim, self).__init__(dataset) + + +class Powder1DPolExp(Powder1DExp): + def __init__(self, dataset, simulation_prefix): + super(Powder1DPolExp, self).__init__(dataset, simulation_prefix) + + +class Powder1DParameters(BaseObj): + _name = '1DPowderProfile' + _defaults = { + 'zero_shift': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'zero_shift', + 'units': 'degree', + 'value': 0.0, + 'fixed': True + }, + 'scale': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'scale', + 'value': 1, + 'fixed': True + }, + 'backgrounds': { + '@module': 'easyDiffractionLib.Elements.Backgrounds.Background', + '@class': 'BackgroundContainer', + '@version': '0.0.1', + 'data': [], + } + } + + def __init__(self, + zero_shift: Parameter, scale: Parameter, + backgrounds: BackgroundContainer, + interface=None): + super().__init__(self.__class__.__name__, + zero_shift=zero_shift, scale=scale, + backgrounds=backgrounds) + self.name = self._name + self.interface = interface + + @classmethod + def from_pars(cls, + zero_shift: float = _defaults['zero_shift']['value'], + scale: float = _defaults['scale']['value'] + ): + defaults = deepcopy(cls._defaults) + defaults['zero_shift']['value'] = zero_shift + zero_shift = _decoder.process_decoded(defaults['zero_shift']) + defaults['scale']['value'] = scale + scale = _decoder.process_decoded(defaults['scale']) + backgrounds = BackgroundContainer() + return cls(zero_shift=zero_shift, scale=scale, backgrounds=backgrounds) + + @classmethod + def default(cls): + defaults = deepcopy(cls._defaults) + zero_shift = _decoder.process_decoded(defaults['zero_shift']) + scale = _decoder.process_decoded(defaults['scale']) + backgrounds = BackgroundContainer() + + return cls(zero_shift=zero_shift, scale=scale, backgrounds=backgrounds) + + +class PolPowder1DParameters(Powder1DParameters): + pass + + +class Instrument1DParameters(BaseObj): + _name = 'InstrumentalParameters' + _defaults = { + 'wavelength': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'wavelength', + 'units': 'angstrom', + 'value': 1.54056, + 'fixed': True + }, + 'resolution_u': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'resolution_u', + 'value': 0.0002, + 'fixed': True + }, + 'resolution_v': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'resolution_v', + 'value': -0.0002, + 'fixed': True + + }, + 'resolution_w': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'resolution_w', + 'value': 0.012, + 'fixed': True + + }, + 'resolution_x': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'resolution_x', + 'value': 0.0, + 'fixed': True + }, + 'resolution_y': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'resolution_y', + 'value': 0.0, + 'fixed': True + } + } + + def __init__(self, + wavelength: Parameter, + resolution_u: Parameter, resolution_v: Parameter, resolution_w: Parameter, + resolution_x: Parameter, resolution_y: Parameter, + interface=None): + super().__init__(self.__class__.__name__, + wavelength=wavelength, + resolution_u=resolution_u, resolution_v=resolution_v, resolution_w=resolution_w, + resolution_x=resolution_x, resolution_y=resolution_y) + self.name = self._name + self.interface = interface + + @classmethod + def from_pars(cls, + wavelength: float = _defaults['wavelength']['value'], + resolution_u: float = _defaults['resolution_u']['value'], + resolution_v: float = _defaults['resolution_v']['value'], + resolution_w: float = _defaults['resolution_w']['value'], + resolution_x: float = _defaults['resolution_x']['value'], + resolution_y: float = _defaults['resolution_y']['value'] + ): + defaults = deepcopy(cls._defaults) + defaults['wavelength']['value'] = wavelength + wavelength = _decoder.process_decoded(defaults['wavelength']) + defaults['resolution_u']['value'] = resolution_u + resolution_u = _decoder.process_decoded(defaults['resolution_u']) + defaults['resolution_v']['value'] = resolution_v + resolution_v = _decoder.process_decoded(defaults['resolution_v']) + defaults['resolution_w']['value'] = resolution_w + resolution_w = _decoder.process_decoded(defaults['resolution_w']) + defaults['resolution_x']['value'] = resolution_x + resolution_x = _decoder.process_decoded(defaults['resolution_x']) + defaults['resolution_y']['value'] = resolution_y + resolution_y = _decoder.process_decoded(defaults['resolution_y']) + return cls(wavelength=wavelength, + resolution_u=resolution_u, resolution_v=resolution_v, resolution_w=resolution_w, + resolution_x=resolution_x, resolution_y=resolution_y) + + @classmethod + def default(cls): + defaults = deepcopy(cls._defaults) + wavelength = _decoder.process_decoded(defaults['wavelength']) + resolution_u = _decoder.process_decoded(defaults['resolution_u']) + resolution_v = _decoder.process_decoded(defaults['resolution_v']) + resolution_w = _decoder.process_decoded(defaults['resolution_w']) + resolution_x = _decoder.process_decoded(defaults['resolution_x']) + resolution_y = _decoder.process_decoded(defaults['resolution_y']) + return cls(wavelength=wavelength, + resolution_u=resolution_u, resolution_v=resolution_v, resolution_w=resolution_w, + resolution_x=resolution_x, resolution_y=resolution_y) + + +class Instrument1DPolParameters(Instrument1DParameters): + pass + + +Unpolarized1DClasses = JobSetup([Powder1DSim, Powder1DExp], + Powder1DParameters, + Instrument1DParameters) diff --git a/easyDiffractionLib/Profiles/__init__.py b/easyDiffractionLib/Profiles/__init__.py new file mode 100644 index 00000000..19c79bc2 --- /dev/null +++ b/easyDiffractionLib/Profiles/__init__.py @@ -0,0 +1,2 @@ +__author__ = 'github.com/wardsimon' +__version__ = '0.0.1' diff --git a/easyDiffractionLib/Profiles/common.py b/easyDiffractionLib/Profiles/common.py new file mode 100644 index 00000000..9d34ae80 --- /dev/null +++ b/easyDiffractionLib/Profiles/common.py @@ -0,0 +1,148 @@ +__author__ = 'github.com/wardsimon' +__version__ = '0.0.1' + +import os +import tempfile +from typing import Union, TypeVar + +from easyCore.Utils.UndoRedo import property_stack_deco +from easyCore.Objects.Base import BaseObj +from easyDiffractionLib import Phases, Phase +from easyCore.Datasets.xarray import xr + + +DataClassBaseType = TypeVar('DataClassBaseType', bound='_DataClassBase') + + +class _DataClassBase: + def __init__(self, dataset): + self._dataset = dataset + + +class DataContainer: + + def __init__(self, sim_store: DataClassBaseType, exp_store: DataClassBaseType): + self._simulations = sim_store + self._experiments = exp_store + self.store = sim_store._dataset + self._relations = {} + self.coordinate_labels = [] + self.coordinate_units = [] + + @classmethod + def prepare(cls, dataset, simulation_class, experiment_class): + class Simulation(simulation_class): + def __init__(self): + super(Simulation, self).__init__(dataset) + + class Experiment(experiment_class): + def __init__(self, sim_prefix): + super(Experiment, self).__init__(dataset, sim_prefix) + + s = Simulation() + e = Experiment(s._simulation_prefix) + + return cls(s, e) + + def add_coordinate(self, coordinate_name, coordinate_values): + self.store.easyCore.add_coordinate(coordinate_name, coordinate_values) + + def add_variable(self, variable_name, variable_coordinates, values): + self.store.easyCore.add_variable(variable_name, variable_coordinates, values) + + +class JobSetup: + def __init__(self, datastore_classes, + instrumental_parameter_class, + pattern_class): + self.datastore_classes = datastore_classes + self.instrumental_parameter_class = instrumental_parameter_class + self.pattern_class = pattern_class + + +class _PowderBase(BaseObj): + def __init__(self, + name: str = '', + job_type=None, + datastore: xr.Dataset = None, + phases: Union[Phase, Phases] = None, + parameters=None, + pattern=None, + interface=None): + if isinstance(phases, Phase): + phases = Phases('Phases', phases) + elif phases is None: + phases = Phases('Phases') + + if not isinstance(phases, Phases): + raise AttributeError('`phases` must be a Crystal or Crystals') + + if parameters is None: + parameters = job_type.pattern_class.default() + + if pattern is None: + pattern = job_type.instrumental_parameter_class.default() + + super(_PowderBase, self).__init__(name, _phases=phases, _parameters=parameters, _pattern=pattern) + + self.__constituting_classes = job_type + self.__dataset = datastore + self.datastore = DataContainer.prepare(self.__dataset, *job_type.datastore_classes) + + self.filename = os.path.join(tempfile.gettempdir(), 'easydiffraction_temp.cif') + self.output_index = None + self.interface = interface + + def get_phase(self, phase_index): + return self._phases[phase_index] + + def get_background(self, experiment_name: str): + return self._pattern.backgrounds[experiment_name] + + def set_background(self, background): + self._pattern.backgrounds.append(background) + + def remove_background(self, background): + if background.linked_experiment.raw_value in self._pattern.backgrounds.linked_experiments: + del self._pattern.backgrounds[background.linked_experiment.raw_value] + else: + raise ValueError + + @property + def backgrounds(self): + return self._pattern.backgrounds + + @property + def phases(self): + return self._phases + + @phases.setter + @property_stack_deco + def phases(self, value): + if isinstance(value, Phase): + value = Phases('Phases', value) + if not isinstance(value, Phases): + raise ValueError + self._phases = value + self._borg.map.add_edge(self, value) + self._phases.interface = self.interface + @property + def parameters(self): + return self._parameters + + @parameters.setter + @property_stack_deco + def parameters(self, value): + self._parameters = value + self._parameters.interface = self._interface + + @property + def pattern(self): + return self._pattern + + def as_dict(self, skip: list = None) -> dict: + d = super(_PowderBase, self).as_dict(skip=skip) + del d['_phases'] + del d['_parameters'] + del d['_pattern'] + return d \ No newline at end of file diff --git a/easyDiffractionLib/Runner.py b/easyDiffractionLib/Runner.py new file mode 100644 index 00000000..7415936e --- /dev/null +++ b/easyDiffractionLib/Runner.py @@ -0,0 +1,49 @@ +__author__ = 'github.com/wardsimon' +__version__ = '0.0.1' + +from easyCore.Datasets.xarray import xr + + +class Runner: + def __init__(self): + self._data = xr.Dataset() + self._jobs = {} + self._instrumental_parameters = [] + self._instrumental_parameters_link = {} + self._experimental_parameters = [] + self._experimental_parameters_link = {} + self._phases = [] + self._phase_link = {} + + def add_job(self, name: str, job_type: str = 'powder1d'): + if job_type == 'powder1d': + from easyDiffractionLib.Jobs import Powder1D + job_type = Powder1D + else: + raise NotImplementedError + job = job_type(name, self._data) + self._jobs[name] = { + 'object': job, + 'phases': job.phases, + 'instrumental_parameters': job.parameters, + 'experimental_parameters': job.pattern + } + + @property + def phases(self): + return [phase.name for phase in self._phases] + + def add_phase(self, phase, job_name: str = None): + if phase.name in self.phases: + raise AttributeError + if job_name is None: + self._phases.append(phase) + return + if job_name not in self._jobs.keys(): + raise AttributeError + self._phases.append(phase) + self._jobs[job_name]['object'].phases = phase + + @property + def jobs(self): + return {key: job['object'] for key, job in self._jobs.items()} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 0c9f8393..2d5645d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,14 +22,11 @@ url = "https://easyscience.github.io/pypi/" secondary = true [tool.poetry.dependencies] -python = "^3.7, <3.8" +python = "^3.7, <3.9" #cryspy = "^0.5" cryspy = { git = 'https://github.com/ikibalin/cryspy.git', rev = 'bravis_type_fix' } matplotlib = "^3.4" # easyScience -# libsDarwin = { git = 'https://github.com/easyScience/libsDarwin.git', rev = 'develop', platform = 'darwin' } -# libsLinux = { git = 'https://github.com/easyScience/libsLinux.git', rev = 'develop', platform = 'linux' } -# libsWin32 = { git = 'https://github.com/easyScience/libsWin32.git', rev = 'develop', platform = 'win32' } CFML = '^0.0.1' GSASII = '^0.0.1' easyScienceCore = { git = 'https://github.com/easyScience/easyCore.git', rev = 'develop' } diff --git a/tests/fit_script.py b/tests/fit_script.py index 22a30a92..8e9bb53a 100644 --- a/tests/fit_script.py +++ b/tests/fit_script.py @@ -41,17 +41,24 @@ # Vary the scale and the BG points S.pattern.scale.fixed = False +S.pattern.zero_shift.fixed = False S.parameters.resolution_u.fixed = False S.parameters.resolution_v.fixed = False S.parameters.resolution_w.fixed = False -S.backgrounds[0][0].y.fixed = False -S.backgrounds[0][1].y.fixed = False +S.backgrounds[0][0].y.fixed = True +S.backgrounds[0][1].y.fixed = True -# result = f.fit(data_x, data_y, weights=1/data_e) -result = data_set['I'].easyCore.fit(f) +result = f.fit(data_x, data_y) +# result = data_set['I'].easyCore.fit(f) if result.success: print("The fit has been successful: {}".format(result.success)) print("The gooodness of fit is: {}".format(result.goodness_of_fit)) sim_y_data = interface.fit_func(data_x) + +import matplotlib.pyplot as plt + +plt.plot(data_x, data_y, label='Data') +plt.plot(data_x, result.y_calc, label='Calculate') +plt.show() From 456375bcc7ab3462dd856c616751e37c88ef5fbc Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Mon, 9 Aug 2021 16:19:03 +0200 Subject: [PATCH 08/18] TOF support As easy as: ``` from easyCore import np from easyDiffractionLib import Phase from easyDiffractionLib.Runner import Runner import matplotlib.pyplot as plt r = Runner() p = Phase.from_cif_file('tests/PbSO4.cif') r.add_job('testing', job_type='powder1dTOF') r.add_phase(p, 'testing') job = r.jobs['testing'] job.pattern.zero_shift = 2.9121 job.create_simulation(np.linspace(3000, 11000, 801)) job.plot_simulation() plt.show() ``` --- easyDiffractionLib/Calculators/cryspy.py | 132 ++++++++++-- easyDiffractionLib/Interfaces/cryspy.py | 36 +++- easyDiffractionLib/Jobs.py | 42 +++- easyDiffractionLib/Profiles/P1D.py | 261 ++++++++++++++++++++--- easyDiffractionLib/Runner.py | 7 +- 5 files changed, 418 insertions(+), 60 deletions(-) diff --git a/easyDiffractionLib/Calculators/cryspy.py b/easyDiffractionLib/Calculators/cryspy.py index 34ac06b5..9decad32 100644 --- a/easyDiffractionLib/Calculators/cryspy.py +++ b/easyDiffractionLib/Calculators/cryspy.py @@ -1,10 +1,10 @@ __author__ = "github.com/wardsimon" __version__ = "0.0.2" - import cryspy import warnings from easyCore import np, borg + warnings.filterwarnings('ignore') @@ -22,12 +22,28 @@ def __init__(self): } } + self.conditions_TOF = { + 'ttheta_bank': 0, + 'dtt1': 0.1, + 'dtt2': 0, + 'resolution': { + 'sigma0': 0, + 'sigma1': 0, + 'sigma2': 0, + 'gamma0': 0, + 'gamma1': 0, + 'gamma2': 0, + 'alpha0': 0, + 'alpha1': 0, + 'beta0': 0, + 'beta1': 0} + } self.background = None self.hkl_dict = { 'ttheta': np.empty(0), - 'h': np.empty(0), - 'k': np.empty(0), - 'l': np.empty(0) + 'h': np.empty(0), + 'k': np.empty(0), + 'l': np.empty(0) } self.storage = {} self.current_crystal = {} @@ -46,12 +62,13 @@ def cif_str(self, value): def createModel(self, model_id, model_type=''): model = { 'background': cryspy.PdBackgroundL(), - 'phase': cryspy.PhaseL() + 'phase': cryspy.PhaseL() } cls = cryspy.Pd - if model_type == 'tof': - cls = cryspy.Tof + if model_type == 'Powder1DTOF': + cls = cryspy.TOF model['background'] = cryspy.TOFBackground() + self.type = 'powder1DTOF' self.model = cls(**model) def createPhase(self, crystal_name, key='phase'): @@ -104,7 +121,7 @@ def createSpaceGroup(self, key='spacegroup', name_hm_alt='P 1'): # sg = cryspy.SpaceGroup(**opts) # except Exception as e: sg = cryspy.SpaceGroup(**opts) - # print(e) + # print(e) self.storage[key] = sg return key @@ -166,8 +183,18 @@ def createBackground(self, background_obj): self.storage[key] = background_obj return key - def createSetup(self, key='setup'): - setup = cryspy.Setup(wavelength=self.conditions['wavelength'], offset_ttheta=0) + def createSetup(self, key='setup', cls_type = None): + + if cls_type is None: + cls_type = self.type + + if cls_type == 'powder1D': + setup = cryspy.Setup(wavelength=self.conditions['wavelength'], offset_ttheta=0) + elif cls_type == 'powder1DTOF': + setup = cryspy.TOFParameters(zero=0, dtt1=self.conditions_TOF['dtt1'], dtt2=self.conditions_TOF['dtt2'], + ttheta_bank=self.conditions_TOF['ttheta_bank']) + else: + raise AttributeError('The experiment is of an unknown type') self.storage[key] = setup if self.model is not None: setattr(self.model, 'setup', setup) @@ -183,12 +210,23 @@ def genericReturn(self, item_key, value_key): value = getattr(item, value_key) return value - def createResolution(self): - key = 'pd_instr_resolution' - resolution = cryspy.PdInstrResolution(**self.conditions['resolution']) + def createResolution(self, cls_type = None): + + if cls_type is None: + cls_type = self.type + + if cls_type == 'powder1D': + key = 'pd_instr_resolution' + resolution = cryspy.PdInstrResolution(**self.conditions['resolution']) + elif cls_type == 'powder1DTOF': + key = 'tof_profile' + resolution = cryspy.TOFProfile(**self.conditions_TOF['resolution']) + resolution.peak_shape = 'Gauss' + else: + raise AttributeError('The experiment is of an unknown type') self.storage[key] = resolution if self.model is not None: - setattr(self.model, 'pd_instr_resolution', resolution) + setattr(self.model, key, resolution) return key def updateResolution(self, key, **kwargs): @@ -234,15 +272,65 @@ def powder_1d_calculate(self, x_array: np.ndarray) -> np.ndarray: profile = self.model.calc_profile(this_x_array, [crystal], True, False) self.hkl_dict = { 'ttheta': self.model.d_internal_val['peak_' + crystal.data_name].numpy_ttheta, - 'h': self.model.d_internal_val['peak_'+crystal.data_name].numpy_index_h, - 'k': self.model.d_internal_val['peak_'+crystal.data_name].numpy_index_k, - 'l': self.model.d_internal_val['peak_'+crystal.data_name].numpy_index_l, + 'h': self.model.d_internal_val['peak_' + crystal.data_name].numpy_index_h, + 'k': self.model.d_internal_val['peak_' + crystal.data_name].numpy_index_k, + 'l': self.model.d_internal_val['peak_' + crystal.data_name].numpy_index_l, } res = scale * np.array(profile.intensity_total) + bg if borg.debug: print(f"y_calc: {res}") return res + def powder_1d_tof_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 + setup, tof_profile, phase, tof_background, tof_meas + """ + + for key_inner in ['tof_profile', 'setup']: + if not hasattr(self.model, key_inner): + setattr(self.model, key_inner, self.storage[key_inner]) + + if self.pattern is None: + scale = 1.0 + offset = 0 + else: + scale = self.pattern.scale.raw_value / 500.0 + offset = self.pattern.zero_shift.raw_value + + self.model['tof_parameters'].zero = offset + this_x_array = x_array + + if borg.debug: + print('CALLING FROM Cryspy\n----------------------') + # USe the default for now + crystal = self.storage[list(self.current_crystal.keys())[-1]] + + if len(self.pattern.backgrounds) == 0: + bg = np.zeros_like(this_x_array) + else: + bg = self.pattern.backgrounds[0].calculate(this_x_array) + + if crystal is None: + return bg + + profile = self.model.calc_profile(this_x_array, [crystal], True, False) + self.hkl_dict = { + 'time': self.model.d_internal_val['peak_' + crystal.data_name].time, + 'h': self.model.d_internal_val['peak_' + crystal.data_name].index_h, + 'k': self.model.d_internal_val['peak_' + crystal.data_name].index_k, + 'l': self.model.d_internal_val['peak_' + crystal.data_name].index_l, + } + res = scale * np.array(profile.intensity_total) + bg + if borg.debug: + print(f"y_calc: {res}") + return res + + def calculate(self, x_array: np.ndarray) -> np.ndarray: """ For a given x calculate the corresponding y @@ -254,6 +342,8 @@ def calculate(self, x_array: np.ndarray) -> np.ndarray: res = np.zeros_like(x_array) if self.type == 'powder1D': return self.powder_1d_calculate(x_array) + if self.type == 'powder1DTOF': + return self.powder_1d_tof_calculate(x_array) return res def get_hkl(self, tth: np.array = None) -> dict: @@ -274,9 +364,9 @@ def get_hkl(self, tth: np.array = None) -> dict: hkl_dict = { 'ttheta': self.model.d_internal_val['peak_' + crystal.data_name].numpy_ttheta, - 'h': self.model.d_internal_val['peak_' + crystal.data_name].numpy_index_h, - 'k': self.model.d_internal_val['peak_' + crystal.data_name].numpy_index_k, - 'l': self.model.d_internal_val['peak_' + crystal.data_name].numpy_index_l, + 'h': self.model.d_internal_val['peak_' + crystal.data_name].numpy_index_h, + 'k': self.model.d_internal_val['peak_' + crystal.data_name].numpy_index_k, + 'l': self.model.d_internal_val['peak_' + crystal.data_name].numpy_index_l, } - return hkl_dict \ No newline at end of file + return hkl_dict diff --git a/easyDiffractionLib/Interfaces/cryspy.py b/easyDiffractionLib/Interfaces/cryspy.py index 0e9f6dab..748c8207 100644 --- a/easyDiffractionLib/Interfaces/cryspy.py +++ b/easyDiffractionLib/Interfaces/cryspy.py @@ -5,7 +5,7 @@ from easyDiffractionLib.Interfaces.interfaceTemplate import InterfaceTemplate from easyCore.Objects.Inferface import ItemContainer from easyDiffractionLib.Calculators.cryspy import Cryspy as Cryspy_calc -from easyDiffractionLib.Profiles.P1D import Instrument1DParameters, Powder1DParameters +from easyDiffractionLib.Profiles.P1D import Instrument1DCWParameters, Instrument1DTOFParameters, Powder1DParameters from easyDiffractionLib.Elements.Experiments.Pattern import Pattern1D from easyDiffractionLib import Lattice, SpaceGroup, Site, Phase, Phases @@ -49,6 +49,8 @@ class Cryspy(InterfaceTemplate): 'wavelength': 'wavelength' } + _instrument_tof_link = {k:k for k in Instrument1DTOFParameters._defaults.keys()} + name = 'CrysPy' def __init__(self): @@ -58,7 +60,7 @@ def create(self, model): r_list = [] t_ = type(model) model_key = self.__identify(model) - if issubclass(t_, Instrument1DParameters): + if issubclass(t_, Instrument1DCWParameters): # These parameters are linked to the Resolution and Setup cryspy objects res_key = self.calculator.createResolution() setup_key = self.calculator.createSetup() @@ -74,6 +76,31 @@ def create(self, model): self.calculator.genericReturn, self.calculator.genericUpdate) ) + if issubclass(t_, Instrument1DTOFParameters): + # These parameters are linked to the Resolution and Setup cryspy objects + res_key = self.calculator.createResolution(cls_type='powder1DTOF') + setup_key = self.calculator.createSetup(cls_type='powder1DTOF') + keys = self._instrument_tof_link.copy() + + setup_keys = { + k: keys[k] for k in ['ttheta_bank', 'dtt1', 'dtt2'] + } + res_keys = { + k: keys[k] for k in ['sigma0', 'sigma1', 'sigma2', + 'gamma0', 'gamma1', 'gamma2', + 'alpha0', 'alpha1', + 'beta0', 'beta1'] + } + r_list.append( + ItemContainer(res_key, res_keys, + self.calculator.genericReturn, + self.calculator.genericUpdate) + ) + r_list.append( + ItemContainer(setup_key, setup_keys, + self.calculator.genericReturn, + self.calculator.genericUpdate) + ) elif issubclass(t_, Powder1DParameters): # These parameters do not link directly to cryspy objects. self.calculator.pattern = model @@ -112,9 +139,12 @@ def create(self, model): for phase in model: ident = str(self.__identify(phase)) + '_phase' self.calculator.assignPhase(model_key, ident) - elif t_.__name__ == 'Powder1D': + elif t_.__name__ == 'Powder1DCW': # #TODO Check to see if parameters and pattern should be initialized here. self.calculator.createModel(model_key, 'powder1D') + elif t_.__name__ == 'Powder1DTOF': + # #TODO Check to see if parameters and pattern should be initialized here. + self.calculator.createModel(model_key, 'Powder1DTOF') else: if self._borg.debug: print(f"I'm a: {type(model)}") diff --git a/easyDiffractionLib/Jobs.py b/easyDiffractionLib/Jobs.py index 3f0d1f08..620494e1 100644 --- a/easyDiffractionLib/Jobs.py +++ b/easyDiffractionLib/Jobs.py @@ -7,17 +7,25 @@ from easyCore.Fitting.Fitting import Fitter -class Powder1D(_PowderBase): - def __init__(self, name: str, datastore: xr.Dataset, phases=None, parameters=None, pattern=None): - from easyDiffractionLib.Profiles.P1D import Unpolarized1DClasses +class JobBase_1D(_PowderBase): + + def __init__(self, name: str, profileClass, datastore: xr.Dataset, phases=None, parameters=None, pattern=None): interface = InterfaceFactory() - super(Powder1D, self).__init__(name, Unpolarized1DClasses, datastore, phases, parameters, pattern, interface=interface) - self._x_axis_name = 'tth' + super(JobBase_1D, self).__init__(name, profileClass, datastore, phases, parameters, pattern, interface=interface) + self._x_axis_name = '' self._y_axis_prefix = 'Intensity_' + @property + def simulation_data(self): + sim_name = self.datastore._simulations._simulation_prefix + self.name + data = None + if sim_name in self.datastore.store.keys(): + data = self.datastore.store[sim_name] + return data + def create_simulation(self, tth, simulation_name=None): if not isinstance(tth, xr.DataArray): - coord_name = self.datastore._simulations._simulation_prefix + self.name + '_tth' + coord_name = self.datastore._simulations._simulation_prefix + self.name + '_' + self._x_axis_name self.datastore.add_coordinate(coord_name, tth) self.datastore.store[coord_name].name = self._x_axis_name else: @@ -40,7 +48,7 @@ def plot_simulation(self, simulation_name=None): def add_experiment(self, experiment_name, file_path): data_x, data_y, data_e = np.loadtxt(file_path, unpack=True) - coord_name = self.name + '_' + experiment_name + '_tth' + coord_name = self.name + '_' + experiment_name + '_' + self._x_axis_name self.datastore.store.easyCore.add_coordinate(coord_name, data_x) self.datastore.store.easyCore.add_variable(self.name + '_' + experiment_name + '_I', [coord_name], data_y) @@ -48,7 +56,7 @@ def add_experiment(self, experiment_name, file_path): # self._experiments[] def simulate_experiment(self, experiment_name=None): - tth_name = self.name + '_' + experiment_name + '_tth' + tth_name = self.name + '_' + experiment_name + '_' + self._x_axis_name tth = self.datastore.store[tth_name] return self.create_simulation(tth, simulation_name=self.name + '_' + experiment_name) @@ -60,4 +68,20 @@ def fit_experiment(self, experiment_name, fitter=None): dataarray_name = self.name + '_' + experiment_name + '_I' if fitter is None: fitter = Fitter(self, self.interface.fit_func) - return self.datastore.store[dataarray_name].easyCore.fit(fitter) \ No newline at end of file + return self.datastore.store[dataarray_name].easyCore.fit(fitter) + + +class Powder1DCW(JobBase_1D): + + def __init__(self, name: str, datastore: xr.Dataset, phases=None, parameters=None, pattern=None): + from easyDiffractionLib.Profiles.P1D import Unpolarized1DClasses + super(Powder1DCW, self).__init__(name, Unpolarized1DClasses, datastore, phases, parameters, pattern) + self._x_axis_name = 'tth' + + +class Powder1DTOF(JobBase_1D): + + def __init__(self, name: str, datastore: xr.Dataset, phases=None, parameters=None, pattern=None): + from easyDiffractionLib.Profiles.P1D import Unpolarized1DTOFClasses + super(Powder1DTOF, self).__init__(name, Unpolarized1DTOFClasses, datastore, phases, parameters, pattern) + self._x_axis_name = 'time' diff --git a/easyDiffractionLib/Profiles/P1D.py b/easyDiffractionLib/Profiles/P1D.py index d1b98b73..7c00b5f7 100644 --- a/easyDiffractionLib/Profiles/P1D.py +++ b/easyDiffractionLib/Profiles/P1D.py @@ -69,28 +69,28 @@ def __init__(self, dataset, simulation_prefix): class Powder1DParameters(BaseObj): _name = '1DPowderProfile' _defaults = { - 'zero_shift': { - '@module': 'easyCore.Objects.Base', - '@class': 'Parameter', + 'zero_shift': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', '@version': '0.0.1', - 'name': 'zero_shift', - 'units': 'degree', - 'value': 0.0, - 'fixed': True + 'name': 'zero_shift', + 'units': 'degree', + 'value': 0.0, + 'fixed': True }, - 'scale': { + 'scale': { '@module': 'easyCore.Objects.Base', '@class': 'Parameter', '@version': '0.0.1', 'name': 'scale', 'value': 1, - 'fixed': True + 'fixed': True }, 'backgrounds': { - '@module': 'easyDiffractionLib.Elements.Backgrounds.Background', - '@class': 'BackgroundContainer', + '@module': 'easyDiffractionLib.Elements.Backgrounds.Background', + '@class': 'BackgroundContainer', '@version': '0.0.1', - 'data': [], + 'data': [], } } @@ -131,7 +131,7 @@ class PolPowder1DParameters(Powder1DParameters): pass -class Instrument1DParameters(BaseObj): +class Instrument1DCWParameters(BaseObj): _name = 'InstrumentalParameters' _defaults = { 'wavelength': { @@ -141,7 +141,7 @@ class Instrument1DParameters(BaseObj): 'name': 'wavelength', 'units': 'angstrom', 'value': 1.54056, - 'fixed': True + 'fixed': True }, 'resolution_u': { '@module': 'easyCore.Objects.Base', @@ -149,7 +149,7 @@ class Instrument1DParameters(BaseObj): '@version': '0.0.1', 'name': 'resolution_u', 'value': 0.0002, - 'fixed': True + 'fixed': True }, 'resolution_v': { '@module': 'easyCore.Objects.Base', @@ -157,7 +157,7 @@ class Instrument1DParameters(BaseObj): '@version': '0.0.1', 'name': 'resolution_v', 'value': -0.0002, - 'fixed': True + 'fixed': True }, 'resolution_w': { @@ -166,7 +166,7 @@ class Instrument1DParameters(BaseObj): '@version': '0.0.1', 'name': 'resolution_w', 'value': 0.012, - 'fixed': True + 'fixed': True }, 'resolution_x': { @@ -175,15 +175,15 @@ class Instrument1DParameters(BaseObj): '@version': '0.0.1', 'name': 'resolution_x', 'value': 0.0, - 'fixed': True + 'fixed': True }, 'resolution_y': { - '@module': 'easyCore.Objects.Base', - '@class': 'Parameter', + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', '@version': '0.0.1', - 'name': 'resolution_y', - 'value': 0.0, - 'fixed': True + 'name': 'resolution_y', + 'value': 0.0, + 'fixed': True } } @@ -239,10 +239,221 @@ def default(cls): resolution_x=resolution_x, resolution_y=resolution_y) -class Instrument1DPolParameters(Instrument1DParameters): +class Instrument1DTOFParameters(BaseObj): + _name = 'InstrumentalParameters' + _defaults = { + 'ttheta_bank': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'ttheta_bank', + 'units': 'deg', + 'value': 145.00, + 'fixed': True + }, + 'dtt1': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'dtt1', + 'units': 'deg', + 'value': 6167.24700, + 'fixed': True + }, + 'dtt2': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'dtt2', + 'units': 'deg', + 'value': -2.28000, + 'fixed': True + }, + 'sigma0': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'sigma0', + 'value': 0.409, + 'fixed': True + }, + 'sigma1': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'sigma1', + 'value': 8.118, + 'fixed': True + + }, + 'sigma2': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'sigma2', + 'value': 0.0, + 'fixed': True + + }, + 'gamma0': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'gamma0', + 'value': 0.0, + 'fixed': True + }, + 'gamma1': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'gamma1', + 'value': 0.60400, + 'fixed': True + }, + 'gamma2': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'gamma2', + 'value': 0.0, + 'fixed': True + }, + 'alpha0': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'alpha0', + 'value': 0.0, + 'fixed': True + }, + 'alpha1': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'alpha1', + 'value': 0.29710, + 'fixed': True + }, + 'beta0': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'beta0', + 'value': 0.04182, + 'fixed': True + }, + 'beta1': { + '@module': 'easyCore.Objects.Base', + '@class': 'Parameter', + '@version': '0.0.1', + 'name': 'beta1', + 'value': 0.00224, + 'fixed': True + } + } + + def __init__(self, + ttheta_bank: Parameter, + dtt1: Parameter, dtt2: Parameter, + sigma0: Parameter, sigma1: Parameter, sigma2: Parameter, + gamma0: Parameter, gamma1: Parameter, gamma2: Parameter, + alpha0: Parameter, alpha1: Parameter, + beta0: Parameter, beta1: Parameter, + interface=None): + super().__init__(self.__class__.__name__, + ttheta_bank=ttheta_bank, + dtt1=dtt1, dtt2=dtt2, + sigma0=sigma0, sigma1=sigma1, sigma2=sigma2, + gamma0=gamma0, gamma1=gamma1, gamma2=gamma2, + alpha0=alpha0, alpha1=alpha1, + beta0=beta0, beta1=beta1) + self.name = self._name + self.interface = interface + + @classmethod + def from_pars(cls, + ttheta_bank: float = _defaults['ttheta_bank']['value'], + dtt1: float = _defaults['dtt1']['value'], + dtt2: float = _defaults['dtt2']['value'], + sigma0: float = _defaults['sigma0']['value'], + sigma1: float = _defaults['sigma1']['value'], + sigma2: float = _defaults['sigma2']['value'], + gamma0: float = _defaults['gamma0']['value'], + gamma1: float = _defaults['gamma1']['value'], + gamma2: float = _defaults['gamma2']['value'], + alpha0: float = _defaults['alpha0']['value'], + alpha1: float = _defaults['alpha1']['value'], + beta0: float = _defaults['beta0']['value'], + beta1: float = _defaults['beta1']['value'], + ): + defaults = deepcopy(cls._defaults) + defaults['ttheta_bank']['value'] = ttheta_bank + ttheta_bank = _decoder.process_decoded(defaults['ttheta_bank']) + defaults['dtt1']['value'] = dtt1 + dtt1 = _decoder.process_decoded(defaults['dtt1']) + defaults['dtt2']['value'] = dtt2 + dtt2 = _decoder.process_decoded(defaults['dtt2']) + defaults['sigma0']['value'] = sigma0 + sigma0 = _decoder.process_decoded(defaults['sigma0']) + defaults['sigma1']['value'] = sigma1 + sigma1 = _decoder.process_decoded(defaults['sigma1']) + defaults['sigma2']['value'] = sigma2 + sigma2 = _decoder.process_decoded(defaults['sigma2']) + defaults['gamma0']['value'] = gamma0 + gamma0 = _decoder.process_decoded(defaults['gamma0']) + defaults['gamma1']['value'] = gamma1 + gamma1 = _decoder.process_decoded(defaults['gamma1']) + defaults['gamma2']['value'] = gamma2 + gamma2 = _decoder.process_decoded(defaults['gamma2']) + defaults['alpha0']['value'] = alpha0 + alpha0 = _decoder.process_decoded(defaults['alpha0']) + defaults['alpha1']['value'] = alpha1 + alpha1 = _decoder.process_decoded(defaults['alpha1']) + defaults['beta0']['value'] = beta0 + beta0 = _decoder.process_decoded(defaults['beta0']) + defaults['beta1']['value'] = beta1 + beta1 = _decoder.process_decoded(defaults['beta1']) + + return cls(ttheta_bank=ttheta_bank, + dtt1=dtt1, dtt2=dtt2, + sigma0=sigma0, sigma1=sigma1, sigma2=sigma2, + gamma0=gamma0, gamma1=gamma1, gamma2=gamma2, + alpha0=alpha0, alpha1=alpha1, + beta0=beta0, beta1=beta1) + + @classmethod + def default(cls): + defaults = deepcopy(cls._defaults) + ttheta_bank = _decoder.process_decoded(defaults['ttheta_bank']) + dtt1 = _decoder.process_decoded(defaults['dtt1']) + dtt2 = _decoder.process_decoded(defaults['dtt2']) + sigma0 = _decoder.process_decoded(defaults['sigma0']) + sigma1 = _decoder.process_decoded(defaults['sigma1']) + sigma2 = _decoder.process_decoded(defaults['sigma2']) + gamma0 = _decoder.process_decoded(defaults['gamma0']) + gamma1 = _decoder.process_decoded(defaults['gamma1']) + gamma2 = _decoder.process_decoded(defaults['gamma2']) + alpha0 = _decoder.process_decoded(defaults['alpha0']) + alpha1 = _decoder.process_decoded(defaults['alpha1']) + beta0 = _decoder.process_decoded(defaults['beta0']) + beta1 = _decoder.process_decoded(defaults['beta1']) + return cls(ttheta_bank=ttheta_bank, + dtt1=dtt1, dtt2=dtt2, + sigma0=sigma0, sigma1=sigma1, sigma2=sigma2, + gamma0=gamma0, gamma1=gamma1, gamma2=gamma2, + alpha0=alpha0, alpha1=alpha1, + beta0=beta0, beta1=beta1) + + +class Instrument1DCWPolParameters(Instrument1DCWParameters): pass Unpolarized1DClasses = JobSetup([Powder1DSim, Powder1DExp], Powder1DParameters, - Instrument1DParameters) + Instrument1DCWParameters) + +Unpolarized1DTOFClasses = JobSetup([Powder1DSim, Powder1DExp], + Powder1DParameters, + Instrument1DTOFParameters) \ No newline at end of file diff --git a/easyDiffractionLib/Runner.py b/easyDiffractionLib/Runner.py index 7415936e..a7977dfd 100644 --- a/easyDiffractionLib/Runner.py +++ b/easyDiffractionLib/Runner.py @@ -17,8 +17,11 @@ def __init__(self): def add_job(self, name: str, job_type: str = 'powder1d'): if job_type == 'powder1d': - from easyDiffractionLib.Jobs import Powder1D - job_type = Powder1D + from easyDiffractionLib.Jobs import Powder1DCW + job_type = Powder1DCW + elif job_type == 'powder1dTOF': + from easyDiffractionLib.Jobs import Powder1DTOF + job_type = Powder1DTOF else: raise NotImplementedError job = job_type(name, self._data) From 073b41356aea0f8ad3b851a0dbd3ca4bcb3d309d Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Tue, 10 Aug 2021 13:49:29 +0200 Subject: [PATCH 09/18] Decouple phases and EXP in cryspy --- easyDiffractionLib/Calculators/cryspy.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/easyDiffractionLib/Calculators/cryspy.py b/easyDiffractionLib/Calculators/cryspy.py index 9decad32..1c39fdf4 100644 --- a/easyDiffractionLib/Calculators/cryspy.py +++ b/easyDiffractionLib/Calculators/cryspy.py @@ -48,6 +48,7 @@ def __init__(self): self.storage = {} self.current_crystal = {} self.model = None + self.phases = cryspy.PhaseL() self.type = 'powder1D' @property @@ -62,7 +63,7 @@ def cif_str(self, value): def createModel(self, model_id, model_type=''): model = { 'background': cryspy.PdBackgroundL(), - 'phase': cryspy.PhaseL() + 'phase': self.phases } cls = cryspy.Pd if model_type == 'Powder1DTOF': @@ -78,7 +79,7 @@ def createPhase(self, crystal_name, key='phase'): def assignPhase(self, model_name, phase_name): phase = self.storage[phase_name] - self.model.phase.items.append(phase) + self.phases.items.append(phase) def removePhase(self, model_name, phase_name): phase = self.storage[phase_name] From 1f2b9cb7f052caac597fbd236f1b0d5f512a7838 Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Tue, 10 Aug 2021 14:13:19 +0200 Subject: [PATCH 10/18] Fix legacy mode loading --- easyDiffractionLib/Interfaces/cryspy.py | 7 +++++++ easyDiffractionLib/sample.py | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/easyDiffractionLib/Interfaces/cryspy.py b/easyDiffractionLib/Interfaces/cryspy.py index 748c8207..287d4f8c 100644 --- a/easyDiffractionLib/Interfaces/cryspy.py +++ b/easyDiffractionLib/Interfaces/cryspy.py @@ -145,6 +145,13 @@ def create(self, model): elif t_.__name__ == 'Powder1DTOF': # #TODO Check to see if parameters and pattern should be initialized here. self.calculator.createModel(model_key, 'Powder1DTOF') + elif t_.__name__ == 'Sample': # This is legacy mode. Boo + if issubclass(t_.pattern, Instrument1DCWParameters): + self.calculator.createModel(model_key, 'powder1D') + elif issubclass(t_.pattern, Instrument1DTOFParameters): + self.calculator.createModel(model_key, 'Powder1DTOF') + else: + raise AttributeError('Unknown EXP type') else: if self._borg.debug: print(f"I'm a: {type(model)}") diff --git a/easyDiffractionLib/sample.py b/easyDiffractionLib/sample.py index 94503406..db319564 100644 --- a/easyDiffractionLib/sample.py +++ b/easyDiffractionLib/sample.py @@ -8,8 +8,8 @@ from easyCore.Utils.UndoRedo import property_stack_deco from easyDiffractionLib import Phase, Phases -from easyDiffractionLib.Elements.Experiments.Experiment import Pars1D -from easyDiffractionLib.Elements.Experiments.Pattern import Pattern1D +from easyDiffractionLib.Profiles.P1D import Instrument1DCWParameters as Pars1D +from easyDiffractionLib.Profiles.P1D import Powder1DParameters as Pattern1D class Sample(BaseObj): From 236d27de02717f94db434de32e9e419b6863757d Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Tue, 10 Aug 2021 14:28:28 +0200 Subject: [PATCH 11/18] Use the correct references for legacy mode --- easyDiffractionLib/Interfaces/cryspy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/easyDiffractionLib/Interfaces/cryspy.py b/easyDiffractionLib/Interfaces/cryspy.py index 287d4f8c..aff190ea 100644 --- a/easyDiffractionLib/Interfaces/cryspy.py +++ b/easyDiffractionLib/Interfaces/cryspy.py @@ -146,9 +146,9 @@ def create(self, model): # #TODO Check to see if parameters and pattern should be initialized here. self.calculator.createModel(model_key, 'Powder1DTOF') elif t_.__name__ == 'Sample': # This is legacy mode. Boo - if issubclass(t_.pattern, Instrument1DCWParameters): + if issubclass(type(model.parameters), Instrument1DCWParameters): self.calculator.createModel(model_key, 'powder1D') - elif issubclass(t_.pattern, Instrument1DTOFParameters): + elif issubclass(type(model.parameters), Instrument1DTOFParameters): self.calculator.createModel(model_key, 'Powder1DTOF') else: raise AttributeError('Unknown EXP type') From 695473b189d367a05879066127e3b3d275d27908 Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Wed, 11 Aug 2021 10:07:23 +0200 Subject: [PATCH 12/18] Unification of naming conventions --- easyDiffractionLib/Calculators/cryspy.py | 20 ++++++++++---------- easyDiffractionLib/Interfaces/cryspy.py | 18 ++++++++++-------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/easyDiffractionLib/Calculators/cryspy.py b/easyDiffractionLib/Calculators/cryspy.py index 1c39fdf4..c606e208 100644 --- a/easyDiffractionLib/Calculators/cryspy.py +++ b/easyDiffractionLib/Calculators/cryspy.py @@ -49,7 +49,7 @@ def __init__(self): self.current_crystal = {} self.model = None self.phases = cryspy.PhaseL() - self.type = 'powder1D' + self.type = 'powder1DCW' @property def cif_str(self): @@ -60,13 +60,13 @@ def cif_str(self): def cif_str(self, value): self.createCrystal_fromCifStr(value) - def createModel(self, model_id, model_type=''): + def createModel(self, model_id, model_type='powder1DCW'): model = { 'background': cryspy.PdBackgroundL(), 'phase': self.phases } cls = cryspy.Pd - if model_type == 'Powder1DTOF': + if model_type == 'powder1DTOF': cls = cryspy.TOF model['background'] = cryspy.TOFBackground() self.type = 'powder1DTOF' @@ -189,7 +189,7 @@ def createSetup(self, key='setup', cls_type = None): if cls_type is None: cls_type = self.type - if cls_type == 'powder1D': + if cls_type == 'powder1DCW': setup = cryspy.Setup(wavelength=self.conditions['wavelength'], offset_ttheta=0) elif cls_type == 'powder1DTOF': setup = cryspy.TOFParameters(zero=0, dtt1=self.conditions_TOF['dtt1'], dtt2=self.conditions_TOF['dtt2'], @@ -216,7 +216,7 @@ def createResolution(self, cls_type = None): if cls_type is None: cls_type = self.type - if cls_type == 'powder1D': + if cls_type == 'powder1DCW': key = 'pd_instr_resolution' resolution = cryspy.PdInstrResolution(**self.conditions['resolution']) elif cls_type == 'powder1DTOF': @@ -321,10 +321,10 @@ def powder_1d_tof_calculate(self, x_array: np.ndarray) -> np.ndarray: profile = self.model.calc_profile(this_x_array, [crystal], True, False) self.hkl_dict = { - 'time': self.model.d_internal_val['peak_' + crystal.data_name].time, - 'h': self.model.d_internal_val['peak_' + crystal.data_name].index_h, - 'k': self.model.d_internal_val['peak_' + crystal.data_name].index_k, - 'l': self.model.d_internal_val['peak_' + crystal.data_name].index_l, + 'time': np.array(self.model.d_internal_val['peak_' + crystal.data_name].time), + 'h': np.array(self.model.d_internal_val['peak_' + crystal.data_name].index_h), + 'k': np.array(self.model.d_internal_val['peak_' + crystal.data_name].index_k), + 'l': np.array(self.model.d_internal_val['peak_' + crystal.data_name].index_l), } res = scale * np.array(profile.intensity_total) + bg if borg.debug: @@ -341,7 +341,7 @@ def calculate(self, x_array: np.ndarray) -> np.ndarray: :rtype: np.ndarray """ res = np.zeros_like(x_array) - if self.type == 'powder1D': + if self.type == 'powder1DCW': return self.powder_1d_calculate(x_array) if self.type == 'powder1DTOF': return self.powder_1d_tof_calculate(x_array) diff --git a/easyDiffractionLib/Interfaces/cryspy.py b/easyDiffractionLib/Interfaces/cryspy.py index aff190ea..02585fe7 100644 --- a/easyDiffractionLib/Interfaces/cryspy.py +++ b/easyDiffractionLib/Interfaces/cryspy.py @@ -61,6 +61,7 @@ def create(self, model): t_ = type(model) model_key = self.__identify(model) if issubclass(t_, Instrument1DCWParameters): + self.calculator.createModel(model_key, 'powder1DCW') # These parameters are linked to the Resolution and Setup cryspy objects res_key = self.calculator.createResolution() setup_key = self.calculator.createSetup() @@ -77,6 +78,7 @@ def create(self, model): self.calculator.genericUpdate) ) if issubclass(t_, Instrument1DTOFParameters): + self.calculator.createModel(model_key, 'powder1DTOF') # These parameters are linked to the Resolution and Setup cryspy objects res_key = self.calculator.createResolution(cls_type='powder1DTOF') setup_key = self.calculator.createSetup(cls_type='powder1DTOF') @@ -139,17 +141,17 @@ def create(self, model): for phase in model: ident = str(self.__identify(phase)) + '_phase' self.calculator.assignPhase(model_key, ident) - elif t_.__name__ == 'Powder1DCW': - # #TODO Check to see if parameters and pattern should be initialized here. - self.calculator.createModel(model_key, 'powder1D') - elif t_.__name__ == 'Powder1DTOF': - # #TODO Check to see if parameters and pattern should be initialized here. - self.calculator.createModel(model_key, 'Powder1DTOF') + # elif t_.__name__ == 'Powder1DCW': + # # #TODO Check to see if parameters and pattern should be initialized here. + # + # elif t_.__name__ == 'Powder1DTOF': + # # #TODO Check to see if parameters and pattern should be initialized here. + # elif t_.__name__ == 'Sample': # This is legacy mode. Boo if issubclass(type(model.parameters), Instrument1DCWParameters): - self.calculator.createModel(model_key, 'powder1D') + self.calculator.createModel(model_key, 'powder1DCW') elif issubclass(type(model.parameters), Instrument1DTOFParameters): - self.calculator.createModel(model_key, 'Powder1DTOF') + self.calculator.createModel(model_key, 'powder1DTOF') else: raise AttributeError('Unknown EXP type') else: From 888ad48df76ca96d35788515b67933aee1d094f7 Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Wed, 11 Aug 2021 12:02:22 +0200 Subject: [PATCH 13/18] Fix TOF generation --- easyDiffractionLib/Calculators/cryspy.py | 11 ++++++++++- easyDiffractionLib/Interfaces/cryspy.py | 12 ++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/easyDiffractionLib/Calculators/cryspy.py b/easyDiffractionLib/Calculators/cryspy.py index c606e208..f44ad526 100644 --- a/easyDiffractionLib/Calculators/cryspy.py +++ b/easyDiffractionLib/Calculators/cryspy.py @@ -294,7 +294,16 @@ def powder_1d_tof_calculate(self, x_array: np.ndarray) -> np.ndarray: for key_inner in ['tof_profile', 'setup']: if not hasattr(self.model, key_inner): - setattr(self.model, key_inner, self.storage[key_inner]) + try: + setattr(self.model, key_inner, self.storage[key_inner]) + except ValueError: + # Try to fix cryspy.... + s = self.storage[key_inner] + cls = s.__class__ + for idx, item in enumerate(self.model.items): + if isinstance(item, cls) and id(item) is not id(s): + self.model.items[idx] = s + break if self.pattern is None: scale = 1.0 diff --git a/easyDiffractionLib/Interfaces/cryspy.py b/easyDiffractionLib/Interfaces/cryspy.py index 02585fe7..a7c8fa69 100644 --- a/easyDiffractionLib/Interfaces/cryspy.py +++ b/easyDiffractionLib/Interfaces/cryspy.py @@ -141,12 +141,12 @@ def create(self, model): for phase in model: ident = str(self.__identify(phase)) + '_phase' self.calculator.assignPhase(model_key, ident) - # elif t_.__name__ == 'Powder1DCW': - # # #TODO Check to see if parameters and pattern should be initialized here. - # - # elif t_.__name__ == 'Powder1DTOF': - # # #TODO Check to see if parameters and pattern should be initialized here. - # + elif t_.__name__ == 'Powder1DCW': + # #TODO Check to see if parameters and pattern should be initialized here. + self.calculator.createModel(model_key, 'powder1DCW') + elif t_.__name__ == 'Powder1DTOF': + # #TODO Check to see if parameters and pattern should be initialized here. + self.calculator.createModel(model_key, 'powder1DTOF') elif t_.__name__ == 'Sample': # This is legacy mode. Boo if issubclass(type(model.parameters), Instrument1DCWParameters): self.calculator.createModel(model_key, 'powder1DCW') From a88eef87d008439d9979111b01cbf49ae3caa0e3 Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Thu, 12 Aug 2021 10:52:18 +0200 Subject: [PATCH 14/18] Fix changing from TOF->CW --- easyDiffractionLib/Calculators/cryspy.py | 2 +- easyDiffractionLib/Interfaces/cryspy.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/easyDiffractionLib/Calculators/cryspy.py b/easyDiffractionLib/Calculators/cryspy.py index f44ad526..a27ca20b 100644 --- a/easyDiffractionLib/Calculators/cryspy.py +++ b/easyDiffractionLib/Calculators/cryspy.py @@ -69,7 +69,7 @@ def createModel(self, model_id, model_type='powder1DCW'): if model_type == 'powder1DTOF': cls = cryspy.TOF model['background'] = cryspy.TOFBackground() - self.type = 'powder1DTOF' + self.type = model_type self.model = cls(**model) def createPhase(self, crystal_name, key='phase'): diff --git a/easyDiffractionLib/Interfaces/cryspy.py b/easyDiffractionLib/Interfaces/cryspy.py index a7c8fa69..d45c8b8a 100644 --- a/easyDiffractionLib/Interfaces/cryspy.py +++ b/easyDiffractionLib/Interfaces/cryspy.py @@ -1,12 +1,12 @@ __author__ = "github.com/wardsimon" __version__ = "0.0.2" + from easyCore import borg, np from easyDiffractionLib.Interfaces.interfaceTemplate import InterfaceTemplate from easyCore.Objects.Inferface import ItemContainer from easyDiffractionLib.Calculators.cryspy import Cryspy as Cryspy_calc from easyDiffractionLib.Profiles.P1D import Instrument1DCWParameters, Instrument1DTOFParameters, Powder1DParameters -from easyDiffractionLib.Elements.Experiments.Pattern import Pattern1D from easyDiffractionLib import Lattice, SpaceGroup, Site, Phase, Phases From a61dd37ab030571c6661a29eb701d487d8022bd4 Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Wed, 18 Aug 2021 11:09:30 +0200 Subject: [PATCH 15/18] Fix all interfaces to use new schema --- easyDiffractionLib/Interfaces/CFML.py | 18 ++- easyDiffractionLib/Interfaces/GSASII.py | 19 ++- easyDiffractionLib/Interfaces/cryspy.py | 15 +- easyDiffractionLib/interface.py | 203 ------------------------ 4 files changed, 32 insertions(+), 223 deletions(-) diff --git a/easyDiffractionLib/Interfaces/CFML.py b/easyDiffractionLib/Interfaces/CFML.py index fd78e8d3..341afca6 100644 --- a/easyDiffractionLib/Interfaces/CFML.py +++ b/easyDiffractionLib/Interfaces/CFML.py @@ -5,8 +5,7 @@ from easyDiffractionLib.Interfaces.interfaceTemplate import InterfaceTemplate from easyCore.Objects.Inferface import ItemContainer from easyDiffractionLib.Calculators.CFML import CFML as CFML_calc -from easyDiffractionLib.Elements.Experiments.Experiment import Pars1D -from easyDiffractionLib.Elements.Experiments.Pattern import Pattern1D +from easyDiffractionLib.Profiles.P1D import Instrument1DCWParameters, Powder1DParameters from easyDiffractionLib.sample import Sample from easyDiffractionLib import Lattice, SpaceGroup, Site, Phases @@ -66,7 +65,7 @@ def create(self, model): r_list = [] t_ = type(model) model_key = self.__identify(model) - if issubclass(t_, Pars1D): + if issubclass(t_, Instrument1DCWParameters): # These parameters are linked to the Resolution and Setup CFML objects. Note that we can set the job type! self.calculator.createConditions(job_type='N') keys = self._instrument_link.copy() @@ -75,7 +74,7 @@ def create(self, model): self.calculator.conditionsReturn, self.calculator.conditionsUpdate) ) - elif issubclass(t_, Pattern1D): + elif issubclass(t_, Powder1DParameters): # These parameters do not link directly to CFML objects. self.calculator.pattern = model elif issubclass(t_, Lattice): @@ -100,9 +99,9 @@ def create(self, model): elif issubclass(t_, Phases): self._phase = model elif issubclass(t_, Sample): - self._filename = model.filename - self.calculator.filename = model.filename - self.dump_cif() + self.__createModel(model) + elif t_.__name__ in ['Powder1DCW', 'powder1DCW', 'Npowder1DCW']: + self.__createModel(model) else: if self._borg.debug: print(f"I'm a: {type(model)}") @@ -139,6 +138,11 @@ def dump_cif(self, *args, **kwargs): with open(self._filename, 'w') as fid: fid.write(str(self._phase.cif)) + def __createModel(self, model): + self._filename = model.filename + self.calculator.filename = model.filename + self.dump_cif() + def get_value(self, key, item_key): item = borg.map.get_item_by_key(key) return getattr(item, item_key).raw_value diff --git a/easyDiffractionLib/Interfaces/GSASII.py b/easyDiffractionLib/Interfaces/GSASII.py index 610dedec..15574b3a 100644 --- a/easyDiffractionLib/Interfaces/GSASII.py +++ b/easyDiffractionLib/Interfaces/GSASII.py @@ -5,8 +5,7 @@ from ..Interfaces.interfaceTemplate import InterfaceTemplate from easyCore.Objects.Inferface import ItemContainer from ..Calculators.GSASII import GSASII as GSAS_calc -from easyDiffractionLib.Elements.Experiments.Experiment import Pars1D -from easyDiffractionLib.Elements.Experiments.Pattern import Pattern1D +from easyDiffractionLib.Profiles.P1D import Instrument1DCWParameters, Powder1DParameters from easyDiffractionLib.sample import Sample from easyDiffractionLib import Lattice, SpaceGroup, Site, Phases @@ -66,7 +65,7 @@ def create(self, model): r_list = [] t_ = type(model) model_key = self.__identify(model) - if issubclass(t_, Pars1D): + if issubclass(t_, Instrument1DCWParameters): # These parameters are linked to the Resolution and Setup CFML objects. Note that we can set the job type! self.calculator.createConditions(job_type='N') keys = self._instrument_link.copy() @@ -75,7 +74,7 @@ def create(self, model): self.calculator.conditionsReturn, self.calculator.conditionsUpdate) ) - elif issubclass(t_, Pattern1D): + elif issubclass(t_, Powder1DParameters): # These parameters do not link directly to CFML objects. self.calculator.pattern = model elif issubclass(t_, Lattice): @@ -99,10 +98,11 @@ def create(self, model): self.dump_cif)) elif issubclass(t_, Phases): self._phase = model + elif t_.__name__ in ['Powder1DCW', 'powder1DCW', 'Npowder1DCW']: + # #TODO Check to see if parameters and pattern should be initialized here. + self.__createModel(model_key, 'powder1DCW') elif issubclass(t_, Sample): - self._filename = model.filename - self.calculator.filename = model.filename - self.dump_cif() + self.__createModel(model) else: if self._borg.debug: print(f"I'm a: {type(model)}") @@ -143,6 +143,11 @@ def get_value(self, key, item_key): item = borg.map.get_item_by_key(key) return getattr(item, item_key).raw_value + def __createModel(self, model): + self._filename = model.filename + self.calculator.filename = model.filename + self.dump_cif() + @staticmethod def __identify(obj): return borg.map.convert_id_to_key(obj) diff --git a/easyDiffractionLib/Interfaces/cryspy.py b/easyDiffractionLib/Interfaces/cryspy.py index d45c8b8a..24d44bf9 100644 --- a/easyDiffractionLib/Interfaces/cryspy.py +++ b/easyDiffractionLib/Interfaces/cryspy.py @@ -141,17 +141,17 @@ def create(self, model): for phase in model: ident = str(self.__identify(phase)) + '_phase' self.calculator.assignPhase(model_key, ident) - elif t_.__name__ == 'Powder1DCW': + elif t_.__name__ in ['Powder1DCW', 'powder1DCW', 'Npowder1DCW']: # #TODO Check to see if parameters and pattern should be initialized here. - self.calculator.createModel(model_key, 'powder1DCW') - elif t_.__name__ == 'Powder1DTOF': + self.__createModel(model_key, 'powder1DCW') + elif t_.__name__ in ['Powder1DTOF', 'powder1DTOF', 'Npowder1DTOF']: # #TODO Check to see if parameters and pattern should be initialized here. - self.calculator.createModel(model_key, 'powder1DTOF') + self.__createModel(model_key, 'powder1DTOF') elif t_.__name__ == 'Sample': # This is legacy mode. Boo if issubclass(type(model.parameters), Instrument1DCWParameters): - self.calculator.createModel(model_key, 'powder1DCW') + self.__createModel(model_key, 'powder1DCW') elif issubclass(type(model.parameters), Instrument1DTOFParameters): - self.calculator.createModel(model_key, 'powder1DTOF') + self.__createModel(model_key, 'powder1DTOF') else: raise AttributeError('Unknown EXP type') else: @@ -188,6 +188,9 @@ def fit_func(self, x_array: np.ndarray) -> np.ndarray: def get_hkl(self, x_array: np.ndarray = None) -> dict: return self.calculator.get_hkl(x_array) + def __createModel(self, model, model_type): + self.calculator.createModel(model, model_type) + @staticmethod def __identify(obj): return borg.map.convert_id_to_key(obj) diff --git a/easyDiffractionLib/interface.py b/easyDiffractionLib/interface.py index 66cb0a56..52116a4d 100644 --- a/easyDiffractionLib/interface.py +++ b/easyDiffractionLib/interface.py @@ -11,208 +11,5 @@ class InterfaceFactory(InterfaceFactoryTemplate): def __init__(self): super(InterfaceFactory, self).__init__(InterfaceTemplate._interfaces) - # def generate_sample_binding(self, name, *args) -> property: - # """ - # Automatically bind a `Parameter` to the corresponding interface. - # :param name: parameter name - # :type name: str - # :return: binding property - # :rtype: property - # """ - # fun = self.__set_item(self, 'filename') - # fun(args[0].filename) - # return property(fget=None, - # fset=self.__set_sample_item(self, name, *args)) - # - # def generate_instrument_binding(self, name) -> property: - # """ - # Automatically bind a `Parameter` to the corresponding interface. - # :param name: parameter name - # :type name: str - # :return: binding property - # :rtype: property - # """ - # return property(fget=self.__get_instrument_item(self, name), - # fset=self.__set_instrument_item(self, name)) - # - # def generate_background_binding(self, name, background) -> property: - # """ - # Automatically bind a `Parameter` to the corresponding interface. - # :param name: parameter name - # :type name: str - # :return: binding property - # :rtype: property - # """ - # return property(fget=None, - # fset=self.__set_background_item(self, background, name)) - # - # def generate_pattern_binding(self, name, pattern) -> property: - # """ - # Automatically bind a `Parameter` to the corresponding interface. - # :param name: parameter name - # :type name: str - # :return: binding property - # :rtype: property - # """ - # return property(fget=None, - # fset=self.__set_pattern_item(self, pattern, name)) - # - # def generate_binding(self, name, *args, **kwargs) -> property: - # """ - # Automatically bind a `Parameter` to the corresponding interface. - # :param name: parameter name - # :type name: str - # :return: binding property - # :rtype: property - # """ - # return property(self.__get_item(self, name), self.__set_item(self, name)) - # - # @staticmethod - # def __get_item(obj, key: str, external: bool = True) -> Callable: - # """ - # Access the value of a key by a callable object - # :param key: name of parameter to be retrieved - # :type key: str - # :return: function to get key - # :rtype: Callable - # """ - # - # def inner(): - # return obj().get_value(key, external) - # - # return inner - # - # @staticmethod - # def __set_item(obj, key) -> Callable: - # """ - # Set the value of a key by a callable object - # :param obj: object to be created from - # :type obj: InterfaceFactory - # :param key: name of parameter to be set - # :type key: str - # :return: function to set key - # :rtype: Callable - # """ - # - # def inner(value): - # obj().set_value(key, value) - # return inner - # - # @staticmethod - # def __get_sample_item(obj, key: str, holder) -> Callable: - # """ - # Access the value of a key by a callable object - # :param key: name of parameter to be retrieved - # :type key: str - # :return: function to get key - # :rtype: Callable - # """ - # - # def inner(): - # # return obj().get_value(key) - # return None - # return inner - # - # @staticmethod - # def __set_sample_item(obj, key, holder) -> Callable: - # """ - # Set the value of a key by a callable object - # :param obj: object to be created from - # :type obj: InterfaceFactory - # :param key: name of parameter to be set - # :type key: str - # :return: function to set key - # :rtype: Callable - # """ - # - # def inner(value): - # # !!! THIS IS NOT THE WAY TO DO IT !!! - # # !!! FOR TESTING ONLY !!!! - # if obj.current_interface_name == 'CrysPy': - # try: - # obj().set_value(key, holder.phases.cif.__str__(holder.output_index)) - # except: - # obj().set_value(key, holder.phases.cif.__str__(holder.output_index)) - # else: - # holder.phases.cif.to_file(holder.filename, holder.output_index) - # # obj().set_value(key, value) - # return inner - # - # @staticmethod - # def __get_instrument_item(obj, key: str) -> Callable: - # """ - # Access the value of a key by a callable object - # :param key: name of parameter to be retrieved - # :type key: str - # :return: function to get key - # :rtype: Callable - # """ - # - # def inner(): - # return obj().get_instrument_value(key) - # return inner - # - # @staticmethod - # def __set_instrument_item(obj, key) -> Callable: - # """ - # Set the value of a key by a callable object - # :param obj: object to be created from - # :type obj: InterfaceFactory - # :param key: name of parameter to be set - # :type key: str - # :return: function to set key - # :rtype: Callable - # """ - # - # def inner(value): - # obj().set_instrument_value(key, value) - # return inner - # - # @staticmethod - # def __get_background_item(obj, background, index: int) -> Callable: - # """ - # Access the value of a key by a callable object - # :param key: name of parameter to be retrieved - # :type key: str - # :return: function to get key - # :rtype: Callable - # """ - # - # def inner(): - # return obj().get_background_value(background, index) - # return inner - # - # @staticmethod - # def __set_background_item(obj, background, index) -> Callable: - # """ - # Set the value of a key by a callable object - # :param obj: object to be created from - # :type obj: InterfaceFactory - # :param key: name of parameter to be set - # :type key: str - # :return: function to set key - # :rtype: Callable - # """ - # - # def inner(value): - # obj().set_background_value(background, index, value) - # return inner - # - # @staticmethod - # def __set_pattern_item(obj, pattern, index) -> Callable: - # """ - # Set the value of a key by a callable object - # :param obj: object to be created from - # :type obj: InterfaceFactory - # :param key: name of parameter to be set - # :type key: str - # :return: function to set key - # :rtype: Callable - # """ - # - # def inner(value): - # obj().set_pattern_value(pattern, index, value) - # return inner - def get_hkl(self, x_array=None) -> dict: return self().get_hkl(x_array) From 15795213b55598a6c6f4ce11362dfc2faa4b6f73 Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Wed, 18 Aug 2021 14:49:49 +0200 Subject: [PATCH 16/18] Update interface and exp_type verification logic --- easyDiffractionLib/Interfaces/CFML.py | 18 +++++-- easyDiffractionLib/Interfaces/GSASII.py | 10 ++++ easyDiffractionLib/Interfaces/cryspy.py | 17 +++++-- .../Interfaces/interfaceTemplate.py | 48 ++++++++++++++++++- easyDiffractionLib/interface.py | 9 +++- easyDiffractionLib/sample.py | 21 ++++++-- 6 files changed, 110 insertions(+), 13 deletions(-) diff --git a/easyDiffractionLib/Interfaces/CFML.py b/easyDiffractionLib/Interfaces/CFML.py index 341afca6..934a244f 100644 --- a/easyDiffractionLib/Interfaces/CFML.py +++ b/easyDiffractionLib/Interfaces/CFML.py @@ -2,12 +2,12 @@ __version__ = "0.0.2" from easyCore import borg, np -from easyDiffractionLib.Interfaces.interfaceTemplate import InterfaceTemplate from easyCore.Objects.Inferface import ItemContainer -from easyDiffractionLib.Calculators.CFML import CFML as CFML_calc -from easyDiffractionLib.Profiles.P1D import Instrument1DCWParameters, Powder1DParameters -from easyDiffractionLib.sample import Sample from easyDiffractionLib import Lattice, SpaceGroup, Site, Phases +from easyDiffractionLib.sample import Sample +from easyDiffractionLib.Interfaces.interfaceTemplate import InterfaceTemplate +from easyDiffractionLib.Profiles.P1D import Instrument1DCWParameters, Powder1DParameters +from easyDiffractionLib.Calculators.CFML import CFML as CFML_calc class CFML(InterfaceTemplate): @@ -54,6 +54,10 @@ class CFML(InterfaceTemplate): 'x_offset': 'x_offset' } + feature_available = { + 'Npowder1DCW': True + } + name = 'CrysFML' def __init__(self): @@ -61,6 +65,12 @@ def __init__(self): self._phase = None self._filename = None + @staticmethod + def feature_checker(radiation='N', exp_type='CW', sample_type='powder', dimensionality='1D', test_str=None): + return InterfaceTemplate.features(radiation=radiation, exp_type=exp_type, sample_type=sample_type, + dimensionality=dimensionality, test_str=test_str, + FEATURES=CFML.feature_available) + def create(self, model): r_list = [] t_ = type(model) diff --git a/easyDiffractionLib/Interfaces/GSASII.py b/easyDiffractionLib/Interfaces/GSASII.py index 15574b3a..92798bfd 100644 --- a/easyDiffractionLib/Interfaces/GSASII.py +++ b/easyDiffractionLib/Interfaces/GSASII.py @@ -54,6 +54,10 @@ class GSASII(InterfaceTemplate): 'x_offset': 'x_offset' } + feature_available = { + 'Npowder1DCW': True + } + name = 'GSASII' def __init__(self): @@ -61,6 +65,12 @@ def __init__(self): self._phase = None self._filename = None + @staticmethod + def feature_checker(radiation='N', exp_type='CW', sample_type='powder', dimensionality='1D', test_str=None): + return InterfaceTemplate.features(radiation=radiation, exp_type=exp_type, sample_type=sample_type, + dimensionality=dimensionality, test_str=test_str, + FEATURES=GSASII.feature_available) + def create(self, model): r_list = [] t_ = type(model) diff --git a/easyDiffractionLib/Interfaces/cryspy.py b/easyDiffractionLib/Interfaces/cryspy.py index 24d44bf9..20cb2264 100644 --- a/easyDiffractionLib/Interfaces/cryspy.py +++ b/easyDiffractionLib/Interfaces/cryspy.py @@ -3,11 +3,11 @@ from easyCore import borg, np -from easyDiffractionLib.Interfaces.interfaceTemplate import InterfaceTemplate from easyCore.Objects.Inferface import ItemContainer -from easyDiffractionLib.Calculators.cryspy import Cryspy as Cryspy_calc -from easyDiffractionLib.Profiles.P1D import Instrument1DCWParameters, Instrument1DTOFParameters, Powder1DParameters from easyDiffractionLib import Lattice, SpaceGroup, Site, Phase, Phases +from easyDiffractionLib.Profiles.P1D import Instrument1DCWParameters, Instrument1DTOFParameters, Powder1DParameters +from easyDiffractionLib.Interfaces.interfaceTemplate import InterfaceTemplate +from easyDiffractionLib.Calculators.cryspy import Cryspy as Cryspy_calc class Cryspy(InterfaceTemplate): @@ -53,9 +53,20 @@ class Cryspy(InterfaceTemplate): name = 'CrysPy' + feature_available = { + 'Npowder1DCW': True, + 'Npowder1DTOF': True + } + def __init__(self): self.calculator = Cryspy_calc() + @staticmethod + def feature_checker(radiation='N', exp_type='CW', sample_type='powder', dimensionality='1D', test_str=None): + return InterfaceTemplate.features(radiation=radiation, exp_type=exp_type, sample_type=sample_type, + dimensionality=dimensionality, test_str=test_str, + FEATURES=Cryspy.feature_available) + def create(self, model): r_list = [] t_ = type(model) diff --git a/easyDiffractionLib/Interfaces/interfaceTemplate.py b/easyDiffractionLib/Interfaces/interfaceTemplate.py index c91579b0..843b0ac7 100644 --- a/easyDiffractionLib/Interfaces/interfaceTemplate.py +++ b/easyDiffractionLib/Interfaces/interfaceTemplate.py @@ -2,10 +2,17 @@ __version__ = "0.0.1" from abc import ABCMeta, abstractmethod -from typing import Tuple +from typing import Tuple, List from easyCore import np, borg from easyCore.Utils.json import MSONable +exp_type_strings = { + 'radiation_options': ['N', 'X'], + 'exp_type_options': ['CW', 'TOF'], + 'dimensional_options': ['1D', '2D'], + 'sample_options': ['powder', 'single'] +} + class InterfaceTemplate(MSONable, metaclass=ABCMeta): """ @@ -15,6 +22,44 @@ class InterfaceTemplate(MSONable, metaclass=ABCMeta): _borg = borg _link = {} + @staticmethod + def features(radiation='N', exp_type='CW', sample_type='powder', dimensionality='1D', test_str=None, FEATURES=None): + + if FEATURES is None: + raise AttributeError + feature_dict = InterfaceTemplate._feature_generator(radiation=radiation, exp_type=exp_type, + sample_type=sample_type, dimensionality=dimensionality) + + for key in FEATURES.keys(): + feature_dict[key] = FEATURES[key] + if test_str is None: + test_str = radiation + sample_type + dimensionality + exp_type + + return feature_dict[test_str] + + @staticmethod + def _feature_generator(radiation='N', exp_type='CW', sample_type='powder', dimensionality='1D'): + radiation_options = exp_type_strings['radiation_options'] + if radiation not in radiation_options: + raise AttributeError(f'"{radiation}" is not supported, only: {radiation_options}') + exp_type_options = exp_type_strings['exp_type_options'] + if exp_type not in exp_type_options: + raise AttributeError(f'"{exp_type}" is not supported, only: {exp_type_options}') + dimensional_options = exp_type_strings['dimensional_options'] + if dimensionality not in dimensional_options: + raise AttributeError(f'"{dimensionality}" is not supported, only: {dimensional_options}') + sample_options = exp_type_strings['sample_options'] + if sample_type not in sample_options: + raise AttributeError(f'"{sample_type}" is not supported, only: {sample_options}') + + features = [''.join(item) for item in np.array(np.meshgrid(radiation_options, + sample_options, + dimensional_options, + exp_type_options)).T.reshape(-1, + len(exp_type_strings)).tolist()] + feature_dict = dict.fromkeys(features, False) + return feature_dict + def __init_subclass__(cls, is_abstract: bool = False, **kwargs): """ Initialise all subclasses so that they can be created in the factory @@ -83,3 +128,4 @@ def fit_func(self, x_array: np.ndarray) -> np.ndarray: @abstractmethod def get_hkl(self, x_array: np.ndarray = None) -> dict: pass + \ No newline at end of file diff --git a/easyDiffractionLib/interface.py b/easyDiffractionLib/interface.py index 52116a4d..ab294584 100644 --- a/easyDiffractionLib/interface.py +++ b/easyDiffractionLib/interface.py @@ -1,7 +1,7 @@ __author__ = "github.com/wardsimon" __version__ = "0.0.1" -from typing import Callable +from typing import Callable, List from easyDiffractionLib.Interfaces import InterfaceTemplate from easyCore.Objects.Inferface import InterfaceFactoryTemplate @@ -13,3 +13,10 @@ def __init__(self): def get_hkl(self, x_array=None) -> dict: return self().get_hkl(x_array) + + def interface_compatability(self, check_str: str) -> List[str]: + compatible_interfaces = [] + for interface in self._interfaces: + if interface.feature_checker(test_str=check_str): + compatible_interfaces.append(self.return_name(interface)) + return compatible_interfaces diff --git a/easyDiffractionLib/sample.py b/easyDiffractionLib/sample.py index db319564..d7244eae 100644 --- a/easyDiffractionLib/sample.py +++ b/easyDiffractionLib/sample.py @@ -8,7 +8,7 @@ from easyCore.Utils.UndoRedo import property_stack_deco from easyDiffractionLib import Phase, Phases -from easyDiffractionLib.Profiles.P1D import Instrument1DCWParameters as Pars1D +from easyDiffractionLib.Profiles.P1D import Instrument1DCWParameters, Instrument1DTOFParameters from easyDiffractionLib.Profiles.P1D import Powder1DParameters as Pattern1D @@ -25,7 +25,7 @@ def __init__(self, phases: Union[Phase, Phases] = None, raise AttributeError('`phases` must be a Crystal or Crystals') if parameters is None: - parameters = Pars1D.default() + parameters = Instrument1DCWParameters.default() if pattern is None: pattern = Pattern1D.default() @@ -79,12 +79,14 @@ def parameters(self): @parameters.setter @property_stack_deco def parameters(self, value): - if not isinstance(value, Pars1D): + if not isinstance(value, Instrument1DCWParameters): raise ValueError self._parameters = value self._parameters.interface = self._interface def update_bindings(self): + if not self.interface.current_interface.feature_checker(test_str=self.exp_type_str): + raise AssertionError('The interface is not suitable for this experiment') self.generate_bindings() @property @@ -96,4 +98,15 @@ def as_dict(self, skip: list = None) -> dict: del d['_phases'] del d['_parameters'] del d['_pattern'] - return d \ No newline at end of file + return d + + @property + def exp_type_str(self) -> str: + type_str = 'Npowder1D' + if isinstance(self._parameters, Instrument1DCWParameters): + type_str += 'CW' + elif isinstance(self._parameters, Instrument1DTOFParameters): + type_str += 'TOF' + else: + raise TypeError(f'Experiment is of unknown type: {type(self._parameters)}') + return type_str From 28d9177fbaa526a2f2160600cc864f81b8949d0c Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Tue, 24 Aug 2021 10:32:45 +0200 Subject: [PATCH 17/18] Fix get HKL --- easyDiffractionLib/Interfaces/CFML.py | 2 +- easyDiffractionLib/Interfaces/GSASII.py | 2 +- easyDiffractionLib/Interfaces/cryspy.py | 2 +- easyDiffractionLib/Interfaces/interfaceTemplate.py | 2 +- easyDiffractionLib/interface.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/easyDiffractionLib/Interfaces/CFML.py b/easyDiffractionLib/Interfaces/CFML.py index 934a244f..6c47a403 100644 --- a/easyDiffractionLib/Interfaces/CFML.py +++ b/easyDiffractionLib/Interfaces/CFML.py @@ -139,7 +139,7 @@ def fit_func(self, x_array: np.ndarray) -> np.ndarray: """ return self.calculator.calculate(x_array) - def get_hkl(self, x_array: np.ndarray = None) -> dict: + def get_hkl(self, x_array: np.ndarray = None, idx=None) -> dict: return self.calculator.get_hkl(x_array) def dump_cif(self, *args, **kwargs): diff --git a/easyDiffractionLib/Interfaces/GSASII.py b/easyDiffractionLib/Interfaces/GSASII.py index 92798bfd..f4fadd79 100644 --- a/easyDiffractionLib/Interfaces/GSASII.py +++ b/easyDiffractionLib/Interfaces/GSASII.py @@ -140,7 +140,7 @@ def fit_func(self, x_array: np.ndarray) -> np.ndarray: """ return self.calculator.calculate(x_array) - def get_hkl(self, x_array: np.ndarray = None) -> dict: + def get_hkl(self, x_array: np.ndarray = None, idx=None) -> dict: return self.calculator.get_hkl(x_array) def dump_cif(self, *args, **kwargs): diff --git a/easyDiffractionLib/Interfaces/cryspy.py b/easyDiffractionLib/Interfaces/cryspy.py index 20cb2264..2e70a8ce 100644 --- a/easyDiffractionLib/Interfaces/cryspy.py +++ b/easyDiffractionLib/Interfaces/cryspy.py @@ -196,7 +196,7 @@ def fit_func(self, x_array: np.ndarray) -> np.ndarray: """ return self.calculator.calculate(x_array) - def get_hkl(self, x_array: np.ndarray = None) -> dict: + def get_hkl(self, x_array: np.ndarray = None, idx=None) -> dict: return self.calculator.get_hkl(x_array) def __createModel(self, model, model_type): diff --git a/easyDiffractionLib/Interfaces/interfaceTemplate.py b/easyDiffractionLib/Interfaces/interfaceTemplate.py index 843b0ac7..4ce4fed8 100644 --- a/easyDiffractionLib/Interfaces/interfaceTemplate.py +++ b/easyDiffractionLib/Interfaces/interfaceTemplate.py @@ -126,6 +126,6 @@ def fit_func(self, x_array: np.ndarray) -> np.ndarray: pass @abstractmethod - def get_hkl(self, x_array: np.ndarray = None) -> dict: + def get_hkl(self, x_array: np.ndarray = None, idx=None) -> dict: pass \ No newline at end of file diff --git a/easyDiffractionLib/interface.py b/easyDiffractionLib/interface.py index ab294584..f4a3f7bb 100644 --- a/easyDiffractionLib/interface.py +++ b/easyDiffractionLib/interface.py @@ -11,7 +11,7 @@ class InterfaceFactory(InterfaceFactoryTemplate): def __init__(self): super(InterfaceFactory, self).__init__(InterfaceTemplate._interfaces) - def get_hkl(self, x_array=None) -> dict: + def get_hkl(self, x_array=None, idx=None) -> dict: return self().get_hkl(x_array) def interface_compatability(self, check_str: str) -> List[str]: From fa52239b12fae3b533f6b3074c0280144f4ca25d Mon Sep 17 00:00:00 2001 From: Simon Ward <2798086+wardsimon@users.noreply.github.com> Date: Tue, 24 Aug 2021 11:18:09 +0200 Subject: [PATCH 18/18] Update requirements --- requirements.txt | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/requirements.txt b/requirements.txt index d285e7bd..0af28cd3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,29 @@ -cryspy==0.*,>=0.4.11 -git+https://github.com/easyScience/easyCore.git@xarray#egg=easyCore -hvplot==0.*,>=0.7.0 -jupyter==1.*,>=1.0.0 -git+https://github.com/easyScience/libsDarwin.git@main#egg=libsDarwin; sys_platform == "darwin" -git+https://github.com/easyScience/libsLinux.git@main#egg=libsLinux; sys_platform == "linux" -git+https://github.com/easyScience/libsWin32.git@main#egg=libsWin32; sys_platform == "win32" -pytest==5.*,>=5.2.0 -requests==2.*,>=2.24.0 -toml==0.*,>=0.10.0 +--extra-index-url https://easyscience.github.io/pypi + +asteval==0.9.25; python_version >= "3.7" and python_version < "4.0" +bumps==0.8.0; python_version >= "3.7" and python_version < "4.0" +cfml==0.0.1 +cryspy @ git+https://github.com/ikibalin/cryspy.git@bravis_type_fix +cycler==0.10.0; python_version >= "3.7" +easysciencecore @ git+https://github.com/easyScience/easyCore.git@develop ; python_version >= "3.7" and python_version < "4.0" +future==0.18.2; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.3.0" +gsasii==0.0.1 +importlib-metadata==4.6.4; python_version >= "3.7" and python_version < "3.8" +kiwisolver==1.3.1; python_version >= "3.7" +lmfit==1.0.2; python_version >= "3.7" and python_version < "4.0" +matplotlib==3.4.3; python_version >= "3.7" +numpy==1.21.2; python_version >= "3.7" and python_version < "3.10" and python_full_version >= "3.6.1" +packaging==21.0; python_version >= "3.7" and python_version < "4.0" +pandas==1.1.5; python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.6.1" +pillow==8.3.1; python_version >= "3.7" +pint==0.17; python_version >= "3.7" and python_version < "4.0" +pycifstar==0.2.8 +pyparsing==2.4.7; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_full_version >= "3.3.0" and python_version >= "3.7" and python_version < "4.0" +python-dateutil==2.8.2; python_full_version >= "3.6.1" and python_version >= "3.7" and python_version < "4.0" +pytz==2021.1; python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.6.1" +scipy==1.7.1; python_version >= "3.7" and python_version < "3.10" +six==1.16.0; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_full_version >= "3.3.0" and python_version >= "3.7" and python_version < "4.0" +typing-extensions==3.10.0.0; python_version >= "3.7" and python_version < "3.8" +uncertainties==3.1.6; python_version >= "3.7" and python_version < "4.0" +xarray==0.19.0; python_version >= "3.7" and python_version < "4.0" +zipp==3.5.0; python_version >= "3.7" and python_version < "3.8"