-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Python bindings to Btwxt via SWIG #44
base: main
Are you sure you want to change the base?
Conversation
Note: for MSVC, UseSWIG.cmake already sets it to .pyd
(Tired of windows/MSVC...)
- os: macos | ||
os_ver: "11" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
macos-11 was removed from GHA a long time ago.
- os: macos | ||
os_ver: "12" | ||
os_ver: "14" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs/Python_bindings_demo.ipynb
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cmake_policy(SET CMP0078 NEW) | ||
cmake_policy(SET CMP0086 NEW) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of SWIG-related policies
@@ -32,6 +35,7 @@ endif () | |||
add_subdirectory("include/${PROJECT_NAME}") | |||
add_subdirectory(src) | |||
add_subdirectory(vendor) | |||
add_subdirectory(python) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add subdir. Did not change the other directories.
add_custom_target(python_package | ||
COMMAND ${CMAKE_COMMAND} -E rm -Rf "build/" "dist/" "btwxt.egg-info/" | ||
COMMAND ${CMAKE_COMMAND} -E echo "To create the package, cd 'python/'" | ||
COMMAND ${CMAKE_COMMAND} -E echo "${Python_EXECUTABLE} setup.py bdist_wheel" | ||
COMMAND ${Python_EXECUTABLE} setup.py bdist_wheel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Build the python wheel (.whl) with this custom target
COMMAND ${CMAKE_COMMAND} -E echo "To upload the package, cd 'python/'" | ||
COMMAND ${CMAKE_COMMAND} -E echo "${Python_EXECUTABLE} -m twine upload --verbose --skip-existing --repository ${PYTHON_PIP_REPOSITORY} dist/*" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not upload, but tells you how
if (${PROJECT_NAME}_BUILD_TESTING) | ||
set(test_case_name btwxt_python) | ||
add_test( | ||
NAME "${test_case_name}" | ||
COMMAND ${Python_EXECUTABLE} -m pytest --verbose test_btwxt.py | ||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test | ||
) | ||
# Pytest Exit code 5 means all tests were deselected | ||
set_tests_properties("${test_case_name}" PROPERTIES SKIP_RETURN_CODE 5) | ||
set_tests_properties("${test_case_name}" PROPERTIES ENVIRONMENT "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/btwxt") | ||
endif() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Register a pytest file as a CTest.
import btwxt | ||
import pytest | ||
from itertools import product | ||
|
||
def test_grid_axis(): | ||
axis_x = btwxt.GridAxis() | ||
assert axis_x.get_length() == 0 | ||
|
||
xs = [30, 35, 40, 45, 50, 55] | ||
axis_x = btwxt.GridAxis( | ||
[30, 35, 40, 45, 50, 55], # Values | ||
btwxt.InterpolationMethod_cubic, # interpolation method | ||
btwxt.ExtrapolationMethod_linear, # extrapolation_method | ||
(20.0, 60.0), # extrapolation_limits | ||
"LWT", # name | ||
) | ||
assert axis_x.get_length() == 6 | ||
assert set(axis_x.get_values()) == set(xs) | ||
assert axis_x.get_interpolation_method() == btwxt.InterpolationMethod_cubic | ||
assert axis_x.get_extrapolation_method() == btwxt.ExtrapolationMethod_linear | ||
assert axis_x.get_extrapolation_limits() == (20.0, 60.0) | ||
|
||
|
||
def F(x, y): | ||
return x**2 + y**2 | ||
|
||
def test_interpolator(): | ||
x0 = [1.0, 2.0, 3.0] | ||
x1 = [10.0, 20.0] | ||
# Not using numpy on purpose | ||
ys = [F(x,y) for (x, y) in product(x0, x1)] | ||
|
||
axis_x0 = btwxt.GridAxis( | ||
x0, # Values | ||
btwxt.InterpolationMethod_cubic, # interpolation method | ||
btwxt.ExtrapolationMethod_linear, # extrapolation_method | ||
(0, 5), # extrapolation_limits | ||
"x0", # name | ||
) | ||
axis_x1 = btwxt.GridAxis( | ||
x1, # Values | ||
btwxt.InterpolationMethod_cubic, # interpolation method | ||
btwxt.ExtrapolationMethod_linear, # extrapolation_method | ||
(0, 30), # extrapolation_limits | ||
"x1", # name | ||
) | ||
grid_point_y = btwxt.GridPointDataSet(ys, "y") | ||
|
||
interpolator = btwxt.RegularGridInterpolator([axis_x0, axis_x1], [grid_point_y]) | ||
|
||
target_normalization = [2, 10] | ||
normalizationDivisor = 1.0 | ||
normalizationDivisor = interpolator.normalize_grid_point_data_set_at_target( | ||
0, target_normalization, normalizationDivisor | ||
) * normalizationDivisor | ||
assert normalizationDivisor == 104.0 | ||
|
||
interp_x0s = [float(x) for x in range(0, 6, 1)] | ||
interp_oat_dbs = [float(x) for x in range(0, 31, 5)] | ||
|
||
test_points = [(x,y) for (x, y) in product(interp_x0s, interp_oat_dbs)] | ||
true_values = [F(x, y) for (x, y) in test_points] | ||
interp_values = [ | ||
interpolator.get_value_at_target((x0, x1), 0) * normalizationDivisor | ||
for (x0, x1) in test_points | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pytest file to test out the python bindings
- name: Set up Python 3.12 | ||
if: ${{ matrix.python_bindings }} | ||
id: setup-python | ||
uses: actions/setup-python@v5 | ||
with: | ||
python-version: 3.12 | ||
|
||
- name: Build Python Wheel | ||
if: ${{ matrix.python_bindings }} | ||
working-directory: ./build/ | ||
run: | | ||
begin_group() { echo -e "::group::\033[93m$1\033[0m"; } | ||
|
||
begin_group "Install other python dependencies" | ||
python -m pip install --upgrade pip | ||
# Ubuntu 22.04 from apt will get swig that's too old to support attributes like [[ nodiscard ]], so get from PyPi | ||
pip install setuptools wheel twine requests packaging pytest swig | ||
echo -e "::endgroup::" | ||
|
||
begin_group "Build target" | ||
cmake -DBUILD_PYTHON_BINDINGS:BOOL=ON -DPython_REQUIRED_VERSION:STRING=${{ steps.setup-python.outputs.python-version }} -DPython_ROOT_DIR:PATH=$RUNNER_TOOL_CACHE/Python/${{ steps.setup-python.outputs.python-version }}/${{ matrix.arch }}/ . | ||
cmake --build . --config ${{ matrix.config }} --target python_package | ||
echo -e "::endgroup::" | ||
|
||
- name: Python CTest | ||
if: ${{ matrix.python_bindings }} | ||
working-directory: ./build/ | ||
run: | | ||
ctest -C ${{ matrix.config }} -R python -VV | ||
|
||
- name: Zip the wheel to maintain case sensitivy and file permissions | ||
if: ${{ matrix.python_bindings }} | ||
working-directory: ./build/python | ||
shell: bash | ||
run: | | ||
TGZ_STEM="btwxt-python-${{ matrix.os }}-${{ matrix.os_ver }}-${{ matrix.arch }}" | ||
echo "TGZ_STEM=$TGZ_STEM" >> $GITHUB_ENV | ||
tar -cvzf "$TGZ_STEM.tar.gz" dist/ | ||
|
||
- name: Upload .whl to artifact | ||
if: ${{ matrix.python_bindings }} | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: ${{ env.TGZ_STEM }} | ||
path: "./build/python/${{ env.TGZ_STEM }}.tar.gz" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New workflow bits to build the python bindings after the regular build is done.
Runs the python ctest, and uploads the wheel as artifacts
I was struggling to understand what EnergyPlus was doing with my Table:Lookup and Table:IndependentVariables, and wanted a way to calculate and plot what was going on (and compare with
scipy.interpolate.RegularGridInterpolator
).Instead of using a debugger, or tweaking E+ source code to spit out the values I needed, I figured it would be much better to add Python bindings.
Since I had done that, I figured it could be useful for other people (at least some E+ devs, and yourselves as btwxt devs for inspection/validation purposes), so I'm proposing it for inclusion.
This PR:
Find a working workflow with artifacts here: https://github.com/jmarrec/btwxt/actions/runs/12986553478