Edit Source

crystal.types

 1from typing import Union, Iterator, Tuple, List
 2from itertools import product
 3
 4class Point:
 5    """
 6    Represents a single point in one dimension with an associated unit.
 7    """
 8    def __init__(self, value: float, unit: str) -> None:
 9        self.value = value
10        self.unit = unit
11
12    def __repr__(self) -> str:
13        return f"Point({self.value!r}, unit={self.unit!r})"
14
15
16class Interval:
17    """
18    Represents a 1-D interval divided into a fixed number of equally spaced points.
19    """
20    def __init__(self, start: float, end: float, num_points: int, unit: str) -> None:
21        if num_points < 2:
22            raise ValueError("num_points must be >= 2 to define an interval.")
23        self.start = start
24        self.end = end
25        self.num_points = num_points
26        self.unit = unit
27
28    @property
29    def points(self) -> List[Point]:
30        """
31        Generate the list of Points spanning the interval.
32        """
33        step = (self.end - self.start) / (self.num_points - 1)
34        return [Point(self.start + i * step, unit=self.unit) for i in range(self.num_points)]
35
36    def __iter__(self) -> Iterator[Point]:
37        return iter(self.points)
38
39    def __repr__(self) -> str:
40        return (
41            f"Interval({self.start!r}, {self.end!r}, {self.num_points!r}, "
42            f"unit={self.unit!r})"
43        )
44
45# Allow either a Point or an Interval on each axis
46Axis = Union[Point, Interval]
47
48class BoxOfPoints:
49    """
50    A 3-D “box” defined by three axes (x, y, z), each of which is either
51    a single Point or an Interval. Iterating over a BoxOfPoints yields every
52    Point in the 3D grid (Cartesian product of axes).
53    """
54    def __init__(self,
55                 x_axis: Axis,
56                 y_axis: Axis,
57                 z_axis: Axis) -> None:
58        self.axes: Tuple[Axis, Axis, Axis] = (x_axis, y_axis, z_axis)
59        for ax in self.axes:
60            if not isinstance(ax, (Point, Interval)):
61                raise TypeError(
62                    f"Each axis must be a Point or Interval, got {type(ax)}"
63                )
64
65    def __iter__(self) -> Iterator[Point]:
66        # Convert each axis into a sequence of Points
67        seqs = [([axis] if isinstance(axis, Point) else axis.points)
68                for axis in self.axes]
69        # Cartesian product over x, y, z
70        for px, py, pz in product(*seqs):
71            # Preserve the unit from the first axis
72            yield Point(px.value, py.value, pz.value, unit=px.unit)
73
74    def __repr__(self) -> str:
75        return (
76            f"BoxOfPoints(x_axis={self.axes[0]!r}, "
77            f"y_axis={self.axes[1]!r}, z_axis={self.axes[2]!r})"
78        )
class Point:
 5class Point:
 6    """
 7    Represents a single point in one dimension with an associated unit.
 8    """
 9    def __init__(self, value: float, unit: str) -> None:
10        self.value = value
11        self.unit = unit
12
13    def __repr__(self) -> str:
14        return f"Point({self.value!r}, unit={self.unit!r})"

Represents a single point in one dimension with an associated unit.

Point(value: float, unit: str)
 9    def __init__(self, value: float, unit: str) -> None:
10        self.value = value
11        self.unit = unit
value
unit
class Interval:
17class Interval:
18    """
19    Represents a 1-D interval divided into a fixed number of equally spaced points.
20    """
21    def __init__(self, start: float, end: float, num_points: int, unit: str) -> None:
22        if num_points < 2:
23            raise ValueError("num_points must be >= 2 to define an interval.")
24        self.start = start
25        self.end = end
26        self.num_points = num_points
27        self.unit = unit
28
29    @property
30    def points(self) -> List[Point]:
31        """
32        Generate the list of Points spanning the interval.
33        """
34        step = (self.end - self.start) / (self.num_points - 1)
35        return [Point(self.start + i * step, unit=self.unit) for i in range(self.num_points)]
36
37    def __iter__(self) -> Iterator[Point]:
38        return iter(self.points)
39
40    def __repr__(self) -> str:
41        return (
42            f"Interval({self.start!r}, {self.end!r}, {self.num_points!r}, "
43            f"unit={self.unit!r})"
44        )

Represents a 1-D interval divided into a fixed number of equally spaced points.

Interval(start: float, end: float, num_points: int, unit: str)
21    def __init__(self, start: float, end: float, num_points: int, unit: str) -> None:
22        if num_points < 2:
23            raise ValueError("num_points must be >= 2 to define an interval.")
24        self.start = start
25        self.end = end
26        self.num_points = num_points
27        self.unit = unit
start
end
num_points
unit
points: List[Point]
29    @property
30    def points(self) -> List[Point]:
31        """
32        Generate the list of Points spanning the interval.
33        """
34        step = (self.end - self.start) / (self.num_points - 1)
35        return [Point(self.start + i * step, unit=self.unit) for i in range(self.num_points)]

Generate the list of Points spanning the interval.

Axis = typing.Union[Point, Interval]
class BoxOfPoints:
49class BoxOfPoints:
50    """
51    A 3-D “box” defined by three axes (x, y, z), each of which is either
52    a single Point or an Interval. Iterating over a BoxOfPoints yields every
53    Point in the 3D grid (Cartesian product of axes).
54    """
55    def __init__(self,
56                 x_axis: Axis,
57                 y_axis: Axis,
58                 z_axis: Axis) -> None:
59        self.axes: Tuple[Axis, Axis, Axis] = (x_axis, y_axis, z_axis)
60        for ax in self.axes:
61            if not isinstance(ax, (Point, Interval)):
62                raise TypeError(
63                    f"Each axis must be a Point or Interval, got {type(ax)}"
64                )
65
66    def __iter__(self) -> Iterator[Point]:
67        # Convert each axis into a sequence of Points
68        seqs = [([axis] if isinstance(axis, Point) else axis.points)
69                for axis in self.axes]
70        # Cartesian product over x, y, z
71        for px, py, pz in product(*seqs):
72            # Preserve the unit from the first axis
73            yield Point(px.value, py.value, pz.value, unit=px.unit)
74
75    def __repr__(self) -> str:
76        return (
77            f"BoxOfPoints(x_axis={self.axes[0]!r}, "
78            f"y_axis={self.axes[1]!r}, z_axis={self.axes[2]!r})"
79        )

A 3-D “box” defined by three axes (x, y, z), each of which is either a single Point or an Interval. Iterating over a BoxOfPoints yields every Point in the 3D grid (Cartesian product of axes).

BoxOfPoints( x_axis: Union[Point, Interval], y_axis: Union[Point, Interval], z_axis: Union[Point, Interval])
55    def __init__(self,
56                 x_axis: Axis,
57                 y_axis: Axis,
58                 z_axis: Axis) -> None:
59        self.axes: Tuple[Axis, Axis, Axis] = (x_axis, y_axis, z_axis)
60        for ax in self.axes:
61            if not isinstance(ax, (Point, Interval)):
62                raise TypeError(
63                    f"Each axis must be a Point or Interval, got {type(ax)}"
64                )
axes: Tuple[Union[Point, Interval], Union[Point, Interval], Union[Point, Interval]]