diff --git a/.github/workflows/ci-testing.yaml b/.github/workflows/ci-testing.yaml index 8ebd8c3f..565dd2f7 100644 --- a/.github/workflows/ci-testing.yaml +++ b/.github/workflows/ci-testing.yaml @@ -14,7 +14,7 @@ # - Download the Python package from the previous job # - Install the downloaded Python package # - Test the code base -# - Check if Jupiter Notebooks run without errors +# - Check if Jupyter Notebooks run without errors name: Test code, notebooks and package @@ -251,7 +251,7 @@ jobs: run: > pytest tests/ --color=yes -n auto - - name: Check if Jupiter Notebooks run without errors + - name: Check if Jupyter Notebooks run without errors shell: bash run: > pytest --nbmake examples/ --ignore-glob='examples/*emcee*' diff --git a/README.md b/README.md index acb8b765..19d69f6c 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@

- + - + - EasyCrystallography + EasyDiffraction

diff --git a/examples/Fitting_pd-neut-tof_Si-SEPD.ipynb b/examples/Fitting_pd-neut-tof_Si-SEPD.ipynb index 24d18c8d..ecb61003 100644 --- a/examples/Fitting_pd-neut-tof_Si-SEPD.ipynb +++ b/examples/Fitting_pd-neut-tof_Si-SEPD.ipynb @@ -16,6 +16,10 @@ "execution_count": null, "id": "99026183", "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:34:43.698644Z", + "start_time": "2025-01-23T09:34:43.695633Z" + }, "tags": [ "hide_in_docs" ] @@ -46,7 +50,12 @@ "cell_type": "code", "execution_count": null, "id": "cec2a6a5", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:34:49.088064Z", + "start_time": "2025-01-23T09:34:46.202611Z" + } + }, "outputs": [], "source": [ "import easydiffraction as ed" @@ -72,7 +81,12 @@ "cell_type": "code", "execution_count": null, "id": "3a6a92e0", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:34:52.380083Z", + "start_time": "2025-01-23T09:34:52.337822Z" + } + }, "outputs": [], "source": [ "job = ed.Job(type='tof')\n", @@ -93,7 +107,12 @@ "cell_type": "code", "execution_count": null, "id": "d8d3d078", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:34:53.372500Z", + "start_time": "2025-01-23T09:34:53.360076Z" + } + }, "outputs": [], "source": [ "phase = ed.Phase(name='si')" @@ -111,10 +130,17 @@ "cell_type": "code", "execution_count": null, "id": "784719ae", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:39:32.198479Z", + "start_time": "2025-01-23T09:39:31.964001Z" + } + }, "outputs": [], "source": [ - "phase.space_group.name_hm_alt = 'F d -3 m'" + "phase.space_group.name_hm_alt = 'F d -3 m'\n", + "print(phase.space_group.name_hm_alt)\n", + "print(phase.space_group.it_coordinate_system_code)" ] }, { @@ -129,7 +155,12 @@ "cell_type": "code", "execution_count": null, "id": "9d66e7dc", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:35:50.841182Z", + "start_time": "2025-01-23T09:35:50.838592Z" + } + }, "outputs": [], "source": [ "phase.cell.length_a = 5.43146" @@ -147,14 +178,19 @@ "cell_type": "code", "execution_count": null, "id": "a180af30", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:35:51.944868Z", + "start_time": "2025-01-23T09:35:51.876608Z" + } + }, "outputs": [], "source": [ "phase.atom_sites.append(label='Si',\n", " type_symbol='Si',\n", - " fract_x=0,\n", - " fract_y=0,\n", - " fract_z=0,\n", + " fract_x=0.125,\n", + " fract_y=0.125,\n", + " fract_z=0.125,\n", " occupancy=1,\n", " b_iso_or_equiv=0.529)" ] @@ -171,7 +207,12 @@ "cell_type": "code", "execution_count": null, "id": "6b670878", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:35:53.792410Z", + "start_time": "2025-01-23T09:35:53.333729Z" + } + }, "outputs": [], "source": [ "job.add_phase(phase=phase)\n", @@ -190,7 +231,12 @@ "cell_type": "code", "execution_count": null, "id": "08d364c1", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:35:54.843688Z", + "start_time": "2025-01-23T09:35:54.838709Z" + } + }, "outputs": [], "source": [ "phase = job.phases['si']\n", @@ -209,7 +255,12 @@ "cell_type": "code", "execution_count": null, "id": "0339f8c6", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:35:55.871734Z", + "start_time": "2025-01-23T09:35:55.866129Z" + } + }, "outputs": [], "source": [ "job.show_crystal_structure(id='si')" @@ -229,7 +280,12 @@ "cell_type": "code", "execution_count": null, "id": "008b6844", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:01.251409Z", + "start_time": "2025-01-23T09:36:01.249209Z" + } + }, "outputs": [], "source": [ "ed.download_from_repository('sepd.xye', destination='data')" @@ -247,7 +303,12 @@ "cell_type": "code", "execution_count": null, "id": "615a112f", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:02.278118Z", + "start_time": "2025-01-23T09:36:02.274659Z" + } + }, "outputs": [], "source": [ "with open('data/sepd.xye') as f:\n", @@ -266,7 +327,12 @@ "cell_type": "code", "execution_count": null, "id": "30cb31e4", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:04.527565Z", + "start_time": "2025-01-23T09:36:03.309012Z" + } + }, "outputs": [], "source": [ "job.add_experiment_from_file('data/sepd.xye')" @@ -284,7 +350,12 @@ "cell_type": "code", "execution_count": null, "id": "35daa023", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:04.741828Z", + "start_time": "2025-01-23T09:36:04.532639Z" + } + }, "outputs": [], "source": [ "job.show_experiment_chart(show_legend=False)" @@ -302,7 +373,12 @@ "cell_type": "code", "execution_count": null, "id": "8fc580cc", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:06.222660Z", + "start_time": "2025-01-23T09:36:06.219628Z" + } + }, "outputs": [], "source": [ "background_points = [(x, 200) for x in range(0, 35000, 5000)]\n", @@ -313,7 +389,12 @@ "cell_type": "code", "execution_count": null, "id": "98ae6fa7", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:07.171646Z", + "start_time": "2025-01-23T09:36:07.164518Z" + } + }, "outputs": [], "source": [ "job.set_background(background_points)" @@ -331,7 +412,12 @@ "cell_type": "code", "execution_count": null, "id": "825592f8", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:08.633462Z", + "start_time": "2025-01-23T09:36:08.598582Z" + } + }, "outputs": [], "source": [ "job.show_experiment_chart()" @@ -357,7 +443,12 @@ "cell_type": "code", "execution_count": null, "id": "20faa380", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:10.810522Z", + "start_time": "2025-01-23T09:36:10.542087Z" + } + }, "outputs": [], "source": [ "job.show_analysis_chart()" @@ -375,7 +466,12 @@ "cell_type": "code", "execution_count": null, "id": "17e0a56a", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:11.934668Z", + "start_time": "2025-01-23T09:36:11.932778Z" + } + }, "outputs": [], "source": [ "pattern_params = job.pattern\n", @@ -394,7 +490,12 @@ "cell_type": "code", "execution_count": null, "id": "117ddecb", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:13.979115Z", + "start_time": "2025-01-23T09:36:13.739198Z" + } + }, "outputs": [], "source": [ "experiment_params.dtt1 = 7476.91\n", @@ -415,7 +516,12 @@ "cell_type": "code", "execution_count": null, "id": "ebcd54e6", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:15.840051Z", + "start_time": "2025-01-23T09:36:15.599464Z" + } + }, "outputs": [], "source": [ "phase.scale = 10\n", @@ -434,7 +540,12 @@ "cell_type": "code", "execution_count": null, "id": "119417fb", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:17.591973Z", + "start_time": "2025-01-23T09:36:17.351422Z" + } + }, "outputs": [], "source": [ "experiment_params.alpha0 = 0.024\n", @@ -459,7 +570,12 @@ "cell_type": "code", "execution_count": null, "id": "bdf7b6ca", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:19.093090Z", + "start_time": "2025-01-23T09:36:19.090310Z" + } + }, "outputs": [], "source": [ "phase.scale.free = True\n", @@ -478,7 +594,12 @@ "cell_type": "code", "execution_count": null, "id": "95bafcea", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:21.045573Z", + "start_time": "2025-01-23T09:36:21.036012Z" + } + }, "outputs": [], "source": [ "job.show_free_parameters()" @@ -496,7 +617,12 @@ "cell_type": "code", "execution_count": null, "id": "4b5e21a7", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:29.455450Z", + "start_time": "2025-01-23T09:36:23.550380Z" + } + }, "outputs": [], "source": [ "job.fit()" @@ -514,7 +640,12 @@ "cell_type": "code", "execution_count": null, "id": "050c6bd9", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:30.975635Z", + "start_time": "2025-01-23T09:36:30.971687Z" + } + }, "outputs": [], "source": [ "job.show_free_parameters()" @@ -532,7 +663,12 @@ "cell_type": "code", "execution_count": null, "id": "131e47e6", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:32.693558Z", + "start_time": "2025-01-23T09:36:32.458104Z" + } + }, "outputs": [], "source": [ "job.show_analysis_chart()" @@ -550,7 +686,12 @@ "cell_type": "code", "execution_count": null, "id": "21c37665", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:34.541278Z", + "start_time": "2025-01-23T09:36:34.538036Z" + } + }, "outputs": [], "source": [ "for background_point in pattern_params.backgrounds[0]:\n", @@ -569,7 +710,12 @@ "cell_type": "code", "execution_count": null, "id": "9ad2de56", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:36.029938Z", + "start_time": "2025-01-23T09:36:36.025177Z" + } + }, "outputs": [], "source": [ "job.show_free_parameters()" @@ -587,7 +733,12 @@ "cell_type": "code", "execution_count": null, "id": "5f71d9a9", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:41.747019Z", + "start_time": "2025-01-23T09:36:37.380768Z" + } + }, "outputs": [], "source": [ "job.fit()" @@ -605,7 +756,12 @@ "cell_type": "code", "execution_count": null, "id": "be12cf69", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:41.757332Z", + "start_time": "2025-01-23T09:36:41.753025Z" + } + }, "outputs": [], "source": [ "job.show_free_parameters()" @@ -623,7 +779,12 @@ "cell_type": "code", "execution_count": null, "id": "6288fbc7", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:42.069945Z", + "start_time": "2025-01-23T09:36:41.830912Z" + } + }, "outputs": [], "source": [ "job.show_analysis_chart()" @@ -641,7 +802,12 @@ "cell_type": "code", "execution_count": null, "id": "8e94c526", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:42.133001Z", + "start_time": "2025-01-23T09:36:42.130652Z" + } + }, "outputs": [], "source": [ "experiment_params.sigma0.free = True\n", @@ -661,7 +827,12 @@ "cell_type": "code", "execution_count": null, "id": "0c2e839b", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:43.229251Z", + "start_time": "2025-01-23T09:36:43.226632Z" + } + }, "outputs": [], "source": [ "for background_point in pattern_params.backgrounds[0]:\n", @@ -680,7 +851,12 @@ "cell_type": "code", "execution_count": null, "id": "8ec068a3", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:44.678178Z", + "start_time": "2025-01-23T09:36:44.673718Z" + } + }, "outputs": [], "source": [ "job.show_free_parameters()" @@ -698,7 +874,12 @@ "cell_type": "code", "execution_count": null, "id": "7ea43a58", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:56.746074Z", + "start_time": "2025-01-23T09:36:46.229223Z" + } + }, "outputs": [], "source": [ "job.fit()" @@ -716,7 +897,12 @@ "cell_type": "code", "execution_count": null, "id": "c00fcbbe", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:36:58.409307Z", + "start_time": "2025-01-23T09:36:58.405507Z" + } + }, "outputs": [], "source": [ "job.show_free_parameters()" @@ -734,7 +920,12 @@ "cell_type": "code", "execution_count": null, "id": "efb3b460", - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-01-23T09:37:00.179282Z", + "start_time": "2025-01-23T09:36:59.937539Z" + } + }, "outputs": [], "source": [ "job.show_analysis_chart()" @@ -746,6 +937,18 @@ "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" } }, "nbformat": 4, diff --git a/src/easydiffraction/calculators/cryspy/calculator.py b/src/easydiffraction/calculators/cryspy/calculator.py index c6863db4..5b44bc71 100644 --- a/src/easydiffraction/calculators/cryspy/calculator.py +++ b/src/easydiffraction/calculators/cryspy/calculator.py @@ -12,8 +12,10 @@ import cryspy import numpy as np +from cryspy.A_functions_base.function_2_space_group import get_default_it_coordinate_system_code_by_it_number from cryspy.procedure_rhochi.rhochi_by_dictionary import rhochi_calc_chi_sq_by_dictionary from easyscience import global_object as borg +from gemmi import find_spacegroup_by_name from easydiffraction.calculators.cryspy.parser import calcObjAndDictToEdExperiments from easydiffraction.calculators.cryspy.parser import cifV2ToV1 @@ -114,6 +116,8 @@ def __init__(self): self._first_experiment_name = '' self.exp_obj = None self.chisq = None + self.name_hm_alt = '' + self.it_code = '' self.excluded_points = [] self._cryspyData = Data() # {phase_name: CryspyPhase, exp_name: CryspyExperiment} self._cryspyObject = self._cryspyData._cryspyObj @@ -151,6 +155,9 @@ def createModel(self, model_type: str = 'powder1DCW'): self.type = model_type self.model = cls(**model) + def is_tof(self) -> bool: + return self.model.PREFIX.lower() == 'tof' + def createPhase(self, crystal_name: str, key: str = 'phase') -> str: phase = cryspy.Phase(label=crystal_name, scale=1, igsize=0) self.storage[key] = phase @@ -215,7 +222,18 @@ def assignCell_toCrystal(self, cell_name: str, crystal_name: str): cell = self.storage[cell_name] crystal.cell = cell - def createSpaceGroup(self, key: str = 'spacegroup', name_hm_alt: str = 'P 1') -> str: + def createSpaceGroup(self, key: str = 'spacegroup', name_hm_alt: str = '', it_code: Optional[str] = '') -> str: + self.name_hm_alt = name_hm_alt or self.name_hm_alt or 'P 1' + name_hm_alt = self.name_hm_alt + + if not it_code: + sg = find_spacegroup_by_name(name_hm_alt) + self.it_code = get_default_it_coordinate_system_code_by_it_number(sg.number) + it_code = it_code or self.it_code + + if it_code: + name_hm_alt += ':' + it_code + sg_split = name_hm_alt.split(':') opts = {'name_hm_alt': sg_split[0]} if len(sg_split) > 1: @@ -258,6 +276,13 @@ def updateSpacegroup(self, sg_key: str, **kwargs): break sg_key = self.createSpaceGroup(key=sg_key, **kwargs) self.assignSpaceGroup_toCrystal(sg_key, previous_key) + # here, the CIF has the new group, so reload + if not self.current_crystal: + return + if 'it_code' in kwargs: + cif = self.cif_str + self.updateModelCif(cif) + pass def createAtom(self, atom_name: str, **kwargs) -> str: atom = cryspy.AtomSite(**kwargs) diff --git a/src/easydiffraction/calculators/cryspy/wrapper.py b/src/easydiffraction/calculators/cryspy/wrapper.py index 640e582b..4ba7e29d 100644 --- a/src/easydiffraction/calculators/cryspy/wrapper.py +++ b/src/easydiffraction/calculators/cryspy/wrapper.py @@ -126,8 +126,9 @@ def create(self, model: B) -> List[ItemContainer]: ) # Interface with Spacegroup elif issubclass(t_, SpaceGroup): - s_key = self.calculator.createSpaceGroup(key=model_key, name_hm_alt='P 1') - keys = {'hermann_mauguin': 'name_hm_alt'} + name = model.name_hm_alt.raw_value + s_key = self.calculator.createSpaceGroup(key=model_key, name_hm_alt=name) + keys = {'hermann_mauguin': 'name_hm_alt', 'coordinate-code': 'it_code'} r_list.append( ItemContainer( s_key, @@ -247,6 +248,9 @@ def get_calculated_y_for_phase(self, phase_idx: int) -> list: def get_total_y_for_phases(self) -> tuple[ndarray, ndarray]: return self.calculator.get_total_y_for_phases() + def is_tof(self) -> bool: + return self.calculator.is_tof() + @staticmethod def _identify(obj: B, as_str: bool = False) -> Union[int, str]: """ @@ -778,6 +782,10 @@ def get_phase_components(self, phase_name: str) -> dict: data = self._internal.get_phase_components(phase_name) return data + def is_tof(self) -> bool: + if self._internal is not None: + return self._internal.is_tof() + def updateModelCif(self, cif_string: str) -> None: self.calculator.updateModelCif(cif_string) diff --git a/src/easydiffraction/calculators/wrapper_base.py b/src/easydiffraction/calculators/wrapper_base.py index 0f697a85..589c1290 100644 --- a/src/easydiffraction/calculators/wrapper_base.py +++ b/src/easydiffraction/calculators/wrapper_base.py @@ -161,6 +161,10 @@ def get_calculated_y_for_phase(self, idx=None) -> list: def get_total_y_for_phases(self) -> list: pass + @abstractmethod + def is_tof(self) -> bool: + pass + @staticmethod def _get_constructor(known_components, sample_object): all_bases = set([base for base in sample_object.__class__.__bases__ if hasattr(base, '_internal_type')]) diff --git a/src/easydiffraction/calculators/wrapper_factory.py b/src/easydiffraction/calculators/wrapper_factory.py index 37fc6ccf..ede9aec3 100644 --- a/src/easydiffraction/calculators/wrapper_factory.py +++ b/src/easydiffraction/calculators/wrapper_factory.py @@ -28,6 +28,9 @@ def get_phase_components(self, phase_name): def get_component(self, component_name): return self().get_component(component_name) + def is_tof(self) -> bool: + return self().is_tof() + def updateModelCif(self, cif_string): return self().updateModelCif(cif_string) diff --git a/src/easydiffraction/io/cif.py b/src/easydiffraction/io/cif.py index 9c83cb2b..affb8dd1 100644 --- a/src/easydiffraction/io/cif.py +++ b/src/easydiffraction/io/cif.py @@ -347,9 +347,7 @@ def _sanitize_loop(self, data: StarLoop) -> StarLoop: this_data._kwargs[label].raw_value = comparison_frac fracs_changed = True if fracs_changed: - self.warnings.append( - 'Some fractional co-ordinates rounded to ideal values to ' 'avoid issues with finite precision.' - ) + self.warnings.append('Some fractional co-ordinates rounded to ideal values to avoid issues with finite precision.') return data def _sanitize_data(self, data: StarEntry) -> StarEntry: @@ -1058,7 +1056,7 @@ def str2float(text): def dataBlockToCif(block, includeBlockName=True): cif = '' if includeBlockName: - cif += f"data_{block['name']['value']}" + cif += f'data_{block["name"]["value"]}' cif += '\n\n' if 'params' in block: for category in block['params'].values(): diff --git a/src/easydiffraction/io/cif_reader.py b/src/easydiffraction/io/cif_reader.py index f4ad9ab7..195e5bb1 100644 --- a/src/easydiffraction/io/cif_reader.py +++ b/src/easydiffraction/io/cif_reader.py @@ -97,8 +97,8 @@ def parameters_from_cif_block(block) -> dict: parameters['resolution_v']['value'], parameters['resolution_v']['error'] = parse_with_error(value) value = block.find_value('_pd_instr_resolution_w') or block.find_value('_pd_instr.resolution_w') if value is not None: - parameters['resolution_z'] = {} - parameters['resolution_z']['value'], parameters['resolution_z']['error'] = parse_with_error(value) + parameters['resolution_w'] = {} + parameters['resolution_w']['value'], parameters['resolution_w']['error'] = parse_with_error(value) value = block.find_value('_pd_instr_resolution_x') or block.find_value('_pd_instr.resolution_x') if value is not None: parameters['resolution_x'] = {} diff --git a/src/easydiffraction/job/experiment/backgrounds/background.py b/src/easydiffraction/job/experiment/backgrounds/background.py index 7175a209..ebd18837 100644 --- a/src/easydiffraction/job/experiment/backgrounds/background.py +++ b/src/easydiffraction/job/experiment/backgrounds/background.py @@ -31,7 +31,7 @@ def __init__(self, *args, linked_experiment=None, **kwargs): # Convert `linked_experiment` to a Descriptor if linked_experiment is None: raise AttributeError( - 'Backgrounds need to be associated with an experiment. ' 'Use the `linked_experiment` key word argument.' + 'Backgrounds need to be associated with an experiment. Use the `linked_experiment` key word argument.' ) elif isinstance(linked_experiment, str): linked_experiment = Descriptor('linked_experiment', linked_experiment) diff --git a/src/easydiffraction/job/experiment/experiment.py b/src/easydiffraction/job/experiment/experiment.py index 8d131173..ffaf9bca 100644 --- a/src/easydiffraction/job/experiment/experiment.py +++ b/src/easydiffraction/job/experiment/experiment.py @@ -104,6 +104,16 @@ def __init__(self, job_name: str, datastore: xr.Dataset = None, *args, **kwargs) self.pattern = self._datastore._simulations.pattern self.parameters = self._datastore._simulations.parameters + @property + def datastore(self): + return self._datastore + + @datastore.setter + def datastore(self, value): + self._datastore = value + self.pattern = self._datastore._simulations.pattern + self.parameters = self._datastore._simulations.parameters + def add_experiment_data(self, x, y, e, experiment_name='None'): coord_name = self.job_name + '_' + experiment_name + '_' + self._x_axis_name self._datastore.store.easyscience.add_coordinate(coord_name, x) @@ -149,6 +159,7 @@ def pattern_from_cif_block(self, block) -> None: if p['zero_shift'].get('error') is not None: pattern.zero_shift.error = p['zero_shift'].get('error') pattern.zero_shift.fixed = False + self.datastore._simulations.pattern.zero_shift = pattern.zero_shift if 'radiation' in p: pattern.radiation = p['radiation'] @@ -394,7 +405,7 @@ def from_cif_string(self, cif_string, experiment_name=None): self.from_cif_block(block, experiment_name=experiment_name) phase_names = [phase.name for phase in self._datastore._simulations._phases] self.interface.updateExpCif(cif_string, phase_names) - # self.generate_bindings() # ???? NEEDED??? + self.generate_bindings() def from_cif_block(self, block, experiment_name=None): """ @@ -412,9 +423,12 @@ def from_cif_block(self, block, experiment_name=None): self.pattern_from_cif_block(block) bg = self.background_from_cif_block(block, experiment_name=experiment_name) self.pattern.backgrounds.append(bg) + self.datastore._simulations.pattern.backgrounds.append(bg) self.parameters_from_cif_block(block) self.phase_parameters_from_cif_block(block) self.data_from_cif_block(block, experiment_name) + # self.datastore._simulations.pattern = self.pattern # FAILS!! TODO: FIX + self.datastore._simulations.parameters = self.parameters @property def cif(self): diff --git a/src/easydiffraction/job/job.py b/src/easydiffraction/job/job.py index d05f8d89..f416357e 100644 --- a/src/easydiffraction/job/job.py +++ b/src/easydiffraction/job/job.py @@ -94,7 +94,7 @@ def __init__( # Generate the datastore for this job __dataset = datastore if datastore is not None else xr.Dataset() self.add_datastore(__dataset) - self._name = name if name is not None else 'Job' + self._name = name if name is not None else 'sim_' self.cif_string = '' # Dataset specific attributes @@ -110,7 +110,8 @@ def __init__( raise ValueError('Job type and experiment cannot be passed together.') # assign Experiment, so potential type assignment can be done - self.experiment = experiment + self._experiment = self.datastore._experiments + self._experiment.datastore = self.datastore self._summary = None # TODO: implement self._info = None # TODO: implement @@ -135,17 +136,20 @@ def __init__( # update experiment with right type self.update_exp_type() + # initialize the sample, based on job type + self.sample = sample # assign Job components - self.sample = sample # container for phases + self._sample.parameters = self.datastore._experiments.parameters + self.datastore._simulations = self.sample + self.interface = self.sample._interface self.analysis = analysis self.update_experiment_type() - # necessary for the fitter - # TODO: remove the dependency on kwargs + self._kwargs = {} self._kwargs['_phases'] = self.sample.phases self._kwargs['_parameters'] = self.sample.parameters - self._kwargs['_pattern'] = self.sample.pattern + self._kwargs['_pattern'] = self.experiment.pattern @property def sample(self) -> Sample: @@ -168,6 +172,7 @@ def sample(self, value: Union[Sample, None]) -> None: elif self.type.is_tof: parameters = Instrument1DTOFParameters() self._sample = Sample('Sample', parameters=parameters, pattern=pattern) + self._kwargs['_parameters'] = self.sample.parameters @property def theoretical_model(self) -> Sample: @@ -326,30 +331,48 @@ def update_experiment_type(self) -> None: self.type.is_sc = self.experiment.is_single_crystal self.type.is_2d = self.experiment.is_2d # radiation - if hasattr(self.sample, 'pattern') and self.sample.pattern is not None: + if hasattr(self.experiment, 'pattern') and self.experiment.pattern is not None: if self.type.is_xray: - self.sample.pattern.radiation = 'x-ray' + self.experiment.pattern.radiation = 'x-ray' elif self.type.is_neut: - self.sample.pattern.radiation = 'neutron' + self.experiment.pattern.radiation = 'neutron' # axis if self.type.is_tof: self._x_axis_name = 'time' - if self.pattern is not None: - self.pattern.zero_shift.unit = 'μs' + if self.experiment.pattern is not None: + self.experiment.pattern.zero_shift.unit = 'μs' else: self._x_axis_name = 'tth' - if self.pattern is not None: - self.pattern.zero_shift.unit = 'degree' + if self.experiment.pattern is not None: + self.experiment.pattern.zero_shift.unit = 'degree' def update_exp_type(self) -> None: """ Update the experiment type based on the job. """ + self.experiment.is_polarized = self.type.is_pol self.experiment.is_tof = self.type.is_tof self.experiment.is_single_crystal = self.type.is_sc self.experiment.is_2d = self.type.is_2d + if self.type.is_pol: + pattern = PolPowder1DParameters() + else: + pattern = Powder1DParameters() + # if pattern type is not the same as job, re-create the job.patter + if self.experiment.pattern is not None and self.experiment.pattern.name != pattern.name: + self.experiment.pattern = pattern + self._kwargs['_pattern'] = self.experiment.pattern + + if self.type.is_cwl: + parameters = Instrument1DCWParameters() + elif self.type.is_tof: + parameters = Instrument1DTOFParameters() + # self._sample = Sample('Sample', parameters=parameters, pattern=pattern) + if self.experiment.parameters.name != parameters.name: + self.experiment.parameters = parameters + self._kwargs['_parameters'] = self.experiment.parameters def update_phase_scale(self) -> None: """ @@ -421,17 +444,19 @@ def add_experiment_from_file(self, file_url: str) -> None: self.experiment.from_cif_file(file_url) self.update_experiment_type() + # update the kwargs with new pointers + self._kwargs['_parameters'] = self.experiment.parameters + # re-do the sample in case of type change. # Different type read in (likely TOF), so re-create the sample - if self.sample.parameters.name != self.experiment.parameters.name: + if self.interface.is_tof() != self.type.is_tof: parameters = self.experiment.parameters pattern = self.experiment.pattern phases = self.sample.phases name = self.sample.name self.sample = Sample(name, parameters=parameters, pattern=pattern, phases=phases) - self.sample.parameters = self.experiment.parameters - self.update_experiment_type() - self.update_interface() + # self.update_experiment_type() + # self.update_interface() # Temporary fix for dtt1 and dtt2 parameters read from CIF in Scipp format if ( hasattr(self.sample.parameters, 'dtt1') diff --git a/src/easydiffraction/job/old_sample/old_sample.py b/src/easydiffraction/job/old_sample/old_sample.py index 10a87fc4..01736bdd 100644 --- a/src/easydiffraction/job/old_sample/old_sample.py +++ b/src/easydiffraction/job/old_sample/old_sample.py @@ -7,6 +7,7 @@ from typing import ClassVar from typing import Union +from cryspy.A_functions_base.function_2_space_group import get_default_it_coordinate_system_code_by_it_number from easycrystallography.Structures.Phase import Phases as ecPhases from easyscience.Datasets.xarray import xr from easyscience.global_object.undo_redo import property_stack_deco @@ -111,8 +112,14 @@ def add_phase_from_cif(self, cif_file): def add_phase_from_string(self, cif_string): phase = Phase.from_cif_string(cif_string) + # update the settings + if phase[0].space_group.setting is None: + group_number = phase[0].space_group.int_number + default_setting = get_default_it_coordinate_system_code_by_it_number(group_number) + phase[0].space_group.setting = default_setting + fixed_cif = phase.cif if self._interface is not None: - self._interface.updateModelCif(cif_string) + self._interface.updateModelCif(fixed_cif) for p in phase: self.phases.append(p) diff --git a/tests/integration_tests/fitting/test_fitting_pd-neut.py b/tests/functional_tests/fitting/test_fitting_pd-neut.py similarity index 94% rename from tests/integration_tests/fitting/test_fitting_pd-neut.py rename to tests/functional_tests/fitting/test_fitting_pd-neut.py index e96dd156..6685136e 100644 --- a/tests/integration_tests/fitting/test_fitting_pd-neut.py +++ b/tests/functional_tests/fitting/test_fitting_pd-neut.py @@ -62,9 +62,9 @@ def test_fitting_pd_neut_tof_Si_SEPD() -> None: phase.atom_sites.append( label='Si', type_symbol='Si', - fract_x=0, - fract_y=0, - fract_z=0, + fract_x=0.125, + fract_y=0.125, + fract_z=0.125, occupancy=1, b_iso_or_equiv=0.529, ) @@ -95,6 +95,8 @@ def test_fitting_pd_neut_tof_Si_SEPD() -> None: job.fit() + assert phase.space_group.name_hm_alt.raw_value == 'F d -3 m' + assert phase.space_group.it_coordinate_system_code.raw_value == '2' assert job.fitting_results.minimizer_engine.package == 'lmfit' assert job.fitting_results.x.size == 5600 assert job.fitting_results.n_pars == 12 diff --git a/tests/unit_tests/job/experiment/test_experiment.py b/tests/unit_tests/job/experiment/test_experiment.py index 242ce316..926fbda8 100644 --- a/tests/unit_tests/job/experiment/test_experiment.py +++ b/tests/unit_tests/job/experiment/test_experiment.py @@ -59,7 +59,7 @@ def test_add_experiment(setup_experiment): npt.assert_array_equal(add_coordinate_call[0][1], data[:, 0]) for j in range(1, len(data), 2): - var_name = f'test_job_exp2_I{j//2}' + var_name = f'test_job_exp2_I{j // 2}' add_variable_call = mock_datastore.store.easyscience.add_variable.call_args_list[j // 2] assert add_variable_call[0][0] == var_name assert add_variable_call[0][1] == [coord_name] diff --git a/tests/unit_tests/job/test_job.py b/tests/unit_tests/job/test_job.py index bbf8149b..e4dc0db7 100644 --- a/tests/unit_tests/job/test_job.py +++ b/tests/unit_tests/job/test_job.py @@ -18,7 +18,7 @@ def test_job_init(): j = Job() - assert j.name == 'Job' + assert j.name == 'sim_' assert isinstance(j.interface, WrapperFactory) assert isinstance(j.sample, Sample) assert isinstance(j.experiment, Experiment) @@ -33,7 +33,7 @@ def test_job_with_name(): def test_job_direct_import(): j = ed.Job() - assert j.name == 'Job' + assert j.name == 'sim_' def test_powder1dcw(): @@ -67,7 +67,7 @@ def test_job_tof(): assert not j.type.is_cwl assert j.type.is_pol assert isinstance(j.parameters, Instrument1DTOFParameters) - assert isinstance(j.sample.pattern, PolPowder1DParameters) + assert isinstance(j.experiment.pattern, PolPowder1DParameters) def test_get_job_from_file(): diff --git a/tools/linting_and_formatting.sh b/tools/linting_and_formatting.sh index 2fbbd933..bcdc1149 100755 --- a/tools/linting_and_formatting.sh +++ b/tools/linting_and_formatting.sh @@ -5,7 +5,7 @@ echo "\033[0;33m:::::: Check and fix code formatting\033[0m" ruff format . echo "\033[0;33m:::::: Check and fix notebook formatting\033[0m" -nbqa ruff examples/ +nbqa ruff examples/ --fix echo "\033[0;33m:::::: Check and fix non-code formatting\033[0m" npx prettier . --write --config=prettierrc.toml diff --git a/tools/run_notebooks.sh b/tools/run_notebooks.sh new file mode 100755 index 00000000..e6b00324 --- /dev/null +++ b/tools/run_notebooks.sh @@ -0,0 +1,6 @@ +echo "\033[0;33m:::::: Add src to pythonpath\033[0m" +export PYTHONPATH="${PWD}/src:${PYTHONPATH}" +echo "PYTHONPATH: ${PYTHONPATH}" + +echo "\033[0;33m:::::: Run Jupyter notebooks\033[0m" +pytest --nbmake examples/ --ignore-glob='examples/*emcee*' --nbmake-timeout=300 --color=yes -n=auto diff --git a/tools/run_tests.sh b/tools/run_tests.sh index 535e99ef..82c745c1 100755 --- a/tools/run_tests.sh +++ b/tools/run_tests.sh @@ -5,5 +5,8 @@ echo "PYTHONPATH: ${PYTHONPATH}" echo "\033[0;33m:::::: Run unit tests\033[0m" pytest tests/unit_tests/ --color=yes --disable-warnings +echo "\033[0;33m:::::: Run functional tests\033[0m" +pytest tests/functional_tests/ --color=yes --disable-warnings + echo "\033[0;33m:::::: Run integration tests\033[0m" pytest tests/integration_tests/ --color=yes --disable-warnings