Skip to content

structure

categories

atom_sites

default

Atom site category.

Defines :class:AtomSite items and :class:AtomSites collection used in crystallographic structures.

AtomSite

Bases: CategoryItem

Single atom site with fractional coordinates and ADP.

Attributes are represented by descriptors to support validation and CIF serialization.

Source code in src/easydiffraction/datablocks/structure/categories/atom_sites/default.py
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
class AtomSite(CategoryItem):
    """
    Single atom site with fractional coordinates and ADP.

    Attributes are represented by descriptors to support validation and
    CIF serialization.
    """

    def __init__(self) -> None:
        """Initialise the atom site with default descriptor values."""
        super().__init__()

        self._label = StringDescriptor(
            name='label',
            description='Unique identifier for the atom site.',
            value_spec=AttributeSpec(
                default='Si',
                # TODO: the following pattern is valid for dict key
                #  (keywords are not checked). CIF label is less strict.
                #  Do we need conversion between CIF and internal label?
                validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
            ),
            cif_handler=CifHandler(names=['_atom_site.label']),
        )
        self._type_symbol = StringDescriptor(
            name='type_symbol',
            description='Chemical symbol of the atom at this site.',
            value_spec=AttributeSpec(
                default='Tb',
                validator=MembershipValidator(allowed=self._type_symbol_allowed_values),
            ),
            cif_handler=CifHandler(names=['_atom_site.type_symbol']),
        )
        self._fract_x = Parameter(
            name='fract_x',
            description='Fractional x-coordinate of the atom site within the unit cell.',
            value_spec=AttributeSpec(
                default=0.0,
                validator=RangeValidator(),
            ),
            cif_handler=CifHandler(names=['_atom_site.fract_x']),
        )
        self._fract_y = Parameter(
            name='fract_y',
            description='Fractional y-coordinate of the atom site within the unit cell.',
            value_spec=AttributeSpec(
                default=0.0,
                validator=RangeValidator(),
            ),
            cif_handler=CifHandler(names=['_atom_site.fract_y']),
        )
        self._fract_z = Parameter(
            name='fract_z',
            description='Fractional z-coordinate of the atom site within the unit cell.',
            value_spec=AttributeSpec(
                default=0.0,
                validator=RangeValidator(),
            ),
            cif_handler=CifHandler(names=['_atom_site.fract_z']),
        )
        self._wyckoff_letter = StringDescriptor(
            name='wyckoff_letter',
            description='Wyckoff letter indicating the symmetry of the '
            'atom site within the space group.',
            value_spec=AttributeSpec(
                default=self._wyckoff_letter_default_value,
                validator=MembershipValidator(allowed=self._wyckoff_letter_allowed_values),
            ),
            cif_handler=CifHandler(
                names=[
                    '_atom_site.Wyckoff_letter',
                    '_atom_site.Wyckoff_symbol',
                ]
            ),
        )
        self._occupancy = Parameter(
            name='occupancy',
            description='Occupancy of the atom site, representing the '
            'fraction of the site occupied by the atom type.',
            value_spec=AttributeSpec(
                default=1.0,
                validator=RangeValidator(),
            ),
            cif_handler=CifHandler(names=['_atom_site.occupancy']),
        )
        self._b_iso = Parameter(
            name='b_iso',
            description='Isotropic atomic displacement parameter (ADP) for the atom site.',
            units='Ų',
            value_spec=AttributeSpec(
                default=0.0,
                validator=RangeValidator(ge=0.0),
            ),
            cif_handler=CifHandler(names=['_atom_site.B_iso_or_equiv']),
        )
        self._adp_type = StringDescriptor(
            name='adp_type',
            description='Type of atomic displacement parameter (ADP) '
            'used (e.g., Biso, Uiso, Uani, Bani).',
            value_spec=AttributeSpec(
                default='Biso',
                validator=MembershipValidator(allowed=['Biso']),
            ),
            cif_handler=CifHandler(names=['_atom_site.adp_type']),
        )

        self._identity.category_code = 'atom_site'
        self._identity.category_entry_name = lambda: str(self.label.value)

    # ------------------------------------------------------------------
    #  Private helper methods
    # ------------------------------------------------------------------

    @property
    def _type_symbol_allowed_values(self) -> list[str]:
        """
        Return chemical symbols accepted by *cryspy*.

        Returns
        -------
        list[str]
            Unique element/isotope symbols from the database.
        """
        return list({key[1] for key in DATABASE['Isotopes']})

    @property
    def _wyckoff_letter_allowed_values(self) -> list[str]:
        """
        Return allowed Wyckoff-letter symbols.

        Returns
        -------
        list[str]
            Currently a hard-coded placeholder list.
        """
        # TODO: Need to now current space group. How to access it? Via
        #  parent Cell? Then letters =
        #  list(SPACE_GROUPS[62, 'cab']['Wyckoff_positions'].keys())
        #  Temporarily return hardcoded list:
        return ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']

    @property
    def _wyckoff_letter_default_value(self) -> str:
        """
        Return the default Wyckoff letter.

        Returns
        -------
        str
            First element of the allowed values list.
        """
        # TODO: What to pass as default?
        return self._wyckoff_letter_allowed_values[0]

    # ------------------------------------------------------------------
    #  Public properties
    # ------------------------------------------------------------------

    @property
    def label(self) -> StringDescriptor:
        """
        Unique identifier for the atom site.

        Reading this property returns the underlying
        ``StringDescriptor`` object. Assigning to it updates the
        parameter value.
        """
        return self._label

    @label.setter
    def label(self, value: str) -> None:
        self._label.value = value

    @property
    def type_symbol(self) -> StringDescriptor:
        """
        Chemical symbol of the atom at this site.

        Reading this property returns the underlying
        ``StringDescriptor`` object. Assigning to it updates the
        parameter value.
        """
        return self._type_symbol

    @type_symbol.setter
    def type_symbol(self, value: str) -> None:
        self._type_symbol.value = value

    @property
    def adp_type(self) -> StringDescriptor:
        """
        ADP type used (e.g., Biso, Uiso, Uani, Bani).

        Reading this property returns the underlying
        ``StringDescriptor`` object. Assigning to it updates the
        parameter value.
        """
        return self._adp_type

    @adp_type.setter
    def adp_type(self, value: str) -> None:
        self._adp_type.value = value

    @property
    def wyckoff_letter(self) -> StringDescriptor:
        """
        Wyckoff letter for the atom site symmetry position.

        Reading this property returns the underlying
        ``StringDescriptor`` object. Assigning to it updates the
        parameter value.
        """
        return self._wyckoff_letter

    @wyckoff_letter.setter
    def wyckoff_letter(self, value: str) -> None:
        self._wyckoff_letter.value = value

    @property
    def fract_x(self) -> Parameter:
        """
        Fractional x-coordinate of the atom site within the unit cell.

        Reading this property returns the underlying ``Parameter``
        object. Assigning to it updates the parameter value.
        """
        return self._fract_x

    @fract_x.setter
    def fract_x(self, value: float) -> None:
        self._fract_x.value = value

    @property
    def fract_y(self) -> Parameter:
        """
        Fractional y-coordinate of the atom site within the unit cell.

        Reading this property returns the underlying ``Parameter``
        object. Assigning to it updates the parameter value.
        """
        return self._fract_y

    @fract_y.setter
    def fract_y(self, value: float) -> None:
        self._fract_y.value = value

    @property
    def fract_z(self) -> Parameter:
        """
        Fractional z-coordinate of the atom site within the unit cell.

        Reading this property returns the underlying ``Parameter``
        object. Assigning to it updates the parameter value.
        """
        return self._fract_z

    @fract_z.setter
    def fract_z(self, value: float) -> None:
        self._fract_z.value = value

    @property
    def occupancy(self) -> Parameter:
        """
        Occupancy fraction of the atom type at this site.

        Reading this property returns the underlying ``Parameter``
        object. Assigning to it updates the parameter value.
        """
        return self._occupancy

    @occupancy.setter
    def occupancy(self, value: float) -> None:
        self._occupancy.value = value

    @property
    def b_iso(self) -> Parameter:
        """
        Isotropic ADP for the atom site (Ų).

        Reading this property returns the underlying ``Parameter``
        object. Assigning to it updates the parameter value.
        """
        return self._b_iso

    @b_iso.setter
    def b_iso(self, value: float) -> None:
        self._b_iso.value = value
