Python wrapper for the CONTAM Simulation Engine, ContamX
Project description
Python Bindings for ContamX
NOTE: This package is not yet fully functional.
This is the initial implementation of a Python wrapper for contamx-lib
which is a dynamic link library with an API to run ContamX.
Currently, this Python package only includes the basic components required to generate the wrapper, and the Python wrapper only implements a few basic methods. The demonstration case simply imports contamxpy
, obtains a state
, and gets the version of contamx-lib
.
Usage
Typically, one would work within a Python virtual environment which can be created and activated using the following commands.
$ python -m venv .venv
$ .venv\Scripts\activate (on Windows)
Install contamxpy
from PyPI.
$(.venv) python -m pip install contamxpy
Example Test Module: test_cxcffi.py
import contamxpy as cxLib
import os, sys
from optparse import OptionParser
#================================================================= main() =====
def main():
#----- Manage option parser
parser = OptionParser(usage="%prog [options] arg1\n\targ1=PRJ filename\n")
parser.set_defaults(verbose=0)
parser.add_option("-v", "--verbose", action="store", dest="verbose", type="int", default=0,
help="Define verbose output level: 0=Min, 1=Medium, 2=Maximum.")
(options, args) = parser.parse_args()
#----- Process command line options -v
verbose = options.verbose
if len(args) != 1:
parser.error("Need one argument:\n arg1 = PRJ file.")
return
else:
prj_name = args[0]
if ( not os.path.exists(prj_name) ):
print("ERROR: PRJ file not found.")
return
if verbose > 1:
print(f"cxLib attributes =>\n\t{dir(cxLib)}")
msg_cmd = "Running test_cxcffi.py: arg1 = " + args[0] + " " + str(options)
print(msg_cmd, "\n")
#----- Initialize contamx-lib State
p_contam_state: object = cxLib.getState()
cxLib.setWindPressureMode(p_contam_state, 0)
#----- Query State for PRJ info
verStr = cxLib.getVersion(p_contam_state)
if verbose >= 0:
print(f"getVersion() returned {verStr}.")
# --- End main() ---#
if __name__ == "__main__":
main()```
Developer Notes
These bindings were developed using the C Foreign Function Interface (CFFI). CFFI utilizes C header files that define the API to the shared object, i.e., contamx-lib.dll (or .so). It builds a dynamic Python module that provides access to the dll (or .so).
Steps to Develop Python Bindings
1. Create directory for contamxpy
- Either clone the repo OR
- python -m pip install contamxpy (e.g., into a virtual environment)
contamxpy\
|
| setup.py
| setup.cfg
| MANIFEST.in
| LICENSE.txt
| README.md
| contamxpy_build.py
| contamxpy.py
| contamx-lib.lib
| contamx-lib.dll
|
├── include\
| └── common-api.h
| commonState.h
| contam-x-cosim.h
| contam-x-state.h
| element-api.h
| flags.h
| library-api.h
| project-api.h
| string-len-max.h
| types.h
|
└── demo_files\
└── test_cxcffi.py
testOneZoneWthCtm.prj
testOneZoneWthCtm.wth
testOneZoneWthCtm.ctm
2. Create virtual environment
python -m venv .venv
3. Activate virtual environment
- Windows =>
.venv\Scripts\activate.bat
- Linux =>
.venv/bin/activate
4. Install cffi and wheel packages
$ python -m pip install cffi, wheel
5. Generate contamxpy
Run the build module.
contamxpy_build.py
This should generate _contamxpy.c, etc.
contamxpy\
|
├── Release\
| └── *.exp/.lib/.obj
|
└── _contamxpy.c
_contamxpy.cp310-win_amd64.pyd
Most importantly it will generate a .pyd file, e.g.,
_contamxpy-0.0.1-abi3-cp310-win_amd64.pyd
. This is a dynamic python module
which will be imported into a driver program.
6. Install the development version locally
$(.venv) pip install .
7. Generate Files for Distribution
Built distribution, i.e., wheel file:
$(.venv) python -m setup bdist_wheel
Source distribution, i.e., compressed archive (.gz, .zip):
$(.venv) python -m setup sdist
$(.venv) python -m setup sdist --format=zip
Development Files
build_cxcffi.py
The file shown below is a minimum implementation for using cxiLib
.
It must be modified to incorporate the full functionality of the
API associated with contamx-lib.dll
.
from __future__ import annotations
# Using the "out-of-line", "API mode"
from cffi import FFI
CDEF = '''\
// see types.h
typedef int32_t IX;
typedef double R8;
void* cxiGetContamState();
void cxiSetupSimulation(void* contamXState, char* projectPath, IX useCosim);
void cxiSetWindPressureMode(void* contamXState, IX useWP);
IX cxiGetSimulationStartDate(void* contamXState);
IX cxiGetSimulationStartTime(void* contamXState);
IX cxiGetSimulationEndDate(void* contamXState);
IX cxiGetSimulationEndTime(void* contamXState);
IX cxiGetSimulationTimeStep(void* contamXState);
IX cxiGetCurrentDate(void* contamXState);
IX cxiGetCurrentTime(void* contamXState);
void cxiDoCoSimStep(void* contamXState, IX stepForward);
void cxiEndSimulation(void* contamXState);
IX cxiGetVersion(void* contamXState, char* strVersion);
IX cxiGetNumCtms(void* contamXState);
IX cxiGetCtmName(void* contamXState, IX ctmNumber, char* strName);
IX cxiGetNumZones(void* contamXState);
IX cxiGetZoneMF(void* contamXState, IX zoneNumber, IX ctmNumber, R8* pMassFraction);
'''
SRC = '''\
#include "include//contam-x-cosim.h"
'''
ffibuilder = FFI()
ffibuilder.cdef(CDEF)
ffibuilder.set_source(
"_contamxpy", SRC,
include_dirs=['.','include'], # C header files for contam-x-lib
libraries=['contamx-lib'], # Library to link with (.lib, .dll)
)
if __name__ == "__main__":
ffibuilder.compile(verbose=True)
setup.py
This file is only required if you want to install the contamxpy package within a virtual environment using pip. However, once the .pyd file is generated, it can be utilized by a python module along with the contamx-lib.dll.
from __future__ import annotations
import platform
import sys
from setuptools import setup
if platform.python_implementation() == 'CPython':
try:
import wheel.bdist_wheel
except ImportError:
cmdclass = {}
else:
class bdist_wheel(wheel.bdist_wheel.bdist_wheel):
def finalize_options(self) -> None:
self.py_limited_api = f'cp3{sys.version_info[1]}'
super().finalize_options()
cmdclass = {'bdist_wheel': bdist_wheel}
else:
cmdclass = {}
setup(
cffi_modules=['contamxpy_build.py:ffibuilder'], cmdclass=cmdclass,
data_files=[(
'lib\\site-packages\\', ["contamx-lib.dll"])]
)
setup.cfg
[metadata]
name = contamxpy
version = 0.0.1
description = ContamX Python wrapper
long_description = file: README.md
long_description_content_type = text/markdown
url = https://www.nist.gov/el/energy-and-environment-division-73200/nist-multizone-modeling
author = W. Stuart Dols, Brian Polidoro
author_email = william.dols@nist.gov
license = Public Domain
license_files = LICENSE.txt
classifiers =
License :: Public Domain :: BSD License
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy
[options]
py_modules = contamxpy
install_requires = cffi>=1
python_requires = >=3.7
setup_requires = cffi>=1
MANIFEST.in
The manifest file is used to add files to the source builds.
include include\*.h
include contamx-lib.*
include demo_files\*.*
REFERENCES
- https://www.youtube.com/watch?v=X5irxO5VCHw
- https://github.com/asottile/ukkonen
- https://cffi.readthedocs.io/en/latest/index.html
- https://docs.python.org/3.10/distutils/index.html
- https://setuptools.pypa.io/en/latest/setuptools.html
- https://packaging.python.org/en/latest/tutorials/packaging-projects/
TODO
- Implement full API.
- Test on Linux
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for contamxpy-0.0.1-cp310-abi3-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 76d861634f6711aa02bbe06e2db05751b4260db21d7d3401811b0614c3497655 |
|
MD5 | e281e3628f6304a3f1fbbd1f0c85cd5c |
|
BLAKE2b-256 | 7793a0a2c52e0eb981847ba3fdc74d4bbc2c291a3903d60ef96228c1149f92e5 |