diff --git a/crates/cherry-rs/src/examples/wf_epi_excitation.rs b/crates/cherry-rs/src/examples/wf_epi_excitation.rs index c1cc4048..8805731e 100644 --- a/crates/cherry-rs/src/examples/wf_epi_excitation.rs +++ b/crates/cherry-rs/src/examples/wf_epi_excitation.rs @@ -21,6 +21,7 @@ use crate::{ pub fn sequential_model( n_air: Rc, + n_oil: Rc, wavelengths: &[f64], ) -> SequentialModel { let gap_0 = GapSpec { @@ -33,11 +34,11 @@ pub fn sequential_model( }; let gap_2 = GapSpec { thickness: 50.0, - refractive_index: n_air.clone(), + refractive_index: n_air, }; let gap_3 = GapSpec { thickness: 1.0, - refractive_index: n_air, + refractive_index: n_oil, }; let gaps = vec![gap_0, gap_1, gap_2, gap_3]; @@ -50,7 +51,7 @@ pub fn sequential_model( rotation_offset: Rotation3D::None, }; let surf_2 = SurfaceSpec::Conic { - semi_diameter: 23.2, + semi_diameter: 36.0, radius_of_curvature: Float::INFINITY, conic_constant: 0.0, surf_kind: BoundaryKind::Reflecting, @@ -63,7 +64,7 @@ pub fn sequential_model( rotation_offset: Rotation3D::None, }; let surf_3 = SurfaceSpec::ThinLens { - semi_diameter: 4.75, + semi_diameter: 4.25, focal_length: 3.3333, rotation: Rotation3D::None, decenter: Vec3::new(0.0, 0.0, 0.0), diff --git a/crates/cherry-rs/src/views/paraxial.rs b/crates/cherry-rs/src/views/paraxial.rs index 644a127e..a99610e8 100644 --- a/crates/cherry-rs/src/views/paraxial.rs +++ b/crates/cherry-rs/src/views/paraxial.rs @@ -836,17 +836,14 @@ impl ParaxialSubView { let y_1 = parallel_ray.rays_at_surface(1)[0].height; let u_final = parallel_ray.rays_at_surface(parallel_ray.num_surfaces() - 2)[0].angle; - // There should be a negative sign here for lens only systems, but we take abs - // later so it's not needed - let efl = y_1 / u_final; + let efl = -y_1 / u_final; // Handle edge case for negatively infinite EFL if efl.is_infinite() { return Float::INFINITY; } - // abs() handles edge case of apparent negative EFLs in reflecting systems - efl.abs() + efl } #[allow(clippy::too_many_arguments)] @@ -918,7 +915,7 @@ impl ParaxialSubView { Ok(Pupil { location, - semi_diameter, + semi_diameter: semi_diameter.abs(), // semi-diameter is always positive }) } diff --git a/crates/cherry-rs/tests/wf_epi_excitation.rs b/crates/cherry-rs/tests/wf_epi_excitation.rs index 0592dbfb..0a8377b7 100644 --- a/crates/cherry-rs/tests/wf_epi_excitation.rs +++ b/crates/cherry-rs/tests/wf_epi_excitation.rs @@ -4,19 +4,71 @@ use cherry_rs::examples::wf_epi_excitation::sequential_model; use cherry_rs::{FieldSpec, ParaxialView, n}; const WAVELENGTHS: [f64; 1] = [0.5876]; -const FIELD_SPECS: [FieldSpec; 1] = [FieldSpec::PointSource { x: 0.0, y: 0.0 }]; - -// Hand calculation: the stop (second thin lens) is 150 mm (100 mm + 50 mm, -// the fold mirror has no power) downstream of the first thin lens -// (f = 40 mm). Treating the stop as a real object and solving the thin lens -// equation 1/s' = 1/f - 1/s with s = 150 mm gives s' = 600/11 = 54.5455 mm, -// with the resulting image (the entrance pupil) on the object-space side of -// the first thin lens. +const FIELD_SPECS: [FieldSpec; 1] = [FieldSpec::PointSource { x: 0.0, y: 1.5 }]; + +// Paraxial property values +const APERTURE_STOP: usize = 3; +const BACK_FOCAL_DISTANCE: f64 = 5.1562; +const BACK_PRINCIPAL_PLANE: f64 = 7.0312; +const EFFECTIVE_FOCAL_LENGTH: f64 = -1.8750; const ENTRANCE_PUPIL_LOCATION: f64 = -54.5455; +const ENTRANCE_PUPIL_SIZE: f64 = 1.5454; + +#[test] +fn wf_epi_excitation_paraxial_aperture_stop() { + let model = sequential_model(n!(1.0), n!(1.5), &WAVELENGTHS); + let view = + ParaxialView::new(&model, &FIELD_SPECS, false).expect("Could not create paraxial view"); + + for sub_view in view.iter() { + let result = sub_view.aperture_stop(); + + assert_eq!(APERTURE_STOP, *result) + } +} + +#[test] +fn wf_epi_excitation_paraxial_back_principal_plane() { + let model = sequential_model(n!(1.0), n!(1.5), &WAVELENGTHS); + let view = + ParaxialView::new(&model, &FIELD_SPECS, false).expect("Could not create paraxial view"); + + for sub_view in view.iter() { + let result = sub_view.back_principal_plane(); + + assert_abs_diff_eq!(BACK_PRINCIPAL_PLANE, *result, epsilon = 1e-4) + } +} + +#[test] +fn wf_epi_excitation_paraxial_back_focal_distance() { + let model = sequential_model(n!(1.0), n!(1.5), &WAVELENGTHS); + let view = + ParaxialView::new(&model, &FIELD_SPECS, false).expect("Could not create paraxial view"); + + for sub_view in view.iter() { + let result = sub_view.back_focal_distance(); + + assert_abs_diff_eq!(BACK_FOCAL_DISTANCE, *result, epsilon = 1e-4) + } +} + +#[test] +fn wf_epi_excitation_paraxial_effective_focal_length() { + let model = sequential_model(n!(1.0), n!(1.5), &WAVELENGTHS); + let view = + ParaxialView::new(&model, &FIELD_SPECS, false).expect("Could not create paraxial view"); + + for sub_view in view.iter() { + let result = sub_view.effective_focal_length(); + + assert_abs_diff_eq!(EFFECTIVE_FOCAL_LENGTH, *result, epsilon = 1e-4) + } +} #[test] fn wf_epi_excitation_entrance_pupil_location() { - let model = sequential_model(n!(1.0), &WAVELENGTHS); + let model = sequential_model(n!(1.0), n!(1.5), &WAVELENGTHS); let view = ParaxialView::new(&model, &FIELD_SPECS, false).expect("Could not create paraxial view"); @@ -27,5 +79,10 @@ fn wf_epi_excitation_entrance_pupil_location() { ENTRANCE_PUPIL_LOCATION, epsilon = 1e-3 ); + assert_abs_diff_eq!( + entrance_pupil.semi_diameter, + ENTRANCE_PUPIL_SIZE, + epsilon = 1e-3 + ); } }