Wrapping an External Module Using F2PYΒΆ

As the most computationally intensive component, the engine model in engine.py is the main performance bottleneck during repeated execution. As an interpreted language, Python is not the ideal choice for implementing a numerical algorithm, particularly where performance is important. The performance of the engine model can be improved by implementing it in a compiled language like C or Fortran.

Python was designed to be smoothly integrated with other languages, in particular, C and related languages Fortran and C++. This is important for a scripting language, where code execution is generally slower and it is often necessary to use a compiled language like C to implement computationally intensive functions. To complement Python’s native integration ability, the Python community has developed some excellent tools, such as F2PY (Fortran to Python) and SWIG (Simplified Wrapper and Interface Generator), that simplify the process of building the wrapper for a code. As the name implies, F2PY is a Python utility that takes a Fortran source code file and compiles and generates a wrapped object callable from Python. More information on getting your codes to run in Python can be found in the Plugin Developer Guide.

The main algorithm in engine.py was rewritten in C as engine.C. A wrapped shared object of engine.C was created using F2Py; this tool can also be used to generate wrappers for C code provided that the signature file engine.pyf is manually created. The file engine.pyf defines the interface for the functions found in engine.C. The C code has been placed in a function called RunEngineCycle which can be imported and takes the design and simulation variables as inputs.

The intent of this exercise is not to teach you how to write a signature file. More information on this topic can be found in Creating an Extension with F2PY, while more extensive details can be found in the F2PY Users Guide.

The shared object can be generated using the existing signature file and C code by issuing the following command:

f2py engineC.pyf engineC.c -c

A new Python component named engine_wrap_c.py was created to replace engine.py. This component contains the same inputs and outputs as engine.py but replaces the engine internal calculations with a call to the C function RunEngineCycle. We can import and use this function just like any Python function:

        
        # Call the C model and pass it what it needs.
        
        (power, torque, fuel_burn, engine_weight) = RunEngineCycle(
                    stroke, bore, conrod, comp_ratio, spark_angle,
                    n_cyl, IVO, IVC, L_v, D_v, k,
                    R, Ru, Hu, Tw, AFR, P_exth,
                    T_amb, P_amb, air_density, mw_air, mw_fuel,
                    RPM, throttle, thetastep, fuel_density)

        
        # Interogate results of engine simulation and store.
        
        self.power = power[0]
        self.torque = torque[0]
        self.fuel_burn = fuel_burn[0]
        self.engine_weight = engine_weight[0]

        

Notice that the return values are stored in lists, so a scalar value is accessed by grabbing the first element (element zero). This is not needed for return values from Fortran codes compiled with F2PY (arguments in Fortran functions are passed by reference), but it is needed for C codes.

You can compare the execution time to see how much faster the C code runs on your hardware. To run the original Python implementation of Engine, type:

python engine.py

Note the elapsed time, which is printed after the engine completes its run. Now type:

python engine_wrap_c.py

How much faster is the C implementation? We found it to be almost 10 times as fast as the Python implementation. Both of these scripts run the engine 50 times before termination.

OpenMDAO Home

Previous topic

Executing a Component in the Python Shell

Next topic

Assemblies

This Page