sim_lib_interference_core/
error.rs1use std::fmt;
4
5use crate::{SamplingCertificate, WorkMetric};
6
7#[derive(Clone, Debug, PartialEq)]
9pub enum InterferenceError {
10 InvalidQuantity {
12 name: &'static str,
14 value: f64,
16 },
17 InvalidDirection {
19 x: f64,
21 y: f64,
23 z: f64,
25 },
26 EmptySourceSet,
28 EmptySourceId,
30 DuplicateSourceId {
32 id: String,
34 },
35 NonFinitePropagation {
37 source_id: String,
39 name: &'static str,
41 value: f64,
43 },
44 SingularPointSample {
46 source_id: String,
48 distance_metres: f64,
50 singularity_radius_metres: f64,
52 },
53 BehindForwardPlane {
55 source_id: String,
57 signed_distance_metres: f64,
59 },
60 NonOrthogonalSamplingAxes {
62 dot_product: f64,
64 max_abs_dot_product: f64,
66 },
67 ZeroSamplingDimension {
69 name: &'static str,
71 },
72 SamplingCellCountOverflow {
74 rows: usize,
76 columns: usize,
78 },
79 InvalidSamplingCellSize {
81 axis: &'static str,
83 value: f64,
85 },
86 SamplingCellOutOfBounds {
88 row: usize,
90 column: usize,
92 rows: usize,
94 columns: usize,
96 },
97 NonFiniteSamplingMetric {
99 name: &'static str,
101 value: f64,
103 },
104 InvalidSamplingThreshold {
106 name: &'static str,
108 value: f64,
110 },
111 InconsistentSamplingThresholds {
113 resolved_min_samples_per_wavelength: f64,
115 marginal_min_samples_per_wavelength: f64,
117 resolved_max_envelope_fraction_per_cell: f64,
119 marginal_max_envelope_fraction_per_cell: f64,
121 },
122 SamplingRefused {
124 certificate: SamplingCertificate,
126 },
127 UnboundedSamplingEnvelope {
129 nearest_point_source_distance_m: f64,
131 cell_diagonal_m: f64,
133 },
134 WorkEstimateOverflow {
136 metric: WorkMetric,
138 },
139 WorkBudgetExceeded {
141 metric: WorkMetric,
143 estimate: u64,
145 limit: u64,
147 },
148}
149
150impl fmt::Display for InterferenceError {
151 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
152 match self {
153 Self::InvalidQuantity { name, value } => {
154 write!(formatter, "invalid quantity `{name}`: {value:?}")
155 }
156 Self::InvalidDirection { x, y, z } => {
157 write!(formatter, "invalid unit direction: [{x:?}, {y:?}, {z:?}]")
158 }
159 Self::EmptySourceSet => formatter.write_str("a source set cannot be empty"),
160 Self::EmptySourceId => formatter.write_str("a source id cannot be empty"),
161 Self::DuplicateSourceId { id } => {
162 write!(formatter, "duplicate source id `{id}`")
163 }
164 Self::NonFinitePropagation {
165 source_id,
166 name,
167 value,
168 } => write!(
169 formatter,
170 "source `{source_id}` produced non-finite `{name}`: {value:?}"
171 ),
172 Self::SingularPointSample {
173 source_id,
174 distance_metres,
175 singularity_radius_metres,
176 } => write!(
177 formatter,
178 "sample is singular for point source `{source_id}`: distance \
179 {distance_metres:?} m is at or inside radius \
180 {singularity_radius_metres:?} m"
181 ),
182 Self::BehindForwardPlane {
183 source_id,
184 signed_distance_metres,
185 } => write!(
186 formatter,
187 "sample is behind forward-plane source `{source_id}`: signed \
188 distance {signed_distance_metres:?} m"
189 ),
190 Self::NonOrthogonalSamplingAxes {
191 dot_product,
192 max_abs_dot_product,
193 } => write!(
194 formatter,
195 "sampling axes are not orthogonal: dot product \
196 {dot_product:?} exceeds {max_abs_dot_product:?}"
197 ),
198 Self::ZeroSamplingDimension { name } => {
199 write!(formatter, "sampling dimension `{name}` must be non-zero")
200 }
201 Self::SamplingCellCountOverflow { rows, columns } => write!(
202 formatter,
203 "sampling cell count overflows usize: {rows} rows by {columns} columns"
204 ),
205 Self::InvalidSamplingCellSize { axis, value } => write!(
206 formatter,
207 "sampling cell size on `{axis}` must be positive and finite: {value:?} m"
208 ),
209 Self::SamplingCellOutOfBounds {
210 row,
211 column,
212 rows,
213 columns,
214 } => write!(
215 formatter,
216 "sampling cell ({row}, {column}) is outside {rows} rows by {columns} columns"
217 ),
218 Self::NonFiniteSamplingMetric { name, value } => {
219 write!(
220 formatter,
221 "sampling metric `{name}` is non-finite: {value:?}"
222 )
223 }
224 Self::InvalidSamplingThreshold { name, value } => {
225 write!(formatter, "invalid sampling threshold `{name}`: {value:?}")
226 }
227 Self::InconsistentSamplingThresholds {
228 resolved_min_samples_per_wavelength,
229 marginal_min_samples_per_wavelength,
230 resolved_max_envelope_fraction_per_cell,
231 marginal_max_envelope_fraction_per_cell,
232 } => write!(
233 formatter,
234 "sampling thresholds are inconsistent: resolved carrier minimum \
235 {resolved_min_samples_per_wavelength:?} must be at least marginal \
236 minimum {marginal_min_samples_per_wavelength:?}, and resolved \
237 envelope maximum {resolved_max_envelope_fraction_per_cell:?} must \
238 not exceed marginal maximum {marginal_max_envelope_fraction_per_cell:?}"
239 ),
240 Self::SamplingRefused { certificate } => write!(
241 formatter,
242 "strict sampling refused {:?}: carrier samples/wavelength \
243 u={:?}, v={:?}; power-fringe samples/period u={:?}, v={:?}; \
244 envelope fraction/cell={:?}",
245 certificate.verdict,
246 certificate.samples_per_wavelength_u,
247 certificate.samples_per_wavelength_v,
248 certificate.samples_per_power_fringe_u,
249 certificate.samples_per_power_fringe_v,
250 certificate.max_envelope_fraction_per_cell,
251 ),
252 Self::UnboundedSamplingEnvelope {
253 nearest_point_source_distance_m,
254 cell_diagonal_m,
255 } => write!(
256 formatter,
257 "point-source envelope bound is not finite: nearest plane distance \
258 {nearest_point_source_distance_m:?} m, cell diagonal \
259 {cell_diagonal_m:?} m"
260 ),
261 Self::WorkEstimateOverflow { metric } => {
262 write!(formatter, "work estimate for {metric} overflowed u64")
263 }
264 Self::WorkBudgetExceeded {
265 metric,
266 estimate,
267 limit,
268 } => write!(
269 formatter,
270 "work budget exceeded for {metric}: estimate {estimate}, limit {limit}"
271 ),
272 }
273 }
274}
275
276impl std::error::Error for InterferenceError {}