__init__()

Initialise the atom site with default descriptor values.

Source code in src/easydiffraction/datablocks/structure/categories/atom_sites/default.py
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def __init__(self) -> None:
    """Initialise the atom site with default descriptor values."""
    super().__init__()

    self._label = StringDescriptor(
        name='label',
        description='Unique identifier for the atom site.',
        value_spec=AttributeSpec(
            default='Si',
            # TODO: the following pattern is valid for dict key
            #  (keywords are not checked). CIF label is less strict.
            #  Do we need conversion between CIF and internal label?
            validator=RegexValidator(pattern=r'^[A-Za-z_][A-Za-z0-9_]*$'),
        ),
        cif_handler=CifHandler(names=['_atom_site.label']),
    )
    self._type_symbol = StringDescriptor(
        name='type_symbol',
        description='Chemical symbol of the atom at this site.',
        value_spec=AttributeSpec(
            default='Tb',
            validator=MembershipValidator(allowed=self._type_symbol_allowed_values),
        ),
        cif_handler=CifHandler(names=['_atom_site.type_symbol']),
    )
    self._fract_x = Parameter(
        name='fract_x',
        description='Fractional x-coordinate of the atom site within the unit cell.',
        value_spec=AttributeSpec(
            default=0.0,
            validator=RangeValidator(),
        ),
        cif_handler=CifHandler(names=['_atom_site.fract_x']),
    )
    self._fract_y = Parameter(
        name='fract_y',
        description='Fractional y-coordinate of the atom site within the unit cell.',
        value_spec=AttributeSpec(
            default=0.0,
            validator=RangeValidator(),
        ),
        cif_handler=CifHandler(names=['_atom_site.fract_y']),
    )
    self._fract_z = Parameter(
        name='fract_z',
        description='Fractional z-coordinate of the atom site within the unit cell.',
        value_spec=AttributeSpec(
            default=0.0,
            validator=RangeValidator(),
        ),
        cif_handler=CifHandler(names=['_atom_site.fract_z']),
    )
    self._wyckoff_letter = StringDescriptor(
        name='wyckoff_letter',
        description='Wyckoff letter indicating the symmetry of the '
        'atom site within the space group.',
        value_spec=AttributeSpec(
            default=self._wyckoff_letter_default_value,
            validator=MembershipValidator(allowed=self._wyckoff_letter_allowed_values),
        ),
        cif_handler=CifHandler(
            names=[
                '_atom_site.Wyckoff_letter',
                '_atom_site.Wyckoff_symbol',
            ]
        ),
    )
    self._occupancy = Parameter(
        name='occupancy',
        description='Occupancy of the atom site, representing the '
        'fraction of the site occupied by the atom type.',
        value_spec=AttributeSpec(
            default=1.0,
            validator=RangeValidator(),
        ),
        cif_handler=CifHandler(names=['_atom_site.occupancy']),
    )
    self._b_iso = Parameter(
        name='b_iso',
        description='Isotropic atomic displacement parameter (ADP) for the atom site.',
        units='Ų',
        value_spec=AttributeSpec(
            default=0.0,
            validator=RangeValidator(ge=0.0),
        ),
        cif_handler=CifHandler(names=['_atom_site.B_iso_or_equiv']),
    )
    self._adp_type = StringDescriptor(
        name='adp_type',
        description='Type of atomic displacement parameter (ADP) '
        'used (e.g., Biso, Uiso, Uani, Bani).',
        value_spec=AttributeSpec(
            default='Biso',
            validator=MembershipValidator(allowed=['Biso']),
        ),
        cif_handler=CifHandler(names=['_atom_site.adp_type']),
    )

    self._identity.category_code = 'atom_site'
    self._identity.category_entry_name = lambda: str(self.label.value)
adp_type property writable

ADP type used (e.g., Biso, Uiso, Uani, Bani).

Reading this property returns the underlying StringDescriptor object. Assigning to it updates the parameter value.

b_iso property writable

Isotropic ADP for the atom site (Ų).

Reading this property returns the underlying Parameter object. Assigning to it updates the parameter value.

fract_x property writable

Fractional x-coordinate of the atom site within the unit cell.

Reading this property returns the underlying Parameter object. Assigning to it updates the parameter value.

fract_y property writable

Fractional y-coordinate of the atom site within the unit cell.

Reading this property returns the underlying Parameter object. Assigning to it updates the parameter value.

fract_z property writable

Fractional z-coordinate of the atom site within the unit cell.

Reading this property returns the underlying Parameter object. Assigning to it updates the parameter value.

label property writable

Unique identifier for the atom site.

Reading this property returns the underlying StringDescriptor object. Assigning to it updates the parameter value.

occupancy property writable

Occupancy fraction of the atom type at this site.

Reading this property returns the underlying Parameter object. Assigning to it updates the parameter value.

type_symbol property writable

Chemical symbol of the atom at this site.

Reading this property returns the underlying StringDescriptor object. Assigning to it updates the parameter value.

wyckoff_letter property writable

Wyckoff letter for the atom site symmetry position.

Reading this property returns the underlying StringDescriptor object. Assigning to it updates the parameter value.

AtomSites

Bases: CategoryCollection

Collection of :class:AtomSite instances.

Source code in src/easydiffraction/datablocks/structure/categories/atom_sites/default.py
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
@AtomSitesFactory.register
class AtomSites(CategoryCollection):
    """Collection of :class:`AtomSite` instances."""

    type_info = TypeInfo(
        tag='default',
        description='Atom sites collection',
    )

    def __init__(self) -> None:
        """Initialise an empty atom-sites collection."""
        super().__init__(item_type=AtomSite)

    # ------------------------------------------------------------------
    #  Private helper methods
    # ------------------------------------------------------------------

    def _apply_atomic_coordinates_symmetry_constraints(self) -> None:
        """
        Apply symmetry rules to fractional coordinates of every site.

        Uses the parent structure's space-group symbol, IT coordinate
        system code and each atom's Wyckoff letter.  Atoms without a
        Wyckoff letter are silently skipped.
        """
        structure = self._parent
        space_group_name = structure.space_group.name_h_m.value
        space_group_coord_code = structure.space_group.it_coordinate_system_code.value
        for atom in self._items:
            dummy_atom = {
                'fract_x': atom.fract_x.value,
                'fract_y': atom.fract_y.value,
                'fract_z': atom.fract_z.value,
            }
            wl = atom.wyckoff_letter.value
            if not wl:
                # TODO: Decide how to handle this case
                continue
            ecr.apply_atom_site_symmetry_constraints(
                atom_site=dummy_atom,
                name_hm=space_group_name,
                coord_code=space_group_coord_code,
                wyckoff_letter=wl,
            )
            atom.fract_x.value = dummy_atom['fract_x']
            atom.fract_y.value = dummy_atom['fract_y']
            atom.fract_z.value = dummy_atom['fract_z']

    def _update(
        self,
        called_by_minimizer: bool = False,
    ) -> None:
        """
        Recalculate atom sites after a change.

        Parameters
        ----------
        called_by_minimizer : bool, default=False
            Whether the update was triggered by the fitting minimizer.
            Currently unused.
        """
        del called_by_minimizer

        self._apply_atomic_coordinates_symmetry_constraints()
__init__()

Initialise an empty atom-sites collection.

Source code in src/easydiffraction/datablocks/structure/categories/atom_sites/default.py
326
327
328
def __init__(self) -> None:
    """Initialise an empty atom-sites collection."""
    super().__init__(item_type=AtomSite)

factory

Atom-sites factory — delegates entirely to FactoryBase.

AtomSitesFactory

Bases: FactoryBase

Create atom-sites collections by tag.

Source code in src/easydiffraction/datablocks/structure/categories/atom_sites/factory.py
10
11
12
13
14
15
class AtomSitesFactory(FactoryBase):
    """Create atom-sites collections by tag."""

    _default_rules = {
        frozenset(): 'default',
    }

cell

default

Unit cell parameters category for structures.

Cell

Bases: CategoryItem

Unit cell with lengths a, b, c and angles alpha, beta, gamma.

All six lattice parameters are exposed as :class:Parameter descriptors supporting validation, fitting and CIF serialization.

