Exemple #1
0
def dfn(fracs, conforming, intersections=None, keep_geo=False, tol=1e-4,
        **kwargs):
    """ Create a mesh of a DFN model, that is, only of fractures.

    The mesh can eihter be conforming along fracture intersections, or each
    fracture is meshed independently. The latter case will typically require
    some sort of sewing together external to this funciton.

    TODO: What happens if we give in a non-connected network?

    Parameters:
        fracs (either Fractures, or a FractureNetwork).
        conforming (boolean): If True, the mesh will be conforming along 1d
            intersections.
        intersections (list of lists, optional): Each item corresponds to an
            intersection between two fractures. In each sublist, the first two
            indices gives fracture ids (refering to order in fracs). The third
            item is a numpy array representing intersection coordinates. If no
            intersections provided, intersections will be detected using
            function in FractureNetwork.
        **kwargs: Parameters passed to gmsh.

    Returns:
        GridBucket (if conforming is True): Mixed-dimensional mesh that
            represents all fractures, and intersection poitns and line.

    """

    if isinstance(fracs, FractureNetwork) \
       or isinstance(fracs, FractureNetwork_full):
        network = fracs
    else:
        network = FractureNetwork(fracs)

    # Populate intersections in FractureNetowrk, or find intersections if not
    # provided.

    if intersections is not None:
        logger.warn('FractureNetwork use pre-computed intersections')
        network.intersections = [Intersection(*i) for i in intersections]
    else:
        logger.warn('FractureNetwork find intersections in DFN')
        tic = time.time()
        network.find_intersections()
        logger.warn('Done. Elapsed time ' + str(time.time() - tic))

    if conforming:
        logger.warn('Create conforming mesh for DFN network')
        grids = simplex.triangle_grid_embedded(network, find_isect=False,
                                               **kwargs)
    else:
        logger.warn('Create non-conforming mesh for DFN network')
        tic = time.time()
        grid_list = []
        neigh_list = []

        for fi in range(len(network._fractures)):
            logger.info('Meshing of fracture ' + str(fi))
            # Rotate fracture vertexes and intersection points
            fp, ip, other_frac, rot, cp = network.fracture_to_plane(fi)
            frac_i = network[fi]

            f_lines = np.reshape(np.arange(ip.shape[1]), (2, -1), order='F')
            frac_dict = {'points': ip, 'edges': f_lines}
            if keep_geo:
                file_name = 'frac_mesh_' + str(fi)
                kwargs['file_name'] = file_name
            # Create mesh on this fracture surface.
            grids = simplex.triangle_grid(frac_dict, fp, verbose=False,
                                          **kwargs)

            irot = rot.T
            # Loop over grids, rotate back again to 3d coordinates
            for gl in grids:
                for g in gl:
                    g.nodes = irot.dot(g.nodes) + cp

            # Nodes of main (fracture) grid, in 3d coordinates1
            main_nodes = grids[0][0].nodes
            main_global_point_ind = grids[0][0].global_point_ind
            # Loop over intersections, check if the intersection is on the
            # boundary of this fracture.
            for ind, isect in enumerate(network.intersections_of_fracture(fi)):
                of = isect.get_other_fracture(frac_i)
                if isect.on_boundary_of_fracture(frac_i):
                    dist, _, _ = cg.dist_points_polygon(main_nodes, of.p)
                    hit = np.argwhere(dist < tol).reshape((1, -1))[0]
                    nodes_1d = main_nodes[:, hit]
                    global_point_ind = main_global_point_ind[hit]

                    assert cg.is_collinear(nodes_1d, tol=tol)
                    sort_ind = cg.argsort_point_on_line(nodes_1d, tol=tol)
                    g_aux = TensorGrid(np.arange(nodes_1d.shape[1]))
                    g_aux.nodes = nodes_1d[:, sort_ind]
                    g_aux.global_point_ind = global_point_ind[sort_ind]
                    grids[1].insert(ind, g_aux)

            assert len(grids[0]) == 1, 'Fracture should be covered by single'\
                'mesh'

            grid_list.append(grids)
            neigh_list.append(other_frac)

        logger.warn('Finished creating grids. Elapsed time ' +
                    str(time.time() - tic))
        logger.warn('Merge grids')
        tic = time.time()
        grids = non_conforming.merge_grids(grid_list, neigh_list)
        logger.warn('Done. Elapsed time ' + str(time.time() - tic))

        for g_set in grids:
            if len(g_set) > 0:
                s = 'Created ' + str(len(g_set)) + ' ' + str(g_set[0].dim) + \
                    '-d grids with '
                num = 0
                for g in g_set:
                    num += g.num_cells
                s += str(num) + ' cells'
                logger.info(s)

    return grid_list_to_grid_bucket(grids, check_highest_dim=False)
