Skip to content
This repository has been archived by the owner on Oct 23, 2022. It is now read-only.

Fix type validation and parsing in Parameter column #187

Merged
merged 2 commits into from
May 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/Examples/docs_examples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion src/Examples/other_examples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
3 changes: 3 additions & 0 deletions src/Optical/Lenses.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
77 changes: 38 additions & 39 deletions src/Optical/OpticalSystem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down Expand Up @@ -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()"
Expand Down Expand Up @@ -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)
alfredclwong marked this conversation as resolved.
Show resolved Hide resolved
nᵢ = index(mat, λ, temperature = temperature(system), pressure = pressure(system))::T
α = absorption(mat, λ, temperature = temperature(system), pressure = pressure(system))::T
if α > zero(T)
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -388,7 +387,7 @@ struct AxisymmetricOpticalSystem{T,C<:CSGOpticalSystem{T}} <: AbstractOpticalSys
)
end

if firstelement && semidiameter !== NaN
if firstelement
systemsemidiameter = semidiameter
firstelement = false
end
Comment on lines -391 to 393
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change will allow the Stop semidiameter to define the systemsemidiameter if it's the first element. This is different from the previous behaviour - is this OK?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ask Joel Kollin. He could tell us if this makes sense optically.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked the Cambridge team today and the general consensus was that individual semidiameters are important but not so much the system semidiameter. And, if anything, it'd be more useful to have this as the max value. I'll have a look around the code tomorrow to see if this is actually used anywhere currently.

Expand All @@ -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),
Expand All @@ -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)
Expand Down