pyEQL 0.13.0__tar.gz → 0.14.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. pyEQL-0.14.0/.github/workflows/post-process.yml +62 -0
  2. pyEQL-0.14.0/.github/workflows/release.yml +60 -0
  3. {pyEQL-0.13.0 → pyEQL-0.14.0}/.github/workflows/testing.yaml +19 -13
  4. {pyEQL-0.13.0 → pyEQL-0.14.0}/CHANGELOG.md +19 -3
  5. {pyEQL-0.13.0/src/pyEQL.egg-info → pyEQL-0.14.0}/PKG-INFO +1 -1
  6. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/changelog.md +19 -3
  7. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/engines.md +10 -0
  8. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/installation.md +10 -3
  9. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/macos-latest_py3.10.txt +1 -1
  10. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/macos-latest_py3.10_extras.txt +1 -1
  11. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/macos-latest_py3.11.txt +1 -1
  12. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/macos-latest_py3.11_extras.txt +1 -1
  13. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/macos-latest_py3.12.txt +1 -1
  14. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/macos-latest_py3.12_extras.txt +1 -1
  15. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/macos-latest_py3.9.txt +1 -1
  16. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/macos-latest_py3.9_extras.txt +1 -1
  17. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.10.txt +1 -1
  18. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.10_extras.txt +1 -1
  19. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.11.txt +1 -1
  20. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.11_extras.txt +1 -1
  21. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.12.txt +1 -1
  22. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.12_extras.txt +1 -1
  23. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.9.txt +1 -1
  24. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.9_extras.txt +1 -1
  25. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/windows-latest_py3.10.txt +1 -1
  26. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/windows-latest_py3.10_extras.txt +1 -1
  27. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/windows-latest_py3.11.txt +1 -1
  28. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/windows-latest_py3.11_extras.txt +1 -1
  29. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/windows-latest_py3.12.txt +1 -1
  30. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/windows-latest_py3.12_extras.txt +1 -1
  31. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/windows-latest_py3.9.txt +1 -1
  32. {pyEQL-0.13.0 → pyEQL-0.14.0}/requirements/windows-latest_py3.9_extras.txt +1 -1
  33. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/engines.py +9 -1
  34. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/solution.py +1 -1
  35. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/utils.py +5 -0
  36. {pyEQL-0.13.0 → pyEQL-0.14.0/src/pyEQL.egg-info}/PKG-INFO +1 -1
  37. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_activity.py +2 -0
  38. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_functions.py +3 -0
  39. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_phreeqc.py +4 -0
  40. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_salt_matching.py +3 -0
  41. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_solution.py +5 -0
  42. pyEQL-0.13.0/.github/workflows/post-process.yml +0 -29
  43. pyEQL-0.13.0/.github/workflows/release.yml +0 -42
  44. {pyEQL-0.13.0 → pyEQL-0.14.0}/.coveragerc +0 -0
  45. {pyEQL-0.13.0 → pyEQL-0.14.0}/.gitattributes +0 -0
  46. {pyEQL-0.13.0 → pyEQL-0.14.0}/.github/dependabot.yml +0 -0
  47. {pyEQL-0.13.0 → pyEQL-0.14.0}/.github/pull_request_template.md +0 -0
  48. {pyEQL-0.13.0 → pyEQL-0.14.0}/.github/release.yml +0 -0
  49. {pyEQL-0.13.0 → pyEQL-0.14.0}/.github/workflows/upgrade_dependencies.yml +0 -0
  50. {pyEQL-0.13.0 → pyEQL-0.14.0}/.gitignore +0 -0
  51. {pyEQL-0.13.0 → pyEQL-0.14.0}/.pre-commit-config.yaml +0 -0
  52. {pyEQL-0.13.0 → pyEQL-0.14.0}/.readthedocs.yml +0 -0
  53. {pyEQL-0.13.0 → pyEQL-0.14.0}/AUTHORS.md +0 -0
  54. {pyEQL-0.13.0 → pyEQL-0.14.0}/COPYING +0 -0
  55. {pyEQL-0.13.0 → pyEQL-0.14.0}/LICENSE.txt +0 -0
  56. {pyEQL-0.13.0 → pyEQL-0.14.0}/MANIFEST.in +0 -0
  57. {pyEQL-0.13.0 → pyEQL-0.14.0}/README.md +0 -0
  58. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/Makefile +0 -0
  59. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/_static/.gitignore +0 -0
  60. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/amounts.md +0 -0
  61. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/arithmetic.md +0 -0
  62. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/authors.md +0 -0
  63. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/chemistry.md +0 -0
  64. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/class_solution.md +0 -0
  65. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/conf.py +0 -0
  66. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/contributing.md +0 -0
  67. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/creating.md +0 -0
  68. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/database.md +0 -0
  69. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/examples/.ipynb_checkpoints/pyEQL_demo_1-checkpoint.ipynb +0 -0
  70. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/examples/.ipynb_checkpoints/pyeql_demo-checkpoint.ipynb +0 -0
  71. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/examples/.ipynb_checkpoints/pyeql_tutorial_database-checkpoint.ipynb +0 -0
  72. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/examples/.ipynb_checkpoints/pyeql_tutorial_osmotic_pressure-checkpoint.ipynb +0 -0
  73. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/examples/.ipynb_checkpoints/speedup-checkpoint.ipynb +0 -0
  74. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/examples/pyEQL_demo_1.ipynb +0 -0
  75. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/examples/pyeql_demo.ipynb +0 -0
  76. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/examples/pyeql_tutorial_database.ipynb +0 -0
  77. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/examples/pyeql_tutorial_osmotic_pressure.ipynb +0 -0
  78. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/examples/speedup.ipynb +0 -0
  79. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/index.md +0 -0
  80. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/internal.md +0 -0
  81. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/license.md +0 -0
  82. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/mixing.md +0 -0
  83. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/quickstart.md +0 -0
  84. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/readme.md +0 -0
  85. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/requirements.txt +0 -0
  86. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/serialization.md +0 -0
  87. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/tutorials.md +0 -0
  88. {pyEQL-0.13.0 → pyEQL-0.14.0}/docs/units.md +0 -0
  89. {pyEQL-0.13.0 → pyEQL-0.14.0}/pyeql-logo.png +0 -0
  90. {pyEQL-0.13.0 → pyEQL-0.14.0}/pyeql-logo.svg +0 -0
  91. {pyEQL-0.13.0 → pyEQL-0.14.0}/pyproject.toml +0 -0
  92. {pyEQL-0.13.0 → pyEQL-0.14.0}/setup.cfg +0 -0
  93. {pyEQL-0.13.0 → pyEQL-0.14.0}/setup.py +0 -0
  94. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/__init__.py +0 -0
  95. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/activity_correction.py +0 -0
  96. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/database/geothermal.dat +0 -0
  97. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/database/llnl.dat +0 -0
  98. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/database/phreeqc_license.txt +0 -0
  99. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/database/pyeql_db.json +0 -0
  100. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/equilibrium.py +0 -0
  101. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/functions.py +0 -0
  102. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/logging_system.py +0 -0
  103. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/pint_custom_units.txt +0 -0
  104. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/presets/Ringers lactate.yaml +0 -0
  105. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/presets/normal saline.yaml +0 -0
  106. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/presets/rainwater.yaml +0 -0
  107. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/presets/seawater.yaml +0 -0
  108. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/presets/urine.yaml +0 -0
  109. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/presets/wastewater.yaml +0 -0
  110. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/salt_ion_match.py +0 -0
  111. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL/solute.py +0 -0
  112. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL.egg-info/SOURCES.txt +0 -0
  113. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL.egg-info/dependency_links.txt +0 -0
  114. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL.egg-info/not-zip-safe +0 -0
  115. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL.egg-info/requires.txt +0 -0
  116. {pyEQL-0.13.0 → pyEQL-0.14.0}/src/pyEQL.egg-info/top_level.txt +0 -0
  117. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/conftest.py +0 -0
  118. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_bulk_properties.py +0 -0
  119. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_debye_length.py +0 -0
  120. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_density.py +0 -0
  121. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_dielectric.py +0 -0
  122. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_effective_pitzer.py +0 -0
  123. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_equilibrium.py +0 -0
  124. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_mixed_electrolyte_activity.py +0 -0
  125. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_osmotic_coeff.py +0 -0
  126. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_solute.py +0 -0
  127. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_solute_properties.py +0 -0
  128. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_utils.py +0 -0
  129. {pyEQL-0.13.0 → pyEQL-0.14.0}/tests/test_volume_concentration.py +0 -0
  130. {pyEQL-0.13.0 → pyEQL-0.14.0}/tox.ini +0 -0
