def setup_class(self):
        np.random.seed(0)
        self.net = op.network.Cubic(shape=[4, 3, 1], spacing=1.)
        self.geo = op.geometry.GenericGeometry(network=self.net,
                                               pores=self.net.Ps,
                                               throats=self.net.Ts)
        self.geo['throat.conduit_lengths.pore1'] = 0.1
        self.geo['throat.conduit_lengths.throat'] = 0.6
        self.geo['throat.conduit_lengths.pore2'] = 0.1

        self.phase = op.phases.GenericPhase(network=self.net)
        self.phys = op.physics.GenericPhysics(network=self.net,
                                              phase=self.phase,
                                              geometry=self.geo)
        self.phys['throat.diffusive_conductance'] = 1e-15
        self.phys['throat.hydraulic_conductance'] = 1e-15

        self.sf = StokesFlow(network=self.net, phase=self.phase)
        self.sf.set_value_BC(pores=self.net.pores('right'), values=1)
        self.sf.set_value_BC(pores=self.net.pores('left'), values=0)
        self.sf.run()

        self.phase.update(self.sf.results())

        self.ad = AdvectionDiffusion(network=self.net, phase=self.phase)
        self.ad.settings._update({"cache_A": False, "cache_b": False})
        self.ad.set_value_BC(pores=self.net.pores('right'), values=2)
        self.ad.set_value_BC(pores=self.net.pores('left'), values=0)
    def run(self):
        r"""
        Execute the diffusion simulations in the principle directions.

        """
        phase = GenericPhase(network=self.network)
        phase['pore.viscosity'] = 1.0
        phase['throat.viscosity'] = 1.0
        mod = models.physics.hydraulic_conductance.hagen_poiseuille
        for geom in self.project.geometries().values():
            phys = GenericPhysics(network=self.network,
                                  phase=phase, geometry=geom)
            phys.add_model(propname='throat.hydraulic_conductance', model=mod)
        inlet = self.network.pores(self.settings['inlet'])
        outlet = self.network.pores(self.settings['outlet'])
        perm = StokesFlow(network=self.project.network, phase=phase)
        perm.set_value_BC(pores=inlet, values=1)
        perm.set_value_BC(pores=outlet, values=0)
        perm.run()
        phase.update(perm.results())
        K = self._calc_eff_prop(inlets=inlet, outlets=outlet,
                                domain_area=self.settings['area'],
                                domain_length=self.settings['length'],
                                rates=perm.rate(pores=inlet),
                                prop_diff=1)
        return K
Example #3
0
    def _abs_perm_calc(self, phase, flow_pores):
        r"""
        Calculates absolute permeability of the medium using StokesFlow
        algorithm. The direction of flow is defined by flow_pores. This
        permeability is normalized by variables in darcy's law other than
        the rate.

        Parameters
        ----------
        phase: phase object
        The phase for which the flow rate is calculated.

        flow_pores: numpy array
        Boundary pores that will have constant value boundary condition to in
        StokesFlow algorithm. First element is the inlet face (pores) for flow
        of invading phase through porous media. Second element is the outlet
        face (pores).

        Output: array [Kwp, Knwp]
        The value of absolute permeability of defending (if there is any) and
        invadin phase in the direction that is defined by flow_pores.

        Note: Absolute permeability is not dependent to the phase, but here
        we just need to calculate the rate instead of all variables that are
        contributing to the darcy's law.
        """
        network = self.project.network
        St_p = StokesFlow(network=network, phase=phase)
        St_p.set_value_BC(pores=flow_pores[0], values=1)
        St_p.set_value_BC(pores=flow_pores[1], values=0)
        St_p.run()
        val = np.sum(abs(St_p.rate(pores=flow_pores[1])))
        K_abs = val
        self.project.purge_object(obj=St_p)
        return K_abs
