Beispiel #1
0
def implicit(
    field: FieldType,
    diffusivity: float or math.Tensor or Field,
    dt: float or math.Tensor,
    order: int = 1,
    solve_params: math.Solve = math.LinearSolve(bake='sparse')
) -> FieldType:
    """
    Diffusion by solving a linear system of equations.

    Args:
        order: Order of method, 1=first order. This translates to `substeps` for the explicit sharpening.
        field:
        diffusivity: Diffusion per time. `diffusion_amount = diffusivity * dt`
        dt: Time interval. `diffusion_amount = diffusivity * dt`
        solve_params:

    Returns:
        Diffused field of same type as `field`.
    """
    def sharpen(x):
        return explicit(x, diffusivity, -dt, substeps=order)

    converged, diffused, iterations = solve(sharpen,
                                            field,
                                            field,
                                            solve_params=solve_params)
    if math.all_available(converged):
        assert converged, f"Implicit diffusion solve did not converge after {iterations} iterations. Last estimate: {diffused.values}"
    return diffused
Beispiel #2
0
 def _shift_resample(self,
                     resolution: Shape,
                     bounds: Box,
                     threshold=1e-5,
                     max_padding=20):
     assert math.all_available(
         bounds.lower, bounds.upper
     ), "Shift resampling requires 'bounds' to be available."
     lower = math.to_int32(
         math.ceil(
             math.maximum(0, self.box.lower - bounds.lower) / self.dx -
             threshold))
     upper = math.to_int32(
         math.ceil(
             math.maximum(0, bounds.upper - self.box.upper) / self.dx -
             threshold))
     total_padding = (math.sum(lower) + math.sum(upper)).numpy()
     if total_padding > max_padding:
         return NotImplemented
     elif total_padding > 0:
         from phi.field import pad
         padded = pad(
             self, {
                 dim: (int(lower[i]), int(upper[i]))
                 for i, dim in enumerate(self.shape.spatial.names)
             })
         grid_box, grid_resolution, grid_values = padded.box, padded.resolution, padded.values
     else:
         grid_box, grid_resolution, grid_values = self.box, self.resolution, self.values
     origin_in_local = grid_box.global_to_local(
         bounds.lower) * grid_resolution
     data = math.sample_subgrid(grid_values, origin_in_local, resolution)
     return data
Beispiel #3
0
 def __eq__(self, other):
     if not type(self) == type(other):
         return False
     if not (self._bounds == other._bounds
             and self._resolution == other._resolution
             and self._extrapolation == other._extrapolation):
         return False
     if self.values is None:
         return other.values is None
     if other.values is None:
         return False
     if not math.all_available(self.values) or not math.all_available(
             other.values):  # tracers involved
         if math.all_available(self.values) != math.all_available(
                 other.values):
             return False
         else:  # both tracers
             return self.values.shape == other.values.shape
     return bool((self.values == other.values).all)
Beispiel #4
0
 def __eq__(self, other):
     if not type(self) == type(other):
         return False
     # Check everything but __variable_attrs__ (values): elements type, extrapolation, add_overlapping
     if type(self.elements) is not type(other.elements):
         return False
     if self.extrapolation != other.extrapolation:
         return False
     if self._add_overlapping != other._add_overlapping:
         return False
     if self.values is None:
         return other.values is None
     if other.values is None:
         return False
     if not math.all_available(self.values) or not math.all_available(other.values):  # tracers involved
         if math.all_available(self.values) != math.all_available(other.values):
             return False
         else:  # both tracers
             return self.values.shape == other.values.shape
     return bool((self.values == other.values).all)
Beispiel #5
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