Ejemplo n.º 1
0
 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
Ejemplo n.º 2
0
def step_gradient_2d(params, plasma, phi, dt=0):
    """time gradient of model"""

    # Diffusion function
    def diffuse(arr, N, dx):
        for i in range(N):
            arr = field.laplace(arr)  # math.fourier_laplace(arr, dx)
        return arr

    # Calculate Gradients
    dx_p, dy_p = field.gradient(phi).unstack('gradient')
    # Get difference
    diff = (phi - plasma.density)
    # Step 2.1: New Omega.
    nu = (-1)**(params['N'] + 1) * params['nu']
    o = (params['c1'] * diff)
    if params['arakawa_coeff']:
        o += -params[
            'arakawa_coeff'] * math._nd._periodic_2d_arakawa_poisson_bracket(
                phi.values, plasma.omega.values, plasma.dx)
    if nu and params['N']:
        o += nu * diffuse(plasma.omega, params['N'], plasma.dx)
    # Step 2.2: New Density.
    n = (params['c1'] * diff)
    if params['arakawa_coeff']:
        n += -params[
            'arakawa_coeff'] * math._nd._periodic_2d_arakawa_poisson_bracket(
                phi.values, plasma.density.values, plasma.dx)
    if params['kappa_coeff']:
        n += -params['kappa_coeff'] * dy_p
    if nu:
        n += nu * diffuse(plasma.density, params['N'], plasma.dx)
    return Namespace(
        density=n,
        omega=o,
        phi=phi,  # NOTE: NOT A GRADIENT
        age=plasma.age + dt,
        dx=plasma.dx)
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
 def test_gradient(self):
     domain = Domain(x=4, y=3)
     phi = domain.grid() * (1, 2)
     grad = field.gradient(phi, stack_dim='gradient')
     self.assertEqual(('spatial', 'spatial', 'channel', 'channel'),
                      grad.shape.types)
Ejemplo n.º 5
0
def make_incompressible(
    velocity: StaggeredGrid,
    domain: Domain,
    obstacles: tuple or list or StaggeredGrid = (),
    particles: PointCloud or None = None,
    solve_params: math.LinearSolve = math.LinearSolve(),
    pressure_guess: CenteredGrid = None
) -> Tuple[StaggeredGrid, CenteredGrid, math.Tensor, CenteredGrid,
           StaggeredGrid]:
    """
    Projects the given velocity field by solving for the pressure and subtracting its gradient.

    Args:
        velocity: Current velocity field as StaggeredGrid
        obstacles: Sequence of `phi.physics.Obstacle` objects or binary StaggeredGrid marking through-flow cell faces
        particles (Optional if occupation masks are provided): Pointcloud holding the current positions of the particles
        domain (Optional if occupation masks are provided): Domain object
        pressure_guess (Optional): Initial pressure guess as CenteredGrid
        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`
      occupation_mask: StaggeredGrid
    """
    points = particles.with_(values=math.wrap(1))
    occupied_centered = points >> domain.grid()
    occupied_staggered = points >> domain.staggered_grid()

    if isinstance(obstacles, StaggeredGrid):
        accessible = obstacles
    else:
        accessible = domain.accessible_mask(
            union(*[obstacle.geometry for obstacle in obstacles]),
            type=StaggeredGrid)

    # --- Extrapolation is needed to exclude border divergence from the `occupied_centered` mask and thus
    # from the pressure solve. If particles are randomly distributed, the `occupied_centered` mask
    # could sometimes include the divergence at the borders (due to single particles right at the edge
    # which temporarily deform the `occupied_centered` mask when moving into a new cell) which would then
    # get compensated by the pressure. This is unwanted for falling liquids and therefore prevented by this
    # extrapolation. ---
    velocity_field, _ = extrapolate_valid(velocity * occupied_staggered,
                                          occupied_staggered, 1)
    velocity_field *= accessible  # Enforces boundary conditions after extrapolation
    div = field.divergence(
        velocity_field
    ) * occupied_centered  # Multiplication with `occupied_centered` excludes border divergence from pressure solve

    def matrix_eq(p):
        return field.where(
            occupied_centered,
            field.divergence(
                field.gradient(p, type=StaggeredGrid) * accessible), p)

    converged, pressure, iterations = field.solve(matrix_eq,
                                                  div,
                                                  pressure_guess
                                                  or domain.grid(),
                                                  solve_params=solve_params)
    gradp = field.gradient(pressure, type=type(velocity_field)) * accessible
    return velocity_field - gradp, pressure, iterations, div, occupied_staggered
Ejemplo n.º 6
0
 def matrix_eq(p):
     return field.where(
         occupied_centered,
         field.divergence(
             field.gradient(p, type=StaggeredGrid) * accessible), p)