Module phi.geom

Differentiable geometry package.

Classes:

See the phi.geom module documentation at https://tum-pbs.github.io/PhiFlow/Geometry.html

Expand source code
"""
Differentiable geometry package.

Classes:

* `Geometry` (base type)
* `Box`
* `Sphere`

See the `phi.geom` module documentation at https://tum-pbs.github.io/PhiFlow/Geometry.html
"""

from ._geom import Geometry, Point, assert_same_rank
from ._union import union
from ._box import Box, GridCell, BaseBox
from ._sphere import Sphere
from ._stack import stack
from ._geom_math import concat, invert

__all__ = [key for key in globals().keys() if not key.startswith('_')]

Functions

def assert_same_rank(rank1, rank2, error_message)

Tests that two objects have the same spatial rank. Objects can be of types: int, None (no check), Geometry, Shape, Tensor

Expand source code
def assert_same_rank(rank1, rank2, error_message):
    """ Tests that two objects have the same spatial rank. Objects can be of types: `int`, `None` (no check), `Geometry`, `Shape`, `Tensor` """
    rank1_, rank2_ = _rank(rank1), _rank(rank2)
    if rank1_ is not None and rank2_ is not None:
        assert rank1_ == rank2_, 'Ranks do not match: %s and %s. %s' % (rank1_, rank2_, error_message)
def concat(geometries: tuple, dim: phi.math._shape.Shape, sizes: tuple = None)

Concatenates multiple geometries of the same type.

Args

geometries
sequence of Geometry objects of the same type
sizes
implicit
dim
dimension to concatenate

Returns

New Geometry object

Expand source code
def concat(geometries: tuple or list,
           dim: Shape,
           sizes: tuple or list or None = None):
    """
    Concatenates multiple geometries of the same type.

    Args:
        geometries: sequence of `phi.geom.Geometry` objects of the same type
        sizes: implicit
        dim: dimension to concatenate

    Returns:
        New `phi.geom.Geometry` object
    """
    if all(isinstance(g, type(geometries[0])) for g in geometries):
        characteristics = [{a: getattr(g, a) for a in variable_attributes(g)} for g in geometries]
        new_attributes = {}
        for c in characteristics[0].keys():
            if any([item[c].shape.volume > 1 for item in characteristics]) or any([not math.close(item[c], characteristics[0][c]) for item in characteristics]):
                for item, size in zip(characteristics, sizes):
                    item[c] = math.expand(item[c], dim.with_size(size))
                concatenated = math.concat([item[c] for item in characteristics], dim)
                new_attributes[c] = concatenated
        return copy_with(geometries[0], **new_attributes)
    else:
        raise NotImplementedError()
def invert(geometry: phi.geom._geom.Geometry)

Swaps inside and outside.

Args

geometry
Geometry to swap

Returns

New Geometry object with same surface but swapped normals

Expand source code
def invert(geometry: Geometry):
    """
    Swaps inside and outside.

    Args:
        geometry: `phi.geom.Geometry` to swap

    Returns:
        New `phi.geom.Geometry` object with same surface but swapped normals
    """
    return ~geometry
def stack(geometries: List[phi.geom._geom.Geometry], dim: phi.math._shape.Shape)

Stacks geometries along dim. The size of dim is ignored.

Expand source code
def stack(geometries: List[Geometry], dim: Shape):
    """ Stacks `geometries` along `dim`. The size of `dim` is ignored. """
    if all(type(g) == type(geometries[0]) for g in geometries):
        attrs = variable_attributes(geometries[0])
        new_attributes = {a: math.stack([getattr(g, a) for g in geometries], dim) for a in attrs}
        return copy_with(geometries[0], **new_attributes)
    return GeometryStack(geometries, dim)
def union(*geometries) ‑> phi.geom._geom.Geometry

Union of the given geometries. A point lies inside the union if it lies within at least one of the geometries.

Args

geometries
arbitrary geometries with same spatial dims. Arbitrary batch dims are allowed.
*geometries
 

Returns

union Geometry

Expand source code
def union(*geometries) -> Geometry:
    """
    Union of the given geometries.
    A point lies inside the union if it lies within at least one of the geometries.

    Args:
      geometries: arbitrary geometries with same spatial dims. Arbitrary batch dims are allowed.
      *geometries: 

    Returns:
      union Geometry

    """
    if len(geometries) == 1 and isinstance(geometries[0], (tuple, list)):
        geometries = geometries[0]
    if len(geometries) == 0:
        return NO_GEOMETRY
    elif len(geometries) == 1:
        return geometries[0]
    elif all(type(g) == type(geometries[0]) for g in geometries):
        attrs = variable_attributes(geometries[0])
        values = {a: math.stack([getattr(g, a) for g in geometries], math.instance('union')) for a in attrs}
        return copy_with(geometries[0], **values)
    else:
        base_geometries = ()
        for geometry in geometries:
            base_geometries += geometry.geometries if isinstance(geometry, Union) else (geometry,)
        return Union(base_geometries)

Classes

class BaseBox

Abstract base type for box-like geometries.

