comparison src/Grids/mapped_grid.jl @ 2057:8a2a0d678d6f feature/lazy_tensors/pretty_printing

Merge default
author Jonatan Werpers <jonatan@werpers.com>
date Tue, 10 Feb 2026 22:41:19 +0100
parents e97f4352b8d0
children 83759a0d0f28
comparison
equal deleted inserted replaced
1110:c0bff9f6e0fb 2057:8a2a0d678d6f
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 mapped_grid(x, J, size...)
97
98 A `MappedGrid` with a default logical grid on the D-dimensional unit hyper
99 box [0,1]ᴰ. `x` and `J` are functions to be evaluated on the logical grid
100 and `size` determines the size of the logical grid.
101 """
102 function mapped_grid(x, J, size::Vararg{Int})
103 D = length(size)
104 return mapped_grid(x, J, unithyperbox(D), size...)
105 end
106
107 """
108 mapped_grid(x, J, lg::Grid)
109
110 A `MappedGrid` with logical grid `lg`. Physical coordinates and Jacobian are
111 determined by the functions `x` and `J`.
112 """
113 function mapped_grid(x, J, lg::Grid)
114 return MappedGrid(
115 lg,
116 map(x,lg),
117 map(J,lg),
118 )
119 end
120
121 """
122 mapped_grid(x, J, ps::ParameterSpace, size...)
123
124 A `MappedGrid` with logical grid `lg`. Physical coordinates and Jacobian are
125 determined by the functions `x` and `J`.
126 """
127 function mapped_grid(x, J, ps::ParameterSpace, size::Vararg{Int})
128 lg = equidistant_grid(ps, size...)
129 return mapped_grid(x, J, lg)
130 end
131
132 """
133 metric_tensor(g::MappedGrid)
134
135 The metric tensor of `g` as a grid function.
136 """
137 function metric_tensor(g::MappedGrid)
138 return map(jacobian(g)) do ∂x∂ξ
139 ∂x∂ξ'*∂x∂ξ
140 end
141 end
142
143 function min_spacing(g::MappedGrid{T,1} where T)
144 n, = size(g)
145
146 ms = Inf
147 for i ∈ 1:n-1
148 ms = min(ms, norm(g[i+1]-g[i]))
149 end
150
151 return ms
152 end
153
154 function min_spacing(g::MappedGrid{T,2} where T)
155 n, m = size(g)
156
157 ms = Inf
158 for i ∈ 1:n-1, j ∈ 1:m-1 # loop over each cell of the grid
159
160 ms = min(
161 ms,
162 norm(g[i+1,j]-g[i,j]),
163 norm(g[i,j+1]-g[i,j]),
164
165 norm(g[i+1,j]-g[i+1,j+1]),
166 norm(g[i,j+1]-g[i+1,j+1]),
167
168 norm(g[i+1,j+1]-g[i,j]),
169 norm(g[i+1,j]-g[i,j+1]),
170 )
171 # NOTE: This could be optimized to avoid checking all interior edges twice.
172 end
173
174 return ms
175 end
176
177 """
178 normal(g::MappedGrid, boundary)
179
180 The outward pointing normal as a grid function on the corresponding boundary grid.
181 """
182 function normal(g::MappedGrid, boundary)
183 return map(boundary_indices(g, boundary)) do I
184 normal(g, boundary, Tuple(I)...)
185 end
186 end
187
188 """
189 normal(g::MappedGrid, boundary, i...)
190
191 The outward pointing normal to the specified boundary in grid point `i`.
192 """
193 function normal(g::MappedGrid, boundary, i...)
194 σ = _boundary_sign(component_type(g), boundary)
195 ∂ξ∂x = inv(jacobian(g)[i...])
196
197 k = grid_id(boundary)
198 return σ*∂ξ∂x[k,:]/norm(∂ξ∂x[k,:])
199 end
200
201
202 function _boundary_sign(T, boundary)
203 if boundary_id(boundary) == UpperBoundary()
204 return one(T)
205 elseif boundary_id(boundary) == LowerBoundary()
206 return -one(T)
207 else
208 throw(ArgumentError("The boundary identifier must be either `LowerBoundary()` or `UpperBoundary()`"))
209 end
210 end