comparison Notes.md @ 1395:bdcdbd4ea9cd feature/boundary_conditions

Merge with default. Comment out broken tests for boundary_conditions at sat
author Vidar Stiernström <vidar.stiernstrom@it.uu.se>
date Wed, 26 Jul 2023 21:35:50 +0200
parents ea2e8254820a e9dfc1998d31
children 4550beef9694
comparison
equal deleted inserted replaced
1217:ea2e8254820a 1395:bdcdbd4ea9cd
87 If possible the goal should be for the parsing to get all the way to the 87 If possible the goal should be for the parsing to get all the way to the
88 stencils so that a user calls `read_stencil_set` and gets a 88 stencils so that a user calls `read_stencil_set` and gets a
89 dictionary-structure containing stencils, tuples, scalars and other types 89 dictionary-structure containing stencils, tuples, scalars and other types
90 ready for input to the methods creating the operators. 90 ready for input to the methods creating the operators.
91 91
92 ## Variable second derivative
93
94 2020-12-08 after discussion with Vidar:
95 We will have to handle the variable second derivative in a new variant of
96 VolumeOperator, "SecondDerivativeVariable?". Somehow it needs to know about
97 the coefficients. They should be provided as an AbstractVector. Where they are
98 provided is another question. It could be that you provide a reference to the
99 array to the constructor of SecondDerivativeVariable. If that array is mutable
100 you are free to change it whenever and the changes should propagate
101 accordingly. Another option is that the counter part to "Laplace" for this
102 variable second derivate returns a function or acts like a functions that
103 takes an Abstract array and returns a SecondDerivativeVariable with the
104 appropriate array. This would allow syntax like `D2(a)*v`. Can this be made
105 performant?
106
107 For the 1d case we can have a constructor
108 `SecondDerivativeVariable(D2::SecondDerivativeVariable, a)` that just creates
109 a copy with a different `a`.
110
111 Apart from just the second derivative in 1D we need operators for higher
112 dimensions. What happens if a=a(x,y)? Maybe this can be solved orthogonally to
113 the `D2(a)*v` issue, meaning that if a constant nD version of
114 SecondDerivativeVariable is available then maybe it can be wrapped to support
115 function like syntax. We might have to implement `SecondDerivativeVariable`
116 for N dimensions which takes a N dimensional a. If this could be easily
117 closured to allow D(a) syntax we would have come a long way.
118
119 For `Laplace` which might use a variable D2 if it is on a curvilinear grid we
120 might want to choose how to calculate the metric coefficients. They could be
121 known on closed form, they could be calculated from the grid coordinates or
122 they could be provided as a vector. Which way you want to do it might change
123 depending on for example if you are memory bound or compute bound. This choice
124 cannot be done on the grid since the grid shouldn't care about the computer
125 architecture. The most sensible option seems to be to have an argument to the
126 `Laplace` function which controls how the coefficients are gotten from the
127 grid. The argument could for example be a function which is to be applied to
128 the grid.
129
130 What happens if the grid or the varible coefficient is dependent on time?
131 Maybe it becomes important to support `D(a)` or even `D(t,a)` syntax in a more
132 general way.
133
134 ```
135 g = TimeDependentGrid()
136 L = Laplace(g)
137 function Laplace(g::TimeDependentGrid)
138 g_logical = logical(g) # g_logical is time independent
139 ... Build a L(a) assuming we can do that ...
140 a(t) = metric_coeffs(g,t)
141 return t->L(a(t))
142 end
143 ```
144
145 ## Known size of range and domain? 92 ## Known size of range and domain?
146 Is there any reason to use a trait to differentiate between fixed size and unknown size? 93 Is there any reason to use a trait to differentiate between fixed size and unknown size?
147 94
148 When do we need to know the size of the range and domain? 95 When do we need to know the size of the range and domain?
149 * When indexing to provide boundschecking? 96 * When indexing to provide boundschecking?
157 - [ ] Check how the native julia doc generator works 104 - [ ] Check how the native julia doc generator works
158 - [ ] Check if Vidars design docs fit in there 105 - [ ] Check if Vidars design docs fit in there
159 - [ ] 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. 106 - [ ] 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.
160 - [ ] Dispatch on Lower() instead of the type Lower so `::Lower` instead of `::Type{Lower}` ??? 107 - [ ] Dispatch on Lower() instead of the type Lower so `::Lower` instead of `::Type{Lower}` ???
161 Seems better unless there is some specific reason to use the type instead of the value. 108 Seems better unless there is some specific reason to use the type instead of the value.
162 - [ ] 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.
163 - [ ] Can we have a trait to tell if a LazyTensor is transposable? 109 - [ ] Can we have a trait to tell if a LazyTensor is transposable?
164 - [ ] 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? 110 - [ ] 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?
165 - [ ] 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? 111 - [ ] 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?
166 112
167 ## Identifiers for regions 113 ## Identifiers for regions
168 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. 114 The identifiers (`Upper`, `Lower`, `Interior`) used for region indecies should probably be included in the grid module. This allows new grid types to come with their own regions.
115 We implement this by refactoring RegionIndices to be agnostic to the region types and then moving the actual types to Grids.
169 116
170 ## Regions and tensormappings 117 ## Regions and tensormappings
171 - [ ] Use a trait to indicate if a LazyTensor uses indices with regions. 118 - [ ] Use a trait to indicate if a LazyTensor uses indices with regions.
172 The default should be that they do NOT. 119 The default should be that they do NOT.
173 - [ ] What to name this trait? Can we call it IndexStyle but not export it to avoid conflicts with Base.IndexStyle? 120 - [ ] What to name this trait? Can we call it IndexStyle but not export it to avoid conflicts with Base.IndexStyle?
214 Does it make sense to have boundschecking only in getindex methods? 161 Does it make sense to have boundschecking only in getindex methods?
215 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. 162 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.
216 163
217 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. 164 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.
218 165
166 ## Changes to `eval_on`
167 There are reasons to replace `eval_on` with regular `map` from Base, and implement a kind of lazy map perhaps `lmap` that work on indexable collections.
168
169 The benefit of doing this is that we can treat grids as gridfunctions for the coordinate function, and get a more flexible tool. For example `map`/`lmap` can then be used both to evaluate a function on the grid but also get a component of a vector valued grid function or similar.
170
171 A question is how and if we should implement `map`/`lmap` for functions like `(x,y)->x*y` or stick to just using vector inputs. There are a few options.
172
173 * use `Base.splat((x,y)->x*y)` with the single argument `map`/`lmap`.
174 * implement a kind of `unzip` function to get iterators for each component, which can then be used with the multiple-iterators-version of `map`/`lmap`.
175 * Inspect the function in the `map`/`lmap` function to determine which matches.
176
177 Below is a partial implementation of `lmap` with some ideas
178 ```julia
179 struct LazyMapping{T,IT,F}
180 f::F
181 indexable_iterator::IT # ___
182 end
183
184 function LazyMapping(f,I)
185 IT = eltype(I)
186 T = f(zero(T))
187 F = typeof(f)
188
189 return LazyMapping{T,IT,F}(f,I)
190 end
191
192 getindex(lm::LazyMapping, I...) = lm.f(lm.I[I...])
193 # indexabl interface
194 # iterable has shape
195
196 iterate(lm::LazyMapping) = _lazy_mapping_iterate(lm, iterate(lm.I))
197 iterate(lm::LazyMapping, state) = _lazy_mapping_iterate(lm, iterate(lm.I, state))
198
199 _lazy_mapping_iterate(lm, ::Nothing) = nothing
200 _lazy_mapping_iterate(lm, (next, state)) = lm.f(next), state
201
202 lmap(f, I) = LazyIndexableMap(f,I)
203 ```
204
205 The interaction of the map methods with the probable design of multiblock functions involving nested indecies complicate the picture slightly. It's clear at the time of writing how this would work with `Base.map`. Perhaps we want to implement our own versions of both eager and lazy map.
206
207 ## Multiblock implementation
208 We want multiblock things to work very similarly to regular one block things.
209
210 ### Grid functions
211 Should probably support a nested indexing so that we first have an index for subgrid and then an index for nodes on that grid. E.g `g[1,2][2,3]` or `g[3][43,21]`.
212
213 We could also possibly provide a combined indexing style `g[1,2,3,4]` where the first group of indices are for the subgrid and the remaining are for the nodes.
214
215 We should make sure the underlying buffer for gridfunctions are continuously stored and are easy to convert to, so that interaction with for example DifferentialEquations is simple and without much boilerplate.
216
217 #### `map` and `collect` and nested indexing
218 We need to make sure `collect`, `map` and a potential lazy map work correctly through the nested indexing.
219
220 ### Tensor applications
221 Should behave as grid functions
222
223 ### LazyTensors
224 Could be built as a tuple or array of LazyTensors for each grid with a simple apply function.
225
226 Nested indexing for these is problably not needed unless it simplifies their own implementation.
227
228 Possibly useful to provide a simple type that doesn't know about connections between the grids. Antother type can include knowledge of the.
229
230 We have at least two option for how to implement them:
231 * Matrix of LazyTensors
232 * Looking at the grid and determining what the apply should do.
233
234 ### Overall design implications of nested indices
235 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.
236
219 ## Vector valued grid functions 237 ## Vector valued grid functions
220 Från slack konversation:
221
222 Jonatan Werpers:
223 Med vektorvärda gridfunktioner vill vi ju fortfarande att grid funktionen ska vara till exempel AbstractArray{LitenVektor,2}
224 Och att man ska kunna göra allt man vill med LitenVektor
225 typ addera, jämföra osv
226 Och då borde points returnera AbstractArray{LitenVektor{Float,2},2} för ett 2d nät
227 Men det kanske bara ska vara Static arrays?
228
229 Vidar Stiernström:
230 Ja, jag vet inte riktigt vad som är en rimlig representation
231 Du menar en vektor av static arrays då?
232
233 Jonatan Werpers:
234 Ja, att LitenVektor är en StaticArray
235
236 Vidar Stiernström:
237 Tuplar känns typ rätt inuitivt för att representera värdet i en punkt
238 men
239 det suger att man inte har + och - för dem
240
241 Jonatan Werpers:
242 Ja precis
243
244 Vidar Stiernström:
245 så kanske är bra med static arrays i detta fall
246
247 Jonatan Werpers:
248 Man vill ju kunna köra en Operator rakt på och vara klar eller?
249
250 Vidar Stiernström:
251 Har inte alls tänkt på hur det vi gör funkar mot vektorvärda funktioner
252 men känns som staticarrays är hur man vill göra det
253 tuplar är ju immutable också
254 blir jobbigt om man bara agerar på en komponent då
255
256 Jonatan Werpers:
257 Hm…
258 Tål att tänkas på
259 Men det lär ju bli mer indirektion med mutables eller?
260 Hur fungerar det?
261 Det finns ju hur som helst både SVector och MVector i StaticArrays
262
263 Vidar Stiernström:
264 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
265 så man lagrar grejer enl: w = [u1, v1, u2, v2, …] i 1D.
266 Men alltså har ingen aning hur julia hanterar detta
267
268 Jonatan Werpers:
269 Det vi är ute efter kanske är att en grid funcktion är en AbstractArray{T,2} where T<:NågotSomViKanRäknaMed
270 Och så får den typen var lite vad som helst.
271
272 Vidar Stiernström:
273 Tror det kan vara farligt att ha nåt som är AbstractArray{LitenArray{NDof},Dim}
274 Jag gissar att det kompilatorn vill ha är en stor array med doubles
275
276 Jonatan Werpers:
277 Och sen är det upp till den som använder grejerna att vara smart
278 Vill man vara trixig kan man väl då imlementera SuperHaxxorGridFunction <: AbstractArray{Array{…},2} som lagrar allt linjärt eller något sånt
279 Det kommer väl lösa sig när man börjar implementera vektorvärda saker
280 Euler nästa!
281 New
282 Vidar Stiernström:
283 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
284
285 Jonatan Werpers:
286 https://github.com/JuliaArrays/ArraysOfArrays.jl
287 https://github.com/jw3126/Setfield.jl
288 238
289 ### Test-applikationer 239 ### Test-applikationer
290 div och grad operationer 240 div- och grad-operationer
291 241
292 Enligt Wikipedia verkar det som att `∇⋅` agerar på första dimensionen av ett tensor fält och `div()` på sista. 242 Enligt Wikipedia verkar det som att `∇⋅` agerar på första dimensionen av ett tensorfält och `div()` på sista.
293 Om man generaliserar kanske `∇` i så fall bara lägger till en dimension i början. 243 Om man generaliserar kanske `∇` i så fall bara lägger till en dimension i början.
294 244
295 Kan vi implementera `⋅`(\cdot) så att de fungerar som man vill för både tensor-fält och tensor-operatorer? 245 Kan vi implementera `⋅`(\cdot) så att de fungerar som man vill för både tensor-fält och tensor-operatorer?
296 246
297 Ä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? 247 Ä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?
298 248
299 ### Grid-funktionen 249 ### Grid-funktionen
300 Grid-funktionon har typen `AbstractArray{T,2} where T`. 250 Grid-funktioner har typen `AbstractArray{T,2} where T`.
301 `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. 251 `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.
302 252
303 En nackdel kan vara hur man ska få ut gridfunktionen för tex andra komponenten. 253 En nackdel kan vara hur man ska få ut gridfunktionen för tex andra komponenten.
304 254
305 Syntax: 255 Syntax:
306 ``` 256 ```
308 gf = evalOn(g, f) 258 gf = evalOn(g, f)
309 gf[2,3] # x̄ för en viss gridpunkt 259 gf[2,3] # x̄ för en viss gridpunkt
310 gf[2,3][2] # x̄[2] för en viss gridpunkt 260 gf[2,3][2] # x̄[2] för en viss gridpunkt
311 ``` 261 ```
312 262
313 Note: Behöver bestämma om eval on skickar in `x̄` eller `x̄...` till `f`. Eller om man kan stödja båda.
314
315 ### Tensor operatorer 263 ### Tensor operatorer
316 Vi kan ha tensor-operatorer som agerar på ett skalärt fält och ger ett vektorfält eller tensorfält. 264 Vi kan ha tensor-operatorer som agerar på ett skalärt fält och ger ett vektorfält eller tensorfält.
317 Vi kan också ha tensor-operatorer som agerar på ett vektorfält eller tensorfält och ger ett skalärt fält. 265 Vi kan också ha tensor-operatorer som agerar på ett vektorfält eller tensorfält och ger ett skalärt fält.
318 266
319 TBD: Just nu gör `apply_transpose` antagandet att domän-typen är samma som range-typen. Det behöver vi på något sätt bryta. Ett alternativ är låta en LazyTensor ha `T_domain` och `T_range` istället för bara `T`. Känns dock lite grötigt. Ett annat alternativ skulle vara någon typ av trait för transpose? Den skulle kunna innehålla typen som transponatet agerar på? Vet inte om det fungerar dock. 267 TBD: Just nu gör `apply_transpose` antagandet att domän-typen är samma som range-typen. Det behöver vi på något sätt bryta. Ett alternativ är låta en LazyTensor ha `T_domain` och `T_range` istället för bara `T`. Känns dock lite grötigt. Ett annat alternativ skulle vara någon typ av trait för transpose? Den skulle kunna innehålla typen som transponatet agerar på? Vet inte om det fungerar dock.
325 273
326 Skulle kunna ha en funktion `range_type(::LazyTensor, ::Type{domain_type})` 274 Skulle kunna ha en funktion `range_type(::LazyTensor, ::Type{domain_type})`
327 275
328 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. 276 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.
329 277
330 ### Ratade alternativ:
331
332
333 #### 2.AbstractArray{T,2+1} where T (NOPE!)
334 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.
335
336 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.
337
338 Syntax:
339 ```
340 gf = eval_on_grid(g,f)
341 gf[:,2,3] # Hela vektorn för en gridpunkt
342 gf[2,2,3] # Andra komponenten av vektor fältet i en punkt.
343 gf[2,:,:] #
344 ```
345
346 ### Evaluering av funktioner på nät
347 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?
348
349 ```
350 f(x,y) = [x^2, y^2]
351 f(x̄) = [x̄[1]^2, x̄[2]^2]
352 ```
353
354 Påverkas detta av hur vi förväntar oss kunna skapa lata gridfunktioner?
355
356 ### Komponenter som gridfunktioner 278 ### Komponenter som gridfunktioner
357 En viktig operation för vektor fält är att kunna få ut komponenter som grid-funktioner. Detta behöver antagligen kunna ske lazy. 279 En viktig operation för vektorfält är att kunna få ut komponenter som grid-funktioner. Detta behöver antagligen kunna ske lazy.
358 Det finns ett par olika lösningar: 280 Det finns ett par olika lösningar:
281 * Använda map eller en lazy map (se diskussion om eval_on)
359 * Implementera en egen typ av view som tar hand om detta. Eller Accessors.jl? 282 * Implementera en egen typ av view som tar hand om detta. Eller Accessors.jl?
360 * Använda en LazyTensor 283 * Använda en LazyTensor
361 * Någon typ av lazy-broadcast 284 * Någon typ av lazy-broadcast
362 * En lazy array som applicerar en funktion för varje element. 285 * En lazy array som applicerar en funktion för varje element.
363 286
364 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å 287
365 288 ### Prestanda-aspekter
366 Syntax: 289 [Vidar, Discord, 2023-03-03]
367 ``` 290 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
368 gf = eval(...) 291 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]]].
369 component(gf,2) # Andra komponenten av en vektor 292
370 component(gf,2,3) # (2,3) elementet av en matris 293 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.
371 component(gf,:,2) # Andra kolumnen av en matris 294
372 @ourview gf[:,:][2] 295 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.
373 ``` 296
374 297 [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.
375 ## Grids embedded in higher dimensions 298
376 299
377 For grids generated by asking for boundary grids for a regular grid, it would 300 [Vidare tankar]
378 make sense if these grids knew they were embedded in a higher dimension. They 301 * 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.
379 would return coordinates in the full room. This would make sense when 302 * 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".
380 drawing points for example, or when evaluating functions on the boundary. 303 * 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.
381 304 * Om vi behöver special-indexering kommer till exempel LazyTensorApplication att behöva implementera det.
382 Implementation of this is an issue that requires some thought. Adding an extra 305 * 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?
383 "Embedded" type for each grid would make it easy to understand each type but 306
384 contribute to "type bloat". On the other hand adapting existing types to
385 handle embeddedness would complicate the now very simple grid types. Are there
386 other ways of doing the implentation?
387 307
388 ## Performance measuring 308 ## Performance measuring
389 We should be measuring performance early. How does our effective cpu and memory bandwidth utilization compare to peak performance? 309 We should be measuring performance early. How does our effective cpu and memory bandwidth utilization compare to peak performance?
390 310
391 We should make these test simple to run for any solver. 311 We should make these test simple to run for any solver.
403 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. 323 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.
404 324
405 325
406 ## Name of the `VolumeOperator` type for constant stencils 326 ## Name of the `VolumeOperator` type for constant stencils
407 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` 327 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`
328
329
330 ## Implementation of LazyOuterProduct
331 Could the implementation of LazyOuterProduct be simplified by making it a
332 struct containing two or more LazyTensors? (using split_tuple in a similar way
333 as TensorGrid)