コード例 #1
0
    def test_lines_no_crossing(self):
        p = np.array([[-1, 1, 0, 0], [0, 0, -1, 1]])

        lines = np.array([[0, 1], [2, 3]])
        box = np.array([[2], [2]])
        new_pts, new_lines = cg.remove_edge_crossings(p, lines, box=box)
        self.assertTrue(np.allclose(new_pts, p))
        self.assertTrue(np.allclose(new_lines, lines))
コード例 #2
0
    def test_split_segment_partly_overlapping(self):
        p = np.array([[0, 1, 2, 3], [0, 0, 0, 0]])
        lines = np.array([[0, 2], [1, 3]]).T
        box = np.array([[1], [1]])

        new_pts, new_lines = cg.remove_edge_crossings(p, lines, box=box)

        p_known = cg.snap_to_grid(p, box=box)
        lines_known = np.array([[0, 1], [1, 2], [2, 3]]).T

        self.assertTrue(np.allclose(new_pts, p_known))
        self.assertTrue(np.allclose(new_lines, lines_known))
コード例 #3
0
    def test_three_lines_one_crossing(self):
        # This test gave an error at some point
        p = np.array([[0., 0.5, 0.3, 1., 0.3, 0.5], [2 / 3, 0.3, 0.3, 2 / 3, 0.5, 0.5]])
        lines = np.array([[0, 3], [2, 5], [1, 4]]).T
        box = np.array([[1], [2]])

        new_pts, new_lines = cg.remove_edge_crossings(p, lines, box=box)
        p_known = np.hstack((p, np.array([[0.4], [0.4]])))
        p_known = cg.snap_to_grid(p_known, box=box)
        lines_known = np.array([[0, 3], [2, 6], [6, 5], [1, 6], [6, 4]]).T
        self.assertTrue(np.allclose(new_pts, p_known))
        self.assertTrue(np.allclose(new_lines, lines_known))
コード例 #4
0
    def test_three_lines_no_crossing(self):
        # This test gave an error at some point
        p = np.array(
            [[0., 0., 0.3, 1., 1., 0.5], [2 / 3, 1 / .7, 0.3, 2 / 3, 1 / .7, 0.5]]
        )
        lines = np.array([[0, 3], [1, 4], [2, 5]]).T
        box = np.array([[1], [2]])

        new_pts, new_lines = cg.remove_edge_crossings(p, lines, box=box)
        p_known = cg.snap_to_grid(p, box=box)
        self.assertTrue(np.allclose(new_pts, p_known))
        self.assertTrue(np.allclose(new_lines, lines))
コード例 #5
0
    def test_split_segment_fully_overlapping_switched_order(self):
        p = np.array([[0, 1, 2, 3], [0, 0, 0, 0]])
        lines = np.array([[0, 3], [2, 1]]).T
        box = np.array([[1], [1]])

        new_pts, new_lines = cg.remove_edge_crossings(p, lines, box=box)
        new_lines = np.sort(new_lines, axis=0)

        p_known = cg.snap_to_grid(p, box=box)
        lines_known = np.array([[0, 1], [1, 2], [2, 3]]).T

        self.assertTrue(np.allclose(new_pts, p_known))
        self.assertTrue(np.allclose(new_lines, lines_known))
コード例 #6
0
    def test_lines_crossing_origin(self):
        p = np.array([[-1, 1, 0, 0], [0, 0, -1, 1]])
        lines = np.array([[0, 2], [1, 3], [1, 2], [3, 4]])
        box = np.array([[2], [2]])

        new_pts, new_lines = cg.remove_edge_crossings(p, lines, box=box)

        p_known = np.hstack((p, np.array([[0], [0]])))
        p_known = cg.snap_to_grid(p_known, box=box)

        lines_known = np.array([[0, 4, 2, 4], [4, 1, 4, 3], [1, 1, 2, 2], [3, 3, 4, 4]])

        self.assertTrue(np.allclose(new_pts, p_known))
        self.assertTrue(np.allclose(new_lines, lines_known))
コード例 #7
0
    def test_split_segment_partly_overlapping_switched_order(self):
        # Same partly overlapping test, but switch order of edge-point
        # connection. Should make no difference
        p = np.array([[0, 1, 2, 3], [0, 0, 0, 0]])
        lines = np.array([[0, 2], [3, 1]]).T
        box = np.array([[1], [1]])

        new_pts, new_lines = cg.remove_edge_crossings(p, lines, box=box)

        new_lines = np.sort(new_lines, axis=0)
        p_known = cg.snap_to_grid(p, box=box)
        lines_known = np.array([[0, 1], [1, 2], [2, 3]]).T

        self.assertTrue(np.allclose(new_pts, p_known))
        self.assertTrue(np.allclose(new_lines, lines_known))