Expand source code
class BaseBox(Geometry):  # not a Subwoofer
    """
    Abstract base type for box-like geometries.
    """

    def unstack(self, dimension):
        raise NotImplementedError()

    def __eq__(self, other):
        raise NotImplementedError()

    def __hash__(self):
        raise NotImplementedError()

    def __ne__(self, other):
        return not self == other

    @property
    def shape(self):
        raise NotImplementedError()

    @property
    def center(self) -> Tensor:
        raise NotImplementedError()

    def shifted(self, delta) -> 'BaseBox':
        raise NotImplementedError()

    @property
    def size(self) -> Tensor:
        raise NotImplementedError(self)

    @property
    def half_size(self) -> Tensor:
        raise NotImplementedError(self)

    @property
    def lower(self) -> Tensor:
        raise NotImplementedError(self)

    @property
    def upper(self) -> Tensor:
        raise NotImplementedError(self)

    @property
    def volume(self) -> Tensor:
        return math.prod(self.size, 'vector')

    def bounding_radius(self):
        return math.max(self.size, 'vector') * 1.414214

    def bounding_half_extent(self):
        return self.size * 0.5

    def global_to_local(self, global_position: Tensor) -> Tensor:
        if math.close(self.lower, 0):
            return global_position / self.size
        else:
            return (global_position - self.lower) / self.size

    def local_to_global(self, local_position):
        return local_position * self.size + self.lower

    def lies_inside(self, location):
        bool_inside = (location >= self.lower) & (location <= self.upper)
        bool_inside = math.all(bool_inside, 'vector')
        bool_inside = math.any(bool_inside, self.shape.instance)  # union for instance dimensions
        return bool_inside

    def approximate_signed_distance(self, location):
        """
        Computes the signed L-infinity norm (manhattan distance) from the location to the nearest side of the box.
        For an outside location `l` with the closest surface point `s`, the distance is `max(abs(l - s))`.
        For inside locations it is `-max(abs(l - s))`.

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

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

        """
        center = 0.5 * (self.lower + self.upper)
        extent = self.upper - self.lower
        distance = math.abs(location - center) - extent * 0.5
        distance = math.max(distance, 'vector')
        distance = math.min(distance, self.shape.instance)  # union for instance dimensions
        return distance

    def push(self, positions: Tensor, outward: bool = True, shift_amount: float = 0) -> Tensor:
        loc_to_center = positions - self.center
        sgn_dist_from_surface = math.abs(loc_to_center) - self.half_size
        if outward:
            # --- get negative distances (particles are inside) towards the nearest boundary and add shift_amount ---
            distances_of_interest = (sgn_dist_from_surface == math.max(sgn_dist_from_surface, 'vector')) & (sgn_dist_from_surface < 0)
            shift = distances_of_interest * (sgn_dist_from_surface - shift_amount)
        else:
            shift = (sgn_dist_from_surface + shift_amount) * (sgn_dist_from_surface > 0)  # get positive distances (particles are outside) and add shift_amount
            shift = math.where(math.abs(shift) > math.abs(loc_to_center), math.abs(loc_to_center), shift)  # ensure inward shift ends at center
        return positions + math.where(loc_to_center < 0, 1, -1) * shift

    def project(self, *dimensions: str):
        """ Project this box into a lower-dimensional space. """
        indices = self.shape.spatial.index(dimensions)
        return Box(self.lower[indices], self.upper[indices])

    def corner_representation(self) -> 'Box':
        return Box(self.lower, self.upper)

    def center_representation(self) -> 'Cuboid':
        return Cuboid(self.center, self.half_size)

    def contains(self, other: 'BaseBox'):
        """ Tests if the other box lies fully inside this box. """
        return np.all(other.lower >= self.lower) and np.all(other.upper <= self.upper)

    def rotated(self, angle):
        return rotate(self, angle)

Ancestors

  • phi.geom._geom.Geometry

Subclasses

  • phi.geom._box.Box
  • phi.geom._box.Cuboid
  • phi.geom._box.GridCell

Instance variables

var center : phi.math._tensors.Tensor

Center location in single channel dimension, ordered according to GLOBAL_AXIS_ORDER

Expand source code
@property
def center(self) -> Tensor:
    raise NotImplementedError()
var half_size : phi.math._tensors.Tensor
Expand source code
@property
def half_size(self) -> Tensor:
    raise NotImplementedError(self)
var lower : phi.math._tensors.Tensor
Expand source code
@property
def lower(self) -> Tensor:
    raise NotImplementedError(self)
var shape

Specifies the number of copies of the geometry as batch and spatial dimensions.

Expand source code
@property
def shape(self):
    raise NotImplementedError()
var size : phi.math._tensors.Tensor
Expand source code
@property
def size(self) -> Tensor:
    raise NotImplementedError(self)
var upper : phi.math._tensors.Tensor
Expand source code
@property
def upper(self) -> Tensor:
    raise NotImplementedError(self)
var volume : phi.math._tensors.Tensor

Volume of the geometry as Tensor. The result retains all batch dimensions while instance dimensions are summed over.

Expand source code
@property
def volume(self) -> Tensor:
    return math.prod(self.size, 'vector')

Methods

def approximate_signed_distance(self, location)

Computes the signed L-infinity norm (manhattan distance) from the location to the nearest side of the box. For an outside location l with the closest surface point s, the distance is max(abs(l - s)). For inside locations it is -max(abs(l - s)).

Args

location
float tensor of shape (batch_size, …, rank)

Returns

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

Expand source code
def approximate_signed_distance(self, location):
    """
    Computes the signed L-infinity norm (manhattan distance) from the location to the nearest side of the box.
    For an outside location `l` with the closest surface point `s`, the distance is `max(abs(l - s))`.
    For inside locations it is `-max(abs(l - s))`.

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

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

    """
    center = 0.5 * (self.lower + self.upper)
    extent = self.upper - self.lower
    distance = math.abs(location - center) - extent * 0.5
    distance = math.max(distance, 'vector')
    distance = math.min(distance, self.shape.instance)  # union for instance dimensions
    return distance
def bounding_half_extent(self)

The bounding half-extent sets a limit on the outer-most point for each coordinate axis. Each component is non-negative.

Let the bounding half-extent have value e in dimension d (extent[...,d] = e). Then, no point of the geometry lies further away from its center point than e along d (in both axis directions).

:return: float vector

Args:

Returns:

Expand source code
def bounding_half_extent(self):
    return self.size * 0.5
def bounding_radius(self)

Returns the radius of a Sphere object that fully encloses this geometry. The sphere is centered at the center of this geometry.

:return: radius of type float

Args:

Returns:

Expand source code
def bounding_radius(self):
    return math.max(self.size, 'vector') * 1.414214
def center_representation(self) ‑> phi.geom._box.Cuboid
Expand source code
def center_representation(self) -> 'Cuboid':
    return Cuboid(self.center, self.half_size)
def contains(self, other: BaseBox)

Tests if the other box lies fully inside this box.

Expand source code
def contains(self, other: 'BaseBox'):
    """ Tests if the other box lies fully inside this box. """
    return np.all(other.lower >= self.lower) and np.all(other.upper <= self.upper)
def corner_representation(self) ‑> phi.geom._box.Box
Expand source code
def corner_representation(self) -> 'Box':
    return Box(self.lower, self.upper)
def global_to_local(self, global_position: phi.math._tensors.Tensor) ‑> phi.math._tensors.Tensor
Expand source code
def global_to_local(self, global_position: Tensor) -> Tensor:
    if math.close(self.lower, 0):
        return global_position / self.size
    else:
        return (global_position - self.lower) / self.size
def lies_inside(self, location)

Tests whether the given location lies inside or outside of the geometry. Locations on the surface count as inside.

When dealing with unions or collections of geometries (instance dimensions), a point lies inside the geometry if it lies inside any instance.

Args

location
float tensor of shape (batch_size, …, rank)

Returns

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

Expand source code
def lies_inside(self, location):
    bool_inside = (location >= self.lower) & (location <= self.upper)
    bool_inside = math.all(bool_inside, 'vector')
    bool_inside = math.any(bool_inside, self.shape.instance)  # union for instance dimensions
    return bool_inside
def local_to_global(self, local_position)
Expand source code
def local_to_global(self, local_position):
    return local_position * self.size + self.lower
def project(self, *dimensions: str)

Project this box into a lower-dimensional space.

Expand source code
def project(self, *dimensions: str):
    """ Project this box into a lower-dimensional space. """
    indices = self.shape.spatial.index(dimensions)
    return Box(self.lower[indices], self.upper[indices])
def push(self, positions: phi.math._tensors.Tensor, outward: bool = True, shift_amount: float = 0) ‑> phi.math._tensors.Tensor

Shifts positions either into or out of geometry.

Args

positions
Tensor holding positions to shift
outward
Flag for indicating inward (False) or outward (True) shift
shift_amount
Minimum distance between positions and box boundaries after shifting

Returns

Tensor holding shifted positions

Expand source code
def push(self, positions: Tensor, outward: bool = True, shift_amount: float = 0) -> Tensor:
    loc_to_center = positions - self.center
    sgn_dist_from_surface = math.abs(loc_to_center) - self.half_size
    if outward:
        # --- get negative distances (particles are inside) towards the nearest boundary and add shift_amount ---
        distances_of_interest = (sgn_dist_from_surface == math.max(sgn_dist_from_surface, 'vector')) & (sgn_dist_from_surface < 0)
        shift = distances_of_interest * (sgn_dist_from_surface - shift_amount)
    else:
        shift = (sgn_dist_from_surface + shift_amount) * (sgn_dist_from_surface > 0)  # get positive distances (particles are outside) and add shift_amount
        shift = math.where(math.abs(shift) > math.abs(loc_to_center), math.abs(loc_to_center), shift)  # ensure inward shift ends at center
    return positions + math.where(loc_to_center < 0, 1, -1) * shift
def rotated(self, angle)

Returns a rotated version of this geometry. The geometry is rotated about its center point.

Args

angle
scalar (2d) or vector (3D+) representing delta angle

Returns

Geometry
rotated geometry
Expand source code
def rotated(self, angle):
    return rotate(self, angle)
def shifted(self, delta) ‑> phi.geom._box.BaseBox

Returns a translated version of this geometry.

Args

delta
direction vector
delta
Tensor:

Returns

Geometry
shifted geometry
Expand source code
def shifted(self, delta) -> 'BaseBox':
    raise NotImplementedError()
def unstack(self, dimension)

Unstacks this Geometry along the given dimension. The shapes of the returned geometries are reduced by dimension.

Args

dimension
dimension along which to unstack

Returns

geometries
tuple of length equal to geometry.shape.get_size(dimension)
Expand source code
def unstack(self, dimension):
    raise NotImplementedError()
class Box (lower: phi.math._tensors.Tensor, upper: phi.math._tensors.Tensor)

Simple cuboid defined by location of lower and upper corner in physical space.

In addition to the regular constructor Box(lower, upper), Box supports construction via slicing, Box[slice1, slice2,…] Each slice marks the lower and upper edge of the box along one dimension. Start and end can be left blank (None) to set the corner point to infinity (upper=None) or -infinity (lower=None). The parameter slice.step has no effect.

Examples:

Box[0:1, 0:1]  # creates a two-dimensional unit box.
Box[:, 0:1]  # creates an infinite-height Box from x=0 to x=1.

Args

lower
physical location of lower corner
upper
physical location of upper corner
Expand source code
class Box(BaseBox, metaclass=BoxType):
    """
    Simple cuboid defined by location of lower and upper corner in physical space.

    In addition to the regular constructor Box(lower, upper), Box supports construction via slicing, `Box[slice1, slice2,...]`
    Each slice marks the lower and upper edge of the box along one dimension.
    Start and end can be left blank (None) to set the corner point to infinity (upper=None) or -infinity (lower=None).
    The parameter slice.step has no effect.

    **Examples**:

        Box[0:1, 0:1]  # creates a two-dimensional unit box.
        Box[:, 0:1]  # creates an infinite-height Box from x=0 to x=1.
    """

    def __init__(self, lower: Tensor or float or int, upper: Tensor or float or int):
        """
        Args:
          lower: physical location of lower corner
          upper: physical location of upper corner
        """
        self._lower = wrap(lower)
        self._upper = wrap(upper)

    def unstack(self, dimension):
        size = combined_dim(self._lower.shape.get_size(dimension), self._upper.shape.get_size(dimension))
        lowers = self._lower.dimension(dimension).unstack(size)
        uppers = self._upper.dimension(dimension).unstack(size)
        return tuple(Box(lo, up) for lo, up in zip(lowers, uppers))

    def __eq__(self, other):
        return isinstance(other, BaseBox)\
               and set(self.shape) == set(other.shape)\
               and math.close(self._lower, other.lower)\
               and math.close(self._upper, other.upper)

    def __hash__(self):
        return hash(self._upper)

    def __variable_attrs__(self):
        return '_lower', '_upper'

    @property
    def shape(self):
        return _fill_spatial_with_singleton(self._lower.shape & self._upper.shape).non_channel

    @property
    def lower(self):
        return self._lower

    @property
    def upper(self):
        return self._upper

    @property
    def size(self):
        return self.upper - self.lower

    @struct.derived()
    def center(self):
        return 0.5 * (self.lower + self.upper)

    @struct.derived()
    def half_size(self):
        return self.size * 0.5

    def shifted(self, delta):
        return Box(self.lower + delta, self.upper + delta)

    def __repr__(self):
        if self.shape.non_channel.volume == 1:
            return 'Box[%s at %s]' % ('x'.join([str(x) for x in self.size.numpy().flatten()]), ','.join([str(x) for x in self.lower.numpy().flatten()]))
        else:
            return 'Box[shape=%s]' % self._shape

Ancestors

  • phi.geom._box.BaseBox
  • phi.geom._geom.Geometry

Instance variables

var center
Expand source code
def __get__(self, instance, owner):
    if instance is not None:
        return self.getter(instance)
    else:
        self.owner = owner
        return self
var half_size
Expand source code
def __get__(self, instance, owner):
    if instance is not None:
        return self.getter(instance)
    else:
        self.owner = owner
        return self
var lower
Expand source code
@property
def lower(self):
    return self._lower
var shape

Specifies the number of copies of the geometry as batch and spatial dimensions.

Expand source code
@property
def shape(self):
    return _fill_spatial_with_singleton(self._lower.shape & self._upper.shape).non_channel
var size
Expand source code
@property
def size(self):
    return self.upper - self.lower