Example #4
0
    def _eff_perm_calc(self, flow_pores):
        r"""
        Calculates effective permeability of each phase using StokesFlow
        algorithm with updated multiphase physics models to account for the
        multiphase flow.
        The direction of the flow is defined by flow_pores.
        All variables except for the rate in darcy's law will be the same in
        relative permeability ratio. The effective rate represents the
        effective permeability in the nominator of relative permeability
        ratio.

        Parameters
        ----------
        flow_pores: numpy array
        Boundary pores that will have constant value boundary condition in
        StokesFlow algorithm. First element is the inlet face (pores) for flow
        of invading phase through porous media. Second element is the outlet
        face (pores).

        Output: array [Kewp, Kenwp]
        The value of effective permeability of defending (if there is any) and
        invading phase in the direction that is defined by flow_pores.

        Note: To account for multiphase flow, multiphase physics model is added
        and updated in each saturation (saturation is related to
        the presence of another phase). Here, the conduit_hydraulic conductance
        is used as the conductance required by stokes flow algorithm.

        """
        network = self.project.network
        self._regenerate_models()
        if self.settings['wp'] is not None:
            wp = self.project[self.settings['wp']]
            St_mp_wp = StokesFlow(network=network, phase=wp)
            St_mp_wp.setup(conductance='throat.conduit_hydraulic_conductance')
            St_mp_wp.set_value_BC(pores=flow_pores[0], values=1)
            St_mp_wp.set_value_BC(pores=flow_pores[1], values=0)
            St_mp_wp.run()
            Kewp = np.sum(abs(St_mp_wp.rate(pores=flow_pores[1])))
            self.project.purge_object(obj=St_mp_wp)
        else:
            Kewp = None
            pass
        nwp = self.project[self.settings['nwp']]
        St_mp_nwp = StokesFlow(network=network, phase=nwp)
        St_mp_nwp.set_value_BC(pores=flow_pores[0], values=1)
        St_mp_nwp.set_value_BC(pores=flow_pores[1], values=0)
        St_mp_nwp.setup(conductance='throat.conduit_hydraulic_conductance')
        St_mp_nwp.run()
        Kenwp = np.sum(abs(St_mp_nwp.rate(pores=flow_pores[1])))
        Kenwp = Kenwp
        self.project.purge_object(obj=St_mp_nwp)
        return [Kewp, Kenwp]
