From 794db18769b251fc5b34642206ab1457965cbde6 Mon Sep 17 00:00:00 2001 From: Amin Sadeghi Date: Wed, 15 Dec 2021 22:45:44 -0500 Subject: [PATCH 1/8] Add callback mechanism to TransientReactiveTransport --- .../_transient_reactive_transport.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/openpnm/algorithms/_transient_reactive_transport.py b/openpnm/algorithms/_transient_reactive_transport.py index 2c92cce4d0..2d8ddccb65 100644 --- a/openpnm/algorithms/_transient_reactive_transport.py +++ b/openpnm/algorithms/_transient_reactive_transport.py @@ -42,6 +42,7 @@ def __init__(self, phase, settings=None, **kwargs): super().__init__(phase=phase, settings=self.settings, **kwargs) self.settings['phase'] = phase.name self["pore.ic"] = np.nan + self._callbacks = [] def run(self, x0, tspan, saveat=None, integrator=None): """ @@ -88,12 +89,44 @@ def run(self, x0, tspan, saveat=None, integrator=None): self.soln = integrator.solve(rhs, x0, tspan, saveat) return self.soln + def set_callback(self, func): + """ + Stores the given function handle as a callback. + + Callback functions are called at the beginning of every time step. + + Parameters + ---------- + func : function + Function to be called as a callback. Must be a function of + t and y. + + Returns + ------- + None + + Examples + -------- + >>> net = op.network.Cubic([1, 2, 3]) + >>> geo = op.geometry.SpheresAndCylinders(network=net, pores=net.Ps, throats=net.Ts) + >>> air = op.phases.Air(network=net) + >>> phys = op.physics.Standard(network=net, geometry=geo) + >>> trt = op.algorithms.TransientReactiveTransport(network=net, phase=air) + >>> func = lambda t, y: print(t) + >>> trt.set_callback(func) + + """ + if not callable(func): + raise Exception("'func' must be a function. See Examples section.") + self._callbacks.append(func) + def _run_special(self, x0): ... def _build_rhs(self): def ode_func(t, y): # TODO: add a cache mechanism + self._apply_callbacks(t, y) self.x = y self._update_A_and_b() A = self.A.tocsc() @@ -109,3 +142,7 @@ def _merge_inital_and_boundary_values(self): x0[bc_pores] = self['pore.bc_value'][bc_pores] quantity = self.settings['quantity'] self[quantity] = x0 + + def _apply_callbacks(self, t, y): + for func in self._callbacks: + func(t, y) From 1dab05e189957e657c3b24fdd459b3d6b14ddc5f Mon Sep 17 00:00:00 2001 From: Amin Sadeghi Date: Wed, 15 Dec 2021 22:52:22 -0500 Subject: [PATCH 2/8] Fix a typo in docstrings --- openpnm/algorithms/_transient_reactive_transport.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpnm/algorithms/_transient_reactive_transport.py b/openpnm/algorithms/_transient_reactive_transport.py index 2d8ddccb65..fc3d855e36 100644 --- a/openpnm/algorithms/_transient_reactive_transport.py +++ b/openpnm/algorithms/_transient_reactive_transport.py @@ -107,6 +107,7 @@ def set_callback(self, func): Examples -------- + >>> import openpnm as op >>> net = op.network.Cubic([1, 2, 3]) >>> geo = op.geometry.SpheresAndCylinders(network=net, pores=net.Ps, throats=net.Ts) >>> air = op.phases.Air(network=net) From 9026f0fed427d37f4caf1a79e4a7684490d52cf6 Mon Sep 17 00:00:00 2001 From: Amin Sadeghi Date: Wed, 15 Dec 2021 23:07:50 -0500 Subject: [PATCH 3/8] Fix another typo in docstrings.... --- openpnm/algorithms/_transient_reactive_transport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpnm/algorithms/_transient_reactive_transport.py b/openpnm/algorithms/_transient_reactive_transport.py index fc3d855e36..29f10a2c9e 100644 --- a/openpnm/algorithms/_transient_reactive_transport.py +++ b/openpnm/algorithms/_transient_reactive_transport.py @@ -111,7 +111,7 @@ def set_callback(self, func): >>> net = op.network.Cubic([1, 2, 3]) >>> geo = op.geometry.SpheresAndCylinders(network=net, pores=net.Ps, throats=net.Ts) >>> air = op.phases.Air(network=net) - >>> phys = op.physics.Standard(network=net, geometry=geo) + >>> phys = op.physics.Standard(network=net, geometry=geo, phase=air) >>> trt = op.algorithms.TransientReactiveTransport(network=net, phase=air) >>> func = lambda t, y: print(t) >>> trt.set_callback(func) From 646b1f952ab4d633de88ce78e977ce7eac7e2617 Mon Sep 17 00:00:00 2001 From: jgostick Date: Fri, 4 Nov 2022 23:44:41 -0400 Subject: [PATCH 4/8] making callback hidden --- openpnm/algorithms/_transient_reactive_transport.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/openpnm/algorithms/_transient_reactive_transport.py b/openpnm/algorithms/_transient_reactive_transport.py index 32006c57cb..913f542bea 100644 --- a/openpnm/algorithms/_transient_reactive_transport.py +++ b/openpnm/algorithms/_transient_reactive_transport.py @@ -101,8 +101,8 @@ def run(self, x0, tspan, saveat=None, integrator=None): def _run_special(self, x0): pass - - def set_callback(self, func): + + def _set_callback(self, func): """ Stores the given function handle as a callback. @@ -121,10 +121,9 @@ def set_callback(self, func): Examples -------- >>> import openpnm as op - >>> net = op.network.Cubic([1, 2, 3]) - >>> geo = op.geometry.SpheresAndCylinders(network=net, pores=net.Ps, throats=net.Ts) + >>> net = op.network.Demo([1, 2, 3]) >>> air = op.phases.Air(network=net) - >>> phys = op.physics.Standard(network=net, geometry=geo, phase=air) + >>> air.add_model_collection(op.models.collections.physics.standard) >>> trt = op.algorithms.TransientReactiveTransport(network=net, phase=air) >>> func = lambda t, y: print(t) >>> trt.set_callback(func) @@ -134,8 +133,6 @@ def set_callback(self, func): raise Exception("'func' must be a function. See Examples section.") self._callbacks.append(func) - def _run_special(self, x0): ... - def _build_rhs(self): """ Returns a function handle, which calculates dy/dt = rhs(y, t). From ff9027e7f323bba4691cc7a695c12fe22eb4bfb6 Mon Sep 17 00:00:00 2001 From: jgostick Date: Fri, 4 Nov 2022 23:56:06 -0400 Subject: [PATCH 5/8] updating doc string --- openpnm/algorithms/_transient_reactive_transport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpnm/algorithms/_transient_reactive_transport.py b/openpnm/algorithms/_transient_reactive_transport.py index 913f542bea..91f8df4bee 100644 --- a/openpnm/algorithms/_transient_reactive_transport.py +++ b/openpnm/algorithms/_transient_reactive_transport.py @@ -126,7 +126,7 @@ def _set_callback(self, func): >>> air.add_model_collection(op.models.collections.physics.standard) >>> trt = op.algorithms.TransientReactiveTransport(network=net, phase=air) >>> func = lambda t, y: print(t) - >>> trt.set_callback(func) + >>> trt._set_callback(func) """ if not callable(func): From 11776e1bd366577024e908568a555efca1849a95 Mon Sep 17 00:00:00 2001 From: jgostick Date: Sat, 5 Nov 2022 11:09:45 -0400 Subject: [PATCH 6/8] fixing docstring properly --- openpnm/algorithms/_transient_reactive_transport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpnm/algorithms/_transient_reactive_transport.py b/openpnm/algorithms/_transient_reactive_transport.py index 91f8df4bee..eff5efc4d8 100644 --- a/openpnm/algorithms/_transient_reactive_transport.py +++ b/openpnm/algorithms/_transient_reactive_transport.py @@ -122,7 +122,7 @@ def _set_callback(self, func): -------- >>> import openpnm as op >>> net = op.network.Demo([1, 2, 3]) - >>> air = op.phases.Air(network=net) + >>> air = op.phase.Air(network=net) >>> air.add_model_collection(op.models.collections.physics.standard) >>> trt = op.algorithms.TransientReactiveTransport(network=net, phase=air) >>> func = lambda t, y: print(t) From 3f88092167cbb5b45d5ce65dcfc916e3c01c8fe6 Mon Sep 17 00:00:00 2001 From: jgostick Date: Sat, 5 Nov 2022 20:57:17 -0400 Subject: [PATCH 7/8] Adding 'working example' as integration test --- tests/integration/Callback_Functionality.py | 41 +++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/integration/Callback_Functionality.py diff --git a/tests/integration/Callback_Functionality.py b/tests/integration/Callback_Functionality.py new file mode 100644 index 0000000000..5571a23134 --- /dev/null +++ b/tests/integration/Callback_Functionality.py @@ -0,0 +1,41 @@ +if __name__ == '__main__': + + import numpy as np + import openpnm as op + import matplotlib.pyplot as plt + + Nx = 101 + shape = [Nx, 1, 1] + spacing = 1/Nx * 5 + net = op.network.Cubic(shape=shape, spacing=spacing) + air = op.phase.Air(network=net) + air["throat.diffusive_conductance"] = spacing * np.ones(net.Nt) + net["pore.volume"] = spacing**3 + + # Set up transient Fickian diffusion algorithm + tfd = op.algorithms.TransientFickianDiffusion(network=net, phase=air) + tfd.set_value_BC(net.pores("left"), 0) + tfd.set_value_BC(net.pores("right"), 0) + + # Define a pulse signal + def pulse(t, y): + if 0 <= t <= 0.05: + y[net.Np//2] = 1.0 + + # Add the pulse signal to the algorithm as a callback + tfd._set_callback(pulse) + + # Solve the transient algorithm + c0 = np.zeros(tfd.Np) + tspan = [0, 0.4] + tfd.run(x0=c0, tspan=tspan) + + # Plot c vs. time + tout = np.linspace(tspan[0], tspan[1], 10) + fig, ax = plt.subplots() + for i, t in enumerate(tout): + ax.plot(tfd.soln['pore.concentration'](t), label=f"{t:.2f} (s)") + ax.legend() + ax.set_title("Dissipation of a pulse signal , c(x=0,L) = 0") + ax.set_xlabel("distance (m)") + ax.set_ylabel("concentration (mol/m$^3$)") From f5264f5ae756c1d862374593d3239ba4fc22490d Mon Sep 17 00:00:00 2001 From: jgostick Date: Sat, 5 Nov 2022 21:27:41 -0400 Subject: [PATCH 8/8] wrapping integration tests in functions --- tests/integration/Callback_Functionality.py | 9 +++++---- tests/integration/PoreSpyIO_on_Berea.py | 14 ++++++-------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/integration/Callback_Functionality.py b/tests/integration/Callback_Functionality.py index 5571a23134..269ba0abff 100644 --- a/tests/integration/Callback_Functionality.py +++ b/tests/integration/Callback_Functionality.py @@ -1,8 +1,9 @@ -if __name__ == '__main__': +import numpy as np +import openpnm as op +import matplotlib.pyplot as plt - import numpy as np - import openpnm as op - import matplotlib.pyplot as plt + +def test_callback_functionality(): Nx = 101 shape = [Nx, 1, 1] diff --git a/tests/integration/PoreSpyIO_on_Berea.py b/tests/integration/PoreSpyIO_on_Berea.py index 82e4ba93c2..2f62256352 100644 --- a/tests/integration/PoreSpyIO_on_Berea.py +++ b/tests/integration/PoreSpyIO_on_Berea.py @@ -1,11 +1,11 @@ -if __name__ == "__main__": +import openpnm as op +import porespy as ps +import numpy as np +import os +from pathlib import Path - import openpnm as op - import porespy as ps - import numpy as np - import os - from pathlib import Path +def test_porespy_io_on_berea(): # %% Read image from file in fixtures path = Path(os.path.realpath(__file__), @@ -13,7 +13,6 @@ data = np.load(path.resolve()) im = data['im'] - # %% Note meta data for this image data = { 'shape': { @@ -63,7 +62,6 @@ gas.add_model_collection(op.models.collections.physics.basic) gas.regenerate_models() - # %% Perform Fickian Diffusion to find formation factor fd = op.algorithms.FickianDiffusion(network=pn, phase=gas) fd.set_value_BC(pores=pn.pores('xmin'), values=1.0)