Flow over Periodic Hill (fluidsimfoam-phill solver)#

Fluidsimfoam repository contains a simple example solver to simulate the flow over a periodic hill. We will demonstrate how it can be used.

Run simulations by executing scripts#

There are three different geometries available for this solver. We will run the simulation by executing these three scripts:

3D phill#

The script corresponding to the 3d geometry contains:

import argparse

from fluidsimfoam_phill import Simul

parser = argparse.ArgumentParser(
    prog="phill",
    description="Run a flow over periodic hills simulation",
)

parser.add_argument(
    "-nsave",
    default=2,
    type=int,
    help="Number of save output to file.",
)
parser.add_argument(
    "-nx",
    default=50,
    type=int,
    help="Number of grids in x-direction",
)
parser.add_argument("--end-time", default=20.0, type=float)

args = parser.parse_args()

params = Simul.create_default_params()

params.output.sub_directory = "examples_fluidsimfoam/phill"
params.short_name_type_run = "3d_phill"
params.block_mesh_dict.geometry = "3d_phill"

params.init_fields.buoyancy_frequency = 0.001
params.constant.transport.nu = 0.01
params.constant.transport.pr = 10

params.control_dict.end_time = args.end_time
params.control_dict.write_interval = args.end_time / args.nsave

params.block_mesh_dict.lx = 10
params.block_mesh_dict.ly = 10
params.block_mesh_dict.lz = 10
params.block_mesh_dict.ly_porosity = 10
# geometry parameters
params.block_mesh_dict.h_max = 3
params.block_mesh_dict.sigma = 0.2

params.block_mesh_dict.nx = args.nx
params.block_mesh_dict.ny = args.nx
params.block_mesh_dict.nz = args.nx
params.block_mesh_dict.n_porosity = int(args.nx * 15 / 50)

params.constant.g.value = [0, 0, -9.81]

params.fv_options.momentum_source.active = False
params.fv_options.porosity.active = True

# not supported by OpenFOAM 1912...
# params.fv_options.atm_coriolis_u_source.active = True
# params.fv_options.atm_coriolis_u_source.omega = [0, 0, 7.2921e-5]

sim = Simul(params)

sim.make.exec("run")

We see that this script defines parameters. To get the help, we can run:

run examples/scripts/tuto_phill_3d.py -h
usage: phill [-h] [-nsave NSAVE] [-nx NX] [--end-time END_TIME]

Run a flow over periodic hills simulation

options:
  -h, --help           show this help message and exit
  -nsave NSAVE         Number of save output to file.
  -nx NX               Number of grids in x-direction
  --end-time END_TIME

Here we are going to run it with the command:

command = "python3 examples/scripts/tuto_phill_3d.py -nx 20 --end-time 100 -nsave 5"

However, we require a little bit more code in this notebook. You may merely glance at the output of this cell since the way we execute this command is peculiar to this tutorial provided as notebook.

Hide code cell source
from subprocess import run, PIPE, STDOUT
from time import perf_counter

print("Running the script tuto_phill_3d.py... (It can take few minutes.)")
t_start = perf_counter()
process = run(
    command.split(), text=True, stdout=PIPE,  stderr=STDOUT
)
if process.returncode:
    print(process.stdout[-10000:])
    raise RuntimeError(f"Script tuto_phill_3d.py returned non-zero exit status {process.returncode}")
print(f"Script executed in {perf_counter() - t_start:.2f} s")

lines = process.stdout.split("\n")
Running the script tuto_phill_3d.py... (It can take few minutes.)
Script executed in 30.97 s
Hide code cell source
path_run = None
for line in lines:
    if "path_run: " in line:
        path_run = line.split("path_run: ")[1].split(" ", 1)[0]
        break
if path_run is None:
    raise RuntimeError

We can now load the simulation and process the output.

from fluidsimfoam import load

