예제 #1
0
def star_shape_cell_centers(g):

    if g.dim < 2:
        return g.cell_centers

    faces, _, sgn = sps.find(g.cell_faces)
    nodes, _, _ = sps.find(g.face_nodes)

    xn = g.nodes
    if g.dim == 2:
        R = cg.project_plane_matrix(xn)
        xn = np.dot(R, xn)

    cell_centers = np.zeros((3, g.num_cells))
    for c in np.arange(g.num_cells):
        loc = slice(g.cell_faces.indptr[c], g.cell_faces.indptr[c + 1])
        faces_loc = faces[loc]
        loc_n = g.face_nodes.indptr[faces_loc]
        normal = np.multiply(
            sgn[loc],
            np.divide(g.face_normals[:, faces_loc], g.face_areas[faces_loc]))

        x0, x1 = xn[:, nodes[loc_n]], xn[:, nodes[loc_n + 1]]
        coords = np.concatenate((x0, x1), axis=1)
        cell_centers[:, c] = half_space_pt(normal, (x1 + x0) / 2., coords)

    if g.dim == 2:
        cell_centers = np.dot(R.T, cell_centers)

    return cell_centers
예제 #2
0
    def test_project_plane( self ):
        pts = np.array( [ [  2.,  0.,  1.,  1. ],
                          [  1., -2., -1.,  1. ],
                          [ -1.,  0.,  2., -8. ] ] )
        R = cg.project_plane_matrix( pts )
        P_pts = np.dot( R, pts )

        assert np.allclose( P_pts[2,:], -1.15470054 * np.ones(4) )
예제 #3
0
def _create_embedded_2d_grid(loc_coord, glob_id):
    """
    Create a 2d grid that is embedded in a 3d grid.
    """
    loc_center = np.mean(loc_coord, axis=1).reshape((-1, 1))
    loc_coord -= loc_center
    # Check that the points indeed form a line
    assert cg.is_planar(loc_coord)
    # Find the tangent of the line
    # Projection matrix
    rot = cg.project_plane_matrix(loc_coord)
    loc_coord_2d = rot.dot(loc_coord)
    # The points are now 2d along two of the coordinate axis, but we
    # don't know which yet. Find this.
    sum_coord = np.sum(np.abs(loc_coord_2d), axis=1)
    active_dimension = np.logical_not(np.isclose(sum_coord, 0))
    # Check that we are indeed in 2d
    assert np.sum(active_dimension) == 2
    # Sort nodes, and create grid
    coord_2d = loc_coord_2d[active_dimension]
    sort_ind = np.lexsort((coord_2d[0], coord_2d[1]))
    sorted_coord = coord_2d[:, sort_ind]
    sorted_coord = np.round(sorted_coord * 1e10) / 1e10
    unique_x = np.unique(sorted_coord[0])
    unique_y = np.unique(sorted_coord[1])
    # assert unique_x.size == unique_y.size
    g = structured.TensorGrid(unique_x, unique_y)
    assert np.all(g.nodes[0:2] - sorted_coord == 0)

    # Project back to active dimension
    nodes = np.zeros(g.nodes.shape)
    nodes[active_dimension] = g.nodes[0:2]
    g.nodes = nodes
    # Project back again to 3d coordinates

    irot = rot.transpose()
    g.nodes = irot.dot(g.nodes)
    g.nodes += loc_center

    # Add mapping to global point numbers
    g.global_point_ind = glob_id[sort_ind]
    return g
예제 #4
0
def sort_point_plane(pts, centre, normal=None):
    """ Sort the points which lie on a plane.

    The algorithm assumes a star-shaped disposition of the points with respect
    the centre.

    Parameters:
    pts: np.ndarray, 3xn, the points.
    centre: np.ndarray, 3x1, the face centre.
    normal: (optional) the normal of the plane, otherwise three points are
    required.

    Returns:
    map_pts: np.array, 1xn, sorted point ids.

    """
    R = project_plane_matrix(pts, normal)
    pts = np.array([np.dot(R, p) for p in pts.T]).T
    centre = np.dot(R, centre)
    delta = np.array([p - centre for p in pts.T]).T[0:2, :]
    delta = np.array([d / np.linalg.norm(d) for d in delta.T]).T
    return np.argsort(np.arctan2(*delta))
