[docs]@_geodocsdefspheres_and_cylinders(network,pore_diameter="pore.diameter",throat_diameter="throat.diameter",):r""" Calculates conduit lengths in the network assuming pores are spheres and throats are cylinders. A conduit is defined as ( 1/2 pore - full throat - 1/2 pore ). Parameters ---------- %(network)s %(Dp)s %(Dt)s Returns ------- lengths : ndarray Array (Nt by 3) containing conduit values for each element of the pore-throat-pore conduits. The array is formatted as ``[pore1, throat, pore2]``. """L_ctc=_get_L_ctc(network)D1,Dt,D2=network.get_conduit_data(pore_diameter.split(".",1)[-1]).T# Handle the case where Dt > Dpif(Dt>D1).any()or(Dt>D2).any():_raise_incompatible_data()# If spheres do not overlap:L1=np.sqrt(D1**2-Dt**2)/2L2=np.sqrt(D2**2-Dt**2)/2Lt=L_ctc-(L1+L2)ifnp.any(Lt<0):# Find pores that touch/overlap# Find diameter of overlap between spheresd=L_ctcR1=D1/2R2=D2/2# Check distance to the intersectionL1_int=(d**2-R2**2+R1**2)/(2*d)ifnp.any(L1_int<0)ornp.any(L1_int>L_ctc):raiseException('The pores overlap too much')D_int=2/(2*d)*np.sqrt(4*d**2*R1**2-(d**2-R2**2+R1**2)**2)mask=D_int>Dtifnp.any(mask):d=d[mask]R1=R1[mask]R2=R2[mask]L1[mask]=(d**2-R2**2+R1**2)/(2*d)L2[mask]=L_ctc[mask]-L1[mask]Lt[mask]=1e-15returnnp.vstack((L1,Lt,L2)).T
[docs]@_geodocsdefcircles_and_rectangles(network,pore_diameter="pore.diameter",throat_diameter="throat.diameter"):r""" Calculates conduit lengths in the network assuming pores are circles and throats are rectangles. A conduit is defined as ( 1/2 pore - full throat - 1/2 pore ). Parameters ---------- %(network)s %(Dp)s %(Dt)s Returns ------- Notes ----- This model should only be used for true 2D networks, i.e. with planar symmetry. """returnspheres_and_cylinders(network=network,pore_diameter=pore_diameter,throat_diameter=throat_diameter,)
[docs]@_geodocsdefcones_and_cylinders(network,pore_diameter="pore.diameter",throat_diameter="throat.diameter"):r""" Calculates conduit lengths in the network assuming pores are cones and throats are cylinders. A conduit is defined as ( 1/2 pore - full throat - 1/2 pore ). Parameters ---------- %(network)s %(Dp)s %(Dt)s Returns ------- """L_ctc=_get_L_ctc(network)D1,Dt,D2=network.get_conduit_data(pore_diameter.split(".",1)[-1]).TL1=D1/2L2=D2/2# Handle throats w/ overlapping pores_L1=(4*L_ctc**2+D1**2-D2**2)/(8*L_ctc)mask=L_ctc-0.5*(D1+D2)<0L1[mask]=_L1[mask]L2[mask]=(L_ctc-L1)[mask]Lt=np.maximum(L_ctc-(L1+L2),1e-15)returnnp.vstack((L1,Lt,L2)).T
[docs]@_geodocsdefintersecting_cones(network,pore_coords="pore.coords",throat_coords="throat.coords"):r""" Calculates conduit lengths in the network assuming pores are cones that intersect. Therefore, the throat is the cross sectional plane where two pores meet and has negligible/zero volume. A conduit is defined as ( 1/2 pore - 1/2 pore ). Parameters ---------- %(network)s %(Pcoords)s %(Tcoords)s Returns ------- """P12=network["throat.conns"]p_coords=network[pore_coords]t_coords=network[throat_coords]L1=np.sqrt(np.sum(((p_coords[P12[:,0]]-t_coords))**2,axis=1))L2=np.sqrt(np.sum(((p_coords[P12[:,1]]-t_coords))**2,axis=1))Lt=np.zeros(len(network.Ts))returnnp.vstack((L1,Lt,L2)).T
[docs]@_geodocsdefhybrid_cones_and_cylinders(network,pore_diameter="pore.diameter",throat_coords="throat.coords"):r""" Calculates conduit lengths in the network assuming pores are cones and throats are cylinders. A conduit is defined as ( 1/2 pore - full throat - 1/2 pore ). Parameters ---------- %(network)s %(Dp)s %(Tcoords)s Returns ------- """L_ctc=_get_L_ctc(network)D1,Dt,D2=network.get_conduit_data(pore_diameter.split(".",1)[-1]).TL1=D1/2L2=D2/2Lt=np.maximum(L_ctc-(L1+L2),1e-15)# Handle intersecting poresXYp=network.coords[network.conns]XYt=network[throat_coords]_L1,_L2=np.linalg.norm(XYp-XYt[:,None],axis=2).Tmask1=_L1<L1mask2=_L2<L2mask=np.logical_or(mask1,mask2)ifmask.any():L1[mask]=_L1[mask]L2[mask]=_L2[mask]Lt[mask]=0.0returnnp.vstack((L1,Lt,L2)).T
[docs]@_geodocsdeftrapezoids_and_rectangles(network,pore_diameter="pore.diameter",throat_diameter="throat.diameter"):r""" Calculates conduit lengths in the network assuming pores are trapezoids and throats are rectangles. Parameters ---------- %(network)s %(Dp)s %(Dt)s Returns ------- Notes ----- This model should only be used for true 2D networks, i.e. with planar symmetry. """returncones_and_cylinders(network=network,pore_diameter=pore_diameter,throat_diameter=throat_diameter,)
[docs]@_geodocsdefintersecting_trapezoids(network,pore_coords="pore.coords",throat_coords="throat.coords"):r""" Calculates conduit lengths in the network assuming pores are intersecting trapezoids. Parameters ---------- %(network)s %(Pcoords)s %(Tcoords)s Returns ------- Notes ----- This model should only be used for true 2D networks, i.e. with planar symmetry. """returnintersecting_cones(network=network,pore_coords=pore_coords,throat_coords=throat_coords,)
[docs]@_geodocsdefhybrid_trapezoids_and_rectangles(network,pore_diameter="pore.diameter",throat_coords="throat.coords"):r""" Calculates conduit lengths in the network assuming pores are trapezoids and throats are rectangles. Parameters ---------- %(network)s %(Dp)s %(Tcoords)s Returns ------- Notes ----- This model should only be used for true 2D networks, i.e. with planar symmetry. """returnhybrid_cones_and_cylinders(network=network,pore_diameter=pore_diameter,throat_coords=throat_coords,)
[docs]@_geodocsdefpyramids_and_cuboids(network,pore_diameter="pore.diameter",throat_diameter="throat.diameter"):r""" Calculates conduit lengths in the network assuming pores are truncated pyramids and throats are cuboids. Parameters ---------- %(network)s %(Dp)s %(Dt)s Returns ------- """returncones_and_cylinders(network=network,pore_diameter=pore_diameter,throat_diameter=throat_diameter,)
[docs]@_geodocsdefintersecting_pyramids(network,pore_coords="pore.coords",throat_coords="throat.coords"):r""" Calculates conduit lengths in the network assuming pores are intersecting pyramids. Parameters ---------- %(network)s %(Pcoords)s %(Tcoords)s Returns ------- """returnintersecting_cones(network=network,pore_coords=pore_coords,throat_coords=throat_coords,)
[docs]@_geodocsdefhybrid_pyramids_and_cuboids(network,pore_diameter="pore.diameter",throat_coords="throat.coords"):r""" Calculates conduit lengths in the network assuming pores are truncated pyramids that intersect. Therefore, the throat is the cross sectional plane where two pores meet and has negligible/zero volume. Parameters ---------- %(network)s %(Dp)s %(Tcoords)s Returns ------- """returnhybrid_cones_and_cylinders(network=network,pore_diameter=pore_diameter,throat_coords=throat_coords,)
[docs]@_geodocsdefcubes_and_cuboids(network,pore_diameter="pore.diameter",throat_diameter="throat.diameter"):r""" Calculates conduit lengths in the network assuming pores are cubes and throats are cuboids. Parameters ---------- %(network)s %(Dp)s %(Dt)s Returns ------- """L_ctc=_get_L_ctc(network)D1,Dt,D2=network.get_conduit_data(pore_diameter.split(".",1)[-1]).TL1=D1/2L2=D2/2Lt=L_ctc-(L1+L2)# Handle overlapping poresmask=(Lt<0)&(L1>L2)L2[mask]=(L_ctc-L1)[mask]mask=(Lt<0)&(L2>L1)L1[mask]=(L_ctc-L2)[mask]Lt=np.maximum(Lt,1e-15)returnnp.vstack((L1,Lt,L2)).T
[docs]@_geodocsdefsquares_and_rectangles(network,pore_diameter="pore.diameter",throat_diameter="throat.diameter"):r""" Calculates conduit lengths in the network assuming pores are squares and throats are rectangles. Parameters ---------- %(network)s %(Dp)s %(Dt)s Returns ------- Notes ----- This model should only be used for true 2D networks, i.e. with planar symmetry. """returncubes_and_cuboids(network=network,pore_diameter=pore_diameter,throat_diameter=throat_diameter,)
# Dealing with errors and exceptionsdef_raise_incompatible_data():msg=("'spheres_and_cylinders' can only be applied when throat diameter is"" smaller than that of adjacent pores.")raiseException(msg)def_get_L_ctc(network):"""Returns throat spacing if it exists, otherwise calculates it."""try:L_ctc=network["throat.spacing"]exceptKeyError:P12=network["throat.conns"]C1=network["pore.coords"][P12[:,0]]C2=network["pore.coords"][P12[:,1]]L_ctc=np.linalg.norm(C1-C2,axis=1)returnL_ctc