Source code for irfpy.util.domain

""" Represents the domain (the region) in 3D.

The domain is the region where some functionalities are defined (in math) or
the region of the same property (in space physics).

For example, any numerical simulations are conducted in a limited region, e.g. R<10Re.
This is a spherical domain (:class:`SphericalDomain`).
Other implementation is the Martian Induced magnetosphere, which is variable in time,
but can be considered as a domain.



"""
import abc
import numpy as np

[docs]class BaseStaticDomain(metaclass=abc.ABCMeta): """ A base class of a domain without time variation. """
[docs] @abc.abstractmethod def contains(self, xyz): """ Determine the given coordinate is inside of the region. :param xyz: x, y, z position. (3,) shaped list, tuple or array. :return: *True* if inside, *False* if outside. The method returns *True* if the given point is inside the region. Note: Whether or not the boundary is inside or outside is fully reposinible to the developer of this method. """
def __contains__(self, item): return self.contains(item)
[docs] def evaluates(self, xcoords, ycoords, zcoords): """ Vectorized version of the :meth:`contains`. The evaluation done in :meth:`contains` can be done for (N, 3) shaped array. The developer can override this method for optimized vectorization. Without the override, the method :meth:`contains` is called N times. :param xarr: Array of x-coordinates :param yarr: Array of y-coordinates :param zarr: Array of z-coordinates :return: (N,) shaped boolean array. >>> x = [0, 1, 2, 3] >>> y = [0, 1, 2, 3] >>> z = [0, 1, 2, 3] >>> cartDomain = CartesianDomain([0, 1], [0, 1], [0, 1]) # Cartesian domain. >>> print(cartDomain.evaluates(x, y, z)) [ True True False False] """ xarr = np.array(xcoords) yarr = np.array(ycoords) zarr = np.array(zcoords) if not (xarr.shape == yarr.shape == zarr.shape): raise ValueError('Shape differs: {} & {} & {}'.format(xarr.shape, yarr.shape, zarr.shape)) boolarr = np.array([self.contains(xyz) for xyz in zip(xarr, yarr, zarr)]) return boolarr
[docs]class InfiniteDomain(BaseStaticDomain): """ Domain extending to the inifinity. Infinitely-extending domain (indicating that the :meth:`contains` always returns *True*). Usually, you can use the pre-defined variable :attr:`infinite_domain`. >>> infinite_domain.contains((3, 5, 10)) True """
[docs] def contains(self, xyz): return True
infinite_domain = InfiniteDomain() """An instance of :class:`InfiniteDomain`. """
[docs]class CartesianDomain(BaseStaticDomain): """ Domain with a bound defined by x, y, and z ranges. The x-, y-, and z-ranged cuboid is considered as a domain. The following statement will create a domain between -10 and 10 for all three directions. >>> cartDomain = CartesianDomain([-10, 10], [-10, 10], [-10, 10]) To evaluate if the given point is in (or out) of the domain, you can use "in" statement, or :meth:`contains` method. >>> print([0, 0, 0] in cartDomain) True >>> print(cartDomain.contains([5, 7, -12])) False Note that the edges are inside the domain. >>> print(cartDomain.contains([10, 10, 10])) True To evauate multiple points by one call, use :meth:`evaluate` method. >>> print(cartDomain.evaluates([0, 10, 20, 30], [0, 10, 20, 30], [0, 10, 20, 30])) [ True True False False] """ def __init__(self, xrange, yrange, zrange): """ Define the domain :param xrange: (xmin, xmax) :param yrange: (ymin, ymax) :param zrange: (zmin, zmax) """ self.x0, self.x1 = xrange self.y0, self.y1 = yrange self.z0, self.z1 = zrange
[docs] def contains(self, xyz): x, y, z = xyz return (self.x0 <= x <= self.x1) and (self.y0 <= y <= self.y1) and (self.z0 <= z <= self.z1)
[docs]class SphereDomain(BaseStaticDomain): """ Domain with a spherical shape. >>> sphDomain = SphereDomain(10) # Sphere with radius of 10. >>> print([0, 0, 0] in sphDomain) True >>> print([0, 10, 0] in sphDomain) True >>> print([-20, 0, 0] in sphDomain) False >>> sphDomain2 = SphereDomain(10, center=[10, 10, 10]) # Given the center location. >>> print([0, 0, 0] in sphDomain2) False >>> print([10, 10, 0] in sphDomain2) True >>> sphDomain3 = SphereDomain(10, r_inner=5) # Shell-like: Radius between 5 and 10. >>> print([0, 0, 0] in sphDomain3) False >>> print([0, 5, 0] in sphDomain3) True >>> print([0, 0, -10] in sphDomain3) True """ def __init__(self, r_outer, r_inner=0, center=[0, 0, 0]): self._r = r_outer self.__r2 = r_outer * r_outer self.__rin2 = r_inner * r_inner self._center = np.array(center)
[docs] def contains(self, xyz): xyz_arr = np.array(xyz) - self._center r2 = (np.array(xyz_arr) ** 2).sum() return self.__rin2 <= r2 <= self.__r2
[docs] def evaluates(self, xcoords, ycoords, zcoords): """ Multiple points are evaluated For this SphereDomain class, use :meth:`evaluate` method if you want to evaluate multiple point for better performance. :param xcoords: X coordinates :param ycoords: Y coordinates :param zcoords: Z coordinates :return: Bool array (True for in domain, False for out of domain) >>> x = [0, 0, -20, 0] >>> y = [0, 10, 0, 0] >>> z = [0, 0, 0, 8] >>> sphDomain = SphereDomain(10, r_inner=5) >>> print(sphDomain.evaluates(x, y, z)) [False True False True] """ xarr = np.array(xcoords) yarr = np.array(ycoords) zarr = np.array(zcoords) if not (xarr.shape == yarr.shape == zarr.shape): raise ValueError('Shape differs: {} & {} & {}'.format(xarr.shape, yarr.shape, zarr.shape)) r2 = xarr ** 2 + yarr ** 2 + zarr ** 2 return np.logical_and(self.__rin2 <= r2, r2 <= self.__r2)
[docs]class IntersectDomain(BaseStaticDomain): """ Create a domain intersected with two domains. >>> dom1 = SphereDomain(10, center=(-5, 0, 0)) >>> dom2 = SphereDomain(10, center=(5, 0, 0)) >>> idom = IntersectDomain(dom1, dom2) Here two spherical shaped domains are intersected to ``idom``. The origin is both in dom1 and dom2 and thus in idom. >>> print([0, 0, 0] in idom) True [-15, 0, 0] is only for dom1, so that idom returns false. >>> print([-15, 0, 0] in dom1) True >>> print([-15, 0, 0] in dom2) False >>> print([-15, 0, 0] in idom) False """ def __init__(self, dom1, dom2): """ :param dom1: A domain :param dom2: Another domain :return: New domain intersecting dom1 and dom2. """ self._dom1 = dom1 self._dom2 = dom2
[docs] def contains(self, xyz): return (xyz in self._dom1) and (xyz in self._dom2)
[docs] def evaluates(self, xcoords, ycoords, zcoords): ev1 = self._dom1.evaluates(xcoords, ycoords, zcoords) ev2 = self._dom2.evaluates(xcoords, ycoords, zcoords) return np.logical_and(ev1, ev2)