Source code in src/easydiffraction/datablocks/structure/categories/cell/default.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
@CellFactory.register
class Cell(CategoryItem):
    """
    Unit cell with lengths a, b, c and angles alpha, beta, gamma.

    All six lattice parameters are exposed as :class:`Parameter`
    descriptors supporting validation, fitting and CIF serialization.
    """

    type_info = TypeInfo(
        tag='default',
        description='Unit cell parameters',
    )

    def __init__(self) -> None:
        """Initialise the unit cell with default parameter values."""
        super().__init__()

        self._length_a = Parameter(
            name='length_a',
            description='Length of the a axis of the unit cell',
            units='Ã…',
            value_spec=AttributeSpec(
                default=10.0,
                validator=RangeValidator(ge=0, le=1000),
            ),
            cif_handler=CifHandler(names=['_cell.length_a']),
        )
        self._length_b = Parameter(
            name='length_b',
            description='Length of the b axis of the unit cell',
            units='Ã…',
            value_spec=AttributeSpec(
                default=10.0,
                validator=RangeValidator(ge=0, le=1000),
            ),
            cif_handler=CifHandler(names=['_cell.length_b']),
        )
        self._length_c = Parameter(
            name='length_c',
            description='Length of the c axis of the unit cell',
            units='Ã…',
            value_spec=AttributeSpec(
                default=10.0,
                validator=RangeValidator(ge=0, le=1000),
            ),
            cif_handler=CifHandler(names=['_cell.length_c']),
        )
        self._angle_alpha = Parameter(
            name='angle_alpha',
            description='Angle between edges b and c',
            units='deg',
            value_spec=AttributeSpec(
                default=90.0,
                validator=RangeValidator(ge=0, le=180),
            ),
            cif_handler=CifHandler(names=['_cell.angle_alpha']),
        )
        self._angle_beta = Parameter(
            name='angle_beta',
            description='Angle between edges a and c',
            units='deg',
            value_spec=AttributeSpec(
                default=90.0,
                validator=RangeValidator(ge=0, le=180),
            ),
            cif_handler=CifHandler(names=['_cell.angle_beta']),
        )
        self._angle_gamma = Parameter(
            name='angle_gamma',
            description='Angle between edges a and b',
            units='deg',
            value_spec=AttributeSpec(
                default=90.0,
                validator=RangeValidator(ge=0, le=180),
            ),
            cif_handler=CifHandler(names=['_cell.angle_gamma']),
        )

        self._identity.category_code = 'cell'

    # ------------------------------------------------------------------
    #  Private helper methods
    # ------------------------------------------------------------------

    def _apply_cell_symmetry_constraints(self) -> None:
        """
        Apply symmetry constraints to cell parameters in place.

        Uses the parent structure's space-group symbol to determine
        which lattice parameters are dependent and sets them
        accordingly.
        """
        dummy_cell = {
            'lattice_a': self.length_a.value,
            'lattice_b': self.length_b.value,
            'lattice_c': self.length_c.value,
            'angle_alpha': self.angle_alpha.value,
            'angle_beta': self.angle_beta.value,
            'angle_gamma': self.angle_gamma.value,
        }
        space_group_name = self._parent.space_group.name_h_m.value

        ecr.apply_cell_symmetry_constraints(
            cell=dummy_cell,
            name_hm=space_group_name,
        )

        self.length_a.value = dummy_cell['lattice_a']
        self.length_b.value = dummy_cell['lattice_b']
        self.length_c.value = dummy_cell['lattice_c']
        self.angle_alpha.value = dummy_cell['angle_alpha']
        self.angle_beta.value = dummy_cell['angle_beta']
        self.angle_gamma.value = dummy_cell['angle_gamma']

    def _update(
        self,
        called_by_minimizer: bool = False,
    ) -> None:
        """
        Recalculate cell parameters after a change.

        Parameters
        ----------
        called_by_minimizer : bool, default=False
            Whether the update was triggered by the fitting minimizer.
            Currently unused.
        """
        del called_by_minimizer  # TODO: ???

        self._apply_cell_symmetry_constraints()

    # ------------------------------------------------------------------
    #  Public properties
    # ------------------------------------------------------------------

    @property
    def length_a(self) -> Parameter:
        """
        Length of the a axis of the unit cell (Ã…).

        Reading this property returns the underlying ``Parameter``
        object. Assigning to it updates the parameter value.
        """
        return self._length_a

    @length_a.setter
    def length_a(self, value: float) -> None:
        self._length_a.value = value

    @property
    def length_b(self) -> Parameter:
        """
        Length of the b axis of the unit cell (Ã…).

        Reading this property returns the underlying ``Parameter``
        object. Assigning to it updates the parameter value.
        """
        return self._length_b

    @length_b.setter
    def length_b(self, value: float) -> None:
        self._length_b.value = value

    @property
    def length_c(self) -> Parameter:
        """
        Length of the c axis of the unit cell (Ã…).

        Reading this property returns the underlying ``Parameter``
        object. Assigning to it updates the parameter value.
        """
        return self._length_c

    @length_c.setter
    def length_c(self, value: float) -> None:
        self._length_c.value = value

    @property
    def angle_alpha(self) -> Parameter:
        """
        Angle between edges b and c (deg).

        Reading this property returns the underlying ``Parameter``
        object. Assigning to it updates the parameter value.
        """
        return self._angle_alpha

    @angle_alpha.setter
    def angle_alpha(self, value: float) -> None:
        self._angle_alpha.value = value

    @property
    def angle_beta(self) -> Parameter:
        """
        Angle between edges a and c (deg).

        Reading this property returns the underlying ``Parameter``
        object. Assigning to it updates the parameter value.
        """
        return self._angle_beta

    @angle_beta.setter
    def angle_beta(self, value: float) -> None:
        self._angle_beta.value = value

    @property
    def angle_gamma(self) -> Parameter:
        """
        Angle between edges a and b (deg).

        Reading this property returns the underlying ``Parameter``
        object. Assigning to it updates the parameter value.
        """
        return self._angle_gamma

    @angle_gamma.setter
    def angle_gamma(self, value: float) -> None:
        self._angle_gamma.value = value
__init__()

Initialise the unit cell with default parameter values.

Source code in src/easydiffraction/datablocks/structure/categories/cell/default.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def __init__(self) -> None:
    """Initialise the unit cell with default parameter values."""
    super().__init__()

    self._length_a = Parameter(
        name='length_a',
        description='Length of the a axis of the unit cell',
        units='Ã…',
        value_spec=AttributeSpec(
            default=10.0,
            validator=RangeValidator(ge=0, le=1000),
        ),
        cif_handler=CifHandler(names=['_cell.length_a']),
    )
    self._length_b = Parameter(
        name='length_b',
        description='Length of the b axis of the unit cell',
        units='Ã…',
        value_spec=AttributeSpec(
            default=10.0,
            validator=RangeValidator(ge=0, le=1000),
        ),
        cif_handler=CifHandler(names=['_cell.length_b']),
    )
    self._length_c = Parameter(
        name='length_c',
        description='Length of the c axis of the unit cell',
        units='Ã…',
        value_spec=AttributeSpec(
            default=10.0,
            validator=RangeValidator(ge=0, le=1000),
        ),
        cif_handler=CifHandler(names=['_cell.length_c']),
    )
    self._angle_alpha = Parameter(
        name='angle_alpha',
        description='Angle between edges b and c',
        units='deg',
        value_spec=AttributeSpec(
            default=90.0,
            validator=RangeValidator(ge=0, le=180),
        ),
        cif_handler=CifHandler(names=['_cell.angle_alpha']),
    )
    self._angle_beta = Parameter(
        name='angle_beta',
        description='Angle between edges a and c',
        units='deg',
        value_spec=AttributeSpec(
            default=90.0,
            validator=RangeValidator(ge=0, le=180),
        ),
        cif_handler=CifHandler(names=['_cell.angle_beta']),
    )
    self._angle_gamma = Parameter(
        name='angle_gamma',
        description='Angle between edges a and b',
        units='deg',
        value_spec=AttributeSpec(
            default=90.0,
            validator=RangeValidator(ge=0, le=180),
        ),
        cif_handler=CifHandler(names=['_cell.angle_gamma']),
    )

    self._identity.category_code = 'cell'
angle_alpha property writable

Angle between edges b and c (deg).

Reading this property returns the underlying Parameter object. Assigning to it updates the parameter value.

angle_beta property writable

Angle between edges a and c (deg).

Reading this property returns the underlying Parameter object. Assigning to it updates the parameter value.

angle_gamma property writable

Angle between edges a and b (deg).

Reading this property returns the underlying Parameter object. Assigning to it updates the parameter value.

length_a property writable

Length of the a axis of the unit cell (Ã…).

Reading this property returns the underlying Parameter object. Assigning to it updates the parameter value.

length_b property writable

Length of the b axis of the unit cell (Ã…).

