Exemple #1
0
    def approximate_fraction_inside(self, other_geometry: 'Geometry') -> Tensor:
        """
        Computes the approximate overlap between the geometry and a small other geometry.
        Returns 1.0 if `other_geometry` is fully enclosed in this geometry and 0.0 if there is no overlap.
        Close to the surface of this geometry, the fraction filled is differentiable w.r.t. the location and size of `other_geometry`.
        
        To call this method on batches of geometries of same shape, pass a batched Geometry instance.
        The result tensor will match the batch shape of `other_geometry`.
        
        The result may only be accurate in special cases.
        The given geometries may be approximated as spheres or boxes using `bounding_radius()` and `bounding_half_extent()`.
        
        The default implementation of this method approximates other_geometry as a Sphere and computes the fraction using `approximate_signed_distance()`.

        Args:
          other_geometry: batched) Geometry instance
          other_geometry: Geometry: 

        Returns:
          fraction of cell volume lying inside the geometry. float tensor of shape (other_geometry.batch_shape, 1).

        """
        assert isinstance(other_geometry, Geometry)
        radius = other_geometry.bounding_radius()
        location = other_geometry.center
        distance = self.approximate_signed_distance(location)
        inside_fraction = 0.5 - distance / radius
        inside_fraction = math.clip(inside_fraction, 0, 1)
        return inside_fraction
Exemple #2
0
    def approximate_fraction_inside(self,
                                    other_geometry: 'Geometry',
                                    balance: Tensor or Number = 0.5) -> Tensor:
        """
        Computes the approximate overlap between the geometry and a small other geometry.
        Returns 1.0 if `other_geometry` is fully enclosed in this geometry and 0.0 if there is no overlap.
        Close to the surface of this geometry, the fraction filled is differentiable w.r.t. the location and size of `other_geometry`.

        To call this method on batches of geometries of same shape, pass a batched Geometry instance.
        The result tensor will match the batch shape of `other_geometry`.

        The result may only be accurate in special cases.
        The given geometries may be approximated as spheres or boxes using `bounding_radius()` and `bounding_half_extent()`.

        The default implementation of this method approximates other_geometry as a Sphere and computes the fraction using `approximate_signed_distance()`.

        Args:
            other_geometry: `Geometry` or geometry batch for which to compute the overlap with `self`.
            balance: Mid-level between 0 and 1, default 0.5.
                This value is returned when exactly half of `other_geometry` lies inside `self`.
                `0.5 < balance <= 1` makes `self` seem larger while `0 <= balance < 0.5`makes `self` seem smaller.

        Returns:
          fraction of cell volume lying inside the geometry. float tensor of shape (other_geometry.batch_shape, 1).

        """
        assert isinstance(other_geometry, Geometry)
        radius = other_geometry.bounding_radius()
        location = other_geometry.center
        distance = self.approximate_signed_distance(location)
        inside_fraction = balance - distance / radius
        inside_fraction = math.clip(inside_fraction, 0, 1)
        return inside_fraction
Exemple #3
0
def mac_cormack(field, velocity_field, dt, correction_strength=1.0):
    """
    MacCormack advection uses a forward and backward lookup to determine the first-order error of semi-Lagrangian advection.
    It then uses that error estimate to correct the field values.
    To avoid overshoots, the resulting value is bounded by the neighbouring grid cells of the backward lookup.

    :param correction_strength: the estimated error is multiplied by this factor before being applied. The case correction_strength=0 equals semi-lagrangian advection. Set lower than 1.0 to avoid oscillations.
    :param field: Field to be advected
    :param velocity_field: vector field, need not be compatible with `field`.
    :param dt: time increment
    :return: Field compatible with input field
    """
    try:
        x0 = field.points
        v = velocity_field.at(x0)
        x_bwd = x0 - v * dt
        x_fwd = x0 + v * dt
        field_semi_la = field.with_data(field.sample_at(
            x_bwd.data))  # semi-Lagrangian advection
        field_inv_semi_la = field.with_data(field_semi_la.sample_at(
            x_fwd.data))  # inverse semi-Lagrangian advection
        new_field = field_semi_la + correction_strength * 0.5 * (
            field - field_inv_semi_la)
        field_clamped = math.clip(new_field,
                                  *field.general_sample_at(
                                      x_bwd.data,
                                      'minmax'))  # Address overshoots
        return field_clamped
    except StaggeredSamplePoints:
        advected = [
            mac_cormack(component, velocity_field, dt)
            for component in field.unstack()
        ]
        return field.with_data(advected)