Exemple #2
0
def simplex_grid(fracs=None, domain=None, network=None, subdomains=[], verbose=0, **kwargs):
    """
    Main function for grid generation. Creates a fractured simiplex grid in 2
    or 3 dimensions.

    NOTE: For some fracture networks, what appears to be a bug in Gmsh leads to
    surface grids with cells that does not have a corresponding face in the 3d
    grid. The problem may have been resolved (at least partly) by newer
    versions of Gmsh, but can still be an issue for our purposes. If this
    behavior is detected, an assertion error is raised. To avoid the issue,
    and go on with a surface mesh that likely is problematic, kwargs should
    contain a keyword ensure_matching_face_cell=False.

    Parameters
    ----------
    fracs (list of np.ndarray): One list item for each fracture. Each item
        consist of a (nd x n) array describing fracture vertices. The
        fractures may be intersecting.
    domain (dict): Domain specification, determined by xmin, xmax, ...
    subdomains (list of np.ndarray or list of Fractures): One list item
        for each fracture, same format as fracs. Specifies internal boundaries
        for the gridding. Only available in 3D.
    **kwargs: May contain fracture tags, options for gridding, etc.

    Gridding options:
    The mesh parameters are:
        mesh_size_frac (double): Ideal mesh size. Will be added to all points
            that are sufficiently far away from other points.
        mesh_size_min (double): Minimal mesh size; we will make no attempts to
            enforce even smaller mesh sizes upon Gmsh.
        mesh_size_bound (double): Optional boundary mesh size, defaults to the
            value of mesh_size_frac. Will be added to the points
            defining the boundary. In 2d, this parameter dictates the size at
            the boundary corners. In 3d, it is assigned unless there are any
            fractures in the immediate vicinity influencing the size. In other
            words, mesh_size_bound is the boundary point equivalent of
            mesh_size_frac.

    TODO: Update 2d implementation to adhere to 3d in
        porepy.fracs.tools.determine_mesh_size.

    Returns
    -------
    GridBucket: A complete bucket where all fractures are represented as
        lower dim grids. The higher dim fracture faces are split in two,
        and on the edges of the GridBucket graph the mapping from lower dim
        cells to higher dim faces are stored as 'face_cells'. Each face is
        given boolean tags depending on the type:
           domain_boundary_faces: All faces that lie on the domain boundary
               (i.e. should be given a boundary condition).
           fracture_faces: All faces that are split (i.e. has a connection to a
               lower dim grid).
           tip_faces: A boundary face that is not on the domain boundary, nor
               coupled to a lower domentional domain.
        The union of the above three is the tag boundary_faces.

    Examples
    --------
    frac1 = np.array([[1,4],[1,4]])
    frac2 = np.array([[1,4],[4,1]])
    fracs = [frac1, frac2]
    domain = {'xmin': 0, 'ymin': 0, 'xmax':5, 'ymax':5}
    gb = simplex_grid(fracs, domain)

    """
    if domain is None:
        if fracs is not None:
            ndim = fracs[0].shape[0]
        else:
            ndim = network[0].p.shape[0]

    elif 'zmax' in domain:
        ndim = 3
    elif 'ymax' in domain:
        ndim = 2
    else:
        raise ValueError('simplex_grid only supported for 2 or 3 dimensions')


    logger.info('Construct mesh')
    tm_tot = time.time()
    # Call relevant method, depending on grid dimensions.
    if ndim == 2:
        assert fracs is not None, '2d requires definition of fractures'
        assert domain is not None, '2d requires definition of domain'
        # Convert the fracture to a fracture dictionary.
        if len(fracs) == 0:
            f_lines = np.zeros((2, 0))
            f_pts = np.zeros((2, 0))
        else:
            f_lines = np.reshape(np.arange(2 * len(fracs)), (2, -1), order='F')
            f_pts = np.hstack(fracs)
        frac_dic = {'points': f_pts, 'edges': f_lines}
        grids = simplex.triangle_grid(frac_dic, domain, **kwargs)
    elif ndim == 3:
        grids = simplex.tetrahedral_grid(
            fracs, domain, network, subdomains, **kwargs)
    else:
        raise ValueError('Only support for 2 and 3 dimensions')

    return grid_list_to_grid_bucket(grids, time_tot=tm_tot, **kwargs)