예제 #5
0
    def __compute_geometry_2d(self, is_embedded):
        "Compute 2D geometry, with method motivated by similar MRST function"

        if is_embedded:
            R = cg.project_plane_matrix(self.nodes, check_planar=False)
            self.nodes = np.dot(R, self.nodes)

        fn = self.face_nodes.indices
        edge1 = fn[::2]
        edge2 = fn[1::2]

        xe1 = self.nodes[:, edge1]
        xe2 = self.nodes[:, edge2]

        edge_length_x = xe2[0] - xe1[0]
        edge_length_y = xe2[1] - xe1[1]
        edge_length_z = xe2[2] - xe1[2]
        self.face_areas = np.sqrt(
            np.power(edge_length_x, 2) + np.power(edge_length_y, 2) +
            np.power(edge_length_z, 2))
        self.face_centers = 0.5 * (xe1 + xe2)
        n = edge_length_z.shape[0]
        self.face_normals = np.vstack(
            (edge_length_y, -edge_length_x, np.zeros(n)))

        cell_faces, cellno = self.cell_faces.nonzero()
        cx = np.bincount(cellno, weights=self.face_centers[0, cell_faces])
        cy = np.bincount(cellno, weights=self.face_centers[1, cell_faces])
        cz = np.bincount(cellno, weights=self.face_centers[2, cell_faces])
        self.cell_centers = np.vstack((cx, cy, cz)) / np.bincount(cellno)

        a = xe1[:, cell_faces] - self.cell_centers[:, cellno]
        b = xe2[:, cell_faces] - self.cell_centers[:, cellno]

        sub_volumes = 0.5 * np.abs(a[0] * b[1] - a[1] * b[0])
        self.cell_volumes = np.bincount(cellno, weights=sub_volumes)

        sub_centroids = (self.cell_centers[:, cellno] +
                         2 * self.face_centers[:, cell_faces]) / 3

        ccx = np.bincount(cellno, weights=sub_volumes * sub_centroids[0])
        ccy = np.bincount(cellno, weights=sub_volumes * sub_centroids[1])
        ccz = np.bincount(cellno, weights=sub_volumes * sub_centroids[2])

        self.cell_centers = np.vstack((ccx, ccy, ccz)) / self.cell_volumes

        # Ensure that normal vector direction corresponds with sign convention
        # in self.cellFaces
        def nrm(u):
            return np.sqrt(u[0] * u[0] + u[1] * u[1] + u[2] * u[2])

        [fi, ci, val] = sps.find(self.cell_faces)
        _, idx = np.unique(fi, return_index=True)
        sgn = val[idx]
        fc = self.face_centers[:, fi[idx]]
        cc = self.cell_centers[:, ci[idx]]
        v = fc - cc
        # Prolong the vector from cell to face center in the direction of the
        # normal vector. If the prolonged vector is shorter, the normal should
        # flipped
        vn = v + nrm(v) * self.face_normals[:, fi[idx]] * 0.001
        flip = np.logical_or(np.logical_and(nrm(v) > nrm(vn), sgn > 0),
                             np.logical_and(nrm(v) < nrm(vn), sgn < 0))
        self.face_normals[:, flip] *= -1

        if is_embedded:
            self.nodes = np.dot(R.T, self.nodes)
            self.face_normals = np.dot(R.T, self.face_normals)
            self.face_centers = np.dot(R.T, self.face_centers)
            self.cell_centers = np.dot(R.T, self.cell_centers)
예제 #6
0
 def proj_pts(p, cc):
     rot = cg.project_plane_matrix(p - cc)
     return rot.dot(p - cc)[:2]