@@ -0,0 +1,62 @@
1
+ name: Post-merge
2
+
3
+ # triggered when PRs are merged into main branch
4
+ on:
5
+ pull_request:
6
+ branches:
7
+ - main
8
+ types:
9
+ - closed
10
+
11
+ jobs:
12
+ lint:
13
+ if: github.event.pull_request.merged == true
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ max-parallel: 1
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ with:
20
+ fetch-depth: 0
21
+ - name: Set up Python
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: 3.11
25
+ cache: pip
26
+ - name: Run pre-commit
27
+ run: |
28
+ pip install pre-commit
29
+ pre-commit run
30
+
31
+ test-comprehensive:
32
+ needs: lint
33
+ strategy:
34
+ max-parallel: 6
35
+ matrix:
36
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
37
+ os:
38
+ - ubuntu-latest
39
+ - macos-latest
40
+ - windows-latest
41
+ - macos-14
42
+ exclude:
43
+ - os: macos-14
44
+ python-version: "3.9"
45
+ runs-on: ${{ matrix.os }}
46
+ steps:
47
+ - uses: actions/checkout@v4
48
+ - name: Setup Python
49
+ uses: actions/setup-python@v5
50
+ with:
51
+ python-version: ${{ matrix.python-version }}${{ matrix.dev }}
52
+ - name: Install test requirements
53
+ run: |
54
+ python -m pip install --upgrade pip
55
+ pip install -e ".[testing]"
56
+ - name: Run tests
57
+ run: |
58
+ pytest --cov=src/pyEQL --cov-report=xml
59
+ - uses: codecov/codecov-action@v4
60
+ with:
61
+ token: ${{ secrets.CODECOV_TOKEN }}
62
+ file: ./coverage.xml
@@ -0,0 +1,60 @@
1
+ name: release
2
+
3
+ # runs when the Post merge workflow completes successfully
4
+ # tags new release and pushes to PyPi
5
+ # see https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run
6
+ on:
7
+ workflow_run:
8
+ workflows: [Post merge]
9
+ types: [completed]
10
+
11
+ workflow_dispatch:
12
+
13
+ jobs:
14
+ # https://github.com/marketplace/actions/tag-release-on-push-action
15
+ tag-release:
16
+ name: Tag/Release on merge of labeled PRs
17
+ runs-on: ubuntu-latest
18
+ env:
19
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
20
+ steps:
21
+ - uses: rymndhng/release-on-push-action@v0.28.0
22
+ with:
23
+ # loonly release when PRs with release:major/minor/patch labels are merged
24
+ bump_version_scheme: norelease
25
+ use_github_release_notes: true
26
+ release_body: "See [CHANGELOG](https://github.com/KingsburyLab/pyEQL/blob/main/CHANGELOG.md) for a detailed explanation of changes."
27
+
28
+ # https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes
29
+
30
+ deploy:
31
+ name: Upload release to PyPI
32
+ runs-on: ubuntu-latest
33
+ needs: tag-release
34
+ environment:
35
+ name: publish
36
+ url: https://pypi.org/p/pyEQL
37
+
38
+ permissions:
39
+ id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
40
+
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+ with:
44
+ fetch-depth: 0
45
+
46
+ - uses: actions/setup-python@v5
47
+ with:
48
+ python-version: "3.10"
49
+
50
+ - name: Install dependencies
51
+ run: |
52
+ python -m pip install --upgrade pip
53
+ pip install setuptools setuptools_scm wheel tox
54
+
55
+ - name: Build packages for distribution
56
+ run: |
57
+ tox -e clean,build
58
+
59
+ - name: Upload to PyPi
60
+ uses: pypa/gh-action-pypi-publish@v1.8.12
@@ -1,17 +1,12 @@
1
1
  name: testing