class AdvectionDiffusionTest:
    def setup_class(self):
        np.random.seed(0)
        self.net = op.network.Cubic(shape=[4, 3, 1], spacing=1.)
        self.geo = op.geometry.GenericGeometry(network=self.net,
                                               pores=self.net.Ps,
                                               throats=self.net.Ts)
        self.geo['throat.conduit_lengths.pore1'] = 0.1
        self.geo['throat.conduit_lengths.throat'] = 0.6
        self.geo['throat.conduit_lengths.pore2'] = 0.1

        self.phase = op.phases.GenericPhase(network=self.net)
        self.phys = op.physics.GenericPhysics(network=self.net,
                                              phase=self.phase,
                                              geometry=self.geo)
        self.phys['throat.diffusive_conductance'] = 1e-15
        self.phys['throat.hydraulic_conductance'] = 1e-15

        self.sf = StokesFlow(network=self.net, phase=self.phase)
        self.sf.set_value_BC(pores=self.net.pores('right'), values=1)
        self.sf.set_value_BC(pores=self.net.pores('left'), values=0)
        self.sf.run()

        self.phase.update(self.sf.results())

        self.ad = AdvectionDiffusion(network=self.net, phase=self.phase)
        self.ad.settings._update({"cache_A": False, "cache_b": False})
        self.ad.set_value_BC(pores=self.net.pores('right'), values=2)
        self.ad.set_value_BC(pores=self.net.pores('left'), values=0)

    def test_settings(self):
        self.ad.settings._update({
            'quantity': "pore.blah",
            'conductance': "throat.foo",
            'diffusive_conductance': "throat.foo2",
            'hydraulic_conductance': "throat.foo3",
            'pressure': "pore.bar",
        })
        assert self.ad.settings["quantity"] == "pore.blah"
        assert self.ad.settings["conductance"] == "throat.foo"
        assert self.ad.settings["diffusive_conductance"] == "throat.foo2"
        assert self.ad.settings["hydraulic_conductance"] == "throat.foo3"
        assert self.ad.settings["pressure"] == "pore.bar"
        # Reset back to previous values for other tests to run
        self.ad.settings._update({
            'quantity': "pore.concentration",
            'conductance': "throat.ad_dif_conductance",
            "diffusive_conductance": "throat.diffusive_conductance",
            "hydraulic_conductance": "throat.hydraulic_conductance",
            "pressure": "pore.pressure"
        })

    def test_conductance_gets_updated_when_pressure_changes(self):
        mod = op.models.physics.ad_dif_conductance.ad_dif
        self.phys.add_model(propname='throat.ad_dif_conductance',
                            model=mod,
                            s_scheme='powerlaw')
        g_old = self.phys["throat.ad_dif_conductance"]
        # Run StokesFlow with a different BC to change pressure field
        self.sf.set_value_BC(pores=self.net.pores('right'), values=1.5)
        # Running the next line should update "throat.ad_dif_conductance"
        self.sf.run()
        self.phase.update(self.sf.results())
        self.ad.settings["conductance"] = "throat.ad_dif_conductance"
        self.ad.run()
        g_updated = self.phys["throat.ad_dif_conductance"]
        # Ensure conductance values are updated
        assert g_old.mean() != g_updated.mean()
        assert_allclose(g_updated.mean(), 1.01258990e-15)
        # Reset BCs for other tests to run properly
        self.sf.set_value_BC(pores=self.net.pores('right'), values=1)
        self.sf.run()

    def test_powerlaw_advection_diffusion(self):
        mod = op.models.physics.ad_dif_conductance.ad_dif
        self.phys.add_model(propname='throat.ad_dif_conductance_powerlaw',
                            model=mod,
                            s_scheme='powerlaw')
        self.phys.regenerate_models()
        self.ad.settings['conductance'] = 'throat.ad_dif_conductance_powerlaw'
        self.ad.run()
        x = [
            0., 0., 0., 0.89653, 0.89653, 0.89653, 1.53924, 1.53924, 1.53924,
            2., 2., 2.
        ]
        y = self.ad['pore.concentration']
        assert_allclose(actual=y, desired=x, rtol=1e-5)

    def test_upwind_advection_diffusion(self):
        mod = op.models.physics.ad_dif_conductance.ad_dif
        self.phys.add_model(propname='throat.ad_dif_conductance_upwind',
                            model=mod,
                            s_scheme='upwind')
        self.phys.regenerate_models()
        self.ad.settings['conductance'] = 'throat.ad_dif_conductance_upwind'
        self.ad.run()
        x = [
            0., 0., 0., 0.86486, 0.86486, 0.86486, 1.51351, 1.51351, 1.51351,
            2., 2., 2.
        ]
        y = self.ad['pore.concentration']
        assert_allclose(actual=y, desired=x, rtol=1e-5)

    def test_hybrid_advection_diffusion(self):
        mod = op.models.physics.ad_dif_conductance.ad_dif
        self.phys.add_model(propname='throat.ad_dif_conductance_hybrid',
                            model=mod,
                            s_scheme='hybrid')
        self.phys.regenerate_models()

        self.ad.settings['conductance'] = 'throat.ad_dif_conductance_hybrid'
        self.ad.run()
        x = [
            0., 0., 0., 0.89908, 0.89908, 0.89908, 1.54128, 1.54128, 1.54128,
            2., 2., 2.
        ]
        y = self.ad['pore.concentration']
        assert_allclose(actual=y, desired=x, rtol=1e-5)

    def test_exponential_advection_diffusion(self):
        mod = op.models.physics.ad_dif_conductance.ad_dif
        self.phys.add_model(propname='throat.ad_dif_conductance_exponential',
                            model=mod,
                            s_scheme='exponential')
        self.phys.regenerate_models()

        self.ad.settings[
            'conductance'] = 'throat.ad_dif_conductance_exponential'
        self.ad.run()
        x = [
            0., 0., 0., 0.89688173, 0.89688173, 0.89688173, 1.53952557,
            1.53952557, 1.53952557, 2., 2., 2.
        ]
        y = self.ad['pore.concentration']
        assert_allclose(actual=y, desired=x, rtol=1e-5)

    def test_outflow_BC(self):
        ad = AdvectionDiffusion(network=self.net, phase=self.phase)
        ad.settings["cache_A"] = False
        ad.set_value_BC(pores=self.net.pores('right'), values=2)

        for s_scheme in ['upwind', 'hybrid', 'powerlaw', 'exponential']:
            ad.settings._update({
                'quantity':
                'pore.concentration',
                'conductance':
                f'throat.ad_dif_conductance_{s_scheme}'
            })
            ad.set_outflow_BC(pores=self.net.pores('left'))
            ad.run()
            y = ad[ad.settings['quantity']].mean()
            assert_allclose(actual=y, desired=2, rtol=1e-5)

    def test_add_outflow_overwrite_rate_and_value_BC(self):
        ad = AdvectionDiffusion(network=self.net, phase=self.phase)
        ad.set_rate_BC(pores=[0, 1], total_rate=1)
        ad.set_value_BC(pores=[2, 3], values=1)
        assert np.sum(np.isfinite(ad['pore.bc_rate'])) == 2
        assert np.sum(np.isfinite(ad['pore.bc_value'])) == 2
        ad.set_outflow_BC(pores=[0, 1, 2, 3])
        assert np.sum(np.isfinite(ad['pore.bc_rate'])) == 0
        assert np.sum(np.isfinite(ad['pore.bc_value'])) == 0

    def test_value_BC_does_not_overwrite_outflow(self):
        ad = AdvectionDiffusion(network=self.net, phase=self.phase)
        ad.set_outflow_BC(pores=[0, 1])
        with pytest.raises(Exception):
            ad.set_value_BC(pores=[0, 1], values=1)

    def test_add_rate_BC_fails_when_outflow_BC_present(self):
        ad = AdvectionDiffusion(network=self.net, phase=self.phase)
        ad.set_outflow_BC(pores=[0, 1])
        with pytest.raises(Exception):
            ad.set_rate_BC(pores=[0, 1], total_rate=1)
        ad.set_rate_BC(pores=[2, 3], total_rate=1)
        assert np.all(ad['pore.bc_rate'][[2, 3]] == 0.5)

    def test_outflow_BC_rigorous(self):
        ad = AdvectionDiffusion(network=self.net, phase=self.phase)
        ad.settings["cache_A"] = False
        # Add source term so we get a non-uniform concentration profile
        self.phys["pore.A1"] = -5e-16
        linear = op.models.physics.generic_source_term.linear
        self.phys.add_model(propname="pore.rxn",
                            model=linear,
                            A1="pore.A1",
                            X="pore.concentration")
        internal_pores = self.net.pores(["left", "right"], mode="not")
        ad.set_source("pore.rxn", pores=internal_pores)
        ad.set_value_BC(pores=self.net.pores('right'), values=2)
        ad.set_outflow_BC(pores=self.net.pores('left'))

        for s_scheme in ['upwind', 'hybrid', 'powerlaw', 'exponential']:
            ad.settings._update({
                'quantity':
                'pore.concentration',
                'conductance':
                f'throat.ad_dif_conductance_{s_scheme}'
            })
            ad.run()
            y = ad[ad.settings['quantity']].reshape(4, 3).mean(axis=1)
            # Calculate grad_c @ face on which outflow BC was imposed
            dydx_left_mean = (y[1] - y[0]) / (1 - 0)
            # Calculate grad_c across the entire network as reference
            dydx_total_mean = (y[1] - y[-1]) / (3 - 0)
            # Make sure that grad_c @ outflow BC face is "numerically" ~ 0
            assert_allclose(actual=dydx_total_mean + dydx_left_mean,
                            desired=dydx_total_mean)

    def test_rate(self):
        ad = AdvectionDiffusion(network=self.net, phase=self.phase)
        ad.settings["cache_A"] = False
        ad.set_value_BC(pores=self.net.pores('right'), values=2)
        ad.set_value_BC(pores=self.net.pores('left'), values=0)

        for s_scheme in ['upwind', 'hybrid', 'powerlaw', 'exponential']:
            ad.settings._update({
                'quantity': 'pore.concentration',
                'conductance': f'throat.ad_dif_conductance_{s_scheme}',
                's_scheme': s_scheme
            })
            ad.run()

            mdot_inlet = ad.rate(pores=self.net.pores("right"))[0]
            mdot_outlet = ad.rate(pores=self.net.pores("left"))[0]
            temp = np.random.choice(self.net.pores(["right", "left"],
                                                   mode="not"),
                                    size=3,
                                    replace=False)
            mdot_internal = ad.rate(pores=temp)[0]
            # Ensure no mass is generated within the network
            assert_allclose(mdot_inlet - mdot_internal, mdot_inlet)
            # Ensure mass is not conserved
            assert_allclose(mdot_inlet, -mdot_outlet)

    def teardown_class(self):
        ws = op.Workspace()
        ws.clear()