Exemplo n.º 1
0
def test_save_data():
    domain_shape = (2, 2)

    dh = create_data_handling(domain_size=domain_shape, default_ghost_layers=1, parallel=True)
    dh.add_array("src", values_per_cell=9)
    dh.fill("src", 1.0, ghost_layers=True)
    dh.add_array("dst", values_per_cell=9)
    dh.fill("dst", 1.0, ghost_layers=True)

    dh.save_all(str(INPUT_FOLDER) + '/datahandling_parallel_save_test')
Exemplo n.º 2
0
def test_load_data():
    domain_shape = (2, 2)

    dh = create_data_handling(domain_size=domain_shape, default_ghost_layers=1, parallel=True)
    dh.add_array("src", values_per_cell=9)
    dh.fill("src", 0.0, ghost_layers=True)
    dh.add_array("dst", values_per_cell=9)
    dh.fill("dst", 0.0, ghost_layers=True)

    dh.load_all(str(INPUT_FOLDER) + '/datahandling_parallel_load_test')
    assert np.all(dh.gather_array('src')) == 1
    assert np.all(dh.gather_array('src')) == 1
Exemplo n.º 3
0
def test_timeloop():
    dh = create_data_handling(domain_size=(2, 2), periodicity=True)

    pre = dh.add_array('pre_run_field', values_per_cell=1)
    dh.fill("pre_run_field", 0.0, ghost_layers=True)
    f = dh.add_array('field', values_per_cell=1)
    dh.fill("field", 0.0, ghost_layers=True)
    post = dh.add_array('post_run_field', values_per_cell=1)
    dh.fill("post_run_field", 0.0, ghost_layers=True)
    single_step = dh.add_array('single_step_field', values_per_cell=1)
    dh.fill("single_step_field", 0.0, ghost_layers=True)

    pre_assignments = Assignment(pre.center, pre.center + 1)
    pre_kernel = create_kernel(pre_assignments).compile()
    assignments = Assignment(f.center, f.center + 1)
    kernel = create_kernel(assignments).compile()
    post_assignments = Assignment(post.center, post.center + 1)
    post_kernel = create_kernel(post_assignments).compile()
    single_step_assignments = Assignment(single_step.center, single_step.center + 1)
    single_step_kernel = create_kernel(single_step_assignments).compile()

    fixed_steps = 2
    timeloop = TimeLoop(steps=fixed_steps)
    assert timeloop.fixed_steps == fixed_steps

    def pre_run():
        dh.run_kernel(pre_kernel)

    def post_run():
        dh.run_kernel(post_kernel)

    def single_step_run():
        dh.run_kernel(single_step_kernel)

    timeloop.add_pre_run_function(pre_run)
    timeloop.add_post_run_function(post_run)
    timeloop.add_single_step_function(single_step_run)
    timeloop.add_call(kernel, {'field': dh.cpu_arrays["field"]})

    # the timeloop is initialised with 2 steps. This means a single time step consists of two steps.
    # Therefore, we have 2 main iterations and one single step iteration in this configuration
    timeloop.run(time_steps=5)
    assert np.all(dh.cpu_arrays["pre_run_field"] == 1.0)
    assert np.all(dh.cpu_arrays["field"] == 2.0)
    assert np.all(dh.cpu_arrays["single_step_field"] == 1.0)
    assert np.all(dh.cpu_arrays["post_run_field"] == 1.0)

    seconds = 2
    start = time.perf_counter()
    timeloop.run_time_span(seconds=seconds)
    end = time.perf_counter()

    np.testing.assert_almost_equal(seconds, end - start, decimal=2)
