def test_completely_outside_lower(self): self.setup() f = self.f_1 f.p[0] -= 2 network = FractureNetwork([f]) network.impose_external_boundary(self.domain) self.assertTrue(len(network._fractures) == (0 + 6))
def test_outside_west_bottom(self): self.setup() f = self.f_1 f.p[0] -= 0.5 f.p[2] -= 1.5 network = FractureNetwork([f]) network.impose_external_boundary(self.domain) self.assertTrue(len(network._fractures) == (0 + 6))
def create(conforming, tol=1e-4): csv_folder = "./" csv_file = "example_4_outcrop.csv" pts, edges = importer.lines_from_csv(csv_folder + csv_file) # A tolerance of 1 seems to be sufficient to recover the T-intersections, but # I might have missed some, though, so take a look at the network and modify # if necessary. snap_pts = cg.snap_points_to_segments(pts, edges, tol=1) extrusion_kwargs = {} extrusion_kwargs["tol"] = tol extrusion_kwargs["exposure_angle"] = np.pi / 4.0 * np.ones(edges.shape[1]) # Added an option not to include the points on the exposed surface. This # reduces cell refinement somewhat, but setting it True should also be okay extrusion_kwargs["outcrop_consistent"] = True fractures = extrusion.fractures_from_outcrop(snap_pts, edges, **extrusion_kwargs) network = FractureNetwork(fractures, tol=tol) # network.to_vtk(folder_export+"network.vtu") bounding_box = { "xmin": -800, "xmax": 600, "ymin": 100, "ymax": 1500, "zmin": -100, "zmax": 1000, } network.impose_external_boundary(bounding_box, truncate_fractures=True, keep_box=False) mesh_kwargs = {} h = 30 mesh_kwargs["mesh_size"] = { "mode": "weighted", # 'distance' "value": h, "bound_value": h, "tol": tol, } if conforming: # Change h_ideal and h_min at will here, but I ran into trouble with h_min < 1. gb = meshing.dfn(network, conforming=True, h_ideal=100, h_min=5) else: # Switch conforming=True to get conforming meshes gb = meshing.dfn(network, conforming=False, **mesh_kwargs, keep_geo=True) gb.remove_nodes(lambda g: g.dim == 0) gb.compute_geometry() gb.assign_node_ordering() return gb
def test_full_incline(self): self.setup() p = np.array([[-0.5, 0.5, 0.5, -0.5], [0.5, 0.5, 1.5, 1.5], [-0.5, -0.5, 1, 1]]) f = Fracture(p, check_convexity=False) network = FractureNetwork([f]) network.impose_external_boundary(self.domain) p_known = np.array([[0., 0.5, 0.5, 0], [5. / 6, 5. / 6, 1, 1], [0., 0., 0.25, 0.25]]) self.assertTrue(len(network._fractures) == (1 + 6)) p_comp = network._fractures[0].p self.assertTrue(self._arrays_equal(p_known, p_comp))
def test_incline_in_plane(self): self.setup() f = self.f_1 f.p[0] -= 0.5 f.p[2, :] = [0, -0.5, 0.5, 1] network = FractureNetwork([f]) network.impose_external_boundary(self.domain) p_known = np.array([[0., 0.5, 0.5, 0], [0.5, 0.5, 0.5, 0.5], [0., 0., 0.5, 0.75]]) self.assertTrue(len(network._fractures) == (1 + 6)) p_comp = network._fractures[0].p self.assertTrue(self._arrays_equal(p_known, p_comp))
def test_intersect_two_same(self): self.setup() f = self.f_1 f.p[0, :] = [-0.5, 1.5, 1.5, -0.5] f.p[2, :] = [0.2, 0.2, 0.8, 0.8] network = FractureNetwork([f]) network.impose_external_boundary(self.domain) p_known = np.array([[0., 1, 1, 0], [0.5, 0.5, 0.5, 0.5], [0.2, 0.2, 0.8, 0.8]]) self.assertTrue(len(network._fractures) == (1 + 6)) p_comp = network._fractures[0].p self.assertTrue(self._arrays_equal(p_known, p_comp))
def test_intersect_one(self): self.setup() f = self.f_1 f.p[0] -= 0.5 f.p[2, :] = [0.2, 0.2, 0.8, 0.8] network = FractureNetwork([f]) network.impose_external_boundary(self.domain) p_known = np.array([[0., 0.5, 0.5, 0], [0.5, 0.5, 0.5, 0.5], [0.2, 0.2, 0.8, 0.8]]) assert len(network._fractures) == (1 + 6) p_comp = network._fractures[0].p assert self._arrays_equal(p_known, p_comp)
def test_conforming_two_fractures(): f_1 = Fracture( np.array([[-1, -1, 0], [1, -1, 0], [1, 1, 0], [-1, 1, 0]]).T) f_2 = Fracture( np.array([[-1, 0, -1], [1, 0, -1], [1, 0, 1], [-1, 0, 1]]).T) network = FractureNetwork([f_1, f_2]) gb = meshing.dfn(network, conforming=True)
def test_non_conforming_three_fractures(): f_1 = Fracture( np.array([[-1, -1, 0], [1, -1, 0], [1, 1, 0], [-1, 1, 0]]).T) f_2 = Fracture( np.array([[-1, 0, -1], [1, 0, -1], [1, 0, 1], [-1, 0, 1]]).T) f_3 = Fracture( np.array([[0, -1, -1], [0, 1, -1], [0, 1, 1], [0, -1, 1]]).T) network = FractureNetwork([f_1, f_2, f_3]) gb = meshing.dfn(network, conforming=False)
def create_grid(**mesh_kwargs): """ Create a grid bucket containing grids from gmsh. NOTE: The line setting 'path_to_gmsh' *must* be modified for this to work. Parameters concerning mesh size, domain size etc. may also be changed, see below. Returns: grid_bucket: A grid_bucket containing the full hierarchy of grids. """ num_fracs = mesh_kwargs.get('num_fracs', 39) # If the # Don't change the path, or move the file data = _soultz_data() data = data[:num_fracs, :] # Data format of the data file is (frac_num, fracture center_xyz, major # axis, minor axis, dip direction, dip angle) centers = data[:, 1:4] major_axis = data[:, 4] minor_axis = data[:, 5] dip_direction = data[:, 6] / 180 * np.pi dip_angle = data[:, 7] / 180 * np.pi # We will define the fractures as elliptic fractures. For this we need # strike angle, rather than dip direction. strike_angle = dip_direction + np.pi / 2 # Modifications of the fracture definition: # These are carried out to ease the gridding; without these, we will end up # with gridding polygons that have very close points. The result may be # Minor axis angle. This is specified as zero (the fractures are # interpreted as circles), but we rotate them in an attempt to avoid close # points in the fracture specification. # Also note that since the fractures are defined as circles, any # orientation of the approximating polygon is equally correct major_axis_angle = np.zeros(num_fracs) # major_axis_angle[14] = 5 * np.pi / 180 ## major_axis_angle[24] = 5 * np.pi / 180 # major_axis_angle[26] = 5 * np.pi / 180 ## major_axis_angle[32-1] = 5 * np.pi / 180 # Also modify some centers. This may potentially have some impact on the # properties of the fracture network, but they been selected as to not # modify the fracture network properties. # centers[3, 2] += 30 if num_fracs > 10: centers[11, 2] += 15 # centers[8, 2] -= 10 # centers[19, 2] -= 20 # centers[22, 2] -= 10 # centers[23, 1:3] -= 15 # centers[24, 2] += 30 # centers[25, 2] += 10 # centers[29, 2] -= 30 # centers[30, 2] += 30 # centers[31, 2] += 30 # centers[34, 2] -= 10 # centers[38, 2] -= 10 # Create a set of fractures frac_list = [] num_points = mesh_kwargs.get('num_points', 16) for fi in range(data.shape[0]): frac_new = EllipticFracture(centers[fi], major_axis[fi], minor_axis[fi], major_axis_angle[fi], strike_angle[fi], dip_angle[fi], num_points=num_points) frac_list.append(frac_new) # Create the network, dump to vtu network = FractureNetwork(frac_list, verbose=1, tol=1e-4) network.to_vtk('soultz_fractures_full.vtu') # Impose domain boundaries. These are set large enough to not be in # conflict with the network. # This may be changed if desirable. domain = { 'xmin': -4000, 'xmax': 4000, 'ymin': -3000, 'ymax': 3000, 'zmin': 0, 'zmax': 8000 } domain = mesh_kwargs.get('domain', domain) network.impose_external_boundary(domain) # Find intersections, and split these network.find_intersections() network.split_intersections() # This may be changed, if desirable. if mesh_kwargs is None: mesh_size = {'mode': 'constant', 'value': 150, 'bound_value': 500} mesh_kwargs = {'mesh_size': mesh_size, 'file_name': 'soultz_fracs'} # Since we have a ready network (and may want to take this file into # jupyter and study the network before gridding), we abuse the workflow # slightly by calling simplex_tetrahedral directly, rather than to go the # way through meshing.simplex_grid (the latter is, for now, restricted to # specifying the grid by individual fractures, rather than networks). grids = simplex.tetrahedral_grid(network=network, **mesh_kwargs) # Convert the grids into a bucket meshing.tag_faces(grids) gb = meshing.assemble_in_bucket(grids) gb.compute_geometry() split_grid.split_fractures(gb) return gb
def network_3d_from_csv(file_name, has_domain=True, tol=1e-4): """ Create the fracture network from a set of 3d fractures stored in a csv file and domain. In the csv file, we assume the following structure - first line (optional) describes the domain as a rectangle with X_MIN, Y_MIN, Z_MIN, X_MAX, Y_MAX, Z_MAX - the other lines descibe the N fractures as a list of points P0_X, P0_Y, P0_Z, ...,PN_X, PN_Y, PN_Z Lines that start with a # are ignored. Parameters: file_name: name of the file has_domain: if the first line in the csv file specify the domain tol: (optional) tolerance for the methods Return: frac_list: the list of fractures network: the fracture network domain: (optional, returned if has_domain==True) the domain """ # The first line of the csv file defines the bounding box for the domain frac_list = [] # Extract the data from the csv file with open(file_name, 'r') as csv_file: spam_reader = csv.reader(csv_file, delimiter=',') # Read the domain first if has_domain: domain = np.asarray(next(spam_reader), dtype=np.float) assert domain.size == 6 domain = { 'xmin': domain[0], 'xmax': domain[3], 'ymin': domain[1], 'ymax': domain[4], 'zmin': domain[2], 'zmax': domain[5] } for row in spam_reader: # If the line starts with a '#', we consider this a comment if row[0][0] == '#': continue # Read the points pts = np.asarray(row, dtype=np.float) assert pts.size % 3 == 0 # Skip empty lines. Useful if the file ends with a blank line. if pts.size == 0: continue frac_list.append(Fracture(pts.reshape((3, -1), order='F'))) # Create the network network = FractureNetwork(frac_list, tol=tol) if has_domain: return frac_list, network, domain else: return frac_list, network
def network_3d_from_fab(f_name, return_all=False, tol=None): """ Read fractures from a .fab file, as specified by FracMan. The filter is based on the .fab-files available at the time of writing, and may not cover all options available. Parameters: f_name (str): Path to .fab file. Returns: network: the network of fractures tess_fracs (optional returned if return_all==True, list of np.ndarray): Each list element contains fracture cut by the domain boundary, represented by vertexes as a nd x n_pt array. tess_sgn (optional returned if return_all==True, np.ndarray): For each element in tess_frac, a +-1 defining which boundary the fracture is on. The function also reads in various other information of unknown usefulness, see implementation for details. This information is currently not returned. """ def read_keyword(line): # Read a single keyword, on the form key = val words = line.split('=') assert len(words) == 2 key = words[0].strip() val = words[1].strip() return key, val def read_section(f, section_name): # Read a section of the file, surrounded by a BEGIN / END wrapping d = {} for line in f: if line.strip() == 'END ' + section_name.upper().strip(): return d k, v = read_keyword(line) d[k] = v def read_fractures(f, is_tess=False): # Read the fracture fracs = [] fracture_ids = [] trans = [] nd = 3 for line in f: if not is_tess and line.strip() == 'END FRACTURE': return fracs, np.asarray(fracture_ids), np.asarray(trans) elif is_tess and line.strip() == 'END TESSFRACTURE': return fracs, np.asarray(fracture_ids), np.asarray(trans) if is_tess: ids, num_vert = line.split() else: ids, num_vert, t = line.split()[:3] trans.append(float(t)) ids = int(ids) num_vert = int(num_vert) vert = np.zeros((num_vert, nd)) for i in range(num_vert): data = f.readline().split() vert[i] = np.asarray(data[1:]) # Transpose to nd x n_pt format vert = vert.T # Read line containing normal vector, but disregard result data = f.readline().split() if is_tess: trans.append(int(data[1])) fracs.append(vert) fracture_ids.append(ids) with open(f_name, 'r') as f: for line in f: if line.strip() == 'BEGIN FORMAT': # Read the format section, but disregard the information for # now formats = read_section(f, 'FORMAT') elif line.strip() == 'BEGIN PROPERTIES': # Read in properties section, but disregard information props = read_section(f, 'PROPERTIES') elif line.strip() == 'BEGIN SETS': # Read set section, but disregard information. sets = read_section(f, 'SETS') elif line.strip() == 'BEGIN FRACTURE': # Read fractures fracs, frac_ids, trans = read_fractures(f, is_tess=False) elif line.strip() == 'BEGIN TESSFRACTURE': # Read tess_fractures tess_fracs, tess_frac_ids, tess_sgn = \ read_fractures(f, is_tess=True) elif line.strip()[:5] == 'BEGIN': # Check for keywords not yet implemented. raise ValueError('Unknown section type ' + line) fractures = [Fracture(f) for f in fracs] if tol is not None: network = FractureNetwork(fractures, tol=tol) else: network = FractureNetwork(fractures) if return_all: return network, tess_fracs, tess_sgn else: return network
def elliptic_network_3d_from_csv(file_name, has_domain=True, tol=1e-4, degrees=False): """ Create the fracture network from a set of 3d fractures stored in a csv file and domain. In the csv file, we assume the following structure - first line (optional) describes the domain as a rectangle with X_MIN, Y_MIN, Z_MIN, X_MAX, Y_MAX, Z_MAX - the other lines descibe the N fractures as a elliptic fractures: center_x, center_y, center_z, major_axis, minor_axis, major_axis_angle, strike_angle, dip_angle, num_points. See EllipticFracture for information about the parameters Lines that start with a # are ignored. Parameters: file_name: name of the file has_domain: if the first line in the csv file specify the domain tol: (optional) tolerance for the methods Return: frac_list: the list of fractures network: the fracture network domain: (optional, returned if has_domain==True) the domain """ # The first line of the csv file defines the bounding box for the domain frac_list = [] # Extract the data from the csv file with open(file_name, 'r') as csv_file: spam_reader = csv.reader(csv_file, delimiter=',') # Read the domain first if has_domain: domain = np.asarray(next(spam_reader), dtype=np.float) assert domain.size == 6 domain = { 'xmin': domain[0], 'xmax': domain[3], 'ymin': domain[1], 'ymax': domain[4], 'zmin': domain[2], 'zmax': domain[5] } for row in spam_reader: # If the line starts with a '#', we consider this a comment if row[0][0] == '#': continue # Read the data data = np.asarray(row, dtype=np.float) assert data.size % 9 == 0 # Skip empty lines. Useful if the file ends with a blank line. if data.size == 0: continue centers = data[0:3] maj_ax = data[3] min_ax = data[4] maj_ax_ang = data[5] * (1 - degrees + degrees * np.pi / 180) strike_ang = data[6] * (1 - degrees + degrees * np.pi / 180) dip_ang = data[7] * (1 - degrees + degrees * np.pi / 180) num_points = data[8] frac_list.append( EllipticFracture(centers, maj_ax, min_ax, maj_ax_ang, strike_ang, dip_ang, num_points)) # Create the network network = FractureNetwork(frac_list, tol=tol) if has_domain: return frac_list, network, domain else: return frac_list, network
def network_3d_from_csv(file_name, has_domain=True, tol=1e-4): """ Create the fracture network from a set of 3d fractures stored in a csv file and domain. In the csv file, we assume the following structure - first line (optional) describes the domain as a rectangle with X_MIN, Y_MIN, Z_MIN, X_MAX, Y_MAX, Z_MAX - the other lines descibe the N fractures as a list of points P0_X, P0_Y, P0_Z, ...,PN_X, PN_Y, PN_Z Parameters: file_name: name of the file has_domain: if the first line in the csv file specify the domain tol: (optional) tolerance for the methods Return: frac_list: the list of fractures network: the fracture network domain: (optional, returned if has_domain==True) the domain """ # The first line of the csv file defines the bounding box for the domain frac_list = [] # Extract the data from the csv file with open(file_name, 'r') as csv_file: spam_reader = csv.reader(csv_file, delimiter=',') # Read the domain first if has_domain: domain = np.asarray(next(spam_reader), dtype=np.float) assert domain.size == 6 domain = { 'xmin': domain[0], 'xmax': domain[3], 'ymin': domain[1], 'ymax': domain[4], 'zmin': domain[2], 'zmax': domain[5] } for row in spam_reader: # Read the points pts = np.asarray(row, dtype=np.float) assert pts.size % 3 == 0 frac_list.append(Fracture(pts.reshape((3, -1), order='F'))) # Create the network network = FractureNetwork(frac_list, tol=tol) # Cut the fractures due to the domain if has_domain: network.impose_external_boundary(domain) # Find intersections, and split these network.find_intersections() network.split_intersections() if has_domain: return frac_list, network, domain else: return frac_list, network