2
2
 
3
3
  on:
4
- push:
4
+ pull_request:
5
5
  branches:
6
6
  - main
7
7
  paths-ignore:
8
- - 'docs/'
9
8
  - CHANGELOG.md
10
9
 
11
- pull_request:
12
- branches:
13
- - main
14
-
15
10
  concurrency:
16
11
  group: ${{ github.workflow }}-${{ github.ref }}
17
12
  cancel-in-progress: true
@@ -40,18 +35,29 @@ jobs:
40
35
  strategy:
41
36
  max-parallel: 6
42
37
  matrix:
43
- python-version: ["3.9", "3.10", "3.11", "3.12"]
44
- platform:
45
- - ubuntu-latest
46
- - macos-latest
47
- - windows-latest
48
- runs-on: ubuntu-latest
38
+ # for most PRs, test the min and max supported python on every platform, test all python on ubuntu
39
+ python-version: ["3.9", "3.12"]
40
+ os:
41
+ - ubuntu-latest
42
+ - macos-latest
43
+ - macos-14
44
+ - windows-latest
45
+ include:
46
+ - os: ubuntu-latest
47
+ python-version: "3.10"
48
+ - os: ubuntu-latest
49
+ python-version: "3.11"
50
+ # no python 3.9 on the macos-14 runner
51
+ exclude:
52
+ - os: macos-14
53
+ python-version: "3.9"
54
+ runs-on: ${{ matrix.os }}
49
55
  steps:
