Skip to content

Commit

Permalink
avoid crystal returning near-zero-but-not-quite-zero values due to …
Browse files Browse the repository at this point in the history
…pi-rounding
  • Loading branch information
thchr committed Oct 17, 2024
1 parent 6208b1f commit 1f10288
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 6 deletions.
16 changes: 10 additions & 6 deletions Bravais/src/systems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,21 @@ function crystal(a::Real, b::Real, c::Real, α::Real, β::Real, γ::Real)
if !isvalid_sphericaltriangle(α,β,γ)
throw(DomainError((α,β,γ), "The provided angles α,β,γ cannot be mapped to a spherical triangle, and thus do not form a valid axis system"))
end
sinγ, cosγ = sincos(γ)
# for many of the "special" input values (e.g., π/2, π/3), we can get better floating
# point accuracy by dividing the angles by π and then using `cospi` instead of `cos`
# etc.; e.g., this makes sure that if γ = π/2, then cosγ = 0.0, not ~6e-17.
α′, β′, γ′ = α/π, β/π, γ/π
sinγ, cosγ = sincospi(γ′)
# R₁ and R₂ are easy
R₁ = SVector{3,Float64}(a, 0.0, 0.0)
R₂ = SVector{3,Float64}(b*cosγ, b*sinγ, 0.0)
# R3 is harder
cosα = cos)
cosβ = cos)
cosα = cospi(α′)
cosβ = cospi(β′)
ϕ = atan(cosα - cosγ*cosβ, sinγ*cosβ)
θ = asin(sign(β)*sqrt(cosα^2 + cosβ^2 - 2*cosα*cosγ*cosβ)/abs(sin(γ))) # more stable than asin(cosβ/cosϕ) when β or γ ≈ π/2
sinθ, cosθ = sincos)
sinϕ, cosϕ = sincos)
θ = asin(sign(β)*sqrt(cosα^2 + cosβ^2 - 2*cosα*cosγ*cosβ)/abs(sinγ)) # more stable than asin(cosβ/cosϕ) when β or γ ≈ π/2
sinθ, cosθ = sincospi/π)
sinϕ, cosϕ = sincospi/π)
R₃ = SVector{3,Float64}(c.*(sinθ*cosϕ, sinθ*sinϕ, cosθ))

Rs = DirectBasis(R₁, R₂, R₃)
Expand Down
6 changes: 6 additions & 0 deletions test/basisvecs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ using Crystalline, Test, LinearAlgebra, StaticArrays
end
end

@testset "crystal(...)" begin
# zero-elements should be _exactly_ =0 for π/2 angles; not merely approximately 0
Rs = crystal(1.0, 1.0, 1.0, π/2, π/2, π/2)
@test all(iszero, (map(filter(R->abs(R)<0.1), Rs)))
end

@testset "Mixed inputs for constructors" begin
Rs = ([1.0, 0.0, 0.0], [-0.5, sqrt(3)/2, 0.0], [0, 0, 1.25])
Rs′ = DirectBasis{3}(Rs)
Expand Down

0 comments on commit 1f10288

Please sign in to comment.