0:27

Let's remember again that streaming takes the population from the out coming state

of one cell to the incoming state on neighboring cells.

By doing so, it maps post-collision values, which are the f out or

red arrows, to pre-collision values which are the f in or green arrows.

Streaming also takes our system from a given time t,

to the next time t + delta t There's nothing more to be said here.

Let's go right away to a Python code which does this.

There is one technical problem by implementing the streaming step,

which is why I will start again with a for loop over both x and

y directions of space before going to the new pi array-based syntax.

This complication is that you have to handle separately populations which are on

our domain boundary, and which are streamed out of the domain.

You have to do something with them.

1:24

What he chose to do in this code, which we are developing today, is to take them,

stream them out of the domain and

re-inject them into the domain in the opposite direction.

In this way, we have periodic boundary conditions,

everything which leaves things to domain on the left, enters on the right.

If it leaves on the right, it enters on the left.

If it leaves on the top, it enters on the bottom, and vice versa.

1:49

We call this periodic boundary condition because the system behaves as if

it was periodically repeated an infinite amount of times

in the direction in which it is periodic.

If it is periodic in the x direction, it is as if you had an infinite amount of

system arranged, which are all the same, and which are aligned in the x direction.

2:10

In y direction, means the same in y direction.

So how do we handle this in the code?

We start with a for loop with both stays base directions, and

then for every direction, for area of one of the nine directions,

we calculate the coordinates of the neighboring cell.

2:29

If the coordinates of the neighboring cell, which is just the current

coordinates plus the Lattice velocity according to the given space direction.

If these coordinates gets out of the system, which means if its

x component is smaller than zero or bigger or equal to nx, or

if its y coordinate is smaller than zero or bigger or equal to ny,

then we have to put it, periodically, to the other end of the system.

If it gets out at the zero end, we put it back to the nx or ny end.

If it gets out of the nx or ny end, we reinject it at the zero end.

We explicitly implement periodicity in this way and

then streaming is nothing else than a copy of outgoing

populations to the incoming populations of the neighboring cells.

3:21

You can do the same thing with new python rabie syntax,

although it is a little bit more difficult to understand.

But easier to write and much faster to execute.

NumPy offers a function which is called roll which will shift a matrix

periodically in one of the space directions.

To do the streaming step, we will have to call the roll function twice,

once to shift it by the right amount of cells in x direction and

once to shift it by the right amount of cells in y direction.

The number of cells by which it is shifted is either 0, 1, or -1, and

it's given by the value of the Lattice velocity indicating space direction.

So the NumPy array-based code for

the streaming step is nothing else than a loop over nine space directions,

a double call to the roll function of the outgoing populations.

As a parameter, axis 0 for the x direction, axis 1 for

y direction, and the amount of shifts given by Lattice velocities.

And we copy the results back into the incoming populations to have them ready

for the next collision step.

4:31

Before we end this session, I would like to make a short comment about the fact,

about the question, if really we need to allocate so much memory.

We have allocated a huge matrix which is of size 9 times nx times ny.

Once for the incoming populations and one for the outgoing populations.

That's a lot.

Couldn't it be possible to just allocate one set of populations and

store both the incoming populations and

the outgoing populations into the same set of variables?

The answer is yes, this is possible.

It's not even difficult to do.

We will not do it today because it makes the code a little bit more tricky.

And I prefer to have a code which is easy to read than

really efficient in terms of memory management.

But I will anyway, if you are interested in this topic, show you how to proceed

if you want to store the particle populations into just one matrix.

5:27

What do you need to be aware of?

When you do the collision step, you take the incoming populations and

compute locally density, velocity.

You compute equilibrium, you do the collision, and

store the result in the outgoing populations.

In this case, because everything is local inside a single cell, there is no problem.

Once you have computed the collision, you can store the result, the outgoing

populations, and overwrite incoming populations, you don't need them anymore.

There's nothing to be thought or changed here, you just do it,

and store both incoming and outgoing populations, in the same matrix.

But during streaming there is a slight complication.

During streaming, you take populations and copy them to neighboring cells.

If you're working with just the one set of matrices,

then by streaming populations to neighboring cells,

you might overwrite a population which you still need and lose information.

However, if you think about this carefully you will see that this is

not a problem at all.

Let's look at the streaming step, and let's look at two populations,

one which from the left cell is propagated to the right cell and

one which from the right cell streams to the left cell.

6:50

The interesting property is that they occupy a position on the neighboring cell,

which just has been vacated because the corresponding population,

which is of opposite direction, also left this cell.

7:04

The trick, if you want to work with just one set of populations, is to not stream

particles from one side to the other one, but to exchange them, to do a swap.

If you take a population on one cell, you identify its neighboring cell.

Identify the opposite population on that neighboring cell and

then you just exchange the two values.

Instead of overwriting anything.

By doing so, it's easy to do a Lattice Boltzmann model with just one set

of populations into which you'll save both incoming and outgoing populations.

But as I said,

we will not explore this property today to keep the code as simple as possible.

7:45

Just for your information.

The same property also holds for diagonal directions.

I show you here what happens if you take the lower left cell and

take the f zero out of population which,

8:00

as a matter of fact, it is the F8, the opposite population,

which streams from the lower left cell to the upper right cell.

And in the upper right cell it is to add zero out population,

which streams on the lower left cell.

Again, you can do this streaming by just exchanging these two values and

avoid overwriting any population.