Skip to content

Commit

Permalink
add a HOWTO notebook depicting how to manage units in PySDM (#1443)
Browse files Browse the repository at this point in the history
Co-authored-by: Agnieszka Żaba <[email protected]>
Co-authored-by: Sylwester Arabas <[email protected]>
  • Loading branch information
3 people authored Dec 5, 2024
1 parent b3a18cf commit e6e8aab
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 6 deletions.
8 changes: 5 additions & 3 deletions PySDM/physics/dimensional_analysis.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""
A context manager (for use with the `with` statement)
for use in unit tests which disables Numba and enables Pint
A context manager (for use with the `with` statement) which
enables Pint physical-units checks and disables Numba in `PySDM.formulae.Formulae`
"""

from importlib import reload

from PySDM import formulae

from PySDM import physics
from . import constants, constants_defaults
from .impl import flag

Expand All @@ -17,9 +17,11 @@ def __enter__(*_): # pylint: disable=no-method-argument,no-self-argument
reload(constants)
reload(constants_defaults)
reload(formulae)
reload(physics)

def __exit__(*_): # pylint: disable=no-method-argument,no-self-argument
flag.DIMENSIONAL_ANALYSIS = False
reload(constants)
reload(constants_defaults)
reload(formulae)
reload(physics)
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ The package features a Pythonic high-performance implementation of the
Super-Droplet Method (SDM) Monte-Carlo algorithm for representing collisional growth
([Shima et al. 2009](https://rmets.onlinelibrary.wiley.com/doi/abs/10.1002/qj.441)), hence the name.

There is a growing set of example Jupyter notebooks exemplifying how to perform
There is a growing set of [example Jupyter notebooks](https://open-atmos.github.io/PySDM/PySDM_examples.html) exemplifying how to perform
various types of calculations and simulations using PySDM.
Most of the example notebooks reproduce results and plot from literature, see below for
a list of examples and links to the notebooks (which can be either executed or viewed
Expand Down Expand Up @@ -149,7 +149,8 @@ To run all pre-commit hooks, run `pre-commit run --all-files`.
The `.pre-commit-config.yaml` file can be modified in case new hooks are to be added or
existing ones need to be altered.

Further hints addressed at PySDM developers are maintained in the [open-atmos/python-dev-hints Wiki](https://github.com/open-atmos/python-dev-hints/wiki).
Further hints addressed at PySDM developers are maintained in the [open-atmos/python-dev-hints Wiki](https://github.com/open-atmos/python-dev-hints/wiki)
and in [PySDM HOWTOs](https://github.com/open-atmos/PySDM/tree/main/examples/PySDM_examples/_HOWTOS).

Issues regarding any incorrect, unintuitive or undocumented bahaviour of
PySDM are best to be reported on the [GitHub issue tracker](https://github.com/open-atmos/PySDM/issues/new).
Expand Down
3 changes: 2 additions & 1 deletion docs/templates/index.html.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@
<pre><code>git clone https://github.com/open-atmos/PySDM.git
pip install -e PySDM[tests] -e PySDM/examples[tests]
pytest PySDM</code></pre>
<p>(the above should be a viable way to set up development environment for PySDM, see also our <a href="https://github.com/open-atmos/python-dev-hints/wiki">Python dev hints Wiki</a> for further information)</p>
<p>(the above should be a viable way to set up development environment for PySDM, see also our <a href="https://github.com/open-atmos/python-dev-hints/wiki">Python dev hints Wiki</a>
and <a href="https://github.com/open-atmos/PySDM/tree/main/examples/PySDM_examples/_HOWTOS">PySDM HOWTOs</a> for further information)</p>
<p>
PySDM-examples is also available on PyPI and can be installed using pip:
</p>
Expand Down
175 changes: 175 additions & 0 deletions examples/PySDM_examples/_HOWTOs/dimensional_analysis.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "c1925b9647eb39e9",
"metadata": {},
"source": [
"[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PySDM/blob/main/examples/PySDM_examples/howtos/units.ipynb)\n",
"[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PySDM.git/main?urlpath=lab/tree/examples/PySDM_examples/howtos/units.ipynb)\n",
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PySDM/blob/main/examples/PySDM_examples/howtos/units.ipynb)"
]
},
{
"cell_type": "markdown",
"id": "57a5c59c4cb8a1ec",
"metadata": {},
"source": [
"### PySDM dimensional analysis HOWTO\n",
"- PySDM depends on the [Pint](https://pint.readthedocs.io/en/stable/) package which offers dimensional analysis (physical units checks) of Python code \n",
"- this improves code readibility with expressions such as `p = 1000 * si.hPa`\n",
"- using [Pint](https://pint.readthedocs.io/en/stable/), `si` is an instance of [`pint.UnitRegistry`](https://pint.readthedocs.io/en/stable/api/base.html#pint.UnitRegistry) \n",
"- however, for performance reasons, by default PySDM uses a custom drop-in-replacement [`FakeUnitRegistry`](https://open-atmos.github.io/PySDM/PySDM/physics/impl/fake_unit_registry.html#FakeUnitRegistry)\n",
"- this way, we keep the readibility advantage, while not incurring any performance overhead\n",
"- moreover, this makes the code potentially Numba JIT-compilable!\n",
"- we also provide a way to leverage the dimensional analysis benefit for testing purposes\n",
"- to this end, the test code can use the [`DimensionalAnalysis`](https://open-atmos.github.io/PySDM/PySDM/physics/dimensional_analysis.html#DimensionalAnalysis) context manager\n",
"- code below demonstrate how a single unit-equipped function can be used with and without unit checks "
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "97dab670c5cf8d8b",
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-04T13:27:07.009798Z",
"start_time": "2024-12-04T13:27:07.003077Z"
}
},
"outputs": [],
"source": [
"import sys\n",
"if 'google.colab' in sys.modules:\n",
" !pip --quiet install open-atmos-jupyter-utils\n",
" from open_atmos_jupyter_utils import pip_install_on_colab\n",
" pip_install_on_colab('PySDM-examples')"
]
},
{
"cell_type": "markdown",
"id": "19a8f1dc-a5ea-461b-9019-50d25dfa7bc6",
"metadata": {},
"source": [
"#### sample physics-related code"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "6ea31783d8e0e849",
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-04T13:27:08.240983Z",
"start_time": "2024-12-04T13:27:07.013763Z"
}
},
"outputs": [],
"source": [
"from PySDM import physics\n",
"\n",
"def code():\n",
" si = physics.si\n",
"\n",
" p = 1000 * si.hPa\n",
" T = 300 * si.K\n",
" R = 286 * si.J / si.K / si.kg\n",
"\n",
" rho = p / R / T\n",
" return rho"
]
},
{
"cell_type": "markdown",
"id": "183bcca3-5546-4676-a6bc-de3a1dd76ccb",
"metadata": {},
"source": [
"#### sample unit-unaware usage (default)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "287ae3f109f11bb6",
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-04T13:27:08.341648Z",
"start_time": "2024-12-04T13:27:08.339564Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1.2\n"
]
}
],
"source": [
"result_unit_unaware = code()\n",
"print(f\"{result_unit_unaware:.2g}\")"
]
},
{
"cell_type": "markdown",
"id": "da1669cf-653b-4138-9ff9-8fac303ad8c4",
"metadata": {},
"source": [
"#### sample unit-aware usage (e.g., for testing)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "85bf8e7bc2ca98a",
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-04T13:27:08.671915Z",
"start_time": "2024-12-04T13:27:08.402327Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.012 hectopascal * kilogram / joule\n",
"1.2 kilogram / meter ** 3\n"
]
}
],
"source": [
"from PySDM.physics.dimensional_analysis import DimensionalAnalysis\n",
"\n",
"with DimensionalAnalysis():\n",
" result_unit_aware = code()\n",
"\n",
"assert result_unit_aware.check(\"[mass] / [volume]\")\n",
"print(f\"{result_unit_aware:.2g}\")\n",
"print(f\"{result_unit_aware.to_base_units():.2g}\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
1 change: 1 addition & 0 deletions tests/examples_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def findfiles(path, regex):
"Morrison_and_Grabowski_2007",
"Szumowski_et_al_1998",
"utils",
"_HOWTOs",
],
}

Expand Down

0 comments on commit e6e8aab

Please sign in to comment.