def test_relaxation_rate(): lbm_config = LBMConfig(stencil=LBStencil(Stencil.D3Q19), method=Method.MRT_RAW, relaxation_rates=[1 + i / 10 for i in range(19)]) method = create_lb_method(lbm_config=lbm_config) with pytest.raises(ValueError) as e: get_shear_relaxation_rate(method) assert 'Shear moments are relaxed with different relaxation' in str(e.value) omegas = sp.symbols("omega_:4") lbm_config = LBMConfig(stencil=LBStencil(Stencil.D3Q19), method=Method.MRT, relaxation_rates=omegas) method = create_lb_method(lbm_config=lbm_config) assert get_shear_relaxation_rate(method) == omegas[0]
def test_population_and_moment_space_equivalence(setup): stencil = LBStencil(setup[0]) method = setup[1] nested_moments = setup[2] fmodel = setup[3] force = sp.symbols(f'F_:{stencil.D}') conserved_moments = 1 + stencil.D rr = [ *[0] * conserved_moments, *sp.symbols(f'omega_:{stencil.Q - conserved_moments}') ] lbm_config = LBMConfig( stencil=stencil, method=method, relaxation_rates=rr, nested_moments=nested_moments, force_model=fmodel, force=force, weighted=True, compressible=True, moment_transform_class=PdfsToMomentsByChimeraTransform) lbm_opt = LBMOptimisation(cse_global=False, cse_pdfs=False, pre_simplification=True, simplification=False) lb_method_moment_space = create_lb_method(lbm_config=lbm_config) lbm_config = replace(lbm_config, moment_transform_class=None) lb_method_pdf_space = create_lb_method(lbm_config=lbm_config) rho = lb_method_moment_space.zeroth_order_equilibrium_moment_symbol u = lb_method_moment_space.first_order_equilibrium_moment_symbols keep = set((rho, ) + u) cr_moment_space = create_lb_collision_rule( lb_method=lb_method_moment_space, lbm_optimisation=lbm_opt) cr_moment_space = cr_moment_space.new_without_subexpressions( subexpressions_to_keep=keep) lbm_opt = replace(lbm_opt, simplification='auto') cr_pdf_space = create_lb_collision_rule(lb_method=lb_method_pdf_space, lbm_optimisation=lbm_opt) cr_pdf_space = cr_pdf_space.new_without_subexpressions( subexpressions_to_keep=keep) for a, b in zip(cr_moment_space.main_assignments, cr_pdf_space.main_assignments): diff = (a.rhs - b.rhs).expand() assert diff == 0, f"Mismatch between population- and moment-space equations in PDFs {a.lhs}, {b.lhs}"
def test_set_get_density_velocity_with_fields(stencil, force_model, compressible, streaming_pattern, prev_timestep): force = (0.1, 0.12, -0.17) stencil = LBStencil(stencil) lbm_config = LBMConfig(stencil=stencil, force_model=force_model, method=Method.TRT, compressible=compressible, force=force) method = create_lb_method(lbm_config=lbm_config) ghost_layers = 1 inner_size = (3, 7, 4)[:method.dim] total_size = tuple(s + 2 * ghost_layers for s in inner_size) pdf_arr = np.zeros(total_size + (len(method.stencil),)) inner_slice = (slice(ghost_layers, -ghost_layers, 1), ) * method.dim density_input_field = np.zeros(total_size) velocity_input_field = np.zeros(total_size + (method.dim, )) density_input_field[inner_slice] = 1 + 0.2 * (np.random.random_sample(inner_size) - 0.5) velocity_input_field[inner_slice] = 0.1 * \ (np.random.random_sample(inner_size + (method.dim, )) - 0.5) quantities_to_set = {'density': density_input_field, 'velocity': velocity_input_field} setter = compile_macroscopic_values_setter(method, pdf_arr=pdf_arr, quantities_to_set=quantities_to_set, ghost_layers=ghost_layers, streaming_pattern=streaming_pattern, previous_timestep=prev_timestep) setter(pdf_arr) getter = compile_macroscopic_values_getter(method, ['density', 'velocity'], pdf_arr=pdf_arr, ghost_layers=ghost_layers, streaming_pattern=streaming_pattern, previous_timestep=prev_timestep) density_output_field = np.zeros_like(density_input_field) velocity_output_field = np.zeros_like(velocity_input_field) getter(pdfs=pdf_arr, density=density_output_field, velocity=velocity_output_field) np.testing.assert_almost_equal(density_input_field, density_output_field) np.testing.assert_almost_equal(velocity_input_field, velocity_output_field)
def test_set_get_constant_velocity(stencil, force_model, compressible): ref_velocity = [0.01, -0.2, 0.1] force = (0.1, 0.12, -0.17) stencil = LBStencil(stencil) lbm_config = LBMConfig(stencil=stencil, force_model=force_model, method=Method.TRT, compressible=compressible, force=force) method = create_lb_method(lbm_config=lbm_config) size = (1, 1, 1)[:method.dim] pdf_arr = np.zeros(size + (len(method.stencil),)) setter = compile_macroscopic_values_setter(method, pdf_arr=pdf_arr, ghost_layers=0, quantities_to_set={'velocity': ref_velocity[:method.dim]}, ) setter(pdf_arr) getter = compile_macroscopic_values_getter(method, ['velocity'], pdf_arr=pdf_arr, ghost_layers=0) velocity_output_field = np.zeros(size + (method.dim, )) getter(pdfs=pdf_arr, velocity=velocity_output_field) if method.dim == 2: computed_velocity = velocity_output_field[0, 0, :] else: computed_velocity = velocity_output_field[0, 0, 0, :] np.testing.assert_almost_equal(np.array(ref_velocity[:method.dim]), computed_velocity)
def test_equilibrium_pdfs(stencil_name, cm_transform): stencil = LBStencil(stencil_name) lbm_config = LBMConfig(stencil=stencil, method=Method.CUMULANT, central_moment_transform_class=cm_transform) c_lb_method = create_lb_method(lbm_config=lbm_config) rho = c_lb_method.zeroth_order_equilibrium_moment_symbol u = c_lb_method.first_order_equilibrium_moment_symbols pdfs = c_lb_method.post_collision_pdf_symbols cqe_assignments = [Assignment(sym, sym) for sym in u] + [Assignment(rho, rho)] cqe = AssignmentCollection(main_assignments=cqe_assignments) method_equilibrium_eqs = c_lb_method.get_equilibrium( cqe, False, False).main_assignments_dict # Reference Equations ref_equilibrium = reference_equilibria.get(stencil_name, None) if ref_equilibrium is None: raw_moments = list( extract_monomials(c_lb_method.cumulants, dim=stencil.D)) ref_equilibrium = generate_equilibrium_by_matching_moments( stencil, tuple(raw_moments), rho=rho, u=u, c_s_sq=sp.Rational(1, 3), order=2 * stencil.D) reference_equilibria[stencil_name] = ref_equilibrium for i in range(stencil.Q): method_eq = method_equilibrium_eqs[pdfs[i]] ref_eq = ref_equilibrium[i] assert method_eq.expand() - ref_eq.expand() == sp.sympify(0), \ f"Equilibrium equation for pdf index {i} did not match."
def test_free_slip_equivalence(): # check if Free slip BC does the same if the normal direction is specified or not stencil = LBStencil(Stencil.D2Q9) dh = create_data_handling(domain_size=(4, 4), periodicity=(False, False)) src1 = dh.add_array('src1', values_per_cell=stencil.Q, alignment=True) src2 = dh.add_array('src2', values_per_cell=stencil.Q, alignment=True) dh.fill('src1', 0.0, ghost_layers=True) dh.fill('src2', 0.0, ghost_layers=True) shape = dh.gather_array('src1', ghost_layers=True).shape num = 0 for x in range(shape[0]): for y in range(shape[1]): for direction in range(shape[2]): dh.cpu_arrays['src1'][x, y, direction] = num dh.cpu_arrays['src2'][x, y, direction] = num num += 1 method = create_lb_method(lbm_config=LBMConfig( stencil=stencil, method=Method.SRT, relaxation_rate=1.8)) bh1 = LatticeBoltzmannBoundaryHandling(method, dh, 'src1', name="bh1") free_slip1 = FreeSlip(stencil=stencil) bh1.set_boundary(free_slip1, slice_from_direction('N', dh.dim)) bh2 = LatticeBoltzmannBoundaryHandling(method, dh, 'src2', name="bh2") free_slip2 = FreeSlip(stencil=stencil, normal_direction=(0, -1)) bh2.set_boundary(free_slip2, slice_from_direction('N', dh.dim)) bh1() bh2() assert np.array_equal(dh.cpu_arrays['src1'], dh.cpu_arrays['src2'])
def test_hydro_lb(): stencil_hydro = LBStencil(Stencil.D3Q27) density_liquid = 1.0 density_gas = 0.001 surface_tension = 0.0001 W = 5 # phase-field parameter drho3 = (density_liquid - density_gas) / 3 # coefficient related to surface tension beta = 12.0 * (surface_tension / W) # coefficient related to surface tension kappa = 1.5 * surface_tension * W u = fields("vel_field(" + str(stencil_hydro.D) + "): [" + str(stencil_hydro.D) + "D]", layout='fzyx') C = fields("phase_field: [" + str(stencil_hydro.D) + "D]", layout='fzyx') g = fields("lb_velocity_field(" + str(stencil_hydro.Q) + "): [" + str(stencil_hydro.D) + "D]", layout='fzyx') g_tmp = fields("lb_velocity_field_tmp(" + str(stencil_hydro.Q) + "): [" + str(stencil_hydro.D) + "D]", layout='fzyx') # calculate the relaxation rate for the hydro lb as well as the body force density = density_gas + C.center * (density_liquid - density_gas) # force acting on all phases of the model body_force = [0, 0, 0] relaxation_time = 0.03 + 0.5 relaxation_rate = 1.0 / relaxation_time lbm_config = LBMConfig(stencil=stencil_hydro, method=Method.MRT, weighted=True, relaxation_rates=[relaxation_rate, 1, 1, 1, 1, 1]) method_hydro = create_lb_method(lbm_config=lbm_config) # create the kernels for the initialization of the g and h field g_updates = initializer_kernel_hydro_lb(g, u, method_hydro) force_g = hydrodynamic_force(g, C, method_hydro, relaxation_time, density_liquid, density_gas, kappa, beta, body_force) force_model_g = MultiphaseForceModel(force=force_g, rho=density) hydro_lb_update_rule_normal = get_collision_assignments_hydro(lb_method=method_hydro, density=density, velocity_input=u, force_model=force_model_g, sub_iterations=2, symbolic_fields={"symbolic_field": g, "symbolic_temporary_field": g_tmp}, kernel_type='collide_only') hydro_lb_update_rule_push = get_collision_assignments_hydro(lb_method=method_hydro, density=density, velocity_input=u, force_model=force_model_g, sub_iterations=2, symbolic_fields={"symbolic_field": g, "symbolic_temporary_field": g_tmp}, kernel_type='collide_stream_push')
def test_boundary_2D(): with ManualCodeGenerationContext(openmp=True, double_accuracy=True) as ctx: lb_method = create_lb_method(stencil='D2Q9', method='srt') generate_boundary(ctx, 'Boundary', NoSlip(), lb_method, target='gpu')
def compare_weights(method, maxwellian_moments, stencil_name): stencil = LBStencil(stencil_name) hardcoded_weights = get_weights(stencil) method = create_lb_method(LBMConfig(stencil=stencil, method=method, maxwellian_moments=maxwellian_moments)) weights = method.weights for i in range(len(weights)): assert hardcoded_weights[i] == weights[i]
def test_boundary_utility_functions(): stencil = LBStencil(Stencil.D2Q9) method = create_lb_method(lbm_config=LBMConfig(stencil=stencil)) noslip = NoSlip("noslip") assert noslip == NoSlip("noslip") assert not noslip == NoSlip("test") assert not noslip == UBB((0, 0), name="ubb") assert noslip.name == "noslip" noslip.name = "test name setter" assert noslip.name == "test name setter" ubb = UBB((0, 0), name="ubb") assert ubb == UBB((0, 0), name="ubb") assert not noslip == UBB((0, 0), name="test") assert not ubb == NoSlip("noslip") simple_extrapolation = SimpleExtrapolationOutflow( normal_direction=stencil[4], stencil=stencil, name="simple") assert simple_extrapolation == SimpleExtrapolationOutflow( normal_direction=stencil[4], stencil=stencil, name="simple") assert not simple_extrapolation == SimpleExtrapolationOutflow( normal_direction=stencil[4], stencil=stencil, name="test") assert not simple_extrapolation == NoSlip("noslip") outflow = ExtrapolationOutflow(normal_direction=stencil[4], lb_method=method, name="outflow") assert outflow == ExtrapolationOutflow(normal_direction=stencil[4], lb_method=method, name="outflow") assert not outflow == ExtrapolationOutflow( normal_direction=stencil[4], lb_method=method, name="test") assert not outflow == simple_extrapolation density = FixedDensity(density=1.0, name="fixedDensity") assert density == FixedDensity(density=1.0, name="fixedDensity") assert not density == FixedDensity(density=1.0, name="test") assert not density == UBB((0, 0), name="ubb") diffusion = DiffusionDirichlet(concentration=1.0, name="diffusion") assert diffusion == DiffusionDirichlet(concentration=1.0, name="diffusion") assert not diffusion == DiffusionDirichlet(concentration=1.0, name="test") assert not diffusion == density neumann = NeumannByCopy(name="Neumann") assert neumann == NeumannByCopy(name="Neumann") assert not neumann == NeumannByCopy(name="test") assert not neumann == diffusion stream = StreamInConstant(constant=1.0, name="stream") assert stream == StreamInConstant(constant=1.0, name="stream") assert not stream == StreamInConstant(constant=1.0, name="test") assert not stream == noslip
def test_advanced_streaming_noslip_single_cell(stencil, streaming_pattern, prev_timestep): """ Advanced Streaming NoSlip Test """ stencil = LBStencil(stencil) pdf_field = ps.fields(f'pdfs({stencil.Q}): [{stencil.D}D]') prev_pdf_access = AccessPdfValues(stencil, streaming_pattern, prev_timestep, 'out') next_pdf_access = AccessPdfValues(stencil, streaming_pattern, prev_timestep.next(), 'in') pdfs = np.zeros((3, ) * stencil.D + (stencil.Q, )) pos = (1, ) * stencil.D for d in range(stencil.Q): prev_pdf_access.write_pdf(pdfs, pos, d, d) lbm_config = LBMConfig(stencil=stencil, method=Method.SRT) lb_method = create_lb_method(lbm_config=lbm_config) noslip = NoSlip() index_struct_dtype = numpy_data_type_for_boundary_object(noslip, stencil.D) index_field = Field('indexVector', FieldType.INDEXED, index_struct_dtype, layout=[0], shape=(TypedSymbol("indexVectorSize", create_type(np.int64)), 1), strides=(1, 1)) index_vector = np.array([pos + (d, ) for d in range(stencil.Q)], dtype=index_struct_dtype) ast = create_lattice_boltzmann_boundary_kernel( pdf_field, index_field, lb_method, noslip, prev_timestep=prev_timestep, streaming_pattern=streaming_pattern) flex_kernel = ast.compile() flex_kernel(pdfs=pdfs, indexVector=index_vector, indexVectorSize=len(index_vector)) reflected_pdfs = [ next_pdf_access.read_pdf(pdfs, pos, d) for d in range(stencil.Q) ] inverse_pdfs = [inverse_dir_index(stencil, d) for d in range(stencil.Q)] assert reflected_pdfs == inverse_pdfs
def test_moment_comparison_table(): pytest.importorskip('ipy_table') lbm_config_new = LBMConfig(stencil=LBStencil(Stencil.D3Q19), maxwellian_moments=True) lbm_config_old = LBMConfig(stencil=LBStencil(Stencil.D3Q19), maxwellian_moments=False) new = create_lb_method(lbm_config=lbm_config_new) old = create_lb_method(lbm_config=lbm_config_old) assert old.zeroth_order_equilibrium_moment_symbol == new.zeroth_order_equilibrium_moment_symbol assert '<td' in new._repr_html_() res_deviations_only = compare_moment_based_lb_methods(old, new, show_deviations_only=True) assert len(res_deviations_only.array) == 4 res_all = compare_moment_based_lb_methods(old, new, show_deviations_only=False) assert len(res_all.array) == 20 d3q27 = create_lb_method(LBMConfig(stencil=LBStencil(Stencil.D3Q27))) compare_moment_based_lb_methods(d3q27, new, show_deviations_only=False) compare_moment_based_lb_methods(new, d3q27, show_deviations_only=False)
def test_relaxation_rate_setter(): o1, o2, o3 = sp.symbols("o1 o2 o3") lbm_config_1 = LBMConfig(method=Method.SRT, stencil=LBStencil(Stencil.D2Q9), relaxation_rates=[o3]) lbm_config_2 = LBMConfig(method=Method.MRT, stencil=LBStencil(Stencil.D2Q9), relaxation_rates=[o3, o3, o3, o3]) lbm_config_3 = LBMConfig(method=Method.MRT, stencil=LBStencil(Stencil.D2Q9), relaxation_rates=[o3] * 9, entropic=True) method = create_lb_method(lbm_config=lbm_config_1) method2 = create_lb_method(lbm_config=lbm_config_2) method3 = create_lb_method(lbm_config=lbm_config_3) method.set_zeroth_moment_relaxation_rate(o1) method.set_first_moment_relaxation_rate(o2) assert get_shear_relaxation_rate(method) == o3 method.set_zeroth_moment_relaxation_rate(o3) method.set_first_moment_relaxation_rate(o3) method2.set_conserved_moments_relaxation_rate(o3) assert method.collision_matrix == method2.collision_matrix == method3.collision_matrix
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 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
def test_momentum_density_shift(force_model): target = Target.CPU stencil = LBStencil(Stencil.D2Q9) domain_size = (4, 4) dh = ps.create_data_handling(domain_size=domain_size, default_target=target) rho = dh.add_array('rho', values_per_cell=1) dh.fill('rho', 0.0, ghost_layers=True) momentum_density = dh.add_array('momentum_density', values_per_cell=dh.dim) dh.fill('momentum_density', 0.0, ghost_layers=True) src = dh.add_array('src', values_per_cell=len(stencil)) dh.fill('src', 0.0, ghost_layers=True) lbm_config = LBMConfig(method=Method.SRT, compressible=True, force_model=force_model, force=(1, 2)) method = create_lb_method(lbm_config=lbm_config) cqc = method.conserved_quantity_computation momentum_density_getter = cqc.output_equations_from_pdfs( src.center_vector, { 'density': rho.center, 'momentum_density': momentum_density.center_vector }) config = ps.CreateKernelConfig(target=dh.default_target) momentum_density_ast = ps.create_kernel(momentum_density_getter, config=config) momentum_density_kernel = momentum_density_ast.compile() dh.run_kernel(momentum_density_kernel) assert np.sum(dh.gather_array( momentum_density.name)[:, :, 0]) == np.prod(domain_size) / 2 assert np.sum(dh.gather_array( momentum_density.name)[:, :, 1]) == np.prod(domain_size)
def test_allen_cahn_lb(): stencil_phase = LBStencil(Stencil.D3Q15) # fields u = fields("vel_field(" + str(stencil_phase.D) + "): [" + str(stencil_phase.D) + "D]", layout='fzyx') C = fields("phase_field: [" + str(stencil_phase.D) + "D]", layout='fzyx') C_tmp = fields("phase_field_tmp: [" + str(stencil_phase.D) + "D]", layout='fzyx') h = fields("lb_phase_field(" + str(len(stencil_phase)) + "): [" + str(stencil_phase.D) + "D]", layout='fzyx') h_tmp = fields("lb_phase_field_tmp(" + str(len(stencil_phase)) + "): [" + str(stencil_phase.D) + "D]", layout='fzyx') M = 0.02 W = 5 w_c = 1.0 / (0.5 + (3.0 * M)) lbm_config = LBMConfig(stencil=stencil_phase, method=Method.SRT, relaxation_rate=w_c, compressible=True) method_phase = create_lb_method(lbm_config=lbm_config) h_updates = initializer_kernel_phase_field_lb(h, C, u, method_phase, W) force_h = [f / 3 for f in interface_tracking_force(C, stencil_phase, W)] force_model_h = MultiphaseForceModel(force=force_h) allen_cahn_lb = get_collision_assignments_phase(lb_method=method_phase, velocity_input=u, output={'density': C_tmp}, force_model=force_model_h, symbolic_fields={"symbolic_field": h, "symbolic_temporary_field": h_tmp}, kernel_type='stream_pull_collide') allen_cahn_lb = get_collision_assignments_phase(lb_method=method_phase, velocity_input=u, output={'density': C_tmp}, force_model=force_model_h, symbolic_fields={"symbolic_field": h, "symbolic_temporary_field": h_tmp}, kernel_type='collide_only')
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 test_modes_central_moment_longrun(stencil, force_model, compressible): """check force terms in mode space""" stencil = LBStencil(stencil) omega_s = sp.Symbol("omega_s") F = list(sp.symbols(f"F_:{stencil.D}")) lbm_config = LBMConfig(method=Method.CENTRAL_MOMENT, stencil=stencil, relaxation_rate=omega_s, compressible=compressible, force_model=force_model, force=tuple(F)) method = create_lb_method(lbm_config=lbm_config) subs_dict = method.subs_dict_relxation_rate force_moments = method.force_model.moment_space_forcing(method) force_moments = force_moments.subs(subs_dict) # The mass mode should be zero assert force_moments[0] == 0 # The momentum moments should contain the force assert list(force_moments[1:stencil.D + 1]) == F
def test_forcing_space_equivalences(force_model): if force_model == ForceModel.HE: # We don't expect equivalence for the He model since its # moments are derived from the continuous maxwellian return stencil = LBStencil(Stencil.D3Q27) force = sp.symbols(f"F_:{stencil.D}") lbm_config = LBMConfig(stencil=stencil, method=Method.SRT, force=force, force_model=force_model) fmodel = lbm_config.force_model lb_method = create_lb_method(lbm_config=lbm_config) inv_moment_matrix = lb_method.moment_matrix.inv() force_pdfs = sp.Matrix(fmodel(lb_method)) force_moments = fmodel.moment_space_forcing(lb_method) diff = (force_pdfs - (inv_moment_matrix * force_moments)).expand() for i, d in enumerate(diff): assert d == 0, f"Mismatch between population and moment space forcing " \ f"in force model {force_model}, population f_{i}"
def test_create_cumulant_method_from_existing(): lbm_config = LBMConfig(stencil=LBStencil(Stencil.D2Q9), method=Method.CUMULANT, relaxation_rate=1.5) method = create_lb_method(lbm_config=lbm_config) old_relaxation_info_dict = method.relaxation_info_dict def modification_func(cumulant, eq, rate): if rate == 0: return cumulant, eq, 1.0 return cumulant, eq, rate new_method = create_lb_method_from_existing(method, modification_func) new_relaxation_info_dict = new_method.relaxation_info_dict for i, (o, n) in enumerate( zip(old_relaxation_info_dict.items(), new_relaxation_info_dict.items())): assert o[0] == n[0] assert o[1].equilibrium_value == n[1].equilibrium_value if o[1].relaxation_rate == 0: assert n[1].relaxation_rate == 1.0 else: assert o[1].relaxation_rate == n[1].relaxation_rate
'cse_global': True, 'cse_pdfs': False, 'field_layout': 'fzyx', } } options = options_dict.get(ctx.config, options_dict['srt']) options.update(common_options) stencil_str = options['stencil'] q = int(stencil_str[stencil_str.find('Q') + 1:]) pdfs, velocity_field = ps.fields( "pdfs({q}), velocity(3) : double[3D]".format(q=q), layout='fzyx') options['optimization']['symbolic_field'] = pdfs vp = [('int32_t', 'cudaBlockSize0'), ('int32_t', 'cudaBlockSize1')] lb_method = create_lb_method(**options) # Kernels options_without_opt = options.copy() del options_without_opt['optimization'] update_rules = {} for name, accessor in accessors.items(): update_rule = create_lb_update_rule(lb_method=lb_method, kernel_type=accessor, **options) update_rule = insert_fast_divisions(update_rule) update_rule = insert_fast_sqrts(update_rule) update_rules[name] = update_rule generate_sweep(ctx, 'UniformGridGPU_AA_LbKernel' + name, update_rule,
def genNoSlip(): boundary = NoSlip(name='MyNoSlip') method = create_lb_method(stencil='D3Q19', method='srt') return create_boundary_class(boundary, method)
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 test_central_moment_class(): stencil = LBStencil(Stencil.D2Q9) lbm_config = LBMConfig(stencil=stencil, method=Method.CENTRAL_MOMENT, relaxation_rates=[1.2, 1.3, 1.4, 1.5], equilibrium_order=4, compressible=True) method = create_lb_method(lbm_config=lbm_config) lbm_config = LBMConfig(stencil=stencil, method=Method.SRT, relaxation_rate=1.2, equilibrium_order=4, compressible=True) srt = create_lb_method(lbm_config=lbm_config) rho = method.zeroth_order_equilibrium_moment_symbol u = method.first_order_equilibrium_moment_symbols cs_sq = sp.Rational(1, 3) force_model = Luo(force=sp.symbols(f"F_:{2}")) eq = (rho, 0, 0, 0, 0, 2 * rho * cs_sq, 0, 0, rho * cs_sq**2) default_moments = cascaded_moment_sets_literature(stencil) default_moments = [item for sublist in default_moments for item in sublist] assert method.central_moment_transform_class == PdfsToCentralMomentsByShiftMatrix assert method.conserved_quantity_computation.zeroth_order_moment_symbol == rho assert method.conserved_quantity_computation.first_order_moment_symbols == u assert method.moment_equilibrium_values == eq assert method.force_model is None method.set_force_model(force_model) assert method.force_model == force_model assert method.relaxation_matrix[0, 0] == 0 assert method.relaxation_matrix[1, 1] == 0 assert method.relaxation_matrix[2, 2] == 0 method.set_conserved_moments_relaxation_rate(1.9) assert method.relaxation_matrix[0, 0] == 1.9 assert method.relaxation_matrix[1, 1] == 1.9 assert method.relaxation_matrix[2, 2] == 1.9 moments = list() for i in method.relaxation_info_dict: moments.append(i) assert moments == default_moments for i in range(len(stencil)): assert method.relaxation_rates[i] == method.relaxation_matrix[i, i] M = method.moment_matrix N = method.shift_matrix assert M == moment_matrix(moments, stencil=stencil) assert N == set_up_shift_matrix(moments, stencil=stencil) assert get_weights(stencil) == method.weights eq_terms_central = method.get_equilibrium_terms() eq_terms_srt = srt.get_equilibrium_terms() for i in range(len(stencil)): assert sp.simplify(eq_terms_central[i] - eq_terms_srt[i]) == 0 method = create_lb_method( lbm_config=LBMConfig(stencil=LBStencil(Stencil.D2Q9), method=Method.CENTRAL_MOMENT, relaxation_rates=[1.7, 1.8, 1.2, 1.3, 1.4], equilibrium_order=4, compressible=True)) assert method.relaxation_matrix[0, 0] == 0 assert method.relaxation_matrix[1, 1] == 0 assert method.relaxation_matrix[2, 2] == 0 method = create_lb_method( lbm_config=LBMConfig(stencil=LBStencil(Stencil.D2Q9), method=Method.CENTRAL_MOMENT, relaxation_rates=[1.3] * 9, equilibrium_order=4, compressible=True)) np.testing.assert_almost_equal(sum(method.relaxation_rates), 1.3 * 9)
def test_literature(force_model, stencil, method): # Be aware that the choice of the conserved moments does not affect the forcing although omega is introduced # in the forcing vector then. The reason is that: # m_{100}^{\ast} = m_{100} + \omega ( m_{100}^{eq} - m_{100} ) + \left( 1 - \frac{\omega}{2} \right) F_x # always simplifies to: # m_{100}^{\ast} = m_{100} + F_x # Thus the relaxation rate gets cancled again. stencil = LBStencil(stencil) omega_s = sp.Symbol("omega_s") omega_b = sp.Symbol("omega_b") omega_o = sp.Symbol("omega_o") omega_e = sp.Symbol("omega_e") if method == Method.SRT: rrs = [omega_s] omega_o = omega_b = omega_e = omega_s elif method == Method.TRT: rrs = [omega_e, omega_o] omega_s = omega_b = omega_e else: rrs = [omega_s, omega_b, omega_o, omega_e, omega_o, omega_e] F = sp.symbols(f"F_:{stencil.D}") lbm_config = LBMConfig(method=method, weighted=True, stencil=stencil, relaxation_rates=rrs, compressible=force_model != ForceModel.BUICK, force_model=force_model, force=F) lb_method = create_lb_method(lbm_config=lbm_config) omega_momentum = list(set(lb_method.relaxation_rates[1:stencil.D + 1])) assert len(omega_momentum) == 1 omega_momentum = omega_momentum[0] subs_dict = lb_method.subs_dict_relxation_rate force_term = sp.simplify(lb_method.force_model(lb_method).subs(subs_dict)) u = sp.Matrix(lb_method.first_order_equilibrium_moment_symbols) rho = lb_method.conserved_quantity_computation.zeroth_order_moment_symbol # see silva2020 for nomenclature F = sp.Matrix(F) uf = sp.Matrix(u).dot(F) F2 = sp.Matrix(F).dot(sp.Matrix(F)) Fq = sp.zeros(stencil.Q, 1) uq = sp.zeros(stencil.Q, 1) for i, cq in enumerate(stencil): Fq[i] = sp.Matrix(cq).dot(sp.Matrix(F)) uq[i] = sp.Matrix(cq).dot(u) common_plus = 3 * (1 - omega_e / 2) common_minus = 3 * (1 - omega_momentum / 2) result = [] if method == Method.MRT and force_model == ForceModel.GUO: # check against eq. 4.68 from schiller2008thermal uf = u.dot(F) * sp.eye(len(F)) G = (u * F.transpose() + F * u.transpose() - uf * sp.Rational(2, lb_method.dim)) * sp.Rational(1, 2) * \ (2 - omega_s) + uf * sp.Rational(1, lb_method.dim) * (2 - omega_b) for direction, w_i in zip(lb_method.stencil, lb_method.weights): direction = sp.Matrix(direction) tr = sp.trace(G * (direction * direction.transpose() - sp.Rational(1, 3) * sp.eye(len(F)))) result.append(3 * w_i * (F.dot(direction) + sp.Rational(3, 2) * tr)) elif force_model == ForceModel.GUO: # check against table 2 in silva2020 (correct for SRT and TRT), matches eq. 20 from guo2002discrete (for SRT) Sq_plus = sp.zeros(stencil.Q, 1) Sq_minus = sp.zeros(stencil.Q, 1) for i, w_i in enumerate(lb_method.weights): Sq_plus[i] = common_plus * w_i * (3 * uq[i] * Fq[i] - uf) Sq_minus[i] = common_minus * w_i * Fq[i] result = Sq_plus + Sq_minus elif force_model == ForceModel.BUICK: # check against table 2 in silva2020 (correct for all collision models due to the simplicity of Buick), # matches eq. 18 from silva2010 (for SRT) Sq_plus = sp.zeros(stencil.Q, 1) Sq_minus = sp.zeros(stencil.Q, 1) for i, w_i in enumerate(lb_method.weights): Sq_plus[i] = 0 Sq_minus[i] = common_minus * w_i * Fq[i] result = Sq_plus + Sq_minus elif force_model == ForceModel.EDM: # check against table 2 in silva2020 if method == Method.MRT: # for mrt no literature terms are known at the time of writing this test case. # However it is most likly correct since SRT and TRT are derived from the moment space representation pytest.skip() Sq_plus = sp.zeros(stencil.Q, 1) Sq_minus = sp.zeros(stencil.Q, 1) for i, w_i in enumerate(lb_method.weights): Sq_plus[i] = common_plus * w_i * (3 * uq[i] * Fq[i] - uf) + ( (w_i / (8 * rho)) * (3 * Fq[i]**2 - F2)) Sq_minus[i] = common_minus * w_i * Fq[i] result = Sq_plus + Sq_minus elif force_model == ForceModel.SHANCHEN: # check against table 2 in silva2020 if method == Method.MRT: # for mrt no literature terms are known at the time of writing this test case. # However it is most likly correct since SRT and TRT are derived from the moment space representation pytest.skip() Sq_plus = sp.zeros(stencil.Q, 1) Sq_minus = sp.zeros(stencil.Q, 1) for i, w_i in enumerate(lb_method.weights): Sq_plus[i] = common_plus * w_i * (3 * uq[i] * Fq[i] - uf) + common_plus**2 * ( (w_i / (2 * rho)) * (3 * Fq[i]**2 - F2)) Sq_minus[i] = common_minus * w_i * Fq[i] result = Sq_plus + Sq_minus assert list(sp.simplify(force_term - sp.Matrix(result))) == [0] * len(stencil)
from pystencils_walberla import CodeGeneration, generate_sweep with CodeGeneration() as ctx: # LB options options = { 'method': 'srt', 'stencil': 'D3Q19', 'relaxation_rate': sp.Symbol("omega"), 'field_name': 'pdfs', 'compressible': False, 'temporary_field_name': 'pdfs_tmp', 'optimization': {'cse_global': True, 'cse_pdfs': True, 'gpu_indexing_params': {'block_size': (128, 1, 1)}} } lb_method = create_lb_method(**options) update_rule = create_lb_update_rule(lb_method=lb_method, **options) # CPU lattice model - required for macroscopic value computation, VTK output etc. generate_lattice_model(ctx, 'UniformGridGPU_LatticeModel', lb_method) # gpu LB sweep & boundaries generate_sweep(ctx, 'UniformGridGPU_LbKernel', update_rule, field_swaps=[('pdfs', 'pdfs_tmp')], inner_outer_split=True, target='gpu') generate_boundary(ctx, 'UniformGridGPU_NoSlip', NoSlip(), lb_method, target='gpu') generate_boundary(ctx, 'UniformGridGPU_UBB', UBB([0.05, 0, 0]), lb_method, target='gpu') # communication generate_pack_info_from_kernel(ctx, 'UniformGridGPU_PackInfo', update_rule, target='gpu')
import sympy as sp import pystencils as ps from lbmpy.creationfunctions import create_lb_method from lbmpy.boundaries import NoSlip, UBB from pystencils_walberla import CodeGeneration from lbmpy_walberla import generate_lattice_model, RefinementScaling, generate_boundary with CodeGeneration() as ctx: omega = sp.Symbol("omega") force_field = ps.fields("force(3): [3D]", layout='fzyx') # lattice Boltzmann method lb_method = create_lb_method(stencil='D3Q19', method='srt', relaxation_rates=[omega], force_model='guo', force=force_field.center_vector) scaling = RefinementScaling() scaling.add_standard_relaxation_rate_scaling(omega) scaling.add_force_scaling(force_field) # generate components generate_lattice_model(ctx, 'SrtWithForceFieldModel', lb_method, refinement_scaling=scaling) generate_boundary(ctx, 'MyUBB', UBB([0.05, 0, 0]), lb_method) generate_boundary(ctx, 'MyNoSlip', NoSlip(), lb_method)
import sympy as sp import pystencils as ps from lbmpy.creationfunctions import create_lb_method from lbmpy.boundaries import NoSlip, UBB from pystencils_walberla import CodeGeneration from lbmpy_walberla import generate_lattice_model, RefinementScaling, generate_boundary with CodeGeneration() as ctx: omega = sp.Symbol("omega") force_field = ps.fields("force(3): [3D]", layout='fzyx') # lattice Boltzmann method lb_method = create_lb_method(stencil='D3Q19', method='srt', relaxation_rates=[omega], force_model='guo', force=force_field.center_vector) scaling = RefinementScaling() scaling.add_standard_relaxation_rate_scaling(omega) scaling.add_force_scaling(force_field) # generate components generate_lattice_model(ctx, 'SrtWithForceFieldModel', lb_method, refinement_scaling=scaling) generate_boundary(ctx, 'MyUBB', UBB([0.05, 0, 0]), lb_method) generate_boundary(ctx, 'MyNoSlip', NoSlip(), lb_method)
def _check_modes(stencil, force_model, compressible): omega_s = sp.Symbol("omega_s") omega_b = sp.Symbol("omega_b") omega_o = sp.Symbol("omega_o") omega_e = sp.Symbol("omega_e") F = list(sp.symbols(f"F_:{stencil.D}")) lbm_config = LBMConfig(method=Method.MRT, stencil=stencil, relaxation_rates=[ omega_s, omega_b, omega_o, omega_e, omega_o, omega_e ], compressible=compressible, force_model=force_model, force=tuple(F)) method = create_lb_method(lbm_config=lbm_config) subs_dict = method.subs_dict_relxation_rate force_moments = method.force_model.moment_space_forcing(method) force_moments = force_moments.subs(subs_dict) # The mass mode should be zero assert force_moments[0] == 0 # The momentum moments should contain the force assert list(force_moments[1:stencil.D + 1]) == F if force_model == ForceModel.GUO: num_stresses = (stencil.D * stencil.D - stencil.D) // 2 + stencil.D lambda_s, lambda_b = -omega_s, -omega_b # The stress moments should match eq. 47 from https://doi.org/10.1023/A:1010414013942 u = method.first_order_equilibrium_moment_symbols def traceless(m): tr = sp.simplify(sum([m[i, i] for i in range(stencil.D)])) return m - tr / m.shape[0] * sp.eye(m.shape[0]) C = sp.Rational(1, 2) * (2 + lambda_s) * (traceless(sp.Matrix(u) * sp.Matrix(F).transpose()) + traceless(sp.Matrix(F) * sp.Matrix(u).transpose())) + \ sp.Rational(1, method.dim) * (2 + lambda_b) * sp.Matrix(u).dot(F) * sp.eye(method.dim) subs = { sp.Symbol(chr(ord("x") + i)) * sp.Symbol(chr(ord("x") + j)): C[i, j] for i in range(stencil.D) for j in range(stencil.D) } for force_moment, moment in zip( force_moments[stencil.D + 1:stencil.D + 1 + num_stresses], method.moments[stencil.D + 1:stencil.D + 1 + num_stresses]): ref = moment.subs(subs) diff = sp.simplify(ref - force_moment) if is_bulk_moment(moment, stencil.D): assert diff == 0 or isinstance( diff, sp.Rational) # difference should be zero or a constant else: assert diff == 0 # difference should be zero ff = method.moment_matrix.inv() * sp.Matrix( method.force_model.moment_space_forcing(method).subs(subs_dict)) # Check eq. 4.53a from schiller2008thermal assert sp.simplify(sum(ff)) == 0 # Check eq. 4.53b from schiller2008thermal assert [ sp.simplify(sum(ff[i] * stencil[i][j] for i in range(len(stencil)))) for j in range(stencil.D) ] == F # Check eq. 4.61a from schiller2008thermal ref = (2 + lambda_s) / 2 * ( traceless(sp.Matrix(u) * sp.Matrix(F).transpose()) + traceless(sp.Matrix(F) * sp.Matrix(u).transpose())) s = sp.zeros(stencil.D) for i in range(0, len(stencil)): s += ff[i] * traceless( sp.Matrix(stencil[i]) * sp.Matrix(stencil[i]).transpose()) assert sp.simplify(s - ref) == sp.zeros(stencil.D) # Check eq. 4.61b from schiller2008thermal assert sp.simplify( sum(ff[i] * stencil[i][a]**2 for i in range(len(stencil)) for a in range(stencil.D)) - (2 + lambda_b) * sp.Matrix(u).dot(F)) == 0 # All other moments should be zero assert list(force_moments[stencil.D + 1 + num_stresses:]) == [0] * ( len(stencil) - (stencil.D + 1 + num_stresses)) elif force_model == ForceModel.SIMPLE: # All other moments should be zero assert list(force_moments[stencil.D + 1:]) == [0] * (len(stencil) - (stencil.D + 1))
def test_diffusion_boundary(): domain_size = (10, 10) stencil = LBStencil(Stencil.D2Q9) weights = get_weights(stencil, c_s_sq=sp.Rational(1, 3)) concentration = 1.0 # Data Handling dh = ps.create_data_handling(domain_size=domain_size) dh.add_array('pdfs', values_per_cell=stencil.Q) dh.fill("pdfs", 0.0, ghost_layers=True) lbm_config = LBMConfig(stencil=stencil, method=Method.SRT, relaxation_rate=1.8, compressible=True) method = create_lb_method(lbm_config=lbm_config) # Boundary Handling bh = LatticeBoltzmannBoundaryHandling(method, dh, 'pdfs', name="bh") add_box_boundary(bh, boundary=DiffusionDirichlet(concentration)) bh() assert all( np.abs( dh.gather_array("pdfs", ghost_layers=True)[0, 1:-2, 4] - 2 * weights[4]) < 1e-14) assert all( np.abs( dh.gather_array("pdfs", ghost_layers=True)[0, 1:-2, 6] - 2 * weights[6]) < 1e-14) assert all( np.abs( dh.gather_array("pdfs", ghost_layers=True)[0, 2:-1, 8] - 2 * weights[8]) < 1e-14) assert all( np.abs( dh.gather_array("pdfs", ghost_layers=True)[-1, 1:-2, 3] - 2 * weights[3]) < 1e-14) assert all( np.abs( dh.gather_array("pdfs", ghost_layers=True)[-1, 1:-2, 5] - 2 * weights[5]) < 1e-14) assert all( np.abs( dh.gather_array("pdfs", ghost_layers=True)[-1, 2:-1, 7] - 2 * weights[7]) < 1e-14) assert all( np.abs( dh.gather_array("pdfs", ghost_layers=True)[1:-2, 0, 1] - 2 * weights[1]) < 1e-14) assert all( np.abs( dh.gather_array("pdfs", ghost_layers=True)[2:, 0, 5] - 2 * weights[5]) < 1e-14) assert all( np.abs( dh.gather_array("pdfs", ghost_layers=True)[:-2, 0, 6] - 2 * weights[6]) < 1e-14) assert all( np.abs( dh.gather_array("pdfs", ghost_layers=True)[1:-2, -1, 2] - 2 * weights[2]) < 1e-14) assert all( np.abs( dh.gather_array("pdfs", ghost_layers=True)[2:, -1, 7] - 2 * weights[7]) < 1e-14) assert all( np.abs( dh.gather_array("pdfs", ghost_layers=True)[:-2, -1, 8] - 2 * weights[8]) < 1e-14)
def test_diffusion(): """ Runs the "Diffusion from Plate in Uniform Flow" benchmark as it is described in [ch. 8.6.3, The Lattice Boltzmann Method, Krüger et al.]. dC/dy = 0 ┌───────────────┐ │ → → → │ C = 0 │ → u → │ dC/dx = 0 │ → → → │ └───────────────┘ C = 1 The analytical solution is given by: C(x,y) = 1 * erfc(y / sqrt(4Dx/u)) The hydrodynamic field is not simulated, instead a constant velocity is assumed. """ pytest.importorskip("pycuda") # Parameters domain_size = (1600, 160) omega = 1.38 diffusion = (1 / omega - 0.5) / 3 velocity = 0.05 time_steps = 50000 stencil = LBStencil(Stencil.D2Q9) target = ps.Target.GPU # Data Handling dh = ps.create_data_handling(domain_size=domain_size, default_target=target) vel_field = dh.add_array('vel_field', values_per_cell=stencil.D) dh.fill('vel_field', velocity, 0, ghost_layers=True) dh.fill('vel_field', 0.0, 1, ghost_layers=True) con_field = dh.add_array('con_field', values_per_cell=1) dh.fill('con_field', 0.0, ghost_layers=True) pdfs = dh.add_array('pdfs', values_per_cell=stencil.Q) dh.fill('pdfs', 0.0, ghost_layers=True) pdfs_tmp = dh.add_array('pdfs_tmp', values_per_cell=stencil.Q) dh.fill('pdfs_tmp', 0.0, ghost_layers=True) # Lattice Boltzmann method lbm_config = LBMConfig(stencil=stencil, method=Method.MRT, relaxation_rates=[1, 1.5, 1, 1.5, 1], velocity_input=vel_field, output={'density': con_field}, compressible=True, weighted=True, kernel_type='stream_pull_collide') lbm_opt = LBMOptimisation(symbolic_field=pdfs, symbolic_temporary_field=pdfs_tmp) config = ps.CreateKernelConfig(target=dh.default_target, cpu_openmp=True) method = create_lb_method(lbm_config=lbm_config) method.set_conserved_moments_relaxation_rate(omega) lbm_config = replace(lbm_config, lb_method=method) update_rule = create_lb_update_rule(lbm_config=lbm_config, lbm_optimisation=lbm_opt) kernel = ps.create_kernel(update_rule, config=config).compile() # PDF initalization init = pdf_initialization_assignments(method, con_field.center, vel_field.center_vector, pdfs.center_vector) dh.run_kernel(ps.create_kernel(init).compile()) dh.all_to_gpu() # Boundary Handling bh = LatticeBoltzmannBoundaryHandling(update_rule.method, dh, 'pdfs', name="bh", target=dh.default_target) add_box_boundary(bh, boundary=NeumannByCopy()) bh.set_boundary(DiffusionDirichlet(0), slice_from_direction('W', dh.dim)) bh.set_boundary(DiffusionDirichlet(1), slice_from_direction('S', dh.dim)) # Timeloop for i in range(time_steps): bh() dh.run_kernel(kernel) dh.swap("pdfs", "pdfs_tmp") dh.all_to_cpu() # Verification x = np.arange(1, domain_size[0], 1) y = np.arange(0, domain_size[1], 1) X, Y = np.meshgrid(x, y) analytical = np.zeros(domain_size) analytical[1:, :] = np.vectorize(math.erfc)( Y / np.vectorize(math.sqrt)(4 * diffusion * X / velocity)).transpose() simulated = dh.gather_array('con_field', ghost_layers=False) residual = 0 for i in x: for j in y: residual += (simulated[i, j] - analytical[i, j])**2 residual = math.sqrt(residual / (domain_size[0] * domain_size[1])) assert residual < 1e-2