Skip to content
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

Open
wants to merge 22 commits into
base: main
Choose a base branch
from

Conversation

jmarrec
Copy link

@jmarrec jmarrec commented Jan 27, 2025

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

- os: macos
os_ver: "11"
Copy link
Author

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"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

macos-12 is removed.

actions/runner-images#10721

I uses 13 (x86_64) and 14 (arm64, M1)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +3 to +4
cmake_policy(SET CMP0078 NEW)
cmake_policy(SET CMP0086 NEW)
Copy link
Author

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)
Copy link
Author

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.

Comment on lines +85 to +89
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
Copy link
Author

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

Comment on lines +90 to +91
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/*"
Copy link
Author

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

Comment on lines +99 to +109
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()
Copy link
Author

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.

Comment on lines +1 to +66
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
]
Copy link
Author

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

Comment on lines +114 to +158
- 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"
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant