def __init__(self, shapes, spacings, origins, labels=None, name=None, project=None): # If no labels given, then use default if labels is None: labels = [None for i in shapes] # Generate cubic networks of specified properties and store in a list nets = [] for n in range(len(shapes)): net = Cubic(shape=shapes[n], spacing=spacings[n], name=labels[n]) # label_faces(net) net['pore.coords'] += origins[n] nets.append(net) # Begin the process of merging the networks, but no stitching yet main_net = nets.pop(0) for net in nets: extend(network=main_net, pore_coords=net['pore.coords'], throat_conns=net['throat.conns'] + main_net.Np) for net in nets: P1 = main_net.pores('surface') P1 = net.pores('surface') stitch(network=main_net)
def test_check_network_health_duplicate_throat(self): net = op.network.Cubic(shape=[5, 5, 5]) P12 = net['throat.conns'][0] extend(network=net, throat_conns=[P12]) a = net.check_network_health() assert len(a['duplicate_throats']) == 1 assert len(a['duplicate_throats'][0]) == 2
def test_check_network_health_headless_throats(self): net = op.network.Cubic(shape=[5, 5, 5]) with pytest.raises(Exception): extend(network=net, throat_conns=[[5, 5555]]) net['throat.conns'][0] = [5, 5555] a = net.check_network_health() assert a['headless_throats'] == np.array([0])
def load(cls, path, node_file="throats_cellsThroatsGraph_Nodes.txt", graph_file="throats_cellsThroatsGraph.txt", network=None, voxel_size=None, return_geometry=False): r""" Loads network data from an iMorph processed image stack Parameters ---------- path : string The path of the folder where the subfiles are held node_file : string The file that describes the pores and throats, the default iMorph name is: throats_cellsThroatsGraph_Nodes.txt graph_file : string The file that describes the connectivity of the network, the default iMorph name is: throats_cellsThroatsGraph.txt network : OpenPNM Network Object The OpenPNM Network onto which the data should be loaded. If no network is supplied then an empty import network is created and returned. voxel_size : float Allows the user to define a voxel size different than what is contained in the node_file. The value must be in meters. return_geometry : Boolean If True, then all geometrical related properties are removed from the Network object and added to a GenericGeometry object. In this case the method returns a tuple containing (network, geometry). If False (default) then the returned Network will contain all properties that were in the original file. In this case, the user can call the ```split_geometry``` method explicitly to perform the separation. Returns ------- If no Network object is supplied then one will be created and returned. If return_geometry is True, then a tuple is returned containing both the network and a geometry object. """ # path = Path(path) node_file = os.path.join(path.resolve(), node_file) graph_file = os.path.join(path.resolve(), graph_file) # parsing the nodes file with open(node_file, 'r') as file: Np = sp.fromstring(file.readline().rsplit('=')[1], sep='\t', dtype=int)[0] vox_size = sp.fromstring( file.readline().rsplit(')')[1], sep='\t', )[0] # network always recreated to prevent errors network = GenericNetwork(Np=Np, Nt=0) # Define expected properies network['pore.volume'] = sp.nan scrap_lines = [file.readline() for line in range(4)] while True: vals = file.readline().split('\t') if len(vals) == 1: break network['pore.volume'][int(vals[0])] = float(vals[3]) if 'pore.' + vals[2] not in network.labels(): network['pore.' + vals[2]] = False network['pore.' + vals[2]][int(vals[0])] = True if voxel_size is None: voxel_size = vox_size * 1.0E-6 # file stores value in microns if voxel_size < 0: raise (Exception('Error - Voxel size must be specfied in ' + 'the Nodes file or as a keyword argument.')) # parsing the graph file with open(graph_file, 'r') as file: # Define expected properties network['pore.coords'] = sp.zeros((Np, 3)) * sp.nan network['pore.types'] = sp.nan network['pore.color'] = sp.nan network['pore.radius'] = sp.nan network['pore.dmax'] = sp.nan network['pore.node_number'] = sp.nan # Scan file to get pore coordinate data scrap_lines = [file.readline() for line in range(3)] line = file.readline() xmax = 0.0 ymax = 0.0 zmax = 0.0 node_num = 0 while line != 'connectivity table\n': vals = sp.fromstring(line, sep='\t') xmax = vals[1] if vals[1] > xmax else xmax ymax = vals[2] if vals[2] > ymax else ymax zmax = vals[3] if vals[3] > zmax else zmax network['pore.coords'][int(vals[0]), :] = vals[1:4] network['pore.types'][int(vals[0])] = vals[4] network['pore.color'][int(vals[0])] = vals[5] network['pore.radius'][int(vals[0])] = vals[6] network['pore.dmax'][int(vals[0])] = vals[7] network['pore.node_number'][int(vals[0])] = node_num node_num += 1 line = file.readline() # Scan file to get to connectivity data scrap_lines.append(file.readline()) # Skip line # Create sparse lil array incrementally build adjacency matrix lil = sp.sparse.lil_matrix((Np, Np), dtype=int) while True: vals = sp.fromstring(file.readline(), sep='\t', dtype=int) if len(vals) <= 1: break lil.rows[vals[0]] = vals[2:] lil.data[vals[0]] = sp.ones(vals[1]) # fixing any negative volumes or distances so they are 1 voxel/micron network['pore.volume'][sp.where(network['pore.volume'] < 0)[0]] = 1.0 network['pore.radius'][sp.where(network['pore.radius'] < 0)[0]] = 1.0 network['pore.dmax'][sp.where(network['pore.dmax'] < 0)[0]] = 1.0 # Add adjacency matrix to OpenPNM network conns = sp.sparse.triu(lil, k=1, format='coo') network.update({'throat.all': sp.ones(len(conns.col), dtype=bool)}) network['throat.conns'] = sp.vstack([conns.row, conns.col]).T network['pore.to_trim'] = False network['pore.to_trim'][network.pores('*throat')] = True Ts = network.pores('to_trim') new_conns = network.find_neighbor_pores(pores=Ts, flatten=False) extend(network=network, throat_conns=new_conns, labels='new_conns') for item in network.props('pore'): item = item.split('.')[1] arr = sp.ones_like(network['pore.' + item])[0] arr = sp.tile(A=arr, reps=[network.Nt, 1]) * sp.nan network['throat.' + item] = sp.squeeze(arr) network['throat.'+item][network.throats('new_conns')] = \ network['pore.'+item][Ts] trim(network=network, pores=Ts) # setting up boundary pores x_coord, y_coord, z_coord = sp.hsplit(network['pore.coords'], 3) network['pore.front_boundary'] = sp.ravel(x_coord == 0) network['pore.back_boundary'] = sp.ravel(x_coord == xmax) network['pore.left_boundary'] = sp.ravel(y_coord == 0) network['pore.right_boundary'] = sp.ravel(y_coord == ymax) network['pore.bottom_boundary'] = sp.ravel(z_coord == 0) network['pore.top_boundary'] = sp.ravel(z_coord == zmax) # removing any pores that got classified as a boundary pore but # weren't labled a border_cell_face ps = sp.where(~sp.in1d(network.pores('*_boundary'), network.pores('border_cell_face')))[0] ps = network.pores('*_boundary')[ps] for side in ['front', 'back', 'left', 'right', 'top', 'bottom']: network['pore.' + side + '_boundary'][ps] = False # setting internal label network['pore.internal'] = False network['pore.internal'][network.pores('*_boundary', mode='not')] = True # adding props to border cell face throats and from pores Ts = sp.where( network['throat.conns'][:, 1] > network.pores('border_cell_face')[0] - 1)[0] faces = network['throat.conns'][Ts, 1] for item in network.props('pore'): item = item.split('.')[1] network['throat.' + item][Ts] = network['pore.' + item][faces] network['pore.volume'][faces] = 0.0 # applying unit conversions # TODO: Determine if radius and dmax are indeed microns and not voxels network['pore.coords'] = network['pore.coords'] * 1e-6 network['pore.radius'] = network['pore.radius'] * 1e-6 network['pore.dmax'] = network['pore.dmax'] * 1e-6 network['pore.volume'] = network['pore.volume'] * voxel_size**3 network['throat.coords'] = network['throat.coords'] * 1e-6 network['throat.radius'] = network['throat.radius'] * 1e-6 network['throat.dmax'] = network['throat.dmax'] * 1e-6 network['throat.volume'] = network['throat.volume'] * voxel_size**3 return network.project
def import_data(cls, path, node_file="throats_cellsThroatsGraph_Nodes.txt", graph_file="throats_cellsThroatsGraph.txt", voxel_size=None): r""" Loads network data from an iMorph processed image stack Parameters ---------- path : string The path of the folder where the subfiles are held node_file : string The file that describes the pores and throats, the default iMorph name is: throats_cellsThroatsGraph_Nodes.txt graph_file : string The file that describes the connectivity of the network, the default iMorph name is: throats_cellsThroatsGraph.txt voxel_size : float Allows the user to define a voxel size different than what is contained in the node_file. The value must be in meters. Returns ------- project : list An OpenPNM project object containing a network and a geometry object. The geometry-related data are automatically placed on the geometry object using the ``Imported`` geometry class. """ path = Path(path) node_file = os.path.join(path.resolve(), node_file) graph_file = os.path.join(path.resolve(), graph_file) # Parsing the nodes file with open(node_file, "r") as file: Np = np.fromstring(file.readline().rsplit("=")[1], sep="\t", dtype=int)[0] vox_size = np.fromstring( file.readline().rsplit(")")[1], sep="\t", )[0] # Network always recreated to prevent errors network = GenericNetwork(Np=Np, Nt=0) # Define expected properies network["pore.volume"] = np.nan scrap_lines = [file.readline() for line in range(4)] while True: vals = file.readline().split("\t") if len(vals) == 1: break network["pore.volume"][int(vals[0])] = float(vals[3]) if "pore." + vals[2] not in network.labels(): network["pore." + vals[2]] = False network["pore." + vals[2]][int(vals[0])] = True if voxel_size is None: voxel_size = vox_size * 1.0e-6 # File stores value in microns if voxel_size < 0: raise Exception("Error - Voxel size must be specfied in " + "the Nodes file or as a keyword argument.") # Parsing the graph file with open(graph_file, "r") as file: # Define expected properties network["pore.coords"] = np.zeros((Np, 3)) * np.nan network["pore.types"] = np.nan network["pore.color"] = np.nan network["pore.radius"] = np.nan network["pore.dmax"] = np.nan network["pore.node_number"] = np.nan # Scan file to get pore coordinate data scrap_lines = [file.readline() for line in range(3)] line = file.readline() xmax = 0.0 ymax = 0.0 zmax = 0.0 node_num = 0 while line != "connectivity table\n": vals = np.fromstring(line, sep="\t") xmax = vals[1] if vals[1] > xmax else xmax ymax = vals[2] if vals[2] > ymax else ymax zmax = vals[3] if vals[3] > zmax else zmax network["pore.coords"][int(vals[0]), :] = vals[1:4] network["pore.types"][int(vals[0])] = vals[4] network["pore.color"][int(vals[0])] = vals[5] network["pore.radius"][int(vals[0])] = vals[6] network["pore.dmax"][int(vals[0])] = vals[7] network["pore.node_number"][int(vals[0])] = node_num node_num += 1 line = file.readline() # Scan file to get to connectivity data scrap_lines.append(file.readline()) # Skip line # Create sparse lil array incrementally build adjacency matrix lil = sp.sparse.lil_matrix((Np, Np), dtype=int) while True: vals = np.fromstring(file.readline(), sep="\t", dtype=int) if len(vals) <= 1: break lil.rows[vals[0]] = vals[2:].tolist() lil.data[vals[0]] = np.ones(vals[1]).tolist() # Fixing any negative volumes or distances so they are 1 voxel/micron network["pore.volume"][np.where(network["pore.volume"] < 0)[0]] = 1.0 network["pore.radius"][np.where(network["pore.radius"] < 0)[0]] = 1.0 network["pore.dmax"][np.where(network["pore.dmax"] < 0)[0]] = 1.0 # Add adjacency matrix to OpenPNM network conns = sp.sparse.triu(lil, k=1, format="coo") network.update({"throat.all": np.ones(len(conns.col), dtype=bool)}) network["throat.conns"] = np.vstack([conns.row, conns.col]).T network["pore.to_trim"] = False network["pore.to_trim"][network.pores("*throat")] = True Ts = network.pores("to_trim") new_conns = network.find_neighbor_pores(pores=Ts, flatten=False) extend(network=network, throat_conns=new_conns, labels="new_conns") for item in network.props("pore"): item = item.split(".")[1] arr = np.ones_like(network["pore." + item])[0] arr = np.tile(A=arr, reps=[network.Nt, 1]) * np.nan network["throat." + item] = np.squeeze(arr) network["throat." + item][network.throats("new_conns")] = network["pore." + item][Ts] trim(network=network, pores=Ts) # Setting up boundary pores x_coord, y_coord, z_coord = np.hsplit(network["pore.coords"], 3) network["pore.front_boundary"] = np.ravel(x_coord == 0) network["pore.back_boundary"] = np.ravel(x_coord == xmax) network["pore.left_boundary"] = np.ravel(y_coord == 0) network["pore.right_boundary"] = np.ravel(y_coord == ymax) network["pore.bottom_boundary"] = np.ravel(z_coord == 0) network["pore.top_boundary"] = np.ravel(z_coord == zmax) # Removing any pores that got classified as a boundary pore but # Weren't labled a border_cell_face ps = np.where(~np.in1d(network.pores("*_boundary"), network.pores("border_cell_face")))[0] ps = network.pores("*_boundary")[ps] for side in ["front", "back", "left", "right", "top", "bottom"]: network["pore." + side + "_boundary"][ps] = False # Setting internal label network["pore.internal"] = False network["pore.internal"][network.pores("*_boundary", mode="not")] = True # Adding props to border cell face throats and from pores Ts = np.where( network["throat.conns"][:, 1] > network.pores("border_cell_face")[0] - 1)[0] faces = network["throat.conns"][Ts, 1] for item in network.props("pore"): item = item.split(".")[1] network["throat." + item][Ts] = network["pore." + item][faces] network["pore.volume"][faces] = 0.0 # Applying unit conversions # TODO: Determine if radius and dmax are indeed microns and not voxels network["pore.coords"] = network["pore.coords"] * 1e-6 network["pore.radius"] = network["pore.radius"] * 1e-6 network["pore.dmax"] = network["pore.dmax"] * 1e-6 network["pore.volume"] = network["pore.volume"] * voxel_size**3 network["throat.coords"] = network["throat.coords"] * 1e-6 network["throat.radius"] = network["throat.radius"] * 1e-6 network["throat.dmax"] = network["throat.dmax"] * 1e-6 network["throat.volume"] = network["throat.volume"] * voxel_size**3 return network.project
def load(cls, path, node_file="throats_cellsThroatsGraph_Nodes.txt", graph_file="throats_cellsThroatsGraph.txt", network=None, voxel_size=None, return_geometry=False): r""" Loads network data from an iMorph processed image stack Parameters ---------- path : string The path of the folder where the subfiles are held node_file : string The file that describes the pores and throats, the default iMorph name is: throats_cellsThroatsGraph_Nodes.txt graph_file : string The file that describes the connectivity of the network, the default iMorph name is: throats_cellsThroatsGraph.txt network : OpenPNM Network Object The OpenPNM Network onto which the data should be loaded. If no network is supplied then an empty import network is created and returned. voxel_size : float Allows the user to define a voxel size different than what is contained in the node_file. The value must be in meters. return_geometry : Boolean If True, then all geometrical related properties are removed from the Network object and added to a GenericGeometry object. In this case the method returns a tuple containing (network, geometry). If False (default) then the returned Network will contain all properties that were in the original file. In this case, the user can call the ```split_geometry``` method explicitly to perform the separation. Returns ------- If no Network object is supplied then one will be created and returned. If return_geometry is True, then a tuple is returned containing both the network and a geometry object. """ # path = Path(path) node_file = os.path.join(path.resolve(), node_file) graph_file = os.path.join(path.resolve(), graph_file) # parsing the nodes file with open(node_file, 'r') as file: Np = sp.fromstring(file.readline().rsplit('=')[1], sep='\t', dtype=int)[0] vox_size = sp.fromstring(file.readline().rsplit(')')[1], sep='\t',)[0] # network always recreated to prevent errors network = GenericNetwork(Np=Np, Nt=0) # Define expected properies network['pore.volume'] = sp.nan scrap_lines = [file.readline() for line in range(4)] while True: vals = file.readline().split('\t') if len(vals) == 1: break network['pore.volume'][int(vals[0])] = float(vals[3]) if 'pore.'+vals[2] not in network.labels(): network['pore.'+vals[2]] = False network['pore.'+vals[2]][int(vals[0])] = True if voxel_size is None: voxel_size = vox_size * 1.0E-6 # file stores value in microns if voxel_size < 0: raise(Exception('Error - Voxel size must be specfied in ' + 'the Nodes file or as a keyword argument.')) # parsing the graph file with open(graph_file, 'r') as file: # Define expected properties network['pore.coords'] = sp.zeros((Np, 3))*sp.nan network['pore.types'] = sp.nan network['pore.color'] = sp.nan network['pore.radius'] = sp.nan network['pore.dmax'] = sp.nan network['pore.node_number'] = sp.nan # Scan file to get pore coordinate data scrap_lines = [file.readline() for line in range(3)] line = file.readline() xmax = 0.0 ymax = 0.0 zmax = 0.0 node_num = 0 while line != 'connectivity table\n': vals = sp.fromstring(line, sep='\t') xmax = vals[1] if vals[1] > xmax else xmax ymax = vals[2] if vals[2] > ymax else ymax zmax = vals[3] if vals[3] > zmax else zmax network['pore.coords'][int(vals[0]), :] = vals[1:4] network['pore.types'][int(vals[0])] = vals[4] network['pore.color'][int(vals[0])] = vals[5] network['pore.radius'][int(vals[0])] = vals[6] network['pore.dmax'][int(vals[0])] = vals[7] network['pore.node_number'][int(vals[0])] = node_num node_num += 1 line = file.readline() # Scan file to get to connectivity data scrap_lines.append(file.readline()) # Skip line # Create sparse lil array incrementally build adjacency matrix lil = sp.sparse.lil_matrix((Np, Np), dtype=int) while True: vals = sp.fromstring(file.readline(), sep='\t', dtype=int) if len(vals) <= 1: break lil.rows[vals[0]] = vals[2:] lil.data[vals[0]] = sp.ones(vals[1]) # fixing any negative volumes or distances so they are 1 voxel/micron network['pore.volume'][sp.where(network['pore.volume'] < 0)[0]] = 1.0 network['pore.radius'][sp.where(network['pore.radius'] < 0)[0]] = 1.0 network['pore.dmax'][sp.where(network['pore.dmax'] < 0)[0]] = 1.0 # Add adjacency matrix to OpenPNM network conns = sp.sparse.triu(lil, k=1, format='coo') network.update({'throat.all': sp.ones(len(conns.col), dtype=bool)}) network['throat.conns'] = sp.vstack([conns.row, conns.col]).T network['pore.to_trim'] = False network['pore.to_trim'][network.pores('*throat')] = True Ts = network.pores('to_trim') new_conns = network.find_neighbor_pores(pores=Ts, flatten=False) extend(network=network, throat_conns=new_conns, labels='new_conns') for item in network.props('pore'): item = item.split('.')[1] arr = sp.ones_like(network['pore.'+item])[0] arr = sp.tile(A=arr, reps=[network.Nt, 1])*sp.nan network['throat.'+item] = sp.squeeze(arr) network['throat.'+item][network.throats('new_conns')] = \ network['pore.'+item][Ts] trim(network=network, pores=Ts) # setting up boundary pores x_coord, y_coord, z_coord = sp.hsplit(network['pore.coords'], 3) network['pore.front_boundary'] = sp.ravel(x_coord == 0) network['pore.back_boundary'] = sp.ravel(x_coord == xmax) network['pore.left_boundary'] = sp.ravel(y_coord == 0) network['pore.right_boundary'] = sp.ravel(y_coord == ymax) network['pore.bottom_boundary'] = sp.ravel(z_coord == 0) network['pore.top_boundary'] = sp.ravel(z_coord == zmax) # removing any pores that got classified as a boundary pore but # weren't labled a border_cell_face ps = sp.where(~sp.in1d(network.pores('*_boundary'), network.pores('border_cell_face')))[0] ps = network.pores('*_boundary')[ps] for side in ['front', 'back', 'left', 'right', 'top', 'bottom']: network['pore.'+side+'_boundary'][ps] = False # setting internal label network['pore.internal'] = False network['pore.internal'][network.pores('*_boundary', mode='not')] = True # adding props to border cell face throats and from pores Ts = sp.where(network['throat.conns'][:, 1] > network.pores('border_cell_face')[0] - 1)[0] faces = network['throat.conns'][Ts, 1] for item in network.props('pore'): item = item.split('.')[1] network['throat.'+item][Ts] = network['pore.'+item][faces] network['pore.volume'][faces] = 0.0 # applying unit conversions # TODO: Determine if radius and dmax are indeed microns and not voxels network['pore.coords'] = network['pore.coords'] * 1e-6 network['pore.radius'] = network['pore.radius'] * 1e-6 network['pore.dmax'] = network['pore.dmax'] * 1e-6 network['pore.volume'] = network['pore.volume'] * voxel_size**3 network['throat.coords'] = network['throat.coords'] * 1e-6 network['throat.radius'] = network['throat.radius'] * 1e-6 network['throat.dmax'] = network['throat.dmax'] * 1e-6 network['throat.volume'] = network['throat.volume'] * voxel_size**3 return network.project
def test_check_network_health_looped_throats(self): net = op.network.Cubic(shape=[5, 5, 5]) extend(network=net, throat_conns=[[5, 5]]) a = net.check_network_health() assert a['looped_throats'] == np.array([300])
def spider_web_network(im_soft, mhs, cc_im, dtheta=10, pixel_size=10.4e-6, path=None, filename='spider_net'): # Make spiderweb dividing lines (inds_x, inds_y) = np.indices(im_soft.shape) inds_x -= mhs inds_y -= mhs theta = np.around(np.arctan2(inds_y, inds_x) * 360 / (2 * np.pi), 2) remainder = theta % dtheta lines = np.logical_or((remainder < 1.0), (remainder > (dtheta - 1.0))) med_ax = medial_axis(lines) plt.figure() plt.imshow(med_ax) med_ax = binary_dilation(med_ax, selem=square(2)) # Multiply cc image by -1 along the dividing lines to define nodes cc_im[med_ax] *= -1 # Extract coordinates of nodes on the current collectors cc_coords = [] for i in np.unique(cc_im)[np.unique(cc_im) < 0]: lab, N = label(cc_im == i, return_num=True) coords = np.zeros([N, 2], dtype=int) for l_int in np.arange(N): x, y = np.where(lab == l_int + 1) if len(x) > 0: coords[l_int] = [ np.int(np.around(np.mean(x), 0)), np.int(np.around(np.mean(y), 0)) ] cc_coords.append(coords) plt.figure() plt.imshow(cc_im) plt.scatter(cc_coords[0][:, 1], cc_coords[0][:, 0], c='r', s=50) plt.scatter(cc_coords[1][:, 1], cc_coords[1][:, 0], c='b', s=50) # Collect coords together coords = np.vstack((cc_coords[0], cc_coords[1])) # Record which belongs to same cc first_cc = np.zeros(len(coords), dtype=bool) first_cc[:len(cc_coords[0])] = True # Find which theta bin coords are in point_thetas = theta[coords[:, 0], coords[:, 1]] bins = np.arange(-180, 180 + 2 * dtheta, dtheta) - dtheta / 2 point_group_theta = np.zeros(len(coords[:, 0]), dtype=int) for i in range(len(bins) - 1): lower = bins[i] upper = bins[i + 1] sel = (point_thetas > lower) * (point_thetas < upper) point_group_theta[sel] = i # Get radial position of coords rads = np.linalg.norm(coords - mhs, axis=1) groups, counts = np.unique(point_group_theta, return_counts=True) sorted_groups = np.zeros([len(groups), counts.max()], dtype=int) sorted_groups.fill(-1) for g in range(len(groups)): Ps = np.arange(0, len(point_group_theta), 1)[point_group_theta == g] group_rads = rads[Ps] sorted_rads = group_rads.argsort() sorted_Ps = Ps[sorted_rads] sorted_groups[g, :len(sorted_Ps)] = sorted_Ps plt.figure() cc_groups = first_cc[sorted_groups].astype(int) cc_groups[sorted_groups == -1] = -1 plt.imshow(cc_groups) inner_Ps = sorted_groups[:, 0] inner_cc = first_cc[inner_Ps] change_cc = inner_cc != np.roll(inner_cc, -1) sorted_cc = first_cc[sorted_groups].astype(int) arc_indices = [] cc_roll = [[], []] for cc_num, this_cc in enumerate([True, False]): start_group = np.argwhere( np.logical_and(inner_cc == this_cc, change_cc)).flatten()[0] layer_group = np.arange(0, len(groups), 1) layer_group = np.roll(layer_group, -start_group) i = 0 layer = 0 numbered_cc = first_cc[sorted_groups].astype(int) while layer < counts.max(): g = layer_group[0] if sorted_cc[g, layer] == this_cc: pass else: layer += 1 if layer < counts.max(): if sorted_groups[g, layer] > -1: cc_roll[cc_num].append(sorted_groups[g, layer]) numbered_cc[g, layer] = i arc_indices.append(g) i += 1 else: pass layer_group = np.roll(layer_group, 1) plt.figure() plt.imshow(numbered_cc) print(len(np.unique(cc_roll[0] + cc_roll[1]))) ordered_neg_Ps = np.asarray(cc_roll[0]) ordered_pos_Ps = np.asarray(cc_roll[1]) neg_coords = coords[ordered_neg_Ps] neg_conns = np.vstack( (np.arange(0, len(ordered_neg_Ps) - 1, 1), np.arange(0, len(ordered_neg_Ps) - 1, 1) + 1)).T pos_coords = coords[ordered_pos_Ps] pos_conns = np.vstack( (np.arange(0, len(ordered_pos_Ps) - 1, 1), np.arange(0, len(ordered_pos_Ps) - 1, 1) + 1)).T pos_conns += len(ordered_neg_Ps) neg_inner_conns = [] for i, p_neg in enumerate(ordered_neg_Ps): g, layer = np.argwhere(sorted_groups == p_neg).flatten() if layer < counts.max() - 1: neighbor = sorted_groups[g, layer + 1] if neighbor > -1: sorted_neighbor = np.argwhere( ordered_pos_Ps == neighbor).flatten()[0] neg_inner_conns.append( [i, sorted_neighbor + len(ordered_neg_Ps)]) pos_inner_conns = [] for i, p_pos in enumerate(ordered_pos_Ps): g, layer = np.argwhere(sorted_groups == p_pos).flatten() if layer < counts.max() - 1: neighbor = sorted_groups[g, layer + 1] if neighbor > -1: sorted_neighbor = np.argwhere( ordered_neg_Ps == neighbor).flatten() if len(sorted_neighbor) > 0: sorted_neighbor = sorted_neighbor[0] pos_inner_conns.append( [i + len(ordered_neg_Ps), sorted_neighbor]) neg_inner_conns = np.asarray(neg_inner_conns) pos_inner_conns = np.asarray(pos_inner_conns) new_coords = np.vstack((neg_coords, pos_coords)) coords_3d = np.vstack( (new_coords[:, 0], new_coords[:, 1], np.zeros(new_coords.shape[0]))).T new_conns = np.vstack( (neg_conns, pos_conns, neg_inner_conns, pos_inner_conns)) net = op.network.GenericNetwork(conns=new_conns, coords=coords_3d) Ps, counts = np.unique(np.hstack( (net['throat.conns'][:, 0], net['throat.conns'][:, 1])), return_counts=True) net['pore.surface'] = False net['pore.terminal'] = False net['pore.neg_cc'] = False net['pore.pos_cc'] = False net['pore.inner'] = False net['pore.outer'] = False net['pore.surface'][Ps[counts < 4]] = True net['pore.terminal'][Ps[counts == 2]] = True net['pore.neg_cc'][:len(ordered_neg_Ps)] = True net['pore.pos_cc'][len(ordered_neg_Ps):] = True net['pore.cell_id'] = np.arange(0, net.Np, 1).astype(int) net['pore.arc_index'] = np.asarray(arc_indices) net['pore.radial_position'] = np.linalg.norm(new_coords - mhs, axis=1) rad_pos = net['pore.radial_position'] inner_mask = np.logical_and(net['pore.surface'], rad_pos < mhs / 2) outer_mask = np.logical_and(net['pore.surface'], rad_pos > mhs / 2) net['pore.inner'][inner_mask] = True net['pore.outer'][outer_mask] = True net['throat.neg_cc'] = False net['throat.pos_cc'] = False net['throat.spm_resistor'] = True pos_cc_Ts = net.find_neighbor_throats(net.pores("pos_cc"), mode="xnor") neg_cc_Ts = net.find_neighbor_throats(net.pores("neg_cc"), mode="xnor") net['throat.neg_cc'][neg_cc_Ts] = True net['throat.pos_cc'][pos_cc_Ts] = True net['throat.spm_resistor'][neg_cc_Ts] = False net['throat.spm_resistor'][pos_cc_Ts] = False net['throat.spm_resistor_order'] = -1 spm_res = net['throat.spm_resistor'] n_spm = np.sum(spm_res) net['throat.spm_resistor_order'][spm_res] = np.arange(0, n_spm, 1, dtype=int) net['throat.spm_neg_inner'] = False net['throat.spm_pos_inner'] = False res_order = net['throat.spm_resistor_order'] net['throat.spm_neg_inner'][spm_res * (res_order < len(neg_inner_conns))] = True net['throat.spm_pos_inner'][spm_res * (res_order >= len(neg_inner_conns))] = True net['pore.neg_tab'] = False net['pore.pos_tab'] = False net['pore.neg_tab'][net.pores(['inner', 'neg_cc', 'terminal'], mode='and')] = True net['pore.pos_tab'][net.pores(['outer', 'pos_cc', 'terminal'], mode='and')] = True num_free = net.num_pores('outer') outer_pos = net['pore.coords'][net.pores('outer')] x = outer_pos[:, 0] - mhs y = outer_pos[:, 1] - mhs r, t = ecm.polar_transform(x, y) r_new = np.ones(num_free) * (r + 50) new_x, new_y = ecm.cartesian_transform(r_new, t) free_coords = outer_pos.copy() free_coords[:, 0] = new_x + mhs free_coords[:, 1] = new_y + mhs start = len(ordered_neg_Ps) + len(ordered_pos_Ps) - num_free free_conns = np.vstack( (np.arange(0, num_free, 1), np.arange(0, num_free, 1) + num_free)).T free_conns += start tt.extend(net, pore_coords=free_coords, throat_conns=free_conns, labels=['free_stream']) free_Ps = net['pore.free_stream'] net['pore.arc_index'][free_Ps] = net['pore.arc_index'][net['pore.outer']] net['pore.cell_id'][net.pores('free_stream')] = -1 net['pore.cell_id'] = net['pore.cell_id'].astype(int) fig, ax = plt.subplots(figsize=(12, 12)) ax = ecm.plot_resistors(net, net.throats('neg_cc'), c='r', ax=ax) ax = ecm.plot_resistors(net, net.throats('pos_cc'), c='b', ax=ax) ax = ecm.plot_resistors(net, net.throats('spm_resistor'), c='k', ax=ax) ax = tt.plot_coordinates(net, pores=net.pores('neg_cc'), c='r', ax=ax) ax = tt.plot_coordinates(net, pores=net.pores('pos_cc'), c='b', ax=ax) ax = tt.plot_coordinates(net, pores=net.pores('surface'), c='k', ax=ax) ax = tt.plot_coordinates(net, pores=net.pores('outer'), c='pink', ax=ax) ax = tt.plot_coordinates(net, pores=net.pores('inner'), c='purple', ax=ax) ax = tt.plot_coordinates(net, pores=net.pores('terminal'), c='y', s=100, ax=ax) ax = tt.plot_coordinates(net, pores=net.pores('free_stream'), c='g', ax=ax) ax = tt.plot_connections(net, throats=net.throats('free_stream'), c='g', ax=ax) fig, ax = plt.subplots(figsize=(12, 12)) ax = ecm.plot_resistors(net, net.throats('neg_cc'), c='r', ax=ax) ax = ecm.plot_resistors(net, net.throats('pos_cc'), c='b', ax=ax) ax = ecm.plot_resistors(net, net.throats('spm_resistor'), c='k', ax=ax) plt.imshow(im_soft.T) # Scale and save net prj = wrk['proj_01'] net['pore.coords'] *= pixel_size mean = mhs * pixel_size net['pore.coords'][:, 0] -= mean net['pore.coords'][:, 1] -= mean net['pore.radial_position'] = np.linalg.norm(net['pore.coords'], axis=1) net['throat.radial_position'] = net.interpolate_data( 'pore.radial_position') net['throat.arc_length'] = net['throat.radial_position'] * np.deg2rad( dtheta) if path is None: path = ecm.INPUT_DIR wrk.save_project(project=prj, filename=os.path.join(path, filename)) return net