Fluidsimfoam, a new Python framework for OpenFOAM


https://foss.heptapod.net/fluiddyn/fluidsimfoam

Pierre Augier, Pooria Danaeifar



OpenFOAM users conference, 13-14 June 2023

Who am I?

(Fluidsimfoam perspective…)

  • CNRS researcher, stratified turbulence and scientific programming with Python

  • Fluiddyn project: open-source Python for fluid dynamics
    (lab experiments, image processing and simulations)

  • Fluidsim: user centric CFD framework, specialized in pseudo-spectral methods

  • Snek5000: Fluidsim framework for the CFD code Nek5000 (success…)

Note

In my lab (LEGI), 0 Nek5000 user but many OpenFOAM users…

So let’s try to build a Fluidsim framework for OpenFOAM (Master 2 internship, Pooria Danaeifar)!

Fluidsim + OpenFOAM -> Fluidsimfoam

Pooria Danaeifar’s internship on Fluidsimfoam

  • (Re)discovery of OpenFOAM

  • Study the standard workflow (official tutorials)

  • Study a particular solver (SedFoam) developed at LEGI

  • Write a parser for OpenFOAM input files (Lark grammar)

First personal conclusion

User experience is not optimum…

Fluidsimfoam

What is it?

  • A (very) new Python package

  • Good quality software:
    tested (coverage > 95%), documented, modular, user centric

  • A new workflow for OpenFOAM

Fluidsimfoam

Goal

  • Improve OpenFOAM user experience and productivity with Python/IPython

Fluidsimfoam

Principles

  • Avoid manual copy and edition of OpenFOAM input files

  • Split the workflow in 2 steps

    1. Described sets of potential simulations

    2. Create the directory/files for 1 particular simulation and launch it

  • Integrated object oriented API (sim object)

Workflow split in 2 steps

1st step: described a set of potential simulations

The set (and the associated parameters) are described in a small Python package called a “Fluidsimfoam solver”

  • Python API to describe and create parametrized input OF files
  • Potentially Jinja templates

Warning

“Fluidsim solver” and “OpenFOAM solvers” are very different things!

Fluidsimfoam

Particularly suitable for

  • 1 script with arguments to launch different simulations

  • Automation of case generation, simulation launching and postprocessing (parametric studies, optimization, …)

  • Programmatic generation of complex and parametrized input files (for example blockMeshDict) and initial conditions (computed in Python)

  • Programmatic control of simulations at runtime (example here)

Note

Also facilities for simpler tasks… Much more with future versions…

Demo

  1. Install

  2. Launch a simulation with an existing solver

  3. Reload a sim object for runtime control / post-processing / plots

  4. Create a new Fluidsimfoam solver from a case

Demo: install Fluidsimfoam

Requirements: Python >=3.9 (with pip) and OpenFOAM

Future

pip install fluidsimfoam


Now

Requirements: Mercurial (or manual download source code…) and Poetry

python3 -m pip install poetry
hg clone https://foss.heptapod.net/fluiddyn/fluidsimfoam
cd fluidsimfoam
poetry install --all-extras
poetry shell
pytest -v

Demo: launch a simulation with an existing solver

Run a script with

python doc/examples/fluidsimfoam-dam/doc/tuto_simple.py

Content of the script:

from fluidsimfoam_dam import Simul

params = Simul.create_default_params()

params.output.sub_directory = "tuto_fluidsimfoam/dam"
params.control_dict.end_time = 4.0

params.parallel.method = "simple"
params.parallel.nsubdoms = 2
params.parallel.nsubdoms_xyz = [2, 1, 1]

params.constant.transport.water.nu = 0.5e-6

params.block_mesh_dict.height_dam = 0.5
params.block_mesh_dict.width_dam = 0.2
params.block_mesh_dict.nx = 80
params.block_mesh_dict.ny = 80

# creation of the simulation directory
sim = Simul(params)
# run the simulation (i.e. all necessary OpenFOAM commands)
sim.make.exec("run")
# or for programmatic control of the simulation
# sim.make.exec_async("run")

Demo: reload a sim object

Applications: runtime control / post-processing / plots

In a terminal:

cd /path/to/directory
fluidsimfoam-ipy-load

which gives:

