diff --git a/src/Examples/docs_examples.jl b/src/Examples/docs_examples.jl index c4a89d568..c9f7c0002 100644 --- a/src/Examples/docs_examples.jl +++ b/src/Examples/docs_examples.jl @@ -41,9 +41,9 @@ function draw_zoomlenses(filenames::Vector{<:Union{Nothing,AbstractString}} = re raygenerator = Sources.Source(; transform, origins, directions) aspherics = [ - [4 => 1.0386E-04, 6 => 1.4209E-07, 8 => -8.8495E-09, 10 => 1.2477E-10, 12 => -1.0367E-12, 14 => 3.6556E-15], - [4 => 4.2721E-05, 6 => 1.2484E-07, 8 => 9.7079E-09, 10 => -1.8444E-10, 12 => 1.8644E-12, 14 => -7.7975E-15], - [4 => 1.1339E-04, 6 => 4.8165E-07, 8 => 1.8778E-08, 10 => -5.7571E-10, 12 => 8.9994E-12, 14 => -4.6768E-14], + ["4" => 1.0386E-04, "6" => 1.4209E-07, "8" => -8.8495E-09, "10" => 1.2477E-10, "12" => -1.0367E-12, "14" => 3.6556E-15], + ["4" => 4.2721E-05, "6" => 1.2484E-07, "8" => 9.7079E-09, "10" => -1.8444E-10, "12" => 1.8644E-12, "14" => -7.7975E-15], + ["4" => 1.1339E-04, "6" => 4.8165E-07, "8" => 1.8778E-08, "10" => -5.7571E-10, "12" => 8.9994E-12, "14" => -4.6768E-14], ] syss = [ AxisymmetricOpticalSystem{Float64}(DataFrame( diff --git a/src/Examples/other_examples.jl b/src/Examples/other_examples.jl index 9ef9592f2..e2a36f2b5 100644 --- a/src/Examples/other_examples.jl +++ b/src/Examples/other_examples.jl @@ -72,7 +72,7 @@ function convexplano(::Type{T} = Float64) where {T<:Real} ) end -function doubleconvex(frontradius::T,rearradius::T) where{T<:Real} +function doubleconvex(frontradius::T, rearradius::T) where {T<:Real} AxisymmetricOpticalSystem{T}( DataFrame( SurfaceType = ["Object", "Standard", "Standard", "Image"], diff --git a/src/Optical/Lenses.jl b/src/Optical/Lenses.jl index 00df159ae..ecb0bcea6 100644 --- a/src/Optical/Lenses.jl +++ b/src/Optical/Lenses.jl @@ -100,6 +100,9 @@ function AsphericLens(insidematerial::T, frontvertex::S, frontradius::S, frontco end else #conic or aspheric + if backaspherics !== nothing + backaspherics = Tuple{Int,S}.(backaspherics) + end surf = AcceleratedParametricSurface(ZernikeSurface(semidiameter + backdecenter_l + S(0.01), radius = backradius, conic = backconic, aspherics = backaspherics), nsamples, interface = opticinterface(S, insidematerial, nextmaterial, backsurfacereflectance, interfacemode)) lens_rear = leaf(surf, Transform{S}(zero(S), S(π), zero(S), backdecenter[1], backdecenter[2], frontvertex - thickness)) end diff --git a/src/Optical/OpticalSystem.jl b/src/Optical/OpticalSystem.jl index 33853fed5..42c6e807a 100644 --- a/src/Optical/OpticalSystem.jl +++ b/src/Optical/OpticalSystem.jl @@ -2,14 +2,15 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # See LICENSE in the project root for full license information. +using DataFrames +using Unitful.DefaultSymbols +using .OpticSim.GlassCat: AbstractGlass, TEMP_REF, PRESSURE_REF, Glass, Air + export AbstractOpticalSystem export CSGOpticalSystem, temperature, pressure, detectorimage, resetdetector!, assembly export AxisymmetricOpticalSystem, semidiameter export trace, traceMT, tracehits, tracehitsMT -using DataFrames -using Unitful.DefaultSymbols - """ AbstractOpticalSystem{T<:Real} @@ -55,8 +56,8 @@ struct CSGOpticalSystem{T,D<:Number,S<:Surface{T},L<:LensAssembly{T}} <: Abstrac detectorpixelsx::Int = 1000, detectorpixelsy::Int = 1000, ::Type{D} = Float32; - temperature::Union{T,Unitful.Temperature} = convert(T, OpticSim.GlassCat.TEMP_REF), - pressure::T = convert(T, OpticSim.GlassCat.PRESSURE_REF) + temperature::Union{T,Unitful.Temperature} = convert(T, TEMP_REF), + pressure::T = convert(T, PRESSURE_REF) ) where {T<:Real,S<:Surface{T},L<:LensAssembly{T},D<:Number} @assert hasmethod(uv, (S, SVector{3,T})) "Detector must implement uv()" @assert hasmethod(uvtopix, (S, SVector{2,T}, Tuple{Int,Int})) "Detector must implement uvtopix()" @@ -180,7 +181,7 @@ function trace( # this will almost always not apply as the detector will be in air, but it's possible that the detector is # not in air, in which case this is necessary if !isair(m) - mat = glassforid(m)::OpticSim.GlassCat.Glass + mat::Glass = glassforid(m) nᵢ = index(mat, λ, temperature = temperature(system), pressure = pressure(system))::T α = absorption(mat, λ, temperature = temperature(system), pressure = pressure(system))::T if α > zero(T) @@ -228,14 +229,14 @@ function validate_axisymmetricopticalsystem_dataframe(prescription::DataFrame) # the prescription DataFrame columns; the former refers to the actual `surface_type` values, which are all strings required_cols = ["SurfaceType", "Radius", "Thickness", "Material", "SemiDiameter"] supported_col_types = Dict( - "SurfaceType" => String, + "SurfaceType" => AbstractString, "Radius" => Real, "Thickness" => Real, - "Material" => GlassCat.AbstractGlass, + "Material" => AbstractGlass, "SemiDiameter" => Real, "Conic" => Real, "Reflectance" => Real, - "Parameters" => Vector{<:Real}, + "Parameters" => Vector{<:Pair{<:AbstractString,<:Real}}, ) cols = names(prescription) @@ -312,54 +313,52 @@ struct AxisymmetricOpticalSystem{T,C<:CSGOpticalSystem{T}} <: AbstractOpticalSys detectorpixelsx::Int = 1000, detectorpixelsy::Int = 1000, ::Type{D} = Float32; - temperature::Union{T,Unitful.Temperature} = convert(T, OpticSim.GlassCat.TEMP_REF), - pressure::T = convert(T, OpticSim.GlassCat.PRESSURE_REF) + temperature::Union{T,Unitful.Temperature} = convert(T, TEMP_REF), + pressure::T = convert(T, PRESSURE_REF) ) where {T<:Real,D<:Number} validate_axisymmetricopticalsystem_dataframe(prescription) - elements = Vector{Union{Surface{T},CSGTree{T}}}() - systemsemidiameter = zero(T) - firstelement = true + elements::Vector{Union{Surface{T},CSGTree{T}}} = [] + systemsemidiameter::T = zero(T) + firstelement::Bool = true # track sequential movement along the z-axis - vertices = convert(Vector{T}, -cumsum(replace(prescription[!, "Thickness"], Inf => 0, missing => 0))) + vertices::Vector{T} = -cumsum(replace(prescription[!, "Thickness"], Inf => 0, missing => 0)) # pre-construct list of rows which we will skip over (e.g. air gaps, but never Stop surfaces) # later on, this may get more complicated as we add in compound surfaces function skip_row(i::Int) return ( prescription[i, "SurfaceType"] != "Stop" && - (prescription[i, "Material"] === missing || prescription[i, "Material"] == GlassCat.Air) + (prescription[i, "Material"] === missing || prescription[i, "Material"] == Air) ) end - skips = skip_row.(1:nrow(prescription)) + skips::Vector{Bool} = skip_row.(1:nrow(prescription)) for i in 2:nrow(prescription)-1 if skips[i] continue end - surface_type = prescription[i, "SurfaceType"] - lastmaterial, material, nextmaterial = prescription[i-1:i+1, "Material"] - thickness = convert(T, prescription[i, "Thickness"]) + surface_type::String = prescription[i, "SurfaceType"] + lastmaterial::AbstractGlass, material::AbstractGlass, nextmaterial::AbstractGlass = prescription[i-1:i+1, "Material"] + thickness::T = prescription[i, "Thickness"] - frontradius, backradius = get_front_back_property(prescription, i, "Radius") - frontsurfacereflectance, backsurfacereflectance = get_front_back_property( + frontradius::T, backradius::T = get_front_back_property(prescription, i, "Radius") + frontsurfacereflectance::T, backsurfacereflectance::T = get_front_back_property( prescription, i, "Reflectance", zero(T) ) + frontconic::T, backconic::T = get_front_back_property(prescription, i, "Conic", zero(T)) + frontparams::Vector{Pair{String,T}}, backparams::Vector{Pair{String,T}} = get_front_back_property( + prescription, i, "Parameters", Vector{Pair{String,T}}() + ) - semidiameter = NaN + semidiameter::T = max(get_front_back_property(prescription, i, "SemiDiameter", zero(T))...) if surface_type == "Stop" - newelement = CircularAperture( - convert(T, prescription[i, "SemiDiameter"]), - SVector{3,T}(0.0, 0.0, 1.0), - SVector{3,T}(0.0, 0.0, vertices[i-1]) - ) + semidiameter = prescription[i, "SemiDiameter"] + newelement = CircularAperture(semidiameter, SVector{3,T}(0, 0, 1), SVector{3,T}(0, 0, vertices[i-1])) elseif surface_type == "Standard" - semidiameter = convert(T, max(get_front_back_property(prescription, i, "SemiDiameter", zero(T))...)) - frontconic, backconic = get_front_back_property(prescription, i, "Conic", zero(T)) - if frontconic != zero(T) || backconic != zero(T) newelement = ConicLens( material, vertices[i-1], frontradius, frontconic, backradius, backconic, thickness, @@ -372,9 +371,9 @@ struct AxisymmetricOpticalSystem{T,C<:CSGOpticalSystem{T}} <: AbstractOpticalSys )() end elseif surface_type == "Aspheric" - semidiameter = convert(T, max(get_front_back_property(prescription, i, "SemiDiameter", zero(T))...)) - frontconic, backconic = get_front_back_property(prescription, i, "Conic", zero(T)) - frontaspherics, backaspherics = get_front_back_property(prescription, i, "Parameters", Vector{Pair{Int,Float64}}()) + frontaspherics::Vector{Pair{Int,T}}, backaspherics::Vector{Pair{Int,T}} = [ + [parse(Int, k) => v for (k, v) in params] for params in [frontparams, backparams] + ] newelement = AsphericLens( material, vertices[i-1], frontradius, frontconic, frontaspherics, backradius, backconic, @@ -388,7 +387,7 @@ struct AxisymmetricOpticalSystem{T,C<:CSGOpticalSystem{T}} <: AbstractOpticalSys ) end - if firstelement && semidiameter !== NaN + if firstelement systemsemidiameter = semidiameter firstelement = false end @@ -397,8 +396,8 @@ struct AxisymmetricOpticalSystem{T,C<:CSGOpticalSystem{T}} <: AbstractOpticalSys end # make the detector (Image) - imagesize = prescription[end, "SemiDiameter"] - imagerad = prescription[end, "Radius"] + imagesize::T = prescription[end, "SemiDiameter"] + imagerad::T = prescription[end, "Radius"] if imagerad != zero(T) && imagerad != typemax(T) det = SphericalCap( abs(imagerad), @@ -409,8 +408,8 @@ struct AxisymmetricOpticalSystem{T,C<:CSGOpticalSystem{T}} <: AbstractOpticalSys ) else det = Rectangle( - convert(T, imagesize), - convert(T, imagesize), + imagesize, + imagesize, SVector{3,T}(0, 0, 1), SVector{3,T}(0, 0, vertices[end-1]), interface = opaqueinterface(T)