Julia calls PyFR

PyFR is written in Python3 and does not provide an interface, so we need to do it ourselves and transform it to call its internal functions in Julia. Last time we introduced an example of PyFR operation: two-dimensional Euler vortex. In this example, there is a step to convert the .msh file. How to call PyFR to achieve this step in Julia? We will start from here and finally realize that Julia calls PyFR to run a complete simulation. Before everything starts, it is assumed that you have installed Julia's PyCall package and read the readme document of PyCall's Github project.

Getting Started: How to call PyFR function to convert grid file

First, we have to install PyFR. This is not the Python virtual environment, but a normal environment, installing, executing, directly in the terminal: sudo apt install pyfr.

After installation, you still need to switch to the euler_vortex_2d directory in the examples in the source code mentioned last time. Try to run: pyfr import euler_vortex_2d.msh euler_vortex_2d.pyfrmto see if it runs normally.

The source code of the installed PyFR is /usr/local/lib/python3.7/dist-packages/pyfr/in, open the __main__.pyfile, there are several vital functions inside. _process_commonIt is the most important solving function, but before you understand it, you need to learn how to call some simple functions. For example, we are going to talk about process_importit now . It receives a parameter args, and uses the variables in args to perform several operations, and save the .msh file Convert to .pyfrm.

args is actually a Namespace, a special dictionary in python. We noticed that most __main__.pyof the functions inside take args as input parameters, so it is necessary to figure out what args contains. There is a mainfunction at the beginning of the file , which uses python's argparse package to customize terminal commands. Take a closer look, this code is also an excellent example for learning argparse. One line args = ap.parse_args()is where args is created. We add a line below:

    args = ap.parse_args()
    print(args)

And save the file (permission required).

Then rerun in the terminal pyfr import euler_vortex_2d.msh euler_vortex_2d.pyfrm, you will see the following output:

Namespace(cmd='import', inmesh=<_io.TextIOWrapper name='euler_vortex_2d.msh' mode='r' encoding='UTF-8'>, outmesh='euler_vortex_2d.pyfrm', process=<function process_import at 0x7f4ab21be7a0>, type=None, verbose=None)

This is the true face of args. When we run other PyFR commands such as pyfr run, we will see that the output contained in this Namespace is different.

There seems to be no corresponding Namespace in Julia. Although you can call the argparse package with PyCall, you cannot use custom commands. So we should abandon the Namespace, decompose args into multiple variables, and pass them to the function to achieve the goal.

Specifically, we observe process_importthe statement and find that the args here has three variables: type, inmesh, and outmesh. The type can be [], inmesh is the io stream created by python reading the .msh file, and outmesh is the file name of the converted file. So we rewrite the function as:

def process_import(type,inmesh,outmesh):
    # Get a suitable mesh reader instance
    if type:
        reader = get_reader_by_name(type, inmesh)
    else:
        extn = os.path.splitext(inmesh.name)[1]
        reader = get_reader_by_extn(extn, inmesh)

    # Get the mesh in the PyFR format
    mesh = reader.to_pyfrm()

    # Save to disk
    with h5py.File(outmesh, 'w') as f:
        for k, v in mesh.items():
            f[k] = v

save. Open another file __init__.py. Add two lines:

from pyfr.__main__ import process_import as pimp
__all__ = ['pimp']

This __init__.pyfile is the export interface of the python package. Anything in this scope can be referenced externally. The first line we added imported the function in the __main__file process_importinto the scope and named pimpit so that it could be referenced externally. __all__The variable is just a list of names used for prompting, and it can be omitted. save.

Now we are operating in Julia REPL. First open a new terminal in the euler_vortex_2d directory and enter the Julia REPL.

The first step is to call the necessary packages.

using PyCall
pyfr = pyimport("pyfr")

We use python's io package to create inmesh variables:

io = pyimport("io") 
inmesh = io.open("euler_vortex_2d.msh","r",encoding="utf-8")

Declare a type and outmesh variable:

type = []
outmesh="euler_vortex_2d.pyfrm"

Call pimpfunction (note the file path):

pyfr.pimp(type,inmesh,outmesh)

You can see the newly generated .pyfrm file in the folder, indicating that it has been completed.

Finally, we can check the list we just wrote to see what can be called:

julia> pyfr.__all__
1-element Array{
    
    String,1}:
 "pimp"

Tip 1: Note that there are two underscores before and after the __main__.py file. Sometimes there is one underscore, sometimes two, which is easy to misunderstand.
Tip 2: Every time the pyfr source file is modified, it will not be automatically updated in the python environment or Julia REPL environment. It needs to be re-imported. If it does not work, restart the environment.

Advanced: How to call the PyFR function to complete the simulation

With the above foundation, the rest will come naturally. We open the terminal in the euler_vortex_2d directory and run:

pyfr run -b openmp -p euler_vortex_2d.pyfrm euler_vortex_2d.ini

__main__.pyThe sentence we wrote earlier print(args)now makes a new contribution to us, revealing the pyfr runcontent of args under the command:

Namespace(backend='openmp', cfg=<_io.TextIOWrapper name='euler_vortex_2d.ini' mode='r' encoding='UTF-8'>, cmd='run', mesh='euler_vortex_2d.pyfrm', process=<function process_run at 0x7fafd342f680>, progress=True, verbose=None)

Among them, the three parameters of backend, cfg, and mesh are useful to us. Both backend and mesh are strings, and cfg is an io stream. Continuing the previous idea, we decompose args, and rewrite pyfr runthe two functions _process_commonand the implementation commands process_runas:

def _process_common(backend, progress, mesh, soln, cfg):
    # Prefork to allow us to exec processes after MPI is initialised
    if hasattr(os, 'fork'):
        from pytools.prefork import enable_prefork

        enable_prefork()

    # Import but do not initialise MPI
    from mpi4py import MPI

    # Manually initialise MPI
    MPI.Init()

    # Ensure MPI is suitably cleaned up
    register_finalize_handler()

    # Create a backend
    backend = get_backend(backend, cfg)

    # Get the mapping from physical ranks to MPI ranks
    rallocs = get_rank_allocation(mesh, cfg)

    # Construct the solver
    solver = get_solver(backend, rallocs, mesh, soln, cfg)

    # If we are running interactively then create a progress bar
    if progress and MPI.COMM_WORLD.rank == 0:
        pb = ProgressBar(solver.tstart, solver.tcurr, solver.tend)

        # Register a callback to update the bar after each step
        callb = lambda intg: pb.advance_to(intg.tcurr)
        solver.completed_step_handlers.append(callb)

    # Execute!
    solver.run()

    # Finalise MPI
    MPI.Finalize()


def process_run(backend, progress, mesh, cfg):
    _process_common(
        backend, progress, NativeReader(mesh), None, Inifile.load(cfg)
    )

Simply put, just change the parameters. Note that there is a progress parameter here, which is generally set to true to display a simulation progress bar.

We call process_runit externally , so __init__.pywrite it in :

# -*- coding: utf-8 -*-

from pyfr._version import __version__
from pyfr.__main__ import process_import as pimp
from pyfr.__main__ import process_run as prun

__all__ = ['pimp','prun']

Finally, we call prunto execute in Julia , might as well write a script:

using PyCall
io = pyimport("io")
pyfr=pyimport("pyfr")
mesh = "euler_vortex_2d.pyfrm"
cfg = io.open("euler_vortex_2d.ini","r",encoding="utf-8")
# 运行仿真
pyfr.prun("openmp",true,mesh,cfg)

You should see a progress bar. Congratulations on your completion.

Guess you like

Origin blog.csdn.net/iamzhtr/article/details/103134007