Exemple #1
0
 def sample(value, domain, batch_size=None, name=None):
     assert isinstance(domain, Domain)
     if isinstance(value, Field):
         assert_same_rank(
             value.rank, domain.rank,
             'rank of value (%s) does not match domain (%s)' %
             (value.rank, domain.rank))
         if isinstance(value,
                       CenteredGrid) and value.box == domain.box and np.all(
                           value.resolution == domain.resolution):
             data = value.data
         else:
             point_field = CenteredGrid.getpoints(domain.box,
                                                  domain.resolution)
             point_field._batch_size = batch_size
             data = value.at(point_field).data
     else:  # value is constant
         if callable(value):
             x = CenteredGrid.getpoints(
                 domain.box, domain.resolution).copied_with(
                     extrapolation=Material.extrapolation_mode(
                         domain.boundaries),
                     name=name)
             value = value(x)
             return value
         components = math.staticshape(
             value)[-1] if math.ndims(value) > 0 else 1
         data = math.add(
             math.zeros((batch_size, ) + tuple(domain.resolution) +
                        (components, )), value)
     return CenteredGrid(data,
                         box=domain.box,
                         extrapolation=Material.extrapolation_mode(
                             domain.boundaries),
                         name=name)
Exemple #2
0
    def solve(self, field, domain, guess):
        assert isinstance(domain, FluidDomain)
        active_mask = domain.active_tensor(extend=1)
        fluid_mask = domain.accessible_tensor(extend=1)
        dimensions = math.staticshape(field)[1:-1]
        N = int(np.prod(dimensions))
        periodic = Material.periodic(domain.domain.boundaries)

        if math.choose_backend([field, active_mask, fluid_mask]).matches_name('SciPy'):
            A = sparse_pressure_matrix(dimensions, active_mask, fluid_mask, periodic)
        else:
            sidx, sorting = sparse_indices(dimensions, periodic)
            sval_data = sparse_values(dimensions, active_mask, fluid_mask, sorting, periodic)
            backend = math.choose_backend(field)
            sval_data = backend.cast(sval_data, field.dtype)
            A = backend.sparse_tensor(indices=sidx, values=sval_data, shape=[N, N])

        if self.autodiff:
            return sparse_cg(field, A, self.max_iterations, guess, self.accuracy, back_prop=True)
        else:
            def pressure_gradient(op, grad):
                return sparse_cg(grad, A, max_gradient_iterations, None, self.gradient_accuracy)[0]

            pressure, iteration = math.with_custom_gradient(sparse_cg,
                                                            [field, A, self.max_iterations, guess, self.accuracy],
                                                            pressure_gradient, input_index=0, output_index=0,
                                                            name_base='scg_pressure_solve')

            max_gradient_iterations = iteration if self.max_gradient_iterations == 'mirror' else self.max_gradient_iterations
            return pressure, iteration
Exemple #3
0
 def accessible(self, accessible):
     if accessible is not None:
         assert isinstance(accessible, CenteredGrid)
         assert accessible.rank == self.domain.rank
         return accessible
     else:
         return self.domain.centered_grid(1, extrapolation=Material.extrapolation_mode(self.domain.boundaries))
Exemple #4
0
    def solve(self, field, domain, guess, enable_backprop):
        assert isinstance(domain, FluidDomain)
        active_mask = domain.active_tensor(extend=1)
        fluid_mask = domain.accessible_tensor(extend=1)
        dimensions = math.staticshape(field)[1:-1]
        N = int(np.prod(dimensions))
        periodic = Material.periodic(domain.domain.boundaries)

        if math.choose_backend([field, active_mask,
                                fluid_mask]).matches_name('SciPy'):
            A = sparse_pressure_matrix(dimensions, active_mask, fluid_mask,
                                       periodic)
        else:
            sidx, sorting = sparse_indices(dimensions, periodic)
            sval_data = sparse_values(dimensions, active_mask, fluid_mask,
                                      sorting, periodic)
            backend = math.choose_backend(field)
            sval_data = backend.cast(sval_data, field.dtype)
            A = backend.sparse_tensor(indices=sidx,
                                      values=sval_data,
                                      shape=[N, N])

        div_vec = math.reshape(field, [-1, int(np.prod(field.shape[1:]))])
        if guess is not None:
            guess = math.reshape(guess, [-1, int(np.prod(field.shape[1:]))])

        def apply_A(pressure):
            return math.matmul(A, pressure)

        result_vec, iterations = conjugate_gradient(div_vec, apply_A, guess,
                                                    self.accuracy,
                                                    self.max_iterations,
                                                    enable_backprop)
        return math.reshape(result_vec, math.shape(field)), iterations
