comparison src/Grids/mapped_grid.jl @ 1835:a6f28a8b8f3f refactor/lazy_tensors/elementwise_ops

Merge default
author Jonatan Werpers <jonatan@werpers.com>
date Thu, 09 Jan 2025 12:40:49 +0100
parents f21bfc5f21aa
children d91a9f47380f 85f8855473ab 1c58005429fd
comparison
equal deleted inserted replaced
1789:48eaa973159a 1835:a6f28a8b8f3f
1 """
2 MappedGrid{T,D} <: Grid{T,D}
3
4 A grid defined by a coordinate mapping from a logical grid to some physical
5 coordinates. The physical coordinates and the Jacobian are stored as grid
6 functions corresponding to the logical grid.
7
8 See also: [`logical_grid`](@ref), [`jacobian`](@ref), [`metric_tensor`](@ref).
9 """
10 struct MappedGrid{T,D, GT<:Grid{<:Any,D}, CT<:AbstractArray{T,D}, JT<:AbstractArray{<:AbstractMatrix{<:Any}, D}} <: Grid{T,D}
11 logical_grid::GT
12 physicalcoordinates::CT
13 jacobian::JT
14
15 """
16 MappedGrid(logical_grid, physicalcoordinates, jacobian)
17
18 A MappedGrid with the given physical coordinates and jacobian.
19 """
20 function MappedGrid(logical_grid::GT, physicalcoordinates::CT, jacobian::JT) where {T,D, GT<:Grid{<:Any,D}, CT<:AbstractArray{T,D}, JT<:AbstractArray{<:AbstractMatrix{<:Any}, D}}
21 if !(size(logical_grid) == size(physicalcoordinates) == size(jacobian))
22 throw(ArgumentError("Sizes must match"))
23 end
24
25 if size(first(jacobian)) != (length(first(physicalcoordinates)),D)
26 throw(ArgumentError("The size of the jacobian must match the dimensions of the grid and coordinates"))
27 end
28
29 return new{T,D,GT,CT,JT}(logical_grid, physicalcoordinates, jacobian)
30 end
31 end
32
33 function Base.:(==)(a::MappedGrid, b::MappedGrid)
34 same_logical_grid = logical_grid(a) == logical_grid(b)
35 same_coordinates = collect(a) == collect(b)
36 same_jacobian = jacobian(a) == jacobian(b)
37
38 return same_logical_grid && same_coordinates && same_jacobian
39 end
40
41 """
42 logical_grid(g::MappedGrid)
43
44 The logical grid of `g`.
45 """
46 logical_grid(g::MappedGrid) = g.logical_grid
47
48 """
49 jacobian(g::MappedGrid)
50
51 The Jacobian matrix of `g` as a grid function.
52 """
53 jacobian(g::MappedGrid) = g.jacobian
54
55
56 # Indexing interface
57 Base.getindex(g::MappedGrid, I::Vararg{Int}) = g.physicalcoordinates[I...]
58 Base.eachindex(g::MappedGrid) = eachindex(g.logical_grid)
59
60 Base.firstindex(g::MappedGrid, d) = firstindex(g.logical_grid, d)
61 Base.lastindex(g::MappedGrid, d) = lastindex(g.logical_grid, d)
62
63 # Iteration interface
64 Base.iterate(g::MappedGrid) = iterate(g.physicalcoordinates)
65 Base.iterate(g::MappedGrid, state) = iterate(g.physicalcoordinates, state)
66
67 Base.IteratorSize(::Type{<:MappedGrid{<:Any, D}}) where D = Base.HasShape{D}()
68 Base.length(g::MappedGrid) = length(g.logical_grid)
69 Base.size(g::MappedGrid) = size(g.logical_grid)
70 Base.size(g::MappedGrid, d) = size(g.logical_grid, d)
71
72 boundary_identifiers(g::MappedGrid) = boundary_identifiers(g.logical_grid)
73 boundary_indices(g::MappedGrid, id::TensorGridBoundary) = boundary_indices(g.logical_grid, id)
74
75 function boundary_grid(g::MappedGrid, id::TensorGridBoundary)
76 b_indices = boundary_indices(g.logical_grid, id)
77
78 # Calculate indices of needed jacobian components
79 D = ndims(g)
80 all_indices = SVector{D}(1:D)
81 free_variable_indices = deleteat(all_indices, grid_id(id))
82 jacobian_components = (:, free_variable_indices)
83
84 # Create grid function for boundary grid jacobian
85 boundary_jacobian = componentview((@view g.jacobian[b_indices...]) , jacobian_components...)
86 boundary_physicalcoordinates = @view g.physicalcoordinates[b_indices...]
87
88 return MappedGrid(
89 boundary_grid(g.logical_grid, id),
90 boundary_physicalcoordinates,
91 boundary_jacobian,
92 )
93 end
94
95
96 """
97 mapped_grid(x, J, size::Vararg{Int})
98
99 A `MappedGrid` with a default logical grid on the D-dimensional unit hyper
100 box [0,1]ᴰ. `x` and `J`are functions to be evaluated on the logical grid
101 and `size` determines the size of the logical grid.
102 """
103 function mapped_grid(x, J, size::Vararg{Int})
104 D = length(size)
105 lg = equidistant_grid(ntuple(i->0., D), ntuple(i->1., D), size...)
106 return mapped_grid(lg, x, J)
107 end
108
109 """
110 mapped_grid(lg::Grid, x, J)
111
112 A `MappedGrid` with logical grid `lg`. Physical coordinates and Jacobian are
113 determined by the functions `x` and `J`.
114 """
115 function mapped_grid(lg::Grid, x, J)
116 return MappedGrid(
117 lg,
118 map(x,lg),
119 map(J,lg),
120 )
121 end
122
123 """
124 metric_tensor(g::MappedGrid)
125
126 The metric tensor of `g` as a grid function.
127 """
128 function metric_tensor(g::MappedGrid)
129 return map(jacobian(g)) do ∂x∂ξ
130 ∂x∂ξ'*∂x∂ξ
131 end
132 end
133
134 function min_spacing(g::MappedGrid{T,1} where T)
135 n, = size(g)
136
137 ms = Inf
138 for i ∈ 1:n-1
139 ms = min(ms, norm(g[i+1]-g[i]))
140 end
141
142 return ms
143 end
144
145 function min_spacing(g::MappedGrid{T,2} where T)
146 n, m = size(g)
147
148 ms = Inf
149 for i ∈ 1:n-1, j ∈ 1:m-1 # loop over each cell of the grid
150
151 ms = min(
152 ms,
153 norm(g[i+1,j]-g[i,j]),
154 norm(g[i,j+1]-g[i,j]),
155
156 norm(g[i+1,j]-g[i+1,j+1]),
157 norm(g[i,j+1]-g[i+1,j+1]),
158
159 norm(g[i+1,j+1]-g[i,j]),
160 norm(g[i+1,j]-g[i,j+1]),
161 )
162 # NOTE: This could be optimized to avoid checking all interior edges twice.
163 end
164
165 return ms
166 end
167
168 """
169 normal(g::MappedGrid, boundary)
170
171 The outward pointing normal as a grid function on the corresponding boundary grid.
172 """
173 function normal(g::MappedGrid, boundary)
174 b_indices = boundary_indices(g, boundary)
175 σ = _boundary_sign(component_type(g), boundary)
176
177 # TODO: Refactor this when `boundary_indices(g, ...)` has been made iterable.
178 return map(jacobian(g)[b_indices...]) do ∂x∂ξ
179 ∂ξ∂x = inv(∂x∂ξ)
180 k = grid_id(boundary)
181 σ*∂ξ∂x[k,:]/norm(∂ξ∂x[k,:])
182 end
183 end
184
185 """
186 normal(g::MappedGrid, boundary, i...)
187
188 The outward pointing normal to the specified boundary in grid point `i`.
189 """
190 function normal(g::MappedGrid, boundary, i...)
191 σ = _boundary_sign(component_type(g), boundary)
192 ∂ξ∂x = inv(jacobian(g)[i...])
193
194 k = grid_id(boundary)
195 return σ*∂ξ∂x[k,:]/norm(∂ξ∂x[k,:])
196 end
197
198
199 function _boundary_sign(T, boundary)
200 if boundary_id(boundary) == UpperBoundary()
201 return one(T)
202 elseif boundary_id(boundary) == LowerBoundary()
203 return -one(T)
204 else
205 throw(ArgumentError("The boundary identifier must be either `LowerBoundary()` or `UpperBoundary()`"))
206 end
207 end