50
56
  - uses: actions/checkout@v4
51
57
  - name: Setup Python
52
58
  uses: actions/setup-python@v5
53
59
  with:
54
- python-version: ${{ matrix.python }}${{ matrix.dev }}
60
+ python-version: ${{ matrix.python-version }}${{ matrix.dev }}
55
61
  - name: Install test requirements
56
62
  run: |
57
63
  python -m pip install --upgrade pip
@@ -5,17 +5,33 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## Unreleased
8
+ ## [0.14.0] - 2024-03-05
9
9
 
10
- ## [0.13.0] - 2024-03-05
10
+ ### Added
11
+
12
+ - `NativeEOS` / `PhreeqcEOS`: Added `try`/`catch` so that `pyEQL` can still be used on platforms that PHREEQC does
13
+ not support, such as Apple Silicon. In such cases, functions like `equilibrate` that depend on PHREEQC will
14
+ raise errors, but everything else can still be used.
15
+ - CI: Added Apple M1 runner (GitHub: `macos-14`) to the CI tests.
11
16
 
12
17
  ### Fixed
13
18
 
14
- - `equilibrium.alpha()`: Fixed incorrect calculation of acid-base distribution coefficient for multiprotic acids.
19
+ - CI: Addressed several issues in the testing configuration which had resulted in testing
20
+ fewer operating systems x python version combinations than intended. CI tests now
21
+ correctly and comprehensively test every supported version of python on every os
22
+ (macos, windows, ubuntu).
23
+ - `utils.FormulaDict`: implemented `__contains__` so that `get()` works correctly in
24
+ python 3.12+. See https://github.com/python/cpython/issues/105524
15
25
  - Docs: fixed many small problems in documentation causing equations and examples to
16
26
  render incorrectly.
17
27
  - `Solution.from_file`: Add missing `@classmethod` decorator; update documentation.
18
28
 
29
+ ## [0.13.0] - 2024-03-05
30
+
31
+ ### Fixed
32
+
33
+ - `equilibrium.alpha()`: Fixed incorrect calculation of acid-base distribution coefficient for multiprotic acids.
34
+
19
35
  ## [0.12.2] - 2024-02-25
20
36
 
21
37
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyEQL
3
- Version: 0.13.0
3
+ Version: 0.14.0
4
4
  Summary: Python tools for solution chemistry
5
5
  Home-page: https://github.com/KingsburyLab/pyEQL
6
6
  Author: Ryan Kingsbury
@@ -5,17 +5,33 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## Unreleased
8
+ ## [0.14.0] - 2024-03-05
9
9
 
10
- ## [0.13.0] - 2024-03-05
10
+ ### Added
11
+
12
+ - `NativeEOS` / `PhreeqcEOS`: Added `try`/`catch` so that `pyEQL` can still be used on platforms that PHREEQC does
13
+ not support, such as Apple Silicon. In such cases, functions like `equilibrate` that depend on PHREEQC will
14
+ raise errors, but everything else can still be used.
15
+ - CI: Added Apple M1 runner (GitHub: `macos-14`) to the CI tests.
11
16
 
12
17
  ### Fixed
13
18
 
14
- - `equilibrium.alpha()`: Fixed incorrect calculation of acid-base distribution coefficient for multiprotic acids.
19
+ - CI: Addressed several issues in the testing configuration which had resulted in testing
20
+ fewer operating systems x python version combinations than intended. CI tests now
21
+ correctly and comprehensively test every supported version of python on every os
22
+ (macos, windows, ubuntu).
23
+ - `utils.FormulaDict`: implemented `__contains__` so that `get()` works correctly in
24
+ python 3.12+. See https://github.com/python/cpython/issues/105524
15
25
  - Docs: fixed many small problems in documentation causing equations and examples to
16
26
  render incorrectly.
17
27
  - `Solution.from_file`: Add missing `@classmethod` decorator; update documentation.
18
28
 
29
+ ## [0.13.0] - 2024-03-05
30
+
31
+ ### Fixed
32
+
33
+ - `equilibrium.alpha()`: Fixed incorrect calculation of acid-base distribution coefficient for multiprotic acids.
34
+
19
35
  ## [0.12.2] - 2024-02-25
20
36
 
21
37
  ### Fixed
@@ -21,6 +21,16 @@ as needed to particular use cases.
21
21
  `pyEQL` currently supports three modeling engines: `ideal`, `native`, and `phreeqc`, which
22
22
  are selected via the `engine` kwarg to `Solution.__init__()`. Each engine is briefly described below.
23
23
 
