コード例 #1
0
 class HasCollections(properties.HasProperties):
     tuple_unobs = properties.Tuple('')
     dict_unobs = properties.Dictionary('')
     dict_obs = properties.Dictionary('', observe_mutations=True)
     list_unobs = properties.List('')
     list_obs = properties.List('', observe_mutations=True)
     set_unobs = properties.Set('')
     set_obs = properties.Set('', observe_mutations=True)
コード例 #2
0
 class HasCoercedIntTuple(properties.HasProperties):
     aaa = properties.Tuple('tuple of ints',
                            properties.Integer(''),
                            coerce=True)
コード例 #3
0
 class HasIntTuple(properties.HasProperties):
     aaa = properties.Tuple('tuple of ints',
                            properties.Integer(''),
                            default=tuple)
コード例 #4
0
 class HasDummyTuple(properties.HasProperties):
     mytuple = properties.Tuple('dummy has properties tuple',
                                prop=HasPropsDummy)
コード例 #5
0
 class UntypedTuple(properties.HasProperties):
     mytuple = properties.Tuple('no type')
コード例 #6
0
 class HasOptPropTuple(properties.HasProperties):
     mytuple = properties.Tuple(
         doc='',
         prop=properties.Bool('', required=False),
         default=properties.undefined,
     )
コード例 #7
0
 class HasOptionalTuple(properties.HasProperties):
     mytuple = properties.Tuple('', properties.Bool(''), required=False)
コード例 #8
0
 class HasIntATuple(properties.HasProperties):
     mytuple = properties.Tuple('tuple of HasIntA', HasIntA)
コード例 #9
0
    def test_tuple(self):

        with self.assertRaises(TypeError):
            properties.Tuple('bad string tuple', prop=str)
        with self.assertRaises(TypeError):
            properties.Tuple('bad max', properties.Integer(''), max_length=-10)
        with self.assertRaises(TypeError):
            properties.Tuple('bad max',
                             properties.Integer(''),
                             max_length='ten')
        with self.assertRaises(TypeError):
            mytuple = properties.Tuple('bad max',
                                       properties.Integer(''),
                                       min_length=20)
            mytuple.max_length = 10
        with self.assertRaises(TypeError):
            properties.Tuple('bad min', properties.Integer(''), min_length=-10)
        with self.assertRaises(TypeError):
            properties.Tuple('bad min',
                             properties.Integer(''),
                             min_length='ten')
        with self.assertRaises(TypeError):
            mytuple = properties.Tuple('bad min',
                                       properties.Integer(''),
                                       max_length=10)
            mytuple.min_length = 20
        with self.assertRaises(AttributeError):
            properties.Tuple('bad observe',
                             properties.Integer(''),
                             observe_mutations=5)
        with self.assertRaises(TypeError):
            properties.Tuple('bad coerce', properties.Integer(''), coerce=5)

        class HasPropsDummy(properties.HasProperties):
            pass

        mytuple = properties.Tuple('dummy has properties tuple',
                                   prop=HasPropsDummy)
        assert isinstance(mytuple.prop, properties.Instance)
        assert mytuple.prop.instance_class is HasPropsDummy

        class HasDummyTuple(properties.HasProperties):
            mytuple = properties.Tuple('dummy has properties tuple',
                                       prop=HasPropsDummy)

        assert HasDummyTuple()._props['mytuple'].name == 'mytuple'
        assert HasDummyTuple()._props['mytuple'].prop.name == 'mytuple'

        class HasIntTuple(properties.HasProperties):
            aaa = properties.Tuple('tuple of ints',
                                   properties.Integer(''),
                                   default=tuple)

        li = HasIntTuple()
        li.aaa = (1, 2, 3)
        with self.assertRaises(ValueError):
            li.aaa = [1, 2, 3]
        li.aaa = (1., 2., 3.)
        with self.assertRaises(ValueError):
            li.aaa = 4
        with self.assertRaises(ValueError):
            li.aaa = ('a', 'b', 'c')

        li1 = HasIntTuple()
        li2 = HasIntTuple()
        assert li1.aaa == li2.aaa
        assert li1.aaa is li2.aaa
        li1.aaa += (1, )
        assert li1.aaa is not li2.aaa

        class HasCoercedIntTuple(properties.HasProperties):
            aaa = properties.Tuple('tuple of ints',
                                   properties.Integer(''),
                                   coerce=True)

        li = HasCoercedIntTuple()
        li.aaa = 1
        assert li.aaa == (1, )
        li.aaa = [1, 2, 3]
        assert li.aaa == (1, 2, 3)
        li.aaa = {1, 2, 3}
        assert isinstance(li.aaa, tuple)
        assert all(val in li.aaa for val in [1, 2, 3])

        li.aaa = np.array([3, 2, 1])
        assert li.aaa == (3, 2, 1)

        class HasConstrianedTuple(properties.HasProperties):
            aaa = properties.Tuple('tuple of ints',
                                   properties.Integer(''),
                                   min_length=2)

        li = HasConstrianedTuple()
        li.aaa = (1, 2, 3)
        li.validate()
        li.aaa = (1, )
        with self.assertRaises(ValueError):
            li.validate()

        class HasConstrianedTuple(properties.HasProperties):
            aaa = properties.Tuple('tuple of ints',
                                   properties.Integer(''),
                                   max_length=2)

        li = HasConstrianedTuple()
        li.aaa = (1, 2)
        li.validate()
        li.aaa = (1, 2, 3, 4, 5)
        with self.assertRaises(ValueError):
            li.validate()

        class HasColorTuple(properties.HasProperties):
            ccc = properties.Tuple('tuple of colors',
                                   properties.Color(''),
                                   min_length=2,
                                   max_length=2)

        li = HasColorTuple()
        li.ccc = ('red', '#00FF00')
        assert li.ccc[0] == (255, 0, 0)
        assert li.ccc[1] == (0, 255, 0)

        numtuple = (1, 2, 3, 4)
        assert properties.Tuple.to_json(numtuple) == list(numtuple)
        assert properties.Tuple.from_json(list(numtuple)) == numtuple

        class HasIntA(properties.HasProperties):
            a = properties.Integer('int a', required=True)

        assert properties.Tuple.to_json((HasIntA(a=5), HasIntA(a=10))) == [{
            '__class__':
            'HasIntA',
            'a':
            5
        }, {
            '__class__':
            'HasIntA',
            'a':
            10
        }]

        assert li.serialize(include_class=False) == {
            'ccc': [[255, 0, 0], [0, 255, 0]]
        }

        class HasIntATuple(properties.HasProperties):
            mytuple = properties.Tuple('tuple of HasIntA', HasIntA)

        deser_tuple = HasIntATuple.deserialize({
            'mytuple': [{
                'a': 0
            }, {
                'a': 10
            }, {
                'a': 100
            }]
        }).mytuple
        assert isinstance(deser_tuple, tuple)
        assert len(deser_tuple) == 3
        assert isinstance(deser_tuple[0], HasIntA) and deser_tuple[0].a == 0
        assert isinstance(deser_tuple[1], HasIntA) and deser_tuple[1].a == 10
        assert isinstance(deser_tuple[2], HasIntA) and deser_tuple[2].a == 100

        class HasOptionalTuple(properties.HasProperties):
            mytuple = properties.Tuple('', properties.Bool(''), required=False)

        hol = HasOptionalTuple()
        hol.validate()

        assert HasIntATuple._props['mytuple'].deserialize(None) is None

        assert properties.Tuple('', properties.Instance('', HasIntA)).equal(
            (HasIntA(a=1), HasIntA(a=2)), (HasIntA(a=1), HasIntA(a=2)))
        assert not properties.Tuple('', properties.Instance(
            '', HasIntA)).equal((HasIntA(a=1), HasIntA(a=2)),
                                (HasIntA(a=1), HasIntA(a=2), HasIntA(a=3)))
        assert not properties.Tuple('', properties.Instance(
            '', HasIntA)).equal((HasIntA(a=1), HasIntA(a=2)),
                                (HasIntA(a=1), HasIntA(a=3)))
        assert not properties.Tuple('', properties.Integer('')).equal(5, 5)

        class HasOptPropTuple(properties.HasProperties):
            mytuple = properties.Tuple(
                doc='',
                prop=properties.Bool('', required=False),
                default=properties.undefined,
            )

        hopt = HasOptPropTuple()
        with self.assertRaises(ValueError):
            hopt.validate()

        with self.assertRaises(ValueError):
            hopt.mytuple = (None, )

        with self.assertRaises(ValueError):
            hopt.mytuple = (properties.undefined, )

        hopt._backend = {'mytuple': (properties.undefined, )}

        with self.assertRaises(ValueError):
            hopt.validate()

        hopt.mytuple = (True, )

        del hopt.mytuple

        with self.assertRaises(ValueError):
            hopt.validate()

        class UntypedTuple(properties.HasProperties):
            mytuple = properties.Tuple('no type')

        ut = UntypedTuple(mytuple=(1, 'hi', UntypedTuple))
        ut.validate()
