.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_tutorials/example_04_surface_monitors.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_tutorials_example_04_surface_monitors.py: Surface monitors alongside volume ones ====================================== The post-processor doesn't care whether the reducer feeds on volume cells, a sampled plane, or an iso-surface — it routes whatever the function returns through the same write step. This tutorial adds two surface-derived ``@Table`` outputs to the class we've been building: - **interface area** — the area of the ``alpha.water = 0.5`` iso-surface, a proxy for the gas–liquid interface, - **plane-averaged pressure** — mean ``p`` on a horizontal cutting plane. Both use the same builders documented in :mod:`pyOFTools.builders`. .. GENERATED FROM PYTHON SOURCE LINES 18-20 Clone, mesh, set fields ----------------------- .. GENERATED FROM PYTHON SOURCE LINES 20-47 .. code-block:: Python import subprocess import numpy as np # noqa: F401 — imported early to dodge SIGFPE import pyOFTools.patch_pybfoam # noqa: F401 from pyOFTools import clone_example CASE = clone_example("damBreak") from pybFoam import Time, argList, dictionary, fvMesh, volScalarField from pybFoam.meshing import generate_blockmesh time = Time(argList([str(CASE), "-case", str(CASE)])) generate_blockmesh(time, dictionary.read(str(CASE / "system" / "blockMeshDict"))) subprocess.run( ["pyoftools", "setFields", "system/setFields.py"], cwd=CASE, check=True, capture_output=True, text=True, ) mesh = fvMesh(time) volScalarField.read_field(mesh, "alpha.water") volScalarField.read_field(mesh, "p") .. rst-class:: sphx-glr-script-out .. code-block:: none .. GENERATED FROM PYTHON SOURCE LINES 48-60 Two new builders ---------------- ``iso_surface(mesh, field, value)`` returns a workflow carrying just the iso-surface *geometry* — no field on it yet. Pipe into: - ``area()`` to write face-area magnitudes onto the surface, then ``Sum`` to total them. That's the "how much interface" number. - ``sample(mesh, name)`` to interpolate a volume field onto the surface, then any reducer. ``plane(mesh, point, normal)`` works the same way. We use it with ``sample`` + ``Mean`` for an area-weighted-free average pressure on a slice. .. GENERATED FROM PYTHON SOURCE LINES 60-88 .. code-block:: Python from pyOFTools.aggregators import Mean, Sum, VolIntegrate from pyOFTools.builders import area, field, iso_surface, plane, sample from pyOFTools.postprocessor import PostProcessorBase postProcess = PostProcessorBase(base_path=str(CASE) + "/postProcessing/") @postProcess.Table("water_volume.csv") def water_volume(m): return field(m, "alpha.water") | VolIntegrate(name="water_volume") @postProcess.Table("interface_area.csv") def interface_area(m): return iso_surface(m, "alpha.water", 0.5) | area() | Sum(name="interface_area") @postProcess.Table("mean_p_midplane.csv") def mean_p_midplane(m): # Horizontal plane at y = 0.146 m, halfway up the initial water column. return ( plane(m, point=(0.0, 0.146, 0.0), normal=(0.0, 1.0, 0.0)) | sample(m, "p") | Mean(name="mean_p_midplane") ) .. GENERATED FROM PYTHON SOURCE LINES 89-95 Evaluate at the current time step --------------------------------- Same as before — we call ``execute`` / ``write`` on the initial fields instead of running a solver. :doc:`example_05_live_run_and_plot` wires this class into ``controlDict`` and runs ``compressibleInterFoam`` so OpenFOAM does the calls for us. .. GENERATED FROM PYTHON SOURCE LINES 95-111 .. code-block:: Python runner = postProcess(mesh) runner.execute() runner.write() runner.end() import pandas as pd results = {} for name in ("water_volume.csv", "interface_area.csv", "mean_p_midplane.csv"): print(f"--- {name} ---") text = (CASE / "postProcessing" / name).read_text() print(text) df = pd.read_csv(CASE / "postProcessing" / name) results[name] = df.iloc[-1] .. rst-class:: sphx-glr-script-out .. code-block:: none --- water_volume.csv --- time,water_volume 0.0,0.0008404216050726664 --- interface_area.csv --- time,interface_area 0.0,0.006872853348567688 --- mean_p_midplane.csv --- time,mean_p_midplane 0.0,100000.0 .. GENERATED FROM PYTHON SOURCE LINES 112-119 Visualise the surfaces ---------------------- The two surface monitors are easier to read as 3-D geometry: the ``alpha = 0.5`` iso-surface (the gas–liquid interface) and the horizontal cutting plane at ``y = 0.146 m`` (where we average pressure). pyvista extracts them from the same case and lets us draw them in one scene with the mesh outline for context. .. GENERATED FROM PYTHON SOURCE LINES 119-141 .. code-block:: Python import pyvista as pv from pybFoam import pyvista_read reader = pyvista_read(CASE, time=0.0) internal = reader.read()["internalMesh"] interface = internal.contour(isosurfaces=[0.5], scalars="alpha.water") midplane = internal.slice(normal="y", origin=(0.292, 0.146, 0.0075)) plotter = pv.Plotter(window_size=(640, 400), off_screen=True) plotter.add_mesh(internal.outline(), color="gray") plotter.add_mesh(interface, color="royalblue", opacity=0.85, label="α = 0.5") plotter.add_mesh(midplane, scalars="p", cmap="coolwarm", opacity=0.65) plotter.add_text( f"interface area = {results['interface_area.csv']['interface_area']:.4g} m²\n" f"mean p (plane) = {results['mean_p_midplane.csv']['mean_p_midplane']:.4g} Pa", position="upper_left", font_size=10, ) plotter.view_isometric() plotter.show() .. image-sg:: /auto_tutorials/images/sphx_glr_example_04_surface_monitors_001.png :alt: example 04 surface monitors :srcset: /auto_tutorials/images/sphx_glr_example_04_surface_monitors_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 142-156 Takeaways --------- - ``iso_surface`` and ``plane`` give you a ``SurfaceDataSet``; pipe into ``area()`` (face-area magnitudes) or ``sample(mesh, name)`` (interpolated volume field) before any reducer. - Once the dataset is populated, **the same reducers work everywhere** — ``Mean`` / ``Sum`` / ``Min`` / ``Max`` on the surface, ``VolIntegrate`` on the volume. One vocabulary, many geometries. - ``Box`` / ``Sphere`` selectors and ``Directional`` binners apply to surface datasets too (they only read ``positions``). Next: :doc:`example_05_live_run_and_plot` — wire this class into the solver, run a real time window, and plot the resulting time-series. .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.929 seconds) .. _sphx_glr_download_auto_tutorials_example_04_surface_monitors.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: example_04_surface_monitors.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: example_04_surface_monitors.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: example_04_surface_monitors.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_