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
21 changes: 9 additions & 12 deletions src/pyFAI/crystallography/cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "06/10/2025"
__date__ = "09/01/2026"
__status__ = "production"

import os
Expand All @@ -49,6 +49,7 @@
import itertools
from math import sin, cos, sqrt, pi, ceil
from ..io.calibrant_config import CalibrantConfig, Miller, Reflection
from .space_groups import ReflectionCondition
from ..utils.decorators import deprecated

logger = logging.getLogger(__name__)
Expand All @@ -74,7 +75,9 @@ class Cell:
"P": "Primitive",
"I": "Body centered",
"F": "Face centered",
"C": "Side centered",
"A": "a-End centered",
"B": "b-End centered",
"C": "c-End centered",
"R": "Rhombohedral",
}

Expand All @@ -91,7 +94,7 @@ def __init__(
):
"""Constructor of the Cell class:

Crystalographic units are Angstrom for distances and degrees for angles !
Crystallographic units are Angstrom for distances and degrees for angles !

:param a,b,c: unit cell length in Angstrom
:param alpha, beta, gamma: unit cell angle in degrees
Expand Down Expand Up @@ -258,15 +261,9 @@ def type(self):
@type.setter
def type(self, lattice_type):
self._type = lattice_type if lattice_type in self.types else "P"
self.selection_rules = [lambda h, k, l: not (h == 0 and k == 0 and l == 0)] # noqa: E741
if self._type == "I":
self.selection_rules.append(lambda h, k, l: (h + k + l) % 2 == 0) # noqa: E741
if self._type == "F":
self.selection_rules.append(
lambda h, k, l: (h % 2 + k % 2 + l % 2) in (0, 3) # noqa: E741
)
if self._type == "R":
self.selection_rules.append(lambda h, k, l: ((h - k + l) % 3 == 0)) # noqa: E741
self.selection_rules = [ReflectionCondition.default]
if self._type != "P":
self.selection_rules.append(getattr(ReflectionCondition, f"type_{self._type}"))

get_type = deprecated(type.fset, reason="property", replacement="type", since_version="2025.07")
set_type = deprecated(type.fset, reason="property", replacement="type", since_version="2025.07")
Expand Down
54 changes: 53 additions & 1 deletion src/pyFAI/crystallography/space_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "08/10/2025"
__date__ = "12/01/2026"
__status__ = "production"


Expand All @@ -57,6 +57,58 @@ class ReflectionCondition:
Help is welcome to polish this class and fix the non-validated ones.
"""

@staticmethod
def default(h: int, k: int, l: int) -> bool: # noqa: E741
"""
Default selection rule: h=k=l=0 is forbidden
"""
return not (h == 0 and k == 0 and l == 0)

type_P = default

@staticmethod
def type_A(h: int, k: int, l: int) -> bool: # noqa: E741
"""
End-centered A type: k+l even
"""
return ((k + l) % 2 == 0)

@staticmethod
def type_B(h: int, k: int, l: int) -> bool: # noqa: E741
"""
End-centered B type: h+l even
"""
return ((h + l) % 2 == 0)

@staticmethod
def type_C(h: int, k: int, l: int) -> bool: # noqa: E741
"""
End-centered C type: h+k even
"""
return ((h + k) % 2 == 0)

@staticmethod
def type_F(h: int, k: int, l: int) -> bool: # noqa: E741
"""
Face-centered type: h,k,l all even or all odd
"""
return (h % 2 + k % 2 + l % 2) in (0, 3)

@staticmethod
def type_I(h: int, k: int, l: int) -> bool: # noqa: E741
"""
Body-centered type: h+k+l even
"""
return (h + k + l) % 2 == 0

@staticmethod
def type_R(h: int, k: int, l: int) -> bool: # noqa: E741
"""
Rhombohedral type: -h+k+l multiple of 3
http://img.chem.ucl.ac.uk/sgp/large/146bz2.htm
"""
return ((-h + k + l) % 3 == 0)

@staticmethod
def group1_P1(h: int, k: int, l: int) -> bool: # noqa: E741
"""
Expand Down
16 changes: 14 additions & 2 deletions src/pyFAI/test/test_crystallography.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "09/07/2025"
__date__ = "09/01/2026"

import unittest
import numpy
import logging
from .utilstest import UtilsTest
from ..crystallography import resolution
from ..crystallography import resolution, Cell, ReflectionCondition

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -79,6 +79,18 @@ def test_langford(self):
self.assertTrue(numpy.allclose(c.sigma(numpy.linspace(0.1,1,10)), ref))
self.assertTrue(isinstance(c.fwhm(1), float))

def test_bug_2755(self):
"Missing default selection rule for C-type cells"
phase1 = Cell.monoclinic(3, 4, 5, 115, lattice_type='C')
res1 = len(phase1.calculate_dspacing(dmin=1))

phase2 = Cell.monoclinic(3, 4, 5, 115, lattice_type='P')
res0 = len(phase2.calculate_dspacing(dmin=1))
phase2.selection_rules.append(ReflectionCondition.group5_C2)
res2 = len(phase2.calculate_dspacing(dmin=1))
self.assertEqual(res1, res2)
self.assertGreater(res0, res2)


def suite():
testsuite = unittest.TestSuite()
Expand Down
6 changes: 3 additions & 3 deletions version.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@
"gamma": 12,
"rc": 13,
"final": 15}
MAJOR = 2025
MINOR = 12
MICRO = 2
MAJOR = 2026
MINOR = 1
MICRO = 0
RELEV = "dev" # <16
SERIAL = 0 # <16

Expand Down