Reading this property returns the underlying Parameter object. Assigning to it updates the parameter value.

length_c property writable

Length of the c axis of the unit cell (Ã…).

Reading this property returns the underlying Parameter object. Assigning to it updates the parameter value.

factory

Cell factory — delegates entirely to FactoryBase.

CellFactory

Bases: FactoryBase

Create unit-cell categories by tag.

Source code in src/easydiffraction/datablocks/structure/categories/cell/factory.py
10
11
12
13
14
15
class CellFactory(FactoryBase):
    """Create unit-cell categories by tag."""

    _default_rules = {
        frozenset(): 'default',
    }

space_group

default

Space group category for crystallographic structures.

SpaceGroup

Bases: CategoryItem

Space group with H-M symbol and IT coordinate system code.

Holds the space-group symbol (name_h_m) and the International Tables coordinate-system qualifier (it_coordinate_system_code). Changing the symbol automatically resets the coordinate-system code to the first allowed value for the new group.

Source code in src/easydiffraction/datablocks/structure/categories/space_group/default.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
@SpaceGroupFactory.register
class SpaceGroup(CategoryItem):
    """
    Space group with H-M symbol and IT coordinate system code.

    Holds the space-group symbol (``name_h_m``) and the International
    Tables coordinate-system qualifier (``it_coordinate_system_code``).
    Changing the symbol automatically resets the coordinate-system code
    to the first allowed value for the new group.
    """

    type_info = TypeInfo(
        tag='default',
        description='Space group symmetry',
    )

    def __init__(self) -> None:
        """Initialise the space group with default values."""
        super().__init__()

        self._name_h_m = StringDescriptor(
            name='name_h_m',
            description='Hermann-Mauguin symbol of the space group.',
            value_spec=AttributeSpec(
                default='P 1',
                validator=MembershipValidator(
                    allowed=lambda: self._name_h_m_allowed_values,
                ),
            ),
            cif_handler=CifHandler(
                # TODO: Keep only version with "." and automate ...
                names=[
                    '_space_group.name_H-M_alt',
                    '_space_group_name_H-M_alt',
                    '_symmetry.space_group_name_H-M',
                    '_symmetry_space_group_name_H-M',
                ]
            ),
        )
        self._it_coordinate_system_code = StringDescriptor(
            name='it_coordinate_system_code',
            description='A qualifier identifying which setting in IT is used.',
            value_spec=AttributeSpec(
                default=lambda: self._it_coordinate_system_code_default_value,
                validator=MembershipValidator(
                    allowed=lambda: self._it_coordinate_system_code_allowed_values
                ),
            ),
            cif_handler=CifHandler(
                names=[
                    '_space_group.IT_coordinate_system_code',
                    '_space_group_IT_coordinate_system_code',
                    '_symmetry.IT_coordinate_system_code',
                    '_symmetry_IT_coordinate_system_code',
                ]
            ),
        )

        self._identity.category_code = 'space_group'

    # ------------------------------------------------------------------
    #  Private helper methods
    # ------------------------------------------------------------------

    def _reset_it_coordinate_system_code(self) -> None:
        """Reset IT coordinate system code to default for this group."""
        self._it_coordinate_system_code.value = self._it_coordinate_system_code_default_value

    @property
    def _name_h_m_allowed_values(self) -> list[str]:
        """
        Return the list of recognised Hermann–Mauguin short symbols.

        Returns
        -------
        list[str]
            All short H-M symbols known to *cryspy*.
        """
        return ACCESIBLE_NAME_HM_SHORT

    @property
    def _it_coordinate_system_code_allowed_values(self) -> list[str]:
        """
        Return allowed IT coordinate system codes for the current group.

        Returns
        -------
        list[str]
            Coordinate-system codes, or ``['']`` when none are defined.
        """
        name = self.name_h_m.value
        it_number = get_it_number_by_name_hm_short(name)
        codes = get_it_coordinate_system_codes_by_it_number(it_number)
        codes = [str(code) for code in codes]
        return codes if codes else ['']

    @property
    def _it_coordinate_system_code_default_value(self) -> str:
        """
        Return the default IT coordinate system code.

        Returns
        -------
        str
            First element of the allowed codes list.
        """
        return self._it_coordinate_system_code_allowed_values[0]

    # ------------------------------------------------------------------
    #  Public properties
    # ------------------------------------------------------------------

    @property
    def name_h_m(self) -> StringDescriptor:
        """
        Hermann-Mauguin symbol of the space group.

        Reading this property returns the underlying
        ``StringDescriptor`` object. Assigning to it updates the
        parameter value.
        """
        return self._name_h_m

    @name_h_m.setter
    def name_h_m(self, value: str) -> None:
        self._name_h_m.value = value
        self._reset_it_coordinate_system_code()

    @property
    def it_coordinate_system_code(self) -> StringDescriptor:
        """
        A qualifier identifying which setting in IT is used.

        Reading this property returns the underlying
        ``StringDescriptor`` object. Assigning to it updates the
        parameter value.
        """
        return self._it_coordinate_system_code

    @it_coordinate_system_code.setter
    def it_coordinate_system_code(self, value: str) -> None:
        self._it_coordinate_system_code.value = value
__init__()

Initialise the space group with default values.

Source code in src/easydiffraction/datablocks/structure/categories/space_group/default.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def __init__(self) -> None:
    """Initialise the space group with default values."""
    super().__init__()

    self._name_h_m = StringDescriptor(
        name='name_h_m',
        description='Hermann-Mauguin symbol of the space group.',
        value_spec=AttributeSpec(
            default='P 1',
            validator=MembershipValidator(
                allowed=lambda: self._name_h_m_allowed_values,
            ),
        ),
        cif_handler=CifHandler(
            # TODO: Keep only version with "." and automate ...
            names=[
                '_space_group.name_H-M_alt',
                '_space_group_name_H-M_alt',
                '_symmetry.space_group_name_H-M',
                '_symmetry_space_group_name_H-M',
            ]
        ),
    )
    self._it_coordinate_system_code = StringDescriptor(
        name='it_coordinate_system_code',
        description='A qualifier identifying which setting in IT is used.',
        value_spec=AttributeSpec(
            default=lambda: self._it_coordinate_system_code_default_value,
            validator=MembershipValidator(
                allowed=lambda: self._it_coordinate_system_code_allowed_values
            ),
        ),
        cif_handler=CifHandler(
            names=[
                '_space_group.IT_coordinate_system_code',
                '_space_group_IT_coordinate_system_code',
                '_symmetry.IT_coordinate_system_code',
                '_symmetry_IT_coordinate_system_code',
            ]
        ),
    )

    self._identity.category_code = 'space_group'
