Skip to content

Commit

Permalink
Add Bolin 1958 Table 1 example (isotopic adjustment timescale, formul…
Browse files Browse the repository at this point in the history
…ae-only no-environment setup) (open-atmos#1431)

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 e6e8aab commit d2ed10c
Show file tree
Hide file tree
Showing 8 changed files with 386 additions and 1 deletion.
9 changes: 9 additions & 0 deletions PySDM/physics/constants_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,12 @@ def compute_derived_values(c: dict):
bulk_phase_partitioning_T_cold = 235 * si.K
bulk_phase_partitioning_T_warm = 273 * si.K
bulk_phase_partitioning_exponent = np.nan


BOLIN_ISOTOPE_TIMESCALE_COEFF_C1 = np.nan * si.dimensionless
"""
Coeffitient c1 used in [Bolin 1958](https://https://digitallibrary.un.org/record/3892725)
for the falling drop evaporation timescale of equilibration with ambient air void of a given
isotopologue; in the paper timescale is calculated for tritium with assumption of no tritium
in the environment around the drop (Table 1).
"""
1 change: 1 addition & 0 deletions PySDM/physics/isotope_relaxation_timescale/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

from PySDM.impl.null_physics_class import Null
from .miyake_et_al_1968 import MiyakeEtAl1968
from .bolin_1958 import Bolin1958
17 changes: 17 additions & 0 deletions PySDM/physics/isotope_relaxation_timescale/bolin_1958.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
""" Timescale derived for tritium with assumption of zero ambient concentration - see text above
Table 1 [Bolin 1958](https://digitallibrary.un.org/record/3892725) """

import numpy as np


class Bolin1958: # pylint: disable=too-few-public-methods
"""Implementation of timescale used by Bolin 1958"""

def __init__(self, const):
assert np.isfinite(const.BOLIN_ISOTOPE_TIMESCALE_COEFF_C1)

@staticmethod
# pylint: disable=too-many-arguments
def tau(const, radius, r_dr_dt):
"""timescale for evaporation of a falling drop with tritium"""
return (-3 / radius**2 * r_dr_dt * const.BOLIN_ISOTOPE_TIMESCALE_COEFF_C1) ** -1
Empty file.
267 changes: 267 additions & 0 deletions examples/PySDM_examples/Bolin_1958/table_1.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "e3f1edc815e974f4",
"metadata": {},
"source": [
"[![preview notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/devops_tests/blob/main/test_files/template.ipynb)\n",
"[![launch on mybinder.org](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/devops_tests.git/main?urlpath=lab/tree/test_files/template.ipynb)\n",
"[![launch on Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/devops_tests/blob/main/test_files/template.ipynb)"
]
},
{
"cell_type": "markdown",
"id": "bb84c52c57a729ec",
"metadata": {},
"source": "### based on Table 1 from B.Bolin 1958 \"On the use of tritium as a tracer for water in nature\" (https://digitallibrary.un.org/record/3892725)"
},
{
"cell_type": "code",
"id": "5e4e58050cc01f64",
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-05T21:30:20.214060Z",
"start_time": "2024-12-05T21:30:20.209298Z"
}
},
"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')"
],
"outputs": [],
"execution_count": 1
},
{
"cell_type": "code",
"id": "95f360dc62f373f9",
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-05T21:30:21.622672Z",
"start_time": "2024-12-05T21:30:20.218383Z"
}
},
"source": [
"import numpy as np\n",
"import pandas\n",
"from PySDM.physics import in_unit, si\n",
"from PySDM import Formulae"
],
"outputs": [],
"execution_count": 2
},
{
"cell_type": "code",
"id": "e29a92d6680c4e51",
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-05T21:30:21.689116Z",
"start_time": "2024-12-05T21:30:21.687254Z"
}
},
"source": "radii = np.asarray([0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.15, 0.20]) * si.cm",
"outputs": [],
"execution_count": 3
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-05T21:30:24.420354Z",
"start_time": "2024-12-05T21:30:21.698737Z"
}
},
"cell_type": "code",
"source": [
"formulae = Formulae(\n",
" terminal_velocity='RogersYau',\n",
" drop_growth='Mason1951',\n",
" diffusion_thermics='Neglect',\n",
" saturation_vapour_pressure='AugustRocheMagnus',\n",
" ventilation='Froessling1938',\n",
" particle_shape_and_density='LiquidSpheres',\n",
" air_dynamic_viscosity='ZografosEtAl1987',\n",
" constants={'BOLIN_ISOTOPE_TIMESCALE_COEFF_C1': 1.63},\n",
" isotope_relaxation_timescale='Bolin1958',\n",
")\n",
"const = formulae.constants\n",
"any_non_zero_value = 44.\n",
"temperature = const.T0 + 10 * si.K\n",
"pressure = const.p_STP\n",
"pvs = formulae.saturation_vapour_pressure.pvs_water(temperature)\n",
"v_term = formulae.terminal_velocity.v_term(radii)\n",
"eta_air=formulae.air_dynamic_viscosity.eta_air(temperature)\n",
"D=formulae.diffusion_thermics.D(T=temperature, p=pressure)\n",
"\n",
"air_density = pressure/const.Rd/temperature\n",
"assert abs(air_density - 1)/air_density <.3\n",
"Re = formulae.particle_shape_and_density.reynolds_number(\n",
" radius=radii,\n",
" velocity_wrt_air=v_term,\n",
" dynamic_viscosity=eta_air,\n",
" density=air_density,\n",
")\n",
"Sc = formulae.trivia.air_schmidt_number(\n",
" dynamic_viscosity=eta_air, \n",
" diffusivity=D, \n",
" density=air_density,\n",
")\n",
"F = formulae.ventilation.ventilation_coefficient(sqrt_re_times_cbrt_sc=Re**(1/2) * Sc**(1/3))\n",
"\n",
"r_dr_dt = formulae.drop_growth.r_dr_dt(\n",
" RH_eq=1,\n",
" T=temperature,\n",
" RH=0,\n",
" lv=0,\n",
" pvs=pvs,\n",
" D=D,\n",
" K=any_non_zero_value,\n",
" ventilation_factor=F\n",
")\n",
"adjustment_time = formulae.isotope_relaxation_timescale.tau(radius = radii, r_dr_dt = r_dr_dt)\n",
"\n",
"\n",
"pandas.options.display.float_format = '{:>,.2g}'.format\n",
"data = pandas.DataFrame({\n",
" 'radius [cm]': in_unit(radii, si.cm),\n",
" 'adjustment time [s]': adjustment_time,\n",
" 'terminal velocity [m/s]': v_term,\n",
" 'distance [m]': v_term * adjustment_time,\n",
"})\n",
"\n",
"data # pylint: disable=pointless-statement"
],
"id": "314f42c310883cfa",
"outputs": [
{
"data": {
"text/plain": [
" radius [cm] adjustment time [s] terminal velocity [m/s] distance [m]\n",
"0 0.005 1.7 0.4 0.69\n",
"1 0.01 5.4 0.8 4.3\n",
"2 0.025 20 2 40\n",
"3 0.05 48 4 1.9e+02\n",
"4 0.075 81 5.5 4.4e+02\n",
"5 0.1 1.2e+02 6.4 7.6e+02\n",
"6 0.15 2e+02 7.8 1.6e+03\n",
"7 0.2 3e+02 9 2.7e+03"
],
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>radius [cm]</th>\n",
" <th>adjustment time [s]</th>\n",
" <th>terminal velocity [m/s]</th>\n",
" <th>distance [m]</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>0.005</td>\n",
" <td>1.7</td>\n",
" <td>0.4</td>\n",
" <td>0.69</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>0.01</td>\n",
" <td>5.4</td>\n",
" <td>0.8</td>\n",
" <td>4.3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>0.025</td>\n",
" <td>20</td>\n",
" <td>2</td>\n",
" <td>40</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>0.05</td>\n",
" <td>48</td>\n",
" <td>4</td>\n",
" <td>1.9e+02</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>0.075</td>\n",
" <td>81</td>\n",
" <td>5.5</td>\n",
" <td>4.4e+02</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>0.1</td>\n",
" <td>1.2e+02</td>\n",
" <td>6.4</td>\n",
" <td>7.6e+02</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>0.15</td>\n",
" <td>2e+02</td>\n",
" <td>7.8</td>\n",
" <td>1.6e+03</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>0.2</td>\n",
" <td>3e+02</td>\n",
" <td>9</td>\n",
" <td>2.7e+03</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 4
}
],
"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.6"
}
},
"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 @@ -32,6 +32,7 @@ def findfiles(path, regex):
"Miyake_et_al_1968",
"Rozanski_and_Sonntag_1982",
"Abade_and_Albuquerque_2024",
"Bolin_1958",
],
"condensation_a": [
"Lowe_et_al_2019",
Expand Down
69 changes: 69 additions & 0 deletions tests/smoke_tests/no_env/bolin_1958/test_table_1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""
test checking values in the notebook table against those listed in the paper
"""

from pathlib import Path
from collections import defaultdict
import numpy as np
import pytest
from PySDM_examples.utils import notebook_vars
from PySDM_examples import Bolin_1958


@pytest.fixture(scope="session", name="notebook_variables")
def notebook_variables_fixture():
"""returns variables from the notebook Bolin_1958/table_1.ipynb"""
return notebook_vars(
file=Path(Bolin_1958.__file__).parent / "table_1.ipynb",
plot=False,
)


COLUMNS = {
"row": None,
"radius_cm": "radius [cm]",
"adjustment_time": "adjustment time [s]",
"terminal_velocity": "terminal velocity [m/s]",
"distance": "distance [m]",
}


@pytest.mark.parametrize(
",".join(COLUMNS.keys()),
(
(0, 0.005, 3.3, 0.27, 0.9),
(1, 0.01, 7.1, 0.72, 5.1),
(2, 0.025, 33, 2.1, 69),
(3, 0.05, 93, 4.0, 370),
(4, 0.075, 165, 5.4, 890),
(5, 0.1, 245, 6.5, 1600),
(6, 0.15, 365, 8.1, 3000),
(7, 0.2, 435, 8.8, 3800),
),
)
@pytest.mark.parametrize(
"column_var, column_label",
{k: v for k, v in COLUMNS.items() if k != "row"}.items(),
)
def test_table_1_against_values_from_the_paper(
# pylint: disable=unused-argument
# (used via locals())
*,
notebook_variables,
column_var,
column_label,
row,
radius_cm,
adjustment_time,
terminal_velocity,
distance,
):
"""comparison of the calculated values of isotopic adjustment time
and terminal velocity for drops with different radii with those presented
in the paper
"""
np.testing.assert_allclose(
actual=notebook_variables["data"][column_label][row],
desired=locals()[column_var],
rtol=defaultdict(lambda: 0.53, radius_cm=0)[column_var],
)
Loading

0 comments on commit d2ed10c

Please sign in to comment.