Coverage for src/crispatt/_fsd.py: 100%

41 statements  

« prev     ^ index     » next       coverage.py v7.15.3, created at 2026-09-03 17:23 +1200

1import abc 

2import functools 

3import typing 

4 

5import attrs 

6import numpy as np 

7 

8# TODO: remove commented out blocks when edges and noise handling has been 

9# implemented somewhere else. 

10 

11 

12@attrs.frozen 

13class _FSDHandler(abc.ABC): 

14 # noise_handler: NoiseHandler 

15 # concentration: float 

16 

17 @typing.final 

18 def compute_lengths(self, n_floes) -> np.ndarray: 

19 lengths = self._compute_lengths(n_floes) 

20 lengths = self._finalise_lengths(lengths) 

21 return lengths 

22 

23 @abc.abstractmethod 

24 def _compute_lengths(self, n_floes: int) -> np.ndarray: 

25 """Compute floe lengths according to the distribution. 

26 

27 Parameters 

28 ---------- 

29 n_floes : int 

30 Number of finite floes. 

31 

32 Returns 

33 ------- 

34 np.ndarray 

35 1d array of floe lengths, in m. 

36 

37 """ 

38 

39 @property 

40 @abc.abstractmethod 

41 def average_length(self) -> float: 

42 """Expected value of the distribution, in m. 

43 

44 Returns 

45 ------- 

46 float 

47 

48 """ 

49 

50 def _finalise_lengths(self, lengths): 

51 lengths[-1] = np.inf 

52 return lengths 

53 

54 # def compute_lengths(self, n_floes) -> np.ndarray: 

55 # lengths = self._compute_lengths(n_floes) 

56 # lengths += self.noise_handler.get_noise() 

57 # return lengths 

58 

59 # def compute_edges(self, floe_lengths: np.ndarray) -> np.ndarray: 

60 # floe_edges = np.zeros(floe_lengths.size) 

61 # floe_edges[1:] = np.cumsum(floe_lengths[:-1]) 

62 # return floe_edges 

63 

64 # def build_floe_array( 

65 # self, n_floes: int, ice_edge: float, ices: Iterable[Ice] 

66 # ) -> list[Floe]: 

67 # lengths = self.compute_lengths(n_floes) 

68 # edges = self.compute_edges(lengths / self.concentration) + ice_edge 

69 # return [Floe(_e, _l, ice) for _e, _l, ice, in zip(edges, lengths, ices)] 

70 

71 

72@attrs.frozen 

73class MonoFSD(_FSDHandler): 

74 length: float 

75 

76 @property 

77 def average_length(self): 

78 return self.length 

79 

80 def _compute_lengths(self, n_floes: int) -> np.ndarray: 

81 lengths = self.length * np.ones(n_floes + 1, dtype=float) 

82 return lengths 

83 

84 

85@attrs.frozen 

86class RampFSD(_FSDHandler): 

87 length_left: float 

88 length_right: float 

89 

90 @functools.cached_property 

91 def average_length(self) -> float: 

92 return (self.length_left + self.length_right) / 2 

93 

94 def _ramp(self, n_floes: int) -> np.ndarray: 

95 slope = (self.length_right - self.length_left) / n_floes 

96 lengths = np.arange(n_floes + 1, dtype=float) 

97 lengths *= slope 

98 lengths += self.length_left 

99 return lengths 

100 

101 def _compute_lengths(self, n_floes: int) -> np.ndarray: 

102 return self._ramp(n_floes)