it_coordinate_system_code property writable

A qualifier identifying which setting in IT is used.

Reading this property returns the underlying StringDescriptor object. Assigning to it updates the parameter value.

name_h_m property writable

Hermann-Mauguin symbol of the space group.

Reading this property returns the underlying StringDescriptor object. Assigning to it updates the parameter value.

factory

Space-group factory — delegates entirely to FactoryBase.

SpaceGroupFactory

Bases: FactoryBase

Create space-group categories by tag.

Source code in src/easydiffraction/datablocks/structure/categories/space_group/factory.py
10
11
12
13
14
15
class SpaceGroupFactory(FactoryBase):
    """Create space-group categories by tag."""

    _default_rules = {
        frozenset(): 'default',
    }

collection

Collection of structure data blocks.

Structures

Bases: DatablockCollection

Ordered collection of :class:Structure instances.

Provides convenience add_from_* methods that mirror the :class:StructureFactory classmethods plus a bare :meth:add for inserting pre-built structures.

Source code in src/easydiffraction/datablocks/structure/collection.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class Structures(DatablockCollection):
    """
    Ordered collection of :class:`Structure` instances.

    Provides convenience ``add_from_*`` methods that mirror the
    :class:`StructureFactory` classmethods plus a bare :meth:`add` for
    inserting pre-built structures.
    """

    def __init__(self) -> None:
        """Initialise an empty structures collection."""
        super().__init__(item_type=Structure)

    # ------------------------------------------------------------------
    # Public methods
    # ------------------------------------------------------------------

    # TODO: Make abstract in DatablockCollection?
    @typechecked
    def create(
        self,
        *,
        name: str,
    ) -> None:
        """
        Create a minimal structure and add it to the collection.

        Parameters
        ----------
        name : str
            Identifier for the new structure.
        """
        structure = StructureFactory.from_scratch(name=name)
        self.add(structure)

    # TODO: Move to DatablockCollection?
    @typechecked
    def add_from_cif_str(
        self,
        cif_str: str,
    ) -> None:
        """
        Create a structure from CIF content and add it.

        Parameters
        ----------
        cif_str : str
            CIF file content as a string.
        """
        structure = StructureFactory.from_cif_str(cif_str)
        self.add(structure)

    # TODO: Move to DatablockCollection?
    @typechecked
    def add_from_cif_path(
        self,
        cif_path: str,
    ) -> None:
        """
        Create a structure from a CIF file and add it.

        Parameters
        ----------
        cif_path : str
            Filesystem path to a CIF file.
        """
        structure = StructureFactory.from_cif_path(cif_path)
        self.add(structure)

    # TODO: Move to DatablockCollection?
    def show_names(self) -> None:
        """List all structure names in the collection."""
        console.paragraph('Defined structures' + ' 🧩')
        console.print(self.names)

    # TODO: Move to DatablockCollection?
    def show_params(self) -> None:
        """Show parameters of all structures in the collection."""
        for structure in self.values():
            structure.show_params()

__init__()

Initialise an empty structures collection.

Source code in src/easydiffraction/datablocks/structure/collection.py
22
23
24
def __init__(self) -> None:
    """Initialise an empty structures collection."""
    super().__init__(item_type=Structure)

add_from_cif_path(cif_path)

Create a structure from a CIF file and add it.

Parameters:

Name Type Description Default
cif_path str

Filesystem path to a CIF file.

required
Source code in src/easydiffraction/datablocks/structure/collection.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
@typechecked
def add_from_cif_path(
    self,
    cif_path: str,
) -> None:
    """
    Create a structure from a CIF file and add it.

    Parameters
    ----------
    cif_path : str
        Filesystem path to a CIF file.
    """
    structure = StructureFactory.from_cif_path(cif_path)
    self.add(structure)

add_from_cif_str(cif_str)

Create a structure from CIF content and add it.

Parameters:

Name Type Description Default
cif_str str

CIF file content as a string.

required
Source code in src/easydiffraction/datablocks/structure/collection.py
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@typechecked
def add_from_cif_str(
    self,
    cif_str: str,
) -> None:
    """
    Create a structure from CIF content and add it.

    Parameters
    ----------
    cif_str : str
        CIF file content as a string.
    """
    structure = StructureFactory.from_cif_str(cif_str)
    self.add(structure)

create(*, name)

Create a minimal structure and add it to the collection.

Parameters:

Name Type Description Default
name str

Identifier for the new structure.

required
Source code in src/easydiffraction/datablocks/structure/collection.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@typechecked
def create(
    self,
    *,
    name: str,
) -> None:
    """
    Create a minimal structure and add it to the collection.

    Parameters
    ----------
    name : str
        Identifier for the new structure.
    """
    structure = StructureFactory.from_scratch(name=name)
    self.add(structure)

show_names()

List all structure names in the collection.

Source code in src/easydiffraction/datablocks/structure/collection.py
83
84
85
86
def show_names(self) -> None:
    """List all structure names in the collection."""
    console.paragraph('Defined structures' + ' 🧩')
    console.print(self.names)

show_params()

Show parameters of all structures in the collection.

Source code in src/easydiffraction/datablocks/structure/collection.py
89
90
91
92
def show_params(self) -> None:
    """Show parameters of all structures in the collection."""
    for structure in self.values():
        structure.show_params()

item

base

Structure datablock item.

Structure

Bases: DatablockItem

Structure datablock item.

