pyEQL 0.12.2__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 (132) 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.12.2 → pyEQL-0.14.0}/.github/workflows/testing.yaml +19 -13
  4. {pyEQL-0.12.2 → pyEQL-0.14.0}/CHANGELOG.md +27 -0
  5. {pyEQL-0.12.2/src/pyEQL.egg-info → pyEQL-0.14.0}/PKG-INFO +1 -1
  6. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/amounts.md +2 -2
  7. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/arithmetic.md +2 -2
  8. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/changelog.md +27 -0
  9. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/chemistry.md +11 -10
  10. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/contributing.md +21 -25
  11. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/engines.md +16 -8
  12. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/index.md +4 -3
  13. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/installation.md +17 -8
  14. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/quickstart.md +5 -5
  15. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/serialization.md +7 -13
  16. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/units.md +4 -4
  17. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/macos-latest_py3.10.txt +1 -1
  18. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/macos-latest_py3.10_extras.txt +1 -1
  19. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/macos-latest_py3.11.txt +1 -1
  20. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/macos-latest_py3.11_extras.txt +1 -1
  21. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/macos-latest_py3.12.txt +1 -1
  22. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/macos-latest_py3.12_extras.txt +1 -1
  23. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/macos-latest_py3.9.txt +1 -1
  24. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/macos-latest_py3.9_extras.txt +1 -1
  25. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.10.txt +1 -1
  26. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.10_extras.txt +1 -1
  27. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.11.txt +1 -1
  28. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.11_extras.txt +1 -1
  29. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.12.txt +1 -1
  30. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.12_extras.txt +1 -1
  31. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.9.txt +1 -1
  32. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/ubuntu-latest_py3.9_extras.txt +1 -1
  33. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/windows-latest_py3.10.txt +1 -1
  34. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/windows-latest_py3.10_extras.txt +1 -1
  35. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/windows-latest_py3.11.txt +1 -1
  36. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/windows-latest_py3.11_extras.txt +1 -1
  37. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/windows-latest_py3.12.txt +1 -1
  38. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/windows-latest_py3.12_extras.txt +1 -1
  39. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/windows-latest_py3.9.txt +1 -1
  40. {pyEQL-0.12.2 → pyEQL-0.14.0}/requirements/windows-latest_py3.9_extras.txt +1 -1
  41. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/__init__.py +1 -1
  42. pyEQL-0.14.0/src/pyEQL/activity_correction.py +876 -0
  43. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/engines.py +40 -40
  44. pyEQL-0.14.0/src/pyEQL/equilibrium.py +221 -0
  45. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/functions.py +15 -13
  46. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/logging_system.py +1 -1
  47. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/salt_ion_match.py +19 -27
  48. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/solute.py +8 -4
  49. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/solution.py +378 -439
  50. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/utils.py +11 -5
  51. {pyEQL-0.12.2 → pyEQL-0.14.0/src/pyEQL.egg-info}/PKG-INFO +1 -1
  52. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL.egg-info/SOURCES.txt +1 -0
  53. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_activity.py +2 -0
  54. pyEQL-0.14.0/tests/test_equilibrium.py +42 -0
  55. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_functions.py +3 -0
  56. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_phreeqc.py +4 -0
  57. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_salt_matching.py +3 -0
  58. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_solution.py +7 -2
  59. pyEQL-0.12.2/.github/workflows/post-process.yml +0 -29
  60. pyEQL-0.12.2/.github/workflows/release.yml +0 -42
  61. pyEQL-0.12.2/src/pyEQL/activity_correction.py +0 -1009
  62. pyEQL-0.12.2/src/pyEQL/equilibrium.py +0 -287
  63. {pyEQL-0.12.2 → pyEQL-0.14.0}/.coveragerc +0 -0
  64. {pyEQL-0.12.2 → pyEQL-0.14.0}/.gitattributes +0 -0
  65. {pyEQL-0.12.2 → pyEQL-0.14.0}/.github/dependabot.yml +0 -0
  66. {pyEQL-0.12.2 → pyEQL-0.14.0}/.github/pull_request_template.md +0 -0
  67. {pyEQL-0.12.2 → pyEQL-0.14.0}/.github/release.yml +0 -0
  68. {pyEQL-0.12.2 → pyEQL-0.14.0}/.github/workflows/upgrade_dependencies.yml +0 -0
  69. {pyEQL-0.12.2 → pyEQL-0.14.0}/.gitignore +0 -0
  70. {pyEQL-0.12.2 → pyEQL-0.14.0}/.pre-commit-config.yaml +0 -0
  71. {pyEQL-0.12.2 → pyEQL-0.14.0}/.readthedocs.yml +0 -0
  72. {pyEQL-0.12.2 → pyEQL-0.14.0}/AUTHORS.md +0 -0
  73. {pyEQL-0.12.2 → pyEQL-0.14.0}/COPYING +0 -0
  74. {pyEQL-0.12.2 → pyEQL-0.14.0}/LICENSE.txt +0 -0
  75. {pyEQL-0.12.2 → pyEQL-0.14.0}/MANIFEST.in +0 -0
  76. {pyEQL-0.12.2 → pyEQL-0.14.0}/README.md +0 -0
  77. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/Makefile +0 -0
  78. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/_static/.gitignore +0 -0
  79. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/authors.md +0 -0
  80. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/class_solution.md +0 -0
  81. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/conf.py +0 -0
  82. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/creating.md +0 -0
  83. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/database.md +0 -0
  84. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/examples/.ipynb_checkpoints/pyEQL_demo_1-checkpoint.ipynb +0 -0
  85. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/examples/.ipynb_checkpoints/pyeql_demo-checkpoint.ipynb +0 -0
  86. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/examples/.ipynb_checkpoints/pyeql_tutorial_database-checkpoint.ipynb +0 -0
  87. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/examples/.ipynb_checkpoints/pyeql_tutorial_osmotic_pressure-checkpoint.ipynb +0 -0
  88. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/examples/.ipynb_checkpoints/speedup-checkpoint.ipynb +0 -0
  89. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/examples/pyEQL_demo_1.ipynb +0 -0
  90. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/examples/pyeql_demo.ipynb +0 -0
  91. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/examples/pyeql_tutorial_database.ipynb +0 -0
  92. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/examples/pyeql_tutorial_osmotic_pressure.ipynb +0 -0
  93. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/examples/speedup.ipynb +0 -0
  94. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/internal.md +0 -0
  95. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/license.md +0 -0
  96. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/mixing.md +0 -0
  97. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/readme.md +0 -0
  98. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/requirements.txt +0 -0
  99. {pyEQL-0.12.2 → pyEQL-0.14.0}/docs/tutorials.md +0 -0
  100. {pyEQL-0.12.2 → pyEQL-0.14.0}/pyeql-logo.png +0 -0
  101. {pyEQL-0.12.2 → pyEQL-0.14.0}/pyeql-logo.svg +0 -0
  102. {pyEQL-0.12.2 → pyEQL-0.14.0}/pyproject.toml +0 -0
  103. {pyEQL-0.12.2 → pyEQL-0.14.0}/setup.cfg +0 -0
  104. {pyEQL-0.12.2 → pyEQL-0.14.0}/setup.py +0 -0
  105. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/database/geothermal.dat +0 -0
  106. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/database/llnl.dat +0 -0
  107. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/database/phreeqc_license.txt +0 -0
  108. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/database/pyeql_db.json +0 -0
  109. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/pint_custom_units.txt +0 -0
  110. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/presets/Ringers lactate.yaml +0 -0
  111. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/presets/normal saline.yaml +0 -0
  112. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/presets/rainwater.yaml +0 -0
  113. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/presets/seawater.yaml +0 -0
  114. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/presets/urine.yaml +0 -0
  115. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL/presets/wastewater.yaml +0 -0
  116. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL.egg-info/dependency_links.txt +0 -0
  117. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL.egg-info/not-zip-safe +0 -0
  118. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL.egg-info/requires.txt +0 -0
  119. {pyEQL-0.12.2 → pyEQL-0.14.0}/src/pyEQL.egg-info/top_level.txt +0 -0
  120. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/conftest.py +0 -0
  121. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_bulk_properties.py +0 -0
  122. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_debye_length.py +0 -0
  123. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_density.py +0 -0
  124. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_dielectric.py +0 -0
  125. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_effective_pitzer.py +0 -0
  126. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_mixed_electrolyte_activity.py +0 -0
  127. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_osmotic_coeff.py +0 -0
  128. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_solute.py +0 -0
  129. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_solute_properties.py +0 -0
  130. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_utils.py +0 -0
  131. {pyEQL-0.12.2 → pyEQL-0.14.0}/tests/test_volume_concentration.py +0 -0
  132. {pyEQL-0.12.2 → 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,6 +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
+ ## [0.14.0] - 2024-03-05
9
+
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.
16
+
17
+ ### Fixed
18
+
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
25
+ - Docs: fixed many small problems in documentation causing equations and examples to
26
+ render incorrectly.
27
+ - `Solution.from_file`: Add missing `@classmethod` decorator; update documentation.
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
+
8
35
  ## [0.12.2] - 2024-02-25
9
36
 
10
37
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyEQL
3
- Version: 0.12.2
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
@@ -30,11 +30,11 @@ unit of mass or volume.
30
30
  <Quantity(1.21525e+10, 'nanogram / liter')>
31
31
  ```
32
32
 
33
- :::{important}
33
+ ```{important}
34
34
  The unit `'ppt'` is ambiguous in the water community. To most researchers, it means
35
35
  "parts per trillion" or ng/L, while to many engineers and operators it means "parts
36
36
  per THOUSAND" or g/L. `pyEQL` interprets `ppt` as **parts per trillion**.
37
- :::
37
+ ```
38
38
 
39
39
  You can also request dimensionless concentrations as weight percent (`'%'`),
40
40
  mole fraction (`'fraction'`) or the total _number_
@@ -20,10 +20,10 @@ as volume-weighted averages.
20
20
  <Quantity(1.99989659, 'liter')>
21
21
  ```
22
22
 
23
- :::{note}
23
+ ```{note}
24
24
  Both `Solution` involved in an addition operation must use the same [electrolyte
25
25
  modeling engine](engines.md).
26
- :::
26
+ ```
27
27
 
28
28
  Subtraction is not implemented and will raise a `NotImplementedError`.
29
29
 
@@ -5,6 +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
+ ## [0.14.0] - 2024-03-05
9
+
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.
16
+
17
+ ### Fixed
18
+
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
25
+ - Docs: fixed many small problems in documentation causing equations and examples to
26
+ render incorrectly.
27
+ - `Solution.from_file`: Add missing `@classmethod` decorator; update documentation.
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
+
8
35
  ## [0.12.2] - 2024-02-25
9
36
 
10
37
  ### Fixed
@@ -10,32 +10,33 @@ used in subsequent calculations.
10
10
  ## How to Enter Valid Chemical Formulas
11
11
 
12
12
  Generally speaking, type the chemical formula of your solute the "normal" way
13
- and `pyEQL` should be able to inerpret it. Internally, `pyEQL` uses a utility function `pyEQL.utils.standardize_formula`
13
+ and `pyEQL` should be able to interpret it. Internally, `pyEQL` uses a utility function `pyEQL.utils.standardize_formula`
14
14
  to process all formulas into a standard form. At present, this is done by passing the formula through the
15
15
  [`pymatgen.core.ion.Ion`](https://pymatgen.org/pymatgen.core.html#pymatgen.core.ion.Ion) class. Anything that the `Ion`
16
16
  class can understand will be processed into a valid formula by `pyEQL`.
17
17
 
18
18
  Here are some examples:
19
19
 
20
- | Substance | You enter | `pyEQL` understands |
21
- | :--- | :---: | :---: |
22
- | Sodium Chloride | "NaCl", "NaCl(aq)", or "ClNa" | "NaCl(aq)" |
23
- | Sodium Sulfate | "Na(SO4)2" or "NaS2O8" | "Na(SO4)2(aq)" |
24
- | Sodium Ion | "Na+", "Na+1", "Na1+", or "Na[+]" | "Na[+1]" |
25
- | Magnesium Ion | "Mg+2", "Mg++", or "Mg[++]" | "Mg[+2]" |
26
- | Methanol | "CH3OH", "CH4O" | "'CH3OH(aq)'" |
20
+ | Substance | You enter | `pyEQL` understands |
21
+ | :-------------- | :-------------------------------: | :-----------------: |
22
+ | Sodium Chloride | "NaCl", "NaCl(aq)", or "ClNa" | "NaCl(aq)" |
23
+ | Sodium Sulfate | "Na2(SO4)" or "Na2SO4" | "Na(SO4)(aq)" |
24
+ | Sodium Ion | "Na+", "Na+1", "Na1+", or "Na[+]" | "Na[+1]" |
25
+ | Magnesium Ion | "Mg+2", "Mg++", or "Mg[++]" | "Mg[+2]" |
26
+ | Methanol | "CH3OH", "CH4O" | "'CH3OH(aq)'" |
27
27
 
28
28
  Specifically, `standardize_formula` uses `Ion.from_formula(<formula>).reduced_formla` (shown in the right hand column
29
29
  of the table) to identify solutes. Notice that for charged species, the charges are always placed inside square brackets
30
30
  (e.g., `Na[+1]`) and always include the charge number (even for monovalent ions). Uncharged species are always suffixed
31
31
  by `(aq)` to disambiguate them from solids.
32
32
 
33
- :::{important}
33
+ ```{important}
34
34
  **When writing multivalent ion formulas, it is strongly recommended that you put the charge number AFTER the + or -
35
35
  sign** (e.g., type "Mg+2" NOT "Mg2+"). The latter formula is ambiguous - it could mean $Mg_2^+$ or $Mg^{+2}$ and it will be processed incorrectly into `Mg[+0.5]`
36
- :::
36
+ ```
37
37
 
38
38
  (manual-testing)=
39
+
39
40
  ## Manually testing a formula
40
41
 
41
42
  If you want to make sure `pyEQL` is understanding your formula correctly, you can manually test it as follows:
@@ -8,7 +8,9 @@ You can help the project simply by using pyEQL and comparing the output to exper
8
8
  If you encounter any bugs, packaging issues, feature requests, comments, or questions, please report them
9
9
  using the [issue tracker](https://github.com/KingsburyLab/pyEQL/issues) on [github](https://github.com/KingsburyLab/pyeql).
10
10
 
11
- :::{tip} Please don't forget to include the closed issues in your search. Sometimes a solution was already reported, and the problem is considered solved. :::
11
+ ```{tip}
12
+ Please don't forget to include the closed issues in your search. Sometimes a solution was already reported, and the problem is considered solved.
13
+ ```
12
14
 
13
15
  New issue reports should include information about your programming environment (e.g., operating system, Python version) and steps to reproduce the problem. Please try also to simplify the reproduction steps to a very minimal example that still illustrates the problem you are facing. By removing other factors, you help us to identify the root cause of the issue.
14
16
 
@@ -17,31 +19,23 @@ New issue reports should include information about your programming environment
17
19
  You can help improve `pyEQL` docs by making them more readable and coherent, or
18
20
  by adding missing information and correcting mistakes.
19
21
 
20
- `pyEQL` documentation uses [Sphinx] as its main documentation compiler.
22
+ `pyEQL` documentation uses [Sphinx](https://www.sphinx-doc.org/en/master/) as its main documentation compiler.
21
23
  This means that the docs are kept in the same repository as the project code, and
22
24
  that any documentation update is done in the same way was a code contribution.
23
25
 
24
- ```{todo} Don't forget to mention which markup language you are using.
25
-
26
- e.g., [reStructuredText] or [CommonMark] with [MyST] extensions.
27
- ```
28
-
29
- ```{todo} If your project is hosted on GitHub, you can also mention the following tip:
30
-
31
- :::{tip}
32
- Please notice that the [GitHub web interface] provides a quick way of
33
- propose changes in `pyEQL`'s files. While this mechanism can
34
- be tricky for normal code contributions, it works perfectly fine for
35
- contributing to the docs, and can be quite handy.
36
-
37
- If you are interested in trying this method out, please navigate to
38
- the `docs` folder in the source [repository], find which file you
39
- would like to propose changes and click in the little pencil icon at the
40
- top, to open [GitHub's code editor]. Once you finish editing the file,
41
- please write a message in the form at the bottom of the page describing
42
- which changes have you made and what are the motivations behind them and
43
- submit your proposal.
44
- :::
26
+ ```{tip}
27
+ Please notice that the [GitHub web interface] provides a quick way of
28
+ propose changes in `pyEQL`'s files. While this mechanism can
29
+ be tricky for normal code contributions, it works perfectly fine for
30
+ contributing to the docs, and can be quite handy.
31
+
32
+ If you are interested in trying this method out, please navigate to
33
+ the `docs` folder in the source [repository], find which file you
34
+ would like to propose changes and click in the little pencil icon at the
35
+ top, to open [GitHub's code editor]. Once you finish editing the file,
36
+ please write a message in the form at the bottom of the page describing
37
+ which changes have you made and what are the motivations behind them and
38
+ submit your proposal.
45
39
  ```
46
40
 
47
41
  When working on documentation changes in your local machine, you can
@@ -91,11 +85,15 @@ a report in the [issue tracker](https://github.com/KingsburyLab/pyEQL/issues) to
91
85
  ```
92
86
  git checkout -b mybranch
93
87
  ```
88
+
94
89
  or
90
+
95
91
  ```
96
92
  git checkout -b doc-mydoc
97
93
  ```
94
+
98
95
  or
96
+
99
97
  ```
100
98
  git checkout -b feature-myfeature
101
99
  ```
@@ -110,7 +108,6 @@ a report in the [issue tracker](https://github.com/KingsburyLab/pyEQL/issues) to
110
108
 
111
109
  7. Create a pull request with your changes. See [this tutorial](https://yangsu.github.io/pull-request-tutorial) for instructions.
112
110
 
113
-
114
111
  ## Guidelines
115
112
 
116
113
  Please abide by the following guidelines when contributing code to `pyEQL`:
@@ -133,7 +130,6 @@ Please abide by the following guidelines when contributing code to `pyEQL`:
133
130
 
134
131
  Improvements to the documentation are most welcome! Our documentation system uses `sphinx` with the [Materials for Sphinx](https://bashtage.github.io/sphinx-material/) theme. To edit the documentation locally, run `tox -e autodocs` from the repository root directory. This will serve the documents to http://localhost:8000/ so you can view them in your web browser. When you make changes to the files in the `docs/` directory, the documentation will automatically rebuild and update in your browser (you might have to refresh the page to see changes).
135
132
 
136
-
137
133
  ## Changelog
138
134
 
139
135
  We keep a `CHANGELOG.md` file in the base directory of the repository. Before submitting your PR, be sure to update the `CHANGELOG.md` file under the "Unreleased" section with a brief description of your changes. Our `CHANGELOG.md` file lossely follows the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format, beginning with `v0.6.0`.
@@ -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
@@ -55,14 +65,13 @@ number of species included (see [Lu et al.](https://doi.org/10.1016/j.earscirev.
55
65
  See `pyEQL.equilibrium.eqiulibrate_phreeqc` in the [module reference](internal.md#speciation-functions)
56
66
  for more details.
57
67
 
58
-
59
- :::{warning}
68
+ ```{warning}
60
69
  Speciation support was added to the `native` engine in `v0.8.0` and should be considered
61
70
  experimental. Specifically, because the `native` engine uses a non-Pitzer PHREEQC database
62
71
  for speciation but uses the Pitzer model (when possible) for activity coefficients. As such,
63
72
  there may be subtle thermodynamic inconsistencies between the activities and the equilibrium
64
73
  concentrations returned by `equilibrate()`.
65
- :::
74
+ ```
66
75
 
67
76
  ## The `'phreeqc'` engine
68
77
 
@@ -81,11 +90,11 @@ concentration of the solute.
81
90
  Due to limitations in the `phreeqpython` interface, the osmotic coefficient is always
82
91
  returned as 1 at present.
83
92
 
84
- :::{warning}
93
+ ```{warning}
85
94
  The `phreeqc` engine currently returns an osmotic coefficient of 1 and solute volume of
86
95
  0 for all solutions. There appear to be limitations in the `phreeqpython` interface that
87
96
  make it difficult to access these properties.
88
- :::
97
+ ```
89
98
 
90
99
  ### Solute volumes
91
100
 
@@ -94,17 +103,16 @@ the `ideal` engine). More
94
103
  research is needed to determine whether this is consistent with intended PHREEQC behavior
95
104
  (when using the default database) or not.
96
105
 
97
- :::{warning}
106
+ ```{warning}
98
107
  The `phreeqc` engine currently returns an osmotic coefficient of 1 and solute volume of
99
108
  0 for all solutions. There appear to be limitations in the `phreeqpython` interface that
100
109
  make it difficult to access these properties.
101
- :::
110
+ ```
102
111
 
103
112
  ### Speciation
104
113
 
105
114
  Speciation calculations are provided by PHREEQC via `phreeqpython`.
106
115
 
107
-
108
116
  ## The `'ideal'` engine
109
117
 
110
118
  The `'ideal'` engine applies ideal solution behavior. Activity and osmotic coefficients
@@ -1,6 +1,6 @@
1
1
  ---
2
- Date: '{{ today }}'
3
- Release: '{{ release }}'
2
+ Date: "{{ today }}"
3
+ Release: "{{ release }}"
4
4
  ---
5
5
 
6
6
  ![pyeql-logo](../pyeql-logo.png)
@@ -30,11 +30,12 @@ pip install pyEQL
30
30
 
31
31
  ```python
32
32
  >>> from pyEQL import Solution
33
- >>> s1 = pyEQL.Solution({'Na+':'0.5 mol/kg', 'Cl-': '0.5 mol/kg'},
33
+ >>> s1 = Solution({'Na+':'0.5 mol/kg', 'Cl-': '0.5 mol/kg'},
34
34
  pH=8,
35
35
  temperature = '20 degC',
36
36
  volume='8 L')
37
37
  ```
38
+
38
39
  ### Get properties
39
40
 
40
41
  ```python
@@ -6,10 +6,9 @@
6
6
 
7
7
  We highly recommend installing python in an isolated environment using [`conda`](https://docs.conda.io/en/latest/) (or its speedier, backward-compatible successor, [mamba](https://mamba.readthedocs.io/en/latest/)). In particular, we recommend the [miniforge](https://github.com/conda-forge/miniforge#miniforge3) or [mambaforge](https://github.com/conda-forge/miniforge#mambaforge) distributions of Python, which are lightweight distributions of conda that automatically activate the `conda-forge` channel for up-to-date scientific packages.
8
8
 
9
-
10
- :::{note}
9
+ ```{note}
11
10
  If you are on a Windows machine, we recommend you install the [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install) and set up your conda environments inside the WSL environment.
12
- :::
11
+ ```
13
12
 
14
13
  After installing `conda` / `mamba`, follow their instructions to create an environment. The steps should be similar to the following:
15
14
 
@@ -28,10 +27,12 @@ pip install pyEQL
28
27
 
29
28
  This should automatically pull in the required [dependencies](#other-dependencies) as well.
30
29
 
31
- :::{important}
30
+ ```{important}
32
31
  If you are NOT using a `conda` environment, may have to run 'pip3' rather than 'pip'. This will be the case if Python 2.x and Python 3.x are installed side-by-side on your system.
33
32
  You can tell if this is the case by typing the following command:
34
33
 
34
+ ```
35
+
35
36
  ```
36
37
  $ python --version
37
38
  Python 2.7.12
@@ -46,7 +47,16 @@ Python 3.9.7
46
47
  ```
47
48
 
48
49
  To get to Python 3.x, you have to type 'python3'. In this case, you would run 'pip3 install'
49
- :::
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
59
+ ```
50
60
 
51
61
  ## Other dependencies
52
62
 
@@ -62,7 +72,6 @@ pyEQL also requires the following packages:
62
72
 
63
73
  If you use pip to install pyEQL (recommended), they should be installed automatically.
64
74
 
65
-
66
75
  ## Installing the development branch
67
76
 
68
77
  If you want to use the bleeding edge version before it is released to PyPi instead of the latest stable release, you can substitute the following for the above 'pip install' command:
@@ -85,6 +94,6 @@ Then install by executing:
85
94
  pip install -e pyEQL
86
95
  ```
87
96
 
88
- :::{note}
97
+ ```{note}
89
98
  You may have to run 'pip3' rather than 'pip'. See the note in the [pip install](#pip-install) section.
90
- :::
99
+ ```
@@ -15,10 +15,10 @@ Create a Solution object by invoking the Solution class:
15
15
  ```{eval-rst}
16
16
  .. doctest::
17
17
 
18
- >>> import pyEQL
19
- >>> s1 = pyEQL.Solution()
18
+ >>> from pyEQL import Solution
19
+ >>> s1 = Solution()
20
20
  >>> s1
21
- <pyEQL.pyEQL.Solution at 0x7f9d188309b0>
21
+ <pyEQL.Solution at 0x7f9d188309b0>
22
22
 
23
23
  ```
24
24
 
@@ -30,7 +30,7 @@ More usefully, you can specify solutes and bulk properties:
30
30
  ```{eval-rst}
31
31
  .. doctest::
32
32
 
33
- >>> s2 = pyEQL.Solution({'Na+':'0.5 mol/kg', 'Cl-': '0.5 mol/kg'}, pH=8, temperature = '20 degC', volume='8 L')
33
+ >>> s2 = Solution({'Na+':'0.5 mol/kg', 'Cl-': '0.5 mol/kg'}, pH=8, temperature = '20 degC', volume='8 L')
34
34
  ```
35
35
 
36
36
  See [Creating a Solution](creating.md) for more details.
@@ -83,7 +83,7 @@ Quantity objects that contain both a magnitude and a unit.
83
83
  1.0
84
84
  ```
85
85
 
86
- Many `pyEQL` methods require physical quantities to be input as strings, then these methods return pint `Quantity` objects. A string quantity must contain both a magnitude and a unit (e.g. '0.5 mol/L').
86
+ Many `pyEQL` methods require physical quantities to be input as strings, then these methods return pint `Quantity` objects. A string quantity must contain both a magnitude and a unit (e.g. '0.5 mol/L').
87
87
  In general, pint recognizes common abbreviations and SI prefixes. Compound units must follow Python math syntax (e.g. cm\*\*2 not cm2).
88
88
 
89
89
  See the [Converting Units](units.md) for more details.
@@ -16,33 +16,27 @@ using `from_dict()`.
16
16
 
17
17
  ## Saving to a `.json` file
18
18
 
19
- `Solution` can be serialized (and later recreated from) a `.json` file using
19
+ `Solution` can be serialized (and later recreated from) a `.json` file using `to_file`, which is based on
20
20
  [`monty.serializtaion.dumpfn`](https://pythonhosted.org/monty/monty.html#module-monty.serialization).
21
21
 
22
22
  ```python
23
23
  >>> from pyEQL import Solution
24
- >>> from monty.serialization import dumpfn
25
24
  >>> s = Solution({"Na+": "0.5 mol/L", "Cl-": "0.5 mol/L"})
26
- >>> dumpfn(s, 'test.json')
25
+ >>> s.to_file('test.json')
27
26
  ```
28
27
 
29
28
  ## Loading from a `.json` file
30
29
 
31
- Similarly, [monty.serialization.loadfn](https://pythonhosted.org/monty/monty.html#module-monty.serialization) can be used to create a `Solution` from a compatible
30
+ Similarly, `from_file` (based on [monty.serialization.loadfn](https://pythonhosted.org/monty/monty.html#module-monty.serialization)) can be used to create a `Solution` from a compatible
32
31
  `.json` file.
33
32
 
34
33
  ```python
35
- >>> from monty.serialization import loadfn
36
- >>> s = loadfn('test.json')
37
- print(s)
34
+ >>> from pyEQL import Solution
35
+ >>> s = Solution.from_file('test.json')
36
+ <pyEQL.solution.Solution object at 0x7febf742d8a0>
37
+ >>> print(s)
38
38
  Volume: 1.000 l
39
39
  Pressure: 1.000 atm
40
40
  Temperature: 298.150 K
41
41
  Components: ['H2O(aq)', 'H[+1]', 'OH[-1]']
42
42
  ```
43
-
44
- :::{note}
45
- In a future release, `to_file()` / `from_file()` methods may be added to `Solution` to
46
- make the above steps easier. Please post on GitHub if you have strong opinions about
47
- this!
48
- :::