Exemplo n.º 4
0
def test_extrapolation_outflow_initialization_by_copy():
    stencil = LBStencil(Stencil.D2Q9)
    domain_size = (1, 5)

    streaming_pattern = 'esotwist'
    zeroth_timestep = 'even'

    pdf_acc = AccessPdfValues(stencil,
                              streaming_pattern=streaming_pattern,
                              timestep=zeroth_timestep,
                              streaming_dir='out')

    dh = create_data_handling(domain_size, default_target=Target.CPU)
    lb_method = create_lb_method(stencil=stencil)
    pdf_field = dh.add_array('f', values_per_cell=stencil.Q)
    dh.fill(pdf_field.name, np.nan, ghost_layers=True)
    pdf_arr = dh.cpu_arrays[pdf_field.name]
    bh = LatticeBoltzmannBoundaryHandling(lb_method,
                                          dh,
                                          pdf_field.name,
                                          streaming_pattern=streaming_pattern,
                                          target=Target.CPU)

    for y in range(1, 6):
        for j in range(stencil.Q):
            pdf_acc.write_pdf(pdf_arr, (1, y), j, j)

    normal_dir = (1, 0)
    outflow = ExtrapolationOutflow(normal_dir,
                                   lb_method,
                                   streaming_pattern=streaming_pattern,
                                   zeroth_timestep=zeroth_timestep)
    boundary_slice = get_ghost_region_slice(normal_dir)
    bh.set_boundary(outflow, boundary_slice)
    bh.prepare()

    blocks = list(dh.iterate())
    index_list = blocks[0][
        bh._index_array_name].boundary_object_to_index_list[outflow]
    assert len(index_list) == 13
    for entry in index_list:
        direction = stencil[entry["dir"]]
        inv_dir = stencil.index(inverse_direction(direction))
        assert entry[f'pdf'] == inv_dir
        assert entry[f'pdf_nd'] == inv_dir
Exemplo n.º 5
0
def test_pdf_simple_extrapolation(stencil_enum, streaming_pattern):
    stencil = LBStencil(stencil_enum)

    #   Field contains exactly one fluid cell
    domain_size = (3, ) * stencil.D
    for timestep in get_timesteps(streaming_pattern):
        dh = create_data_handling(domain_size, default_target=Target.CPU)
        lb_method = create_lb_method(lbm_config=LBMConfig(stencil=stencil))
        pdf_field = dh.add_array('f', values_per_cell=stencil.Q)
        dh.fill(pdf_field.name, np.nan, ghost_layers=True)
        bh = LatticeBoltzmannBoundaryHandling(lb_method,
                                              dh,
                                              pdf_field.name,
                                              streaming_pattern,
                                              target=Target.CPU)

        #   Set up outflows in all directions
        for normal_dir in stencil[1:]:
            boundary_obj = SimpleExtrapolationOutflow(normal_dir, stencil)
            boundary_slice = get_ghost_region_slice(normal_dir)
            bh.set_boundary(boundary_obj, boundary_slice)

        pdf_arr = dh.cpu_arrays[pdf_field.name]

        #   Set up the domain with artificial PDF values
        # center = (1,) * dim
        out_access = AccessPdfValues(stencil, streaming_pattern, timestep,
                                     'out')
        for cell in product(*(range(1, 4) for _ in range(stencil.D))):
            for q in range(stencil.Q):
                out_access.write_pdf(pdf_arr, cell, q, q)

        #   Do boundary handling
        bh(prev_timestep=timestep)

        #   Check PDF values
        in_access = AccessPdfValues(stencil, streaming_pattern,
                                    timestep.next(), 'in')

        #   Inbound in center cell
        for cell in product(*(range(1, 4) for _ in range(stencil.D))):
            for q in range(stencil.Q):
                f = in_access.read_pdf(pdf_arr, cell, q)
                assert f == q
Exemplo n.º 6
0
def create_fully_periodic_flow(initial_velocity,
                               periodicity_in_kernel=False,
                               lbm_kernel=None,
                               data_handling=None,
                               parallel=False,
                               **kwargs):
    """Creates a fully periodic setup with prescribed velocity field.

    Args:
        initial_velocity: numpy array that defines an initial velocity for each cell. The shape of this
                         array determines the domain size.
        periodicity_in_kernel: don't use boundary handling for periodicity, but directly generate the kernel periodic
        lbm_kernel: a LBM function, which would otherwise automatically created
        data_handling: data handling instance that is used to create the necessary fields. If a data handling is
                       passed the periodicity and parallel arguments are ignored.
        parallel: True for distributed memory parallelization with walberla
        kwargs: other parameters are passed on to the method, see :mod:`lbmpy.creationfunctions`

    Returns:
        instance of :class:`Scenario`
    """
    if 'optimization' not in kwargs:
        kwargs['optimization'] = {}
    else:
        kwargs['optimization'] = kwargs['optimization'].copy()
    domain_size = initial_velocity.shape[:-1]
    if periodicity_in_kernel:
        kwargs['optimization']['builtin_periodicity'] = (True, True, True)

    if data_handling is None:
        data_handling = create_data_handling(
            domain_size,
            periodicity=not periodicity_in_kernel,
            default_ghost_layers=1,
            parallel=parallel)
    step = LatticeBoltzmannStep(data_handling=data_handling,
                                name="periodic_scenario",
                                lbm_kernel=lbm_kernel,
                                **kwargs)
    for b in step.data_handling.iterate(ghost_layers=False):
        np.copyto(b[step.velocity_data_name], initial_velocity[b.global_slice])
    step.set_pdf_fields_from_macroscopic_values()
    return step
