cherry_rs/views/components/
mod.rs1use std::collections::HashSet;
2
3use anyhow::{anyhow, Result};
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 core::{sequential_model::Surface, Float, RefractiveIndex},
8 RefractiveIndexSpec, SequentialModel, SequentialSubModel,
9};
10
11const TOL: Float = 1e-6;
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
23pub enum Component {
24 Element { surf_idxs: (usize, usize) },
25 Stop { stop_idx: usize },
26 UnpairedSurface { surf_idx: usize },
27}
28
29pub fn components_view(
41 sequential_model: &SequentialModel,
42 background: RefractiveIndexSpec,
43) -> Result<HashSet<Component>> {
44 let mut components = HashSet::new();
45
46 let surfaces = sequential_model.surfaces();
47 let surface_pairs = surfaces.iter().zip(surfaces.iter().skip(1)).enumerate();
48 let max_idx = surfaces.len() - 1;
49 let mut paired_surfaces = HashSet::new();
50
51 let sequential_sub_model = sequential_model
54 .submodels()
55 .values()
56 .next()
57 .ok_or(anyhow!("No submodels found in the sequential model."))?;
58 let gaps = sequential_sub_model.gaps();
59
60 let background_refractive_index = RefractiveIndex::try_from_spec(&background, None)?;
61
62 if max_idx < 2 {
63 return Ok(components);
65 }
66
67 for (i, surf_pair) in surface_pairs {
68 if i == 0 || i == max_idx {
69 continue;
71 }
72
73 if let Surface::Stop(_) = surf_pair.0 {
74 components.insert(Component::Stop { stop_idx: i });
76 continue;
77 }
78
79 if let Surface::Stop(_) = surf_pair.1 {
80 continue;
82 }
83
84 if same_medium(gaps[i].refractive_index, background_refractive_index) {
85 continue;
88 }
89
90 if let Surface::Image(_) = surf_pair.1 {
91 if !paired_surfaces.contains(&i) {
93 components.insert(Component::UnpairedSurface { surf_idx: i });
94 continue;
95 }
96 }
97
98 components.insert(Component::Element {
99 surf_idxs: (i, i + 1),
100 });
101 paired_surfaces.insert(i);
102 paired_surfaces.insert(i + 1);
103 }
104
105 Ok(components)
106}
107
108fn same_medium(eta_1: RefractiveIndex, eta_2: RefractiveIndex) -> bool {
111 (eta_1.n() - eta_2.n()).abs() < TOL && (eta_1.k() - eta_2.k()).abs() < TOL
112}
113
114#[cfg(test)]
115mod tests {
116 use crate::{core::Float, GapSpec, RefractiveIndexSpec, SequentialModel, SurfaceSpec};
117
118 use super::*;
119
120 const AIR: RefractiveIndexSpec = RefractiveIndexSpec {
121 real: crate::RealSpec::Constant(1.0),
122 imag: None,
123 };
124
125 const NBK7: RefractiveIndexSpec = RefractiveIndexSpec {
126 real: crate::RealSpec::Constant(1.515),
127 imag: None,
128 };
129
130 pub fn empty_system() -> SequentialModel {
131 let surf_0 = SurfaceSpec::Object;
132 let gap_0 = GapSpec {
133 thickness: 1.0,
134 refractive_index: RefractiveIndexSpec {
135 real: crate::RealSpec::Constant(1.0),
136 imag: None,
137 },
138 };
139 let surf_1 = SurfaceSpec::Image;
140
141 let surfaces = vec![surf_0, surf_1];
142 let gaps = vec![gap_0];
143 let wavelengths = vec![0.567];
144
145 SequentialModel::new(&gaps, &surfaces, &wavelengths).unwrap()
146 }
147
148 pub fn silly_unpaired_surface() -> SequentialModel {
149 let surf_0 = SurfaceSpec::Object;
152 let gap_0 = GapSpec {
153 thickness: Float::INFINITY,
154 refractive_index: AIR,
155 };
156 let surf_1 = SurfaceSpec::Conic {
157 semi_diameter: 12.5,
158 radius_of_curvature: 25.8,
159 conic_constant: 0.0,
160 surf_type: crate::SurfaceType::Refracting,
161 };
162 let gap_1 = GapSpec {
163 thickness: 5.3,
164 refractive_index: NBK7,
165 };
166 let surf_2 = SurfaceSpec::Conic {
167 semi_diameter: 12.5,
168 radius_of_curvature: Float::INFINITY,
169 conic_constant: 0.0,
170 surf_type: crate::SurfaceType::Refracting,
171 };
172 let gap_2 = GapSpec {
173 thickness: 46.6,
174 refractive_index: AIR,
175 };
176 let surf_3 = SurfaceSpec::Conic {
177 semi_diameter: 12.5,
178 radius_of_curvature: 25.8,
179 conic_constant: 0.0,
180 surf_type: crate::SurfaceType::Refracting,
181 }; let gap_3 = GapSpec {
183 thickness: 20.0,
184 refractive_index: NBK7,
185 };
186 let surf_4 = SurfaceSpec::Image;
187
188 let surfaces = vec![surf_0, surf_1, surf_2, surf_3, surf_4];
189 let gaps = vec![gap_0, gap_1, gap_2, gap_3];
190 let wavelengths = vec![0.567];
191
192 SequentialModel::new(&gaps, &surfaces, &wavelengths).unwrap()
193 }
194
195 pub fn silly_single_surface_and_stop() -> SequentialModel {
196 let surf_0 = SurfaceSpec::Object;
199 let gap_0 = GapSpec {
200 thickness: Float::INFINITY,
201 refractive_index: AIR,
202 };
203 let surf_1 = SurfaceSpec::Conic {
204 semi_diameter: 12.5,
205 radius_of_curvature: 25.8,
206 conic_constant: 0.0,
207 surf_type: crate::SurfaceType::Refracting,
208 };
209 let gap_1 = GapSpec {
210 thickness: 10.0,
211 refractive_index: NBK7,
212 };
213 let surf_2 = SurfaceSpec::Stop {
214 semi_diameter: 12.5,
215 };
216 let gap_2 = GapSpec {
217 thickness: 10.0,
218 refractive_index: AIR,
219 };
220 let surf_3 = SurfaceSpec::Image;
221
222 let surfaces = vec![surf_0, surf_1, surf_2, surf_3];
223 let gaps = vec![gap_0, gap_1, gap_2];
224 let wavelengths = vec![0.567];
225
226 SequentialModel::new(&gaps, &surfaces, &wavelengths).unwrap()
227 }
228
229 pub fn wollaston_landscape_lens() -> SequentialModel {
230 let surf_0 = SurfaceSpec::Object;
235 let gap_0 = GapSpec::from_thickness_and_real_refractive_index(Float::INFINITY, 1.0);
236 let surf_1 = SurfaceSpec::Stop { semi_diameter: 5.0 };
237 let gap_1 = GapSpec::from_thickness_and_real_refractive_index(5.0, 1.0);
238 let surf_2 = SurfaceSpec::Conic {
239 semi_diameter: 6.882,
240 radius_of_curvature: Float::INFINITY,
241 conic_constant: 0.0,
242 surf_type: crate::SurfaceType::Refracting,
243 };
244 let gap_2 = GapSpec::from_thickness_and_real_refractive_index(5.0, 1.515);
245 let surf_3 = SurfaceSpec::Conic {
246 semi_diameter: 7.367,
247 radius_of_curvature: -25.84,
248 conic_constant: 0.0,
249 surf_type: crate::SurfaceType::Refracting,
250 };
251 let gap_3 = GapSpec::from_thickness_and_real_refractive_index(47.974, 1.0);
252 let surf_4 = SurfaceSpec::Image;
253
254 let surfaces = vec![surf_0, surf_1, surf_2, surf_3, surf_4];
255 let gaps = vec![gap_0, gap_1, gap_2, gap_3];
256 let wavelengths = vec![0.5876];
257
258 SequentialModel::new(&gaps, &surfaces, &wavelengths).unwrap()
259 }
260
261 #[test]
335 fn test_new_no_components() {
336 let sequential_model = empty_system();
337
338 let components = components_view(&sequential_model, AIR).unwrap();
339
340 assert_eq!(components.len(), 0);
341 }
342
343 #[test]
344 fn test_planoconvex_lens() {
345 let sequential_model = crate::examples::convexplano_lens::sequential_model();
346
347 let components = components_view(&sequential_model, AIR).unwrap();
348
349 assert_eq!(components.len(), 1);
350 assert!(components.contains(&Component::Element { surf_idxs: (1, 2) }));
351 }
352
353 #[test]
354 fn test_silly_single_surface_and_stop() {
355 let sequential_model = silly_single_surface_and_stop();
357
358 let components = components_view(&sequential_model, AIR).unwrap();
359
360 assert_eq!(components.len(), 1);
361 assert!(components.contains(&Component::Stop { stop_idx: 2 })); }
363
364 #[test]
365 fn test_silly_unpaired_surface() {
366 let sequential_model = silly_unpaired_surface();
368
369 let components = components_view(&sequential_model, AIR).unwrap();
370
371 assert_eq!(components.len(), 2);
372 assert!(components.contains(&Component::Element { surf_idxs: (1, 2) }));
373 assert!(components.contains(&Component::UnpairedSurface { surf_idx: 3 }));
374 }
375
376 #[test]
377 fn test_wollaston_landscape_lens() {
378 let sequential_model = wollaston_landscape_lens();
379
380 let components = components_view(&sequential_model, AIR).unwrap();
381
382 assert_eq!(components.len(), 2);
383 assert!(components.contains(&Component::Stop { stop_idx: 1 })); assert!(components.contains(&Component::Element { surf_idxs: (2, 3) }));
385 }
387}