comparison Notes.md @ 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 c0bff9f6e0fb fe058a0ebd97
children
comparison
equal deleted inserted replaced
1110:c0bff9f6e0fb 2057:8a2a0d678d6f
1 # Notes 1 # Notes
2
3 ## How to dispatch for different operators
4 We have a problem in how dispatch for different operators work.
5 * We want to keep the types simple and flat (Awkward to forward `apply`)
6 * We want to dispatch SATs on the parameters of the continuous operator. (a * div for example)
7 * We want to allow keeping the same stencil_set across different calls. (maybe not so bad for the user to be responsible)
8
9 Could remove the current opset idea and introduce a description of continuous operators
10 ```julia
11 abstract type DifferentialOperator end
12
13 struct Laplace <: DifferentialOperator end
14 struct Advection <: DifferentialOperator
15 v
16 end
17
18 difference_operator(::Laplace, grid, stencil_set) = ... # Returns a plain LazyTensor. Replaces the current `laplace()` function.
19 sat_tensors(::Laplace, grid, stencil_set, bc) = ...
20
21 sat(::DifferentialOperator, grid, stencil_set, bc) = ...
22 ```
23
24
25 ### Update 2024-06-26
26 We will run into trouble if we start assuming things about the coupling
27 between the continuous and discrete setting. We could add representations of
28 continuous operators but we will also need representations of discrete
29 operators. Ideally it should be possible to ignore the continuous
30 representations and only work with the discrete operators without losing
31 functionality. The discrete representations does not have to be LazyTensors.
32 The could be used as inputs to methods for `sat`, `difference_operator` and so
33 on.
34
35 To see need for a fully functional discrete layer we can consider the
36 optimization of material parameters or something similar. In this case we do
37 not necessarily want to handle continuous objects.
2 38
3 ## Reading operators 39 ## Reading operators
4 40
5 Jonatan's suggestion is to add methods to `Laplace`, `SecondDerivative` and 41 Jonatan's suggestion is to add methods to `Laplace`, `SecondDerivative` and
6 similar functions that take in a filename from which to read stencils. These 42 similar functions that take in a filename from which to read stencils. These
77 the instance if possible. With a specified MIME type 113 the instance if possible. With a specified MIME type
78 `show(::IO, ::MIME"text/plain", x)` should print a human readable, possible 114 `show(::IO, ::MIME"text/plain", x)` should print a human readable, possible
79 more verbose version. Compare to what the standard library does for regular 115 more verbose version. Compare to what the standard library does for regular
80 vectors. 116 vectors.
81 117
82 ## Variable second derivative
83
84 2020-12-08 after discussion with Vidar:
85 We will have to handle the variable second derivative in a new variant of
86 VolumeOperator, "SecondDerivativeVariable?". Somehow it needs to know about
87 the coefficients. They should be provided as an AbstractVector. Where they are
88 provided is another question. It could be that you provide a reference to the
89 array to the constructor of SecondDerivativeVariable. If that array is mutable
90 you are free to change it whenever and the changes should propagate
91 accordingly. Another option is that the counter part to "Laplace" for this
92 variable second derivate returns a function or acts like a functions that
93 takes an Abstract array and returns a SecondDerivativeVariable with the
94 appropriate array. This would allow syntax like `D2(a)*v`. Can this be made
95 performant?
96
97 For the 1d case we can have a constructor
98 `SecondDerivativeVariable(D2::SecondDerivativeVariable, a)` that just creates
99 a copy with a different `a`.
100
101 Apart from just the second derivative in 1D we need operators for higher
102 dimensions. What happens if a=a(x,y)? Maybe this can be solved orthogonally to
103 the `D2(a)*v` issue, meaning that if a constant nD version of
104 SecondDerivativeVariable is available then maybe it can be wrapped to support
105 function like syntax. We might have to implement `SecondDerivativeVariable`
106 for N dimensions which takes a N dimensional a. If this could be easily
107 closured to allow D(a) syntax we would have come a long way.
108
109 For `Laplace` which might use a variable D2 if it is on a curvilinear grid we
110 might want to choose how to calculate the metric coefficients. They could be
111 known on closed form, they could be calculated from the grid coordinates or
112 they could be provided as a vector. Which way you want to do it might change
113 depending on for example if you are memory bound or compute bound. This choice
114 cannot be done on the grid since the grid shouldn't care about the computer
115 architecture. The most sensible option seems to be to have an argument to the
116 `Laplace` function which controls how the coefficients are gotten from the
117 grid. The argument could for example be a function which is to be applied to
118 the grid.
119
120 What happens if the grid or the varible coefficient is dependent on time?
121 Maybe it becomes important to support `D(a)` or even `D(t,a)` syntax in a more
122 general way.
123
124 ```
125 g = TimeDependentGrid()
126 L = Laplace(g)
127 function Laplace(g::TimeDependentGrid)
128 g_logical = logical(g) # g_logical is time independent
129 ... Build a L(a) assuming we can do that ...
130 a(t) = metric_coeffs(g,t)
131 return t->L(a(t))
132 end
133 ```
134
135 ## Known size of range and domain? 118 ## Known size of range and domain?
136 Is there any reason to use a trait to differentiate between fixed size and unknown size? 119 Is there any reason to use a trait to differentiate between fixed size and unknown size?
137 120
138 When do we need to know the size of the range and domain? 121 When do we need to know the size of the range and domain?
139 * When indexing to provide boundschecking? 122 * When indexing to provide boundschecking?
141 * More? 124 * More?
142 125
143 Maybe if we should have dynamic sizing it could be only for the range. `domain_size` would not be implemented. And the `range_size` would be a function of a vector that the LazyTensor is applied to. 126 Maybe if we should have dynamic sizing it could be only for the range. `domain_size` would not be implemented. And the `range_size` would be a function of a vector that the LazyTensor is applied to.
144 127
145 ## Reasearch and thinking 128 ## Reasearch and thinking
146 - [ ] Use a trait to indicate that a LazyTensor har the same range and domain?
147 - [ ] Check how the native julia doc generator works 129 - [ ] Check how the native julia doc generator works
148 - [ ] Check if Vidars design docs fit in there 130 - [ ] Check if Vidars design docs fit in there
149 - [ ] Create a macro @lazy which replaces a binary op (+,-) by its lazy equivalent? Would be a neat way to indicate which evaluations are lazy without cluttering/confusing with special characters. 131 - [ ] Create a macro @lazy which replaces a binary op (+,-) by its lazy equivalent? Would be a neat way to indicate which evaluations are lazy without cluttering/confusing with special characters.
150 - [ ] Dispatch on Lower() instead of the type Lower so `::Lower` instead of `::Type{Lower}` ???
151 Seems better unless there is some specific reason to use the type instead of the value.
152 - [ ] How do we handle mixes of periodic and non-periodic grids? Seems it should be supported on the grid level and on the 1d operator level. Between there it should be transparent.
153 - [ ] Can we have a trait to tell if a LazyTensor is transposable? 132 - [ ] Can we have a trait to tell if a LazyTensor is transposable?
154 - [ ] Is it ok to have "Constructors" for abstract types which create subtypes? For example a Grids() functions that gives different kind of grids based on input?
155 - [ ] Figure out how to treat the borrowing parameters of operators. Include in into the struct? Expose via function dispatched on the operator type and grid?
156
157 ## Identifiers for regions
158 The identifiers (`Upper`, `Lower`, `Interior`) used for region indecies should probabily be included in the grid module. This allows new grid types to come with their own regions.
159 133
160 ## Regions and tensormappings 134 ## Regions and tensormappings
161 - [ ] Use a trait to indicate if a LazyTensor uses indices with regions. 135 - [ ] Use a trait to indicate if a LazyTensor uses indices with regions.
162 The default should be that they do NOT. 136 The default should be that they do NOT.
163 - [ ] What to name this trait? Can we call it IndexStyle but not export it to avoid conflicts with Base.IndexStyle? 137 - [ ] What to name this trait? Can we call it IndexStyle but not export it to avoid conflicts with Base.IndexStyle?
204 Does it make sense to have boundschecking only in getindex methods? 178 Does it make sense to have boundschecking only in getindex methods?
205 This would mean no bounds checking in applys, however any indexing that they do would be boundschecked. The only loss would be readability of errors. But users aren't really supposed to call apply directly anyway. 179 This would mean no bounds checking in applys, however any indexing that they do would be boundschecked. The only loss would be readability of errors. But users aren't really supposed to call apply directly anyway.
206 180
207 Preferably dimensions and sizes should be checked when lazy objects are created, for example TensorApplication, TensorComposition and so on. If dimension checks decreases performance we can make them skippable later. 181 Preferably dimensions and sizes should be checked when lazy objects are created, for example TensorApplication, TensorComposition and so on. If dimension checks decreases performance we can make them skippable later.
208 182
183 ## Changes to `eval_on`
184 There are reasons to replace `eval_on` with regular `map` from Base, and
185 implement a kind of lazy map perhaps `lmap` that work on indexable
186 collections.
187
188 The benefit of doing this is that we can treat grids as gridfunctions for the
189 coordinate function, and get a more flexible tool. For example `map`/`lmap`
190 can then be used both to evaluate a function on the grid but also get a
191 component of a vector valued grid function or similar.
192
193 Below is a partial implementation of `lmap` with some ideas
194 ```julia
195 struct LazyMapping{T,IT,F}
196 f::F
197 indexable_iterator::IT # ___
198 end
199
200 function LazyMapping(f,I)
201 IT = eltype(I)
202 T = f(zero(T))
203 F = typeof(f)
204
205 return LazyMapping{T,IT,F}(f,I)
206 end
207
208 getindex(lm::LazyMapping, I...) = lm.f(lm.I[I...])
209 # indexabl interface
210 # iterable has shape
211
212 iterate(lm::LazyMapping) = _lazy_mapping_iterate(lm, iterate(lm.I))
213 iterate(lm::LazyMapping, state) = _lazy_mapping_iterate(lm, iterate(lm.I, state))
214
215 _lazy_mapping_iterate(lm, ::Nothing) = nothing
216 _lazy_mapping_iterate(lm, (next, state)) = lm.f(next), state
217
218 lmap(f, I) = LazyIndexableMap(f,I)
219 ```
220
221 The interaction of the map methods with the probable design of multiblock
222 functions involving nested indecies complicate the picture slightly. It's
223 unclear at the time of writing how this would work with `Base.map`. Perhaps we
224 want to implement our own versions of both eager and lazy map.
225
226
227 ### 2024-04
228 MappedArrays.jl provides a simple array type and function like the description
229 of LazyMapping above. One option is to remove `eval_on` completely and rely on
230 destructuring arguments if handling the function input as a vector is
231 undesirable.
232
233 If we can let multi-block grids be iterators over grid points we could even
234 handle those by specialized implementation of `map` and `mappedarray`.
235
236 ## Multiblock implementation
237 We want multiblock things to work very similarly to regular one block things.
238
239 ### Grid functions
240 Should probably support a nested indexing so that we first have an index for
241 subgrid and then an index for nodes on that grid. E.g `g[1,2][2,3]` or
242 `g[3][43,21]`.
243
244 We could also possibly provide a combined indexing style `g[1,2,3,4]` where
245 the first group of indices are for the subgrid and the remaining are for the
246 nodes.
247
248 We should make sure the underlying buffer for grid functions are continuously
249 stored and are easy to convert to, so that interaction with for example
250 DifferentialEquations is simple and without much boilerplate.
251
252 #### `map` and `collect` and nested indexing
253 We need to make sure `collect`, `map` and a potential lazy map work correctly
254 through the nested indexing. Also see notes on `eval_on` above.
255
256 Possibly this can be achieved by providing special nested indexing but not
257 adhering to an array interface at the top level, instead being implemented as
258 an iterator over the grid points. A custom trait can let map and other methods
259 know the shape (or structure) of the nesting so that they can efficiently
260 allocate result arrays.
261
262 ### Tensor applications
263 Should behave as grid functions
264
265 ### LazyTensors
266 Could be built as a tuple or array of LazyTensors for each grid with a simple apply function.
267
268 Nested indexing for these is problably not needed unless it simplifies their own implementation.
269
270 Possibly useful to provide a simple type that doesn't know about connections between the grids. Antother type can include knowledge of the.
271
272 We have at least two option for how to implement them:
273 * Matrix of LazyTensors
274 * Looking at the grid and determining what the apply should do.
275
276 ### Overall design implications of nested indices
277 If some grids accept nested indexing there might be a clash with how LazyArrays work. It would be nice if the grid functions and lazy arrays that actually are arrays can be AbstractArray and things can be relaxed for nested index types.
278
209 ## Vector valued grid functions 279 ## Vector valued grid functions
210 Från slack konversation:
211
212 Jonatan Werpers:
213 Med vektorvärda gridfunktioner vill vi ju fortfarande att grid funktionen ska vara till exempel AbstractArray{LitenVektor,2}
214 Och att man ska kunna göra allt man vill med LitenVektor
215 typ addera, jämföra osv
216 Och då borde points returnera AbstractArray{LitenVektor{Float,2},2} för ett 2d nät
217 Men det kanske bara ska vara Static arrays?
218
219 Vidar Stiernström:
220 Ja, jag vet inte riktigt vad som är en rimlig representation
221 Du menar en vektor av static arrays då?
222
223 Jonatan Werpers:
224 Ja, att LitenVektor är en StaticArray
225
226 Vidar Stiernström:
227 Tuplar känns typ rätt inuitivt för att representera värdet i en punkt
228 men
229 det suger att man inte har + och - för dem
230
231 Jonatan Werpers:
232 Ja precis
233
234 Vidar Stiernström:
235 så kanske är bra med static arrays i detta fall
236
237 Jonatan Werpers:
238 Man vill ju kunna köra en Operator rakt på och vara klar eller?
239
240 Vidar Stiernström:
241 Har inte alls tänkt på hur det vi gör funkar mot vektorvärda funktioner
242 men känns som staticarrays är hur man vill göra det
243 tuplar är ju immutable också
244 blir jobbigt om man bara agerar på en komponent då
245
246 Jonatan Werpers:
247 Hm…
248 Tål att tänkas på
249 Men det lär ju bli mer indirektion med mutables eller?
250 Hur fungerar det?
251 Det finns ju hur som helst både SVector och MVector i StaticArrays
252
253 Vidar Stiernström:
254 När vi jobbat i c/c++ och kollat runt lite hur man brukar göra så lagrar man i princip alla sina obekanta i en lång vektor och så får man specificera i funktioerna vilken komponent man agerar på och till vilken man skriver
255 så man lagrar grejer enl: w = [u1, v1, u2, v2, …] i 1D.
256 Men alltså har ingen aning hur julia hanterar detta
257
258 Jonatan Werpers:
259 Det vi är ute efter kanske är att en grid funcktion är en AbstractArray{T,2} where T<:NågotSomViKanRäknaMed
260 Och så får den typen var lite vad som helst.
261
262 Vidar Stiernström:
263 Tror det kan vara farligt att ha nåt som är AbstractArray{LitenArray{NDof},Dim}
264 Jag gissar att det kompilatorn vill ha är en stor array med doubles
265
266 Jonatan Werpers:
267 Och sen är det upp till den som använder grejerna att vara smart
268 Vill man vara trixig kan man väl då imlementera SuperHaxxorGridFunction <: AbstractArray{Array{…},2} som lagrar allt linjärt eller något sånt
269 Det kommer väl lösa sig när man börjar implementera vektorvärda saker
270 Euler nästa!
271 New
272 Vidar Stiernström:
273 Det vore skönt att inte behöva skriva såhär varje gång man testar mot en tupel :smile: @test [gp[i]...] ≈ [p[i]...] atol=5e-13
274
275 Jonatan Werpers:
276 https://github.com/JuliaArrays/ArraysOfArrays.jl
277 https://github.com/jw3126/Setfield.jl
278 280
279 ### Test-applikationer 281 ### Test-applikationer
280 div och grad operationer 282 div- och grad-operationer
281 283
282 Enligt Wikipedia verkar det som att `∇⋅` agerar på första dimensionen av ett tensor fält och `div()` på sista. 284 Enligt Wikipedia verkar det som att `∇⋅` agerar på första dimensionen av ett tensorfält och `div()` på sista.
283 Om man generaliserar kanske `∇` i så fall bara lägger till en dimension i början. 285 Om man generaliserar kanske `∇` i så fall bara lägger till en dimension i början.
284 286
285 Kan vi implementera `⋅`(\cdot) så att de fungerar som man vill för både tensor-fält och tensor-operatorer? 287 Kan vi implementera `⋅`(\cdot) så att de fungerar som man vill för både tensor-fält och tensor-operatorer?
286 288
287 Är `∇` ett tensor-fält av tensor-operatorer? Vad är ett tensor-fält i vår kod? Är det en special-fall av en tensor-mapping? 289 Är `∇` ett tensor-fält av tensor-operatorer? Vad är ett tensor-fält i vår kod? Är det en special-fall av en tensor-mapping?
288 290
289 ### Grid-funktionen 291 ### Grid-funktionen
290 Grid-funktionon har typen `AbstractArray{T,2} where T`. 292 Grid-funktioner har typen `AbstractArray{T,2} where T`.
291 `T` kan vara lite vad som helst, tillexemel en SVector eller Array, eller tuple. TensorOperatorerna bryr sig inte om exakt vad det är, mer än att typen måste stödja de operationer som operatorn använder. 293 `T` kan vara lite vad som helst, tillexemel en SVector eller Array, eller Tuple. Tensoroperatorerna bryr sig inte om exakt vad det är, mer än att typen måste stödja de operationer som operatorn använder.
292
293 En nackdel kan vara hur man ska få ut gridfunktionen för tex andra komponenten.
294
295 Syntax:
296 ```
297 f(x̄) = x̄
298 gf = evalOn(g, f)
299 gf[2,3] # x̄ för en viss gridpunkt
300 gf[2,3][2] # x̄[2] för en viss gridpunkt
301 ```
302
303 Note: Behöver bestämma om eval on skickar in `x̄` eller `x̄...` till `f`. Eller om man kan stödja båda.
304 294
305 ### Tensor operatorer 295 ### Tensor operatorer
306 Vi kan ha tensor-operatorer som agerar på ett skalärt fält och ger ett vektorfält eller tensorfält. 296 Vi kan ha tensor-operatorer som agerar på ett skalärt fält och ger ett vektorfält eller tensorfält.
307 Vi kan också ha tensor-operatorer som agerar på ett vektorfält eller tensorfält och ger ett skalärt fält. 297 Vi kan också ha tensor-operatorer som agerar på ett vektorfält eller tensorfält och ger ett skalärt fält.
308 298
315 305
316 Skulle kunna ha en funktion `range_type(::LazyTensor, ::Type{domain_type})` 306 Skulle kunna ha en funktion `range_type(::LazyTensor, ::Type{domain_type})`
317 307
318 Kanske kan man implementera `⋅(tm::LazyTensor{R,D}, v::AbstractArray{T,D})` där T är en AbstractArray, tm på något sätt har komponenter, lika många som T har element. 308 Kanske kan man implementera `⋅(tm::LazyTensor{R,D}, v::AbstractArray{T,D})` där T är en AbstractArray, tm på något sätt har komponenter, lika många som T har element.
319 309
320 ### Ratade alternativ: 310 ### Prestanda-aspekter
321 311 [Vidar, Discord, 2023-03-03]
322 312 Typiskt sett finns det två sätt att representera vektorvärda gridfunktioner AbstractArray{T,Dim} där T är en vektor över komponenterna. Man skulle alltså i 1D ha
323 #### 2.AbstractArray{T,2+1} where T (NOPE!) 313 u = [ [u1[x1], u2[x1]] , [u1[x2], u2[x2]], ... [u1[xN], u2[xN]]]. Detta brukar kallas array of structs (AoS). Alternativet är struct of arrays (SoA), där man har alla gridpunkter för en given komponent u = [[u1[x1], u1[x2]],... u1[xN]], [u2[x1], u2[x2], ... u2[xN]]].
324 Blir inte den här. Bryter mot alla tankar om hur grid funktioner ska fungera. Om de tillåts ha en annan dimension än nätet blir allt hemskt. 314
325 315 Personligen tycker jag att AoS känns som den mer naturliga representationen? Det skulle göra det enklarare att parallelisera en vektorvärd gridfunktion över gridpunkterna, och om man opererar på olika komponenter i samma funktion så är det också bra ur en minnesaccess-synpunkt då dessa kommer ligga nära vandra i minnet. Problemet är att AoS sabbar vektorisering på CPU då två gridpunkter i en komponent ligger långt bort från varandra. Efter lite eftersökningar (och efter att snackat lite med Ossian) så verkar det ändå som att AoS är dåligt på GPU, där man vill att trådar typiskt sett utföra samma operation på närliggande minne.
326 Man låter helt enkelt arrayen ha en extra dimension. En fördel är att man har en väldigt "native" typ. En nackdel kan vara att det eventuellt blir rörigt vilken dimension olika operatorer ska agera på. I värsta fall behöver vi "kroneckra in" de tillagda dimensionerna. Vektorfältets index kommer också att bli det första eftersom vi vill att de ska lagras kontinuerligt i minnet pga chachen. (Går kanske att lösa med en custom typ men då krånglar man till det för sig). En fördel skulle vara att man enkelt får ut olika komponenter. 316
327 317 Vad tänker du kring detta ur ett interface-perspektiv? Jag hittade paketet https://github.com/JuliaArrays/StructArrays.jl som verkar erbjuda AoS-interface men SoA-minneslayout så det kanske kan vara något vi kan använda? Inte native-stödd på samma sätt som SVector, men verkar iaf utvecklas aktivt.
328 Syntax: 318
329 ``` 319 [Efter telefonsamtal] För optimal prestanda behöver vi antagligen se till att man kan räkna ut varje komponent i en punkt individuellt. Detta så att man har frihet att till exempel låta den innersta loopen hålla komponentindexet konstant för att underlätta intruktionsvektorisering.
330 gf = eval_on_grid(g,f) 320
331 gf[:,2,3] # Hela vektorn för en gridpunkt 321
332 gf[2,2,3] # Andra komponenten av vektor fältet i en punkt. 322 [Vidare tankar]
333 gf[2,:,:] # 323 * Det borde bara vara output-gridfunktionen som behöver special-indexeras? Det viktiga på inputsidan är att den är lagrad på rätt sätt i minnet.
334 ``` 324 * Det borde inte vara några problem att behålla det "optimala" interfacet (gf[1,1,1][2]) till gridfunktionerna. Om man verkligen behöver kan skapa parallella indexeringsmetoder som gör det man behöver, i.e, "deep indexing".
335 325 * Det är inte säkert att vi behöver göra något speciellt på outputsidan överhuvudtaget. Det känns inte orimligt att kompilatorn skulle kunna optimera bort den koden som räknar ut onödiga komponenter.
336 ### Evaluering av funktioner på nät 326 * Om vi behöver special-indexering kommer till exempel LazyTensorApplication att behöva implementera det.
337 Hur ska man skriva funktioner som evalueras på nätet? `f(x,y) = ...` eller `f(x̄) = ...`? Eller båda? Kan eval_on_grid se skillnad eller får användaren specificera? 327 * För att komma vidare med något mer avancerat behöver vi antagligen implementera några operatorer som ger och agerar på vektorvärda funktioner. Tex grad, elastiska operatorn, andra?
338 328
339 ```
340 f(x,y) = [x^2, y^2]
341 f(x̄) = [x̄[1]^2, x̄[2]^2]
342 ```
343
344 Påverkas detta av hur vi förväntar oss kunna skapa lata gridfunktioner?
345
346 ### Komponenter som gridfunktioner
347 En viktig operation för vektor fält är att kunna få ut komponenter som grid-funktioner. Detta behöver antagligen kunna ske lazy.
348 Det finns ett par olika lösningar:
349 * Implementera en egen typ av view som tar hand om detta. Eller Accessors.jl?
350 * Använda en LazyTensor
351 * Någon typ av lazy-broadcast
352 * En lazy array som applicerar en funktion för varje element.
353
354 Skulle vara en fördel om det är hyffsat generiskt så att en eventuell användare kan utöka det enkelt om de har någon egen exotisk typ. Eller ska man vila helt på
355
356 Syntax:
357 ```
358 gf = eval(...)
359 component(gf,2) # Andra komponenten av en vektor
360 component(gf,2,3) # (2,3) elementet av en matris
361 component(gf,:,2) # Andra kolumnen av en matris
362 @ourview gf[:,:][2]
363 ```
364
365 ## Grids embedded in higher dimensions
366
367 For grids generated by asking for boundary grids for a regular grid, it would
368 make sense if these grids knew they were embedded in a higher dimension. They
369 would return coordinates in the full room. This would make sense when
370 drawing points for example, or when evaluating functions on the boundary.
371
372 Implementation of this is an issue that requires some thought. Adding an extra
373 "Embedded" type for each grid would make it easy to understand each type but
374 contribute to "type bloat". On the other hand adapting existing types to
375 handle embeddedness would complicate the now very simple grid types. Are there
376 other ways of doing the implentation?
377 329
378 ## Performance measuring 330 ## Performance measuring
379 We should be measuring performance early. How does our effective cpu and memory bandwidth utilization compare to peak performance? 331 We should be measuring performance early. How does our effective cpu and memory bandwidth utilization compare to peak performance?
380 332
381 We should make these test simple to run for any solver. 333 We should make these test simple to run for any solver.
393 A different approach would be to include it as a trait for operators so that you can specify what the adjoint for that operator is. 345 A different approach would be to include it as a trait for operators so that you can specify what the adjoint for that operator is.
394 346
395 347
396 ## Name of the `VolumeOperator` type for constant stencils 348 ## Name of the `VolumeOperator` type for constant stencils
397 It seems that the name is too general. The name of the method `volume_operator` makes sense. It should return different types of `LazyTensor` specialized for the grid. A suggetion for a better name is `ConstantStencilVolumeOperator` 349 It seems that the name is too general. The name of the method `volume_operator` makes sense. It should return different types of `LazyTensor` specialized for the grid. A suggetion for a better name is `ConstantStencilVolumeOperator`
350
351
352 ## Implementation of LazyOuterProduct
353 Could the implementation of LazyOuterProduct be simplified by making it a
354 struct containing two or more LazyTensors? (using split_tuple in a similar way
355 as TensorGrid)
356
357 ## Implementation of boundary_indices for more complex grids
358 To represent boundaries of for example tet-elements we can use a type `IndexCollection` to index a grid function directly.
359
360 ```julia
361 I = IndexCollection(...)
362 v[I]
363 ```
364
365 * This would impact how tensor grid works.
366 * To make things homogenous maybe these index collections should be used for the more simple grids too.
367 * The function `to_indices` from Base could be useful to implement for `IndexCollection`
368
369
370 ## Stencil application pipeline
371 We should make sure that `@inbounds` and `Base.@propagate_inbounds` are
372 applied correctly throughout the stack. When testing the performance of
373 stencil application on the bugfix/sbp_operators/stencil_return_type branch
374 there seemed to be some strange results where such errors could be the
375 culprit.
376
377
378 ## Tiled loops and regions in apply
379 There should be easy ways to use functionalty splitting the application of a lazy array into regions and using tiled iteration. This could make the application more efficient by reducing branching and improving cache usage in the tight loop. On commit f215ac2a5c66 and before there were some early tests regarding this in a DiffOp submodule.
380
381 The main ideas were:
382 ```julia
383 function apply_region!(D::DiffOpCartesian{2}, u::AbstractArray{T,2}, v::AbstractArray{T,2}) where T
384 apply_region!(D, u, v, Lower, Lower)
385 apply_region!(D, u, v, Lower, Interior)
386 apply_region!(D, u, v, Lower, Upper)
387 apply_region!(D, u, v, Interior, Lower)
388 apply_region!(D, u, v, Interior, Interior)
389 apply_region!(D, u, v, Interior, Upper)
390 apply_region!(D, u, v, Upper, Lower)
391 apply_region!(D, u, v, Upper, Interior)
392 apply_region!(D, u, v, Upper, Upper)
393 return nothing
394 end
395 ```
396
397 ```julia
398 using TiledIteration
399 function apply_region_tiled!(D::DiffOpCartesian{2}, u::AbstractArray{T,2}, v::AbstractArray{T,2}, r1::Type{<:Region}, r2::Type{<:Region}) where T
400 ri = regionindices(D.grid.size, closuresize(D.op), (r1,r2))
401 # TODO: Pass Tilesize to function
402 for tileaxs ∈ TileIterator(axes(ri), padded_tilesize(T, (5,5), 2))
403 for j ∈ tileaxs[2], i ∈ tileaxs[1]
404 I = ri[i,j]
405 u[I] = apply(D, v, (Index{r1}(I[1]), Index{r2}(I[2])))
406 end
407 end
408 return nothing
409 end
410 ```