Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pixi.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ select = [
'I', # Import sorting issues (e.g., unsorted imports)
'S', # Security-related issues (e.g., use of insecure functions or libraries)
'W', # General PEP 8 warnings (e.g., lines too long, trailing whitespace)
'TCH', # Type checking issues (e.g., incompatible types, missing type annotations)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do static type checking code analysis with ruff? Nice! Probably worth extending to other projects.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, Ruff has some basic type-checking support - it already helped catch one or two issues, and that's better than nothing. But we'll still need a more advanced solution, like typeguard or similar.

]

[tool.ruff.lint.isort]
Expand Down
3 changes: 3 additions & 0 deletions src/easydiffraction/analysis/calculators/calculator_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ def calculate_pattern(
called_by_minimizer=called_by_minimizer,
)

# if not sample_model_y_calc:
# return np.ndarray([])

sample_model_y_calc_scaled = sample_model_scale * sample_model_y_calc
y_calc_scaled += sample_model_y_calc_scaled

Expand Down
24 changes: 14 additions & 10 deletions src/easydiffraction/analysis/calculators/calculator_cryspy.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import numpy as np

from easydiffraction.experiments.components.experiment_type import BeamModeEnum
from easydiffraction.experiments.experiment import Experiment
from easydiffraction.sample_models.sample_model import SampleModel

Expand Down Expand Up @@ -111,7 +112,10 @@ def _calculate_single_model_pattern(
flag_calc_analytical_derivatives=False,
)

