From 4283200c88efaa0c29700b5a6bfe917060ec311c Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 2 Apr 2026 22:07:51 +0200 Subject: [PATCH 01/24] Replace uid_map with direct parameter references in aliases --- docs/docs/tutorials/ed-17.py | 4 +- docs/docs/tutorials/ed-3.py | 8 +- docs/docs/tutorials/ed-5.py | 4 +- .../analysis/categories/aliases/default.py | 81 ++++++++++++++----- src/easydiffraction/core/singleton.py | 74 +++-------------- src/easydiffraction/core/variable.py | 21 +---- ..._powder-diffraction_constant-wavelength.py | 8 +- .../analysis/categories/test_aliases.py | 16 +++- .../easydiffraction/core/test_singletons.py | 12 ++- 9 files changed, 103 insertions(+), 125 deletions(-) diff --git a/docs/docs/tutorials/ed-17.py b/docs/docs/tutorials/ed-17.py index af06031f..6e2a50ee 100644 --- a/docs/docs/tutorials/ed-17.py +++ b/docs/docs/tutorials/ed-17.py @@ -250,11 +250,11 @@ # %% project.analysis.aliases.create( label='biso_Co1', - param_uid=structure.atom_sites['Co1'].b_iso.uid, + param=structure.atom_sites['Co1'].b_iso, ) project.analysis.aliases.create( label='biso_Co2', - param_uid=structure.atom_sites['Co2'].b_iso.uid, + param=structure.atom_sites['Co2'].b_iso, ) # %% [markdown] diff --git a/docs/docs/tutorials/ed-3.py b/docs/docs/tutorials/ed-3.py index 23b60d88..5404f847 100644 --- a/docs/docs/tutorials/ed-3.py +++ b/docs/docs/tutorials/ed-3.py @@ -567,11 +567,11 @@ # %% project.analysis.aliases.create( label='biso_La', - param_uid=project.structures['lbco'].atom_sites['La'].b_iso.uid, + param=project.structures['lbco'].atom_sites['La'].b_iso, ) project.analysis.aliases.create( label='biso_Ba', - param_uid=project.structures['lbco'].atom_sites['Ba'].b_iso.uid, + param=project.structures['lbco'].atom_sites['Ba'].b_iso, ) # %% [markdown] @@ -636,11 +636,11 @@ # %% project.analysis.aliases.create( label='occ_La', - param_uid=project.structures['lbco'].atom_sites['La'].occupancy.uid, + param=project.structures['lbco'].atom_sites['La'].occupancy, ) project.analysis.aliases.create( label='occ_Ba', - param_uid=project.structures['lbco'].atom_sites['Ba'].occupancy.uid, + param=project.structures['lbco'].atom_sites['Ba'].occupancy, ) # %% [markdown] diff --git a/docs/docs/tutorials/ed-5.py b/docs/docs/tutorials/ed-5.py index 74e1d887..c87a964e 100644 --- a/docs/docs/tutorials/ed-5.py +++ b/docs/docs/tutorials/ed-5.py @@ -255,11 +255,11 @@ # %% project.analysis.aliases.create( label='biso_Co1', - param_uid=project.structures['cosio'].atom_sites['Co1'].b_iso.uid, + param=project.structures['cosio'].atom_sites['Co1'].b_iso, ) project.analysis.aliases.create( label='biso_Co2', - param_uid=project.structures['cosio'].atom_sites['Co2'].b_iso.uid, + param=project.structures['cosio'].atom_sites['Co2'].b_iso, ) # %% [markdown] diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index 7b1e0df0..ecc4a1c3 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -1,10 +1,12 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause """ -Alias category for mapping friendly names to parameter UIDs. +Alias category for mapping friendly names to parameters. Defines a small record type used by analysis configuration to refer to -parameters via readable labels instead of raw unique identifiers. +parameters via readable labels instead of opaque identifiers. At runtime +each alias holds a direct object reference to the parameter; for CIF +serialization the parameter's ``unique_name`` is stored. """ from __future__ import annotations @@ -23,8 +25,9 @@ class Alias(CategoryItem): """ Single alias entry. - Maps a human-readable ``label`` to a concrete ``param_uid`` used by - the engine. + Maps a human-readable ``label`` to a parameter object. The + ``param_unique_name`` descriptor stores the parameter's + ``unique_name`` for CIF serialization. """ def __init__(self) -> None: @@ -32,23 +35,27 @@ def __init__(self) -> None: self._label = StringDescriptor( name='label', - description='...', # TODO + description='Human-readable alias for a parameter.', value_spec=AttributeSpec( default='_', # TODO, Maybe None? validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'), ), cif_handler=CifHandler(names=['_alias.label']), ) - self._param_uid = StringDescriptor( - name='param_uid', - description='...', # TODO + self._param_unique_name = StringDescriptor( + name='param_unique_name', + description='Unique name of the referenced parameter.', value_spec=AttributeSpec( default='_', - validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'), + validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_.]*$'), ), - cif_handler=CifHandler(names=['_alias.param_uid']), + cif_handler=CifHandler(names=['_alias.param_unique_name']), ) + # Direct reference to the Parameter object (runtime only). + # Stored via object.__setattr__ to avoid parent-chain mutation. + object.__setattr__(self, '_param_ref', None) + self._identity.category_code = 'alias' self._identity.category_entry_name = lambda: str(self.label.value) @@ -59,7 +66,7 @@ def __init__(self) -> None: @property def label(self) -> StringDescriptor: """ - ... + Human-readable alias label (e.g. ``'biso_La'``). Reading this property returns the underlying ``StringDescriptor`` object. Assigning to it updates the @@ -72,19 +79,38 @@ def label(self, value: str) -> None: self._label.value = value @property - def param_uid(self) -> StringDescriptor: + def param(self) -> object | None: + """ + The referenced parameter object, or None before resolution. + """ + return self._param_ref + + @property + def param_unique_name(self) -> StringDescriptor: """ - ... + Unique name of the referenced parameter (for CIF). Reading this property returns the underlying - ``StringDescriptor`` object. Assigning to it updates the - parameter value. + ``StringDescriptor`` object. """ - return self._param_uid + return self._param_unique_name - @param_uid.setter - def param_uid(self, value: str) -> None: - self._param_uid.value = value + def _set_param(self, param: object) -> None: + """ + Store a direct reference to the parameter. + + Also updates ``param_unique_name`` from the parameter's + ``unique_name`` for CIF round-tripping. + """ + object.__setattr__(self, '_param_ref', param) + self._param_unique_name.value = param.unique_name + + @property + def parameters(self) -> list: + """ + Descriptors owned by this alias (excludes the param reference). + """ + return [self._label, self._param_unique_name] @AliasesFactory.register @@ -99,3 +125,20 @@ class Aliases(CategoryCollection): def __init__(self) -> None: """Create an empty collection of aliases.""" super().__init__(item_type=Alias) + + def create(self, *, label: str, param: object) -> None: + """ + Create a new alias mapping a label to a parameter. + + Parameters + ---------- + label : str + Human-readable alias name (e.g. ``'biso_La'``). + param : object + The parameter object to reference. + """ + item = Alias() + item.label = label + item._set_param(param) + self.add(item) + diff --git a/src/easydiffraction/core/singleton.py b/src/easydiffraction/core/singleton.py index 9d8a1d89..a4ac6b28 100644 --- a/src/easydiffraction/core/singleton.py +++ b/src/easydiffraction/core/singleton.py @@ -3,12 +3,9 @@ from typing import Any from typing import Self -from typing import TypeVar from asteval import Interpreter -T = TypeVar('T', bound='SingletonBase') - # ====================================================================== @@ -33,54 +30,6 @@ def get(cls) -> Self: # ====================================================================== -class UidMapHandler(SingletonBase): - """Global handler to manage UID-to-Parameter object mapping.""" - - def __init__(self) -> None: - # Internal map: uid (str) → Parameter instance - self._uid_map: dict[str, Any] = {} - - def get_uid_map(self) -> dict[str, Any]: - """Return the current UID-to-Parameter map.""" - return self._uid_map - - def add_to_uid_map(self, parameter: object) -> None: - """ - Add a single Parameter or Descriptor object to the UID map. - - Only Descriptor or Parameter instances are allowed (not - Components or others). - """ - from easydiffraction.core.variable import GenericDescriptorBase # noqa: PLC0415 - - if not isinstance(parameter, GenericDescriptorBase): - msg = ( - f'Cannot add object of type {type(parameter).__name__} to UID map. ' - 'Only Descriptor or Parameter instances are allowed.' - ) - raise TypeError(msg) - self._uid_map[parameter.uid] = parameter - - def replace_uid(self, old_uid: str, new_uid: str) -> None: - """ - Replace an existing UID key in the UID map with a new UID. - - Moves the associated parameter from old_uid to new_uid. Raises a - KeyError if the old_uid doesn't exist. - """ - if old_uid not in self._uid_map: - # Only raise if old_uid is not None and not empty - print('DEBUG: replace_uid failed', old_uid, 'current map:', list(self._uid_map.keys())) - msg = f"UID '{old_uid}' not found in the UID map." - raise KeyError(msg) - self._uid_map[new_uid] = self._uid_map.pop(old_uid) - - # TODO: Implement removing from the UID map - - -# ====================================================================== - - # TODO: Implement changing atrr '.constrained' back to False # when removing constraints class ConstraintsHandler(SingletonBase): @@ -94,7 +43,7 @@ class ConstraintsHandler(SingletonBase): def __init__(self) -> None: # Maps alias names - # (like 'biso_La') → ConstraintAlias(param=Parameter) + # (like 'biso_La') → Alias(param=Parameter) self._alias_to_param: dict[str, Any] = {} # Stores raw user-defined constraints indexed by lhs_alias @@ -106,7 +55,7 @@ def __init__(self) -> None: def set_aliases(self, aliases: object) -> None: """ - Set the alias map (name → parameter wrapper). + Set the alias map (name → alias wrapper). Called when user registers parameter aliases like: alias='biso_La', param=model.atom_sites['La'].b_iso @@ -137,25 +86,21 @@ def _parse_constraints(self) -> None: def apply(self) -> None: """ - Evaluate constraints and applies them to dependent parameters. + Evaluate constraints and apply them to dependent parameters. - For each constraint: - Evaluate RHS using current values of - aliases - Locate the dependent parameter by alias → uid → param + For each constraint: + - Evaluate RHS using current values of aliased parameters + - Locate the dependent parameter via direct alias reference - Update its value and mark it as constrained """ if not self._parsed_constraints: return # Nothing to apply - # Retrieve global UID → Parameter object map - uid_map = UidMapHandler.get().get_uid_map() - # Prepare a flat dict of {alias: value} for use in expressions param_values = {} for alias, alias_obj in self._alias_to_param.items(): - uid = alias_obj.param_uid.value - param = uid_map[uid] - value = param.value - param_values[alias] = value + param = alias_obj.param + param_values[alias] = param.value # Create an asteval interpreter for safe expression evaluation ae = Interpreter() @@ -167,8 +112,7 @@ def apply(self) -> None: rhs_value = ae(rhs_expr) # Get the actual parameter object we want to update - dependent_uid = self._alias_to_param[lhs_alias].param_uid.value - param = uid_map[dependent_uid] + param = self._alias_to_param[lhs_alias].param # Update its value and mark it as constrained param._set_value_constrained(rhs_value) diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index 2acce18d..9d18f455 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -3,15 +3,12 @@ from __future__ import annotations -import secrets -import string from typing import TYPE_CHECKING import numpy as np from easydiffraction.core.diagnostic import Diagnostics from easydiffraction.core.guard import GuardedBase -from easydiffraction.core.singleton import UidMapHandler from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.validation import DataTypes from easydiffraction.core.validation import RangeValidator @@ -287,9 +284,6 @@ def __init__( self._constrained_spec = self._BOOL_SPEC_TEMPLATE self._constrained = self._constrained_spec.default - self._uid: str = self._generate_uid() - UidMapHandler.get().add_to_uid_map(self) - def __str__(self) -> str: """Return string representation with uncertainty and free.""" s = GenericDescriptorBase.__str__(self) @@ -301,21 +295,10 @@ def __str__(self) -> str: s += f' (free={self.free})' return f'<{s}>' - @staticmethod - def _generate_uid(length: int = 16) -> str: - letters = string.ascii_lowercase - return ''.join(secrets.choice(letters) for _ in range(length)) - - @property - def uid(self) -> str: - """Stable random identifier for this descriptor.""" - return self._uid - @property def _minimizer_uid(self) -> str: - """Variant of uid that is safe for minimizer engines.""" - # return self.unique_name.replace('.', '__') - return self.uid + """Variant of unique_name that is safe for minimizer engines.""" + return self.unique_name.replace('.', '__') @property def constrained(self) -> bool: diff --git a/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py b/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py index 7a98c15d..ee3a984b 100644 --- a/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py +++ b/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py @@ -277,19 +277,19 @@ def test_single_fit_neutron_pd_cwl_lbco_with_constraints() -> None: # Set aliases for parameters project.analysis.aliases.create( label='biso_La', - param_uid=atom_sites['La'].b_iso.uid, + param=atom_sites['La'].b_iso, ) project.analysis.aliases.create( label='biso_Ba', - param_uid=atom_sites['Ba'].b_iso.uid, + param=atom_sites['Ba'].b_iso, ) project.analysis.aliases.create( label='occ_La', - param_uid=atom_sites['La'].occupancy.uid, + param=atom_sites['La'].occupancy, ) project.analysis.aliases.create( label='occ_Ba', - param_uid=atom_sites['Ba'].occupancy.uid, + param=atom_sites['Ba'].occupancy, ) # Set constraints diff --git a/tests/unit/easydiffraction/analysis/categories/test_aliases.py b/tests/unit/easydiffraction/analysis/categories/test_aliases.py index 2545218a..5efd265c 100644 --- a/tests/unit/easydiffraction/analysis/categories/test_aliases.py +++ b/tests/unit/easydiffraction/analysis/categories/test_aliases.py @@ -3,15 +3,25 @@ from easydiffraction.analysis.categories.aliases import Alias from easydiffraction.analysis.categories.aliases import Aliases +from easydiffraction.core.validation import AttributeSpec +from easydiffraction.core.variable import Parameter +from easydiffraction.io.cif.handler import CifHandler def test_alias_creation_and_collection(): + p1 = Parameter( + name='b_iso', + value_spec=AttributeSpec(default=0.5), + cif_handler=CifHandler(names=['_atom_site.b_iso']), + ) a = Alias() a.label = 'x' - a.param_uid = 'p1' + a._set_param(p1) assert a.label.value == 'x' + assert a.param is p1 coll = Aliases() - coll.create(label='x', param_uid='p1') + coll.create(label='x', param=p1) # Collections index by entry name; check via names or direct indexing assert 'x' in coll.names - assert coll['x'].param_uid.value == 'p1' + assert coll['x'].param is p1 + assert coll['x'].param_unique_name.value == p1.unique_name diff --git a/tests/unit/easydiffraction/core/test_singletons.py b/tests/unit/easydiffraction/core/test_singletons.py index ba69f07a..a68f76d8 100644 --- a/tests/unit/easydiffraction/core/test_singletons.py +++ b/tests/unit/easydiffraction/core/test_singletons.py @@ -1,12 +1,10 @@ # SPDX-FileCopyrightText: 2025 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -import pytest +from easydiffraction.core.singleton import ConstraintsHandler -def test_uid_map_handler_rejects_non_descriptor(): - from easydiffraction.core.singleton import UidMapHandler - - h = UidMapHandler.get() - with pytest.raises(TypeError): - h.add_to_uid_map(object()) +def test_constraints_handler_is_singleton(): + h1 = ConstraintsHandler.get() + h2 = ConstraintsHandler.get() + assert h1 is h2 From eb1202f31e72991e8079f8d88fe6ffdbafa36be3 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Thu, 2 Apr 2026 22:22:23 +0200 Subject: [PATCH 02/24] Auto-enable constraints on create, add enable/disable API --- docs/docs/tutorials/ed-17.py | 5 ---- docs/docs/tutorials/ed-3.py | 19 +------------ docs/docs/tutorials/ed-5.py | 5 ---- src/easydiffraction/analysis/analysis.py | 28 ++++++++----------- .../analysis/categories/aliases/default.py | 5 ++-- .../categories/constraints/default.py | 24 +++++++++++----- src/easydiffraction/core/variable.py | 13 ++++++--- ..._powder-diffraction_constant-wavelength.py | 3 -- 8 files changed, 40 insertions(+), 62 deletions(-) diff --git a/docs/docs/tutorials/ed-17.py b/docs/docs/tutorials/ed-17.py index 6e2a50ee..68be3ff0 100644 --- a/docs/docs/tutorials/ed-17.py +++ b/docs/docs/tutorials/ed-17.py @@ -265,11 +265,6 @@ expression='biso_Co2 = biso_Co1', ) -# %% [markdown] -# Apply constraints. - -# %% -project.analysis.apply_constraints() # %% [markdown] # #### Set Fit Mode diff --git a/docs/docs/tutorials/ed-3.py b/docs/docs/tutorials/ed-3.py index 5404f847..1a79d789 100644 --- a/docs/docs/tutorials/ed-3.py +++ b/docs/docs/tutorials/ed-3.py @@ -587,19 +587,7 @@ project.analysis.show_constraints() # %% [markdown] -# Show free parameters before applying constraints. - -# %% -project.analysis.show_free_params() - -# %% [markdown] -# Apply constraints. - -# %% -project.analysis.apply_constraints() - -# %% [markdown] -# Show free parameters after applying constraints. +# Show free parameters. # %% project.analysis.show_free_params() @@ -657,11 +645,6 @@ # %% project.analysis.show_constraints() -# %% [markdown] -# Apply constraints. - -# %% -project.analysis.apply_constraints() # %% [markdown] # Set structure parameters to be refined. diff --git a/docs/docs/tutorials/ed-5.py b/docs/docs/tutorials/ed-5.py index c87a964e..4e41a905 100644 --- a/docs/docs/tutorials/ed-5.py +++ b/docs/docs/tutorials/ed-5.py @@ -270,11 +270,6 @@ expression='biso_Co2 = biso_Co1', ) -# %% [markdown] -# Apply constraints. - -# %% -project.analysis.apply_constraints() # %% [markdown] # #### Run Fitting diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index c21db3e6..3a675381 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -563,16 +563,7 @@ def show_constraints(self) -> None: columns_alignment=['left'], columns_data=rows, ) - - def apply_constraints(self) -> None: - """Apply currently defined constraints to the project.""" - if not self.constraints._items: - log.warning('No constraints defined.') - return - - self.constraints_handler.set_aliases(self.aliases) - self.constraints_handler.set_constraints(self.constraints) - self.constraints_handler.apply() + console.print(f'Constraints enabled: {self.constraints.enabled}') def fit(self, verbosity: str | None = None) -> None: """ @@ -616,6 +607,11 @@ def fit(self, verbosity: str | None = None) -> None: log.warning('No experiments found in the project. Cannot run fit.') return + # Apply constraints before fitting so that constrained + # parameters are marked and excluded from the free parameter + # list built by the fitter. + self._update_categories() + # Run the fitting process mode = FitModeEnum(self._fit_mode.mode.value) if mode is FitModeEnum.JOINT: @@ -762,16 +758,14 @@ def _update_categories(self, called_by_minimizer: bool = False) -> None: called_by_minimizer : bool, default=False Whether this is called during fitting. """ + del called_by_minimizer + # Apply constraints to sync dependent parameters - if self.constraints._items: + if self.constraints.enabled and self.constraints._items: + self.constraints_handler.set_aliases(self.aliases) + self.constraints_handler.set_constraints(self.constraints) self.constraints_handler.apply() - # Update category-specific logic - # TODO: Need self.categories as in the case of datablock.py - for category in [self.aliases, self.constraints]: - if hasattr(category, '_update'): - category._update(called_by_minimizer=called_by_minimizer) - def as_cif(self) -> str: """ Serialize the analysis section to a CIF string. diff --git a/src/easydiffraction/analysis/categories/aliases/default.py b/src/easydiffraction/analysis/categories/aliases/default.py index ecc4a1c3..8aac2cdc 100644 --- a/src/easydiffraction/analysis/categories/aliases/default.py +++ b/src/easydiffraction/analysis/categories/aliases/default.py @@ -54,7 +54,7 @@ def __init__(self) -> None: # Direct reference to the Parameter object (runtime only). # Stored via object.__setattr__ to avoid parent-chain mutation. - object.__setattr__(self, '_param_ref', None) + object.__setattr__(self, '_param_ref', None) # noqa: PLC2801 self._identity.category_code = 'alias' self._identity.category_entry_name = lambda: str(self.label.value) @@ -102,7 +102,7 @@ def _set_param(self, param: object) -> None: Also updates ``param_unique_name`` from the parameter's ``unique_name`` for CIF round-tripping. """ - object.__setattr__(self, '_param_ref', param) + object.__setattr__(self, '_param_ref', param) # noqa: PLC2801 self._param_unique_name.value = param.unique_name @property @@ -141,4 +141,3 @@ def create(self, *, label: str, param: object) -> None: item.label = label item._set_param(param) self.add(item) - diff --git a/src/easydiffraction/analysis/categories/constraints/default.py b/src/easydiffraction/analysis/categories/constraints/default.py index 3bb1b77e..63a4264d 100644 --- a/src/easydiffraction/analysis/categories/constraints/default.py +++ b/src/easydiffraction/analysis/categories/constraints/default.py @@ -14,7 +14,6 @@ from easydiffraction.core.category import CategoryCollection from easydiffraction.core.category import CategoryItem from easydiffraction.core.metadata import TypeInfo -from easydiffraction.core.singleton import ConstraintsHandler from easydiffraction.core.validation import AttributeSpec from easydiffraction.core.validation import RegexValidator from easydiffraction.core.variable import StringDescriptor @@ -102,11 +101,27 @@ class Constraints(CategoryCollection): def __init__(self) -> None: """Create an empty constraints collection.""" super().__init__(item_type=Constraint) + self._enabled: bool = False + + @property + def enabled(self) -> bool: + """Whether constraints are currently active.""" + return self._enabled + + def enable(self) -> None: + """Activate constraints so they are applied during fitting.""" + self._enabled = True + + def disable(self) -> None: + """Deactivate constraints without deleting them.""" + self._enabled = False def create(self, *, expression: str) -> None: """ Create a constraint from an expression string. + Automatically enables constraints on the first call. + Parameters ---------- expression : str @@ -116,9 +131,4 @@ def create(self, *, expression: str) -> None: item = Constraint() item.expression = expression self.add(item) - - def _update(self, called_by_minimizer: bool = False) -> None: - del called_by_minimizer - - constraints = ConstraintsHandler.get() - constraints.apply() + self._enabled = True diff --git a/src/easydiffraction/core/variable.py b/src/easydiffraction/core/variable.py index 9d18f455..6d987fd1 100644 --- a/src/easydiffraction/core/variable.py +++ b/src/easydiffraction/core/variable.py @@ -309,12 +309,17 @@ def _set_value_constrained(self, v: object) -> None: """ Set the value from a constraint expression. - Validates against the spec, marks the parent datablock dirty, - and flags the parameter as constrained. Used exclusively by - ``ConstraintsHandler.apply()``. + Bypasses validation and marks the parent datablock dirty, like + ``_set_value_from_minimizer``, because constraints are applied + inside the minimizer loop where trial values may exceed + physical-range validators. Flags the parameter as constrained. + Used exclusively by ``ConstraintsHandler.apply()``. """ - self.value = v + self._value = v self._constrained = True + parent_datablock = self._datablock_item() + if parent_datablock is not None: + parent_datablock._need_categories_update = True @property def free(self) -> bool: diff --git a/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py b/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py index ee3a984b..2184fc07 100644 --- a/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py +++ b/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py @@ -296,9 +296,6 @@ def test_single_fit_neutron_pd_cwl_lbco_with_constraints() -> None: project.analysis.constraints.create(expression='biso_Ba = biso_La') project.analysis.constraints.create(expression='occ_Ba = 1 - occ_La') - # Apply constraints - project.analysis.apply_constraints() - # Perform fit project.analysis.fit() From 0a0385cef3dfa77c3f15452f2f6c1bab528f7f43 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 08:33:45 +0200 Subject: [PATCH 03/24] Implement Project.load() from CIF directory --- docs/architecture/architecture.md | 2 +- docs/architecture/issues_closed.md | 26 ++ docs/architecture/issues_open.md | 60 ++--- .../architecture/sequential_fitting_design.md | 222 ++++++++-------- src/easydiffraction/io/cif/serialize.py | 165 +++++++++++- src/easydiffraction/project/project.py | 122 ++++++++- .../integration/fitting/test_project_load.py | 238 ++++++++++++++++++ .../project/test_project_load.py | 142 +++++++++++ .../test_project_load_and_summary_wrap.py | 22 +- 9 files changed, 816 insertions(+), 183 deletions(-) create mode 100644 tests/integration/fitting/test_project_load.py create mode 100644 tests/unit/easydiffraction/project/test_project_load.py diff --git a/docs/architecture/architecture.md b/docs/architecture/architecture.md index f4c6fc52..d9827c07 100644 --- a/docs/architecture/architecture.md +++ b/docs/architecture/architecture.md @@ -188,7 +188,7 @@ GuardedBase └── GenericDescriptorBase # name, value (validated via AttributeSpec), description ├── GenericStringDescriptor # _value_type = DataTypes.STRING └── GenericNumericDescriptor # _value_type = DataTypes.NUMERIC, + units - └── GenericParameter # + free, uncertainty, fit_min, fit_max, constrained, uid + └── GenericParameter # + free, uncertainty, fit_min, fit_max, constrained ``` CIF-bound concrete classes add a `CifHandler` for serialisation: diff --git a/docs/architecture/issues_closed.md b/docs/architecture/issues_closed.md index a67edcbe..d6e14219 100644 --- a/docs/architecture/issues_closed.md +++ b/docs/architecture/issues_closed.md @@ -4,6 +4,32 @@ Issues that have been fully resolved. Kept for historical reference. --- +## Replace UID Map with Direct References and Auto-Apply Constraints + +**Resolution:** eliminated `UidMapHandler` and random UID generation +from parameters entirely. Aliases now store a direct object reference to +the parameter (`Alias._param_ref`) instead of a random UID string. +`ConstraintsHandler.apply()` uses the direct reference — no map lookup. +For CIF serialisation, `Alias._param_unique_name` stores the parameter's +deterministic `unique_name`. `_minimizer_uid` now returns +`unique_name.replace('.', '__')` instead of a random string. + +Also added `enable()`/`disable()` on `Constraints` with auto-enable on +`create()`, replacing the manual `apply_constraints()` call. +`Analysis._update_categories()` now always syncs handler state from the +current aliases and constraints when `constraints.enabled` is `True`, +eliminating stale-state bugs (former issue #4). `_set_value_constrained` +bypasses validation like `_set_value_from_minimizer` since constraints +run inside the minimiser loop. `Analysis.fit()` calls +`_update_categories()` before collecting free parameters so that +constrained parameters are correctly excluded. + +API change: `aliases.create(label=..., param_uid=...uid)` → +`aliases.create(label=..., param=...)`. `apply_constraints()` removed; +`constraints.create()` auto-enables. + +--- + ## Dirty-Flag Guard Was Disabled **Resolution:** added `_set_value_from_minimizer()` on diff --git a/docs/architecture/issues_open.md b/docs/architecture/issues_open.md index 05ed175f..b721a2b2 100644 --- a/docs/architecture/issues_open.md +++ b/docs/architecture/issues_open.md @@ -83,31 +83,6 @@ exactly match `project.experiments.names`. --- -## 4. 🔴 Refresh Constraint State Before Automatic Updates and Fitting - -**Type:** Correctness - -`ConstraintsHandler` is only synchronised from `analysis.aliases` and -`analysis.constraints` when the user explicitly calls -`project.analysis.apply_constraints()`. The normal fit / serialisation -path calls `constraints_handler.apply()` directly, so newly added or -edited aliases and constraints can be ignored until that manual sync -step happens. - -**Why high:** this produces silently incorrect results. A user can -define constraints, run a fit, and believe they were applied when the -active singleton still contains stale state from a previous run or no -state at all. - -**Fix:** before any automatic constraint application, always refresh the -singleton from the current `Aliases` and `Constraints` collections. The -sync should happen inside `Analysis._update_categories()` or inside the -constraints category itself, not only in a user-facing helper method. - -**Depends on:** nothing. - ---- - ## 5. 🟡 Make `Analysis` a `DatablockItem` **Type:** Consistency @@ -339,21 +314,20 @@ re-derivable default. ## Summary -| # | Issue | Severity | Type | -| --- | ------------------------------------------ | -------- | ----------------------- | -| 1 | Implement `Project.load()` | 🔴 High | Completeness | -| 2 | Restore minimiser variants | 🟡 Med | Feature loss | -| 3 | Rebuild joint-fit weights | 🟡 Med | Fragility | -| 4 | Refresh constraint state before auto-apply | 🔴 High | Correctness | -| 5 | `Analysis` as `DatablockItem` | 🟡 Med | Consistency | -| 6 | Restrict `data_type` switching | 🔴 High | Correctness/Data safety | -| 7 | Eliminate dummy `Experiments` | 🟡 Med | Fragility | -| 8 | Explicit `create()` signatures | 🟡 Med | API safety | -| 9 | Future enum extensions | 🟢 Low | Design | -| 10 | Unify update orchestration | 🟢 Low | Maintainability | -| 11 | Document `_update` contract | 🟢 Low | Maintainability | -| 12 | CIF round-trip integration test | 🟢 Low | Quality | -| 13 | Suppress redundant dirty-flag sets | 🟢 Low | Performance | -| 14 | Finer-grained change tracking | 🟢 Low | Performance | -| 15 | Validate joint-fit weights | 🟡 Med | Correctness | -| 16 | Persist per-experiment `calculator_type` | 🟡 Med | Completeness | +| # | Issue | Severity | Type | +| --- | ---------------------------------------- | -------- | ----------------------- | +| 1 | Implement `Project.load()` | 🔴 High | Completeness | +| 2 | Restore minimiser variants | 🟡 Med | Feature loss | +| 3 | Rebuild joint-fit weights | 🟡 Med | Fragility | +| 5 | `Analysis` as `DatablockItem` | 🟡 Med | Consistency | +| 6 | Restrict `data_type` switching | 🔴 High | Correctness/Data safety | +| 7 | Eliminate dummy `Experiments` | 🟡 Med | Fragility | +| 8 | Explicit `create()` signatures | 🟡 Med | API safety | +| 9 | Future enum extensions | 🟢 Low | Design | +| 10 | Unify update orchestration | 🟢 Low | Maintainability | +| 11 | Document `_update` contract | 🟢 Low | Maintainability | +| 12 | CIF round-trip integration test | 🟢 Low | Quality | +| 13 | Suppress redundant dirty-flag sets | 🟢 Low | Performance | +| 14 | Finer-grained change tracking | 🟢 Low | Performance | +| 15 | Validate joint-fit weights | 🟡 Med | Correctness | +| 16 | Persist per-experiment `calculator_type` | 🟡 Med | Completeness | diff --git a/docs/architecture/sequential_fitting_design.md b/docs/architecture/sequential_fitting_design.md index fd10a104..16f01634 100644 --- a/docs/architecture/sequential_fitting_design.md +++ b/docs/architecture/sequential_fitting_design.md @@ -845,10 +845,11 @@ here. `analysis/` directory. All analysis artifacts (settings + results) live under one directory. See § 5.4 and § 9.6. -11. **Singletons (`UidMapHandler`, `ConstraintsHandler`)** → replace - with instance-owned state on `Project` and `Analysis`. Fixes - notebook rerun issues, simplifies worker isolation, resolves issue - #4. See § 9.5. +11. **Singletons (`UidMapHandler`, `ConstraintsHandler`)** → + `UidMapHandler` eliminated (aliases use direct references + + `unique_name`). `ConstraintsHandler` stays singleton but is now + always synced before use. Fixes notebook rerun issues, resolves + issue #4. See § 9.5. --- @@ -857,19 +858,16 @@ here. These changes are needed before implementing `fit_sequential()` itself. Each is a separate, atomic change. -### 9.1 Switch alias `param_uid` to `param_unique_name` +### 9.1 Switch alias `param_uid` to `param_unique_name` ✅ -The `Alias` category currently stores `param_uid` (random UID). Change -to `param_unique_name` (deterministic `unique_name`). Update: - -- `Alias._param_uid` → `Alias._param_unique_name` -- `CifHandler(names=['_alias.param_uid'])` → - `CifHandler(names=['_alias.param_unique_name'])` -- `ConstraintsHandler` to resolve via `unique_name` lookup instead of - UID lookup. -- `UidMapHandler` — may no longer be needed for constraint resolution - (but still used for other purposes). -- Tutorial `ed-17.py` and any tests that create aliases. +**Done.** Went further than planned: eliminated `UidMapHandler` and +random UIDs entirely. Aliases now store a direct object reference to the +parameter (`Alias._param_ref`, runtime) plus `Alias._param_unique_name` +(`StringDescriptor`, CIF serialisation with tag +`_alias.param_unique_name`). `ConstraintsHandler.apply()` uses the +direct reference — no map lookup needed. `_minimizer_uid` returns +`unique_name.replace('.', '__')` instead of a random string. All +tutorials, tests, and call sites updated. ### 9.2 Fix `category_collection_to_cif` truncation @@ -901,7 +899,7 @@ Currently extracts to a temp dir. Add optional `destination` parameter to extract to a user-specified directory, enabling a clean two-step workflow (extract → fit_sequential). -### 9.5 Replace singletons with instance-owned state +### 9.5 Replace singletons with instance-owned state (partially done) #### Problem @@ -927,53 +925,34 @@ workflow (extract → fit_sequential). the same session (e.g. to compare fits), their constraints and UID maps collide in the shared singleton. -#### Proposed fix - -Move the state owned by singletons into `Analysis` (for constraints) and -`Project` (for the UID map): - -| Current singleton | New owner | Lifetime | -| -------------------- | ------------------------------ | ----------------------- | -| `ConstraintsHandler` | `Analysis._constraints_engine` | Per-`Analysis` instance | -| `UidMapHandler` | `Project._uid_map` | Per-`Project` instance | - -The objects are the same classes, just no longer singletons — they are -instantiated in `__init__` and passed explicitly to the components that -need them (e.g. `Parameter.__init__` receives a `uid_map` reference from -its owning project, `ConstraintsHandler` is accessed via -`self.project.analysis._constraints_engine`). - -#### Impact on sequential fitting +#### Current status -- **Simplifies workers:** each worker's `Project()` naturally creates - its own `_uid_map` and `_constraints_engine`. No singleton isolation - concern at all. -- **Simplifies crash recovery and notebook reruns:** creating a new - `Project` starts with a blank slate, no stale state leaks. -- **No impact on the `fit_sequential` API** — the change is purely - internal. +**`UidMapHandler`: eliminated entirely.** Random UIDs and the global +UID-to-Parameter map have been removed. Aliases store direct object +references at runtime and deterministic `unique_name` strings for CIF +serialisation. This fully resolves problems 1–3 for the UID map. -#### Scope and sequencing +**`ConstraintsHandler`: stale-state bug fixed, still a singleton.** +`Analysis._update_categories()` now always syncs the handler from the +current `aliases` and `constraints` before calling `apply()`. This +resolves problem 1 (notebook reruns) and problem 2 (worker isolation is +natural with `spawn`). Problem 3 (multiple projects) remains theoretical +— if multi-project support becomes a real need, moving +`ConstraintsHandler` to instance scope is a standalone follow-up. -This is a self-contained refactor that can be done independently of -sequential fitting. It improves correctness for existing workflows -(notebook reruns, issue #4) and simplifies the sequential fitting -implementation. It is listed as a prerequisite because it eliminates a -class of bugs that would otherwise need workaround code in the worker. +#### Remaining work (optional) -However, if the refactor proves too large for the initial sequential -fitting work, the `spawn`-based multiprocessing provides natural -isolation and the singletons can be addressed in a follow-up. The -sequential fitting design does **not** depend on this change — it works -either way. +Move `ConstraintsHandler` from singleton to per-`Analysis` instance. +This only matters for the multiple-projects edge case. The sequential +fitting design does **not** depend on this change. #### Relationship to issue #4 -Open issue #4 ("Refresh constraint state before auto-apply") is a -symptom of the singleton problem. If constraints are instance-owned, -there is no stale state to refresh — the constraint engine always -reflects the current `Analysis` instance's aliases and constraints. -Fixing the singleton issue resolves issue #4 as a side effect. +Issue #4 ("Refresh constraint state before auto-apply") is **fully +resolved.** `_update_categories()` syncs handler state on every call. +Constraints auto-enable on `create()` and are applied before fitting +starts. The manual `apply_constraints()` method has been removed. Fixing +the singleton issue resolves issue #4 as a side effect. ### 9.6 Move `analysis.cif` into `analysis/` directory @@ -1017,30 +996,28 @@ Fixing it now gives the worker a clean `Fitter.fit(structures, [experiment])` call without any collection ceremony. -#### PR 2 — Replace singletons with instance-owned state (issue #4 + § 9.5) +#### PR 2 — Replace UID map with direct references and auto-apply constraints (issue #4 + § 9.5) ✅ > **Title:** -> `Move ConstraintsHandler and UidMapHandler to instance scope` +> `Replace UID map with direct references and auto-apply constraints` > -> **Description:** Replace the `SingletonBase` pattern for -> `ConstraintsHandler` and `UidMapHandler` with per-project instances. -> `Project.__init__` creates `_uid_map`; `Analysis.__init__` creates -> `_constraints_engine`. Thread the references through to `Parameter` -> and constraint resolution. Remove `SingletonBase` class if no longer -> used. Update all call sites that use `.get()`. This also fixes issue -> #4 (stale constraint state) as a side effect — the constraint engine -> is always in sync with its owning `Analysis`. - -**Why second:** removes the global mutable state that makes notebook -reruns unreliable and multi-project sessions impossible. Sequential -fitting workers benefit from natural isolation (each `Project()` has its -own engine), but the main benefit is correctness for existing workflows. - -This is a sub-step breakdown if the PR proves too large: - -- **PR 2a:** `Move UidMapHandler to Project instance scope` -- **PR 2b:** `Move ConstraintsHandler to Analysis instance scope` -- **PR 2c:** `Remove SingletonBase if unused` +> **Description:** Eliminated `UidMapHandler` and random UID generation +> entirely. Aliases store direct parameter object references at runtime +> and deterministic `unique_name` strings for CIF. Added +> `enable()`/`disable()` on `Constraints` with auto-enable on +> `create()`, replacing the manual `apply_constraints()` call. +> `Analysis._update_categories()` always syncs handler state when +> constraints are enabled. Also fixes issue #4 (stale constraint state) +> and completes PR 4 (alias `param_unique_name`). + +**Why second:** removes the global UID map that made constraint +resolution opaque and fragile. The stale-state bug (issue #4) is fully +fixed. `ConstraintsHandler` remains a singleton but is now always in +sync — moving it to instance scope is an optional follow-up for the +multi-project edge case. + +This PR also absorbed PR 4 (§ 9.1) since switching from random UIDs to +`unique_name` was a natural part of the same change. #### PR 3 — Implement Project.load() (issue #1) @@ -1061,15 +1038,10 @@ to fix any serialisation gaps before they become worker bugs. Phase 3 ### Sequential-fitting prerequisite PRs -#### PR 4 — Switch alias param_uid to param_unique_name (§ 9.1) +#### PR 4 — Switch alias param_uid to param_unique_name (§ 9.1) ✅ -> **Title:** `Use unique_name instead of random UID in aliases` -> -> **Description:** Rename `Alias._param_uid` to -> `Alias._param_unique_name`. Update `CifHandler` names. Change -> `ConstraintsHandler` to resolve parameters via `unique_name` lookup -> instead of UID. Update `ed-17.py` tutorial and all tests that create -> aliases. +> Absorbed into PR 2. Aliases now use `param_unique_name` with direct +> object references. All tutorials and tests updated. #### PR 5 — Fix CIF collection truncation (§ 9.2) @@ -1186,24 +1158,23 @@ This is a sub-step breakdown if the PR proves too large: ``` PR 1 (issue #7: eliminate dummy Experiments) - └─► PR 2 (issue #4: singletons → instance-owned) + └─► PR 2 (issue #4: UID map + constraints) ✅ └─► PR 3 (issue #1: Project.load) - └─► PR 4 (alias unique_name) - └─► PR 5 (CIF truncation) - └─► PR 6 (CIF round-trip test) - ├─► PR 7 (analysis.cif → analysis/) - │ └─► PR 9 (streaming sequential fit) - │ ├─► PR 10 (plot from CSV) - │ │ └─► PR 13 (CSV for existing fit) - │ └─► PR 11 (parallel fitting) - │ └─► PR 14 (optional: parallel fit()) - └─► PR 8 (zip destination) - └─► PR 12 (dataset replay) + └─► PR 5 (CIF truncation) + └─► PR 6 (CIF round-trip test) + ├─► PR 7 (analysis.cif → analysis/) + │ └─► PR 9 (streaming sequential fit) + │ ├─► PR 10 (plot from CSV) + │ │ └─► PR 13 (CSV for existing fit) + │ └─► PR 11 (parallel fitting) + │ └─► PR 14 (optional: parallel fit()) + └─► PR 8 (zip destination) + └─► PR 12 (dataset replay) ``` -Note: PRs 4–8 are largely independent of each other and can be -parallelised or reordered as long as PRs 1–3 are done first and PRs 4–6 -are done before PR 9. +Note: PR 4 was absorbed into PR 2. PRs 5–8 are largely independent of +each other and can be parallelised or reordered as long as PRs 1–3 are +done first and PRs 5–6 are done before PR 9. --- @@ -1228,31 +1199,38 @@ are all stdlib. - **Issue #7 (dummy Experiments wrapper):** resolved in PR 1. The worker uses the clean `Fitter.fit(structures, [experiment])` API. -- **Issue #4 (constraint refresh) + § 9.5 (singletons):** resolved in - PR 2. Instance-owned constraint engine eliminates stale state. +- **Issue #4 (constraint refresh) + § 9.1 (alias unique_name) + § 9.5 + (singletons):** resolved in PR 2. `UidMapHandler` eliminated; aliases + use direct object references and deterministic `unique_name` for CIF; + `_update_categories()` always syncs handler state; constraints + auto-enable on `create()`. `ConstraintsHandler` remains a singleton + but is always in sync — multi-project isolation is an optional + follow-up. - **Issue #1 (Project.load):** resolved in PR 3. CIF round-trip reliability is proven before workers depend on it. Dataset replay - (PR 12) uses `load()` directly. + (PR 12) uses `load()` directly. Note: `Project.load()` must now + resolve `_alias.param_unique_name` strings back to `Parameter` objects + by building a temporary `unique_name → Parameter` map. --- ## 12. Summary -| Aspect | Decision | -| ------------------- | --------------------------------------------------------------------- | -| Parallelism backend | `concurrent.futures.ProcessPoolExecutor` with `spawn` | -| Worker isolation | Each worker creates a fresh `Project` — no shared state | -| Data source | `data_dir` argument; ZIP → extract first | -| Data flow | Template CIF + data path → worker → result dict → CSV | -| Parameter IDs | `unique_name` (deterministic), not `uid` (random) | -| Parameter seeding | Last successful result in chunk → next chunk | -| CSV location | `project_dir/analysis/results.csv` (deterministic) | -| CSV contents | Fit metrics + diffrn metadata + all free param values/uncert | -| Metadata extraction | User-provided `extract_diffrn` callback, not hidden in lib | -| Crash recovery | Read existing CSV, skip fitted files, resume | -| Plotting | Unified `plot_param_series()` always reads from CSV | -| Configuration | `max_workers` + `data_dir` on `fit_sequential()` | -| Project layout | `analysis.cif` moves into `analysis/` directory | -| Singletons | Replace with instance-owned state (recommended prerequisite) | -| New dependencies | None (stdlib only) | -| First step | PRs 1–3 (foundation issues), then PRs 4–8 (prerequisites), then PR 9+ | +| Aspect | Decision | +| ------------------- | ---------------------------------------------------------------------------------- | +| Parallelism backend | `concurrent.futures.ProcessPoolExecutor` with `spawn` | +| Worker isolation | Each worker creates a fresh `Project` — no shared state | +| Data source | `data_dir` argument; ZIP → extract first | +| Data flow | Template CIF + data path → worker → result dict → CSV | +| Parameter IDs | `unique_name` (deterministic), not `uid` (random) | +| Parameter seeding | Last successful result in chunk → next chunk | +| CSV location | `project_dir/analysis/results.csv` (deterministic) | +| CSV contents | Fit metrics + diffrn metadata + all free param values/uncert | +| Metadata extraction | User-provided `extract_diffrn` callback, not hidden in lib | +| Crash recovery | Read existing CSV, skip fitted files, resume | +| Plotting | Unified `plot_param_series()` always reads from CSV | +| Configuration | `max_workers` + `data_dir` on `fit_sequential()` | +| Project layout | `analysis.cif` moves into `analysis/` directory | +| Singletons | `UidMapHandler` eliminated; `ConstraintsHandler` stays singleton but always synced | +| New dependencies | None (stdlib only) | +| First step | PRs 1–3 (foundation issues), then PRs 4–8 (prerequisites), then PR 9+ | diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index 42a215eb..89655c64 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -35,9 +35,15 @@ def format_value(value: object) -> str: # Converting + # None → CIF unknown marker + if value is None: + value = '?' # Convert ints to floats - if isinstance(value, int): + elif isinstance(value, int): value = float(value) + # Empty strings → CIF unknown marker + elif isinstance(value, str) and not value.strip(): + value = '?' # Strings with whitespace are quoted elif isinstance(value, str) and (' ' in value or '\t' in value): value = f'"{value}"' @@ -83,12 +89,22 @@ def category_item_to_cif(item: object) -> str: def category_collection_to_cif( collection: object, - max_display: int | None = 20, + max_display: int | None = None, ) -> str: """ Render a CategoryCollection-like object to CIF text. - Uses first item to build loop header, then emits rows for each item. + Uses first item to build loop header, then emits rows for each + item. + + Parameters + ---------- + collection : object + A ``CategoryCollection``-like object. + max_display : int | None, default=None + When set to a positive integer, truncate the output to at most + this many rows (half from the start, half from the end) with an + ``...`` separator. ``None`` emits all rows. """ if not len(collection): return '' @@ -104,7 +120,7 @@ def category_collection_to_cif( # Rows # Limit number of displayed rows if requested - if len(collection) > max_display: + if max_display is not None and len(collection) > max_display: half_display = max_display // 2 for i in range(half_display): item = list(collection.values())[i] @@ -161,10 +177,12 @@ def project_info_to_cif(info: object) -> str: if len(info.description) > 60: description = f'\n;\n{info.description}\n;' - else: + elif info.description: description = f'{info.description}' if ' ' in description: description = f"'{description}'" + else: + description = '?' created = f"'{info._created.strftime('%d %b %Y %H:%M:%S')}'" last_modified = f"'{info._last_modified.strftime('%d %b %Y %H:%M:%S')}'" @@ -221,6 +239,135 @@ def summary_to_cif(_summary: object) -> str: return 'To be added...' +def _wrap_in_data_block(cif_text: str, block_name: str = '_') -> str: + """ + Wrap bare CIF key-value pairs in a ``data_`` block header. + + Parameters + ---------- + cif_text : str + CIF text without a ``data_`` header. + block_name : str, default='_' + Name for the CIF data block. + + Returns + ------- + str + CIF text with a ``data_`` header prepended. + """ + return f'data_{block_name}\n\n{cif_text}' + + +def project_info_from_cif(info: object, cif_text: str) -> None: + """ + Populate a ProjectInfo instance from CIF text. + + Reads ``_project.id``, ``_project.title``, and + ``_project.description`` from the given CIF string and sets them on + the *info* object. + + Parameters + ---------- + info : object + The ``ProjectInfo`` instance to populate. + cif_text : str + CIF text content of ``project.cif``. + """ + import gemmi # noqa: PLC0415 + + doc = gemmi.cif.read_string(_wrap_in_data_block(cif_text, 'project')) + block = doc.sole_block() + + _read_cif_string = _make_cif_string_reader(block) + + name = _read_cif_string('_project.id') + if name is not None: + info.name = name + + title = _read_cif_string('_project.title') + if title is not None: + info.title = title + + description = _read_cif_string('_project.description') + if description is not None: + info.description = description + + +def analysis_from_cif(analysis: object, cif_text: str) -> None: + """ + Populate an Analysis instance from CIF text. + + Reads the fitting engine, fit mode, aliases, constraints, and + joint-fit experiment weights from the given CIF string. + + Parameters + ---------- + analysis : object + The ``Analysis`` instance to populate. + cif_text : str + CIF text content of ``analysis.cif``. + """ + import gemmi # noqa: PLC0415 + + doc = gemmi.cif.read_string(_wrap_in_data_block(cif_text, 'analysis')) + block = doc.sole_block() + + _read_cif_string = _make_cif_string_reader(block) + + # Restore minimizer selection + engine = _read_cif_string('_analysis.fitting_engine') + if engine is not None: + from easydiffraction.analysis.fitting import Fitter # noqa: PLC0415 + + analysis.fitter = Fitter(engine) + + # Restore fit mode + analysis.fit_mode.from_cif(block) + + # Restore aliases (loop) + analysis.aliases.from_cif(block) + + # Restore constraints (loop) + analysis.constraints.from_cif(block) + if analysis.constraints._items: + analysis.constraints.enable() + + # Restore joint-fit experiment weights (loop) + analysis._joint_fit_experiments.from_cif(block) + + +def _make_cif_string_reader(block: gemmi.cif.Block) -> object: + """ + Return a helper that reads a single CIF tag as a stripped string. + + Parameters + ---------- + block : gemmi.cif.Block + Parsed CIF data block. + + Returns + ------- + object + A function ``(tag) -> str | None`` that returns the unquoted + value for *tag*, or ``None`` if not found. + """ + + def _read(tag: str) -> str | None: + vals = list(block.find_values(tag)) + if not vals: + return None + raw = vals[0] + # CIF unknown / inapplicable markers + if raw in ('?', '.'): + return None + # Strip surrounding quotes + if len(raw) >= 2 and raw[0] == raw[-1] and raw[0] in {"'", '"'}: + raw = raw[1:-1] + return raw + + return _read + + # TODO: Check the following methods: ###################### @@ -262,6 +409,10 @@ def param_from_cif( # If found, pick the one at the given index raw = found_values[idx] + # CIF unknown / inapplicable markers → keep default + if raw in ('?', '.'): + return + # If numeric, parse with uncertainty if present if self._value_type == DataTypes.NUMERIC: u = str_to_ufloat(raw) @@ -363,6 +514,10 @@ def _get_loop(block: object, category_item: object) -> object | None: # param_from_cif raw = array[row_idx][col_idx] + # CIF unknown / inapplicable markers → keep default + if raw in ('?', '.'): + break + # If numeric, parse with uncertainty if present if param._value_type == DataTypes.NUMERIC: u = str_to_ufloat(raw) diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index 42bb71a0..06e36d14 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -2,6 +2,8 @@ # SPDX-License-Identifier: BSD-3-Clause """Project facade to orchestrate models, experiments, and analysis.""" +from __future__ import annotations + import pathlib import tempfile @@ -32,6 +34,9 @@ class Project(GuardedBase): # ------------------------------------------------------------------ # Initialization # ------------------------------------------------------------------ + # Class-level sentinel: True while load() is constructing a project. + _loading: bool = False + def __init__( self, name: str = 'untitled_project', @@ -48,7 +53,7 @@ def __init__( self._analysis = Analysis(self) self._summary = Summary(self) self._saved = False - self._varname = varname() + self._varname = 'project' if type(self)._loading else varname() self._verbosity: VerbosityEnum = VerbosityEnum.FULL # ------------------------------------------------------------------ @@ -172,15 +177,118 @@ def verbosity(self, value: str) -> None: # Project File I/O # ------------------------------------------ - def load(self, dir_path: str) -> None: + @classmethod + def load(cls, dir_path: str) -> Project: """ - Load a project from a given directory. + Load a project from a saved directory. + + Reads ``project.cif``, ``structures/*.cif``, + ``experiments/*.cif``, and ``analysis.cif`` from *dir_path* and + reconstructs the full project state. + + Parameters + ---------- + dir_path : str + Path to the project directory previously created by + :meth:`save_as`. + + Returns + ------- + Project + A fully reconstructed project instance. - Loads project info, structures, experiments, etc. + Raises + ------ + FileNotFoundError + If *dir_path* does not exist. """ - # TODO: load project components from files inside dir_path - msg = 'Project.load() is not implemented yet.' - raise NotImplementedError(msg) + from easydiffraction.io.cif.serialize import analysis_from_cif # noqa: PLC0415 + from easydiffraction.io.cif.serialize import project_info_from_cif # noqa: PLC0415 + + project_path = pathlib.Path(dir_path) + if not project_path.is_dir(): + msg = f"Project directory not found: '{dir_path}'" + raise FileNotFoundError(msg) + + # Create a minimal project. + # Use _loading sentinel to skip varname() inside __init__. + cls._loading = True + try: + project = cls() + finally: + cls._loading = False + project._saved = True + + # 1. Load project info + project_cif_path = project_path / 'project.cif' + if project_cif_path.is_file(): + cif_text = project_cif_path.read_text() + project_info_from_cif(project._info, cif_text) + + project._info.path = project_path + + # 2. Load structures + structures_dir = project_path / 'structures' + if structures_dir.is_dir(): + for cif_file in sorted(structures_dir.glob('*.cif')): + project._structures.add_from_cif_path(str(cif_file)) + + # 3. Load experiments + experiments_dir = project_path / 'experiments' + if experiments_dir.is_dir(): + for cif_file in sorted(experiments_dir.glob('*.cif')): + project._experiments.add_from_cif_path(str(cif_file)) + + # 4. Load analysis + # Check analysis/analysis.cif first (future layout), then + # fall back to analysis.cif at root (current layout). + analysis_cif_path = project_path / 'analysis' / 'analysis.cif' + if not analysis_cif_path.is_file(): + analysis_cif_path = project_path / 'analysis.cif' + if analysis_cif_path.is_file(): + cif_text = analysis_cif_path.read_text() + analysis_from_cif(project._analysis, cif_text) + + # 5. Resolve alias param references + project._resolve_alias_references() + + # 6. Apply symmetry constraints and update categories + for structure in project._structures: + structure._update_categories() + + log.info(f"Project '{project.name}' loaded from '{dir_path}'.") + return project + + def _resolve_alias_references(self) -> None: + """ + Resolve alias ``param_unique_name`` strings to live objects. + + After loading structures and experiments from CIF, aliases only + contain the ``param_unique_name`` string. This method builds a + ``{unique_name: param}`` map from all project parameters and + wires each alias's ``_param_ref``. + """ + aliases = self._analysis.aliases + if not aliases._items: + return + + # Build unique_name → parameter map + all_params = self._structures.parameters + self._experiments.parameters + param_map: dict[str, object] = {} + for p in all_params: + uname = getattr(p, 'unique_name', None) + if uname is not None: + param_map[uname] = p + + for alias in aliases: + uname = alias.param_unique_name.value + if uname in param_map: + alias._set_param(param_map[uname]) + else: + log.warning( + f"Alias '{alias.label.value}' references unknown " + f"parameter '{uname}'. Reference not resolved." + ) def save(self) -> None: """Save the project into the existing project directory.""" diff --git a/tests/integration/fitting/test_project_load.py b/tests/integration/fitting/test_project_load.py new file mode 100644 index 00000000..3598ed69 --- /dev/null +++ b/tests/integration/fitting/test_project_load.py @@ -0,0 +1,238 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Integration tests for Project save → load round-trip.""" + +from __future__ import annotations + +import tempfile + +from numpy.testing import assert_almost_equal + +from easydiffraction import ExperimentFactory +from easydiffraction import Project +from easydiffraction import StructureFactory +from easydiffraction import download_data + +TEMP_DIR = tempfile.gettempdir() + + +# ------------------------------------------------------------------ +# Helpers +# ------------------------------------------------------------------ + + +def _create_lbco_project() -> Project: + """ + Build a complete LBCO project ready for fitting. + + Returns a project with one structure, one experiment (with data), + instrument settings, peak profile, background, linked phases, free + parameters, aliases, and constraints. + """ + # Structure + model = StructureFactory.from_scratch(name='lbco') + model.space_group.name_h_m = 'P m -3 m' + model.cell.length_a = 3.8909 + model.atom_sites.create( + label='La', + type_symbol='La', + fract_x=0, + fract_y=0, + fract_z=0, + wyckoff_letter='a', + occupancy=0.5, + b_iso=0.5, + ) + model.atom_sites.create( + label='Ba', + type_symbol='Ba', + fract_x=0, + fract_y=0, + fract_z=0, + wyckoff_letter='a', + occupancy=0.5, + b_iso=0.5, + ) + model.atom_sites.create( + label='Co', + type_symbol='Co', + fract_x=0.5, + fract_y=0.5, + fract_z=0.5, + wyckoff_letter='b', + b_iso=0.5, + ) + model.atom_sites.create( + label='O', + type_symbol='O', + fract_x=0, + fract_y=0.5, + fract_z=0.5, + wyckoff_letter='c', + b_iso=0.5, + ) + + # Experiment + data_path = download_data(id=3, destination=TEMP_DIR) + expt = ExperimentFactory.from_data_path( + name='hrpt', + data_path=data_path, + ) + expt.instrument.setup_wavelength = 1.494 + expt.instrument.calib_twotheta_offset = 0.6225 + expt.peak.broad_gauss_u = 0.0834 + expt.peak.broad_gauss_v = -0.1168 + expt.peak.broad_gauss_w = 0.123 + expt.peak.broad_lorentz_x = 0 + expt.peak.broad_lorentz_y = 0.0797 + expt.background.create(id='1', x=10, y=170) + expt.background.create(id='2', x=165, y=170) + expt.linked_phases.create(id='lbco', scale=9.0) + + # Project assembly + project = Project(name='lbco_project') + project.structures.add(model) + project.experiments.add(expt) + + # Free parameters + model.cell.length_a.free = True + expt.linked_phases['lbco'].scale.free = True + expt.instrument.calib_twotheta_offset.free = True + expt.background['1'].y.free = True + expt.background['2'].y.free = True + + # Aliases and constraints + project.analysis.aliases.create( + label='biso_La', + param=model.atom_sites['La'].b_iso, + ) + project.analysis.aliases.create( + label='biso_Ba', + param=model.atom_sites['Ba'].b_iso, + ) + project.analysis.constraints.create(expression='biso_Ba = biso_La') + + return project + + +def _collect_param_snapshot(project: Project) -> dict[str, float]: + """Return ``{unique_name: value}`` for all project parameters.""" + return {p.unique_name: p.value for p in project.parameters} + + +def _collect_free_flags(project: Project) -> dict[str, bool]: + """Return ``{unique_name: free}`` for fittable parameters.""" + from easydiffraction.core.variable import Parameter # noqa: PLC0415 + + return {p.unique_name: p.free for p in project.parameters if isinstance(p, Parameter)} + + +# ------------------------------------------------------------------ +# Test 1: save → load → compare all parameters +# ------------------------------------------------------------------ + + +def test_save_load_round_trip_preserves_parameters(tmp_path) -> None: + """ + Every parameter value must survive a save → load cycle. + + Also verifies project info, free flags, aliases, and constraints. + """ + original = _create_lbco_project() + original_params = _collect_param_snapshot(original) + original_free = _collect_free_flags(original) + + # Save + proj_dir = str(tmp_path / 'lbco_project') + original.save_as(proj_dir) + + # Load + loaded = Project.load(proj_dir) + + # Compare project info + assert loaded.name == original.name + assert loaded.info.title == original.info.title + + # Compare structures + assert loaded.structures.names == original.structures.names + orig_s = original.structures['lbco'] + load_s = loaded.structures['lbco'] + assert load_s.space_group.name_h_m.value == orig_s.space_group.name_h_m.value + assert_almost_equal(load_s.cell.length_a.value, orig_s.cell.length_a.value, decimal=6) + assert len(load_s.atom_sites) == len(orig_s.atom_sites) + + # Compare experiments + assert loaded.experiments.names == original.experiments.names + + # Compare all parameter values + loaded_params = _collect_param_snapshot(loaded) + for name, orig_val in original_params.items(): + assert name in loaded_params, f'Parameter {name} missing after load' + if isinstance(orig_val, float): + assert_almost_equal( + loaded_params[name], + orig_val, + decimal=6, + err_msg=f'Mismatch for {name}', + ) + else: + assert loaded_params[name] == orig_val, f'Mismatch for {name}' + + # Compare free flags + loaded_free = _collect_free_flags(loaded) + for name, orig_flag in original_free.items(): + if name in loaded_free: + assert loaded_free[name] == orig_flag, ( + f'Free flag mismatch for {name}: expected {orig_flag}, got {loaded_free[name]}' + ) + + # Compare aliases + assert len(loaded.analysis.aliases) == len(original.analysis.aliases) + for orig_alias in original.analysis.aliases: + label = orig_alias.label.value + loaded_alias = loaded.analysis.aliases[label] + assert loaded_alias.param_unique_name.value == orig_alias.param_unique_name.value + assert loaded_alias.param is not None, f"Alias '{label}' param reference not resolved" + + # Compare constraints + assert len(loaded.analysis.constraints) == len(original.analysis.constraints) + for i, orig_c in enumerate(original.analysis.constraints): + assert loaded.analysis.constraints[i].expression.value == orig_c.expression.value + assert loaded.analysis.constraints.enabled is True + + # Compare analysis settings + assert loaded.analysis.current_minimizer == original.analysis.current_minimizer + assert loaded.analysis.fit_mode.mode.value == original.analysis.fit_mode.mode.value + + +# ------------------------------------------------------------------ +# Test 2: create → fit → save → load → fit → compare χ² +# ------------------------------------------------------------------ + + +def test_save_load_round_trip_preserves_fit_quality(tmp_path) -> None: + """ + A loaded project must produce the same χ² as the original. + + Fits the original project, saves it, loads it back, fits again, + and compares reduced χ² values. + """ + # Create and fit the original project + original = _create_lbco_project() + original.analysis.fit(verbosity='silent') + original_chi2 = original.analysis.fit_results.reduced_chi_square + + # Save the fitted project + proj_dir = str(tmp_path / 'lbco_fitted') + original.save_as(proj_dir) + + # Load + loaded = Project.load(proj_dir) + + # Fit the loaded project + loaded.analysis.fit(verbosity='silent') + loaded_chi2 = loaded.analysis.fit_results.reduced_chi_square + + # The χ² values should be very close (same starting point, + # same data, same model) + assert_almost_equal(loaded_chi2, original_chi2, decimal=1) diff --git a/tests/unit/easydiffraction/project/test_project_load.py b/tests/unit/easydiffraction/project/test_project_load.py new file mode 100644 index 00000000..6d9020b7 --- /dev/null +++ b/tests/unit/easydiffraction/project/test_project_load.py @@ -0,0 +1,142 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Unit tests for Project.load().""" + +from __future__ import annotations + +import pytest + +from easydiffraction.project.project import Project + + +class TestLoadMinimal: + """Load a project that has no structures or experiments.""" + + def test_raises_on_missing_directory(self, tmp_path): + missing = tmp_path / 'nonexistent' + with pytest.raises(FileNotFoundError, match='not found'): + Project.load(str(missing)) + + def test_round_trips_empty_project(self, tmp_path): + original = Project(name='empty', title='Empty', description='nothing') + original.save_as(str(tmp_path / 'proj')) + + loaded = Project.load(str(tmp_path / 'proj')) + + assert loaded.name == 'empty' + assert loaded.info.title == 'Empty' + assert loaded.info.description == 'nothing' + assert loaded.info.path is not None + assert len(loaded.structures) == 0 + assert len(loaded.experiments) == 0 + + +class TestLoadStructures: + """Load structures from a saved project.""" + + def test_round_trips_structure(self, tmp_path): + original = Project(name='s1') + original.structures.create(name='cosio') + s = original.structures['cosio'] + s.space_group.name_h_m = 'P m -3 m' + s.cell.length_a = 3.88 + s.atom_sites.create( + label='Co', + type_symbol='Co', + fract_x=0.0, + fract_y=0.0, + fract_z=0.0, + b_iso=0.5, + ) + original.save_as(str(tmp_path / 'proj')) + + loaded = Project.load(str(tmp_path / 'proj')) + + assert len(loaded.structures) == 1 + ls = loaded.structures['cosio'] + assert ls.space_group.name_h_m.value == 'P m -3 m' + assert abs(ls.cell.length_a.value - 3.88) < 1e-6 + assert len(ls.atom_sites) == 1 + assert ls.atom_sites['Co'].type_symbol.value == 'Co' + assert abs(ls.atom_sites['Co'].b_iso.value - 0.5) < 1e-6 + + +class TestLoadAnalysis: + """Load analysis settings from a saved project.""" + + def test_round_trips_minimizer(self, tmp_path): + original = Project(name='a1') + original.save_as(str(tmp_path / 'proj')) + + loaded = Project.load(str(tmp_path / 'proj')) + + assert loaded.analysis.current_minimizer == 'lmfit' + + def test_round_trips_fit_mode(self, tmp_path): + original = Project(name='a2') + original.analysis.fit_mode.mode = 'joint' + original.save_as(str(tmp_path / 'proj')) + + loaded = Project.load(str(tmp_path / 'proj')) + + assert loaded.analysis.fit_mode.mode.value == 'joint' + + def test_round_trips_constraints(self, tmp_path): + original = Project(name='c1') + original.structures.create(name='s') + s = original.structures['s'] + s.cell.length_a = 5.0 + s.cell.length_b = 5.0 + + original.analysis.aliases.create( + label='a_param', + param=s.cell.length_a, + ) + original.analysis.aliases.create( + label='b_param', + param=s.cell.length_b, + ) + original.analysis.constraints.create(expression='b_param = a_param') + original.save_as(str(tmp_path / 'proj')) + + loaded = Project.load(str(tmp_path / 'proj')) + + assert len(loaded.analysis.aliases) == 2 + assert loaded.analysis.aliases['a_param'].label.value == 'a_param' + assert loaded.analysis.aliases['b_param'].label.value == 'b_param' + # Verify alias param references are resolved + assert loaded.analysis.aliases['a_param'].param is not None + assert loaded.analysis.aliases['b_param'].param is not None + + assert len(loaded.analysis.constraints) == 1 + assert loaded.analysis.constraints[0].expression.value == 'b_param = a_param' + assert loaded.analysis.constraints.enabled is True + + +class TestLoadAnalysisCifFallback: + """Load falls back from analysis/analysis.cif to analysis.cif at root.""" + + def test_loads_analysis_from_root(self, tmp_path): + """Current save layout: analysis.cif at project root.""" + original = Project(name='fb1') + original.save_as(str(tmp_path / 'proj')) + + # Verify analysis.cif is at root (current save layout) + assert (tmp_path / 'proj' / 'analysis.cif').is_file() + + loaded = Project.load(str(tmp_path / 'proj')) + assert loaded.analysis.current_minimizer == 'lmfit' + + def test_loads_analysis_from_subdir(self, tmp_path): + """Future layout: analysis/analysis.cif takes priority.""" + original = Project(name='fb2') + original.save_as(str(tmp_path / 'proj')) + + # Move analysis.cif to analysis/ subdirectory + proj_dir = tmp_path / 'proj' + analysis_dir = proj_dir / 'analysis' + analysis_dir.mkdir(exist_ok=True) + (proj_dir / 'analysis.cif').rename(analysis_dir / 'analysis.cif') + + loaded = Project.load(str(proj_dir)) + assert loaded.analysis.current_minimizer == 'lmfit' diff --git a/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py b/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py index cdeafd35..69f84127 100644 --- a/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py +++ b/tests/unit/easydiffraction/project/test_project_load_and_summary_wrap.py @@ -2,15 +2,27 @@ # SPDX-License-Identifier: BSD-3-Clause -def test_project_load_prints_and_sets_path(tmp_path, capsys): +def test_project_load_raises_on_missing_directory(tmp_path): import pytest from easydiffraction.project.project import Project - p = Project() - dir_path = tmp_path / 'pdir' - with pytest.raises(NotImplementedError, match='not implemented yet'): - p.load(str(dir_path)) + missing_dir = tmp_path / 'nonexistent' + with pytest.raises(FileNotFoundError, match='not found'): + Project.load(str(missing_dir)) + + +def test_project_load_reads_project_info(tmp_path): + from easydiffraction.project.project import Project + + p = Project(name='myproj', title='My Title', description='A description') + p.save_as(str(tmp_path / 'proj')) + + loaded = Project.load(str(tmp_path / 'proj')) + assert loaded.name == 'myproj' + assert loaded.info.title == 'My Title' + assert loaded.info.description == 'A description' + assert loaded.info.path is not None def test_summary_show_project_info_wraps_description(capsys): From 47e268eed5e3f0bb7de13b25188aadf6ebffd494 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 11:15:40 +0200 Subject: [PATCH 04/24] Encode free flags via CIF uncertainty brackets --- docs/architecture/architecture.md | 4 + docs/docs/tutorials/ed-18.py | 84 +++++++++++++++++++ docs/docs/tutorials/index.json | 7 ++ docs/docs/tutorials/index.md | 4 + docs/mkdocs.yml | 1 + src/easydiffraction/io/cif/serialize.py | 79 ++++++++++++++--- src/easydiffraction/utils/utils.py | 19 +++-- .../integration/fitting/test_project_load.py | 12 ++- .../easydiffraction/core/test_parameters.py | 4 +- 9 files changed, 192 insertions(+), 22 deletions(-) create mode 100644 docs/docs/tutorials/ed-18.py diff --git a/docs/architecture/architecture.md b/docs/architecture/architecture.md index d9827c07..605ee28c 100644 --- a/docs/architecture/architecture.md +++ b/docs/architecture/architecture.md @@ -919,6 +919,10 @@ project.experiments['xray_pdf'].peak_profile_type = 'gaussian-damped-sinc' - `DatablockItem` = one CIF `data_` block, `DatablockCollection` = set of blocks. - `CategoryItem` = one CIF category, `CategoryCollection` = CIF loop. +- **Free-flag encoding**: A parameter's free/fixed status is encoded in + CIF via uncertainty brackets. `3.89` = fixed, `3.89(2)` = free with + esd, `3.89()` = free without esd. There is no separate list of free + parameters; the brackets are the single source of truth. ### 9.2 Immutability of Experiment Type diff --git a/docs/docs/tutorials/ed-18.py b/docs/docs/tutorials/ed-18.py new file mode 100644 index 00000000..b883e9d0 --- /dev/null +++ b/docs/docs/tutorials/ed-18.py @@ -0,0 +1,84 @@ +# %% [markdown] +# # Load Project and Fit: LBCO, HRPT +# +# This is the most minimal example of using EasyDiffraction. It shows +# how to load a previously saved project from a directory and run +# refinement — all in just a few lines of code. +# +# The project is first created and saved as a setup step (this would +# normally be done once and the directory would already exist on disk). +# Then the saved project is loaded back and fitted. +# +# For details on how to define structures and experiments, see the other +# tutorials. + +# %% [markdown] +# ## Import Library + +# %% +import easydiffraction as ed + +# %% [markdown] +# ## Setup: Create and Save a Project +# +# This step creates a project from CIF files and saves it to a +# directory. In practice, the project directory would already exist +# on disk from a previous session. + +# %% +# Create a project from CIF files +project = ed.Project() +project.structures.add_from_cif_path(ed.download_data(id=1, destination='data')) +project.experiments.add_from_cif_path(ed.download_data(id=2, destination='data')) + +# %% +project.analysis.aliases.create( + label='biso_La', + param=project.structures['lbco'].atom_sites['La'].b_iso, +) +project.analysis.aliases.create( + label='biso_Ba', + param=project.structures['lbco'].atom_sites['Ba'].b_iso, +) + +project.analysis.aliases.create( + label='occ_La', + param=project.structures['lbco'].atom_sites['La'].occupancy, +) +project.analysis.aliases.create( + label='occ_Ba', + param=project.structures['lbco'].atom_sites['Ba'].occupancy, +) + +project.analysis.constraints.create(expression='biso_Ba = biso_La') +project.analysis.constraints.create(expression='occ_Ba = 1 - occ_La') + +project.structures['lbco'].atom_sites['La'].occupancy.free = True + +# %% +# Save to a directory +project.save_as('lbco_project') + +# %% [markdown] +# ## Step 1: Load Project from Directory + +# %% +project = ed.Project.load('lbco_project') + +# %% [markdown] +# ## Step 2: Perform Analysis + +# %% +project.analysis.fit() + +# %% +project.analysis.show_fit_results() + +# %% +project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True) + +# %% [markdown] +# ## Step 3: Show Project Summary + +# %% +project.summary.show_report() diff --git a/docs/docs/tutorials/index.json b/docs/docs/tutorials/index.json index 3f2f223c..9a438874 100644 --- a/docs/docs/tutorials/index.json +++ b/docs/docs/tutorials/index.json @@ -117,5 +117,12 @@ "title": "Structure Refinement: Co2SiO4, D20 (Temperature scan)", "description": "Sequential Rietveld refinement of Co2SiO4 using constant wavelength neutron powder diffraction data from D20 at ILL across a temperature scan", "level": "advanced" + }, + "18": { + "url": "https://easyscience.github.io/diffraction-lib/{version}/tutorials/ed-18/ed-18.ipynb", + "original_name": "", + "title": "Quick Start: LBCO Load Project", + "description": "Most minimal example: load a saved project from a directory and run Rietveld refinement of La0.5Ba0.5CoO3", + "level": "quick" } } diff --git a/docs/docs/tutorials/index.md b/docs/docs/tutorials/index.md index ef22e949..06a9c69e 100644 --- a/docs/docs/tutorials/index.md +++ b/docs/docs/tutorials/index.md @@ -17,6 +17,10 @@ The tutorials are organized into the following categories. ## Getting Started +- [LBCO `quick` `load`](ed-18.ipynb) – The most minimal example showing + how to load a previously saved project from a directory and run + refinement. Useful when a project has already been set up and saved in + a prior session. - [LBCO `quick` CIF](ed-1.ipynb) – A minimal example intended as a quick reference for users already familiar with the EasyDiffraction API or who want to see how Rietveld refinement of the La0.5Ba0.5CoO3 crystal diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index c272bdec..5f2415c3 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -191,6 +191,7 @@ nav: - Tutorials: - Tutorials: tutorials/index.md - Getting Started: + - LBCO quick load: tutorials/ed-18.ipynb - LBCO quick CIF: tutorials/ed-1.ipynb - LBCO quick code: tutorials/ed-2.ipynb - LBCO complete: tutorials/ed-3.ipynb diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index 89655c64..184e40f4 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -65,15 +65,67 @@ def format_value(value: object) -> str: ################## +def format_param_value(param: object) -> str: + """ + Format a parameter value for CIF output, encoding the free flag. + + CIF convention for numeric parameters: + + - Fixed or constrained parameter: plain value, e.g. ``3.89090000`` + - Free parameter without uncertainty: value with empty brackets, + e.g. ``3.89090000()`` + - Free parameter with uncertainty: value with esd in brackets, + e.g. ``3.89090000(200000)`` + + Constrained (dependent) parameters are always written without + brackets, even if their ``free`` flag is ``True``, because they are + not independently varied by the minimizer. + + Non-numeric parameters and descriptors without a ``free`` attribute + are formatted with :func:`format_value`. + + Parameters + ---------- + param : object + A descriptor or parameter exposing ``.value`` and optionally + ``.free``, ``.constrained``, and ``.uncertainty``. + + Returns + ------- + str + Formatted CIF value string. + """ + is_free = getattr(param, 'free', False) + is_constrained = getattr(param, 'constrained', False) + value = param.value # type: ignore[attr-defined] + + if not is_free or is_constrained or not isinstance(value, (int, float)): + return format_value(value) + + precision = 8 + uncertainty = getattr(param, 'uncertainty', None) + formatted_value = f'{float(value):.{precision}f}' + + if uncertainty is not None and uncertainty > 0: + from uncertainties import ufloat as _ufloat # noqa: PLC0415 + + u = _ufloat(float(value), float(uncertainty)) + return f'{u:.{precision}fS}' + + return f'{formatted_value}()' + + def param_to_cif(param: object) -> str: """ Render a single descriptor/parameter to a CIF line. Expects ``param`` to expose ``_cif_handler.names`` and ``value``. + Free parameters are written with uncertainty brackets (see + :func:`format_param_value`). """ tags: Sequence[str] = param._cif_handler.names # type: ignore[attr-defined] main_key: str = tags[0] - return f'{main_key} {format_value(param.value)}' + return f'{main_key} {format_param_value(param)}' def category_item_to_cif(item: object) -> str: @@ -94,8 +146,7 @@ def category_collection_to_cif( """ Render a CategoryCollection-like object to CIF text. - Uses first item to build loop header, then emits rows for each - item. + Uses first item to build loop header, then emits rows for each item. Parameters ---------- @@ -124,17 +175,17 @@ def category_collection_to_cif( half_display = max_display // 2 for i in range(half_display): item = list(collection.values())[i] - row_vals = [format_value(p.value) for p in item.parameters] + row_vals = [format_param_value(p) for p in item.parameters] lines.append(' '.join(row_vals)) lines.append('...') for i in range(-half_display, 0): item = list(collection.values())[i] - row_vals = [format_value(p.value) for p in item.parameters] + row_vals = [format_param_value(p) for p in item.parameters] lines.append(' '.join(row_vals)) # No limit else: for item in collection.values(): - row_vals = [format_value(p.value) for p in item.parameters] + row_vals = [format_param_value(p) for p in item.parameters] lines.append(' '.join(row_vals)) return '\n'.join(lines) @@ -415,11 +466,13 @@ def param_from_cif( # If numeric, parse with uncertainty if present if self._value_type == DataTypes.NUMERIC: + has_brackets = '(' in raw u = str_to_ufloat(raw) self.value = u.n - if not np.isnan(u.s) and hasattr(self, 'uncertainty'): - self.uncertainty = u.s # type: ignore[attr-defined] - self.free = True # Mark as free if uncertainty is present + if has_brackets and hasattr(self, 'free'): + self.free = True # type: ignore[attr-defined] + if not np.isnan(u.s) and hasattr(self, 'uncertainty'): + self.uncertainty = u.s # type: ignore[attr-defined] # If string, strip quotes if present elif self._value_type == DataTypes.STRING: @@ -520,11 +573,13 @@ def _get_loop(block: object, category_item: object) -> object | None: # If numeric, parse with uncertainty if present if param._value_type == DataTypes.NUMERIC: + has_brackets = '(' in raw u = str_to_ufloat(raw) param.value = u.n - if not np.isnan(u.s) and hasattr(param, 'uncertainty'): - param.uncertainty = u.s # type: ignore[attr-defined] - param.free = True # Mark as free if uncertainty is present + if has_brackets and hasattr(param, 'free'): + param.free = True # type: ignore[attr-defined] + if not np.isnan(u.s) and hasattr(param, 'uncertainty'): + param.uncertainty = u.s # type: ignore[attr-defined] # If string, strip quotes if present # TODO: Make a helper function for this diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index 4a029917..a54038fc 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -705,19 +705,23 @@ def str_to_ufloat(s: str | None, default: float | None = None) -> UFloat: Parse a CIF-style numeric string into a ufloat. Examples of supported input: - "3.566" → ufloat(3.566, nan) - - "3.566(2)" → ufloat(3.566, 0.002) - None → ufloat(default, nan) + "3.566(2)" → ufloat(3.566, 0.002) - "3.566()" → ufloat(3.566, 0.0) - + None → ufloat(default, nan) Behavior: - If the input string contains a value with parentheses (e.g. "3.566(2)"), the number in parentheses is interpreted as an - estimated standard deviation (esd) in the last digit(s). - If the - input string has no parentheses, an uncertainty of NaN is assigned - to indicate "no esd provided". - If parsing fails, the function - falls back to the given ``default`` value with uncertainty NaN. + estimated standard deviation (esd) in the last digit(s). - Empty + parentheses (e.g. "3.566()") are treated as zero uncertainty. - If + the input string has no parentheses, an uncertainty of NaN is + assigned to indicate "no esd provided". - If parsing fails, the + function falls back to the given ``default`` value with uncertainty + NaN. Parameters ---------- s : str | None - Numeric string in CIF format (e.g. "3.566", "3.566(2)") or None. + Numeric string in CIF format (e.g. "3.566", "3.566(2)", + "3.566()") or None. default : float | None, default=None Default value to use if ``s`` is None or parsing fails. @@ -733,6 +737,9 @@ def str_to_ufloat(s: str | None, default: float | None = None) -> UFloat: if '(' not in s and ')' not in s: s = f'{s}(nan)' + elif s.endswith('()'): + # Empty brackets → zero uncertainty (free parameter, no esd yet) + s = s[:-2] + '(0)' try: return ufloat_fromstr(s) except Exception: diff --git a/tests/integration/fitting/test_project_load.py b/tests/integration/fitting/test_project_load.py index 3598ed69..789482f3 100644 --- a/tests/integration/fitting/test_project_load.py +++ b/tests/integration/fitting/test_project_load.py @@ -116,8 +116,12 @@ def _create_lbco_project() -> Project: def _collect_param_snapshot(project: Project) -> dict[str, float]: - """Return ``{unique_name: value}`` for all project parameters.""" - return {p.unique_name: p.value for p in project.parameters} + """Return ``{unique_name: value}`` for model parameters (excluding raw data).""" + return { + p.unique_name: p.value + for p in project.parameters + if not p.unique_name.startswith('pd_data.') + } def _collect_free_flags(project: Project) -> dict[str, bool]: @@ -139,6 +143,10 @@ def test_save_load_round_trip_preserves_parameters(tmp_path) -> None: Also verifies project info, free flags, aliases, and constraints. """ original = _create_lbco_project() + # Apply symmetry constraints so snapshot matches the loaded state + # (load() calls _update_categories which applies symmetry). + for structure in original.structures: + structure._update_categories() original_params = _collect_param_snapshot(original) original_free = _collect_free_flags(original) diff --git a/tests/unit/easydiffraction/core/test_parameters.py b/tests/unit/easydiffraction/core/test_parameters.py index 87ded2b6..d9f96b0e 100644 --- a/tests/unit/easydiffraction/core/test_parameters.py +++ b/tests/unit/easydiffraction/core/test_parameters.py @@ -66,8 +66,8 @@ def test_parameter_string_repr_and_as_cif_and_flags(): assert 'A' in s assert '(free=True)' in s - # CIF line is ` ` - assert p.as_cif == '_param.a 2.50000000' + # CIF line: free param with uncertainty uses esd brackets + assert p.as_cif == '_param.a 2.50000000(10000000)' # CifHandler uid is owner's unique_name (parameter name here) assert p._cif_handler.uid == p.unique_name == 'a' From 4db00cf7e7a9b7e679c6a714a5ca48cd67639cb4 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 11:59:23 +0200 Subject: [PATCH 05/24] Update notebooks --- docs/docs/tutorials/ed-1.ipynb | 53 ++-- docs/docs/tutorials/ed-10.ipynb | 55 ++-- docs/docs/tutorials/ed-11.ipynb | 61 +++-- docs/docs/tutorials/ed-12.ipynb | 69 +++-- docs/docs/tutorials/ed-13.ipynb | 427 ++++++++++++++++--------------- docs/docs/tutorials/ed-14.ipynb | 77 +++--- docs/docs/tutorials/ed-15.ipynb | 73 +++--- docs/docs/tutorials/ed-16.ipynb | 133 +++++----- docs/docs/tutorials/ed-17.ipynb | 143 ++++++----- docs/docs/tutorials/ed-18.ipynb | 225 +++++++++++++++++ docs/docs/tutorials/ed-2.ipynb | 67 +++-- docs/docs/tutorials/ed-3.ipynb | 431 +++++++++++++++----------------- docs/docs/tutorials/ed-4.ipynb | 139 +++++----- docs/docs/tutorials/ed-5.ipynb | 143 ++++++----- docs/docs/tutorials/ed-6.ipynb | 175 +++++++------ docs/docs/tutorials/ed-7.ipynb | 163 ++++++------ docs/docs/tutorials/ed-8.ipynb | 133 +++++----- docs/docs/tutorials/ed-9.ipynb | 143 ++++++----- 18 files changed, 1553 insertions(+), 1157 deletions(-) create mode 100644 docs/docs/tutorials/ed-18.ipynb diff --git a/docs/docs/tutorials/ed-1.ipynb b/docs/docs/tutorials/ed-1.ipynb index d2e178a2..830d7889 100644 --- a/docs/docs/tutorials/ed-1.ipynb +++ b/docs/docs/tutorials/ed-1.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8dbf8e63", + "id": "74c72059", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Structure Refinement: LBCO, HRPT\n", "\n", @@ -47,7 +62,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -56,7 +71,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -65,7 +80,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Step 1: Define Project" @@ -74,7 +89,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -84,7 +99,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "## Step 2: Define Crystal Structure" @@ -93,7 +108,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -104,7 +119,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -113,7 +128,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "9", "metadata": {}, "source": [ "## Step 3: Define Experiment" @@ -122,7 +137,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -133,7 +148,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -142,7 +157,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "## Step 4: Perform Analysis" @@ -151,7 +166,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -163,7 +178,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -174,7 +189,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -184,7 +199,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -193,7 +208,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "17", "metadata": {}, "source": [ "## Step 5: Show Project Summary" @@ -202,7 +217,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "18", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-10.ipynb b/docs/docs/tutorials/ed-10.ipynb index 0c865ce0..2fa9c6c5 100644 --- a/docs/docs/tutorials/ed-10.ipynb +++ b/docs/docs/tutorials/ed-10.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f42176f2", + "id": "239bda80", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Pair Distribution Function: Ni, NPD\n", "\n", @@ -36,7 +51,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -45,7 +60,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -54,7 +69,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Create Project" @@ -63,7 +78,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -72,7 +87,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "## Add Structure" @@ -81,7 +96,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -91,7 +106,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -111,7 +126,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "9", "metadata": {}, "source": [ "## Add Experiment" @@ -120,7 +135,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -130,7 +145,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -147,7 +162,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -162,7 +177,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "13", "metadata": {}, "source": [ "## Select Fitting Parameters" @@ -171,7 +186,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -182,7 +197,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -193,7 +208,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "16", "metadata": {}, "source": [ "## Run Fitting" @@ -202,7 +217,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -212,7 +227,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "18", "metadata": {}, "source": [ "## Plot Measured vs Calculated" @@ -221,7 +236,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-11.ipynb b/docs/docs/tutorials/ed-11.ipynb index 92714987..cbb509bf 100644 --- a/docs/docs/tutorials/ed-11.ipynb +++ b/docs/docs/tutorials/ed-11.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b38dbf4f", + "id": "958d9ba3", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Pair Distribution Function: Si, NPD\n", "\n", @@ -33,7 +48,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -42,7 +57,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -51,7 +66,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Create Project" @@ -60,7 +75,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -69,7 +84,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "## Set Plotting Engine" @@ -78,7 +93,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -90,7 +105,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -100,7 +115,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "9", "metadata": {}, "source": [ "## Add Structure" @@ -109,7 +124,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -119,7 +134,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -140,7 +155,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "## Add Experiment" @@ -149,7 +164,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -159,7 +174,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -176,7 +191,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -192,7 +207,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "16", "metadata": {}, "source": [ "## Select Fitting Parameters" @@ -201,7 +216,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -213,7 +228,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -225,7 +240,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "19", "metadata": {}, "source": [ "## Run Fitting" @@ -234,7 +249,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -244,7 +259,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "21", "metadata": {}, "source": [ "## Plot Measured vs Calculated" @@ -253,7 +268,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "22", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-12.ipynb b/docs/docs/tutorials/ed-12.ipynb index 51bdc129..deaca165 100644 --- a/docs/docs/tutorials/ed-12.ipynb +++ b/docs/docs/tutorials/ed-12.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "effff825", + "id": "a6c12e68", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Pair Distribution Function: NaCl, XRD\n", "\n", @@ -36,7 +51,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -45,7 +60,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -54,7 +69,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Create Project" @@ -63,7 +78,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -72,7 +87,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "## Set Plotting Engine" @@ -81,7 +96,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -93,7 +108,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -104,7 +119,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "9", "metadata": {}, "source": [ "## Add Structure" @@ -113,7 +128,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -123,7 +138,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -152,7 +167,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "## Add Experiment" @@ -161,7 +176,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -171,7 +186,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -188,7 +203,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -198,7 +213,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -208,7 +223,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -218,7 +233,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -233,7 +248,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -242,7 +257,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "20", "metadata": {}, "source": [ "## Select Fitting Parameters" @@ -251,7 +266,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -263,7 +278,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -274,7 +289,7 @@ }, { "cell_type": "markdown", - "id": "22", + "id": "23", "metadata": {}, "source": [ "## Run Fitting" @@ -283,7 +298,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -293,7 +308,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "25", "metadata": {}, "source": [ "## Plot Measured vs Calculated" @@ -302,7 +317,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "26", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-13.ipynb b/docs/docs/tutorials/ed-13.ipynb index bb4dd4c2..ef68958b 100644 --- a/docs/docs/tutorials/ed-13.ipynb +++ b/docs/docs/tutorials/ed-13.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "263b6625", + "id": "1c60b738", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Fitting Powder Diffraction data\n", "\n", @@ -55,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "📖 See\n", @@ -67,7 +82,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -76,7 +91,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## 📘 Introduction: Simple Reference Fit – Si\n", @@ -103,7 +118,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "5", "metadata": {}, "source": [ "📖 See\n", @@ -115,7 +130,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -124,7 +139,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "7", "metadata": {}, "source": [ "You can set the title and description of the project to provide\n", @@ -136,7 +151,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -146,7 +161,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "9", "metadata": {}, "source": [ "### 🔬 Create an Experiment\n", @@ -159,7 +174,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "10", "metadata": {}, "source": [ "📖 See\n", @@ -171,7 +186,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -182,7 +197,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "Uncomment the following cell if your data reduction failed and the\n", @@ -196,7 +211,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -205,7 +220,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "14", "metadata": {}, "source": [ "Now we can create the experiment and load the measured data. In this\n", @@ -217,7 +232,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "15", "metadata": {}, "source": [ "📖 See\n", @@ -228,7 +243,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -243,7 +258,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "17", "metadata": {}, "source": [ "#### Inspect Measured Data\n", @@ -259,7 +274,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "18", "metadata": {}, "source": [ "📖 See\n", @@ -273,7 +288,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "19", "metadata": {}, "source": [ "📖 See\n", @@ -284,7 +299,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -296,7 +311,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -305,7 +320,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "22", "metadata": {}, "source": [ "If you zoom in on the highest TOF peak (around 120,000 μs), you will\n", @@ -328,7 +343,7 @@ }, { "cell_type": "markdown", - "id": "22", + "id": "23", "metadata": {}, "source": [ "📖 See\n", @@ -339,7 +354,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -349,7 +364,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "25", "metadata": {}, "source": [ "To visualize the effect of excluding the high TOF region, we can plot\n", @@ -360,7 +375,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -369,7 +384,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "27", "metadata": {}, "source": [ "#### Set Instrument Parameters\n", @@ -392,7 +407,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "28", "metadata": {}, "source": [ "📖 See\n", @@ -403,7 +418,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -417,7 +432,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "30", "metadata": {}, "source": [ "Before proceeding, let's take a quick look at the concept of\n", @@ -437,7 +452,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -446,7 +461,7 @@ }, { "cell_type": "markdown", - "id": "31", + "id": "32", "metadata": {}, "source": [ "The `value` attribute represents the current value of the parameter as\n", @@ -460,7 +475,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -469,7 +484,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "34", "metadata": {}, "source": [ "Note that to set the value of the parameter, you can simply assign a\n", @@ -479,7 +494,7 @@ }, { "cell_type": "markdown", - "id": "34", + "id": "35", "metadata": {}, "source": [ "📖 See\n", @@ -490,7 +505,7 @@ }, { "cell_type": "markdown", - "id": "35", + "id": "36", "metadata": {}, "source": [ "#### Set Peak Profile Parameters\n", @@ -532,7 +547,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "37", "metadata": {}, "source": [ "📖 See\n", @@ -543,7 +558,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37", + "id": "38", "metadata": {}, "outputs": [], "source": [ @@ -559,7 +574,7 @@ }, { "cell_type": "markdown", - "id": "38", + "id": "39", "metadata": {}, "source": [ "#### Set Background\n", @@ -594,7 +609,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "40", "metadata": {}, "source": [ "📖 See\n", @@ -605,7 +620,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -621,7 +636,7 @@ }, { "cell_type": "markdown", - "id": "41", + "id": "42", "metadata": {}, "source": [ "### 🧩 Create a Structure – Si\n", @@ -664,7 +679,7 @@ }, { "cell_type": "markdown", - "id": "42", + "id": "43", "metadata": {}, "source": [ "📖 See\n", @@ -674,7 +689,7 @@ }, { "cell_type": "markdown", - "id": "43", + "id": "44", "metadata": {}, "source": [ "```\n", @@ -706,7 +721,7 @@ }, { "cell_type": "markdown", - "id": "44", + "id": "45", "metadata": {}, "source": [ "As with adding the experiment in the previous step, we will create a\n", @@ -716,7 +731,7 @@ }, { "cell_type": "markdown", - "id": "45", + "id": "46", "metadata": {}, "source": [ "📖 See\n", @@ -727,7 +742,7 @@ }, { "cell_type": "markdown", - "id": "46", + "id": "47", "metadata": {}, "source": [ "#### Add Structure" @@ -736,7 +751,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "48", "metadata": {}, "outputs": [], "source": [ @@ -745,7 +760,7 @@ }, { "cell_type": "markdown", - "id": "48", + "id": "49", "metadata": {}, "source": [ "#### Set Space Group" @@ -753,7 +768,7 @@ }, { "cell_type": "markdown", - "id": "49", + "id": "50", "metadata": {}, "source": [ "📖 See\n", @@ -764,7 +779,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50", + "id": "51", "metadata": {}, "outputs": [], "source": [ @@ -774,7 +789,7 @@ }, { "cell_type": "markdown", - "id": "51", + "id": "52", "metadata": {}, "source": [ "#### Set Lattice Parameters" @@ -782,7 +797,7 @@ }, { "cell_type": "markdown", - "id": "52", + "id": "53", "metadata": {}, "source": [ "📖 See\n", @@ -793,7 +808,7 @@ { "cell_type": "code", "execution_count": null, - "id": "53", + "id": "54", "metadata": {}, "outputs": [], "source": [ @@ -802,7 +817,7 @@ }, { "cell_type": "markdown", - "id": "54", + "id": "55", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -810,7 +825,7 @@ }, { "cell_type": "markdown", - "id": "55", + "id": "56", "metadata": {}, "source": [ "📖 See\n", @@ -821,7 +836,7 @@ { "cell_type": "code", "execution_count": null, - "id": "56", + "id": "57", "metadata": {}, "outputs": [], "source": [ @@ -838,7 +853,7 @@ }, { "cell_type": "markdown", - "id": "57", + "id": "58", "metadata": {}, "source": [ "### 🔗 Assign Structure to Experiment\n", @@ -851,7 +866,7 @@ }, { "cell_type": "markdown", - "id": "58", + "id": "59", "metadata": {}, "source": [ "📖 See\n", @@ -862,7 +877,7 @@ { "cell_type": "code", "execution_count": null, - "id": "59", + "id": "60", "metadata": {}, "outputs": [], "source": [ @@ -871,7 +886,7 @@ }, { "cell_type": "markdown", - "id": "60", + "id": "61", "metadata": {}, "source": [ "### 🚀 Analyze and Fit the Data\n", @@ -893,7 +908,7 @@ }, { "cell_type": "markdown", - "id": "61", + "id": "62", "metadata": { "title": "**Reminder:**" }, @@ -910,7 +925,7 @@ }, { "cell_type": "markdown", - "id": "62", + "id": "63", "metadata": {}, "source": [ "📖 See\n", @@ -920,7 +935,7 @@ }, { "cell_type": "markdown", - "id": "63", + "id": "64", "metadata": {}, "source": [ "#### Set Fit Parameters\n", @@ -943,7 +958,7 @@ { "cell_type": "code", "execution_count": null, - "id": "64", + "id": "65", "metadata": {}, "outputs": [], "source": [ @@ -963,7 +978,7 @@ }, { "cell_type": "markdown", - "id": "65", + "id": "66", "metadata": {}, "source": [ "#### Show Free Parameters\n", @@ -974,7 +989,7 @@ }, { "cell_type": "markdown", - "id": "66", + "id": "67", "metadata": {}, "source": [ "📖 See\n", @@ -988,7 +1003,7 @@ { "cell_type": "code", "execution_count": null, - "id": "67", + "id": "68", "metadata": {}, "outputs": [], "source": [ @@ -997,7 +1012,7 @@ }, { "cell_type": "markdown", - "id": "68", + "id": "69", "metadata": {}, "source": [ "#### Visualize Diffraction Patterns\n", @@ -1013,7 +1028,7 @@ { "cell_type": "code", "execution_count": null, - "id": "69", + "id": "70", "metadata": {}, "outputs": [], "source": [ @@ -1022,7 +1037,7 @@ }, { "cell_type": "markdown", - "id": "70", + "id": "71", "metadata": {}, "source": [ "#### Run Fitting\n", @@ -1033,7 +1048,7 @@ }, { "cell_type": "markdown", - "id": "71", + "id": "72", "metadata": {}, "source": [ "📖 See\n", @@ -1044,7 +1059,7 @@ { "cell_type": "code", "execution_count": null, - "id": "72", + "id": "73", "metadata": {}, "outputs": [], "source": [ @@ -1054,7 +1069,7 @@ }, { "cell_type": "markdown", - "id": "73", + "id": "74", "metadata": {}, "source": [ "#### Check Fit Results\n", @@ -1073,7 +1088,7 @@ }, { "cell_type": "markdown", - "id": "74", + "id": "75", "metadata": {}, "source": [ "#### Visualize Fit Results\n", @@ -1087,7 +1102,7 @@ { "cell_type": "code", "execution_count": null, - "id": "75", + "id": "76", "metadata": {}, "outputs": [], "source": [ @@ -1096,7 +1111,7 @@ }, { "cell_type": "markdown", - "id": "76", + "id": "77", "metadata": {}, "source": [ "#### TOF vs d-spacing\n", @@ -1130,7 +1145,7 @@ { "cell_type": "code", "execution_count": null, - "id": "77", + "id": "78", "metadata": {}, "outputs": [], "source": [ @@ -1139,7 +1154,7 @@ }, { "cell_type": "markdown", - "id": "78", + "id": "79", "metadata": {}, "source": [ "As you can see, the calculated diffraction pattern now matches the\n", @@ -1165,7 +1180,7 @@ { "cell_type": "code", "execution_count": null, - "id": "79", + "id": "80", "metadata": {}, "outputs": [], "source": [ @@ -1174,7 +1189,7 @@ }, { "cell_type": "markdown", - "id": "80", + "id": "81", "metadata": {}, "source": [ "## 💪 Exercise: Complex Fit – LBCO\n", @@ -1195,7 +1210,7 @@ }, { "cell_type": "markdown", - "id": "81", + "id": "82", "metadata": {}, "source": [ "**Hint:**" @@ -1203,7 +1218,7 @@ }, { "cell_type": "markdown", - "id": "82", + "id": "83", "metadata": {}, "source": [ "You can use the same approach as in the previous part of the notebook,\n", @@ -1212,7 +1227,7 @@ }, { "cell_type": "markdown", - "id": "83", + "id": "84", "metadata": {}, "source": [ "**Solution:**" @@ -1221,7 +1236,7 @@ { "cell_type": "code", "execution_count": null, - "id": "84", + "id": "85", "metadata": {}, "outputs": [], "source": [ @@ -1232,7 +1247,7 @@ }, { "cell_type": "markdown", - "id": "85", + "id": "86", "metadata": {}, "source": [ "### 🔬 Exercise 2: Define an Experiment\n", @@ -1245,7 +1260,7 @@ }, { "cell_type": "markdown", - "id": "86", + "id": "87", "metadata": {}, "source": [ "**Hint:**" @@ -1253,7 +1268,7 @@ }, { "cell_type": "markdown", - "id": "87", + "id": "88", "metadata": {}, "source": [ "You can use the same approach as in the previous part of the notebook,\n", @@ -1262,7 +1277,7 @@ }, { "cell_type": "markdown", - "id": "88", + "id": "89", "metadata": {}, "source": [ "**Solution:**" @@ -1271,7 +1286,7 @@ { "cell_type": "code", "execution_count": null, - "id": "89", + "id": "90", "metadata": {}, "outputs": [], "source": [ @@ -1294,7 +1309,7 @@ }, { "cell_type": "markdown", - "id": "90", + "id": "91", "metadata": {}, "source": [ "#### Exercise 2.1: Inspect Measured Data\n", @@ -1306,7 +1321,7 @@ }, { "cell_type": "markdown", - "id": "91", + "id": "92", "metadata": {}, "source": [ "**Hint:**" @@ -1314,7 +1329,7 @@ }, { "cell_type": "markdown", - "id": "92", + "id": "93", "metadata": {}, "source": [ "You can use the `plot_meas` method of the project to visualize the\n", @@ -1325,7 +1340,7 @@ }, { "cell_type": "markdown", - "id": "93", + "id": "94", "metadata": {}, "source": [ "**Solution:**" @@ -1334,7 +1349,7 @@ { "cell_type": "code", "execution_count": null, - "id": "94", + "id": "95", "metadata": {}, "outputs": [], "source": [ @@ -1348,7 +1363,7 @@ }, { "cell_type": "markdown", - "id": "95", + "id": "96", "metadata": {}, "source": [ "#### Exercise 2.2: Set Instrument Parameters\n", @@ -1358,7 +1373,7 @@ }, { "cell_type": "markdown", - "id": "96", + "id": "97", "metadata": {}, "source": [ "**Hint:**" @@ -1366,7 +1381,7 @@ }, { "cell_type": "markdown", - "id": "97", + "id": "98", "metadata": {}, "source": [ "Use the values from the data reduction process for the LBCO and\n", @@ -1375,7 +1390,7 @@ }, { "cell_type": "markdown", - "id": "98", + "id": "99", "metadata": {}, "source": [ "**Solution:**" @@ -1384,7 +1399,7 @@ { "cell_type": "code", "execution_count": null, - "id": "99", + "id": "100", "metadata": {}, "outputs": [], "source": [ @@ -1398,7 +1413,7 @@ }, { "cell_type": "markdown", - "id": "100", + "id": "101", "metadata": {}, "source": [ "#### Exercise 2.3: Set Peak Profile Parameters\n", @@ -1408,7 +1423,7 @@ }, { "cell_type": "markdown", - "id": "101", + "id": "102", "metadata": {}, "source": [ "**Hint:**" @@ -1416,7 +1431,7 @@ }, { "cell_type": "markdown", - "id": "102", + "id": "103", "metadata": {}, "source": [ "Use the values from the\n", @@ -1428,7 +1443,7 @@ }, { "cell_type": "markdown", - "id": "103", + "id": "104", "metadata": {}, "source": [ "**Solution:**" @@ -1437,7 +1452,7 @@ { "cell_type": "code", "execution_count": null, - "id": "104", + "id": "105", "metadata": {}, "outputs": [], "source": [ @@ -1455,7 +1470,7 @@ }, { "cell_type": "markdown", - "id": "105", + "id": "106", "metadata": {}, "source": [ "#### Exercise 2.4: Set Background\n", @@ -1466,7 +1481,7 @@ }, { "cell_type": "markdown", - "id": "106", + "id": "107", "metadata": {}, "source": [ "**Hint:**" @@ -1474,7 +1489,7 @@ }, { "cell_type": "markdown", - "id": "107", + "id": "108", "metadata": {}, "source": [ "Use the same approach as in the previous part of the notebook, but\n", @@ -1485,7 +1500,7 @@ }, { "cell_type": "markdown", - "id": "108", + "id": "109", "metadata": {}, "source": [ "**Solution:**" @@ -1494,7 +1509,7 @@ { "cell_type": "code", "execution_count": null, - "id": "109", + "id": "110", "metadata": {}, "outputs": [], "source": [ @@ -1510,7 +1525,7 @@ }, { "cell_type": "markdown", - "id": "110", + "id": "111", "metadata": {}, "source": [ "### 🧩 Exercise 3: Define a Structure – LBCO\n", @@ -1526,7 +1541,7 @@ }, { "cell_type": "markdown", - "id": "111", + "id": "112", "metadata": {}, "source": [ "```\n", @@ -1561,7 +1576,7 @@ }, { "cell_type": "markdown", - "id": "112", + "id": "113", "metadata": {}, "source": [ "Note that the `occupancy` of the La and Ba atoms is 0.5\n", @@ -1591,7 +1606,7 @@ }, { "cell_type": "markdown", - "id": "113", + "id": "114", "metadata": {}, "source": [ "#### Exercise 3.1: Create Structure\n", @@ -1602,7 +1617,7 @@ }, { "cell_type": "markdown", - "id": "114", + "id": "115", "metadata": {}, "source": [ "**Hint:**" @@ -1610,7 +1625,7 @@ }, { "cell_type": "markdown", - "id": "115", + "id": "116", "metadata": {}, "source": [ "You can use the same approach as in the previous part of the notebook,\n", @@ -1620,7 +1635,7 @@ }, { "cell_type": "markdown", - "id": "116", + "id": "117", "metadata": {}, "source": [ "**Solution:**" @@ -1629,7 +1644,7 @@ { "cell_type": "code", "execution_count": null, - "id": "117", + "id": "118", "metadata": {}, "outputs": [], "source": [ @@ -1638,7 +1653,7 @@ }, { "cell_type": "markdown", - "id": "118", + "id": "119", "metadata": {}, "source": [ "#### Exercise 3.2: Set Space Group\n", @@ -1648,7 +1663,7 @@ }, { "cell_type": "markdown", - "id": "119", + "id": "120", "metadata": {}, "source": [ "**Hint:**" @@ -1656,7 +1671,7 @@ }, { "cell_type": "markdown", - "id": "120", + "id": "121", "metadata": {}, "source": [ "Use the space group name and IT coordinate system code from the CIF\n", @@ -1665,7 +1680,7 @@ }, { "cell_type": "markdown", - "id": "121", + "id": "122", "metadata": {}, "source": [ "**Solution:**" @@ -1674,7 +1689,7 @@ { "cell_type": "code", "execution_count": null, - "id": "122", + "id": "123", "metadata": {}, "outputs": [], "source": [ @@ -1684,7 +1699,7 @@ }, { "cell_type": "markdown", - "id": "123", + "id": "124", "metadata": {}, "source": [ "#### Exercise 3.3: Set Lattice Parameters\n", @@ -1694,7 +1709,7 @@ }, { "cell_type": "markdown", - "id": "124", + "id": "125", "metadata": {}, "source": [ "**Hint:**" @@ -1702,7 +1717,7 @@ }, { "cell_type": "markdown", - "id": "125", + "id": "126", "metadata": {}, "source": [ "Use the lattice parameters from the CIF data." @@ -1710,7 +1725,7 @@ }, { "cell_type": "markdown", - "id": "126", + "id": "127", "metadata": {}, "source": [ "**Solution:**" @@ -1719,7 +1734,7 @@ { "cell_type": "code", "execution_count": null, - "id": "127", + "id": "128", "metadata": {}, "outputs": [], "source": [ @@ -1728,7 +1743,7 @@ }, { "cell_type": "markdown", - "id": "128", + "id": "129", "metadata": {}, "source": [ "#### Exercise 3.4: Set Atom Sites\n", @@ -1738,7 +1753,7 @@ }, { "cell_type": "markdown", - "id": "129", + "id": "130", "metadata": {}, "source": [ "**Hint:**" @@ -1746,7 +1761,7 @@ }, { "cell_type": "markdown", - "id": "130", + "id": "131", "metadata": {}, "source": [ "Use the atom sites from the CIF data. You can use the `add` method of\n", @@ -1755,7 +1770,7 @@ }, { "cell_type": "markdown", - "id": "131", + "id": "132", "metadata": {}, "source": [ "**Solution:**" @@ -1764,7 +1779,7 @@ { "cell_type": "code", "execution_count": null, - "id": "132", + "id": "133", "metadata": {}, "outputs": [], "source": [ @@ -1810,7 +1825,7 @@ }, { "cell_type": "markdown", - "id": "133", + "id": "134", "metadata": {}, "source": [ "### 🔗 Exercise 4: Assign Structure to Experiment\n", @@ -1820,7 +1835,7 @@ }, { "cell_type": "markdown", - "id": "134", + "id": "135", "metadata": {}, "source": [ "**Hint:**" @@ -1828,7 +1843,7 @@ }, { "cell_type": "markdown", - "id": "135", + "id": "136", "metadata": {}, "source": [ "Use the `linked_phases` attribute of the experiment to link the\n", @@ -1837,7 +1852,7 @@ }, { "cell_type": "markdown", - "id": "136", + "id": "137", "metadata": {}, "source": [ "**Solution:**" @@ -1846,7 +1861,7 @@ { "cell_type": "code", "execution_count": null, - "id": "137", + "id": "138", "metadata": {}, "outputs": [], "source": [ @@ -1855,7 +1870,7 @@ }, { "cell_type": "markdown", - "id": "138", + "id": "139", "metadata": {}, "source": [ "### 🚀 Exercise 5: Analyze and Fit the Data\n", @@ -1868,7 +1883,7 @@ }, { "cell_type": "markdown", - "id": "139", + "id": "140", "metadata": {}, "source": [ "**Hint:**" @@ -1876,7 +1891,7 @@ }, { "cell_type": "markdown", - "id": "140", + "id": "141", "metadata": {}, "source": [ "You can start with the scale factor and the background points, as in\n", @@ -1885,7 +1900,7 @@ }, { "cell_type": "markdown", - "id": "141", + "id": "142", "metadata": {}, "source": [ "**Solution:**" @@ -1894,7 +1909,7 @@ { "cell_type": "code", "execution_count": null, - "id": "142", + "id": "143", "metadata": {}, "outputs": [], "source": [ @@ -1906,7 +1921,7 @@ }, { "cell_type": "markdown", - "id": "143", + "id": "144", "metadata": {}, "source": [ "#### Exercise 5.2: Run Fitting\n", @@ -1917,7 +1932,7 @@ }, { "cell_type": "markdown", - "id": "144", + "id": "145", "metadata": {}, "source": [ "**Hint:**" @@ -1925,7 +1940,7 @@ }, { "cell_type": "markdown", - "id": "145", + "id": "146", "metadata": {}, "source": [ "Use the `plot_meas_vs_calc` method of the project to visualize the\n", @@ -1936,7 +1951,7 @@ }, { "cell_type": "markdown", - "id": "146", + "id": "147", "metadata": {}, "source": [ "**Solution:**" @@ -1945,7 +1960,7 @@ { "cell_type": "code", "execution_count": null, - "id": "147", + "id": "148", "metadata": {}, "outputs": [], "source": [ @@ -1957,7 +1972,7 @@ }, { "cell_type": "markdown", - "id": "148", + "id": "149", "metadata": {}, "source": [ "#### Exercise 5.3: Find the Misfit in the Fit\n", @@ -1972,7 +1987,7 @@ }, { "cell_type": "markdown", - "id": "149", + "id": "150", "metadata": {}, "source": [ "**Hint:**" @@ -1980,7 +1995,7 @@ }, { "cell_type": "markdown", - "id": "150", + "id": "151", "metadata": {}, "source": [ "Consider the following options:\n", @@ -1992,7 +2007,7 @@ }, { "cell_type": "markdown", - "id": "151", + "id": "152", "metadata": {}, "source": [ "**Solution:**" @@ -2000,7 +2015,7 @@ }, { "cell_type": "markdown", - "id": "152", + "id": "153", "metadata": {}, "source": [ "\n", @@ -2022,7 +2037,7 @@ { "cell_type": "code", "execution_count": null, - "id": "153", + "id": "154", "metadata": {}, "outputs": [], "source": [ @@ -2031,7 +2046,7 @@ }, { "cell_type": "markdown", - "id": "154", + "id": "155", "metadata": {}, "source": [ "#### Exercise 5.4: Refine the LBCO Lattice Parameter\n", @@ -2041,7 +2056,7 @@ }, { "cell_type": "markdown", - "id": "155", + "id": "156", "metadata": {}, "source": [ "**Hint:**" @@ -2049,7 +2064,7 @@ }, { "cell_type": "markdown", - "id": "156", + "id": "157", "metadata": {}, "source": [ "To achieve this, we will set the `free` attribute of the `length_a`\n", @@ -2064,7 +2079,7 @@ }, { "cell_type": "markdown", - "id": "157", + "id": "158", "metadata": {}, "source": [ "**Solution:**" @@ -2073,7 +2088,7 @@ { "cell_type": "code", "execution_count": null, - "id": "158", + "id": "159", "metadata": {}, "outputs": [], "source": [ @@ -2087,7 +2102,7 @@ }, { "cell_type": "markdown", - "id": "159", + "id": "160", "metadata": {}, "source": [ "One of the main goals of this study was to refine the lattice\n", @@ -2100,7 +2115,7 @@ }, { "cell_type": "markdown", - "id": "160", + "id": "161", "metadata": {}, "source": [ "#### Exercise 5.5: Visualize the Fit Results in d-spacing\n", @@ -2111,7 +2126,7 @@ }, { "cell_type": "markdown", - "id": "161", + "id": "162", "metadata": {}, "source": [ "**Hint:**" @@ -2119,7 +2134,7 @@ }, { "cell_type": "markdown", - "id": "162", + "id": "163", "metadata": {}, "source": [ "Use the `plot_meas_vs_calc` method of the project and set the\n", @@ -2128,7 +2143,7 @@ }, { "cell_type": "markdown", - "id": "163", + "id": "164", "metadata": {}, "source": [ "**Solution:**" @@ -2137,7 +2152,7 @@ { "cell_type": "code", "execution_count": null, - "id": "164", + "id": "165", "metadata": {}, "outputs": [], "source": [ @@ -2146,7 +2161,7 @@ }, { "cell_type": "markdown", - "id": "165", + "id": "166", "metadata": {}, "source": [ "#### Exercise 5.6: Refine the Peak Profile Parameters\n", @@ -2166,7 +2181,7 @@ { "cell_type": "code", "execution_count": null, - "id": "166", + "id": "167", "metadata": {}, "outputs": [], "source": [ @@ -2175,7 +2190,7 @@ }, { "cell_type": "markdown", - "id": "167", + "id": "168", "metadata": {}, "source": [ "The peak profile parameters are determined based on both the\n", @@ -2189,7 +2204,7 @@ }, { "cell_type": "markdown", - "id": "168", + "id": "169", "metadata": {}, "source": [ "**Hint:**" @@ -2197,7 +2212,7 @@ }, { "cell_type": "markdown", - "id": "169", + "id": "170", "metadata": {}, "source": [ "You can set the `free` attribute of the peak profile parameters to\n", @@ -2208,7 +2223,7 @@ }, { "cell_type": "markdown", - "id": "170", + "id": "171", "metadata": {}, "source": [ "**Solution:**" @@ -2217,7 +2232,7 @@ { "cell_type": "code", "execution_count": null, - "id": "171", + "id": "172", "metadata": {}, "outputs": [], "source": [ @@ -2237,7 +2252,7 @@ }, { "cell_type": "markdown", - "id": "172", + "id": "173", "metadata": {}, "source": [ "#### Exercise 5.7: Find Undefined Features\n", @@ -2249,7 +2264,7 @@ }, { "cell_type": "markdown", - "id": "173", + "id": "174", "metadata": {}, "source": [ "**Hint:**" @@ -2257,7 +2272,7 @@ }, { "cell_type": "markdown", - "id": "174", + "id": "175", "metadata": {}, "source": [ "While the fit is now significantly better, there are still some\n", @@ -2269,7 +2284,7 @@ }, { "cell_type": "markdown", - "id": "175", + "id": "176", "metadata": {}, "source": [ "**Solution:**" @@ -2278,7 +2293,7 @@ { "cell_type": "code", "execution_count": null, - "id": "176", + "id": "177", "metadata": {}, "outputs": [], "source": [ @@ -2287,7 +2302,7 @@ }, { "cell_type": "markdown", - "id": "177", + "id": "178", "metadata": {}, "source": [ "#### Exercise 5.8: Identify the Cause of the Unexplained Peaks\n", @@ -2300,7 +2315,7 @@ }, { "cell_type": "markdown", - "id": "178", + "id": "179", "metadata": {}, "source": [ "**Hint:**" @@ -2308,7 +2323,7 @@ }, { "cell_type": "markdown", - "id": "179", + "id": "180", "metadata": {}, "source": [ "Consider the following options:\n", @@ -2320,7 +2335,7 @@ }, { "cell_type": "markdown", - "id": "180", + "id": "181", "metadata": {}, "source": [ "**Solution:**" @@ -2328,7 +2343,7 @@ }, { "cell_type": "markdown", - "id": "181", + "id": "182", "metadata": {}, "source": [ "1. ❌ In principle, this could be the case, as sometimes the presence\n", @@ -2348,7 +2363,7 @@ }, { "cell_type": "markdown", - "id": "182", + "id": "183", "metadata": {}, "source": [ "#### Exercise 5.9: Identify the impurity phase\n", @@ -2359,7 +2374,7 @@ }, { "cell_type": "markdown", - "id": "183", + "id": "184", "metadata": {}, "source": [ "**Hint:**" @@ -2367,7 +2382,7 @@ }, { "cell_type": "markdown", - "id": "184", + "id": "185", "metadata": {}, "source": [ "Check the positions of the unexplained peaks in the diffraction\n", @@ -2377,7 +2392,7 @@ }, { "cell_type": "markdown", - "id": "185", + "id": "186", "metadata": {}, "source": [ "**Solution:**" @@ -2385,7 +2400,7 @@ }, { "cell_type": "markdown", - "id": "186", + "id": "187", "metadata": {}, "source": [ "The unexplained peaks are likely due to the presence of a small amount\n", @@ -2400,7 +2415,7 @@ { "cell_type": "code", "execution_count": null, - "id": "187", + "id": "188", "metadata": {}, "outputs": [], "source": [ @@ -2410,7 +2425,7 @@ }, { "cell_type": "markdown", - "id": "188", + "id": "189", "metadata": {}, "source": [ "#### Exercise 5.10: Create a Second Structure – Si as Impurity\n", @@ -2422,7 +2437,7 @@ }, { "cell_type": "markdown", - "id": "189", + "id": "190", "metadata": {}, "source": [ "**Hint:**" @@ -2430,7 +2445,7 @@ }, { "cell_type": "markdown", - "id": "190", + "id": "191", "metadata": {}, "source": [ "You can use the same approach as in the previous part of the notebook,\n", @@ -2440,7 +2455,7 @@ }, { "cell_type": "markdown", - "id": "191", + "id": "192", "metadata": {}, "source": [ "**Solution:**" @@ -2449,7 +2464,7 @@ { "cell_type": "code", "execution_count": null, - "id": "192", + "id": "193", "metadata": {}, "outputs": [], "source": [ @@ -2478,7 +2493,7 @@ }, { "cell_type": "markdown", - "id": "193", + "id": "194", "metadata": {}, "source": [ "#### Exercise 5.11: Refine the Scale of the Si Phase\n", @@ -2491,7 +2506,7 @@ }, { "cell_type": "markdown", - "id": "194", + "id": "195", "metadata": {}, "source": [ "**Hint:**" @@ -2499,7 +2514,7 @@ }, { "cell_type": "markdown", - "id": "195", + "id": "196", "metadata": {}, "source": [ "You can use the `plot_meas_vs_calc` method of the project to visualize\n", @@ -2510,7 +2525,7 @@ }, { "cell_type": "markdown", - "id": "196", + "id": "197", "metadata": {}, "source": [ "**Solution:**" @@ -2519,7 +2534,7 @@ { "cell_type": "code", "execution_count": null, - "id": "197", + "id": "198", "metadata": {}, "outputs": [], "source": [ @@ -2548,7 +2563,7 @@ }, { "cell_type": "markdown", - "id": "198", + "id": "199", "metadata": {}, "source": [ "All previously unexplained peaks are now accounted for in the pattern,\n", @@ -2571,7 +2586,7 @@ { "cell_type": "code", "execution_count": null, - "id": "199", + "id": "200", "metadata": {}, "outputs": [], "source": [ @@ -2580,7 +2595,7 @@ }, { "cell_type": "markdown", - "id": "200", + "id": "201", "metadata": {}, "source": [ "Finally, we save the project to disk to preserve the current state of\n", @@ -2590,7 +2605,7 @@ { "cell_type": "code", "execution_count": null, - "id": "201", + "id": "202", "metadata": {}, "outputs": [], "source": [ @@ -2599,7 +2614,7 @@ }, { "cell_type": "markdown", - "id": "202", + "id": "203", "metadata": {}, "source": [ "#### Final Remarks\n", @@ -2618,7 +2633,7 @@ }, { "cell_type": "markdown", - "id": "203", + "id": "204", "metadata": {}, "source": [ "## 🎁 Bonus\n", @@ -2647,7 +2662,7 @@ ], "metadata": { "jupytext": { - "cell_metadata_filter": "title,tags,-all", + "cell_metadata_filter": "tags,title,-all", "main_language": "python", "notebook_metadata_filter": "-all" } diff --git a/docs/docs/tutorials/ed-14.ipynb b/docs/docs/tutorials/ed-14.ipynb index 5c1f6717..a5db5807 100644 --- a/docs/docs/tutorials/ed-14.ipynb +++ b/docs/docs/tutorials/ed-14.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "502ed03e", + "id": "5bd8b55f", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Structure Refinement: Tb2TiO7, HEiDi\n", "\n", @@ -32,7 +47,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -41,7 +56,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -50,7 +65,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Step 1: Define Project" @@ -59,7 +74,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -69,7 +84,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "## Step 2: Define Structure" @@ -78,7 +93,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -89,7 +104,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -99,7 +114,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -109,7 +124,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -119,7 +134,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -132,7 +147,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -141,7 +156,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "13", "metadata": {}, "source": [ "## Step 3: Define Experiment" @@ -150,7 +165,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -160,7 +175,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -176,7 +191,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -186,7 +201,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -197,7 +212,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -207,7 +222,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -217,7 +232,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "20", "metadata": {}, "source": [ "## Step 4: Perform Analysis" @@ -226,7 +241,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -236,7 +251,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -247,7 +262,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -257,7 +272,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -269,7 +284,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -280,7 +295,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -290,7 +305,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -300,7 +315,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -309,7 +324,7 @@ }, { "cell_type": "markdown", - "id": "28", + "id": "29", "metadata": {}, "source": [ "## Step 5: Show Project Summary" @@ -318,7 +333,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "30", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-15.ipynb b/docs/docs/tutorials/ed-15.ipynb index 6e2b2547..cdd646ac 100644 --- a/docs/docs/tutorials/ed-15.ipynb +++ b/docs/docs/tutorials/ed-15.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "65ccac80", + "id": "4e0ef3ea", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Structure Refinement: Taurine, SENJU\n", "\n", @@ -32,7 +47,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -41,7 +56,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -50,7 +65,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Step 1: Define Project" @@ -59,7 +74,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -69,7 +84,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "## Step 2: Define Structure" @@ -78,7 +93,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -89,7 +104,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -99,7 +114,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -109,7 +124,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -119,7 +134,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -128,7 +143,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "## Step 3: Define Experiment" @@ -137,7 +152,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -147,7 +162,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -163,7 +178,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -173,7 +188,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -184,7 +199,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -194,7 +209,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "18", "metadata": {}, "source": [ "## Step 4: Perform Analysis" @@ -203,7 +218,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -213,7 +228,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -224,7 +239,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -234,7 +249,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -246,7 +261,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -257,7 +272,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -267,7 +282,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -277,7 +292,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -286,7 +301,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "27", "metadata": {}, "source": [ "## Step 5: Show Project Summary" @@ -295,7 +310,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "28", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-16.ipynb b/docs/docs/tutorials/ed-16.ipynb index 4cb7d1be..458ded2c 100644 --- a/docs/docs/tutorials/ed-16.ipynb +++ b/docs/docs/tutorials/ed-16.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d57a9295", + "id": "7311bb93", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Joint Refinement: Si, Bragg + PDF\n", "\n", @@ -36,7 +51,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -45,7 +60,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -57,7 +72,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Define Structure\n", @@ -72,7 +87,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -81,7 +96,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "#### Set Space Group" @@ -90,7 +105,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -100,7 +115,7 @@ }, { "cell_type": "markdown", - "id": "7", + "id": "8", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -109,7 +124,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -118,7 +133,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "10", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -127,7 +142,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -144,7 +159,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "## Define Experiments\n", @@ -160,7 +175,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -169,7 +184,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "14", "metadata": {}, "source": [ "#### Create Experiment" @@ -178,7 +193,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -189,7 +204,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "16", "metadata": {}, "source": [ "#### Set Instrument" @@ -198,7 +213,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -210,7 +225,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "18", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -219,7 +234,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -235,7 +250,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "20", "metadata": {}, "source": [ "#### Set Background" @@ -244,7 +259,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -255,7 +270,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "22", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -264,7 +279,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -273,7 +288,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "24", "metadata": {}, "source": [ "### Experiment 2: PDF (NOMAD, TOF)\n", @@ -284,7 +299,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -293,7 +308,7 @@ }, { "cell_type": "markdown", - "id": "25", + "id": "26", "metadata": {}, "source": [ "#### Create Experiment" @@ -302,7 +317,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -316,7 +331,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "28", "metadata": {}, "source": [ "#### Set Peak Profile (PDF Parameters)" @@ -325,7 +340,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -339,7 +354,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "30", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -348,7 +363,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -357,7 +372,7 @@ }, { "cell_type": "markdown", - "id": "31", + "id": "32", "metadata": {}, "source": [ "## Define Project\n", @@ -371,7 +386,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -380,7 +395,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "34", "metadata": {}, "source": [ "#### Add Structure" @@ -389,7 +404,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -398,7 +413,7 @@ }, { "cell_type": "markdown", - "id": "35", + "id": "36", "metadata": {}, "source": [ "#### Add Experiments" @@ -407,7 +422,7 @@ { "cell_type": "code", "execution_count": null, - "id": "36", + "id": "37", "metadata": {}, "outputs": [], "source": [ @@ -417,7 +432,7 @@ }, { "cell_type": "markdown", - "id": "37", + "id": "38", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -431,7 +446,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "39", "metadata": {}, "outputs": [], "source": [ @@ -442,7 +457,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "40", "metadata": {}, "source": [ "#### Set Minimizer" @@ -451,7 +466,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -460,7 +475,7 @@ }, { "cell_type": "markdown", - "id": "41", + "id": "42", "metadata": {}, "source": [ "#### Plot Measured vs Calculated (Before Fit)" @@ -469,7 +484,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -479,7 +494,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "44", "metadata": {}, "outputs": [], "source": [ @@ -488,7 +503,7 @@ }, { "cell_type": "markdown", - "id": "44", + "id": "45", "metadata": {}, "source": [ "#### Set Fitting Parameters\n", @@ -500,7 +515,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "46", "metadata": {}, "outputs": [], "source": [ @@ -510,7 +525,7 @@ }, { "cell_type": "markdown", - "id": "46", + "id": "47", "metadata": {}, "source": [ "Bragg experiment parameters." @@ -519,7 +534,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "48", "metadata": {}, "outputs": [], "source": [ @@ -534,7 +549,7 @@ }, { "cell_type": "markdown", - "id": "48", + "id": "49", "metadata": {}, "source": [ "PDF experiment parameters." @@ -543,7 +558,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "50", "metadata": {}, "outputs": [], "source": [ @@ -556,7 +571,7 @@ }, { "cell_type": "markdown", - "id": "50", + "id": "51", "metadata": {}, "source": [ "#### Show Free Parameters" @@ -565,7 +580,7 @@ { "cell_type": "code", "execution_count": null, - "id": "51", + "id": "52", "metadata": {}, "outputs": [], "source": [ @@ -574,7 +589,7 @@ }, { "cell_type": "markdown", - "id": "52", + "id": "53", "metadata": {}, "source": [ "#### Run Fitting" @@ -583,7 +598,7 @@ { "cell_type": "code", "execution_count": null, - "id": "53", + "id": "54", "metadata": {}, "outputs": [], "source": [ @@ -593,7 +608,7 @@ }, { "cell_type": "markdown", - "id": "54", + "id": "55", "metadata": {}, "source": [ "#### Plot Measured vs Calculated (After Fit)" @@ -602,7 +617,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "56", "metadata": {}, "outputs": [], "source": [ @@ -612,7 +627,7 @@ { "cell_type": "code", "execution_count": null, - "id": "56", + "id": "57", "metadata": { "lines_to_next_cell": 2 }, @@ -624,7 +639,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57", + "id": "58", "metadata": {}, "outputs": [], "source": [] diff --git a/docs/docs/tutorials/ed-17.ipynb b/docs/docs/tutorials/ed-17.ipynb index bbb422ca..08eb5a20 100644 --- a/docs/docs/tutorials/ed-17.ipynb +++ b/docs/docs/tutorials/ed-17.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9fd2be7c", + "id": "1c3cb779", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Structure Refinement: Co2SiO4, D20 (T-scan)\n", "\n", @@ -35,7 +50,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -44,7 +59,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -53,7 +68,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Step 1: Define Project\n", @@ -64,7 +79,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -73,7 +88,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "Set output verbosity level to \"short\" to show only one-line status\n", @@ -83,7 +98,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -92,7 +107,7 @@ }, { "cell_type": "markdown", - "id": "7", + "id": "8", "metadata": {}, "source": [ "## Step 2: Define Crystal Structure\n", @@ -106,7 +121,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -116,7 +131,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "10", "metadata": {}, "source": [ "#### Set Space Group" @@ -125,7 +140,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -135,7 +150,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -144,7 +159,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -155,7 +170,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "14", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -164,7 +179,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -226,7 +241,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "16", "metadata": {}, "source": [ "## Step 3: Define Experiments\n", @@ -240,7 +255,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -249,7 +264,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "18", "metadata": {}, "source": [ "#### Create Experiments and Set Temperature" @@ -258,7 +273,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -278,7 +293,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "20", "metadata": {}, "source": [ "#### Set Instrument" @@ -287,7 +302,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -298,7 +313,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "22", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -307,7 +322,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -320,7 +335,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "24", "metadata": {}, "source": [ "#### Set Excluded Regions" @@ -329,7 +344,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -340,7 +355,7 @@ }, { "cell_type": "markdown", - "id": "25", + "id": "26", "metadata": {}, "source": [ "#### Set Background" @@ -349,7 +364,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -372,7 +387,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "28", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -381,7 +396,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -391,7 +406,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "30", "metadata": {}, "source": [ "## Step 4: Perform Analysis\n", @@ -402,7 +417,7 @@ }, { "cell_type": "markdown", - "id": "30", + "id": "31", "metadata": {}, "source": [ "#### Set Free Parameters" @@ -411,7 +426,7 @@ { "cell_type": "code", "execution_count": null, - "id": "31", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -442,7 +457,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -462,7 +477,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "34", "metadata": {}, "source": [ "#### Set Constraints\n", @@ -473,23 +488,23 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "35", "metadata": {}, "outputs": [], "source": [ "project.analysis.aliases.create(\n", " label='biso_Co1',\n", - " param_uid=structure.atom_sites['Co1'].b_iso.uid,\n", + " param=structure.atom_sites['Co1'].b_iso,\n", ")\n", "project.analysis.aliases.create(\n", " label='biso_Co2',\n", - " param_uid=structure.atom_sites['Co2'].b_iso.uid,\n", + " param=structure.atom_sites['Co2'].b_iso,\n", ")" ] }, { "cell_type": "markdown", - "id": "35", + "id": "36", "metadata": {}, "source": [ "Set constraints." @@ -498,8 +513,10 @@ { "cell_type": "code", "execution_count": null, - "id": "36", - "metadata": {}, + "id": "37", + "metadata": { + "lines_to_next_cell": 2 + }, "outputs": [], "source": [ "project.analysis.constraints.create(\n", @@ -509,26 +526,8 @@ }, { "cell_type": "markdown", - "id": "37", - "metadata": {}, - "source": [ - "Apply constraints." - ] - }, - { - "cell_type": "code", - "execution_count": null, "id": "38", "metadata": {}, - "outputs": [], - "source": [ - "project.analysis.apply_constraints()" - ] - }, - { - "cell_type": "markdown", - "id": "39", - "metadata": {}, "source": [ "#### Set Fit Mode" ] @@ -536,7 +535,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "39", "metadata": {}, "outputs": [], "source": [ @@ -545,7 +544,7 @@ }, { "cell_type": "markdown", - "id": "41", + "id": "40", "metadata": {}, "source": [ "#### Run Fitting" @@ -554,7 +553,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -563,7 +562,7 @@ }, { "cell_type": "markdown", - "id": "43", + "id": "42", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -572,7 +571,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -582,7 +581,7 @@ }, { "cell_type": "markdown", - "id": "45", + "id": "44", "metadata": {}, "source": [ "#### Plot Parameter Evolution\n", @@ -593,7 +592,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -602,7 +601,7 @@ }, { "cell_type": "markdown", - "id": "47", + "id": "46", "metadata": {}, "source": [ "Plot unit cell parameters vs. temperature." @@ -611,7 +610,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "47", "metadata": {}, "outputs": [], "source": [ @@ -622,7 +621,7 @@ }, { "cell_type": "markdown", - "id": "49", + "id": "48", "metadata": {}, "source": [ "Plot isotropic displacement parameters vs. temperature." @@ -631,7 +630,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50", + "id": "49", "metadata": {}, "outputs": [], "source": [ @@ -644,7 +643,7 @@ }, { "cell_type": "markdown", - "id": "51", + "id": "50", "metadata": {}, "source": [ "Plot selected fractional coordinates vs. temperature." @@ -653,7 +652,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "51", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-18.ipynb b/docs/docs/tutorials/ed-18.ipynb new file mode 100644 index 00000000..9f51a133 --- /dev/null +++ b/docs/docs/tutorials/ed-18.ipynb @@ -0,0 +1,225 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "a3ec0a8c", + "metadata": { + "tags": [ + "hide-in-docs" + ] + }, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0", + "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "# Load Project and Fit: LBCO, HRPT\n", + "\n", + "This is the most minimal example of using EasyDiffraction. It shows\n", + "how to load a previously saved project from a directory and run\n", + "refinement — all in just a few lines of code.\n", + "\n", + "The project is first created and saved as a setup step (this would\n", + "normally be done once and the directory would already exist on disk).\n", + "Then the saved project is loaded back and fitted.\n", + "\n", + "For details on how to define structures and experiments, see the other\n", + "tutorials." + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "import easydiffraction as ed" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "## Setup: Create and Save a Project\n", + "\n", + "This step creates a project from CIF files and saves it to a\n", + "directory. In practice, the project directory would already exist\n", + "on disk from a previous session." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a project from CIF files\n", + "project = ed.Project()\n", + "project.structures.add_from_cif_path(ed.download_data(id=1, destination='data'))\n", + "project.experiments.add_from_cif_path(ed.download_data(id=2, destination='data'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.aliases.create(\n", + " label='biso_La',\n", + " param=project.structures['lbco'].atom_sites['La'].b_iso,\n", + ")\n", + "project.analysis.aliases.create(\n", + " label='biso_Ba',\n", + " param=project.structures['lbco'].atom_sites['Ba'].b_iso,\n", + ")\n", + "\n", + "project.analysis.aliases.create(\n", + " label='occ_La',\n", + " param=project.structures['lbco'].atom_sites['La'].occupancy,\n", + ")\n", + "project.analysis.aliases.create(\n", + " label='occ_Ba',\n", + " param=project.structures['lbco'].atom_sites['Ba'].occupancy,\n", + ")\n", + "\n", + "project.analysis.constraints.create(expression='biso_Ba = biso_La')\n", + "project.analysis.constraints.create(expression='occ_Ba = 1 - occ_La')\n", + "\n", + "project.structures['lbco'].atom_sites['La'].occupancy.free = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "# Save to a directory\n", + "project.save_as('lbco_project')" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "## Step 1: Load Project from Directory" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "project = ed.Project.load('lbco_project')" + ] + }, + { + "cell_type": "markdown", + "id": "10", + "metadata": {}, + "source": [ + "## Step 2: Perform Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.fit()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "project.analysis.show_fit_results()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "14", + "metadata": {}, + "source": [ + "## Step 3: Show Project Summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "project.summary.show_report()" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/tutorials/ed-2.ipynb b/docs/docs/tutorials/ed-2.ipynb index 06f40dc9..cb95e408 100644 --- a/docs/docs/tutorials/ed-2.ipynb +++ b/docs/docs/tutorials/ed-2.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7dda0a11", + "id": "d9a613b4", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Structure Refinement: LBCO, HRPT\n", "\n", @@ -48,7 +63,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -57,7 +72,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -66,7 +81,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Step 1: Define Project" @@ -75,7 +90,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -84,7 +99,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "## Step 2: Define Structure" @@ -93,7 +108,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -103,7 +118,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -113,7 +128,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -124,7 +139,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -134,7 +149,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -180,7 +195,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "## Step 3: Define Experiment" @@ -189,7 +204,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -199,7 +214,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -215,7 +230,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -225,7 +240,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -236,7 +251,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -249,7 +264,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -263,7 +278,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -274,7 +289,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -283,7 +298,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "21", "metadata": {}, "source": [ "## Step 4: Perform Analysis" @@ -292,7 +307,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -307,7 +322,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "23", "metadata": { "lines_to_next_cell": 2 }, @@ -332,7 +347,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -343,7 +358,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "25", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-3.ipynb b/docs/docs/tutorials/ed-3.ipynb index f64a041a..3dd711e2 100644 --- a/docs/docs/tutorials/ed-3.ipynb +++ b/docs/docs/tutorials/ed-3.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "be19f628", + "id": "d59d709c", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Structure Refinement: LBCO, HRPT\n", "\n", @@ -46,7 +61,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -55,7 +70,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -64,7 +79,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Step 1: Create a Project\n", @@ -74,7 +89,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "5", "metadata": {}, "source": [ "#### Create Project" @@ -83,7 +98,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -92,7 +107,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "7", "metadata": {}, "source": [ "#### Set Project Metadata" @@ -101,7 +116,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -114,7 +129,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "9", "metadata": {}, "source": [ "#### Show Project Metadata as CIF" @@ -123,7 +138,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -132,7 +147,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "11", "metadata": {}, "source": [ "#### Save Project\n", @@ -145,7 +160,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -154,7 +169,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "13", "metadata": {}, "source": [ "#### Set Up Data Plotter" @@ -162,7 +177,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "14", "metadata": {}, "source": [ "Show supported plotting engines." @@ -171,7 +186,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -180,7 +195,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "16", "metadata": {}, "source": [ "Show current plotting configuration." @@ -189,7 +204,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -198,7 +213,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "18", "metadata": {}, "source": [ "Set plotting engine." @@ -207,7 +222,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -218,7 +233,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "20", "metadata": {}, "source": [ "## Step 2: Define Structure\n", @@ -229,7 +244,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "21", "metadata": {}, "source": [ "#### Add Structure" @@ -238,7 +253,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -247,7 +262,7 @@ }, { "cell_type": "markdown", - "id": "22", + "id": "23", "metadata": {}, "source": [ "#### Show Defined Structures\n", @@ -261,7 +276,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -270,7 +285,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "25", "metadata": {}, "source": [ "#### Set Space Group\n", @@ -281,7 +296,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -291,7 +306,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "27", "metadata": {}, "source": [ "#### Set Unit Cell\n", @@ -302,7 +317,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -311,7 +326,7 @@ }, { "cell_type": "markdown", - "id": "28", + "id": "29", "metadata": {}, "source": [ "#### Set Atom Sites\n", @@ -322,7 +337,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -368,7 +383,7 @@ }, { "cell_type": "markdown", - "id": "30", + "id": "31", "metadata": {}, "source": [ "#### Show Structure as CIF" @@ -377,7 +392,7 @@ { "cell_type": "code", "execution_count": null, - "id": "31", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -386,7 +401,7 @@ }, { "cell_type": "markdown", - "id": "32", + "id": "33", "metadata": {}, "source": [ "#### Show Structure Structure" @@ -395,7 +410,7 @@ { "cell_type": "code", "execution_count": null, - "id": "33", + "id": "34", "metadata": {}, "outputs": [], "source": [ @@ -404,7 +419,7 @@ }, { "cell_type": "markdown", - "id": "34", + "id": "35", "metadata": {}, "source": [ "#### Save Project State\n", @@ -417,7 +432,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "36", "metadata": {}, "outputs": [], "source": [ @@ -426,7 +441,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "37", "metadata": {}, "source": [ "## Step 3: Define Experiment\n", @@ -437,7 +452,7 @@ }, { "cell_type": "markdown", - "id": "37", + "id": "38", "metadata": {}, "source": [ "#### Download Measured Data\n", @@ -448,7 +463,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "39", "metadata": {}, "outputs": [], "source": [ @@ -457,7 +472,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "40", "metadata": {}, "source": [ "#### Add Diffraction Experiment" @@ -466,7 +481,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -481,7 +496,7 @@ }, { "cell_type": "markdown", - "id": "41", + "id": "42", "metadata": {}, "source": [ "#### Show Defined Experiments" @@ -490,7 +505,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -499,7 +514,7 @@ }, { "cell_type": "markdown", - "id": "43", + "id": "44", "metadata": {}, "source": [ "#### Show Measured Data" @@ -508,7 +523,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -517,7 +532,7 @@ }, { "cell_type": "markdown", - "id": "45", + "id": "46", "metadata": {}, "source": [ "#### Set Instrument\n", @@ -528,7 +543,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "47", "metadata": {}, "outputs": [], "source": [ @@ -538,7 +553,7 @@ }, { "cell_type": "markdown", - "id": "47", + "id": "48", "metadata": {}, "source": [ "#### Set Peak Profile\n", @@ -549,7 +564,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "49", "metadata": {}, "outputs": [], "source": [ @@ -558,7 +573,7 @@ }, { "cell_type": "markdown", - "id": "49", + "id": "50", "metadata": {}, "source": [ "Show the current peak profile type." @@ -567,7 +582,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50", + "id": "51", "metadata": {}, "outputs": [], "source": [ @@ -576,7 +591,7 @@ }, { "cell_type": "markdown", - "id": "51", + "id": "52", "metadata": {}, "source": [ "Select the desired peak profile type." @@ -585,7 +600,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "53", "metadata": {}, "outputs": [], "source": [ @@ -594,7 +609,7 @@ }, { "cell_type": "markdown", - "id": "53", + "id": "54", "metadata": {}, "source": [ "Modify default peak profile parameters." @@ -603,7 +618,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "55", "metadata": {}, "outputs": [], "source": [ @@ -616,7 +631,7 @@ }, { "cell_type": "markdown", - "id": "55", + "id": "56", "metadata": {}, "source": [ "#### Set Background" @@ -624,7 +639,7 @@ }, { "cell_type": "markdown", - "id": "56", + "id": "57", "metadata": {}, "source": [ "Show supported background types." @@ -633,7 +648,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57", + "id": "58", "metadata": {}, "outputs": [], "source": [ @@ -642,7 +657,7 @@ }, { "cell_type": "markdown", - "id": "58", + "id": "59", "metadata": {}, "source": [ "Show current background type." @@ -651,7 +666,7 @@ { "cell_type": "code", "execution_count": null, - "id": "59", + "id": "60", "metadata": {}, "outputs": [], "source": [ @@ -660,7 +675,7 @@ }, { "cell_type": "markdown", - "id": "60", + "id": "61", "metadata": {}, "source": [ "Select the desired background type." @@ -669,7 +684,7 @@ { "cell_type": "code", "execution_count": null, - "id": "61", + "id": "62", "metadata": {}, "outputs": [], "source": [ @@ -678,7 +693,7 @@ }, { "cell_type": "markdown", - "id": "62", + "id": "63", "metadata": {}, "source": [ "Add background points." @@ -687,7 +702,7 @@ { "cell_type": "code", "execution_count": null, - "id": "63", + "id": "64", "metadata": {}, "outputs": [], "source": [ @@ -700,7 +715,7 @@ }, { "cell_type": "markdown", - "id": "64", + "id": "65", "metadata": {}, "source": [ "Show current background points." @@ -709,7 +724,7 @@ { "cell_type": "code", "execution_count": null, - "id": "65", + "id": "66", "metadata": {}, "outputs": [], "source": [ @@ -718,7 +733,7 @@ }, { "cell_type": "markdown", - "id": "66", + "id": "67", "metadata": {}, "source": [ "#### Set Linked Phases\n", @@ -729,7 +744,7 @@ { "cell_type": "code", "execution_count": null, - "id": "67", + "id": "68", "metadata": {}, "outputs": [], "source": [ @@ -738,7 +753,7 @@ }, { "cell_type": "markdown", - "id": "68", + "id": "69", "metadata": {}, "source": [ "#### Show Experiment as CIF" @@ -747,7 +762,7 @@ { "cell_type": "code", "execution_count": null, - "id": "69", + "id": "70", "metadata": {}, "outputs": [], "source": [ @@ -756,7 +771,7 @@ }, { "cell_type": "markdown", - "id": "70", + "id": "71", "metadata": {}, "source": [ "#### Save Project State" @@ -765,7 +780,7 @@ { "cell_type": "code", "execution_count": null, - "id": "71", + "id": "72", "metadata": {}, "outputs": [], "source": [ @@ -774,7 +789,7 @@ }, { "cell_type": "markdown", - "id": "72", + "id": "73", "metadata": {}, "source": [ "## Step 4: Perform Analysis\n", @@ -790,7 +805,7 @@ { "cell_type": "code", "execution_count": null, - "id": "73", + "id": "74", "metadata": {}, "outputs": [], "source": [ @@ -799,7 +814,7 @@ }, { "cell_type": "markdown", - "id": "74", + "id": "75", "metadata": {}, "source": [ "Show current calculation engine for this experiment." @@ -808,7 +823,7 @@ { "cell_type": "code", "execution_count": null, - "id": "75", + "id": "76", "metadata": {}, "outputs": [], "source": [ @@ -817,7 +832,7 @@ }, { "cell_type": "markdown", - "id": "76", + "id": "77", "metadata": {}, "source": [ "Select the desired calculation engine." @@ -826,7 +841,7 @@ { "cell_type": "code", "execution_count": null, - "id": "77", + "id": "78", "metadata": {}, "outputs": [], "source": [ @@ -835,7 +850,7 @@ }, { "cell_type": "markdown", - "id": "78", + "id": "79", "metadata": {}, "source": [ "#### Show Calculated Data" @@ -844,7 +859,7 @@ { "cell_type": "code", "execution_count": null, - "id": "79", + "id": "80", "metadata": {}, "outputs": [], "source": [ @@ -853,7 +868,7 @@ }, { "cell_type": "markdown", - "id": "80", + "id": "81", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -862,7 +877,7 @@ { "cell_type": "code", "execution_count": null, - "id": "81", + "id": "82", "metadata": {}, "outputs": [], "source": [ @@ -872,7 +887,7 @@ { "cell_type": "code", "execution_count": null, - "id": "82", + "id": "83", "metadata": {}, "outputs": [], "source": [ @@ -881,7 +896,7 @@ }, { "cell_type": "markdown", - "id": "83", + "id": "84", "metadata": {}, "source": [ "#### Show Parameters\n", @@ -892,7 +907,7 @@ { "cell_type": "code", "execution_count": null, - "id": "84", + "id": "85", "metadata": {}, "outputs": [], "source": [ @@ -901,7 +916,7 @@ }, { "cell_type": "markdown", - "id": "85", + "id": "86", "metadata": {}, "source": [ "Show all fittable parameters." @@ -910,7 +925,7 @@ { "cell_type": "code", "execution_count": null, - "id": "86", + "id": "87", "metadata": {}, "outputs": [], "source": [ @@ -919,7 +934,7 @@ }, { "cell_type": "markdown", - "id": "87", + "id": "88", "metadata": {}, "source": [ "Show only free parameters." @@ -928,7 +943,7 @@ { "cell_type": "code", "execution_count": null, - "id": "88", + "id": "89", "metadata": {}, "outputs": [], "source": [ @@ -937,7 +952,7 @@ }, { "cell_type": "markdown", - "id": "89", + "id": "90", "metadata": {}, "source": [ "Show how to access parameters in the code." @@ -946,7 +961,7 @@ { "cell_type": "code", "execution_count": null, - "id": "90", + "id": "91", "metadata": {}, "outputs": [], "source": [ @@ -955,7 +970,7 @@ }, { "cell_type": "markdown", - "id": "91", + "id": "92", "metadata": {}, "source": [ "#### Set Fit Mode\n", @@ -966,7 +981,7 @@ { "cell_type": "code", "execution_count": null, - "id": "92", + "id": "93", "metadata": {}, "outputs": [], "source": [ @@ -975,7 +990,7 @@ }, { "cell_type": "markdown", - "id": "93", + "id": "94", "metadata": {}, "source": [ "Show current fit mode." @@ -984,7 +999,7 @@ { "cell_type": "code", "execution_count": null, - "id": "94", + "id": "95", "metadata": {}, "outputs": [], "source": [ @@ -993,7 +1008,7 @@ }, { "cell_type": "markdown", - "id": "95", + "id": "96", "metadata": {}, "source": [ "Select desired fit mode." @@ -1002,7 +1017,7 @@ { "cell_type": "code", "execution_count": null, - "id": "96", + "id": "97", "metadata": {}, "outputs": [], "source": [ @@ -1011,7 +1026,7 @@ }, { "cell_type": "markdown", - "id": "97", + "id": "98", "metadata": {}, "source": [ "#### Set Minimizer\n", @@ -1022,7 +1037,7 @@ { "cell_type": "code", "execution_count": null, - "id": "98", + "id": "99", "metadata": {}, "outputs": [], "source": [ @@ -1031,7 +1046,7 @@ }, { "cell_type": "markdown", - "id": "99", + "id": "100", "metadata": {}, "source": [ "Show current fitting engine." @@ -1040,7 +1055,7 @@ { "cell_type": "code", "execution_count": null, - "id": "100", + "id": "101", "metadata": {}, "outputs": [], "source": [ @@ -1049,7 +1064,7 @@ }, { "cell_type": "markdown", - "id": "101", + "id": "102", "metadata": {}, "source": [ "Select desired fitting engine." @@ -1058,7 +1073,7 @@ { "cell_type": "code", "execution_count": null, - "id": "102", + "id": "103", "metadata": {}, "outputs": [], "source": [ @@ -1067,7 +1082,7 @@ }, { "cell_type": "markdown", - "id": "103", + "id": "104", "metadata": {}, "source": [ "### Perform Fit 1/5\n", @@ -1078,7 +1093,7 @@ { "cell_type": "code", "execution_count": null, - "id": "104", + "id": "105", "metadata": {}, "outputs": [], "source": [ @@ -1087,7 +1102,7 @@ }, { "cell_type": "markdown", - "id": "105", + "id": "106", "metadata": {}, "source": [ "Set experiment parameters to be refined." @@ -1096,7 +1111,7 @@ { "cell_type": "code", "execution_count": null, - "id": "106", + "id": "107", "metadata": {}, "outputs": [], "source": [ @@ -1111,7 +1126,7 @@ }, { "cell_type": "markdown", - "id": "107", + "id": "108", "metadata": {}, "source": [ "Show free parameters after selection." @@ -1120,7 +1135,7 @@ { "cell_type": "code", "execution_count": null, - "id": "108", + "id": "109", "metadata": {}, "outputs": [], "source": [ @@ -1129,7 +1144,7 @@ }, { "cell_type": "markdown", - "id": "109", + "id": "110", "metadata": {}, "source": [ "#### Run Fitting" @@ -1138,7 +1153,7 @@ { "cell_type": "code", "execution_count": null, - "id": "110", + "id": "111", "metadata": {}, "outputs": [], "source": [ @@ -1148,7 +1163,7 @@ }, { "cell_type": "markdown", - "id": "111", + "id": "112", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -1157,7 +1172,7 @@ { "cell_type": "code", "execution_count": null, - "id": "112", + "id": "113", "metadata": {}, "outputs": [], "source": [ @@ -1167,7 +1182,7 @@ { "cell_type": "code", "execution_count": null, - "id": "113", + "id": "114", "metadata": {}, "outputs": [], "source": [ @@ -1176,7 +1191,7 @@ }, { "cell_type": "markdown", - "id": "114", + "id": "115", "metadata": {}, "source": [ "#### Save Project State" @@ -1185,7 +1200,7 @@ { "cell_type": "code", "execution_count": null, - "id": "115", + "id": "116", "metadata": {}, "outputs": [], "source": [ @@ -1194,7 +1209,7 @@ }, { "cell_type": "markdown", - "id": "116", + "id": "117", "metadata": {}, "source": [ "### Perform Fit 2/5\n", @@ -1205,7 +1220,7 @@ { "cell_type": "code", "execution_count": null, - "id": "117", + "id": "118", "metadata": {}, "outputs": [], "source": [ @@ -1217,7 +1232,7 @@ }, { "cell_type": "markdown", - "id": "118", + "id": "119", "metadata": {}, "source": [ "Show free parameters after selection." @@ -1226,7 +1241,7 @@ { "cell_type": "code", "execution_count": null, - "id": "119", + "id": "120", "metadata": {}, "outputs": [], "source": [ @@ -1235,7 +1250,7 @@ }, { "cell_type": "markdown", - "id": "120", + "id": "121", "metadata": {}, "source": [ "#### Run Fitting" @@ -1244,7 +1259,7 @@ { "cell_type": "code", "execution_count": null, - "id": "121", + "id": "122", "metadata": {}, "outputs": [], "source": [ @@ -1254,7 +1269,7 @@ }, { "cell_type": "markdown", - "id": "122", + "id": "123", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -1263,7 +1278,7 @@ { "cell_type": "code", "execution_count": null, - "id": "123", + "id": "124", "metadata": {}, "outputs": [], "source": [ @@ -1273,7 +1288,7 @@ { "cell_type": "code", "execution_count": null, - "id": "124", + "id": "125", "metadata": {}, "outputs": [], "source": [ @@ -1282,7 +1297,7 @@ }, { "cell_type": "markdown", - "id": "125", + "id": "126", "metadata": {}, "source": [ "#### Save Project State" @@ -1291,7 +1306,7 @@ { "cell_type": "code", "execution_count": null, - "id": "126", + "id": "127", "metadata": {}, "outputs": [], "source": [ @@ -1300,7 +1315,7 @@ }, { "cell_type": "markdown", - "id": "127", + "id": "128", "metadata": {}, "source": [ "### Perform Fit 3/5\n", @@ -1311,7 +1326,7 @@ { "cell_type": "code", "execution_count": null, - "id": "128", + "id": "129", "metadata": {}, "outputs": [], "source": [ @@ -1323,7 +1338,7 @@ }, { "cell_type": "markdown", - "id": "129", + "id": "130", "metadata": {}, "source": [ "Show free parameters after selection." @@ -1332,7 +1347,7 @@ { "cell_type": "code", "execution_count": null, - "id": "130", + "id": "131", "metadata": {}, "outputs": [], "source": [ @@ -1341,7 +1356,7 @@ }, { "cell_type": "markdown", - "id": "131", + "id": "132", "metadata": {}, "source": [ "#### Run Fitting" @@ -1350,7 +1365,7 @@ { "cell_type": "code", "execution_count": null, - "id": "132", + "id": "133", "metadata": {}, "outputs": [], "source": [ @@ -1360,7 +1375,7 @@ }, { "cell_type": "markdown", - "id": "133", + "id": "134", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -1369,7 +1384,7 @@ { "cell_type": "code", "execution_count": null, - "id": "134", + "id": "135", "metadata": {}, "outputs": [], "source": [ @@ -1379,7 +1394,7 @@ { "cell_type": "code", "execution_count": null, - "id": "135", + "id": "136", "metadata": {}, "outputs": [], "source": [ @@ -1388,7 +1403,7 @@ }, { "cell_type": "markdown", - "id": "136", + "id": "137", "metadata": {}, "source": [ "#### Save Project State" @@ -1397,7 +1412,7 @@ { "cell_type": "code", "execution_count": null, - "id": "137", + "id": "138", "metadata": {}, "outputs": [], "source": [ @@ -1406,7 +1421,7 @@ }, { "cell_type": "markdown", - "id": "138", + "id": "139", "metadata": {}, "source": [ "### Perform Fit 4/5\n", @@ -1419,23 +1434,23 @@ { "cell_type": "code", "execution_count": null, - "id": "139", + "id": "140", "metadata": {}, "outputs": [], "source": [ "project.analysis.aliases.create(\n", " label='biso_La',\n", - " param_uid=project.structures['lbco'].atom_sites['La'].b_iso.uid,\n", + " param=project.structures['lbco'].atom_sites['La'].b_iso,\n", ")\n", "project.analysis.aliases.create(\n", " label='biso_Ba',\n", - " param_uid=project.structures['lbco'].atom_sites['Ba'].b_iso.uid,\n", + " param=project.structures['lbco'].atom_sites['Ba'].b_iso,\n", ")" ] }, { "cell_type": "markdown", - "id": "140", + "id": "141", "metadata": {}, "source": [ "Set constraints." @@ -1444,7 +1459,7 @@ { "cell_type": "code", "execution_count": null, - "id": "141", + "id": "142", "metadata": {}, "outputs": [], "source": [ @@ -1453,7 +1468,7 @@ }, { "cell_type": "markdown", - "id": "142", + "id": "143", "metadata": {}, "source": [ "Show defined constraints." @@ -1462,7 +1477,7 @@ { "cell_type": "code", "execution_count": null, - "id": "143", + "id": "144", "metadata": {}, "outputs": [], "source": [ @@ -1471,52 +1486,16 @@ }, { "cell_type": "markdown", - "id": "144", - "metadata": {}, - "source": [ - "Show free parameters before applying constraints." - ] - }, - { - "cell_type": "code", - "execution_count": null, "id": "145", "metadata": {}, - "outputs": [], - "source": [ - "project.analysis.show_free_params()" - ] - }, - { - "cell_type": "markdown", - "id": "146", - "metadata": {}, "source": [ - "Apply constraints." + "Show free parameters." ] }, { "cell_type": "code", "execution_count": null, - "id": "147", - "metadata": {}, - "outputs": [], - "source": [ - "project.analysis.apply_constraints()" - ] - }, - { - "cell_type": "markdown", - "id": "148", - "metadata": {}, - "source": [ - "Show free parameters after applying constraints." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "149", + "id": "146", "metadata": {}, "outputs": [], "source": [ @@ -1525,7 +1504,7 @@ }, { "cell_type": "markdown", - "id": "150", + "id": "147", "metadata": {}, "source": [ "#### Run Fitting" @@ -1534,7 +1513,7 @@ { "cell_type": "code", "execution_count": null, - "id": "151", + "id": "148", "metadata": {}, "outputs": [], "source": [ @@ -1544,7 +1523,7 @@ }, { "cell_type": "markdown", - "id": "152", + "id": "149", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -1553,7 +1532,7 @@ { "cell_type": "code", "execution_count": null, - "id": "153", + "id": "150", "metadata": {}, "outputs": [], "source": [ @@ -1563,7 +1542,7 @@ { "cell_type": "code", "execution_count": null, - "id": "154", + "id": "151", "metadata": {}, "outputs": [], "source": [ @@ -1572,7 +1551,7 @@ }, { "cell_type": "markdown", - "id": "155", + "id": "152", "metadata": {}, "source": [ "#### Save Project State" @@ -1581,7 +1560,7 @@ { "cell_type": "code", "execution_count": null, - "id": "156", + "id": "153", "metadata": {}, "outputs": [], "source": [ @@ -1590,7 +1569,7 @@ }, { "cell_type": "markdown", - "id": "157", + "id": "154", "metadata": {}, "source": [ "### Perform Fit 5/5\n", @@ -1603,23 +1582,23 @@ { "cell_type": "code", "execution_count": null, - "id": "158", + "id": "155", "metadata": {}, "outputs": [], "source": [ "project.analysis.aliases.create(\n", " label='occ_La',\n", - " param_uid=project.structures['lbco'].atom_sites['La'].occupancy.uid,\n", + " param=project.structures['lbco'].atom_sites['La'].occupancy,\n", ")\n", "project.analysis.aliases.create(\n", " label='occ_Ba',\n", - " param_uid=project.structures['lbco'].atom_sites['Ba'].occupancy.uid,\n", + " param=project.structures['lbco'].atom_sites['Ba'].occupancy,\n", ")" ] }, { "cell_type": "markdown", - "id": "159", + "id": "156", "metadata": {}, "source": [ "Set more constraints." @@ -1628,7 +1607,7 @@ { "cell_type": "code", "execution_count": null, - "id": "160", + "id": "157", "metadata": {}, "outputs": [], "source": [ @@ -1639,7 +1618,7 @@ }, { "cell_type": "markdown", - "id": "161", + "id": "158", "metadata": {}, "source": [ "Show defined constraints." @@ -1648,8 +1627,10 @@ { "cell_type": "code", "execution_count": null, - "id": "162", - "metadata": {}, + "id": "159", + "metadata": { + "lines_to_next_cell": 2 + }, "outputs": [], "source": [ "project.analysis.show_constraints()" @@ -1657,25 +1638,7 @@ }, { "cell_type": "markdown", - "id": "163", - "metadata": {}, - "source": [ - "Apply constraints." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "164", - "metadata": {}, - "outputs": [], - "source": [ - "project.analysis.apply_constraints()" - ] - }, - { - "cell_type": "markdown", - "id": "165", + "id": "160", "metadata": {}, "source": [ "Set structure parameters to be refined." @@ -1684,7 +1647,7 @@ { "cell_type": "code", "execution_count": null, - "id": "166", + "id": "161", "metadata": {}, "outputs": [], "source": [ @@ -1693,7 +1656,7 @@ }, { "cell_type": "markdown", - "id": "167", + "id": "162", "metadata": {}, "source": [ "Show free parameters after selection." @@ -1702,7 +1665,7 @@ { "cell_type": "code", "execution_count": null, - "id": "168", + "id": "163", "metadata": {}, "outputs": [], "source": [ @@ -1711,7 +1674,7 @@ }, { "cell_type": "markdown", - "id": "169", + "id": "164", "metadata": {}, "source": [ "#### Run Fitting" @@ -1720,7 +1683,7 @@ { "cell_type": "code", "execution_count": null, - "id": "170", + "id": "165", "metadata": {}, "outputs": [], "source": [ @@ -1730,7 +1693,7 @@ }, { "cell_type": "markdown", - "id": "171", + "id": "166", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -1739,7 +1702,7 @@ { "cell_type": "code", "execution_count": null, - "id": "172", + "id": "167", "metadata": {}, "outputs": [], "source": [ @@ -1749,7 +1712,7 @@ { "cell_type": "code", "execution_count": null, - "id": "173", + "id": "168", "metadata": {}, "outputs": [], "source": [ @@ -1758,7 +1721,7 @@ }, { "cell_type": "markdown", - "id": "174", + "id": "169", "metadata": {}, "source": [ "#### Save Project State" @@ -1767,7 +1730,7 @@ { "cell_type": "code", "execution_count": null, - "id": "175", + "id": "170", "metadata": {}, "outputs": [], "source": [ @@ -1776,7 +1739,7 @@ }, { "cell_type": "markdown", - "id": "176", + "id": "171", "metadata": {}, "source": [ "## Step 5: Summary\n", @@ -1786,7 +1749,7 @@ }, { "cell_type": "markdown", - "id": "177", + "id": "172", "metadata": {}, "source": [ "#### Show Project Summary" @@ -1795,7 +1758,7 @@ { "cell_type": "code", "execution_count": null, - "id": "178", + "id": "173", "metadata": {}, "outputs": [], "source": [ @@ -1805,7 +1768,7 @@ { "cell_type": "code", "execution_count": null, - "id": "179", + "id": "174", "metadata": {}, "outputs": [], "source": [] diff --git a/docs/docs/tutorials/ed-4.ipynb b/docs/docs/tutorials/ed-4.ipynb index 9d9381b6..5bba8e84 100644 --- a/docs/docs/tutorials/ed-4.ipynb +++ b/docs/docs/tutorials/ed-4.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ebfd4b4e", + "id": "0e6027e8", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Structure Refinement: PbSO4, NPD + XRD\n", "\n", @@ -39,7 +54,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -48,7 +63,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -60,7 +75,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Define Structure\n", @@ -74,7 +89,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -83,7 +98,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "#### Set Space Group" @@ -92,7 +107,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -101,7 +116,7 @@ }, { "cell_type": "markdown", - "id": "7", + "id": "8", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -110,7 +125,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -121,7 +136,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "10", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -130,7 +145,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": { "lines_to_next_cell": 2 }, @@ -185,7 +200,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "## Define Experiments\n", @@ -201,7 +216,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -210,7 +225,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "14", "metadata": {}, "source": [ "#### Create Experiment" @@ -219,7 +234,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -232,7 +247,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "16", "metadata": {}, "source": [ "#### Set Instrument" @@ -241,7 +256,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -251,7 +266,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "18", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -260,7 +275,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -273,7 +288,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "20", "metadata": {}, "source": [ "#### Set Background" @@ -281,7 +296,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "21", "metadata": {}, "source": [ "Select the background type." @@ -290,7 +305,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -299,7 +314,7 @@ }, { "cell_type": "markdown", - "id": "22", + "id": "23", "metadata": {}, "source": [ "Add background points." @@ -308,7 +323,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -327,7 +342,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "25", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -336,7 +351,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -345,7 +360,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "27", "metadata": {}, "source": [ "### Experiment 2: xrd\n", @@ -356,7 +371,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -365,7 +380,7 @@ }, { "cell_type": "markdown", - "id": "28", + "id": "29", "metadata": {}, "source": [ "#### Create Experiment" @@ -374,7 +389,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -387,7 +402,7 @@ }, { "cell_type": "markdown", - "id": "30", + "id": "31", "metadata": {}, "source": [ "#### Set Instrument" @@ -396,7 +411,7 @@ { "cell_type": "code", "execution_count": null, - "id": "31", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -406,7 +421,7 @@ }, { "cell_type": "markdown", - "id": "32", + "id": "33", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -415,7 +430,7 @@ { "cell_type": "code", "execution_count": null, - "id": "33", + "id": "34", "metadata": {}, "outputs": [], "source": [ @@ -428,7 +443,7 @@ }, { "cell_type": "markdown", - "id": "34", + "id": "35", "metadata": {}, "source": [ "#### Set Background" @@ -436,7 +451,7 @@ }, { "cell_type": "markdown", - "id": "35", + "id": "36", "metadata": {}, "source": [ "Select background type." @@ -445,7 +460,7 @@ { "cell_type": "code", "execution_count": null, - "id": "36", + "id": "37", "metadata": {}, "outputs": [], "source": [ @@ -454,7 +469,7 @@ }, { "cell_type": "markdown", - "id": "37", + "id": "38", "metadata": {}, "source": [ "Add background points." @@ -463,7 +478,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "39", "metadata": {}, "outputs": [], "source": [ @@ -480,7 +495,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "40", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -489,7 +504,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -498,7 +513,7 @@ }, { "cell_type": "markdown", - "id": "41", + "id": "42", "metadata": {}, "source": [ "## Define Project\n", @@ -512,7 +527,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -521,7 +536,7 @@ }, { "cell_type": "markdown", - "id": "43", + "id": "44", "metadata": {}, "source": [ "#### Add Structure" @@ -530,7 +545,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -539,7 +554,7 @@ }, { "cell_type": "markdown", - "id": "45", + "id": "46", "metadata": {}, "source": [ "#### Add Experiments" @@ -548,7 +563,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "47", "metadata": {}, "outputs": [], "source": [ @@ -558,7 +573,7 @@ }, { "cell_type": "markdown", - "id": "47", + "id": "48", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -572,7 +587,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "49", "metadata": {}, "outputs": [], "source": [ @@ -581,7 +596,7 @@ }, { "cell_type": "markdown", - "id": "49", + "id": "50", "metadata": {}, "source": [ "#### Set Minimizer" @@ -590,7 +605,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50", + "id": "51", "metadata": {}, "outputs": [], "source": [ @@ -599,7 +614,7 @@ }, { "cell_type": "markdown", - "id": "51", + "id": "52", "metadata": {}, "source": [ "#### Set Fitting Parameters\n", @@ -610,7 +625,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "53", "metadata": {}, "outputs": [], "source": [ @@ -621,7 +636,7 @@ }, { "cell_type": "markdown", - "id": "53", + "id": "54", "metadata": {}, "source": [ "Set experiment parameters to be optimized." @@ -630,7 +645,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "55", "metadata": {}, "outputs": [], "source": [ @@ -647,7 +662,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "56", "metadata": {}, "outputs": [], "source": [ @@ -666,7 +681,7 @@ }, { "cell_type": "markdown", - "id": "56", + "id": "57", "metadata": {}, "source": [ "#### Perform Fit" @@ -675,7 +690,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57", + "id": "58", "metadata": {}, "outputs": [], "source": [ @@ -685,7 +700,7 @@ }, { "cell_type": "markdown", - "id": "58", + "id": "59", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -694,7 +709,7 @@ { "cell_type": "code", "execution_count": null, - "id": "59", + "id": "60", "metadata": {}, "outputs": [], "source": [ @@ -704,7 +719,7 @@ { "cell_type": "code", "execution_count": null, - "id": "60", + "id": "61", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-5.ipynb b/docs/docs/tutorials/ed-5.ipynb index f3b3ba67..ef7e672a 100644 --- a/docs/docs/tutorials/ed-5.ipynb +++ b/docs/docs/tutorials/ed-5.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d07ce7b8", + "id": "1302dcf1", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Structure Refinement: Co2SiO4, D20\n", "\n", @@ -33,7 +48,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -42,7 +57,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -54,7 +69,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Define Structure\n", @@ -68,7 +83,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -77,7 +92,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "#### Set Space Group" @@ -86,7 +101,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -96,7 +111,7 @@ }, { "cell_type": "markdown", - "id": "7", + "id": "8", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -105,7 +120,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -116,7 +131,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "10", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -125,7 +140,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -187,7 +202,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "## Define Experiment\n", @@ -201,7 +216,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -210,7 +225,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "14", "metadata": {}, "source": [ "#### Create Experiment" @@ -219,7 +234,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -228,7 +243,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "16", "metadata": {}, "source": [ "#### Set Instrument" @@ -237,7 +252,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -247,7 +262,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "18", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -256,7 +271,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -267,7 +282,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "20", "metadata": {}, "source": [ "#### Set Background" @@ -276,7 +291,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -298,7 +313,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "22", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -307,7 +322,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -316,7 +331,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "24", "metadata": {}, "source": [ "## Define Project\n", @@ -330,7 +345,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -339,7 +354,7 @@ }, { "cell_type": "markdown", - "id": "25", + "id": "26", "metadata": {}, "source": [ "#### Set Plotting Engine" @@ -348,7 +363,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -359,7 +374,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "28", "metadata": {}, "source": [ "#### Add Structure" @@ -368,7 +383,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -377,7 +392,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "30", "metadata": {}, "source": [ "#### Add Experiment" @@ -386,7 +401,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -395,7 +410,7 @@ }, { "cell_type": "markdown", - "id": "31", + "id": "32", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -409,7 +424,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -418,7 +433,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "34", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -427,7 +442,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -437,7 +452,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "36", "metadata": {}, "outputs": [], "source": [ @@ -446,7 +461,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "37", "metadata": {}, "source": [ "#### Set Free Parameters" @@ -455,7 +470,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37", + "id": "38", "metadata": {}, "outputs": [], "source": [ @@ -486,7 +501,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "39", "metadata": {}, "outputs": [], "source": [ @@ -505,7 +520,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "40", "metadata": {}, "source": [ "#### Set Constraints\n", @@ -516,23 +531,23 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "41", "metadata": {}, "outputs": [], "source": [ "project.analysis.aliases.create(\n", " label='biso_Co1',\n", - " param_uid=project.structures['cosio'].atom_sites['Co1'].b_iso.uid,\n", + " param=project.structures['cosio'].atom_sites['Co1'].b_iso,\n", ")\n", "project.analysis.aliases.create(\n", " label='biso_Co2',\n", - " param_uid=project.structures['cosio'].atom_sites['Co2'].b_iso.uid,\n", + " param=project.structures['cosio'].atom_sites['Co2'].b_iso,\n", ")" ] }, { "cell_type": "markdown", - "id": "41", + "id": "42", "metadata": {}, "source": [ "Set constraints." @@ -541,8 +556,10 @@ { "cell_type": "code", "execution_count": null, - "id": "42", - "metadata": {}, + "id": "43", + "metadata": { + "lines_to_next_cell": 2 + }, "outputs": [], "source": [ "project.analysis.constraints.create(\n", @@ -552,26 +569,8 @@ }, { "cell_type": "markdown", - "id": "43", - "metadata": {}, - "source": [ - "Apply constraints." - ] - }, - { - "cell_type": "code", - "execution_count": null, "id": "44", "metadata": {}, - "outputs": [], - "source": [ - "project.analysis.apply_constraints()" - ] - }, - { - "cell_type": "markdown", - "id": "45", - "metadata": {}, "source": [ "#### Run Fitting" ] @@ -579,7 +578,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -589,7 +588,7 @@ }, { "cell_type": "markdown", - "id": "47", + "id": "46", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -598,7 +597,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "47", "metadata": {}, "outputs": [], "source": [ @@ -608,7 +607,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "48", "metadata": {}, "outputs": [], "source": [ @@ -617,7 +616,7 @@ }, { "cell_type": "markdown", - "id": "50", + "id": "49", "metadata": {}, "source": [ "## Summary\n", @@ -627,7 +626,7 @@ }, { "cell_type": "markdown", - "id": "51", + "id": "50", "metadata": {}, "source": [ "#### Show Project Summary" @@ -636,7 +635,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "51", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-6.ipynb b/docs/docs/tutorials/ed-6.ipynb index 70a334b8..c4e2d34d 100644 --- a/docs/docs/tutorials/ed-6.ipynb +++ b/docs/docs/tutorials/ed-6.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "08932f7f", + "id": "a1e678e6", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Structure Refinement: HS, HRPT\n", "\n", @@ -33,7 +48,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -42,7 +57,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -54,7 +69,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Define Structure\n", @@ -68,7 +83,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -77,7 +92,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "#### Set Space Group" @@ -86,7 +101,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -96,7 +111,7 @@ }, { "cell_type": "markdown", - "id": "7", + "id": "8", "metadata": { "lines_to_next_cell": 2 }, @@ -107,7 +122,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -117,7 +132,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "10", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -126,7 +141,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -179,7 +194,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "## Define Experiment\n", @@ -193,7 +208,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -202,7 +217,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "14", "metadata": {}, "source": [ "#### Create Experiment" @@ -211,7 +226,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -220,7 +235,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "16", "metadata": {}, "source": [ "#### Set Instrument" @@ -229,7 +244,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -239,7 +254,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "18", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -248,7 +263,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -261,7 +276,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "20", "metadata": {}, "source": [ "#### Set Background" @@ -270,7 +285,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -287,7 +302,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "22", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -296,7 +311,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -305,7 +320,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "24", "metadata": {}, "source": [ "## Define Project\n", @@ -319,7 +334,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -328,7 +343,7 @@ }, { "cell_type": "markdown", - "id": "25", + "id": "26", "metadata": {}, "source": [ "#### Set Plotting Engine" @@ -337,7 +352,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -348,7 +363,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "28", "metadata": {}, "source": [ "#### Add Structure" @@ -357,7 +372,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -366,7 +381,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "30", "metadata": {}, "source": [ "#### Add Experiment" @@ -375,7 +390,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -384,7 +399,7 @@ }, { "cell_type": "markdown", - "id": "31", + "id": "32", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -398,7 +413,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -407,7 +422,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "34", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -416,7 +431,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -426,7 +441,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "36", "metadata": {}, "outputs": [], "source": [ @@ -435,7 +450,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "37", "metadata": {}, "source": [ "### Perform Fit 1/5\n", @@ -446,7 +461,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37", + "id": "38", "metadata": {}, "outputs": [], "source": [ @@ -459,7 +474,7 @@ }, { "cell_type": "markdown", - "id": "38", + "id": "39", "metadata": {}, "source": [ "Show free parameters after selection." @@ -468,7 +483,7 @@ { "cell_type": "code", "execution_count": null, - "id": "39", + "id": "40", "metadata": {}, "outputs": [], "source": [ @@ -477,7 +492,7 @@ }, { "cell_type": "markdown", - "id": "40", + "id": "41", "metadata": {}, "source": [ "#### Run Fitting" @@ -486,7 +501,7 @@ { "cell_type": "code", "execution_count": null, - "id": "41", + "id": "42", "metadata": {}, "outputs": [], "source": [ @@ -496,7 +511,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -505,7 +520,7 @@ }, { "cell_type": "markdown", - "id": "43", + "id": "44", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -514,7 +529,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -524,7 +539,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "46", "metadata": {}, "outputs": [], "source": [ @@ -533,7 +548,7 @@ }, { "cell_type": "markdown", - "id": "46", + "id": "47", "metadata": {}, "source": [ "### Perform Fit 2/5\n", @@ -544,7 +559,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "48", "metadata": {}, "outputs": [], "source": [ @@ -559,7 +574,7 @@ }, { "cell_type": "markdown", - "id": "48", + "id": "49", "metadata": {}, "source": [ "Show free parameters after selection." @@ -568,7 +583,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "50", "metadata": {}, "outputs": [], "source": [ @@ -577,7 +592,7 @@ }, { "cell_type": "markdown", - "id": "50", + "id": "51", "metadata": {}, "source": [ "#### Run Fitting" @@ -586,7 +601,7 @@ { "cell_type": "code", "execution_count": null, - "id": "51", + "id": "52", "metadata": {}, "outputs": [], "source": [ @@ -596,7 +611,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "53", "metadata": {}, "outputs": [], "source": [ @@ -605,7 +620,7 @@ }, { "cell_type": "markdown", - "id": "53", + "id": "54", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -614,7 +629,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "55", "metadata": {}, "outputs": [], "source": [ @@ -624,7 +639,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "56", "metadata": {}, "outputs": [], "source": [ @@ -633,7 +648,7 @@ }, { "cell_type": "markdown", - "id": "56", + "id": "57", "metadata": {}, "source": [ "### Perform Fit 3/5\n", @@ -644,7 +659,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57", + "id": "58", "metadata": {}, "outputs": [], "source": [ @@ -657,7 +672,7 @@ }, { "cell_type": "markdown", - "id": "58", + "id": "59", "metadata": {}, "source": [ "Show free parameters after selection." @@ -666,7 +681,7 @@ { "cell_type": "code", "execution_count": null, - "id": "59", + "id": "60", "metadata": {}, "outputs": [], "source": [ @@ -675,7 +690,7 @@ }, { "cell_type": "markdown", - "id": "60", + "id": "61", "metadata": {}, "source": [ "#### Run Fitting" @@ -684,7 +699,7 @@ { "cell_type": "code", "execution_count": null, - "id": "61", + "id": "62", "metadata": {}, "outputs": [], "source": [ @@ -694,7 +709,7 @@ { "cell_type": "code", "execution_count": null, - "id": "62", + "id": "63", "metadata": {}, "outputs": [], "source": [ @@ -703,7 +718,7 @@ }, { "cell_type": "markdown", - "id": "63", + "id": "64", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -712,7 +727,7 @@ { "cell_type": "code", "execution_count": null, - "id": "64", + "id": "65", "metadata": {}, "outputs": [], "source": [ @@ -722,7 +737,7 @@ { "cell_type": "code", "execution_count": null, - "id": "65", + "id": "66", "metadata": {}, "outputs": [], "source": [ @@ -731,7 +746,7 @@ }, { "cell_type": "markdown", - "id": "66", + "id": "67", "metadata": {}, "source": [ "### Perform Fit 4/5\n", @@ -742,7 +757,7 @@ { "cell_type": "code", "execution_count": null, - "id": "67", + "id": "68", "metadata": {}, "outputs": [], "source": [ @@ -755,7 +770,7 @@ }, { "cell_type": "markdown", - "id": "68", + "id": "69", "metadata": {}, "source": [ "Show free parameters after selection." @@ -764,7 +779,7 @@ { "cell_type": "code", "execution_count": null, - "id": "69", + "id": "70", "metadata": {}, "outputs": [], "source": [ @@ -773,7 +788,7 @@ }, { "cell_type": "markdown", - "id": "70", + "id": "71", "metadata": {}, "source": [ "#### Run Fitting" @@ -782,7 +797,7 @@ { "cell_type": "code", "execution_count": null, - "id": "71", + "id": "72", "metadata": {}, "outputs": [], "source": [ @@ -792,7 +807,7 @@ { "cell_type": "code", "execution_count": null, - "id": "72", + "id": "73", "metadata": {}, "outputs": [], "source": [ @@ -801,7 +816,7 @@ }, { "cell_type": "markdown", - "id": "73", + "id": "74", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -810,7 +825,7 @@ { "cell_type": "code", "execution_count": null, - "id": "74", + "id": "75", "metadata": {}, "outputs": [], "source": [ @@ -820,7 +835,7 @@ { "cell_type": "code", "execution_count": null, - "id": "75", + "id": "76", "metadata": {}, "outputs": [], "source": [ @@ -829,7 +844,7 @@ }, { "cell_type": "markdown", - "id": "76", + "id": "77", "metadata": {}, "source": [ "## Summary\n", @@ -839,7 +854,7 @@ }, { "cell_type": "markdown", - "id": "77", + "id": "78", "metadata": {}, "source": [ "#### Show Project Summary" @@ -848,7 +863,7 @@ { "cell_type": "code", "execution_count": null, - "id": "78", + "id": "79", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-7.ipynb b/docs/docs/tutorials/ed-7.ipynb index ce490e56..7284f08d 100644 --- a/docs/docs/tutorials/ed-7.ipynb +++ b/docs/docs/tutorials/ed-7.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "229e169b", + "id": "5dd7adf5", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Structure Refinement: Si, SEPD\n", "\n", @@ -33,7 +48,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -42,7 +57,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -54,7 +69,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Define Structure\n", @@ -68,7 +83,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -77,7 +92,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "#### Set Space Group" @@ -86,7 +101,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -96,7 +111,7 @@ }, { "cell_type": "markdown", - "id": "7", + "id": "8", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -105,7 +120,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -114,7 +129,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "10", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -123,7 +138,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -139,7 +154,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "## Define Experiment\n", @@ -153,7 +168,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -162,7 +177,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "14", "metadata": {}, "source": [ "#### Create Experiment" @@ -171,7 +186,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -182,7 +197,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "16", "metadata": {}, "source": [ "#### Set Instrument" @@ -191,7 +206,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -203,7 +218,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "18", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -212,7 +227,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -226,7 +241,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "20", "metadata": {}, "source": [ "#### Set Peak Asymmetry" @@ -235,7 +250,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -245,7 +260,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "22", "metadata": {}, "source": [ "#### Set Background" @@ -254,7 +269,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -265,7 +280,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "24", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -274,7 +289,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -283,7 +298,7 @@ }, { "cell_type": "markdown", - "id": "25", + "id": "26", "metadata": {}, "source": [ "## Define Project\n", @@ -297,7 +312,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -306,7 +321,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "28", "metadata": {}, "source": [ "#### Add Structure" @@ -315,7 +330,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -324,7 +339,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "30", "metadata": {}, "source": [ "#### Add Experiment" @@ -333,7 +348,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -342,7 +357,7 @@ }, { "cell_type": "markdown", - "id": "31", + "id": "32", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -356,7 +371,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -365,7 +380,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "34", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -374,7 +389,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -384,7 +399,7 @@ }, { "cell_type": "markdown", - "id": "35", + "id": "36", "metadata": {}, "source": [ "### Perform Fit 1/5\n", @@ -395,7 +410,7 @@ { "cell_type": "code", "execution_count": null, - "id": "36", + "id": "37", "metadata": {}, "outputs": [], "source": [ @@ -407,7 +422,7 @@ }, { "cell_type": "markdown", - "id": "37", + "id": "38", "metadata": {}, "source": [ "Show free parameters after selection." @@ -416,7 +431,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "39", "metadata": {}, "outputs": [], "source": [ @@ -425,7 +440,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "40", "metadata": {}, "source": [ "#### Run Fitting" @@ -434,7 +449,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -444,7 +459,7 @@ }, { "cell_type": "markdown", - "id": "41", + "id": "42", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -453,7 +468,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -463,7 +478,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "44", "metadata": {}, "outputs": [], "source": [ @@ -472,7 +487,7 @@ }, { "cell_type": "markdown", - "id": "44", + "id": "45", "metadata": {}, "source": [ "### Perform Fit 2/5\n", @@ -483,7 +498,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "46", "metadata": {}, "outputs": [], "source": [ @@ -493,7 +508,7 @@ }, { "cell_type": "markdown", - "id": "46", + "id": "47", "metadata": {}, "source": [ "Show free parameters after selection." @@ -502,7 +517,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "48", "metadata": {}, "outputs": [], "source": [ @@ -511,7 +526,7 @@ }, { "cell_type": "markdown", - "id": "48", + "id": "49", "metadata": {}, "source": [ "#### Run Fitting" @@ -520,7 +535,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "50", "metadata": {}, "outputs": [], "source": [ @@ -530,7 +545,7 @@ }, { "cell_type": "markdown", - "id": "50", + "id": "51", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -539,7 +554,7 @@ { "cell_type": "code", "execution_count": null, - "id": "51", + "id": "52", "metadata": {}, "outputs": [], "source": [ @@ -549,7 +564,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "53", "metadata": {}, "outputs": [], "source": [ @@ -558,7 +573,7 @@ }, { "cell_type": "markdown", - "id": "53", + "id": "54", "metadata": {}, "source": [ "### Perform Fit 3/5\n", @@ -569,7 +584,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "55", "metadata": {}, "outputs": [], "source": [ @@ -579,7 +594,7 @@ }, { "cell_type": "markdown", - "id": "55", + "id": "56", "metadata": {}, "source": [ "Set more parameters to be refined." @@ -588,7 +603,7 @@ { "cell_type": "code", "execution_count": null, - "id": "56", + "id": "57", "metadata": {}, "outputs": [], "source": [ @@ -599,7 +614,7 @@ }, { "cell_type": "markdown", - "id": "57", + "id": "58", "metadata": {}, "source": [ "Show free parameters after selection." @@ -608,7 +623,7 @@ { "cell_type": "code", "execution_count": null, - "id": "58", + "id": "59", "metadata": {}, "outputs": [], "source": [ @@ -617,7 +632,7 @@ }, { "cell_type": "markdown", - "id": "59", + "id": "60", "metadata": {}, "source": [ "#### Run Fitting" @@ -626,7 +641,7 @@ { "cell_type": "code", "execution_count": null, - "id": "60", + "id": "61", "metadata": {}, "outputs": [], "source": [ @@ -636,7 +651,7 @@ }, { "cell_type": "markdown", - "id": "61", + "id": "62", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -645,7 +660,7 @@ { "cell_type": "code", "execution_count": null, - "id": "62", + "id": "63", "metadata": {}, "outputs": [], "source": [ @@ -655,7 +670,7 @@ { "cell_type": "code", "execution_count": null, - "id": "63", + "id": "64", "metadata": {}, "outputs": [], "source": [ @@ -664,7 +679,7 @@ }, { "cell_type": "markdown", - "id": "64", + "id": "65", "metadata": {}, "source": [ "### Perform Fit 4/5\n", @@ -675,7 +690,7 @@ { "cell_type": "code", "execution_count": null, - "id": "65", + "id": "66", "metadata": {}, "outputs": [], "source": [ @@ -684,7 +699,7 @@ }, { "cell_type": "markdown", - "id": "66", + "id": "67", "metadata": {}, "source": [ "Show free parameters after selection." @@ -693,7 +708,7 @@ { "cell_type": "code", "execution_count": null, - "id": "67", + "id": "68", "metadata": {}, "outputs": [], "source": [ @@ -702,7 +717,7 @@ }, { "cell_type": "markdown", - "id": "68", + "id": "69", "metadata": {}, "source": [ "#### Run Fitting" @@ -711,7 +726,7 @@ { "cell_type": "code", "execution_count": null, - "id": "69", + "id": "70", "metadata": {}, "outputs": [], "source": [ @@ -721,7 +736,7 @@ }, { "cell_type": "markdown", - "id": "70", + "id": "71", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -730,7 +745,7 @@ { "cell_type": "code", "execution_count": null, - "id": "71", + "id": "72", "metadata": {}, "outputs": [], "source": [ @@ -740,7 +755,7 @@ { "cell_type": "code", "execution_count": null, - "id": "72", + "id": "73", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-8.ipynb b/docs/docs/tutorials/ed-8.ipynb index 13ce802e..79e03966 100644 --- a/docs/docs/tutorials/ed-8.ipynb +++ b/docs/docs/tutorials/ed-8.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8550c647", + "id": "f45eca14", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Structure Refinement: NCAF, WISH\n", "\n", @@ -36,7 +51,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -45,7 +60,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -57,7 +72,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Define Structure\n", @@ -71,7 +86,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -80,7 +95,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "#### Set Space Group" @@ -89,7 +104,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -99,7 +114,7 @@ }, { "cell_type": "markdown", - "id": "7", + "id": "8", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -108,7 +123,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -117,7 +132,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "10", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -126,7 +141,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -188,7 +203,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "## Define Experiment\n", @@ -202,7 +217,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -212,7 +227,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -221,7 +236,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "15", "metadata": {}, "source": [ "#### Create Experiment" @@ -230,7 +245,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -244,7 +259,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -257,7 +272,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "18", "metadata": {}, "source": [ "#### Set Instrument" @@ -266,7 +281,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -279,7 +294,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -291,7 +306,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "21", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -300,7 +315,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -316,7 +331,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -331,7 +346,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "24", "metadata": {}, "source": [ "#### Set Background" @@ -340,7 +355,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -384,7 +399,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -426,7 +441,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "27", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -435,7 +450,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -445,7 +460,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -454,7 +469,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "30", "metadata": {}, "source": [ "#### Set Excluded Regions" @@ -463,7 +478,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -474,7 +489,7 @@ { "cell_type": "code", "execution_count": null, - "id": "31", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -484,7 +499,7 @@ }, { "cell_type": "markdown", - "id": "32", + "id": "33", "metadata": {}, "source": [ "## Define Project\n", @@ -498,7 +513,7 @@ { "cell_type": "code", "execution_count": null, - "id": "33", + "id": "34", "metadata": {}, "outputs": [], "source": [ @@ -507,7 +522,7 @@ }, { "cell_type": "markdown", - "id": "34", + "id": "35", "metadata": {}, "source": [ "#### Set Plotting Engine" @@ -516,7 +531,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "36", "metadata": {}, "outputs": [], "source": [ @@ -527,7 +542,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "37", "metadata": {}, "source": [ "#### Add Structure" @@ -536,7 +551,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37", + "id": "38", "metadata": {}, "outputs": [], "source": [ @@ -545,7 +560,7 @@ }, { "cell_type": "markdown", - "id": "38", + "id": "39", "metadata": {}, "source": [ "#### Add Experiment" @@ -554,7 +569,7 @@ { "cell_type": "code", "execution_count": null, - "id": "39", + "id": "40", "metadata": {}, "outputs": [], "source": [ @@ -564,7 +579,7 @@ }, { "cell_type": "markdown", - "id": "40", + "id": "41", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -578,7 +593,7 @@ { "cell_type": "code", "execution_count": null, - "id": "41", + "id": "42", "metadata": {}, "outputs": [], "source": [ @@ -587,7 +602,7 @@ }, { "cell_type": "markdown", - "id": "42", + "id": "43", "metadata": {}, "source": [ "#### Set Fit Mode" @@ -596,7 +611,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "44", "metadata": {}, "outputs": [], "source": [ @@ -605,7 +620,7 @@ }, { "cell_type": "markdown", - "id": "44", + "id": "45", "metadata": {}, "source": [ "#### Set Free Parameters" @@ -614,7 +629,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "46", "metadata": {}, "outputs": [], "source": [ @@ -629,7 +644,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "47", "metadata": {}, "outputs": [], "source": [ @@ -652,7 +667,7 @@ }, { "cell_type": "markdown", - "id": "47", + "id": "48", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -661,7 +676,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "49", "metadata": {}, "outputs": [], "source": [ @@ -671,7 +686,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "50", "metadata": {}, "outputs": [], "source": [ @@ -680,7 +695,7 @@ }, { "cell_type": "markdown", - "id": "50", + "id": "51", "metadata": {}, "source": [ "#### Run Fitting" @@ -689,7 +704,7 @@ { "cell_type": "code", "execution_count": null, - "id": "51", + "id": "52", "metadata": {}, "outputs": [], "source": [ @@ -699,7 +714,7 @@ }, { "cell_type": "markdown", - "id": "52", + "id": "53", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -708,7 +723,7 @@ { "cell_type": "code", "execution_count": null, - "id": "53", + "id": "54", "metadata": {}, "outputs": [], "source": [ @@ -718,7 +733,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "55", "metadata": {}, "outputs": [], "source": [ @@ -727,7 +742,7 @@ }, { "cell_type": "markdown", - "id": "55", + "id": "56", "metadata": {}, "source": [ "## Summary\n", @@ -737,7 +752,7 @@ }, { "cell_type": "markdown", - "id": "56", + "id": "57", "metadata": {}, "source": [ "#### Show Project Summary" @@ -746,7 +761,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57", + "id": "58", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-9.ipynb b/docs/docs/tutorials/ed-9.ipynb index 1d3c883d..b9b845da 100644 --- a/docs/docs/tutorials/ed-9.ipynb +++ b/docs/docs/tutorials/ed-9.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "520a3ca6", + "id": "e2e25ed0", "metadata": { "tags": [ "hide-in-docs" @@ -20,9 +20,24 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "0", "metadata": {}, + "outputs": [], + "source": [ + "# Check whether easydiffraction is installed; install it if needed.\n", + "# Required for remote environments such as Google Colab.\n", + "import importlib.util\n", + "\n", + "if importlib.util.find_spec('easydiffraction') is None:\n", + " %pip install easydiffraction" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, "source": [ "# Structure Refinement: LBCO+Si, McStas\n", "\n", @@ -33,7 +48,7 @@ }, { "cell_type": "markdown", - "id": "1", + "id": "2", "metadata": {}, "source": [ "## Import Library" @@ -42,7 +57,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2", + "id": "3", "metadata": {}, "outputs": [], "source": [ @@ -54,7 +69,7 @@ }, { "cell_type": "markdown", - "id": "3", + "id": "4", "metadata": {}, "source": [ "## Define Structures\n", @@ -68,7 +83,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -77,7 +92,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "6", "metadata": {}, "source": [ "#### Set Space Group" @@ -86,7 +101,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -96,7 +111,7 @@ }, { "cell_type": "markdown", - "id": "7", + "id": "8", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -105,7 +120,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -114,7 +129,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "10", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -123,7 +138,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -169,7 +184,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "12", "metadata": {}, "source": [ "### Create Structure 2: Si" @@ -178,7 +193,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -187,7 +202,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "14", "metadata": {}, "source": [ "#### Set Space Group" @@ -196,7 +211,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -206,7 +221,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "16", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -215,7 +230,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -224,7 +239,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "18", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -233,7 +248,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -250,7 +265,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "20", "metadata": {}, "source": [ "## Define Experiment\n", @@ -264,7 +279,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -273,7 +288,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "22", "metadata": {}, "source": [ "#### Create Experiment" @@ -282,7 +297,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -298,7 +313,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "24", "metadata": {}, "source": [ "#### Set Instrument" @@ -307,7 +322,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -319,7 +334,7 @@ }, { "cell_type": "markdown", - "id": "25", + "id": "26", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -328,7 +343,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -344,7 +359,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "28", "metadata": {}, "source": [ "#### Set Background" @@ -352,7 +367,7 @@ }, { "cell_type": "markdown", - "id": "28", + "id": "29", "metadata": {}, "source": [ "Select the background type." @@ -361,7 +376,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -370,7 +385,7 @@ }, { "cell_type": "markdown", - "id": "30", + "id": "31", "metadata": {}, "source": [ "Add background points." @@ -379,7 +394,7 @@ { "cell_type": "code", "execution_count": null, - "id": "31", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -400,7 +415,7 @@ }, { "cell_type": "markdown", - "id": "32", + "id": "33", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -409,7 +424,7 @@ { "cell_type": "code", "execution_count": null, - "id": "33", + "id": "34", "metadata": {}, "outputs": [], "source": [ @@ -419,7 +434,7 @@ }, { "cell_type": "markdown", - "id": "34", + "id": "35", "metadata": {}, "source": [ "## Define Project\n", @@ -433,7 +448,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "36", "metadata": {}, "outputs": [], "source": [ @@ -442,7 +457,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "37", "metadata": {}, "source": [ "#### Add Structures" @@ -451,7 +466,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37", + "id": "38", "metadata": {}, "outputs": [], "source": [ @@ -461,7 +476,7 @@ }, { "cell_type": "markdown", - "id": "38", + "id": "39", "metadata": {}, "source": [ "#### Show Structures" @@ -470,7 +485,7 @@ { "cell_type": "code", "execution_count": null, - "id": "39", + "id": "40", "metadata": {}, "outputs": [], "source": [ @@ -479,7 +494,7 @@ }, { "cell_type": "markdown", - "id": "40", + "id": "41", "metadata": {}, "source": [ "#### Add Experiments" @@ -488,7 +503,7 @@ { "cell_type": "code", "execution_count": null, - "id": "41", + "id": "42", "metadata": {}, "outputs": [], "source": [ @@ -497,7 +512,7 @@ }, { "cell_type": "markdown", - "id": "42", + "id": "43", "metadata": {}, "source": [ "#### Set Excluded Regions\n", @@ -508,7 +523,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "44", "metadata": {}, "outputs": [], "source": [ @@ -517,7 +532,7 @@ }, { "cell_type": "markdown", - "id": "44", + "id": "45", "metadata": {}, "source": [ "Add excluded regions." @@ -526,7 +541,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "46", "metadata": {}, "outputs": [], "source": [ @@ -536,7 +551,7 @@ }, { "cell_type": "markdown", - "id": "46", + "id": "47", "metadata": {}, "source": [ "Show excluded regions." @@ -545,7 +560,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "48", "metadata": {}, "outputs": [], "source": [ @@ -554,7 +569,7 @@ }, { "cell_type": "markdown", - "id": "48", + "id": "49", "metadata": {}, "source": [ "Show measured data after adding excluded regions." @@ -563,7 +578,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "50", "metadata": {}, "outputs": [], "source": [ @@ -572,7 +587,7 @@ }, { "cell_type": "markdown", - "id": "50", + "id": "51", "metadata": {}, "source": [ "Show experiment as CIF." @@ -581,7 +596,7 @@ { "cell_type": "code", "execution_count": null, - "id": "51", + "id": "52", "metadata": {}, "outputs": [], "source": [ @@ -590,7 +605,7 @@ }, { "cell_type": "markdown", - "id": "52", + "id": "53", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -604,7 +619,7 @@ { "cell_type": "code", "execution_count": null, - "id": "53", + "id": "54", "metadata": {}, "outputs": [], "source": [ @@ -613,7 +628,7 @@ }, { "cell_type": "markdown", - "id": "54", + "id": "55", "metadata": {}, "source": [ "#### Set Fitting Parameters\n", @@ -624,7 +639,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "56", "metadata": {}, "outputs": [], "source": [ @@ -637,7 +652,7 @@ }, { "cell_type": "markdown", - "id": "56", + "id": "57", "metadata": {}, "source": [ "Set experiment parameters to be optimized." @@ -646,7 +661,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57", + "id": "58", "metadata": {}, "outputs": [], "source": [ @@ -667,7 +682,7 @@ }, { "cell_type": "markdown", - "id": "58", + "id": "59", "metadata": {}, "source": [ "#### Perform Fit" @@ -676,7 +691,7 @@ { "cell_type": "code", "execution_count": null, - "id": "59", + "id": "60", "metadata": {}, "outputs": [], "source": [ @@ -686,7 +701,7 @@ }, { "cell_type": "markdown", - "id": "60", + "id": "61", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -695,7 +710,7 @@ { "cell_type": "code", "execution_count": null, - "id": "61", + "id": "62", "metadata": {}, "outputs": [], "source": [ @@ -705,7 +720,7 @@ { "cell_type": "code", "execution_count": null, - "id": "62", + "id": "63", "metadata": {}, "outputs": [], "source": [] From 0cbfc94e3cc28e473a043516417a394752792ead Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 12:02:34 +0200 Subject: [PATCH 06/24] Update serialization process to apply constraints before saving project --- src/easydiffraction/io/cif/serialize.py | 5 +++++ src/easydiffraction/project/project.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index 184e40f4..fa358e9e 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -156,6 +156,11 @@ def category_collection_to_cif( When set to a positive integer, truncate the output to at most this many rows (half from the start, half from the end) with an ``...`` separator. ``None`` emits all rows. + + Returns + ------- + str + CIF text representing the collection as a loop. """ if not len(collection): return '' diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index 06e36d14..33ec12e5 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -299,6 +299,11 @@ def save(self) -> None: console.paragraph(f"Saving project 📦 '{self.name}' to") console.print(self.info.path.resolve()) + # Apply constraints so dependent parameters are flagged + # before serialization (constrained params are written + # without brackets). + self._analysis._update_categories() + # Ensure project directory exists self._info.path.mkdir(parents=True, exist_ok=True) From 87d36697dd727e0ba184e73023bab65cc825c42f Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 13:06:49 +0200 Subject: [PATCH 07/24] Move CIF loop truncation from persistence to display methods --- src/easydiffraction/core/datablock.py | 14 ++++++++++++++ .../datablocks/experiment/item/base.py | 3 +-- .../datablocks/structure/item/base.py | 2 +- src/easydiffraction/io/cif/serialize.py | 19 +++++++++++++++++-- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index 36c5b589..d9fc9f42 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -91,6 +91,20 @@ def as_cif(self) -> str: self._update_categories() return datablock_item_to_cif(self) + def _cif_for_display(self, max_loop_display: int = 20) -> str: + """ + Return CIF text with loop categories truncated for display. + + Parameters + ---------- + max_loop_display : int, default=20 + Maximum number of rows to show per loop category. + """ + from easydiffraction.io.cif.serialize import datablock_item_to_cif # noqa: PLC0415 + + self._update_categories() + return datablock_item_to_cif(self, max_loop_display=max_loop_display) + def help(self) -> None: """Print a summary of public attributes and categories.""" super().help() diff --git a/src/easydiffraction/datablocks/experiment/item/base.py b/src/easydiffraction/datablocks/experiment/item/base.py index 1ac72d0f..d3a0f4a0 100644 --- a/src/easydiffraction/datablocks/experiment/item/base.py +++ b/src/easydiffraction/datablocks/experiment/item/base.py @@ -129,10 +129,9 @@ def as_cif(self) -> str: def show_as_cif(self) -> None: """Pretty-print the experiment as CIF text.""" - experiment_cif = super().as_cif paragraph_title: str = f"Experiment 🔬 '{self.name}' as cif" console.paragraph(paragraph_title) - render_cif(experiment_cif) + render_cif(self._cif_for_display()) @abstractmethod def _load_ascii_data_to_experiment(self, data_path: str) -> None: diff --git a/src/easydiffraction/datablocks/structure/item/base.py b/src/easydiffraction/datablocks/structure/item/base.py index 80d8f76a..8181f1db 100644 --- a/src/easydiffraction/datablocks/structure/item/base.py +++ b/src/easydiffraction/datablocks/structure/item/base.py @@ -252,4 +252,4 @@ def show(self) -> None: def show_as_cif(self) -> None: """Render the CIF text for this structure in the terminal.""" console.paragraph(f"Structure 🧩 '{self.name}' as cif") - render_cif(self.as_cif) + render_cif(self._cif_for_display()) diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index fa358e9e..cfceca74 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -196,11 +196,22 @@ def category_collection_to_cif( return '\n'.join(lines) -def datablock_item_to_cif(datablock: object) -> str: +def datablock_item_to_cif( + datablock: object, + max_loop_display: int | None = None, +) -> str: """ Render a DatablockItem-like object to CIF text. Emits a data_ header and then concatenates category CIF sections. + + Parameters + ---------- + datablock : object + A ``DatablockItem``-like object. + max_loop_display : int | None, default=None + When set, truncate loop categories to this many rows. ``None`` + emits all rows (used for serialisation). """ # Local imports to avoid import-time cycles from easydiffraction.core.category import CategoryCollection # noqa: PLC0415 @@ -213,7 +224,11 @@ def datablock_item_to_cif(datablock: object) -> str: parts.extend(v.as_cif for v in vars(datablock).values() if isinstance(v, CategoryItem)) # Then collections - parts.extend(v.as_cif for v in vars(datablock).values() if isinstance(v, CategoryCollection)) + parts.extend( + category_collection_to_cif(v, max_display=max_loop_display) + for v in vars(datablock).values() + if isinstance(v, CategoryCollection) + ) return '\n\n'.join(parts) From 9a11882613b412691a063d8ded5111e34f1bed30 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 13:13:13 +0200 Subject: [PATCH 08/24] Add CIF round-trip integration tests for experiments and structures --- .../fitting/test_cif_round_trip.py | 322 ++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 tests/integration/fitting/test_cif_round_trip.py diff --git a/tests/integration/fitting/test_cif_round_trip.py b/tests/integration/fitting/test_cif_round_trip.py new file mode 100644 index 00000000..b089027b --- /dev/null +++ b/tests/integration/fitting/test_cif_round_trip.py @@ -0,0 +1,322 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Integration tests for experiment CIF round-trip (as_cif → from_cif_str).""" + +from __future__ import annotations + +import tempfile + +from numpy.testing import assert_almost_equal + +from easydiffraction import ExperimentFactory +from easydiffraction import StructureFactory +from easydiffraction import download_data +from easydiffraction.core.variable import Parameter + +TEMP_DIR = tempfile.gettempdir() + + +def _build_fully_configured_experiment() -> ExperimentFactory: + """ + Create a fully configured powder CWL neutron experiment. + + Includes instrument, peak profile, background, excluded regions, + linked phases, and measured data. + + Returns + ------- + ExperimentBase + A complete experiment ready for CIF round-trip testing. + """ + data_path = download_data(id=3, destination=TEMP_DIR) + expt = ExperimentFactory.from_data_path( + name='hrpt', + data_path=data_path, + ) + # Instrument + expt.instrument.setup_wavelength = 1.494 + expt.instrument.calib_twotheta_offset = 0.6225 + + # Peak profile + expt.peak.broad_gauss_u = 0.0834 + expt.peak.broad_gauss_v = -0.1168 + expt.peak.broad_gauss_w = 0.123 + expt.peak.broad_lorentz_x = 0.0 + expt.peak.broad_lorentz_y = 0.0797 + + # Background + expt.background.create(id='1', x=10, y=170) + expt.background.create(id='2', x=80, y=160) + expt.background.create(id='3', x=165, y=170) + + # Excluded regions + expt.excluded_regions.create(id='1', start=0, end=5) + expt.excluded_regions.create(id='2', start=165, end=180) + + # Linked phases + expt.linked_phases.create(id='lbco', scale=9.0) + + # Free parameters + expt.instrument.calib_twotheta_offset.free = True + expt.linked_phases['lbco'].scale.free = True + expt.background['1'].y.free = True + expt.background['2'].y.free = True + expt.background['3'].y.free = True + + return expt + + +def _collect_param_values(expt: object) -> dict[str, object]: + """ + Collect all parameter values from an experiment. + + Returns a dict keyed by unique_name with the parameter value. + Skips raw data parameters (pd_data.*) since those are large arrays. + """ + result = {} + for p in expt.parameters: + uname = getattr(p, 'unique_name', None) + if uname is None: + continue + # Skip raw data arrays + if 'pd_data.' in uname: + continue + result[uname] = p.value + return result + + +def _collect_free_flags(expt: object) -> dict[str, bool]: + """Return {unique_name: free} for fittable parameters.""" + return { + p.unique_name: p.free + for p in expt.parameters + if isinstance(p, Parameter) and not p.unique_name.startswith('pd_data.') + } + + +# ------------------------------------------------------------------ +# Test 1: Experiment CIF round-trip preserves all parameter values +# ------------------------------------------------------------------ + + +def test_experiment_cif_round_trip_preserves_parameters() -> None: + """ + Every parameter value must survive an as_cif → from_cif_str cycle. + + Creates a fully configured experiment, serialises it to CIF, + reconstructs from CIF, and compares all parameter values. + """ + original = _build_fully_configured_experiment() + + # Serialise + cif_str = original.as_cif + + # Reconstruct + loaded = ExperimentFactory.from_cif_str(cif_str) + + # Compare parameter values + orig_params = _collect_param_values(original) + loaded_params = _collect_param_values(loaded) + + for name, orig_val in orig_params.items(): + assert name in loaded_params, f'Parameter {name} missing after round-trip' + loaded_val = loaded_params[name] + if isinstance(orig_val, float): + assert_almost_equal( + loaded_val, + orig_val, + decimal=4, + err_msg=f'Value mismatch for {name}', + ) + else: + assert loaded_val == orig_val, ( + f'Value mismatch for {name}: expected {orig_val!r}, got {loaded_val!r}' + ) + + +# ------------------------------------------------------------------ +# Test 2: Free flags survive the round-trip +# ------------------------------------------------------------------ + + +def test_experiment_cif_round_trip_preserves_free_flags() -> None: + """ + Free flags must survive an as_cif → from_cif_str cycle. + + Parameters marked as free on the original experiment must also be + free on the reconstructed experiment. + """ + original = _build_fully_configured_experiment() + + cif_str = original.as_cif + loaded = ExperimentFactory.from_cif_str(cif_str) + + orig_free = _collect_free_flags(original) + loaded_free = _collect_free_flags(loaded) + + for name, orig_flag in orig_free.items(): + if name in loaded_free: + assert loaded_free[name] == orig_flag, ( + f'Free flag mismatch for {name}: expected {orig_flag}, got {loaded_free[name]}' + ) + + +# ------------------------------------------------------------------ +# Test 3: Categories survive the round-trip +# ------------------------------------------------------------------ + + +def test_experiment_cif_round_trip_preserves_categories() -> None: + """ + Category collections (background, excluded regions, linked phases) + must preserve their item count after a round-trip. + """ + original = _build_fully_configured_experiment() + + cif_str = original.as_cif + loaded = ExperimentFactory.from_cif_str(cif_str) + + # Background points + assert len(loaded.background) == len(original.background), ( + f'Background count mismatch: ' + f'expected {len(original.background)}, got {len(loaded.background)}' + ) + + # Excluded regions + assert len(loaded.excluded_regions) == len(original.excluded_regions), ( + f'Excluded regions count mismatch: ' + f'expected {len(original.excluded_regions)}, ' + f'got {len(loaded.excluded_regions)}' + ) + + # Linked phases + assert len(loaded.linked_phases) == len(original.linked_phases), ( + f'Linked phases count mismatch: ' + f'expected {len(original.linked_phases)}, ' + f'got {len(loaded.linked_phases)}' + ) + + +# ------------------------------------------------------------------ +# Test 4: Data points survive the round-trip +# ------------------------------------------------------------------ + + +def test_experiment_cif_round_trip_preserves_data() -> None: + """ + Measured data points must survive an as_cif → from_cif_str cycle. + + The number of data points and the first/last values must match. + """ + original = _build_fully_configured_experiment() + + cif_str = original.as_cif + loaded = ExperimentFactory.from_cif_str(cif_str) + + # Number of data points + assert len(loaded.data) == len(original.data), ( + f'Data point count mismatch: expected {len(original.data)}, got {len(loaded.data)}' + ) + + # First and last data point two_theta and intensity_meas + orig_first = list(original.data.values())[0] + loaded_first = list(loaded.data.values())[0] + orig_last = list(original.data.values())[-1] + loaded_last = list(loaded.data.values())[-1] + + assert_almost_equal( + loaded_first.two_theta.value, + orig_first.two_theta.value, + decimal=4, + err_msg='First data point two_theta mismatch', + ) + assert_almost_equal( + loaded_last.two_theta.value, + orig_last.two_theta.value, + decimal=4, + err_msg='Last data point two_theta mismatch', + ) + assert_almost_equal( + loaded_first.intensity_meas.value, + orig_first.intensity_meas.value, + decimal=2, + err_msg='First data point intensity_meas mismatch', + ) + + +# ------------------------------------------------------------------ +# Test 5: Structure CIF round-trip preserves all parameter values +# ------------------------------------------------------------------ + + +def test_structure_cif_round_trip_preserves_parameters() -> None: + """ + Every structure parameter must survive an as_cif → from_cif_str + cycle, including atom sites with symmetry constraints. + """ + original = StructureFactory.from_scratch(name='lbco') + original.space_group.name_h_m = 'P m -3 m' + original.cell.length_a = 3.8909 + original.atom_sites.create( + label='La', + type_symbol='La', + fract_x=0, + fract_y=0, + fract_z=0, + wyckoff_letter='a', + occupancy=0.5, + b_iso=0.5, + ) + original.atom_sites.create( + label='Co', + type_symbol='Co', + fract_x=0.5, + fract_y=0.5, + fract_z=0.5, + wyckoff_letter='b', + b_iso=0.5, + ) + original.atom_sites.create( + label='O', + type_symbol='O', + fract_x=0, + fract_y=0.5, + fract_z=0.5, + wyckoff_letter='c', + b_iso=0.5, + ) + # Apply symmetry constraints before serialisation + original._update_categories() + + cif_str = original.as_cif + loaded = StructureFactory.from_cif_str(cif_str) + # Apply symmetry on loaded to match original state + loaded._update_categories() + + # Compare cell parameters + assert_almost_equal( + loaded.cell.length_a.value, + original.cell.length_a.value, + decimal=6, + ) + + # Compare space group + assert loaded.space_group.name_h_m.value == original.space_group.name_h_m.value + + # Compare atom sites count and values + assert len(loaded.atom_sites) == len(original.atom_sites) + for label in ['La', 'Co', 'O']: + orig_site = original.atom_sites[label] + loaded_site = loaded.atom_sites[label] + assert_almost_equal( + loaded_site.fract_x.value, + orig_site.fract_x.value, + decimal=6, + err_msg=f'fract_x mismatch for {label}', + ) + assert_almost_equal( + loaded_site.b_iso.value, + orig_site.b_iso.value, + decimal=4, + err_msg=f'b_iso mismatch for {label}', + ) From ca8d73673dfd8e1685f118405b574e99e4e9b3c7 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 13:13:35 +0200 Subject: [PATCH 09/24] Add new integration test --- ..._powder-diffraction_constant-wavelength.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py b/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py index 2184fc07..cb5e640a 100644 --- a/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py +++ b/tests/integration/fitting/test_powder-diffraction_constant-wavelength.py @@ -479,6 +479,56 @@ def test_fit_neutron_pd_cwl_hs() -> None: ) +def test_single_fit_neutron_pd_cwl_lbco_with_constraints_from_project() -> None: + import easydiffraction as ed + + # Create a project from CIF files + project = ed.Project() + project.structures.add_from_cif_path(ed.download_data(id=1, destination='data')) + project.experiments.add_from_cif_path(ed.download_data(id=2, destination='data')) + + # Set constraints + project.analysis.aliases.create( + label='biso_La', + param=project.structures['lbco'].atom_sites['La'].b_iso, + ) + project.analysis.aliases.create( + label='biso_Ba', + param=project.structures['lbco'].atom_sites['Ba'].b_iso, + ) + + project.analysis.aliases.create( + label='occ_La', + param=project.structures['lbco'].atom_sites['La'].occupancy, + ) + project.analysis.aliases.create( + label='occ_Ba', + param=project.structures['lbco'].atom_sites['Ba'].occupancy, + ) + + project.analysis.constraints.create(expression='biso_Ba = biso_La') + project.analysis.constraints.create(expression='occ_Ba = 1 - occ_La') + + # More fit patams + project.structures['lbco'].atom_sites['La'].occupancy.free = True + + # Save to a directory + project.save_as('lbco_project') + + # Load Project from Directory + project = ed.Project.load('lbco_project') + + # Perform Analysis + project.analysis.fit() + + # Compare fit quality + assert_almost_equal( + project.analysis.fit_results.reduced_chi_square, + desired=1.28, + decimal=1, + ) + + if __name__ == '__main__': test_fit_neutron_pd_cwl_hs() test_single_fit_neutron_pd_cwl_lbco() From 21941c9f408adf53110a27514512350ca8d8c83e Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 13:26:28 +0200 Subject: [PATCH 10/24] Move analysis.cif into analysis/ directory --- docs/architecture/architecture.md | 7 ++++--- src/easydiffraction/project/project.py | 7 +++++-- .../project/test_project_load.py | 18 +++++++++--------- .../project/test_project_save.py | 4 ++-- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/docs/architecture/architecture.md b/docs/architecture/architecture.md index 605ee28c..8a7a9369 100644 --- a/docs/architecture/architecture.md +++ b/docs/architecture/architecture.md @@ -714,12 +714,13 @@ Projects are saved as a directory of CIF files: ```shell project_dir/ ├── project.cif # ProjectInfo -├── analysis.cif # Analysis settings ├── summary.cif # Summary report ├── structures/ │ └── lbco.cif # One file per structure -└── experiments/ - └── hrpt.cif # One file per experiment +├── experiments/ +│ └── hrpt.cif # One file per experiment +└── analysis/ + └── analysis.cif # Analysis settings ``` ### 7.3 Verbosity diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index 33ec12e5..cb3a90fc 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -335,9 +335,12 @@ def save(self) -> None: console.print(f'│ └── 📄 {file_name}') # Save analysis - with (self._info.path / 'analysis.cif').open('w') as f: + analysis_dir = self._info.path / 'analysis' + analysis_dir.mkdir(parents=True, exist_ok=True) + with (analysis_dir / 'analysis.cif').open('w') as f: f.write(self.analysis.as_cif()) - console.print('├── 📄 analysis.cif') + console.print('├── 📁 analysis/') + console.print('│ └── 📄 analysis.cif') # Save summary with (self._info.path / 'summary.cif').open('w') as f: diff --git a/tests/unit/easydiffraction/project/test_project_load.py b/tests/unit/easydiffraction/project/test_project_load.py index 6d9020b7..c676cb89 100644 --- a/tests/unit/easydiffraction/project/test_project_load.py +++ b/tests/unit/easydiffraction/project/test_project_load.py @@ -116,27 +116,27 @@ def test_round_trips_constraints(self, tmp_path): class TestLoadAnalysisCifFallback: """Load falls back from analysis/analysis.cif to analysis.cif at root.""" - def test_loads_analysis_from_root(self, tmp_path): - """Current save layout: analysis.cif at project root.""" + def test_loads_analysis_from_subdir(self, tmp_path): + """Current save layout: analysis/analysis.cif.""" original = Project(name='fb1') original.save_as(str(tmp_path / 'proj')) - # Verify analysis.cif is at root (current save layout) - assert (tmp_path / 'proj' / 'analysis.cif').is_file() + # Verify analysis.cif is in analysis/ subdirectory (current save layout) + assert (tmp_path / 'proj' / 'analysis' / 'analysis.cif').is_file() loaded = Project.load(str(tmp_path / 'proj')) assert loaded.analysis.current_minimizer == 'lmfit' - def test_loads_analysis_from_subdir(self, tmp_path): - """Future layout: analysis/analysis.cif takes priority.""" + def test_loads_analysis_from_root_fallback(self, tmp_path): + """Old layout fallback: analysis.cif at project root.""" original = Project(name='fb2') original.save_as(str(tmp_path / 'proj')) - # Move analysis.cif to analysis/ subdirectory + # Move analysis.cif from analysis/ subdirectory to project root proj_dir = tmp_path / 'proj' analysis_dir = proj_dir / 'analysis' - analysis_dir.mkdir(exist_ok=True) - (proj_dir / 'analysis.cif').rename(analysis_dir / 'analysis.cif') + (analysis_dir / 'analysis.cif').rename(proj_dir / 'analysis.cif') + analysis_dir.rmdir() loaded = Project.load(str(proj_dir)) assert loaded.analysis.current_minimizer == 'lmfit' diff --git a/tests/unit/easydiffraction/project/test_project_save.py b/tests/unit/easydiffraction/project/test_project_save.py index ac8b9895..bf632e11 100644 --- a/tests/unit/easydiffraction/project/test_project_save.py +++ b/tests/unit/easydiffraction/project/test_project_save.py @@ -13,7 +13,7 @@ def test_project_save_uses_cwd_when_no_explicit_path(monkeypatch, tmp_path, caps # It should announce saving and create the three core files assert 'Saving project' in out assert (tmp_path / 'project.cif').exists() - assert (tmp_path / 'analysis.cif').exists() + assert (tmp_path / 'analysis' / 'analysis.cif').exists() assert (tmp_path / 'summary.cif').exists() @@ -34,7 +34,7 @@ def test_project_save_as_writes_core_files(tmp_path, monkeypatch): # Assert expected files/dirs exist assert (target / 'project.cif').is_file() - assert (target / 'analysis.cif').is_file() + assert (target / 'analysis' / 'analysis.cif').is_file() assert (target / 'summary.cif').is_file() assert (target / 'structures').is_dir() assert (target / 'experiments').is_dir() From 5dffac30b816405a02c7932754f2770c1e35e87d Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 13:28:28 +0200 Subject: [PATCH 11/24] Add destination parameter to extract_data_paths_from_zip --- src/easydiffraction/io/ascii.py | 28 +++-- tests/unit/easydiffraction/io/test_ascii.py | 125 ++++++++++++++++++++ 2 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 tests/unit/easydiffraction/io/test_ascii.py diff --git a/src/easydiffraction/io/ascii.py b/src/easydiffraction/io/ascii.py index ae231a2c..75ec0fcb 100644 --- a/src/easydiffraction/io/ascii.py +++ b/src/easydiffraction/io/ascii.py @@ -13,21 +13,27 @@ import numpy as np -def extract_data_paths_from_zip(zip_path: str | Path) -> list[str]: +def extract_data_paths_from_zip( + zip_path: str | Path, + destination: str | Path | None = None, +) -> list[str]: """ Extract all files from a ZIP archive and return their paths. - Files are extracted into a temporary directory that persists for the - lifetime of the process. The returned paths are sorted - lexicographically by file name so that numbered data files (e.g. - ``scan_001.dat``, ``scan_002.dat``) appear in natural order. Hidden - files and directories (names starting with ``'.'`` or ``'__'``) are - excluded. + Files are extracted into *destination* when provided, or into a + temporary directory that persists for the lifetime of the process. + The returned paths are sorted lexicographically by file name so that + numbered data files (e.g. ``scan_001.dat``, ``scan_002.dat``) appear + in natural order. Hidden files and directories (names starting with + ``'.'`` or ``'__'``) are excluded. Parameters ---------- zip_path : str | Path Path to the ZIP archive. + destination : str | Path | None, default=None + Directory to extract files into. When ``None``, a temporary + directory is created. Returns ------- @@ -46,8 +52,12 @@ def extract_data_paths_from_zip(zip_path: str | Path) -> list[str]: msg = f'ZIP file not found: {zip_path}' raise FileNotFoundError(msg) - # TODO: Unify mkdir with other uses in the code - extract_dir = Path(tempfile.mkdtemp(prefix='ed_zip_')) + if destination is not None: + extract_dir = Path(destination) + extract_dir.mkdir(parents=True, exist_ok=True) + else: + # TODO: Unify mkdir with other uses in the code + extract_dir = Path(tempfile.mkdtemp(prefix='ed_zip_')) with zipfile.ZipFile(zip_path, 'r') as zf: zf.extractall(extract_dir) diff --git a/tests/unit/easydiffraction/io/test_ascii.py b/tests/unit/easydiffraction/io/test_ascii.py new file mode 100644 index 00000000..8519c6d7 --- /dev/null +++ b/tests/unit/easydiffraction/io/test_ascii.py @@ -0,0 +1,125 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Tests for extract_data_paths_from_zip and extract_data_paths_from_dir.""" + +from __future__ import annotations + +import zipfile + +import pytest + +from easydiffraction.io.ascii import extract_data_paths_from_dir +from easydiffraction.io.ascii import extract_data_paths_from_zip + + +class TestExtractDataPathsFromZip: + """Tests for extract_data_paths_from_zip.""" + + def test_extracts_to_temp_dir_by_default(self, tmp_path): + """Without destination, files go to a temp directory.""" + zip_path = tmp_path / 'test.zip' + with zipfile.ZipFile(zip_path, 'w') as zf: + zf.writestr('scan_001.dat', '1 2 3\n') + zf.writestr('scan_002.dat', '4 5 6\n') + + paths = extract_data_paths_from_zip(zip_path) + + assert len(paths) == 2 + assert 'scan_001.dat' in paths[0] + assert 'scan_002.dat' in paths[1] + + def test_extracts_to_destination(self, tmp_path): + """With destination, files go to the specified directory.""" + zip_path = tmp_path / 'test.zip' + dest = tmp_path / 'output' + with zipfile.ZipFile(zip_path, 'w') as zf: + zf.writestr('scan_001.dat', '1 2 3\n') + zf.writestr('scan_002.dat', '4 5 6\n') + + paths = extract_data_paths_from_zip(zip_path, destination=dest) + + assert len(paths) == 2 + assert all(str(dest) in p for p in paths) + assert (dest / 'scan_001.dat').is_file() + assert (dest / 'scan_002.dat').is_file() + + def test_destination_creates_directory(self, tmp_path): + """Destination directory is created if it does not exist.""" + zip_path = tmp_path / 'test.zip' + dest = tmp_path / 'nested' / 'output' + with zipfile.ZipFile(zip_path, 'w') as zf: + zf.writestr('data.dat', '1 2 3\n') + + paths = extract_data_paths_from_zip(zip_path, destination=dest) + + assert len(paths) == 1 + assert dest.is_dir() + + def test_raises_file_not_found(self, tmp_path): + """Raises FileNotFoundError for missing ZIP path.""" + with pytest.raises(FileNotFoundError): + extract_data_paths_from_zip(tmp_path / 'missing.zip') + + def test_raises_value_error_for_empty_zip(self, tmp_path): + """Raises ValueError when ZIP has no usable files.""" + zip_path = tmp_path / 'empty.zip' + with zipfile.ZipFile(zip_path, 'w') as zf: + zf.writestr('.hidden', 'hidden\n') + + with pytest.raises(ValueError, match='No data files found'): + extract_data_paths_from_zip(zip_path) + + def test_excludes_hidden_files(self, tmp_path): + """Hidden files are excluded from returned paths.""" + zip_path = tmp_path / 'test.zip' + with zipfile.ZipFile(zip_path, 'w') as zf: + zf.writestr('data.dat', '1 2 3\n') + zf.writestr('.hidden', 'hidden\n') + zf.writestr('__meta', 'meta\n') + + paths = extract_data_paths_from_zip(zip_path) + + assert len(paths) == 1 + assert 'data.dat' in paths[0] + + def test_returns_sorted_paths(self, tmp_path): + """Returned paths are sorted lexicographically.""" + zip_path = tmp_path / 'test.zip' + with zipfile.ZipFile(zip_path, 'w') as zf: + zf.writestr('c.dat', '3\n') + zf.writestr('a.dat', '1\n') + zf.writestr('b.dat', '2\n') + + paths = extract_data_paths_from_zip(zip_path) + + assert 'a.dat' in paths[0] + assert 'b.dat' in paths[1] + assert 'c.dat' in paths[2] + + +class TestExtractDataPathsFromDir: + """Tests for extract_data_paths_from_dir.""" + + def test_lists_files_in_directory(self, tmp_path): + """Returns sorted paths for files in a directory.""" + (tmp_path / 'scan_002.dat').write_text('2\n') + (tmp_path / 'scan_001.dat').write_text('1\n') + + paths = extract_data_paths_from_dir(tmp_path) + + assert len(paths) == 2 + assert 'scan_001.dat' in paths[0] + assert 'scan_002.dat' in paths[1] + + def test_raises_for_missing_directory(self, tmp_path): + """Raises FileNotFoundError for non-existent directory.""" + with pytest.raises(FileNotFoundError): + extract_data_paths_from_dir(tmp_path / 'missing') + + def test_raises_for_empty_directory(self, tmp_path): + """Raises ValueError when directory has no matching files.""" + empty = tmp_path / 'empty' + empty.mkdir() + + with pytest.raises(ValueError, match='No files matching'): + extract_data_paths_from_dir(empty) From 16af6de5711942463fe7b0097d595058aa548e35 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 13:31:55 +0200 Subject: [PATCH 12/24] Add missing Returns sections to docstrings --- src/easydiffraction/core/datablock.py | 7 +++++++ src/easydiffraction/io/cif/serialize.py | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/easydiffraction/core/datablock.py b/src/easydiffraction/core/datablock.py index d9fc9f42..5d497e4c 100644 --- a/src/easydiffraction/core/datablock.py +++ b/src/easydiffraction/core/datablock.py @@ -99,6 +99,13 @@ def _cif_for_display(self, max_loop_display: int = 20) -> str: ---------- max_loop_display : int, default=20 Maximum number of rows to show per loop category. + + Returns + ------- + str + CIF representation of this object, with loop categories + truncated to at most *max_loop_display* rows for display + purposes. """ from easydiffraction.io.cif.serialize import datablock_item_to_cif # noqa: PLC0415 diff --git a/src/easydiffraction/io/cif/serialize.py b/src/easydiffraction/io/cif/serialize.py index cfceca74..fa035981 100644 --- a/src/easydiffraction/io/cif/serialize.py +++ b/src/easydiffraction/io/cif/serialize.py @@ -212,6 +212,11 @@ def datablock_item_to_cif( max_loop_display : int | None, default=None When set, truncate loop categories to this many rows. ``None`` emits all rows (used for serialisation). + + Returns + ------- + str + CIF text representing the datablock as a loop. """ # Local imports to avoid import-time cycles from easydiffraction.core.category import CategoryCollection # noqa: PLC0415 From 4ddd38e889997b36f8c411e71f936c8e9e85914c Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 13:42:53 +0200 Subject: [PATCH 13/24] Add sequential fitting infrastructure with CSV output --- .github/copilot-instructions.md | 5 +- docs/architecture/issues_closed.md | 24 + docs/architecture/issues_open.md | 37 - .../architecture/sequential_fitting_design.md | 210 +++--- src/easydiffraction/analysis/analysis.py | 54 ++ src/easydiffraction/analysis/sequential.py | 681 ++++++++++++++++++ tests/integration/fitting/test_sequential.py | 282 ++++++++ 7 files changed, 1153 insertions(+), 140 deletions(-) create mode 100644 src/easydiffraction/analysis/sequential.py create mode 100644 tests/integration/fitting/test_sequential.py diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 67a761c8..1fabd502 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -42,7 +42,8 @@ and UPPER_SNAKE_CASE for constants. - Use `from __future__ import annotations` in every module. - Type-annotate all public function signatures. -- Docstrings on all public classes and methods (numpy style). +- Docstrings on all public classes and methods (numpy style). These must + include sections Parameters, Returns and Raises, where applicable. - Prefer flat over nested, explicit over clever. - Write straightforward code; do not add defensive checks for unlikely edge cases. @@ -147,6 +148,8 @@ `docs/architecture/architecture.md`. - After changes, run linting and formatting fixes with `pixi run fix`. Do not check what was auto-fixed, just accept the fixes and move on. + Then, run linting and formatting checks with `pixi run check` and + address any remaining issues until the code is clean. - After changes, run unit tests with `pixi run unit-tests`. - After changes, run integration tests with `pixi run integration-tests`. diff --git a/docs/architecture/issues_closed.md b/docs/architecture/issues_closed.md index d6e14219..0612ea04 100644 --- a/docs/architecture/issues_closed.md +++ b/docs/architecture/issues_closed.md @@ -4,6 +4,30 @@ Issues that have been fully resolved. Kept for historical reference. --- +## Implement `Project.load()` + +**Resolution:** implemented `Project.load(dir_path)` as a classmethod +that reads `project.cif`, `structures/*.cif`, `experiments/*.cif`, and +`analysis/analysis.cif` (with fallback to `analysis.cif` at root for +backward compatibility). Reconstructs the full project state including +alias parameter references via `_resolve_alias_references()`. Integration +tests verify save → load → parameter comparison and save → load → fit → +χ² comparison. Also used by `fit_sequential` workers to reconstruct +projects from CIF strings. + +--- + +## Eliminate Dummy `Experiments` Wrapper in Single-Fit Mode + +**Resolution:** refactored `Fitter.fit()` and `_residual_function()` to +accept `experiments: list[ExperimentBase]` instead of requiring an +`Experiments` collection. `Analysis.fit()` passes +`experiments_list = [experiment]` in single-fit mode and +`list(experiments.values())` in joint-fit mode. Removed the +`object.__setattr__` hack that forced `_parent` on the dummy wrapper. + +--- + ## Replace UID Map with Direct References and Auto-Apply Constraints **Resolution:** eliminated `UidMapHandler` and random UID generation diff --git a/docs/architecture/issues_open.md b/docs/architecture/issues_open.md index b721a2b2..e01cc36d 100644 --- a/docs/architecture/issues_open.md +++ b/docs/architecture/issues_open.md @@ -10,24 +10,6 @@ needed. --- -## 1. 🔴 Implement `Project.load()` - -**Type:** Completeness - -`save()` serialises all components to CIF files but `load()` is a stub -that raises `NotImplementedError`. Users cannot round-trip a project. - -**Why first:** this is the highest-severity gap. Without it the save -functionality is only half useful — CIF files are written but cannot be -read back. Tutorials that demonstrate save/load are blocked. - -**Fix:** implement `load()` that reads CIF files from the project -directory and reconstructs structures, experiments, and analysis -settings. - -**Depends on:** nothing (standalone). - ---- ## 2. 🟡 Restore Minimiser Variant Support @@ -125,23 +107,6 @@ effectively fixed after experiment creation. --- -## 7. 🟡 Eliminate Dummy `Experiments` Wrapper in Single-Fit Mode - -**Type:** Fragility - -Single-fit mode creates a throw-away `Experiments` collection per -experiment, manually forces `_parent` via `object.__setattr__`, and -passes it to `Fitter`. This bypasses `GuardedBase` parent tracking and -is fragile. - -**Fix:** make `Fitter.fit()` accept a list of experiment objects (or a -single experiment) instead of requiring an `Experiments` collection. Or -add a `fit_single(experiment)` method. - -**Depends on:** nothing, but simpler after issue 5 (Analysis refactor) -clarifies the fitting orchestration. - ---- ## 8. 🟡 Add Explicit `create()` Signatures on Collections @@ -316,12 +281,10 @@ re-derivable default. | # | Issue | Severity | Type | | --- | ---------------------------------------- | -------- | ----------------------- | -| 1 | Implement `Project.load()` | 🔴 High | Completeness | | 2 | Restore minimiser variants | 🟡 Med | Feature loss | | 3 | Rebuild joint-fit weights | 🟡 Med | Fragility | | 5 | `Analysis` as `DatablockItem` | 🟡 Med | Consistency | | 6 | Restrict `data_type` switching | 🔴 High | Correctness/Data safety | -| 7 | Eliminate dummy `Experiments` | 🟡 Med | Fragility | | 8 | Explicit `create()` signatures | 🟡 Med | API safety | | 9 | Future enum extensions | 🟢 Low | Design | | 10 | Unify update orchestration | 🟢 Low | Maintainability | diff --git a/docs/architecture/sequential_fitting_design.md b/docs/architecture/sequential_fitting_design.md index 16f01634..9e11e8ca 100644 --- a/docs/architecture/sequential_fitting_design.md +++ b/docs/architecture/sequential_fitting_design.md @@ -1,7 +1,7 @@ # Sequential Fitting — Architecture Design -**Status:** Draft — for discussion before implementation **Date:** -2026-04-02 +**Status:** Implementation in progress (PRs 1–9 complete, PRs 10–14 +remaining) **Date:** 2026-04-02 (updated 2026-04-03) --- @@ -869,35 +869,28 @@ direct reference — no map lookup needed. `_minimizer_uid` returns `unique_name.replace('.', '__')` instead of a random string. All tutorials, tests, and call sites updated. -### 9.2 Fix `category_collection_to_cif` truncation +### 9.2 Fix `category_collection_to_cif` truncation ✅ -`category_collection_to_cif` has `max_display=20` which truncates loop -output. For CIF used in save/load/round-trip, all rows must be emitted. +**Done.** `category_collection_to_cif` default changed to +`max_display=None` (emit all rows). Truncation is opt-in via explicit +`max_display` parameter, used only by display methods. -Options: +### 9.3 Verify CIF round-trip for experiments ✅ -- (a) Remove `max_display` from `category_collection_to_cif` entirely, - add truncation only in display methods. -- (b) Add a `full=True` parameter and use it when serialising for - persistence. +**Done.** Five integration tests in `test_cif_round_trip.py`: -### 9.3 Verify CIF round-trip for experiments +1. Parameter values survive `as_cif` → `from_cif_str`. +2. Free flags survive the round-trip. +3. Category collections (background, excluded regions, linked phases) + preserve item count. +4. Data points survive (count, first/last values). +5. Structure round-trip with symmetry constraints. -Write an integration test: +### 9.4 Add `destination` parameter to `extract_data_paths_from_zip` ✅ -1. Create a fully configured experiment (instrument, peak, background, - excluded regions, linked phases, data). -2. Serialise to CIF (`experiment.as_cif`). -3. Reconstruct from CIF (`ExperimentFactory.from_cif_str(cif_str)`). -4. Compare all parameter values. - -Fix any parameters that don't survive the round-trip. - -### 9.4 Add `destination` parameter to `extract_data_paths_from_zip` - -Currently extracts to a temp dir. Add optional `destination` parameter -to extract to a user-specified directory, enabling a clean two-step -workflow (extract → fit_sequential). +**Done.** Optional `destination` parameter added. When provided, extracts +to the given directory. When `None`, uses a temporary directory (original +behaviour). ### 9.5 Replace singletons with instance-owned state (partially done) @@ -954,18 +947,12 @@ Constraints auto-enable on `create()` and are applied before fitting starts. The manual `apply_constraints()` method has been removed. Fixing the singleton issue resolves issue #4 as a side effect. -### 9.6 Move `analysis.cif` into `analysis/` directory +### 9.6 Move `analysis.cif` into `analysis/` directory ✅ -Currently `analysis.cif` lives at the project root alongside -`project.cif` and `summary.cif`. Adding an `analysis/` directory for -`results.csv` next to a file named `analysis.cif` at the same level -creates a naming conflict and a confusing layout. - -**Fix:** update `Project.save()` to write `analysis.cif` to -`project_dir/analysis/analysis.cif`. Update `Project.load()` (when -implemented) to read from the new path, with a fallback to the old path -for backward compatibility with existing saved projects. Update docs -(`architecture.md`, `project.md`), tests, and the save output messages. +**Done.** `Project.save()` writes to `analysis/analysis.cif`. +`Project.load()` checks `analysis/analysis.cif` first, falls back to +`analysis.cif` at root for backward compatibility. Unit tests verify +both layouts. --- @@ -978,7 +965,7 @@ resolved first because they clean up the fitting internals that ### Foundation PRs (resolve existing issues) -#### PR 1 — Eliminate dummy Experiments wrapper in single-fit mode (issue #7) +#### PR 1 — Eliminate dummy Experiments wrapper in single-fit mode (issue #7) ✅ > **Title:** `Accept single Experiment in Fitter.fit()` > @@ -990,11 +977,11 @@ resolved first because they clean up the fitting internals that > Update all callers (single-fit, joint-fit). Update unit and > integration tests. -**Why first:** the current dummy-wrapper pattern is the exact -antipattern that `fit_sequential` workers would otherwise inherit. -Fixing it now gives the worker a clean -`Fitter.fit(structures, [experiment])` call without any collection -ceremony. +**Done.** `Fitter.fit()` and `_residual_function()` now accept +`experiments: list[ExperimentBase]`. `Analysis.fit()` passes +`experiments_list = [experiment]` in single-fit mode and +`list(experiments.values())` in joint-fit mode. No more dummy +`Experiments` wrapper or `object.__setattr__` hack. #### PR 2 — Replace UID map with direct references and auto-apply constraints (issue #4 + § 9.5) ✅ @@ -1019,7 +1006,7 @@ multi-project edge case. This PR also absorbed PR 4 (§ 9.1) since switching from random UIDs to `unique_name` was a natural part of the same change. -#### PR 3 — Implement Project.load() (issue #1) +#### PR 3 — Implement Project.load() (issue #1) ✅ > **Title:** `Implement Project.load() from CIF directory` > @@ -1030,11 +1017,11 @@ This PR also absorbed PR 4 (§ 9.1) since switching from random UIDs to > as a fallback. Add integration test: save → load → compare all > parameter values. -**Why third:** the CIF round-trip reliability that `load()` proves is -the same reliability that `fit_sequential` workers depend on (they -reconstruct a project from CIF strings). Implementing `load()` forces us -to fix any serialisation gaps before they become worker bugs. Phase 3 -(dataset replay) also directly uses `load()`. +**Done.** `Project.load()` reads CIF files from the project directory, +reconstructs structures, experiments, and analysis. Resolves alias +`param_unique_name` strings back to live `Parameter` references. +Integration tests verify save → load → parameter comparison and +save → load → fit → χ² comparison. ### Sequential-fitting prerequisite PRs @@ -1043,7 +1030,7 @@ to fix any serialisation gaps before they become worker bugs. Phase 3 > Absorbed into PR 2. Aliases now use `param_unique_name` with direct > object references. All tutorials and tests updated. -#### PR 5 — Fix CIF collection truncation (§ 9.2) +#### PR 5 — Fix CIF collection truncation (§ 9.2) ✅ > **Title:** `Remove max_display truncation from CIF serialisation` > @@ -1052,7 +1039,11 @@ to fix any serialisation gaps before they become worker bugs. Phase 3 > (`show_as_cif()`). Ensures experiments with many background/data > points survive CIF round-trips. -#### PR 6 — Verify CIF round-trip for experiments (§ 9.3) +**Done.** `category_collection_to_cif` default changed to +`max_display=None` (emit all rows). Truncation is now opt-in, only used +by display methods. + +#### PR 6 — Verify CIF round-trip for experiments (§ 9.3) ✅ > **Title:** `Add CIF round-trip integration test for experiments` > @@ -1062,7 +1053,11 @@ to fix any serialisation gaps before they become worker bugs. Phase 3 > asserts all parameter values match. Fix any parameters that don't > survive the round-trip. -#### PR 7 — Move analysis.cif into analysis/ directory (§ 9.6) +**Done.** Five integration tests in `test_cif_round_trip.py`: parameter +values, free flags, categories (background/excluded regions/linked +phases), data points, and structure round-trip. + +#### PR 7 — Move analysis.cif into analysis/ directory (§ 9.6) ✅ > **Title:** `Move analysis.cif into analysis/ directory` > @@ -1071,7 +1066,12 @@ to fix any serialisation gaps before they become worker bugs. Phase 3 > from the new path (with fallback to old path). Update docs > (`architecture.md`, `project.md`), tests, and console output messages. -#### PR 8 — Add destination to extract_data_paths_from_zip (§ 9.4) +**Done.** `Project.save()` writes to `analysis/analysis.cif`. +`Project.load()` checks `analysis/analysis.cif` first, falls back to +`analysis.cif` at root for backward compatibility. Unit tests verify +both layouts. + +#### PR 8 — Add destination to extract_data_paths_from_zip (§ 9.4) ✅ > **Title:** `Add destination parameter to extract_data_paths_from_zip` > @@ -1080,9 +1080,13 @@ to fix any serialisation gaps before they become worker bugs. Phase 3 > directory instead of a temp dir. Enables clean two-step workflow: > extract ZIP → pass directory to `fit_sequential()`. +**Done.** `extract_data_paths_from_zip` accepts `destination` parameter. +When provided, extracts to the given directory. When `None`, uses a +temporary directory (original behaviour). + ### Sequential-fitting core PRs -#### PR 9 — Streaming sequential fit (max_workers=1) +#### PR 9 — Streaming sequential fit (max_workers=1) ✅ > **Title:** `Add fit_sequential() for streaming single-worker fitting` > @@ -1094,14 +1098,14 @@ to fix any serialisation gaps before they become worker bugs. Phase 3 > `extract_diffrn` callback support for metadata columns. Unit tests for > CSV writing, crash recovery, parameter propagation. -This is a sub-step breakdown if the PR proves too large: - -- **PR 9a:** `Add SequentialFitTemplate and _fit_worker function` — - dataclass, worker function, no CSV, no recovery. -- **PR 9b:** `Add CSV output and crash recovery to fit_sequential` — CSV - writing, reading, resumption logic. -- **PR 9c:** `Add parameter propagation and extract_diffrn callback` — - chunk-to-chunk seeding, diffrn metadata columns. +**Done.** Full implementation in `analysis/sequential.py`: +`SequentialFitTemplate` dataclass, `_fit_worker()` module-level function, +CSV helpers (`_build_csv_header`, `_write_csv_header`, `_append_to_csv`, +`_read_csv_for_recovery`), `_build_template()`, chunk-based processing +with parameter propagation, `extract_diffrn` callback support, progress +reporting. Five integration tests in `test_sequential.py`: CSV +production, crash recovery, parameter propagation, diffrn callback, +precondition validation. #### PR 10 — Update plot_param_series to read from CSV @@ -1157,18 +1161,18 @@ This is a sub-step breakdown if the PR proves too large: ### Dependency graph ``` -PR 1 (issue #7: eliminate dummy Experiments) +PR 1 (issue #7: eliminate dummy Experiments) ✅ └─► PR 2 (issue #4: UID map + constraints) ✅ - └─► PR 3 (issue #1: Project.load) - └─► PR 5 (CIF truncation) - └─► PR 6 (CIF round-trip test) - ├─► PR 7 (analysis.cif → analysis/) - │ └─► PR 9 (streaming sequential fit) - │ ├─► PR 10 (plot from CSV) + └─► PR 3 (issue #1: Project.load) ✅ + └─► PR 5 (CIF truncation) ✅ + └─► PR 6 (CIF round-trip test) ✅ + ├─► PR 7 (analysis.cif → analysis/) ✅ + │ └─► PR 9 (streaming sequential fit) ✅ + │ ├─► PR 10 (plot from CSV) ← next │ │ └─► PR 13 (CSV for existing fit) │ └─► PR 11 (parallel fitting) │ └─► PR 14 (optional: parallel fit()) - └─► PR 8 (zip destination) + └─► PR 8 (zip destination) ✅ └─► PR 12 (dataset replay) ``` @@ -1187,18 +1191,20 @@ are all stdlib. ### Risks -| Risk | Mitigation | -| ------------------------------------------------ | -------------------------------------------------------- | -| CIF round-trip loses information | PR 3 (load) + PR 6 (round-trip test) verify before PR 9 | -| CIF collection truncation at 20 rows | PR 5 fixes before PR 9 | -| Worker memory leak (large N, long-running pool) | Use `max_tasks_per_child=100` on the pool | -| Pickling failures for SequentialFitTemplate | Keep it a plain dataclass with only str/dict/list fields | -| crysfml Fortran global state in forked processes | Enforced `spawn` context avoids fork issues | +| Risk | Mitigation | +| ------------------------------------------------ | ------------------------------------------------------------- | +| CIF round-trip loses information | ✅ PR 3 (load) + PR 6 (round-trip test) verified | +| CIF collection truncation at 20 rows | ✅ PR 5 fixed (default `max_display=None`) | +| Worker memory leak (large N, long-running pool) | Use `max_tasks_per_child=100` on the pool (PR 11) | +| Pickling failures for SequentialFitTemplate | ✅ Keep it a plain dataclass with only str/dict/list fields | +| crysfml Fortran global state in forked processes | Enforced `spawn` context avoids fork issues (PR 11) | -### Resolved open issues (now prerequisites) +### Resolved open issues (now prerequisites) — all done ✅ -- **Issue #7 (dummy Experiments wrapper):** resolved in PR 1. The worker - uses the clean `Fitter.fit(structures, [experiment])` API. +- **Issue #7 (dummy Experiments wrapper):** resolved in PR 1. + `Fitter.fit()` and `_residual_function()` accept + `list[ExperimentBase]`. The worker uses the clean + `Fitter.fit(structures, [experiment])` API. - **Issue #4 (constraint refresh) + § 9.1 (alias unique_name) + § 9.5 (singletons):** resolved in PR 2. `UidMapHandler` eliminated; aliases use direct object references and deterministic `unique_name` for CIF; @@ -1206,31 +1212,31 @@ are all stdlib. auto-enable on `create()`. `ConstraintsHandler` remains a singleton but is always in sync — multi-project isolation is an optional follow-up. -- **Issue #1 (Project.load):** resolved in PR 3. CIF round-trip - reliability is proven before workers depend on it. Dataset replay - (PR 12) uses `load()` directly. Note: `Project.load()` must now - resolve `_alias.param_unique_name` strings back to `Parameter` objects - by building a temporary `unique_name → Parameter` map. +- **Issue #1 (Project.load):** resolved in PR 3. `Project.load()` reads + CIF files, reconstructs full project state, resolves alias + `param_unique_name` strings back to `Parameter` objects via + `_resolve_alias_references()`. Dataset replay (PR 12) uses `load()` + directly. --- ## 12. Summary -| Aspect | Decision | -| ------------------- | ---------------------------------------------------------------------------------- | -| Parallelism backend | `concurrent.futures.ProcessPoolExecutor` with `spawn` | -| Worker isolation | Each worker creates a fresh `Project` — no shared state | -| Data source | `data_dir` argument; ZIP → extract first | -| Data flow | Template CIF + data path → worker → result dict → CSV | -| Parameter IDs | `unique_name` (deterministic), not `uid` (random) | -| Parameter seeding | Last successful result in chunk → next chunk | -| CSV location | `project_dir/analysis/results.csv` (deterministic) | -| CSV contents | Fit metrics + diffrn metadata + all free param values/uncert | -| Metadata extraction | User-provided `extract_diffrn` callback, not hidden in lib | -| Crash recovery | Read existing CSV, skip fitted files, resume | -| Plotting | Unified `plot_param_series()` always reads from CSV | -| Configuration | `max_workers` + `data_dir` on `fit_sequential()` | -| Project layout | `analysis.cif` moves into `analysis/` directory | -| Singletons | `UidMapHandler` eliminated; `ConstraintsHandler` stays singleton but always synced | -| New dependencies | None (stdlib only) | -| First step | PRs 1–3 (foundation issues), then PRs 4–8 (prerequisites), then PR 9+ | +| Aspect | Decision | Status | +| ------------------- | ---------------------------------------------------------------------------------- | ------ | +| Parallelism backend | `concurrent.futures.ProcessPoolExecutor` with `spawn` | PR 11 | +| Worker isolation | Each worker creates a fresh `Project` — no shared state | ✅ | +| Data source | `data_dir` argument; ZIP → extract first | ✅ | +| Data flow | Template CIF + data path → worker → result dict → CSV | ✅ | +| Parameter IDs | `unique_name` (deterministic), not `uid` (random) | ✅ | +| Parameter seeding | Last successful result in chunk → next chunk | ✅ | +| CSV location | `project_dir/analysis/results.csv` (deterministic) | ✅ | +| CSV contents | Fit metrics + diffrn metadata + all free param values/uncert | ✅ | +| Metadata extraction | User-provided `extract_diffrn` callback, not hidden in lib | ✅ | +| Crash recovery | Read existing CSV, skip fitted files, resume | ✅ | +| Plotting | Unified `plot_param_series()` always reads from CSV | PR 10 | +| Configuration | `max_workers` + `data_dir` on `fit_sequential()` | ✅ | +| Project layout | `analysis.cif` moves into `analysis/` directory | ✅ | +| Singletons | `UidMapHandler` eliminated; `ConstraintsHandler` stays singleton but always synced | ✅ | +| New dependencies | None (stdlib only) | ✅ | +| First step | PRs 1–9 done; PRs 10–14 remaining | ✅ | diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index 3a675381..98517729 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -722,6 +722,60 @@ def fit(self, verbosity: str | None = None) -> None: if self.project.info.path is not None: self.project.save() + def fit_sequential( + self, + data_dir: str, + max_workers: int | str = 1, + chunk_size: int | None = None, + file_pattern: str = '*', + extract_diffrn: object = None, + verbosity: str | None = None, + ) -> None: + """ + Run sequential fitting over all data files in a directory. + + Fits each dataset independently using the current structure and + experiment as a template. Results are written incrementally to + ``analysis/results.csv`` in the project directory. + + The project must contain exactly one structure and one + experiment (the template), and must have been saved + (``save_as()``) before calling this method. + + Parameters + ---------- + data_dir : str + Path to directory containing data files. + max_workers : int | str, default=1 + Number of parallel worker processes. ``1`` = sequential. + ``'auto'`` = physical CPU count. + chunk_size : int | None, default=None + Files per chunk. Default ``None`` uses *max_workers*. + file_pattern : str, default='*' + Glob pattern to filter files in *data_dir*. + extract_diffrn : object, default=None + User callback ``f(file_path) → {diffrn_field: value}``. + Called per file after fitting. ``None`` = no diffrn + metadata. + verbosity : str | None, default=None + ``'full'``, ``'short'``, or ``'silent'``. Default: project + verbosity. + """ + from easydiffraction.analysis.sequential import fit_sequential as _fit_seq # noqa: PLC0415 + + # Apply constraints before building the template + self._update_categories() + + _fit_seq( + analysis=self, + data_dir=data_dir, + max_workers=max_workers, + chunk_size=chunk_size, + file_pattern=file_pattern, + extract_diffrn=extract_diffrn, + verbosity=verbosity, + ) + def show_fit_results(self) -> None: """ Display a summary of the fit results. diff --git a/src/easydiffraction/analysis/sequential.py b/src/easydiffraction/analysis/sequential.py new file mode 100644 index 00000000..739fc1c4 --- /dev/null +++ b/src/easydiffraction/analysis/sequential.py @@ -0,0 +1,681 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +""" +Sequential fitting infrastructure: template, worker, CSV, recovery. +""" + +from __future__ import annotations + +import contextlib +import csv +from dataclasses import dataclass +from dataclasses import replace +from pathlib import Path +from typing import TYPE_CHECKING +from typing import Any + +from easydiffraction.io.ascii import extract_data_paths_from_dir +from easydiffraction.utils.enums import VerbosityEnum +from easydiffraction.utils.logging import log + +if TYPE_CHECKING: + from collections.abc import Callable + +# ------------------------------------------------------------------ +# Template dataclass (picklable for future multiprocessing) +# ------------------------------------------------------------------ + + +@dataclass(frozen=True) +class SequentialFitTemplate: + """ + Snapshot of everything a worker needs to recreate and fit a project. + + All fields are plain Python types (str, dict, list) so that the + template can be pickled for ``ProcessPoolExecutor`` in the future. + """ + + structure_cif: str + experiment_cif: str + initial_params: dict[str, float] + free_param_unique_names: list[str] + alias_defs: list[dict[str, str]] + constraint_defs: list[str] + constraints_enabled: bool + minimizer_tag: str + calculator_tag: str + diffrn_field_names: list[str] + + +# ------------------------------------------------------------------ +# Worker function (module-level for pickling) +# ------------------------------------------------------------------ + + +def _fit_worker( + template: SequentialFitTemplate, + data_path: str, +) -> dict[str, Any]: + """ + Fit a single dataset in isolation. + + Creates a fresh Project, loads the template configuration via CIF, + replaces data from *data_path*, applies initial parameters, fits, + and returns a plain dict of results. + + Parameters + ---------- + template : SequentialFitTemplate + Snapshot of the project configuration. + data_path : str + Path to the data file to fit. + + Returns + ------- + dict[str, Any] + Result dict with keys: ``file_path``, ``fit_success``, + ``chi_squared``, ``reduced_chi_squared``, ``n_iterations``, and + per-parameter ``{unique_name}`` / ``{unique_name}.uncertainty``. + """ + # Lazy import to avoid circular dependencies and keep the module + # importable without heavy imports at top level. + from easydiffraction.project.project import Project # noqa: PLC0415 + + result: dict[str, Any] = {'file_path': data_path} + + try: + # 1. Create a fresh, isolated project + Project._loading = True + try: + project = Project(name='_worker') + finally: + Project._loading = False + + # 2. Load structure from template CIF + project.structures.add_from_cif_str(template.structure_cif) + + # 3. Load experiment from template CIF + # (full config + template data) + project.experiments.add_from_cif_str(template.experiment_cif) + expt = list(project.experiments.values())[0] + + # 4. Replace data from the new data path + expt._load_ascii_data_to_experiment(data_path) + + # 5. Override parameter values from propagated starting values + _apply_param_overrides(project, template.initial_params) + + # 6. Set free flags + _set_free_params(project, template.free_param_unique_names) + + # 7. Apply constraints + if template.constraints_enabled and template.alias_defs: + _apply_constraints( + project, + template.alias_defs, + template.constraint_defs, + ) + + # 8. Set calculator and minimizer + # (internal, no console output) + from easydiffraction.analysis.calculators.factory import CalculatorFactory # noqa: PLC0415 + from easydiffraction.analysis.fitting import Fitter # noqa: PLC0415 + + expt._calculator = CalculatorFactory.create(template.calculator_tag) + expt._calculator_type = template.calculator_tag + project.analysis.fitter = Fitter(template.minimizer_tag) + + # 9. Fit + project.analysis.fit(verbosity='silent') + + # 10. Collect results + result.update(_collect_results(project, template)) + + except Exception as exc: # noqa: BLE001 + result['fit_success'] = False + result['chi_squared'] = None + result['reduced_chi_squared'] = None + result['n_iterations'] = 0 + result['error'] = str(exc) + + return result + + +# ------------------------------------------------------------------ +# Helper functions +# ------------------------------------------------------------------ + + +def _apply_param_overrides( + project: object, + overrides: dict[str, float], +) -> None: + """ + Set parameter values from a ``{unique_name: value}`` dict. + + Parameters + ---------- + project : object + The worker's project instance. + overrides : dict[str, float] + Map of parameter unique names to values. + """ + all_params = project.structures.parameters + project.experiments.parameters + by_name = {p.unique_name: p for p in all_params if hasattr(p, 'unique_name')} + for name, value in overrides.items(): + if name in by_name: + by_name[name].value = value + + +def _set_free_params( + project: object, + free_names: list[str], +) -> None: + """ + Mark parameters as free based on their unique names. + + Parameters + ---------- + project : object + The worker's project instance. + free_names : list[str] + Unique names of parameters to mark as free. + """ + from easydiffraction.core.variable import Parameter # noqa: PLC0415 + + all_params = project.structures.parameters + project.experiments.parameters + free_set = set(free_names) + for p in all_params: + if isinstance(p, Parameter) and hasattr(p, 'unique_name'): + p.free = p.unique_name in free_set + + +def _apply_constraints( + project: object, + alias_defs: list[dict[str, str]], + constraint_defs: list[str], +) -> None: + """ + Recreate aliases and constraints in the worker project. + + Parameters + ---------- + project : object + The worker's project instance. + alias_defs : list[dict[str, str]] + Each dict has ``label`` and ``param_unique_name``. + constraint_defs : list[str] + Constraint expression strings. + """ + all_params = project.structures.parameters + project.experiments.parameters + by_name = {p.unique_name: p for p in all_params if hasattr(p, 'unique_name')} + + for alias_def in alias_defs: + param = by_name.get(alias_def['param_unique_name']) + if param is not None: + project.analysis.aliases.create( + label=alias_def['label'], + param=param, + ) + + for expr in constraint_defs: + project.analysis.constraints.create(expression=expr) + + +def _collect_results( + project: object, + template: SequentialFitTemplate, +) -> dict[str, Any]: + """ + Collect fit results into a plain dict. + + Parameters + ---------- + project : object + The worker's project instance after fitting. + template : SequentialFitTemplate + The template (for knowing which params to collect). + + Returns + ------- + dict[str, Any] + Fit metrics and parameter values/uncertainties. + """ + from easydiffraction.core.variable import Parameter # noqa: PLC0415 + + result: dict[str, Any] = {} + fit_results = project.analysis.fit_results + + if fit_results is not None: + result['fit_success'] = fit_results.success + result['chi_squared'] = fit_results.chi_square + result['reduced_chi_squared'] = fit_results.reduced_chi_square + result['n_iterations'] = project.analysis.fitter.minimizer.tracker.best_iteration or 0 + else: + result['fit_success'] = False + result['chi_squared'] = None + result['reduced_chi_squared'] = None + result['n_iterations'] = 0 + + # Collect all free parameter values and uncertainties + all_params = project.structures.parameters + project.experiments.parameters + free_set = set(template.free_param_unique_names) + result['params'] = {} + for p in all_params: + if isinstance(p, Parameter) and p.unique_name in free_set: + result[p.unique_name] = p.value + result[f'{p.unique_name}.uncertainty'] = p.uncertainty + result['params'][p.unique_name] = p.value + + return result + + +# ------------------------------------------------------------------ +# CSV helpers +# ------------------------------------------------------------------ + +_META_COLUMNS = [ + 'file_path', + 'chi_squared', + 'reduced_chi_squared', + 'fit_success', + 'n_iterations', +] + + +def _build_csv_header( + template: SequentialFitTemplate, +) -> list[str]: + """ + Build the CSV column header list. + + Parameters + ---------- + template : SequentialFitTemplate + The template for diffrn fields and free param names. + + Returns + ------- + list[str] + Ordered list of column names. + """ + header = list(_META_COLUMNS) + header.extend(f'diffrn.{field}' for field in template.diffrn_field_names) + for name in template.free_param_unique_names: + header.append(name) + header.append(f'{name}.uncertainty') + return header + + +def _write_csv_header( + csv_path: Path, + header: list[str], +) -> None: + """ + Create the CSV file and write the header row. + + Parameters + ---------- + csv_path : Path + Path to the CSV file. + header : list[str] + Column names. + """ + with csv_path.open('w', newline='', encoding='utf-8') as f: + writer = csv.DictWriter(f, fieldnames=header) + writer.writeheader() + + +def _append_to_csv( + csv_path: Path, + header: list[str], + results: list[dict[str, Any]], +) -> None: + """ + Append result rows to the CSV file. + + Parameters + ---------- + csv_path : Path + Path to the CSV file. + header : list[str] + Column names (for DictWriter fieldnames). + results : list[dict[str, Any]] + Result dicts from workers. + """ + with csv_path.open('a', newline='', encoding='utf-8') as f: + writer = csv.DictWriter(f, fieldnames=header, extrasaction='ignore') + for result in results: + writer.writerow(result) + + +def _read_csv_for_recovery( + csv_path: Path, +) -> tuple[set[str], dict[str, float] | None]: + """ + Read an existing CSV for crash recovery. + + Parameters + ---------- + csv_path : Path + Path to the CSV file. + + Returns + ------- + tuple[set[str], dict[str, float] | None] + A set of already-fitted file paths and the parameter values from + the last successful row (or ``None`` if no rows). + """ + fitted: set[str] = set() + last_params: dict[str, float] | None = None + + if not csv_path.is_file(): + return fitted, last_params + + with csv_path.open(newline='', encoding='utf-8') as f: + reader = csv.DictReader(f) + for row in reader: + file_path = row.get('file_path', '') + if file_path: + fitted.add(file_path) + if row.get('fit_success', '').lower() == 'true': + # Extract parameter values from this row + params: dict[str, float] = {} + for key, val in row.items(): + if key in _META_COLUMNS: + continue + if key.startswith('diffrn.'): + continue + if key.endswith('.uncertainty'): + continue + if val: + with contextlib.suppress(ValueError, TypeError): + params[key] = float(val) + if params: + last_params = params + + return fitted, last_params + + +# ------------------------------------------------------------------ +# Template builder +# ------------------------------------------------------------------ + + +def _build_template(project: object) -> SequentialFitTemplate: + """ + Build a SequentialFitTemplate from the current project state. + + Parameters + ---------- + project : object + The main project instance (must have exactly 1 structure and 1 + experiment). + + Returns + ------- + SequentialFitTemplate + A frozen, picklable snapshot. + """ + from easydiffraction.core.variable import Parameter # noqa: PLC0415 + + structure = list(project.structures.values())[0] + experiment = list(project.experiments.values())[0] + + # Collect free parameter unique_names and initial values + all_params = project.structures.parameters + project.experiments.parameters + free_names: list[str] = [] + initial_params: dict[str, float] = {} + for p in all_params: + if isinstance(p, Parameter) and not p.constrained and p.free: + free_names.append(p.unique_name) + initial_params[p.unique_name] = p.value + + # Collect alias definitions + alias_defs: list[dict[str, str]] = [ + { + 'label': alias.label.value, + 'param_unique_name': alias.param_unique_name.value, + } + for alias in project.analysis.aliases + ] + + # Collect constraint expressions + constraint_defs: list[str] = [ + constraint.expression.value for constraint in project.analysis.constraints + ] + + # Collect diffrn field names from the experiment + diffrn_field_names: list[str] = [] + if hasattr(experiment, 'diffrn'): + diffrn_field_names.extend( + p.name + for p in experiment.diffrn.parameters + if hasattr(p, 'name') and p.name not in ('type',) + ) + + return SequentialFitTemplate( + structure_cif=structure.as_cif, + experiment_cif=experiment.as_cif, + initial_params=initial_params, + free_param_unique_names=free_names, + alias_defs=alias_defs, + constraint_defs=constraint_defs, + constraints_enabled=project.analysis.constraints.enabled, + minimizer_tag=project.analysis.current_minimizer or 'lmfit', + calculator_tag=experiment.calculator_type, + diffrn_field_names=diffrn_field_names, + ) + + +# ------------------------------------------------------------------ +# Progress reporting +# ------------------------------------------------------------------ + + +def _report_chunk_progress( + chunk_idx: int, + total_chunks: int, + results: list[dict[str, Any]], + verbosity: VerbosityEnum, +) -> None: + """ + Report progress after a chunk completes. + + Parameters + ---------- + chunk_idx : int + 1-based index of the current chunk. + total_chunks : int + Total number of chunks. + results : list[dict[str, Any]] + Results from the chunk. + verbosity : VerbosityEnum + Output verbosity. + """ + if verbosity is VerbosityEnum.SILENT: + return + + num_files = len(results) + successful = [r for r in results if r.get('fit_success')] + if successful: + avg_chi2 = sum(r['reduced_chi_squared'] for r in successful) / len(successful) + chi2_str = f'{avg_chi2:.2f}' + else: + chi2_str = '—' + + if verbosity is VerbosityEnum.SHORT: + status = '✅' if successful else '❌' + print(f'{status} Chunk {chunk_idx}/{total_chunks}: {num_files} files, avg χ² = {chi2_str}') + elif verbosity is VerbosityEnum.FULL: + print( + f'Chunk {chunk_idx}/{total_chunks}: ' + f'{num_files} files, {len(successful)} succeeded, ' + f'avg reduced χ² = {chi2_str}' + ) + for r in results: + status = '✅' if r.get('fit_success') else '❌' + rchi2 = r.get('reduced_chi_squared') + rchi2_str = f'{rchi2:.2f}' if rchi2 is not None else '—' + print(f' {status} {Path(r["file_path"]).name}: χ² = {rchi2_str}') + + +# ------------------------------------------------------------------ +# Main orchestration +# ------------------------------------------------------------------ + + +def fit_sequential( + analysis: object, + data_dir: str, + max_workers: int | str = 1, + chunk_size: int | None = None, + file_pattern: str = '*', + extract_diffrn: Callable | None = None, + verbosity: str | None = None, +) -> None: + """ + Run sequential fitting over all data files in a directory. + + Parameters + ---------- + analysis : object + The ``Analysis`` instance (owns project reference). + data_dir : str + Path to directory containing data files. + max_workers : int | str, default=1 + Number of parallel worker processes. ``1`` = sequential. + ``'auto'`` = physical CPU count (future). + chunk_size : int | None, default=None + Files per chunk. Default ``None`` uses ``max_workers``. + file_pattern : str, default='*' + Glob pattern to filter files in *data_dir*. + extract_diffrn : Callable | None, default=None + User callback: ``f(file_path) → {diffrn_field: value}``. + verbosity : str | None, default=None + ``'full'``, ``'short'``, ``'silent'``. Default: project + verbosity. + + Raises + ------ + ValueError + If preconditions are not met (e.g. multiple structures, missing + project path, no free parameters). + """ + project = analysis.project + verb = VerbosityEnum(verbosity if verbosity is not None else project.verbosity) + + # ── Preconditions ──────────────────────────────────────────── + if len(project.structures) != 1: + msg = f'Sequential fitting requires exactly 1 structure, found {len(project.structures)}.' + raise ValueError(msg) + + if len(project.experiments) != 1: + msg = ( + f'Sequential fitting requires exactly 1 experiment (the template), ' + f'found {len(project.experiments)}.' + ) + raise ValueError(msg) + + if project.info.path is None: + msg = 'Project must be saved before sequential fitting. Call save_as() first.' + raise ValueError(msg) + + # Discover data files + data_paths = extract_data_paths_from_dir(data_dir, file_pattern=file_pattern) + + from easydiffraction.core.variable import Parameter # noqa: PLC0415 + + free_params = [ + p for p in project.parameters if isinstance(p, Parameter) and not p.constrained and p.free + ] + if not free_params: + msg = 'No free parameters found. Mark at least one parameter as free.' + raise ValueError(msg) + + # ── Build template ─────────────────────────────────────────── + template = _build_template(project) + + # ── CSV setup and crash recovery ───────────────────────────── + csv_path = project.info.path / 'analysis' / 'results.csv' + csv_path.parent.mkdir(parents=True, exist_ok=True) + header = _build_csv_header(template) + + already_fitted, recovered_params = _read_csv_for_recovery(csv_path) + + if already_fitted: + num_skipped = len(already_fitted) + log.info(f'Resuming: {num_skipped} files already fitted, skipping.') + if verb is not VerbosityEnum.SILENT: + print(f'📂 Resuming from CSV: {num_skipped} files already fitted.') + # Seed from recovered params if available + if recovered_params is not None: + template = replace(template, initial_params=recovered_params) + else: + _write_csv_header(csv_path, header) + + # Filter out already-fitted files + remaining = [p for p in data_paths if p not in already_fitted] + if not remaining: + if verb is not VerbosityEnum.SILENT: + print('✅ All files already fitted. Nothing to do.') + return + + # ── Resolve workers and chunk size ─────────────────────────── + if isinstance(max_workers, str) and max_workers == 'auto': + import os # noqa: PLC0415 + + max_workers = os.cpu_count() or 1 + + if not isinstance(max_workers, int) or max_workers < 1: + msg = f"max_workers must be a positive integer or 'auto', got {max_workers!r}" + raise ValueError(msg) + + if chunk_size is None: + chunk_size = max_workers + + # ── Chunk and fit ──────────────────────────────────────────── + chunks = [remaining[i : i + chunk_size] for i in range(0, len(remaining), chunk_size)] + total_chunks = len(chunks) + + if verb is not VerbosityEnum.SILENT: + print( + f'🚀 Sequential fitting: {len(remaining)} files in ' + f'{total_chunks} chunks (max_workers={max_workers})' + ) + + for chunk_idx, chunk in enumerate(chunks, start=1): + # Single-worker mode: call worker directly + results = [_fit_worker(template, path) for path in chunk] + + # Extract diffrn metadata in the main process + if extract_diffrn is not None: + for result in results: + try: + diffrn_values = extract_diffrn(result['file_path']) + for key, val in diffrn_values.items(): + result[f'diffrn.{key}'] = val + except Exception as exc: # noqa: BLE001 + log.warning(f'extract_diffrn failed for {result["file_path"]}: {exc}') + + # Write to CSV + _append_to_csv(csv_path, header, results) + + # Report progress + _report_chunk_progress(chunk_idx, total_chunks, results, verb) + + # Propagate: use last successful file's + # params as starting values + last_ok = None + for r in reversed(results): + if r.get('fit_success') and r.get('params'): + last_ok = r + break + + if last_ok is not None: + template = replace(template, initial_params=last_ok['params']) + + if verb is not VerbosityEnum.SILENT: + total_fitted = len(already_fitted) + len(remaining) + print(f'✅ Sequential fitting complete: {total_fitted} files processed.') + print(f'📄 Results saved to: {csv_path}') diff --git a/tests/integration/fitting/test_sequential.py b/tests/integration/fitting/test_sequential.py new file mode 100644 index 00000000..c88f3b4b --- /dev/null +++ b/tests/integration/fitting/test_sequential.py @@ -0,0 +1,282 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Integration tests for Analysis.fit_sequential().""" + +from __future__ import annotations + +import csv +import shutil +import tempfile +from pathlib import Path + +import pytest +from numpy.testing import assert_almost_equal + +from easydiffraction import ExperimentFactory +from easydiffraction import Project +from easydiffraction import StructureFactory +from easydiffraction import download_data +from easydiffraction.utils.enums import VerbosityEnum + +TEMP_DIR = tempfile.gettempdir() + + +def _create_sequential_project(tmp_path: Path) -> tuple[Project, str]: + """ + Build a project for sequential fitting and save it. + + Returns the project and the path to a data directory with a few + copies of the same data file (to simulate a scan). + """ + # Structure + model = StructureFactory.from_scratch(name='lbco') + model.space_group.name_h_m = 'P m -3 m' + model.cell.length_a = 3.8909 + model.atom_sites.create( + label='La', + type_symbol='La', + fract_x=0, + fract_y=0, + fract_z=0, + wyckoff_letter='a', + occupancy=0.5, + b_iso=0.5, + ) + model.atom_sites.create( + label='Ba', + type_symbol='Ba', + fract_x=0, + fract_y=0, + fract_z=0, + wyckoff_letter='a', + occupancy=0.5, + b_iso=0.5, + ) + model.atom_sites.create( + label='Co', + type_symbol='Co', + fract_x=0.5, + fract_y=0.5, + fract_z=0.5, + wyckoff_letter='b', + b_iso=0.5, + ) + model.atom_sites.create( + label='O', + type_symbol='O', + fract_x=0, + fract_y=0.5, + fract_z=0.5, + wyckoff_letter='c', + b_iso=0.5, + ) + + # Experiment (template) + data_path = download_data(id=3, destination=TEMP_DIR) + expt = ExperimentFactory.from_data_path( + name='template', + data_path=data_path, + verbosity=VerbosityEnum.SILENT, + ) + expt.instrument.setup_wavelength = 1.494 + expt.instrument.calib_twotheta_offset = 0.6225 + expt.peak.broad_gauss_u = 0.0834 + expt.peak.broad_gauss_v = -0.1168 + expt.peak.broad_gauss_w = 0.123 + expt.peak.broad_lorentz_x = 0 + expt.peak.broad_lorentz_y = 0.0797 + expt.background.create(id='1', x=10, y=170) + expt.background.create(id='2', x=165, y=170) + expt.linked_phases.create(id='lbco', scale=9.0) + + # Project assembly + project = Project(name='seq_test') + project.structures.add(model) + project.experiments.add(expt) + + # Free parameters + model.cell.length_a.free = True + expt.linked_phases['lbco'].scale.free = True + expt.instrument.calib_twotheta_offset.free = True + expt.background['1'].y.free = True + expt.background['2'].y.free = True + + # Initial fit on the template + project.analysis.fit(verbosity='silent') + + # Save project + proj_dir = str(tmp_path / 'seq_project') + project.save_as(proj_dir) + + # Create a data directory with copies of the same data file + data_dir = tmp_path / 'scan_data' + data_dir.mkdir() + for i in range(3): + shutil.copy(data_path, data_dir / f'scan_{i + 1:03d}.xye') + + return project, str(data_dir) + + +# ------------------------------------------------------------------ +# Test 1: Basic sequential fit produces CSV +# ------------------------------------------------------------------ + + +def test_fit_sequential_produces_csv(tmp_path) -> None: + """fit_sequential creates a results.csv with one row per file.""" + project, data_dir = _create_sequential_project(tmp_path) + + project.analysis.fit_sequential( + data_dir=data_dir, + verbosity='silent', + ) + + csv_path = project.info.path / 'analysis' / 'results.csv' + assert csv_path.is_file(), 'results.csv was not created' + + with csv_path.open() as f: + reader = csv.DictReader(f) + rows = list(reader) + + assert len(rows) == 3, f'Expected 3 rows, got {len(rows)}' + + # Each row should have fit_success + for row in rows: + assert row['fit_success'] == 'True', f'Fit failed for {row["file_path"]}' + + # Each row should have parameter values + assert 'lbco.cell.length_a' in rows[0] + assert rows[0]['lbco.cell.length_a'] != '' + + +# ------------------------------------------------------------------ +# Test 2: Crash recovery skips already-fitted files +# ------------------------------------------------------------------ + + +def test_fit_sequential_crash_recovery(tmp_path) -> None: + """Running fit_sequential twice does not re-fit already-fitted files.""" + project, data_dir = _create_sequential_project(tmp_path) + + # First run: fit all 3 files + project.analysis.fit_sequential( + data_dir=data_dir, + verbosity='silent', + ) + + csv_path = project.info.path / 'analysis' / 'results.csv' + with csv_path.open() as f: + rows_first = list(csv.DictReader(f)) + assert len(rows_first) == 3 + + # Second run: should skip all 3 files + project.analysis.fit_sequential( + data_dir=data_dir, + verbosity='silent', + ) + + with csv_path.open() as f: + rows_second = list(csv.DictReader(f)) + # Still 3 rows — no duplicates + assert len(rows_second) == 3 + + +# ------------------------------------------------------------------ +# Test 3: Parameter propagation +# ------------------------------------------------------------------ + + +def test_fit_sequential_parameter_propagation(tmp_path) -> None: + """Parameters from one fit propagate to the next.""" + project, data_dir = _create_sequential_project(tmp_path) + + project.analysis.fit_sequential( + data_dir=data_dir, + verbosity='silent', + ) + + csv_path = project.info.path / 'analysis' / 'results.csv' + with csv_path.open() as f: + rows = list(csv.DictReader(f)) + + # All rows should have similar parameter values (same data) + vals = [float(r['lbco.cell.length_a']) for r in rows] + for v in vals: + assert_almost_equal(v, vals[0], decimal=3) + + +# ------------------------------------------------------------------ +# Test 4: extract_diffrn callback +# ------------------------------------------------------------------ + + +def test_fit_sequential_with_diffrn_callback(tmp_path) -> None: + """extract_diffrn callback populates diffrn columns in CSV.""" + project, data_dir = _create_sequential_project(tmp_path) + + temperatures = {'scan_001.xye': 300.0, 'scan_002.xye': 350.0, 'scan_003.xye': 400.0} + + def extract_diffrn(file_path: str) -> dict[str, float]: + name = Path(file_path).name + return {'ambient_temperature': temperatures.get(name, 0.0)} + + project.analysis.fit_sequential( + data_dir=data_dir, + extract_diffrn=extract_diffrn, + verbosity='silent', + ) + + csv_path = project.info.path / 'analysis' / 'results.csv' + with csv_path.open() as f: + rows = list(csv.DictReader(f)) + + # Check that temperature column is present and populated + for row in rows: + name = Path(row['file_path']).name + if 'diffrn.ambient_temperature' in row: + expected = temperatures.get(name, 0.0) + assert_almost_equal(float(row['diffrn.ambient_temperature']), expected) + + +# ------------------------------------------------------------------ +# Test 5: Precondition checks +# ------------------------------------------------------------------ + + +def test_fit_sequential_requires_saved_project(tmp_path) -> None: + """fit_sequential raises if project hasn't been saved.""" + data_path = download_data(id=3, destination=TEMP_DIR) + model = StructureFactory.from_scratch(name='s') + expt = ExperimentFactory.from_data_path( + name='e', + data_path=data_path, + verbosity=VerbosityEnum.SILENT, + ) + expt.linked_phases.create(id='s', scale=1.0) + expt.linked_phases['s'].scale.free = True + project = Project(name='unsaved') + project.structures.add(model) + project.experiments.add(expt) + + with pytest.raises(ValueError, match='must be saved'): + project.analysis.fit_sequential(data_dir=str(tmp_path)) + + +def test_fit_sequential_requires_one_structure(tmp_path) -> None: + """fit_sequential raises if no structures exist.""" + project = Project(name='no_struct') + project.save_as(str(tmp_path / 'proj')) + + with pytest.raises(ValueError, match='exactly 1 structure'): + project.analysis.fit_sequential(data_dir=str(tmp_path)) + + +def test_fit_sequential_requires_one_experiment(tmp_path) -> None: + """fit_sequential raises if no experiments exist.""" + model = StructureFactory.from_scratch(name='s') + project = Project(name='no_expt') + project.structures.add(model) + project.save_as(str(tmp_path / 'proj')) + + with pytest.raises(ValueError, match='exactly 1 experiment'): + project.analysis.fit_sequential(data_dir=str(tmp_path)) From de0d2184288016b29ecee82cd51b27267d2338b5 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 16:38:17 +0200 Subject: [PATCH 14/24] Unify plot_param_series to read from CSV with snapshot fallback --- docs/architecture/issues_closed.md | 8 +- docs/architecture/issues_open.md | 2 - .../architecture/sequential_fitting_design.md | 58 +++++++------ src/easydiffraction/display/plotting.py | 86 +++++++++++++++++-- src/easydiffraction/project/project.py | 36 ++++++-- 5 files changed, 145 insertions(+), 45 deletions(-) diff --git a/docs/architecture/issues_closed.md b/docs/architecture/issues_closed.md index 0612ea04..f95e3b89 100644 --- a/docs/architecture/issues_closed.md +++ b/docs/architecture/issues_closed.md @@ -10,10 +10,10 @@ Issues that have been fully resolved. Kept for historical reference. that reads `project.cif`, `structures/*.cif`, `experiments/*.cif`, and `analysis/analysis.cif` (with fallback to `analysis.cif` at root for backward compatibility). Reconstructs the full project state including -alias parameter references via `_resolve_alias_references()`. Integration -tests verify save → load → parameter comparison and save → load → fit → -χ² comparison. Also used by `fit_sequential` workers to reconstruct -projects from CIF strings. +alias parameter references via `_resolve_alias_references()`. +Integration tests verify save → load → parameter comparison and save → +load → fit → χ² comparison. Also used by `fit_sequential` workers to +reconstruct projects from CIF strings. --- diff --git a/docs/architecture/issues_open.md b/docs/architecture/issues_open.md index e01cc36d..b1ed9aa8 100644 --- a/docs/architecture/issues_open.md +++ b/docs/architecture/issues_open.md @@ -10,7 +10,6 @@ needed. --- - ## 2. 🟡 Restore Minimiser Variant Support **Type:** Feature loss + Design limitation @@ -107,7 +106,6 @@ effectively fixed after experiment creation. --- - ## 8. 🟡 Add Explicit `create()` Signatures on Collections **Type:** API safety diff --git a/docs/architecture/sequential_fitting_design.md b/docs/architecture/sequential_fitting_design.md index 9e11e8ca..183b01fc 100644 --- a/docs/architecture/sequential_fitting_design.md +++ b/docs/architecture/sequential_fitting_design.md @@ -1,6 +1,6 @@ # Sequential Fitting — Architecture Design -**Status:** Implementation in progress (PRs 1–9 complete, PRs 10–14 +**Status:** Implementation in progress (PRs 1–10 complete, PRs 11–14 remaining) **Date:** 2026-04-02 (updated 2026-04-03) --- @@ -888,9 +888,9 @@ tutorials, tests, and call sites updated. ### 9.4 Add `destination` parameter to `extract_data_paths_from_zip` ✅ -**Done.** Optional `destination` parameter added. When provided, extracts -to the given directory. When `None`, uses a temporary directory (original -behaviour). +**Done.** Optional `destination` parameter added. When provided, +extracts to the given directory. When `None`, uses a temporary directory +(original behaviour). ### 9.5 Replace singletons with instance-owned state (partially done) @@ -1020,8 +1020,8 @@ This PR also absorbed PR 4 (§ 9.1) since switching from random UIDs to **Done.** `Project.load()` reads CIF files from the project directory, reconstructs structures, experiments, and analysis. Resolves alias `param_unique_name` strings back to live `Parameter` references. -Integration tests verify save → load → parameter comparison and -save → load → fit → χ² comparison. +Integration tests verify save → load → parameter comparison and save → +load → fit → χ² comparison. ### Sequential-fitting prerequisite PRs @@ -1099,15 +1099,15 @@ temporary directory (original behaviour). > CSV writing, crash recovery, parameter propagation. **Done.** Full implementation in `analysis/sequential.py`: -`SequentialFitTemplate` dataclass, `_fit_worker()` module-level function, -CSV helpers (`_build_csv_header`, `_write_csv_header`, `_append_to_csv`, -`_read_csv_for_recovery`), `_build_template()`, chunk-based processing -with parameter propagation, `extract_diffrn` callback support, progress -reporting. Five integration tests in `test_sequential.py`: CSV -production, crash recovery, parameter propagation, diffrn callback, -precondition validation. +`SequentialFitTemplate` dataclass, `_fit_worker()` module-level +function, CSV helpers (`_build_csv_header`, `_write_csv_header`, +`_append_to_csv`, `_read_csv_for_recovery`), `_build_template()`, +chunk-based processing with parameter propagation, `extract_diffrn` +callback support, progress reporting. Five integration tests in +`test_sequential.py`: CSV production, crash recovery, parameter +propagation, diffrn callback, precondition validation. -#### PR 10 — Update plot_param_series to read from CSV +#### PR 10 — Update plot_param_series to read from CSV ✅ > **Title:** `Unify plot_param_series to always read from CSV` > @@ -1117,7 +1117,13 @@ precondition validation. > and existing `fit()` single-mode (Phase 4). Remove the old > `_parameter_snapshots` dict. -#### PR 11 — Parallel fitting (max_workers > 1) +**Implemented:** `Plotter.plot_param_series()` reads CSV via pandas. +`Plotter.plot_param_series_from_snapshots()` preserves backward +compatibility for `fit()` single-mode (no CSV yet). `Project.plot_param_series()` +tries CSV first, falls back to snapshots. Axis labels derived from live +descriptor objects. + +#### PR 11 — Parallel fitting (max_workers > 1) ← next > **Title:** `Add multiprocessing support to fit_sequential` > @@ -1168,9 +1174,9 @@ PR 1 (issue #7: eliminate dummy Experiments) ✅ └─► PR 6 (CIF round-trip test) ✅ ├─► PR 7 (analysis.cif → analysis/) ✅ │ └─► PR 9 (streaming sequential fit) ✅ - │ ├─► PR 10 (plot from CSV) ← next + │ ├─► PR 10 (plot from CSV) ✅ │ │ └─► PR 13 (CSV for existing fit) - │ └─► PR 11 (parallel fitting) + │ └─► PR 11 (parallel fitting) ← next │ └─► PR 14 (optional: parallel fit()) └─► PR 8 (zip destination) ✅ └─► PR 12 (dataset replay) @@ -1191,13 +1197,13 @@ are all stdlib. ### Risks -| Risk | Mitigation | -| ------------------------------------------------ | ------------------------------------------------------------- | -| CIF round-trip loses information | ✅ PR 3 (load) + PR 6 (round-trip test) verified | -| CIF collection truncation at 20 rows | ✅ PR 5 fixed (default `max_display=None`) | -| Worker memory leak (large N, long-running pool) | Use `max_tasks_per_child=100` on the pool (PR 11) | -| Pickling failures for SequentialFitTemplate | ✅ Keep it a plain dataclass with only str/dict/list fields | -| crysfml Fortran global state in forked processes | Enforced `spawn` context avoids fork issues (PR 11) | +| Risk | Mitigation | +| ------------------------------------------------ | ----------------------------------------------------------- | +| CIF round-trip loses information | ✅ PR 3 (load) + PR 6 (round-trip test) verified | +| CIF collection truncation at 20 rows | ✅ PR 5 fixed (default `max_display=None`) | +| Worker memory leak (large N, long-running pool) | Use `max_tasks_per_child=100` on the pool (PR 11) | +| Pickling failures for SequentialFitTemplate | ✅ Keep it a plain dataclass with only str/dict/list fields | +| crysfml Fortran global state in forked processes | Enforced `spawn` context avoids fork issues (PR 11) | ### Resolved open issues (now prerequisites) — all done ✅ @@ -1234,9 +1240,9 @@ are all stdlib. | CSV contents | Fit metrics + diffrn metadata + all free param values/uncert | ✅ | | Metadata extraction | User-provided `extract_diffrn` callback, not hidden in lib | ✅ | | Crash recovery | Read existing CSV, skip fitted files, resume | ✅ | -| Plotting | Unified `plot_param_series()` always reads from CSV | PR 10 | +| Plotting | Unified `plot_param_series()` always reads from CSV | ✅ | | Configuration | `max_workers` + `data_dir` on `fit_sequential()` | ✅ | | Project layout | `analysis.cif` moves into `analysis/` directory | ✅ | | Singletons | `UidMapHandler` eliminated; `ConstraintsHandler` stays singleton but always synced | ✅ | | New dependencies | None (stdlib only) | ✅ | -| First step | PRs 1–9 done; PRs 10–14 remaining | ✅ | +| First step | PRs 1–10 done; PRs 11–14 remaining | ✅ | diff --git a/src/easydiffraction/display/plotting.py b/src/easydiffraction/display/plotting.py index 5b010ea4..92a3a031 100644 --- a/src/easydiffraction/display/plotting.py +++ b/src/easydiffraction/display/plotting.py @@ -570,6 +570,79 @@ def plot_meas_vs_calc( ) def plot_param_series( + self, + csv_path: str, + unique_name: str, + param_descriptor: object, + versus_descriptor: object | None = None, + ) -> None: + """ + Plot a parameter's value across sequential fit results. + + Reads data from the CSV file at *csv_path*. The y-axis values + come from the column named *unique_name*, uncertainties from + ``{unique_name}.uncertainty``. When *versus_descriptor* is + provided, the x-axis uses the corresponding ``diffrn.{name}`` + column; otherwise the row index is used. + + Axis labels are derived from the live descriptor objects + (*param_descriptor* and *versus_descriptor*), which carry + ``.description`` and ``.units`` attributes. + + Parameters + ---------- + csv_path : str + Path to the ``results.csv`` file. + unique_name : str + Unique name of the parameter to plot (CSV column key). + param_descriptor : object + The live parameter descriptor (for axis label / units). + versus_descriptor : object | None, default=None + A diffrn descriptor whose ``.name`` maps to a + ``diffrn.{name}`` CSV column. ``None`` → use row index. + """ + df = pd.read_csv(csv_path) + + if unique_name not in df.columns: + log.warning( + f"Parameter '{unique_name}' not found in CSV columns. " + f'Available: {list(df.columns)}' + ) + return + + y = df[unique_name].astype(float).tolist() + uncert_col = f'{unique_name}.uncertainty' + sy = df[uncert_col].astype(float).tolist() if uncert_col in df.columns else [0.0] * len(y) + + # X-axis: diffrn column or row index + versus_name = versus_descriptor.name if versus_descriptor is not None else None + diffrn_col = f'diffrn.{versus_name}' if versus_name else None + + if diffrn_col and diffrn_col in df.columns: + x = pd.to_numeric(df[diffrn_col], errors='coerce').tolist() + x_label = getattr(versus_descriptor, 'description', None) or versus_name + if hasattr(versus_descriptor, 'units') and versus_descriptor.units: + x_label = f'{x_label} ({versus_descriptor.units})' + else: + x = list(range(1, len(y) + 1)) + x_label = 'Experiment No.' + + # Y-axis label from descriptor + param_units = getattr(param_descriptor, 'units', '') + y_label = f'Parameter value ({param_units})' if param_units else 'Parameter value' + + title = f"Parameter '{unique_name}' across fit results" + + self._backend.plot_scatter( + x=x, + y=y, + sy=sy, + axes_labels=[x_label, y_label], + title=title, + height=self.height, + ) + + def plot_param_series_from_snapshots( self, unique_name: str, versus_name: str | None, @@ -577,21 +650,22 @@ def plot_param_series( parameter_snapshots: dict[str, dict[str, dict]], ) -> None: """ - Plot a parameter's value across sequential fit results. + Plot a parameter's value from in-memory snapshots. + + This is a backward-compatibility method used when no CSV file is + available (e.g. after ``fit()`` in single mode, before PR 13 + adds CSV output to the existing fit loop). Parameters ---------- unique_name : str Unique name of the parameter to plot. versus_name : str | None - Name of the diffrn descriptor to use as the x-axis (e.g. - ``'ambient_temperature'``). When ``None``, the experiment - sequence index is used instead. + Name of the diffrn descriptor for the x-axis. experiments : object Experiments collection for accessing diffrn conditions. parameter_snapshots : dict[str, dict[str, dict]] - Per-experiment parameter value snapshots keyed by experiment - name, then by parameter unique name. + Per-experiment parameter value snapshots. """ x = [] y = [] diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index cb3a90fc..fe3db57d 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -480,6 +480,11 @@ def plot_param_series(self, param: object, versus: object | None = None) -> None """ Plot a parameter's value across sequential fit results. + When a ``results.csv`` file exists in the project's + ``analysis/`` directory, data is read from CSV. Otherwise, + falls back to in-memory parameter snapshots (produced by + ``fit()`` in single mode). + Parameters ---------- param : object @@ -492,10 +497,27 @@ def plot_param_series(self, param: object, versus: object | None = None) -> None experiment sequence number is used instead. """ unique_name = param.unique_name - versus_name = versus.name if versus is not None else None - self.plotter.plot_param_series( - unique_name, - versus_name, - self.experiments, - self.analysis._parameter_snapshots, - ) + + # Try CSV first (produced by fit_sequential or future fit) + csv_path = None + if self.info.path is not None: + candidate = pathlib.Path(self.info.path) / 'analysis' / 'results.csv' + if candidate.is_file(): + csv_path = str(candidate) + + if csv_path is not None: + self.plotter.plot_param_series( + csv_path=csv_path, + unique_name=unique_name, + param_descriptor=param, + versus_descriptor=versus, + ) + else: + # Fallback: in-memory snapshots from fit() single mode + versus_name = versus.name if versus is not None else None + self.plotter.plot_param_series_from_snapshots( + unique_name, + versus_name, + self.experiments, + self.analysis._parameter_snapshots, + ) From 0a6bd3bf2ed517e2cfdde403b5cfec24a24b5bd1 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 17:30:31 +0200 Subject: [PATCH 15/24] Add multiprocessing support to fit_sequential --- .../architecture/sequential_fitting_design.md | 29 +++--- src/easydiffraction/analysis/analysis.py | 3 +- src/easydiffraction/analysis/sequential.py | 88 ++++++++++++------- tests/integration/fitting/test_sequential.py | 34 +++++++ 4 files changed, 108 insertions(+), 46 deletions(-) diff --git a/docs/architecture/sequential_fitting_design.md b/docs/architecture/sequential_fitting_design.md index 183b01fc..9b844980 100644 --- a/docs/architecture/sequential_fitting_design.md +++ b/docs/architecture/sequential_fitting_design.md @@ -1,6 +1,6 @@ # Sequential Fitting — Architecture Design -**Status:** Implementation in progress (PRs 1–10 complete, PRs 11–14 +**Status:** Implementation in progress (PRs 1–11 complete, PRs 12–14 remaining) **Date:** 2026-04-02 (updated 2026-04-03) --- @@ -1119,11 +1119,11 @@ propagation, diffrn callback, precondition validation. **Implemented:** `Plotter.plot_param_series()` reads CSV via pandas. `Plotter.plot_param_series_from_snapshots()` preserves backward -compatibility for `fit()` single-mode (no CSV yet). `Project.plot_param_series()` -tries CSV first, falls back to snapshots. Axis labels derived from live -descriptor objects. +compatibility for `fit()` single-mode (no CSV yet). +`Project.plot_param_series()` tries CSV first, falls back to snapshots. +Axis labels derived from live descriptor objects. -#### PR 11 — Parallel fitting (max_workers > 1) ← next +#### PR 11 — Parallel fitting (max_workers > 1) ✅ > **Title:** `Add multiprocessing support to fit_sequential` > @@ -1133,6 +1133,13 @@ descriptor objects. > `max_workers='auto'` support (`os.cpu_count()`). Integration test: > parallel sequential fit (10 files, 2 workers). +**Implemented:** `ProcessPoolExecutor` with `mp.get_context('spawn')` +and `max_tasks_per_child=100` dispatches chunks in parallel when +`max_workers > 1`. Single-worker mode (`max_workers=1`) still calls +`_fit_worker` directly (no subprocess overhead). `max_workers='auto'` +resolves to `os.cpu_count()`. Integration test +`test_fit_sequential_parallel` verifies 2-worker parallel fitting. + ### Post-sequential PRs #### PR 12 — Dataset replay from CSV @@ -1175,8 +1182,8 @@ PR 1 (issue #7: eliminate dummy Experiments) ✅ ├─► PR 7 (analysis.cif → analysis/) ✅ │ └─► PR 9 (streaming sequential fit) ✅ │ ├─► PR 10 (plot from CSV) ✅ - │ │ └─► PR 13 (CSV for existing fit) - │ └─► PR 11 (parallel fitting) ← next + │ │ └─► PR 13 (CSV for existing fit) ← next + │ └─► PR 11 (parallel fitting) ✅ │ └─► PR 14 (optional: parallel fit()) └─► PR 8 (zip destination) ✅ └─► PR 12 (dataset replay) @@ -1201,9 +1208,9 @@ are all stdlib. | ------------------------------------------------ | ----------------------------------------------------------- | | CIF round-trip loses information | ✅ PR 3 (load) + PR 6 (round-trip test) verified | | CIF collection truncation at 20 rows | ✅ PR 5 fixed (default `max_display=None`) | -| Worker memory leak (large N, long-running pool) | Use `max_tasks_per_child=100` on the pool (PR 11) | +| Worker memory leak (large N, long-running pool) | ✅ `max_tasks_per_child=100` on the pool (PR 11) | | Pickling failures for SequentialFitTemplate | ✅ Keep it a plain dataclass with only str/dict/list fields | -| crysfml Fortran global state in forked processes | Enforced `spawn` context avoids fork issues (PR 11) | +| crysfml Fortran global state in forked processes | ✅ Enforced `spawn` context avoids fork issues (PR 11) | ### Resolved open issues (now prerequisites) — all done ✅ @@ -1230,7 +1237,7 @@ are all stdlib. | Aspect | Decision | Status | | ------------------- | ---------------------------------------------------------------------------------- | ------ | -| Parallelism backend | `concurrent.futures.ProcessPoolExecutor` with `spawn` | PR 11 | +| Parallelism backend | `concurrent.futures.ProcessPoolExecutor` with `spawn` | ✅ | | Worker isolation | Each worker creates a fresh `Project` — no shared state | ✅ | | Data source | `data_dir` argument; ZIP → extract first | ✅ | | Data flow | Template CIF + data path → worker → result dict → CSV | ✅ | @@ -1245,4 +1252,4 @@ are all stdlib. | Project layout | `analysis.cif` moves into `analysis/` directory | ✅ | | Singletons | `UidMapHandler` eliminated; `ConstraintsHandler` stays singleton but always synced | ✅ | | New dependencies | None (stdlib only) | ✅ | -| First step | PRs 1–10 done; PRs 11–14 remaining | ✅ | +| First step | PRs 1–11 done; PRs 12–14 remaining | ✅ | diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index 98517729..ce0c3304 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -748,7 +748,8 @@ def fit_sequential( Path to directory containing data files. max_workers : int | str, default=1 Number of parallel worker processes. ``1`` = sequential. - ``'auto'`` = physical CPU count. + ``'auto'`` = physical CPU count. Uses + ``ProcessPoolExecutor`` with ``spawn`` context when > 1. chunk_size : int | None, default=None Files per chunk. Default ``None`` uses *max_workers*. file_pattern : str, default='*' diff --git a/src/easydiffraction/analysis/sequential.py b/src/easydiffraction/analysis/sequential.py index 739fc1c4..412b5d10 100644 --- a/src/easydiffraction/analysis/sequential.py +++ b/src/easydiffraction/analysis/sequential.py @@ -8,6 +8,8 @@ import contextlib import csv +import multiprocessing as mp +from concurrent.futures import ProcessPoolExecutor from dataclasses import dataclass from dataclasses import replace from pathlib import Path @@ -22,7 +24,7 @@ from collections.abc import Callable # ------------------------------------------------------------------ -# Template dataclass (picklable for future multiprocessing) +# Template dataclass (picklable for ProcessPoolExecutor) # ------------------------------------------------------------------ @@ -32,7 +34,7 @@ class SequentialFitTemplate: Snapshot of everything a worker needs to recreate and fit a project. All fields are plain Python types (str, dict, list) so that the - template can be pickled for ``ProcessPoolExecutor`` in the future. + template can be pickled for ``ProcessPoolExecutor``. """ structure_cif: str @@ -544,8 +546,9 @@ def fit_sequential( data_dir : str Path to directory containing data files. max_workers : int | str, default=1 - Number of parallel worker processes. ``1`` = sequential. - ``'auto'`` = physical CPU count (future). + Number of parallel worker processes. ``1`` = sequential (no + subprocess overhead). ``'auto'`` = physical CPU count. Uses + ``ProcessPoolExecutor`` with ``spawn`` context when > 1. chunk_size : int | None, default=None Files per chunk. Default ``None`` uses ``max_workers``. file_pattern : str, default='*' @@ -644,36 +647,53 @@ def fit_sequential( f'{total_chunks} chunks (max_workers={max_workers})' ) - for chunk_idx, chunk in enumerate(chunks, start=1): - # Single-worker mode: call worker directly - results = [_fit_worker(template, path) for path in chunk] - - # Extract diffrn metadata in the main process - if extract_diffrn is not None: - for result in results: - try: - diffrn_values = extract_diffrn(result['file_path']) - for key, val in diffrn_values.items(): - result[f'diffrn.{key}'] = val - except Exception as exc: # noqa: BLE001 - log.warning(f'extract_diffrn failed for {result["file_path"]}: {exc}') - - # Write to CSV - _append_to_csv(csv_path, header, results) - - # Report progress - _report_chunk_progress(chunk_idx, total_chunks, results, verb) - - # Propagate: use last successful file's - # params as starting values - last_ok = None - for r in reversed(results): - if r.get('fit_success') and r.get('params'): - last_ok = r - break - - if last_ok is not None: - template = replace(template, initial_params=last_ok['params']) + # Create a process pool for parallel dispatch, or a no-op context + # for single-worker mode (avoids process-spawn overhead). + if max_workers > 1: + spawn_ctx = mp.get_context('spawn') + pool_cm = ProcessPoolExecutor( + max_workers=max_workers, + mp_context=spawn_ctx, + max_tasks_per_child=100, + ) + else: + pool_cm = contextlib.nullcontext() + + with pool_cm as executor: + for chunk_idx, chunk in enumerate(chunks, start=1): + # Dispatch: parallel or sequential + if executor is not None: + templates = [template] * len(chunk) + results = list(executor.map(_fit_worker, templates, chunk)) + else: + results = [_fit_worker(template, path) for path in chunk] + + # Extract diffrn metadata in the main process + if extract_diffrn is not None: + for result in results: + try: + diffrn_values = extract_diffrn(result['file_path']) + for key, val in diffrn_values.items(): + result[f'diffrn.{key}'] = val + except Exception as exc: # noqa: BLE001 + log.warning(f'extract_diffrn failed for {result["file_path"]}: {exc}') + + # Write to CSV + _append_to_csv(csv_path, header, results) + + # Report progress + _report_chunk_progress(chunk_idx, total_chunks, results, verb) + + # Propagate: use last successful file's + # params as starting values + last_ok = None + for r in reversed(results): + if r.get('fit_success') and r.get('params'): + last_ok = r + break + + if last_ok is not None: + template = replace(template, initial_params=last_ok['params']) if verb is not VerbosityEnum.SILENT: total_fitted = len(already_fitted) + len(remaining) diff --git a/tests/integration/fitting/test_sequential.py b/tests/integration/fitting/test_sequential.py index c88f3b4b..a018be9d 100644 --- a/tests/integration/fitting/test_sequential.py +++ b/tests/integration/fitting/test_sequential.py @@ -280,3 +280,37 @@ def test_fit_sequential_requires_one_experiment(tmp_path) -> None: with pytest.raises(ValueError, match='exactly 1 experiment'): project.analysis.fit_sequential(data_dir=str(tmp_path)) + + +# ------------------------------------------------------------------ +# Test 6: Parallel sequential fit (max_workers=2) +# ------------------------------------------------------------------ + + +def test_fit_sequential_parallel(tmp_path) -> None: + """fit_sequential with max_workers=2 produces correct CSV.""" + project, data_dir = _create_sequential_project(tmp_path) + + project.analysis.fit_sequential( + data_dir=data_dir, + max_workers=2, + verbosity='silent', + ) + + csv_path = project.info.path / 'analysis' / 'results.csv' + assert csv_path.is_file(), 'results.csv was not created' + + with csv_path.open() as f: + reader = csv.DictReader(f) + rows = list(reader) + + assert len(rows) == 3, f'Expected 3 rows, got {len(rows)}' + + for row in rows: + assert row['fit_success'] == 'True', f'Fit failed for {row["file_path"]}' + + # Parameter values should be present and reasonable + assert 'lbco.cell.length_a' in rows[0] + vals = [float(r['lbco.cell.length_a']) for r in rows] + for v in vals: + assert_almost_equal(v, vals[0], decimal=3) From 69accc15963c675cd4c5ee6de270d54db80ae7e4 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 18:02:49 +0200 Subject: [PATCH 16/24] Write results.csv from existing single-fit mode --- .../architecture/sequential_fitting_design.md | 20 ++++-- src/easydiffraction/analysis/analysis.py | 61 ++++++++++++++++++- 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/docs/architecture/sequential_fitting_design.md b/docs/architecture/sequential_fitting_design.md index 9b844980..1b47e90e 100644 --- a/docs/architecture/sequential_fitting_design.md +++ b/docs/architecture/sequential_fitting_design.md @@ -1,7 +1,7 @@ # Sequential Fitting — Architecture Design -**Status:** Implementation in progress (PRs 1–11 complete, PRs 12–14 -remaining) **Date:** 2026-04-02 (updated 2026-04-03) +**Status:** Implementation in progress (PRs 1–11, 13 complete; PRs 12, +14 remaining) **Date:** 2026-04-02 (updated 2026-04-03) --- @@ -1151,7 +1151,7 @@ resolves to `os.cpu_count()`. Integration test > reloads data from the file path in that row. Enables > `plot_meas_vs_calc()` for any previously fitted dataset. -#### PR 13 — CSV output for existing single-fit mode +#### PR 13 — CSV output for existing single-fit mode ✅ > **Title:** `Write results.csv from existing single-fit mode` > @@ -1160,6 +1160,14 @@ resolves to `os.cpu_count()`. Integration test > `fit_sequential`). This gives `ed-17.py`-style workflows persistent > CSV output and unified `plot_param_series()`. +**Implemented:** `Analysis.fit()` single-mode now writes +`analysis/results.csv` incrementally (one row per experiment) when the +project has been saved. Reuses `_META_COLUMNS`, `_write_csv_header`, and +`_append_to_csv` from `sequential.py`. Diffrn metadata and free +parameter values/uncertainties are written per row. The in-memory +`_parameter_snapshots` is kept for unsaved-project fallback. +`plot_param_series()` now uses CSV for saved projects automatically. + #### PR 14 (optional) — Parallel single-fit for pre-loaded experiments > **Title:** @@ -1182,11 +1190,11 @@ PR 1 (issue #7: eliminate dummy Experiments) ✅ ├─► PR 7 (analysis.cif → analysis/) ✅ │ └─► PR 9 (streaming sequential fit) ✅ │ ├─► PR 10 (plot from CSV) ✅ - │ │ └─► PR 13 (CSV for existing fit) ← next + │ │ └─► PR 13 (CSV for existing fit) ✅ │ └─► PR 11 (parallel fitting) ✅ │ └─► PR 14 (optional: parallel fit()) └─► PR 8 (zip destination) ✅ - └─► PR 12 (dataset replay) + └─► PR 12 (dataset replay) ← next ``` Note: PR 4 was absorbed into PR 2. PRs 5–8 are largely independent of @@ -1252,4 +1260,4 @@ are all stdlib. | Project layout | `analysis.cif` moves into `analysis/` directory | ✅ | | Singletons | `UidMapHandler` eliminated; `ConstraintsHandler` stays singleton but always synced | ✅ | | New dependencies | None (stdlib only) | ✅ | -| First step | PRs 1–11 done; PRs 12–14 remaining | ✅ | +| First step | PRs 1–11, 13 done; PRs 12, 14 remaining | ✅ | diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index ce0c3304..3f685b24 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: BSD-3-Clause from contextlib import suppress +from pathlib import Path import numpy as np import pandas as pd @@ -644,6 +645,44 @@ def fit(self, verbosity: str | None = None) -> None: expt_names = experiments.names num_expts = len(expt_names) + # CSV setup: write results if the project has been saved + csv_path = None + csv_header = None + csv_free_names = None + csv_diffrn_fields = None + if self.project.info.path is not None: + from easydiffraction.analysis.sequential import _META_COLUMNS # noqa: PLC0415 + from easydiffraction.analysis.sequential import _append_to_csv # noqa: PLC0415 + from easydiffraction.analysis.sequential import _write_csv_header # noqa: PLC0415 + + csv_path = Path(self.project.info.path) / 'analysis' / 'results.csv' + csv_path.parent.mkdir(parents=True, exist_ok=True) + + all_params = ( + self.project.structures.parameters + self.project.experiments.parameters + ) + csv_free_names = [ + p.unique_name + for p in all_params + if isinstance(p, Parameter) and not p.constrained and p.free + ] + + first_expt = list(experiments.values())[0] + csv_diffrn_fields = [] + if hasattr(first_expt, 'diffrn'): + csv_diffrn_fields = [ + p.name + for p in first_expt.diffrn.parameters + if hasattr(p, 'name') and p.name not in ('type',) + ] + + csv_header = list(_META_COLUMNS) + csv_header.extend(f'diffrn.{f}' for f in csv_diffrn_fields) + for name in csv_free_names: + csv_header.append(name) + csv_header.append(f'{name}.uncertainty') + _write_csv_header(csv_path, csv_header) + # Short mode: print header and create display handle once short_headers = ['experiment', 'χ²', 'iterations', 'status'] short_alignments = ['left', 'right', 'right', 'center'] @@ -689,6 +728,25 @@ def fit(self, verbosity: str | None = None) -> None: self._parameter_snapshots[expt_name] = snapshot self.fit_results = results + # Append row to CSV + if csv_path is not None: + row = { + 'file_path': expt_name, + 'fit_success': results.success, + 'chi_squared': results.chi_square, + 'reduced_chi_squared': results.reduced_chi_square, + 'n_iterations': (self.fitter.minimizer.tracker.best_iteration or 0), + } + if hasattr(experiment, 'diffrn') and csv_diffrn_fields: + for p in experiment.diffrn.parameters: + if hasattr(p, 'name') and p.name not in ('type',): + row[f'diffrn.{p.name}'] = p.value + for uname in csv_free_names: + if uname in snapshot: + row[uname] = snapshot[uname]['value'] + row[f'{uname}.uncertainty'] = snapshot[uname]['uncertainty'] + _append_to_csv(csv_path, csv_header, [row]) + # Short mode: append one summary row and update in-place if verb is VerbosityEnum.SHORT: chi2_str = ( @@ -716,9 +774,6 @@ def fit(self, verbosity: str | None = None) -> None: raise NotImplementedError(msg) # After fitting, save the project - # TODO: Consider saving individual data during sequential - # (single) fitting, instead of waiting until the end and save - # only the last one if self.project.info.path is not None: self.project.save() From 5a4e760dc4c7d55ed8a76b74e9e172c4a27a0210 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 18:37:10 +0200 Subject: [PATCH 17/24] Add apply_params_from_csv for dataset replay --- .../architecture/sequential_fitting_design.md | 15 ++-- src/easydiffraction/project/project.py | 80 +++++++++++++++++++ tests/integration/fitting/test_sequential.py | 57 +++++++++++++ 3 files changed, 147 insertions(+), 5 deletions(-) diff --git a/docs/architecture/sequential_fitting_design.md b/docs/architecture/sequential_fitting_design.md index 1b47e90e..0513c89f 100644 --- a/docs/architecture/sequential_fitting_design.md +++ b/docs/architecture/sequential_fitting_design.md @@ -1,7 +1,7 @@ # Sequential Fitting — Architecture Design -**Status:** Implementation in progress (PRs 1–11, 13 complete; PRs 12, -14 remaining) **Date:** 2026-04-02 (updated 2026-04-03) +**Status:** Implementation in progress (PRs 1–13 complete; PR 14 +optional) **Date:** 2026-04-02 (updated 2026-04-03) --- @@ -1142,7 +1142,7 @@ resolves to `os.cpu_count()`. Integration test ### Post-sequential PRs -#### PR 12 — Dataset replay from CSV +#### PR 12 — Dataset replay from CSV ✅ > **Title:** `Add apply_params_from_csv() for dataset replay` > @@ -1151,6 +1151,11 @@ resolves to `os.cpu_count()`. Integration test > reloads data from the file path in that row. Enables > `plot_meas_vs_calc()` for any previously fitted dataset. +**Implemented:** `Project.apply_params_from_csv(row_index)` reads a CSV +row, overrides parameter values and uncertainties, and reloads measured +data when `file_path` points to a real file (sequential-fit case). Three +integration tests: parameter override, missing CSV, out-of-range index. + #### PR 13 — CSV output for existing single-fit mode ✅ > **Title:** `Write results.csv from existing single-fit mode` @@ -1194,7 +1199,7 @@ PR 1 (issue #7: eliminate dummy Experiments) ✅ │ └─► PR 11 (parallel fitting) ✅ │ └─► PR 14 (optional: parallel fit()) └─► PR 8 (zip destination) ✅ - └─► PR 12 (dataset replay) ← next + └─► PR 12 (dataset replay) ✅ ``` Note: PR 4 was absorbed into PR 2. PRs 5–8 are largely independent of @@ -1260,4 +1265,4 @@ are all stdlib. | Project layout | `analysis.cif` moves into `analysis/` directory | ✅ | | Singletons | `UidMapHandler` eliminated; `ConstraintsHandler` stays singleton but always synced | ✅ | | New dependencies | None (stdlib only) | ✅ | -| First step | PRs 1–11, 13 done; PRs 12, 14 remaining | ✅ | +| First step | PRs 1–13 done; PR 14 optional | ✅ | diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index fe3db57d..31cd84ca 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -362,6 +362,86 @@ def save_as( self._info.path = dir_path self.save() + def apply_params_from_csv(self, row_index: int) -> None: + """ + Load a single CSV row and apply its parameters to the project. + + Reads the row at *row_index* from ``analysis/results.csv``, + overrides parameter values in the live project, and (for + sequential-fit results where ``file_path`` points to a real + file) reloads the measured data into the template experiment. + + After calling this method, ``plot_meas_vs_calc()`` will show the + fit for that specific dataset. + + Parameters + ---------- + row_index : int + 0-based row index in the CSV file. + + Raises + ------ + FileNotFoundError + If ``analysis/results.csv`` does not exist. + IndexError + If *row_index* is out of range. + """ + import pandas as pd # noqa: PLC0415 + + from easydiffraction.analysis.sequential import _META_COLUMNS # noqa: PLC0415 + from easydiffraction.core.variable import Parameter # noqa: PLC0415 + + if self.info.path is None: + msg = 'Project has no saved path. Save the project first.' + raise FileNotFoundError(msg) + + csv_path = pathlib.Path(self.info.path) / 'analysis' / 'results.csv' + if not csv_path.is_file(): + msg = f"Results CSV not found: '{csv_path}'" + raise FileNotFoundError(msg) + + df = pd.read_csv(csv_path) + if row_index < 0 or row_index >= len(df): + msg = f'Row index {row_index} out of range (CSV has {len(df)} rows).' + raise IndexError(msg) + + row = df.iloc[row_index] + + # 1. Reload data if file_path points to a real file + file_path = row.get('file_path', '') + if file_path and pathlib.Path(file_path).is_file(): + experiment = list(self.experiments.values())[0] + experiment._load_ascii_data_to_experiment(file_path) + + # 2. Override parameter values + all_params = self.structures.parameters + self.experiments.parameters + param_map = { + p.unique_name: p + for p in all_params + if isinstance(p, Parameter) and hasattr(p, 'unique_name') + } + + skip_cols = set(_META_COLUMNS) + for col_name in df.columns: + if col_name in skip_cols: + continue + if col_name.startswith('diffrn.'): + continue + if col_name.endswith('.uncertainty'): + continue + if col_name in param_map and pd.notna(row[col_name]): + param_map[col_name].value = float(row[col_name]) + + # 3. Apply uncertainties + for col_name in df.columns: + if not col_name.endswith('.uncertainty'): + continue + base_name = col_name.removesuffix('.uncertainty') + if base_name in param_map and pd.notna(row[col_name]): + param_map[base_name].uncertainty = float(row[col_name]) + + log.info(f'Applied parameters from CSV row {row_index} (file: {file_path}).') + # ------------------------------------------ # Plotting # ------------------------------------------ diff --git a/tests/integration/fitting/test_sequential.py b/tests/integration/fitting/test_sequential.py index a018be9d..59945e87 100644 --- a/tests/integration/fitting/test_sequential.py +++ b/tests/integration/fitting/test_sequential.py @@ -314,3 +314,60 @@ def test_fit_sequential_parallel(tmp_path) -> None: vals = [float(r['lbco.cell.length_a']) for r in rows] for v in vals: assert_almost_equal(v, vals[0], decimal=3) + + +# ------------------------------------------------------------------ +# Test 7: Dataset replay from CSV (apply_params_from_csv) +# ------------------------------------------------------------------ + + +def test_apply_params_from_csv_loads_data_and_params(tmp_path) -> None: + """apply_params_from_csv overrides params and reloads data.""" + project, data_dir = _create_sequential_project(tmp_path) + + project.analysis.fit_sequential( + data_dir=data_dir, + verbosity='silent', + ) + + csv_path = project.info.path / 'analysis' / 'results.csv' + with csv_path.open() as f: + rows = list(csv.DictReader(f)) + + # Read the expected cell_length_a from CSV row 1 + expected_a = float(rows[1]['lbco.cell.length_a']) + + # Apply params from row 1 + project.apply_params_from_csv(row_index=1) + + # Verify the parameter value was overridden + model = list(project.structures.values())[0] + assert_almost_equal(model.cell.length_a.value, expected_a, decimal=5) + + # Verify that the experiment has measured data loaded + # (from the file_path in that CSV row) + expt = list(project.experiments.values())[0] + assert expt.data.intensity_meas is not None + + +def test_apply_params_from_csv_raises_on_missing_csv(tmp_path) -> None: + """apply_params_from_csv raises if no CSV exists.""" + project = Project(name='no_csv') + project.save_as(str(tmp_path / 'proj')) + + with pytest.raises(FileNotFoundError, match='Results CSV not found'): + project.apply_params_from_csv(row_index=0) + + +def test_apply_params_from_csv_raises_on_bad_index(tmp_path) -> None: + """apply_params_from_csv raises on out-of-range index.""" + project, data_dir = _create_sequential_project(tmp_path) + + project.analysis.fit_sequential( + data_dir=data_dir, + verbosity='silent', + ) + + with pytest.raises(IndexError, match='out of range'): + project.apply_params_from_csv(row_index=99) + From 2d49d168aefd5ffd21874152666f7dfd5f6b6c22 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 21:08:47 +0200 Subject: [PATCH 18/24] Prevent spawn re-import of __main__ in parallel fit_sequential --- docs/docs/tutorials/ed-17.py | 170 +++++++++++-------- src/easydiffraction/analysis/analysis.py | 10 +- src/easydiffraction/analysis/sequential.py | 115 ++++++++----- tests/integration/fitting/test_sequential.py | 1 - 4 files changed, 184 insertions(+), 112 deletions(-) diff --git a/docs/docs/tutorials/ed-17.py b/docs/docs/tutorials/ed-17.py index 68be3ff0..f8cde1e7 100644 --- a/docs/docs/tutorials/ed-17.py +++ b/docs/docs/tutorials/ed-17.py @@ -3,14 +3,16 @@ # # This example demonstrates a Rietveld refinement of the Co2SiO4 crystal # structure using constant-wavelength neutron powder diffraction data -# from D20 at ILL. A sequential refinement of the same structure against -# a temperature scan is performed to show how to manage multiple -# experiments in a project. +# from D20 at ILL. A sequential refinement is performed against a +# temperature scan using `fit_sequential`, which processes each data +# file independently without loading all datasets into memory at once. # %% [markdown] # ## Import Library # %% +import pandas as pd + import easydiffraction as ed # %% [markdown] @@ -22,11 +24,11 @@ project = ed.Project() # %% [markdown] -# Set output verbosity level to "short" to show only one-line status -# messages during the analysis process. +# The project must be saved before running sequential fitting, so that +# results can be written to `analysis/results.csv`. # %% -project.verbosity = 'short' +project.save_as('data/cosio_project', temporary=False) # %% [markdown] # ## Step 2: Define Crystal Structure @@ -115,91 +117,88 @@ ) # %% [markdown] -# ## Step 3: Define Experiments +# ## Step 3: Define Template Experiment # -# This section shows how to add experiments, configure their parameters, -# and link the structures defined above. +# For sequential fitting, we create a single template experiment from +# the first data file. This template defines the instrument, peak +# profile, background, and linked phases that will be reused for every +# data file in the scan. # # #### Download Measured Data # %% -file_path = ed.download_data(id=27, destination='data') +zip_path = ed.download_data(id=27, destination='data') # %% [markdown] -# #### Create Experiments and Set Temperature +# #### Extract Data Files # %% -data_paths = ed.extract_data_paths_from_zip(file_path) -for i, data_path in enumerate(data_paths, start=1): - name = f'd20_{i}' - project.experiments.add_from_data_path( - name=name, - data_path=data_path, - ) - expt = project.experiments[name] - expt.diffrn.ambient_temperature = ed.extract_metadata( - file_path=data_path, - pattern=r'^TEMP\s+([0-9.]+)', - ) +data_dir = 'data/d20_scan' +data_paths = ed.extract_data_paths_from_zip(zip_path, destination=data_dir) + +# %% [markdown] +# #### Create Template Experiment from the First File + +# %% +project.experiments.add_from_data_path( + name='d20', + data_path=data_paths[0], +) +expt = project.experiments['d20'] # %% [markdown] # #### Set Instrument # %% -for expt in project.experiments: - expt.instrument.setup_wavelength = 1.87 - expt.instrument.calib_twotheta_offset = 0.29 +expt.instrument.setup_wavelength = 1.87 +expt.instrument.calib_twotheta_offset = 0.29 # %% [markdown] # #### Set Peak Profile # %% -for expt in project.experiments: - expt.peak.broad_gauss_u = 0.24 - expt.peak.broad_gauss_v = -0.53 - expt.peak.broad_gauss_w = 0.38 - expt.peak.broad_lorentz_y = 0.02 +expt.peak.broad_gauss_u = 0.24 +expt.peak.broad_gauss_v = -0.53 +expt.peak.broad_gauss_w = 0.38 +expt.peak.broad_lorentz_y = 0.02 # %% [markdown] # #### Set Excluded Regions # %% -for expt in project.experiments: - expt.excluded_regions.create(id='1', start=0, end=8) - expt.excluded_regions.create(id='2', start=150, end=180) +expt.excluded_regions.create(id='1', start=0, end=8) +expt.excluded_regions.create(id='2', start=150, end=180) # %% [markdown] # #### Set Background # %% -for expt in project.experiments: - expt.background.create(id='1', x=8, y=609) - expt.background.create(id='2', x=9, y=581) - expt.background.create(id='3', x=10, y=563) - expt.background.create(id='4', x=11, y=540) - expt.background.create(id='5', x=12, y=520) - expt.background.create(id='6', x=15, y=507) - expt.background.create(id='7', x=25, y=463) - expt.background.create(id='8', x=30, y=434) - expt.background.create(id='9', x=50, y=451) - expt.background.create(id='10', x=70, y=431) - expt.background.create(id='11', x=90, y=414) - expt.background.create(id='12', x=110, y=361) - expt.background.create(id='13', x=130, y=292) - expt.background.create(id='14', x=150, y=241) +expt.background.create(id='1', x=8, y=609) +expt.background.create(id='2', x=9, y=581) +expt.background.create(id='3', x=10, y=563) +expt.background.create(id='4', x=11, y=540) +expt.background.create(id='5', x=12, y=520) +expt.background.create(id='6', x=15, y=507) +expt.background.create(id='7', x=25, y=463) +expt.background.create(id='8', x=30, y=434) +expt.background.create(id='9', x=50, y=451) +expt.background.create(id='10', x=70, y=431) +expt.background.create(id='11', x=90, y=414) +expt.background.create(id='12', x=110, y=361) +expt.background.create(id='13', x=130, y=292) +expt.background.create(id='14', x=150, y=241) # %% [markdown] # #### Set Linked Phases # %% -for expt in project.experiments: - expt.linked_phases.create(id='cosio', scale=1.2) +expt.linked_phases.create(id='cosio', scale=1.2) # %% [markdown] # ## Step 4: Perform Analysis # # This section shows how to set free parameters, define constraints, -# and run the refinement. +# and run the sequential refinement. # %% [markdown] # #### Set Free Parameters @@ -229,18 +228,17 @@ structure.atom_sites['O3'].b_iso.free = True # %% -for expt in project.experiments: - expt.linked_phases['cosio'].scale.free = True +expt.linked_phases['cosio'].scale.free = True - expt.instrument.calib_twotheta_offset.free = True +expt.instrument.calib_twotheta_offset.free = True - expt.peak.broad_gauss_u.free = True - expt.peak.broad_gauss_v.free = True - expt.peak.broad_gauss_w.free = True - expt.peak.broad_lorentz_y.free = True +expt.peak.broad_gauss_u.free = True +expt.peak.broad_gauss_v.free = True +expt.peak.broad_gauss_w.free = True +expt.peak.broad_lorentz_y.free = True - for point in expt.background: - point.y.free = True +for point in expt.background: + point.y.free = True # %% [markdown] # #### Set Constraints @@ -265,25 +263,59 @@ expression='biso_Co2 = biso_Co1', ) +# %% [markdown] +# #### Run Single Fitting +# +# This is the fitting of the first dataset to optimize the initial +# parameters for the sequential fitting. This step is optional but can +# help with convergence and speed of the sequential fitting, especially +# if the initial parameters are far from optimal. + +# %% +project.analysis.fit() + +# %% [markdown] +# #### Run Sequential Fitting +# +# Define a callback that extracts the temperature from each data file. + + +# %% +def extract_diffrn(file_path): + temperature = ed.extract_metadata( + file_path=file_path, + pattern=r'^TEMP\s+([0-9.]+)', + ) + return {'ambient_temperature': temperature} + # %% [markdown] -# #### Set Fit Mode +# Set output verbosity level to "short" to show only one-line status +# messages during the analysis process. # %% -project.analysis.fit_mode.mode = 'single' +project.verbosity = 'short' # %% [markdown] -# #### Run Fitting +# Run the sequential fit over all data files in the scan directory. # %% -project.analysis.fit() +project.analysis.fit_sequential( + data_dir=data_dir, + extract_diffrn=extract_diffrn, + max_workers='auto', +) # %% [markdown] -# #### Plot Measured vs Calculated +# #### Replay a Dataset +# +# Apply fitted parameters from the last CSV row and plot the result. # %% -last_expt_name = project.experiments.names[-1] -project.plot_meas_vs_calc(expt_name=last_expt_name, show_residual=True) +csv_path = project.info.path / 'analysis' / 'results.csv' +n_rows = len(pd.read_csv(csv_path)) +project.apply_params_from_csv(row_index=n_rows - 1) +project.plot_meas_vs_calc(expt_name='d20', show_residual=True) # %% [markdown] # #### Plot Parameter Evolution @@ -291,7 +323,7 @@ # Define the quantity to use as the x-axis in the following plots. # %% -temperature = project.experiments[0].diffrn.ambient_temperature +temperature = expt.diffrn.ambient_temperature # %% [markdown] # Plot unit cell parameters vs. temperature. diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index 3f685b24..4218b480 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -688,12 +688,14 @@ def fit(self, verbosity: str | None = None) -> None: short_alignments = ['left', 'right', 'right', 'center'] short_rows: list[list[str]] = [] short_display_handle: object | None = None + if verb is not VerbosityEnum.SILENT: + console.paragraph('Standard fitting') if verb is VerbosityEnum.SHORT: first = expt_names[0] last = expt_names[-1] minimizer_name = self.fitter.selection - console.paragraph( - f"Using {num_expts} experiments 🔬 from '{first}' to " + console.print( + f"📋 Using {num_expts} experiments 🔬 from '{first}' to " f"'{last}' for '{mode.value}' fitting" ) console.print(f"🚀 Starting fit process with '{minimizer_name}'...") @@ -702,8 +704,8 @@ def fit(self, verbosity: str | None = None) -> None: for _idx, expt_name in enumerate(expt_names, start=1): if verb is VerbosityEnum.FULL: - console.paragraph( - f"Using experiment 🔬 '{expt_name}' for '{mode.value}' fitting" + console.print( + f"📋 Using experiment 🔬 '{expt_name}' for '{mode.value}' fitting" ) experiment = experiments[expt_name] diff --git a/src/easydiffraction/analysis/sequential.py b/src/easydiffraction/analysis/sequential.py index 412b5d10..9c3b45f6 100644 --- a/src/easydiffraction/analysis/sequential.py +++ b/src/easydiffraction/analysis/sequential.py @@ -9,6 +9,7 @@ import contextlib import csv import multiprocessing as mp +import sys from concurrent.futures import ProcessPoolExecutor from dataclasses import dataclass from dataclasses import replace @@ -18,6 +19,7 @@ from easydiffraction.io.ascii import extract_data_paths_from_dir from easydiffraction.utils.enums import VerbosityEnum +from easydiffraction.utils.logging import console from easydiffraction.utils.logging import log if TYPE_CHECKING: @@ -565,6 +567,13 @@ def fit_sequential( If preconditions are not met (e.g. multiple structures, missing project path, no free parameters). """ + # Guard against re-entry in spawned child processes. With the + # ``spawn`` multiprocessing context the child re-imports __main__, + # which re-executes the user script and would call fit_sequential + # again, causing infinite process spawning. + if mp.parent_process() is not None: + return + project = analysis.project verb = VerbosityEnum(verbosity if verbosity is not None else project.verbosity) @@ -642,14 +651,37 @@ def fit_sequential( total_chunks = len(chunks) if verb is not VerbosityEnum.SILENT: - print( - f'🚀 Sequential fitting: {len(remaining)} files in ' - f'{total_chunks} chunks (max_workers={max_workers})' + minimizer_name = analysis.fitter.selection + console.paragraph('Sequential fitting') + console.print(f"🚀 Starting fit process with '{minimizer_name}'...") + console.print( + f'📋 {len(remaining)} files in {total_chunks} chunks (max_workers={max_workers})' ) + console.print('📈 Goodness-of-fit (reduced χ²):') # Create a process pool for parallel dispatch, or a no-op context # for single-worker mode (avoids process-spawn overhead). + # + # When max_workers > 1 we use ``spawn`` context, which normally + # re-imports ``__main__`` in every child process. If the user runs + # a script without an ``if __name__ == '__main__':`` guard the + # whole script would re-execute in every worker, causing infinite + # process spawning. To prevent this we temporarily hide + # ``__main__.__file__`` and ``__main__.__spec__`` so that the spawn + # bootstrap has no path to re-import the script. ``_fit_worker`` + # lives in this module (not ``__main__``), so it is still resolved + # via normal pickle/import machinery. + _main_mod = sys.modules.get('__main__') + _main_file_bak = getattr(_main_mod, '__file__', None) + _main_spec_bak = getattr(_main_mod, '__spec__', None) + if max_workers > 1: + # Hide __main__ origin from spawn + if _main_mod is not None and _main_file_bak is not None: + _main_mod.__file__ = None # type: ignore[assignment] + if _main_mod is not None and _main_spec_bak is not None: + _main_mod.__spec__ = None + spawn_ctx = mp.get_context('spawn') pool_cm = ProcessPoolExecutor( max_workers=max_workers, @@ -659,41 +691,48 @@ def fit_sequential( else: pool_cm = contextlib.nullcontext() - with pool_cm as executor: - for chunk_idx, chunk in enumerate(chunks, start=1): - # Dispatch: parallel or sequential - if executor is not None: - templates = [template] * len(chunk) - results = list(executor.map(_fit_worker, templates, chunk)) - else: - results = [_fit_worker(template, path) for path in chunk] - - # Extract diffrn metadata in the main process - if extract_diffrn is not None: - for result in results: - try: - diffrn_values = extract_diffrn(result['file_path']) - for key, val in diffrn_values.items(): - result[f'diffrn.{key}'] = val - except Exception as exc: # noqa: BLE001 - log.warning(f'extract_diffrn failed for {result["file_path"]}: {exc}') - - # Write to CSV - _append_to_csv(csv_path, header, results) - - # Report progress - _report_chunk_progress(chunk_idx, total_chunks, results, verb) - - # Propagate: use last successful file's - # params as starting values - last_ok = None - for r in reversed(results): - if r.get('fit_success') and r.get('params'): - last_ok = r - break - - if last_ok is not None: - template = replace(template, initial_params=last_ok['params']) + try: + with pool_cm as executor: + for chunk_idx, chunk in enumerate(chunks, start=1): + # Dispatch: parallel or sequential + if executor is not None: + templates = [template] * len(chunk) + results = list(executor.map(_fit_worker, templates, chunk)) + else: + results = [_fit_worker(template, path) for path in chunk] + + # Extract diffrn metadata in the main process + if extract_diffrn is not None: + for result in results: + try: + diffrn_values = extract_diffrn(result['file_path']) + for key, val in diffrn_values.items(): + result[f'diffrn.{key}'] = val + except Exception as exc: # noqa: BLE001 + log.warning(f'extract_diffrn failed for {result["file_path"]}: {exc}') + + # Write to CSV + _append_to_csv(csv_path, header, results) + + # Report progress + _report_chunk_progress(chunk_idx, total_chunks, results, verb) + + # Propagate: use last successful file's + # params as starting values + last_ok = None + for r in reversed(results): + if r.get('fit_success') and r.get('params'): + last_ok = r + break + + if last_ok is not None: + template = replace(template, initial_params=last_ok['params']) + finally: + # Restore __main__ attributes + if _main_mod is not None and _main_file_bak is not None: + _main_mod.__file__ = _main_file_bak + if _main_mod is not None and _main_spec_bak is not None: + _main_mod.__spec__ = _main_spec_bak if verb is not VerbosityEnum.SILENT: total_fitted = len(already_fitted) + len(remaining) diff --git a/tests/integration/fitting/test_sequential.py b/tests/integration/fitting/test_sequential.py index 59945e87..12c14bea 100644 --- a/tests/integration/fitting/test_sequential.py +++ b/tests/integration/fitting/test_sequential.py @@ -370,4 +370,3 @@ def test_apply_params_from_csv_raises_on_bad_index(tmp_path) -> None: with pytest.raises(IndexError, match='out of range'): project.apply_params_from_csv(row_index=99) - From 8b209df50695d8afb38323ea2a1ff49ea45be7d4 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 22:08:43 +0200 Subject: [PATCH 19/24] Support negative indexing and force recalc in apply_params_from_csv --- docs/docs/tutorials/ed-17.py | 25 ++++++++++--------------- src/easydiffraction/project/project.py | 20 +++++++++++++++++--- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/docs/docs/tutorials/ed-17.py b/docs/docs/tutorials/ed-17.py index f8cde1e7..eb8bcd2a 100644 --- a/docs/docs/tutorials/ed-17.py +++ b/docs/docs/tutorials/ed-17.py @@ -11,8 +11,6 @@ # ## Import Library # %% -import pandas as pd - import easydiffraction as ed # %% [markdown] @@ -259,9 +257,7 @@ # Set constraints. # %% -project.analysis.constraints.create( - expression='biso_Co2 = biso_Co1', -) +project.analysis.constraints.create(expression='biso_Co2 = biso_Co1') # %% [markdown] # #### Run Single Fitting @@ -277,6 +273,14 @@ # %% [markdown] # #### Run Sequential Fitting # +# Set output verbosity level to "short" to show only one-line status +# messages during the analysis process. + +# %% +project.verbosity = 'short' + +# %% [markdown] +# # Define a callback that extracts the temperature from each data file. @@ -289,13 +293,6 @@ def extract_diffrn(file_path): return {'ambient_temperature': temperature} -# %% [markdown] -# Set output verbosity level to "short" to show only one-line status -# messages during the analysis process. - -# %% -project.verbosity = 'short' - # %% [markdown] # Run the sequential fit over all data files in the scan directory. @@ -312,9 +309,7 @@ def extract_diffrn(file_path): # Apply fitted parameters from the last CSV row and plot the result. # %% -csv_path = project.info.path / 'analysis' / 'results.csv' -n_rows = len(pd.read_csv(csv_path)) -project.apply_params_from_csv(row_index=n_rows - 1) +project.apply_params_from_csv(row_index=-1) project.plot_meas_vs_calc(expt_name='d20', show_residual=True) # %% [markdown] diff --git a/src/easydiffraction/project/project.py b/src/easydiffraction/project/project.py index 31cd84ca..5bc96e79 100644 --- a/src/easydiffraction/project/project.py +++ b/src/easydiffraction/project/project.py @@ -377,7 +377,8 @@ def apply_params_from_csv(self, row_index: int) -> None: Parameters ---------- row_index : int - 0-based row index in the CSV file. + Row index in the CSV file. Supports Python-style negative + indexing (e.g. ``-1`` for the last row). Raises ------ @@ -401,8 +402,14 @@ def apply_params_from_csv(self, row_index: int) -> None: raise FileNotFoundError(msg) df = pd.read_csv(csv_path) - if row_index < 0 or row_index >= len(df): - msg = f'Row index {row_index} out of range (CSV has {len(df)} rows).' + n_rows = len(df) + + # Support Python-style negative indexing + if row_index < 0: + row_index += n_rows + + if row_index < 0 or row_index >= n_rows: + msg = f'Row index {row_index} out of range (CSV has {n_rows} rows).' raise IndexError(msg) row = df.iloc[row_index] @@ -440,6 +447,13 @@ def apply_params_from_csv(self, row_index: int) -> None: if base_name in param_map and pd.notna(row[col_name]): param_map[base_name].uncertainty = float(row[col_name]) + # 4. Force recalculation: data was replaced directly (bypassing + # value setters), so the dirty flag may not be set. + for structure in self.structures: + structure._need_categories_update = True + for experiment in self.experiments.values(): + experiment._need_categories_update = True + log.info(f'Applied parameters from CSV row {row_index} (file: {file_path}).') # ------------------------------------------ From e88d18d1d81a3d92bab597088772d9b29a1ef6d8 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 23:04:34 +0200 Subject: [PATCH 20/24] Add extract_project_from_zip helper function --- docs/docs/tutorials/ed-18.py | 65 +++++++-------------- src/easydiffraction/__init__.py | 1 + src/easydiffraction/io/__init__.py | 1 + src/easydiffraction/io/ascii.py | 56 ++++++++++++++++++ src/easydiffraction/utils/utils.py | 2 +- tests/unit/easydiffraction/io/test_ascii.py | 56 +++++++++++++++++- 6 files changed, 134 insertions(+), 47 deletions(-) diff --git a/docs/docs/tutorials/ed-18.py b/docs/docs/tutorials/ed-18.py index b883e9d0..3d6cfca4 100644 --- a/docs/docs/tutorials/ed-18.py +++ b/docs/docs/tutorials/ed-18.py @@ -5,80 +5,55 @@ # how to load a previously saved project from a directory and run # refinement — all in just a few lines of code. # -# The project is first created and saved as a setup step (this would -# normally be done once and the directory would already exist on disk). -# Then the saved project is loaded back and fitted. -# # For details on how to define structures and experiments, see the other # tutorials. # %% [markdown] -# ## Import Library +# ## Import Modules # %% -import easydiffraction as ed +from easydiffraction import Project +from easydiffraction import download_data +from easydiffraction import extract_project_from_zip # %% [markdown] -# ## Setup: Create and Save a Project -# -# This step creates a project from CIF files and saves it to a -# directory. In practice, the project directory would already exist -# on disk from a previous session. +# ## Download Project Archive # %% -# Create a project from CIF files -project = ed.Project() -project.structures.add_from_cif_path(ed.download_data(id=1, destination='data')) -project.experiments.add_from_cif_path(ed.download_data(id=2, destination='data')) +zip_path = download_data(id=28, destination='data') -# %% -project.analysis.aliases.create( - label='biso_La', - param=project.structures['lbco'].atom_sites['La'].b_iso, -) -project.analysis.aliases.create( - label='biso_Ba', - param=project.structures['lbco'].atom_sites['Ba'].b_iso, -) - -project.analysis.aliases.create( - label='occ_La', - param=project.structures['lbco'].atom_sites['La'].occupancy, -) -project.analysis.aliases.create( - label='occ_Ba', - param=project.structures['lbco'].atom_sites['Ba'].occupancy, -) - -project.analysis.constraints.create(expression='biso_Ba = biso_La') -project.analysis.constraints.create(expression='occ_Ba = 1 - occ_La') - -project.structures['lbco'].atom_sites['La'].occupancy.free = True +# %% [markdown] +# ## Extract Project # %% -# Save to a directory -project.save_as('lbco_project') +project_dir = extract_project_from_zip('lbco_project.zip', destination='data') # %% [markdown] -# ## Step 1: Load Project from Directory +# ## Load Project # %% -project = ed.Project.load('lbco_project') +project = Project.load(project_dir) # %% [markdown] -# ## Step 2: Perform Analysis +# ## Perform Analysis # %% project.analysis.fit() +# %% [markdown] +# ## Show Results + # %% project.analysis.show_fit_results() +# %% [markdown] +# ## Plot Meas vs Calc + # %% project.plot_meas_vs_calc(expt_name='hrpt', show_residual=True) # %% [markdown] -# ## Step 3: Show Project Summary +# ## Save Project # %% -project.summary.show_report() +project.save() diff --git a/src/easydiffraction/__init__.py b/src/easydiffraction/__init__.py index 10308402..11ea117c 100644 --- a/src/easydiffraction/__init__.py +++ b/src/easydiffraction/__init__.py @@ -6,6 +6,7 @@ from easydiffraction.io.ascii import extract_data_paths_from_dir from easydiffraction.io.ascii import extract_data_paths_from_zip from easydiffraction.io.ascii import extract_metadata +from easydiffraction.io.ascii import extract_project_from_zip from easydiffraction.project.project import Project from easydiffraction.utils.logging import Logger from easydiffraction.utils.logging import console diff --git a/src/easydiffraction/io/__init__.py b/src/easydiffraction/io/__init__.py index 6ce45a95..4d0c1560 100644 --- a/src/easydiffraction/io/__init__.py +++ b/src/easydiffraction/io/__init__.py @@ -4,4 +4,5 @@ from easydiffraction.io.ascii import extract_data_paths_from_dir from easydiffraction.io.ascii import extract_data_paths_from_zip from easydiffraction.io.ascii import extract_metadata +from easydiffraction.io.ascii import extract_project_from_zip from easydiffraction.io.ascii import load_numeric_block diff --git a/src/easydiffraction/io/ascii.py b/src/easydiffraction/io/ascii.py index 75ec0fcb..45061787 100644 --- a/src/easydiffraction/io/ascii.py +++ b/src/easydiffraction/io/ascii.py @@ -13,6 +13,62 @@ import numpy as np +def extract_project_from_zip( + zip_path: str | Path, + destination: str | Path | None = None, +) -> str: + """ + Extract a project directory from a ZIP archive. + + The archive must contain exactly one directory with a + ``project.cif`` file. Files are extracted into *destination* when + provided, or into a temporary directory that persists for the + lifetime of the process. + + Parameters + ---------- + zip_path : str | Path + Path to the ZIP archive containing the project. + destination : str | Path | None, default=None + Directory to extract into. When ``None``, a temporary directory + is created. + + Returns + ------- + str + Absolute path to the extracted project directory (the directory + that contains ``project.cif``). + + Raises + ------ + FileNotFoundError + If *zip_path* does not exist. + ValueError + If the archive does not contain a ``project.cif`` file. + """ + zip_path = Path(zip_path) + if not zip_path.exists(): + msg = f'ZIP file not found: {zip_path}' + raise FileNotFoundError(msg) + + if destination is not None: + extract_dir = Path(destination) + extract_dir.mkdir(parents=True, exist_ok=True) + else: + extract_dir = Path(tempfile.mkdtemp(prefix='ed_zip_')) + + with zipfile.ZipFile(zip_path, 'r') as zf: + zf.extractall(extract_dir) + + # Find the project directory (the one containing project.cif) + project_cifs = list(extract_dir.rglob('project.cif')) + if not project_cifs: + msg = f'No project.cif found in ZIP archive: {zip_path}' + raise ValueError(msg) + + return str(project_cifs[0].parent.resolve()) + + def extract_data_paths_from_zip( zip_path: str | Path, destination: str | Path | None = None, diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index a54038fc..50af41fe 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -73,7 +73,7 @@ def _fetch_data_index() -> dict: _validate_url(index_url) # macOS: sha256sum index.json - index_hash = 'sha256:f421aab32ec532782dc62f4440a97320e5cec23b9e64f5ae3f8a3e818d013430' + index_hash = 'sha256:1032db0c04ef713c3f5209020a14b18dcdc3cfa4d995664ae5c9f5096f4508d4' destination_dirname = 'easydiffraction' destination_fname = 'data-index.json' cache_dir = pooch.os_cache(destination_dirname) diff --git a/tests/unit/easydiffraction/io/test_ascii.py b/tests/unit/easydiffraction/io/test_ascii.py index 8519c6d7..1410f8e9 100644 --- a/tests/unit/easydiffraction/io/test_ascii.py +++ b/tests/unit/easydiffraction/io/test_ascii.py @@ -1,6 +1,6 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -"""Tests for extract_data_paths_from_zip and extract_data_paths_from_dir.""" +"""Tests for extract_project_from_zip, extract_data_paths_from_zip and extract_data_paths_from_dir.""" from __future__ import annotations @@ -10,6 +10,60 @@ from easydiffraction.io.ascii import extract_data_paths_from_dir from easydiffraction.io.ascii import extract_data_paths_from_zip +from easydiffraction.io.ascii import extract_project_from_zip + + +class TestExtractProjectFromZip: + """Tests for extract_project_from_zip.""" + + def test_extracts_project_dir(self, tmp_path): + """Returns path to the directory containing project.cif.""" + zip_path = tmp_path / 'proj.zip' + with zipfile.ZipFile(zip_path, 'w') as zf: + zf.writestr('my_project/project.cif', 'data_project\n') + zf.writestr('my_project/structures/struct.cif', 'data_struct\n') + + result = extract_project_from_zip(zip_path, destination=tmp_path / 'out') + + assert result.endswith('my_project') + assert (tmp_path / 'out' / 'my_project' / 'project.cif').is_file() + + def test_extracts_to_temp_dir_by_default(self, tmp_path): + """Without destination, files go to a temp directory.""" + zip_path = tmp_path / 'proj.zip' + with zipfile.ZipFile(zip_path, 'w') as zf: + zf.writestr('myproj/project.cif', 'data_project\n') + + result = extract_project_from_zip(zip_path) + + assert 'myproj' in result + assert 'project.cif' not in result # returns parent dir, not file + + def test_raises_file_not_found(self, tmp_path): + """Raises FileNotFoundError for missing ZIP path.""" + with pytest.raises(FileNotFoundError): + extract_project_from_zip(tmp_path / 'missing.zip') + + def test_raises_value_error_no_project_cif(self, tmp_path): + """Raises ValueError when ZIP has no project.cif.""" + zip_path = tmp_path / 'bad.zip' + with zipfile.ZipFile(zip_path, 'w') as zf: + zf.writestr('data.dat', '1 2 3\n') + + with pytest.raises(ValueError, match='No project.cif found'): + extract_project_from_zip(zip_path) + + def test_destination_creates_directory(self, tmp_path): + """Destination directory is created if it does not exist.""" + zip_path = tmp_path / 'proj.zip' + dest = tmp_path / 'nested' / 'output' + with zipfile.ZipFile(zip_path, 'w') as zf: + zf.writestr('proj/project.cif', 'data\n') + + result = extract_project_from_zip(zip_path, destination=dest) + + assert dest.is_dir() + assert 'proj' in result class TestExtractDataPathsFromZip: From dcaef627bd1dbd3298941b221850926ffef7dde3 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 23:23:10 +0200 Subject: [PATCH 21/24] Refactor extract_project_from_zip call to use zip_path variable --- docs/docs/tutorials/ed-18.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tutorials/ed-18.py b/docs/docs/tutorials/ed-18.py index 3d6cfca4..f4485dbc 100644 --- a/docs/docs/tutorials/ed-18.py +++ b/docs/docs/tutorials/ed-18.py @@ -26,7 +26,7 @@ # ## Extract Project # %% -project_dir = extract_project_from_zip('lbco_project.zip', destination='data') +project_dir = extract_project_from_zip(zip_path, destination='data') # %% [markdown] # ## Load Project From ed22516720f3843d0633933c61c405cef7106da5 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Fri, 3 Apr 2026 23:23:33 +0200 Subject: [PATCH 22/24] Refactor notebook cell IDs and update project loading process --- docs/docs/tutorials/ed-1.ipynb | 53 ++-- docs/docs/tutorials/ed-10.ipynb | 55 ++-- docs/docs/tutorials/ed-11.ipynb | 61 ++--- docs/docs/tutorials/ed-12.ipynb | 69 ++---- docs/docs/tutorials/ed-13.ipynb | 427 +++++++++++++++----------------- docs/docs/tutorials/ed-14.ipynb | 77 +++--- docs/docs/tutorials/ed-15.ipynb | 73 +++--- docs/docs/tutorials/ed-16.ipynb | 133 +++++----- docs/docs/tutorials/ed-17.ipynb | 329 +++++++++++++----------- docs/docs/tutorials/ed-18.ipynb | 122 ++++----- docs/docs/tutorials/ed-2.ipynb | 67 ++--- docs/docs/tutorials/ed-3.ipynb | 365 +++++++++++++-------------- docs/docs/tutorials/ed-4.ipynb | 139 +++++------ docs/docs/tutorials/ed-5.ipynb | 119 ++++----- docs/docs/tutorials/ed-6.ipynb | 175 ++++++------- docs/docs/tutorials/ed-7.ipynb | 163 ++++++------ docs/docs/tutorials/ed-8.ipynb | 133 +++++----- docs/docs/tutorials/ed-9.ipynb | 143 +++++------ 18 files changed, 1239 insertions(+), 1464 deletions(-) diff --git a/docs/docs/tutorials/ed-1.ipynb b/docs/docs/tutorials/ed-1.ipynb index 830d7889..3b8085bb 100644 --- a/docs/docs/tutorials/ed-1.ipynb +++ b/docs/docs/tutorials/ed-1.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "74c72059", + "id": "e1c6f514", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Structure Refinement: LBCO, HRPT\n", @@ -62,7 +47,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -71,7 +56,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -80,7 +65,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Step 1: Define Project" @@ -89,7 +74,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -99,7 +84,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "## Step 2: Define Crystal Structure" @@ -108,7 +93,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -119,7 +104,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -128,7 +113,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "8", "metadata": {}, "source": [ "## Step 3: Define Experiment" @@ -137,7 +122,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -148,7 +133,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -157,7 +142,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "## Step 4: Perform Analysis" @@ -166,7 +151,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -178,7 +163,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -189,7 +174,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -199,7 +184,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -208,7 +193,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "16", "metadata": {}, "source": [ "## Step 5: Show Project Summary" @@ -217,7 +202,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "17", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-10.ipynb b/docs/docs/tutorials/ed-10.ipynb index 2fa9c6c5..5707c038 100644 --- a/docs/docs/tutorials/ed-10.ipynb +++ b/docs/docs/tutorials/ed-10.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "239bda80", + "id": "2e0ed9d7", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Pair Distribution Function: Ni, NPD\n", @@ -51,7 +36,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -60,7 +45,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -69,7 +54,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Create Project" @@ -78,7 +63,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -87,7 +72,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "## Add Structure" @@ -96,7 +81,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -106,7 +91,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -126,7 +111,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "8", "metadata": {}, "source": [ "## Add Experiment" @@ -135,7 +120,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -145,7 +130,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -162,7 +147,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -177,7 +162,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "12", "metadata": {}, "source": [ "## Select Fitting Parameters" @@ -186,7 +171,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -197,7 +182,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -208,7 +193,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "15", "metadata": {}, "source": [ "## Run Fitting" @@ -217,7 +202,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -227,7 +212,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ "## Plot Measured vs Calculated" @@ -236,7 +221,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-11.ipynb b/docs/docs/tutorials/ed-11.ipynb index cbb509bf..30fc3e26 100644 --- a/docs/docs/tutorials/ed-11.ipynb +++ b/docs/docs/tutorials/ed-11.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "958d9ba3", + "id": "e0a12c6e", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Pair Distribution Function: Si, NPD\n", @@ -48,7 +33,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -57,7 +42,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -66,7 +51,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Create Project" @@ -75,7 +60,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -84,7 +69,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "## Set Plotting Engine" @@ -93,7 +78,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -105,7 +90,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -115,7 +100,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "8", "metadata": {}, "source": [ "## Add Structure" @@ -124,7 +109,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -134,7 +119,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -155,7 +140,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "## Add Experiment" @@ -164,7 +149,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -174,7 +159,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -191,7 +176,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -207,7 +192,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "15", "metadata": {}, "source": [ "## Select Fitting Parameters" @@ -216,7 +201,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -228,7 +213,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -240,7 +225,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "18", "metadata": {}, "source": [ "## Run Fitting" @@ -249,7 +234,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -259,7 +244,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "20", "metadata": {}, "source": [ "## Plot Measured vs Calculated" @@ -268,7 +253,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "21", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-12.ipynb b/docs/docs/tutorials/ed-12.ipynb index deaca165..6aa16c6b 100644 --- a/docs/docs/tutorials/ed-12.ipynb +++ b/docs/docs/tutorials/ed-12.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a6c12e68", + "id": "edee23bc", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Pair Distribution Function: NaCl, XRD\n", @@ -51,7 +36,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -60,7 +45,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -69,7 +54,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Create Project" @@ -78,7 +63,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -87,7 +72,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "## Set Plotting Engine" @@ -96,7 +81,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -108,7 +93,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -119,7 +104,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "8", "metadata": {}, "source": [ "## Add Structure" @@ -128,7 +113,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -138,7 +123,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -167,7 +152,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "## Add Experiment" @@ -176,7 +161,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -186,7 +171,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -203,7 +188,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -213,7 +198,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -223,7 +208,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -233,7 +218,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -248,7 +233,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -257,7 +242,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "19", "metadata": {}, "source": [ "## Select Fitting Parameters" @@ -266,7 +251,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -278,7 +263,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -289,7 +274,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "22", "metadata": {}, "source": [ "## Run Fitting" @@ -298,7 +283,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -308,7 +293,7 @@ }, { "cell_type": "markdown", - "id": "25", + "id": "24", "metadata": {}, "source": [ "## Plot Measured vs Calculated" @@ -317,7 +302,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "25", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-13.ipynb b/docs/docs/tutorials/ed-13.ipynb index ef68958b..66854a4c 100644 --- a/docs/docs/tutorials/ed-13.ipynb +++ b/docs/docs/tutorials/ed-13.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "1c60b738", + "id": "1a143d79", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Fitting Powder Diffraction data\n", @@ -70,7 +55,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "📖 See\n", @@ -82,7 +67,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -91,7 +76,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## 📘 Introduction: Simple Reference Fit – Si\n", @@ -118,7 +103,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "4", "metadata": {}, "source": [ "📖 See\n", @@ -130,7 +115,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -139,7 +124,7 @@ }, { "cell_type": "markdown", - "id": "7", + "id": "6", "metadata": {}, "source": [ "You can set the title and description of the project to provide\n", @@ -151,7 +136,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -161,7 +146,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "8", "metadata": {}, "source": [ "### 🔬 Create an Experiment\n", @@ -174,7 +159,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ "📖 See\n", @@ -186,7 +171,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -197,7 +182,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "Uncomment the following cell if your data reduction failed and the\n", @@ -211,7 +196,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -220,7 +205,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "13", "metadata": {}, "source": [ "Now we can create the experiment and load the measured data. In this\n", @@ -232,7 +217,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "14", "metadata": {}, "source": [ "📖 See\n", @@ -243,7 +228,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -258,7 +243,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "16", "metadata": {}, "source": [ "#### Inspect Measured Data\n", @@ -274,7 +259,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ "📖 See\n", @@ -288,7 +273,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "18", "metadata": {}, "source": [ "📖 See\n", @@ -299,7 +284,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -311,7 +296,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -320,7 +305,7 @@ }, { "cell_type": "markdown", - "id": "22", + "id": "21", "metadata": {}, "source": [ "If you zoom in on the highest TOF peak (around 120,000 μs), you will\n", @@ -343,7 +328,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "22", "metadata": {}, "source": [ "📖 See\n", @@ -354,7 +339,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -364,7 +349,7 @@ }, { "cell_type": "markdown", - "id": "25", + "id": "24", "metadata": {}, "source": [ "To visualize the effect of excluding the high TOF region, we can plot\n", @@ -375,7 +360,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -384,7 +369,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "26", "metadata": {}, "source": [ "#### Set Instrument Parameters\n", @@ -407,7 +392,7 @@ }, { "cell_type": "markdown", - "id": "28", + "id": "27", "metadata": {}, "source": [ "📖 See\n", @@ -418,7 +403,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -432,7 +417,7 @@ }, { "cell_type": "markdown", - "id": "30", + "id": "29", "metadata": {}, "source": [ "Before proceeding, let's take a quick look at the concept of\n", @@ -452,7 +437,7 @@ { "cell_type": "code", "execution_count": null, - "id": "31", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -461,7 +446,7 @@ }, { "cell_type": "markdown", - "id": "32", + "id": "31", "metadata": {}, "source": [ "The `value` attribute represents the current value of the parameter as\n", @@ -475,7 +460,7 @@ { "cell_type": "code", "execution_count": null, - "id": "33", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -484,7 +469,7 @@ }, { "cell_type": "markdown", - "id": "34", + "id": "33", "metadata": {}, "source": [ "Note that to set the value of the parameter, you can simply assign a\n", @@ -494,7 +479,7 @@ }, { "cell_type": "markdown", - "id": "35", + "id": "34", "metadata": {}, "source": [ "📖 See\n", @@ -505,7 +490,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "35", "metadata": {}, "source": [ "#### Set Peak Profile Parameters\n", @@ -547,7 +532,7 @@ }, { "cell_type": "markdown", - "id": "37", + "id": "36", "metadata": {}, "source": [ "📖 See\n", @@ -558,7 +543,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "37", "metadata": {}, "outputs": [], "source": [ @@ -574,7 +559,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "38", "metadata": {}, "source": [ "#### Set Background\n", @@ -609,7 +594,7 @@ }, { "cell_type": "markdown", - "id": "40", + "id": "39", "metadata": {}, "source": [ "📖 See\n", @@ -620,7 +605,7 @@ { "cell_type": "code", "execution_count": null, - "id": "41", + "id": "40", "metadata": {}, "outputs": [], "source": [ @@ -636,7 +621,7 @@ }, { "cell_type": "markdown", - "id": "42", + "id": "41", "metadata": {}, "source": [ "### 🧩 Create a Structure – Si\n", @@ -679,7 +664,7 @@ }, { "cell_type": "markdown", - "id": "43", + "id": "42", "metadata": {}, "source": [ "📖 See\n", @@ -689,7 +674,7 @@ }, { "cell_type": "markdown", - "id": "44", + "id": "43", "metadata": {}, "source": [ "```\n", @@ -721,7 +706,7 @@ }, { "cell_type": "markdown", - "id": "45", + "id": "44", "metadata": {}, "source": [ "As with adding the experiment in the previous step, we will create a\n", @@ -731,7 +716,7 @@ }, { "cell_type": "markdown", - "id": "46", + "id": "45", "metadata": {}, "source": [ "📖 See\n", @@ -742,7 +727,7 @@ }, { "cell_type": "markdown", - "id": "47", + "id": "46", "metadata": {}, "source": [ "#### Add Structure" @@ -751,7 +736,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "47", "metadata": {}, "outputs": [], "source": [ @@ -760,7 +745,7 @@ }, { "cell_type": "markdown", - "id": "49", + "id": "48", "metadata": {}, "source": [ "#### Set Space Group" @@ -768,7 +753,7 @@ }, { "cell_type": "markdown", - "id": "50", + "id": "49", "metadata": {}, "source": [ "📖 See\n", @@ -779,7 +764,7 @@ { "cell_type": "code", "execution_count": null, - "id": "51", + "id": "50", "metadata": {}, "outputs": [], "source": [ @@ -789,7 +774,7 @@ }, { "cell_type": "markdown", - "id": "52", + "id": "51", "metadata": {}, "source": [ "#### Set Lattice Parameters" @@ -797,7 +782,7 @@ }, { "cell_type": "markdown", - "id": "53", + "id": "52", "metadata": {}, "source": [ "📖 See\n", @@ -808,7 +793,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "53", "metadata": {}, "outputs": [], "source": [ @@ -817,7 +802,7 @@ }, { "cell_type": "markdown", - "id": "55", + "id": "54", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -825,7 +810,7 @@ }, { "cell_type": "markdown", - "id": "56", + "id": "55", "metadata": {}, "source": [ "📖 See\n", @@ -836,7 +821,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57", + "id": "56", "metadata": {}, "outputs": [], "source": [ @@ -853,7 +838,7 @@ }, { "cell_type": "markdown", - "id": "58", + "id": "57", "metadata": {}, "source": [ "### 🔗 Assign Structure to Experiment\n", @@ -866,7 +851,7 @@ }, { "cell_type": "markdown", - "id": "59", + "id": "58", "metadata": {}, "source": [ "📖 See\n", @@ -877,7 +862,7 @@ { "cell_type": "code", "execution_count": null, - "id": "60", + "id": "59", "metadata": {}, "outputs": [], "source": [ @@ -886,7 +871,7 @@ }, { "cell_type": "markdown", - "id": "61", + "id": "60", "metadata": {}, "source": [ "### 🚀 Analyze and Fit the Data\n", @@ -908,7 +893,7 @@ }, { "cell_type": "markdown", - "id": "62", + "id": "61", "metadata": { "title": "**Reminder:**" }, @@ -925,7 +910,7 @@ }, { "cell_type": "markdown", - "id": "63", + "id": "62", "metadata": {}, "source": [ "📖 See\n", @@ -935,7 +920,7 @@ }, { "cell_type": "markdown", - "id": "64", + "id": "63", "metadata": {}, "source": [ "#### Set Fit Parameters\n", @@ -958,7 +943,7 @@ { "cell_type": "code", "execution_count": null, - "id": "65", + "id": "64", "metadata": {}, "outputs": [], "source": [ @@ -978,7 +963,7 @@ }, { "cell_type": "markdown", - "id": "66", + "id": "65", "metadata": {}, "source": [ "#### Show Free Parameters\n", @@ -989,7 +974,7 @@ }, { "cell_type": "markdown", - "id": "67", + "id": "66", "metadata": {}, "source": [ "📖 See\n", @@ -1003,7 +988,7 @@ { "cell_type": "code", "execution_count": null, - "id": "68", + "id": "67", "metadata": {}, "outputs": [], "source": [ @@ -1012,7 +997,7 @@ }, { "cell_type": "markdown", - "id": "69", + "id": "68", "metadata": {}, "source": [ "#### Visualize Diffraction Patterns\n", @@ -1028,7 +1013,7 @@ { "cell_type": "code", "execution_count": null, - "id": "70", + "id": "69", "metadata": {}, "outputs": [], "source": [ @@ -1037,7 +1022,7 @@ }, { "cell_type": "markdown", - "id": "71", + "id": "70", "metadata": {}, "source": [ "#### Run Fitting\n", @@ -1048,7 +1033,7 @@ }, { "cell_type": "markdown", - "id": "72", + "id": "71", "metadata": {}, "source": [ "📖 See\n", @@ -1059,7 +1044,7 @@ { "cell_type": "code", "execution_count": null, - "id": "73", + "id": "72", "metadata": {}, "outputs": [], "source": [ @@ -1069,7 +1054,7 @@ }, { "cell_type": "markdown", - "id": "74", + "id": "73", "metadata": {}, "source": [ "#### Check Fit Results\n", @@ -1088,7 +1073,7 @@ }, { "cell_type": "markdown", - "id": "75", + "id": "74", "metadata": {}, "source": [ "#### Visualize Fit Results\n", @@ -1102,7 +1087,7 @@ { "cell_type": "code", "execution_count": null, - "id": "76", + "id": "75", "metadata": {}, "outputs": [], "source": [ @@ -1111,7 +1096,7 @@ }, { "cell_type": "markdown", - "id": "77", + "id": "76", "metadata": {}, "source": [ "#### TOF vs d-spacing\n", @@ -1145,7 +1130,7 @@ { "cell_type": "code", "execution_count": null, - "id": "78", + "id": "77", "metadata": {}, "outputs": [], "source": [ @@ -1154,7 +1139,7 @@ }, { "cell_type": "markdown", - "id": "79", + "id": "78", "metadata": {}, "source": [ "As you can see, the calculated diffraction pattern now matches the\n", @@ -1180,7 +1165,7 @@ { "cell_type": "code", "execution_count": null, - "id": "80", + "id": "79", "metadata": {}, "outputs": [], "source": [ @@ -1189,7 +1174,7 @@ }, { "cell_type": "markdown", - "id": "81", + "id": "80", "metadata": {}, "source": [ "## 💪 Exercise: Complex Fit – LBCO\n", @@ -1210,7 +1195,7 @@ }, { "cell_type": "markdown", - "id": "82", + "id": "81", "metadata": {}, "source": [ "**Hint:**" @@ -1218,7 +1203,7 @@ }, { "cell_type": "markdown", - "id": "83", + "id": "82", "metadata": {}, "source": [ "You can use the same approach as in the previous part of the notebook,\n", @@ -1227,7 +1212,7 @@ }, { "cell_type": "markdown", - "id": "84", + "id": "83", "metadata": {}, "source": [ "**Solution:**" @@ -1236,7 +1221,7 @@ { "cell_type": "code", "execution_count": null, - "id": "85", + "id": "84", "metadata": {}, "outputs": [], "source": [ @@ -1247,7 +1232,7 @@ }, { "cell_type": "markdown", - "id": "86", + "id": "85", "metadata": {}, "source": [ "### 🔬 Exercise 2: Define an Experiment\n", @@ -1260,7 +1245,7 @@ }, { "cell_type": "markdown", - "id": "87", + "id": "86", "metadata": {}, "source": [ "**Hint:**" @@ -1268,7 +1253,7 @@ }, { "cell_type": "markdown", - "id": "88", + "id": "87", "metadata": {}, "source": [ "You can use the same approach as in the previous part of the notebook,\n", @@ -1277,7 +1262,7 @@ }, { "cell_type": "markdown", - "id": "89", + "id": "88", "metadata": {}, "source": [ "**Solution:**" @@ -1286,7 +1271,7 @@ { "cell_type": "code", "execution_count": null, - "id": "90", + "id": "89", "metadata": {}, "outputs": [], "source": [ @@ -1309,7 +1294,7 @@ }, { "cell_type": "markdown", - "id": "91", + "id": "90", "metadata": {}, "source": [ "#### Exercise 2.1: Inspect Measured Data\n", @@ -1321,7 +1306,7 @@ }, { "cell_type": "markdown", - "id": "92", + "id": "91", "metadata": {}, "source": [ "**Hint:**" @@ -1329,7 +1314,7 @@ }, { "cell_type": "markdown", - "id": "93", + "id": "92", "metadata": {}, "source": [ "You can use the `plot_meas` method of the project to visualize the\n", @@ -1340,7 +1325,7 @@ }, { "cell_type": "markdown", - "id": "94", + "id": "93", "metadata": {}, "source": [ "**Solution:**" @@ -1349,7 +1334,7 @@ { "cell_type": "code", "execution_count": null, - "id": "95", + "id": "94", "metadata": {}, "outputs": [], "source": [ @@ -1363,7 +1348,7 @@ }, { "cell_type": "markdown", - "id": "96", + "id": "95", "metadata": {}, "source": [ "#### Exercise 2.2: Set Instrument Parameters\n", @@ -1373,7 +1358,7 @@ }, { "cell_type": "markdown", - "id": "97", + "id": "96", "metadata": {}, "source": [ "**Hint:**" @@ -1381,7 +1366,7 @@ }, { "cell_type": "markdown", - "id": "98", + "id": "97", "metadata": {}, "source": [ "Use the values from the data reduction process for the LBCO and\n", @@ -1390,7 +1375,7 @@ }, { "cell_type": "markdown", - "id": "99", + "id": "98", "metadata": {}, "source": [ "**Solution:**" @@ -1399,7 +1384,7 @@ { "cell_type": "code", "execution_count": null, - "id": "100", + "id": "99", "metadata": {}, "outputs": [], "source": [ @@ -1413,7 +1398,7 @@ }, { "cell_type": "markdown", - "id": "101", + "id": "100", "metadata": {}, "source": [ "#### Exercise 2.3: Set Peak Profile Parameters\n", @@ -1423,7 +1408,7 @@ }, { "cell_type": "markdown", - "id": "102", + "id": "101", "metadata": {}, "source": [ "**Hint:**" @@ -1431,7 +1416,7 @@ }, { "cell_type": "markdown", - "id": "103", + "id": "102", "metadata": {}, "source": [ "Use the values from the\n", @@ -1443,7 +1428,7 @@ }, { "cell_type": "markdown", - "id": "104", + "id": "103", "metadata": {}, "source": [ "**Solution:**" @@ -1452,7 +1437,7 @@ { "cell_type": "code", "execution_count": null, - "id": "105", + "id": "104", "metadata": {}, "outputs": [], "source": [ @@ -1470,7 +1455,7 @@ }, { "cell_type": "markdown", - "id": "106", + "id": "105", "metadata": {}, "source": [ "#### Exercise 2.4: Set Background\n", @@ -1481,7 +1466,7 @@ }, { "cell_type": "markdown", - "id": "107", + "id": "106", "metadata": {}, "source": [ "**Hint:**" @@ -1489,7 +1474,7 @@ }, { "cell_type": "markdown", - "id": "108", + "id": "107", "metadata": {}, "source": [ "Use the same approach as in the previous part of the notebook, but\n", @@ -1500,7 +1485,7 @@ }, { "cell_type": "markdown", - "id": "109", + "id": "108", "metadata": {}, "source": [ "**Solution:**" @@ -1509,7 +1494,7 @@ { "cell_type": "code", "execution_count": null, - "id": "110", + "id": "109", "metadata": {}, "outputs": [], "source": [ @@ -1525,7 +1510,7 @@ }, { "cell_type": "markdown", - "id": "111", + "id": "110", "metadata": {}, "source": [ "### 🧩 Exercise 3: Define a Structure – LBCO\n", @@ -1541,7 +1526,7 @@ }, { "cell_type": "markdown", - "id": "112", + "id": "111", "metadata": {}, "source": [ "```\n", @@ -1576,7 +1561,7 @@ }, { "cell_type": "markdown", - "id": "113", + "id": "112", "metadata": {}, "source": [ "Note that the `occupancy` of the La and Ba atoms is 0.5\n", @@ -1606,7 +1591,7 @@ }, { "cell_type": "markdown", - "id": "114", + "id": "113", "metadata": {}, "source": [ "#### Exercise 3.1: Create Structure\n", @@ -1617,7 +1602,7 @@ }, { "cell_type": "markdown", - "id": "115", + "id": "114", "metadata": {}, "source": [ "**Hint:**" @@ -1625,7 +1610,7 @@ }, { "cell_type": "markdown", - "id": "116", + "id": "115", "metadata": {}, "source": [ "You can use the same approach as in the previous part of the notebook,\n", @@ -1635,7 +1620,7 @@ }, { "cell_type": "markdown", - "id": "117", + "id": "116", "metadata": {}, "source": [ "**Solution:**" @@ -1644,7 +1629,7 @@ { "cell_type": "code", "execution_count": null, - "id": "118", + "id": "117", "metadata": {}, "outputs": [], "source": [ @@ -1653,7 +1638,7 @@ }, { "cell_type": "markdown", - "id": "119", + "id": "118", "metadata": {}, "source": [ "#### Exercise 3.2: Set Space Group\n", @@ -1663,7 +1648,7 @@ }, { "cell_type": "markdown", - "id": "120", + "id": "119", "metadata": {}, "source": [ "**Hint:**" @@ -1671,7 +1656,7 @@ }, { "cell_type": "markdown", - "id": "121", + "id": "120", "metadata": {}, "source": [ "Use the space group name and IT coordinate system code from the CIF\n", @@ -1680,7 +1665,7 @@ }, { "cell_type": "markdown", - "id": "122", + "id": "121", "metadata": {}, "source": [ "**Solution:**" @@ -1689,7 +1674,7 @@ { "cell_type": "code", "execution_count": null, - "id": "123", + "id": "122", "metadata": {}, "outputs": [], "source": [ @@ -1699,7 +1684,7 @@ }, { "cell_type": "markdown", - "id": "124", + "id": "123", "metadata": {}, "source": [ "#### Exercise 3.3: Set Lattice Parameters\n", @@ -1709,7 +1694,7 @@ }, { "cell_type": "markdown", - "id": "125", + "id": "124", "metadata": {}, "source": [ "**Hint:**" @@ -1717,7 +1702,7 @@ }, { "cell_type": "markdown", - "id": "126", + "id": "125", "metadata": {}, "source": [ "Use the lattice parameters from the CIF data." @@ -1725,7 +1710,7 @@ }, { "cell_type": "markdown", - "id": "127", + "id": "126", "metadata": {}, "source": [ "**Solution:**" @@ -1734,7 +1719,7 @@ { "cell_type": "code", "execution_count": null, - "id": "128", + "id": "127", "metadata": {}, "outputs": [], "source": [ @@ -1743,7 +1728,7 @@ }, { "cell_type": "markdown", - "id": "129", + "id": "128", "metadata": {}, "source": [ "#### Exercise 3.4: Set Atom Sites\n", @@ -1753,7 +1738,7 @@ }, { "cell_type": "markdown", - "id": "130", + "id": "129", "metadata": {}, "source": [ "**Hint:**" @@ -1761,7 +1746,7 @@ }, { "cell_type": "markdown", - "id": "131", + "id": "130", "metadata": {}, "source": [ "Use the atom sites from the CIF data. You can use the `add` method of\n", @@ -1770,7 +1755,7 @@ }, { "cell_type": "markdown", - "id": "132", + "id": "131", "metadata": {}, "source": [ "**Solution:**" @@ -1779,7 +1764,7 @@ { "cell_type": "code", "execution_count": null, - "id": "133", + "id": "132", "metadata": {}, "outputs": [], "source": [ @@ -1825,7 +1810,7 @@ }, { "cell_type": "markdown", - "id": "134", + "id": "133", "metadata": {}, "source": [ "### 🔗 Exercise 4: Assign Structure to Experiment\n", @@ -1835,7 +1820,7 @@ }, { "cell_type": "markdown", - "id": "135", + "id": "134", "metadata": {}, "source": [ "**Hint:**" @@ -1843,7 +1828,7 @@ }, { "cell_type": "markdown", - "id": "136", + "id": "135", "metadata": {}, "source": [ "Use the `linked_phases` attribute of the experiment to link the\n", @@ -1852,7 +1837,7 @@ }, { "cell_type": "markdown", - "id": "137", + "id": "136", "metadata": {}, "source": [ "**Solution:**" @@ -1861,7 +1846,7 @@ { "cell_type": "code", "execution_count": null, - "id": "138", + "id": "137", "metadata": {}, "outputs": [], "source": [ @@ -1870,7 +1855,7 @@ }, { "cell_type": "markdown", - "id": "139", + "id": "138", "metadata": {}, "source": [ "### 🚀 Exercise 5: Analyze and Fit the Data\n", @@ -1883,7 +1868,7 @@ }, { "cell_type": "markdown", - "id": "140", + "id": "139", "metadata": {}, "source": [ "**Hint:**" @@ -1891,7 +1876,7 @@ }, { "cell_type": "markdown", - "id": "141", + "id": "140", "metadata": {}, "source": [ "You can start with the scale factor and the background points, as in\n", @@ -1900,7 +1885,7 @@ }, { "cell_type": "markdown", - "id": "142", + "id": "141", "metadata": {}, "source": [ "**Solution:**" @@ -1909,7 +1894,7 @@ { "cell_type": "code", "execution_count": null, - "id": "143", + "id": "142", "metadata": {}, "outputs": [], "source": [ @@ -1921,7 +1906,7 @@ }, { "cell_type": "markdown", - "id": "144", + "id": "143", "metadata": {}, "source": [ "#### Exercise 5.2: Run Fitting\n", @@ -1932,7 +1917,7 @@ }, { "cell_type": "markdown", - "id": "145", + "id": "144", "metadata": {}, "source": [ "**Hint:**" @@ -1940,7 +1925,7 @@ }, { "cell_type": "markdown", - "id": "146", + "id": "145", "metadata": {}, "source": [ "Use the `plot_meas_vs_calc` method of the project to visualize the\n", @@ -1951,7 +1936,7 @@ }, { "cell_type": "markdown", - "id": "147", + "id": "146", "metadata": {}, "source": [ "**Solution:**" @@ -1960,7 +1945,7 @@ { "cell_type": "code", "execution_count": null, - "id": "148", + "id": "147", "metadata": {}, "outputs": [], "source": [ @@ -1972,7 +1957,7 @@ }, { "cell_type": "markdown", - "id": "149", + "id": "148", "metadata": {}, "source": [ "#### Exercise 5.3: Find the Misfit in the Fit\n", @@ -1987,7 +1972,7 @@ }, { "cell_type": "markdown", - "id": "150", + "id": "149", "metadata": {}, "source": [ "**Hint:**" @@ -1995,7 +1980,7 @@ }, { "cell_type": "markdown", - "id": "151", + "id": "150", "metadata": {}, "source": [ "Consider the following options:\n", @@ -2007,7 +1992,7 @@ }, { "cell_type": "markdown", - "id": "152", + "id": "151", "metadata": {}, "source": [ "**Solution:**" @@ -2015,7 +2000,7 @@ }, { "cell_type": "markdown", - "id": "153", + "id": "152", "metadata": {}, "source": [ "\n", @@ -2037,7 +2022,7 @@ { "cell_type": "code", "execution_count": null, - "id": "154", + "id": "153", "metadata": {}, "outputs": [], "source": [ @@ -2046,7 +2031,7 @@ }, { "cell_type": "markdown", - "id": "155", + "id": "154", "metadata": {}, "source": [ "#### Exercise 5.4: Refine the LBCO Lattice Parameter\n", @@ -2056,7 +2041,7 @@ }, { "cell_type": "markdown", - "id": "156", + "id": "155", "metadata": {}, "source": [ "**Hint:**" @@ -2064,7 +2049,7 @@ }, { "cell_type": "markdown", - "id": "157", + "id": "156", "metadata": {}, "source": [ "To achieve this, we will set the `free` attribute of the `length_a`\n", @@ -2079,7 +2064,7 @@ }, { "cell_type": "markdown", - "id": "158", + "id": "157", "metadata": {}, "source": [ "**Solution:**" @@ -2088,7 +2073,7 @@ { "cell_type": "code", "execution_count": null, - "id": "159", + "id": "158", "metadata": {}, "outputs": [], "source": [ @@ -2102,7 +2087,7 @@ }, { "cell_type": "markdown", - "id": "160", + "id": "159", "metadata": {}, "source": [ "One of the main goals of this study was to refine the lattice\n", @@ -2115,7 +2100,7 @@ }, { "cell_type": "markdown", - "id": "161", + "id": "160", "metadata": {}, "source": [ "#### Exercise 5.5: Visualize the Fit Results in d-spacing\n", @@ -2126,7 +2111,7 @@ }, { "cell_type": "markdown", - "id": "162", + "id": "161", "metadata": {}, "source": [ "**Hint:**" @@ -2134,7 +2119,7 @@ }, { "cell_type": "markdown", - "id": "163", + "id": "162", "metadata": {}, "source": [ "Use the `plot_meas_vs_calc` method of the project and set the\n", @@ -2143,7 +2128,7 @@ }, { "cell_type": "markdown", - "id": "164", + "id": "163", "metadata": {}, "source": [ "**Solution:**" @@ -2152,7 +2137,7 @@ { "cell_type": "code", "execution_count": null, - "id": "165", + "id": "164", "metadata": {}, "outputs": [], "source": [ @@ -2161,7 +2146,7 @@ }, { "cell_type": "markdown", - "id": "166", + "id": "165", "metadata": {}, "source": [ "#### Exercise 5.6: Refine the Peak Profile Parameters\n", @@ -2181,7 +2166,7 @@ { "cell_type": "code", "execution_count": null, - "id": "167", + "id": "166", "metadata": {}, "outputs": [], "source": [ @@ -2190,7 +2175,7 @@ }, { "cell_type": "markdown", - "id": "168", + "id": "167", "metadata": {}, "source": [ "The peak profile parameters are determined based on both the\n", @@ -2204,7 +2189,7 @@ }, { "cell_type": "markdown", - "id": "169", + "id": "168", "metadata": {}, "source": [ "**Hint:**" @@ -2212,7 +2197,7 @@ }, { "cell_type": "markdown", - "id": "170", + "id": "169", "metadata": {}, "source": [ "You can set the `free` attribute of the peak profile parameters to\n", @@ -2223,7 +2208,7 @@ }, { "cell_type": "markdown", - "id": "171", + "id": "170", "metadata": {}, "source": [ "**Solution:**" @@ -2232,7 +2217,7 @@ { "cell_type": "code", "execution_count": null, - "id": "172", + "id": "171", "metadata": {}, "outputs": [], "source": [ @@ -2252,7 +2237,7 @@ }, { "cell_type": "markdown", - "id": "173", + "id": "172", "metadata": {}, "source": [ "#### Exercise 5.7: Find Undefined Features\n", @@ -2264,7 +2249,7 @@ }, { "cell_type": "markdown", - "id": "174", + "id": "173", "metadata": {}, "source": [ "**Hint:**" @@ -2272,7 +2257,7 @@ }, { "cell_type": "markdown", - "id": "175", + "id": "174", "metadata": {}, "source": [ "While the fit is now significantly better, there are still some\n", @@ -2284,7 +2269,7 @@ }, { "cell_type": "markdown", - "id": "176", + "id": "175", "metadata": {}, "source": [ "**Solution:**" @@ -2293,7 +2278,7 @@ { "cell_type": "code", "execution_count": null, - "id": "177", + "id": "176", "metadata": {}, "outputs": [], "source": [ @@ -2302,7 +2287,7 @@ }, { "cell_type": "markdown", - "id": "178", + "id": "177", "metadata": {}, "source": [ "#### Exercise 5.8: Identify the Cause of the Unexplained Peaks\n", @@ -2315,7 +2300,7 @@ }, { "cell_type": "markdown", - "id": "179", + "id": "178", "metadata": {}, "source": [ "**Hint:**" @@ -2323,7 +2308,7 @@ }, { "cell_type": "markdown", - "id": "180", + "id": "179", "metadata": {}, "source": [ "Consider the following options:\n", @@ -2335,7 +2320,7 @@ }, { "cell_type": "markdown", - "id": "181", + "id": "180", "metadata": {}, "source": [ "**Solution:**" @@ -2343,7 +2328,7 @@ }, { "cell_type": "markdown", - "id": "182", + "id": "181", "metadata": {}, "source": [ "1. ❌ In principle, this could be the case, as sometimes the presence\n", @@ -2363,7 +2348,7 @@ }, { "cell_type": "markdown", - "id": "183", + "id": "182", "metadata": {}, "source": [ "#### Exercise 5.9: Identify the impurity phase\n", @@ -2374,7 +2359,7 @@ }, { "cell_type": "markdown", - "id": "184", + "id": "183", "metadata": {}, "source": [ "**Hint:**" @@ -2382,7 +2367,7 @@ }, { "cell_type": "markdown", - "id": "185", + "id": "184", "metadata": {}, "source": [ "Check the positions of the unexplained peaks in the diffraction\n", @@ -2392,7 +2377,7 @@ }, { "cell_type": "markdown", - "id": "186", + "id": "185", "metadata": {}, "source": [ "**Solution:**" @@ -2400,7 +2385,7 @@ }, { "cell_type": "markdown", - "id": "187", + "id": "186", "metadata": {}, "source": [ "The unexplained peaks are likely due to the presence of a small amount\n", @@ -2415,7 +2400,7 @@ { "cell_type": "code", "execution_count": null, - "id": "188", + "id": "187", "metadata": {}, "outputs": [], "source": [ @@ -2425,7 +2410,7 @@ }, { "cell_type": "markdown", - "id": "189", + "id": "188", "metadata": {}, "source": [ "#### Exercise 5.10: Create a Second Structure – Si as Impurity\n", @@ -2437,7 +2422,7 @@ }, { "cell_type": "markdown", - "id": "190", + "id": "189", "metadata": {}, "source": [ "**Hint:**" @@ -2445,7 +2430,7 @@ }, { "cell_type": "markdown", - "id": "191", + "id": "190", "metadata": {}, "source": [ "You can use the same approach as in the previous part of the notebook,\n", @@ -2455,7 +2440,7 @@ }, { "cell_type": "markdown", - "id": "192", + "id": "191", "metadata": {}, "source": [ "**Solution:**" @@ -2464,7 +2449,7 @@ { "cell_type": "code", "execution_count": null, - "id": "193", + "id": "192", "metadata": {}, "outputs": [], "source": [ @@ -2493,7 +2478,7 @@ }, { "cell_type": "markdown", - "id": "194", + "id": "193", "metadata": {}, "source": [ "#### Exercise 5.11: Refine the Scale of the Si Phase\n", @@ -2506,7 +2491,7 @@ }, { "cell_type": "markdown", - "id": "195", + "id": "194", "metadata": {}, "source": [ "**Hint:**" @@ -2514,7 +2499,7 @@ }, { "cell_type": "markdown", - "id": "196", + "id": "195", "metadata": {}, "source": [ "You can use the `plot_meas_vs_calc` method of the project to visualize\n", @@ -2525,7 +2510,7 @@ }, { "cell_type": "markdown", - "id": "197", + "id": "196", "metadata": {}, "source": [ "**Solution:**" @@ -2534,7 +2519,7 @@ { "cell_type": "code", "execution_count": null, - "id": "198", + "id": "197", "metadata": {}, "outputs": [], "source": [ @@ -2563,7 +2548,7 @@ }, { "cell_type": "markdown", - "id": "199", + "id": "198", "metadata": {}, "source": [ "All previously unexplained peaks are now accounted for in the pattern,\n", @@ -2586,7 +2571,7 @@ { "cell_type": "code", "execution_count": null, - "id": "200", + "id": "199", "metadata": {}, "outputs": [], "source": [ @@ -2595,7 +2580,7 @@ }, { "cell_type": "markdown", - "id": "201", + "id": "200", "metadata": {}, "source": [ "Finally, we save the project to disk to preserve the current state of\n", @@ -2605,7 +2590,7 @@ { "cell_type": "code", "execution_count": null, - "id": "202", + "id": "201", "metadata": {}, "outputs": [], "source": [ @@ -2614,7 +2599,7 @@ }, { "cell_type": "markdown", - "id": "203", + "id": "202", "metadata": {}, "source": [ "#### Final Remarks\n", @@ -2633,7 +2618,7 @@ }, { "cell_type": "markdown", - "id": "204", + "id": "203", "metadata": {}, "source": [ "## 🎁 Bonus\n", @@ -2662,7 +2647,7 @@ ], "metadata": { "jupytext": { - "cell_metadata_filter": "tags,title,-all", + "cell_metadata_filter": "title,tags,-all", "main_language": "python", "notebook_metadata_filter": "-all" } diff --git a/docs/docs/tutorials/ed-14.ipynb b/docs/docs/tutorials/ed-14.ipynb index a5db5807..df9a857c 100644 --- a/docs/docs/tutorials/ed-14.ipynb +++ b/docs/docs/tutorials/ed-14.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5bd8b55f", + "id": "80ba77ad", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Structure Refinement: Tb2TiO7, HEiDi\n", @@ -47,7 +32,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -56,7 +41,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -65,7 +50,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Step 1: Define Project" @@ -74,7 +59,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -84,7 +69,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "## Step 2: Define Structure" @@ -93,7 +78,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -104,7 +89,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -114,7 +99,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -124,7 +109,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -134,7 +119,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -147,7 +132,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -156,7 +141,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "12", "metadata": {}, "source": [ "## Step 3: Define Experiment" @@ -165,7 +150,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -175,7 +160,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -191,7 +176,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -201,7 +186,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -212,7 +197,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -222,7 +207,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -232,7 +217,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "19", "metadata": {}, "source": [ "## Step 4: Perform Analysis" @@ -241,7 +226,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -251,7 +236,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -262,7 +247,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -272,7 +257,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -284,7 +269,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -295,7 +280,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -305,7 +290,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -315,7 +300,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -324,7 +309,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "28", "metadata": {}, "source": [ "## Step 5: Show Project Summary" @@ -333,7 +318,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "29", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-15.ipynb b/docs/docs/tutorials/ed-15.ipynb index cdd646ac..60ec08ce 100644 --- a/docs/docs/tutorials/ed-15.ipynb +++ b/docs/docs/tutorials/ed-15.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4e0ef3ea", + "id": "7a35fc22", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Structure Refinement: Taurine, SENJU\n", @@ -47,7 +32,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -56,7 +41,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -65,7 +50,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Step 1: Define Project" @@ -74,7 +59,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -84,7 +69,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "## Step 2: Define Structure" @@ -93,7 +78,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -104,7 +89,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -114,7 +99,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -124,7 +109,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -134,7 +119,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -143,7 +128,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "## Step 3: Define Experiment" @@ -152,7 +137,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -162,7 +147,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -178,7 +163,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -188,7 +173,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -199,7 +184,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -209,7 +194,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ "## Step 4: Perform Analysis" @@ -218,7 +203,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -228,7 +213,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -239,7 +224,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -249,7 +234,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -261,7 +246,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -272,7 +257,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -282,7 +267,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -292,7 +277,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -301,7 +286,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "26", "metadata": {}, "source": [ "## Step 5: Show Project Summary" @@ -310,7 +295,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "27", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-16.ipynb b/docs/docs/tutorials/ed-16.ipynb index 458ded2c..5d0aa659 100644 --- a/docs/docs/tutorials/ed-16.ipynb +++ b/docs/docs/tutorials/ed-16.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7311bb93", + "id": "0956c08b", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Joint Refinement: Si, Bragg + PDF\n", @@ -51,7 +36,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -60,7 +45,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -72,7 +57,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Define Structure\n", @@ -87,7 +72,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -96,7 +81,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "#### Set Space Group" @@ -105,7 +90,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -115,7 +100,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "7", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -124,7 +109,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -133,7 +118,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -142,7 +127,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -159,7 +144,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "## Define Experiments\n", @@ -175,7 +160,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -184,7 +169,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "13", "metadata": {}, "source": [ "#### Create Experiment" @@ -193,7 +178,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -204,7 +189,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "15", "metadata": {}, "source": [ "#### Set Instrument" @@ -213,7 +198,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -225,7 +210,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -234,7 +219,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -250,7 +235,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "19", "metadata": {}, "source": [ "#### Set Background" @@ -259,7 +244,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -270,7 +255,7 @@ }, { "cell_type": "markdown", - "id": "22", + "id": "21", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -279,7 +264,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -288,7 +273,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "23", "metadata": {}, "source": [ "### Experiment 2: PDF (NOMAD, TOF)\n", @@ -299,7 +284,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -308,7 +293,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "25", "metadata": {}, "source": [ "#### Create Experiment" @@ -317,7 +302,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -331,7 +316,7 @@ }, { "cell_type": "markdown", - "id": "28", + "id": "27", "metadata": {}, "source": [ "#### Set Peak Profile (PDF Parameters)" @@ -340,7 +325,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -354,7 +339,7 @@ }, { "cell_type": "markdown", - "id": "30", + "id": "29", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -363,7 +348,7 @@ { "cell_type": "code", "execution_count": null, - "id": "31", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -372,7 +357,7 @@ }, { "cell_type": "markdown", - "id": "32", + "id": "31", "metadata": {}, "source": [ "## Define Project\n", @@ -386,7 +371,7 @@ { "cell_type": "code", "execution_count": null, - "id": "33", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -395,7 +380,7 @@ }, { "cell_type": "markdown", - "id": "34", + "id": "33", "metadata": {}, "source": [ "#### Add Structure" @@ -404,7 +389,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "34", "metadata": {}, "outputs": [], "source": [ @@ -413,7 +398,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "35", "metadata": {}, "source": [ "#### Add Experiments" @@ -422,7 +407,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37", + "id": "36", "metadata": {}, "outputs": [], "source": [ @@ -432,7 +417,7 @@ }, { "cell_type": "markdown", - "id": "38", + "id": "37", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -446,7 +431,7 @@ { "cell_type": "code", "execution_count": null, - "id": "39", + "id": "38", "metadata": {}, "outputs": [], "source": [ @@ -457,7 +442,7 @@ }, { "cell_type": "markdown", - "id": "40", + "id": "39", "metadata": {}, "source": [ "#### Set Minimizer" @@ -466,7 +451,7 @@ { "cell_type": "code", "execution_count": null, - "id": "41", + "id": "40", "metadata": {}, "outputs": [], "source": [ @@ -475,7 +460,7 @@ }, { "cell_type": "markdown", - "id": "42", + "id": "41", "metadata": {}, "source": [ "#### Plot Measured vs Calculated (Before Fit)" @@ -484,7 +469,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "42", "metadata": {}, "outputs": [], "source": [ @@ -494,7 +479,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -503,7 +488,7 @@ }, { "cell_type": "markdown", - "id": "45", + "id": "44", "metadata": {}, "source": [ "#### Set Fitting Parameters\n", @@ -515,7 +500,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -525,7 +510,7 @@ }, { "cell_type": "markdown", - "id": "47", + "id": "46", "metadata": {}, "source": [ "Bragg experiment parameters." @@ -534,7 +519,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "47", "metadata": {}, "outputs": [], "source": [ @@ -549,7 +534,7 @@ }, { "cell_type": "markdown", - "id": "49", + "id": "48", "metadata": {}, "source": [ "PDF experiment parameters." @@ -558,7 +543,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50", + "id": "49", "metadata": {}, "outputs": [], "source": [ @@ -571,7 +556,7 @@ }, { "cell_type": "markdown", - "id": "51", + "id": "50", "metadata": {}, "source": [ "#### Show Free Parameters" @@ -580,7 +565,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "51", "metadata": {}, "outputs": [], "source": [ @@ -589,7 +574,7 @@ }, { "cell_type": "markdown", - "id": "53", + "id": "52", "metadata": {}, "source": [ "#### Run Fitting" @@ -598,7 +583,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "53", "metadata": {}, "outputs": [], "source": [ @@ -608,7 +593,7 @@ }, { "cell_type": "markdown", - "id": "55", + "id": "54", "metadata": {}, "source": [ "#### Plot Measured vs Calculated (After Fit)" @@ -617,7 +602,7 @@ { "cell_type": "code", "execution_count": null, - "id": "56", + "id": "55", "metadata": {}, "outputs": [], "source": [ @@ -627,7 +612,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57", + "id": "56", "metadata": { "lines_to_next_cell": 2 }, @@ -639,7 +624,7 @@ { "cell_type": "code", "execution_count": null, - "id": "58", + "id": "57", "metadata": {}, "outputs": [], "source": [] diff --git a/docs/docs/tutorials/ed-17.ipynb b/docs/docs/tutorials/ed-17.ipynb index 08eb5a20..e9369b01 100644 --- a/docs/docs/tutorials/ed-17.ipynb +++ b/docs/docs/tutorials/ed-17.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "1c3cb779", + "id": "79fce5be", "metadata": { "tags": [ "hide-in-docs" @@ -19,38 +19,23 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Structure Refinement: Co2SiO4, D20 (T-scan)\n", "\n", "This example demonstrates a Rietveld refinement of the Co2SiO4 crystal\n", "structure using constant-wavelength neutron powder diffraction data\n", - "from D20 at ILL. A sequential refinement of the same structure against\n", - "a temperature scan is performed to show how to manage multiple\n", - "experiments in a project." + "from D20 at ILL. A sequential refinement is performed against a\n", + "temperature scan using `fit_sequential`, which processes each data\n", + "file independently without loading all datasets into memory at once." ] }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -59,7 +44,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -68,7 +53,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Step 1: Define Project\n", @@ -79,7 +64,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -88,26 +73,26 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ - "Set output verbosity level to \"short\" to show only one-line status\n", - "messages during the analysis process." + "The project must be saved before running sequential fitting, so that\n", + "results can be written to `analysis/results.csv`." ] }, { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ - "project.verbosity = 'short'" + "project.save_as('data/cosio_project', temporary=False)" ] }, { "cell_type": "markdown", - "id": "8", + "id": "7", "metadata": {}, "source": [ "## Step 2: Define Crystal Structure\n", @@ -121,7 +106,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -131,7 +116,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ "#### Set Space Group" @@ -140,7 +125,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -150,7 +135,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -159,7 +144,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -170,7 +155,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "13", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -179,7 +164,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -241,13 +226,15 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "15", "metadata": {}, "source": [ - "## Step 3: Define Experiments\n", + "## Step 3: Define Template Experiment\n", "\n", - "This section shows how to add experiments, configure their parameters,\n", - "and link the structures defined above.\n", + "For sequential fitting, we create a single template experiment from\n", + "the first data file. This template defines the instrument, peak\n", + "profile, background, and linked phases that will be reused for every\n", + "data file in the scan.\n", "\n", "#### Download Measured Data" ] @@ -255,46 +242,58 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ - "file_path = ed.download_data(id=27, destination='data')" + "zip_path = ed.download_data(id=24, destination='data')" ] }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ - "#### Create Experiments and Set Temperature" + "#### Extract Data Files" ] }, { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ - "data_paths = ed.extract_data_paths_from_zip(file_path)\n", - "for i, data_path in enumerate(data_paths, start=1):\n", - " name = f'd20_{i}'\n", - " project.experiments.add_from_data_path(\n", - " name=name,\n", - " data_path=data_path,\n", - " )\n", - " expt = project.experiments[name]\n", - " expt.diffrn.ambient_temperature = ed.extract_metadata(\n", - " file_path=data_path,\n", - " pattern=r'^TEMP\\s+([0-9.]+)',\n", - " )" + "data_dir = 'data/d20_scan'\n", + "data_paths = ed.extract_data_paths_from_zip(zip_path, destination=data_dir)" ] }, { "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "#### Create Template Experiment from the First File" + ] + }, + { + "cell_type": "code", + "execution_count": null, "id": "20", "metadata": {}, + "outputs": [], + "source": [ + "project.experiments.add_from_data_path(\n", + " name='d20',\n", + " data_path=data_paths[0],\n", + ")\n", + "expt = project.experiments['d20']" + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, "source": [ "#### Set Instrument" ] @@ -302,18 +301,17 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "22", "metadata": {}, "outputs": [], "source": [ - "for expt in project.experiments:\n", - " expt.instrument.setup_wavelength = 1.87\n", - " expt.instrument.calib_twotheta_offset = 0.29" + "expt.instrument.setup_wavelength = 1.87\n", + "expt.instrument.calib_twotheta_offset = 0.29" ] }, { "cell_type": "markdown", - "id": "22", + "id": "23", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -322,20 +320,19 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "24", "metadata": {}, "outputs": [], "source": [ - "for expt in project.experiments:\n", - " expt.peak.broad_gauss_u = 0.24\n", - " expt.peak.broad_gauss_v = -0.53\n", - " expt.peak.broad_gauss_w = 0.38\n", - " expt.peak.broad_lorentz_y = 0.02" + "expt.peak.broad_gauss_u = 0.24\n", + "expt.peak.broad_gauss_v = -0.53\n", + "expt.peak.broad_gauss_w = 0.38\n", + "expt.peak.broad_lorentz_y = 0.02" ] }, { "cell_type": "markdown", - "id": "24", + "id": "25", "metadata": {}, "source": [ "#### Set Excluded Regions" @@ -344,18 +341,17 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "26", "metadata": {}, "outputs": [], "source": [ - "for expt in project.experiments:\n", - " expt.excluded_regions.create(id='1', start=0, end=8)\n", - " expt.excluded_regions.create(id='2', start=150, end=180)" + "expt.excluded_regions.create(id='1', start=0, end=8)\n", + "expt.excluded_regions.create(id='2', start=150, end=180)" ] }, { "cell_type": "markdown", - "id": "26", + "id": "27", "metadata": {}, "source": [ "#### Set Background" @@ -364,30 +360,29 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "28", "metadata": {}, "outputs": [], "source": [ - "for expt in project.experiments:\n", - " expt.background.create(id='1', x=8, y=609)\n", - " expt.background.create(id='2', x=9, y=581)\n", - " expt.background.create(id='3', x=10, y=563)\n", - " expt.background.create(id='4', x=11, y=540)\n", - " expt.background.create(id='5', x=12, y=520)\n", - " expt.background.create(id='6', x=15, y=507)\n", - " expt.background.create(id='7', x=25, y=463)\n", - " expt.background.create(id='8', x=30, y=434)\n", - " expt.background.create(id='9', x=50, y=451)\n", - " expt.background.create(id='10', x=70, y=431)\n", - " expt.background.create(id='11', x=90, y=414)\n", - " expt.background.create(id='12', x=110, y=361)\n", - " expt.background.create(id='13', x=130, y=292)\n", - " expt.background.create(id='14', x=150, y=241)" + "expt.background.create(id='1', x=8, y=609)\n", + "expt.background.create(id='2', x=9, y=581)\n", + "expt.background.create(id='3', x=10, y=563)\n", + "expt.background.create(id='4', x=11, y=540)\n", + "expt.background.create(id='5', x=12, y=520)\n", + "expt.background.create(id='6', x=15, y=507)\n", + "expt.background.create(id='7', x=25, y=463)\n", + "expt.background.create(id='8', x=30, y=434)\n", + "expt.background.create(id='9', x=50, y=451)\n", + "expt.background.create(id='10', x=70, y=431)\n", + "expt.background.create(id='11', x=90, y=414)\n", + "expt.background.create(id='12', x=110, y=361)\n", + "expt.background.create(id='13', x=130, y=292)\n", + "expt.background.create(id='14', x=150, y=241)" ] }, { "cell_type": "markdown", - "id": "28", + "id": "29", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -396,28 +391,27 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "30", "metadata": {}, "outputs": [], "source": [ - "for expt in project.experiments:\n", - " expt.linked_phases.create(id='cosio', scale=1.2)" + "expt.linked_phases.create(id='cosio', scale=1.2)" ] }, { "cell_type": "markdown", - "id": "30", + "id": "31", "metadata": {}, "source": [ "## Step 4: Perform Analysis\n", "\n", "This section shows how to set free parameters, define constraints,\n", - "and run the refinement." + "and run the sequential refinement." ] }, { "cell_type": "markdown", - "id": "31", + "id": "32", "metadata": {}, "source": [ "#### Set Free Parameters" @@ -426,7 +420,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -457,27 +451,26 @@ { "cell_type": "code", "execution_count": null, - "id": "33", + "id": "34", "metadata": {}, "outputs": [], "source": [ - "for expt in project.experiments:\n", - " expt.linked_phases['cosio'].scale.free = True\n", + "expt.linked_phases['cosio'].scale.free = True\n", "\n", - " expt.instrument.calib_twotheta_offset.free = True\n", + "expt.instrument.calib_twotheta_offset.free = True\n", "\n", - " expt.peak.broad_gauss_u.free = True\n", - " expt.peak.broad_gauss_v.free = True\n", - " expt.peak.broad_gauss_w.free = True\n", - " expt.peak.broad_lorentz_y.free = True\n", + "expt.peak.broad_gauss_u.free = True\n", + "expt.peak.broad_gauss_v.free = True\n", + "expt.peak.broad_gauss_w.free = True\n", + "expt.peak.broad_lorentz_y.free = True\n", "\n", - " for point in expt.background:\n", - " point.y.free = True" + "for point in expt.background:\n", + " point.y.free = True" ] }, { "cell_type": "markdown", - "id": "34", + "id": "35", "metadata": {}, "source": [ "#### Set Constraints\n", @@ -488,7 +481,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "36", "metadata": {}, "outputs": [], "source": [ @@ -504,7 +497,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "37", "metadata": {}, "source": [ "Set constraints." @@ -513,75 +506,129 @@ { "cell_type": "code", "execution_count": null, - "id": "37", - "metadata": { - "lines_to_next_cell": 2 - }, + "id": "38", + "metadata": {}, "outputs": [], "source": [ - "project.analysis.constraints.create(\n", - " expression='biso_Co2 = biso_Co1',\n", - ")" + "project.analysis.constraints.create(expression='biso_Co2 = biso_Co1')" ] }, { "cell_type": "markdown", - "id": "38", + "id": "39", "metadata": {}, "source": [ - "#### Set Fit Mode" + "#### Run Single Fitting\n", + "\n", + "This is the fitting of the first dataset to optimize the initial\n", + "parameters for the sequential fitting. This step is optional but can\n", + "help with convergence and speed of the sequential fitting, especially\n", + "if the initial parameters are far from optimal." ] }, { "cell_type": "code", "execution_count": null, - "id": "39", + "id": "40", "metadata": {}, "outputs": [], "source": [ - "project.analysis.fit_mode.mode = 'single'" + "project.analysis.fit()" ] }, { "cell_type": "markdown", - "id": "40", + "id": "41", "metadata": {}, "source": [ - "#### Run Fitting" + "#### Run Sequential Fitting\n", + "\n", + "Set output verbosity level to \"short\" to show only one-line status\n", + "messages during the analysis process." ] }, { "cell_type": "code", "execution_count": null, - "id": "41", + "id": "42", "metadata": {}, "outputs": [], "source": [ - "project.analysis.fit()" + "project.verbosity = 'short'" ] }, { "cell_type": "markdown", - "id": "42", + "id": "43", + "metadata": { + "lines_to_next_cell": 2 + }, + "source": [ + "\n", + "Define a callback that extracts the temperature from each data file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44", "metadata": {}, + "outputs": [], "source": [ - "#### Plot Measured vs Calculated" + "def extract_diffrn(file_path):\n", + " temperature = ed.extract_metadata(\n", + " file_path=file_path,\n", + " pattern=r'^TEMP\\s+([0-9.]+)',\n", + " )\n", + " return {'ambient_temperature': temperature}" + ] + }, + { + "cell_type": "markdown", + "id": "45", + "metadata": {}, + "source": [ + "Run the sequential fit over all data files in the scan directory." ] }, { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "46", "metadata": {}, "outputs": [], "source": [ - "last_expt_name = project.experiments.names[-1]\n", - "project.plot_meas_vs_calc(expt_name=last_expt_name, show_residual=True)" + "project.analysis.fit_sequential(\n", + " data_dir=data_dir,\n", + " extract_diffrn=extract_diffrn,\n", + " max_workers='auto',\n", + ")" ] }, { "cell_type": "markdown", - "id": "44", + "id": "47", + "metadata": {}, + "source": [ + "#### Replay a Dataset\n", + "\n", + "Apply fitted parameters from the last CSV row and plot the result." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48", + "metadata": {}, + "outputs": [], + "source": [ + "project.apply_params_from_csv(row_index=-1)\n", + "project.plot_meas_vs_calc(expt_name='d20', show_residual=True)" + ] + }, + { + "cell_type": "markdown", + "id": "49", "metadata": {}, "source": [ "#### Plot Parameter Evolution\n", @@ -592,16 +639,16 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "50", "metadata": {}, "outputs": [], "source": [ - "temperature = project.experiments[0].diffrn.ambient_temperature" + "temperature = expt.diffrn.ambient_temperature" ] }, { "cell_type": "markdown", - "id": "46", + "id": "51", "metadata": {}, "source": [ "Plot unit cell parameters vs. temperature." @@ -610,7 +657,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "52", "metadata": {}, "outputs": [], "source": [ @@ -621,7 +668,7 @@ }, { "cell_type": "markdown", - "id": "48", + "id": "53", "metadata": {}, "source": [ "Plot isotropic displacement parameters vs. temperature." @@ -630,7 +677,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "54", "metadata": {}, "outputs": [], "source": [ @@ -643,7 +690,7 @@ }, { "cell_type": "markdown", - "id": "50", + "id": "55", "metadata": {}, "source": [ "Plot selected fractional coordinates vs. temperature." @@ -652,7 +699,7 @@ { "cell_type": "code", "execution_count": null, - "id": "51", + "id": "56", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-18.ipynb b/docs/docs/tutorials/ed-18.ipynb index 9f51a133..60ce7707 100644 --- a/docs/docs/tutorials/ed-18.ipynb +++ b/docs/docs/tutorials/ed-18.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a3ec0a8c", + "id": "71e56392", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Load Project and Fit: LBCO, HRPT\n", @@ -45,135 +30,110 @@ "how to load a previously saved project from a directory and run\n", "refinement — all in just a few lines of code.\n", "\n", - "The project is first created and saved as a setup step (this would\n", - "normally be done once and the directory would already exist on disk).\n", - "Then the saved project is loaded back and fitted.\n", - "\n", "For details on how to define structures and experiments, see the other\n", "tutorials." ] }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ - "## Import Library" + "## Import Modules" ] }, { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ - "import easydiffraction as ed" + "from easydiffraction import Project\n", + "from easydiffraction import download_data\n", + "from easydiffraction import extract_project_from_zip" ] }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ - "## Setup: Create and Save a Project\n", - "\n", - "This step creates a project from CIF files and saves it to a\n", - "directory. In practice, the project directory would already exist\n", - "on disk from a previous session." + "## Download Project Archive" ] }, { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ - "# Create a project from CIF files\n", - "project = ed.Project()\n", - "project.structures.add_from_cif_path(ed.download_data(id=1, destination='data'))\n", - "project.experiments.add_from_cif_path(ed.download_data(id=2, destination='data'))" + "zip_path = download_data(id=28, destination='data')" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "6", + "cell_type": "markdown", + "id": "5", "metadata": {}, - "outputs": [], "source": [ - "project.analysis.aliases.create(\n", - " label='biso_La',\n", - " param=project.structures['lbco'].atom_sites['La'].b_iso,\n", - ")\n", - "project.analysis.aliases.create(\n", - " label='biso_Ba',\n", - " param=project.structures['lbco'].atom_sites['Ba'].b_iso,\n", - ")\n", - "\n", - "project.analysis.aliases.create(\n", - " label='occ_La',\n", - " param=project.structures['lbco'].atom_sites['La'].occupancy,\n", - ")\n", - "project.analysis.aliases.create(\n", - " label='occ_Ba',\n", - " param=project.structures['lbco'].atom_sites['Ba'].occupancy,\n", - ")\n", - "\n", - "project.analysis.constraints.create(expression='biso_Ba = biso_La')\n", - "project.analysis.constraints.create(expression='occ_Ba = 1 - occ_La')\n", - "\n", - "project.structures['lbco'].atom_sites['La'].occupancy.free = True" + "## Extract Project" ] }, { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ - "# Save to a directory\n", - "project.save_as('lbco_project')" + "project_dir = extract_project_from_zip(zip_path, destination='data')" ] }, { "cell_type": "markdown", - "id": "8", + "id": "7", "metadata": {}, "source": [ - "## Step 1: Load Project from Directory" + "## Load Project" ] }, { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ - "project = ed.Project.load('lbco_project')" + "project = Project.load(project_dir)" ] }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ - "## Step 2: Perform Analysis" + "## Perform Analysis" ] }, { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ "project.analysis.fit()" ] }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Show Results" + ] + }, { "cell_type": "code", "execution_count": null, @@ -184,10 +144,18 @@ "project.analysis.show_fit_results()" ] }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "## Plot Meas vs Calc" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -196,20 +164,20 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "15", "metadata": {}, "source": [ - "## Step 3: Show Project Summary" + "## Save Project" ] }, { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "16", "metadata": {}, "outputs": [], "source": [ - "project.summary.show_report()" + "project.save()" ] } ], diff --git a/docs/docs/tutorials/ed-2.ipynb b/docs/docs/tutorials/ed-2.ipynb index cb95e408..00d769ad 100644 --- a/docs/docs/tutorials/ed-2.ipynb +++ b/docs/docs/tutorials/ed-2.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d9a613b4", + "id": "b94f1ffd", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Structure Refinement: LBCO, HRPT\n", @@ -63,7 +48,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -72,7 +57,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -81,7 +66,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Step 1: Define Project" @@ -90,7 +75,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -99,7 +84,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "## Step 2: Define Structure" @@ -108,7 +93,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -118,7 +103,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -128,7 +113,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -139,7 +124,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -149,7 +134,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -195,7 +180,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "## Step 3: Define Experiment" @@ -204,7 +189,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -214,7 +199,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -230,7 +215,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -240,7 +225,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -251,7 +236,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -264,7 +249,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -278,7 +263,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -289,7 +274,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -298,7 +283,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "20", "metadata": {}, "source": [ "## Step 4: Perform Analysis" @@ -307,7 +292,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -322,7 +307,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "22", "metadata": { "lines_to_next_cell": 2 }, @@ -347,7 +332,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -358,7 +343,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "24", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-3.ipynb b/docs/docs/tutorials/ed-3.ipynb index 3dd711e2..78c3ed5c 100644 --- a/docs/docs/tutorials/ed-3.ipynb +++ b/docs/docs/tutorials/ed-3.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d59d709c", + "id": "725cf769", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Structure Refinement: LBCO, HRPT\n", @@ -61,7 +46,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -70,7 +55,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -79,7 +64,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Step 1: Create a Project\n", @@ -89,7 +74,7 @@ }, { "cell_type": "markdown", - "id": "5", + "id": "4", "metadata": {}, "source": [ "#### Create Project" @@ -98,7 +83,7 @@ { "cell_type": "code", "execution_count": null, - "id": "6", + "id": "5", "metadata": {}, "outputs": [], "source": [ @@ -107,7 +92,7 @@ }, { "cell_type": "markdown", - "id": "7", + "id": "6", "metadata": {}, "source": [ "#### Set Project Metadata" @@ -116,7 +101,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "7", "metadata": {}, "outputs": [], "source": [ @@ -129,7 +114,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "8", "metadata": {}, "source": [ "#### Show Project Metadata as CIF" @@ -138,7 +123,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -147,7 +132,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "10", "metadata": {}, "source": [ "#### Save Project\n", @@ -160,7 +145,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -169,7 +154,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "12", "metadata": {}, "source": [ "#### Set Up Data Plotter" @@ -177,7 +162,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "13", "metadata": {}, "source": [ "Show supported plotting engines." @@ -186,7 +171,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -195,7 +180,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "15", "metadata": {}, "source": [ "Show current plotting configuration." @@ -204,7 +189,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -213,7 +198,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ "Set plotting engine." @@ -222,7 +207,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -233,7 +218,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "19", "metadata": {}, "source": [ "## Step 2: Define Structure\n", @@ -244,7 +229,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "20", "metadata": {}, "source": [ "#### Add Structure" @@ -253,7 +238,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -262,7 +247,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "22", "metadata": {}, "source": [ "#### Show Defined Structures\n", @@ -276,7 +261,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -285,7 +270,7 @@ }, { "cell_type": "markdown", - "id": "25", + "id": "24", "metadata": {}, "source": [ "#### Set Space Group\n", @@ -296,7 +281,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -306,7 +291,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "26", "metadata": {}, "source": [ "#### Set Unit Cell\n", @@ -317,7 +302,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -326,7 +311,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "28", "metadata": {}, "source": [ "#### Set Atom Sites\n", @@ -337,7 +322,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -383,7 +368,7 @@ }, { "cell_type": "markdown", - "id": "31", + "id": "30", "metadata": {}, "source": [ "#### Show Structure as CIF" @@ -392,7 +377,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -401,7 +386,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "32", "metadata": {}, "source": [ "#### Show Structure Structure" @@ -410,7 +395,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -419,7 +404,7 @@ }, { "cell_type": "markdown", - "id": "35", + "id": "34", "metadata": {}, "source": [ "#### Save Project State\n", @@ -432,7 +417,7 @@ { "cell_type": "code", "execution_count": null, - "id": "36", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -441,7 +426,7 @@ }, { "cell_type": "markdown", - "id": "37", + "id": "36", "metadata": {}, "source": [ "## Step 3: Define Experiment\n", @@ -452,7 +437,7 @@ }, { "cell_type": "markdown", - "id": "38", + "id": "37", "metadata": {}, "source": [ "#### Download Measured Data\n", @@ -463,7 +448,7 @@ { "cell_type": "code", "execution_count": null, - "id": "39", + "id": "38", "metadata": {}, "outputs": [], "source": [ @@ -472,7 +457,7 @@ }, { "cell_type": "markdown", - "id": "40", + "id": "39", "metadata": {}, "source": [ "#### Add Diffraction Experiment" @@ -481,7 +466,7 @@ { "cell_type": "code", "execution_count": null, - "id": "41", + "id": "40", "metadata": {}, "outputs": [], "source": [ @@ -496,7 +481,7 @@ }, { "cell_type": "markdown", - "id": "42", + "id": "41", "metadata": {}, "source": [ "#### Show Defined Experiments" @@ -505,7 +490,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "42", "metadata": {}, "outputs": [], "source": [ @@ -514,7 +499,7 @@ }, { "cell_type": "markdown", - "id": "44", + "id": "43", "metadata": {}, "source": [ "#### Show Measured Data" @@ -523,7 +508,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "44", "metadata": {}, "outputs": [], "source": [ @@ -532,7 +517,7 @@ }, { "cell_type": "markdown", - "id": "46", + "id": "45", "metadata": {}, "source": [ "#### Set Instrument\n", @@ -543,7 +528,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "46", "metadata": {}, "outputs": [], "source": [ @@ -553,7 +538,7 @@ }, { "cell_type": "markdown", - "id": "48", + "id": "47", "metadata": {}, "source": [ "#### Set Peak Profile\n", @@ -564,7 +549,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "48", "metadata": {}, "outputs": [], "source": [ @@ -573,7 +558,7 @@ }, { "cell_type": "markdown", - "id": "50", + "id": "49", "metadata": {}, "source": [ "Show the current peak profile type." @@ -582,7 +567,7 @@ { "cell_type": "code", "execution_count": null, - "id": "51", + "id": "50", "metadata": {}, "outputs": [], "source": [ @@ -591,7 +576,7 @@ }, { "cell_type": "markdown", - "id": "52", + "id": "51", "metadata": {}, "source": [ "Select the desired peak profile type." @@ -600,7 +585,7 @@ { "cell_type": "code", "execution_count": null, - "id": "53", + "id": "52", "metadata": {}, "outputs": [], "source": [ @@ -609,7 +594,7 @@ }, { "cell_type": "markdown", - "id": "54", + "id": "53", "metadata": {}, "source": [ "Modify default peak profile parameters." @@ -618,7 +603,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "54", "metadata": {}, "outputs": [], "source": [ @@ -631,7 +616,7 @@ }, { "cell_type": "markdown", - "id": "56", + "id": "55", "metadata": {}, "source": [ "#### Set Background" @@ -639,7 +624,7 @@ }, { "cell_type": "markdown", - "id": "57", + "id": "56", "metadata": {}, "source": [ "Show supported background types." @@ -648,7 +633,7 @@ { "cell_type": "code", "execution_count": null, - "id": "58", + "id": "57", "metadata": {}, "outputs": [], "source": [ @@ -657,7 +642,7 @@ }, { "cell_type": "markdown", - "id": "59", + "id": "58", "metadata": {}, "source": [ "Show current background type." @@ -666,7 +651,7 @@ { "cell_type": "code", "execution_count": null, - "id": "60", + "id": "59", "metadata": {}, "outputs": [], "source": [ @@ -675,7 +660,7 @@ }, { "cell_type": "markdown", - "id": "61", + "id": "60", "metadata": {}, "source": [ "Select the desired background type." @@ -684,7 +669,7 @@ { "cell_type": "code", "execution_count": null, - "id": "62", + "id": "61", "metadata": {}, "outputs": [], "source": [ @@ -693,7 +678,7 @@ }, { "cell_type": "markdown", - "id": "63", + "id": "62", "metadata": {}, "source": [ "Add background points." @@ -702,7 +687,7 @@ { "cell_type": "code", "execution_count": null, - "id": "64", + "id": "63", "metadata": {}, "outputs": [], "source": [ @@ -715,7 +700,7 @@ }, { "cell_type": "markdown", - "id": "65", + "id": "64", "metadata": {}, "source": [ "Show current background points." @@ -724,7 +709,7 @@ { "cell_type": "code", "execution_count": null, - "id": "66", + "id": "65", "metadata": {}, "outputs": [], "source": [ @@ -733,7 +718,7 @@ }, { "cell_type": "markdown", - "id": "67", + "id": "66", "metadata": {}, "source": [ "#### Set Linked Phases\n", @@ -744,7 +729,7 @@ { "cell_type": "code", "execution_count": null, - "id": "68", + "id": "67", "metadata": {}, "outputs": [], "source": [ @@ -753,7 +738,7 @@ }, { "cell_type": "markdown", - "id": "69", + "id": "68", "metadata": {}, "source": [ "#### Show Experiment as CIF" @@ -762,7 +747,7 @@ { "cell_type": "code", "execution_count": null, - "id": "70", + "id": "69", "metadata": {}, "outputs": [], "source": [ @@ -771,7 +756,7 @@ }, { "cell_type": "markdown", - "id": "71", + "id": "70", "metadata": {}, "source": [ "#### Save Project State" @@ -780,7 +765,7 @@ { "cell_type": "code", "execution_count": null, - "id": "72", + "id": "71", "metadata": {}, "outputs": [], "source": [ @@ -789,7 +774,7 @@ }, { "cell_type": "markdown", - "id": "73", + "id": "72", "metadata": {}, "source": [ "## Step 4: Perform Analysis\n", @@ -805,7 +790,7 @@ { "cell_type": "code", "execution_count": null, - "id": "74", + "id": "73", "metadata": {}, "outputs": [], "source": [ @@ -814,7 +799,7 @@ }, { "cell_type": "markdown", - "id": "75", + "id": "74", "metadata": {}, "source": [ "Show current calculation engine for this experiment." @@ -823,7 +808,7 @@ { "cell_type": "code", "execution_count": null, - "id": "76", + "id": "75", "metadata": {}, "outputs": [], "source": [ @@ -832,7 +817,7 @@ }, { "cell_type": "markdown", - "id": "77", + "id": "76", "metadata": {}, "source": [ "Select the desired calculation engine." @@ -841,7 +826,7 @@ { "cell_type": "code", "execution_count": null, - "id": "78", + "id": "77", "metadata": {}, "outputs": [], "source": [ @@ -850,7 +835,7 @@ }, { "cell_type": "markdown", - "id": "79", + "id": "78", "metadata": {}, "source": [ "#### Show Calculated Data" @@ -859,7 +844,7 @@ { "cell_type": "code", "execution_count": null, - "id": "80", + "id": "79", "metadata": {}, "outputs": [], "source": [ @@ -868,7 +853,7 @@ }, { "cell_type": "markdown", - "id": "81", + "id": "80", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -877,7 +862,7 @@ { "cell_type": "code", "execution_count": null, - "id": "82", + "id": "81", "metadata": {}, "outputs": [], "source": [ @@ -887,7 +872,7 @@ { "cell_type": "code", "execution_count": null, - "id": "83", + "id": "82", "metadata": {}, "outputs": [], "source": [ @@ -896,7 +881,7 @@ }, { "cell_type": "markdown", - "id": "84", + "id": "83", "metadata": {}, "source": [ "#### Show Parameters\n", @@ -907,7 +892,7 @@ { "cell_type": "code", "execution_count": null, - "id": "85", + "id": "84", "metadata": {}, "outputs": [], "source": [ @@ -916,7 +901,7 @@ }, { "cell_type": "markdown", - "id": "86", + "id": "85", "metadata": {}, "source": [ "Show all fittable parameters." @@ -925,7 +910,7 @@ { "cell_type": "code", "execution_count": null, - "id": "87", + "id": "86", "metadata": {}, "outputs": [], "source": [ @@ -934,7 +919,7 @@ }, { "cell_type": "markdown", - "id": "88", + "id": "87", "metadata": {}, "source": [ "Show only free parameters." @@ -943,7 +928,7 @@ { "cell_type": "code", "execution_count": null, - "id": "89", + "id": "88", "metadata": {}, "outputs": [], "source": [ @@ -952,7 +937,7 @@ }, { "cell_type": "markdown", - "id": "90", + "id": "89", "metadata": {}, "source": [ "Show how to access parameters in the code." @@ -961,7 +946,7 @@ { "cell_type": "code", "execution_count": null, - "id": "91", + "id": "90", "metadata": {}, "outputs": [], "source": [ @@ -970,7 +955,7 @@ }, { "cell_type": "markdown", - "id": "92", + "id": "91", "metadata": {}, "source": [ "#### Set Fit Mode\n", @@ -981,7 +966,7 @@ { "cell_type": "code", "execution_count": null, - "id": "93", + "id": "92", "metadata": {}, "outputs": [], "source": [ @@ -990,7 +975,7 @@ }, { "cell_type": "markdown", - "id": "94", + "id": "93", "metadata": {}, "source": [ "Show current fit mode." @@ -999,7 +984,7 @@ { "cell_type": "code", "execution_count": null, - "id": "95", + "id": "94", "metadata": {}, "outputs": [], "source": [ @@ -1008,7 +993,7 @@ }, { "cell_type": "markdown", - "id": "96", + "id": "95", "metadata": {}, "source": [ "Select desired fit mode." @@ -1017,7 +1002,7 @@ { "cell_type": "code", "execution_count": null, - "id": "97", + "id": "96", "metadata": {}, "outputs": [], "source": [ @@ -1026,7 +1011,7 @@ }, { "cell_type": "markdown", - "id": "98", + "id": "97", "metadata": {}, "source": [ "#### Set Minimizer\n", @@ -1037,7 +1022,7 @@ { "cell_type": "code", "execution_count": null, - "id": "99", + "id": "98", "metadata": {}, "outputs": [], "source": [ @@ -1046,7 +1031,7 @@ }, { "cell_type": "markdown", - "id": "100", + "id": "99", "metadata": {}, "source": [ "Show current fitting engine." @@ -1055,7 +1040,7 @@ { "cell_type": "code", "execution_count": null, - "id": "101", + "id": "100", "metadata": {}, "outputs": [], "source": [ @@ -1064,7 +1049,7 @@ }, { "cell_type": "markdown", - "id": "102", + "id": "101", "metadata": {}, "source": [ "Select desired fitting engine." @@ -1073,7 +1058,7 @@ { "cell_type": "code", "execution_count": null, - "id": "103", + "id": "102", "metadata": {}, "outputs": [], "source": [ @@ -1082,7 +1067,7 @@ }, { "cell_type": "markdown", - "id": "104", + "id": "103", "metadata": {}, "source": [ "### Perform Fit 1/5\n", @@ -1093,7 +1078,7 @@ { "cell_type": "code", "execution_count": null, - "id": "105", + "id": "104", "metadata": {}, "outputs": [], "source": [ @@ -1102,7 +1087,7 @@ }, { "cell_type": "markdown", - "id": "106", + "id": "105", "metadata": {}, "source": [ "Set experiment parameters to be refined." @@ -1111,7 +1096,7 @@ { "cell_type": "code", "execution_count": null, - "id": "107", + "id": "106", "metadata": {}, "outputs": [], "source": [ @@ -1126,7 +1111,7 @@ }, { "cell_type": "markdown", - "id": "108", + "id": "107", "metadata": {}, "source": [ "Show free parameters after selection." @@ -1135,7 +1120,7 @@ { "cell_type": "code", "execution_count": null, - "id": "109", + "id": "108", "metadata": {}, "outputs": [], "source": [ @@ -1144,7 +1129,7 @@ }, { "cell_type": "markdown", - "id": "110", + "id": "109", "metadata": {}, "source": [ "#### Run Fitting" @@ -1153,7 +1138,7 @@ { "cell_type": "code", "execution_count": null, - "id": "111", + "id": "110", "metadata": {}, "outputs": [], "source": [ @@ -1163,7 +1148,7 @@ }, { "cell_type": "markdown", - "id": "112", + "id": "111", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -1172,7 +1157,7 @@ { "cell_type": "code", "execution_count": null, - "id": "113", + "id": "112", "metadata": {}, "outputs": [], "source": [ @@ -1182,7 +1167,7 @@ { "cell_type": "code", "execution_count": null, - "id": "114", + "id": "113", "metadata": {}, "outputs": [], "source": [ @@ -1191,7 +1176,7 @@ }, { "cell_type": "markdown", - "id": "115", + "id": "114", "metadata": {}, "source": [ "#### Save Project State" @@ -1200,7 +1185,7 @@ { "cell_type": "code", "execution_count": null, - "id": "116", + "id": "115", "metadata": {}, "outputs": [], "source": [ @@ -1209,7 +1194,7 @@ }, { "cell_type": "markdown", - "id": "117", + "id": "116", "metadata": {}, "source": [ "### Perform Fit 2/5\n", @@ -1220,7 +1205,7 @@ { "cell_type": "code", "execution_count": null, - "id": "118", + "id": "117", "metadata": {}, "outputs": [], "source": [ @@ -1232,7 +1217,7 @@ }, { "cell_type": "markdown", - "id": "119", + "id": "118", "metadata": {}, "source": [ "Show free parameters after selection." @@ -1241,7 +1226,7 @@ { "cell_type": "code", "execution_count": null, - "id": "120", + "id": "119", "metadata": {}, "outputs": [], "source": [ @@ -1250,7 +1235,7 @@ }, { "cell_type": "markdown", - "id": "121", + "id": "120", "metadata": {}, "source": [ "#### Run Fitting" @@ -1259,7 +1244,7 @@ { "cell_type": "code", "execution_count": null, - "id": "122", + "id": "121", "metadata": {}, "outputs": [], "source": [ @@ -1269,7 +1254,7 @@ }, { "cell_type": "markdown", - "id": "123", + "id": "122", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -1278,7 +1263,7 @@ { "cell_type": "code", "execution_count": null, - "id": "124", + "id": "123", "metadata": {}, "outputs": [], "source": [ @@ -1288,7 +1273,7 @@ { "cell_type": "code", "execution_count": null, - "id": "125", + "id": "124", "metadata": {}, "outputs": [], "source": [ @@ -1297,7 +1282,7 @@ }, { "cell_type": "markdown", - "id": "126", + "id": "125", "metadata": {}, "source": [ "#### Save Project State" @@ -1306,7 +1291,7 @@ { "cell_type": "code", "execution_count": null, - "id": "127", + "id": "126", "metadata": {}, "outputs": [], "source": [ @@ -1315,7 +1300,7 @@ }, { "cell_type": "markdown", - "id": "128", + "id": "127", "metadata": {}, "source": [ "### Perform Fit 3/5\n", @@ -1326,7 +1311,7 @@ { "cell_type": "code", "execution_count": null, - "id": "129", + "id": "128", "metadata": {}, "outputs": [], "source": [ @@ -1338,7 +1323,7 @@ }, { "cell_type": "markdown", - "id": "130", + "id": "129", "metadata": {}, "source": [ "Show free parameters after selection." @@ -1347,7 +1332,7 @@ { "cell_type": "code", "execution_count": null, - "id": "131", + "id": "130", "metadata": {}, "outputs": [], "source": [ @@ -1356,7 +1341,7 @@ }, { "cell_type": "markdown", - "id": "132", + "id": "131", "metadata": {}, "source": [ "#### Run Fitting" @@ -1365,7 +1350,7 @@ { "cell_type": "code", "execution_count": null, - "id": "133", + "id": "132", "metadata": {}, "outputs": [], "source": [ @@ -1375,7 +1360,7 @@ }, { "cell_type": "markdown", - "id": "134", + "id": "133", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -1384,7 +1369,7 @@ { "cell_type": "code", "execution_count": null, - "id": "135", + "id": "134", "metadata": {}, "outputs": [], "source": [ @@ -1394,7 +1379,7 @@ { "cell_type": "code", "execution_count": null, - "id": "136", + "id": "135", "metadata": {}, "outputs": [], "source": [ @@ -1403,7 +1388,7 @@ }, { "cell_type": "markdown", - "id": "137", + "id": "136", "metadata": {}, "source": [ "#### Save Project State" @@ -1412,7 +1397,7 @@ { "cell_type": "code", "execution_count": null, - "id": "138", + "id": "137", "metadata": {}, "outputs": [], "source": [ @@ -1421,7 +1406,7 @@ }, { "cell_type": "markdown", - "id": "139", + "id": "138", "metadata": {}, "source": [ "### Perform Fit 4/5\n", @@ -1434,7 +1419,7 @@ { "cell_type": "code", "execution_count": null, - "id": "140", + "id": "139", "metadata": {}, "outputs": [], "source": [ @@ -1450,7 +1435,7 @@ }, { "cell_type": "markdown", - "id": "141", + "id": "140", "metadata": {}, "source": [ "Set constraints." @@ -1459,7 +1444,7 @@ { "cell_type": "code", "execution_count": null, - "id": "142", + "id": "141", "metadata": {}, "outputs": [], "source": [ @@ -1468,7 +1453,7 @@ }, { "cell_type": "markdown", - "id": "143", + "id": "142", "metadata": {}, "source": [ "Show defined constraints." @@ -1477,7 +1462,7 @@ { "cell_type": "code", "execution_count": null, - "id": "144", + "id": "143", "metadata": {}, "outputs": [], "source": [ @@ -1486,7 +1471,7 @@ }, { "cell_type": "markdown", - "id": "145", + "id": "144", "metadata": {}, "source": [ "Show free parameters." @@ -1495,7 +1480,7 @@ { "cell_type": "code", "execution_count": null, - "id": "146", + "id": "145", "metadata": {}, "outputs": [], "source": [ @@ -1504,7 +1489,7 @@ }, { "cell_type": "markdown", - "id": "147", + "id": "146", "metadata": {}, "source": [ "#### Run Fitting" @@ -1513,7 +1498,7 @@ { "cell_type": "code", "execution_count": null, - "id": "148", + "id": "147", "metadata": {}, "outputs": [], "source": [ @@ -1523,7 +1508,7 @@ }, { "cell_type": "markdown", - "id": "149", + "id": "148", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -1532,7 +1517,7 @@ { "cell_type": "code", "execution_count": null, - "id": "150", + "id": "149", "metadata": {}, "outputs": [], "source": [ @@ -1542,7 +1527,7 @@ { "cell_type": "code", "execution_count": null, - "id": "151", + "id": "150", "metadata": {}, "outputs": [], "source": [ @@ -1551,7 +1536,7 @@ }, { "cell_type": "markdown", - "id": "152", + "id": "151", "metadata": {}, "source": [ "#### Save Project State" @@ -1560,7 +1545,7 @@ { "cell_type": "code", "execution_count": null, - "id": "153", + "id": "152", "metadata": {}, "outputs": [], "source": [ @@ -1569,7 +1554,7 @@ }, { "cell_type": "markdown", - "id": "154", + "id": "153", "metadata": {}, "source": [ "### Perform Fit 5/5\n", @@ -1582,7 +1567,7 @@ { "cell_type": "code", "execution_count": null, - "id": "155", + "id": "154", "metadata": {}, "outputs": [], "source": [ @@ -1598,7 +1583,7 @@ }, { "cell_type": "markdown", - "id": "156", + "id": "155", "metadata": {}, "source": [ "Set more constraints." @@ -1607,7 +1592,7 @@ { "cell_type": "code", "execution_count": null, - "id": "157", + "id": "156", "metadata": {}, "outputs": [], "source": [ @@ -1618,7 +1603,7 @@ }, { "cell_type": "markdown", - "id": "158", + "id": "157", "metadata": {}, "source": [ "Show defined constraints." @@ -1627,7 +1612,7 @@ { "cell_type": "code", "execution_count": null, - "id": "159", + "id": "158", "metadata": { "lines_to_next_cell": 2 }, @@ -1638,7 +1623,7 @@ }, { "cell_type": "markdown", - "id": "160", + "id": "159", "metadata": {}, "source": [ "Set structure parameters to be refined." @@ -1647,7 +1632,7 @@ { "cell_type": "code", "execution_count": null, - "id": "161", + "id": "160", "metadata": {}, "outputs": [], "source": [ @@ -1656,7 +1641,7 @@ }, { "cell_type": "markdown", - "id": "162", + "id": "161", "metadata": {}, "source": [ "Show free parameters after selection." @@ -1665,7 +1650,7 @@ { "cell_type": "code", "execution_count": null, - "id": "163", + "id": "162", "metadata": {}, "outputs": [], "source": [ @@ -1674,7 +1659,7 @@ }, { "cell_type": "markdown", - "id": "164", + "id": "163", "metadata": {}, "source": [ "#### Run Fitting" @@ -1683,7 +1668,7 @@ { "cell_type": "code", "execution_count": null, - "id": "165", + "id": "164", "metadata": {}, "outputs": [], "source": [ @@ -1693,7 +1678,7 @@ }, { "cell_type": "markdown", - "id": "166", + "id": "165", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -1702,7 +1687,7 @@ { "cell_type": "code", "execution_count": null, - "id": "167", + "id": "166", "metadata": {}, "outputs": [], "source": [ @@ -1712,7 +1697,7 @@ { "cell_type": "code", "execution_count": null, - "id": "168", + "id": "167", "metadata": {}, "outputs": [], "source": [ @@ -1721,7 +1706,7 @@ }, { "cell_type": "markdown", - "id": "169", + "id": "168", "metadata": {}, "source": [ "#### Save Project State" @@ -1730,7 +1715,7 @@ { "cell_type": "code", "execution_count": null, - "id": "170", + "id": "169", "metadata": {}, "outputs": [], "source": [ @@ -1739,7 +1724,7 @@ }, { "cell_type": "markdown", - "id": "171", + "id": "170", "metadata": {}, "source": [ "## Step 5: Summary\n", @@ -1749,7 +1734,7 @@ }, { "cell_type": "markdown", - "id": "172", + "id": "171", "metadata": {}, "source": [ "#### Show Project Summary" @@ -1758,7 +1743,7 @@ { "cell_type": "code", "execution_count": null, - "id": "173", + "id": "172", "metadata": {}, "outputs": [], "source": [ @@ -1768,7 +1753,7 @@ { "cell_type": "code", "execution_count": null, - "id": "174", + "id": "173", "metadata": {}, "outputs": [], "source": [] diff --git a/docs/docs/tutorials/ed-4.ipynb b/docs/docs/tutorials/ed-4.ipynb index 5bba8e84..fe06bd1e 100644 --- a/docs/docs/tutorials/ed-4.ipynb +++ b/docs/docs/tutorials/ed-4.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "0e6027e8", + "id": "16833253", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Structure Refinement: PbSO4, NPD + XRD\n", @@ -54,7 +39,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -63,7 +48,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -75,7 +60,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Define Structure\n", @@ -89,7 +74,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -98,7 +83,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "#### Set Space Group" @@ -107,7 +92,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -116,7 +101,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "7", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -125,7 +110,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -136,7 +121,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -145,7 +130,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": { "lines_to_next_cell": 2 }, @@ -200,7 +185,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "## Define Experiments\n", @@ -216,7 +201,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -225,7 +210,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "13", "metadata": {}, "source": [ "#### Create Experiment" @@ -234,7 +219,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -247,7 +232,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "15", "metadata": {}, "source": [ "#### Set Instrument" @@ -256,7 +241,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -266,7 +251,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -275,7 +260,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -288,7 +273,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "19", "metadata": {}, "source": [ "#### Set Background" @@ -296,7 +281,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "20", "metadata": {}, "source": [ "Select the background type." @@ -305,7 +290,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -314,7 +299,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "22", "metadata": {}, "source": [ "Add background points." @@ -323,7 +308,7 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -342,7 +327,7 @@ }, { "cell_type": "markdown", - "id": "25", + "id": "24", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -351,7 +336,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -360,7 +345,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "26", "metadata": {}, "source": [ "### Experiment 2: xrd\n", @@ -371,7 +356,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -380,7 +365,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "28", "metadata": {}, "source": [ "#### Create Experiment" @@ -389,7 +374,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -402,7 +387,7 @@ }, { "cell_type": "markdown", - "id": "31", + "id": "30", "metadata": {}, "source": [ "#### Set Instrument" @@ -411,7 +396,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -421,7 +406,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "32", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -430,7 +415,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -443,7 +428,7 @@ }, { "cell_type": "markdown", - "id": "35", + "id": "34", "metadata": {}, "source": [ "#### Set Background" @@ -451,7 +436,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "35", "metadata": {}, "source": [ "Select background type." @@ -460,7 +445,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37", + "id": "36", "metadata": {}, "outputs": [], "source": [ @@ -469,7 +454,7 @@ }, { "cell_type": "markdown", - "id": "38", + "id": "37", "metadata": {}, "source": [ "Add background points." @@ -478,7 +463,7 @@ { "cell_type": "code", "execution_count": null, - "id": "39", + "id": "38", "metadata": {}, "outputs": [], "source": [ @@ -495,7 +480,7 @@ }, { "cell_type": "markdown", - "id": "40", + "id": "39", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -504,7 +489,7 @@ { "cell_type": "code", "execution_count": null, - "id": "41", + "id": "40", "metadata": {}, "outputs": [], "source": [ @@ -513,7 +498,7 @@ }, { "cell_type": "markdown", - "id": "42", + "id": "41", "metadata": {}, "source": [ "## Define Project\n", @@ -527,7 +512,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "42", "metadata": {}, "outputs": [], "source": [ @@ -536,7 +521,7 @@ }, { "cell_type": "markdown", - "id": "44", + "id": "43", "metadata": {}, "source": [ "#### Add Structure" @@ -545,7 +530,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "44", "metadata": {}, "outputs": [], "source": [ @@ -554,7 +539,7 @@ }, { "cell_type": "markdown", - "id": "46", + "id": "45", "metadata": {}, "source": [ "#### Add Experiments" @@ -563,7 +548,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "46", "metadata": {}, "outputs": [], "source": [ @@ -573,7 +558,7 @@ }, { "cell_type": "markdown", - "id": "48", + "id": "47", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -587,7 +572,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "48", "metadata": {}, "outputs": [], "source": [ @@ -596,7 +581,7 @@ }, { "cell_type": "markdown", - "id": "50", + "id": "49", "metadata": {}, "source": [ "#### Set Minimizer" @@ -605,7 +590,7 @@ { "cell_type": "code", "execution_count": null, - "id": "51", + "id": "50", "metadata": {}, "outputs": [], "source": [ @@ -614,7 +599,7 @@ }, { "cell_type": "markdown", - "id": "52", + "id": "51", "metadata": {}, "source": [ "#### Set Fitting Parameters\n", @@ -625,7 +610,7 @@ { "cell_type": "code", "execution_count": null, - "id": "53", + "id": "52", "metadata": {}, "outputs": [], "source": [ @@ -636,7 +621,7 @@ }, { "cell_type": "markdown", - "id": "54", + "id": "53", "metadata": {}, "source": [ "Set experiment parameters to be optimized." @@ -645,7 +630,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "54", "metadata": {}, "outputs": [], "source": [ @@ -662,7 +647,7 @@ { "cell_type": "code", "execution_count": null, - "id": "56", + "id": "55", "metadata": {}, "outputs": [], "source": [ @@ -681,7 +666,7 @@ }, { "cell_type": "markdown", - "id": "57", + "id": "56", "metadata": {}, "source": [ "#### Perform Fit" @@ -690,7 +675,7 @@ { "cell_type": "code", "execution_count": null, - "id": "58", + "id": "57", "metadata": {}, "outputs": [], "source": [ @@ -700,7 +685,7 @@ }, { "cell_type": "markdown", - "id": "59", + "id": "58", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -709,7 +694,7 @@ { "cell_type": "code", "execution_count": null, - "id": "60", + "id": "59", "metadata": {}, "outputs": [], "source": [ @@ -719,7 +704,7 @@ { "cell_type": "code", "execution_count": null, - "id": "61", + "id": "60", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-5.ipynb b/docs/docs/tutorials/ed-5.ipynb index ef7e672a..f94b7564 100644 --- a/docs/docs/tutorials/ed-5.ipynb +++ b/docs/docs/tutorials/ed-5.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "1302dcf1", + "id": "a3281949", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Structure Refinement: Co2SiO4, D20\n", @@ -48,7 +33,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -57,7 +42,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -69,7 +54,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Define Structure\n", @@ -83,7 +68,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -92,7 +77,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "#### Set Space Group" @@ -101,7 +86,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -111,7 +96,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "7", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -120,7 +105,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -131,7 +116,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -140,7 +125,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -202,7 +187,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "## Define Experiment\n", @@ -216,7 +201,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -225,7 +210,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "13", "metadata": {}, "source": [ "#### Create Experiment" @@ -234,7 +219,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -243,7 +228,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "15", "metadata": {}, "source": [ "#### Set Instrument" @@ -252,7 +237,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -262,7 +247,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -271,7 +256,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -282,7 +267,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "19", "metadata": {}, "source": [ "#### Set Background" @@ -291,7 +276,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -313,7 +298,7 @@ }, { "cell_type": "markdown", - "id": "22", + "id": "21", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -322,7 +307,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -331,7 +316,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "23", "metadata": {}, "source": [ "## Define Project\n", @@ -345,7 +330,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -354,7 +339,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "25", "metadata": {}, "source": [ "#### Set Plotting Engine" @@ -363,7 +348,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -374,7 +359,7 @@ }, { "cell_type": "markdown", - "id": "28", + "id": "27", "metadata": {}, "source": [ "#### Add Structure" @@ -383,7 +368,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -392,7 +377,7 @@ }, { "cell_type": "markdown", - "id": "30", + "id": "29", "metadata": {}, "source": [ "#### Add Experiment" @@ -401,7 +386,7 @@ { "cell_type": "code", "execution_count": null, - "id": "31", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -410,7 +395,7 @@ }, { "cell_type": "markdown", - "id": "32", + "id": "31", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -424,7 +409,7 @@ { "cell_type": "code", "execution_count": null, - "id": "33", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -433,7 +418,7 @@ }, { "cell_type": "markdown", - "id": "34", + "id": "33", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -442,7 +427,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "34", "metadata": {}, "outputs": [], "source": [ @@ -452,7 +437,7 @@ { "cell_type": "code", "execution_count": null, - "id": "36", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -461,7 +446,7 @@ }, { "cell_type": "markdown", - "id": "37", + "id": "36", "metadata": {}, "source": [ "#### Set Free Parameters" @@ -470,7 +455,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "37", "metadata": {}, "outputs": [], "source": [ @@ -501,7 +486,7 @@ { "cell_type": "code", "execution_count": null, - "id": "39", + "id": "38", "metadata": {}, "outputs": [], "source": [ @@ -520,7 +505,7 @@ }, { "cell_type": "markdown", - "id": "40", + "id": "39", "metadata": {}, "source": [ "#### Set Constraints\n", @@ -531,7 +516,7 @@ { "cell_type": "code", "execution_count": null, - "id": "41", + "id": "40", "metadata": {}, "outputs": [], "source": [ @@ -547,7 +532,7 @@ }, { "cell_type": "markdown", - "id": "42", + "id": "41", "metadata": {}, "source": [ "Set constraints." @@ -556,7 +541,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "42", "metadata": { "lines_to_next_cell": 2 }, @@ -569,7 +554,7 @@ }, { "cell_type": "markdown", - "id": "44", + "id": "43", "metadata": {}, "source": [ "#### Run Fitting" @@ -578,7 +563,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "44", "metadata": {}, "outputs": [], "source": [ @@ -588,7 +573,7 @@ }, { "cell_type": "markdown", - "id": "46", + "id": "45", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -597,7 +582,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "46", "metadata": {}, "outputs": [], "source": [ @@ -607,7 +592,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "47", "metadata": {}, "outputs": [], "source": [ @@ -616,7 +601,7 @@ }, { "cell_type": "markdown", - "id": "49", + "id": "48", "metadata": {}, "source": [ "## Summary\n", @@ -626,7 +611,7 @@ }, { "cell_type": "markdown", - "id": "50", + "id": "49", "metadata": {}, "source": [ "#### Show Project Summary" @@ -635,7 +620,7 @@ { "cell_type": "code", "execution_count": null, - "id": "51", + "id": "50", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-6.ipynb b/docs/docs/tutorials/ed-6.ipynb index c4e2d34d..92130f6b 100644 --- a/docs/docs/tutorials/ed-6.ipynb +++ b/docs/docs/tutorials/ed-6.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a1e678e6", + "id": "48d300a4", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Structure Refinement: HS, HRPT\n", @@ -48,7 +33,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -57,7 +42,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -69,7 +54,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Define Structure\n", @@ -83,7 +68,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -92,7 +77,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "#### Set Space Group" @@ -101,7 +86,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -111,7 +96,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "7", "metadata": { "lines_to_next_cell": 2 }, @@ -122,7 +107,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -132,7 +117,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -141,7 +126,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -194,7 +179,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "## Define Experiment\n", @@ -208,7 +193,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -217,7 +202,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "13", "metadata": {}, "source": [ "#### Create Experiment" @@ -226,7 +211,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -235,7 +220,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "15", "metadata": {}, "source": [ "#### Set Instrument" @@ -244,7 +229,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -254,7 +239,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -263,7 +248,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -276,7 +261,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "19", "metadata": {}, "source": [ "#### Set Background" @@ -285,7 +270,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -302,7 +287,7 @@ }, { "cell_type": "markdown", - "id": "22", + "id": "21", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -311,7 +296,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -320,7 +305,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "23", "metadata": {}, "source": [ "## Define Project\n", @@ -334,7 +319,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -343,7 +328,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "25", "metadata": {}, "source": [ "#### Set Plotting Engine" @@ -352,7 +337,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -363,7 +348,7 @@ }, { "cell_type": "markdown", - "id": "28", + "id": "27", "metadata": {}, "source": [ "#### Add Structure" @@ -372,7 +357,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -381,7 +366,7 @@ }, { "cell_type": "markdown", - "id": "30", + "id": "29", "metadata": {}, "source": [ "#### Add Experiment" @@ -390,7 +375,7 @@ { "cell_type": "code", "execution_count": null, - "id": "31", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -399,7 +384,7 @@ }, { "cell_type": "markdown", - "id": "32", + "id": "31", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -413,7 +398,7 @@ { "cell_type": "code", "execution_count": null, - "id": "33", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -422,7 +407,7 @@ }, { "cell_type": "markdown", - "id": "34", + "id": "33", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -431,7 +416,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "34", "metadata": {}, "outputs": [], "source": [ @@ -441,7 +426,7 @@ { "cell_type": "code", "execution_count": null, - "id": "36", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -450,7 +435,7 @@ }, { "cell_type": "markdown", - "id": "37", + "id": "36", "metadata": {}, "source": [ "### Perform Fit 1/5\n", @@ -461,7 +446,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "37", "metadata": {}, "outputs": [], "source": [ @@ -474,7 +459,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "38", "metadata": {}, "source": [ "Show free parameters after selection." @@ -483,7 +468,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "39", "metadata": {}, "outputs": [], "source": [ @@ -492,7 +477,7 @@ }, { "cell_type": "markdown", - "id": "41", + "id": "40", "metadata": {}, "source": [ "#### Run Fitting" @@ -501,7 +486,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -511,7 +496,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "42", "metadata": {}, "outputs": [], "source": [ @@ -520,7 +505,7 @@ }, { "cell_type": "markdown", - "id": "44", + "id": "43", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -529,7 +514,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "44", "metadata": {}, "outputs": [], "source": [ @@ -539,7 +524,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -548,7 +533,7 @@ }, { "cell_type": "markdown", - "id": "47", + "id": "46", "metadata": {}, "source": [ "### Perform Fit 2/5\n", @@ -559,7 +544,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "47", "metadata": {}, "outputs": [], "source": [ @@ -574,7 +559,7 @@ }, { "cell_type": "markdown", - "id": "49", + "id": "48", "metadata": {}, "source": [ "Show free parameters after selection." @@ -583,7 +568,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50", + "id": "49", "metadata": {}, "outputs": [], "source": [ @@ -592,7 +577,7 @@ }, { "cell_type": "markdown", - "id": "51", + "id": "50", "metadata": {}, "source": [ "#### Run Fitting" @@ -601,7 +586,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "51", "metadata": {}, "outputs": [], "source": [ @@ -611,7 +596,7 @@ { "cell_type": "code", "execution_count": null, - "id": "53", + "id": "52", "metadata": {}, "outputs": [], "source": [ @@ -620,7 +605,7 @@ }, { "cell_type": "markdown", - "id": "54", + "id": "53", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -629,7 +614,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "54", "metadata": {}, "outputs": [], "source": [ @@ -639,7 +624,7 @@ { "cell_type": "code", "execution_count": null, - "id": "56", + "id": "55", "metadata": {}, "outputs": [], "source": [ @@ -648,7 +633,7 @@ }, { "cell_type": "markdown", - "id": "57", + "id": "56", "metadata": {}, "source": [ "### Perform Fit 3/5\n", @@ -659,7 +644,7 @@ { "cell_type": "code", "execution_count": null, - "id": "58", + "id": "57", "metadata": {}, "outputs": [], "source": [ @@ -672,7 +657,7 @@ }, { "cell_type": "markdown", - "id": "59", + "id": "58", "metadata": {}, "source": [ "Show free parameters after selection." @@ -681,7 +666,7 @@ { "cell_type": "code", "execution_count": null, - "id": "60", + "id": "59", "metadata": {}, "outputs": [], "source": [ @@ -690,7 +675,7 @@ }, { "cell_type": "markdown", - "id": "61", + "id": "60", "metadata": {}, "source": [ "#### Run Fitting" @@ -699,7 +684,7 @@ { "cell_type": "code", "execution_count": null, - "id": "62", + "id": "61", "metadata": {}, "outputs": [], "source": [ @@ -709,7 +694,7 @@ { "cell_type": "code", "execution_count": null, - "id": "63", + "id": "62", "metadata": {}, "outputs": [], "source": [ @@ -718,7 +703,7 @@ }, { "cell_type": "markdown", - "id": "64", + "id": "63", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -727,7 +712,7 @@ { "cell_type": "code", "execution_count": null, - "id": "65", + "id": "64", "metadata": {}, "outputs": [], "source": [ @@ -737,7 +722,7 @@ { "cell_type": "code", "execution_count": null, - "id": "66", + "id": "65", "metadata": {}, "outputs": [], "source": [ @@ -746,7 +731,7 @@ }, { "cell_type": "markdown", - "id": "67", + "id": "66", "metadata": {}, "source": [ "### Perform Fit 4/5\n", @@ -757,7 +742,7 @@ { "cell_type": "code", "execution_count": null, - "id": "68", + "id": "67", "metadata": {}, "outputs": [], "source": [ @@ -770,7 +755,7 @@ }, { "cell_type": "markdown", - "id": "69", + "id": "68", "metadata": {}, "source": [ "Show free parameters after selection." @@ -779,7 +764,7 @@ { "cell_type": "code", "execution_count": null, - "id": "70", + "id": "69", "metadata": {}, "outputs": [], "source": [ @@ -788,7 +773,7 @@ }, { "cell_type": "markdown", - "id": "71", + "id": "70", "metadata": {}, "source": [ "#### Run Fitting" @@ -797,7 +782,7 @@ { "cell_type": "code", "execution_count": null, - "id": "72", + "id": "71", "metadata": {}, "outputs": [], "source": [ @@ -807,7 +792,7 @@ { "cell_type": "code", "execution_count": null, - "id": "73", + "id": "72", "metadata": {}, "outputs": [], "source": [ @@ -816,7 +801,7 @@ }, { "cell_type": "markdown", - "id": "74", + "id": "73", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -825,7 +810,7 @@ { "cell_type": "code", "execution_count": null, - "id": "75", + "id": "74", "metadata": {}, "outputs": [], "source": [ @@ -835,7 +820,7 @@ { "cell_type": "code", "execution_count": null, - "id": "76", + "id": "75", "metadata": {}, "outputs": [], "source": [ @@ -844,7 +829,7 @@ }, { "cell_type": "markdown", - "id": "77", + "id": "76", "metadata": {}, "source": [ "## Summary\n", @@ -854,7 +839,7 @@ }, { "cell_type": "markdown", - "id": "78", + "id": "77", "metadata": {}, "source": [ "#### Show Project Summary" @@ -863,7 +848,7 @@ { "cell_type": "code", "execution_count": null, - "id": "79", + "id": "78", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-7.ipynb b/docs/docs/tutorials/ed-7.ipynb index 7284f08d..12ad852a 100644 --- a/docs/docs/tutorials/ed-7.ipynb +++ b/docs/docs/tutorials/ed-7.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5dd7adf5", + "id": "8cc5d312", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Structure Refinement: Si, SEPD\n", @@ -48,7 +33,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -57,7 +42,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -69,7 +54,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Define Structure\n", @@ -83,7 +68,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -92,7 +77,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "#### Set Space Group" @@ -101,7 +86,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -111,7 +96,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "7", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -120,7 +105,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -129,7 +114,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -138,7 +123,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -154,7 +139,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "## Define Experiment\n", @@ -168,7 +153,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -177,7 +162,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "13", "metadata": {}, "source": [ "#### Create Experiment" @@ -186,7 +171,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -197,7 +182,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "15", "metadata": {}, "source": [ "#### Set Instrument" @@ -206,7 +191,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -218,7 +203,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -227,7 +212,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -241,7 +226,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "19", "metadata": {}, "source": [ "#### Set Peak Asymmetry" @@ -250,7 +235,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -260,7 +245,7 @@ }, { "cell_type": "markdown", - "id": "22", + "id": "21", "metadata": {}, "source": [ "#### Set Background" @@ -269,7 +254,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -280,7 +265,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "23", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -289,7 +274,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -298,7 +283,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "25", "metadata": {}, "source": [ "## Define Project\n", @@ -312,7 +297,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -321,7 +306,7 @@ }, { "cell_type": "markdown", - "id": "28", + "id": "27", "metadata": {}, "source": [ "#### Add Structure" @@ -330,7 +315,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -339,7 +324,7 @@ }, { "cell_type": "markdown", - "id": "30", + "id": "29", "metadata": {}, "source": [ "#### Add Experiment" @@ -348,7 +333,7 @@ { "cell_type": "code", "execution_count": null, - "id": "31", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -357,7 +342,7 @@ }, { "cell_type": "markdown", - "id": "32", + "id": "31", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -371,7 +356,7 @@ { "cell_type": "code", "execution_count": null, - "id": "33", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -380,7 +365,7 @@ }, { "cell_type": "markdown", - "id": "34", + "id": "33", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -389,7 +374,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "34", "metadata": {}, "outputs": [], "source": [ @@ -399,7 +384,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "35", "metadata": {}, "source": [ "### Perform Fit 1/5\n", @@ -410,7 +395,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37", + "id": "36", "metadata": {}, "outputs": [], "source": [ @@ -422,7 +407,7 @@ }, { "cell_type": "markdown", - "id": "38", + "id": "37", "metadata": {}, "source": [ "Show free parameters after selection." @@ -431,7 +416,7 @@ { "cell_type": "code", "execution_count": null, - "id": "39", + "id": "38", "metadata": {}, "outputs": [], "source": [ @@ -440,7 +425,7 @@ }, { "cell_type": "markdown", - "id": "40", + "id": "39", "metadata": {}, "source": [ "#### Run Fitting" @@ -449,7 +434,7 @@ { "cell_type": "code", "execution_count": null, - "id": "41", + "id": "40", "metadata": {}, "outputs": [], "source": [ @@ -459,7 +444,7 @@ }, { "cell_type": "markdown", - "id": "42", + "id": "41", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -468,7 +453,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "42", "metadata": {}, "outputs": [], "source": [ @@ -478,7 +463,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -487,7 +472,7 @@ }, { "cell_type": "markdown", - "id": "45", + "id": "44", "metadata": {}, "source": [ "### Perform Fit 2/5\n", @@ -498,7 +483,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -508,7 +493,7 @@ }, { "cell_type": "markdown", - "id": "47", + "id": "46", "metadata": {}, "source": [ "Show free parameters after selection." @@ -517,7 +502,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "47", "metadata": {}, "outputs": [], "source": [ @@ -526,7 +511,7 @@ }, { "cell_type": "markdown", - "id": "49", + "id": "48", "metadata": {}, "source": [ "#### Run Fitting" @@ -535,7 +520,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50", + "id": "49", "metadata": {}, "outputs": [], "source": [ @@ -545,7 +530,7 @@ }, { "cell_type": "markdown", - "id": "51", + "id": "50", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -554,7 +539,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "51", "metadata": {}, "outputs": [], "source": [ @@ -564,7 +549,7 @@ { "cell_type": "code", "execution_count": null, - "id": "53", + "id": "52", "metadata": {}, "outputs": [], "source": [ @@ -573,7 +558,7 @@ }, { "cell_type": "markdown", - "id": "54", + "id": "53", "metadata": {}, "source": [ "### Perform Fit 3/5\n", @@ -584,7 +569,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "54", "metadata": {}, "outputs": [], "source": [ @@ -594,7 +579,7 @@ }, { "cell_type": "markdown", - "id": "56", + "id": "55", "metadata": {}, "source": [ "Set more parameters to be refined." @@ -603,7 +588,7 @@ { "cell_type": "code", "execution_count": null, - "id": "57", + "id": "56", "metadata": {}, "outputs": [], "source": [ @@ -614,7 +599,7 @@ }, { "cell_type": "markdown", - "id": "58", + "id": "57", "metadata": {}, "source": [ "Show free parameters after selection." @@ -623,7 +608,7 @@ { "cell_type": "code", "execution_count": null, - "id": "59", + "id": "58", "metadata": {}, "outputs": [], "source": [ @@ -632,7 +617,7 @@ }, { "cell_type": "markdown", - "id": "60", + "id": "59", "metadata": {}, "source": [ "#### Run Fitting" @@ -641,7 +626,7 @@ { "cell_type": "code", "execution_count": null, - "id": "61", + "id": "60", "metadata": {}, "outputs": [], "source": [ @@ -651,7 +636,7 @@ }, { "cell_type": "markdown", - "id": "62", + "id": "61", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -660,7 +645,7 @@ { "cell_type": "code", "execution_count": null, - "id": "63", + "id": "62", "metadata": {}, "outputs": [], "source": [ @@ -670,7 +655,7 @@ { "cell_type": "code", "execution_count": null, - "id": "64", + "id": "63", "metadata": {}, "outputs": [], "source": [ @@ -679,7 +664,7 @@ }, { "cell_type": "markdown", - "id": "65", + "id": "64", "metadata": {}, "source": [ "### Perform Fit 4/5\n", @@ -690,7 +675,7 @@ { "cell_type": "code", "execution_count": null, - "id": "66", + "id": "65", "metadata": {}, "outputs": [], "source": [ @@ -699,7 +684,7 @@ }, { "cell_type": "markdown", - "id": "67", + "id": "66", "metadata": {}, "source": [ "Show free parameters after selection." @@ -708,7 +693,7 @@ { "cell_type": "code", "execution_count": null, - "id": "68", + "id": "67", "metadata": {}, "outputs": [], "source": [ @@ -717,7 +702,7 @@ }, { "cell_type": "markdown", - "id": "69", + "id": "68", "metadata": {}, "source": [ "#### Run Fitting" @@ -726,7 +711,7 @@ { "cell_type": "code", "execution_count": null, - "id": "70", + "id": "69", "metadata": {}, "outputs": [], "source": [ @@ -736,7 +721,7 @@ }, { "cell_type": "markdown", - "id": "71", + "id": "70", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -745,7 +730,7 @@ { "cell_type": "code", "execution_count": null, - "id": "72", + "id": "71", "metadata": {}, "outputs": [], "source": [ @@ -755,7 +740,7 @@ { "cell_type": "code", "execution_count": null, - "id": "73", + "id": "72", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-8.ipynb b/docs/docs/tutorials/ed-8.ipynb index 79e03966..80aec4e4 100644 --- a/docs/docs/tutorials/ed-8.ipynb +++ b/docs/docs/tutorials/ed-8.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f45eca14", + "id": "0bc22f40", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Structure Refinement: NCAF, WISH\n", @@ -51,7 +36,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -60,7 +45,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -72,7 +57,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Define Structure\n", @@ -86,7 +71,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -95,7 +80,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "#### Set Space Group" @@ -104,7 +89,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -114,7 +99,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "7", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -123,7 +108,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -132,7 +117,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -141,7 +126,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -203,7 +188,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "## Define Experiment\n", @@ -217,7 +202,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -227,7 +212,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -236,7 +221,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "14", "metadata": {}, "source": [ "#### Create Experiment" @@ -245,7 +230,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -259,7 +244,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -272,7 +257,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ "#### Set Instrument" @@ -281,7 +266,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -294,7 +279,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -306,7 +291,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "20", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -315,7 +300,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -331,7 +316,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -346,7 +331,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "23", "metadata": {}, "source": [ "#### Set Background" @@ -355,7 +340,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -399,7 +384,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "25", "metadata": {}, "outputs": [], "source": [ @@ -441,7 +426,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "26", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -450,7 +435,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -460,7 +445,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -469,7 +454,7 @@ }, { "cell_type": "markdown", - "id": "30", + "id": "29", "metadata": {}, "source": [ "#### Set Excluded Regions" @@ -478,7 +463,7 @@ { "cell_type": "code", "execution_count": null, - "id": "31", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -489,7 +474,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -499,7 +484,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "32", "metadata": {}, "source": [ "## Define Project\n", @@ -513,7 +498,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -522,7 +507,7 @@ }, { "cell_type": "markdown", - "id": "35", + "id": "34", "metadata": {}, "source": [ "#### Set Plotting Engine" @@ -531,7 +516,7 @@ { "cell_type": "code", "execution_count": null, - "id": "36", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -542,7 +527,7 @@ }, { "cell_type": "markdown", - "id": "37", + "id": "36", "metadata": {}, "source": [ "#### Add Structure" @@ -551,7 +536,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "37", "metadata": {}, "outputs": [], "source": [ @@ -560,7 +545,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "38", "metadata": {}, "source": [ "#### Add Experiment" @@ -569,7 +554,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "39", "metadata": {}, "outputs": [], "source": [ @@ -579,7 +564,7 @@ }, { "cell_type": "markdown", - "id": "41", + "id": "40", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -593,7 +578,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -602,7 +587,7 @@ }, { "cell_type": "markdown", - "id": "43", + "id": "42", "metadata": {}, "source": [ "#### Set Fit Mode" @@ -611,7 +596,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -620,7 +605,7 @@ }, { "cell_type": "markdown", - "id": "45", + "id": "44", "metadata": {}, "source": [ "#### Set Free Parameters" @@ -629,7 +614,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -644,7 +629,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "46", "metadata": {}, "outputs": [], "source": [ @@ -667,7 +652,7 @@ }, { "cell_type": "markdown", - "id": "48", + "id": "47", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -676,7 +661,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "48", "metadata": {}, "outputs": [], "source": [ @@ -686,7 +671,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50", + "id": "49", "metadata": {}, "outputs": [], "source": [ @@ -695,7 +680,7 @@ }, { "cell_type": "markdown", - "id": "51", + "id": "50", "metadata": {}, "source": [ "#### Run Fitting" @@ -704,7 +689,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "51", "metadata": {}, "outputs": [], "source": [ @@ -714,7 +699,7 @@ }, { "cell_type": "markdown", - "id": "53", + "id": "52", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -723,7 +708,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "53", "metadata": {}, "outputs": [], "source": [ @@ -733,7 +718,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "54", "metadata": {}, "outputs": [], "source": [ @@ -742,7 +727,7 @@ }, { "cell_type": "markdown", - "id": "56", + "id": "55", "metadata": {}, "source": [ "## Summary\n", @@ -752,7 +737,7 @@ }, { "cell_type": "markdown", - "id": "57", + "id": "56", "metadata": {}, "source": [ "#### Show Project Summary" @@ -761,7 +746,7 @@ { "cell_type": "code", "execution_count": null, - "id": "58", + "id": "57", "metadata": {}, "outputs": [], "source": [ diff --git a/docs/docs/tutorials/ed-9.ipynb b/docs/docs/tutorials/ed-9.ipynb index b9b845da..cc1f0ba4 100644 --- a/docs/docs/tutorials/ed-9.ipynb +++ b/docs/docs/tutorials/ed-9.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "execution_count": null, - "id": "e2e25ed0", + "id": "9c2a5d62", "metadata": { "tags": [ "hide-in-docs" @@ -19,24 +19,9 @@ " %pip install easydiffraction" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "0", - "metadata": {}, - "outputs": [], - "source": [ - "# Check whether easydiffraction is installed; install it if needed.\n", - "# Required for remote environments such as Google Colab.\n", - "import importlib.util\n", - "\n", - "if importlib.util.find_spec('easydiffraction') is None:\n", - " %pip install easydiffraction" - ] - }, { "cell_type": "markdown", - "id": "1", + "id": "0", "metadata": {}, "source": [ "# Structure Refinement: LBCO+Si, McStas\n", @@ -48,7 +33,7 @@ }, { "cell_type": "markdown", - "id": "2", + "id": "1", "metadata": {}, "source": [ "## Import Library" @@ -57,7 +42,7 @@ { "cell_type": "code", "execution_count": null, - "id": "3", + "id": "2", "metadata": {}, "outputs": [], "source": [ @@ -69,7 +54,7 @@ }, { "cell_type": "markdown", - "id": "4", + "id": "3", "metadata": {}, "source": [ "## Define Structures\n", @@ -83,7 +68,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5", + "id": "4", "metadata": {}, "outputs": [], "source": [ @@ -92,7 +77,7 @@ }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "#### Set Space Group" @@ -101,7 +86,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -111,7 +96,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "7", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -120,7 +105,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -129,7 +114,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -138,7 +123,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -184,7 +169,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "### Create Structure 2: Si" @@ -193,7 +178,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -202,7 +187,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "13", "metadata": {}, "source": [ "#### Set Space Group" @@ -211,7 +196,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -221,7 +206,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "15", "metadata": {}, "source": [ "#### Set Unit Cell" @@ -230,7 +215,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -239,7 +224,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ "#### Set Atom Sites" @@ -248,7 +233,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -265,7 +250,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "19", "metadata": {}, "source": [ "## Define Experiment\n", @@ -279,7 +264,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -288,7 +273,7 @@ }, { "cell_type": "markdown", - "id": "22", + "id": "21", "metadata": {}, "source": [ "#### Create Experiment" @@ -297,7 +282,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "22", "metadata": {}, "outputs": [], "source": [ @@ -313,7 +298,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "23", "metadata": {}, "source": [ "#### Set Instrument" @@ -322,7 +307,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -334,7 +319,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "25", "metadata": {}, "source": [ "#### Set Peak Profile" @@ -343,7 +328,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -359,7 +344,7 @@ }, { "cell_type": "markdown", - "id": "28", + "id": "27", "metadata": {}, "source": [ "#### Set Background" @@ -367,7 +352,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "28", "metadata": {}, "source": [ "Select the background type." @@ -376,7 +361,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -385,7 +370,7 @@ }, { "cell_type": "markdown", - "id": "31", + "id": "30", "metadata": {}, "source": [ "Add background points." @@ -394,7 +379,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "31", "metadata": {}, "outputs": [], "source": [ @@ -415,7 +400,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "32", "metadata": {}, "source": [ "#### Set Linked Phases" @@ -424,7 +409,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -434,7 +419,7 @@ }, { "cell_type": "markdown", - "id": "35", + "id": "34", "metadata": {}, "source": [ "## Define Project\n", @@ -448,7 +433,7 @@ { "cell_type": "code", "execution_count": null, - "id": "36", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -457,7 +442,7 @@ }, { "cell_type": "markdown", - "id": "37", + "id": "36", "metadata": {}, "source": [ "#### Add Structures" @@ -466,7 +451,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "37", "metadata": {}, "outputs": [], "source": [ @@ -476,7 +461,7 @@ }, { "cell_type": "markdown", - "id": "39", + "id": "38", "metadata": {}, "source": [ "#### Show Structures" @@ -485,7 +470,7 @@ { "cell_type": "code", "execution_count": null, - "id": "40", + "id": "39", "metadata": {}, "outputs": [], "source": [ @@ -494,7 +479,7 @@ }, { "cell_type": "markdown", - "id": "41", + "id": "40", "metadata": {}, "source": [ "#### Add Experiments" @@ -503,7 +488,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -512,7 +497,7 @@ }, { "cell_type": "markdown", - "id": "43", + "id": "42", "metadata": {}, "source": [ "#### Set Excluded Regions\n", @@ -523,7 +508,7 @@ { "cell_type": "code", "execution_count": null, - "id": "44", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -532,7 +517,7 @@ }, { "cell_type": "markdown", - "id": "45", + "id": "44", "metadata": {}, "source": [ "Add excluded regions." @@ -541,7 +526,7 @@ { "cell_type": "code", "execution_count": null, - "id": "46", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -551,7 +536,7 @@ }, { "cell_type": "markdown", - "id": "47", + "id": "46", "metadata": {}, "source": [ "Show excluded regions." @@ -560,7 +545,7 @@ { "cell_type": "code", "execution_count": null, - "id": "48", + "id": "47", "metadata": {}, "outputs": [], "source": [ @@ -569,7 +554,7 @@ }, { "cell_type": "markdown", - "id": "49", + "id": "48", "metadata": {}, "source": [ "Show measured data after adding excluded regions." @@ -578,7 +563,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50", + "id": "49", "metadata": {}, "outputs": [], "source": [ @@ -587,7 +572,7 @@ }, { "cell_type": "markdown", - "id": "51", + "id": "50", "metadata": {}, "source": [ "Show experiment as CIF." @@ -596,7 +581,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "51", "metadata": {}, "outputs": [], "source": [ @@ -605,7 +590,7 @@ }, { "cell_type": "markdown", - "id": "53", + "id": "52", "metadata": {}, "source": [ "## Perform Analysis\n", @@ -619,7 +604,7 @@ { "cell_type": "code", "execution_count": null, - "id": "54", + "id": "53", "metadata": {}, "outputs": [], "source": [ @@ -628,7 +613,7 @@ }, { "cell_type": "markdown", - "id": "55", + "id": "54", "metadata": {}, "source": [ "#### Set Fitting Parameters\n", @@ -639,7 +624,7 @@ { "cell_type": "code", "execution_count": null, - "id": "56", + "id": "55", "metadata": {}, "outputs": [], "source": [ @@ -652,7 +637,7 @@ }, { "cell_type": "markdown", - "id": "57", + "id": "56", "metadata": {}, "source": [ "Set experiment parameters to be optimized." @@ -661,7 +646,7 @@ { "cell_type": "code", "execution_count": null, - "id": "58", + "id": "57", "metadata": {}, "outputs": [], "source": [ @@ -682,7 +667,7 @@ }, { "cell_type": "markdown", - "id": "59", + "id": "58", "metadata": {}, "source": [ "#### Perform Fit" @@ -691,7 +676,7 @@ { "cell_type": "code", "execution_count": null, - "id": "60", + "id": "59", "metadata": {}, "outputs": [], "source": [ @@ -701,7 +686,7 @@ }, { "cell_type": "markdown", - "id": "61", + "id": "60", "metadata": {}, "source": [ "#### Plot Measured vs Calculated" @@ -710,7 +695,7 @@ { "cell_type": "code", "execution_count": null, - "id": "62", + "id": "61", "metadata": {}, "outputs": [], "source": [ @@ -720,7 +705,7 @@ { "cell_type": "code", "execution_count": null, - "id": "63", + "id": "62", "metadata": {}, "outputs": [], "source": [] From 8a30ae1b50b1a34c9bff734c986d90504a2cbc3d Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Sat, 4 Apr 2026 00:46:35 +0200 Subject: [PATCH 23/24] Remove CSV writing from fit() to fix sequential crash recovery --- src/easydiffraction/analysis/analysis.py | 58 ------------------------ src/easydiffraction/utils/utils.py | 2 +- 2 files changed, 1 insertion(+), 59 deletions(-) diff --git a/src/easydiffraction/analysis/analysis.py b/src/easydiffraction/analysis/analysis.py index 4218b480..80c5c1a3 100644 --- a/src/easydiffraction/analysis/analysis.py +++ b/src/easydiffraction/analysis/analysis.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: BSD-3-Clause from contextlib import suppress -from pathlib import Path import numpy as np import pandas as pd @@ -645,44 +644,6 @@ def fit(self, verbosity: str | None = None) -> None: expt_names = experiments.names num_expts = len(expt_names) - # CSV setup: write results if the project has been saved - csv_path = None - csv_header = None - csv_free_names = None - csv_diffrn_fields = None - if self.project.info.path is not None: - from easydiffraction.analysis.sequential import _META_COLUMNS # noqa: PLC0415 - from easydiffraction.analysis.sequential import _append_to_csv # noqa: PLC0415 - from easydiffraction.analysis.sequential import _write_csv_header # noqa: PLC0415 - - csv_path = Path(self.project.info.path) / 'analysis' / 'results.csv' - csv_path.parent.mkdir(parents=True, exist_ok=True) - - all_params = ( - self.project.structures.parameters + self.project.experiments.parameters - ) - csv_free_names = [ - p.unique_name - for p in all_params - if isinstance(p, Parameter) and not p.constrained and p.free - ] - - first_expt = list(experiments.values())[0] - csv_diffrn_fields = [] - if hasattr(first_expt, 'diffrn'): - csv_diffrn_fields = [ - p.name - for p in first_expt.diffrn.parameters - if hasattr(p, 'name') and p.name not in ('type',) - ] - - csv_header = list(_META_COLUMNS) - csv_header.extend(f'diffrn.{f}' for f in csv_diffrn_fields) - for name in csv_free_names: - csv_header.append(name) - csv_header.append(f'{name}.uncertainty') - _write_csv_header(csv_path, csv_header) - # Short mode: print header and create display handle once short_headers = ['experiment', 'χ²', 'iterations', 'status'] short_alignments = ['left', 'right', 'right', 'center'] @@ -730,25 +691,6 @@ def fit(self, verbosity: str | None = None) -> None: self._parameter_snapshots[expt_name] = snapshot self.fit_results = results - # Append row to CSV - if csv_path is not None: - row = { - 'file_path': expt_name, - 'fit_success': results.success, - 'chi_squared': results.chi_square, - 'reduced_chi_squared': results.reduced_chi_square, - 'n_iterations': (self.fitter.minimizer.tracker.best_iteration or 0), - } - if hasattr(experiment, 'diffrn') and csv_diffrn_fields: - for p in experiment.diffrn.parameters: - if hasattr(p, 'name') and p.name not in ('type',): - row[f'diffrn.{p.name}'] = p.value - for uname in csv_free_names: - if uname in snapshot: - row[uname] = snapshot[uname]['value'] - row[f'{uname}.uncertainty'] = snapshot[uname]['uncertainty'] - _append_to_csv(csv_path, csv_header, [row]) - # Short mode: append one summary row and update in-place if verb is VerbosityEnum.SHORT: chi2_str = ( diff --git a/src/easydiffraction/utils/utils.py b/src/easydiffraction/utils/utils.py index 50af41fe..0108422d 100644 --- a/src/easydiffraction/utils/utils.py +++ b/src/easydiffraction/utils/utils.py @@ -73,7 +73,7 @@ def _fetch_data_index() -> dict: _validate_url(index_url) # macOS: sha256sum index.json - index_hash = 'sha256:1032db0c04ef713c3f5209020a14b18dcdc3cfa4d995664ae5c9f5096f4508d4' + index_hash = 'sha256:dfde966a084579c2103b0d35ed3e8688ddc6941335e251d3e1735a792ca06144' destination_dirname = 'easydiffraction' destination_fname = 'data-index.json' cache_dir = pooch.os_cache(destination_dirname) From 592a2c5e96095dac8295592a0f5a7bd0b4b8f0f0 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Sat, 4 Apr 2026 13:33:07 +0200 Subject: [PATCH 24/24] Fix extract_project_from_zip to find project.cif from zip contents --- src/easydiffraction/io/ascii.py | 17 ++++++++++------- tests/unit/easydiffraction/io/test_ascii.py | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/easydiffraction/io/ascii.py b/src/easydiffraction/io/ascii.py index 45061787..1bba03b0 100644 --- a/src/easydiffraction/io/ascii.py +++ b/src/easydiffraction/io/ascii.py @@ -58,15 +58,18 @@ def extract_project_from_zip( extract_dir = Path(tempfile.mkdtemp(prefix='ed_zip_')) with zipfile.ZipFile(zip_path, 'r') as zf: - zf.extractall(extract_dir) + # Determine the project directory from the archive contents + # *before* extraction, so we are not confused by unrelated + # project.cif files already present in the destination. + project_cif_entries = [name for name in zf.namelist() if name.endswith('project.cif')] + if not project_cif_entries: + msg = f'No project.cif found in ZIP archive: {zip_path}' + raise ValueError(msg) - # Find the project directory (the one containing project.cif) - project_cifs = list(extract_dir.rglob('project.cif')) - if not project_cifs: - msg = f'No project.cif found in ZIP archive: {zip_path}' - raise ValueError(msg) + zf.extractall(extract_dir) - return str(project_cifs[0].parent.resolve()) + project_cif_path = extract_dir / project_cif_entries[0] + return str(project_cif_path.parent.resolve()) def extract_data_paths_from_zip( diff --git a/tests/unit/easydiffraction/io/test_ascii.py b/tests/unit/easydiffraction/io/test_ascii.py index 1410f8e9..ab180701 100644 --- a/tests/unit/easydiffraction/io/test_ascii.py +++ b/tests/unit/easydiffraction/io/test_ascii.py @@ -65,6 +65,23 @@ def test_destination_creates_directory(self, tmp_path): assert dest.is_dir() assert 'proj' in result + def test_ignores_other_project_cif_in_destination(self, tmp_path): + """Only finds project.cif from the zip, not pre-existing ones.""" + dest = tmp_path / 'data' + # Pre-create another project directory in the destination + other_project = dest / 'aaa_other' / 'project.cif' + other_project.parent.mkdir(parents=True) + other_project.write_text('other\n') + + zip_path = tmp_path / 'proj.zip' + with zipfile.ZipFile(zip_path, 'w') as zf: + zf.writestr('target_project/project.cif', 'correct\n') + + result = extract_project_from_zip(zip_path, destination=dest) + + assert 'target_project' in result + assert 'aaa_other' not in result + class TestExtractDataPathsFromZip: """Tests for extract_data_paths_from_zip."""