diff --git a/.github/disabled_workflows/python_linux.yml b/.github/disabled_workflows/python_linux.yml new file mode 100644 index 00000000..cc02845f --- /dev/null +++ b/.github/disabled_workflows/python_linux.yml @@ -0,0 +1,71 @@ +name: Python Linux + +on: + push: + branches: [ master, develop, actions_pypi ] + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + pull_request: + branches: [ master ] + +jobs: + python_bindings: + name: Build ${{ matrix.name }} + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: 3.10.x + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install twine + + - name: Build manylinux Python wheels + uses: RalfG/python-wheels-manylinux-build@v0.4.2 + with: + package-path: wrappers/Python/ + pre-build-command: 'export COOLPROP_CMAKE=default,64' + python-versions: 'cp36-cp36m cp37-cp37m cp38-cp38 cp39-cp39 cp310-cp310' + build-requirements: 'cython' + pip-wheel-args: '-w ./dist --verbose' + + - name: Zip the wheels to maintain case sensitivy and file permissions + working-directory: ./wrappers/Python/ + shell: bash + run: | + tar -cvzf CoolProp-Linux_wheels.tar.gz dist/ + + - name: Upload .whl to artifact + uses: actions/upload-artifact@v2 + with: + name: CoolProp-Linux_wheels + path: ./wrappers/Python/CoolProp-Linux_wheels.tar.gz + + - name: Publish wheels to (Test)PyPI + # TODO: for now I'm effectively disabling uploading to testpypi on each build + if: contains(github.ref, 'refs/tags') + working-directory: ./wrappers/Python/ + env: + TWINE_USERNAME: __token__ + run: | + + if [[ "$GITHUB_REF" == *"refs/tags"* ]]; then + TWINE_REPOSITORY=pypi + TWINE_PASSWORD=${{ secrets.PYPI_TOKEN }} + else + TWINE_REPOSITORY=testpypi + TWINE_PASSWORD=${{ secrets.TESTPYPI_TOKEN }} + fi; + echo "TWINE_REPOSITORY=$TWINE_REPOSITORY" >> $GITHUB_ENV + echo "TWINE_PASSWORD=$TWINE_PASSWORD" >> $GITHUB_ENV + + twine upload dist/*-manylinux*.whl diff --git a/.github/workflows/python_cibuildwheel.yml b/.github/workflows/python_cibuildwheel.yml new file mode 100644 index 00000000..c5a6c28a --- /dev/null +++ b/.github/workflows/python_cibuildwheel.yml @@ -0,0 +1,233 @@ +name: Python cibuildwheel + +on: + push: + branches: [ master, develop, actions_pypi ] + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + pull_request: + branches: [ master ] + +jobs: + + python_bindings: + name: Build wheel for cp${{ matrix.python }}-${{ matrix.platform_id }}-${{ matrix.manylinux_image }} + runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.allow_failure }} + strategy: + # Ensure that a wheel builder finishes even if another fails + fail-fast: false + matrix: + include: + # Window 64 bit + # Note: windows-2019 is needed for older Python versions: + # https://github.com/scikit-learn/scikit-learn/issues/22530 + - os: windows-2019 + python: 38 + bitness: 64 + platform_id: win_amd64 + cmake_compiler: vc16 + arch: x64 + allow_failure: false + - os: windows-latest + python: 39 + bitness: 64 + cmake_compiler: vc17 + platform_id: win_amd64 + arch: x64 + allow_failure: false + - os: windows-latest + python: 310 + bitness: 64 + platform_id: win_amd64 + cmake_compiler: vc17 + arch: x64 + allow_failure: false + + # Window 32 bit + - os: windows-latest + python: 38 + bitness: 32 + platform_id: win32 + cmake_compiler: vc17 + arch: x86 + allow_failure: true + - os: windows-latest + python: 39 + bitness: 32 + platform_id: win32 + cmake_compiler: vc17 + arch: x86 + allow_failure: true + + # Linux 64 bit manylinux2014 + - os: ubuntu-latest + python: 37 + bitness: 64 + platform_id: manylinux_x86_64 + manylinux_image: manylinux2014 + cmake_compiler: default + allow_failure: false + - os: ubuntu-latest + python: 38 + bitness: 64 + platform_id: manylinux_x86_64 + manylinux_image: manylinux2014 + cmake_compiler: default + allow_failure: false + - os: ubuntu-latest + python: 39 + bitness: 64 + platform_id: manylinux_x86_64 + manylinux_image: manylinux2014 + cmake_compiler: default + allow_failure: false + - os: ubuntu-latest + python: 310 + bitness: 64 + platform_id: manylinux_x86_64 + manylinux_image: manylinux2014 + cmake_compiler: default + allow_failure: false + + # MacOS x86_64 + - os: macos-latest + bitness: 64 + python: 37 + platform_id: macosx_x86_64 + cmake_compiler: default + allow_failure: false + - os: macos-latest + bitness: 64 + python: 38 + platform_id: macosx_x86_64 + cmake_compiler: default + allow_failure: false + - os: macos-latest + bitness: 64 + python: 39 + platform_id: macosx_x86_64 + cmake_compiler: default + allow_failure: false + - os: macos-latest + bitness: 64 + python: 310 + platform_id: macosx_x86_64 + cmake_compiler: default + allow_failure: false + + # MacOS arm64 + - os: macos-latest + bitness: 64 + python: 38 + platform_id: macosx_arm64 + cmake_compiler: default + allow_failure: false + - os: macos-latest + bitness: 64 + python: 39 + platform_id: macosx_arm64 + cmake_compiler: default + allow_failure: false + - os: macos-latest + bitness: 64 + python: 310 + platform_id: macosx_arm64 + cmake_compiler: default + allow_failure: false + + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: 3.9.x + + - name: Setup Deps + shell: bash + run: | + if [ "$RUNNER_OS" == "Windows" ]; then + # C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise + MSVC_DIR=$(cmd.exe /c "vswhere -products * -requires Microsoft.Component.MSBuild -property installationPath -latest") + echo "Latest is: $MSVC_DIR" + echo "MSVC_DIR=$MSVC_DIR" >> $GITHUB_ENV + # add folder containing vcvarsall.bat + echo "$MSVC_DIR\VC\Auxiliary\Build" >> $GITHUB_PATH + fi + + - name: Build and test wheels + env: + COOLPROP_CMAKE: ${{ matrix.cmake_compiler}},${{ matrix.bitness }} + CIBW_ENVIRONMENT: COOLPROP_CMAKE=${{ matrix.cmake_compiler }},${{ matrix.bitness }} + MACOSX_DEPLOYMENT_TARGET: 10.9 + CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET=10.9 SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk + CIBW_BEFORE_BUILD: pip install setuptools wheel Cython requests jinja2 pyyaml + CIBW_BEFORE_ALL_WINDOWS: call vcvarsall.bat ${{ matrix.arch }} + CIBW_BUILD: cp${{ matrix.python }}-${{ matrix.platform_id }} + CIBW_ARCHS: all + CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux_image }} + CIBW_MANYLINUX_I686_IMAGE: ${{ matrix.manylinux_image }} + CIBW_TEST_SKIP: "*-macosx_arm64" + # CIBW_TEST_COMMAND: python -c 'from CoolProp.CoolProp import get_global_param_string; print("CoolProp gitrevision:", get_global_param_string("gitrevision"))' + CIBW_BUILD_VERBOSITY: 1 + + run: | + python -m pip install cibuildwheel + python -m cibuildwheel --output-dir wheelhouse ./wrappers/Python + + - name: Store artifacts + uses: actions/upload-artifact@v2 + with: + path: wheelhouse/*.whl + + upload_python_bindings_to_pypi: + needs: python_bindings + name: Upload to PyPi + runs-on: ubuntu-latest + steps: + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9.x + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine requests packaging + mkdir wheels + + - name: Download ALL wheels + uses: actions/download-artifact@v2 + with: + path: ./wheels + + - name: Display structure of downloaded files + working-directory: ./wheels + run: | + ls -R + + - name: Publish wheels to (Test)PyPI + # TODO: for now I'm effectively disabling uploading to testpypi on each build + if: contains(github.ref, 'refs/tags') + working-directory: ./wheels/artifact + env: + TWINE_USERNAME: __token__ + run: | + + if [[ "$GITHUB_REF" == *"refs/tags"* ]]; then + TWINE_REPOSITORY=pypi + TWINE_PASSWORD=${{ secrets.PYPI_TOKEN }} + else + TWINE_REPOSITORY=testpypi + TWINE_PASSWORD=${{ secrets.TESTPYPI_TOKEN }} + fi; + echo "TWINE_REPOSITORY=$TWINE_REPOSITORY" >> $GITHUB_ENV + echo "TWINE_PASSWORD=$TWINE_PASSWORD" >> $GITHUB_ENV + + twine upload *.whl + diff --git a/CMakeLists.txt b/CMakeLists.txt index b33e020e..d841936c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,9 @@ option (COOLPROP_NO_EXAMPLES # "On Darwin systems, compile and link with -std=libc++ instead of the default -std=libstdc++" # ON) +# Force C++11 since lambdas are used in CPStrings.h +set(CMAKE_CXX_STANDARD 11) + # see # https://stackoverflow.com/questions/52509602/cant-compile-c-program-on-a-mac-after-upgrade-to-mojave # https://support.enthought.com/hc/en-us/articles/204469410-OS-X-GCC-Clang-and-Cython-in-10-9-Mavericks diff --git a/src/Backends/IF97/IF97Backend.h b/src/Backends/IF97/IF97Backend.h index 4fca4e0e..3b06f346 100644 --- a/src/Backends/IF97/IF97Backend.h +++ b/src/Backends/IF97/IF97Backend.h @@ -7,6 +7,7 @@ #include "AbstractState.h" #include "Exceptions.h" #include +#include namespace CoolProp { @@ -44,14 +45,14 @@ public: this->_rhomass.clear(); this->_hmass.clear(); this->_smass.clear(); - this->_phase = iphase_not_imposed; + this->_phase = iphase_not_imposed; return true; }; void set_phase() { double epsilon = 3.3e-5; // IAPWS-IF97 RMS saturated pressure inconsistency - if ((abs(_T - IF97::Tcrit) < epsilon/10.0) && // RMS temperature inconsistency ~ epsilon/10 - (abs(_p - IF97::Pcrit) < epsilon)) { // within epsilon of [Tcrit,Pcrit] + if ((std::abs(_T - IF97::Tcrit) < epsilon/10.0) && // RMS temperature inconsistency ~ epsilon/10 + (std::abs(_p - IF97::Pcrit) < epsilon)) { // within epsilon of [Tcrit,Pcrit] _phase = iphase_critical_point; // at critical point } else if (_T >= IF97::Tcrit) { // to the right of the critical point @@ -92,31 +93,31 @@ public: void update(CoolProp::input_pairs input_pair, double value1, double value2){ double H,S,hLmass,hVmass,sLmass,sVmass; - + clear(); //clear the few cached values we are using switch(input_pair){ - case PT_INPUTS: - _p = value1; - _T = value2; + case PT_INPUTS: + _p = value1; + _T = value2; _Q = -1; set_phase(); - //Two-Phase Check, with PT Inputs: + //Two-Phase Check, with PT Inputs: if (_phase == iphase_twophase) throw ValueError(format("Saturation pressure [%g Pa] corresponding to T [%g K] is within 3.3e-3 %% of given p [%Lg Pa]", IF97::psat97(_T), _T, _p)); break; - case PQ_INPUTS: - _p = value1; + case PQ_INPUTS: + _p = value1; _Q = value2; - if ((_Q < 0) || (_Q > 1)) + if ((_Q < 0) || (_Q > 1)) throw CoolProp::OutOfRangeError("Input vapor quality [Q] must be between 0 and 1"); _T = IF97::Tsat97(_p); // ...will throw exception if _P not on saturation curve _phase = iphase_twophase; break; - case QT_INPUTS: - _Q = value1; - _T = value2; - if ((_Q < 0) || (_Q > 1)) + case QT_INPUTS: + _Q = value1; + _T = value2; + if ((_Q < 0) || (_Q > 1)) throw CoolProp::OutOfRangeError("Input vapor quality [Q] must be between 0 and 1"); _p = IF97::psat97(_T); // ...will throw exception if _P not on saturation curve _phase = iphase_twophase; @@ -241,8 +242,8 @@ public: else if (std::abs(_Q-1) < 1e-10){ return calc_SatVapor(iCalc); // dew point (Q == 1) on Sat. Vapor curve } - else { // else "inside" bubble ( 0 < Q < 1 ) - switch(iCalc){ + else { // else "inside" bubble ( 0 < Q < 1 ) + switch(iCalc){ case iDmass: // Density is an inverse phase weighted property, since it's the inverse of specific volume return 1.0/(_Q/calc_SatVapor(iDmass) + (1.0-_Q)/calc_SatLiquid(iDmass)); break; diff --git a/wrappers/Python/setup.py b/wrappers/Python/setup.py index 525d6438..e7b098b1 100644 --- a/wrappers/Python/setup.py +++ b/wrappers/Python/setup.py @@ -4,6 +4,7 @@ import subprocess, shutil, os, sys, glob, tempfile from distutils.version import LooseVersion from distutils.sysconfig import get_config_var from setuptools.command.build_ext import build_ext +from multiprocessing import cpu_count def copy_files(): def copytree(old, new): @@ -105,11 +106,15 @@ if __name__ == '__main__': i = sys.argv.index(cmake_args[0]) sys.argv.pop(i) cmake_compiler, cmake_bitness = cmake_args[0].split('cmake=')[1].split(',') + elif os.environ.get('COOLPROP_CMAKE'): + cmake_compiler, cmake_bitness = os.environ.get('COOLPROP_CMAKE').split(',') else: if '--cmake-compiler' in sys.argv: i = sys.argv.index('--cmake-compiler') sys.argv.pop(i) cmake_compiler = sys.argv.pop(i) + elif os.environ.get('COOLPROP_CMAKE_COMPILER'): + cmake_compiler = os.environ.get('COOLPROP_CMAKE_COMPILER') else: cmake_compiler = '' @@ -117,6 +122,8 @@ if __name__ == '__main__': i = sys.argv.index('--cmake-bitness') sys.argv.pop(i) cmake_bitness = sys.argv.pop(i) + elif os.environ.get('COOLPROP_CMAKE_BITNESS'): + cmake_bitness = os.environ.get('COOLPROP_CMAKE_BITNESS') else: cmake_bitness = '' @@ -127,6 +134,10 @@ if __name__ == '__main__': STATIC_LIBRARY_BUILT = False if USING_CMAKE: + if not cmake_compiler: + # Assume default + cmake_compiler = 'default' + # Always force build since any changes in the C++ files will not force a rebuild touch('CoolProp/CoolProp.pyx') @@ -169,6 +180,26 @@ if __name__ == '__main__': cmake_config_args += ['-G', '"Visual Studio 15 2017 Win64"'] else: raise ValueError('cmake_bitness must be either 32 or 64; got ' + cmake_bitness) + elif cmake_compiler == 'vc16': + cmake_build_args = ['--config', '"Release"'] + if cmake_bitness == '32': + cmake_config_args += ['-G', '"Visual Studio 16 2019"', '-A', + 'x86'] + elif cmake_bitness == '64': + cmake_config_args += ['-G', '"Visual Studio 16 2019"', '-A', + 'x64'] + else: + raise ValueError('cmake_bitness must be either 32 or 64; got ' + cmake_bitness) + elif cmake_compiler == 'vc17': + cmake_build_args = ['--config', '"Release"'] + if cmake_bitness == '32': + cmake_config_args += ['-G', '"Visual Studio 17 2022"', '-A', + 'x86'] + elif cmake_bitness == '64': + cmake_config_args += ['-G', '"Visual Studio 17 2022"', '-A', + 'x64'] + else: + raise ValueError('cmake_bitness must be either 32 or 64; got ' + cmake_bitness) elif cmake_compiler == 'mingw': cmake_config_args = ['-G', '"MinGW Makefiles"'] if cmake_bitness == '32': @@ -180,7 +211,7 @@ if __name__ == '__main__': elif cmake_compiler == 'default': cmake_config_args = [] if sys.platform.startswith('win'): - cmake_build_args = ['--config', '"Release"'] + cmake_build_args = ['--config', '"Release"'] if cmake_bitness == '32': cmake_config_args += ['-DFORCE_BITNESS_32=ON'] elif cmake_bitness == '64': @@ -209,11 +240,15 @@ if __name__ == '__main__': if not os.path.exists(cmake_build_dir): os.makedirs(cmake_build_dir) - cmake_call_string = ' '.join(['cmake', '../../../..', '-DCOOLPROP_STATIC_LIBRARY=ON', '-DCMAKE_VERBOSE_MAKEFILE=ON', '-DCMAKE_BUILD_TYPE=Release'] + cmake_config_args) + if 'vc' not in cmake_compiler: + cmake_config_args += ['-DCMAKE_BUILD_TYPE=Release'] + + cmake_call_string = ' '.join(['cmake', '../../../..', '-DCOOLPROP_STATIC_LIBRARY=ON', '-DCMAKE_VERBOSE_MAKEFILE=ON'] + cmake_config_args) print('calling: ' + cmake_call_string) subprocess.check_call(cmake_call_string, shell=True, stdout=sys.stdout, stderr=sys.stderr, cwd=cmake_build_dir) - cmake_build_string = ' '.join(['cmake', '--build', '.'] + cmake_build_args) + cmake_build_string = ' '.join(['cmake', '--build', '.', '-j', + str(cpu_count())] + cmake_build_args) print('calling: ' + cmake_build_string) subprocess.check_call(cmake_build_string, shell=True, stdout=sys.stdout, stderr=sys.stderr, cwd=cmake_build_dir) @@ -448,6 +483,7 @@ if __name__ == '__main__': "Operating System :: OS Independent", "Topic :: Software Development :: Libraries :: Python Modules" ], + setup_requires=['Cython'], **setup_kwargs ) except BaseException as E: