Exemple #1
0
def make_incompressible(velocity: StaggeredGrid,
                        domain: Domain,
                        particles: PointCloud,
                        obstacles: tuple or list or StaggeredGrid = (),
                        solve=math.Solve('auto', 1e-5, 0, gradient_solve=math.Solve('auto', 1e-5, 1e-5))):
    """
    Projects the given velocity field by solving for the pressure and subtracting its spatial_gradient.

    Args:
        velocity: Current velocity field as StaggeredGrid
        domain: Domain object
        particles: `PointCloud` holding the current positions of the particles
        obstacles: Sequence of `phi.physics.Obstacle` objects or binary StaggeredGrid marking through-flow cell faces
        solve: Parameters for the pressure solve_linear

    Returns:
      velocity: divergence-free velocity of type `type(velocity)`
      pressure: solved pressure field, `CenteredGrid`
      iterations: Number of iterations required to solve_linear for the pressure
      divergence: divergence field of input velocity, `CenteredGrid`
      occupation_mask: StaggeredGrid
    """
    points = particles.with_values(math.tensor(1., convert=True))
    occupied_centered = points @ domain.scalar_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_linear. 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). This 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_linear

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

    if solve.x0 is None:
        solve = copy_with(solve, x0=domain.scalar_grid())
    pressure = field.solve_linear(matrix_eq, div, solve)

    def pressure_backward(_p, _p_, dp):
        return dp * occupied_centered.values,

    add_mask_in_gradient = math.custom_gradient(lambda p: p, pressure_backward)
    pressure = pressure.with_values(add_mask_in_gradient(pressure.values))

    gradp = field.spatial_gradient(pressure, type=type(velocity_field)) * accessible
    return velocity_field - gradp, pressure, occupied_staggered
Exemple #2
0
 def test_linear_solve_matrix_tape(self):
     y = CenteredGrid(1, extrapolation.ZERO, x=3) * (1, 2)
     x0 = CenteredGrid(0, extrapolation.ZERO, x=3)
     for method in ['CG', 'CG-adaptive', 'auto']:
         solve = math.Solve(method, 0, 1e-3, x0=x0, max_iterations=100)
         with math.SolveTape() as solves:
             x = field.solve_linear(math.jit_compile_linear(field.laplace),
                                    y, solve)
         math.assert_close(x.values, [[-1.5, -2, -1.5], [-3, -4, -3]],
                           abs_tolerance=1e-3)
         assert len(solves) == 1
         assert solves[0] == solves[solve]
         math.assert_close(solves[solve].residual.values,
                           0,
                           abs_tolerance=1e-3)
         assert math.close(solves[solve].iterations, 2) or math.close(
             solves[solve].iterations, -1)
         with math.SolveTape(record_trajectories=True) as solves:
             x = field.solve_linear(math.jit_compile_linear(field.laplace),
                                    y, solve)
         math.assert_close(x.values, [[-1.5, -2, -1.5], [-3, -4, -3]],
                           abs_tolerance=1e-3)
         assert solves[solve].x.trajectory.size == 3
         math.assert_close(solves[solve].residual.trajectory[-1].values,
                           0,
                           abs_tolerance=1e-3)
Exemple #3
0
 def solve(y, method):
     print(f"Tracing {method} with {backend}...")
     solve = math.Solve(method, 0, 1e-3, x0=x0, max_iterations=100)
     with SolveTape() as solves:
         x = field.solve_linear(math.jit_compile_linear(field.laplace),
                                y, solve)
     return x
Exemple #4
0
 def test_linear_solve_matrix_batched(
         self):  # TODO also test batched matrix
     y = CenteredGrid(1, extrapolation.ZERO, x=3) * (1, 2)
     x0 = CenteredGrid(0, extrapolation.ZERO, x=3)
     for method in ['CG', 'CG-adaptive', 'auto']:
         solve = math.Solve(method, 0, 1e-3, x0=x0, max_iterations=100)
         x = field.solve_linear(math.jit_compile_linear(field.laplace), y,
                                solve)
         math.assert_close(x.values, [[-1.5, -2, -1.5], [-3, -4, -3]],
                           abs_tolerance=1e-3)
Exemple #5
0
 def test_solve_linear_matrix_dirichlet(self):
     for backend in BACKENDS:
         with backend:
             y = CenteredGrid(1, extrapolation.ONE, x=3)
             x0 = CenteredGrid(0, extrapolation.ONE, x=3)
             solve = math.Solve('CG', 0, 1e-3, x0=x0, max_iterations=100)
             x_ref = field.solve_linear(field.laplace, y, solve)
             x_jit = field.solve_linear(
                 math.jit_compile_linear(field.laplace), y, solve)
             math.assert_close(x_ref.values,
                               x_jit.values, [-0.5, -1, -0.5],
                               abs_tolerance=1e-3,
                               msg=backend)
