Skip to content

Commit b362495

Browse files
authored
Add support for custom CRS in StructuredGrid (#1051)
* Add support for custom CRS in 'StructuredGrid' * More adjustments * Fix tests * Add more tests * Move helpers to units.jl
1 parent 6f7fcf3 commit b362495

File tree

13 files changed

+175
-79
lines changed

13 files changed

+175
-79
lines changed

src/coarsening/regular.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ end
2121
RegularCoarsening(factors::Vararg{Int,N}) where {N} = RegularCoarsening(factors)
2222

2323
function coarsen(grid::CartesianGrid, method::RegularCoarsening)
24-
factors = fitdims(method.factors, embeddim(grid))
24+
factors = fitdims(method.factors, paramdim(grid))
2525
CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) factors)
2626
end
2727

@@ -34,13 +34,13 @@ function coarsen(grid::RectilinearGrid, method::RegularCoarsening)
3434
RectilinearGrid{manifold(grid),crs(grid)}(xyzₜ)
3535
end
3636

37-
function coarsen(grid::StructuredGrid{Datum}, method::RegularCoarsening) where {Datum}
38-
factors = fitdims(method.factors, embeddim(grid))
37+
function coarsen(grid::StructuredGrid, method::RegularCoarsening)
38+
factors = fitdims(method.factors, paramdim(grid))
3939
dims = vsize(grid)
40-
rngs = ntuple(i -> 1:factors[i]:dims[i], embeddim(grid))
40+
rngs = ntuple(i -> 1:factors[i]:dims[i], paramdim(grid))
4141
XYZₛ = XYZ(grid)
42-
XYZₜ = ntuple(i -> XYZₛ[i][rngs...], embeddim(grid))
43-
StructuredGrid{Datum}(XYZₜ)
42+
XYZₜ = ntuple(i -> XYZₛ[i][rngs...], paramdim(grid))
43+
StructuredGrid{manifold(grid),crs(grid)}(XYZₜ)
4444
end
4545

4646
coarsen(grid::TransformedGrid, method::RegularCoarsening) =

src/domains.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,6 @@ Base.convert(::Type{GeometrySet}, d::Domain) = GeometrySet(collect(d))
152152

153153
Base.convert(::Type{SimpleMesh}, m::Mesh) = SimpleMesh(vertices(m), topology(m))
154154

155-
Base.convert(::Type{StructuredGrid}, g::Grid) = StructuredGrid{datum(crs(g))}(XYZ(g))
155+
Base.convert(::Type{StructuredGrid}, g::Grid) = StructuredGrid{manifold(g),crs(g)}(XYZ(g))
156156

157157
Base.convert(::Type{RectilinearGrid}, g::RegularGrid) = RectilinearGrid{manifold(g),crs(g)}(xyz(g))

src/domains/meshes/rectilineargrid.jl

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function RectilinearGrid{M,C}(xyz::NTuple{N,AbstractVector}, topology::GridTopol
4242
"""))
4343
end
4444

45-
xyz′ = ntuple(i -> numconvert.(T, _withunit.(xyz[i], us[i])), nc)
45+
xyz′ = ntuple(i -> numconvert.(T, withunit.(xyz[i], us[i])), nc)
4646
RectilinearGrid{M,C,N,typeof(xyz′)}(xyz′, topology)
4747
end
4848

@@ -54,14 +54,10 @@ end
5454
RectilinearGrid{M,C}(xyz::AbstractVector...) where {M<:Manifold,C<:CRS} = RectilinearGrid{M,C}(xyz)
5555

5656
function RectilinearGrid(xyz::NTuple{N,AbstractVector}) where {N}
57-
try
58-
L = promote_type(ntuple(i -> _lentype(eltype(xyz[i])), N)...)
59-
M = 𝔼{N}
60-
C = Cartesian{NoDatum,N,L}
61-
RectilinearGrid{M,C}(xyz)
62-
catch
63-
throw(ArgumentError("invalid units for cartesian coordinates"))
64-
end
57+
L = promote_type(ntuple(i -> aslentype(eltype(xyz[i])), N)...)
58+
M = 𝔼{N}
59+
C = Cartesian{NoDatum,N,L}
60+
RectilinearGrid{M,C}(xyz)
6561
end
6662

6763
RectilinearGrid(xyz::AbstractVector...) = RectilinearGrid(xyz)
@@ -94,10 +90,3 @@ function Base.summary(io::IO, g::RectilinearGrid)
9490
join(io, size(g), "×")
9591
print(io, " RectilinearGrid")
9692
end
97-
98-
# -----------------
99-
# HELPER FUNCTIONS
100-
# -----------------
101-
102-
_lentype(::Type{T}) where {T<:Len} = T
103-
_lentype(::Type{T}) where {T<:Number} = Met{T}

src/domains/meshes/regulargrid.jl

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ function RegularGrid(
6767
"""))
6868
end
6969

