def __init__(self, pdf_field, stencil, prev_timestep=Timestep.BOTH, streaming_pattern='pull', index_dtype=np.int64, offsets_dtype=np.int64): if prev_timestep == Timestep.BOTH and is_inplace(streaming_pattern): raise ValueError('Cannot create index arrays for both kinds of timesteps for inplace streaming pattern ' + streaming_pattern) prev_accessor = get_accessor(streaming_pattern, prev_timestep) next_accessor = get_accessor(streaming_pattern, prev_timestep.next()) outward_accesses = prev_accessor.write(pdf_field, stencil) inward_accesses = next_accessor.read(pdf_field, stencil) self._accesses = {'out': outward_accesses, 'in': inward_accesses} self._pdf_field = pdf_field self._stencil = stencil self._dim = stencil.D self._q = stencil.Q self._coordinate_names = ['x', 'y', 'z'][:self._dim] self._index_dtype = create_type(index_dtype) self._offsets_dtype = create_type(offsets_dtype) self._required_index_arrays = set() self._required_offset_arrays = set() self._trivial_index_translations, self._trivial_offset_translations = self._collect_trivial_translations()
def pdf_initialization_assignments(lb_method, density, velocity, pdfs, streaming_pattern='pull', previous_timestep=Timestep.BOTH, set_pre_collision_pdfs=False): """Assignments to initialize the pdf field with equilibrium""" if isinstance(pdfs, Field): accessor = get_accessor(streaming_pattern, previous_timestep) if set_pre_collision_pdfs: field_accesses = accessor.read(pdfs, lb_method.stencil) else: field_accesses = accessor.write(pdfs, lb_method.stencil) elif streaming_pattern == 'pull' and not set_pre_collision_pdfs: field_accesses = pdfs else: raise ValueError( "Invalid value of pdfs: A PDF field reference is required to derive " + f"initialization assignments for streaming pattern {streaming_pattern}." ) cqc = lb_method.conserved_quantity_computation inp_eqs = cqc.equilibrium_input_equations_from_init_values( density, velocity, force_substitution=False) setter_eqs = lb_method.get_equilibrium( conserved_quantity_equations=inp_eqs) setter_eqs = setter_eqs.new_with_substitutions({ sym: field_accesses[i] for i, sym in enumerate(lb_method.post_collision_pdf_symbols) }) return setter_eqs
def macroscopic_values_getter(lb_method, density, velocity, pdfs, streaming_pattern='pull', previous_timestep=Timestep.BOTH, use_pre_collision_pdfs=False): if isinstance(pdfs, Field): accessor = get_accessor(streaming_pattern, previous_timestep) if use_pre_collision_pdfs: field_accesses = accessor.read(pdfs, lb_method.stencil) else: field_accesses = accessor.write(pdfs, lb_method.stencil) elif streaming_pattern == 'pull' and not use_pre_collision_pdfs: field_accesses = pdfs else: raise ValueError( "Invalid value of pdfs: A PDF field reference is required to derive " + f"getter assignments for streaming pattern {streaming_pattern}.") cqc = lb_method.conserved_quantity_computation assert not (velocity is None and density is None) output_spec = {} if velocity is not None: output_spec['velocity'] = velocity if density is not None: output_spec['density'] = density return cqc.output_equations_from_pdfs(field_accesses, output_spec)
def test_stream_only_kernel(streaming_pattern): domain_size = (4, 4) stencil = LBStencil(Stencil.D2Q9) dh = ps.create_data_handling(domain_size, default_target=Target.CPU) pdfs = dh.add_array('pdfs', values_per_cell=len(stencil)) pdfs_tmp = dh.add_array_like('pdfs_tmp', 'pdfs') for t in get_timesteps(streaming_pattern): accessor = get_accessor(streaming_pattern, t) src = pdfs dst = pdfs if is_inplace(streaming_pattern) else pdfs_tmp dh.fill(src.name, 0.0) dh.fill(dst.name, 0.0) stream_kernel = create_stream_only_kernel(stencil, src, dst, accessor=accessor) stream_func = create_kernel(stream_kernel).compile() # Check functionality acc_in = AccessPdfValues(stencil, streaming_dir='in', accessor=accessor) for i in range(len(stencil)): acc_in.write_pdf(dh.cpu_arrays[src.name], (1,1), i, i) dh.run_kernel(stream_func) acc_out = AccessPdfValues(stencil, streaming_dir='out', accessor=accessor) for i in range(len(stencil)): assert acc_out.read_pdf(dh.cpu_arrays[dst.name], (1,1), i) == i
def create_lb_update_rule(collision_rule=None, lbm_config=None, lbm_optimisation=None, config=None, optimization=None, **kwargs): """Creates an update rule (list of Assignments) for a LB method that describe a full sweep""" lbm_config, lbm_optimisation, config = update_with_default_parameters(kwargs, optimization, lbm_config, lbm_optimisation, config) if lbm_config.collision_rule is not None: collision_rule = lbm_config.collision_rule if collision_rule is None: collision_rule = create_lb_collision_rule(lbm_config.lb_method, lbm_config=lbm_config, lbm_optimisation=lbm_optimisation, config=config) lb_method = collision_rule.method field_data_type = config.data_type q = collision_rule.method.stencil.Q if lbm_optimisation.symbolic_field is not None: src_field = lbm_optimisation.symbolic_field elif lbm_optimisation.field_size: field_size = tuple([s + 2 for s in lbm_optimisation.field_size] + [q]) src_field = Field.create_fixed_size(lbm_config.field_name, field_size, index_dimensions=1, layout=lbm_optimisation.field_layout, dtype=field_data_type) else: src_field = Field.create_generic(lbm_config.field_name, spatial_dimensions=collision_rule.method.dim, index_shape=(q,), layout=lbm_optimisation.field_layout, dtype=field_data_type) if lbm_optimisation.symbolic_temporary_field is not None: dst_field = lbm_optimisation.symbolic_temporary_field else: dst_field = src_field.new_field_with_different_name(lbm_config.temporary_field_name) kernel_type = lbm_config.kernel_type if kernel_type == 'stream_pull_only': return create_stream_pull_with_output_kernel(lb_method, src_field, dst_field, lbm_config.output) else: if kernel_type == 'default_stream_collide': if lbm_config.streaming_pattern == 'pull' and any(lbm_optimisation.builtin_periodicity): accessor = PeriodicTwoFieldsAccessor(lbm_optimisation.builtin_periodicity, ghost_layers=1) else: accessor = get_accessor(lbm_config.streaming_pattern, lbm_config.timestep) elif kernel_type == 'collide_only': accessor = CollideOnlyInplaceAccessor elif isinstance(kernel_type, PdfFieldAccessor): accessor = kernel_type else: raise ValueError("Invalid value of parameter 'kernel_type'", lbm_config.kernel_type) return create_lbm_kernel(collision_rule, src_field, dst_field, accessor)
def update_rule_with_push_boundaries(collision_rule, field, boundary_spec, streaming_pattern='pull', timestep=Timestep.BOTH): method = collision_rule.method accessor = get_accessor(streaming_pattern, timestep) loads = [ Assignment(a, b) for a, b in zip(method.pre_collision_pdf_symbols, accessor.read(field, method.stencil)) ] stores = [ Assignment(a, b) for a, b in zip(accessor.write(field, method.stencil), method.post_collision_pdf_symbols) ] result = collision_rule.copy() result.subexpressions = loads + result.subexpressions result.main_assignments += stores for direction, boundary in boundary_spec.items(): cond = boundary_conditional(boundary, direction, streaming_pattern, timestep, method, field) result.main_assignments.append(cond) if 'split_groups' in result.simplification_hints: substitutions = { b: a for a, b in zip(accessor.write(field, method.stencil), method.post_collision_pdf_symbols) } new_split_groups = [] for split_group in result.simplification_hints['split_groups']: new_split_groups.append( [fast_subs(e, substitutions) for e in split_group]) result.simplification_hints['split_groups'] = new_split_groups return result
def compile_macroscopic_values_getter(lb_method, output_quantities, pdf_arr=None, ghost_layers=1, iteration_slice=None, field_layout='numpy', target=Target.CPU, streaming_pattern='pull', previous_timestep=Timestep.BOTH): """ Create kernel to compute macroscopic value(s) from a pdf field (e.g. density or velocity) Args: lb_method: instance of :class:`lbmpy.methods.AbstractLbMethod` output_quantities: sequence of quantities to compute e.g. ['density', 'velocity'] pdf_arr: optional numpy array for pdf field - used to get optimal loop structure for kernel ghost_layers: a sequence of pairs for each coordinate with lower and upper nr of ghost layers that should be excluded from the iteration. If None, the number of ghost layers is determined automatically and assumed to be equal for all dimensions. iteration_slice: if not None, iteration is done only over this slice of the field field_layout: layout for output field, also used for pdf field if pdf_arr is not given target: `Target.CPU` or `Target.GPU` previous_step_accessor: The accessor used by the streaming pattern of the previous timestep Returns: a function to compute macroscopic values: - pdf_array - keyword arguments from name of conserved quantity (as in output_quantities) to numpy field """ if not (isinstance(output_quantities, list) or isinstance(output_quantities, tuple)): output_quantities = [output_quantities] cqc = lb_method.conserved_quantity_computation unknown_quantities = [ oq for oq in output_quantities if oq not in cqc.conserved_quantities ] if unknown_quantities: raise ValueError( "No such conserved quantity: %s, conserved quantities are %s" % (str(unknown_quantities), str(cqc.conserved_quantities.keys()))) if pdf_arr is None: pdf_field = Field.create_generic('pdfs', lb_method.dim, index_dimensions=1, layout=field_layout) else: pdf_field = Field.create_from_numpy_array('pdfs', pdf_arr, index_dimensions=1) output_mapping = {} for output_quantity in output_quantities: number_of_elements = cqc.conserved_quantities[output_quantity] assert number_of_elements >= 1 ind_dims = 0 if number_of_elements <= 1 else 1 if pdf_arr is None: output_field = Field.create_generic(output_quantity, lb_method.dim, layout=field_layout, index_dimensions=ind_dims) else: output_field_shape = pdf_arr.shape[:-1] if ind_dims > 0: output_field_shape += (number_of_elements, ) field_layout = get_layout_of_array(pdf_arr) else: field_layout = get_layout_of_array( pdf_arr, index_dimension_ids=[len(pdf_field.shape) - 1]) output_field = Field.create_fixed_size(output_quantity, output_field_shape, ind_dims, pdf_arr.dtype, field_layout) output_mapping[output_quantity] = [ output_field(i) for i in range(number_of_elements) ] if len(output_mapping[output_quantity]) == 1: output_mapping[output_quantity] = output_mapping[output_quantity][ 0] stencil = lb_method.stencil previous_step_accessor = get_accessor(streaming_pattern, previous_timestep) pdf_symbols = previous_step_accessor.write(pdf_field, stencil) eqs = cqc.output_equations_from_pdfs(pdf_symbols, output_mapping).all_assignments if target == Target.CPU: import pystencils.cpu as cpu kernel = cpu.make_python_function( cpu.create_kernel(eqs, ghost_layers=ghost_layers, iteration_slice=iteration_slice)) elif target == Target.GPU: import pystencils.gpucuda as gpu kernel = gpu.make_python_function( gpu.create_cuda_kernel(eqs, ghost_layers=ghost_layers, iteration_slice=iteration_slice)) else: raise ValueError( "Unknown target '%s'. Possible targets are `Target.CPU` and `Target.GPU`" % (target, )) def getter(pdfs, **kwargs): if pdf_arr is not None: assert pdfs.shape == pdf_arr.shape and pdfs.strides == pdf_arr.strides, \ "Pdf array not matching blueprint which was used to compile" + str(pdfs.shape) + str(pdf_arr.shape) if not set(output_quantities).issubset(kwargs.keys()): raise ValueError( "You have to specify the output field for each of the following quantities: %s" % (str(output_quantities), )) kernel(pdfs=pdfs, **kwargs) return getter
def compile_macroscopic_values_setter(lb_method, quantities_to_set, pdf_arr=None, ghost_layers=1, iteration_slice=None, field_layout='numpy', target=Target.CPU, streaming_pattern='pull', previous_timestep=Timestep.BOTH): """ Creates a function that sets a pdf field to specified macroscopic quantities The returned function can be called with the pdf field to set as single argument Args: lb_method: instance of :class:`lbmpy.methods.AbstractLbMethod` quantities_to_set: map from conserved quantity name to fixed value or numpy array pdf_arr: optional numpy array for pdf field - used to get optimal loop structure for kernel ghost_layers: a sequence of pairs for each coordinate with lower and upper nr of ghost layers that should be excluded from the iteration. If None, the number of ghost layers is determined automatically and assumed to be equal for all dimensions. iteration_slice: if not None, iteration is done only over this slice of the field field_layout: layout of the pdf field if pdf_arr was not given target: `Target.CPU` or `Target.GPU` previous_step_accessor: The accessor used by the streaming pattern of the previous timestep Returns: function taking pdf array as single argument and which sets the field to the given values """ if pdf_arr is not None: pdf_field = Field.create_from_numpy_array('pdfs', pdf_arr, index_dimensions=1) else: pdf_field = Field.create_generic('pdfs', lb_method.dim, index_dimensions=1, layout=field_layout) fixed_kernel_parameters = {} cqc = lb_method.conserved_quantity_computation value_map = {} at_least_one_field_input = False for quantity_name, value in quantities_to_set.items(): if hasattr(value, 'shape'): fixed_kernel_parameters[quantity_name] = value at_least_one_field_input = True num_components = cqc.conserved_quantities[quantity_name] field = Field.create_from_numpy_array( quantity_name, value, index_dimensions=0 if num_components <= 1 else 1) if num_components == 1: value = field(0) else: value = [field(i) for i in range(num_components)] value_map[quantity_name] = value cq_equations = cqc.equilibrium_input_equations_from_init_values( **value_map, force_substitution=False) eq = lb_method.get_equilibrium(conserved_quantity_equations=cq_equations) if at_least_one_field_input: simplification = create_simplification_strategy(lb_method) eq = simplification(eq) else: eq = eq.new_without_subexpressions() previous_step_accessor = get_accessor(streaming_pattern, previous_timestep) write_accesses = previous_step_accessor.write(pdf_field, lb_method.stencil) substitutions = { sym: write_accesses[i] for i, sym in enumerate(lb_method.post_collision_pdf_symbols) } eq = eq.new_with_substitutions(substitutions).all_assignments if target == Target.CPU: import pystencils.cpu as cpu kernel = cpu.make_python_function(cpu.create_kernel(eq)) kernel = functools.partial(kernel, **fixed_kernel_parameters) elif target == Target.GPU: import pystencils.gpucuda as gpu kernel = gpu.make_python_function(gpu.create_cuda_kernel(eq)) kernel = functools.partial(kernel, **fixed_kernel_parameters) else: raise ValueError( "Unknown target '%s'. Possible targets are `Target.CPU` and `Target.GPU`" % (target, )) def setter(pdfs, **kwargs): if pdf_arr is not None: assert pdfs.shape == pdf_arr.shape and pdfs.strides == pdf_arr.strides, \ "Pdf array not matching blueprint which was used to compile" + str(pdfs.shape) + str(pdf_arr.shape) kernel(pdfs=pdfs, **kwargs) return setter