Python 3.9.2 (default, Feb 28 2021, 17:03:44)
Type 'copyright', 'credits' or 'license' for more information
IPython 8.14.0 -- An enhanced Interactive Python. Type '?' for help.
Loading simulation
path_run: /home/users/me/Sim_data/tests_fluidsimfoam/dambreak/dambreak_run_2023-06-08_14-56-26
INFO     sim:                        <class 'fluidsimfoam_dambreak.Simul'>
         sim.output.log:             <class 'fluidsimfoam.output.log.Log'>
         sim.output.fields:          <class 'fluidsimfoam.output.fields.Fields'>
         input_files:
           - in 0:          U alpha_water p_rgh
           - in constant:   g transportProperties turbulenceProperties
           - in system:     blockMeshDict controlDict decomposeParDict fvSchemes fvSolution sampling setFieldsDict
         sim.output:                 <class 'fluidsimfoam_dambreak.output.OutputDambreak'>
         sim.oper:                   <class 'fluidsimfoam.operators.Operators'>
         sim.init_fields:            <class 'fluidsimfoam.init_fields.InitFields'>
         sim.make:                   <class 'fluidsimfoam.make.MakeInvoke'>

`sim`, `params`, `np`, `plt` and `pd` variables are available

In [1]:

Demo: reload a sim object

Then, in IPython shell:

sim.params

sim.stop_time_loop()

sim.output.log.plot_clock_times()
sim.output.log.time_last

# Change a parameter affecting just one file:
sim.params.control_dict.end_time = 2
sim.input_files.control_dict.generate_file()

# Get the cells coordinates
x, y, z = sim.oper.get_cells_coords()

# Read output fields:
field = sim.output.fields.read_field("U", time_approx="last")
vx, vy, vz = field.get_components()

How the fluidsimfoam-dam solver was created?


fluidsimfoam-initiate-solver dambreak \
  -c $FOAM_TUTORIALS/multiphase/interFoam/laminar/damBreak/damBreak


Important

Please try it for one of your case!

fluidsimfoam-initiate-solver great-short-name -c /path/to/your/case

Errors? Create an issue here: https://foss.heptapod.net/fluiddyn/fluidsimfoam/-/issues

How the fluidsimfoam-dam solver was created?

Modify one file in the solver (output.py)

    _helper_control_dict = Output._helper_control_dict.new(
        """
        application     interFoam
        endTime         1
        ...
    """
    )
    _helper_control_dict.include_function('"sampling"', kind="#sinclude")

    _helper_transport_properties = ConstantFileHelper(
        "transportProperties",
        {
            "phases": ["water", "air"],
            "water": {
                "transportModel": "Newtonian",
                "nu": 1e-06,
                "rho": 1000,
            },
            "air": {...},
        },
    )

    @classmethod
    def _complete_params_block_mesh_dict(cls, params):
        params._set_child(
            "block_mesh_dict",
            {
                "scale": 0.146,
                "nx": 46,
                "lx": 4.0,
                ...
                "x_dam": 2.0,
                "width_dam": 0.16438,
                "height_dam": 0.32873,
            },
        )

    def _make_code_block_mesh_dict(self, params):
        ...

Programmatic generation of BlockMeshDict

bmd = BlockMeshDict()
bmd.set_scale(params.block_mesh_dict.scale)

for x_y_z_name in (
    (0, 0, 0, "left_bot"),
    (x_dam, 0, 0, "leftdam_bot"),
    (x1_dam, 0, 0, "rightdam_bot"),
    (lx, 0, 0, "right_bot"),
    ...
):
    bmd.add_vertex(*x_y_z_name)

bmd.replicate_vertices_further_z(lz)

b_bot_left = bmd.add_hexblock_from_2d(
    ["left_bot", "leftdam_bot", "leftdam_topdam", "left_topdam"],
    [nx_left, ny_bot, nz],
    "left_bot",
)

Or classy_blocks!

Generation of initial conditions

Alternative to setFields, funkySetFields and codeStream

    @classmethod
    def _complete_params_alpha_a(cls, params):
        params._set_child(
            "init_fields",
            attribs={"type": "tanh", "width": 0.005, "bed_height": 0.08},
        )

    def _make_tree_alpha_a(self, params):
        field = VolScalarField("alpha_a", dimension="")
        field.set_boundary("top", "fixedValue", "uniform 0")
        ...

        if params.init_fields.type == "tanh":
            x, y, z = self.sim.oper.get_cells_coords()
            width = params.init_fields.width
            bed_height = params.init_fields.bed_height
            field.set_values(0.305 * (1.0 + np.tanh((bed_height - y) / width)))
        else:
            raise ValueError(f"Unsupported {params.init_fields.type = }")

        return field

Taken from doc/examples/fluidsimfoam-sed/src/fluidsimfoam_sed/output.py

Conclusions and perspectives

Fluidsimfoam: a young and promissing Python package

  • Strong underpinnings

  • A new workflow based on Python/IPython

  • Good bases for different Python usages related to OpenFOAM

  • Work in progress

    • Needs to be polished
    • Simple things missing (restart utilities, plots/movies, probes and co.)
  • Can greatly improve productivity of OpenFOAM users with Python skills

Become a community project

We need to build a community of users (creating issues and feature requests), contributors and core developers.

If you are interested, please (i) star the project on foss.heptapod.net and (ii) open issues!