.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_tutorials/example_05_live_run_and_plot.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_05_live_run_and_plot.py: Run live with the solver, then plot =================================== So far we drove ``execute`` / ``write`` by hand to keep tutorials self-contained. The whole point of pyOFTools, though, is that the running solver calls them for you. This tutorial closes the loop: 1. write a ``postProcess.py`` to the cloned case that combines the monitors from tutorials 02-04, 2. truncate the case's ``endTime`` so the run completes in seconds, 3. run ``compressibleInterFoam`` — OpenFOAM picks up the ``pyPostProcessing`` function object from ``system/controlDict`` and calls the registered ``@Table`` outputs on every step, 4. load the resulting CSVs with pandas and plot the time-series. The wiring in ``system/controlDict`` is already there in the ``damBreak`` case — see :doc:`example_02_first_postprocessor` for the snippet. .. GENERATED FROM PYTHON SOURCE LINES 22-24 Clone the case -------------- .. GENERATED FROM PYTHON SOURCE LINES 24-36 .. code-block:: Python import os 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") print(f"working case: {CASE}") .. rst-class:: sphx-glr-script-out .. code-block:: none working case: /tmp/pyoftools_vu416hp1/damBreak .. GENERATED FROM PYTHON SOURCE LINES 37-52 The post-processor file ----------------------- OpenFOAM's ``pyPostProcessing`` function object imports ``postProcess.py`` from the case directory, calls the module-level ``postProcess`` object with the mesh to get a runner, then calls ``execute`` / ``write`` / ``end`` on it. The damBreak case already ships such a file, and ``clone_example`` brought it across with the rest of the case: .. literalinclude:: ../../examples/damBreak/postProcess.py :language: python :caption: examples/damBreak/postProcess.py Four ``@Table`` outputs — water volume, interface area, mass profile along x, mean pressure on a horizontal mid-plane — combine the patterns from tutorials 02-04 into the single post-processor the live solver will call. .. GENERATED FROM PYTHON SOURCE LINES 54-60 Trim ``endTime`` for the tutorial --------------------------------- The shipped ``controlDict`` runs to ``t = 2.0`` — fine for a real study, too slow for a doc build. ``dictionary.read`` parses it, ``cd.set`` patches the entry, ``cd.write`` puts it back as a valid OpenFOAM dictionary (preserving format, comments, and adjacent entries). .. GENERATED FROM PYTHON SOURCE LINES 60-68 .. code-block:: Python from pybFoam import dictionary control_dict_path = CASE / "system" / "controlDict" cd = dictionary.read(str(control_dict_path)) cd.set("endTime", 0.05) cd.write(str(control_dict_path)) .. GENERATED FROM PYTHON SOURCE LINES 69-74 Mesh, set fields, run the solver -------------------------------- Same sequence ``./Allrun`` would do. The mesh is built in-process with ``generate_blockmesh``; setFields and the solver run as subprocesses (the solver is a C++ binary, so no in-process equivalent). .. GENERATED FROM PYTHON SOURCE LINES 74-93 .. code-block:: Python from pybFoam import Time, argList, dictionary from pybFoam.meshing import generate_blockmesh env = {**os.environ} def run(cmd): res = subprocess.run(cmd, cwd=CASE, env=env, capture_output=True, text=True, check=True) # OpenFOAM is chatty — show the tail so the gallery output stays useful. tail = "\n".join(res.stdout.splitlines()[-5:]) print(f"$ {' '.join(cmd)}\n{tail}\n") time = Time(argList([str(CASE), "-case", str(CASE)])) generate_blockmesh(time, dictionary.read(str(CASE / "system" / "blockMeshDict"))) run(["pyoftools", "setFields", "system/setFields.py"]) run(["compressibleInterFoam"]) .. rst-class:: sphx-glr-script-out .. code-block:: none $ pyoftools setFields system/setFields.py fileModificationChecking : Monitoring run-time modified files using timeStampMaster (fileModificationSkew 5, maxFileModificationPolls 20) allowSystemOperations : Allowing user-supplied system call operations // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // I/O : uncollated $ compressibleInterFoam Viscous : (-4.263073240876908e-06 -7.678422107909316e-05 6.094828253545187e-05) writing force and moment files. End .. GENERATED FROM PYTHON SOURCE LINES 94-99 Load the CSVs ------------- Each ``@Table`` produced one CSV under ``postProcessing/``. They have a ``time`` column plus the named value column (and ``group`` for binned outputs). .. GENERATED FROM PYTHON SOURCE LINES 99-110 .. code-block:: Python import pandas as pd vol = pd.read_csv(CASE / "postProcessing" / "water_volume.csv") area_df = pd.read_csv(CASE / "postProcessing" / "interface_area.csv") mass = pd.read_csv(CASE / "postProcessing" / "mass_profile_x.csv") plane_p = pd.read_csv(CASE / "postProcessing" / "mean_p_midplane.csv") print(vol.head()) print(mass.head()) .. rst-class:: sphx-glr-script-out .. code-block:: none time water_volume 0 0.001190 0.00084 1 0.002626 0.00084 2 0.004318 0.00084 3 0.006304 0.00084 4 0.008732 0.00084 time mass group 0 0.00119 0.000000 0 1 0.00119 0.612963 1 2 0.00119 0.224757 2 3 0.00119 0.001472 3 4 0.00119 0.001393 4 .. GENERATED FROM PYTHON SOURCE LINES 111-116 Plot the monitors ----------------- Four panels: water volume (should be conserved-ish), interface area (grows as the wave breaks up), per-bin mass along x (the wave front advances), and mean pressure on the mid-plane. .. GENERATED FROM PYTHON SOURCE LINES 116-139 .. code-block:: Python import matplotlib.pyplot as plt fig, axes = plt.subplots(2, 2, figsize=(10, 6.5)) axes[0, 0].plot(vol["time"], vol["water_volume"], marker="o", markersize=3) axes[0, 0].set(xlabel="time [s]", ylabel="water volume [m³]", title="Total water volume") axes[0, 1].plot(area_df["time"], area_df["interface_area"], marker="o", markersize=3) axes[0, 1].set(xlabel="time [s]", ylabel="interface area [m²]", title="α = 0.5 interface area") # Long-format: one row per (time, bin). Pivot so each bin is its own line. mass_wide = mass.pivot(index="time", columns="group", values="mass") mass_wide.plot(ax=axes[1, 0], marker="o", markersize=3) axes[1, 0].set(xlabel="time [s]", ylabel="mass per bin [kg]", title="Mass along x (per bin)") axes[1, 0].legend(title="bin", fontsize=8) axes[1, 1].plot(plane_p["time"], plane_p["mean_p_midplane"], marker="o", markersize=3) axes[1, 1].set(xlabel="time [s]", ylabel="mean p [Pa]", title="Mean p on y = 0.146 m plane") fig.tight_layout() plt.show() .. image-sg:: /auto_tutorials/images/sphx_glr_example_05_live_run_and_plot_001.png :alt: Total water volume, α = 0.5 interface area, Mass along x (per bin), Mean p on y = 0.146 m plane :srcset: /auto_tutorials/images/sphx_glr_example_05_live_run_and_plot_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 140-145 Visualise the interface at the final time ----------------------------------------- The CSVs only carry scalars; for the geometry of what just happened, drop back to pyvista. We pick the α = 0.5 iso-surface — the gas–liquid interface — at the end of the run and colour it by pressure. .. GENERATED FROM PYTHON SOURCE LINES 145-165 .. code-block:: Python import pyvista as pv from pybFoam import pyvista_read reader = pyvista_read(CASE) reader.set_active_time_value(reader.time_values[-1]) internal = reader.read()["internalMesh"] interface = internal.contour(isosurfaces=[0.5], scalars="alpha.water") plotter = pv.Plotter(window_size=(640, 400), off_screen=True) plotter.add_mesh(internal.outline(), color="gray") plotter.add_mesh( interface, scalars="p", cmap="coolwarm", scalar_bar_args={"title": "p [Pa]"}, ) plotter.view_isometric() plotter.show() .. image-sg:: /auto_tutorials/images/sphx_glr_example_05_live_run_and_plot_002.png :alt: example 05 live run and plot :srcset: /auto_tutorials/images/sphx_glr_example_05_live_run_and_plot_002.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 166-175 Where to go next ---------------- - :doc:`/auto_how_to/index` — point-in-time recipes for specific tasks (iso-surface area, residual extraction, custom aggregator). - :doc:`/how-to/configure_controlDict` — full ``pyPostProcessing`` reference including ``writeControl`` variants and HDF5 output. - :doc:`/explanation/datastructures` — what's actually flowing through the pipeline (geometry, mask, groups, field) and how custom nodes fit in. .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 1.604 seconds) .. _sphx_glr_download_auto_tutorials_example_05_live_run_and_plot.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: example_05_live_run_and_plot.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: example_05_live_run_and_plot.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: example_05_live_run_and_plot.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_