コード例 #10
0
 class HasColorTuple(properties.HasProperties):
     ccc = properties.Tuple('tuple of colors',
                            properties.Color(''),
                            min_length=2,
                            max_length=2)
コード例 #11
0
 class HasConstrianedTuple(properties.HasProperties):
     aaa = properties.Tuple('tuple of ints',
                            properties.Integer(''),
                            max_length=2)
コード例 #12
0
class BaseMesh(properties.HasProperties, InterfaceMixins):
    """
    BaseMesh does all the counting you don't want to do.
    BaseMesh should be inherited by meshes with a regular structure.
    """

    _REGISTRY = {}
    _aliases = {
        "nC": "n_cells",
        "nN": "n_nodes",
        "nEx": "n_edges_x",
        "nEy": "n_edges_y",
        "nEz": "n_edges_z",
        "nE": "n_edges",
        "vnE": "n_edges_per_direction",
        "nFx": "n_faces_x",
        "nFy": "n_faces_y",
        "nFz": "n_faces_z",
        "nF": "n_faces",
        "vnF": "n_faces_per_direction",
    }

    # Properties
    _n = properties.Tuple(
        "Tuple of number of cells in each direction (dim, )",
        prop=properties.Integer("Number of cells along a particular direction",
                                cast=True,
                                min=1),
        min_length=1,
        max_length=3,
        coerce=True,
        required=True,
    )

    origin = properties.Array(
        "origin of the mesh (dim, )",
        dtype=(float, int),
        shape=("*", ),
        required=True,
    )

    # Instantiate the class
    def __init__(self, n=None, origin=None, **kwargs):
        if n is not None:
            self._n = n  # number of dimensions

        if "x0" in kwargs:
            origin = kwargs.pop('x0')
        if origin is None:
            self.origin = np.zeros(len(self._n))
        else:
            self.origin = origin

        super(BaseMesh, self).__init__(**kwargs)

    def __getattr__(self, name):
        if name == "_aliases":
            raise AttributeError  # http://nedbatchelder.com/blog/201010/surprising_getattr_recursion.html
        name = self._aliases.get(name, name)
        return object.__getattribute__(self, name)

    @property
    def x0(self):
        return self.origin

    @x0.setter
    def x0(self, val):
        self.origin = val

    @classmethod
    def deserialize(cls, value, **kwargs):
        if "x0" in value:
            value["origin"] = value.pop("x0")
        return super().deserialize(value, **kwargs)

    # Validators
    @properties.validator("_n")
    def _check_n_shape(self, change):
        if change["previous"] != properties.undefined:
            # _n can only be set once
            if change["previous"] != change["value"]:
                raise AttributeError(
                    "Cannot change n. Instead, create a new mesh")
        else:
            # check that if h has been set, sizes still agree
            if getattr(self, "h", None) is not None and len(self.h) > 0:
                for i in range(len(change["value"])):
                    if len(self.h[i]) != change["value"][i]:
                        raise properties.ValidationError(
                            "Mismatched shape of n. Expected {}, len(h[{}]), got "
                            "{}".format(len(self.h[i]), i, change["value"][i]))

            # check that if nodes have been set for curvi mesh, sizes still
            # agree
            if getattr(self, "node_list",
                       None) is not None and len(self.node_list) > 0:
                for i in range(len(change["value"])):
                    if self.node_list[0].shape[i] - 1 != change["value"][i]:
                        raise properties.ValidationError(
                            "Mismatched shape of n. Expected {}, len(node_list[{}]), "
                            "got {}".format(self.node_list[0].shape[i] - 1, i,
                                            change["value"][i]))

    @properties.validator("origin")
    def _check_origin(self, change):
        if not (not isinstance(change["value"], properties.utils.Sentinel)
                and change["value"] is not None):
            raise Exception("n must be set prior to setting origin")

        if len(self._n) != len(change["value"]):
            raise Exception(
                "Dimension mismatch. origin has length {} != len(n) which is "
                "{}".format(len(self.origin), len(self._n)))

    @property
    def dim(self):
        """The dimension of the mesh (1, 2, or 3).

        Returns
        -------
        int
            dimension of the mesh
        """
        return len(self._n)

    @property
    def n_cells(self):
        """Total number of cells in the mesh.

        Returns
        -------
        int
            number of cells in the mesh

        Notes
        -----
        Also accessible as `nC`.

        Examples
        --------
        >>> import discretize
        >>> import numpy as np
        >>> import matplotlib.pyplot as plt
        >>> mesh = discretize.TensorMesh([np.ones(n) for n in [2,3]])
        >>> mesh.plot_grid(centers=True, show_it=True)
        >>> print(mesh.n_cells)
        """
        return int(np.prod(self._n))

    def __len__(self):
        """The number of cells on the mesh."""
        return self.n_cells

    @property
    def n_nodes(self):
        """Total number of nodes

        Returns
        -------
        int
            number of nodes in the mesh

        Notes
        -----
        Also accessible as `nN`.

        Examples
        --------
        >>> import discretize
        >>> import numpy as np
        >>> import matplotlib.pyplot as plt
        >>> mesh = discretize.TensorMesh([np.ones(n) for n in [2,3]])
        >>> mesh.plot_grid(nodes=True, show_it=True)
        >>> print(mesh.n_nodes)
        """
        return int(np.prod(x + 1 for x in self._n))

    @property
    def n_edges_x(self):
        """Number of x-edges

        Returns
        -------
        int

        Notes
        -----
        Also accessible as `nEx`.

        """
        return int(np.prod(x + y for x, y in zip(self._n, (0, 1, 1))))

    @property
    def n_edges_y(self):
        """Number of y-edges

        Returns
        -------
        int

        Notes
        -----
        Also accessible as `nEy`.

        """
        if self.dim < 2:
            return None
        return int(np.prod(x + y for x, y in zip(self._n, (1, 0, 1))))

    @property
    def n_edges_z(self):
        """Number of z-edges

        Returns
        -------
        int

        Notes
        -----
        Also accessible as `nEz`.

        """
        if self.dim < 3:
            return None
        return int(np.prod(x + y for x, y in zip(self._n, (1, 1, 0))))

    @property
    def n_edges_per_direction(self):
        """The number of edges in each direction

        Returns
        -------
        n_edges_per_direction : tuple
            [n_edges_x, n_edges_y, n_edges_z], (dim, )

        Notes
        -----
        Also accessible as `vnE`.

        Examples
        --------
        >>> import discretize
        >>> import matplotlib.pyplot as plt
        >>> import numpy as np
        >>> M = discretize.TensorMesh([np.ones(n) for n in [2,3]])
        >>> M.plot_grid(edges=True, show_it=True)
        """
        return tuple(x
                     for x in [self.n_edges_x, self.n_edges_y, self.n_edges_z]
                     if x is not None)

    @property
    def n_edges(self):
        """Total number of edges.

        Returns
        -------
        int
            sum([n_edges_x, n_edges_y, n_edges_z])

        Notes
        -----
        Also accessible as `nE`.

        """
        n = self.n_edges_x
        if self.dim > 1:
            n += self.n_edges_y
        if self.dim > 2:
            n += self.n_edges_z
        return n

    @property
    def n_faces_x(self):
        """Number of x-faces

        Returns
        -------
        int

        Notes
        -----
        Also accessible as `nFx`.
        """
        return int(np.prod(x + y for x, y in zip(self._n, (1, 0, 0))))

    @property
    def n_faces_y(self):
        """Number of y-faces

        Returns
        -------
        int

        Notes
        -----
        Also accessible as `nFy`.
        """
        if self.dim < 2:
            return None
        return int(np.prod(x + y for x, y in zip(self._n, (0, 1, 0))))

    @property
    def n_faces_z(self):
        """Number of z-faces

        Returns
        -------
        int

        Notes
        -----
        Also accessible as `nFz`.
        """
        if self.dim < 3:
            return None
        return int(np.prod(x + y for x, y in zip(self._n, (0, 0, 1))))

    @property
    def n_faces_per_direction(self):
        """The number of faces in each direction

        Returns
        -------
        n_faces_per_direction : tuple
            [n_faces_x, n_faces_y, n_faces_z], (dim, )

        Notes
        -----
        Also accessible as `vnF`.

        Examples
        --------
        >>> import discretize
        >>> import numpy as np
        >>> import matplotlib.pyplot as plt
        >>> M = discretize.TensorMesh([np.ones(n) for n in [2,3]])
        >>> M.plot_grid(faces=True, show_it=True)
        """
        return tuple(x
                     for x in [self.n_faces_x, self.n_faces_y, self.n_faces_z]
                     if x is not None)

    @property
    def n_faces(self):
        """Total number of faces.

        Returns
        -------
        int
            sum([n_faces_x, n_faces_y, n_faces_z])

        Notes
        -----
        Also accessible as `nF`.

        """
        n = self.n_faces_x
        if self.dim > 1:
            n += self.n_faces_y
        if self.dim > 2:
            n += self.n_faces_z
        return n

    @property
    def face_normals(self):
        """Face Normals

        Returns
        -------
        numpy.ndarray
            normals, (n_faces, dim)
        """
        if self.dim == 2:
            nX = np.c_[np.ones(self.n_faces_x), np.zeros(self.n_faces_x)]
            nY = np.c_[np.zeros(self.n_faces_y), np.ones(self.n_faces_y)]
            return np.r_[nX, nY]
        elif self.dim == 3:
            nX = np.c_[np.ones(self.n_faces_x),
                       np.zeros(self.n_faces_x),
                       np.zeros(self.n_faces_x), ]
            nY = np.c_[np.zeros(self.n_faces_y),
                       np.ones(self.n_faces_y),
                       np.zeros(self.n_faces_y), ]
            nZ = np.c_[np.zeros(self.n_faces_z),
                       np.zeros(self.n_faces_z),
                       np.ones(self.n_faces_z), ]
            return np.r_[nX, nY, nZ]

    @property
    def edge_tangents(self):
        """Edge Tangents

        Returns
        -------
        numpy.ndarray
            normals, (n_edges, dim)
        """
        if self.dim == 2:
            tX = np.c_[np.ones(self.n_edges_x), np.zeros(self.n_edges_x)]
            tY = np.c_[np.zeros(self.n_edges_y), np.ones(self.n_edges_y)]
            return np.r_[tX, tY]
        elif self.dim == 3:
            tX = np.c_[np.ones(self.n_edges_x),
                       np.zeros(self.n_edges_x),
                       np.zeros(self.n_edges_x), ]
            tY = np.c_[np.zeros(self.n_edges_y),
                       np.ones(self.n_edges_y),
                       np.zeros(self.n_edges_y), ]
            tZ = np.c_[np.zeros(self.n_edges_z),
                       np.zeros(self.n_edges_z),
                       np.ones(self.n_edges_z), ]
            return np.r_[tX, tY, tZ]

    def project_face_vector(self, face_vector):
        """Project vectors onto the faces of the mesh.

        Given a vector, face_vector, in cartesian coordinates, this will project
        it onto the mesh using the normals

        Parameters
        ----------
        face_vector : numpy.ndarray
            face vector with shape (n_faces, dim)

        Returns
        -------
        numpy.ndarray
            projected face vector, (n_faces, )

        """
        if not isinstance(face_vector, np.ndarray):
            raise Exception("face_vector must be an ndarray")
        if not (len(face_vector.shape) == 2 and face_vector.shape[0]
                == self.n_faces and face_vector.shape[1] == self.dim):
            raise Exception(
                "face_vector must be an ndarray of shape (n_faces x dim)")
        return np.sum(face_vector * self.face_normals, 1)

    def project_edge_vector(self, edge_vector):
        """Project vectors onto the edges of the mesh

        Given a vector, edge_vector, in cartesian coordinates, this will project
        it onto the mesh using the tangents

        Parameters
        ----------
        edge_vector : numpy.ndarray
            edge vector with shape (n_edges, dim)

        Returns
        -------
        numpy.ndarray
            projected edge vector, (n_edges, )

        """
        if not isinstance(edge_vector, np.ndarray):
            raise Exception("edge_vector must be an ndarray")
        if not (len(edge_vector.shape) == 2 and edge_vector.shape[0]
                == self.n_edges and edge_vector.shape[1] == self.dim):
            raise Exception(
                "edge_vector must be an ndarray of shape (nE x dim)")
        return np.sum(edge_vector * self.edge_tangents, 1)

    def save(self, file_name="mesh.json", verbose=False, **kwargs):
        """
        Save the mesh to json
        :param str file: file_name for saving the casing properties
        :param str directory: working directory for saving the file
        """

        if 'filename' in kwargs:
            file_name = kwargs['filename']
            warnings.warn(
                "The filename keyword argument has been deprecated, please use file_name. "
                "This will be removed in discretize 1.0.0",
                DeprecationWarning,
            )
        f = os.path.abspath(
            file_name)  # make sure we are working with abs path
        with open(f, "w") as outfile:
            json.dump(self.serialize(), outfile)

        if verbose:
            print("Saved {}".format(f))

        return f

    def copy(self):
        """
        Make a copy of the current mesh
        """
        return properties.copy(self)

    axis_u = properties.Vector3(
        "Vector orientation of u-direction. For more details see the docs for the :attr:`~discretize.base.BaseMesh.rotation_matrix` property.",
        default="X",
        length=1,
    )
    axis_v = properties.Vector3(
        "Vector orientation of v-direction. For more details see the docs for the :attr:`~discretize.base.BaseMesh.rotation_matrix` property.",
        default="Y",
        length=1,
    )
    axis_w = properties.Vector3(
        "Vector orientation of w-direction. For more details see the docs for the :attr:`~discretize.base.BaseMesh.rotation_matrix` property.",
        default="Z",
        length=1,
    )

    @properties.validator
    def _validate_orientation(self):
        """Check if axes are orthogonal"""
        tol = 1E-6
        if not (np.abs(self.axis_u.dot(self.axis_v) < tol)
                and np.abs(self.axis_v.dot(self.axis_w) < tol)
                and np.abs(self.axis_w.dot(self.axis_u) < tol)):
            raise ValueError("axis_u, axis_v, and axis_w must be orthogonal")
        return True

    @property
    def reference_is_rotated(self):
        """True if the axes are rotated from the traditional <X,Y,Z> system
        with vectors of :math:`(1,0,0)`, :math:`(0,1,0)`, and :math:`(0,0,1)`
        """
        if (np.allclose(self.axis_u,
                        (1, 0, 0)) and np.allclose(self.axis_v, (0, 1, 0))
                and np.allclose(self.axis_w, (0, 0, 1))):
            return False
        return True

    @property
    def rotation_matrix(self):
        """Builds a rotation matrix to transform coordinates from their coordinate
        system into a conventional cartesian system. This is built off of the
        three `axis_u`, `axis_v`, and `axis_w` properties; these mapping
        coordinates use the letters U, V, and W (the three letters preceding X,
        Y, and Z in the alphabet) to define the projection of the X, Y, and Z
        durections. These UVW vectors describe the placement and transformation
        of the mesh's coordinate sytem assuming at most 3 directions.

        Why would you want to use these UVW mapping vectors the this
        `rotation_matrix` property? They allow us to define the realationship
        between local and global coordinate systems and provide a tool for
        switching between the two while still maintaing the connectivity of the
        mesh's cells. For a visual example of this, please see the figure in the
        docs for the :class:`~discretize.mixins.vtk_mod.InterfaceVTK`.
        """
        return np.array([self.axis_u, self.axis_v, self.axis_w])

    reference_system = properties.String(
        "The type of coordinate reference frame. Can take on the values " +
        "cartesian, cylindrical, or spherical. Abbreviations of these are allowed.",
        default="cartesian",
        change_case="lower",
    )

    @properties.validator
    def _validate_reference_system(self):
        """Check if the reference system is of a known type."""
        choices = ["cartesian", "cylindrical", "spherical"]
        # Here are a few abbreviations that users can harnes
        abrevs = {
            "car": choices[0],
            "cart": choices[0],
            "cy": choices[1],
            "cyl": choices[1],
            "sph": choices[2],
        }
        # Get the name and fix it if it is abbreviated
        self.reference_system = abrevs.get(self.reference_system,
                                           self.reference_system)
        if self.reference_system not in choices:
            raise ValueError("Coordinate system ({}) unknown.".format(
                self.reference_system))
        return True

    def _parse_location_type(self, location_type):
        if len(location_type) == 0:
            return location_type
        elif location_type[0] == "F":
            if len(location_type) > 1:
                return "faces_" + location_type[-1]
            else:
                return "faces"
        elif location_type[0] == "E":
            if len(location_type) > 1:
                return "edges_" + location_type[-1]
            else:
                return "edges"
        elif location_type[0] == "N":
            return "nodes"
        elif location_type[0] == "C":
            if len(location_type) > 2:
                return "cell_centers_" + location_type[-1]
            else:
                return "cell_centers"
        else:
            return location_type

    # DEPRECATED
    normals = deprecate_property("face_normals",
                                 "normals",
                                 removal_version="1.0.0")
    tangents = deprecate_property("edge_tangents",
                                  "tangents",
                                  removal_version="1.0.0")
    projectEdgeVector = deprecate_method("project_edge_vector",
                                         "projectEdgeVector",
                                         removal_version="1.0.0")
    projectFaceVector = deprecate_method("project_face_vector",
                                         "projectFaceVector",
                                         removal_version="1.0.0")