24
+ ```{warning}
25
+ If you are using a Mac with an Apple M1, M2, etc. chip (i.e., Arm64 architecture), some features of `pyEQL` will be
26
+ unavailable. Specifically, anything which depends on PHREEQC (e.g., the
27
+ `equilibrate` method in the native engine and the entire
28
+ [`phreeqc` engine](engines.md#the-phreeqc-engine)) will not work. This is because `phreeqpython` is currently
29
+ not available for this platform. All other functions of `pyEQL` should work as expected.
30
+
31
+ Feel free to post your experiences or proposed solutions at https://github.com/KingsburyLab/pyEQL/issues/109
32
+ ```
33
+
24
34
  ## The `'native'` engine (Default)
25
35
 
26
36
  The `native` engine is the default choice and was the only option available prior to
@@ -33,22 +33,29 @@ You can tell if this is the case by typing the following command:
33
33
 
34
34
  ```
35
35
 
36
+ ```
36
37
  $ python --version
37
38
  Python 2.7.12
38
-
39
39
  ```
40
40
 
41
41
  This means Python 2.x is installed. If you run 'pip install' it will point to the Python 2.7 installation, but pyEQL
42
42
  only works on Python 3. So, try this:
43
43
 
44
44
  ```
45
-
46
45
  $ python3 --version
47
46
  Python 3.9.7