Exemplo n.º 7
0
def test_parallel_datahandling_boundary_conditions():
    pytest.importorskip('waLBerla.cuda')

    dh = create_data_handling(domain_size=(7, 7), periodicity=True, parallel=True,
                              default_target=pystencils.Target.GPU)

    src = dh.add_array('src', values_per_cell=1)
    dh.fill(src.name, 0.0, ghost_layers=True)
    dh.fill(src.name, 1.0, ghost_layers=False)

    src2 = dh.add_array('src2', values_per_cell=1)

    src_cpu = dh.add_array('src_cpu', values_per_cell=1, gpu=False)
    dh.fill(src_cpu.name, 0.0, ghost_layers=True)
    dh.fill(src_cpu.name, 1.0, ghost_layers=False)

    boundary_stencil = [(1, 0), (-1, 0), (0, 1), (0, -1)]
    boundary_handling_cpu = BoundaryHandling(dh, src_cpu.name, boundary_stencil,
                                             name="boundary_handling_cpu", target=pystencils.Target.CPU)

    boundary_handling = BoundaryHandling(dh, src.name, boundary_stencil,
                                         name="boundary_handling_gpu", target=pystencils.Target.GPU)

    neumann = Neumann()
    for d in ('N', 'S', 'W', 'E'):
        boundary_handling.set_boundary(neumann, slice_from_direction(d, dim=2))
        boundary_handling_cpu.set_boundary(neumann, slice_from_direction(d, dim=2))

    boundary_handling.prepare()
    boundary_handling_cpu.prepare()

    boundary_handling_cpu()

    dh.all_to_gpu()
    boundary_handling()
    dh.all_to_cpu()
    for block in dh.iterate():
        np.testing.assert_almost_equal(block[src_cpu.name], block[src.name])

    assert dh.custom_data_names == ('boundary_handling_cpuIndexArrays', 'boundary_handling_gpuIndexArrays')
    dh.swap(src.name, src2.name, gpu=True)
Exemplo n.º 8
0
def create_lid_driven_cavity(domain_size=None,
                             lid_velocity=0.005,
                             lbm_kernel=None,
                             parallel=False,
                             data_handling=None,
                             **kwargs):
    """Creates a lid driven cavity scenario.

    Args:
        domain_size: tuple specifying the number of cells in each dimension
        lid_velocity: x velocity of lid in lattice coordinates.
        lbm_kernel: a LBM function, which would otherwise automatically created
        kwargs: other parameters are passed on to the method, see :mod:`lbmpy.creationfunctions`
        parallel: True for distributed memory parallelization with walberla
        data_handling: see documentation of :func:`create_fully_periodic_flow`
    Returns:
        instance of :class:`Scenario`
    """
    assert domain_size is not None or data_handling is not None
    if data_handling is None:
        optimization = kwargs.get('optimization', None)
        target = optimization.get('target', None) if optimization else None
        data_handling = create_data_handling(domain_size,
                                             periodicity=False,
                                             default_ghost_layers=1,
                                             parallel=parallel,
                                             default_target=target)
    step = LatticeBoltzmannStep(data_handling=data_handling,
                                lbm_kernel=lbm_kernel,
                                name="ldc",
                                **kwargs)

    my_ubb = UBB(velocity=[lid_velocity, 0, 0][:step.method.dim])
    step.boundary_handling.set_boundary(my_ubb,
                                        slice_from_direction('N', step.dim))
    for direction in ('W', 'E', 'S') if step.dim == 2 else ('W', 'E', 'S', 'T',
                                                            'B'):
        step.boundary_handling.set_boundary(
            NoSlip(), slice_from_direction(direction, step.dim))

    return step