var upper
Expand source code
@property
def upper(self):
    return self._upper

Methods

def shifted(self, delta)

Returns a translated version of this geometry.

Args

delta
direction vector
delta
Tensor:

Returns

Geometry
shifted geometry
Expand source code
def shifted(self, delta):
    return Box(self.lower + delta, self.upper + delta)
def unstack(self, dimension)

Unstacks this Geometry along the given dimension. The shapes of the returned geometries are reduced by dimension.

Args

dimension
dimension along which to unstack

Returns

geometries
tuple of length equal to geometry.shape.get_size(dimension)
Expand source code
def unstack(self, dimension):
    size = combined_dim(self._lower.shape.get_size(dimension), self._upper.shape.get_size(dimension))
    lowers = self._lower.dimension(dimension).unstack(size)
    uppers = self._upper.dimension(dimension).unstack(size)
    return tuple(Box(lo, up) for lo, up in zip(lowers, uppers))
class Geometry

Abstract base class for N-dimensional shapes.

Main implementing classes:

  • Sphere
  • box family: box (generator), Box, Cuboid, BaseBox

All geometry objects support batching. Thereby any parameter defining the geometry can be varied along arbitrary batch dims. All batch dimensions are listed in Geometry.shape.

Expand source code
class Geometry:
    """
    Abstract base class for N-dimensional shapes.

    Main implementing classes:

    * Sphere
    * box family: box (generator), Box, Cuboid, BaseBox

    All geometry objects support batching.
    Thereby any parameter defining the geometry can be varied along arbitrary batch dims.
    All batch dimensions are listed in Geometry.shape.
    """

    @property
    def center(self) -> Tensor:
        """
        Center location in single channel dimension, ordered according to GLOBAL_AXIS_ORDER
        """
        raise NotImplementedError(self)

    @property
    def shape(self) -> Shape:
        """
        Specifies the number of copies of the geometry as batch and spatial dimensions.
        """
        raise NotImplementedError()

    @property
    def volume(self) -> Tensor:
        """
        Volume of the geometry as `phi.math.Tensor`.
        The result retains all batch dimensions while instance dimensions are summed over.
        """
        raise NotImplementedError()

    def unstack(self, dimension: str) -> tuple:
        """
        Unstacks this Geometry along the given dimension.
        The shapes of the returned geometries are reduced by `dimension`.

        Args:
            dimension: dimension along which to unstack

        Returns:
            geometries: tuple of length equal to `geometry.shape.get_size(dimension)`
        """
        raise NotImplementedError()

    @property
    def spatial_rank(self) -> int:
        """ Number of spatial dimensions of the geometry, 1 = 1D, 2 = 2D, 3 = 3D, etc. """
        return self.shape.spatial.rank

    def lies_inside(self, location: Tensor) -> Tensor:
        """
        Tests whether the given location lies inside or outside of the geometry. Locations on the surface count as inside.

        When dealing with unions or collections of geometries (instance dimensions), a point lies inside the geometry if it lies inside any instance.

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

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

        """
        raise NotImplementedError(self.__class__)

    def approximate_signed_distance(self, location: Tensor) -> Tensor:
        """
        Computes the approximate distance from location to the surface of the geometry.
        Locations outside return positive values, inside negative values and zero exactly at the boundary.

        The exact distance metric used depends on the geometry.
        The approximation holds close to the surface and the distance grows to infinity as the location is moved infinitely far from the geometry.
        The distance metric is differentiable and its gradients are bounded at every point in space.

        When dealing with unions or collections of geometries (instance dimensions), the shortest distance to any instance is returned.
        This also holds for negative distances.

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

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

        """
        raise NotImplementedError(self.__class__)

    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

    def push(self, positions: Tensor, outward: bool = True, shift_amount: float = 0) -> Tensor:
        """
        Shifts positions either into or out of geometry.

        Args:
            positions: Tensor holding positions to shift
            outward: Flag for indicating inward (False) or outward (True) shift
            shift_amount: Minimum distance between positions and box boundaries after shifting

        Returns:
            Tensor holding shifted positions
        """
        raise NotImplementedError(self.__class__)

    def bounding_radius(self) -> Tensor:
        """
        Returns the radius of a Sphere object that fully encloses this geometry.
        The sphere is centered at the center of this geometry.

        :return: radius of type float

        Args:

        Returns:

        """
        raise NotImplementedError(self.__class__)

    def bounding_half_extent(self) -> Tensor:
        """
        The bounding half-extent sets a limit on the outer-most point for each coordinate axis.
        Each component is non-negative.

        Let the bounding half-extent have value `e` in dimension `d` (`extent[...,d] = e`).
        Then, no point of the geometry lies further away from its center point than `e` along `d` (in both axis directions).

        :return: float vector

        Args:

        Returns:

        """
        raise NotImplementedError(self.__class__)

    def shifted(self, delta: Tensor) -> 'Geometry':
        """
        Returns a translated version of this geometry.

        Args:
          delta: direction vector
          delta: Tensor:

        Returns:
          Geometry: shifted geometry

        """
        raise NotImplementedError(self.__class__)

    def rotated(self, angle) -> 'Geometry':
        """
        Returns a rotated version of this geometry.
        The geometry is rotated about its center point.

        Args:
          angle: scalar (2d) or vector (3D+) representing delta angle

        Returns:
          Geometry: rotated geometry

        """
        raise NotImplementedError(self.__class__)

    def __invert__(self):
        return _InvertedGeometry(self)

    def __eq__(self, other):
        """
        Slow equality check.
        Unlike `==`, this method compares all tensor elements to check whether they are equal.
        Use `==` for a faster check which only checks whether the referenced tensors are the same.

        See Also:
            `shallow_equals()`
        """
        if self is other:
            return True
        if not isinstance(other, type(self)):
            return False
        if self.shape != other.shape:
            return False
        c1 = {a: getattr(self, a) for a in variable_attributes(self)}
        c2 = {a: getattr(other, a) for a in variable_attributes(self)}
        for c in c1.keys():
            if c1[c] is not c2[c] and math.any(c1[c] != c2[c]):
                return False
        return True

    def shallow_equals(self, other):
        """
        Quick equality check.
        May return `False` even if `other == self`.
        However, if `True` is returned, the geometries are guaranteed to be equal.

        The `shallow_equals()` check does not compare all tensor elements but merely checks whether the same tensors are referenced.
        """
        if self is other:
            return True
        if not isinstance(other, type(self)):
            return False
        if self.shape != other.shape:
            return False
        c1 = {a: getattr(self, a) for a in variable_attributes(self)}
        c2 = {a: getattr(other, a) for a in variable_attributes(self)}
        for c in c1.keys():
            if c1[c] is not c2[c]:
                return False
        return True

    def __ne__(self, other):
        return not self == other

    def __hash__(self):
        raise NotImplementedError(self.__class__)

    def __repr__(self):
        return f"{self.__class__.__name__}{self.shape}"

    def __getitem__(self, item: dict):
        assert isinstance(item, dict), "Index must be dict of type {dim: slice/int}."
        item = {dim: sel for dim, sel in item.items() if dim != 'vector'}
        attrs = {a: getattr(self, a)[item] for a in variable_attributes(self)}
        return copy_with(self, **attrs)