70-
sp = ntuple(i -> numconvert(T, _withunit(spacing[i], us[i])), nc)
70+
sp = ntuple(i -> numconvert(T, withunit(spacing[i], us[i])), nc)
7171

7272
RegularGrid{M,C,typeof(sp),N}(origin, sp, offset, topology)
7373
end
@@ -135,10 +135,3 @@ function Base.show(io::IO, ::MIME"text/plain", g::RegularGrid)
135135
println(io, "├─ maximum: ", maximum(g))
136136
print(io, "└─ spacing: ", spacing(g))
137137
end
138-
139-
# -----------------
140-
# HELPER FUNCTIONS
141-
# -----------------
142-
143-
_withunit(x::Number, u) = x * u
144-
_withunit(x::Quantity, u) = uconvert(u, x)

src/domains/meshes/structuredgrid.jl

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55
"""
66
StructuredGrid(X, Y, Z, ...)
7-
StructuredGrid{Datum}(X, Y, Z, ...)
7+
StructuredGrid{M,C}(X, Y, Z, ...)
88
99
A structured grid with vertices at sorted coordinates `X`, `Y`, `Z`, ...,
10-
and a given `Datum` (default to `NoDatum`).
10+
manifold `M` (default to `𝔼`) and CRS type `C` (default to `Cartesian`).
1111
1212
## Examples
1313
@@ -20,41 +20,78 @@ julia> Y = repeat([0.0, 0.1, 0.3, 0.7, 0.9, 1.0]', 6, 1)
2020
julia> StructuredGrid(X, Y)
2121
```
2222
"""
23-
struct StructuredGrid{Datum,Dim,ℒ<:Len,A<:AbstractArray{ℒ}} <: Grid{𝔼{Dim},Cartesian{Datum,Dim,ℒ},Dim}
24-
XYZ::NTuple{Dim,A}
25-
topology::GridTopology{Dim}
26-
27-
function StructuredGrid{Datum}(XYZ::NTuple{Dim,<:AbstractArray{<:Len}}, topology::GridTopology{Dim}) where {Datum,Dim}
28-
coords = float.(XYZ)
29-
A = eltype(coords)
30-
new{Datum,Dim,eltype(A),A}(coords, topology)
23+
struct StructuredGrid{M<:Manifold,C<:CRS,N,X<:NTuple{N,AbstractArray}} <: Grid{M,C,N}
24+
XYZ::X
25+
topology::GridTopology{N}
26+
StructuredGrid{M,C,N,X}(XYZ, topology) where {M<:Manifold,C<:CRS,N,X<:NTuple{N,AbstractArray}} = new(XYZ, topology)
27+
end
28+
29+
function StructuredGrid{M,C}(XYZ::NTuple{N,AbstractArray}, topology::GridTopology{N}) where {M<:Manifold,C<:CRS,N}
30+
if M <: 🌐 && !(C <: LatLon)
31+
throw(ArgumentError("rectilinear grid on `🌐` requires `LatLon` coordinates"))
32+
end
33+
34+
T = CoordRefSystems.mactype(C)
35+
nc = CoordRefSystems.ncoords(C)
36+
us = CoordRefSystems.units(C)
37+
38+
if N nc
39+
throw(ArgumentError("""
40+
A $N-dimensional structured grid requires a CRS with $N coordinates.
41+
The provided CRS has $nc coordinates.
42+
"""))
3143
end
44+
45+
XYZ′ = ntuple(i -> numconvert.(T, withunit.(XYZ[i], us[i])), nc)
46+
StructuredGrid{M,C,N,typeof(XYZ′)}(XYZ′, topology)
3247
end
3348

34-
StructuredGrid{Datum}(XYZ::NTuple{Dim,<:AbstractArray}, topology::GridTopology{Dim}) where {Datum,Dim} =
35-
StructuredGrid{Datum}(addunit.(XYZ, u"m"), topology)
49+
function StructuredGrid{M,C}(XYZ::NTuple{N,AbstractArray}) where {M<:Manifold,C<:CRS,N}
50+
if !allequal(size(X) for X in XYZ)
51+
throw(ArgumentError("all coordinate arrays must be the same size"))
52+
end
53+
54+
nd = ndims(first(XYZ))
3655

