Coverage for local_installation_linux/mumott/methods/projectors/base_projector.py: 94%

48 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2024-08-11 23:08 +0000

1from abc import ABC, abstractmethod 

2from typing import Tuple 

3 

4import numpy as np 

5from numpy.typing import NDArray, DTypeLike 

6from numpy import float32 

7 

8from mumott import Geometry 

9 

10 

11class Projector(ABC): 

12 """Projector for transforms of tensor fields from three-dimensional 

13 space to projection space. 

14 """ 

15 

16 def __init__(self, 

17 geometry: Geometry): 

18 self._geometry = geometry 

19 self._geometry_hash = hash(geometry) 

20 self._basis_vector_projection = np.zeros((len(self._geometry), 3), dtype=np.float64) 

21 self._basis_vector_j = np.zeros((len(self._geometry), 3), dtype=np.float64) 

22 self._basis_vector_k = np.zeros((len(self._geometry), 3), dtype=np.float64) 

23 

24 @abstractmethod 

25 def forward(self, 

26 field: NDArray, 

27 index: NDArray[int]) -> NDArray: 

28 pass 

29 

30 @abstractmethod 

31 def adjoint(self, 

32 projection: NDArray, 

33 index: NDArray[int]) -> NDArray: 

34 pass 

35 

36 def _calculate_basis_vectors(self) -> None: 

37 """ Calculates the basis vectors for the John transform, one projection vector 

38 and two coordinate vectors. """ 

39 self._basis_vector_projection = np.einsum( 

40 'kij,i->kj', self._geometry.rotations_as_array, self._geometry.p_direction_0) 

41 self._basis_vector_j = np.einsum( 

42 'kij,i->kj', self._geometry.rotations_as_array, self._geometry.j_direction_0) 

43 self._basis_vector_k = np.einsum( 

44 'kij,i->kj', self._geometry.rotations_as_array, self._geometry.k_direction_0) 

45 

46 def _check_indices_kind_is_integer(self, indices: NDArray): 

47 if indices.dtype.kind != 'i': 

48 raise TypeError('Elements of indices must be of integer kind,' 

49 f' but the provided indices have dtype.kind {indices.dtype.kind}!') 

50 

51 @property 

52 def dtype(self) -> DTypeLike: 

53 """ The dtype input fields and projections require.""" 

54 return float32 

55 

56 @property 

57 def is_dirty(self) -> bool: 

58 """ Returns ``True`` if the system geometry has changed without 

59 the projection geometry having been updated. """ 

60 return self._geometry_hash != hash(self._geometry) 

61 

62 def _update(self, force_update: bool = False) -> None: 

63 if self.is_dirty or force_update: 

64 self._calculate_basis_vectors() 

65 self._geometry_hash = hash(self._geometry) 

66 

67 @property 

68 def number_of_projections(self) -> int: 

69 """ The number of projections as defined by the length of the 

70 :class:`Geometry <mumott.Geometry>` object attached to this 

71 instance. """ 

72 self._update() 

73 return len(self._geometry) 

74 

75 @property 

76 def volume_shape(self) -> Tuple[int]: 

77 """ The shape of the volume defined by the :class:`Geometry <mumott.Geometry>` 

78 object attached to this instance, as a tuple. """ 

79 self._update() 

80 return tuple(self._geometry.volume_shape) 

81 

82 @property 

83 def projection_shape(self) -> Tuple[int]: 

84 """ The shape of each projection defined by the :class:`Geometry <mumott.Geometry>` 

85 object attached to this instance, as a tuple. """ 

86 self._update() 

87 return tuple(self._geometry.projection_shape)