コード例 #8
0
def triangle_grid(fracs, domain, do_snap_to_grid=False, **kwargs):
    """
    Generate a gmsh grid in a 2D domain with fractures.

    The function uses modified versions of pygmsh and mesh_io,
    both downloaded from github.

    To be added:
    Functionality for tuning gmsh, including grid size, refinements, etc.

    Parameters
    ----------
    fracs: (dictionary) Two fields: points (2 x num_points) np.ndarray,
        edges (2 x num_lines) connections between points, defines fractures.
    box: (dictionary) keys xmin, xmax, ymin, ymax, [together bounding box
        for the domain]
    do_snap_to_grid (boolean, optional): If true, points are snapped to an
        underlying Cartesian grid with resolution tol before geometry
        computations are carried out. This used to be the standard, but
        indications are it is better not to do this. This keyword construct is
        a stop-gap measure to invoke the old functionality if desired. This
        option will most likely dissapear in the future.
    **kwargs: To be explored.

    Returns
    -------
    list (length 3): For each dimension (2 -> 0), a list of all grids in
        that dimension.

    Examples
    p = np.array([[-1, 1, 0, 0], [0, 0, -1, 1]])
    lines = np.array([[0, 2], [1, 3]])
    char_h = 0.5 * np.ones(p.shape[1])
    tags = np.array([1, 3])
    fracs = {'points': p, 'edges': lines}
    box = {'xmin': -2, 'xmax': 2, 'ymin': -2, 'ymax': 2}
    g = triangle_grid(fracs, box)

    """
    logger.info("Create 2d mesh")

    # Verbosity level
    verbose = kwargs.get("verbose", 1)

    # File name for communication with gmsh
    file_name = kwargs.get("file_name", "gmsh_frac_file")
    kwargs.pop("file_name", str())

    tol = kwargs.get("tol", 1e-4)

    in_file = file_name + ".geo"
    out_file = file_name + ".msh"

    # Pick out fracture points, and their connections
    frac_pts = fracs["points"]
    frac_con = fracs["edges"]

    # Unified description of points and lines for domain, and fractures
    pts_all, lines, domain_pts = __merge_domain_fracs_2d(
        domain, frac_pts, frac_con)

    # Snap to underlying grid before comparing points
    if do_snap_to_grid:
        pts_all = cg.snap_to_grid(pts_all, tol)

    assert np.all(np.diff(lines[:2], axis=0) != 0)

    # Ensure unique description of points
    pts_all, _, old_2_new = unique_columns_tol(pts_all, tol=tol)
    lines[:2] = old_2_new[lines[:2]]
    to_remove = np.where(lines[0, :] == lines[1, :])[0]
    lines = np.delete(lines, to_remove, axis=1)

    # In some cases the fractures and boundaries impose the same constraint
    # twice, although it is not clear why. Avoid this by uniquifying the lines.
    # This may disturb the line tags in lines[2], but we should not be
    # dependent on those.
    li = np.sort(lines[:2], axis=0)
    _, new_2_old, old_2_new = unique_columns_tol(li, tol=tol)
    lines = lines[:, new_2_old]

    assert np.all(np.diff(lines[:2], axis=0) != 0)

    # We split all fracture intersections so that the new lines do not
    # intersect, except possible at the end points
    logger.info("Remove edge crossings")
    tm = time.time()
    pts_split, lines_split = cg.remove_edge_crossings(pts_all,
                                                      lines,
                                                      tol=tol,
                                                      snap=do_snap_to_grid)
    logger.info("Done. Elapsed time " + str(time.time() - tm))

    # Ensure unique description of points
    if do_snap_to_grid:
        pts_split = cg.snap_to_grid(pts_split, tol)

    pts_split, _, old_2_new = unique_columns_tol(pts_split, tol=tol)
    lines_split[:2] = old_2_new[lines_split[:2]]
    to_remove = np.where(lines[0, :] == lines[1, :])[0]
    lines = np.delete(lines, to_remove, axis=1)

    # Remove lines with the same start and end-point.
    # This can be caused by L-intersections, or possibly also if the two
    # endpoints are considered equal under tolerance tol.
    remove_line_ind = np.where(np.diff(lines_split[:2], axis=0)[0] == 0)[0]
    lines_split = np.delete(lines_split, remove_line_ind, axis=1)

    # TODO: This operation may leave points that are not referenced by any
    # lines. We should probably delete these.

    # We find the end points that are shared by more than one intersection
    intersections = __find_intersection_points(lines_split)

    # Gridding size
    if "mesh_size_frac" in kwargs.keys():
        # Tag points at the domain corners
        logger.info("Determine mesh size")
        tm = time.time()
        boundary_pt_ind = ismember_rows(pts_split, domain_pts, sort=False)[0]
        mesh_size, pts_split, lines_split = tools.determine_mesh_size(
            pts_split, boundary_pt_ind, lines_split, **kwargs)

        logger.info("Done. Elapsed time " + str(time.time() - tm))
    else:
        mesh_size = None

    # gmsh options

    meshing_algorithm = kwargs.get("meshing_algorithm")

    # Create a writer of gmsh .geo-files
    gw = gmsh_interface.GmshWriter(
        pts_split,
        lines_split,
        domain=domain,
        mesh_size=mesh_size,
        intersection_points=intersections,
        meshing_algorithm=meshing_algorithm,
    )
    gw.write_geo(in_file)

    triangle_grid_run_gmsh(file_name, **kwargs)
    return triangle_grid_from_gmsh(file_name, **kwargs)