Subclasses

  • phi.geom._box.BaseBox
  • phi.geom._geom.Point
  • phi.geom._geom._InvertedGeometry
  • phi.geom._geom._NoGeometry
  • phi.geom._sphere.Sphere
  • phi.geom._stack.GeometryStack
  • phi.geom._transform.RotatedGeometry
  • phi.geom._union.Union

Instance variables

var center : phi.math._tensors.Tensor

Center location in single channel dimension, ordered according to GLOBAL_AXIS_ORDER

Expand source code
@property
def center(self) -> Tensor:
    """
    Center location in single channel dimension, ordered according to GLOBAL_AXIS_ORDER
    """
    raise NotImplementedError(self)
var shape : phi.math._shape.Shape

Specifies the number of copies of the geometry as batch and spatial dimensions.

Expand source code
@property
def shape(self) -> Shape:
    """
    Specifies the number of copies of the geometry as batch and spatial dimensions.
    """
    raise NotImplementedError()
var spatial_rank : int

Number of spatial dimensions of the geometry, 1 = 1D, 2 = 2D, 3 = 3D, etc.

Expand source code
@property
def spatial_rank(self) -> int:
    """ Number of spatial dimensions of the geometry, 1 = 1D, 2 = 2D, 3 = 3D, etc. """
    return self.shape.spatial.rank
var volume : phi.math._tensors.Tensor

Volume of the geometry as Tensor. The result retains all batch dimensions while instance dimensions are summed over.

Expand source code
@property
def volume(self) -> Tensor:
    """
    Volume of the geometry as `phi.math.Tensor`.
    The result retains all batch dimensions while instance dimensions are summed over.
    """
    raise NotImplementedError()

Methods

def approximate_fraction_inside(self, other_geometry: Geometry, balance: phi.math._tensors.Tensor = 0.5) ‑> phi.math._tensors.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.5makes self seem smaller.

Returns

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

Expand source code
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
def approximate_signed_distance(self, location: phi.math._tensors.Tensor) ‑> phi.math._tensors.Tensor

Computes the approximate distance from location to the surface of the geometry. Locations outside return positive values, inside negative values and zero exactly at the boundary.

The exact distance metric used depends on the geometry. The approximation holds close to the surface and the distance grows to infinity as the location is moved infinitely far from the geometry. The distance metric is differentiable and its gradients are bounded at every point in space.

When dealing with unions or collections of geometries (instance dimensions), the shortest distance to any instance is returned. This also holds for negative distances.

Args

location
float tensor of shape (batch_size, …, rank)
location
Tensor:

Returns

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

Expand source code
def approximate_signed_distance(self, location: Tensor) -> Tensor:
    """
    Computes the approximate distance from location to the surface of the geometry.
    Locations outside return positive values, inside negative values and zero exactly at the boundary.

    The exact distance metric used depends on the geometry.
    The approximation holds close to the surface and the distance grows to infinity as the location is moved infinitely far from the geometry.
    The distance metric is differentiable and its gradients are bounded at every point in space.

    When dealing with unions or collections of geometries (instance dimensions), the shortest distance to any instance is returned.
    This also holds for negative distances.

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

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

    """
    raise NotImplementedError(self.__class__)
def bounding_half_extent(self) ‑> phi.math._tensors.Tensor

The bounding half-extent sets a limit on the outer-most point for each coordinate axis. Each component is non-negative.

Let the bounding half-extent have value e in dimension d (extent[...,d] = e). Then, no point of the geometry lies further away from its center point than e along d (in both axis directions).

:return: float vector

Args:

Returns:

Expand source code
def bounding_half_extent(self) -> Tensor:
    """
    The bounding half-extent sets a limit on the outer-most point for each coordinate axis.
    Each component is non-negative.

    Let the bounding half-extent have value `e` in dimension `d` (`extent[...,d] = e`).
    Then, no point of the geometry lies further away from its center point than `e` along `d` (in both axis directions).

    :return: float vector

    Args:

    Returns:

    """
    raise NotImplementedError(self.__class__)
def bounding_radius(self) ‑> phi.math._tensors.Tensor

Returns the radius of a Sphere object that fully encloses this geometry. The sphere is centered at the center of this geometry.

:return: radius of type float

Args:

Returns:

Expand source code
def bounding_radius(self) -> Tensor:
    """
    Returns the radius of a Sphere object that fully encloses this geometry.
    The sphere is centered at the center of this geometry.

    :return: radius of type float

    Args:

    Returns:

    """
    raise NotImplementedError(self.__class__)
def lies_inside(self, location: phi.math._tensors.Tensor) ‑> phi.math._tensors.Tensor

Tests whether the given location lies inside or outside of the geometry. Locations on the surface count as inside.

When dealing with unions or collections of geometries (instance dimensions), a point lies inside the geometry if it lies inside any instance.

Args

location
float tensor of shape (batch_size, …, rank)

Returns

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

Expand source code
def lies_inside(self, location: Tensor) -> Tensor:
    """
    Tests whether the given location lies inside or outside of the geometry. Locations on the surface count as inside.

    When dealing with unions or collections of geometries (instance dimensions), a point lies inside the geometry if it lies inside any instance.

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

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

    """
    raise NotImplementedError(self.__class__)
def push(self, positions: phi.math._tensors.Tensor, outward: bool = True, shift_amount: float = 0) ‑> phi.math._tensors.Tensor

Shifts positions either into or out of geometry.

Args

positions
Tensor holding positions to shift
outward
Flag for indicating inward (False) or outward (True) shift
shift_amount
Minimum distance between positions and box boundaries after shifting

Returns

Tensor holding shifted positions

Expand source code
def push(self, positions: Tensor, outward: bool = True, shift_amount: float = 0) -> Tensor:
    """
    Shifts positions either into or out of geometry.

    Args:
        positions: Tensor holding positions to shift
        outward: Flag for indicating inward (False) or outward (True) shift
        shift_amount: Minimum distance between positions and box boundaries after shifting

    Returns:
        Tensor holding shifted positions
    """
    raise NotImplementedError(self.__class__)
