jipolanco / PencilFFTs.jl

Fast Fourier transforms of MPI-distributed Julia arrays
https://jipolanco.github.io/PencilFFTs.jl/dev/
MIT License
77 stars 7 forks source link

Illustrate broadcasting over wavenumber grids (or wavenumber PencilArray) in `gradient.jl` ? #42

Closed glwagner closed 2 years ago

glwagner commented 2 years ago

In the gradient.jl example, a gradient is computed via FFTs by multiplying a transformed field by a wavenumber vector and then transforming back to physical space.

The multiplication between the wavenumbers and the transformed field is by looping over indices

https://github.com/jipolanco/PencilFFTs.jl/blob/4e0cdfe19f73473d48a9bd2132217d15e468b90c/docs/examples/gradient.jl#L140-L154

I'm wondering if it might make sense to illustrate how this computation might be done with broadcasting syntax? Broadcasting might preferred in some applications: it's more compact, and generalizes (in principle...) to GPU.

Partly I'm motivated to propose this modification because I'd like to use this syntax but don't understand quite how to achieve it! I see that

https://github.com/jipolanco/PencilFFTs.jl/blob/4e0cdfe19f73473d48a9bd2132217d15e468b90c/examples/gradient.jl#L258

is used in

https://github.com/jipolanco/PencilFFTs.jl/blob/4e0cdfe19f73473d48a9bd2132217d15e468b90c/examples/gradient.jl#L133-L138

and might illustrate how localgrid abstracts the notion of three "coordinates" (basically, three arrays with size (Nx, 1, 1), (1, Ny, 1), and (1, 1, Nz)... ?)

An alternative might use broadcasting with wavenumbers wrapped in a PencilArray. Not sure which is preferred (is localgrid just a wrapper for 3 "special" PencilArray with dimensions as above?)

Thanks for the super useful package btw!

glwagner commented 2 years ago

@johnryantaylor @simone-silvestri might be interested too

jipolanco commented 2 years ago

Right, so localgrid was introduced quite recently (https://github.com/jipolanco/PencilArrays.jl/pull/45) precisely to be able to broadcast PencilArrays with coordinates / wavenumbers in an efficient manner. So this is the recommended way of doing this kind of broadcasting.

I'm wondering if it might make sense to illustrate how this computation might be done with broadcasting syntax? Broadcasting might preferred in some applications: it's more compact, and generalizes (in principle...) to GPU.

Maybe I'm misunderstanding the question, but this is precisely what "Method 4" illustrates. Perhaps it should appear first, as it's by far the simplest implementation. It's just that this wasn't possible until quite recently...

This is also illustrated in the PencilArrays docs (but maybe this is not that easy to find?).

and might illustrate how localgrid abstracts the notion of three "coordinates" (basically, three arrays with size (Nx, 1, 1), (1, Ny, 1), and (1, 1, Nz)... ?)

Yeah, that's precisely the way one should think about localgrid.

An alternative might use broadcasting with wavenumbers wrapped in a PencilArray. Not sure which is preferred (is localgrid just a wrapper for 3 "special" PencilArray with dimensions as above?)

The problem is that one cannot construct a 1D PencilArray over a 3D geometry, because that would be incompatible with the domain partitioning scheme (how does one partition a 1D array?). Maybe there is a clever way to somehow allow this, but for now it's not possible. In any case, that's what localgrid is for.

Alternatively, one could create a full 3D array of wavenumbers (with e.g. u[i, j, k] = kx[i] * ky[j] * kz[k]) and broadcast with that, and that would be totally fine, except that it's a bit wasteful as it allocates a whole array (but, once again, that might be totally fine for some use cases).

glwagner commented 2 years ago

Maybe I'm misunderstanding the question, but this is precisely what "Method 4" illustrates.

Bah! I missed that!

The problem is that one cannot construct a 1D PencilArray over a 3D geometry, because that would be incompatible with the domain partitioning scheme (how does one partition a 1D array?).

Ah I see the issue. I think what I'm proposing is to partition three 3D PencilArray that have singleton dimensions, but maybe this doesn't make sense. (I guess this requires behavior where singleton dimensions are "extruded" to anticipate their use in broadcasting...)

This is also illustrated in the PencilArrays docs (but maybe this is not that easy to find?).

Ah, indeed! However, that example doesn't use "provided" coordinates but rather generates x, y, z under the hood, I suppose.

jipolanco commented 2 years ago

I think what I'm proposing is to partition three 3D PencilArray that have singleton dimensions, but maybe this doesn't make sense.

I see, that should be possible, and could be added if there's an interest (but I guess localgrid already solves part of the need for that).

Ah, indeed! However, that example doesn't use "provided" coordinates but rather generates x, y, z under the hood, I suppose.

The coordinates are provided further above :)

It's the same as in the gradient example really, only that here we're using the grid.x, grid.y, grid.z syntax (instead of grid[1], grid[2], grid[3]) to extract the coordinates. Note that the x, y and z names are hardcoded (this was inspired by a recent StaticArrays change). I guess all of this should be clarified in the docs...

glwagner commented 2 years ago

The coordinates are provided further above :)

Wow, you're right, I missed it again...

glwagner commented 2 years ago

I guess all of this should be clarified in the docs...

I think part of it is that the name "grid" carries some baggage / meaning; when I see that I immediately assume that we're talking about a physical grid (until I read further and realized it could be a wavenumber grid in Fourier space). But now I understand that it doesn't have to be a "coordinate" at all --- the restriction I guess is that the three component of the triplet have to vary only with i, j, k respectively, where i, j, k are the indices of the 3D array...

But I'm not sure this means that we need a different name... just musing...

glwagner commented 2 years ago

Anyways since broadcasting is actually in the gradient.jl example I'll close this.