Exemple #5
0
    def accessible_tensor(self, extend=0):
        """
        Scalar channel encoding cells that are accessible, i.e. not solid, as ones and obstacles as zero.

        :param extend: Extend the grid in all directions beyond the grid size specified by the domain
        """
        pad_values = struct.map(lambda solid: int(not solid), Material.solid(self.domain.boundaries))
        if isinstance(pad_values, (list, tuple)):
            pad_values = [0] + list(pad_values) + [0]
        result = math.pad(self.accessible.data, [[0,0]] + [[extend, extend]] * self.rank + [[0,0]], constant_values=pad_values)
        return result
Exemple #6
0
 def active(self, active):
     extrapolation = _active_extrapolation(
         Material.extrapolation_mode(self.domain.boundaries))
     if active is not None:
         assert isinstance(active, CenteredGrid)
         assert active.rank == self.domain.rank
         if active.extrapolation != extrapolation:
             active = active.copied_with(extrapolation=extrapolation)
         return active
     else:
         return self.domain.centered_grid(1, extrapolation=extrapolation)
Exemple #7
0
    def solve(self, divergence, domain, guess, enable_backprop):
        assert isinstance(domain, PoissonDomain)
        fluid_mask = domain.accessible_tensor(extend=1)
        extrapolation = Material.extrapolation_mode(domain.domain.boundaries)

        def apply_A(pressure):
            pressure = CenteredGrid(pressure, extrapolation=extrapolation)
            pressure_padded = pressure.padded([[1, 1]] * pressure.rank)
            return _weighted_sliced_laplace_nd(pressure_padded.data,
                                               weights=fluid_mask)

        return conjugate_gradient(divergence,
                                  apply_A,
                                  guess,
                                  self.accuracy,
                                  self.max_iterations,
                                  back_prop=enable_backprop)
Exemple #8
0
def poisson_solve(input_field, poisson_domain, solver=None, guess=None, gradient='implicit'):
    """
    Solves the Poisson equation Δp = input_field for p.

    :param gradient: one of ('implicit', 'autodiff', 'inverse')
        If 'autodiff', use the built-in autodiff for backpropagation.
        The intermediate results of each loop iteration will be permanently stored if backpropagation is used.
        If 'implicit', computes a forward pressure solve in reverse accumulation backpropagation.
        This requires less memory but is only accurate if the solution is fully converged.
    :param input_field: CenteredGrid
    :param poisson_domain: PoissonDomain instance
    :param solver: PoissonSolver to use, None for default
    :param guess: CenteredGrid with same size and resolution as input_field
    :return: p as CenteredGrid, iteration count as int or None if not available
    :rtype: CenteredGrid, int
    """
    assert isinstance(input_field, CenteredGrid)
    if guess is not None:
        assert isinstance(guess, CenteredGrid)
        assert guess.compatible(input_field)
        guess = guess.data
    if isinstance(poisson_domain, Domain):
        poisson_domain = PoissonDomain(poisson_domain)
    if solver is None:
        solver = _choose_solver(input_field.resolution, math.choose_backend([input_field.data, poisson_domain.active.data, poisson_domain.accessible.data]))
    if not struct.any(Material.open(poisson_domain.domain.boundaries)):  # has no open boundary
        input_field = input_field - math.mean(input_field.data, axis=tuple(range(1, 1 + input_field.rank)), keepdims=True)  # Subtract mean divergence

    assert gradient in ('autodiff', 'implicit', 'inverse')
    if gradient == 'autodiff':
        pressure, iteration = solver.solve(input_field.data, poisson_domain, guess, enable_backprop=True)
    else:
        if gradient == 'implicit':
            def poisson_gradient(_op, grad):
                return poisson_solve(CenteredGrid.sample(grad, poisson_domain.domain), poisson_domain, solver, None, gradient=gradient)[0].data
        else:  # gradient = 'inverse'
            def poisson_gradient(_op, grad):
                return CenteredGrid.sample(grad, poisson_domain.domain).laplace(physical_units=False).data
        pressure, iteration = math.with_custom_gradient(solver.solve, [input_field.data, poisson_domain, guess, False], poisson_gradient, input_index=0, output_index=0, name_base='poisson_solve')

    pressure = CenteredGrid(pressure, input_field.box, extrapolation=input_field.extrapolation, name='pressure')
    return pressure, iteration
