diff --git a/.readthedocs.yml b/.readthedocs.yml index 1133df02..df748372 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -4,6 +4,12 @@ # Required version: 2 +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.10" + # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py @@ -17,6 +23,5 @@ formats: - pdf python: - version: 3.8 install: - requirements: docs/requirements.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 64d950e0..602b8d81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,14 @@ # pyEQL Changelog -## 0.6.0 (in progress) +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added -- **BREAKING CHANGE** `Solute`: methods `get_formal_charge()`, `get_name()`, and `get_molecular_weight()` have been - replaced by direct access to the attributes `charge`, `formula`, and `mw`, respectively. -- **DEPRECATION NOTICE** `Solution`: new properties `pressure`, `temperature`, `pE`, -- `pH`, `mass`, `density`, `viscosity_dynamic`, `viscosity_kinematic`, `ionic_strength`, -- `conductivity`, `debye_length`, `bjerrum_length`, `alkalinity`, `hardness`, -- `dielectric_constant`, `charge_balance` have replaced the corresponding get_XXX and - set_XXX (for temperature and pressure) methods, which will be removed in a future - release. `get_viscosity_relative` will be removed entirely. - `Solution`: add support for passing solutes as a `dict` - Implement extensible system for connecting `Solution` to various activity and speciation models. Models can be integrated into pyEQL by implementing an `EOS` class. The desired @@ -17,22 +16,38 @@ implementation of Pitzer, which decays gracefully into Debye-Huckel and other models if parameters are not available) or `ideal` for a dummy engine that returns unit activity coefficients. Support for additional external engines such as [`phreeqpython`](https://github.com/Vitens/phreeqpython) is planned. -- **BREAKING CHANGE** disable 'verbose' kwarg in `get_activity` and `get_activity_coefficient` +- Add `pymatgen`, `monty`, and `maggma` as dependencies +- Add pre-commit configuration and lint with `ruff` - Add more comprehensive platform testing via `tox` + +### Changed + - Replace `water_properties.py` with [iapws](https://github.com/jjgomera/iapws) package -- Replace elements.py with `pymatgen.core.periodic_table` -- Add `pymatgen` as a dependency +- Replace `elements.py`` with `pymatgen.core.periodic_table` - Migrate all tests to `pytest` -- Add pre-commit configuration and lint with `black` -- Update packaging to use [pyscaffold](https://pyscaffold.org/en/stable/index.html) +- Update packaging format to use [pyscaffold](https://pyscaffold.org/en/stable/index.html) + +### Deprecated + +- `Solution`: new properties `pressure`, `temperature`, `pE`, `pH`, `mass`, `density`, `viscosity_dynamic`, `viscosity_kinematic`, `ionic_strength`, `conductivity`, `debye_length`, `bjerrum_length`, `alkalinity`, `hardness`, `dielectric_constant`, `charge_balance` have replaced the corresponding get_XXX and set_XXX (for temperature and pressure) methods, which will be removed in a future release. `get_viscosity_relative` will be removed entirely. +- `Solute`: methods `get_formal_charge()`, `get_name()`, and `get_molecular_weight()` have been + replaced by direct access to the attributes `charge`, `formula`, and `mw`, respectively. + +### Removed + +- disable 'verbose' kwarg in `get_activity` and `get_activity_coefficient` + +### Fixed + +- Fixed various documentation rendering issues -## 0.5.2 (2020-04-21) +## [0.5.2] - 2020-04-21 - Fix breaking bug introduced by upstream pint change to avogadro_number - Format project with black - Misc. linting and docstring changes -## 0.5.0 (2018-09-19) +## [0.5.0] 2018-09-19 - Implement the effective Pitzer model for improved activity calculations in multicomponent solutions - Add support for calculation of activity and osmotic coefficients on different scales @@ -46,7 +61,7 @@ - DEPRECATED get_mole_fraction. Use get_amount() instead - Fix bug causing get_activity_coefficient to fail if the solute concentration was zero -## 0.4.0 (2016-07-14) +## [0.4.0] 2016-07-14 - Add ability to calculate dielectric constant based on solution composition for salts - Add database entries for the viscosity 'B' parameter for 63 more inorganic ions @@ -59,12 +74,12 @@ - Fix bug causing ValueError exceptions when a solute has zero concentration - Numerous fixes and corrections in the documentation -## 0.3.1 (2016-02-24) +## [0.3.1] 2016-02-24 - Fix packaging problems preventing installation from PyPi - Fix character encoding issue in Erying_viscosity database file -## 0.3.0 (2016-01-15) +## [0.3.0] 2016-01-15 - Add method to calculate the total concentration of an element in a solution - Add method to automatically generate certain solutions (like seawater) @@ -81,11 +96,11 @@ - Fix bug related to retrieval of water properties - Documentation enhancements and fixes -## 0.2.2 (2015-08-28) +## [0.2.2] 2015-08-28 - Fix bug in get_amount() causing no output when mass-based units were specified. -## 0.2.1 (2015-05-06) +## [0.2.1] 2015-05-06 - Add 93 entries to diffusion coefficient database - Add 93 entries to Pitzer partial molar volume parameters database @@ -95,6 +110,6 @@ - Uploaded to the Python Package Index for easier installation - Add this changelog -## 0.2.0 (2015-03-26) +## [0.2.0] 2015-03-26 - First public release diff --git a/README.md b/README.md index fc5b951d..03a474e4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -[![testing](https://github.com/rkingsbury/pyeql/workflows/testing/badge.svg)](https://github.com/rkingsbury/pyeql/actions?query=workflow%3Atesting) [![codecov](https://codecov.io/gh/rkingsbury/pyeql/branch/main/graph/badge.svg?token=I7RP0QML6S)](https://codecov.io/gh/rkingsbury/pyeql) +[![testing](https://github.com/rkingsbury/pyeql/workflows/testing/badge.svg)](https://github.com/rkingsbury/pyeql/actions?query=workflow%3Atesting) +[![codecov](https://codecov.io/gh/rkingsbury/pyeql/branch/main/graph/badge.svg?token=I7RP0QML6S)](https://codecov.io/gh/rkingsbury/pyeql) +![Supported python versions](https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11-blue) # pyEQL diff --git a/docs/chemistry.md b/docs/chemistry.md new file mode 100644 index 00000000..49177b9e --- /dev/null +++ b/docs/chemistry.md @@ -0,0 +1,56 @@ +(chemistry)= + +# Chemical Formulas + +pyEQL interprets the chemical formula of a substance to calculate its molecular +weight and formal charge. The formula is also used as a key to search the +database for parameters (e.g. diffusion coefficient) that are used in subsequent +calculations. + +## How to Enter Valid Chemical Formulas + +Generally speaking, type the chemical formula of your solute the "normal" way +and `pyEQL` should be able to inerpret it. Internally, `pyEQL` uses the [`pymatgen.core.ion.Ion`](https://pymatgen.org/pymatgen.core.html#pymatgen.core.ion.Ion) + class to "translate" chemical formulas into a consistent format. Anything that the `Ion` class can understand will + be processed into a valid formula by `pyEQL`. + +Here are some examples: + +| Substance | You enter | `pyEQL` understands | +| :--- | :---: | :---: | +| Sodium Chloride | "NaCl", "NaCl(aq)", or "ClNa" | "NaCl(aq)" | +| Sodium Sulfate | "Na(SO4)2" or "NaS2O8" | "Na(SO4)2(aq)" | +| Sodium Ion | "Na+", "Na+1", "Na1+", or "Na[+]" | "Na[+1]" | +| Magnesium Ion | "Mg+2", "Mg++", or "Mg[++]" | "Mg[+2]" | +| Methanol | "CH3OH", "CH4O" | "'CH3OH(aq)'" | + +Specifically, `pyEQL` uses `Ion.from_formula().reduced_formla` (shown in the right hand column of the table) to +identify solutes. Notice that for charged species, the charges are always placed inside square brackets (e.g., `Na[+1]`) +and always include the charge number (even for monovalent ions). Uncharged species are always suffixed by `(aq)` to +disambiguate them from solids. + +:::{important} +**When writing multivalent ion formulas, it is strongly recommended that you put the charge number AFTER the + or - +sign** (e.g., type "Mg+2" NOT "Mg2+"). The latter formula is ambiguous - it could mean $`Mg_2^+`$ or $`Mg^{+2}`$ +::: + +(manual-testing)= +## Manually testing a formula + +If you want to make sure `pyEQL` is understanding your formula correctly, you can manually test it via `pymatgen` as +follows: + +``` +>>> from pymatgen.core.ion import Ion +>>> Ion.from_formula().reduced_formula +... +``` + +## Formulas you will see when using `Solution` + +When using the `Solution` class, + +- When creating a `Solution`, you can enter chemical formulas in any format you prefer, as long as `pymatgen` can understand it (see [manual testing](#manually-testing-a-formula)). +- The keys (solute formulas) in `Solution.components` are preserved in the same format the user enters them. So if you entered `Na+` for sodium ion, it will stay that way. +- Arguments to `Solution.get_property` can be entered in any format you prefer. When `pyEQL` queries the database, it will automatically convert the formula to the canonical one from `pymatgen` +- Property data in the database is uniquely identified by the canonical ion formula (output of `Ion.from_formula().reduced_formla`, e.g. "Na[+1]" for sodium ion). diff --git a/docs/chemistry.rst b/docs/chemistry.rst deleted file mode 100644 index 44905fd3..00000000 --- a/docs/chemistry.rst +++ /dev/null @@ -1,58 +0,0 @@ -.. _chemistry: - -Chemical Formulas -***************** - -Representing Chemical Substances in pyEQL -========================================= - -pyEQL interprets the chemical formula of a substance to calculate its molecular -weight and formal charge. The formula is also used as a key to search the -database for parameters (e.g. diffusion coefficient) that are used in subsequent -calculations. - -How to Enter Valid Chemical Formulas ------------------------------------- - -Generally speaking, type the chemical formula of your solute the "normal" way -and pyEQL should be able to inerpret it. Here are some examples: - -- Sodium Chloride - NaCl -- Sodium Sulfate - Na(SO4)2 -- Methanol - CH4OH or CH5O -- Magnesium Ion - Mg+2 -- Chloride Ion - Cl- - -Formula Rules: - -1. Are composed of valid atomic symbols that start with capital letters -2. Contain no non-alphanumeric characters other than '(', ')', '+', or '-' -3. If a '+' or '-' is present, the formula must contain ONLY '+' or '-' - (e.g. 'Na+-' is invalid) and the formula must end with either a series of - charges (e.g. 'Fe+++') or a numeric charge (e.g. 'Fe+3') -4. Formula must contain matching numbers of '(' and ')' -5. Open parentheses must precede closed parentheses - -Alternate Formulas and Isomers ------------------------------- - -Many complex molecules can be written in multiple ways. pyEQL cares only about -the number and identity of the elements and the formal charge on the molecule, -so you can use any form you choose. The hill_order() method takes a formula -and reduces it to its simplest form, like so:: - - >>> pyEQL.chemical_formula.hill_order('CH2(CH3)4COOH') - 'C6H15O2' - -When searching the parameters database, pyEQL uses this method to reduce -both user-entered formulas AND keys in the database. So even if you created -a solution containing 'ClNa', pyEQL would still match it with parameters for -'NaCl'. - -Currently pyEQL **does not distinguish between isomers**. - -API Documentation (chemical_formula.py) -======================================= - -.. automodule:: pyEQL.chemical_formula - :members: diff --git a/docs/class_solute.rst b/docs/class_solute.rst deleted file mode 100644 index 9c8d8483..00000000 --- a/docs/class_solute.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. _class_solute: - -The Solute Class -****************** - -.. automodule:: pyEQL.solute - :members: diff --git a/docs/class_solution.md b/docs/class_solution.md new file mode 100644 index 00000000..849376aa --- /dev/null +++ b/docs/class_solution.md @@ -0,0 +1,80 @@ +(class-solution)= + +# The Solution Class + +The `Solution` class defines a pythonic interface for **creating**, **modifying**, and **estimating properties** of +electrolyte solutions. It is the core feature of `pyEQL` and the primary user-facing class. + +## Creating a solution + +A `Solution` created with no arguments will default to pure water at pH=7, T=25 degrees Celsius, and 1 atm pressure. + +``` +>>> from pyEQL import Solution +>>> s1 = Solution() +>>> s1.pH +6.998877352386266 +``` + +Alternatively, you can use the `autogenerate()` function to easily create common solutions like seawater: + +``` +>>> from pyEQL.functions import autogenerate +>>> s2 = autogenerate('seawater') + +``` + +You can inspect the solutes present in the solution via the `components` attribute. This comprises a dictionary of solute formula: moles, where 'moles' is the number of moles of that solute in the `Solution`. Note that the solvent (water) is present in `components`, too. + +``` +>>> s2.components +{'H2O': 55.34455401423017, + 'H+': 7.943282347242822e-09, + 'OH-': 8.207436858780226e-06, + 'Na+': 0.46758273714962967, + 'Mg+2': 0.052661180523467986, + 'Ca+2': 0.010251594148212318, + 'K+': 0.010177468379526856, + 'Sr+2': 9.046483353663286e-05, + 'Cl-': 0.54425785619973, + 'SO4-2': 0.028151873448454025, + 'HCO3-': 0.001712651176926199, + 'Br-': 0.0008395244921424563, + 'CO3-2': 0.00023825904349479546, + 'B(OH)4': 0.0001005389715937341, + 'F-': 6.822478260456777e-05, + 'B(OH)3': 0.0003134669156396757, + 'CO2': 9.515218476861175e-06 + } +``` + +To get the amount of a specific solute, use `get_amount()` and specify the units you want: + +``` +>>> s2.get_amount('Na+', 'g/L') + +``` + +Finally, you can manually create a solution with any list of solutes, temperature, pressure, etc. that you need: + +``` +>>> from pyEQL import Solution +>>> s1 = Solution(solutes={'Na+':'0.5 mol/kg', 'Cl-': '0.5 mol/kg'}, + pH=8, + temperature = '20 degC', + volume='8 L' + ) +``` + +## Class reference + +The remainder of this page contains detailed information on each of the methods, attributes, and properties in `Solution`. Use the sidebar on the right for easier navigation. + +```{eval-rst} +.. autoclass:: pyEQL.Solution + :members: + :inherited-members: + :private-members: _get_property + :special-members: __init__ + :member-order: bysource +``` diff --git a/docs/class_solution.rst b/docs/class_solution.rst deleted file mode 100644 index 1e1067f4..00000000 --- a/docs/class_solution.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. _class_solution: - -The Solution Class -****************** - -.. automodule:: pyEQL.solution - :members: diff --git a/docs/conf.py b/docs/conf.py index 407d3657..063ddc6d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -8,8 +8,8 @@ # serve to show the default. import os -import shutil import sys +import shutil # -- Path setup -------------------------------------------------------------- @@ -52,13 +52,16 @@ apidoc.main(args) except Exception as e: - print(f"Running `sphinx-apidoc` failed!\n{e}") + print("Running `sphinx-apidoc` failed!\n{}".format(e)) # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ @@ -74,10 +77,6 @@ "sphinx.ext.napoleon", ] -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - - # Enable markdown extensions.append("myst_parser") @@ -114,7 +113,7 @@ # # version: The short X.Y version. # release: The full version, including alpha/beta/rc tags. -# If you don`t need the separation provided between version and release, +# If you don’t need the separation provided between version and release, # just set them both to the same value. try: from pyEQL import __version__ as version @@ -171,14 +170,25 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = "alabaster" +html_theme = "sphinx_material" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { "sidebar_width": "300px", - "page_width": "1200px" + "page_width": "1200px", + 'base_url': 'https://pyeql.readthedocs.io/en/latest/', + 'repo_url': 'https://github.com/rkingsbury/pyEQL/', + 'repo_name': 'pyEQL', + 'html_minify': True, + 'css_minify': True, + 'nav_title': 'pyEQL', + 'color_primary': "blue", + 'color_accent': "light-blue", +} +html_sidebars = { + "**": ["logo-text.html", "globaltoc.html", "localtoc.html", "searchbox.html"] } # Add any paths that contain custom themes here, relative to this directory. diff --git a/docs/contributing.md b/docs/contributing.md index fc1b2138..ffa2f9d5 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,4 +1,73 @@ -```{include} ../CONTRIBUTING.md -:relative-docs: docs/ -:relative-images: -``` +(contributing)= + +# Contributing to pyEQL + +## Reporting Issues + +You can help the project simply by using pyEQL and comparing the output to experimental data and/or other models and tools. +If you encounter any bugs, packaging issues, feature requests, comments, or questions, please report them +using the [issue tracker](https://github.com/rkingsbury/pyEQL/issues) on [github](https://github.com/rkingsbury/pyeql). + +## Contributing Code + +To contribute bug fixes, documentation enhancements, or new code, please fork pyEQL and send us a pull request. It's not as hard as it sounds! Beginning with version 0.6.0, we follow the [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) workflow model. + + +### Hacking pyEQL in Six Easy Steps: + +1. [Fork the pyEQL repository](https://help.github.com/articles/fork-a-repo/) on Github + +2. Clone your repository to a directory of your choice: + + ``` + git clone https://github.com//pyEQL + ``` + +3. Create a branch for your work. Preferably, start your branch name with "feature-", "fix-", or "doc-" depending on whether you are contributing **bug fixes**, **documentation** or a **new feature**, e.g. + prefix your branch with "fix-" or "doc-" as appropriate: + + ``` + git checkout -b mybranch + ``` + or + ``` + git checkout -b doc-mydoc + ``` + or + ``` + git checkout -b feature-myfeature + ``` + +4. Hack away until you're satisfied. + +5. Push your work back to Github: + + ``` + git push origin feature-myfeature + ``` + +6. Create a pull request with your changes. See [this tutorial](https://yangsu.github.io/pull-request-tutorial) for instructions. + + +## Guidelines + +Please abide by the following guidelines when contributing code to `pyEQL`: + +- All changes you make to quacc should be accompanied by unit tests and should not break existing tests. To run the full test suite, run `pytest tests/` from the repository directory. + +- Code coverage should be maintained or increase. Each PR will report code coverage after the tests pass, but you can check locally using [pytest-cov](https://pytest-cov.readthedocs.io/en/latest/), by running `pytest --cov tests/` + +- All code should include type hints and have internally consistent documentation for the inputs and outputs. + +- Use Google style docstrings + +- Lint your code with [`ruff`](https://github.com/astral-sh/ruff) by running `ruff check --fix src/` from the repo directory. Alternatively, you can install the `pre-commit` hooks by running `pre-commit install` from the repository directory. This will prevent committing new changes until all linting errors are fixed. + +- Update the `CHANGELOG.md` file. + +- Ask questions and be open to feedback! + + +## Changelog + +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`. \ No newline at end of file diff --git a/docs/contributing.rst b/docs/contributing.rst deleted file mode 100644 index 2cb96ace..00000000 --- a/docs/contributing.rst +++ /dev/null @@ -1,71 +0,0 @@ -.. _contributing: - -Contributing to pyEQL -********************* - -Reporting Issues -================ - -You can report any bugs, packaging issues, feature requests, comments, or questions -using the `issue tracker `_ on `github `_. - -Contributing Code -================= - -To contribute bug fixes, documentation enhancements, or new code, please -fork pyEQL and send us a pull request. It's not as hard as it sounds! - -It is **strongly** recommended that you read the following short articles -before starting your work, especially if you are new to the open source community. - -* `Open Source Contribution Etiquette `_ -* `Don't "Push" Your Pull Requests `_ -* `A Successful Git Branching Model `_ - -Hacking pyEQL in Six Easy Steps: ---------------------------------- - -1. `Fork the pyEQL repository `_ on Github - -2. Clone your repository to a directory of your choice:: - - git clone https://github.com//pyEQL - -3. Create a branch for your work. We loosely follow the branching guidelines - outlined at http://nvie.com/posts/a-successful-git-branching-model. - - If you are adding **documentation** or **bug fixes**, start with the **main** branch and - prefix your branch with "fix-" or "doc-" as appropriate:: - - git checkout -b fix-myfix main - - git checkout -b doc-mydoc main - - If you are adding a **new feature**, start with the **develop** branch and prefix your - branch with "feature-":: - - git checkout -b feature-myfeature develop - -4. Hack away until you're satisfied. - -5. Push your work back to Github:: - - git push origin feature-myfeature - -6. Create a pull request with your changes. See `this tutorial `_ for instructions. - -Generating Test Cases -===================== - -pyEQL has many capabilities that have not been tested thoroughly. You can help -the project simply by using pyEQL and comparing the output to experimental data -and/or more established models. Report back your results on the -`issue tracker `_. - -Even better, write up an automated test case (see the tests/ directory for examples). - -Making a Donation -================= - -If you'd like to leave a 'tip' for the project maintainer to support the time and effort -required to develop pyEQL, simply send it via Paypal to RyanSKingsbury@alumni.unc.edu diff --git a/docs/database.md b/docs/database.md new file mode 100644 index 00000000..8fa08d1d --- /dev/null +++ b/docs/database.md @@ -0,0 +1,462 @@ +(database)= + +# Property Database + +pyEQL is distributed with a database of solute properties and model parameters needed to perform +it's calculations. The database includes: + +- Molecular weight, charge, and other chemical informatics information for any species +- Diffusion coefficients for 104 ions +- Pitzer model activity correction coefficients for 157 salts +- Pitzer model partial molar volume coefficients for 120 salts +- Jones-Dole "B" coefficients for 83 ions +- Hydrated and ionic radii for 23 ions +- Dielectric constant model parameters for 18 ions +- Partial molar volumes for 24 ions + +`pyEQL` can automatically infer basic chemical informatics such as molecular weight and charge by passing a solute's formula to `pymatgen.core.ion.Ion` (See [chemical formulas](#chemistry)). For other physicochemical properties, it relies on data compiled into the included database. A list of the data and species covered is available [below](#species-included) + +## Format + +The database is distributed as a `.json` file containing serialized `Solute` objects that define the schema for aggregated property data (see [below](#the-solute-class)). By default, each instance of `Solution` loads this file as a [`maggma`](https://materialsproject.github.io/maggma/) [`JSONStore`](https://materialsproject.github.io/maggma/reference/stores/#maggma.stores.mongolike.JSONStore) and queries data from it using the [`Store`](https://materialsproject.github.io/maggma/concepts/#store) interface. + +If desired, users can point a `Solution` instance to an alternate database by using the `database` keyword argument at creation. The argument should contain either 1) the path to an alternate `.json` file (as a `str`) or 2) a `maggma.Store` instance. The data in the file or `Store` must match the schema defined by `Solute`, with the field `formula` used as the key field (unique identifier). + +``` +s1 = Solution(database='/path/to/my_database.json') +``` + +or + +``` +from maggma.core import JSONStore + +db_store = JSONStore('/path/to/my_database.json', key='formula') +s1 = Solution(database=db_store) +``` + +## The `Solute` class + +`pyEQL.Solute` is a [`dataclass`](https://docs.python.org/3/library/dataclasses.html) that defines a schema for organizing solute property data. You can think of the schema as a structured dictionary: `Solute` defines the naming and organization of the keys. You can create a basic `Solute` from just the solute's formula as follows: + +``` +>>> from pyEQL.solute import Solute +>>> Solute.from_formula('Ti+2') +Solute(formula='Ti[+2]', charge=2, molecular_weight='47.867 g/mol', elements=['Ti'], chemsys='Ti', pmg_ion=Ion: Ti1 +2, formula_html='Ti+2', formula_latex='Ti$^{+2}$', formula_hill='Ti', formula_pretty='Ti^+2', oxi_state_guesses=({'Ti': 2.0},), n_atoms=1, n_elements=1, size={'radius_ionic': None, 'radius_hydrated': None, 'radius_vdw': None, 'molar_volume': None}, thermo={'ΔG_hydration': None, 'ΔG_formation': None}, transport={'diffusion_coefficient': None}, model_parameters={'activity_pitzer': {'Beta0': None, 'Beta1': None, 'Beta2': None, 'Cphi': None, 'Max_C': None}, 'molar_volume_pitzer': {'Beta0': None, 'Beta1': None, 'Beta2': None, 'Cphi': None, 'V_o': None, 'Max_C': None}, 'viscosity_jones_dole': {'B': None}}) +``` + +This method uses `pymatgen` to populate the `Solute` with basic chemical information like molecular weight. You can access top-level keys in the schema via attribute, e.g. + +``` +>>> s.molecular_weight +'47.867 g/mol' +>>> s.charge +2.0 +``` + +Other properties that are present in the schema, but not set, are `None`. For example, here we have not specified a diffusion coefficient. If we inspect the `transport` attribute, we see + +``` +>>> s.transport +{'diffusion_coefficient': None} +``` + +You can convert a `Solute` into a regular dictionary using `Solute.as_dict()` + +''' +>>> s.as_dict() +{'formula': 'Ti[+2]', 'charge': 2, 'molecular_weight': '47.867 g/mol', 'elements': ['Ti'], 'chemsys': 'Ti', 'pmg_ion': Ion: Ti1 +2, 'formula_html': 'Ti+2', 'formula_latex': 'Ti$^{+2}$', 'formula_hill': 'Ti', 'formula_pretty': 'Ti^+2', 'oxi_state_guesses': ({'Ti': 2.0},), 'n_atoms': 1, 'n_elements': 1, 'size': {'radius_ionic': None, 'radius_hydrated': None, 'radius_vdw': None, 'molar_volume': None}, 'thermo': {'ΔG_hydration': None, 'ΔG_formation': None}, 'transport': {'diffusion_coefficient': None}, 'model_parameters': {'activity_pitzer': {'Beta0': None, 'Beta1': None, 'Beta2': None, 'Cphi': None, 'Max_C': None}, 'molar_volume_pitzer': {'Beta0': None, 'Beta1': None, 'Beta2': None, 'Cphi': None, 'V_o': None, 'Max_C': None}, 'viscosity_jones_dole': {'B': None}}} +''' + +## Searching the database + +Once you have a created a `Solution`, it will automatically search the database for needed parameters whenever it needs +to perform a calculation. For example, if you call `get_transport_number`, `pyEQL` will search the property database +for diffusion coefficient data to use in the calculation. No user action is needed. + +If you want to search the database yourself, or to inspect the values that `pyEQL` uses for a particular parameter, you +can do so via the `get_property` method. First, create a `Solution` + +``` +>>> from pyEQL import Solution +>>> s1 = pyEQL.Solution +``` + +Next, call `get_property` with a solute name and the name of the property you need. Valid property names are any key +in the `Solute` schema. Nested keys can be separated by periods, e.g. "model_parameters.activity_pitzer": + +``` +>>> s1.get_property('Mg+2', 'transport.diffusion_coefficient') + +``` + +If the property exists, it will be returned as a [`pint`](https://pint.readthedocs.io/en/stable/) `Quantity` object, which you can convert to specific units if needed, e.g. + +``` +>>> s1.get_property('Mg+2', 'transport.diffusion_coefficient').to('m**2/s') + +``` + +If the property does not exist in the database, `None` will be returned. + +``` +>>> s1.get_property('Mg+2', 'transport.randomproperty') +>>> +``` + +Although the database contains additional context about each and every property value, such as a citation, this information is not currently exposed via the `Solution` interface. Richer methods for exploring and adding to the database may be added in the future. + +## Species included + +The database currently contains one or more physichochemical properties for each of the solutes listed below. More detailed information about which properties are available for which solutes may be added in the future. + + - Ac[+3] + - Ag(CN)2[-1] + - AgNO3(aq) + - Ag[+1] + - Ag[+2] + - Ag[+3] + - Al2(SO4)3(aq) + - Al[+3] + - AsO4[-3] + - Au(CN)2[-1] + - Au(CN)4[-1] + - Au[+1] + - Au[+2] + - Au[+3] + - B(H5C6)4[-1] + - B(OH)3(aq) + - B(OH)4[-1] + - BF4[-1] + - BO2[-1] + - Ba(ClO4)2(aq) + - Ba(NO3)2(aq) + - BaBr2(aq) + - BaC4O.3H2O(aq) + - BaCl2(aq) + - BaI2(aq) + - Ba[+2] + - BeSO4(aq) + - Be[+2] + - Bi[+3] + - BrO3[-1] + - Br[-0.33333333] + - Br[-1] + - C2N3[-1] + - CH3COO[-1] + - CNO[-1] + - CN[-1] + - CO3[-2] + - CSN[-1] + - CSeN[-1] + - Ca(ClO4)2(aq) + - Ca(NO3)2(aq) + - CaBr2(aq) + - CaCl2(aq) + - CaI2(aq) + - Ca[+2] + - Cd(ClO4)2(aq) + - Cd(NO2)2(aq) + - Cd(NO3)2(aq) + - CdSO4(aq) + - Cd[+2] + - CeCl3(aq) + - Ce[+3] + - Ce[+4] + - ClO2[-1] + - ClO3[-1] + - ClO4[-1] + - Cl[-1] + - Co(CN)6[-3] + - Co(H3N)6[-3] + - Co(NO3)2(aq) + - CoBr2(aq) + - CoCl2(aq) + - CoI2(aq) + - Co[+2] + - Co[+3] + - Cr(NO3)3(aq) + - CrCl3(aq) + - CrO4[-2] + - Cr[+2] + - Cr[+3] + - Cs2SO4(aq) + - CsBr(aq) + - CsCl(aq) + - CsF(aq) + - CsHC2O.1H2O(aq) + - CsI(aq) + - CsNO2(aq) + - CsNO3(aq) + - CsOH(aq) + - Cs[+1] + - Cu(NO3)2(aq) + - CuCl2(aq) + - CuSO4(aq) + - Cu[+1] + - Cu[+2] + - Cu[+3] + - Dy[+2] + - Dy[+3] + - Er[+2] + - Er[+3] + - Eu(NO3)3(aq) + - EuCl3(aq) + - Eu[+2] + - Eu[+3] + - F[-1] + - Fe(CN)6[-3] + - Fe(CN)6[-4] + - FeCl2(aq) + - FeCl3(aq) + - Fe[+2] + - Fe[+3] + - Ga[+3] + - GdCl3(aq) + - Gd[+3] + - Ge[+2] + - H2CO3(aq) + - H2O(aq) + - H2SNO3[-1] + - H2SO4(aq) + - H3O[+1] + - H4BrN(aq) + - H4IN(aq) + - H4N2O3(aq) + - H4NCl(aq) + - H4NClO4(aq) + - H4N[+1] + - H4SNO4(aq) + - H5C6O7[-3] + - H5N2[+1] + - H8S(NO2)2(aq) + - HBr(aq) + - HCO2[-1] + - HCO3[-1] + - HCl(aq) + - HClO4(aq) + - HF2[-1] + - HI(aq) + - HNO3(aq) + - HO2[-1] + - HOsO5[-1] + - HSO3[-1] + - HSO4[-1] + - HS[-1] + - HSeO3[-1] + - H[+1] + - Hf[+4] + - Hg[+2] + - Ho[+2] + - Ho[+3] + - IO3[-1] + - IO4[-1] + - I[-1] + - In[+1] + - In[+2] + - In[+3] + - IrO4[-1] + - Ir[+3] + - K2CO3(aq) + - K2PHO4(aq) + - K2SO4(aq) + - K3Fe(CN)6(aq) + - K3PO4(aq) + - K4Fe(CN)6(aq) + - KBr(aq) + - KBrO3(aq) + - KCSN(aq) + - KCl(aq) + - KClO3(aq) + - KClO4(aq) + - KCrO4(aq) + - KF(aq) + - KHC2O.1H2O(aq) + - KHCO3(aq) + - KI(aq) + - KNO2(aq) + - KNO3(aq) + - KOH(aq) + - KPO3.1H2O(aq) + - K[+1] + - La(NO3)3(aq) + - LaCl3(aq) + - La[+3] + - Li2SO4(aq) + - LiBr(aq) + - LiCl(aq) + - LiClO4(aq) + - LiHC2O.1H2O(aq) + - LiI(aq) + - LiNO2(aq) + - LiNO3(aq) + - LiOH(aq) + - Li[+1] + - Lu[+3] + - Mg(ClO4)2(aq) + - Mg(NO3)2(aq) + - MgBr2(aq) + - MgC4O.3H2O(aq) + - MgCl2(aq) + - MgI2(aq) + - MgSO4(aq) + - Mg[+2] + - MnCl2(aq) + - MnO4[-1] + - MnSO4(aq) + - Mn[+2] + - Mn[+3] + - MoO4[-2] + - Mo[+3] + - NO2[-1] + - NO3[-1] + - N[-0.33333333] + - Na2CO3(aq) + - Na2PHO4(aq) + - Na2S2O3(aq) + - Na2SO4(aq) + - Na3PO4(aq) + - NaBr(aq) + - NaBrO3(aq) + - NaCSN(aq) + - NaCl(aq) + - NaClO4(aq) + - NaCrO4(aq) + - NaF(aq) + - NaHC2O.1H2O(aq) + - NaHC3.2H2O(aq) + - NaHCO2(aq) + - NaHCO3(aq) + - NaI(aq) + - NaNO2(aq) + - NaNO3(aq) + - NaOH(aq) + - NaPO3.1H2O(aq) + - Na[+1] + - Nb[+3] + - Nd(NO3)3(aq) + - NdCl3(aq) + - Nd[+2] + - Nd[+3] + - Ni(NO3)2(aq) + - NiCl2(aq) + - NiSO4(aq) + - Ni[+2] + - Ni[+3] + - Np[+3] + - Np[+4] + - OH[-1] + - Os[+3] + - P(HO2)2[-1] + - P(OH)2[-1] + - P2O7[-4] + - P3O10[-5] + - PF6[-1] + - PH9(NO2)2(aq) + - PHO4[-2] + - PO3F[-2] + - PO3[-1] + - PO4[-3] + - Pa[+3] + - Pb(ClO4)2(aq) + - Pb(NO3)2(aq) + - Pb[+2] + - Pd[+2] + - Pm[+2] + - Pm[+3] + - Po[+2] + - PrCl3(aq) + - Pr[+2] + - Pr[+3] + - Pt[+2] + - Pu[+2] + - Pu[+4] + - Ra[+2] + - Rb2SO4(aq) + - RbBr(aq) + - RbCl(aq) + - RbF(aq) + - RbHC2O.1H2O(aq) + - RbI(aq) + - RbNO2(aq) + - RbNO3(aq) + - RbOH(aq) + - Rb[+1] + - ReO4[-1] + - Re[+1] + - Re[+3] + - Re[-1] + - Rh[+3] + - Ru[+2] + - Ru[+3] + - S2O3[-2] + - SO2[-1] + - SO3[-1] + - SO3[-2] + - SO4[-1] + - SO4[-2] + - S[-2] + - Sb(HO2)2[-1] + - Sb(OH)6[-1] + - ScCl3(aq) + - Sc[+2] + - Sc[+3] + - SeO3[-1] + - SeO4[-1] + - SeO4[-2] + - SiF6[-2] + - SmCl3(aq) + - Sm[+2] + - Sm[+3] + - Sn[+2] + - Sn[+4] + - Sr(ClO4)2(aq) + - Sr(NO3)2(aq) + - SrBr2(aq) + - SrCl2(aq) + - SrI2(aq) + - Sr[+2] + - Ta[+3] + - Tb[+3] + - TcO4[-1] + - Tc[+2] + - Tc[+3] + - Th(NO3)4(aq) + - Th[+4] + - Ti[+2] + - Ti[+3] + - Tl(ClO4)3(aq) + - Tl(NO2)3(aq) + - Tl(NO3)3(aq) + - TlH(C3O)2.4H2O(aq) + - Tl[+1] + - Tl[+3] + - Tm[+2] + - Tm[+3] + - U(ClO)2(aq) + - U(ClO5)2(aq) + - U(NO4)2(aq) + - UO2[+1] + - UO2[+2] + - USO6(aq) + - U[+3] + - U[+4] + - VO2[+1] + - V[+2] + - V[+3] + - WO4[-1] + - WO4[-2] + - W[+3] + - YCl3(aq) + - YNO3(aq) + - Y[+3] + - Yb[+2] + - Yb[+3] + - Zn(ClO4)2(aq) + - Zn(NO3)2(aq) + - ZnBr2(aq) + - ZnCl2(aq) + - ZnI2(aq) + - ZnSO4(aq) + - Zn[+2] + - Zr[+4] diff --git a/docs/database.rst b/docs/database.rst deleted file mode 100644 index 4b1cdd33..00000000 --- a/docs/database.rst +++ /dev/null @@ -1,165 +0,0 @@ -.. _database: - - -Database System -*************** - -pyEQL creates a database to collect various parameters needed to perform -it's calculations. pyEQL's default database includes a collection of the -following parameters for some common electrolytes: - - - Diffusion coefficients for 104 ions - - Pitzer model activity correction coefficients for 157 salts - - Pitzer model partial molar volume coefficients for 120 salts - - Jones-Dole "B" coefficients for 83 ions - - Hydrated and ionic radii for 23 ions - - Dielectric constant model parameters for 18 ions - - Partial molar volumes for 10 ions - -Basics -====== - -The Paramsdb class creates a container for parameters. Each parameter -is an object which contains not only the value, but also information about -the units, the reference, and the conditions of measurement. paramsdb() also -defines several methods that are helpful for retrieving parameters. - -pyEQL automatically initializes an instance of Paramsdb under the name 'db'. -You can access database methods like this: - -.. doctest:: - - >>> import pyEQL - >>> pyEQL.db - - >>> pyEQL.db.has_species('H+') - True - -Anytime a new solute is added to a solution, the search_parameters() method -is called. This method searches every database file within the search path -(by default, only pyEQL's built-in databases) for any parameters associated -with that solute, and adds them to the database. - -Adding your own Database Files -============================== - -Custom Search Paths -------------------- - -The database system is meant to be easily extensible. To include your own -parameters, first you need to add a directory of your choosing to the -search path. - -.. doctest:: - - >>> pyEQL.db.add_path('/home/user') - -You can always check to see which paths pyEQL is searching by using list_path(): - -.. doctest:: - - >>> pyEQL.db.list_path() - /database - /home/user - -Then, place your custom database file inside that directory. **NOTE: custom -database files are searched IN ADDITION TO the default databases.** You don't -need to re-create the information from the built-in files. Custom databases -only need to contain extra parameters that are not included already. - -File Format ------------ - -Databases are formatted as TAB-SEPARATED text files and carry the .tsv extension. -The intent of this format is to make database files easy to edit with common -spreadsheet software. - -.. warning:: If you open an existing or template database file for editing, some spreadsheet software will try to replace the tabs with commas when you save it again. pyEQL does NOT read comma-separated files. - -Since pyEQL compiles the database from multiple files, the intent is for each -file to contain values for one type of parameter (such as a diffusion coefficient) -from one source. The file can then list values of that parameter for a number of -different solutes. - -The upper section of each file contains information about the source of the -data, the units, the name of the parameter, and the conditions of measurement. -The top of each database file must, at a minimum, contain rows for 'Name' and 'Units'. -Preferably, other information such as conditions, notes and a reference are also supplied. -See `template.tsv` in the \database subdirectory for an example. - -The remainder of the file contains solute formulas in the first column (see -:ref:`chemistry`) and corresponding values of the parameter in the following columns. -Sets of parameters (such as activity correction coefficients) can be specified -by using more than one column. - -.. warning:: Currently there is no way to handle duplicated parameters. So if you supply a parameter with the same name as a built-in one, unexpected behavior may result. - -Special Names -------------- -The name of a parameter is used as a kind of index within pyEQL. Certain methods -expect certain parameter names. The following are the currently-used internal -names: - - - 'diffusion_coefficient' - diffusion coefficient - - 'model_parameters.activity_pitzer' - coefficients for the Pitzer model for activity correction - - 'pitzer_parameters_volume'- coefficients for the Pitzer model for partial molar volume - - 'erying_viscosity_coefficients' - coefficients for an Erying-type viscosity correction model - - 'partial_molar_volume'- the partial molar volume (used if Pitzer parameters are not available) - - 'hydrated_radius' - hydrated radius - - 'ionic_radius' - ionic radius - - 'jones_dole_B' - Jones-Dole "B" coefficient - -If you wish to supply these parameters for a custom solute not included in the built-in -database, make sure to format the name exactly the same way. - -You can also specify a custom parameter name, and retrieve it using the get_parameter() -method. If the solute is 'Na+' - -.. doctest:: - - >>> pyEQL.db.get_parameter('Na+','my_parameter_name') - -Viewing the Database -==================== - -You can view the entire contents of the database using the print_database() method. -Since pyEQL searches for parameters as they are added, the database will only -contain parameters for solutes that have actually been used during the execution -of your script. The output is organized by solute. - -.. doctest:: - - >>> pyEQL.db.print_database() - - >>> s1 = pyEQL.Solution([['Na+','0.5 mol/kg'],['Cl-','0.5 mol/kg']]) - >>> pyEQL.db.print_database() - Parameters for species Cl-: - -------------------------- - Parameter diffusion_coefficient - Diffusion Coefficient - ------------------------------------------- - Value: 2.032e-05 cm²/s - Conditions (T,P,Ionic Strength): 25 celsius, 1 atm, 0 - Notes: For most ions, increases 2-3% per degree above 25C - Reference: CRC Handbook of Chemistry and Physics, 92nd Ed., pp. 5-77 to 5-79 - - Parameter partial_molar_volume - Partial molar volume - ------------------------------------------- - Value: 21.6 cm³/mol - Conditions (T,P,Ionic Strength): 25 celsius, 1 atm, 0 - Notes: correction factor 5e-4 cm3/g-K - Reference: Durchschlag, H., Zipper, P., 1994. "Calculation of the Partial Molal Volume of Organic Compounds and Polymers." Progress in Colloid & Polymer Science (94), 20-39. - ... - -API Documentation (database.py) -=============================== - -.. automodule:: pyEQL.database - :members: - -API Documentation (parameter.py) -================================ - -.. automodule:: pyEQL.parameter - :members: diff --git a/docs/index.md b/docs/index.md index dc96c168..e36afab5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,38 +1,21 @@ -# pyEQL - -Add a short description here! - - -## Note - -> This is the main page of your project's [Sphinx] documentation. It is -> formatted in [Markdown]. Add additional pages by creating md-files in -> `docs` or rst-files (formatted in [reStructuredText]) and adding links to -> them in the `Contents` section below. -> -> Please check [Sphinx], [recommonmark] and [autostructify] for more information -> about how to document your project and how to configure your preferences. - - -## Contents - -* [Overview](readme) -* [License](license) -* [Authors](authors) -* [Changelog](changelog) -* [Module Reference](api/modules) - - -## Indices and tables - -```eval_rst -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` +--- +Date: '{{ today }}' +Release: '{{ release }}' +--- + +# Welcome to pyEQL's documentation! + +Contents: + +```{toctree} +:maxdepth: 2 + +installation +tutorial +class_solution +chemistry +database +utilities +contributing +internal ``` - -[Sphinx]: http://www.sphinx-doc.org/ -[Markdown]: https://daringfireball.net/projects/markdown/ -[reStructuredText]: http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html -[recommonmark]: https://recommonmark.readthedocs.io/en/latest -[autostructify]: https://recommonmark.readthedocs.io/en/latest/auto_structify.html diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index e0c602b9..00000000 --- a/docs/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -Welcome to pyEQL's documentation! -********************************* - -:Release: |release| -:Date: |today| - -Contents: - -.. toctree:: - :maxdepth: 2 - - installation - tutorial - contributing - chemistry - database - class_solution - class_solute - internal - module_functions diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 00000000..c68c9b87 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,91 @@ +(installation)= + +# Installation + +## Use a conda environment + +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. + + +:::{note} +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. +::: + +After installing `conda` / `mamba`, follow their instructions to create an environment. The steps should be similar to the following: + +1. Open your terminal (or "Anaconda prompt" or "Miniforge prompt" on Windows) +2. Pick a name for your environment (note: you can create many environments if you want) +3. type `conda create -n python=3.10` (if you install miniforge) or `mamba create -n python=3.10` (if you installed mambaforge) and press enter +4. After the environment is installed, type `conda activate ` / `mamba activate ` and press enter + +## pip install + +Once Python is installed and your environment is activated you can install `pyEQL` from [PyPi](https://pypi.python.org/pypi) by typing the following command: + +``` +pip install pyEQL +``` + +This should automatically pull in the required [dependencies](#other-dependencies) as well. + +:::{important} +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. +You can tell if this is the case by typing the following command: +::: + +``` +$ python --version +Python 2.7.12 +``` + +This means Python 2.x is installed. If you run 'pip install' it will point to the Python 2.7 installation, but pyEQL +only works on Python 3. So, try this: + +``` +$ python3 --version +Python 3.9.7 +``` + +To get to Python 3.x, you have to type 'python3'. In this case, you would run 'pip3 install' +::: + +## Other dependencies + +pyEQL also requires the following packages: + +- [pint](https://github.com/hgrecco/pint) - for automated unit conversion +- [pymatgen](https://github.com/materialsproject/pymatgen/) - used to interpret chemical formulas +- [iapws](https://github.com/jjgomera/iapws/) - used to calculate the properties of water +- [monty](https://github.com/materialsvirtuallab/monty) - used for saving and loading `Solution` objects to files +- [maggma](https://materialsproject.github.io/maggma/) - used by the internal property database +- [scipy](http://scipy.org/) +- [numpy](http://numpy.org/) + +If you use pip to install pyEQL (recommended), they should be installed automatically. + + +## Installing the development branch + +If you want to use the bleeding edge (and potentially unstable!) development branch instead of the latest stable release, you can substitute the following for the above 'pip install' command: + +``` +pip install git+https://github.com/rkingsbury/pyEQL.git@develop +``` + +## Manually install via Git + +Simply navigate to a directory of your choice on your computer and clone the repository by executing the following terminal command: + +``` +git clone https://github.com/rkingsbury/pyEQL +``` + +Then install by executing: + +``` +pip install -e pyEQL +``` + +:::{note} +You may have to run 'pip3' rather than 'pip'. See the note in the [pip install](#pip-install) section. +::: diff --git a/docs/installation.rst b/docs/installation.rst deleted file mode 100644 index 311e6a3d..00000000 --- a/docs/installation.rst +++ /dev/null @@ -1,65 +0,0 @@ -.. _installation: - - -Installation -************ - -Dependencies -============ - -pyEQL requires Python 3.8 or greater. We highly recommend the Anaconda distribution of Python, which bundles many other -scientific computing packages and is easier to install (especially on Windows). You can download it at -https://www.anaconda.com/products/distribution. - -pyEQL also requires the following packages: - - * `pint `_ - * `scipy `_ - * `pymatgen `_ - * `iapws `_ - * `monty `_ - -If you use pip to install pyEQL (recommended), they should be installed automatically. - -Automatically install via pip and PyPI -====================================== - -Once Python is installed, The `Python Package Index `_ repository will allow installation -to be done easily from the command line as follows:: - - pip install pyEQL - -This should automatically pull in the required dependencies as well. - -.. note:: You may have to run 'pip3' rather than 'pip' if you intend to use your system's default Python installation - rather than Anaconda. For example, on many Linux and Mac systems Python 2.x and Python 3.x are installed side-by-side. - You can tell if this is the case on your system by going to a command line and typing 'python' like so:: - - $ python --version - Python 2.7.12 - - This means Python 2.x is installed. If you run 'pip install' it will point to the Python 2.7 installation, but pyEQL - only works on Python 3. So, try this:: - - $ python3 --version - Python 3.9.7 - - To get to Python 3.x, you have to type 'python3'. In this case, you would run 'pip3 install' - -Installing the development branch -================================= -If you want to use the bleeding edge (and potentially unstable!) development branch instead of the latest stable release, you can substitute the following for the above 'pip install' command:: - - pip install git+https://github.com/rkingsbury/pyEQL.git@develop - -Manually install via Git -======================== -Simply navigate to a directory of your choice on your computer and clone the repository by executing the following terminal command:: - - git clone https://github.com/rkingsbury/pyEQL - -Then install by executing:: - - pip install -e pyEQL - -.. note:: You may have to run 'pip3' rather than 'pip'. See the note in the Automatic installation section. diff --git a/docs/internal.md b/docs/internal.md new file mode 100644 index 00000000..26bfe65b --- /dev/null +++ b/docs/internal.md @@ -0,0 +1,28 @@ +(internal)= + +# Internal module reference + +These internal modules are used by `Solution` but typically are not directly accessed by the user. + +## Salt analysis module + +```{eval-rst} +.. automodule:: pyEQL.salt_ion_match + :members: + :private-members: +``` + +## Activity Correction module + +```{eval-rst} +.. automodule:: pyEQL.activity_correction + :members: + :private-members: +``` + +## Speciation Engines module + +```{eval-rst} +.. automodule:: pyEQL.engines + :members: +``` diff --git a/docs/internal.rst b/docs/internal.rst deleted file mode 100644 index cfbb8d70..00000000 --- a/docs/internal.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _internal: - -Internal Reference Documentation -******************************** - -Activity Correction API -======================= - -.. automodule:: pyEQL.activity_correction - :members: - :private-members: - - -Water Properties API -==================== - -.. automodule:: pyEQL.water_properties - :members: diff --git a/docs/license.md b/docs/license.md index b231201e..fc3bb6bb 100644 --- a/docs/license.md +++ b/docs/license.md @@ -1,5 +1,7 @@ +(license)= + # License -```{literalinclude} ../LICENSE.txt -:language: text +```{eval-rst} +.. include:: ../LICENSE.txt ``` diff --git a/docs/license.rst b/docs/license.rst deleted file mode 100644 index 3989c513..00000000 --- a/docs/license.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. _license: - -======= -License -======= - -.. include:: ../LICENSE.txt diff --git a/docs/module_functions.rst b/docs/module_functions.rst deleted file mode 100644 index 93764118..00000000 --- a/docs/module_functions.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. _module_functions: - -Functions Module -**************** - -.. automodule:: pyEQL.functions - :members: diff --git a/docs/pyEQL.solution.rst b/docs/pyEQL.solution.rst new file mode 100644 index 00000000..111e91c5 --- /dev/null +++ b/docs/pyEQL.solution.rst @@ -0,0 +1,29 @@ +pyEQL.solution +============== + +.. automodule:: pyEQL.solution + + + + + + + + + + + + .. rubric:: Classes + + .. autosummary:: + + Solution + + + + + + + + + diff --git a/docs/requirements.txt b/docs/requirements.txt index eef58a58..4b0aece4 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,12 +1,13 @@ # Requirements file for ReadTheDocs, check .readthedocs.yml. # To build the module reference correctly, make sure every external package # under `install_requires` in `setup.cfg` is also listed here! -iapws -importlib-metadata; python_version<"3.8" -monty pint -pymatgen>=2022.0.17 -recommonmark scipy +pymatgen>=2022.0.17 +iapws +monty +maggma sphinx>=3.2.1 sphinx-rtd-theme +myst-parser[linkify] +git+https://github.com/bashtage/sphinx-material.git # Material theme diff --git a/docs/tutorial.md b/docs/tutorial.md new file mode 100644 index 00000000..8c299f3a --- /dev/null +++ b/docs/tutorial.md @@ -0,0 +1,124 @@ +(tutorial)= + +# Tutorial + +pyEQL creates a new type (`Solution` class) to represent a chemical solution. +It also comes pre-loaded with a database of diffusion coefficients, activity +correction parameters, and other data on a variety of common electrolytes. +Virtually all of the user-facing functions in `pyEQL` are accessed through the +`Solution` class. + +## Creating a Solution Object + +Create a Solution object by invoking the Solution class: + +```{eval-rst} +.. doctest:: + + >>> import pyEQL + >>> s1 = pyEQL.Solution() + >>> s1 + + +``` + +If no arguments are specified, `pyEQL` creates a 1-L solution of water at +pH 7 and 25 degC. + +More usefully, you can specify solutes and bulk properties: + +```{eval-rst} +.. doctest:: + + >>> s2 = pyEQL.Solution({'Na+':'0.5 mol/kg', 'Cl-': '0.5 mol/kg'}, pH=8, temperature = '20 degC', volume='8 L') +``` + +## Retrieving Solution Properties + +### Bulk Solution Properties + +pyEQL provides a variety of methods to calculate or look up bulk properties +like temperature, ionic strength, conductivity, and density. + +> ```pycon +> >>> s2.volume +> 8.071524653929277 liter +> >>> s2.density +> 1.0182802742389558 kilogram/liter +> >>> s2.conductivity +> 4.083570230022633 siemens/meter +> >>> s2.ionic_strength +> 0.500000505903012 mole/kilogram +> ``` + +### Individual Solute Properties + +You can also retrieve properties for individual solutes (or the solvent, water) + +```{eval-rst} +.. doctest:: + + >>> s2.get_amount('Na+','mol/L') + 0.4946847550064916 mole/liter + >>> s2.get_activity_coefficient('Na+) + 0.6838526233869155 + >>> s2.get_activity('Na+') + 0.3419263116934578 + >>> s2.get_property('Na+','transport.diffusion_coefficient') + 1.1206048116287536e-05 centimeter2/second +``` + +## Units-Aware Calculations using pint + +pyEQL uses [pint](https://github.com/hgrecco/pint) to perform units-aware calculations. The pint library creates +Quantity objects that contain both a magnitude and a unit. + +> ```pycon +> >>> from pyEQL import unit +> >>> test_qty = pyEQL.unit('1 kg/m**3') +> 1.0 kilogram/meter3 +> ``` + +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'). +In general, pint recognizes common abbreviations and SI prefixes. Compound units must follow Python math syntax (e.g. cm\*\*2 not cm2). + +Pint `Quantity` objects have several useful attributes. They can be converted to strings: + +> ```pycon +> >>> str(test_qty) +> '1.0 kg/m**3' +> ``` + +the magnitude, units, or dimensionality can be retrieved via attributes: + +> ```pycon +> >>> test_qty.magnitude +> 1.0 +> >>> test_qty.units +> +> >>> test_qty.dimensionality +> +> ``` + +See the [pint documentation](http://pint.readthedocs.io/) for more details on creating and manipulating `Quantity` objects. + +## Using `pyEQL` in your projects + +To access pyEQL's main features in your project all that is needed is an import statement: + +> ```pycon +> >>> import pyEQL +> ``` + +In order to directly create `Quantity` objects, you need to explicitly import the `unit` module: + +> ```pycon +> >>> from pyEQL import unit +> >>> test_qty = unit('1 kg/m**3') +> 1.0 kilogram/meter3 +> ``` + +:::{warning} +if you use `pyEQL` in conjunction with another module that also uses pint for units-aware calculations, you must convert all `Quantity` objects to strings before passing them to the other module, as pint cannot perform mathematical operations on units that belong to different "registries." See the [pint documentation](http://pint.readthedocs.io/) for more details. +::: diff --git a/docs/tutorial.rst b/docs/tutorial.rst deleted file mode 100644 index 6e5695eb..00000000 --- a/docs/tutorial.rst +++ /dev/null @@ -1,113 +0,0 @@ -.. _tutorial: - - -Tutorial -******** - -pyEQL creates a new type (`Solution` class) to represent a chemical solution. -It also comes pre-loaded with a database of diffusion coefficients, activity -correction parameters, and other data on a variety of common electrolytes. -Virtually all of the user-facing functions in pyEQL are accessed through the -`Solution` class. - -Creating a Solution Object -========================== - -Create a Solution object by invoking the Solution class: - -.. doctest:: - - >>> import pyEQL - >>> s1 = pyEQL.Solution() - >>> s1 - - - -If no arguments are specified, pyEQL creates a 1-L solution of water at -pH 7 and 25 degC. - -More usefully, you can specify solutes and bulk properties: - -.. doctest:: - - >>> s2 = pyEQL.Solution({'Na+':'0.5 mol/kg', 'Cl-': '0.5 mol/kg'},pH=8,temperature = '20 degC', volume='8 L') - -Retrieving Solution Properties -============================== - -Bulk Solution Properties --------------------------- - -pyEQL provides a variety of methods to calculate or look up bulk properties -like temperature, ionic strength, conductivity, and density. - - >>> s2.volume - 8.071524653929277 liter - >>> s2.density - 1.0182802742389558 kilogram/liter - >>> s2.conductivity - 4.083570230022633 siemens/meter - >>> s2.ionic_strength - 0.500000505903012 mole/kilogram - -Individual Solute Properties ----------------------------- - -You can also retrieve properties for individual solutes (or the solvent, water) - -.. doctest:: - - >>> s2.get_amount('Na+','mol/L') - 0.4946847550064916 mole/liter - >>> s2.get_activity_coefficient('Na+) - 0.6838526233869155 - >>> s2.get_activity('Na+') - 0.3419263116934578 - >>> s2.get_property('Na+','diffusion_coefficient') - 1.1206048116287536e-05 centimeter2/second - -Units-Aware Calculations using pint -=================================== - -pyEQL uses `pint `_ to perform units-aware calculations. The pint library creates -Quantity objects that contain both a magnitude and a unit. - - >>> from pyEQL import unit - >>> test_qty = pyEQL.unit('1 kg/m**3') - 1.0 kilogram/meter3 - -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'). -In general, pint recognizes common abbreviations and SI prefixes. Compound units must follow Python math syntax (e.g. cm**2 not cm2). - -Pint Quantity objects have several useful attributes. They can be converted to strings: - - >>> str(test_qty) - '1.0 kg/m**3' - -the magnitude, units, or dimensionality can be retrieved via attributes: - - >>> test_qty.magnitude - 1.0 - >>> test_qty.units - - >>> test_qty.dimensionality - - -See the `pint documentation `_ for more details on creating and manipulating Quantity objects. - - -Using pyEQL in your projects -============================ - -To access pyEQL's main features in your project all that is needed is an import statement: - - >>> import pyEQL - -In order to directly create Quantity objects, you need to explicitly import the `unit` module: - - >>> from pyEQL import unit - >>> test_qty = pyEQL.unit('1 kg/m**3') - 1.0 kilogram/meter3 - -.. warning:: if you use pyEQL in conjunction with another module that also uses pint for units-aware calculations, you must convert all Quantity objects to strings before passing them to the other module, as pint cannot perform mathematical operations on units that belong to different "registries." See the `pint documentation `_ for more details. diff --git a/docs/utilities.md b/docs/utilities.md new file mode 100644 index 00000000..9ec501d6 --- /dev/null +++ b/docs/utilities.md @@ -0,0 +1,8 @@ +(module-functions)= + +# Functions Module + +```{eval-rst} +.. automodule:: pyEQL.functions + :members: +``` diff --git a/setup.cfg b/setup.cfg index fa53e511..f56b56ce 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,7 +51,9 @@ python_requires = >=3.8 install_requires = importlib-metadata; python_version<"3.8" pint>=0.19 + numpy scipy + pint pymatgen>=2022.0.17 iapws monty @@ -77,6 +79,10 @@ testing = mypy ruff tox +docs = + sphinx>=3.2.1 + sphinx-rtd-theme + myst-parser[linkify] full = rich diff --git a/src/pyEQL/activity_correction.py b/src/pyEQL/activity_correction.py index 4afa1188..c0ca9c4f 100644 --- a/src/pyEQL/activity_correction.py +++ b/src/pyEQL/activity_correction.py @@ -30,13 +30,15 @@ def _debye_parameter_B(temperature="25 degC"): temperature : str Quantity, optional String representing the temperature of the solution. Defaults to '25 degC' if not specified. - Notes: + Notes ----- - The parameter B is equal to: [#]_ + The parameter B is equal to: .. math:: B = ( {8 \\pi N_A e^2 \\over 1000 \\epsilon k T} ) ^ {1 \\over 2} - .. [#] Bockris and Reddy. /Modern Electrochemistry/, vol 1. Plenum/Rosetta, 1977, p.210. + References + ---------- + Bockris and Reddy. /Modern Electrochemistry/, vol 1. Plenum/Rosetta, 1977, p.210. Examples: -------- @@ -75,13 +77,13 @@ def _debye_parameter_activity(temperature="25 degC"): temperature : str Quantity, optional String representing the temperature of the solution. Defaults to '25 degC' if not specified. - Returns: + Returns ------- Quantity The parameter A for use in the Debye-Huckel limiting law (base e) - Notes: + Notes ----- - The parameter A is equal to: [#]_ + The parameter A is equal to: .. math:: A^{\\gamma} = {e^3 ( 2 \\pi N_A {\\rho})^{0.5} \\over (4 \\pi \\epsilon_o \\epsilon_r k T)^{1.5}} @@ -91,9 +93,9 @@ def _debye_parameter_activity(temperature="25 degC"): value returned by 2.303. The value is often given in base 10 terms (0.509 at 25 degC) in older textbooks. - References: + References ---------- - .. [#] Archer, Donald G. and Wang, Peiming. "The Dielectric Constant of Water \ + Archer, Donald G. and Wang, Peiming. "The Dielectric Constant of Water \ and Debye-Huckel Limiting Law Slopes." /J. Phys. Chem. Ref. Data/ 19(2), 1990. Examples: @@ -102,8 +104,7 @@ def _debye_parameter_activity(temperature="25 degC"): 1.17499... See Also: - -------- - _debye_parameter_osmotic + :py:func:`_debye_parameter_osmotic` """ water_substance = IAPWS95( @@ -138,21 +139,20 @@ def _debye_parameter_osmotic(temperature="25 degC"): temperature : str Quantity, optional String representing the temperature of the solution. Defaults to '25 degC' if not specified. - Notes: + Notes ----- Not to be confused with the Debye-Huckel constant used for activity coefficients in the limiting law. Takes the value 0.392 at 25 C. - This constant is calculated according to: [#]_ [#]_ + This constant is calculated according to: [kim]_ [arch]_ .. math:: A^{\\phi} = {1 \\over 3} A^{\\gamma} - References: + References ---------- - .. [#] Kim, Hee-Talk and Frederick, William Jr, 1988. "Evaluation of Pitzer Ion Interaction Parameters of Aqueous \ - Electrolytes at 25 C. 1. Single Salt Parameters," + .. [kim] Kim, Hee-Talk and Frederick, William Jr, 1988. "Evaluation of Pitzer Ion Interaction Parameters of Aqueous Electrolytes at 25 C. 1. Single Salt Parameters," *J. Chemical Engineering Data* 33, pp.177-184. - .. [#] Archer, Donald G. and Wang, Peiming. "The Dielectric Constant of Water \ + .. [arch] Archer, Donald G. and Wang, Peiming. "The Dielectric Constant of Water \ and Debye-Huckel Limiting Law Slopes." /J. Phys. Chem. Ref. Data/ 19(2), 1990. Examples: @@ -161,8 +161,7 @@ def _debye_parameter_osmotic(temperature="25 degC"): 0.3916... See Also: - -------- - _debye_parameter_activity + :py:func:`_debye_parameter_activity` """ output = 1 / 3 * _debye_parameter_activity(temperature) @@ -180,10 +179,10 @@ def _debye_parameter_volume(temperature="25 degC"): temperature : str Quantity, optional String representing the temperature of the solution. Defaults to '25 degC' if not specified. - Notes: + Notes ----- Takes the value 1.8305 cm ** 3 * kg ** 0.5 / mol ** 1.5 at 25 C. - This constant is calculated according to: [#]_ + This constant is calculated according to: [1]_ .. math:: A_V = -2 A_{\\phi} R T [ {3 \\over \\epsilon} {{\\partial \\epsilon \\over \\partial p} \ } - {{1 \\over \\rho}{\\partial \\rho \\over \\partial p} }] @@ -197,14 +196,14 @@ def _debye_parameter_volume(temperature="25 degC"): of real data, but is required to give the correct result. The second term is equivalent to the inverse of the bulk modulus of water, which - is taken to be 2.2 GPa. [#]_ + is taken to be 2.2 GPa. [2]_ - References: + References ---------- - .. [#] Archer, Donald G. and Wang, Peiming. "The Dielectric Constant of Water \ + .. [1] Archer, Donald G. and Wang, Peiming. "The Dielectric Constant of Water \ and Debye-Huckel Limiting Law Slopes." /J. Phys. Chem. Ref. Data/ 19(2), 1990. - .. [#] http://hyperphysics.phy-astr.gsu.edu/hbase/permot3.html + .. [2] http://hyperphysics.phy-astr.gsu.edu/hbase/permot3.html Examples: -------- @@ -255,7 +254,7 @@ def get_activity_coefficient_debyehuckel(ionic_strength, formal_charge=1, temper temperature : str Quantity, optional String representing the temperature of the solution. Defaults to '25 degC' if not specified. - Returns: + Returns ------- Quantity The mean molal (mol/kg) scale ionic activity coefficient of solute, dimensionless. @@ -264,17 +263,17 @@ def get_activity_coefficient_debyehuckel(ionic_strength, formal_charge=1, temper -------- _debye_parameter_activity - Notes: + Notes ----- - Activity coefficient is calculated according to: [#]_ + Activity coefficient is calculated according to: .. math:: \\ln \\gamma = A^{\\gamma} z_i^2 \\sqrt I Valid only for I < 0.005 - References: + References ---------- - .. [#] Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, + Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 103. Wiley Interscience, 1996. """ @@ -300,7 +299,7 @@ def get_activity_coefficient_guntelberg(ionic_strength, formal_charge=1, tempera temperature : str Quantity, optional String representing the temperature of the solution. Defaults to '25 degC' if not specified. - Returns: + Returns ------- Quantity The mean molal (mol/kg) scale ionic activity coefficient of solute, dimensionless. @@ -309,17 +308,17 @@ def get_activity_coefficient_guntelberg(ionic_strength, formal_charge=1, tempera -------- _debye_parameter_activity - Notes: + Notes ------ - Activity coefficient is calculated according to: [#]_ + Activity coefficient is calculated according to: .. math:: \\ln \\gamma = A^{\\gamma} z_i^2 {\\sqrt I \\over (1 + \\sqrt I)} Valid for I < 0.1 - References: + References ---------- - .. [#] Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, + Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 103. Wiley Interscience, 1996. """ @@ -344,13 +343,13 @@ def get_activity_coefficient_davies(ionic_strength, formal_charge=1, temperature Parameters ---------- formal_charge : int, optional - The charge on the solute, including sign. Defaults to +1 if not specified. + The charge on the solute, including sign. Defaults to +1 if not specified. ionic_strength : Quantity - The ionic strength of the parent solution, mol/kg + The ionic strength of the parent solution, mol/kg temperature : str Quantity, optional - String representing the temperature of the solution. Defaults to '25 degC' if not specified. + String representing the temperature of the solution. Defaults to '25 degC' if not specified. - Returns: + Returns ------- Quantity The mean molal (mol/kg) scale ionic activity coefficient of solute, dimensionless. @@ -359,17 +358,17 @@ def get_activity_coefficient_davies(ionic_strength, formal_charge=1, temperature -------- _debye_parameter_activity - Notes: + Notes ----- - Activity coefficient is calculated according to: [#]_ + Activity coefficient is calculated according to: .. math:: \\ln \\gamma = A^{\\gamma} z_i^2 ({\\sqrt I \\over (1 + \\sqrt I)} + 0.2 I) Valid for 0.1 < I < 0.5 - References: + References ---------- - .. [#] Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, + Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 103. Wiley Interscience, 1996. """ @@ -430,7 +429,7 @@ def get_activity_coefficient_pitzer( coefficient is assigned proper units of kg ** 0.5 / mol ** 0.5 after entry. - Returns: + Returns ------- Quantity The mean molal (mol/kg) scale ionic activity coefficient of solute, dimensionless @@ -461,7 +460,7 @@ def get_activity_coefficient_pitzer( >>> get_activity_coefficient_pitzer(18*unit.Quantity('mol/kg'),18*unit.Quantity('mol/kg'),2,0,-0.01709,0.09198,0,0.000419,1,-1,1,1,b=1.2) 0.16241 ... - References: + References ---------- Scharge, T., Munoz, A.G., and Moog, H.C. (2012). Activity Coefficients of Fission Products in Highly Salinary Solutions of Na+, K+, Mg2+, Ca2+, Cl-, and SO42- : Cs+. @@ -561,7 +560,7 @@ def get_apparent_volume_pitzer( coefficient is assigned proper units of kg ** 0.5 / mol ** 0.5 after entry. - Returns: + Returns ------- Quantity The apparent molar volume of the solute, cm ** 3 / mol @@ -600,7 +599,7 @@ def get_apparent_volume_pitzer( 0.22595 ... - References: + References ---------- May, P. M., Rowland, D., Hefter, G., & Königsberger, E. (2011). A Generic and Updatable Pitzer Characterization of Aqueous Binary Electrolyte Solutions at 1 bar and 25 °C. @@ -655,7 +654,7 @@ def _pitzer_f1(x): .. math:: f(x) = 2 [ 1- (1+x) \\exp(-x)] / x ^ 2 - References: + References ---------- Scharge, T., Munoz, A.G., and Moog, H.C. (2012). Activity Coefficients of Fission Products in Highly Salinary Solutions of Na+, K+, Mg2+, Ca2+, Cl-, and SO42- : Cs+. @@ -677,7 +676,7 @@ def _pitzer_f2(x): .. math:: f(x) = -{2 \\over x ^ 2} [ 1 - ({1+x+ x^2 \\over 2}) \\exp(-x)] - References: + References ---------- Scharge, T., Munoz, A.G., and Moog, H.C. (2012). Activity Coefficients of Fission Products in Highly Salinary Solutions of Na+, K+, Mg2+, Ca2+, Cl-, and SO42- : Cs+. @@ -709,12 +708,12 @@ def _pitzer_B_MX(ionic_strength, alpha1, alpha2, beta0, beta1, beta2): Coefficients for the Pitzer model. These ion-interaction parameters are specific to each salt system. - Returns: + Returns ------- float The B_MX parameter for the Pitzer ion interaction model. - References: + References ---------- Scharge, T., Munoz, A.G., and Moog, H.C. (2012). Activity Coefficients of Fission Products in Highly Salinary Solutions of Na+, K+, Mg2+, Ca2+, Cl-, and SO42- : Cs+. @@ -793,12 +792,12 @@ def _pitzer_B_phi(ionic_strength, alpha1, alpha2, beta0, beta1, beta2): Coefficients for the Pitzer model. These ion-interaction parameters are specific to each salt system. - Returns: + Returns ------- float The B^Phi parameter for the Pitzer ion interaction model. - References: + References ---------- Scharge, T., Munoz, A.G., and Moog, H.C. (2012). Activity Coefficients of Fission Products in Highly Salinary Solutions of Na+, K+, Mg2+, Ca2+, Cl-, and SO42- : Cs+. @@ -886,12 +885,12 @@ def _pitzer_log_gamma( b: number, optional Coefficient. Usually set equal to 1.2 kg ** 0.5 / mol ** 0.5 and considered independent of temperature and pressure - Returns: + Returns ------- float The natural logarithm of the binary activity coefficient calculated by the Pitzer ion interaction model. - References: + References ---------- Kim, H., & Jr, W. F. (1988). Evaluation of Pitzer ion interaction parameters of aqueous electrolytes at 25 degree C. 1. Single salt parameters. @@ -955,7 +954,7 @@ def get_osmotic_coefficient_pitzer( coefficient is assigned proper units of kg ** 0.5 / mol ** 0.5 after entry. - Returns: + Returns ------- Quantity The osmotic coefficient of water, dimensionless @@ -990,7 +989,7 @@ def get_osmotic_coefficient_pitzer( >>> get_osmotic_coefficient_pitzer(18*unit.Quantity('mol/kg'),18*unit.Quantity('mol/kg'),2,0,-0.01709,0.09198,0,0.000419,1,-1,1,1,b=1.2) 0.5556 ... - References: + References ---------- Scharge, T., Munoz, A.G., and Moog, H.C. (2012). Activity Coefficients of Fission Products in Highly Salinary Solutions of Na+, K+, Mg2+, Ca2+, Cl-, and SO42- : Cs+. diff --git a/src/pyEQL/engines.py b/src/pyEQL/engines.py index ddd09308..deecf0cc 100644 --- a/src/pyEQL/engines.py +++ b/src/pyEQL/engines.py @@ -32,7 +32,7 @@ def get_activity_coefficient(self, solution, solute): solution: pyEQL Solution object solute: str identifying the solute of interest - Returns: + Returns Quantity: dimensionless quantity object Raises: @@ -48,7 +48,7 @@ def get_osmotic_coefficient(self, solution): Args: solution: pyEQL Solution object - Returns: + Returns Quantity: dimensionless molal scale osmotic coefficient Raises: @@ -64,7 +64,7 @@ def get_solute_volume(self): Args: solution: pyEQL Solution object - Returns: + Returns Quantity: solute volume in L Raises: @@ -82,7 +82,7 @@ def equilibrate(self, solution): Args: solution: pyEQL Solution object - Returns: + Returns Nothing. The speciation of the Solution is modified in-place. Raises: @@ -125,9 +125,9 @@ class NativeEOS(EOS): def get_activity_coefficient(self, solution, solute): """ - Whenever the appropriate parameters are available, the Pitzer model [#]_ is used. + Whenever the appropriate parameters are available, the Pitzer model [may]_ is used. If no Pitzer parameters are available, then the appropriate equations are selected - according to the following logic: [#]_. + according to the following logic: [stumm]_. I <= 0.0005: Debye-Huckel equation 0.005 < I <= 0.1: Guntelberg approximation @@ -137,7 +137,7 @@ def get_activity_coefficient(self, solution, solute): The ionic strength, activity coefficients, and activities are all calculated based on the molal (mol/kg) concentration scale. If a different scale is given as input, then the molal-scale activity coefficient :math:`\\gamma_\\pm` is - converted according to [#]_ + converted according to [rbs]_ .. math:: f_\\pm = \\gamma_\\pm * (1 + M_w \\sum_i \\nu_i \\m_i) @@ -147,56 +147,51 @@ def get_activity_coefficient(self, solution, solute): the molecular weight of water, the summation represents the total molality of all solute species, :math:`y_\\pm` is the molar activity coefficient, :math:`\\rho_w` is the density of pure water, :math:`m` and :math:`C` are - the molal and molar concentrations of the chosen salt (not individual solute), - respectively. - - Parameters - ---------- - solute : str - String representing the name of the solute of interest - scale : str, optional - The concentration scale for the returned activity coefficient. - Valid options are "molal", "molar", and "rational" (i.e., mole fraction). - By default, the molal scale activity coefficient is returned. - verbose : bool, optional - If True, pyEQL will print a message indicating the parent salt - that is being used for activity calculations. This option is - useful when modeling multicomponent solutions. False by default. - - Returns: - ------- - The mean ion activity coefficient of the solute in question on the selected scale. + the molal and molar concentrations of the chosen salt (not individual solute), respectively. + + Args: + solute: String representing the name of the solute of interest + scale: The concentration scale for the returned activity coefficient. + Valid options are "molal", "molar", and "rational" (i.e., mole fraction). + By default, the molal scale activity coefficient is returned. + verbose: If True, pyEQL will print a message indicating the parent salt + that is being used for activity calculations. This option is + useful when modeling multicomponent solutions. False by default. + + Returns + The mean ion activity coefficient of the solute in question on the selected scale. See Also: - -------- - get_ionic_strength - get_salt - activity_correction.get_activity_coefficient_debyehuckel - activity_correction.get_activity_coefficient_guntelberg - activity_correction.get_activity_coefficient_davies - activity_correction.get_activity_coefficient_pitzer + get_ionic_strength + get_salt + activity_correction.get_activity_coefficient_debyehuckel + activity_correction.get_activity_coefficient_guntelberg + activity_correction.get_activity_coefficient_davies + activity_correction.get_activity_coefficient_pitzer Notes: - ----- - For multicomponent mixtures, pyEQL implements the "effective Pitzer model" - presented by Mistry et al. [#]_. In this model, the activity coefficient - of a salt in a multicomponent mixture is calculated using an "effective - molality," which is the molality that would result in a single-salt - mixture with the same total ionic strength as the multicomponent solution. - - .. math:: m_effective = 2 I \\over (\\nu_+ z_+^2 + \\nu_- z_- ^2) - - References: - ---------- - .. [#] May, P. M., Rowland, D., Hefter, G., & Königsberger, E. (2011). + For multicomponent mixtures, pyEQL implements the "effective Pitzer model" + presented by Mistry et al. [mistry]_. In this model, the activity coefficient + of a salt in a multicomponent mixture is calculated using an "effective + molality," which is the molality that would result in a single-salt + mixture with the same total ionic strength as the multicomponent solution. + + .. math:: m_effective = 2 I \\over (\\nu_{+} z_{+}^2 + \\nu{_}- z_{-} ^2) + + References + + .. [may] May, P. M., Rowland, D., Hefter, G., & Königsberger, E. (2011). A Generic and Updatable Pitzer Characterization of Aqueous Binary Electrolyte Solutions at 1 bar and 25 °C. *Journal of Chemical & Engineering Data*, 56(12), 5066-5077. doi:10.1021/je2009329 - .. [#] Stumm, Werner and Morgan, James J. *Aquatic Chemistry*, 3rd ed, + .. [stumm] Stumm, Werner and Morgan, James J. *Aquatic Chemistry*, 3rd ed, pp 165. Wiley Interscience, 1996. - .. [#] Robinson, R. A.; Stokes, R. H. Electrolyte Solutions: Second Revised + .. [rbs] Robinson, R. A.; Stokes, R. H. Electrolyte Solutions: Second Revised Edition; Butterworths: London, 1968, p.32. + + .. [mistry] Mistry, K. H.; Hunter, H. a.; Lienhard V, J. H. Effect of composition and nonideal solution behavior on + desalination calculations for mixed electrolyte solutions with comparison to seawater. Desalination 2013, 318, 34-47. """ verbose = False @@ -316,79 +311,74 @@ def get_osmotic_coefficient(self, solution): Return the *molal scale* osmotic coefficient of solute, given a Solution object. - Osmotic coefficient is calculated using the Pitzer model.[#]_ If appropriate parameters for + Osmotic coefficient is calculated using the Pitzer model. [may]_ If appropriate parameters for the model are not available, then pyEQL raises a WARNING and returns an osmotic coefficient of 1. If the 'rational' scale is given as input, then the molal-scale osmotic - coefficient :math:`\\phi` is converted according to [#]_ + coefficient :math:`\\phi` is converted according to [rbs]_ - .. math:: g = - \\phi * M_w \\sum_i \\nu_i \\m_i) / \\ln x_w + .. math:: g = - \\phi * M_{w} \\sum_{i} \\nu_{i} \\m_{i}) / \\ln x_{w} - where :math:`g` is the rational osmotic coefficient, :math:`M_w` is + where :math:`g` is the rational osmotic coefficient, :math:`M_{w}` is the molecular weight of water, the summation represents the total molality of - all solute species, and :math:`x_w` is the mole fraction of water. + all solute species, and :math:`x_{w}` is the mole fraction of water. - Parameters - ---------- - scale : str, optional - The concentration scale for the returned osmotic coefficient. - Valid options are "molal", "rational" (i.e., mole fraction), - and "fugacity". By default, the molal scale osmotic coefficient is returned. + Args: + scale: + The concentration scale for the returned osmotic coefficient. Valid options are "molal", + "rational" (i.e., mole fraction), and "fugacity". By default, the molal scale osmotic + coefficient is returned. - Returns: - ------- - Quantity : - The osmotic coefficient + Returns + Quantity: + The osmotic coefficient See Also: - -------- - get_water_activity - get_ionic_strength - get_salt + get_water_activity + get_ionic_strength + get_salt Notes: - ----- - For multicomponent mixtures, pyEQL adopts the "effective Pitzer model" - presented by Mistry et al. [#]_. In this approach, the osmotic coefficient of - each individual salt is calculated using the normal Pitzer model based - on its respective concentration. Then, an effective osmotic coefficient - is calculated as the concentration-weighted average of the individual - osmotic coefficients. - - For example, in a mixture of 0.5 M NaCl and 0.5 M KBr, one would calculate - the osmotic coefficient for each salt using a concentration of 0.5 M and - an ionic strength of 1 M. Then, one would average the two resulting - osmotic coefficients to obtain an effective osmotic coefficient for the - mixture. - - (Note: in the paper referenced below, the effective - osmotic coefficient is determined by weighting using the "effective molality" - rather than the true molality. Subsequent checking and correspondence with - the author confirmed that the weight factor should be the true molality, and - that is what is implemented in pyEQL.) - - References: - ---------- - .. [#] May, P. M., Rowland, D., Hefter, G., & Königsberger, E. (2011). - A Generic and Updatable Pitzer Characterization of Aqueous Binary Electrolyte Solutions at 1 bar and 25 °C. - *Journal of Chemical & Engineering Data*, 56(12), 5066-5077. doi:10.1021/je2009329 + For multicomponent mixtures, pyEQL adopts the "effective Pitzer model" + presented by Mistry et al. [mstry]_. In this approach, the osmotic coefficient of + each individual salt is calculated using the normal Pitzer model based + on its respective concentration. Then, an effective osmotic coefficient + is calculated as the concentration-weighted average of the individual + osmotic coefficients. - .. [#] Robinson, R. A.; Stokes, R. H. Electrolyte Solutions: Second Revised - Edition; Butterworths: London, 1968, p.32. + For example, in a mixture of 0.5 M NaCl and 0.5 M KBr, one would calculate + the osmotic coefficient for each salt using a concentration of 0.5 M and + an ionic strength of 1 M. Then, one would average the two resulting + osmotic coefficients to obtain an effective osmotic coefficient for the + mixture. + + (Note: in the paper referenced below, the effective osmotic coefficient is determined by weighting + using the "effective molality" rather than the true molality. Subsequent checking and correspondence with + the author confirmed that the weight factor should be the true molality, and that is what is implemented + in pyEQL.) + + References - .. [#] Mistry, K. H.; Hunter, H. a.; Lienhard V, J. H. Effect of composition and nonideal solution behavior on desalination calculations for mixed - electrolyte solutions with comparison to seawater. Desalination 2013, 318, 34-47. + .. [may] May, P. M., Rowland, D., Hefter, G., & Königsberger, E. (2011). + A Generic and Updatable Pitzer Characterization of Aqueous Binary Electrolyte Solutions at 1 bar and + 25 °C. Journal of Chemical & Engineering Data, 56(12), 5066-5077. doi:10.1021/je2009329 + + .. [rbs] Robinson, R. A.; Stokes, R. H. Electrolyte Solutions: Second Revised + Edition; Butterworths: London, 1968, p.32. + + .. [mstry] Mistry, K. H.; Hunter, H. a.; Lienhard V, J. H. Effect of composition and nonideal solution + behavior on desalination calculations for mixed electrolyte solutions with comparison to seawater. Desalination 2013, 318, 34-47. Examples: - -------- - >>> s1 = pyEQL.Solution([['Na+','0.2 mol/kg'],['Cl-','0.2 mol/kg']]) - >>> s1.get_osmotic_coefficient() - - - >>> s1 = pyEQL.Solution([['Mg+2','0.3 mol/kg'],['Cl-','0.6 mol/kg']],temperature='30 degC') - >>> s1.get_osmotic_coefficient() - + + >>> s1 = pyEQL.Solution([['Na+','0.2 mol/kg'],['Cl-','0.2 mol/kg']]) + >>> s1.get_osmotic_coefficient() + + + >>> s1 = pyEQL.Solution([['Mg+2','0.3 mol/kg'],['Cl-','0.6 mol/kg']],temperature='30 degC') + >>> s1.get_osmotic_coefficient() + """ ionic_strength = solution.ionic_strength diff --git a/src/pyEQL/equilibrium.py b/src/pyEQL/equilibrium.py index e5226fc2..1f6feb4f 100644 --- a/src/pyEQL/equilibrium.py +++ b/src/pyEQL/equilibrium.py @@ -63,12 +63,12 @@ def adjust_temp_vanthoff(equilibrium_constant, enthalpy, temperature, reference_ reference_temperature : Quantity, optional the temperature at which equilibrium_constant is valid. (25 degrees C if omitted). - Returns: + Returns ------- float adjusted reaction equilibrium constant - Notes: + Notes ----- This function implements the Van't Hoff equation to adjust measured equilibrium constants to other temperatures. @@ -77,9 +77,11 @@ def adjust_temp_vanthoff(equilibrium_constant, enthalpy, temperature, reference_ ln(K2 / K1) = {\delta H \over R} ( {1 \over T_1} - {1 \over T_2} ) This implementation assumes that the enthalpy is independent of temperature - over the range of interest.[1] + over the range of interest. - .. [1] Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 53. + References + ---------- + Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 53. Wiley Interscience, 1996. Examples: @@ -127,7 +129,7 @@ def adjust_temp_arrhenius( the temperature at which equilibrium_constant is valid Defaults to 25 degrees C if omitted. - Returns: + Returns ------- Quantity The adjusted reaction equilibrium constant @@ -136,16 +138,20 @@ def adjust_temp_arrhenius( -------- kelvin - Notes: + Notes ----- This function implements the Arrhenius equation to adjust measured rate - constants to other temperatures. [1] + constants to other temperatures. + TODO - add better reference .. math:: - ln(K2 / K1) = {E_a \over R} ( {1 \over T_1} - {1 \over T_2} ) + ln(\\frac{K2}{K1} = \\frac{E_a}{R} ( \\frac{1}{T_{1}} - {\\frac{1}{T_2}} ) + + References + ---------- + + http://chemwiki.ucdavis.edu/Physical_Chemistry/Kinetics/Reaction_Rates/Temperature_Dependence_of_Reaction_Rates/Arrhenius_Equation - .. [1] http://chemwiki.ucdavis.edu/Physical_Chemistry/Kinetics/Reaction_Rates/Temperature_Dependence_of_Reaction_Rates/Arrhenius_Equation - TODO - add better reference Examples: -------- @@ -165,64 +171,59 @@ def adjust_temp_arrhenius( def alpha(n, pH, pKa_list): - """(int,number,list of numbers) - Returns the acid-base distribution coefficient (alpha) of an acid in the + """Returns the acid-base distribution coefficient (alpha) of an acid in the n-deprotonated form at a given pH. - Parameters - ---------- - n : int - The number of protons that have been lost by the desired form of the - acid. Also the subscript on the alpha value. E.g. for bicarbonate - (HCO3-), n=1 because 1 proton has been lost from the fully-protonated - carbonic acid (H2CO3) form. - pH : float or int - The pH of the solution. - pKa_list : list of floats or ints - The pKa values (negative log of equilibrium constants) for the acid - of interest. There must be a minimum of n pKa values in the list. - - Returns: - ------- + Parameters + ---------- + n : int + The number of protons that have been lost by the desired form of the + acid. Also the subscript on the alpha value. E.g. for bicarbonate + (HCO3-), n=1 because 1 proton has been lost from the fully-protonated + carbonic acid (H2CO3) form. + pH : float or int + The pH of the solution. + pKa_list : list of floats or ints + The pKa values (negative log of equilibrium constants) for the acid + of interest. There must be a minimum of n pKa values in the list. + + Returns + ------- float The fraction of total acid present in the specified form. - Notes: - ----- - The acid-base distribution coefficient is calculated as follows:[1] + Notes + ----- + The acid-base cient is calculated as follows: [stm]_ .. math:: - \alpha_n = {term_n \\over [H+]^n + k_{a1}[H+]^n-1 + k_{a1}k_{a2}[H+]^n-2 ... k_{a1}k_{a2}...k_{an} } + + \\alpha_n = \\frac{term_n}{[H+]^n + k_{a1}[H+]^{n-1} + k_{a1}k_{a2}[H+]^{n-2} ... k_{a1}k_{a2}...k_{an} } Where :math: '\term_n' refers to the nth term in the denominator, starting from 0 - .. [1] Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, - pp 127-130. Wiley Interscience, 1996. + References + ---------- + .. [stm] Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 127-130. Wiley Interscience, 1996. Examples: - -------- - >>> alpha(1,8,[4.7]) #doctest: +ELLIPSIS - 0.999... - - The sum of all alpha values should equal 1 + -------- + >>> alpha(1,8,[4.7]) #doctest: +ELLIPSIS + 0.999... - >>> alpha(0,8,[6.35,10.33]) #doctest: +ELLIPSIS - 0.021... - >>> alpha(1,8,[6.35,10.33]) #doctest: +ELLIPSIS - 0.979... - >>> alpha(2,8,[6.35,10.33]) #doctest: +ELLIPSIS - 2.043...e-09 + The sum of all alpha values should equal 1 - If pH is equal to one of the pKa values the function should return 0.5. + >>> alpha(0,8,[6.35,10.33]) #doctest: +ELLIPSIS + 0.021... + >>> alpha(1,8,[6.35,10.33]) #doctest: +ELLIPSIS + 0.979... + >>> alpha(2,8,[6.35,10.33]) #doctest: +ELLIPSIS + 2.043...e-09 - >>> alpha(1,6.35,[6.35,10.33]) - 0.5 + If pH is equal to one of the pKa values the function should return 0.5. - # The function will return an error if the number ofpKa's is less than n. - # - # >>> alpha(2,8,[]) - # ERROR: insufficient number of pKa values given - # 0.5 + >>> alpha(1,6.35,[6.35,10.33]) + 0.5 """ # generate an error if no pKa values are specified diff --git a/src/pyEQL/functions.py b/src/pyEQL/functions.py index 04cf8baa..070e73df 100644 --- a/src/pyEQL/functions.py +++ b/src/pyEQL/functions.py @@ -5,13 +5,9 @@ :license: LGPL, see LICENSE for more details. """ -# import libraries for scientific functions import math -# internal pyEQL imports import pyEQL - -# the pint unit registry from pyEQL import unit from pyEQL.logging_system import logger @@ -25,18 +21,19 @@ def gibbs_mix(Solution1, Solution2): Solution1, Solution2 : Solution objects The two solutions to be mixed. - Returns: + Returns ------- Quantity The change in Gibbs energy associated with complete mixing of the Solutions, in Joules. - Notes: + Notes ----- - The Gibbs energy of mixing is calculated as follows: [#]_ + The Gibbs energy of mixing is calculated as follows .. math:: - \Delta_{mix} G = \sum_i (n_c + n_d) R T \ln a_b - \sum_i n_c R T \ln a_c - \sum_i n_d R T \ln a_d + + \\Delta_{mix} G = \\sum_i (n_c + n_d) R T \\ln a_b - \\sum_i n_c R T \\ln a_c - \\sum_i n_d R T \\ln a_d Where :math:`n` is the number of moles of substance, :math:`T` is the temperature in kelvin, and subscripts :math:`b`, :math:`c`, and :math:`d` refer to the concentrated, dilute, and blended @@ -46,13 +43,10 @@ def gibbs_mix(Solution1, Solution2): so a simple salt dissolved in water is a three component solution (cation, anion, and water). - References: + References ---------- - .. [#] Koga, Yoshikata, 2007. *Solution Thermodynamics and its Application to Aqueous Solutions: - A differential approach.* Elsevier, 2007, pp. 23-37. - - Examples: - -------- + Koga, Yoshikata, 2007. *Solution Thermodynamics and its Application to Aqueous Solutions: + A differential approach.* Elsevier, 2007, pp. 23-37. """ concentrate = Solution1 @@ -80,18 +74,19 @@ def entropy_mix(Solution1, Solution2): Solution1, Solution2 : Solution objects The two solutions to be mixed. - Returns: + Returns ------- Quantity The ideal mixing entropy associated with complete mixing of the Solutions, in Joules. - Notes: + Notes ----- - The ideal entropy of mixing is calculated as follows:[#]_ + The ideal entropy of mixing is calculated as follows .. math:: - \Delta_{mix} S = \sum_i (n_c + n_d) R T \ln x_b - \sum_i n_c R T \ln x_c - \sum_i n_d R T \ln x_d + + \\Delta_{mix} S = \\sum_i (n_c + n_d) R T \\ln x_b - \\sum_i n_c R T \\ln x_c - \\sum_i n_d R T \\ln x_d Where :math:`n` is the number of moles of substance, :math:`T` is the temperature in kelvin, and subscripts :math:`b`, :math:`c`, and :math:`d` refer to the concentrated, dilute, and blended @@ -101,13 +96,10 @@ def entropy_mix(Solution1, Solution2): so a simple salt dissolved in water is a three component solution (cation, anion, and water). - References: + References ---------- - .. [#] Koga, Yoshikata, 2007. *Solution Thermodynamics and its Application to Aqueous Solutions: - A differential approach.* Elsevier, 2007, pp. 23-37. - - Examples: - -------- + Koga, Yoshikata, 2007. *Solution Thermodynamics and its Application to Aqueous Solutions: + A differential approach.* Elsevier, 2007, pp. 23-37. """ concentrate = Solution1 @@ -134,27 +126,29 @@ def donnan_eql(solution, fixed_charge): Parameters ---------- - Solution : Solution object + solution : Solution object The external solution to be brought into equilibrium with the fixed charges fixed_charge : str quantity String representing the concentration of fixed charges, including sign. May be specified in mol/L or mol/kg units. e.g. '1 mol/kg' - Returns: + Returns ------- Solution A solution that has established Donnan equilibrium with the external (input) Solution - Notes: + Notes ----- The general equation representing the equilibrium between an external electrolyte solution and an ion-exchange medium containing fixed charges - is:[#]_ + is + + .. math:: - .. math:: {a_- \\over \\bar a_-}^{1 \\over z_-} {\\bar a_+ \\over a_+}^{1 \\over z_+} \ - = exp({\\Delta \\pi \\bar V \\over {RT z_+ \\nu_+}}) + \\frac{a_{-}}{\\bar a_{-}}^{\\frac{1}{z_{-}} \\frac{\\bar a_{+}}{a_{+}}^{\\frac{1}{z_{+}} \ + = exp(\\frac{\\Delta \\pi \\bar V}{{RT z_{+} \\nu_{+}}}) Where subscripts :math:`+` and :math:`-` indicate the cation and anion, respectively, the overbar indicates the membrane phase, @@ -165,7 +159,7 @@ def donnan_eql(solution, fixed_charge): In addition, electroneutrality must prevail within the membrane phase: - .. math:: \\bar C_+ z_+ + \\bar X + \\bar C_- z_- = 0 + .. math:: \\bar C_{+} z_{+} + \\bar X + \\bar C_{-} z_{-} = 0 Where :math:`C` represents concentration and :math:`X` is the fixed charge concentration in the membrane or ion exchange phase. @@ -179,16 +173,10 @@ def donnan_eql(solution, fixed_charge): NOTE that this treatment is only capable of equilibrating a single salt. This salt is identified by the get_salt() method. - References: + References ---------- - .. [#] Strathmann, Heiner, ed. *Membrane Science and Technology* vol. 9, 2004. \ - Chapter 2, p. 51. http://dx.doi.org/10.1016/S0927-5193(04)80033-0 - - - Examples: - -------- - - Todo: + Strathmann, Heiner, ed. *Membrane Science and Technology* vol. 9, 2004. Chapter 2, p. 51. + http://dx.doi.org/10.1016/S0927-5193(04)80033-0 See Also: -------- @@ -304,7 +292,7 @@ def mix(Solution1, Solution2): Solution1, Solution2 : Solution objects The two solutions to be mixed. - Returns: + Returns ------- Solution A Solution object representing the mixed solution. @@ -379,34 +367,34 @@ def autogenerate(solution=""): Valid entries are 'seawater', 'rainwater', 'wastewater',and 'urine' - Returns: + Returns ------- Solution A pyEQL Solution object. - Notes: + Notes ----- The following sections explain the different solution options: - '' - empty solution, equivalent to pyEQL.Solution() - 'rainwater' - pure water in equilibrium with atmospheric CO2 at pH 6 - - 'seawater' or 'SW'- Standard Seawater. See Table 4 of the Reference for Composition [#]_ - - 'wastewater' or 'WW' - medium strength domestic wastewater. See Table 3-18 of [#]_ - - 'urine' - typical human urine. See Table 3-15 of [#]_ - - 'normal saline' or 'NS' - normal saline solution used in medicine [#]_ - - 'Ringers lacatate' or 'RL' - Ringer's lactate solution used in medicine [#]_ + - 'seawater' or 'SW'- Standard Seawater. See Table 4 of the Reference for Composition [1]_ + - 'wastewater' or 'WW' - medium strength domestic wastewater. See Table 3-18 of [2]_ + - 'urine' - typical human urine. See Table 3-15 of [2]_ + - 'normal saline' or 'NS' - normal saline solution used in medicine [3]_ + - 'Ringers lacatate' or 'RL' - Ringer's lactate solution used in medicine [4]_ References: ---------- - .. [#] Millero, Frank J. "The composition of Standard Seawater and the definition of - the Reference-Composition Salinity Scale." *Deep-sea Research. Part I* 55(1), 2008, 50-72. + .. [1] Millero, Frank J. "The composition of Standard Seawater and the definition of + the Reference-Composition Salinity Scale." *Deep-sea Research. Part I* 55(1), 2008, 50-72. - .. [#] Metcalf & Eddy, Inc. et al. *Wastewater Engineering: Treatment and Resource Recovery*, 5th Ed. - McGraw-Hill, 2013. + .. [2] Metcalf & Eddy, Inc. et al. *Wastewater Engineering: Treatment and Resource Recovery*, 5th Ed. + McGraw-Hill, 2013. - .. [#] https://en.wikipedia.org/wiki/Saline_(medicine) + .. [3] https://en.wikipedia.org/wiki/Saline_(medicine) - .. [#] https://en.wikipedia.org/wiki/Ringer%27s_lactate_solution + .. [4] https://en.wikipedia.org/wiki/Ringer%27s_lactate_solution """ if solution == "": diff --git a/src/pyEQL/salt_ion_match.py b/src/pyEQL/salt_ion_match.py index 92416641..835f91d7 100644 --- a/src/pyEQL/salt_ion_match.py +++ b/src/pyEQL/salt_ion_match.py @@ -28,7 +28,7 @@ def __init__(self, cation, anion): cation, anion : str Chemical formula of the cation and anion, respectively - Returns: + Returns ------- Salt : An object representing the properties of the salt @@ -90,7 +90,7 @@ def __init__(self, cation, anion): self.formula = salt_formula def get_effective_molality(self, ionic_strength): - """Calculate the effective molality according to [#]_. + """Calculate the effective molality according to [mistry]_. .. math:: 2 I \\over (\\nu_+ z_+^2 + \\nu_- z_- ^2) @@ -99,16 +99,15 @@ def get_effective_molality(self, ionic_strength): ionic_strength: Quantity The ionic strength of the parent solution, mol/kg - Returns: + Returns ------- Quantity: the effective molality of the salt in the parent solution - References: + References ---------- - .. [#] Mistry, K. H.; Hunter, H. a.; Lienhard V, J. H. Effect of - composition and nonideal solution behavior on desalination calculations - for mixed electrolyte solutions with comparison to seawater. - Desalination 2013, 318, 34-47. + .. [mistry] Mistry, K. H.; Hunter, H. a.; Lienhard V, J. H. Effect of composition and nonideal solution behavior + on desalination calculations for mixed electrolyte solutions with comparison to seawater. Desalination + 2013, 318, 34-47. """ m_effective = 2 * ionic_strength / (self.nu_cation * self.z_cation**2 + self.nu_anion * self.z_anion**2) @@ -119,19 +118,18 @@ def _sort_components(Solution, type="all"): """ Sort the components of a solution in descending order (by mol). - Parameters: + Parameters ---------- Solution : Solution object type : The type of component to be sorted. Defaults to 'all' for all - solutes. Other valid arguments are 'cations' and 'anions' which - return sorted lists of cations and anions, respectively. + solutes. Other valid arguments are 'cations' and 'anions' which + return sorted lists of cations and anions, respectively. - Returns: + Returns ------- A list whose keys are the component names (formulas) and whose values are the component objects themselves - """ formula_list = [] @@ -156,7 +154,7 @@ def identify_salt(sol): Create a Salt object for this salt. - Returns: + Returns ------- A Salt object. """ @@ -195,7 +193,7 @@ def generate_salt_list(sol, unit="mol/kg"): Generate a list of salts that represents the ionic composition of a solution. - Returns: + Returns ------- dict A dictionary of Salt objects, where Salt objects are the keys and diff --git a/src/pyEQL/solution.py b/src/pyEQL/solution.py index f82605b2..7331344e 100644 --- a/src/pyEQL/solution.py +++ b/src/pyEQL/solution.py @@ -6,7 +6,6 @@ """ -# import libraries for scientific functions import math from functools import lru_cache from pathlib import Path @@ -314,13 +313,13 @@ def get_solvent_mass(self): ---------- None - Returns: + Returns ------- Quantity: the mass of the solvent, in kg - See Also: + See Also -------- - get_amount() + :py:meth:`get_amount()` """ # return the total mass (kg) of the solvent mw = self.get_property(self.solvent, "molecular_weight").to("kg/mol").magnitude @@ -335,7 +334,7 @@ def get_volume(self): ---------- None - Returns: + Returns ------- Quantity: the volume of the solution, in L """ @@ -389,7 +388,7 @@ def mass(self) -> Quantity: ---------- None - Returns: + Returns ------- Quantity: the mass of the solution, in kg @@ -406,7 +405,7 @@ def density(self) -> Quantity: Density is calculated from the mass and volume each time this method is called. - Returns: + Returns ------- Quantity: The density of the solution. """ @@ -421,23 +420,23 @@ def dielectric_constant(self) -> Quantity: ---------- None - Returns: + Returns ------- Quantity: the dielectric constant of the solution, dimensionless. - Notes: + Notes ----- - Implements the following equation as given by [#]_ + Implements the following equation as given by Zuber et al. - .. math:: \\epsilon = \\epsilon_solvent \\over 1 + \\sum_i \\alpha_i x_i + .. math:: \\epsilon = \\epsilon_{solvent} \\over 1 + \\sum_i \\alpha_i x_i where :math:`\\alpha_i` is a coefficient specific to the solvent and ion, and :math:`x_i` is the mole fraction of the ion in solution. - References: + References ---------- - .. [#] [1] A. Zuber, L. Cardozo-Filho, V.F. Cabral, R.F. Checoni, M. Castier, + .A. Zuber, L. Cardozo-Filho, V.F. Cabral, R.F. Checoni, M. Castier, An empirical equation for the dielectric constant in aqueous and nonaqueous electrolyte mixtures, Fluid Phase Equilib. 376 (2014) 116-123. doi:10.1016/j.fluid.2014.05.037. @@ -500,10 +499,10 @@ def viscosity_kinematic(self): """ Return the kinematic viscosity of the solution. - Notes: + Notes ----- The calculation is based on a model derived from the Eyring equation - and presented in [#]_ + and presented in .. math:: @@ -515,21 +514,19 @@ def viscosity_kinematic(self): .. math:: \\delta G^*_{123} = a_o + a_1 (T)^{0.75} .. math:: \\delta G^*_{23} = b_o + b_1 (T)^{0.5} - In which :math: `\\nu` is the kinematic viscosity, MW is the molecular weight, - `x_+` is the mole fraction of cations, and T is the temperature in degrees C. + In which :math:`\\nu` is the kinematic viscosity, MW is the molecular weight, + :math:`x_{+}` is the mole fraction of cations, and :math:`T` is the temperature in degrees C. The a and b fitting parameters for a variety of common salts are included in the database. - References: + References ---------- - .. [#] Vásquez-Castillo, G.; Iglesias-Silva, G. a.; Hall, K. R. An extension - of the McAllister model to correlate kinematic viscosity of electrolyte solutions. - Fluid Phase Equilib. 2013, 358, 44-49. + Vásquez-Castillo, G.; Iglesias-Silva, G. a.; Hall, K. R. An extension of the McAllister model to correlate kinematic viscosity of electrolyte solutions. Fluid Phase Equilib. 2013, 358, 44-49. See Also: -------- - viscosity_dynamic + :py:meth:`viscosity_dynamic` """ # identify the main salt in the solution salt = self.get_salt() @@ -583,16 +580,16 @@ def conductivity(self): ---------- None - Returns: + Returns ------- Quantity The electrical conductivity of the solution in Siemens / meter. - Notes: + Notes ----- Conductivity is calculated by summing the molar conductivities of the respective solutes, but they are activity-corrected and adjusted using an empricial exponent. - This approach is used in PHREEQC and Aqion models [#]_ [#]_ + This approach is used in PHREEQC and Aqion models [aq]_ [hc]_ .. math:: @@ -602,21 +599,25 @@ def conductivity(self): .. math:: - \\alpha = \\begin{cases} {0.6 \\over \\sqrt{|z_i|}} & {I < 0.36|z_i|} \\ {\\sqrt{I} \\over |z_i|} & otherwise \\end{cases} + \\alpha = + \\begin{cases} + {\\frac{0.6}{\\sqrt{| z_{i} | }}} & {I < 0.36 | z_{i} | } + {\\frac{\\sqrt{I}}{| z_i |}} & otherwise + \\end{cases} Note: PHREEQC uses the molal rather than molar concentration according to http://wwwbrr.cr.usgs.gov/projects/GWC_coupled/phreeqc/phreeqc3-html/phreeqc3-43.htm - References: + References ---------- - .. [#] https://www.aqion.de/site/electrical-conductivity - .. [#] http://www.hydrochemistry.eu/exmpls/sc.html + .. [aq] https://www.aqion.de/site/electrical-conductivity + .. [hc] http://www.hydrochemistry.eu/exmpls/sc.html - See Also: + See Also -------- - ionic_strength - get_molar_conductivity() - get_activity_coefficient() + :py:attr:`ionic_strength` + :py:meth:`get_molar_conductivity()` + :py:meth:`get_activity_coefficient()` """ EC = 0 * unit.Quantity("S/m") @@ -653,17 +654,17 @@ def ionic_strength(self) -> Quantity: Molal (mol/kg) scale concentrations are used for compatibility with the activity correction formulas. - Returns: + Returns ------- Quantity : The ionic strength of the parent solution, mol/kg. See Also: -------- - get_activity - get_water_activity + :py:meth:`get_activity` + :py:meth:`get_water_activity` - Notes: + Notes ----- The ionic strength is calculated according to: @@ -694,21 +695,17 @@ def charge_balance(self) -> float: Return the charge balance of the solution. The charge balance represents the net electric charge on the solution and SHOULD equal zero at all times, but due to numerical errors will usually - have a small nonzero value. + have a small nonzero value. It is calculated according to: - Returns: + .. math:: CB = F \\sum_i n_i z_i + + where :math:`n_i` is the number of moles, :math:`z_i` is the charge on species i, and :math:`F` is the Faraday constant. + + Returns ------- float : The charge balance of the solution, in equivalents. - Notes: - ----- - The charge balance is calculated according to: - - .. math:: CB = F \\sum_i n_i z_i - - Where :math:`n_i` is the number of moles, :math:`z_i` is the charge on species i, and :math:`F` is the Faraday constant. - """ charge_balance = 0 F = (unit.e * unit.N_A).magnitude @@ -723,25 +720,27 @@ def alkalinity(self): """ Return the alkalinity or acid neutralizing capacity of a solution. - Returns: + Returns ------- Quantity : The alkalinity of the solution in mg/L as CaCO3 - Notes: + Notes ----- - The alkalinity is calculated according to: [#]_ + The alkalinity is calculated according to [stm]_ - .. math:: Alk = F \\sum_i z_i C_B - \\sum_i z_i C_A + .. math:: Alk = F \\sum_{i} z_{i} C_{B} - \\sum_{i} z_{i} C_{A} - Where :math:`C_B` and :math:`C_A` are conservative cations and anions, respectively - (i.e. ions that do not participate in acid-base reactions), and :math:`z_i` is their charge. - In this method, the set of conservative cations is all Group I and Group II cations, and the conservative anions are all the anions of strong acids. + Where :math:`C_{B}` and :math:`C_{A}` are conservative cations and anions, respectively + (i.e. ions that do not participate in acid-base reactions), and :math:`z_{i}` is their charge. + In this method, the set of conservative cations is all Group I and Group II cations, and the + conservative anions are all the anions of strong acids. - References: + References ---------- - .. [#] Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, - pp 165. Wiley Interscience, 1996. + + .. [stm] Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 165. Wiley Interscience, 1996. + """ alkalinity = 0 * unit.Quantity("mol/L") equiv_wt_CaCO3 = 100.09 / 2 * unit.Quantity("g/mol") @@ -788,7 +787,7 @@ def hardness(self): ---------- None - Returns: + Returns ------- Quantity The hardness of the solution in mg/L as CaCO3 @@ -810,34 +809,25 @@ def debye_length(self) -> Quantity: """ Return the Debye length of a solution. - Debye length is calculated as [#]_ + Debye length is calculated as [wk3]_ .. math:: \\kappa^{-1} = \\sqrt({\\epsilon_r \\epsilon_o k_B T \\over (2 N_A e^2 I)}) - where :math:`I` is the ionic strength, :math:`epsilon_r` and :math:`epsilon_r` + where :math:`I` is the ionic strength, :math:`\\epsilon_r` and :math:`\\epsilon_r` are the relative permittivity and vacuum permittivity, :math:`k_B` is the Boltzmann constant, and :math:`T` is the temperature, :math:`e` is the elementary charge, and :math:`N_A` is Avogadro's number. - Parameters - ---------- - None + Returns The Debye length, in nanometers. - Returns: - ------- - Quantity - The Debye length, in nanometers. - - References: - ---------- - .. [#] https://en.wikipedia.org/wiki/Debye_length#Debye_length_in_an_electrolyte + References + .. [wk3] https://en.wikipedia.org/wiki/Debye_length#Debye_length_in_an_electrolyte See Also: - -------- - ionic_strength - dielectric_constant + :attr:`ionic_strength` + :attr:`dielectric_constant` """ # to preserve dimensionality, convert the ionic strength into mol/L units @@ -861,13 +851,13 @@ def bjerrum_length(self) -> Quantity: Bjerrum length represents the distance at which electrostatic interactions between particles become comparable in magnitude - to the thermal energy.:math:`\\lambda_B` is calculated as [#]_ + to the thermal energy.:math:`\\lambda_B` is calculated as .. math:: \\lambda_B = {e^2 \\over (4 \\pi \\epsilon_r \\epsilon_o k_B T)} - where :math:`e` is the fundamental charge, :math:`epsilon_r` and :math:`epsilon_r` + where :math:`e` is the fundamental charge, :math:`\\epsilon_r` and :math:`\\epsilon_r` are the relative permittivity and vacuum permittivity, :math:`k_B` is the Boltzmann constant, and :math:`T` is the temperature. @@ -875,24 +865,24 @@ def bjerrum_length(self) -> Quantity: ---------- None - Returns: + Returns ------- Quantity The Bjerrum length, in nanometers. - References: + References ---------- - .. [#] https://en.wikipedia.org/wiki/Bjerrum_length + https://en.wikipedia.org/wiki/Bjerrum_length - Examples: + Examples -------- >>> s1 = pyEQL.Solution() >>> s1.bjerrum_length - See Also: + See Also -------- - dielectric_constant + :attr:`dielectric_constant` """ bjerrum_length = unit.e**2 / ( @@ -904,47 +894,37 @@ def get_osmotic_pressure(self): """ Return the osmotic pressure of the solution relative to pure water. - Parameters - ---------- - None - - Returns: - ------- - Quantity - The osmotic pressure of the solution relative to pure water in Pa + Returns + The osmotic pressure of the solution relative to pure water in Pa See Also: - -------- - get_water_activity - get_osmotic_coefficient - get_salt + get_water_activity + get_osmotic_coefficient + get_salt Notes: - ----- - Osmotic pressure is calculated based on the water activity [#]_ [#]_ : + Osmotic pressure is calculated based on the water activity [sata]_ [wk]_ - .. math:: \\Pi = {RT \\over V_w} \\ln a_w + .. math:: \\Pi = \\frac{RT}{V_{w}} \\ln a_{w} - Where :math:`\\Pi` is the osmotic pressure, :math:`V_w` is the partial - molar volume of water (18.2 cm**3/mol), and :math:`a_w` is the water - activity. + Where :math:`\\Pi` is the osmotic pressure, :math:`V_{w}` is the partial + molar volume of water (18.2 cm**3/mol), and :math:`a_{w}` is the water + activity. + References + .. [sata] Sata, Toshikatsu. Ion Exchange Membranes: Preparation, Characterization, and Modification. + Royal Society of Chemistry, 2004, p. 10. - References: - ---------- - .. [#] Sata, Toshikatsu. Ion Exchange Membranes: Preparation, Characterization, and Modification. Royal Society of Chemistry, 2004, p. 10. - - .. [#] http://en.wikipedia.org/wiki/Osmotic_pressure#Derivation_of_osmotic_pressure + .. [wk] http://en.wikipedia.org/wiki/Osmotic_pressure#Derivation_of_osmotic_pressure Examples: - -------- - >>> s1=pyEQL.Solution() - >>> s1.get_osmotic_pressure() - 0.0 + >>> s1=pyEQL.Solution() + >>> s1.get_osmotic_pressure() + 0.0 - >>> s1 = pyEQL.Solution([['Na+','0.2 mol/kg'],['Cl-','0.2 mol/kg']]) - >>> soln.get_osmotic_pressure() - + >>> s1 = pyEQL.Solution([['Na+','0.2 mol/kg'],['Cl-','0.2 mol/kg']]) + >>> soln.get_osmotic_pressure() + """ # TODO - tie this into parameter() and solvent() objects partial_molar_volume_water = 1.82e-5 * unit.Quantity("m ** 3/mol") @@ -973,7 +953,7 @@ def p(self, solute, activity=True): If False, the function will use the molar concentration rather than the activity to calculate p. Defaults to True. - Returns: + Returns ------- Quantity The negative log10 of the activity (or molar concentration if @@ -1017,12 +997,12 @@ def get_amount(self, solute, units): Use 'fraction' to return the mole fraction. Use '%' to return the mass percent - Returns: + Returns ------- The amount of the solute in question, in the specified units - See Also: + See Also -------- add_amount set_amount @@ -1081,17 +1061,17 @@ def get_total_amount(self, element, units): Units desired for the output. Examples of valid units are 'mol/L','mol/kg','mol', 'kg', and 'g/L' - Returns: + Returns ------- The total amount of the element in the solution, in the specified units - Notes: + Notes ----- There is currently no way to distinguish between different oxidation states of the same element (e.g. TOTFe(II) vs. TOTFe(III)). This is planned for a future release. (TODO) - See Also: + See Also -------- get_amount """ @@ -1135,16 +1115,16 @@ def add_amount(self, solute, amount): Parameters ---------- solute : str - String representing the name of the solute of interest + String representing the name of the solute of interest amount : str quantity - String representing the concentration desired, e.g. '1 mol/kg' - If the units are given on a per-volume basis, the solution - volume is not recalculated - If the units are given on a mass, substance, per-mass, or - per-substance basis, then the solution volume is recalculated - based on the new composition - - Returns: + String representing the concentration desired, e.g. '1 mol/kg' + If the units are given on a per-volume basis, the solution + volume is not recalculated + If the units are given on a mass, substance, per-mass, or + per-substance basis, then the solution volume is recalculated + based on the new composition + + Returns ------- Nothing. The concentration of solute is modified. """ @@ -1231,21 +1211,21 @@ def set_amount(self, solute, amount): Parameters ---------- solute : str - String representing the name of the solute of interest + String representing the name of the solute of interest amount : str Quantity - String representing the concentration desired, e.g. '1 mol/kg' - If the units are given on a per-volume basis, the solution - volume is not recalculated and the molar concentrations of - other components in the solution are not altered, while the - molal concentrations are modified. - - If the units are given on a mass, substance, per-mass, or - per-substance basis, then the solution volume is recalculated - based on the new composition and the molal concentrations of - other components are not altered, while the molar concentrations - are modified. - - Returns: + String representing the concentration desired, e.g. '1 mol/kg' + If the units are given on a per-volume basis, the solution + volume is not recalculated and the molar concentrations of + other components in the solution are not altered, while the + molal concentrations are modified. + + If the units are given on a mass, substance, per-mass, or + per-substance basis, then the solution volume is recalculated + based on the new composition and the molal concentrations of + other components are not altered, while the molar concentrations + are modified. + + Returns ------- Nothing. The concentration of solute is modified. @@ -1317,10 +1297,10 @@ def get_osmolarity(self, activity_correction=False): Parameters ---------- activity_correction : bool - If TRUE, the osmotic coefficient is used to calculate the - osmolarity. This correction is appropriate when trying to predict - the osmolarity that would be measured from e.g. freezing point - depression. Defaults to FALSE if omitted. + If TRUE, the osmotic coefficient is used to calculate the + osmolarity. This correction is appropriate when trying to predict + the osmolarity that would be measured from e.g. freezing point + depression. Defaults to FALSE if omitted. """ factor = self.get_osmotic_coefficient() if activity_correction is True else 1 return factor * self.get_total_moles_solute() / self.get_volume().to("L") @@ -1331,10 +1311,10 @@ def get_osmolality(self, activity_correction=False): Parameters ---------- activity_correction : bool - If TRUE, the osmotic coefficient is used to calculate the - osmolarity. This correction is appropriate when trying to predict - the osmolarity that would be measured from e.g. freezing point - depression. Defaults to FALSE if omitted. + If TRUE, the osmotic coefficient is used to calculate the + osmolarity. This correction is appropriate when trying to predict + the osmolarity that would be measured from e.g. freezing point + depression. Defaults to FALSE if omitted. """ factor = self.get_osmotic_coefficient() if activity_correction is True else 1 return factor * self.get_total_moles_solute() / self.get_solvent_mass().to("kg") @@ -1351,13 +1331,7 @@ def get_moles_solvent(self) -> Quantity: """ Return the moles of solvent present in the solution. - Parameters - ---------- - None - - Returns: - ------- - Quantity + Returns The moles of solvent in the solution. """ @@ -1384,21 +1358,21 @@ def get_salt(self): ---------- None - Returns: + Returns ------- Salt Salt object containing information about the parent salt. - See Also: + See Also -------- - get_activity - get_activity_coefficient - get_water_activity - get_osmotic_coefficient - get_osmotic_pressure - get_viscosity_kinematic - - Examples: + :py:meth:`get_activity` + :py:meth:`get_activity_coefficient` + :py:meth:`get_water_activity` + :py:meth:`get_osmotic_coefficient` + :py:meth:`get_osmotic_pressure` + :py:meth:`get_viscosity_kinematic` + + Examples -------- >>> s1 = Solution([['Na+','0.5 mol/kg'],['Cl-','0.5 mol/kg']]) >>> s1.get_salt() @@ -1442,19 +1416,19 @@ def get_salt_list(self): ---------- None - Returns: + Returns ------- dict A dictionary of Salt objects, keyed to the salt formula See Also: -------- - get_activity - get_activity_coefficient - get_water_activity - get_osmotic_coefficient - get_osmotic_pressure - get_viscosity_kinematic + :py:meth:`get_activity` + :py:meth:`get_activity_coefficient` + :py:meth:`get_water_activity` + :py:meth:`get_osmotic_coefficient` + :py:meth:`get_osmotic_pressure` + :py:meth:`get_viscosity_kinematic` """ # identify the predominant salt in the solution @@ -1480,7 +1454,7 @@ def get_activity_coefficient( that is being used for activity calculations. This option is useful when modeling multicomponent solutions. False by default. - Returns: + Returns Quantity: the activity coefficient as a dimensionless pint Quantity """ # return unit activity coefficient if the concentration of the solute is zero @@ -1519,37 +1493,33 @@ def get_activity( Return the thermodynamic activity of the solute in solution on the chosen concentration scale. Args: - solute : str - String representing the name of the solute of interest - scale : str, optional - The concentration scale for the returned activity. - Valid options are "molal", "molar", and "rational" (i.e., mole fraction). - By default, the molal scale activity is returned. - verbose : bool, optional - If True, pyEQL will print a message indicating the parent salt - that is being used for activity calculations. This option is - useful when modeling multicomponent solutions. False by default. - - Returns: - ------- - The thermodynamic activity of the solute in question (dimensionless) - - See Also: - -------- - get_activity_coefficient - get_ionic_strength - get_salt + solute: + String representing the name of the solute of interest + scale: + The concentration scale for the returned activity. + Valid options are "molal", "molar", and "rational" (i.e., mole fraction). + By default, the molal scale activity is returned. + verbose: + If True, pyEQL will print a message indicating the parent salt + that is being used for activity calculations. This option is + useful when modeling multicomponent solutions. False by default. + + Returns + The thermodynamic activity of the solute in question (dimensionless) Notes: - ----- - The thermodynamic activity depends on the concentration scale used [#]. - By default, the ionic strength, activity coefficients, and activities are all - calculated based on the molal (mol/kg) concentration scale. + The thermodynamic activity depends on the concentration scale used [rs]_ . + By default, the ionic strength, activity coefficients, and activities are all + calculated based on the molal (mol/kg) concentration scale. References: - ---------- - .. [#] Robinson, R. A.; Stokes, R. H. Electrolyte Solutions: Second Revised - Edition; Butterworths: London, 1968, p.32. + .. [rs] Robinson, R. A.; Stokes, R. H. Electrolyte Solutions: Second Revised + Edition; Butterworths: London, 1968, p.32. + + See Also: + :py:meth:`get_activity_coefficient` + :attr:`ionic_strength` + :py:meth:`get_salt` """ # switch to the water activity function if the species is H2O @@ -1609,32 +1579,32 @@ def get_water_activity(self): """ Return the water activity. - Returns: + Returns ------- Quantity : The thermodynamic activity of water in the solution. - See Also: + See Also -------- - get_osmotic_coefficient - get_ionic_strength - get_salt + :py:meth:`get_activity_coefficient` + :attr:`ionic_strength` + :py:meth:`get_salt` - Notes: + Notes ----- - Water activity is related to the osmotic coefficient in a solution containing i solutes by: [#]_ + Water activity is related to the osmotic coefficient in a solution containing i solutes by: - .. math:: \\ln a_w = - \\Phi M_w \\sum_i m_i + .. math:: \\ln a_{w} = - \\Phi M_{w} \\sum_{i} m_{i} - Where :math:`M_w` is the molar mass of water (0.018015 kg/mol) and :math:`m_i` is the molal concentration + Where :math:`M_{w}` is the molar mass of water (0.018015 kg/mol) and :math:`m_{i}` is the molal concentration of each species. If appropriate Pitzer model parameters are not available, the water activity is assumed equal to the mole fraction of water. - References: + References ---------- - .. [#] Blandamer, Mike J., Engberts, Jan B. F. N., Gleeson, Peter T., Reis, Joao Carlos R., 2005. "Activity of + Blandamer, Mike J., Engberts, Jan B. F. N., Gleeson, Peter T., Reis, Joao Carlos R., 2005. "Activity of water in aqueous systems: A frequently neglected property." *Chemical Society Review* 34, 440-458. Examples: @@ -1676,43 +1646,35 @@ def get_water_activity(self): def get_transport_number(self, solute, activity_correction=False): """Calculate the transport number of the solute in the solution. - Parameters - ---------- - solute : str - String identifying the solute for which the transport number is - to be calculated. + Args: + solute : String identifying the solute for which the transport number is + to be calculated. - activity_correction: bool - If True, the transport number will be corrected for activity following - the same method used for solution conductivity. Defaults to False - if omitted. + activity_correction: If True, the transport number will be corrected for activity following + the same method used for solution conductivity. Defaults to False if omitted. - Returns: - ------- - float - The transport number of `solute` + Returns + The transport number of `solute` - Notes: - ----- - Transport number is calculated according to [#]_ : + Notes: + Transport number is calculated according to : - .. math:: + .. math:: - t_i = {D_i z_i^2 C_i \\over \\sum D_i z_i^2 C_i} + t_i = {D_i z_i^2 C_i \\over \\sum D_i z_i^2 C_i} - Where :math:`C_i` is the concentration in mol/L, :math:`D_i` is the diffusion - coefficient, and :math:`z_i` is the charge, and the summation extends - over all species in the solution. + Where :math:`C_i` is the concentration in mol/L, :math:`D_i` is the diffusion + coefficient, and :math:`z_i` is the charge, and the summation extends + over all species in the solution. - If `activity_correction` is True, the contribution of each ion to the - transport number is corrected with an activity factor. See the documentation - for Solution.conductivity for an explanation of this correction. + If `activity_correction` is True, the contribution of each ion to the + transport number is corrected with an activity factor. See the documentation + for Solution.conductivity for an explanation of this correction. - References: - ---------- - .. [#] Geise, G. M.; Cassady, H. J.; Paul, D. R.; Logan, E.; Hickner, M. A. "Specific - ion effects on membrane potential and the permselectivity of ion exchange membranes."" - *Phys. Chem. Chem. Phys.* 2014, 16, 21673-21681. + References: + Geise, G. M.; Cassady, H. J.; Paul, D. R.; Logan, E.; Hickner, M. A. "Specific + ion effects on membrane potential and the permselectivity of ion exchange membranes."" + *Phys. Chem. Chem. Phys.* 2014, 16, 21673-21681. """ denominator = unit.Quantity("0 mol / m / s") @@ -1751,37 +1713,25 @@ def get_molar_conductivity(self, solute): """ Calculate the molar (equivalent) conductivity for a solute. - Parameters - ---------- - solute : str - String identifying the solute for which the molar conductivity is - to be calculated. + Args: + solute: String identifying the solute for which the molar conductivity is + to be calculated. - Returns: - ------- - float - The molar or equivalent conductivity of the species in the solution. - Zero if the solute is not charged. + Returns + The molar or equivalent conductivity of the species in the solution. + Zero if the solute is not charged. Notes: - ----- - Molar conductivity is calculated from the Nernst-Einstein relation [#]_ + Molar conductivity is calculated from the Nernst-Einstein relation [smed]_ - .. math:: + .. math:: - \\kappa_i = {z_i^2 D_i F^2 \\over RT} + \\kappa_i = {z_i^2 D_i F^2 \\over RT} - Note that the diffusion coefficient is strongly variable with temperature. + Note that the diffusion coefficient is strongly variable with temperature. References: - ---------- - .. [#] Smedley, Stuart. The Interpretation of Ionic Conductivity in Liquids, pp 1-9. Plenum Press, 1980. - - Examples: - -------- - - Todo: - + .. [smed] Smedley, Stuart. The Interpretation of Ionic Conductivity in Liquids, pp 1-9. Plenum Press, 1980. """ D = self.get_property(solute, "transport.diffusion_coefficient") @@ -1806,23 +1756,23 @@ def get_mobility(self, solute): String identifying the solute for which the mobility is to be calculated. - Returns: + Returns ------- float : The ionic mobility. Zero if the solute is not charged. - Notes: + Notes ----- This function uses the Einstein relation to convert a diffusion coefficient - into an ionic mobility [#]_ + into an ionic mobility [smed]_ .. math:: \\mu_i = {F |z_i| D_i \\over RT} - References: + References ---------- - .. [#] Smedley, Stuart I. The Interpretation of Ionic Conductivity in Liquids. Plenum Press, 1980. + .. [smed] Smedley, Stuart I. The Interpretation of Ionic Conductivity in Liquids. Plenum Press, 1980. """ D = self.get_property(solute, "transport.diffusion_coefficient") @@ -1846,7 +1796,7 @@ def _get_property(self, solute: str, name: str) -> Optional[Quantity]: The name of the property needed, e.g. 'diffusion coefficient' - Returns: + Returns ------- Quantity: The desired parameter or None if not found @@ -1951,15 +1901,15 @@ def get_chemical_potential_energy(self, activity_correction=True): potential. If False, mole fraction will be used, resulting in a calculation of the ideal chemical potential. - Returns: + Returns ------- Quantity The actual or ideal chemical potential energy of the solution, in Joules. - Notes: + Notes ----- The chemical potential energy (related to the Gibbs mixing energy) is - calculated as follows: [#]_ + calculated as follows: [koga]_ .. math:: E = R T \\sum_i n_i \\ln a_i @@ -1975,12 +1925,9 @@ def get_chemical_potential_energy(self, activity_correction=True): so a simple salt dissolved in water is a three component solution (cation, anion, and water). - References: + References ---------- - .. [#] Koga, Yoshikata, 2007. *Solution Thermodynamics and its Application to Aqueous Solutions: A differential approach.* Elsevier, 2007, pp. 23-37. - - Examples: - -------- + .. [koga] Koga, Yoshikata, 2007. *Solution Thermodynamics and its Application to Aqueous Solutions: A differential approach.* Elsevier, 2007, pp. 23-37. """ E = unit.Quantity("0 J") @@ -2019,19 +1966,19 @@ def get_lattice_distance(self, solute): Parameters ---------- solute : str - String representing the name of the solute of interest + String representing the name of the solute of interest - Returns: + Returns ------- Quantity : The average distance between solute molecules - Examples: + Examples -------- >>> soln = Solution([['Na+','0.5 mol/kg'],['Cl-','0.5 mol/kg']]) >>> soln.get_lattice_distance('Na+') 1.492964.... nanometer - Notes: + Notes ----- The lattice distance is related to the molar concentration as follows: @@ -2130,7 +2077,7 @@ def list_concentrations(self, unit="mol/kg", decimals=4, type="all"): solutes. Other valid arguments are 'cations' and 'anions' which return lists of cations and anions, respectively. - Returns: + Returns ------- dict Dictionary containing a list of the species in solution paired with their amount in the specified units @@ -2179,7 +2126,7 @@ def list_activities(self, decimals=4): decimals: int The number of decimal places to display. Defaults to 4. - Returns: + Returns ------- dict Dictionary containing a list of the species in solution paired with their activity @@ -2206,14 +2153,20 @@ def __str__(self): message="get_solute() is deprecated and will be removed in the next release! Access solutes via the Solution.components attribute and their properties via Solution.get_property(solute, ...)" ) def get_solute(self, i): - """Return the specified solute object.""" + """Return the specified solute object. + + :meta private: + """ return self.components[i] @deprecated( message="get_solvent is deprecated and will be removed in the next release! Use Solution.solvent instead." ) def get_solvent(self): - """Return the solvent object.""" + """Return the solvent object. + + :meta private: + """ return self.components[self.solvent] @deprecated( @@ -2227,9 +2180,11 @@ def get_temperature(self): ---------- None - Returns: + Returns ------- Quantity: The temperature of the solution, in Kelvin. + + :meta private: """ return self.temperature @@ -2244,6 +2199,8 @@ def set_temperature(self, temperature): ---------- temperature : str String representing the temperature, e.g. '25 degC' + + :meta private: """ self.temperature = unit.Quantity(temperature) @@ -2257,9 +2214,11 @@ def get_pressure(self): """ Return the hydrostatic pressure of the solution. - Returns: + Returns ------- Quantity: The hydrostatic pressure of the solution, in atm. + + :meta private: """ return self.pressure @@ -2274,6 +2233,8 @@ def set_pressure(self, pressure): ---------- pressure : str String representing the temperature, e.g. '25 degC' + + :meta private: """ self._pressure = unit.Quantity(pressure) @@ -2287,10 +2248,12 @@ def get_mass(self): ---------- None - Returns: + Returns ------- Quantity: the mass of the solution, in kg + :meta private: + """ return self.mass @@ -2301,9 +2264,11 @@ def get_density(self): Density is calculated from the mass and volume each time this method is called. - Returns: + Returns ------- Quantity: The density of the solution. + + :meta private: """ return self.density @@ -2320,6 +2285,9 @@ def get_viscosity_relative(self): See + + :meta private: + """ # if self.ionic_strength.magnitude > 0.2: # logger.warning('Viscosity calculation has limited accuracy above 0.2m') @@ -2350,6 +2318,8 @@ def get_viscosity_dynamic(self): See Also: -------- get_viscosity_kinematic + + :meta private: """ return self.viscosity_dynamic @@ -2360,10 +2330,10 @@ def get_viscosity_kinematic(self): """ Return the kinematic viscosity of the solution. - Notes: + Notes ----- The calculation is based on a model derived from the Eyring equation - and presented in [#]_ + and presented by Vásquez-Castillo et al. .. math:: @@ -2375,21 +2345,24 @@ def get_viscosity_kinematic(self): .. math:: \\delta G^*_{123} = a_o + a_1 (T)^{0.75} .. math:: \\delta G^*_{23} = b_o + b_1 (T)^{0.5} - In which :math: `\\nu` is the kinematic viscosity, MW is the molecular weight, + In which :math:`\\nu` is the kinematic viscosity, MW is the molecular weight, `x_+` is the mole fraction of cations, and T is the temperature in degrees C. The a and b fitting parameters for a variety of common salts are included in the database. - References: + References ---------- - .. [#] Vásquez-Castillo, G.; Iglesias-Silva, G. a.; Hall, K. R. An extension - of the McAllister model to correlate kinematic viscosity of electrolyte solutions. - Fluid Phase Equilib. 2013, 358, 44-49. + Vásquez-Castillo, G.; Iglesias-Silva, G. a.; Hall, K. R. An extension + of the McAllister model to correlate kinematic viscosity of electrolyte solutions. + Fluid Phase Equilib. 2013, 358, 44-49. See Also: -------- viscosity_dynamic + + :meta private: + """ return self.viscosity_kinematic @@ -2404,12 +2377,12 @@ def get_conductivity(self): ---------- None - Returns: + Returns ------- Quantity The electrical conductivity of the solution in Siemens / meter. - Notes: + Notes ----- Conductivity is calculated by summing the molar conductivities of the respective solutes, but they are activity-corrected and adjusted using an empricial exponent. @@ -2428,7 +2401,7 @@ def get_conductivity(self): Note: PHREEQC uses the molal rather than molar concentration according to http://wwwbrr.cr.usgs.gov/projects/GWC_coupled/phreeqc/phreeqc3-html/phreeqc3-43.htm - References: + References ---------- .. [#] https://www.aqion.de/site/electrical-conductivity .. [#] http://www.hydrochemistry.eu/exmpls/sc.html @@ -2439,6 +2412,8 @@ def get_conductivity(self): get_molar_conductivity() get_activity_coefficient() + :meta private: + """ return self.conductivity @@ -2450,10 +2425,12 @@ def get_mole_fraction(self, solute): """ Return the mole fraction of 'solute' in the solution. - Notes: + Notes ----- This function is DEPRECATED. Use get_amount() instead and specify 'fraction' as the unit type. + + :meta private: """ @deprecated( @@ -2466,7 +2443,7 @@ def get_ionic_strength(self): Return the ionic strength of the solution, calculated as 1/2 * sum ( molality * charge ^2) over all the ions. Molal (mol/kg) scale concentrations are used for compatibility with the activity correction formulas. - Returns: + Returns ------- Quantity : The ionic strength of the parent solution, mol/kg. @@ -2476,7 +2453,7 @@ def get_ionic_strength(self): get_activity get_water_activity - Notes: + Notes ----- The ionic strength is calculated according to: @@ -2493,6 +2470,8 @@ def get_ionic_strength(self): >>> s1 = pyEQL.Solution([['Mg+2','0.3 mol/kg'],['Na+','0.1 mol/kg'],['Cl-','0.7 mol/kg']],temperature='30 degC') >>> s1.ionic_strength + + :meta private: """ return self.ionic_strength @@ -2507,12 +2486,12 @@ def get_charge_balance(self): on the solution and SHOULD equal zero at all times, but due to numerical errors will usually have a small nonzero value. - Returns: + Returns ------- float : The charge balance of the solution, in equivalents. - Notes: + Notes ----- The charge balance is calculated according to: @@ -2520,6 +2499,8 @@ def get_charge_balance(self): Where :math:`n_i` is the number of moles, :math:`z_i` is the charge on species i, and :math:`F` is the Faraday constant. + :meta private: + """ return self.charge_balance @@ -2530,14 +2511,14 @@ def get_alkalinity(self): """ Return the alkalinity or acid neutralizing capacity of a solution. - Returns: + Returns ------- Quantity : The alkalinity of the solution in mg/L as CaCO3 - Notes: + Notes ----- - The alkalinity is calculated according to: [#]_ + The alkalinity is calculated according to: .. math:: Alk = F \\sum_i z_i C_B - \\sum_i z_i C_A @@ -2545,10 +2526,13 @@ def get_alkalinity(self): (i.e. ions that do not participate in acid-base reactions), and :math:`z_i` is their charge. In this method, the set of conservative cations is all Group I and Group II cations, and the conservative anions are all the anions of strong acids. - References: + References ---------- - .. [#] Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, + Stumm, Werner and Morgan, James J. Aquatic Chemistry, 3rd ed, pp 165. Wiley Interscience, 1996. + + :meta private: + """ return self.alkalinity @@ -2569,11 +2553,13 @@ def get_hardness(self): ---------- None - Returns: + Returns ------- Quantity The hardness of the solution in mg/L as CaCO3 + :meta private: + """ return self.hardness @@ -2584,7 +2570,7 @@ def get_debye_length(self): """ Return the Debye length of a solution. - Debye length is calculated as [#]_ + Debye length is calculated as .. math:: @@ -2599,20 +2585,22 @@ def get_debye_length(self): ---------- None - Returns: + Returns ------- Quantity The Debye length, in nanometers. - References: + References ---------- - .. [#] https://en.wikipedia.org/wiki/Debye_length#Debye_length_in_an_electrolyte + https://en.wikipedia.org/wiki/Debye_length#Debye_length_in_an_electrolyte See Also: -------- ionic_strength get_dielectric_constant() + :meta private: + """ return self.debye_length @@ -2625,7 +2613,7 @@ def get_bjerrum_length(self): Bjerrum length represents the distance at which electrostatic interactions between particles become comparable in magnitude - to the thermal energy.:math:`\\lambda_B` is calculated as [#]_ + to the thermal energy.:math:`\\lambda_B` is calculated as .. math:: @@ -2639,14 +2627,14 @@ def get_bjerrum_length(self): ---------- None - Returns: + Returns ------- Quantity The Bjerrum length, in nanometers. - References: + References ---------- - .. [#] https://en.wikipedia.org/wiki/Bjerrum_length + https://en.wikipedia.org/wiki/Bjerrum_length Examples: -------- @@ -2658,6 +2646,8 @@ def get_bjerrum_length(self): -------- get_dielectric_constant() + :meta private: + """ return self.bjerrum_length @@ -2672,13 +2662,13 @@ def get_dielectric_constant(self): ---------- None - Returns: + Returns ------- Quantity: the dielectric constant of the solution, dimensionless. - Notes: + Notes ----- - Implements the following equation as given by [#]_ + Implements the following equation as given by [zub]_ .. math:: \\epsilon = \\epsilon_solvent \\over 1 + \\sum_i \\alpha_i x_i @@ -2686,11 +2676,13 @@ def get_dielectric_constant(self): is the mole fraction of the ion in solution. - References: + References ---------- - .. [#] [1] A. Zuber, L. Cardozo-Filho, V.F. Cabral, R.F. Checoni, M. Castier, + .. [zub] A. Zuber, L. Cardozo-Filho, V.F. Cabral, R.F. Checoni, M. Castier, An empirical equation for the dielectric constant in aqueous and nonaqueous electrolyte mixtures, Fluid Phase Equilib. 376 (2014) 116-123. doi:10.1016/j.fluid.2014.05.037. + + :meta private: """ return self.dielectric_constant diff --git a/tox.ini b/tox.ini index 9819088e..6f4cb57b 100644 --- a/tox.ini +++ b/tox.ini @@ -59,7 +59,7 @@ deps = -r {toxinidir}/docs/requirements.txt # ^ requirements.txt shared with Read The Docs commands = - sphinx-build -b {env:BUILD} -d "{env:BUILDDIR}/doctrees" "{env:DOCSDIR}" "{env:BUILDDIR}/{env:BUILD}" {posargs} + sphinx-build -b {env:BUILD} -d "{env:BUILDDIR}/doctrees" "{env:DOCSDIR}" "{env:BUILDDIR}/{env:BUILD}" -j auto {posargs} [testenv:publish]