sim = load(path_run)
path_run: /home/docs/Sim_data/examples_fluidsimfoam/phill/phill_3d_phill_2024-01-21_22-31-01
INFO     sim:                        <class 'fluidsimfoam_phill.Simul'>                                            
         sim.output.log:             <class 'fluidsimfoam.output.log.Log'>                                         
         sim.output.fields:          <class 'fluidsimfoam.output.fields.Fields'>                                   
         input_files:                                                                                              
           - in 0:          U p_rgh T alphat                                                                       
           - in constant:   g transportProperties turbulenceProperties                                             
           - in system:     controlDict fvSchemes fvSolution decomposeParDict blockMeshDict fvOptions              
         sim.output:                 <class 'fluidsimfoam_phill.output.OutputPHill'>                               
         sim.oper:                   <class 'fluidsimfoam.operators.Operators'>                                    
         sim.init_fields:            <class 'fluidsimfoam.init_fields.InitFields'>                                 
         sim.make:                   <class 'fluidsimfoam.make.MakeInvoke'>                                        
                                                                                                                   

Pyvista output#

With the sim object, one can simply visualize the simulation with few methods in sim.output.fields, see here.

First, we specify some theme configuration for this notebook:

Hide code cell source
import pyvista as pv
pv.set_jupyter_backend("static")

pv.global_theme.background = 'white'
pv.global_theme.font.color = 'black'
pv.global_theme.font.label_size = 12
pv.global_theme.font.title_size = 16
pv.global_theme.colorbar_orientation = 'vertical'

We start with an overview of the mesh with the method fluidsimfoam.output.fields.Fields.plot_mesh().

sim.output.fields.plot_mesh(color="black");
_images/f277dbae13159e0e580116701679edeea189ef318c6acae5ddfdca8277e873c4.png

Add options to plotter#

All plot methods return plotter object. With this object, one can modify some properties of the plot and show the figure when it is ready. Note the usage of show=False in this situation!

plotter = sim.output.fields.plot_boundary(
    "bottom", color="grey", mesh_opacity=0, show=False
)
plotter.show_grid()
plotter.camera.zoom(0.9)
plotter.show()
_images/1d277bd0cdb86eb99829a0a3427123ea800d81badff53024a63a66ff1b06cea0.png

Save figure as a file#

You may use plotter.save_graphic(filename) to save plot as a file in the following formats: ‘.svg’, ‘.eps’, ‘.ps’, ‘.pdf’, and ‘.tex’. Save the figure before plotter.show().

One can quickly produce contour plots with fluidsimfoam.output.fields.Fields.plot_contour(), for example, variable U in plane \(y = 0\) and \(t = 20\):

plotter = sim.output.fields.plot_contour(
    equation="y=0",
    variable="U",
    mesh_opacity=0.1,
    time=20,
    show=False,
);
plotter.save_graphic("ufield_100.pdf")
plotter.show()
_images/507840d58ede494eb9469477c69910a0751054c1f3dcdf229af74f1bd4cc0156.png

Add several plots to figure#

Multiple plots can be superposed. As an illustration, suppose we wish to superpose the bottom boundary and contour lines to the previous plot. To achieve this, we first obtain the plotter without displaying the plot (show=False), and then we give this plotter object to plot_boundary to add mesh to this object. After adding the contour lines, the figure is ready to be displayed.

plotter = sim.output.fields.plot_contour(
    equation="y=0",
    variable="U",
    mesh_opacity=0.08,
    time=100,
    show=False,
)
sim.output.fields.plot_boundary(
    "bottom",
    color="#e0e0eb",
    show_edges=True,
    mesh_opacity=0,
    show=False,
    plotter=plotter
)
plotter = sim.output.fields.plot_contour(
    equation="y=0",
    variable="U",
    time=100,
    contour=True,
    show=False,
    plotter=plotter,
)
plotter.show_grid(show_yaxis=False)
plotter.show()
_images/3fcde60c979e932531bf91c6d3eb85eaafb497dcb561b447a4f8a1a683a5cc0e.png

We’re now going to plot some “Uz” contours as another example. We start with 2 plot_contour in 2 cross-sections and one plot_boundary. We then add the contours lines in a loop.

To plot other components of a vector, use the component argument ({"x": 0, "y": 1, "z": 2}), for example here we added component=2 for plotting “Uz”.