Exemple #3
0
def simplex_grid(fracs=None,
                 domain=None,
                 network=None,
                 subdomains=[],
                 verbose=0,
                 **kwargs):
    """
    Main function for grid generation. Creates a fractured simiplex grid in 2
    or 3 dimensions.

    NOTE: For some fracture networks, what appears to be a bug in Gmsh leads to
    surface grids with cells that does not have a corresponding face in the 3d
    grid. The problem may have been resolved (at least partly) by newer
    versions of Gmsh, but can still be an issue for our purposes. If this
    behavior is detected, an assertion error is raised. To avoid the issue,
    and go on with a surface mesh that likely is problematic, kwargs should
    contain a keyword ensure_matching_face_cell=False.

    Parameters
    ----------
    fracs (list of np.ndarray): One list item for each fracture. Each item
        consist of a (nd x n) array describing fracture vertices. The
        fractures may be intersecting.
    domain (dict): Domain specification, determined by xmin, xmax, ...
    subdomains (list of np.ndarray or list of Fractures): One list item
        for each fracture, same format as fracs. Specifies internal boundaries
        for the gridding. Only available in 3D.
    **kwargs: May contain fracture tags, options for gridding, etc.

    Returns
    -------
    GridBucket: A complete bucket where all fractures are represented as
        lower dim grids. The higher dim fracture faces are split in two,
        and on the edges of the GridBucket graph the mapping from lower dim
        cells to higher dim faces are stored as 'face_cells'. Each face is
        given a FaceTag depending on the type:
           NONE: None of the below (i.e. an internal face)
           DOMAIN_BOUNDARY: All faces that lie on the domain boundary
               (i.e. should be given a boundary condition).
           FRACTURE: All faces that are split (i.e. has a connection to a
               lower dim grid).
           TIP: A boundary face that is not on the domain boundary, nor
               coupled to a lower domentional domain.

    Examples
    --------
    frac1 = np.array([[1,4],[1,4]])
    frac2 = np.array([[1,4],[4,1]])
    fracs = [frac1, frac2]
    domain = {'xmin': 0, 'ymin': 0, 'xmax':5, 'ymax':5}
    gb = simplex_grid(fracs, domain)

    """
    if domain is None:
        ndim = 3
    elif 'zmax' in domain:
        ndim = 3
    elif 'ymax' in domain:
        ndim = 2
    else:
        raise ValueError('simplex_grid only supported for 2 or 3 dimensions')

    if verbose > 0:
        print('Construct mesh')
        tm_msh = time.time()
        tm_tot = time.time()
    # Call relevant method, depending on grid dimensions.
    if ndim == 2:
        assert fracs is not None, '2d requires definition of fractures'
        assert domain is not None, '2d requires definition of domain'
        # Convert the fracture to a fracture dictionary.
        if len(fracs) == 0:
            f_lines = np.zeros((2, 0))
            f_pts = np.zeros((2, 0))
        else:
            f_lines = np.reshape(np.arange(2 * len(fracs)), (2, -1), order='F')
            f_pts = np.hstack(fracs)
        frac_dic = {'points': f_pts, 'edges': f_lines}
        grids = simplex.triangle_grid(frac_dic, domain, **kwargs)
    elif ndim == 3:
        grids = simplex.tetrahedral_grid(fracs, domain, network, subdomains,
                                         **kwargs)
    else:
        raise ValueError('Only support for 2 and 3 dimensions')

    if verbose > 0:
        print('Done. Elapsed time ' + str(time.time() - tm_msh))

    # Tag tip faces
    tag_faces(grids)

    # Assemble grids in a bucket

    if verbose > 0:
        print('Assemble in bucket')
        tm_bucket = time.time()
    gb = assemble_in_bucket(grids, **kwargs)
    if verbose > 0:
        print('Done. Elapsed time ' + str(time.time() - tm_bucket))
        print('Compute geometry')
        tm_geom = time.time()

    gb.compute_geometry()
    # Split the grids.
    if verbose > 0:
        print('Done. Elapsed time ' + str(time.time() - tm_geom))
        print('Split fractures')
        tm_split = time.time()
    split_grid.split_fractures(gb, **kwargs)
    if verbose > 0:
        print('Done. Elapsed time ' + str(time.time() - tm_split))
    gb.assign_node_ordering()

    if verbose > 0:
        print('Mesh construction completed. Total time ' +
              str(time.time() - tm_tot))

    return gb