Exemple #6
0
 def test_solve_linear_matrix(self):
     for backend in BACKENDS:
         with backend:
             y = CenteredGrid(1, extrapolation.ZERO, x=3)
             x0 = CenteredGrid(0, extrapolation.ZERO, x=3)
             for method in ['CG', 'CG-adaptive', 'auto']:
                 solve = math.Solve(method,
                                    0,
                                    1e-3,
                                    x0=x0,
                                    max_iterations=100)
                 x = field.solve_linear(
                     math.jit_compile_linear(field.laplace), y, solve)
                 math.assert_close(x.values, [-1.5, -2, -1.5],
                                   abs_tolerance=1e-3,
                                   msg=backend)
Exemple #7
0
 def test_solve_linear_function_batched(self):
     y = CenteredGrid(1, extrapolation.ZERO, x=3) * (1, 2)
     x0 = CenteredGrid(0, extrapolation.ZERO, x=3)
     for method in ['CG', 'CG-adaptive', 'auto']:
         solve = math.Solve(method, 0, 1e-3, x0=x0, max_iterations=100)
         x = field.solve_linear(math.jit_compile_linear(field.laplace), y,
                                solve)
         math.assert_close(x.values,
                           math.wrap([[-1.5, -2, -1.5], [-3, -4, -3]],
                                     channel('vector'), spatial('x')),
                           abs_tolerance=1e-3)
         with math.SolveTape() as solves:
             x = field.solve_linear(math.jit_compile_linear(field.laplace),
                                    y, solve)
         math.assert_close(x.values,
                           math.wrap([[-1.5, -2, -1.5], [-3, -4, -3]],
                                     channel('vector'), spatial('x')),
                           abs_tolerance=1e-3)
         assert len(solves) == 1
         assert solves[0] == solves[solve]
         math.assert_close(solves[solve].residual.values,
                           0,
                           abs_tolerance=1e-3)
Exemple #8
0
 def solve(y, method):
     solve = math.Solve(method, 0, 1e-3, x0=x0, max_iterations=100)
     return field.solve_linear(math.jit_compile_linear(field.laplace),
                               y, solve)
Exemple #9
0
    def test_minimize(self):
        def loss(x, y):
            return math.l2_loss(x - 1) + math.l2_loss(y + 1)

        for backend in BACKENDS:
            if backend.supports(Backend.functional_gradient):
                with backend:
                    x0 = tensor([0, 0, 0], spatial('x')), tensor([-1, -1, -1],
                                                                 spatial('y'))
                    x, y = math.minimize(
                        loss, math.Solve('L-BFGS-B', 0, 1e-3, x0=x0))
                    math.assert_close(x,
                                      1,
                                      abs_tolerance=1e-3,
                                      msg=backend.name)
                    math.assert_close(y,
                                      -1,
                                      abs_tolerance=1e-3,
                                      msg=backend.name)

                    x0 = tensor([[0, 0, 0], [1, 1, 1]], batch('batch'),
                                spatial('x')), tensor(
                                    [[0, 0, 0], [-1, -1, -1]], batch('batch'),
                                    spatial('y'))
                    x, y = math.minimize(
                        loss, math.Solve('L-BFGS-B', 0, 1e-3, x0=x0))
                    math.assert_close(x,
                                      1,
                                      abs_tolerance=1e-3,
                                      msg=backend.name)
                    math.assert_close(y,
                                      -1,
                                      abs_tolerance=1e-3,
                                      msg=backend.name)

                    with math.SolveTape() as solves:
                        x, y = math.minimize(
                            loss, math.Solve('L-BFGS-B', 0, 1e-3, x0=x0))
                    math.assert_close(x,
                                      1,
                                      abs_tolerance=1e-3,
                                      msg=backend.name)
                    math.assert_close(y,
                                      -1,
                                      abs_tolerance=1e-3,
                                      msg=backend.name)
                    math.assert_close(solves[0].residual,
                                      0,
                                      abs_tolerance=1e-4)
                    assert (solves[0].iterations <= (4, 0)).all
                    assert (solves[0].function_evaluations <= (30, 1)).all

                    with math.SolveTape(
                            record_trajectories=True) as trajectories:
                        x, y = math.minimize(
                            loss, math.Solve('L-BFGS-B', 0, 1e-3, x0=x0))
                    math.assert_close(x,
                                      1,
                                      abs_tolerance=1e-3,
                                      msg=backend.name)
                    math.assert_close(y,
                                      -1,
                                      abs_tolerance=1e-3,
                                      msg=backend.name)
                    math.assert_close(trajectories[0].residual.trajectory[-1],
                                      0,
                                      abs_tolerance=1e-4)
                    assert (
                        trajectories[0].iterations == solves[0].iterations).all
                    assert trajectories[
                        0].residual.trajectory.size == trajectories[0].x[
                            0].trajectory.size
                    assert trajectories[0].residual.trajectory.size > 1
Exemple #10
0
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