def rotated(self, angle) ‑> phi.geom._geom.Geometry

Returns a rotated version of this geometry. The geometry is rotated about its center point.

Args

angle
scalar (2d) or vector (3D+) representing delta angle

Returns

Geometry
rotated geometry
Expand source code
def rotated(self, angle) -> 'Geometry':
    """
    Returns a rotated version of this geometry.
    The geometry is rotated about its center point.

    Args:
      angle: scalar (2d) or vector (3D+) representing delta angle

    Returns:
      Geometry: rotated geometry

    """
    raise NotImplementedError(self.__class__)
def shallow_equals(self, other)

Quick equality check. May return False even if other == self. However, if True is returned, the geometries are guaranteed to be equal.

The shallow_equals() check does not compare all tensor elements but merely checks whether the same tensors are referenced.

Expand source code
def shallow_equals(self, other):
    """
    Quick equality check.
    May return `False` even if `other == self`.
    However, if `True` is returned, the geometries are guaranteed to be equal.

    The `shallow_equals()` check does not compare all tensor elements but merely checks whether the same tensors are referenced.
    """
    if self is other:
        return True
    if not isinstance(other, type(self)):
        return False
    if self.shape != other.shape:
        return False
    c1 = {a: getattr(self, a) for a in variable_attributes(self)}
    c2 = {a: getattr(other, a) for a in variable_attributes(self)}
    for c in c1.keys():
        if c1[c] is not c2[c]:
            return False
    return True
def shifted(self, delta: phi.math._tensors.Tensor) ‑> phi.geom._geom.Geometry

Returns a translated version of this geometry.

Args

delta
direction vector
delta
Tensor:

Returns

Geometry
shifted geometry
Expand source code
def shifted(self, delta: Tensor) -> 'Geometry':
    """
    Returns a translated version of this geometry.

    Args:
      delta: direction vector
      delta: Tensor:

    Returns:
      Geometry: shifted geometry

    """
    raise NotImplementedError(self.__class__)
def unstack(self, dimension: str) ‑> tuple

Unstacks this Geometry along the given dimension. The shapes of the returned geometries are reduced by dimension.

Args

dimension
dimension along which to unstack

Returns

geometries
tuple of length equal to geometry.shape.get_size(dimension)
Expand source code
def unstack(self, dimension: str) -> tuple:
    """
    Unstacks this Geometry along the given dimension.
    The shapes of the returned geometries are reduced by `dimension`.

    Args:
        dimension: dimension along which to unstack

    Returns:
        geometries: tuple of length equal to `geometry.shape.get_size(dimension)`
    """
    raise NotImplementedError()
class GridCell (resolution: phi.math._shape.Shape, bounds: phi.geom._box.BaseBox)

An instance of GridCell represents all cells of a regular grid as a batch of boxes.

Expand source code
class GridCell(BaseBox):
    """
    An instance of GridCell represents all cells of a regular grid as a batch of boxes.
    """

    def __init__(self, resolution: math.Shape, bounds: BaseBox):
        assert resolution.spatial_rank == resolution.rank, 'resolution must be purely spatial but got %s' % (resolution,)
        self._resolution = resolution
        self._bounds = bounds
        self._shape = resolution & bounds.shape.non_spatial

    @property
    def resolution(self):
        return self._resolution

    @property
    def bounds(self):
        return self._bounds

    @property
    def center(self):
        local_coords = math.meshgrid(**{dim: math.linspace(0.5 / size, 1 - 0.5 / size, size) for dim, size in zip(self.resolution.names, self.resolution.sizes)})
        points = self.bounds.local_to_global(local_coords)
        return points

    @property
    def grid_size(self):
        return self._bounds.size

    @property
    def size(self):
        return self.bounds.size / math.wrap(self.resolution.sizes)

    @property
    def lower(self):
        return self.center - self.half_size

    @property
    def upper(self):
        return self.center + self.half_size

    @property
    def half_size(self):
        return self.bounds.size / self.resolution.sizes / 2

    def __getitem__(self, item: dict):
        bounds = self._bounds
        dx = self.size
        for dim, selection in item.items():
            if dim in self._resolution:
                if isinstance(selection, int):
                    start = selection
                    stop = selection + 1
                elif isinstance(selection, slice):
                    start = selection.start or 0
                    stop = selection.stop or self.resolution.get_size(dim)
                    if stop < 0:
                        stop += self.resolution.get_size(dim)
                    assert selection.step is None or selection.step == 1
                else:
                    raise ValueError(f"Illegal selection: {item}")
                dim_mask = math.wrap(self.resolution.mask(dim))
                lower = bounds.lower + start * dim_mask * dx
                upper = bounds.upper + (stop - self.resolution.get_size(dim)) * dim_mask * dx
                bounds = Box(lower, upper)
        resolution = self._resolution.after_gather(item)
        return GridCell(resolution, bounds)

    def list_cells(self, dim_name):
        center = math.pack_dims(self.center, self._shape.spatial.names, dim_name)
        return Cuboid(center, self.half_size)

    def stagger(self, dim: str, lower: bool, upper: bool):
        dim_mask = np.array(self.resolution.mask(dim))
        unit = self.bounds.size / self.resolution * dim_mask
        bounds = Box(self.bounds.lower + unit * (-0.5 if lower else 0.5), self.bounds.upper + unit * (0.5 if upper else -0.5))
        ext_res = self.resolution.sizes + dim_mask * (int(lower) + int(upper) - 1)
        return GridCell(self.resolution.with_sizes(ext_res), bounds)

    # def face_centers(self, staggered_name='staggered'):
    #     face_centers = [self.extend_symmetric(dim).center for dim in self.shape.spatial.names]
    #     return math.channel_stack(face_centers, staggered_name)

    @property
    def shape(self):
        return self._shape

    def shifted(self, delta: Tensor) -> BaseBox:
        if delta.shape.spatial_rank == 0:
            return GridCell(self.resolution, self.bounds.shifted(delta))
        else:
            center = self.center + delta
            return Cuboid(center, self.half_size)

    def rotated(self, angle) -> Geometry:
        raise NotImplementedError()

    def unstack(self, dimension):
        raise NotImplementedError()

    def __eq__(self, other):
        return isinstance(other, GridCell) and self._bounds == other._bounds and self._resolution == other._resolution

    def shallow_equals(self, other):
        return self == other

    def __hash__(self):
        return hash(self._resolution) + hash(self._bounds)

    def __repr__(self):
        return f"{self._resolution}, bounds={self._bounds}"