Exemple #4
0
def mac_cormack(field: GridType,
                velocity: Field,
                dt: float,
                correction_strength=1.0,
                integrator=euler) -> GridType:
    """
    MacCormack advection uses a forward and backward lookup to determine the first-order error of semi-Lagrangian advection.
    It then uses that error estimate to correct the field values.
    To avoid overshoots, the resulting value is bounded by the neighbouring grid cells of the backward lookup.

    Args:
        field: Field to be advected, one of `(CenteredGrid, StaggeredGrid)`
        velocity: Vector field, need not be sampled at same locations as `field`.
        dt: Time increment
        correction_strength: The estimated error is multiplied by this factor before being applied.
            The case correction_strength=0 equals semi-lagrangian advection. Set lower than 1.0 to avoid oscillations.
        integrator: ODE integrator for solving the movement.

    Returns:
        Advected field of type `type(field)`

    """
    v0 = sample(velocity, field.elements)
    points_bwd = integrator(field.elements, velocity, -dt, v0=v0)
    points_fwd = integrator(field.elements, velocity, dt, v0=v0)
    # Semi-Lagrangian advection
    field_semi_la = field.with_values(reduce_sample(field, points_bwd))
    # Inverse semi-Lagrangian advection
    field_inv_semi_la = field.with_values(
        reduce_sample(field_semi_la, points_fwd))
    # correction
    new_field = field_semi_la + correction_strength * 0.5 * (field -
                                                             field_inv_semi_la)
    # Address overshoots
    limits = field.closest_values(points_bwd)
    lower_limit = math.min(
        limits, [f'closest_{dim}' for dim in field.shape.spatial.names])
    upper_limit = math.max(
        limits, [f'closest_{dim}' for dim in field.shape.spatial.names])
    values_clamped = math.clip(new_field.values, lower_limit, upper_limit)
    return new_field.with_values(values_clamped)
Exemple #5
0
def mac_cormack(field: GridType,
                velocity: Field,
                dt: float,
                correction_strength=1.0) -> GridType:
    """
    MacCormack advection uses a forward and backward lookup to determine the first-order error of semi-Lagrangian advection.
    It then uses that error estimate to correct the field values.
    To avoid overshoots, the resulting value is bounded by the neighbouring grid cells of the backward lookup.

    Args:
      field: Field to be advected, one of `(CenteredGrid, StaggeredGrid)`
      velocity: Vector field, need not be sampled at same locations as `field`.
      dt: Time increment
      correction_strength: The estimated error is multiplied by this factor before being applied. The case correction_strength=0 equals semi-lagrangian advection. Set lower than 1.0 to avoid oscillations. (Default value = 1.0)

    Returns:
      Advected field of type `type(field)`

    """
    v = velocity.sample_in(field.elements)
    x0 = field.points
    x_bwd = x0 - v * dt
    x_fwd = x0 + v * dt
    reduce = x0.shape.non_channel.without(field.shape).names
    # Semi-Lagrangian advection
    field_semi_la = field.with_(
        values=field.sample_at(x_bwd, reduce_channels=reduce))
    # Inverse semi-Lagrangian advection
    field_inv_semi_la = field.with_(
        values=field_semi_la.sample_at(x_fwd, reduce_channels=reduce))
    # correction
    new_field = field_semi_la + correction_strength * 0.5 * (field -
                                                             field_inv_semi_la)
    # Address overshoots
    limits = field.closest_values(x_bwd, reduce_channels=reduce)
    lower_limit = math.min(
        limits, [f'closest_{dim}' for dim in field.shape.spatial.names])
    upper_limit = math.max(
        limits, [f'closest_{dim}' for dim in field.shape.spatial.names])
    values_clamped = math.clip(new_field.values, lower_limit, upper_limit)
    return new_field.with_(values=values_clamped)