37-
function StructuredGrid{Datum}(XYZ::Tuple) where {Datum}
38-
coords = promote(XYZ...)
39-
topology = GridTopology(size(first(coords)) .- 1)
40-
StructuredGrid{Datum}(coords, topology)
56+
if nd N
57+
throw(ArgumentError("""
58+
A $N-dimensional structured grid requires coordinate arrays with $N dimensions.
59+
The provided coordinate arrays have $nd dimensions.
60+
"""))
61+
end
62+
63+
topology = GridTopology(size(first(XYZ)) .- 1)
64+
StructuredGrid{M,C}(XYZ, topology)
4165
end
4266

43-
StructuredGrid{Datum}(XYZ...) where {Datum} = StructuredGrid{Datum}(XYZ)
67+
StructuredGrid{M,C}(XYZ::AbstractArray...) where {M<:Manifold,C<:CRS} = StructuredGrid{M,C}(XYZ)
4468

45-
StructuredGrid(args...) = StructuredGrid{NoDatum}(args...)
69+
function StructuredGrid(XYZ::NTuple{N,AbstractArray}) where {N}
70+
L = promote_type(ntuple(i -> aslentype(eltype(XYZ[i])), N)...)
71+
M = 𝔼{N}
72+
C = Cartesian{NoDatum,N,L}
73+
StructuredGrid{M,C}(XYZ)
74+
end
4675

47-
vertex(g::StructuredGrid{Datum}, ijk::Dims) where {Datum} =
48-
Point(Cartesian{Datum}(ntuple(d -> g.XYZ[d][ijk...], embeddim(g))))
76+
StructuredGrid(XYZ::AbstractArray...) = StructuredGrid(XYZ)
77+
78+
function vertex(g::StructuredGrid, ijk::Dims)
79+
ctor = CoordRefSystems.constructor(crs(g))
80+
Point(ctor(ntuple(d -> g.XYZ[d][ijk...], paramdim(g))...))
81+
end
4982

5083
XYZ(g::StructuredGrid) = g.XYZ
5184

52-
function Base.getindex(g::StructuredGrid{Datum}, I::CartesianIndices) where {Datum}
53-
@boundscheck _checkbounds(g, I)
54-
dims = size(I)
55-
cinds = first(I):CartesianIndex(Tuple(last(I)) .+ 1)
56-
XYZ = ntuple(i -> g.XYZ[i][cinds], embeddim(g))
57-
StructuredGrid{Datum}(XYZ, GridTopology(dims))
85+
@generated function Base.getindex(g::StructuredGrid{M,C,N}, I::CartesianIndices) where {M,C,N}
86+
exprs = ntuple(i -> :(g.XYZ[$i][cinds]), N)
87+
88+
quote
89+
@boundscheck _checkbounds(g, I)
90+
dims = size(I)
91+
cinds = first(I):CartesianIndex(Tuple(last(I)) .+ 1)
92+
XYZ = ($(exprs...),)
93+
StructuredGrid{M,C}(XYZ, GridTopology(dims))
94+
end
5895
end
5996

6097
function Base.summary(io::IO, g::StructuredGrid)

src/refinement/regular.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ end
2121
RegularRefinement(factors::Vararg{Int,N}) where {N} = RegularRefinement(factors)
2222

2323
function refine(grid::CartesianGrid, method::RegularRefinement)
24-
factors = fitdims(method.factors, embeddim(grid))
24+
factors = fitdims(method.factors, paramdim(grid))
2525
CartesianGrid(minimum(grid), maximum(grid), dims=size(grid) .* factors)
2626
end
2727

@@ -32,10 +32,10 @@ function refine(grid::RectilinearGrid, method::RegularRefinement)
3232
RectilinearGrid{manifold(grid),crs(grid)}(xyzₜ)
3333
end
3434

35-
function refine(grid::StructuredGrid{Datum}, method::RegularRefinement) where {Datum}
36-
factors = fitdims(method.factors, embeddim(grid))
35+
function refine(grid::StructuredGrid, method::RegularRefinement)
36+
factors = fitdims(method.factors, paramdim(grid))
3737
XYZ′ = _XYZ(grid, factors)
38-
StructuredGrid{Datum}(XYZ′)
38+
StructuredGrid{manifold(grid),crs(grid)}(XYZ′)
3939
end
4040

4141
refine(grid::TransformedGrid, method::RegularRefinement) =
@@ -49,7 +49,7 @@ function _refinedims(x, f)
4949
x′
5050
end
5151

52-
_XYZ(grid::StructuredGrid, factors::Dims) = _XYZ(grid, Val(embeddim(grid)), factors)
52+
_XYZ(grid::StructuredGrid, factors::Dims) = _XYZ(grid, Val(paramdim(grid)), factors)
5353

