[docs]defplot_connections(network,throats=None,ax=None,size_by=None,color_by=None,label_by=None,cmap='jet',color='b',alpha=1.0,linestyle='solid',linewidth=1,**kwargs):# pragma: no coverr""" Produce a 3D plot of the network topology. This shows how throats connect for quick visualization without having to export data to veiw in Paraview. Parameters ---------- network : Network The network whose topological connections to plot throats : array_like (optional) The list of throats to plot if only a sub-sample is desired. This is useful for inspecting a small region of the network. If no throats are specified then all throats are shown. fig : Matplotlib figure handle and line property arguments (optional) If a ``fig`` is supplied, then the topology will be overlaid on this plot. This makes it possible to combine coordinates and connections, and to color throats differently for instance. size_by : array_like (optional) An ndarray of throat values (e.g. alg['throat.rate']). These values are used to scale the ``linewidth``, so if the lines are too thin, then increase ``linewidth``. color_by : array_like (optional) An ndarray of throat values (e.g. alg['throat.rate']). label_by : array_like (optional) An array or list of values to use as labels cmap : str or cmap object (optional) The matplotlib colormap to use if specfying a throat property for ``color_by`` color : str, optional (optional) A matplotlib named color (e.g. 'r' for red). alpha : float (optional) The transparency of the lines, with 1 being solid and 0 being invisible linestyle : str (optional) Can be one of {'solid', 'dashed', 'dashdot', 'dotted'}. Default is 'solid'. linewidth : float (optional) Controls the thickness of drawn lines. Is used to scale the thickness if ``size_by`` is given. Default is 1. If a value is provided for ``size_by`` then they are used to scale the ``linewidth``. font : dict A dictionary of key-value pairs that are used to control the font appearance if `label_by` is provided. **kwargs : dict All other keyword arguments are passed on to the ``Line3DCollection`` class of matplotlib, so check their documentation for additional formatting options. Returns ------- lc : LineCollection or Line3DCollection Matplotlib object containing the lines representing the throats. Notes ----- To create a single plot containing both pore coordinates and throats, consider creating an empty figure and then pass the ``ax`` object as an argument to ``plot_connections`` and ``plot_coordinates``. Otherwise, each call to either of these methods creates a new figure. See Also -------- plot_coordinates Examples -------- >>> import openpnm as op >>> import matplotlib as mpl >>> import matplotlib.pyplot as plt >>> mpl.use('Agg') >>> pn = op.network.Cubic(shape=[10, 10, 3]) >>> pn.add_boundary_pores() >>> Ts = pn.throats('*boundary', mode='not') # find internal throats >>> fig, ax = plt.subplots() # create empty figure >>> _ = op.visualization.plot_connections(network=pn, ... throats=Ts) # plot internal throats >>> Ts = pn.throats('*boundary') # find boundary throats >>> _ = op.visualization.plot_connections(network=pn, ... throats=Ts, ... ax=ax, ... color='r') # plot boundary throats in red """importmatplotlib.pyplotaspltfrommatplotlibimportcmfrommatplotlibimportcolorsasmcolorsfrommatplotlib.collectionsimportLineCollectionfrommpl_toolkits.mplot3dimportAxes3Dfrommpl_toolkits.mplot3d.art3dimportLine3DCollectionfromopenpnm.topotoolsimportdimensionalityTs=network.TsifthroatsisNoneelsenetwork._parse_indices(throats)dim=dimensionality(network)ThreeD=Trueifdim.sum()==3elseFalse# Add a dummy axis for 1D networksifdim.sum()==1:dim[np.argwhere(~dim)[0]]=Trueif"fig"inkwargs.keys():raiseException("'fig' argument is deprecated, use 'ax' instead.")ifaxisNone:fig,ax=plt.subplots()else:# The next line is necessary if ax was created using plt.subplots()fig,ax=ax.get_figure(),ax.get_figure().gca()ifThreeDandax.name!='3d':fig.delaxes(ax)ax=fig.add_subplot(111,projection='3d')# Collect coordinatesPs=np.unique(network['throat.conns'][Ts])X,Y,Z=network['pore.coords'][Ps].Txyz=network["pore.coords"][:,dim]P1,P2=network["throat.conns"][Ts].Tthroat_pos=np.column_stack((xyz[P1],xyz[P2])).reshape((Ts.size,2,dim.sum()))# Deal with optional style related argumentsif'c'inkwargs.keys():color=kwargs.pop('c')color=mcolors.to_rgb(color)+tuple([alpha])ifisinstance(cmap,str):try:cmap=plt.colormaps.get_cmap(cmap)exceptAttributeError:cmap=plt.cm.get_cmap(cmap)# Override colors with color_by if givenifcolor_byisnotNone:color_by=np.array(color_by,dtype=np.float16)iflen(color_by)!=len(Ts):color_by=color_by[Ts]ifnotnp.all(np.isfinite(color_by)):color_by[~np.isfinite(color_by)]=0logger.warning('nans or infs found in color_by array, setting to 0')vmin=kwargs.pop('vmin',color_by.min())vmax=kwargs.pop('vmax',color_by.max())cscale=(color_by-vmin)/(vmax-vmin)color=cmap(cscale)color[:,3]=alphaifsize_byisnotNone:iflen(size_by)!=len(Ts):size_by=size_by[Ts]ifnotnp.all(np.isfinite(size_by)):size_by[~np.isfinite(size_by)]=0logger.warning('nans or infs found in size_by array, setting to 0')linewidth=size_by/size_by.max()*linewidthiflabel_byisnotNone:iflen(label_by)!=len(Ts):label_by=label_by[Ts]fontkws=kwargs.pop('font',{})ifThreeD:lc=Line3DCollection(throat_pos,colors=color,cmap=cmap,linestyles=linestyle,linewidths=linewidth,antialiaseds=np.ones_like(network.Ts),**kwargs)else:lc=LineCollection(throat_pos,colors=color,cmap=cmap,linestyles=linestyle,linewidths=linewidth,antialiaseds=np.ones_like(network.Ts),**kwargs)iflabel_byisnotNone:forcount,(P1,P2)inenumerate(network.conns[Ts,:]):i,j,k=np.mean(network.coords[[P1,P2],:],axis=0)ax.text(i,j,label_by[count],ha='center',va='center',**fontkws)ax.add_collection(lc)ifnp.size(Ts)>0:_scale_axes(ax=ax,X=X,Y=Y,Z=Z)_label_axes(ax=ax,X=X,Y=Y,Z=Z)fig.tight_layout()returnlc
[docs]defplot_coordinates(network,pores=None,ax=None,size_by=None,color_by=None,label_by=None,cmap='jet',color='r',alpha=1.0,marker='o',markersize=10,**kwargs):# pragma: no coverr""" Produce a 3D plot showing specified pore coordinates as markers. Parameters ---------- network : Network The network whose topological connections to plot. pores : array_like (optional) The list of pores to plot if only a sub-sample is desired. This is useful for inspecting a small region of the network. If no pores are specified then all are shown. ax : Matplotlib axis handle If ``ax`` is supplied, then the coordinates will be overlaid. This enables the plotting of multiple different sets of pores as well as throat connections from ``plot_connections``. size_by : str or array_like An ndarray of pore values (e.g. alg['pore.concentration']). These values are normalized by scaled by ``markersize``. Note that this controls the marker *area*, so if you want the markers to be proportional to diameter you should do `size_by=net['pore.diameter']**2`. color_by : str or array_like An ndarray of pore values (e.g. alg['pore.concentration']). label_by : array_like (optional) An array or list of values to use as labels cmap : str or cmap object The matplotlib colormap to use if specfying a pore property for ``color_by`` color : str A matplotlib named color (e.g. 'r' for red). alpha : float The transparency of the lines, with 1 being solid and 0 being invisible marker : 's' The marker to use. The default is a circle. Options are explained `here <https://matplotlib.org/3.2.1/api/markers_api.html>`_ markersize : scalar Controls size of marker, default is 1.0. This value is used to scale the ``size_by`` argument if given. font : dict A dictionary of key-value pairs that are used to control the font appearance if `label_by` is provided. **kwargs All other keyword arguments are passed on to the ``scatter`` function of matplotlib, so check their documentation for additional formatting options. Returns ------- pc : PathCollection Matplotlib object containing the markers representing the pores. Notes ----- To create a single plot containing both pore coordinates and throats, consider creating an empty figure and then pass the ``ax`` object as an argument to ``plot_connections`` and ``plot_coordinates``. Otherwise, each call to either of these methods creates a new figure. See Also -------- plot_connections Examples -------- >>> import openpnm as op >>> import matplotlib as mpl >>> import matplotlib.pyplot as plt >>> mpl.use('Agg') >>> pn = op.network.Cubic(shape=[10, 10, 3]) >>> pn['pore.internal'] = True >>> pn.add_boundary_pores() >>> Ps = pn.pores('internal') # find internal pores >>> fig, ax = plt.subplots() # create empty figure >>> _ = op.visualization.plot_coordinates(network=pn, ... pores=Ps, ... color='b', ... ax=ax) # plot internal pores >>> Ps = pn.pores('*boundary') # find boundary pores >>> _ = op.visualization.plot_coordinates(network=pn, ... pores=Ps, ... color='r', ... ax=ax) # plot boundary pores in red """importmatplotlib.pyplotaspltfrommatplotlibimportcmfrommpl_toolkits.mplot3dimportAxes3Dfromopenpnm.topotoolsimportdimensionalityPs=network.PsifporesisNoneelsenetwork._parse_indices(pores)dim=dimensionality(network)ThreeD=Trueifdim.sum()==3elseFalse# Add a dummy axis for 1D networksifdim.sum()==1:dim[np.argwhere(~dim)[0]]=True# Add 2 dummy axes for 0D networks (1 pore only)ifdim.sum()==0:dim[[0,1]]=Trueif"fig"inkwargs.keys():raiseException("'fig' argument is deprecated, use 'ax' instead.")ifaxisNone:fig,ax=plt.subplots()else:# The next line is necessary if ax was created using plt.subplots()fig,ax=ax.get_figure(),ax.get_figure().gca()ifThreeDandax.name!='3d':fig.delaxes(ax)ax=fig.add_subplot(111,projection='3d')# Collect specified coordinatesX,Y,Z=network['pore.coords'][Ps].T# The bounding box for fig is the entire ntwork (to fix the problem with# overwriting figures' axes lim)Xl,Yl,Zl=network['pore.coords'].T# Parse formatting kwargsif'c'inkwargs.keys():color=kwargs.pop('c')if's'inkwargs.keys():markersize=kwargs.pop('s')ifisinstance(cmap,str):try:cmap=plt.colormaps.get_cmap(cmap)exceptAttributeError:cmap=plt.cm.get_cmap(cmap)ifcolor_byisnotNone:color_by=np.array(color_by,dtype=np.float16)iflen(color_by)!=len(Ps):color_by=color_by[Ps]ifnotnp.all(np.isfinite(color_by)):color_by[~np.isfinite(color_by)]=0logger.warning('nans or infs found in color_by array, setting to 0')vmin=kwargs.pop('vmin',color_by.min())vmax=kwargs.pop('vmax',color_by.max())cscale=(color_by-vmin)/(vmax-vmin)color=cmap(cscale)ifsize_byisnotNone:iflen(size_by)!=len(Ps):size_by=size_by[Ps]ifnotnp.all(np.isfinite(size_by)):size_by[~np.isfinite(size_by)]=0logger.warning('nans or infs found in size_by array, setting to 0')markersize=size_by/size_by.max()*markersizeiflabel_byisnotNone:iflen(label_by)!=len(Ps):label_by=label_by[Ps]fontkws=kwargs.pop('font',{})ifThreeD:sc=ax.scatter(X,Y,Z,c=color,s=markersize,marker=marker,alpha=alpha,**kwargs)_scale_axes(ax=ax,X=Xl,Y=Yl,Z=Zl)else:_X,_Y=np.column_stack((X,Y,Z))[:,dim].Tsc=ax.scatter(_X,_Y,c=color,s=markersize,marker=marker,alpha=alpha,**kwargs)iflabel_byisnotNone:forcount,(i,j,k)inenumerate(network.coords[Ps,:]):ax.text(i,j,label_by[count],ha='center',va='center',**fontkws)_scale_axes(ax=ax,X=Xl,Y=Yl,Z=np.zeros_like(Yl))_label_axes(ax=ax,X=Xl,Y=Yl,Z=Zl)fig.tight_layout()returnsc
def_label_axes(ax,X,Y,Z):labels=["X","Y","Z"]dim=np.zeros(3,dtype=bool)fori,arrinenumerate([X,Y,Z]):ifnp.unique(arr).size>1:dim[i]=True# Add a dummy axis for 1D networksifdim.sum()==1:dim[np.argwhere(~dim)[0]]=True# Add 2 dummy axes for 0D networks (1 pore only)ifdim.sum()==0:dim[[0,1]]=Truedim_idx=np.argwhere(dim).squeeze()ax.set_xlabel(labels[dim_idx[0]])ax.set_ylabel(labels[dim_idx[1]])ifhasattr(ax,"set_zlim"):ax.set_zlabel("Z")def_scale_axes(ax,X,Y,Z):max_range=np.ptp([X,Y,Z]).max()/2mid_x=(X.max()+X.min())*0.5mid_y=(Y.max()+Y.min())*0.5mid_z=(Z.max()+Z.min())*0.5ax.set_xlim(mid_x-max_range,mid_x+max_range)ax.set_ylim(mid_y-max_range,mid_y+max_range)ifhasattr(ax,"set_zlim"):ax.set_zlim(mid_z-max_range,mid_z+max_range)else:ax.axis("equal")
[docs]defplot_networkx(network,plot_throats=True,labels=None,colors=None,scale=1,ax=None,alpha=1.0):# pragma: no coverr""" Creates a pretty 2D plot for 2D OpenPNM networks. Parameters ---------- network : Network plot_throats : bool, optional Plots throats as well as pores, if True. labels : list, optional List of OpenPNM labels colors : list, optional List of corresponding colors to the given `labels`. scale : float, optional Scale factor for size of pores. ax : matplotlib.Axes, optional Matplotlib axes object alpha: float, optional Transparency value, 1 is opaque and 0 is transparent """importmatplotlib.pyplotaspltfrommatplotlib.collectionsimportPathCollectionfromnetworkximportGraph,draw_networkx_edges,draw_networkx_nodesfromopenpnm.topotoolsimportdimensionalitydims=dimensionality(network)ifdims.sum()>2:raiseException("NetworkX plotting only works for 2D networks.")temp=network['pore.coords'].T[dims].squeeze()ifdims.sum()==1:x=tempy=np.zeros_like(x)ifdims.sum()==2:x,y=temptry:node_size=scale*network['pore.diameter']exceptKeyError:node_size=np.ones_like(x)*scale*0.5G=Graph()pos={network.Ps[i]:[x[i],y[i]]foriinrange(network.Np)}ifnotnp.isfinite(node_size).all():node_size[~np.isfinite(node_size)]=np.nanmin(node_size)node_color=np.array(['k']*len(network.Ps))iflabels:ifnotisinstance(labels,list):labels=[labels]ifnotisinstance(colors,list):colors=[colors]iflen(labels)!=len(colors):raiseException('len(colors) must be equal to len(labels)!')forlabel,colorinzip(labels,colors):node_color[network.pores(label)]=colorifaxisNone:fig,ax=plt.subplots()ax.set_aspect('equal',adjustable='datalim')offset=node_size.max()*0.5ax.set_xlim((x.min()-offset,x.max()+offset))ax.set_ylim((y.min()-offset,y.max()+offset))ax.axis("off")# Keep track of already plotted nodestemp=[id(item)foriteminax.collectionsifisinstance(item,PathCollection)]# Plot poresgplot=draw_networkx_nodes(G,ax=ax,pos=pos,nodelist=network.Ps.tolist(),alpha=alpha,node_color=node_color,edgecolors=node_color,node_size=node_size)# (Optionally) Plot throatsifplot_throats:draw_networkx_edges(G,pos=pos,edge_color='k',alpha=alpha,edgelist=network['throat.conns'].tolist(),ax=ax)spi=2700# 1250 was obtained by trial and errorfigwidth,figheight=ax.get_figure().get_size_inches()figsize_ratio=figheight/figwidthdata_ratio=ax.get_data_ratio()corr=min(figsize_ratio/data_ratio,1)xrange=np.ptp(ax.get_xlim())markersize=np.atleast_1d((corr*figwidth)**2/xrange**2*node_size**2*spi)foriteminax.collections:ifisinstance(item,PathCollection)andid(item)notintemp:item.set_sizes(markersize)returngplot
[docs]defplot_tutorial(network,pore_labels=None,throat_labels=None,font_size=12,line_width=2,node_color='b',edge_color='r',node_size=500):# pragma: no coverr""" Generate a network plot suitable for tutorials and explanations. Parameters ---------- network : Network The network to plot, should be 2D, since the z-coordinate will be ignored. pore_labels : array_like A list of values to use for labeling the pores. If not provided then pore index is used. throat_labels : array_like A list of values to use for labeling the throat. If not provided then throat index is used. font_size : int Size of font to use for labels. line_width : int Thickness of edge lines and node borders. node_color : str Color of node border. edge_color : str Color of edge lines. node_size : int Size of node circle. Returns ------- g : NetworkX plot object """importmatplotlib.pyplotaspltimportnetworkxasnxfromopenpnm.ioimportnetwork_to_networkxG=network_to_networkx(network=network)pos={i:network['pore.coords'][i,0:2]foriinnetwork.Ps}ifpore_labelsisNone:labels={i:iforiinnetwork.Ps}else:labels={i:pore_labels[i]foriinnetwork.Ps}ifthroat_labelsisNone:edge_labels={tuple(network['throat.conns'][i,:]):iforiinnetwork.Ts}else:edge_labels={tuple(network['throat.conns'][i,:]):throat_labels[i]foriinnetwork.Ts}gplot=nx.draw_networkx_nodes(G,pos,node_size=node_size,node_color='w',edgecolors=node_color,linewidths=line_width)nx.draw_networkx_edges(G,pos,width=line_width,edge_color=edge_color)nx.draw_networkx_labels(G,pos,labels=labels,font_size=font_size,font_color='k')nx.draw_networkx_edge_labels(G,pos,edge_labels=edge_labels,font_size=font_size,font_color='k')# Prettify the figure (margins, etc.)plt.axis('off')ax=plt.gca()ax.margins(0.1,0.1)ax.set_aspect("equal")fig=plt.gcf()fig.tight_layout()dims=op.topotools.dimensionality(network)xy_range=np.ptp(network.coords,axis=0)[dims]aspect_ratio=xy_range[0]/xy_range[1]fig.set_size_inches(5,5/aspect_ratio)returngplot
[docs]defplot_notebook(network,node_color=0,edge_color=0,node_size=1,node_scale=20,edge_scale=5,colormap='viridis'):r""" Visualize a network in 3D using Plotly. The pores and throats are scaled and colored by their properties. The final figure can be rotated and zoomed. Parameters ---------- network : Network The network to visualize node_color : ndarray An array of values used for coloring the pores. If not given, the lowest value of the employed colormap is assigned to all markers. edge_color : ndarray An array of values used for coloring the throats. If not given, the lowest value of the employed colormap is assigned to all lines. node_size : ndarray An array of values controlling the size of the markers. If not given all markers will be the same size node_scale : scalar A scaler to resize the markers edge_scale : scalar A scaler to the line thickness colormap : str The colormap to use Returns ------- fig : Plotly graph object The graph object containing the generated plots. The object has several useful methods. Notes ----- **Important** a) This does not work in Spyder. It should only be called from a Jupyter Notebook. b) This is only meant for relatively small networks. For proper visualization use Paraview. """try:importplotly.graph_objectsasgoexceptImportError:raiseException('Plotly is not installed.''Please install Plotly using "pip install plotly"')# Get xyz coords for pointsx_nodes,y_nodes,z_nodes=network.coords.Tnode_size=np.ones(network.Np)*node_sizenode_color=np.ones(network.Np)*node_coloredge_color=np.ones(network.Nt)*edge_colornode_labels=[str(i)+': '+str(x)fori,xinenumerate(zip(node_size,node_color))]edge_labels=[str(i)+': '+str(x)fori,xinenumerate(edge_color)]# Create edges and nodes coordinatesN=network.Nt*3x_edges=np.zeros(N)x_edges[np.arange(0,N,3)]=network.coords[network.conns[:,0]][:,0]x_edges[np.arange(1,N,3)]=network.coords[network.conns[:,1]][:,0]x_edges[np.arange(2,N,3)]=np.nany_edges=np.zeros(network.Nt*3)y_edges[np.arange(0,N,3)]=network.coords[network.conns[:,0]][:,1]y_edges[np.arange(1,N,3)]=network.coords[network.conns[:,1]][:,1]y_edges[np.arange(2,N,3)]=np.nanz_edges=np.zeros(network.Nt*3)z_edges[np.arange(0,N,3)]=network.coords[network.conns[:,0]][:,2]z_edges[np.arange(1,N,3)]=network.coords[network.conns[:,1]][:,2]z_edges[np.arange(2,N,3)]=np.nan# Create plotly's Scatter3d object for pores and throatstrace_edges=go.Scatter3d(x=x_edges,y=y_edges,z=z_edges,mode='lines',line=dict(color=edge_color,width=edge_scale,colorscale=colormap),text=edge_labels,hoverinfo='text')trace_nodes=go.Scatter3d(x=x_nodes,y=y_nodes,z=z_nodes,mode='markers',marker=dict(symbol='circle',size=node_size*node_scale,color=node_color,colorscale=colormap,line=dict(color='black',width=0.5)),text=node_labels,hoverinfo='text')axis=dict(showbackground=False,showline=False,zeroline=False,showgrid=False,showticklabels=False,title='')layout=go.Layout(width=650,height=625,showlegend=False,scene=dict(xaxis=dict(axis),yaxis=dict(axis),zaxis=dict(axis),),margin=dict(t=100),hovermode='closest')data=[trace_edges,trace_nodes]fig=go.Figure(data=data,layout=layout)returnfig
def_generate_voxel_image(network,pore_shape,throat_shape,max_dim=200):r""" Generates a 3d numpy array from an OpenPNM network Parameters ---------- network : OpenPNM Network Network from which voxel image is to be generated pore_shape : str Shape of pores in the network, valid choices are "sphere", "cube" throat_shape : str Shape of throats in the network, valid choices are "cylinder", "cuboid" max_dim : int Number of voxels in the largest dimension of the network Returns ------- im : ndarray Voxelated image corresponding to the given pore network model Notes ----- (1) The generated voxel image is labeled with 0s, 1s and 2s signifying solid phase, pores, and throats respectively. """fromporespy.toolsimportinsert_cylinder,overlayfromskimage.morphologyimportball,cubexyz=network["pore.coords"]cn=network["throat.conns"]# Distance bounding box from the network by a fixed amountdelta=network["pore.diameter"].mean()/2ifisinstance(network,op.network.Cubic):try:delta=op.topotools.get_spacing(network).mean()/2exceptAttributeError:delta=network.spacing.mean()/2# Shift everything to avoid out-of-boundsextra_clearance=int(max_dim*0.05)# Transform points to satisfy origin at (0, 0, 0)xyz0=xyz.min(axis=0)-deltaxyz+=-xyz0res=(np.ptp(xyz,axis=0).max()+2*delta)/max_dimshape=np.rint((xyz.max(axis=0)+delta)/res).astype(int)+2*extra_clearance# Transforming from real coords to matrix coordsxyz=np.rint(xyz/res).astype(int)+extra_clearancepore_radi=np.rint(network["pore.diameter"]*0.5/res).astype(int)throat_radi=np.rint(network["throat.diameter"]*0.5/res).astype(int)im_pores=np.zeros(shape,dtype=np.uint8)im_throats=np.zeros_like(im_pores)ifpore_shape=="cube":pore_elem=cuberp=pore_radi*2+1# +1 since num_voxel must be oddrp_max=int(2*round(delta/res))+1ifpore_shape=="sphere":pore_elem=ballrp=pore_radirp_max=int(round(delta/res))ifthroat_shape=="cuboid":raiseException("Not yet implemented, try 'cylinder'.")# Generating voxels for poresfori,poreinenumerate(tqdm(network.Ps)):elem=pore_elem(rp[i])try:im_pores=overlay(im1=im_pores,im2=elem,c=xyz[i])exceptValueError:elem=pore_elem(rp_max)im_pores=overlay(im1=im_pores,im2=elem,c=xyz[i])# Get rid of pore overlapsim_pores[im_pores>0]=1# Generating voxels for throatsfori,throatinenumerate(tqdm(network.Ts)):try:im_throats=insert_cylinder(im_throats,r=throat_radi[i],xyz0=xyz[cn[i,0]],xyz1=xyz[cn[i,1]])exceptValueError:im_throats=insert_cylinder(im_throats,r=rp_max,xyz0=xyz[cn[i,0]],xyz1=xyz[cn[i,1]])# Get rid of throat overlapsim_throats[im_throats>0]=1# Subtract pore-throat overlap from throatsim_throats=(im_throats.astype(bool)*~im_pores.astype(bool)).astype(np.uint8)im=im_pores*1+im_throats*2returnim[extra_clearance:-extra_clearance,extra_clearance:-extra_clearance,extra_clearance:-extra_clearance]
[docs]defgenerate_voxel_image(network,pore_shape="sphere",throat_shape="cylinder",max_dim=None,rtol=0.1):r""" Generate a voxel image from a Network Parameters ---------- network : OpenPNM Network Network from which voxel image is to be generated pore_shape : str Shape of pores in the network, valid choices are "sphere", "cube" throat_shape : str Shape of throats in the network, valid choices are "cylinder", "cuboid" max_dim : int Number of voxels in the largest dimension of the network rtol : float Stopping criteria for finding the smallest voxel image such that further increasing the number of voxels in each dimension by 25% would improve the predicted porosity of the image by less that ``rtol`` Returns ------- im : ndarray Voxelated image corresponding to the given pore network model Notes ----- (1) The generated voxelated image is labeled with 0s, 1s and 2s signifying solid phase, pores, and throats respectively. (2) If max_dim is not provided, the method calculates it such that the further increasing it doesn't change porosity by much. """# If max_dim is provided, generate voxel image using max_dimifmax_dimisnotNone:return_generate_voxel_image(network,pore_shape,throat_shape,max_dim=max_dim)max_dim=200# If max_dim is not provided, find best max_dim that predicts porosityerr=100eps_old=200whileerr>rtol:im=_generate_voxel_image(network,pore_shape,throat_shape,max_dim=max_dim)eps=im.astype(bool).sum()/np.prod(im.shape)err=abs(1-eps/eps_old)eps_old=epsmax_dim=int(max_dim*1.25)returnim
[docs]defplot_vispy(network,pore_color=None,pore_size=None,throat_color=None,throat_size=None,bgcolor='grey',):r""" Creates a pretty network plot using VisPy. Parameters ---------- network """try:fromvispyimportsceneexceptModuleNotFoundError:raiseException("vispy must be installed to use this function")canvas=scene.SceneCanvas(keys='interactive',show=True,bgcolor=bgcolor)view=canvas.central_widget.add_view()view.camera='turntable'view.camera.fov=30view.camera.distance=3*np.max(network['pore.coords'])ifpore_colorisNone:pore_color=create_pore_colors_from_array(network['pore.diameter'],cmap='viridis')else:pore_color=create_pore_colors_from_array(pore_color,cmap='viridis')ifthroat_colorisNone:throat_color=create_throat_colors_from_array(network['throat.diameter'],cmap='viridis')else:throat_color=create_throat_colors_from_array(throat_color,cmap='viridis')ifpore_sizeisNone:pore_size=network['pore.diameter']ifthroat_sizeisNone:throat_size=2else:throat_size=np.max(throat_size)# Arrays not supported here# plot spheresvis=scene.visuals.Markers(pos=network['pore.coords'],size=pore_size,antialias=0,face_color=pore_color,edge_width=0,scaling=True,spherical=True,)vis.parent=view.scene# plot axis# vispy.scene.visuals.XYZAxis(parent=view.scene)# set camera centerview.camera.center=np.array((network['pore.coords'][:,0].max()/2,network['pore.coords'][:,1].max()/2,network['pore.coords'][:,2].max()/2))# data preparationlines=np.zeros((len(network['throat.conns']),2,3))foriinrange(len(network['throat.conns'])):pair=network['throat.conns'][i]line=np.array([[network['pore.coords'][pair[0]],network['pore.coords'][pair[1]]]])lines[i,:,:]=line# plot throatsvis2=scene.visuals.Line(lines,width=throat_size,color=throat_color,connect='segments',antialias=True,)vis2.parent=view.scene
[docs]defset_mpl_style():# pragma: no coverr""" Prettifies matplotlib's output by adjusting fonts, markersize etc. """sfont=12mfont=12lfont=12image_props={'interpolation':'none','cmap':'viridis'}line_props={'linewidth':2,'markersize':7,'markerfacecolor':'w'}font_props={'size':sfont}axes_props={'titlesize':lfont,'labelsize':mfont,'linewidth':2,'labelpad':8}xtick_props={'labelsize':sfont,'top':True,'direction':'in','major.size':6,'major.width':2}ytick_props={'labelsize':sfont,'right':True,'direction':'in','major.size':6,'major.width':2}legend_props={'fontsize':mfont,'frameon':False}figure_props={'titlesize':sfont,'autolayout':True}plt.rc('font',**font_props)plt.rc('lines',**line_props)plt.rc('axes',**axes_props)plt.rc('xtick',**xtick_props)plt.rc('ytick',**ytick_props)plt.rc('legend',**legend_props)plt.rc('figure',**figure_props)plt.rc('image',**image_props)try:importIPythonIPython.display.set_matplotlib_formats('png2x')exceptModuleNotFoundError:pass