48
-
49
47
  ```
50
48
 
51
49
  To get to Python 3.x, you have to type 'python3'. In this case, you would run 'pip3 install'
50
+
51
+ ```{warning}
52
+ If you are using a Mac with an Apple M1, M2, etc. chip (i.e., Arm64 architecture), some features of `pyEQL` will be
53
+ unavailable. Specifically, anything which depends on PHREEQC (e.g., the
54
+ `equilibrate` method in the native engine and the entire
55
+ [`phreeqc` engine](engines.md)) will not work. This is because `phreeqpython` is currently
56
+ not available for this platform. All other functions of `pyEQL` should work as expected.
57
+
58
+ Feel free to post your experiences or proposed solutions at https://github.com/KingsburyLab/pyEQL/issues/109
52
59
  ```
53
60
 
54
61
  ## Other dependencies
@@ -117,7 +117,7 @@ numpy==1.26.4
117
117
  # pymatgen
118
118
  # scipy
119
119
  # spglib
120
- orjson==3.9.14
120
+ orjson==3.9.15
121
121
  # via maggma
122
122
  packaging==23.2
123
123
  # via
@@ -171,7 +171,7 @@ numpy==1.26.4
171
171
  # pymatgen
172
172
  # scipy
173
173
  # spglib
174
- orjson==3.9.14
174
+ orjson==3.9.15
175
175
  # via maggma
176
176
  packaging==23.2
177
177
  # via
@@ -115,7 +115,7 @@ numpy==1.26.4
115
115
  # pymatgen
116
116
  # scipy
117
117
  # spglib
118
- orjson==3.9.14
118
+ orjson==3.9.15
119
119
  # via maggma
120
120
  packaging==23.2
121
121
  # via
@@ -167,7 +167,7 @@ numpy==1.26.4
167
167
  # pymatgen
168
168
  # scipy
169
169
  # spglib
170
- orjson==3.9.14
170
+ orjson==3.9.15
171
171
  # via maggma
172
172
  packaging==23.2
173
173
  # via
@@ -115,7 +115,7 @@ numpy==1.26.4
115
115
  # pymatgen
116
116
  # scipy
117
117
  # spglib
118
- orjson==3.9.14
118
+ orjson==3.9.15
119
119
  # via maggma
120
120
  packaging==23.2
121
121
  # via
@@ -167,7 +167,7 @@ numpy==1.26.4
167
167
  # pymatgen
168
168
  # scipy
169
169
  # spglib
170
- orjson==3.9.14
170
+ orjson==3.9.15
171
171
  # via maggma
172
172
  packaging==23.2
173
173
  # via
@@ -121,7 +121,7 @@ numpy==1.26.4
121
121
  # pymatgen
122
122
  # scipy
123
123
  # spglib
124
- orjson==3.9.14
124
+ orjson==3.9.15
125
125
  # via maggma
126
126
  packaging==23.2
127
127
  # via
@@ -177,7 +177,7 @@ numpy==1.26.4
177
177
  # pymatgen
178
178
  # scipy
179
179
  # spglib
180
- orjson==3.9.14
180
+ orjson==3.9.15
181
181
  # via maggma
182
182
  packaging==23.2
183
183
  # via
@@ -117,7 +117,7 @@ numpy==1.26.4
117
117
  # pymatgen
118
118
  # scipy
119
119
  # spglib
120
- orjson==3.9.14
120
+ orjson==3.9.15
121
121
  # via maggma
122
122
  packaging==23.2
123
123
  # via
@@ -171,7 +171,7 @@ numpy==1.26.4
171
171
  # pymatgen
172
172
  # scipy
173
173
  # spglib
174
- orjson==3.9.14
174
+ orjson==3.9.15
175
175
  # via maggma
176
176
  packaging==23.2
177
177
  # via
@@ -115,7 +115,7 @@ numpy==1.26.4
115
115
  # pymatgen
116
116
  # scipy
117
117
  # spglib
118
- orjson==3.9.14
118
+ orjson==3.9.15
119
119
  # via maggma
120
120
  packaging==23.2
121
121
  # via
@@ -167,7 +167,7 @@ numpy==1.26.4
167
167
  # pymatgen
168
168
  # scipy
169
169
  # spglib
170
- orjson==3.9.14
170
+ orjson==3.9.15
171
171
  # via maggma
172
172
  packaging==23.2
173
173
  # via
@@ -115,7 +115,7 @@ numpy==1.26.4
115
115
  # pymatgen
116
116
  # scipy
117
117
  # spglib
118
- orjson==3.9.14
118
+ orjson==3.9.15
119
119
  # via maggma
120
120
  packaging==23.2
121
121
  # via
@@ -167,7 +167,7 @@ numpy==1.26.4
167
167
  # pymatgen
168
168
  # scipy
169
169
  # spglib
170
- orjson==3.9.14
170
+ orjson==3.9.15
171
171
  # via maggma
172
172
  packaging==23.2
173
173
  # via
@@ -121,7 +121,7 @@ numpy==1.26.4
121
121
  # pymatgen
122
122
  # scipy
123
123
  # spglib
124
- orjson==3.9.14
124
+ orjson==3.9.15
125
125
  # via maggma
126
126
  packaging==23.2
127
127
  # via
@@ -177,7 +177,7 @@ numpy==1.26.4
177
177
  # pymatgen
178
178
  # scipy
179
179
  # spglib
180
- orjson==3.9.14
180
+ orjson==3.9.15
181
181
  # via maggma
182
182
  packaging==23.2
183
183
  # via
@@ -121,7 +121,7 @@ numpy==1.26.4
121
121
  # pymatgen
122
122
  # scipy
123
123
  # spglib
124
- orjson==3.9.14
124
+ orjson==3.9.15
125
125
  # via maggma
126
126
  packaging==23.2
127
127
  # via
@@ -178,7 +178,7 @@ numpy==1.26.4
178
178
  # pymatgen
179
179
  # scipy
180
180
  # spglib
181
- orjson==3.9.14
181
+ orjson==3.9.15
182
182
  # via maggma
183
183
  packaging==23.2
184
184
  # via
@@ -119,7 +119,7 @@ numpy==1.26.4
119
119
  # pymatgen
120
120
  # scipy
121
121
  # spglib
122
- orjson==3.9.14
122
+ orjson==3.9.15
123
123
  # via maggma
124
124
  packaging==23.2
125
125
  # via
@@ -174,7 +174,7 @@ numpy==1.26.4
174
174
  # pymatgen
175
175
  # scipy
176
176
  # spglib
177
- orjson==3.9.14
177
+ orjson==3.9.15
178
178
  # via maggma
179
179
  packaging==23.2
180
180
  # via
@@ -119,7 +119,7 @@ numpy==1.26.4
119
119
  # pymatgen
120
120
  # scipy
121
121
  # spglib
122
- orjson==3.9.14
122
+ orjson==3.9.15
123
123
  # via maggma
124
124
  packaging==23.2
125
125
  # via
@@ -174,7 +174,7 @@ numpy==1.26.4
174
174
  # pymatgen
175
175
  # scipy
176
176
  # spglib
177
- orjson==3.9.14
177
+ orjson==3.9.15
178
178
  # via maggma
179
179
  packaging==23.2
180
180
  # via
@@ -125,7 +125,7 @@ numpy==1.26.4
125
125
  # pymatgen
126
126
  # scipy
127
127
  # spglib
128
- orjson==3.9.14
128
+ orjson==3.9.15
129
129
  # via maggma
130
130
  packaging==23.2
131
131
  # via
@@ -184,7 +184,7 @@ numpy==1.26.4
184
184
  # pymatgen
185
185
  # scipy
186
186
  # spglib
187
- orjson==3.9.14
187
+ orjson==3.9.15
188
188
  # via maggma
189
189
  packaging==23.2
190
190
  # via
@@ -159,7 +159,15 @@ class NativeEOS(EOS):
159
159
  Path(os.path.dirname(__file__)) / "database" if self.phreeqc_db in ["llnl.dat", "geothermal.dat"] else None
160
160
  )
161
161
  # create the PhreeqcPython instance
162
- self.pp = PhreeqPython(database=self.phreeqc_db, database_directory=self.db_path)
162
+ # try/except added to catch unsupported architectures, such as Apple Silicon
163
+ try:
164
+ self.pp = PhreeqPython(database=self.phreeqc_db, database_directory=self.db_path)
165
+ except OSError:
166
+ logger.error(
167
+ "OSError encountered when trying to instantiate phreeqpython. Most likely this means you"
168
+ " are running on an architecture that is not supported by PHREEQC, such as Apple M1/M2 chips."
169
+ " pyEQL will work, but equilibrate() will have no effect."
170
+ )
163
171
  # attributes to hold the PhreeqPython solution.
164
172
  self.ppsol = None
165
173
  # store the solution composition to see whether we need to re-instantiate the solution
@@ -169,7 +169,7 @@ class Solution(MSONable):
169
169
  if database is None:
170
170
  # load the default database, which is a JSONStore
171
171
  db_store = IonDB
172
- elif isinstance(database, str | Path):
172
+ elif isinstance(database, (str, Path)):
173
173
  db_store = JSONStore(str(database), key="formula")
174
174
  logger.info(f"Created maggma JSONStore from .json file {database}")
175
175
  else:
@@ -99,5 +99,10 @@ class FormulaDict(UserDict):
99
99
  # sort contents anytime an item is set
100
100
  self.data = dict(sorted(self.items(), key=lambda x: x[1], reverse=True))
101
101
 
102
+ # Necessary to define this so that .get() works properly in python 3.12+
103
+ # see https://github.com/python/cpython/issues/105524
104
+ def __contains__(self, key):
105
+ return standardize_formula(key) in self.data
106
+
102
107
  def __delitem__(self, key):
103
108
  super().__delitem__(standardize_formula(key))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyEQL
3
- Version: 0.13.0
3
+ Version: 0.14.0
4
4
  Summary: Python tools for solution chemistry
5
5
  Home-page: https://github.com/KingsburyLab/pyEQL
6
6
  Author: Ryan Kingsbury
@@ -10,6 +10,7 @@ data rather than the theoretical result of the respective functions. In some
10
10
  cases, the output is also tested against a well-established model published
11
11
  by USGS(PHREEQC)
12
12
  """
13
+ import platform
13
14
 
14
15
  import numpy as np
15
16
  import pytest
@@ -280,6 +281,7 @@ def test_activity_crc_rbcl():
280
281
  assert np.isclose(result, expected, rtol=0.05)
281
282
 
282
283
 
284
+ @pytest.mark.skipif(platform.machine() == "arm64" and platform.system() == "Darwin", reason="arm64 not supported")
283
285
  def test_activity_crc_MgCl2():
284
286
  """
285
287
  calculate the activity coefficient of MgCl2 at each concentration and compare
@@ -2,6 +2,8 @@
2
2
  Tests of pyEQL.functions module
3
3
 
4
4
  """
5
+ import platform
6
+
5
7
  import numpy as np
6
8
  import pytest
7
9
 
@@ -39,6 +41,7 @@ def s2_i():
39
41
  return Solution({"Na+": "1 mol/L", "Cl-": "1 mol/L"}, volume="10 L", engine="ideal")
40
42
 
41
43
 
44
+ @pytest.mark.skipif(platform.machine() == "arm64" and platform.system() == "Darwin", reason="arm64 not supported")
42
45
  def test_mixing_functions(s1, s2, s1_p, s2_p, s1_i, s2_i):
43
46
  # mixing energy and entropy of any solution with itself should be zero
44
47
  assert np.isclose(gibbs_mix(s1, s1).magnitude, 0)
@@ -7,6 +7,7 @@ used by pyEQL's Solution class
7
7
  """
8
8
 
9
9
  import logging
10
+ import platform
10
11
 
11
12
  import numpy as np
12
13
  import pytest
@@ -14,6 +15,9 @@ import pytest
14
15
  from pyEQL import Solution
15
16
  from pyEQL.engines import PhreeqcEOS
16
17
 
18
+ if platform.machine().startswith("arm64") and platform.system().startswith("Darwin"):
19
+ pytest.skip("skipping PHREEQC tests because arm64 is not supported", allow_module_level=True)
20
+
17
21
 
18
22
  @pytest.fixture()
19
23
  def s1():
@@ -5,8 +5,10 @@ pyEQL salt matching test suite
5
5
  This file contains tests for the salt-matching algorithm used by pyEQL in
6
6
  salt_ion_match.py
7
7
  """
8
+ import platform
8
9
 
9
10
  import numpy as np
11
+ import pytest
10
12
 
11
13
  import pyEQL
12
14
  from pyEQL.salt_ion_match import Salt
@@ -97,6 +99,7 @@ def test_single_ion():
97
99
  assert s1.get_salt().nu_anion == 3
98
100
 
99
101
 
102
+ @pytest.mark.skipif(platform.machine() == "arm64" and platform.system() == "Darwin", reason="arm64 not supported")
100
103
  def test_salt_with_equilibration():
101
104
  """
102
105
  test matching a solution containing a salt, before and after equilibration.
@@ -8,6 +8,7 @@ used by pyEQL's Solution class
8
8
 
9
9
  import copy
10
10
  import os
11
+ import platform
11
12
  from pathlib import Path
12
13
 
13
14
  import numpy as np
@@ -366,6 +367,8 @@ def test_components_by_element(s1, s2):
366
367
  "Na(1.0)": ["Na[+1]"],
367
368
  "Cl(-1.0)": ["Cl[-1]"],
368
369
  }
370
+ if platform.machine() == "arm64" and platform.system() == "Darwin":
371
+ pytest.skip(reason="arm64 not supported")
369
372
  s2.equilibrate()
370
373
  assert s2.get_components_by_element() == {
371
374
  "H(1.0)": ["H2O(aq)", "OH[-1]", "H[+1]", "HCl(aq)", "NaOH(aq)", "HClO(aq)", "HClO2(aq)"],
@@ -401,6 +404,7 @@ def test_get_total_amount(s2):
401
404
  assert np.isclose(sox.get_total_amount("Fe", "mol").magnitude, 0.05)
402
405
 
403
406
 
407
+ @pytest.mark.skipif(platform.machine() == "arm64" and platform.system() == "Darwin", reason="arm64 not supported")
404
408
  def test_equilibrate(s1, s2, s5_pH):
405
409
  assert "H2(aq)" not in s1.components
406
410
  orig_pH = s1.pH
@@ -505,6 +509,7 @@ def test_conductivity(s1, s2):
505
509
  assert np.isclose(s_kcl.conductivity.magnitude, 10.862, atol=0.45)
506
510
 
507
511
 
512
+ @pytest.mark.skipif(platform.machine() == "arm64" and platform.system() == "Darwin", reason="arm64 not supported")
508
513
  def test_arithmetic_and_copy(s2, s6):
509
514
  s6_scale = copy.deepcopy(s6)
510
515
  s6_scale *= 1.5
@@ -1,29 +0,0 @@
1
- name: Post-process
2
-
3
- on:
4
- workflow_run:
5
- branches:
6
- - main
7
- types:
8
- - completed
9
- workflows:
10
- # List all required workflow names here.
11
- - 'testing'
12
-
13
- jobs:
14
- # https://github.com/marketplace/actions/tag-release-on-push-action
15
- auto-gen-release:
16
- name: Tag/Release on merge of labeled PRs
17
- runs-on: ubuntu-latest
18
- env:
19
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
20
- steps:
21
- - # It is often a desired behavior to merge only when a workflow execution
22
- # succeeds. This can be changed as needed.
23
- if: ${{ github.event.workflow_run.conclusion == 'success' }}
24
- uses: rymndhng/release-on-push-action@v0.28.0
25
- with:
26
- # loonly release when PRs with release:major/minor/patch labels are merged
27
- bump_version_scheme: norelease
28
- use_github_release_notes: true
29
- release_body: "See [CHANGELOG](https://github.com/KingsburyLab/pyEQL/blob/main/CHANGELOG.md) for a detailed explanation of changes."
@@ -1,42 +0,0 @@
1
- name: release
2
-
3
- on:
4
- release:
5
- types: [published]
6
-
7
- workflow_dispatch:
8
-
9
- jobs:
10
- # https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes
11
-
12
- deploy:
13
- name: Upload release to PyPI
14
- runs-on: ubuntu-latest
15
- environment:
16
- name: publish
17
- url: https://pypi.org/p/pyEQL
18
-
19
- permissions:
20
- id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
21
-
22
- steps:
23
- - uses: actions/checkout@v4
24
- with:
25
- fetch-depth: 0
26
-
27
- - uses: actions/setup-python@v5
28
- with:
29
- python-version: "3.10"
30
-
31
- - name: Install dependencies
32
- run: |
33
- python -m pip install --upgrade pip
34
- pip install setuptools setuptools_scm wheel tox
35
-
36
- - name: Build packages for distribution
37
- run: |
38
- tox -e clean,build
39
-
40
- - name: Upload to PyPi
41
- uses: pypa/gh-action-pypi-publish@v1.8.11
42
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes