Esempio n. 1
0
def create_surface_mask(liquid_mask):
    """
Computes inner contours of the liquid_mask.
A cell i is flagged 1 if liquid_mask[i] = 1 and it has a non-liquid neighbour.
    :param liquid_mask: binary tensor
    :return: tensor
    """
    # When we create inner contour, we don't want the fluid-wall boundaries to show up as surface, so we should pad with symmetric edge values.
    mask = math.pad(liquid_mask, [[0, 0]] +
                    [[1, 1]] * math.spatial_rank(liquid_mask) + [[0, 0]],
                    "constant")
    dims = range(math.spatial_rank(mask))
    bcs = math.zeros_like(liquid_mask)

    # Move in every possible direction to assure corners are properly set.
    directions = np.array(
        list(itertools.product(*np.tile((-1, 0, 1), (len(dims), 1)))))

    for d in directions:
        d_slice = tuple([(slice(2, None) if d[i] == -1 else
                          slice(0, -2) if d[i] == 1 else slice(1, -1))
                         for i in dims])
        center_slice = tuple([slice(1, -1) for _ in dims])

        # Create inner contour of particles
        bc_d = math.maximum(mask[(slice(None),) + d_slice + (slice(None),)],
                            mask[(slice(None),) + center_slice + (slice(None),)]) - \
            mask[(slice(None),) + d_slice + (slice(None),)]
        bcs = math.maximum(bcs, bc_d)
    return bcs
Esempio n. 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
Esempio n. 3
0
 def _shift_resample(self, resolution, box, threshold=1e-5, max_padding=20):
     lower = math.to_int(
         math.ceil(
             math.maximum(0, self.box.lower - box.lower) / self.dx -
             threshold))
     upper = math.to_int(
         math.ceil(
             math.maximum(0, box.upper - self.box.upper) / self.dx -
             threshold))
     total_padding = math.sum(lower) + math.sum(upper)
     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(box.lower) * grid_resolution
     data = math.sample_subgrid(grid_values, origin_in_local, resolution)
     return data
Esempio n. 4
0
    def _stagger_sample(self, box, resolution):
        """
    Samples this field on a staggered grid.
    In addition to sampling, extrapolates the field using an occupancy mask generated from the points.
        :param box: physical dimensions of the grid
        :param resolution: grid resolution
        :return: StaggeredGrid
        """
        resolution = np.array(resolution)
        valid_indices = math.to_int(math.floor(self.sample_points))
        valid_indices = math.minimum(math.maximum(0, valid_indices), resolution - 1)
        # Correct format for math.scatter
        valid_indices = batch_indices(valid_indices)

        active_mask = math.scatter(self.sample_points, valid_indices, 1, math.concat([[valid_indices.shape[0]], resolution, [1]], axis=-1), duplicates_handling='any')

        mask = math.pad(active_mask, [[0, 0]] + [[1, 1]] * self.rank + [[0, 0]], "constant")

        if isinstance(self.data, (int, float, np.ndarray)):
            values = math.zeros_like(self.sample_points) + self.data
        else:
            values = self.data
        
        result = []
        ones_1d = math.unstack(math.ones_like(values), axis=-1)[0]
        staggered_shape = [i + 1 for i in resolution]
        dx = box.size / resolution

        dims = range(len(resolution))
        for d in dims: 
            staggered_offset = math.stack([(0.5 * dx[i] * ones_1d if i == d else 0.0 * ones_1d) for i in dims], axis=-1)

            indices = math.to_int(math.floor(self.sample_points + staggered_offset))
            
            valid_indices = math.maximum(0, math.minimum(indices, resolution))
            valid_indices = batch_indices(valid_indices)

            values_d = math.expand_dims(math.unstack(values, axis=-1)[d], axis=-1)
            result.append(math.scatter(self.sample_points, valid_indices, values_d, [indices.shape[0]] + staggered_shape + [1], duplicates_handling=self.mode))

            d_slice = tuple([(slice(0, -2) if i == d else slice(1,-1)) for i in dims])
            u_slice = tuple([(slice(2, None) if i == d else slice(1,-1)) for i in dims])
            active_mask = math.minimum(mask[(slice(None),) + d_slice + (slice(None),)], active_mask)
            active_mask = math.minimum(mask[(slice(None),) + u_slice + (slice(None),)], active_mask)
        
        staggered_tensor_prep = unstack_staggered_tensor(math.concat(result, axis=-1))
        grid_values = StaggeredGrid(staggered_tensor_prep)
        # Fix values at boundary of liquids (using StaggeredGrid these might not receive a value, so we replace it with a value inside the liquid)
        grid_values, _ = extrapolate(grid_values, active_mask, voxel_distance=2)
        return grid_values
Esempio n. 5
0
 def cell_index(self, global_position):
     local_position = self.box.global_to_local(
         global_position) * self.resolution
     position = math.to_int(local_position - 0.5)
     position = math.maximum(0, position)
     position = math.minimum(position, self.resolution - 1)
     return position
