From 5e3feeeabada66a104261a4388d95851d93c08ef Mon Sep 17 00:00:00 2001 From: "Luiz M. Faria" Date: Fri, 29 Sep 2023 21:24:51 +0200 Subject: [PATCH 1/6] first implementation of reference shapes + tests --- Project.toml | 5 +- src/Geometry/Geometry.jl | 26 ++++++++++ src/Geometry/referenceshapes.jl | 72 +++++++++++++++++++++++++++ src/Inti.jl | 8 ++- src/utils.jl | 13 +++++ test/Geometry/referenceshapes_test.jl | 37 ++++++++++++++ test/Project.toml | 2 + test/aqua_test.jl | 7 +++ test/runtests.jl | 13 ++--- 9 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 src/Geometry/Geometry.jl create mode 100644 src/Geometry/referenceshapes.jl create mode 100644 src/utils.jl create mode 100644 test/Geometry/referenceshapes_test.jl create mode 100644 test/aqua_test.jl diff --git a/Project.toml b/Project.toml index 973459ca..13d0de5a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,10 @@ name = "Inti" uuid = "fb74042b-437e-4c5b-88cf-d4e2beb394d5" -authors = ["Luiz M. Faria"] version = "0.1.0" +[deps] +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + [compat] +StaticArrays = "1" julia = "1.6" diff --git a/src/Geometry/Geometry.jl b/src/Geometry/Geometry.jl new file mode 100644 index 00000000..580e2bbd --- /dev/null +++ b/src/Geometry/Geometry.jl @@ -0,0 +1,26 @@ +#= + +Definition of basic geometrical concepts. + +=# + + +""" + ambient_dimension(x) + +Dimension of the ambient space where `x` lives. For geometrical objects this can +differ from its [`geometric_dimension`](@ref); for example a triangle in `ℝ³` +has ambient dimension `3` but geometric dimension `2`, while a curve in `ℝ³` has +ambient dimension 3 but geometric dimension 1. +""" +function ambient_dimension end + +""" + geometric_dimension(x) + +Number of independent coordinates needed to describe `x`. Lines have geometric +dimension 1, triangles have geometric dimension 2, etc. +""" +function geometric_dimension end + +include("referenceshapes.jl") diff --git a/src/Geometry/referenceshapes.jl b/src/Geometry/referenceshapes.jl new file mode 100644 index 00000000..58fbb4b1 --- /dev/null +++ b/src/Geometry/referenceshapes.jl @@ -0,0 +1,72 @@ +""" + abstract type AbstractReferenceShape + +A fixed reference domain/shape. Used mostly for defining more complex shapes as +transformations mapping an `AbstractReferenceShape` to some region of `ℜᴹ`. + +See e.g. [`ReferenceLine`](@ref) or [`ReferenceTriangle`](@ref) for some +examples of concrete subtypes. +""" +abstract type AbstractReferenceShape end + +""" + struct ReferenceTriangle + +Singleton type representing the triangle with vertices `(0,0),(1,0),(0,1)` +""" +struct ReferenceTriangle <: AbstractReferenceShape end +geometric_dimension(::ReferenceTriangle) = 2 +ambient_dimension(::ReferenceTriangle) = 2 +vertices(::ReferenceTriangle) = SVector(0, 0), SVector(1, 0), SVector(0, 1) +Base.in(x, ::ReferenceTriangle) = 0 ≤ x[1] ≤ 1 && 0 ≤ x[2] ≤ 1 - x[1] +center(::ReferenceTriangle) = svector(i -> 1 / 3, 3) + +""" + struct ReferenceTetrahedron + +Singleton type representing the tetrahedron with vertices +`(0,0,0),(0,0,1),(0,1,0),(1,0,0)` +""" +struct ReferenceTetrahedron <: AbstractReferenceShape end +geometric_dimension(::ReferenceTetrahedron) = 3 +ambient_dimension(::ReferenceTetrahedron) = 3 +vertices(::ReferenceTetrahedron) = SVector(0, 0, 0), SVector(1, 0, 0), SVector(0, 1, 0), SVector(0, 0, 1) +Base.in(x,::ReferenceTetrahedron) = 0 ≤ x[1] ≤ 1 && 0 ≤ x[2] ≤ 1 - x[1] && 0 ≤ x[3] ≤ 1 - x[1] - x[2] +center(::ReferenceTetrahedron) = svector(i -> 1 / 4, 4) + + +# TODO: generalize structs above to `ReferenceSimplex{N}` and + +""" + struct ReferenceHyperCube{N} <: AbstractReferenceShape{N} + +Singleton type representing the axis-aligned hypercube in `N` dimensions with +the lower corner at the origin and the upper corner at `(1,1,…,1)`. +""" +struct ReferenceHyperCube{N} <: AbstractReferenceShape end +geometric_dimension(::ReferenceHyperCube{N}) where {N} = N +ambient_dimension(::ReferenceHyperCube{N}) where {N} = N +vertices(::ReferenceHyperCube{N}) where {N} = ntuple(i -> SVector(ntuple(j -> (i >> j) & 1, N)), 2^N) +Base.in(x, ::ReferenceHyperCube{N}) where {N} = all(0 .≤ x .≤ 1) +center(::ReferenceHyperCube{N}) where {N} = svector(i -> 0.5, N) + +""" + const ReferenceLine = ReferenceHyperCube{1} + +Singleton type representing the `[0,1]` segment. +""" +const ReferenceLine = ReferenceHyperCube{1} + +""" + const ReferenceSquare = ReferenceHyperCube{2} + +Singleton type representing the unit square `[0,1]²`. +""" +const ReferenceSquare = ReferenceHyperCube{2} + +""" + const ReferenceCube = ReferenceHyperCube{3} + +Singleton type representing the unit cube `[0,1]³`. +""" +const ReferenceCube = ReferenceHyperCube{3} diff --git a/src/Inti.jl b/src/Inti.jl index c23e615d..2e10a22a 100644 --- a/src/Inti.jl +++ b/src/Inti.jl @@ -1,5 +1,11 @@ module Inti -# Write your package code here. +const PROJECT_ROOT = pkgdir(Inti) + +using StaticArrays + +include("utils.jl") +include("Geometry/Geometry.jl") + end diff --git a/src/utils.jl b/src/utils.jl new file mode 100644 index 00000000..1a19fe39 --- /dev/null +++ b/src/utils.jl @@ -0,0 +1,13 @@ +#= + +Utility functions that have nowhere obvious to go. + +=# + +""" + svector(f,n) + +Create an `SVector` of length n, computing each element as f(i), where i is the +index of the element. +""" +svector(f,n)= ntuple(f,n) |> SVector diff --git a/test/Geometry/referenceshapes_test.jl b/test/Geometry/referenceshapes_test.jl new file mode 100644 index 00000000..882355f4 --- /dev/null +++ b/test/Geometry/referenceshapes_test.jl @@ -0,0 +1,37 @@ +using Test +import Inti +using StaticArrays + +@testset "Line" begin + l = Inti.ReferenceLine() + @test Inti.ambient_dimension(l) == 1 + @test Inti.geometric_dimension(l) == 1 + x = SVector(0.5) + @test x ∈ l + x = SVector(1.0) + @test x ∈ l + x = SVector(1.1) + @test !in(x, l) +end +@testset "Triangle" begin + t = Inti.ReferenceTriangle() + @test Inti.ambient_dimension(t) == 2 + @test Inti.geometric_dimension(t) == 2 + x = SVector(0.5, 0.5) + @test x ∈ t + x = SVector(1.0, 0.0) + @test x ∈ t + x = SVector(1.1, 0.0) + @test !in(x, t) +end +@testset "Tetrahedron" begin + t = Inti.ReferenceTetrahedron() + @test Inti.ambient_dimension(t) == 3 + @test Inti.geometric_dimension(t) == 3 + x = SVector(0.5, 0.5, 0.0) + @test x ∈ t + x = SVector(1.0, 0.0, 0.0) # point on edge + @test x ∈ t + x = SVector(1.1, 0.0, 0.0) + @test !in(x, t) +end diff --git a/test/Project.toml b/test/Project.toml index b35b5edd..5e3af3c7 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,3 +1,5 @@ [deps] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/aqua_test.jl b/test/aqua_test.jl new file mode 100644 index 00000000..19bfbfeb --- /dev/null +++ b/test/aqua_test.jl @@ -0,0 +1,7 @@ +using Inti +using Test +using Aqua + +@testset "Aqua" begin + Aqua.test_all(Inti) +end diff --git a/test/runtests.jl b/test/runtests.jl index 7fc3bc2b..9b203116 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,10 +1,11 @@ using Inti -using Test +using SafeTestsets using Aqua -@testset "Inti.jl" begin - @testset "Code quality (Aqua.jl)" begin - Aqua.test_all(Inti) - end - # Write your tests here. +@safetestset "Code quality" begin + include("aqua_test.jl") +end + +@safetestset "Geometry" begin + @safetestset "Reference shapes" include("Geometry/referenceshapes_test.jl") end From 24ae4d35702101f8a7770359c20a4f00fa6c18ae Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 30 Sep 2023 20:24:14 -0500 Subject: [PATCH 2/6] Add reference n-simplex --- src/Geometry/referenceshapes.jl | 23 ++++++++++++++++++++++- test/Geometry/referenceshapes_test.jl | 13 ++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/Geometry/referenceshapes.jl b/src/Geometry/referenceshapes.jl index 58fbb4b1..17d9ef2a 100644 --- a/src/Geometry/referenceshapes.jl +++ b/src/Geometry/referenceshapes.jl @@ -34,8 +34,29 @@ vertices(::ReferenceTetrahedron) = SVector(0, 0, 0), SVector(1, 0, 0), SVector(0 Base.in(x,::ReferenceTetrahedron) = 0 ≤ x[1] ≤ 1 && 0 ≤ x[2] ≤ 1 - x[1] && 0 ≤ x[3] ≤ 1 - x[1] - x[2] center(::ReferenceTetrahedron) = svector(i -> 1 / 4, 4) +""" + struct ReferenceSimplex{N} -# TODO: generalize structs above to `ReferenceSimplex{N}` and +Singleton type representing the N-simplex with N+1 vertices +`(0,...,0),(0,...,0,1),(0,...,0,1,0),(1,0,...,0)` +""" +struct ReferenceSimplex{N} <: AbstractReferenceShape end +geometric_dimension(::ReferenceSimplex{N}) where {N} = N +ambient_dimension(::ReferenceSimplex{N}) where {N} = N +function Base.in(x, ::ReferenceSimplex{N}) where {N} + for i in range(1, N) + 0 ≤ x[i] ≤ 1 - sum(x[1:i-1]) || return false + end + return true +end + +function standard_basis_vector(T, ::Val{I}, ::Val{N}) where {I,N} + v = zero(MVector{N,T}) + v[I] = one(T) + SVector(v) +end + +vertices(::ReferenceSimplex{N}) where {N} = ntuple(i -> standard_basis_vector(Int64, Val{i}, Val{N}), N) |> SVector """ struct ReferenceHyperCube{N} <: AbstractReferenceShape{N} diff --git a/test/Geometry/referenceshapes_test.jl b/test/Geometry/referenceshapes_test.jl index 882355f4..1b7e33dd 100644 --- a/test/Geometry/referenceshapes_test.jl +++ b/test/Geometry/referenceshapes_test.jl @@ -1,5 +1,5 @@ using Test -import Inti +using Inti using StaticArrays @testset "Line" begin @@ -35,3 +35,14 @@ end x = SVector(1.1, 0.0, 0.0) @test !in(x, t) end +@testset "NSimplex" begin + t = Inti.ReferenceSimplex{4}() + @test Inti.ambient_dimension(t) == 4 + @test Inti.geometric_dimension(t) == 4 + x = SVector(0.5, 0.4, 0.0, 0.05) + @test x ∈ t + x = SVector(1.0, 0.0, 0.0, 0.0) + @test x ∈ t + x = SVector(1.1, 0.0, 0.0, 0.0) + @test !in(x, t) +end \ No newline at end of file From 78752f0d6b39fd196091e4d5ac3bbb5b6845246e Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 30 Sep 2023 20:34:41 -0500 Subject: [PATCH 3/6] Fix ranges for v1.6 --- src/Geometry/referenceshapes.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Geometry/referenceshapes.jl b/src/Geometry/referenceshapes.jl index 17d9ef2a..2b9cd387 100644 --- a/src/Geometry/referenceshapes.jl +++ b/src/Geometry/referenceshapes.jl @@ -44,7 +44,7 @@ struct ReferenceSimplex{N} <: AbstractReferenceShape end geometric_dimension(::ReferenceSimplex{N}) where {N} = N ambient_dimension(::ReferenceSimplex{N}) where {N} = N function Base.in(x, ::ReferenceSimplex{N}) where {N} - for i in range(1, N) + for i in 1:N 0 ≤ x[i] ≤ 1 - sum(x[1:i-1]) || return false end return true From 49fac720de4ebd64c86fc9d7c278165990e2026c Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sat, 30 Sep 2023 20:57:06 -0500 Subject: [PATCH 4/6] Make ReferenceTriangle and ReferenceTetrahedron instances of ReferenceSimplex This also fixes errors in the center function for those shapes. --- src/Geometry/referenceshapes.jl | 43 ++++++++++++--------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/src/Geometry/referenceshapes.jl b/src/Geometry/referenceshapes.jl index 2b9cd387..e512fb7a 100644 --- a/src/Geometry/referenceshapes.jl +++ b/src/Geometry/referenceshapes.jl @@ -9,31 +9,6 @@ examples of concrete subtypes. """ abstract type AbstractReferenceShape end -""" - struct ReferenceTriangle - -Singleton type representing the triangle with vertices `(0,0),(1,0),(0,1)` -""" -struct ReferenceTriangle <: AbstractReferenceShape end -geometric_dimension(::ReferenceTriangle) = 2 -ambient_dimension(::ReferenceTriangle) = 2 -vertices(::ReferenceTriangle) = SVector(0, 0), SVector(1, 0), SVector(0, 1) -Base.in(x, ::ReferenceTriangle) = 0 ≤ x[1] ≤ 1 && 0 ≤ x[2] ≤ 1 - x[1] -center(::ReferenceTriangle) = svector(i -> 1 / 3, 3) - -""" - struct ReferenceTetrahedron - -Singleton type representing the tetrahedron with vertices -`(0,0,0),(0,0,1),(0,1,0),(1,0,0)` -""" -struct ReferenceTetrahedron <: AbstractReferenceShape end -geometric_dimension(::ReferenceTetrahedron) = 3 -ambient_dimension(::ReferenceTetrahedron) = 3 -vertices(::ReferenceTetrahedron) = SVector(0, 0, 0), SVector(1, 0, 0), SVector(0, 1, 0), SVector(0, 0, 1) -Base.in(x,::ReferenceTetrahedron) = 0 ≤ x[1] ≤ 1 && 0 ≤ x[2] ≤ 1 - x[1] && 0 ≤ x[3] ≤ 1 - x[1] - x[2] -center(::ReferenceTetrahedron) = svector(i -> 1 / 4, 4) - """ struct ReferenceSimplex{N} @@ -49,14 +24,28 @@ function Base.in(x, ::ReferenceSimplex{N}) where {N} end return true end - function standard_basis_vector(T, ::Val{I}, ::Val{N}) where {I,N} v = zero(MVector{N,T}) v[I] = one(T) SVector(v) end - vertices(::ReferenceSimplex{N}) where {N} = ntuple(i -> standard_basis_vector(Int64, Val{i}, Val{N}), N) |> SVector +center(::ReferenceSimplex{N}) where {N} = svector(i -> 1 / (N + 1), N ) + +""" + struct ReferenceTriangle + +Singleton type representing the triangle with vertices `(0,0),(1,0),(0,1)` +""" +const ReferenceTriangle = ReferenceSimplex{2} + +""" + struct ReferenceTetrahedron + +Singleton type representing the tetrahedron with vertices +`(0,0,0),(0,0,1),(0,1,0),(1,0,0)` +""" +const ReferenceTetrahedron = ReferenceSimplex{3} """ struct ReferenceHyperCube{N} <: AbstractReferenceShape{N} From 4d6b7e8a51ab79cb254cb09be12fb73080e7ed89 Mon Sep 17 00:00:00 2001 From: "Thomas G. Anderson" Date: Sun, 1 Oct 2023 11:00:49 -0500 Subject: [PATCH 5/6] Reference shapes: more tests; fix simplex vertices --- src/Geometry/referenceshapes.jl | 2 +- test/Geometry/referenceshapes_test.jl | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Geometry/referenceshapes.jl b/src/Geometry/referenceshapes.jl index e512fb7a..77b158ba 100644 --- a/src/Geometry/referenceshapes.jl +++ b/src/Geometry/referenceshapes.jl @@ -29,7 +29,7 @@ function standard_basis_vector(T, ::Val{I}, ::Val{N}) where {I,N} v[I] = one(T) SVector(v) end -vertices(::ReferenceSimplex{N}) where {N} = ntuple(i -> standard_basis_vector(Int64, Val{i}, Val{N}), N) |> SVector +vertices(::ReferenceSimplex{N}) where {N} = ntuple(i -> i == 1 ? zero(SVector{N, Int64}) : standard_basis_vector(Int64, Val(i-1), Val(N)), N+1) |> SVector center(::ReferenceSimplex{N}) where {N} = svector(i -> 1 / (N + 1), N ) """ diff --git a/test/Geometry/referenceshapes_test.jl b/test/Geometry/referenceshapes_test.jl index 1b7e33dd..033a7ed5 100644 --- a/test/Geometry/referenceshapes_test.jl +++ b/test/Geometry/referenceshapes_test.jl @@ -12,6 +12,8 @@ using StaticArrays @test x ∈ l x = SVector(1.1) @test !in(x, l) + @test Inti.center(l) == SVector(1/2) + @test Inti.vertices(l) == (SVector(0), SVector(1)) end @testset "Triangle" begin t = Inti.ReferenceTriangle() @@ -23,6 +25,8 @@ end @test x ∈ t x = SVector(1.1, 0.0) @test !in(x, t) + @test Inti.center(t) == SVector(1/3, 1/3) + @test Inti.vertices(t) == SVector{3, SVector{2, Int64}}([0, 0], [1, 0], [0, 1]) end @testset "Tetrahedron" begin t = Inti.ReferenceTetrahedron() @@ -34,6 +38,8 @@ end @test x ∈ t x = SVector(1.1, 0.0, 0.0) @test !in(x, t) + @test Inti.center(t) == SVector(1/4, 1/4, 1/4) + @test Inti.vertices(t) == SVector{4, SVector{3, Int64}}([0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]) end @testset "NSimplex" begin t = Inti.ReferenceSimplex{4}() @@ -45,4 +51,6 @@ end @test x ∈ t x = SVector(1.1, 0.0, 0.0, 0.0) @test !in(x, t) + @test Inti.center(t) == SVector(1/5, 1/5, 1/5, 1/5) + @test Inti.vertices(t) == SVector{5, SVector{4, Int64}}([0, 0, 0, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]) end \ No newline at end of file From 9e3e08ce7b2499de84afea6a2b875c98662cec27 Mon Sep 17 00:00:00 2001 From: "Luiz M. Faria" Date: Mon, 2 Oct 2023 11:22:40 +0200 Subject: [PATCH 6/6] reduce verbosity, simplify `standard_basis_vector` --- src/Geometry/referenceshapes.jl | 7 +------ src/utils.jl | 7 +++++++ test/referenceshapes_test.jl | 37 +++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 test/referenceshapes_test.jl diff --git a/src/Geometry/referenceshapes.jl b/src/Geometry/referenceshapes.jl index 77b158ba..46074341 100644 --- a/src/Geometry/referenceshapes.jl +++ b/src/Geometry/referenceshapes.jl @@ -24,12 +24,7 @@ function Base.in(x, ::ReferenceSimplex{N}) where {N} end return true end -function standard_basis_vector(T, ::Val{I}, ::Val{N}) where {I,N} - v = zero(MVector{N,T}) - v[I] = one(T) - SVector(v) -end -vertices(::ReferenceSimplex{N}) where {N} = ntuple(i -> i == 1 ? zero(SVector{N, Int64}) : standard_basis_vector(Int64, Val(i-1), Val(N)), N+1) |> SVector +vertices(::ReferenceSimplex{N}) where {N} = svector(i -> i == 1 ? zero(SVector{N, Int64}) : standard_basis_vector(i-1, Val(N)), N+1) center(::ReferenceSimplex{N}) where {N} = svector(i -> 1 / (N + 1), N ) """ diff --git a/src/utils.jl b/src/utils.jl index 1a19fe39..2b5911a8 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -11,3 +11,10 @@ Create an `SVector` of length n, computing each element as f(i), where i is the index of the element. """ svector(f,n)= ntuple(f,n) |> SVector + +""" + standard_basis_vector(k, ::Val{N}) + +Create an `SVector` of length N with a 1 in the kth position and zeros elsewhere. +""" +standard_basis_vector(k, n::Val{N}) where {N} = svector(i-> i == k ? 1 : 0, n) diff --git a/test/referenceshapes_test.jl b/test/referenceshapes_test.jl new file mode 100644 index 00000000..882355f4 --- /dev/null +++ b/test/referenceshapes_test.jl @@ -0,0 +1,37 @@ +using Test +import Inti +using StaticArrays + +@testset "Line" begin + l = Inti.ReferenceLine() + @test Inti.ambient_dimension(l) == 1 + @test Inti.geometric_dimension(l) == 1 + x = SVector(0.5) + @test x ∈ l + x = SVector(1.0) + @test x ∈ l + x = SVector(1.1) + @test !in(x, l) +end +@testset "Triangle" begin + t = Inti.ReferenceTriangle() + @test Inti.ambient_dimension(t) == 2 + @test Inti.geometric_dimension(t) == 2 + x = SVector(0.5, 0.5) + @test x ∈ t + x = SVector(1.0, 0.0) + @test x ∈ t + x = SVector(1.1, 0.0) + @test !in(x, t) +end +@testset "Tetrahedron" begin + t = Inti.ReferenceTetrahedron() + @test Inti.ambient_dimension(t) == 3 + @test Inti.geometric_dimension(t) == 3 + x = SVector(0.5, 0.5, 0.0) + @test x ∈ t + x = SVector(1.0, 0.0, 0.0) # point on edge + @test x ∈ t + x = SVector(1.1, 0.0, 0.0) + @test !in(x, t) +end