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
« prev ^ index » next coverage.py v7.15.3, created at 2026-09-03 17:23 +1200
1import abc
2import functools
3import typing
5import attrs
6import numpy as np
8# TODO: remove commented out blocks when edges and noise handling has been
9# implemented somewhere else.
12@attrs.frozen
13class _FSDHandler(abc.ABC):
14 # noise_handler: NoiseHandler
15 # concentration: float
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
23 @abc.abstractmethod
24 def _compute_lengths(self, n_floes: int) -> np.ndarray:
25 """Compute floe lengths according to the distribution.
27 Parameters
28 ----------
29 n_floes : int
30 Number of finite floes.
32 Returns
33 -------
34 np.ndarray
35 1d array of floe lengths, in m.
37 """
39 @property
40 @abc.abstractmethod
41 def average_length(self) -> float:
42 """Expected value of the distribution, in m.
44 Returns
45 -------
46 float
48 """
50 def _finalise_lengths(self, lengths):
51 lengths[-1] = np.inf
52 return lengths
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
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
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)]
72@attrs.frozen
73class MonoFSD(_FSDHandler):
74 length: float
76 @property
77 def average_length(self):
78 return self.length
80 def _compute_lengths(self, n_floes: int) -> np.ndarray:
81 lengths = self.length * np.ones(n_floes + 1, dtype=float)
82 return lengths
85@attrs.frozen
86class RampFSD(_FSDHandler):
87 length_left: float
88 length_right: float
90 @functools.cached_property
91 def average_length(self) -> float:
92 return (self.length_left + self.length_right) / 2
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
101 def _compute_lengths(self, n_floes: int) -> np.ndarray:
102 return self._ramp(n_floes)