-
Notifications
You must be signed in to change notification settings - Fork 37
Constructor with units #305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Constructor with units #305
Conversation
This is a very straightforward way to implement units. Do we want to allow for units in both constructor functions or only the one I changed so far? |
Also I am unsure if we should convert all default values to the user-specified unit, if only one keyword argument is given |
function Box(CO, hX::Quantity, hY::Quantity, hZ::Quantity, origin, rotation) | ||
_hX = to_internal_units(hX) | ||
_hY = to_internal_units(hY) | ||
_hZ = to_internal_units(hZ) | ||
Box(CO, _hX, _hY, _hZ, origin, rotation) | ||
end | ||
|
||
function Box(::Type{CO}=ClosedPrimitive; | ||
hX = 1, | ||
hY = 1, | ||
hZ = 1, | ||
hX = 1u"m", | ||
hY = 1u"m", | ||
hZ = 1u"m", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SSD does not know which function to call.
Try to specify in the already existing function above this one that TX
, TY
and TZ
can only be a Real
.
@@ -70,6 +70,15 @@ function Box(CO, hX::TX, hY::TY, hZ::TZ, origin::PT, rotation::ROT) where {TX, T | |||
Box{T}(CO,T(hX), T(hY), T(hZ), origin, rotation) | |||
end | |||
|
|||
#Unit convesion | |||
Box(CO, hX::Quantity, hY, hZ, origin, rotation) = Box(CO, to_internal_units(hX), hY, hZ, origin, rotation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a convenient way to write those permutations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use RealQuantity
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is RealQuantity
part of Unitful? I can not find it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RealQuantity
is part of SSD:
SolidStateDetectors.jl/src/Units.jl
Line 90 in 9dabbf6
const RealQuantity = MaybeWithUnits{<:Real} |
50680e0
to
bdf53b9
Compare
Box(CO, hX, hY::Quantity, hZ, origin, rotation) = Box(CO, hX, to_internal_units(hY), hZ, origin, rotation) | ||
Box(CO, hX, hY, hZ::Quantity, origin, rotation) = Box(CO, hX, hY, to_internal_units(hZ), origin, rotation) | ||
Box(CO, hX::Quantity, hY::Quantity, hZ, origin, rotation) = Box(CO, to_internal_units(hX), to_internal_units(hY), hZ, origin, rotation) | ||
Box(CO, hX, hY::Quantity, hZ::Quantity, origin, rotation) = Box(CO, hX, to_internal_units(hY), to_internal_units(hZ), origin, rotation) | ||
Box(CO, hX::Quantity, hY, hZ::Quantity, origin, rotation) = Box(CO, to_internal_units(hX), hY, to_internal_units(hZ), origin, rotation) | ||
Box(CO, hX::Quantity, hY::Quantity, hZ::Quantity, origin, rotation) = Box(CO, to_internal_units(hX), to_internal_units(hY), to_internal_units(hZ), origin, rotation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not a fan of these permutations...
What is the problem here and why doesn't this work without permutations?
to_internal_units
also works for numbers without units and just returns the number:
to_internal_units(x::Real) = x |
This could be fixed by RealQuantity
but I see that RealQuantity
is only defined in SSD and not CSG and we are modifying CSG functions. Maybe it makes sense to define RealQuantity
in CSG already and export it?
Then, the following might (?) work:
function Box(CO, hX::RealQuantity, hY::RealQuantity, hZ::RealQuantity, origin, rotation)
_hX = to_internal_units(hX)
_hY = to_internal_units(hY)
_hZ = to_internal_units(hZ)
Box(CO, _hX, _hY, _hZ, origin, rotation)
end
@@ -109,6 +109,8 @@ end | |||
@inline _convert_point(pt::AbstractCoordinatePoint, ::Type{Cylindrical}) = CylindricalPoint(pt) | |||
@inline _convert_point(pt::AbstractCoordinatePoint, ::Type{Cartesian}) = CartesianPoint(pt) | |||
|
|||
scale(cart::CartesianPoint{T}, fact) where T = CartesianPoint{T}(cart.x*fact, cart.y*fact, cart.z*fact) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be careful with this!
If we look at the docstring, CartesianPoint
will always be saved in units of m
.
I would prefer to have a constructor with units for CartesianPoint
and solve this via the constructor and not with scale
..
Box{T}(CO, hX, hY, hZ, origin, rotation) | ||
) where {T, CO, UN<:Unitful.Units} | ||
unit_factor = ustrip(uconvert(unt,1u"m")) | ||
Box{T}(CO, hX/unit_factor, hY/unit_factor, hZ/unit_factor, scale(CartesianPoint{T}(origin),1/unit_factor), rotation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(origin)
here does not need brackets, but as said above: maybe there is another way of implementing this.
I don't think we should do this like that. Let's take just the CartesianPoint(1u"m", 2u"m", 3u"m") should return Instead we have to add methods to If not, we could (in SSD but not in the submodule CSG), define some |
I just tested on the current main branch: julia> CartesianPoint(1u"m", 2u"m", 3u"m")
3-element CartesianPoint{Quantity{Int64, 𝐋, Unitful.FreeUnits{(m,), 𝐋, nothing}}} with indices SOneTo(3):
1 m
2 m
3 m That works already out of the box as we do not limit julia> CartesianPoint(1u"m", 2u"m", 3u"mm")
3-element CartesianPoint{Quantity{Rational{Int64}, 𝐋, Unitful.FreeUnits{(m,), 𝐋, nothing}}} with indices SOneTo(3):
1//1 m
2//1 m
3//1000 m And there seems also already to be some promotion rules defined in Unitful.jl as the second case also works :) |
b688a67
to
3c62d0b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, this topic is a bit tricky.
In principle, we (Julia ecosystem) don't want to convert a Quantity by default to its underlying type. This is should be done by implementing a method for ustrip
for Box
.
So a primitive with units could be constructed (and the information of the units
are still attached to the primitive and not removed like it is implemented right now).
In order to do this we would need to change the structs of the primitives though.
We would need to separate the type of the rotation
from the types of the other fields
like hX
:
struct Box{T,CO,RT} <: AbstractVolumePrimitive{T,CO}
hX::T # so `T` could also be a Quantity here
hY::T
hZ::T
origin::CartesianPoint{T}
rotation::SMatrix{3,3,RT,9}
end
We also would need to change the definition of the CylindricalPoint
by the way
as we will need different types for φ
and r
&z
.
This would be in principle the best (julianic) way in my opinion.
However, it requires the most work as well and make things a bit more complicated (more parametric), though not impossible.
Or, we force the ustrip
in the construction and make it impossible to construct primitives with units still attached like it is the design in this PR right now.
But, then we should also do this for the point types like CartesianPoint
. For consistency.
And for the points, I would really like to have them with units attached 🤔
Hmm...
_csg_convert_args(eltype::Type{T}, r::Tuple) where T = broadcast(x -> _csg_convert_args(T, x), r) | ||
_csg_convert_args(eltype::Type{T}, r::Nothing) where T = nothing | ||
_csg_convert_args(eltype::Type{T}, r::Quantity) where T = convert(T,ustrip(uconvert(u"m",r))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only works for LengthQuantity
, not for angles etc.
_precision_type(::Type{T}) where {T <: Real} = T | ||
_precision_type(::Type{Quantity{T}}) where {T <: Real} = T | ||
|
||
float(::Type{Quantity{T}}) where T = T |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Careful! This is type piracy!
Quantity
is a type that SSD does not own, so we should be VERY careful when extending the Base.float
method
test/runtests.jl
Outdated
# @testset "Comparison to analytic solutions" begin | ||
# include("comparison_to_analytic_solutions.jl") | ||
# end | ||
# | ||
# @testset "SOR GPU Backend" begin | ||
# include("SOR_GPU_Backend.jl") | ||
# end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't exclude tests without mentioning why you exclude them.
@@ -38,7 +38,7 @@ module ConstructiveSolidGeometry | |||
const CartesianTicksTuple{T} = NamedTuple{(:x,:y,:z), NTuple{3,Vector{T}}} | |||
const CylindricalTicksTuple{T} = NamedTuple{(:r,:φ,:z), NTuple{3,Vector{T}}} | |||
|
|||
abstract type AbstractGeometry{T <: AbstractFloat} end | |||
abstract type AbstractGeometry{T <: Number} end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why Number
and not AbstractFloat
?
No description provided.