Skip to main content

sim_lib_interference_solve/
multitone_error.rs

1//! Stable diagnostics for certified multi-tone composition.
2
3use std::fmt;
4
5use sim_lib_interference_core::SamplingPlane;
6
7use crate::ReferenceSolveError;
8
9/// A multi-tone study or observation was refused without a partial result.
10#[derive(Clone, Debug, PartialEq)]
11pub enum MultiToneError {
12    /// A tone's scale was zero, negative, or non-finite.
13    InvalidWeight {
14        /// Frequency of the rejected component.
15        frequency_hz: f64,
16        /// Rejected scale.
17        weight: f64,
18    },
19    /// An independently certified component solve failed.
20    ComponentSolve {
21        /// Frequency of the failed component.
22        frequency_hz: f64,
23        /// Exact reference-solver diagnostic.
24        cause: Box<ReferenceSolveError>,
25    },
26    /// A study must contain at least one tone.
27    EmptyStudy,
28    /// Equal frequencies must be modeled as one coherent problem.
29    DuplicateFrequency {
30        /// Rejected repeated frequency.
31        frequency_hz: f64,
32    },
33    /// One component was solved on different physical sample geometry.
34    MismatchedPlane {
35        /// Frequency of the mismatched component.
36        frequency_hz: f64,
37        /// Plane established by the first component.
38        expected: Box<SamplingPlane>,
39        /// Rejected component plane.
40        actual: Box<SamplingPlane>,
41    },
42    /// A shared observation time was non-finite.
43    InvalidSeconds {
44        /// Rejected time.
45        seconds: f64,
46    },
47    /// Frequency-to-angular-time conversion overflowed.
48    NonFiniteAngularTime {
49        /// Component frequency.
50        frequency_hz: f64,
51        /// Requested shared time.
52        seconds: f64,
53        /// Rejected derived angular time.
54        angular_time: f64,
55    },
56    /// Weighting one component scalar produced a non-finite value.
57    NonFiniteContribution {
58        /// Component frequency.
59        frequency_hz: f64,
60        /// Zero-based row.
61        row: usize,
62        /// Zero-based column.
63        column: usize,
64        /// Rejected weighted scalar.
65        value: f64,
66    },
67    /// Compensated cross-tone scalar accumulation became non-finite.
68    NonFiniteAccumulation {
69        /// Zero-based row.
70        row: usize,
71        /// Zero-based column.
72        column: usize,
73        /// Rejected accumulated scalar.
74        value: f64,
75    },
76    /// Scalar result storage could not be reserved.
77    AllocationFailed {
78        /// Requested scalar cells.
79        cells: usize,
80    },
81    /// The set's temporal Nyquist floor could not be represented.
82    NonFiniteTemporalSamplingRequirement {
83        /// Highest component frequency.
84        highest_frequency_hz: f64,
85        /// Rejected derived Nyquist sample rate.
86        samples_per_second: f64,
87    },
88    /// Component-certificate storage could not be reserved.
89    CertificateAllocationFailed {
90        /// Number of component certificates requested.
91        tones: usize,
92    },
93}
94
95impl fmt::Display for MultiToneError {
96    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
97        match self {
98            Self::InvalidWeight {
99                frequency_hz,
100                weight,
101            } => write!(
102                formatter,
103                "tone at {frequency_hz} Hz requires a finite positive weight: {weight:?}"
104            ),
105            Self::ComponentSolve {
106                frequency_hz,
107                cause,
108            } => write!(
109                formatter,
110                "tone at {frequency_hz} Hz could not be certified: {cause}"
111            ),
112            Self::EmptyStudy => formatter.write_str("multi-tone study requires at least one tone"),
113            Self::DuplicateFrequency { frequency_hz } => write!(
114                formatter,
115                "frequency {frequency_hz} Hz occurs more than once; equal-frequency sources belong in one coherent problem"
116            ),
117            Self::MismatchedPlane {
118                frequency_hz,
119                expected,
120                actual,
121            } => write!(
122                formatter,
123                "tone at {frequency_hz} Hz uses plane {actual:?}, not the shared plane {expected:?}"
124            ),
125            Self::InvalidSeconds { seconds } => write!(
126                formatter,
127                "multi-tone observation time must be finite: {seconds:?}"
128            ),
129            Self::NonFiniteAngularTime {
130                frequency_hz,
131                seconds,
132                angular_time,
133            } => write!(
134                formatter,
135                "tone at {frequency_hz} Hz and time {seconds} s produced non-finite angular time {angular_time:?}"
136            ),
137            Self::NonFiniteContribution {
138                frequency_hz,
139                row,
140                column,
141                value,
142            } => write!(
143                formatter,
144                "tone at {frequency_hz} Hz produced non-finite weighted scalar {value:?} at ({row}, {column})"
145            ),
146            Self::NonFiniteAccumulation { row, column, value } => write!(
147                formatter,
148                "multi-tone scalar accumulation became non-finite at ({row}, {column}): {value:?}"
149            ),
150            Self::AllocationFailed { cells } => {
151                write!(
152                    formatter,
153                    "could not reserve {cells} multi-tone scalar cells"
154                )
155            }
156            Self::NonFiniteTemporalSamplingRequirement {
157                highest_frequency_hz,
158                samples_per_second,
159            } => write!(
160                formatter,
161                "highest frequency {highest_frequency_hz} Hz produced non-finite Nyquist rate {samples_per_second:?}"
162            ),
163            Self::CertificateAllocationFailed { tones } => write!(
164                formatter,
165                "could not reserve provenance for {tones} multi-tone components"
166            ),
167        }
168    }
169}
170
171impl std::error::Error for MultiToneError {
172    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
173        match self {
174            Self::ComponentSolve { cause, .. } => Some(cause.as_ref()),
175            Self::InvalidWeight { .. }
176            | Self::EmptyStudy
177            | Self::DuplicateFrequency { .. }
178            | Self::MismatchedPlane { .. }
179            | Self::InvalidSeconds { .. }
180            | Self::NonFiniteAngularTime { .. }
181            | Self::NonFiniteContribution { .. }
182            | Self::NonFiniteAccumulation { .. }
183            | Self::AllocationFailed { .. }
184            | Self::NonFiniteTemporalSamplingRequirement { .. }
185            | Self::CertificateAllocationFailed { .. } => None,
186        }
187    }
188}