Exemplo n.º 9
0
def test_extrapolation_outflow_initialization_by_callback():
    stencil = LBStencil(Stencil.D2Q9)
    domain_size = (1, 5)

    streaming_pattern = 'esotwist'
    zeroth_timestep = 'even'

    dh = create_data_handling(domain_size, default_target=Target.CPU)
    lb_method = create_lb_method(stencil=stencil)
    pdf_field = dh.add_array('f', values_per_cell=stencil.Q)
    dh.fill(pdf_field.name, np.nan, ghost_layers=True)
    bh = LatticeBoltzmannBoundaryHandling(lb_method,
                                          dh,
                                          pdf_field.name,
                                          streaming_pattern=streaming_pattern,
                                          target=Target.CPU)

    normal_dir = (1, 0)
    outflow = ExtrapolationOutflow(normal_direction=normal_dir,
                                   lb_method=lb_method,
                                   streaming_pattern=streaming_pattern,
                                   zeroth_timestep=zeroth_timestep,
                                   initial_density=lambda x, y: 1,
                                   initial_velocity=lambda x, y: (0, 0))
    boundary_slice = get_ghost_region_slice(normal_dir)
    bh.set_boundary(outflow, boundary_slice)
    bh.prepare()

    weights = [w.evalf() for w in lb_method.weights]

    blocks = list(dh.iterate())
    index_list = blocks[0][
        bh._index_array_name].boundary_object_to_index_list[outflow]
    assert len(index_list) == 13
    for entry in index_list:
        direction = stencil[entry["dir"]]
        inv_dir = stencil.index(inverse_direction(direction))
        assert entry[f'pdf_nd'] == weights[inv_dir]
Exemplo n.º 10
0
def create_channel(domain_size=None,
                   force=None,
                   pressure_difference=None,
                   u_max=None,
                   diameter_callback=None,
                   duct=False,
                   wall_boundary=NoSlip(),
                   parallel=False,
                   data_handling=None,
                   **kwargs):
    """Create a channel scenario (2D or 3D).

    The channel can be driven either by force, velocity inflow or pressure difference. Choose one and pass
    exactly one of the parameters 'force', 'pressure_difference' or 'u_max'.

    Args:
        domain_size: size of the simulation domain. First coordinate is the flow direction.
        force: Periodic channel, driven by a body force. Pass force in flow direction in lattice units here.
        pressure_difference: Inflow and outflow are fixed pressure conditions, with the given pressure difference.
        u_max: Parabolic velocity profile prescribed at inflow, pressure boundary =1.0 at outflow.
        diameter_callback: optional callback for channel with varying diameters. Only valid if duct=False.
                          The callback receives x coordinate array and domain_size and returns a
                          an array of diameters of the same shape
        duct: if true the channel has rectangular instead of circular cross section
        wall_boundary: instance of boundary class that should be set at the channel walls
        parallel: True for distributed memory parallelization with walberla
        data_handling: see documentation of :func:`create_fully_periodic_flow`
        kwargs: all other keyword parameters are passed directly to scenario class.
    """
    assert domain_size is not None or data_handling is not None

    if [bool(p) for p in (force, pressure_difference, u_max)].count(True) != 1:
        raise ValueError(
            "Please specify exactly one of the parameters 'force', 'pressure_difference' or 'u_max'"
        )

    periodicity = (True, False, False) if force else (False, False, False)
    if data_handling is None:
        dim = len(domain_size)
        assert dim in (2, 3)
        data_handling = create_data_handling(domain_size,
                                             periodicity=periodicity[:dim],
                                             default_ghost_layers=1,
                                             parallel=parallel)

    dim = data_handling.dim
    if force:
        kwargs['force'] = tuple([force, 0, 0][:dim])
        assert data_handling.periodicity[0]
        step = LatticeBoltzmannStep(data_handling=data_handling,
                                    name="force_driven_channel",
                                    **kwargs)
    elif pressure_difference:
        inflow = FixedDensity(1.0 + pressure_difference)
        outflow = FixedDensity(1.0)
        step = LatticeBoltzmannStep(data_handling=data_handling,
                                    name="pressure_driven_channel",
                                    **kwargs)
        step.boundary_handling.set_boundary(inflow,
                                            slice_from_direction('W', dim))
        step.boundary_handling.set_boundary(outflow,
                                            slice_from_direction('E', dim))
    elif u_max:
        if duct:
            raise NotImplementedError(
                "Velocity inflow for duct flows not yet implemented")
        step = LatticeBoltzmannStep(data_handling=data_handling,
                                    name="velocity_driven_channel",
                                    **kwargs)
        diameter = diameter_callback(np.array([
            0
        ]), domain_size)[0] if diameter_callback else min(domain_size[1:])
        add_pipe_inflow_boundary(step.boundary_handling,
                                 u_max,
                                 slice_from_direction('W', dim),
                                 flow_direction=0,
                                 diameter=diameter)
        outflow = FixedDensity(1.0)
        step.boundary_handling.set_boundary(outflow,
                                            slice_from_direction('E', dim))
    else:
        assert False

    directions = ('N', 'S', 'T', 'B') if dim == 3 else ('N', 'S')
    for direction in directions:
        step.boundary_handling.set_boundary(
            wall_boundary, slice_from_direction(direction, dim))

    if duct and diameter_callback is not None:
        raise ValueError(
            "For duct flows, passing a diameter callback does not make sense.")

    if not duct:
        diameter = min(step.boundary_handling.shape[1:])
        add_pipe_walls(step.boundary_handling,
                       diameter_callback if diameter_callback else diameter,
                       wall_boundary)

    return step
