Skip to content

Commit df3000a

Browse files
committed
Fifth step in transitioning to pydantic: more Experiment
1 parent dae6c36 commit df3000a

File tree

10 files changed

+624
-273
lines changed

10 files changed

+624
-273
lines changed

src/easydiffraction/analysis/calculators/calculator_cryspy.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,8 @@ def _convert_experiment_to_cryspy_cif(
321321
}
322322
cif_lines.append('')
323323
for local_attr_name, engine_key_name in instrument_mapping.items():
324-
attr_obj = instrument.__dict__.get(local_attr_name)
324+
# attr_obj = instrument.__dict__.get(local_attr_name)
325+
attr_obj = getattr(instrument, local_attr_name, None)
325326
if attr_obj is not None:
326327
cif_lines.append(f'{engine_key_name} {attr_obj.value}')
327328

@@ -347,7 +348,8 @@ def _convert_experiment_to_cryspy_cif(
347348
cif_lines.append('_tof_profile_peak_shape Gauss')
348349
cif_lines.append('')
349350
for local_attr_name, engine_key_name in peak_mapping.items():
350-
attr_obj = peak.__dict__.get(local_attr_name)
351+
# attr_obj = peak.__dict__.get(local_attr_name)
352+
attr_obj = getattr(peak, local_attr_name, None)
351353
if attr_obj is not None:
352354
cif_lines.append(f'{engine_key_name} {attr_obj.value}')
353355

src/easydiffraction/core/parameters2.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,14 @@ class DescriptorStr(GenericDescriptorStr, CifHelper):
410410
pass
411411

412412

413+
# ----------------------------------------------------------------------
414+
# DescriptorFloat
415+
# ----------------------------------------------------------------------
416+
@dataclass(kw_only=True)
417+
class DescriptorFloat(GenericDescriptorFloat, CifHelper):
418+
pass
419+
420+
413421
# ----------------------------------------------------------------------
414422
# Parameter
415423
# ----------------------------------------------------------------------

src/easydiffraction/core/singletons.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,13 @@ def add_to_uid_map(self, parameter):
5050
from easydiffraction.core.parameters import Descriptor
5151
from easydiffraction.core.parameters import GenericConstant
5252
from easydiffraction.core.parameters import Parameter
53+
from easydiffraction.core.parameters2 import DescriptorFloat
5354
from easydiffraction.core.parameters2 import DescriptorStr as DescriptorStr
5455
from easydiffraction.core.parameters2 import Parameter as Parameter2
5556

5657
if not isinstance(
57-
parameter, (GenericConstant, Descriptor, Parameter, Parameter2, DescriptorStr)
58+
parameter,
59+
(GenericConstant, Descriptor, Parameter, Parameter2, DescriptorStr, DescriptorFloat),
5860
):
5961
raise TypeError(
6062
f'Cannot add object of type {type(parameter).__name__} to UID map. '

src/easydiffraction/experiments/collections/background.py

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717

1818
from easydiffraction.core.categories import CategoryCollection
1919
from easydiffraction.core.categories import CategoryItem
20-
from easydiffraction.core.parameters import Descriptor
21-
from easydiffraction.core.parameters import Parameter
20+
from easydiffraction.core.parameters2 import DescriptorStr
21+
from easydiffraction.core.parameters2 import Parameter
22+
from easydiffraction.core.parameters2 import RangeValidator
2223
from easydiffraction.utils.formatting import paragraph
2324
from easydiffraction.utils.formatting import warning
2425
from easydiffraction.utils.utils import render_table
@@ -45,27 +46,42 @@ def __init__(
4546
):
4647
super().__init__()
4748

48-
self.x = Descriptor(
49-
value=x,
50-
name='x',
51-
value_type=float,
49+
self._x = DescriptorStr( # TODO: Should be DescriptorFloat?
50+
_init_value=x,
51+
_validator=RangeValidator(ge=0, default=0.0),
5252
_full_cif_names=['_pd_background.line_segment_X'],
53-
default_value=x,
53+
name='x',
5454
description='X-coordinates used to create many straight-line segments '
5555
'representing the background in a calculated diffractogram.',
5656
)
57-
self.y = Parameter(
58-
value=y, # TODO: rename to intensity
59-
name='y', # TODO: rename to intensity
57+
self._y = Parameter(
58+
_init_value=y, # TODO: rename to intensity
59+
_validator=RangeValidator(ge=0, default=0.0),
6060
_full_cif_names=['_pd_background.line_segment_intensity'],
61-
default_value=y,
61+
name='y', # TODO: rename to intensity
6262
description='Intensity used to create many straight-line segments '
6363
'representing the background in a calculated diffractogram',
6464
)
6565
# self._category_entry_attr_name = str(x)
6666
self._category_entry_attr_name = self.x.name
6767
self.name = self.x.value
6868

69+
@property
70+
def x(self):
71+
return self._x
72+
73+
@x.setter
74+
def x(self, value):
75+
self._x.value = value
76+
77+
@property
78+
def y(self):
79+
return self._y
80+
81+
@y.setter
82+
def y(self, value):
83+
self._y.value = value
84+
6985

7086
class PolynomialTerm(CategoryItem):
7187
"""Chebyshev polynomial term.
@@ -80,38 +96,57 @@ class PolynomialTerm(CategoryItem):
8096
# New canonical names
8197
'order',
8298
'coef',
83-
# Backward compatibility (aliases)
84-
'chebyshev_order',
85-
'chebyshev_coef',
99+
# Backward compatibility (aliases) # TODO: Remove?
100+
'chebyshev_order', # TODO: Remove?
101+
'chebyshev_coef', # TODO: Remove?
86102
}
87103

88104
@property
89105
def category_key(self) -> str: # noqa: D401
90106
return 'background'
91107

92-
def __init__(self, order: int, coef: float) -> None:
108+
def __init__(
109+
self,
110+
order: int,
111+
coef: float,
112+
) -> None:
93113
super().__init__()
94114

95115
# Canonical descriptors
96-
self.order = Descriptor(
97-
value=order,
98-
name='order',
99-
value_type=int,
116+
self._order = DescriptorStr( # TODO: Should be DescriptorInt?
117+
_init_value=order,
118+
_validator=RangeValidator(ge=0, default=0.0),
100119
_full_cif_names=['_pd_background.Chebyshev_order'],
101-
default_value=order,
120+
name='order',
102121
description='Order used in a Chebyshev polynomial background term',
103122
)
104-
self.coef = Parameter(
105-
value=coef,
106-
name='coef',
123+
self._coef = Parameter(
124+
_init_value=coef,
125+
_validator=RangeValidator(ge=0, default=0.0),
107126
_full_cif_names=['_pd_background.Chebyshev_coef'],
108-
default_value=coef,
127+
name='coef',
109128
description='Coefficient used in a Chebyshev polynomial background term',
110129
)
111130

131+
@property
132+
def order(self):
133+
return self._order
134+
135+
@order.setter
136+
def order(self, value):
137+
self._order.value = value
138+
139+
@property
140+
def coef(self):
141+
return self._coef
142+
143+
@coef.setter
144+
def coef(self, value):
145+
self._coef.value = value
146+
112147
# Backward-compatible aliases (point to same objects)
113-
self.chebyshev_order = self.order
114-
self.chebyshev_coef = self.coef
148+
self.chebyshev_order = self.order # TODO: Remove?
149+
self.chebyshev_coef = self.coef # TODO: Remove?
115150

116151
# Entry attribute used as the identifier within the collection
117152
self._category_entry_attr_name = self.order.name

src/easydiffraction/experiments/collections/excluded_regions.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
from easydiffraction.core.categories import CategoryCollection
77
from easydiffraction.core.categories import CategoryItem
8-
from easydiffraction.core.parameters import Descriptor
9-
from easydiffraction.core.parameters import Parameter
8+
from easydiffraction.core.parameters2 import DescriptorFloat
9+
from easydiffraction.core.parameters2 import RangeValidator
1010
from easydiffraction.utils.formatting import paragraph
1111
from easydiffraction.utils.utils import render_table
1212

@@ -29,19 +29,18 @@ def __init__(
2929
):
3030
super().__init__()
3131

32-
self.start = Descriptor(
33-
value=start,
34-
name='start',
35-
value_type=float,
32+
self.start = DescriptorFloat(
33+
_init_value=start,
34+
_validator=RangeValidator(ge=0, default=0.0),
3635
_full_cif_names=['_excluded_region.start'],
37-
default_value=start,
36+
name='start',
3837
description='Start of the excluded region.',
3938
)
40-
self.end = Parameter(
41-
value=end,
42-
name='end',
39+
self.end = DescriptorFloat(
40+
_init_value=end,
41+
_validator=RangeValidator(ge=0, default=0.0),
4342
_full_cif_names=['_excluded_region.end'],
44-
default_value=end,
43+
name='end',
4544
description='End of the excluded region.',
4645
)
4746
# self._category_entry_attr_name = f'{start}-{end}'

src/easydiffraction/experiments/collections/linked_phases.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
from easydiffraction.core.categories import CategoryCollection
66
from easydiffraction.core.categories import CategoryItem
7-
from easydiffraction.core.parameters import Descriptor
8-
from easydiffraction.core.parameters import Parameter
7+
from easydiffraction.core.parameters2 import DescriptorStr
8+
from easydiffraction.core.parameters2 import Parameter
9+
from easydiffraction.core.parameters2 import RangeValidator
10+
from easydiffraction.core.parameters2 import RegexValidator
911

1012

1113
class LinkedPhase(CategoryItem):
@@ -26,24 +28,45 @@ def __init__(
2628
):
2729
super().__init__()
2830

29-
self.id = Descriptor(
30-
value=id,
31-
name='id',
32-
value_type=str,
31+
self._id = DescriptorStr(
32+
_init_value=id,
33+
_validator=RegexValidator(
34+
# TODO: the following pattern is valid for dict key
35+
# (keywords are not checked). CIF label is less strict.
36+
# So, we need converter?
37+
pattern=r'^[A-Za-z_][A-Za-z0-9_]*$',
38+
default='Si',
39+
),
3340
_full_cif_names=['_pd_phase_block.id'],
34-
default_value=id,
41+
name='id',
3542
description='Identifier of the linked phase.',
3643
)
37-
self.scale = Parameter(
38-
value=scale,
39-
name='scale',
44+
self._scale = Parameter(
45+
_init_value=scale,
46+
_validator=RangeValidator(default=1.0),
4047
_full_cif_names=['_pd_phase_block.scale'],
41-
default_value=scale,
48+
name='scale',
4249
description='Scale factor of the linked phase.',
4350
)
4451
self._category_entry_attr_name = self.id.name
4552
self.name = self.id.value
4653

54+
@property
55+
def id(self) -> DescriptorStr:
56+
return self._id
57+
58+
@id.setter
59+
def id(self, value: str):
60+
self._id.value = value
61+
62+
@property
63+
def scale(self) -> Parameter:
64+
return self._scale
65+
66+
@scale.setter
67+
def scale(self, value: float):
68+
self._scale.value = value
69+
4770

4871
class LinkedPhases(CategoryCollection[LinkedPhase]):
4972
"""Collection of LinkedPhase instances."""

src/easydiffraction/experiments/components/experiment_type.py

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
from enum import Enum
55

66
from easydiffraction.core.categories import CategoryItem
7-
from easydiffraction.core.parameters import Descriptor
7+
from easydiffraction.core.parameters2 import DescriptorStr
8+
from easydiffraction.core.parameters2 import ListValidator
89

910

1011
class SampleFormEnum(str, Enum):
@@ -87,39 +88,46 @@ def __init__(
8788
scattering_type: str,
8889
):
8990
super().__init__()
90-
91-
self.sample_form: Descriptor = Descriptor(
92-
value=sample_form,
93-
name='sample_form',
94-
value_type=str,
91+
self.sample_form: DescriptorStr = DescriptorStr(
92+
_init_value=sample_form,
93+
_validator=ListValidator(
94+
allowed_values=[e.value for e in SampleFormEnum],
95+
default=SampleFormEnum.default(),
96+
),
9597
_full_cif_names=['_expt_type.sample_form'],
96-
default_value=SampleFormEnum.default(),
98+
name='sample_form',
9799
description='Specifies whether the diffraction data corresponds to '
98100
'powder diffraction or single crystal diffraction',
99101
)
100-
self.beam_mode: Descriptor = Descriptor(
101-
value=beam_mode,
102-
name='beam_mode',
103-
value_type=str,
102+
self.beam_mode: DescriptorStr = DescriptorStr(
103+
_init_value=beam_mode,
104+
_validator=ListValidator(
105+
allowed_values=[e.value for e in BeamModeEnum],
106+
default=BeamModeEnum.default(),
107+
),
104108
_full_cif_names=['_expt_type.beam_mode'],
105-
default_value=BeamModeEnum.default(),
109+
name='beam_mode',
106110
description='Defines whether the measurement is performed with a '
107111
'constant wavelength (CW) or time-of-flight (TOF) method',
108112
)
109-
self.radiation_probe: Descriptor = Descriptor(
110-
value=radiation_probe,
111-
name='radiation_probe',
112-
value_type=str,
113+
self.radiation_probe: DescriptorStr = DescriptorStr(
114+
_init_value=radiation_probe,
115+
_validator=ListValidator(
116+
allowed_values=[e.value for e in RadiationProbeEnum],
117+
default=RadiationProbeEnum.default(),
118+
),
113119
_full_cif_names=['_expt_type.radiation_probe'],
114-
default_value=RadiationProbeEnum.default(),
120+
name='radiation_probe',
115121
description='Specifies whether the measurement uses neutrons or X-rays',
116122
)
117-
self.scattering_type: Descriptor = Descriptor(
118-
value=scattering_type,
119-
name='scattering_type',
120-
value_type=str,
123+
self.scattering_type: DescriptorStr = DescriptorStr(
124+
_init_value=scattering_type,
125+
_validator=ListValidator(
126+
allowed_values=[e.value for e in ScatteringTypeEnum],
127+
default=ScatteringTypeEnum.default(),
128+
),
121129
_full_cif_names=['_expt_type.scattering_type'],
122-
default_value=ScatteringTypeEnum.default(),
130+
name='scattering_type',
123131
description='Specifies whether the experiment uses Bragg scattering '
124132
'(for conventional structure refinement) or total scattering '
125133
'(for pair distribution function analysis - PDF)',

0 commit comments

Comments
 (0)