plotter = sim.output.fields.plot_contour(
    equation="x=-4.9",
    mesh_opacity=0,
    variable="U",
    component=2,
    cmap="plasma",
    show=False,
)
sim.output.fields.plot_contour(
    equation="y=-4.9",
    mesh_opacity=0,
    variable="U",
    component=2,
    cmap="plasma",
    show=False,
    plotter=plotter,
)
sim.output.fields.plot_boundary(
    "bottom", color="#e0e0eb",
    show_edges=False,
    mesh_opacity=0,
    show=False,
    plotter=plotter
)

for y in range(-4, 5, 1):
    sim.output.fields.plot_contour(
        equation=f"y={y}",
        mesh_opacity=0,
        variable="U",
        component=2,
        cmap="plasma",
        contour=True,
        show=False,
        plotter=plotter,
    )

plotter.view_isometric()
plotter.show()
_images/a75d7b8def43fbd1571144ddc4fd56387b33267a8753d4d1617cd79a01b933c7.png

One can plot the variation of a variable over a straight line, by providing two points. By setting show_line_in_domain=True, you can first view the line in the domain to confirm its location.

sim.output.fields.plot_profile(
    point0=[0.5, 0.5, 2],
    point1=[0.5, 0.5, 20],
    variable="U",
    ylabel="$U$(m/s)",
    title="Velocity Profile",
    show_line_in_domain=True,
    show=True,
);
_images/985ada79766a16d0be0c6c7ca3a743220a290801c47354dc41da59a6548c6b5c.png _images/3aad462f20053e1b6db389c3d93988da634415ec633cf537bbcad4a8e6a70663.png
<Figure size 640x480 with 0 Axes>

2D phill#

We now turn to the 2d geometry:

import argparse

from fluidsimfoam_phill import Simul

parser = argparse.ArgumentParser(
    prog="phill",
    description="Run a flow over periodic hills simulation",
)

parser.add_argument(
    "-nsave",
    default=2,
    type=int,
    help="Number of save output to file.",
)
parser.add_argument(
    "-nx",
    default=200,
    type=int,
    help="Number of mesh",
)
parser.add_argument("--end-time", default=20.0, type=float)

args = parser.parse_args()

params = Simul.create_default_params()
params.output.sub_directory = "examples_fluidsimfoam/phill"
params.short_name_type_run = "2d_phill"
params.block_mesh_dict.geometry = "2d_phill"

params.init_fields.buoyancy_frequency = 0.001
params.constant.transport.nu = 0.01
params.constant.transport.pr = 10

params.control_dict.end_time = args.end_time
params.control_dict.write_interval = args.end_time / args.nsave

params.block_mesh_dict.lx = 6
params.block_mesh_dict.ly = 1
params.block_mesh_dict.lz = 0.01
params.block_mesh_dict.ly_porosity = 1
# geometry parameters
params.block_mesh_dict.l_hill = 0.9
params.block_mesh_dict.hill_start = 0.6
params.block_mesh_dict.h_max = 0.2
params.block_mesh_dict.sigma = 0.2

params.block_mesh_dict.nx = args.nx
params.block_mesh_dict.ny = int(args.nx * 30 / 200)
params.block_mesh_dict.n_porosity = int(args.nx * 15 / 200)
params.block_mesh_dict.nz = 1

params.fv_options.momentum_source.active = False
params.fv_options.porosity.active = True

# not supported by OpenFOAM 1912...
# params.fv_options.atm_coriolis_u_source.active = True
# params.fv_options.atm_coriolis_u_source.omega = [0, 0, 7.2921e-5]

sim = Simul(params)

sim.make.exec("run")

We run the simulation:

Hide code cell source
command = "python3 examples/scripts/tuto_phill_2d.py -nx 200"

from subprocess import run, PIPE, STDOUT
from time import perf_counter

print("Running the script tuto_phill_2d.py... (It can take few minutes.)")
t_start = perf_counter()
process = run(
    command.split(), check=True, text=True, stdout=PIPE,  stderr=STDOUT
)
print(f"Script executed in {perf_counter() - t_start:.2f} s")
lines = process.stdout.split("\n")
Running the script tuto_phill_2d.py... (It can take few minutes.)
Script executed in 12.84 s

Get the path of the corresponding directory:

Hide code cell source
path_run = None
for line in lines:
    if "path_run: " in line:
        path_run = line.split("path_run: ")[1].split(" ", 1)[0]
        break
