From ceaf370b690058b1ba6537b3cd84a6e0b32c6052 Mon Sep 17 00:00:00 2001 From: "Bryan W. Weber" Date: Sat, 11 Apr 2020 21:30:53 -0400 Subject: [PATCH 1/5] Fixes dimensionality error in Rankine example The Rankine cycle example had a dimensionality error due to the extra units multiplication on the linspace. It seems like linspace includes units now instead of dropping them. Fixes #24 --- docs/rankine-cycle-example.ipynb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/rankine-cycle-example.ipynb b/docs/rankine-cycle-example.ipynb index 62d09ce..fac841a 100644 --- a/docs/rankine-cycle-example.ipynb +++ b/docs/rankine-cycle-example.ipynb @@ -314,9 +314,7 @@ "metadata": {}, "outputs": [], "source": [ - "y_values = []\n", - "eta_values = []\n", - "for p_2 in linspace(p_low, p_high, 100)*units.MPa:\n", + "for p_2 in linspace(p_low, p_high, 100):\n", " # State 2\n", " s_2 = s_1\n", " st_2 = State(substance, p=p_2, s=s_2)\n", From d94749036ec7fddb55b60ac915def8417e2141ee Mon Sep 17 00:00:00 2001 From: "Bryan W. Weber" Date: Sat, 11 Apr 2020 21:38:05 -0400 Subject: [PATCH 2/5] Set up Matplotlib for use with Pint Pint now includes a function to use Matplotlib correctly. This requires Pint >=0.9. With this function, Matplotlib takes arrays with units attached and plots them automatically. This also requires small changes to the examples. np.arange does not support Quantities as arguments, so switch to np.linspace which has that support. Create arrays for accumulation of results, since lists don't seem to work properly with when units are involved. --- docs/air-standard-brayton-cycle-example.ipynb | 16 ++++++++-------- docs/cold-air-brayton-cycle-example.ipynb | 17 ++++++++--------- docs/rankine-cycle-example.ipynb | 13 ++++++++----- setup.cfg | 9 ++++----- src/thermostate/thermostate.py | 1 + 5 files changed, 29 insertions(+), 27 deletions(-) diff --git a/docs/air-standard-brayton-cycle-example.ipynb b/docs/air-standard-brayton-cycle-example.ipynb index ae569c6..3c5b44a 100644 --- a/docs/air-standard-brayton-cycle-example.ipynb +++ b/docs/air-standard-brayton-cycle-example.ipynb @@ -16,7 +16,7 @@ "outputs": [], "source": [ "from thermostate import State, Q_, units\n", - "from numpy import arange\n", + "import numpy as np\n", "%matplotlib inline\n", "import matplotlib.pyplot as plt" ] @@ -205,10 +205,10 @@ "metadata": {}, "outputs": [], "source": [ - "eta_l = []\n", - "W_net_l = []\n", - "p_range = arange(p_low, p_high, 1)\n", - "for p_ratio in p_range:\n", + "p_range = np.linspace(p_low, p_high, 50)\n", + "eta_l = np.zeros(shape=p_range.shape) * units.dimensionless\n", + "W_net_l = np.zeros(shape=p_range.shape) * units.kJ / units.kg\n", + "for i, p_ratio in enumerate(p_range):\n", " s_2 = s_1\n", " p_2 = p_1*p_ratio\n", " st_2 = State(substance, p=p_2, s=s_2)\n", @@ -229,11 +229,11 @@ " W_c = h_1 - h_2\n", " W_t = h_3 - h_4\n", " W_net = W_c + W_t\n", - " W_net_l.append(W_net.magnitude)\n", + " W_net_l[i] = W_net\n", " \n", " Q_23 = h_3 - h_2\n", " eta = W_net/Q_23\n", - " eta_l.append(eta.magnitude)" + " eta_l[i] = eta" ] }, { @@ -279,7 +279,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.0" + "version": "3.8.1" } }, "nbformat": 4, diff --git a/docs/cold-air-brayton-cycle-example.ipynb b/docs/cold-air-brayton-cycle-example.ipynb index 1d30d43..5e8877e 100644 --- a/docs/cold-air-brayton-cycle-example.ipynb +++ b/docs/cold-air-brayton-cycle-example.ipynb @@ -16,7 +16,7 @@ "outputs": [], "source": [ "from thermostate import State, Q_, units\n", - "from numpy import arange\n", + "import numpy as np\n", "%matplotlib inline\n", "import matplotlib.pyplot as plt" ] @@ -224,19 +224,18 @@ "metadata": {}, "outputs": [], "source": [ - "T_range = arange(T_3_low.magnitude, T_3_high.magnitude, 10)\n", - "Wdot_net_l = []\n", - "eta_l = []\n", - "for T_3_val in T_range:\n", - " T_3 = Q_(T_3_val, 'K')\n", + "T_range = np.linspace(T_3_low, T_3_high, 200)\n", + "Wdot_net_l = np.zeros(T_range.shape) * units.kW\n", + "eta_l = np.zeros(T_range.shape) * units.dimensionless\n", + "for i, T_3 in enumerate(T_range):\n", " T_4 = T_3*(p_4/p_3)**((k - 1)/k)\n", " Wdot_t = (mdot*c_p*(T_3 - T_4)).to('kW')\n", " Wdot_net = Wdot_c + Wdot_t\n", - " Wdot_net_l.append(Wdot_net.magnitude)\n", + " Wdot_net_l[i] = Wdot_net\n", " \n", " Qdot_23 = (mdot*c_p*(T_3 - T_2)).to('kW')\n", " eta = Wdot_net/Qdot_23\n", - " eta_l.append(eta.magnitude)" + " eta_l[i] = eta" ] }, { @@ -282,7 +281,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.0" + "version": "3.8.1" } }, "nbformat": 4, diff --git a/docs/rankine-cycle-example.ipynb b/docs/rankine-cycle-example.ipynb index fac841a..59f2682 100644 --- a/docs/rankine-cycle-example.ipynb +++ b/docs/rankine-cycle-example.ipynb @@ -18,7 +18,7 @@ "from thermostate import State, Q_, units, SystemInternational as SI\n", "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", - "from numpy import linspace" + "import numpy as np" ] }, { @@ -314,7 +314,10 @@ "metadata": {}, "outputs": [], "source": [ - "for p_2 in linspace(p_low, p_high, 100):\n", + "p_range = np.linspace(p_low, p_high, 100)\n", + "y_values = np.zeros(shape=p_range.shape) * units.dimensionless\n", + "eta_values = np.zeros(shape=p_range.shape) * units.dimensionless\n", + "for i, p_2 in enumerate(p_range):\n", " # State 2\n", " s_2 = s_1\n", " st_2 = State(substance, p=p_2, s=s_2)\n", @@ -339,12 +342,12 @@ " h_7 = st_7.h\n", " \n", " y = (h_6 - h_5)/(h_2 - h_5)\n", - " y_values.append(y)\n", + " y_values[i] = y\n", "\n", " Wdot_net = (mdot_1*(h_1 - h_2 + (1 - y)*(h_2 - h_3) + (1 - y)*(h_4 - h_5) + (h_6 - h_7))).to('MW')\n", " Qdot_in = (mdot_1*(h_1 - h_7)).to('MW')\n", " eta = Wdot_net/Qdot_in\n", - " eta_values.append(eta)\n" + " eta_values[i] = eta" ] }, { @@ -388,7 +391,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.0" + "version": "3.8.1" } }, "nbformat": 4, diff --git a/setup.cfg b/setup.cfg index 4e43e90..baaa637 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,6 +20,7 @@ classifiers = Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 Operating System :: MacOS Operating System :: MacOS :: MacOS X Operating System :: Microsoft @@ -35,8 +36,9 @@ classifiers = packages = thermostate python_requires = ~=3.6 install_requires = - coolprop>=6.1.0,<6.4 - pint>=0.7.2,<0.12 + coolprop >=6.1.0,<6.4 + pint >=0.9,<0.12 + matplotlib >=2.0,<4.0 [options.extras_require] docs = @@ -46,13 +48,10 @@ docs = recommonmark >=0.6.0 ipython >=7.5.0 ipykernel >=5.1.0 - numpy >=1.17.0 - matplotlib >=2.0.0 testing = pytest >= 3.0.0 pytest-cov >= 2.5.1 - [flake8] max_line_length = 88 exclude = diff --git a/src/thermostate/thermostate.py b/src/thermostate/thermostate.py index 7698ae9..2efdfe7 100644 --- a/src/thermostate/thermostate.py +++ b/src/thermostate/thermostate.py @@ -15,6 +15,7 @@ units = UnitRegistry(autoconvert_offset_to_baseunit=True) Q_ = units.Quantity units.define(UnitDefinition("percent", "pct", (), ScaleConverter(1.0 / 100.0))) +units.setup_matplotlib() # Don't add the _render_traceback_ function to DimensionalityError if # IPython isn't present. This function is only used by the IPython/ipykernel From 0a3082624c9f920375086fa6c002d6afeecf76c3 Mon Sep 17 00:00:00 2001 From: "Bryan W. Weber" Date: Sat, 11 Apr 2020 22:06:05 -0400 Subject: [PATCH 3/5] Test example Jupyter Notebook files Execute the example Notebooks in the docs folder as part of the tests. Any warnings from Pint should hopefully become errors. --- .github/workflows/pythonpackage.yml | 19 +++++++++++++++++-- .gitignore | 3 ++- tox.ini | 21 ++++++++++++++++++--- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 6d6efaf..56dbe0d 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -15,7 +15,6 @@ on: jobs: build: - runs-on: ${{ matrix.os }} strategy: matrix: @@ -32,7 +31,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - python -m pip install --upgrade pip + python -m pip install --upgrade pip setuptools python -m pip install tox tox-gh-actions tox-venv - name: Test with tox run: tox -v @@ -42,6 +41,22 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} file: ./coverage.xml + notebooks: + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2 + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: Install test dependencies + run: | + python -m pip install --upgrade pip setuptools + python -m pip install tox tox-venv + - name: Run notebooks test + run: tox -v -e notebooks + flake8: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index 4e72051..6349f06 100644 --- a/.gitignore +++ b/.gitignore @@ -69,8 +69,9 @@ docs/_build/ # PyBuilder target/ -# IPython Notebook +# Jupyter Notebook .ipynb_checkpoints +docs/*.nbconvert.ipynb # pyenv .python-version diff --git a/tox.ini b/tox.ini index 75ed336..928e2ca 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py36, py37, py38, flake8 +envlist = py36, py37, py38, flake8, notebooks requires = tox-venv [gh-actions] @@ -25,8 +25,23 @@ download = true [testenv:py38] description = run the tests with pytest under {basepython}, allowing failures due to CoolProp -ignore_outcome = true -setenv = MACOSX_DEPLOYMENT_TARGET = 10.13 +[testenv:notebooks] +description = run the Notebooks in the docs folder +deps = + jupyter_client + nbconvert + ipykernel + matplotlib +basepython = python3.7 +download = true +commands = + jupyter nbconvert --to notebook --execute {toxinidir}/docs/*-example.ipynb + jupyter nbconvert --to notebook --execute --ExecutePreprocessor.allow_errors=True {toxinidir}/docs/Tutorial.ipynb +setenv = + # The options here turn the UnitStrippedWarning into an error + # The variables are set based on the answer: https://stackoverflow.com/a/53538033 + PYTHONWARNINGS=error::pint.UnitStrippedWarning + PYTHONPATH={envsitepackagesdir} [testenv:flake8] deps = From 3d105be2fe457260875205f6ffe2915455246f45 Mon Sep 17 00:00:00 2001 From: "Bryan W. Weber" Date: Sat, 11 Apr 2020 22:11:27 -0400 Subject: [PATCH 4/5] Build CoolProp so we can test on Python 3.8 CoolProp 6.3.0 from PyPI raises a TypeError due to some changes that since the release of Python 3.8. The master branch of CoolProp has the fixes, so build that and test ThermoState on Python 3.8. Don't do this on Windows, since Windows should be covered by the other Python versions, and I can't easily test compiling CoolProp on Windows right now. --- .github/workflows/pythonpackage.yml | 55 +++++++++++++++++++++++++++-- tox.ini | 4 ++- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 56dbe0d..1cb5449 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -18,7 +18,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.6, 3.7, 3.8] + python-version: [3.6, 3.7] os: [ubuntu-latest, macos-latest, windows-latest] fail-fast: false @@ -41,11 +41,62 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} file: ./coverage.xml + # CoolProp has to be built from the master branch for Python 3.8 + build-py38: + runs-on: ${{ matrix.os }} + strategy: + matrix: + # Don't want to figure out compiling CoolProp on Windows for tests. + # These OS should be good enough + os: [ubuntu-latest, macos-latest] + fail-fast: false + steps: + - name: Check out the repository + uses: actions/checkout@v2 + with: + path: main + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - name: Clone CoolProp + uses: actions/checkout@v2 + with: + repository: CoolProp/CoolProp + path: CoolProp + submodules: 'recursive' + - name: Install CoolProp dependencies + run: | + python -m pip install --upgrade pip setuptools + python -m pip install cython wheel + - name: Build CoolProp wheel + env: + MACOSX_DEPLOYMENT_TARGET: 10.13 + run: | + pushd CoolProp/wrappers/Python + python ../../dev/generate_headers.py + python setup.py bdist_wheel + popd + - name: Install test dependencies + run: | + python -m pip install tox tox-venv + - name: Test with tox + env: + CP_PACKAGE_DIST_DIR: ${{ github.workspace }}/CoolProp/wrappers/Python/dist + run: | + pushd main + tox -v -e py38 + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: main/coverage.xml + notebooks: runs-on: ubuntu-latest steps: - name: Check out the repository - uses: actions/checkout@v2 + uses: actions/checkout@v2 - name: Set up Python 3.7 uses: actions/setup-python@v1 with: diff --git a/tox.ini b/tox.ini index 928e2ca..5a6db0c 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,6 @@ requires = tox-venv python = 3.6: py36 3.7: py37 - 3.8: py38 [testenv] description = run the tests with pytest under {basepython} @@ -25,6 +24,9 @@ download = true [testenv:py38] description = run the tests with pytest under {basepython}, allowing failures due to CoolProp +install_command=python -m pip install --find-links={env:CP_PACKAGE_DIST_DIR:} {opts} {packages} +pip_pre = true + [testenv:notebooks] description = run the Notebooks in the docs folder deps = From 127de662d1e92ec8cd244622ea069ac1f505f00e Mon Sep 17 00:00:00 2001 From: "Bryan W. Weber" Date: Sun, 12 Apr 2020 13:20:55 -0400 Subject: [PATCH 5/5] Update CHANGELOG --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 808210a..ad749c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,18 +2,20 @@ All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](http://keepachangelog.com/) -and this project adheres to [Semantic Versioning](http://semver.org/). +The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added +- Build CoolProp and run the tests on Python 3.8 +- Set up the Matplotlib functionality built into Pint. This bumps the minimum Pint version to 0.9 and adds Matplotlib as a dependency ### Changed - Updated documentation links in README and conda recipe to ReadTheDocs ### Fixed +- The Rankine cycle example had a dimensionality error due to better NumPy support in Pint. Fixes #24. ### Removed @@ -23,7 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Use `setup.cfg` and `pyproject.toml` for PEP 517 compliance ### Changed -- Switch to src directory source layout +- Switch to `src` directory source layout - Move tests outside of the package - Apply Black formatter to tests - Use tox to test against multiple Python versions