def accessible_mask( self, not_accessible: tuple or list, type: type = CenteredGrid) -> CenteredGrid or StaggeredGrid: """ Unifies domain and Obstacle or Geometry objects into a binary StaggeredGrid mask which can be used to enforce boundary conditions. Args: not_accessible: blocked region(s) of space specified by geometries Returns: Binary mask indicating valid fields w.r.t. the boundary conditions. The result is of type `type` and uses the extrapolation `Domain.boundaries['accessible_extrapolation']`. """ accessible_mask = self.scalar_grid( HardGeometryMask(~union(not_accessible)), extrapolation=self.boundaries['accessible_extrapolation']) if type is CenteredGrid: return accessible_mask elif type is StaggeredGrid: return field.stagger(accessible_mask, math.minimum, self.boundaries['accessible_extrapolation']) else: raise ValueError('Unknown grid type: %s' % type)
def accessible_mask(self, not_accessible: tuple or list, type: type = CenteredGrid, extrapolation='accessible') -> CenteredGrid or StaggeredGrid: """ Unifies domain and Obstacle or Geometry objects into a binary StaggeredGrid mask which can be used to enforce boundary conditions. Args: not_accessible: blocked region(s) of space specified by geometries type: class of Grid to create, must be either CenteredGrid or StaggeredGrid extrapolation: (optional) grid extrapolation, defaults to Domain.boundaries['accessible'] Returns: Binary mask indicating valid fields w.r.t. the boundary conditions. """ extrapolation = extrapolation if isinstance(extrapolation, math.Extrapolation) else self.boundaries[extrapolation] accessible_mask = self.scalar_grid(HardGeometryMask(~union(not_accessible)), extrapolation=extrapolation) if type is CenteredGrid: return accessible_mask elif type is StaggeredGrid: return field.stagger(accessible_mask, math.minimum, extrapolation) else: raise ValueError('Unknown grid type: %s' % type)
def make_incompressible(velocity: Grid, domain: Domain, obstacles: tuple or list = (), solve_params: math.LinearSolve = math.LinearSolve( None, 1e-3), pressure_guess: CenteredGrid = None): """ Projects the given velocity field by solving for the pressure and subtracting its gradient. This method is similar to :func:`field.divergence_free()` but differs in how the boundary conditions are specified. Args: velocity: Vector field sampled on a grid domain: Used to specify boundary conditions obstacles: List of Obstacles to specify boundary conditions inside the domain (Default value = ()) pressure_guess: Initial guess for the pressure solve solve_params: Parameters for the pressure solve Returns: velocity: divergence-free velocity of type `type(velocity)` pressure: solved pressure field, `CenteredGrid` iterations: Number of iterations required to solve for the pressure divergence: divergence field of input velocity, `CenteredGrid` """ input_velocity = velocity active = domain.grid( HardGeometryMask(~union(*[obstacle.geometry for obstacle in obstacles])), extrapolation=domain.boundaries['active_extrapolation']) accessible = domain.grid( active, extrapolation=domain.boundaries['accessible_extrapolation']) hard_bcs = field.stagger(accessible, math.minimum, domain.boundaries['accessible_extrapolation'], type=type(velocity)) velocity = layer_obstacle_velocities(velocity * hard_bcs, obstacles).with_( extrapolation=domain.boundaries['near_vector_extrapolation']) div = divergence(velocity) if domain.boundaries[ 'near_vector_extrapolation'] == math.extrapolation.BOUNDARY: div -= field.mean(div) # Solve pressure def laplace(p): grad = gradient(p, type(velocity)) grad *= hard_bcs grad = grad.with_( extrapolation=domain.boundaries['near_vector_extrapolation']) div = divergence(grad) lap = where(active, div, p) return lap pressure_guess = pressure_guess if pressure_guess is not None else domain.scalar_grid( 0) converged, pressure, iterations = field.solve(laplace, y=div, x0=pressure_guess, solve_params=solve_params, constants=[active, hard_bcs]) if math.all_available(converged) and not math.all(converged): raise AssertionError( f"pressure solve did not converge after {iterations} iterations\nResult: {pressure.values}" ) # Subtract grad pressure gradp = field.gradient(pressure, type=type(velocity)) * hard_bcs velocity = (velocity - gradp).with_(extrapolation=input_velocity.extrapolation) return velocity, pressure, iterations, div
def make_incompressible( velocity: GridType, obstacles: tuple or list = (), solve=math.Solve('auto', 1e-5, 1e-5, gradient_solve=math.Solve('auto', 1e-5, 1e-5)) ) -> Tuple[GridType, CenteredGrid]: """ Projects the given velocity field by solving for the pressure and subtracting its spatial_gradient. This method is similar to :func:`field.divergence_free()` but differs in how the boundary conditions are specified. Args: velocity: Vector field sampled on a grid obstacles: List of Obstacles to specify boundary conditions inside the domain (Default value = ()) solve: Parameters for the pressure solve as. Returns: velocity: divergence-free velocity of type `type(velocity)` pressure: solved pressure field, `CenteredGrid` """ assert isinstance( obstacles, (tuple, list)), f"obstacles must be a tuple or list but got {type(obstacles)}" input_velocity = velocity accessible_extrapolation = _accessible_extrapolation( input_velocity.extrapolation) active = CenteredGrid( HardGeometryMask(~union(*[obstacle.geometry for obstacle in obstacles])), resolution=velocity.resolution, bounds=velocity.bounds, extrapolation=extrapolation.NONE) accessible = active.with_extrapolation(accessible_extrapolation) hard_bcs = field.stagger(accessible, math.minimum, input_velocity.extrapolation, type=type(velocity)) velocity = apply_boundary_conditions(velocity, obstacles) div = divergence(velocity) * active if not input_velocity.extrapolation.connects_to_outside: assert solve.preprocess_y is None, "fluid.make_incompressible() does not support custom preprocessing" solve = copy_with(solve, preprocess_y=_balance_divergence, preprocess_y_args=(active, )) if solve.x0 is None: pressure_extrapolation = _pressure_extrapolation( input_velocity.extrapolation) solve = copy_with(solve, x0=CenteredGrid( 0, resolution=div.resolution, bounds=div.bounds, extrapolation=pressure_extrapolation)) pressure = math.solve_linear(masked_laplace, f_args=[hard_bcs, active], y=div, solve=solve) grad_pressure = field.spatial_gradient( pressure, input_velocity.extrapolation, type=type(velocity)) * hard_bcs velocity = velocity - grad_pressure return velocity, pressure