Esempio n. 6
0
 def _grid_sample(self, box, resolution):
     """
 Samples this field on a regular grid.
     :param box: physical dimensions of the grid
     :param resolution: grid resolution
     :return: CenteredGrid
     """
     sample_indices_nd = math.to_int(
         math.round(box.global_to_local(self.sample_points) * resolution))
     sample_indices_nd = math.minimum(
         math.maximum(0, sample_indices_nd), resolution - 1
     )  # Snap outside points to edges, otherwise scatter raises an error
     # Correct format for math.scatter
     valid_indices = _batch_indices(sample_indices_nd)
     shape = (math.shape(
         self.data)[0], ) + tuple(resolution) + (self.data.shape[-1], )
     scattered = math.scatter(self.sample_points,
                              valid_indices,
                              self.data,
                              shape,
                              duplicates_handling=self.mode)
     return CenteredGrid(data=scattered,
                         box=box,
                         extrapolation='constant',
                         name=self.name + '_centered')
Esempio n. 7
0
 def _grid_sample(self, box, resolution):
     """
 Samples this field on a regular grid.
     :param box: physical dimensions of the grid
     :param resolution: grid resolution
     :return: CenteredGrid
     """
     valid_indices = math.to_int(math.floor(self.sample_points))
     valid_indices = math.minimum(math.maximum(0, valid_indices), resolution - 1)
     # Correct format for math.scatter
     valid_indices = batch_indices(valid_indices)
     scattered = math.scatter(self.sample_points, valid_indices, self.data, math.concat([[valid_indices.shape[0]], resolution, [1]], axis=-1), duplicates_handling=self.mode)
     return CenteredGrid(data=scattered, box=box, extrapolation='constant', name=self.name+'_centered')
Esempio n. 8
0
 def test_precision_16(self):
     try:
         math.set_precision(16)
         fluid = Fluid(Domain([16, 16]), density=math.maximum(0, Noise()))
         self.assertEqual(fluid.density.data.dtype, numpy.float16)
         self.assertEqual(fluid.velocity.unstack()[0].data.dtype,
                          numpy.float16)
         fluid = IncompressibleFlow().step(fluid, dt=1.0)
         self.assertEqual(fluid.density.data.dtype, numpy.float16)
         self.assertEqual(fluid.velocity.unstack()[0].data.dtype,
                          numpy.float16)
     finally:
         math.set_precision(32)  # Reset environment
Esempio n. 9
0
def maximum(f1: Field or Geometry or float, f2: Field or Geometry or float):
    """
    Element-wise maximum.
    One of the given fields needs to be an instance of `SampledField` and the the result will be sampled at the corresponding points.
    If both are `SampledFields` but have different points, `f1` takes priority.

    Args:
        f1: `Field` or `Geometry` or constant.
        f2: `Field` or `Geometry` or constant.

    Returns:
        `SampledField`
    """
    f1, f2 = _auto_resample(f1, f2)
    return f1.with_values(math.maximum(f1.values, f2.values))
Esempio n. 10
0
    def approximate_signed_distance(self, location):
        """
Computes the exact distance from location to the closest point on the sphere.
Very close to the sphere center, the distance takes a constant value.
        :param location: float tensor of shape (batch_size, ..., rank)
        :return: float tensor of shape (*location.shape[:-1], 1).
        """
        center = math.batch_align(self.center, 1, location)
        radius = math.batch_align(self.radius, 0, location)
        distance_squared = math.sum((location - center)**2,
                                    axis=-1,
                                    keepdims=True)
        distance_squared = math.maximum(
            distance_squared,
            radius * 1e-2)  # Prevent infinite gradient at sphere center
        distance = math.sqrt(distance_squared)
        return distance - radius
Esempio n. 11
0
    def approximate_signed_distance(self, location):
        """
        Computes the exact distance from location to the closest point on the sphere.
        Very close to the sphere center, the distance takes a constant value.

        Args:
          location: float tensor of shape (batch_size, ..., rank)

        Returns:
          float tensor of shape (*location.shape[:-1], 1).

        """
        distance_squared = math.vec_squared(location - self.center)
        distance_squared = math.maximum(
            distance_squared,
            self.radius * 1e-2)  # Prevent infinite gradient at sphere center
        distance = math.sqrt(distance_squared)
        return distance - self.radius
Esempio n. 12
0
def _required_paddings_transposed(box, dx, target):
    lower = math.to_int(
        math.ceil(math.maximum(0, box.lower - target.lower) / dx))
    upper = math.to_int(
        math.ceil(math.maximum(0, target.upper - box.upper) / dx))
    return [lower, upper]
Esempio n. 13
0
 def test_maximum(self):
     v = math.ones(x=4, y=3, vector=2)
     math.assert_close(math.maximum(0, v), 1)
     math.assert_close(math.maximum(0, -v), 0)
Esempio n. 14
0
 def soft_sqrt(self):
     return StaggeredGrid(math.sqrt(math.maximum(self.staggered, 1e-20)))
Esempio n. 15
0
 def test_maximum(self):
     v = math.ones(spatial(x=4, y=3) & channel(vector=2))
     math.assert_close(math.maximum(0, v), 1)
     math.assert_close(math.maximum(0, -v), 0)