Source code in src/easydiffraction/datablocks/structure/item/base.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
class Structure(DatablockItem):
    """Structure datablock item."""

    def __init__(
        self,
        *,
        name: str,
    ) -> None:
        super().__init__()
        self._name = name
        self._cell_type: str = CellFactory.default_tag()
        self._cell = CellFactory.create(self._cell_type)
        self._space_group_type: str = SpaceGroupFactory.default_tag()
        self._space_group = SpaceGroupFactory.create(self._space_group_type)
        self._atom_sites_type: str = AtomSitesFactory.default_tag()
        self._atom_sites = AtomSitesFactory.create(self._atom_sites_type)
        self._identity.datablock_entry_name = lambda: self.name

    # ------------------------------------------------------------------
    # Public properties
    # ------------------------------------------------------------------

    @property
    def name(self) -> str:
        """
        Name identifier for this structure.

        Returns
        -------
        str
            The structure's name.
        """
        return self._name

    @name.setter
    @typechecked
    def name(self, new: str) -> None:
        """
        Set the name identifier for this structure.

        Parameters
        ----------
        new : str
            New name string.
        """
        self._name = new

    # ------------------------------------------------------------------
    #  Cell (switchable-category pattern)
    # ------------------------------------------------------------------

    @property
    def cell(self) -> Cell:
        """Unit-cell category for this structure."""
        return self._cell

    @cell.setter
    @typechecked
    def cell(self, new: Cell) -> None:
        """
        Replace the unit-cell category for this structure.

        Parameters
        ----------
        new : Cell
            New unit-cell instance.
        """
        self._cell = new

    @property
    def cell_type(self) -> str:
        """Tag of the active unit-cell type."""
        return self._cell_type

    @cell_type.setter
    def cell_type(self, new_type: str) -> None:
        """
        Switch to a different unit-cell type.

        Parameters
        ----------
        new_type : str
            Cell tag (e.g. ``'default'``).
        """
        supported_tags = CellFactory.supported_tags()
        if new_type not in supported_tags:
            log.warning(
                f"Unsupported cell type '{new_type}'. "
                f'Supported: {supported_tags}. '
                f"For more information, use 'show_supported_cell_types()'",
            )
            return
        self._cell = CellFactory.create(new_type)
        self._cell_type = new_type
        console.paragraph(f"Cell type for structure '{self.name}' changed to")
        console.print(new_type)

    def show_supported_cell_types(self) -> None:
        """Print a table of supported unit-cell types."""
        CellFactory.show_supported()

    def show_current_cell_type(self) -> None:
        """Print the currently used unit-cell type."""
        console.paragraph('Current cell type')
        console.print(self.cell_type)

    # ------------------------------------------------------------------
    #  Space group (switchable-category pattern)
    # ------------------------------------------------------------------

    @property
    def space_group(self) -> SpaceGroup:
        """Space-group category for this structure."""
        return self._space_group

    @space_group.setter
    @typechecked
    def space_group(self, new: SpaceGroup) -> None:
        """
        Replace the space-group category for this structure.

        Parameters
        ----------
        new : SpaceGroup
            New space-group instance.
        """
        self._space_group = new

    @property
    def space_group_type(self) -> str:
        """Tag of the active space-group type."""
        return self._space_group_type

    @space_group_type.setter
    def space_group_type(self, new_type: str) -> None:
        """
        Switch to a different space-group type.

        Parameters
        ----------
        new_type : str
            Space-group tag (e.g. ``'default'``).
        """
        supported_tags = SpaceGroupFactory.supported_tags()
        if new_type not in supported_tags:
            log.warning(
                f"Unsupported space group type '{new_type}'. "
                f'Supported: {supported_tags}. '
                f"For more information, use 'show_supported_space_group_types()'",
            )
            return
        self._space_group = SpaceGroupFactory.create(new_type)
        self._space_group_type = new_type
        console.paragraph(f"Space group type for structure '{self.name}' changed to")
        console.print(new_type)

    def show_supported_space_group_types(self) -> None:
        """Print a table of supported space-group types."""
        SpaceGroupFactory.show_supported()

    def show_current_space_group_type(self) -> None:
        """Print the currently used space-group type."""
        console.paragraph('Current space group type')
        console.print(self.space_group_type)

    # ------------------------------------------------------------------
    #  Atom sites (switchable-category pattern)
    # ------------------------------------------------------------------

    @property
    def atom_sites(self) -> AtomSites:
        """Atom-sites collection for this structure."""
        return self._atom_sites

    @atom_sites.setter
    @typechecked
    def atom_sites(self, new: AtomSites) -> None:
        """
        Replace the atom-sites collection for this structure.

        Parameters
        ----------
        new : AtomSites
            New atom-sites collection.
        """
        self._atom_sites = new

    @property
    def atom_sites_type(self) -> str:
        """Tag of the active atom-sites collection type."""
        return self._atom_sites_type

    @atom_sites_type.setter
    def atom_sites_type(self, new_type: str) -> None:
        """
        Switch to a different atom-sites collection type.

        Parameters
        ----------
        new_type : str
            Atom-sites tag (e.g. ``'default'``).
        """
        supported_tags = AtomSitesFactory.supported_tags()
        if new_type not in supported_tags:
            log.warning(
                f"Unsupported atom sites type '{new_type}'. "
                f'Supported: {supported_tags}. '
                f"For more information, use 'show_supported_atom_sites_types()'",
            )
            return
        self._atom_sites = AtomSitesFactory.create(new_type)
        self._atom_sites_type = new_type
        console.paragraph(f"Atom sites type for structure '{self.name}' changed to")
        console.print(new_type)

    def show_supported_atom_sites_types(self) -> None:
        """Print a table of supported atom-sites collection types."""
        AtomSitesFactory.show_supported()

    def show_current_atom_sites_type(self) -> None:
        """Print the currently used atom-sites collection type."""
        console.paragraph('Current atom sites type')
        console.print(self.atom_sites_type)

    # ------------------------------------------------------------------
    # Public methods
    # ------------------------------------------------------------------

    def show(self) -> None:
        """Display an ASCII projection of the structure in 2D."""
        console.paragraph(f"Structure 🧩 '{self.name}'")
        console.print('Not implemented yet.')

    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)
atom_sites property writable

Atom-sites collection for this structure.

atom_sites_type property writable

Tag of the active atom-sites collection type.

cell property writable

Unit-cell category for this structure.

cell_type property writable

Tag of the active unit-cell type.

name property writable

Name identifier for this structure.

Returns:

Type Description
str

The structure's name.

show()

Display an ASCII projection of the structure in 2D.

Source code in src/easydiffraction/datablocks/structure/item/base.py
247
248
249
250
def show(self) -> None:
    """Display an ASCII projection of the structure in 2D."""
    console.paragraph(f"Structure 🧩 '{self.name}'")
    console.print('Not implemented yet.')
show_as_cif()

Render the CIF text for this structure in the terminal.

Source code in src/easydiffraction/datablocks/structure/item/base.py
252
253
254
255
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)
show_current_atom_sites_type()

Print the currently used atom-sites collection type.