Ancestors

  • phi.geom._box.BaseBox
  • phi.geom._geom.Geometry

Instance variables

var bounds
Expand source code
@property
def bounds(self):
    return self._bounds
var center

Center location in single channel dimension, ordered according to GLOBAL_AXIS_ORDER

Expand source code
@property
def center(self):
    local_coords = math.meshgrid(**{dim: math.linspace(0.5 / size, 1 - 0.5 / size, size) for dim, size in zip(self.resolution.names, self.resolution.sizes)})
    points = self.bounds.local_to_global(local_coords)
    return points
var grid_size
Expand source code
@property
def grid_size(self):
    return self._bounds.size
var half_size
Expand source code
@property
def half_size(self):
    return self.bounds.size / self.resolution.sizes / 2
var lower
Expand source code
@property
def lower(self):
    return self.center - self.half_size
var resolution
Expand source code
@property
def resolution(self):
    return self._resolution
var shape

Specifies the number of copies of the geometry as batch and spatial dimensions.

Expand source code
@property
def shape(self):
    return self._shape
var size
Expand source code
@property
def size(self):
    return self.bounds.size / math.wrap(self.resolution.sizes)
var upper
Expand source code
@property
def upper(self):
    return self.center + self.half_size

Methods

def list_cells(self, dim_name)
Expand source code
def list_cells(self, dim_name):
    center = math.pack_dims(self.center, self._shape.spatial.names, dim_name)
    return Cuboid(center, self.half_size)
def rotated(self, angle) ‑> phi.geom._geom.Geometry

Returns a rotated version of this geometry. The geometry is rotated about its center point.

Args

angle
scalar (2d) or vector (3D+) representing delta angle

Returns

Geometry
rotated geometry
Expand source code
def rotated(self, angle) -> Geometry:
    raise NotImplementedError()
def shallow_equals(self, other)

Quick equality check. May return False even if other == self. However, if True is returned, the geometries are guaranteed to be equal.

The shallow_equals() check does not compare all tensor elements but merely checks whether the same tensors are referenced.

Expand source code
def shallow_equals(self, other):
    return self == other
def shifted(self, delta: phi.math._tensors.Tensor) ‑> phi.geom._box.BaseBox

Returns a translated version of this geometry.

Args

delta
direction vector
delta
Tensor:

Returns

Geometry
shifted geometry
Expand source code
def shifted(self, delta: Tensor) -> BaseBox:
    if delta.shape.spatial_rank == 0:
        return GridCell(self.resolution, self.bounds.shifted(delta))
    else:
        center = self.center + delta
        return Cuboid(center, self.half_size)
def stagger(self, dim: str, lower: bool, upper: bool)
Expand source code
def stagger(self, dim: str, lower: bool, upper: bool):
    dim_mask = np.array(self.resolution.mask(dim))
    unit = self.bounds.size / self.resolution * dim_mask
    bounds = Box(self.bounds.lower + unit * (-0.5 if lower else 0.5), self.bounds.upper + unit * (0.5 if upper else -0.5))
    ext_res = self.resolution.sizes + dim_mask * (int(lower) + int(upper) - 1)
    return GridCell(self.resolution.with_sizes(ext_res), bounds)
def unstack(self, dimension)

Unstacks this Geometry along the given dimension. The shapes of the returned geometries are reduced by dimension.

Args

dimension
dimension along which to unstack

Returns

geometries
tuple of length equal to geometry.shape.get_size(dimension)
Expand source code
def unstack(self, dimension):
    raise NotImplementedError()
class Point (location: phi.math._tensors.Tensor)

Points have zero volume and are determined by a single location. An instance of Point represents a single n-dimensional point or a batch of points.

Expand source code
class Point(Geometry):
    """
    Points have zero volume and are determined by a single location.
    An instance of `Point` represents a single n-dimensional point or a batch of points.
    """

    def __init__(self, location: math.Tensor):
        self._location = location

    @property
    def center(self) -> Tensor:
        return self._location

    @property
    def shape(self) -> Shape:
        return self._location.shape.without('vector')

    def unstack(self, dimension: str) -> tuple:
        return tuple(Point(loc) for loc in self._location.unstack(dimension))

    def lies_inside(self, location: Tensor) -> Tensor:
        return math.wrap(False)

    def approximate_signed_distance(self, location: Tensor) -> Tensor:
        return math.vec_abs(location - self._location)

    def push(self, positions: Tensor, outward: bool = True, shift_amount: float = 0) -> Tensor:
        return positions

    def bounding_radius(self) -> Tensor:
        return math.zeros()

    def bounding_half_extent(self) -> Tensor:
        return math.zeros()

    def shifted(self, delta: Tensor) -> 'Geometry':
        return Point(self._location + delta)

    def rotated(self, angle) -> 'Geometry':
        return self

    def __hash__(self):
        return hash(self._location)

    def _characteristics_(self) -> Dict[str, math.Tensor]:
        return {'location': self._location}

Ancestors

  • phi.geom._geom.Geometry

Instance variables

var center : phi.math._tensors.Tensor

Center location in single channel dimension, ordered according to GLOBAL_AXIS_ORDER

Expand source code
@property
def center(self) -> Tensor:
    return self._location
var shape : phi.math._shape.Shape

Specifies the number of copies of the geometry as batch and spatial dimensions.

Expand source code
@property
def shape(self) -> Shape:
    return self._location.shape.without('vector')

Methods

def approximate_signed_distance(self, location: phi.math._tensors.Tensor) ‑> phi.math._tensors.Tensor

Computes the approximate distance from location to the surface of the geometry. Locations outside return positive values, inside negative values and zero exactly at the boundary.

The exact distance metric used depends on the geometry. The approximation holds close to the surface and the distance grows to infinity as the location is moved infinitely far from the geometry. The distance metric is differentiable and its gradients are bounded at every point in space.

When dealing with unions or collections of geometries (instance dimensions), the shortest distance to any instance is returned. This also holds for negative distances.

Args

location
float tensor of shape (batch_size, …, rank)
location
Tensor:

Returns

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

Expand source code
def approximate_signed_distance(self, location: Tensor) -> Tensor:
    return math.vec_abs(location - self._location)
def bounding_half_extent(self) ‑> phi.math._tensors.Tensor

The bounding half-extent sets a limit on the outer-most point for each coordinate axis. Each component is non-negative.

Let the bounding half-extent have value e in dimension d (extent[...,d] = e). Then, no point of the geometry lies further away from its center point than e along d (in both axis directions).