prefixes = {'constant wavelength': 'pd', 'time-of-flight': 'tof'}
prefixes = {
BeamModeEnum.CONSTANT_WAVELENGTH: 'pd',
BeamModeEnum.TIME_OF_FLIGHT: 'tof',
}
beam_mode = experiment.type.beam_mode.value
if beam_mode in prefixes.keys():
cryspy_block_name = f'{prefixes[beam_mode]}_{experiment.name}'
Expand Down Expand Up @@ -177,7 +181,7 @@ def _recreate_cryspy_dict(
cryspy_biso[idx] = atom_site.b_iso.value

# ---------- Update experiment parameters ----------
if experiment.type.beam_mode.value == 'constant wavelength':
if experiment.type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
cryspy_expt_name = f'pd_{experiment.name}'
cryspy_expt_dict = cryspy_dict[cryspy_expt_name]
# Instrument
Expand All @@ -191,7 +195,7 @@ def _recreate_cryspy_dict(
cryspy_resolution[3] = experiment.peak.broad_lorentz_x.value
cryspy_resolution[4] = experiment.peak.broad_lorentz_y.value

elif experiment.type.beam_mode.value == 'time-of-flight':
elif experiment.type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
cryspy_expt_name = f'tof_{experiment.name}'
cryspy_expt_dict = cryspy_dict[cryspy_expt_name]
# Instrument
Expand Down Expand Up @@ -321,7 +325,7 @@ def _convert_experiment_to_cryspy_cif(
'asym_alpha_1': '_tof_profile_alpha1',
}
cif_lines.append('')
if expt_type.beam_mode.value == 'time-of-flight':
if expt_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
cif_lines.append('_tof_profile_peak_shape Gauss')
for local_attr_name, engine_key_name in peak_mapping.items():
if hasattr(peak, local_attr_name):
Expand All @@ -332,10 +336,10 @@ def _convert_experiment_to_cryspy_cif(
twotheta_min = float(x_data.min())
twotheta_max = float(x_data.max())
cif_lines.append('')
if expt_type.beam_mode.value == 'constant wavelength':
if expt_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
cif_lines.append(f'_range_2theta_min {twotheta_min}')
cif_lines.append(f'_range_2theta_max {twotheta_max}')
elif expt_type.beam_mode.value == 'time-of-flight':
elif expt_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
cif_lines.append(f'_range_time_min {twotheta_min}')
cif_lines.append(f'_range_time_max {twotheta_max}')

Expand All @@ -345,28 +349,28 @@ def _convert_experiment_to_cryspy_cif(
cif_lines.append('_phase_scale')
cif_lines.append(f'{linked_phase.name} 1.0')

if expt_type.beam_mode.value == 'constant wavelength':
if expt_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
cif_lines.append('')
cif_lines.append('loop_')
cif_lines.append('_pd_background_2theta')
cif_lines.append('_pd_background_intensity')
cif_lines.append(f'{twotheta_min} 0.0')
cif_lines.append(f'{twotheta_max} 0.0')
elif expt_type.beam_mode.value == 'time-of-flight':
elif expt_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
cif_lines.append('')
cif_lines.append('loop_')
cif_lines.append('_tof_backgroundpoint_time')
cif_lines.append('_tof_backgroundpoint_intensity')
cif_lines.append(f'{twotheta_min} 0.0')
cif_lines.append(f'{twotheta_max} 0.0')

if expt_type.beam_mode.value == 'constant wavelength':
if expt_type.beam_mode.value == BeamModeEnum.CONSTANT_WAVELENGTH:
cif_lines.append('')
cif_lines.append('loop_')
cif_lines.append('_pd_meas_2theta')
cif_lines.append('_pd_meas_intensity')
cif_lines.append('_pd_meas_intensity_sigma')
elif expt_type.beam_mode.value == 'time-of-flight':
elif expt_type.beam_mode.value == BeamModeEnum.TIME_OF_FLIGHT:
cif_lines.append('')
cif_lines.append('loop_')
cif_lines.append('_tof_meas_time')
Expand Down
5 changes: 4 additions & 1 deletion src/easydiffraction/analysis/minimization.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SPDX-FileCopyrightText: 2021-2025 EasyDiffraction Python Library contributors <https://github.com/easyscience/diffraction-lib>
# SPDX-License-Identifier: BSD-3-Clause

from typing import TYPE_CHECKING
from typing import Any
from typing import Dict
from typing import List
Expand All @@ -14,7 +15,9 @@
from easydiffraction.sample_models.sample_models import SampleModels

from ..analysis.reliability_factors import get_reliability_inputs
from .minimizers.minimizer_base import FitResults

if TYPE_CHECKING:
from .minimizers.minimizer_base import FitResults
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

circular dependencies? 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this change because Ruff showed a TC001 warning (“Move application imports used only for typing into a TYPE_CHECKING block”) after I turned on the type-checking rules 🙃

from .minimizers.minimizer_factory import MinimizerFactory


Expand Down
30 changes: 0 additions & 30 deletions src/easydiffraction/core/constants.py

This file was deleted.

28 changes: 22 additions & 6 deletions src/easydiffraction/experiments/collections/background.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# SPDX-License-Identifier: BSD-3-Clause

from abc import abstractmethod
from enum import Enum
from typing import Dict
from typing import List
from typing import Type
Expand All @@ -11,7 +12,6 @@
from numpy.polynomial.chebyshev import chebval
from scipy.interpolate import interp1d

from easydiffraction.core.constants import DEFAULT_BACKGROUND_TYPE
from easydiffraction.core.objects import Collection
from easydiffraction.core.objects import Component
from easydiffraction.core.objects import Descriptor
Expand Down Expand Up @@ -190,22 +190,38 @@ def show(self) -> None:
)


class BackgroundTypeEnum(str, Enum):
LINE_SEGMENT = 'line-segment'
CHEBYSHEV = 'chebyshev polynomial'

@classmethod
def default(cls) -> 'BackgroundTypeEnum':
return cls.LINE_SEGMENT

def description(self) -> str:
if self is BackgroundTypeEnum.LINE_SEGMENT:
return 'Linear interpolation between points'
elif self is BackgroundTypeEnum.CHEBYSHEV:
return 'Chebyshev polynomial background'


class BackgroundFactory:
_supported: Dict[str, Type[BackgroundBase]] = {
'line-segment': LineSegmentBackground,
'chebyshev polynomial': ChebyshevPolynomialBackground,
_supported: Dict[BackgroundTypeEnum, Type[BackgroundBase]] = {
BackgroundTypeEnum.LINE_SEGMENT: LineSegmentBackground,
BackgroundTypeEnum.CHEBYSHEV: ChebyshevPolynomialBackground,
}

@classmethod
def create(
cls,
background_type: str = DEFAULT_BACKGROUND_TYPE,
background_type: BackgroundTypeEnum = BackgroundTypeEnum.default(),
) -> BackgroundBase:
if background_type not in cls._supported:
supported_types = list(cls._supported.keys())

raise ValueError(
f"Unsupported background type: '{background_type}'.\n Supported background types: {supported_types}"
f"Unsupported background type: '{background_type}'.\n"
f' Supported background types: {[bt.value for bt in supported_types]}'
)

background_class = cls._supported[background_type]
Expand Down
62 changes: 62 additions & 0 deletions src/easydiffraction/experiments/components/experiment_type.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,72 @@
# SPDX-FileCopyrightText: 2021-2025 EasyDiffraction Python Library contributors <https://github.com/easyscience/diffraction-lib>
# SPDX-License-Identifier: BSD-3-Clause

from enum import Enum

from easydiffraction.core.objects import Component
from easydiffraction.core.objects import Descriptor


class SampleFormEnum(str, Enum):
POWDER = 'powder'
SINGLE_CRYSTAL = 'single crystal'

@classmethod
def default(cls) -> 'SampleFormEnum':
return cls.POWDER

def description(self) -> str:
if self is SampleFormEnum.POWDER:
return 'Powdered or polycrystalline sample.'
elif self is SampleFormEnum.SINGLE_CRYSTAL:
return 'Single crystal sample.'


class ScatteringTypeEnum(str, Enum):
BRAGG = 'bragg'
TOTAL = 'total'

@classmethod
def default(cls) -> 'ScatteringTypeEnum':
return cls.BRAGG

def description(self) -> str:
if self is ScatteringTypeEnum.BRAGG:
return 'Bragg diffraction for conventional structure refinement.'
elif self is ScatteringTypeEnum.TOTAL:
return 'Total scattering for pair distribution function analysis (PDF).'


class RadiationProbeEnum(str, Enum):
NEUTRON = 'neutron'
XRAY = 'xray'

@classmethod
def default(cls) -> 'RadiationProbeEnum':
return cls.NEUTRON

def description(self) -> str:
if self is RadiationProbeEnum.NEUTRON:
return 'Neutron diffraction.'
elif self is RadiationProbeEnum.XRAY:
return 'X-ray diffraction.'


class BeamModeEnum(str, Enum):
CONSTANT_WAVELENGTH = 'constant wavelength'
TIME_OF_FLIGHT = 'time-of-flight'

@classmethod
def default(cls) -> 'BeamModeEnum':
return cls.CONSTANT_WAVELENGTH

def description(self) -> str:
if self is BeamModeEnum.CONSTANT_WAVELENGTH:
return 'Constant wavelength (CW) diffraction.'
elif self is BeamModeEnum.TIME_OF_FLIGHT:
return 'Time-of-flight (TOF) diffraction.'


class ExperimentType(Component):
@property
def cif_category_key(self) -> str:
Expand Down
14 changes: 7 additions & 7 deletions src/easydiffraction/experiments/components/instrument.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# SPDX-FileCopyrightText: 2021-2025 EasyDiffraction Python Library contributors <https://github.com/easyscience/diffraction-lib>
# SPDX-License-Identifier: BSD-3-Clause

from easydiffraction.core.constants import DEFAULT_BEAM_MODE
from easydiffraction.core.constants import DEFAULT_SCATTERING_TYPE
from easydiffraction.core.objects import Component
from easydiffraction.core.objects import Parameter
from easydiffraction.experiments.components.experiment_type import BeamModeEnum
from easydiffraction.experiments.components.experiment_type import ScatteringTypeEnum


class InstrumentBase(Component):
Expand Down Expand Up @@ -99,17 +99,17 @@ def __init__(

class InstrumentFactory:
_supported = {
'bragg': {
'constant wavelength': ConstantWavelengthInstrument,
'time-of-flight': TimeOfFlightInstrument,
ScatteringTypeEnum.BRAGG: {
BeamModeEnum.CONSTANT_WAVELENGTH: ConstantWavelengthInstrument,
BeamModeEnum.TIME_OF_FLIGHT: TimeOfFlightInstrument,
}
}

@classmethod
def create(
cls,
scattering_type=DEFAULT_SCATTERING_TYPE,
beam_mode=DEFAULT_BEAM_MODE,
scattering_type=ScatteringTypeEnum.default(),
beam_mode=BeamModeEnum.default(),
):
supported_scattering_types = list(cls._supported.keys())
if scattering_type not in supported_scattering_types:
Expand Down
Loading