5454
function _XYZ(grid::StructuredGrid, ::Val{2}, factors::Dims{2})
5555
T = numtype(lentype(grid))

src/transforms/lengthunit.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ applycoord(t::LengthUnit, lens::NTuple{Dim,Len}) where {Dim} = uconvert.(t.unit,
3434

3535
applycoord(t::LengthUnit, g::RectilinearGrid) = TransformedGrid(g, t)
3636

37-
applycoord(t::LengthUnit, g::StructuredGrid) = StructuredGrid{datum(crs(g))}(map(X -> uconvert.(t.unit, X), XYZ(g)))
37+
applycoord(t::LengthUnit, g::StructuredGrid) = TransformedGrid(g, t)
3838

3939
# -----------------
4040
# HELPER FUNCTIONS

src/transforms/scale.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,5 @@ end
7979
applycoord(t::Scale, g::RectilinearGrid) =
8080
RectilinearGrid{manifold(g),crs(g)}(ntuple(i -> t.factors[i] * xyz(g)[i], paramdim(g)))
8181

82-
applycoord(t::Scale{Dim}, g::StructuredGrid{Datum,Dim}) where {Datum,Dim} =
83-
StructuredGrid{Datum}(ntuple(i -> t.factors[i] * XYZ(g)[i], Dim))
82+
applycoord(t::Scale, g::StructuredGrid) =
83+
StructuredGrid{manifold(g),crs(g)}(ntuple(i -> t.factors[i] * XYZ(g)[i], paramdim(g)))

src/transforms/shadow.jl

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ apply(t::Shadow, ct::CylindricalTrajectory) = apply(t, GeometrySet(collect(ct)))
6565

6666
apply(t::Shadow, g::RectilinearGrid) = TransformedGrid(g, t), nothing
6767

68+
apply(t::Shadow, g::StructuredGrid) = TransformedGrid(g, t), nothing
69+
6870
# -----------------
6971
# HELPER FUNCTIONS
7072
# -----------------
@@ -99,12 +101,6 @@ function _shadow(g::CartesianGrid, dims)
99101
CartesianGrid(sz, or, sp, of)
100102
end
101103

102-
function _shadow(g::StructuredGrid, dims)
103-
ndims = length(size(g))
104-
inds = ntuple(i -> ifelse(i dims, :, 1), ndims)
105-
StructuredGrid{datum(crs(g))}(map(X -> X[inds...], XYZ(g)[dims]))
106-
end
107-
108104
# apply shadow transform recursively
109105
@generated function _shadow(g::G, dims) where {G<:GeometryOrDomain}
110106
ctor = constructor(G)

src/transforms/translate.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ apply(t::Translate, g::RectilinearGrid) =
4242

4343
revert(t::Translate, g::RectilinearGrid, c) = first(apply(inverse(t), g))
4444

45-
apply(t::Translate, g::StructuredGrid{Datum}) where {Datum} =
46-
StructuredGrid{Datum}(ntuple(i -> XYZ(g)[i] .+ t.offsets[i], embeddim(g))), nothing
45+
apply(t::Translate, g::StructuredGrid) =
46+
StructuredGrid{manifold(g),crs(g)}(ntuple(i -> XYZ(g)[i] .+ t.offsets[i], paramdim(g))), nothing
4747

4848
revert(t::Translate, g::StructuredGrid, c) = first(apply(inverse(t), g))

src/units.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,22 @@ addunit(::AbstractArray{<:Quantity}, _) = throw(ArgumentError("invalid units, pl
2525
Converts the number type of quantity `x` to `T`.
2626
"""
2727
numconvert(::Type{T}, x::Quantity{S,D,U}) where {T,S,D,U} = convert(Quantity{T,D,U}, x)
28+
29+
"""
30+
withunit(x, u)
31+
32+
Adds the unit if the argument is not a quantity,
33+
otherwise, converts the unit of `x` to `u`.
34+
"""
35+
withunit(x::Number, u) = x * u
36+
withunit(x::Quantity, u) = uconvert(u, x)
37+
38+
"""
39+
aslentype(T)
40+
41+
If `T` has length unit, return it. If `T` is number, return it with meter unit.
42+
Otherwise, throw an error.
43+
"""
44+
aslentype(::Type{T}) where {T<:Len} = T
45+
aslentype(::Type{T}) where {T<:Number} = Met{T}
46+
aslentype(::Type{<:Quantity}) = throw(ArgumentError("invalid units, please use a valid length unit"))

0 commit comments

Comments
 (0)