Source code in src/easydiffraction/datablocks/structure/item/base.py
238
239
240
241
def show_current_atom_sites_type(self) -> None:
    """Print the currently used atom-sites collection type."""
    console.paragraph('Current atom sites type')
    console.print(self.atom_sites_type)
show_current_cell_type()

Print the currently used unit-cell type.

Source code in src/easydiffraction/datablocks/structure/item/base.py
120
121
122
123
def show_current_cell_type(self) -> None:
    """Print the currently used unit-cell type."""
    console.paragraph('Current cell type')
    console.print(self.cell_type)
show_current_space_group_type()

Print the currently used space-group type.

Source code in src/easydiffraction/datablocks/structure/item/base.py
179
180
181
182
def show_current_space_group_type(self) -> None:
    """Print the currently used space-group type."""
    console.paragraph('Current space group type')
    console.print(self.space_group_type)
show_supported_atom_sites_types()

Print a table of supported atom-sites collection types.

Source code in src/easydiffraction/datablocks/structure/item/base.py
234
235
236
def show_supported_atom_sites_types(self) -> None:
    """Print a table of supported atom-sites collection types."""
    AtomSitesFactory.show_supported()
show_supported_cell_types()

Print a table of supported unit-cell types.

Source code in src/easydiffraction/datablocks/structure/item/base.py
116
117
118
def show_supported_cell_types(self) -> None:
    """Print a table of supported unit-cell types."""
    CellFactory.show_supported()
show_supported_space_group_types()

Print a table of supported space-group types.

Source code in src/easydiffraction/datablocks/structure/item/base.py
175
176
177
def show_supported_space_group_types(self) -> None:
    """Print a table of supported space-group types."""
    SpaceGroupFactory.show_supported()
space_group property writable

Space-group category for this structure.

space_group_type property writable

Tag of the active space-group type.

factory

Factory for creating structure instances from various inputs.

Provides individual class methods for each creation pathway: from_scratch, from_cif_path, or from_cif_str.

StructureFactory

Create :class:Structure instances from supported inputs.

Source code in src/easydiffraction/datablocks/structure/item/factory.py
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
class StructureFactory:
    """Create :class:`Structure` instances from supported inputs."""

    def __init__(self) -> None:
        log.error(
            'Structure objects must be created using class methods such as '
            '`StructureFactory.from_cif_str(...)`, etc.'
        )

    # ------------------------------------------------------------------
    # Private helper methods
    # ------------------------------------------------------------------

    @classmethod
    # TODO: @typechecked fails to find gemmi?
    def _from_gemmi_block(
        cls,
        block: gemmi.cif.Block,
    ) -> Structure:
        """
        Build a structure from a single *gemmi* CIF block.

        Parameters
        ----------
        block : gemmi.cif.Block
            Parsed CIF data block.

        Returns
        -------
        Structure
            A fully populated structure instance.
        """
        name = name_from_block(block)
        structure = Structure(name=name)
        for category in structure.categories:
            category.from_cif(block)
        return structure

    # ------------------------------------------------------------------
    # Public methods
    # ------------------------------------------------------------------

    @classmethod
    @typechecked
    def from_scratch(
        cls,
        *,
        name: str,
    ) -> Structure:
        """
        Create a minimal default structure.

        Parameters
        ----------
        name : str
            Identifier for the new structure.

        Returns
        -------
        Structure
            An empty structure with default categories.
        """
        return Structure(name=name)

    # TODO: add minimal default configuration for missing parameters
    @classmethod
    @typechecked
    def from_cif_str(
        cls,
        cif_str: str,
    ) -> Structure:
        """
        Create a structure by parsing a CIF string.

        Parameters
        ----------
        cif_str : str
            Raw CIF content.

        Returns
        -------
        Structure
            A populated structure instance.
        """
        doc = document_from_string(cif_str)
        block = pick_sole_block(doc)
        return cls._from_gemmi_block(block)

    # TODO: Read content and call self.from_cif_str
    @classmethod
    @typechecked
    def from_cif_path(
        cls,
        cif_path: str,
    ) -> Structure:
        """
        Create a structure by reading and parsing a CIF file.

        Parameters
        ----------
        cif_path : str
            Filesystem path to a CIF file.

        Returns
        -------
        Structure
            A populated structure instance.
        """
        doc = document_from_path(cif_path)
        block = pick_sole_block(doc)
        return cls._from_gemmi_block(block)
from_cif_path(cif_path) classmethod

Create a structure by reading and parsing a CIF file.

Parameters:

Name Type Description Default
cif_path str

Filesystem path to a CIF file.

required

Returns:

Type Description
Structure

A populated structure instance.

Source code in src/easydiffraction/datablocks/structure/item/factory.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
@classmethod
@typechecked
def from_cif_path(
    cls,
    cif_path: str,
) -> Structure:
    """
    Create a structure by reading and parsing a CIF file.

    Parameters
    ----------
    cif_path : str
        Filesystem path to a CIF file.

    Returns
    -------
    Structure
        A populated structure instance.
    """
    doc = document_from_path(cif_path)
    block = pick_sole_block(doc)
    return cls._from_gemmi_block(block)
from_cif_str(cif_str) classmethod

Create a structure by parsing a CIF string.

Parameters:

Name Type Description Default
cif_str str

Raw CIF content.

required

Returns:

Type Description
Structure

A populated structure instance.

Source code in src/easydiffraction/datablocks/structure/item/factory.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
@classmethod
@typechecked
def from_cif_str(
    cls,
    cif_str: str,
) -> Structure:
    """
    Create a structure by parsing a CIF string.

    Parameters
    ----------
    cif_str : str
        Raw CIF content.

    Returns
    -------
    Structure
        A populated structure instance.
    """
    doc = document_from_string(cif_str)
    block = pick_sole_block(doc)
    return cls._from_gemmi_block(block)
from_scratch(*, name) classmethod

Create a minimal default structure.

Parameters:

Name Type Description Default
name str

Identifier for the new structure.

required

Returns:

Type Description
Structure

An empty structure with default categories.

Source code in src/easydiffraction/datablocks/structure/item/factory.py
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@classmethod
@typechecked
def from_scratch(
    cls,
    *,
    name: str,
) -> Structure:
    """
    Create a minimal default structure.

    Parameters
    ----------
    name : str
        Identifier for the new structure.

    Returns
    -------
    Structure
        An empty structure with default categories.
    """
    return Structure(name=name)