コード例 #13
0
class BaseTensorMesh(BaseMesh):
    """
    Base class for tensor-product style meshes

    This class contains properites and methods that are common to cartesian
    and cylindrical meshes defined by tensor-produts of vectors describing
    cell spacings.

    Do not use this class directly, instead, inherit it if you plan to develop
    a tensor-style mesh (e.g. a spherical mesh) or use the
    :meth:`discretize.TensorMesh` class to create a cartesian tensor mesh.

    """

    _meshType = "BASETENSOR"
    _aliases = {
        **BaseMesh._aliases,
        **{
            "gridCC": "cell_centers",
            "gridN": "nodes",
            "gridFx": "faces_x",
            "gridFy": "faces_y",
            "gridFz": "faces_z",
            "gridEx": "edges_x",
            "gridEy": "edges_y",
            "gridEz": "edges_z",
        },
    }

    _unitDimensions = [1, 1, 1]

    # properties
    h = properties.Tuple(
        "h is a list containing the cell widths of the tensor mesh in each "
        "dimension.",
        properties.Array(
            "widths of the tensor mesh in a single dimension",
            dtype=float,
            shape=("*", ),
        ),
        min_length=1,
        max_length=3,
        coerce=True,
        required=True,
    )

    def __init__(self, h=None, origin=None, **kwargs):

        h_in = h
        if "x0" in kwargs:
            origin = kwargs.pop('x0')
        origin_in = origin

        # Sanity Checks
        if not isinstance(h_in, (list, tuple)):
            raise TypeError("h_in must be a list, not {}".format(type(h_in)))
        if len(h_in) not in [1, 2, 3]:
            raise ValueError(
                "h_in must be of dimension 1, 2, or 3 not {}".format(
                    len(h_in)))

        # build h
        h = list(range(len(h_in)))
        for i, h_i in enumerate(h_in):
            if is_scalar(h_i) and not isinstance(h_i, np.ndarray):
                # This gives you something over the unit cube.
                h_i = self._unitDimensions[i] * np.ones(int(h_i)) / int(h_i)
            elif isinstance(h_i, (list, tuple)):
                h_i = unpack_widths(h_i)
            if not isinstance(h_i, np.ndarray):
                raise TypeError("h[{0:d}] is not a numpy array.".format(i))
            if len(h_i.shape) != 1:
                raise ValueError(
                    "h[{0:d}] must be a 1D numpy array.".format(i))
            h[i] = h_i[:]  # make a copy.

        # Origin of the mesh
        origin = np.zeros(len(h))

        if origin_in is not None:
            if len(h) != len(origin_in):
                raise ValueError("Dimension mismatch. origin != len(h)")
            for i in range(len(h)):
                x_i, h_i = origin_in[i], h[i]
                if is_scalar(x_i):
                    origin[i] = x_i
                elif x_i == "0":
                    origin[i] = 0.0
                elif x_i == "C":
                    origin[i] = -h_i.sum() * 0.5
                elif x_i == "N":
                    origin[i] = -h_i.sum()
                else:
                    raise Exception(
                        "origin[{0:d}] must be a scalar or '0' to be zero, "
                        "'C' to center, or 'N' to be negative. The input value"
                        " {1} {2} is invalid".format(i, x_i, type(x_i)))

        if "n" in kwargs.keys():
            n = kwargs.pop("n")
            if np.any(n != np.array([x.size for x in h])):
                raise ValueError(
                    "Dimension mismatch. The provided n doesn't h")
        else:
            n = np.array([x.size for x in h])

        super(BaseTensorMesh, self).__init__(n, origin=origin, **kwargs)

        # Ensure h contains 1D vectors
        self.h = [mkvc(x.astype(float)) for x in h]

    @property
    def nodes_x(self):
        """Nodal grid vector (1D) in the x direction."""
        return np.r_[self.origin[0], self.h[0]].cumsum()

    @property
    def nodes_y(self):
        """Nodal grid vector (1D) in the y direction."""
        return None if self.dim < 2 else np.r_[self.origin[1],
                                               self.h[1]].cumsum()

    @property
    def nodes_z(self):
        """Nodal grid vector (1D) in the z direction."""
        return None if self.dim < 3 else np.r_[self.origin[2],
                                               self.h[2]].cumsum()

    @property
    def cell_centers_x(self):
        """Cell-centered grid vector (1D) in the x direction."""
        nodes = self.nodes_x
        return (nodes[1:] + nodes[:-1]) / 2

    @property
    def cell_centers_y(self):
        """Cell-centered grid vector (1D) in the y direction."""
        if self.dim < 2:
            return None
        nodes = self.nodes_y
        return (nodes[1:] + nodes[:-1]) / 2

    @property
    def cell_centers_z(self):
        """Cell-centered grid vector (1D) in the z direction."""
        if self.dim < 3:
            return None
        nodes = self.nodes_z
        return (nodes[1:] + nodes[:-1]) / 2

    @property
    def cell_centers(self):
        """Cell-centered grid."""
        return self._getTensorGrid("cell_centers")

    @property
    def nodes(self):
        """Nodal grid."""
        return self._getTensorGrid("nodes")

    @property
    def h_gridded(self):
        """
        Returns an (nC, dim) numpy array with the widths of all cells in order
        """
        if self.dim == 1:
            return self.h[0][:, None]
        return ndgrid(*self.h)

    @property
    def faces_x(self):
        """Face staggered grid in the x direction."""
        if self.nFx == 0:
            return
        return self._getTensorGrid("faces_x")

    @property
    def faces_y(self):
        """Face staggered grid in the y direction."""
        if self.nFy == 0 or self.dim < 2:
            return
        return self._getTensorGrid("faces_y")

    @property
    def faces_z(self):
        """Face staggered grid in the z direction."""
        if self.nFz == 0 or self.dim < 3:
            return
        return self._getTensorGrid("faces_z")

    @property
    def edges_x(self):
        """Edge staggered grid in the x direction."""
        if self.nEx == 0:
            return
        return self._getTensorGrid("edges_x")

    @property
    def edges_y(self):
        """Edge staggered grid in the y direction."""
        if self.nEy == 0 or self.dim < 2:
            return
        return self._getTensorGrid("edges_y")

    @property
    def edges_z(self):
        """Edge staggered grid in the z direction."""
        if self.nEz == 0 or self.dim < 3:
            return
        return self._getTensorGrid("edges_z")

    def _getTensorGrid(self, key):
        if getattr(self, "_" + key, None) is None:
            setattr(self, "_" + key, ndgrid(self.get_tensor(key)))
        return getattr(self, "_" + key)

    def get_tensor(self, key):
        """Returns a tensor list.

        Parameters
        ----------
        key : str
            Which tensor (see below)

            key can be::

                'CC', 'cell_centers' -> location of cell centers
                'N', 'nodes'         -> location of nodes
                'Fx', 'faces_x'      -> location of faces with an x normal
                'Fy', 'faces_y'      -> location of faces with an y normal
                'Fz', 'faces_z'      -> location of faces with an z normal
                'Ex', 'edges_x'      -> location of edges with an x tangent
                'Ey', 'edges_y'      -> location of edges with an y tangent
                'Ez', 'edges_z'      -> location of edges with an z tangent

        Returns
        -------
        list
            list of the tensors that make up the mesh.

        """
        key = self._parse_location_type(key)

        if key == "faces_x":
            ten = [
                self.nodes_x,
                self.cell_centers_y,
                self.cell_centers_z,
            ]
        elif key == "faces_y":
            ten = [
                self.cell_centers_x,
                self.nodes_y,
                self.cell_centers_z,
            ]
        elif key == "faces_z":
            ten = [
                self.cell_centers_x,
                self.cell_centers_y,
                self.nodes_z,
            ]
        elif key == "edges_x":
            ten = [self.cell_centers_x, self.nodes_y, self.nodes_z]
        elif key == "edges_y":
            ten = [self.nodes_x, self.cell_centers_y, self.nodes_z]
        elif key == "edges_z":
            ten = [self.nodes_x, self.nodes_y, self.cell_centers_z]
        elif key == "cell_centers":
            ten = [
                self.cell_centers_x,
                self.cell_centers_y,
                self.cell_centers_z,
            ]
        elif key == "nodes":
            ten = [self.nodes_x, self.nodes_y, self.nodes_z]
        else:
            raise KeyError(r"Unrecognized key {key}")

        return [t for t in ten if t is not None]

    # --------------- Methods ---------------------

    def is_inside(self, pts, location_type="nodes", **kwargs):
        """
        Determines if a set of points are inside a mesh.

        :param numpy.ndarray pts: Location of points to test
        :rtype: numpy.ndarray
        :return: inside, numpy array of booleans
        """
        if "locType" in kwargs:
            warnings.warn(
                "The locType keyword argument has been deprecated, please use location_type. "
                "This will be removed in discretize 1.0.0",
                DeprecationWarning,
            )
            location_type = kwargs["locType"]
        pts = as_array_n_by_dim(pts, self.dim)

        tensors = self.get_tensor(location_type)

        if location_type[0].lower() == "n" and self._meshType == "CYL":
            # NOTE: for a CYL mesh we add a node to check if we are inside in
            # the radial direction!
            tensors[0] = np.r_[0.0, tensors[0]]
            tensors[1] = np.r_[tensors[1], 2.0 * np.pi]

        inside = np.ones(pts.shape[0], dtype=bool)
        for i, tensor in enumerate(tensors):
            TOL = np.diff(tensor).min() * 1.0e-10
            inside = (inside
                      & (pts[:, i] >= tensor.min() - TOL)
                      & (pts[:, i] <= tensor.max() + TOL))
        return inside

    def _getInterpolationMat(self,
                             loc,
                             location_type="cell_centers",
                             zeros_outside=False):
        """Produces interpolation matrix

        Parameters
        ----------
        loc : numpy.ndarray
            Location of points to interpolate to

        location_type: stc
            What to interpolate

            location_type can be::

                'Ex', 'edges_x'           -> x-component of field defined on x edges
                'Ey', 'edges_y'           -> y-component of field defined on y edges
                'Ez', 'edges_z'           -> z-component of field defined on z edges
                'Fx', 'faces_x'           -> x-component of field defined on x faces
                'Fy', 'faces_y'           -> y-component of field defined on y faces
                'Fz', 'faces_z'           -> z-component of field defined on z faces
                'N', 'nodes'              -> scalar field defined on nodes
                'CC', 'cell_centers'      -> scalar field defined on cell centers
                'CCVx', 'cell_centers_x'  -> x-component of vector field defined on cell centers
                'CCVy', 'cell_centers_y'  -> y-component of vector field defined on cell centers
                'CCVz', 'cell_centers_z'  -> z-component of vector field defined on cell centers

        Returns
        -------
        scipy.sparse.csr_matrix
            M, the interpolation matrix

        """

        loc = as_array_n_by_dim(loc, self.dim)

        if not zeros_outside:
            if not np.all(self.is_inside(loc)):
                raise ValueError("Points outside of mesh")
        else:
            indZeros = np.logical_not(self.is_inside(loc))
            loc[indZeros, :] = np.array(
                [v.mean() for v in self.get_tensor("CC")])

        location_type = self._parse_location_type(location_type)

        if location_type in [
                "faces_x", "faces_y", "faces_z", "edges_x", "edges_y",
                "edges_z"
        ]:
            ind = {"x": 0, "y": 1, "z": 2}[location_type[-1]]
            if self.dim < ind:
                raise ValueError("mesh is not high enough dimension.")
            if "f" in location_type.lower():
                items = (self.nFx, self.nFy, self.nFz)[:self.dim]
            else:
                items = (self.nEx, self.nEy, self.nEz)[:self.dim]
            components = [spzeros(loc.shape[0], n) for n in items]
            components[ind] = interpolation_matrix(
                loc, *self.get_tensor(location_type))
            # remove any zero blocks (hstack complains)
            components = [comp for comp in components if comp.shape[1] > 0]
            Q = sp.hstack(components)

        elif location_type in ["cell_centers", "nodes"]:
            Q = interpolation_matrix(loc, *self.get_tensor(location_type))

        elif location_type in [
                "cell_centers_x", "cell_centers_y", "cell_centers_z"
        ]:
            Q = interpolation_matrix(loc, *self.get_tensor("CC"))
            Z = spzeros(loc.shape[0], self.nC)
            if location_type[-1] == "x":
                Q = sp.hstack([Q, Z, Z])
            elif location_type[-1] == "y":
                Q = sp.hstack([Z, Q, Z])
            elif location_type[-1] == "z":
                Q = sp.hstack([Z, Z, Q])

        else:
            raise NotImplementedError("getInterpolationMat: location_type==" +
                                      location_type + " and mesh.dim==" +
                                      str(self.dim))

        if zeros_outside:
            Q[indZeros, :] = 0

        return Q.tocsr()

    def get_interpolation_matrix(self,
                                 loc,
                                 location_type="cell_centers",
                                 zeros_outside=False,
                                 **kwargs):
        """Produces linear interpolation matrix

        Parameters
        ----------
        loc : numpy.ndarray
            Location of points to interpolate to

        location_type : str
            What to interpolate (see below)

            location_type can be::

                'Ex', 'edges_x'           -> x-component of field defined on x edges
                'Ey', 'edges_y'           -> y-component of field defined on y edges
                'Ez', 'edges_z'           -> z-component of field defined on z edges
                'Fx', 'faces_x'           -> x-component of field defined on x faces
                'Fy', 'faces_y'           -> y-component of field defined on y faces
                'Fz', 'faces_z'           -> z-component of field defined on z faces
                'N', 'nodes'              -> scalar field defined on nodes
                'CC', 'cell_centers'      -> scalar field defined on cell centers
                'CCVx', 'cell_centers_x'  -> x-component of vector field defined on cell centers
                'CCVy', 'cell_centers_y'  -> y-component of vector field defined on cell centers
                'CCVz', 'cell_centers_z'  -> z-component of vector field defined on cell centers

        Returns
        -------

        scipy.sparse.csr_matrix
            M, the interpolation matrix

        """
        if "locType" in kwargs:
            warnings.warn(
                "The locType keyword argument has been deprecated, please use location_type. "
                "This will be removed in discretize 1.0.0",
                DeprecationWarning,
            )
            location_type = kwargs["locType"]
        if "zerosOutside" in kwargs:
            warnings.warn(
                "The zerosOutside keyword argument has been deprecated, please use zeros_outside. "
                "This will be removed in discretize 1.0.0",
                DeprecationWarning,
            )
            zeros_outside = kwargs["zerosOutside"]
        return self._getInterpolationMat(loc, location_type, zeros_outside)

    def _fastInnerProduct(self,
                          projection_type,
                          model=None,
                          invert_model=False,
                          invert_matrix=False):
        """Fast version of getFaceInnerProduct.
            This does not handle the case of a full tensor property.

        Parameters
        ----------

        model : numpy.array
            material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6))

        projection_type : str
            'edges' or 'faces'

        returnP : bool
            returns the projection matrices

        invert_model : bool
            inverts the material property

        invert_matrix : bool
            inverts the matrix

        Returns
        -------
        scipy.sparse.csr_matrix
            M, the inner product matrix (nF, nF)

        """
        projection_type = projection_type[0].upper()
        if projection_type not in ["F", "E"]:
            raise ValueError(
                "projection_type must be 'F' for faces or 'E' for edges")

        if model is None:
            model = np.ones(self.nC)

        if invert_model:
            model = 1.0 / model

        if is_scalar(model):
            model = model * np.ones(self.nC)

        # number of elements we are averaging (equals dim for regular
        # meshes, but for cyl, where we use symmetry, it is 1 for edge
        # variables and 2 for face variables)
        if self._meshType == "CYL":
            shape = getattr(self, "vn" + projection_type)
            n_elements = sum([1 if x != 0 else 0 for x in shape])
        else:
            n_elements = self.dim

        # Isotropic? or anisotropic?
        if model.size == self.nC:
            Av = getattr(self, "ave" + projection_type + "2CC")
            Vprop = self.cell_volumes * mkvc(model)
            M = n_elements * sdiag(Av.T * Vprop)

        elif model.size == self.nC * self.dim:
            Av = getattr(self, "ave" + projection_type + "2CCV")

            # if cyl, then only certain components are relevant due to symmetry
            # for faces, x, z matters, for edges, y (which is theta) matters
            if self._meshType == "CYL":
                if projection_type == "E":
                    model = model[:,
                                  1]  # this is the action of a projection mat
                elif projection_type == "F":
                    model = model[:, [0, 2]]

            V = sp.kron(sp.identity(n_elements), sdiag(self.cell_volumes))
            M = sdiag(Av.T * V * mkvc(model))
        else:
            return None

        if invert_matrix:
            return sdinv(M)
        else:
            return M

    def _fastInnerProductDeriv(self,
                               projection_type,
                               model,
                               invert_model=False,
                               invert_matrix=False):
        """

        Parameters
        ----------

        projection_type : str
            'E' or 'F'

        tensorType : TensorType
            type of the tensor

        invert_model : bool
            inverts the material property

        invert_matrix : bool
            inverts the matrix


        Returns
        -------
        function
            dMdmu, the derivative of the inner product matrix

        """

        projection_type = projection_type[0].upper()
        if projection_type not in ["F", "E"]:
            raise ValueError(
                "projection_type must be 'F' for faces or 'E' for edges")

        tensorType = TensorType(self, model)

        dMdprop = None

        if invert_matrix or invert_model:
            MI = self._fastInnerProduct(projection_type,
                                        model,
                                        invert_model=invert_model,
                                        invert_matrix=invert_matrix)

        # number of elements we are averaging (equals dim for regular
        # meshes, but for cyl, where we use symmetry, it is 1 for edge
        # variables and 2 for face variables)
        if self._meshType == "CYL":
            shape = getattr(self, "vn" + projection_type)
            n_elements = sum([1 if x != 0 else 0 for x in shape])
        else:
            n_elements = self.dim

        if tensorType == 0:  # isotropic, constant
            Av = getattr(self, "ave" + projection_type + "2CC")
            V = sdiag(self.cell_volumes)
            ones = sp.csr_matrix(
                (np.ones(self.nC), (range(self.nC), np.zeros(self.nC))),
                shape=(self.nC, 1),
            )
            if not invert_matrix and not invert_model:
                dMdprop = n_elements * Av.T * V * ones
            elif invert_matrix and invert_model:
                dMdprop = n_elements * (sdiag(MI.diagonal()**2) * Av.T * V *
                                        ones * sdiag(1.0 / model**2))
            elif invert_model:
                dMdprop = n_elements * Av.T * V * sdiag(-1.0 / model**2)
            elif invert_matrix:
                dMdprop = n_elements * (sdiag(-MI.diagonal()**2) * Av.T * V)

        elif tensorType == 1:  # isotropic, variable in space
            Av = getattr(self, "ave" + projection_type + "2CC")
            V = sdiag(self.cell_volumes)
            if not invert_matrix and not invert_model:
                dMdprop = n_elements * Av.T * V
            elif invert_matrix and invert_model:
                dMdprop = n_elements * (sdiag(MI.diagonal()**2) * Av.T * V *
                                        sdiag(1.0 / model**2))
            elif invert_model:
                dMdprop = n_elements * Av.T * V * sdiag(-1.0 / model**2)
            elif invert_matrix:
                dMdprop = n_elements * (sdiag(-MI.diagonal()**2) * Av.T * V)

        elif tensorType == 2:  # anisotropic
            Av = getattr(self, "ave" + projection_type + "2CCV")
            V = sp.kron(sp.identity(self.dim), sdiag(self.cell_volumes))

            if self._meshType == "CYL":
                Zero = sp.csr_matrix((self.nC, self.nC))
                Eye = sp.eye(self.nC)
                if projection_type == "E":
                    P = sp.hstack([Zero, Eye, Zero])
                    # print(P.todense())
                elif projection_type == "F":
                    P = sp.vstack([
                        sp.hstack([Eye, Zero, Zero]),
                        sp.hstack([Zero, Zero, Eye])
                    ])
                    # print(P.todense())
            else:
                P = sp.eye(self.nC * self.dim)

            if not invert_matrix and not invert_model:
                dMdprop = Av.T * P * V
            elif invert_matrix and invert_model:
                dMdprop = (sdiag(MI.diagonal()**2) * Av.T * P * V *
                           sdiag(1.0 / model**2))
            elif invert_model:
                dMdprop = Av.T * P * V * sdiag(-1.0 / model**2)
            elif invert_matrix:
                dMdprop = sdiag(-MI.diagonal()**2) * Av.T * P * V

        if dMdprop is not None:

            def innerProductDeriv(v=None):
                if v is None:
                    warnings.warn(
                        "Depreciation Warning: TensorMesh.innerProductDeriv."
                        " You should be supplying a vector. "
                        "Use: sdiag(u)*dMdprop",
                        DeprecationWarning,
                    )
                    return dMdprop
                return sdiag(v) * dMdprop

            return innerProductDeriv
        else:
            return None

    # DEPRECATED
    @property
    def hx(self):
        """Width of cells in the x direction

        Returns
        -------
        numpy.ndarray

        .. deprecated:: 0.5.0
          `hx` will be removed in discretize 1.0.0 to reduce namespace clutter,
          please use `mesh.h[0]`.
        """
        warnings.warn("hx has been deprecated, please access as mesh.h[0]",
                      DeprecationWarning)
        return self.h[0]

    @property
    def hy(self):
        """Width of cells in the y direction

        Returns
        -------
        numpy.ndarray or None

        .. deprecated:: 0.5.0
          `hy` will be removed in discretize 1.0.0 to reduce namespace clutter,
          please use `mesh.h[1]`.
        """
        warnings.warn("hy has been deprecated, please access as mesh.h[1]",
                      DeprecationWarning)
        return None if self.dim < 2 else self.h[1]

    @property
    def hz(self):
        """Width of cells in the z direction

        Returns
        -------
        numpy.ndarray or None

        .. deprecated:: 0.5.0
          `hz` will be removed in discretize 1.0.0 to reduce namespace clutter,
          please use `mesh.h[2]`.
        """
        warnings.warn("hz has been deprecated, please access as mesh.h[2]",
                      DeprecationWarning)
        return None if self.dim < 3 else self.h[2]

    vectorNx = deprecate_property("nodes_x",
                                  "vectorNx",
                                  removal_version="1.0.0")
    vectorNy = deprecate_property("nodes_y",
                                  "vectorNy",
                                  removal_version="1.0.0")
    vectorNz = deprecate_property("nodes_z",
                                  "vectorNz",
                                  removal_version="1.0.0")
    vectorCCx = deprecate_property("cell_centers_x",
                                   "vectorCCx",
                                   removal_version="1.0.0")
    vectorCCy = deprecate_property("cell_centers_y",
                                   "vectorCCy",
                                   removal_version="1.0.0")
    vectorCCz = deprecate_property("cell_centers_z",
                                   "vectorCCz",
                                   removal_version="1.0.0")
    getInterpolationMat = deprecate_method("get_interpolation_matrix",
                                           "getInterpolationMat",
                                           removal_version="1.0.0")
    isInside = deprecate_method("is_inside",
                                "isInside",
                                removal_version="1.0.0")
    getTensor = deprecate_method("get_tensor",
                                 "getTensor",
                                 removal_version="1.0.0")
コード例 #14
0
ファイル: test_container.py プロジェクト: grosenkj/properties
 class HasColorTuple(properties.HasProperties):
     ccc = properties.Tuple('tuple of colors', properties.Color(''))