예제 #7
0
def cut_fracture_by_plane(
    main_frac, other_frac, reference_point, tol=1e-4, recompute_center=True, **kwargs
):
    """ Cut a fracture by a plane, and confine it to one side of the plane.

    Intended use is to confine abutting fractures (T-intersections) to one side
    of the fracture it hits. This is done by deleting points on the abutting
    fracture.

    Parameters:
        main_frac (Fracture): The fracture to be cut.
        other_frac (Fracture): The fracture that defines the confining plane.
        reference_point (np.array, nd): Point on the main frature that defines
            which side should be kept. Will typically be the other point of the
            exposed line.

    Returns:
        Fracture: A copy of the main fracture, cut by the other fracture.
        double: In cases where one interseciton point extends beyond the other
            fracture, this is the distance between the center of the other
            fracture and the intersection point. If both intersections are
            within the polygon, None will be returned.

    Raises:
        ValueError if the points in the other fracture is too close. This could
        probably be handled by a scaling of coordinates, it is tacitly assumed
        that we're working in something resembling the unit box.

    """
    reference_point = reference_point.reshape((-1, 1))
    if reference_point.size == 2:
        reference_point = np.vstack((reference_point, 0))

    # First determine extent of the main fracture
    main_min = main_frac.p.min(axis=1)
    main_max = main_frac.p.max(axis=1)

    # Equation for the plane through the other fracture, on the form
    #  n_x(x-c_x) + n_y(y-c_y) + n_z(z-c_z) = 0
    n = cg.compute_normal(other_frac.p).reshape((-1, 1))
    c = other_frac.center

    # max and min coordinates that extends outside the main fracture
    main_min -= 1
    main_max += 1

    # Define points in the plane of the second fracture with min and max
    # coordinates picked from the main fracture.
    # The below tricks with indices are needed to find a dimension with a
    # non-zero gradient of the plane, so that we can divide safely.
    # Implementation note: It might have been possible to do this with a
    # rotation to the natural plane of the other fracture, but it is not clear
    # this will really be simpler.

    # Not sure about the tolerance here
    # We should perhaps do a scaling of coordinates.
    non_zero = np.where(np.abs(n) > 1e-8)[0]
    if non_zero.size == 0:
        raise ValueError("Could not compute normal vector of other fracture")
    ind = np.setdiff1d(np.arange(3), non_zero[0])
    i0 = ind[0]
    i1 = ind[1]
    i2 = non_zero[0]

    p = np.zeros((3, 4))
    # A for-loop might have been possible here.
    p[i0, 0] = main_min[i0]
    p[i1, 0] = main_min[i1]
    p[i2, 0] = (
        c[i2]
        - (n[i0] * (main_min[i0] - c[i0]) + n[i1] * (main_min[i1] - c[i1])) / n[i2]
    )

    p[i0, 1] = main_max[i0]
    p[i1, 1] = main_min[i1]
    p[i2, 1] = (
        c[i2]
        - (n[i0] * (main_max[i0] - c[i0]) + n[i1] * (main_min[i1] - c[i1])) / n[i2]
    )

    p[i0, 2] = main_max[i0]
    p[i1, 2] = main_max[i1]
    p[i2, 2] = (
        c[i2]
        - (n[i0] * (main_max[i0] - c[i0]) + n[i1] * (main_max[i1] - c[i1])) / n[i2]
    )

    p[i0, 3] = main_min[i0]
    p[i1, 3] = main_max[i1]
    p[i2, 3] = (
        c[i2]
        - (n[i0] * (main_min[i0] - c[i0]) + n[i1] * (main_max[i1] - c[i1])) / n[i2]
    )

    # Create an auxiliary fracture that spans the same plane as the other
    # fracture, and with a larger extension than the main fracture.
    aux_frac = Fracture(p, check_convexity=False)

    isect_pt, _, _ = main_frac.intersects(aux_frac, tol)

    # The extension of the abutting fracture will cross the other fracture
    # with a certain angle to the vertical. If the other fracture is rotated
    # with a similar angle, point contact results.
    if isect_pt.size == 0:
        warnings.warn(
            """No intersection found in cutting of fractures. This is
                         likely caused by an unfortunate combination of
                         extrusion and rotation angles, which created fractures
                         that only intersect in a single point (the outcrop
                         plane. Will try to continue, but this may cause
                         trouble for meshing etc."""
        )
        return main_frac, None

    # Next step is to eliminate points in the main fracture that are on the
    # wrong side of the other fracture.
    v = main_frac.p - other_frac.center.reshape((-1, 1))
    sgn = np.sign(np.sum(v * n, axis=0))
    ref_v = reference_point - other_frac.center.reshape((-1, 1))
    right_sign = np.sign(np.sum(ref_v * n, axis=0))

    # Eliminate points that are on the other side.
    eliminate = np.where(sgn * right_sign < 0)[0]
    main_frac.remove_points(eliminate)

    # Add intersection points on the main fracture. One of these may already be
    # present, as the point of extrusion, but add_point will uniquify the point
    # cloud.
    # We add the points after elimination, to ensure that the points on the
    # plane are present in the final fracture.
    main_frac.add_points(isect_pt, check_convexity=False)

    if recompute_center:
        main_frac.compute_centroid()

    # If the main fracture is too large compared to the other, the cut line
    # will extend beyond the confining plane. In these cases, compute the
    # distance from the fracture center to the outside intersection point. This
    # can be used to extend the other fracture so as to avoid such strange
    # configurations.
    other_center = other_frac.center.reshape((-1, 1))
    other_p = other_frac.p
    rot = cg.project_plane_matrix(other_p - other_center)

    other_rot = rot.dot(other_p - other_center)[:2]
    isect_rot = rot.dot(isect_pt - other_center)[:2]

    is_inside = cg.is_inside_polygon(other_rot, isect_rot, tol, default=True)
    # At one point (the exposed point) must be in the polygon of the other
    # fracture.
    assert is_inside.any()

    if not is_inside.all():
        hit = np.logical_not(is_inside)
        r = np.sqrt(np.sum(isect_pt[:, hit] ** 2))
        return main_frac, r
    else:
        return main_frac, None