Exemple #9
0
def solve_pressure_forward(divergence,
                           fluid_mask,
                           max_iterations,
                           guess,
                           accuracy,
                           domain,
                           back_prop=False):
    from phi.physics.material import Material
    extrapolation = Material.extrapolation_mode(domain.domain.boundaries)

    def apply_A(pressure):
        pressure = CenteredGrid(pressure, extrapolation=extrapolation)
        pressure_padded = pressure.padded([[1, 1]] * pressure.rank)
        return _weighted_sliced_laplace_nd(pressure_padded.data,
                                           weights=fluid_mask)

    return conjugate_gradient(divergence,
                              apply_A,
                              guess,
                              accuracy,
                              max_iterations,
                              back_prop=back_prop)
Exemple #10
0
 def sample(value, domain, batch_size=None):
     assert isinstance(domain, Domain)
     if isinstance(value, Field):
         assert_same_rank(
             value.rank, domain.rank,
             'rank of value (%s) does not match domain (%s)' %
             (value.rank, domain.rank))
         if isinstance(value,
                       CenteredGrid) and value.box == domain.box and np.all(
                           value.resolution == domain.resolution):
             data = value.data
         else:
             data = value.sample_at(
                 CenteredGrid.getpoints(domain.box, domain.resolution).data)
     else:  # value is constant
         components = math.staticshape(
             value)[-1] if math.ndims(value) > 0 else 1
         data = math.zeros((batch_size, ) + tuple(domain.resolution) +
                           (components, )) + value
     return CenteredGrid(data,
                         box=domain.box,
                         extrapolation=Material.extrapolation_mode(
                             domain.boundaries))
Exemple #11
0
    def solve(self, field, domain, guess, enable_backprop):
        assert isinstance(domain, FluidDomain)
        dimensions = list(field.shape[1:-1])
        A = sparse_pressure_matrix(dimensions, domain.active_tensor(extend=1),
                                   domain.accessible_tensor(extend=1),
                                   Material.periodic(domain.domain.boundaries))

        def np_solve_p(div):
            div_vec = div.reshape([-1, A.shape[0]])
            pressure = [
                scipy.sparse.linalg.spsolve(A, div_vec[i, ...])
                for i in range(div_vec.shape[0])
            ]
            return np.array(pressure).reshape(div.shape).astype(np.float32)

        def np_solve_p_gradient(op, grad_in):
            return math.py_func(np_solve_p, [grad_in], np.float32, field.shape)

        pressure = math.py_func(np_solve_p, [field],
                                np.float32,
                                field.shape,
                                grad=np_solve_p_gradient)
        return pressure, None
Exemple #12
0
 def apply_A(pressure):
     from phi.physics.material import Material
     mode = 'replicate' if Material.solid(domain.domain.boundaries) else 'constant'
     padded = math.pad(pressure, [[0,0]] + [[1,1]]*(math.ndims(pressure)-2) + [[0,0]], mode=mode)
     return _weighted_sliced_laplace_nd(padded, weights=fluid_mask)