def acoustic_scaling(self, dt): """Computes relaxation rate and lattice velocity from time step length. Args: dt: time step length Returns: relaxation_rate, lattice_velocity """ lattice_velocity = dt / self.dx * self.physical_velocity lattice_viscosity = self.kinematic_viscosity * dt / (self.dx ** 2) relaxation_rate = relaxation_rate_from_lattice_viscosity(lattice_viscosity) ResultType = namedtuple("AcousticScalingResult", ['relaxation_rate', 'lattice_velocity']) return ResultType(relaxation_rate, lattice_velocity)
def schaefer_turek_2d(cells_per_diameter, u_max=0.3, max_lattice_velocity=0.05, **kwargs): """Creates a 2D Schaefer Turek Benchmark. Args: cells_per_diameter: how many lattice cells are used to resolve the obstacle diameter u_max: called U_m in the paper: the maximum inflow velocity in physical units, for the first setup it is 0.3, for the second setup 1.5 max_lattice_velocity: maximum lattice velocity, the lower the more accurate is the simulation should not be larger than 0.1, if chosen too small the relaxation rate gets near 2 and simulation might also get unstable kwargs: parameters forwarded to the lattice boltzmann method Returns: scenario object """ dx = 0.1 / cells_per_diameter viscosity = 1e-3 dt = compute_delta_t(max_lattice_velocity, u_max, dx) lattice_viscosity = viscosity / dx / dx * dt omega = relaxation_rate_from_lattice_viscosity(lattice_viscosity) domain_size, geometry_callback, parameter_info = geometry_2d(dx) cylinder_diameter_l = parameter_info['cylinder_diameter_l'] u_bar_l = 2 / 3 * max_lattice_velocity re_lattice = u_bar_l * cylinder_diameter_l / lattice_viscosity print( "Schaefer-Turek 2D: U_m = %.2f m/s cells=%s, dx=%f, dt=%f, omega=%f, Re=%.1f" % (u_max, domain_size, dx, dt, omega, re_lattice)) initial_velocity = get_pipe_velocity_field(domain_size, max_lattice_velocity) scenario = create_channel(domain_size=domain_size, u_max=max_lattice_velocity, relaxation_rate=omega, initial_velocity=initial_velocity, **kwargs) scenario.boundary_handling.set_boundary(NoSlip('obstacle'), mask_callback=geometry_callback) parameter_info['u_bar_l'] = u_bar_l parameter_info['dt'] = dt scenario.parameterInfo = parameter_info return scenario
def fixed_lattice_velocity_scaling(self, lattice_velocity=0.1): """Computes relaxation rate and time step length from lattice velocity. Check the returned relaxation rate! If it is very close to 2, the simulation might get unstable. In that case increase the resolution (more cells_per_length) All physical quantities have to be passed in the same set of units. Args: lattice_velocity: Lattice velocity corresponding to physical_velocity. Maximum velocity should not be larger than 0.1 i.e. the fluid should not move faster than 0.1 cells per time step Returns: relaxation_rate, dt """ dt = lattice_velocity / self.physical_velocity * self.dx lattice_viscosity = self.kinematic_viscosity * dt / (self.dx ** 2) relaxation_rate = relaxation_rate_from_lattice_viscosity(lattice_viscosity) ResultType = namedtuple("FixedLatticeVelocityScalingResult", ['relaxation_rate', 'dt']) return ResultType(relaxation_rate, dt)
def relaxation_rate_from_reynolds_number(re, u_0, domain_size): nu = u_0 * domain_size / re return relaxation_rate_from_lattice_viscosity(nu)
def rod_simulation(stencil_name, diameter, parallel=False, entropic=False, re=150, time_to_simulate=17.0, eval_interval=0.5, write_vtk=True, write_numpy=True): if stencil_name == 'D3Q19_new': stencil = Stencil.D3Q19 maxwellian_moments = True elif stencil_name == 'D3Q19_old': stencil = Stencil.D3Q19 maxwellian_moments = False elif stencil_name == 'D3Q27': stencil = Stencil.D3Q27 maxwellian_moments = False else: raise ValueError("Unknown stencil name " + stencil_name) d = diameter length = 16 * d u_max_at_throat = 0.07 u_max_at_inflow = 0.07 / (3**2) # Geometry parameters inflow_area = int(1.5 * d) constriction_length = int(1.8904 * d) throat_length = int(10 / 3 * d) constriction_diameter = int(d / 3) u_mean_at_throat = u_max_at_throat / 2 lattice_viscosity = u_mean_at_throat * constriction_diameter / re omega = relaxation_rate_from_lattice_viscosity(lattice_viscosity) lbm_config = LBMConfig(stencil=LBStencil(stencil), compressible=True, method=Method.SRT, relaxation_rate=omega, entropic=entropic, maxwellian_moments=maxwellian_moments) kernel_params = {} print("ω=", omega) dh = create_data_handling(domain_size=(length, d, d), parallel=parallel) sc = LatticeBoltzmannStep(data_handling=dh, kernel_params=kernel_params, name=stencil_name, lbm_config=lbm_config) # ----------------- Boundary Setup -------------------------------- def pipe_geometry(x, *_): # initialize with full diameter everywhere result = np.ones_like(x) * d # constriction constriction_begin = inflow_area constriction_end = constriction_begin + constriction_length c_x = np.linspace(0, 1, constriction_length) result[constriction_begin:constriction_end] = d * ( 1 - c_x) + constriction_diameter * c_x # throat throat_start = constriction_end throat_end = throat_start + throat_length result[throat_start:throat_end] = constriction_diameter return result bh = sc.boundary_handling add_pipe_inflow_boundary(bh, u_max_at_inflow, make_slice[0, :, :]) outflow = FixedDensity(1.0) bh.set_boundary(outflow, slice_from_direction('E', 3)) wall = NoSlip() add_pipe_walls(bh, pipe_geometry, wall) # ----------------- Run -------------------------------------------- scenario_name = stencil_name if not os.path.exists(scenario_name): os.mkdir(scenario_name) def to_time_steps(non_dimensional_time): return int(diameter / (u_max_at_inflow / 2) * non_dimensional_time) time_steps = to_time_steps(time_to_simulate) eval_interval = to_time_steps(eval_interval) print("Total number of time steps", time_steps) for i in range(time_steps // eval_interval): sc.run(eval_interval) if write_vtk: sc.write_vtk() vel = sc.velocity[:, :, :, :] max_vel = np.max(vel) print("Time steps:", sc.time_steps_run, "/", time_steps, "Max velocity: ", max_vel) if np.isnan(max_vel): raise ValueError("Simulation went unstable") if write_numpy: np.save(scenario_name + f'/velocity_{sc.time_steps_run}', sc.velocity_slice().filled(0.0))
def _update_omega_from_viscosity_and_dt_dx(self): if self.dx.value == 0: return lattice_viscosity = self.kinematic_viscosity.value * 1e-6 * self.dt.value / (self.dx.value ** 2) self.omega.value = relaxation_rate_from_lattice_viscosity(lattice_viscosity)
def run(l, l_0, u_0, v_0, nu, y_size, periodicity_in_kernel, **kwargs): inv_result = { 'viscosity_error': np.nan, 'phase_error': np.nan, 'mlups': np.nan, } if 'initial_velocity' in kwargs: del kwargs['initial_velocity'] print("Running size l=%d nu=%f " % (l, nu), kwargs) initial_vel_arr = get_initial_velocity_field(l, l_0, u_0, v_0, y_size) omega = relaxation_rate_from_lattice_viscosity(nu) kwargs['relaxation_rates'] = [ sp.sympify(r) for r in kwargs['relaxation_rates'] ] if 'entropic' not in kwargs or not kwargs['entropic']: kwargs['relaxation_rates'] = [ r.subs(sp.Symbol("omega"), omega) for r in kwargs['relaxation_rates'] ] scenario = create_fully_periodic_flow( initial_vel_arr, periodicity_in_kernel=periodicity_in_kernel, **kwargs) if 'entropic' in kwargs and kwargs['entropic']: scenario.kernelParams['omega'] = kwargs['relaxation_rates'][0].subs( sp.Symbol("omega"), omega) total_time_steps = 20000 * (l // l_0)**2 initial_time_steps = 11000 * (l // l_0)**2 eval_interval = 1000 * (l // l_0)**2 scenario.run(initial_time_steps) if np.isnan(scenario.velocity_slice()).any(): return inv_result magnitudes = [] phases = [] mlups = [] while scenario.time_steps_run < total_time_steps: mlup_current_run = scenario.benchmark_run(eval_interval) if np.isnan(scenario.velocity_slice()).any(): return inv_result magnitude, phase = get_amplitude_and_phase( scenario.velocity[:, y_size // 2, :, 1]) magnitudes.append(magnitude) phases.append(abs(phase)) mlups.append(mlup_current_run) assert scenario.time_steps_run == total_time_steps estimated_viscosity = get_viscosity(magnitudes, eval_interval, l) viscosity_error = abs(estimated_viscosity - nu) / nu # called ER_\nu in the paper result = { 'viscosity_error': viscosity_error, 'phaseError': get_phase_error(phases, eval_interval), 'mlups': max(mlups), } print(" Result", result) return result
def test_shear_flow(target, stencil_name): # Cuda if target == ps.Target.GPU: pytest.importorskip("pycuda") # LB parameters stencil = LBStencil(stencil_name) if stencil.D == 2: L = (4, width) elif stencil.D == 3: L = (4, width, 4) else: raise Exception() periodicity = [True, False] + [True] * (stencil.D - 2) omega = relaxation_rate_from_lattice_viscosity(eta) # ## Data structures dh = ps.create_data_handling(L, periodicity=periodicity, default_target=target) src = dh.add_array('src', values_per_cell=stencil.Q) dst = dh.add_array_like('dst', 'src') ρ = dh.add_array('rho', latex_name='\\rho', values_per_cell=1) u = dh.add_array('u', values_per_cell=stencil.D) p = dh.add_array('p', values_per_cell=stencil.D**2) # LB Setup lbm_config = LBMConfig(stencil=stencil, relaxation_rate=omega, method=Method.TRT, compressible=True, kernel_type='collide_only') lbm_opt = LBMOptimisation(symbolic_field=src) collision = create_lb_update_rule(lbm_config=lbm_config, lbm_optimisation=lbm_opt) stream = create_stream_pull_with_output_kernel(collision.method, src, dst, {'velocity': u}) config = ps.CreateKernelConfig(cpu_openmp=False, target=dh.default_target) stream_kernel = ps.create_kernel(stream, config=config).compile() collision_kernel = ps.create_kernel(collision, config=config).compile() # Boundaries lbbh = LatticeBoltzmannBoundaryHandling(collision.method, dh, src.name, target=dh.default_target) # Second moment test setup cqc = collision.method.conserved_quantity_computation getter_eqs = cqc.output_equations_from_pdfs(src.center_vector, {'moment2': p}) kernel_compute_p = ps.create_kernel(getter_eqs, config=config).compile() # ## Set up the simulation init = macroscopic_values_setter(collision.method, velocity=(0,) * dh.dim, pdfs=src.center_vector, density=ρ.center) init_kernel = ps.create_kernel(init, ghost_layers=0).compile() vel_vec = sp.Matrix([0.5 * shear_velocity] + [0] * (stencil.D - 1)) if stencil.D == 2: lbbh.set_boundary(UBB(velocity=vel_vec), ps.make_slice[:, :wall_thickness]) lbbh.set_boundary(UBB(velocity=-vel_vec), ps.make_slice[:, -wall_thickness:]) elif stencil.D == 3: lbbh.set_boundary(UBB(velocity=vel_vec), ps.make_slice[:, :wall_thickness, :]) lbbh.set_boundary(UBB(velocity=-vel_vec), ps.make_slice[:, -wall_thickness:, :]) else: raise Exception() for bh in lbbh, : assert len(bh._boundary_object_to_boundary_info) == 2, "Restart kernel to clear boundaries" def init(): dh.fill(ρ.name, rho_0) dh.fill(u.name, np.nan, ghost_layers=True, inner_ghost_layers=True) dh.fill(u.name, 0) dh.run_kernel(init_kernel) sync_pdfs = dh.synchronization_function([src.name]) # Time loop def time_loop(steps): dh.all_to_gpu() for i in range(steps): dh.run_kernel(collision_kernel) sync_pdfs() lbbh() dh.run_kernel(stream_kernel) dh.run_kernel(kernel_compute_p) dh.swap(src.name, dst.name) if u.name in dh.gpu_arrays: dh.to_cpu(u.name) uu = dh.gather_array(u.name) # average periodic directions if stencil.D == 3: # dont' swap order uu = np.average(uu, axis=2) uu = np.average(uu, axis=0) if p.name in dh.gpu_arrays: dh.to_cpu(p.name) pp = dh.gather_array(p.name) # average periodic directions if stencil.D == 3: # dont' swap order pp = np.average(pp, axis=2) pp = np.average(pp, axis=0) # cut off wall regions uu = uu[wall_thickness:-wall_thickness] pp = pp[wall_thickness:-wall_thickness] if stencil.D == 2: pp = pp.reshape((len(pp), 2, 2)) if stencil.D == 3: pp = pp.reshape((len(pp), 3, 3)) return uu, pp init() # Simulation profile, pressure_profile = time_loop(t_max) expected = shear_flow(x=(np.arange(0, actual_width) + .5), t=t_max, nu=eta / rho_0, v=shear_velocity, h=actual_width, k_max=100) if stencil.D == 2: shear_direction = np.array((1, 0), dtype=float) shear_plane_normal = np.array((0, 1), dtype=float) if stencil.D == 3: shear_direction = np.array((1, 0, 0), dtype=float) shear_plane_normal = np.array((0, 1, 0), dtype=float) shear_rate = shear_velocity / actual_width dynamic_viscosity = eta * rho_0 correction_factor = eta / (eta - 1. / 6.) p_expected = rho_0 * np.identity(dh.dim) / 3.0 + dynamic_viscosity * shear_rate / correction_factor * ( np.outer(shear_plane_normal, shear_direction) + np.transpose(np.outer(shear_plane_normal, shear_direction))) # Sustract the tensorproduct of the velosity to get the pressure pressure_profile[:, 0, 0] -= rho_0 * profile[:, 0]**2 np.testing.assert_allclose(profile[:, 0], expected[1:-1], atol=1E-9) for i in range(actual_width - 2): np.testing.assert_allclose(pressure_profile[i], p_expected, atol=1E-9, rtol=1E-3)
def flow_around_sphere(stencil, galilean_correction, L_LU, total_steps): if galilean_correction and stencil.Q != 27: return True target = Target.GPU streaming_pattern = 'aa' timesteps = get_timesteps(streaming_pattern) u_max = 0.05 Re = 500000 kinematic_viscosity = (L_LU * u_max) / Re initial_velocity = (u_max, ) + (0, ) * (stencil.D - 1) omega_v = relaxation_rate_from_lattice_viscosity(kinematic_viscosity) channel_size = (10 * L_LU, ) + (5 * L_LU, ) * (stencil.D - 1) sphere_position = (channel_size[0] // 3, ) + (channel_size[1] // 2, ) * (stencil.D - 1) sphere_radius = L_LU // 2 lbm_config = LBMConfig(stencil=stencil, method=Method.CUMULANT, relaxation_rate=omega_v, galilean_correction=galilean_correction) lbm_opt = LBMOptimisation(pre_simplification=True) config = CreateKernelConfig(target=target) lb_method = create_lb_method(lbm_config=lbm_config) def get_extrapolation_kernel(timestep): boundary_assignments = [] indexing = BetweenTimestepsIndexing( pdf_field, stencil, streaming_pattern=streaming_pattern, prev_timestep=timestep) f_out, _ = indexing.proxy_fields for i, d in enumerate(stencil): if d[0] == -1: asm = Assignment(f_out.neighbor(0, 1)(i), f_out.center(i)) boundary_assignments.append(asm) boundary_assignments = indexing.substitute_proxies( boundary_assignments) iter_slice = get_slice_before_ghost_layer((1, ) + (0, ) * (stencil.D - 1)) extrapolation_ast = create_kernel(boundary_assignments, config=CreateKernelConfig( iteration_slice=iter_slice, ghost_layers=1, target=target)) return extrapolation_ast.compile() dh = create_data_handling(channel_size, periodicity=False, default_layout='fzyx', default_target=target) u_field = dh.add_array('u', stencil.D) rho_field = dh.add_array('rho', 1) pdf_field = dh.add_array('pdfs', stencil.Q) dh.fill(u_field.name, 0.0, ghost_layers=True) dh.fill(rho_field.name, 0.0, ghost_layers=True) dh.to_gpu(u_field.name) dh.to_gpu(rho_field.name) lbm_opt = replace(lbm_opt, symbolic_field=pdf_field) bh = LatticeBoltzmannBoundaryHandling(lb_method, dh, pdf_field.name, streaming_pattern=streaming_pattern, target=target) wall = NoSlip() inflow = UBB(initial_velocity) bh.set_boundary(inflow, slice_from_direction('W', stencil.D)) directions = ('N', 'S', 'T', 'B') if stencil.D == 3 else ('N', 'S') for direction in directions: bh.set_boundary(wall, slice_from_direction(direction, stencil.D)) outflow_kernels = [ get_extrapolation_kernel(Timestep.EVEN), get_extrapolation_kernel(Timestep.ODD) ] def sphere_boundary_callback(x, y, z=None): x = x - sphere_position[0] y = y - sphere_position[1] z = z - sphere_position[2] if z is not None else 0 return np.sqrt(x**2 + y**2 + z**2) <= sphere_radius bh.set_boundary(wall, mask_callback=sphere_boundary_callback) init_eqs = pdf_initialization_assignments( lb_method, 1.0, initial_velocity, pdf_field, streaming_pattern=streaming_pattern, previous_timestep=timesteps[0]) init_kernel = create_kernel(init_eqs, config=config).compile() output = {'density': rho_field, 'velocity': u_field} lbm_config = replace(lbm_config, output=output) lb_collision_rule = create_lb_collision_rule(lb_method=lb_method, lbm_config=lbm_config, lbm_optimisation=lbm_opt) lb_kernels = [] for t in timesteps: lbm_config = replace(lbm_config, timestep=t) lbm_config = replace(lbm_config, streaming_pattern=streaming_pattern) lb_kernels.append( create_lb_function(collision_rule=lb_collision_rule, lbm_config=lbm_config, lbm_optimisation=lbm_opt, config=config)) timestep = timesteps[0] dh.run_kernel(init_kernel) stability_check_frequency = 1000 for i in range(total_steps): bh(prev_timestep=timestep) dh.run_kernel(outflow_kernels[timestep.idx]) timestep = timestep.next() dh.run_kernel(lb_kernels[timestep.idx]) if i % stability_check_frequency == 0: dh.to_cpu(u_field.name) assert np.isfinite(dh.cpu_arrays[u_field.name]).all()