if path_run is None:
    raise RuntimeError

and recreate the sim object:

from fluidsimfoam import load

sim = load(path_run)
path_run: /home/docs/Sim_data/examples_fluidsimfoam/phill/phill_2d_phill_2024-01-21_22-31-48
INFO     sim:                        <class 'fluidsimfoam_phill.Simul'>                                            
         sim.output.log:             <class 'fluidsimfoam.output.log.Log'>                                         
         sim.output.fields:          <class 'fluidsimfoam.output.fields.Fields'>                                   
         input_files:                                                                                              
           - in 0:          U p_rgh T alphat                                                                       
           - in constant:   g transportProperties turbulenceProperties                                             
           - in system:     controlDict fvSchemes fvSolution decomposeParDict blockMeshDict fvOptions              
         sim.output:                 <class 'fluidsimfoam_phill.output.OutputPHill'>                               
         sim.oper:                   <class 'fluidsimfoam.operators.Operators'>                                    
         sim.init_fields:            <class 'fluidsimfoam.init_fields.InitFields'>                                 
         sim.make:                   <class 'fluidsimfoam.make.MakeInvoke'>                                        
                                                                                                                   

Again, we plot the mesh and demonstrate how to zoom on it:

plotter = sim.output.fields.plot_mesh(color="black", show=False);
plotter.camera.zoom(1.3)
plotter.show()
_images/57cc7576d16f7553a2bcc7727c392ff2d2791755a3196d57899850101ca31e0a.png

Let’s produce a contour plot of variable U with grid:

pv.global_theme.colorbar_orientation = 'horizontal'
plotter = sim.output.fields.plot_contour(
    variable="U", cmap="plasma", time=20, show=False
)
plotter.camera.zoom(1.2)
plotter.show_grid()
plotter.show()
_images/cf0b31c8e01fa1ee00cc7d4fdc854411549c91f0a8e2a82edf68d30ec9a2085d.png

How to select points for plot_profile#

For plotting temperature profile in a vertical line located in \(x=3\), \(z=0\), we can define each point like this:

  • x0=3, x1=3

  • z0=0, z1=0

Additionally, for \(y\), \(y_0 <= y_{min}\) and \(y_1 >= y_{max}\), the segment of the line that is outside of the mesh will not be shown. As a result, points can be like this: point0 = [3, 0, 0]; point1 = [3, 10, 0]. Note that the line is plotted from 0 to 2.

plotter, m_plotter = sim.output.fields.plot_profile(
    point0=[3, 0, 0],
    point1=[3, 10, 0],
    variable="T",
    ylabel="$T$(K)",
    title="Temperature Profile",
    show_line_in_domain=True,
    show=False,
)

plotter.camera_position = "xy"
plotter.camera.zoom(1.2)
plotter.show_grid()
plotter.show()
_images/243b21770e6ab0145f46ab2464ebcaac2f433494df60180ba05cd34f43e2062e.png _images/b2aaaf9934eaccbdc797d1bfe8c049e35029cad10b86e5bf21dde96a9cbe0d33.png

Sinusoidal phill#

Finally, let’s consider the script tuto_phill_2d.py, which contains:

import argparse

from fluidsimfoam_phill import Simul

parser = argparse.ArgumentParser(
    prog="phill",
    description="Run a flow over periodic hills simulation",
)

parser.add_argument(
    "-nsave",
    default=2,
    type=int,
    help="Number of save output to file.",
)
parser.add_argument(
    "-nx",
    default=200,
    type=int,
    help="Number of mesh",
)
parser.add_argument("--end-time", default=20.0, type=float)

args = parser.parse_args()

params = Simul.create_default_params()
params.output.sub_directory = "examples_fluidsimfoam/phill"
params.short_name_type_run = "2d_phill"
params.block_mesh_dict.geometry = "2d_phill"

params.init_fields.buoyancy_frequency = 0.001
params.constant.transport.nu = 0.01
params.constant.transport.pr = 10

params.control_dict.end_time = args.end_time
params.control_dict.write_interval = args.end_time / args.nsave