コード例 #9
0
def triangle_grid(fracs, domain, **kwargs):
    """
    Generate a gmsh grid in a 2D domain with fractures.

    The function uses modified versions of pygmsh and mesh_io,
    both downloaded from github.

    To be added:
    Functionality for tuning gmsh, including grid size, refinements, etc.

    Parameters
    ----------
    fracs: (dictionary) Two fields: points (2 x num_points) np.ndarray,
        edges (2 x num_lines) connections between points, defines fractures.
    box: (dictionary) keys xmin, xmax, ymin, ymax, [together bounding box
        for the domain]
    **kwargs: To be explored.

    Returns
    -------
    list (length 3): For each dimension (2 -> 0), a list of all grids in
        that dimension.

    Examples
    p = np.array([[-1, 1, 0, 0], [0, 0, -1, 1]])
    lines = np.array([[0, 2], [1, 3]])
    char_h = 0.5 * np.ones(p.shape[1])
    tags = np.array([1, 3])
    fracs = {'points': p, 'edges': lines}
    box = {'xmin': -2, 'xmax': 2, 'ymin': -2, 'ymax': 2}
    g = triangle_grid(fracs, box)

    """
    # Verbosity level
    verbose = kwargs.get('verbose', 1)

    # File name for communication with gmsh
    file_name = kwargs.get('file_name', 'gmsh_frac_file')
    kwargs.pop('file_name', str())

    tol = kwargs.get('tol', 1e-4)

    in_file = file_name + '.geo'
    out_file = file_name + '.msh'

    # Pick out fracture points, and their connections
    frac_pts = fracs['points']
    frac_con = fracs['edges']

    # Unified description of points and lines for domain, and fractures
    pts_all, lines = __merge_domain_fracs_2d(domain, frac_pts, frac_con)

    # Snap to underlying grid before comparing points
    pts_all = cg.snap_to_grid(pts_all, tol)

    assert np.all(np.diff(lines[:2], axis=0) != 0)

    # Ensure unique description of points
    pts_all, _, old_2_new = unique_columns_tol(pts_all, tol=tol)
    lines[:2] = old_2_new[lines[:2]]
    to_remove = np.where(lines[0, :] == lines[1, :])[0]
    lines = np.delete(lines, to_remove, axis=1)

    # In some cases the fractures and boundaries impose the same constraint
    # twice, although it is not clear why. Avoid this by uniquifying the lines.
    # This may disturb the line tags in lines[2], but we should not be
    # dependent on those.
    li = np.sort(lines[:2], axis=0)
    li_unique, new_2_old, old_2_new = unique_columns_tol(li)
    lines = lines[:, new_2_old]

    assert np.all(np.diff(lines[:2], axis=0) != 0)

    # We split all fracture intersections so that the new lines do not
    # intersect, except possible at the end points
    pts_split, lines_split = cg.remove_edge_crossings(pts_all, lines, tol=tol)

    # Ensure unique description of points
    pts_split = cg.snap_to_grid(pts_split, tol)
    pts_split, _, old_2_new = unique_columns_tol(pts_split, tol=tol)
    lines_split[:2] = old_2_new[lines_split[:2]]
    to_remove = np.where(lines[0, :] == lines[1, :])[0]
    lines = np.delete(lines, to_remove, axis=1)

    # Remove lines with the same start and end-point.
    # This can be caused by L-intersections, or possibly also if the two
    # endpoints are considered equal under tolerance tol.
    remove_line_ind = np.where(np.diff(lines_split[:2], axis=0)[0] == 0)[0]
    lines_split = np.delete(lines_split, remove_line_ind, axis=1)

    # TODO: This operation may leave points that are not referenced by any
    # lines. We should probably delete these.

    # We find the end points that is shared by more than one intersection
    intersections = __find_intersection_points(lines_split)

    # Gridding size
    if 'mesh_size' in kwargs.keys():
        mesh_size, mesh_size_bound, pts_split, lines_split = \
            utils.determine_mesh_size(pts_split, lines_split,
                                      **kwargs['mesh_size'])
    else:
        mesh_size = None
        mesh_size_bound = None

    # gmsh options

    meshing_algorithm = kwargs.get('meshing_algorithm')

    # Create a writer of gmsh .geo-files
    gw = gmsh_interface.GmshWriter(pts_split,
                                   lines_split,
                                   domain=domain,
                                   mesh_size=mesh_size,
                                   mesh_size_bound=mesh_size_bound,
                                   intersection_points=intersections,
                                   meshing_algorithm=meshing_algorithm)
    gw.write_geo(in_file)

    triangle_grid_run_gmsh(file_name, **kwargs)
    return triangle_grid_from_gmsh(file_name, **kwargs)
コード例 #10
0
def fractures_from_outcrop(
    pt, edges, ensure_realistic_cuts=True, family=None, extrusion_type="disc", **kwargs
):
    """ Create a set of fractures compatible with exposed lines in an outcrop.

    See module-level documentation for futher comments.

    Parameters:
        pt (np.array, 2 x num_pts): Coordinates of start and endpoints of
            extruded lines.
        edges (np.array, 2 x num_pts): Connections between points. Should not
            have their crossings removed before.
        ensure_realistic_cut (boolean, defaults to True): If True, we ensure
            that T-intersections do not have cut fractures that extend beyond
            the confining fracture. May overrule user-supplied controls on
            fracture sizes.
        **kwargs: Potentially user defined options. Forwarded to
            discs_from_exposure() and impose_inclines()

    Returns:
        list of Fracture: Fracture planes.

    """
    logging.info("Extrusion recieved " + str(edges.shape[1]) + " lines")
    assert edges.shape[0] == 2, "Edges have two endpoints"
    edges = np.vstack((edges, np.arange(edges.shape[1], dtype=np.int)))

    # identify crossings
    logging.info("Identify crossings")
    split_pt, split_edges = cg.remove_edge_crossings(pt, edges, **kwargs)
    logging.info("Fractures composed of " + str(split_edges.shape[0]) + "branches")

    # Find t-intersections
    abutment_pts, prim_frac, sec_frac, other_pt = t_intersections(split_edges)
    logging.info("Found " + str(prim_frac.size) + " T-intersections")

    # Calculate fracture lengths
    lengths = fracture_length(pt, edges)

    # Extrude to fracture discs
    logging.info("Create fractures from exposure")
    if extrusion_type.lower().strip() == "disc":
        fractures, extrude_ang = discs_from_exposure(pt, edges, **kwargs)
        disc_type = True
    else:
        fractures = rectangles_from_exposure(pt, edges)
        disc_type = False

    p0 = pt[:, edges[0]]
    p1 = pt[:, edges[1]]
    exposure = p1 - p0

    # Impose incline.
    logging.info("Impose incline")
    rot_ang = impose_inlcine(fractures, exposure, p0, frac_family=family, **kwargs)

    # Cut fractures
    for prim, sec, p in zip(prim_frac, sec_frac, other_pt):
        _, radius = cut_fracture_by_plane(
            fractures[sec], fractures[prim], split_pt[:, p], **kwargs
        )
        # If specified, ensure that cuts in T-intersections appear realistic.
        if ensure_realistic_cuts and disc_type and radius is not None:
            ang = np.arctan2(0.5 * lengths[prim], radius)

            # Ensure the center of both fractures are on the same side of the
            # exposed plane - if not, the cut will still be bad.
            if extrude_ang[sec] > np.pi / 2 and ang < np.pi / 2:
                ang = np.pi - ang
            elif extrude_ang[sec] < np.pi / 2 and ang > np.pi / 2:
                ang = np.pi - ang

            e0 = p0[:, prim]
            e1 = p1[:, prim]
            new_radius, center, _ = disc_radius_center(lengths[prim], e0, e1, theta=ang)
            strike = np.arctan2(e1[1] - e0[1], e1[0] - e0[0])
            f = create_disc_fracture(
                center, new_radius, np.pi / 2, strike, np.vstack((e0, e1)).T
            )
            rotate_fracture(f, e1 - e0, rot_ang[prim], p0[:, prim])
            fractures[prim] = f

    return fractures