Exemplo n.º 11
0
def test_fully_periodic_flow(target, stencil, streaming_pattern):
    gpu = False
    if target == Target.GPU:
        gpu = True

    #   Stencil
    stencil = LBStencil(stencil)

    #   Streaming
    inplace = is_inplace(streaming_pattern)
    timesteps = get_timesteps(streaming_pattern)
    zeroth_timestep = timesteps[0]

    #   Data Handling and PDF fields
    domain_size = (30, ) * stencil.D
    periodicity = (True, ) * stencil.D

    dh = create_data_handling(domain_size=domain_size,
                              periodicity=periodicity,
                              default_target=target)

    pdfs = dh.add_array('pdfs', stencil.Q)
    if not inplace:
        pdfs_tmp = dh.add_array_like('pdfs_tmp', pdfs.name)

    #   LBM Streaming and Collision
    lbm_config = LBMConfig(stencil=stencil,
                           method=Method.SRT,
                           relaxation_rate=1.0,
                           streaming_pattern=streaming_pattern)

    lbm_opt = LBMOptimisation(symbolic_field=pdfs)
    config = CreateKernelConfig(target=target)

    if not inplace:
        lbm_opt = replace(lbm_opt, symbolic_temporary_field=pdfs_tmp)

    lb_collision = create_lb_collision_rule(lbm_config=lbm_config,
                                            lbm_optimisation=lbm_opt,
                                            config=config)
    lb_method = lb_collision.method

    lb_kernels = []
    for t in timesteps:
        lbm_config = replace(lbm_config, timestep=t)
        lb_kernels.append(
            create_lb_function(collision_rule=lb_collision,
                               lbm_config=lbm_config,
                               lbm_optimisation=lbm_opt))

    #   Macroscopic Values
    density = 1.0
    density_field = dh.add_array('rho', 1)
    u_x = 0.01
    velocity = (u_x, ) * stencil.D
    velocity_field = dh.add_array('u', stencil.D)

    u_ref = np.full(domain_size + (stencil.D, ), u_x)

    setter = macroscopic_values_setter(lb_method,
                                       density,
                                       velocity,
                                       pdfs,
                                       streaming_pattern=streaming_pattern,
                                       previous_timestep=zeroth_timestep)
    setter_kernel = create_kernel(
        setter, config=CreateKernelConfig(target=target,
                                          ghost_layers=1)).compile()

    getter_kernels = []
    for t in timesteps:
        getter = macroscopic_values_getter(lb_method,
                                           density_field,
                                           velocity_field,
                                           pdfs,
                                           streaming_pattern=streaming_pattern,
                                           previous_timestep=t)
        getter_kernels.append(
            create_kernel(getter,
                          config=CreateKernelConfig(target=target,
                                                    ghost_layers=1)).compile())

    #   Periodicity
    periodicity_handler = LBMPeriodicityHandling(
        stencil, dh, pdfs.name, streaming_pattern=streaming_pattern)

    # Initialization and Timestep
    current_timestep = zeroth_timestep

    def init():
        global current_timestep
        current_timestep = zeroth_timestep
        dh.run_kernel(setter_kernel)

    def one_step():
        global current_timestep

        # Periodicty
        periodicity_handler(current_timestep)

        # Here, the next time step begins
        current_timestep = current_timestep.next()

        # LBM Step
        dh.run_kernel(lb_kernels[current_timestep.idx])

        # Field Swaps
        if not inplace:
            dh.swap(pdfs.name, pdfs_tmp.name)

        # Macroscopic Values
        dh.run_kernel(getter_kernels[current_timestep.idx])

    #   Run the simulation
    init()

    for _ in range(100):
        one_step()

    #   Evaluation
    if gpu:
        dh.to_cpu(velocity_field.name)
    u = dh.gather_array(velocity_field.name)

    #   Equal to the steady-state velocity field up to numerical errors
    assert_allclose(u, u_ref)

    #   Flow must be equal up to numerical error for all streaming patterns
    global all_results
    for key, prev_u in all_results.items():
        if key[0] == stencil:
            prev_pattern = key[1]
            assert_allclose(
                u,
                prev_u,
                err_msg=
                f'Velocity field for {streaming_pattern} differed from {prev_pattern}!'
            )
    all_results[(stencil, streaming_pattern)] = u