params.block_mesh_dict.lx = 6
params.block_mesh_dict.ly = 1
params.block_mesh_dict.lz = 0.01
params.block_mesh_dict.ly_porosity = 1
# geometry parameters
params.block_mesh_dict.l_hill = 0.9
params.block_mesh_dict.hill_start = 0.6
params.block_mesh_dict.h_max = 0.2
params.block_mesh_dict.sigma = 0.2

params.block_mesh_dict.nx = args.nx
params.block_mesh_dict.ny = int(args.nx * 30 / 200)
params.block_mesh_dict.n_porosity = int(args.nx * 15 / 200)
params.block_mesh_dict.nz = 1

params.fv_options.momentum_source.active = False
params.fv_options.porosity.active = True

# not supported by OpenFOAM 1912...
# params.fv_options.atm_coriolis_u_source.active = True
# params.fv_options.atm_coriolis_u_source.omega = [0, 0, 7.2921e-5]

sim = Simul(params)

sim.make.exec("run")

We run the simulation:

Hide code cell source
command = "python3 examples/scripts/tuto_phill_sinus.py -nx 120 --end_time 300 -nsave 3"
from subprocess import run, PIPE, STDOUT
from time import perf_counter

print("Running the script tuto_phill_sinus.py... (It can take few minutes.)")
t_start = perf_counter()
process = run(
    command.split(), check=True, text=True, stdout=PIPE,  stderr=STDOUT
)
print(f"Script executed in {perf_counter() - t_start:.2f} s")
lines = process.stdout.split("\n")
Running the script tuto_phill_sinus.py... (It can take few minutes.)
Script executed in 7.22 s

Get the corresponding directory path:

Hide code cell source
path_run = None
for line in lines:
    if "path_run: " in line:
        path_run = line.split("path_run: ")[1].split(" ", 1)[0]
        break
if path_run is None:
    raise RuntimeError

and load the simulation:

from fluidsimfoam import load

sim = load(path_run)
path_run: /home/docs/Sim_data/examples_fluidsimfoam/phill/phill_sinus_2024-01-21_22-32-06
INFO     sim:                        <class 'fluidsimfoam_phill.Simul'>                                            
         sim.output.log:             <class 'fluidsimfoam.output.log.Log'>                                         
         sim.output.fields:          <class 'fluidsimfoam.output.fields.Fields'>                                   
         input_files:                                                                                              
           - in 0:          U p_rgh T alphat                                                                       
           - in constant:   g transportProperties turbulenceProperties                                             
           - in system:     controlDict fvSchemes fvSolution decomposeParDict blockMeshDict fvOptions              
         sim.output:                 <class 'fluidsimfoam_phill.output.OutputPHill'>                               
         sim.oper:                   <class 'fluidsimfoam.operators.Operators'>                                    
         sim.init_fields:            <class 'fluidsimfoam.init_fields.InitFields'>                                 
         sim.make:                   <class 'fluidsimfoam.make.MakeInvoke'>                                        
                                                                                                                   

Let’s look at the mesh:

sim.output.fields.plot_mesh(color="black");
_images/ec8b7512745276b6c4a5643a802ffaaa8687a1b8036ae524b64af2cbefdc89eb.png

Subplots#

We can use plotter.subplot to plot the velocity at times 100, 200 and 300:

pv.global_theme.colorbar_orientation = 'horizontal'
pv.global_theme.colorbar_horizontal.position_x = 0.2

plotter = pv.Plotter(shape=(1, 3))
plotter.subplot(0, 0)
plotter.add_title("Time: 100s")
sim.output.fields.plot_contour(
  plotter=plotter, variable="U", time=100, show=False
)
plotter.camera.zoom(1.6)

plotter.subplot(0, 1)
plotter.add_title("Time: 200s")
sim.output.fields.plot_contour(
  plotter=plotter, variable="U", time=200, show=False
)
plotter.camera.zoom(1.6)

plotter.subplot(0, 2)
plotter.add_title("Time: 300s")
sim.output.fields.plot_contour(
  plotter=plotter, variable="U", time=300, show=False
)
plotter.camera.zoom(1.6)

plotter.show()
_images/aba7cbb7cf5280e0380c0a12e9f1cd8c8bd7d51cfaf1d9fc5e9c71a8038e615c.png