def init_callback(self, boundary_data, **_): dim = boundary_data.dim coord_names = ['x', 'y', 'z'][:dim] pdf_acc = AccessPdfValues(self.stencil, streaming_pattern=self.streaming_pattern, timestep=self.zeroth_timestep, streaming_dir='out') def get_boundary_cell_pdfs(f_cell, b_cell, direction): if self.equilibrium_calculation is not None: density = self.initial_density( *b_cell) if callable(self.initial_density) else self.initial_density velocity = self.initial_velocity( *b_cell) if callable(self.initial_velocity) else self.initial_velocity return self.equilibrium_calculation(density, velocity, direction) else: return pdf_acc.read_pdf(boundary_data.pdf_array, f_cell, direction) for entry in boundary_data.index_array: center = tuple(entry[c] for c in coord_names) direction = self.stencil[entry["dir"]] inv_dir = self.stencil.index(inverse_direction(direction)) tangential_offset = tuple(offset - normal for offset, normal in zip(direction, self.normal_direction)) domain_cell = tuple(f + o for f, o in zip(center, tangential_offset)) outflow_cell = tuple(f + o for f, o in zip(center, direction)) # Initial fluid cell PDF values entry['pdf'] = pdf_acc.read_pdf(boundary_data.pdf_array, domain_cell, inv_dir) entry['pdf_nd'] = get_boundary_cell_pdfs(domain_cell, outflow_cell, inv_dir)
def read(field, stencil): res = [] for i, d in enumerate(stencil): inv_dir = inverse_direction(d) field_access = field[inv_dir](stencil.index(inv_dir)) res.append(field_access) return res
def write(field, stencil): result = [] for direction in stencil: inv_dir = inverse_direction(direction) spatial_offset = tuple(max(e, 0) for e in direction) result.append(field[spatial_offset](stencil.index(inv_dir))) return result
def read(self, field, stencil): result = [] for i, d in enumerate(stencil): pull_direction = inverse_direction(d) periodic_pull_direction = [] for coord_id, dir_element in enumerate(pull_direction): if not self._periodicity[coord_id]: periodic_pull_direction.append(dir_element) continue lower_limit = self._ghostLayers upper_limit = field.spatial_shape[ coord_id] - 1 - self._ghostLayers limit_diff = upper_limit - lower_limit loop_counter = LoopOverCoordinate.get_loop_counter_symbol( coord_id) if dir_element == 0: periodic_pull_direction.append(0) elif dir_element == 1: new_dir_element = sp.Piecewise( (dir_element, loop_counter < upper_limit), (-limit_diff, True)) periodic_pull_direction.append(new_dir_element) elif dir_element == -1: new_dir_element = sp.Piecewise( (dir_element, loop_counter > lower_limit), (limit_diff, True)) periodic_pull_direction.append(new_dir_element) else: raise NotImplementedError( "This accessor supports only nearest neighbor stencils" ) result.append(field[tuple(periodic_pull_direction)](i)) return result
def boundary_substitutions(lb_method): stencil = lb_method.stencil w = lb_method.weights replacements = {} for idx, offset in enumerate(stencil): symbolic_offset = BoundaryOffsetInfo.offset_from_dir(idx, dim=lb_method.dim) for sym, value in zip(symbolic_offset, offset): replacements[sym] = value replacements[BoundaryOffsetInfo.inv_dir(idx)] = stencil.index( inverse_direction(offset)) replacements[LbmWeightInfo.weight_of_direction(idx)] = w[idx] return replacements
def generate_pack_info_from_kernel(generation_context, class_name: str, assignments: Sequence[Assignment], kind='pull', **create_kernel_params): """Generates a waLBerla GPU PackInfo from a (pull) kernel. Args: generation_context: see documentation of `generate_sweep` class_name: name of the generated class assignments: list of assignments from the compute kernel - generates PackInfo for "pull" part only i.e. the kernel is expected to only write to the center kind: **create_kernel_params: remaining keyword arguments are passed to `pystencils.create_kernel` """ assert kind in ('push', 'pull') reads = set() writes = set() if isinstance(assignments, AssignmentCollection): assignments = assignments.all_assignments for a in assignments: if not isinstance(a, Assignment): continue reads.update(a.rhs.atoms(Field.Access)) writes.update(a.lhs.atoms(Field.Access)) spec = defaultdict(set) if kind == 'pull': for fa in reads: assert all(abs(e) <= 1 for e in fa.offsets) if all(offset == 0 for offset in fa.offsets): continue comm_direction = inverse_direction(fa.offsets) for comm_dir in comm_directions(comm_direction): spec[(comm_dir, )].add(fa.field.center(*fa.index)) elif kind == 'push': for fa in writes: assert all(abs(e) <= 1 for e in fa.offsets) if all(offset == 0 for offset in fa.offsets): continue for comm_dir in comm_directions(fa.offsets): spec[(comm_dir, )].add(fa) else: raise ValueError("Invalid 'kind' parameter") return generate_pack_info(generation_context, class_name, spec, **create_kernel_params)
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
def _force_on_boundary(self, boundary_obj, prev_timestep): dh = self._data_handling ff_ghost_layers = dh.ghost_layers_of_field( self.flag_interface.flag_field_name) method = self._lb_method stencil = np.array(method.stencil) inv_direction = np.array([ method.stencil.index(inverse_direction(d)) for d in method.stencil ]) result = np.zeros(self.dim) for b in dh.iterate(ghost_layers=ff_ghost_layers): obj_to_ind_list = b[ self._index_array_name].boundary_object_to_index_list pdf_array = b[self._field_name] if boundary_obj in obj_to_ind_list: ind_arr = obj_to_ind_list[boundary_obj] inverse_ind_arr = ind_arr.copy() inverse_ind_arr['dir'] = inv_direction[inverse_ind_arr['dir']] acc_out = AccessPdfValues( self._lb_method.stencil, streaming_pattern=self._streaming_pattern, timestep=prev_timestep, streaming_dir='out') acc_in = AccessPdfValues( self._lb_method.stencil, streaming_pattern=self._streaming_pattern, timestep=prev_timestep.next(), streaming_dir='in') acc_fluid = acc_out if boundary_obj.inner_or_boundary else acc_in acc_boundary = acc_in if boundary_obj.inner_or_boundary else acc_out fluid_values = acc_fluid.collect_from_index_list( pdf_array, ind_arr) boundary_values = acc_boundary.collect_from_index_list( pdf_array, inverse_ind_arr) values = fluid_values + boundary_values forces = stencil[ind_arr['dir']] * values[:, np.newaxis] result += forces.sum(axis=0) return dh.reduce_float_sequence(list(result), 'sum')
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]
def init_callback(self, boundary_data, **_): if len(boundary_data.index_array) > 1e6: warn(f"The calculation of the normal direction for each cell might take a long time, because " f"{len(boundary_data.index_array)} cells are marked as Free Slip boundary cells. Consider specifying " f" the normal direction beforehand, which is possible if it is equal for all cells (e.g. at a wall)") dim = boundary_data.dim coords = [coord for coord, _ in zip(['x', 'y', 'z'], range(dim))] boundary_cells = set() # get a set containing all boundary cells for cell in boundary_data.index_array: fluid_cell = tuple([cell[coord] for coord in coords]) direction = self.stencil[cell['dir']] boundary_cell = tuple([i + d for i, d in zip(fluid_cell, direction)]) boundary_cells.add(boundary_cell) for cell in boundary_data.index_array: fluid_cell = tuple([cell[coord] for coord in coords]) direction = self.stencil[cell['dir']] ref_direction = direction normal_direction = [0] * dim for i in range(dim): sub_direction = [0] * dim sub_direction[i] = direction[i] test_cell = tuple([x + y for x, y in zip(fluid_cell, sub_direction)]) if test_cell in boundary_cells: normal_direction[i] = direction[i] ref_direction = MirroredStencilDirections.mirror_stencil(ref_direction, i) ref_direction = inverse_direction(ref_direction) for i, cell_name in zip(range(dim), self.additional_data): cell[cell_name[0]] = -normal_direction[i] cell['ref_dir'] = self.stencil.index(ref_direction)
def test_free_slip_index_list(): stencil = LBStencil(Stencil.D2Q9) dh = create_data_handling(domain_size=(4, 4), periodicity=(False, False)) src = dh.add_array('src', values_per_cell=len(stencil), alignment=True) dh.fill('src', 0.0, ghost_layers=True) lbm_config = LBMConfig(stencil=stencil, method=Method.SRT, relaxation_rate=1.8) method = create_lb_method(lbm_config=lbm_config) bh = LatticeBoltzmannBoundaryHandling(method, dh, 'src', name="bh") free_slip = FreeSlip(stencil=stencil) add_box_boundary(bh, free_slip) bh.prepare() for b in dh.iterate(): for b_obj, idx_arr in b[ bh._index_array_name].boundary_object_to_index_list.items(): index_array = idx_arr # normal directions normal_west = (1, 0) normal_east = (-1, 0) normal_south = (0, 1) normal_north = (0, -1) normal_south_west = (1, 1) normal_north_west = (1, -1) normal_south_east = (-1, 1) normal_north_east = (-1, -1) for cell in index_array: direction = stencil[cell[2]] inv_dir = inverse_direction(direction) boundary_cell = (cell[0] + direction[0], cell[1] + direction[1]) normal = (cell[3], cell[4]) # the data is written on the inverse direction of the fluid cell near the boundary # the data is read from the mirrored direction of the inverse direction where the mirror axis is the normal assert cell[5] == stencil.index(mirror_stencil(list(inv_dir), normal)) if boundary_cell[0] == 0 and 0 < boundary_cell[1] < 5: assert normal == normal_west if boundary_cell[0] == 5 and 0 < boundary_cell[1] < 5: assert normal == normal_east if 0 < boundary_cell[0] < 5 and boundary_cell[1] == 0: assert normal == normal_south if 0 < boundary_cell[0] < 5 and boundary_cell[1] == 5: assert normal == normal_north if boundary_cell == (0, 0): assert cell[2] == cell[5] assert normal == normal_south_west if boundary_cell == (5, 0): assert cell[2] == cell[5] assert normal == normal_south_east if boundary_cell == (0, 5): assert cell[2] == cell[5] assert normal == normal_north_west if boundary_cell == (5, 5): assert cell[2] == cell[5] assert normal == normal_north_east
def read(field, stencil): return [field[inverse_direction(d)](i) for i, d in enumerate(stencil)]
def write(field, stencil): return [field(stencil.index(inverse_direction(d))) for d in stencil]
def generate_mpidtype_info_from_kernel( generation_context, class_name: str, assignments: Sequence[Assignment], kind='pull', namespace='pystencils', ): assert kind in ('push', 'pull') reads = set() writes = set() if isinstance(assignments, AssignmentCollection): assignments = assignments.all_assignments for a in assignments: if not isinstance(a, Assignment): continue reads.update(a.rhs.atoms(Field.Access)) writes.update(a.lhs.atoms(Field.Access)) spec = defaultdict(set) if kind == 'pull': read_fields = set(fa.field for fa in reads) assert len( read_fields ) == 1, "Only scenarios where one fields neighbors are accessed" field = read_fields.pop() for fa in reads: assert all(abs(e) <= 1 for e in fa.offsets) if all(offset == 0 for offset in fa.offsets): continue comm_direction = inverse_direction(fa.offsets) for comm_dir in comm_directions(comm_direction): assert len( fa.index ) == 1, "Supports only fields with a single index dimension" spec[(offset_to_direction_string(comm_dir), )].add(fa.index[0]) elif kind == 'push': written_fields = set(fa.field for fa in writes) assert len( written_fields ) == 1, "Only scenarios where one fields neighbors are accessed" field = written_fields.pop() for fa in writes: assert all(abs(e) <= 1 for e in fa.offsets) if all(offset == 0 for offset in fa.offsets): continue for comm_dir in comm_directions(fa.offsets): assert len( fa.index ) == 1, "Supports only fields with a single index dimension" spec[(offset_to_direction_string(comm_dir), )].add(fa.index[0]) else: raise ValueError("Invalid 'kind' parameter") jinja_context = { 'class_name': class_name, 'namespace': namespace, 'kind': kind, 'field_name': field.name, 'f_size': field.index_shape[0], 'spec': spec, } env = Environment(loader=PackageLoader('pystencils_walberla'), undefined=StrictUndefined) header = env.get_template("MpiDtypeInfo.tmpl.h").render(**jinja_context) generation_context.write_file("{}.h".format(class_name), header)