:return: float vector

Args:

Returns:

Expand source code
def bounding_half_extent(self) -> Tensor:
    return math.zeros()
def bounding_radius(self) ‑> phi.math._tensors.Tensor

Returns the radius of a Sphere object that fully encloses this geometry. The sphere is centered at the center of this geometry.

:return: radius of type float

Args:

Returns:

Expand source code
def bounding_radius(self) -> Tensor:
    return math.zeros()
def lies_inside(self, location: phi.math._tensors.Tensor) ‑> phi.math._tensors.Tensor

Tests whether the given location lies inside or outside of the geometry. Locations on the surface count as inside.

When dealing with unions or collections of geometries (instance dimensions), a point lies inside the geometry if it lies inside any instance.

Args

location
float tensor of shape (batch_size, …, rank)

Returns

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

Expand source code
def lies_inside(self, location: Tensor) -> Tensor:
    return math.wrap(False)
def push(self, positions: phi.math._tensors.Tensor, outward: bool = True, shift_amount: float = 0) ‑> phi.math._tensors.Tensor

Shifts positions either into or out of geometry.

Args

positions
Tensor holding positions to shift
outward
Flag for indicating inward (False) or outward (True) shift
shift_amount
Minimum distance between positions and box boundaries after shifting

Returns

Tensor holding shifted positions

Expand source code
def push(self, positions: Tensor, outward: bool = True, shift_amount: float = 0) -> Tensor:
    return positions
def rotated(self, angle) ‑> phi.geom._geom.Geometry

Returns a rotated version of this geometry. The geometry is rotated about its center point.

Args

angle
scalar (2d) or vector (3D+) representing delta angle

Returns

Geometry
rotated geometry
Expand source code
def rotated(self, angle) -> 'Geometry':
    return self
def shifted(self, delta: phi.math._tensors.Tensor) ‑> phi.geom._geom.Geometry

Returns a translated version of this geometry.

Args

delta
direction vector
delta
Tensor:

Returns

Geometry
shifted geometry
Expand source code
def shifted(self, delta: Tensor) -> 'Geometry':
    return Point(self._location + delta)
def unstack(self, dimension: str) ‑> tuple

Unstacks this Geometry along the given dimension. The shapes of the returned geometries are reduced by dimension.

Args

dimension
dimension along which to unstack

Returns

geometries
tuple of length equal to geometry.shape.get_size(dimension)
Expand source code
def unstack(self, dimension: str) -> tuple:
    return tuple(Point(loc) for loc in self._location.unstack(dimension))
class Sphere (center, radius)

N-dimensional sphere. Defined through center position and radius.

Args:

Returns:

Expand source code
class Sphere(Geometry):
    """
    N-dimensional sphere.
    Defined through center position and radius.

    Args:

    Returns:

    """

    def __init__(self, center, radius):
        self._center = wrap(center)
        assert 'vector' in self._center.shape, f"Sphere.center must have a 'vector' dimension. Try ({center},) * rank."
        self._radius = wrap(radius)

    @property
    def shape(self):
        return _fill_spatial_with_singleton(self._center.shape & self._radius.shape).without('vector')

    @property
    def radius(self):
        return self._radius

    @property
    def center(self):
        return self._center

    @property
    def volume(self) -> math.Tensor:
        return 4 / 3 * math.PI * self._radius ** 3

    def lies_inside(self, location):
        distance_squared = math.sum((location - self.center) ** 2, dim='vector')
        return math.any(distance_squared <= self.radius ** 2, self.shape.instance)  # union for instance dimensions

    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 spatial_gradient at sphere center
        distance = math.sqrt(distance_squared)
        return math.min(distance - self.radius, self.shape.instance)  # union for instance dimensions

    def bounding_radius(self):
        return self.radius

    def bounding_half_extent(self):
        return self.radius

    def shifted(self, delta):
        return Sphere(self._center + delta, self._radius)

    def rotated(self, angle):
        return self

    def __variable_attrs__(self):
        return '_radius', '_center'

Ancestors

  • phi.geom._geom.Geometry

Instance variables

var center

Center location in single channel dimension, ordered according to GLOBAL_AXIS_ORDER

Expand source code
@property
def center(self):
    return self._center
var radius
Expand source code
@property
def radius(self):
    return self._radius
var shape

Specifies the number of copies of the geometry as batch and spatial dimensions.

Expand source code
@property
def shape(self):
    return _fill_spatial_with_singleton(self._center.shape & self._radius.shape).without('vector')
var volume : phi.math._tensors.Tensor

Volume of the geometry as Tensor. The result retains all batch dimensions while instance dimensions are summed over.

Expand source code
@property
def volume(self) -> math.Tensor:
    return 4 / 3 * math.PI * self._radius ** 3

Methods

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).

Expand source code
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 spatial_gradient at sphere center
    distance = math.sqrt(distance_squared)
    return math.min(distance - self.radius, self.shape.instance)  # union for instance dimensions
def bounding_half_extent(self)

The bounding half-extent sets a limit on the outer-most point for each coordinate axis. Each component is non-negative.

Let the bounding half-extent have value e in dimension d (extent[...,d] = e). Then, no point of the geometry lies further away from its center point than e along d (in both axis directions).

:return: float vector

Args:

Returns:

Expand source code
def bounding_half_extent(self):
    return self.radius
def bounding_radius(self)

Returns the radius of a Sphere object that fully encloses this geometry. The sphere is centered at the center of this geometry.

:return: radius of type float

Args:

Returns:

Expand source code
def bounding_radius(self):
    return self.radius
def lies_inside(self, location)

Tests whether the given location lies inside or outside of the geometry. Locations on the surface count as inside.

When dealing with unions or collections of geometries (instance dimensions), a point lies inside the geometry if it lies inside any instance.

Args

location
float tensor of shape (batch_size, …, rank)

Returns

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

Expand source code
def lies_inside(self, location):
    distance_squared = math.sum((location - self.center) ** 2, dim='vector')
    return math.any(distance_squared <= self.radius ** 2, self.shape.instance)  # union for instance dimensions
def rotated(self, angle)

Returns a rotated version of this geometry. The geometry is rotated about its center point.

Args

angle
scalar (2d) or vector (3D+) representing delta angle

Returns

Geometry
rotated geometry
Expand source code
def rotated(self, angle):
    return self
def shifted(self, delta)

Returns a translated version of this geometry.

Args

delta
direction vector
delta
Tensor:

Returns

Geometry
shifted geometry
Expand source code
def shifted(self, delta):
    return Sphere(self._center + delta, self._radius)