Exemple #1
0
    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)
Exemple #2
0
 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
Exemple #3
0
    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_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])
Exemple #5
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
Exemple #6
0
    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
Exemple #7
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
Exemple #8
0
 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])
Exemple #9
0
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