Exemplo n.º 12
0
    def __init__(self, stencil, streaming_pattern, wall_boundary=None, target=Target.CPU):

        if wall_boundary is None:
            wall_boundary = NoSlip()

        self.target = target
        self.gpu = target in [Target.GPU]

        #   Stencil
        self.stencil = stencil
        self.q = stencil.Q
        self.dim = stencil.D

        #   Streaming
        self.streaming_pattern = streaming_pattern
        self.inplace = is_inplace(self.streaming_pattern)
        self.timesteps = get_timesteps(streaming_pattern)
        self.zeroth_timestep = self.timesteps[0]

        #   Domain, Data Handling and PDF fields
        self.pipe_length = 60
        self.pipe_radius = 15
        self.domain_size = (self.pipe_length, ) + (2 * self.pipe_radius,) * (self.dim - 1)
        self.periodicity = (True, ) + (False, ) * (self.dim - 1)
        self.force = (0.0001, ) + (0.0,) * (self.dim - 1)

        self.dh = create_data_handling(domain_size=self.domain_size,
                                       periodicity=self.periodicity, default_target=self.target)

        self.pdfs = self.dh.add_array('pdfs', self.q)
        if not self.inplace:
            self.pdfs_tmp = self.dh.add_array_like('pdfs_tmp', self.pdfs.name)

        #   LBM Streaming and Collision
        lbm_config = LBMConfig(stencil=stencil, method=Method.SRT, relaxation_rate=1.0,
                               force_model=ForceModel.GUO, force=self.force, streaming_pattern=streaming_pattern)

        lbm_opt = LBMOptimisation(symbolic_field=self.pdfs)
        config = CreateKernelConfig(target=self.target)

        if not self.inplace:
            lbm_opt = replace(lbm_opt, symbolic_temporary_field=self.pdfs_tmp)

        self.lb_collision = create_lb_collision_rule(lbm_config=lbm_config, lbm_optimisation=lbm_opt)
        self.lb_method = self.lb_collision.method

        self.lb_kernels = []
        for t in self.timesteps:
            lbm_config = replace(lbm_config, timestep=t)
            self.lb_kernels.append(create_lb_function(collision_rule=self.lb_collision,
                                                      lbm_config=lbm_config,
                                                      lbm_optimisation=lbm_opt,
                                                      config=config))

        #   Macroscopic Values
        self.density = 1.0
        self.density_field = self.dh.add_array('rho', 1)
        u_x = 0.0
        self.velocity = (u_x,) * self.dim
        self.velocity_field = self.dh.add_array('u', self.dim)

        setter = macroscopic_values_setter(
            self.lb_method, self.density, self.velocity, self.pdfs,
            streaming_pattern=self.streaming_pattern, previous_timestep=self.zeroth_timestep)
        self.init_kernel = create_kernel(setter,
                                         config=CreateKernelConfig(target=target, ghost_layers=1)).compile()

        self.getter_kernels = []
        for t in self.timesteps:
            getter = macroscopic_values_getter(
                self.lb_method, self.density_field, self.velocity_field, self.pdfs,
                streaming_pattern=self.streaming_pattern, previous_timestep=t)
            self.getter_kernels.append(create_kernel(getter,
                                                     config=CreateKernelConfig(target=target, ghost_layers=1)).compile())

        #   Periodicity
        self.periodicity_handler = LBMPeriodicityHandling(
            self.stencil, self.dh, self.pdfs.name, streaming_pattern=self.streaming_pattern)

        #   Boundary Handling
        self.wall = wall_boundary
        self.bh = LatticeBoltzmannBoundaryHandling(
            self.lb_method, self.dh, self.pdfs.name,
            streaming_pattern=self.streaming_pattern, target=self.target)

        self.bh.set_boundary(boundary_obj=self.wall, mask_callback=self.mask_callback)

        self.current_timestep = self.zeroth_timestep