diff --git a/.github/workflows/buildwheels.yml b/.github/workflows/buildwheels.yml index df90af11..0fd530a8 100644 --- a/.github/workflows/buildwheels.yml +++ b/.github/workflows/buildwheels.yml @@ -92,7 +92,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, ubuntu-24.04-arm, macos-14, macos-15, macos-15-intel, windows-latest] + os: [ubuntu-latest, ubuntu-24.04-arm, macos-14, macos-15, windows-latest] steps: - uses: actions/checkout@v7 @@ -391,7 +391,7 @@ jobs: - run: | temp=`find dist/ -name "*cp312*.whl"` - python -m pip install $temp[examples] + python -m pip install $temp - run: pip install pytest-codeblocks pytest - run: mkdir readme_cpp diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 735a6d88..3c4a636e 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -18,11 +18,11 @@ jobs: fetch-depth: 0 - uses: actions/setup-python@v6 with: - python-version: 3.9 + python-version: 3.14 - run: | python -m pip install --upgrade pip pip install pylint nbqa - pip install -e .[tests] + pip install -e .[tests,examples] pip install -r .binder/requirements.txt - run: pylint --unsafe-load-any-extension=y --disable=fixme,no-member,trailing-newlines,missing-module-docstring,missing-class-docstring,missing-function-docstring,unnecessary-pass $(git ls-files '*.py') - run: nbqa pylint --unsafe-load-any-extension=y --disable=fixme,no-member,duplicate-code,wrong-import-position,ungrouped-imports,trailing-whitespace,missing-function-docstring,missing-module-docstring,unsubscriptable-object,invalid-name $(git ls-files '*.ipynb') diff --git a/examples/additive_coag_comparison.ipynb b/examples/additive_coag_comparison.ipynb index 2da7753b..6548654b 100644 --- a/examples/additive_coag_comparison.ipynb +++ b/examples/additive_coag_comparison.ipynb @@ -74,7 +74,7 @@ "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('PyPartMC', 'PySDM','dustpy')\n", + " pip_install_on_colab('PyPartMC', 'PySDM>3.0.0','dustpy')\n", "\n", " # install julia if not in system\n", " if shutil.which('julia') is None:\n", @@ -287,31 +287,31 @@ " import PySDM\n", " from PySDM.physics import si\n", "\n", - " builder = PySDM.Builder(\n", - " settings.N_PART,\n", - " backend=PySDM.backends.CPU(override_jit_flags={\"parallel\": False}),\n", - " environment=PySDM.environments.Box(\n", - " dt=settings.DT_SEC * si.s,\n", - " dv=settings.VOLUME_M3 * si.m**3\n", - " )\n", + " backend = PySDM.backends.CPU()\n", + " box = PySDM.environments.Box(\n", + " dt=settings.DT_SEC * si.s,\n", + " dv=settings.VOLUME_M3 * si.m**3,\n", + " backend=backend,\n", " )\n", - " trivia = builder.formulae.trivia\n", + " trivia = backend.formulae.trivia\n", " spectrum = PySDM.initialisation.spectra.Exponential(\n", " norm_factor=settings.NUM_CONC_PER_M3 * settings.VOLUME_M3 / si.m**3,\n", " scale=trivia.volume(radius=settings.DIAM_AT_MEAN_VOL_M / 2 * si.m)\n", " )\n", - " builder.add_dynamic(PySDM.dynamics.Coalescence(\n", - " collision_kernel=PySDM.dynamics.collisions.collision_kernels.Golovin(\n", - " b=settings.ADDITIVE_KERNEL_COEFF\n", - " ), adaptive=False\n", - " ))\n", - "\n", - " self.particulator = builder.build(\n", - " attributes=builder.particulator.environment.init_attributes(\n", - " spectral_discretisation=PySDM.initialisation.sampling.spectral_sampling.Logarithmic(\n", - " spectrum,\n", - " )\n", - " ),\n", + " attributes = {}\n", + " (\n", + " attributes['volume'],\n", + " attributes['multiplicity'],\n", + " ) = PySDM.initialisation.sampling.spectral_sampling.Logarithmic(\n", + " spectrum\n", + " ).sample_deterministic(\n", + " backend=backend,\n", + " n_sd=settings.N_PART,\n", + " )\n", + " self.particulator = PySDM.Particulator(\n", + " environment=box,\n", + " n_sd=settings.N_PART,\n", + " attributes=attributes,\n", " products=(\n", " PySDM.products.ParticleSizeSpectrumPerVolume(\n", " radius_bins_edges=settings.RADIUS_BIN_EDGES_M,\n", @@ -321,7 +321,14 @@ " radius_bins_edges=settings.RADIUS_BIN_EDGES_M,\n", " name='Volume Density (1/ unit ln R)',\n", " )\n", - " )\n", + " ),\n", + " dynamics=(\n", + " PySDM.dynamics.Coalescence(\n", + " collision_kernel=PySDM.dynamics.collisions.collision_kernels.Golovin(\n", + " b=settings.ADDITIVE_KERNEL_COEFF\n", + " ), adaptive=False\n", + " ),\n", + " ),\n", " )\n", " super().__init__(settings)\n", "\n", @@ -332,7 +339,8 @@ " 'Mass Density (kg/m^3/unit ln R)':{}}\n", " for step in range(self.settings.N_PLOT_STEPS + 1):\n", " if step != 0:\n", - " self.particulator.run(int(self.settings.PLOT_TIME_STEP_SEC // self.settings.DT_SEC))\n", + " n_steps = int(self.settings.PLOT_TIME_STEP_SEC // self.settings.DT_SEC)\n", + " self.particulator.advance(n_steps)\n", " #next two lines convert from dr to dlnr\n", " pysdm_output['Number Concentration (#/m^3/unit ln R)'][step] = (\n", " self.particulator.products['Number Concentration (#/m^3/m)'].get()\n", @@ -1070,7 +1078,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.13.5" } }, "nbformat": 4, diff --git a/examples/lognorm_ex.ipynb b/examples/lognorm_ex.ipynb index 7d8613b9..bb2e20c5 100644 --- a/examples/lognorm_ex.ipynb +++ b/examples/lognorm_ex.ipynb @@ -36,9 +36,9 @@ "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('PyPartMC', 'PySDM')\n", + " pip_install_on_colab('PyPartMC', 'PySDM>=3.0.0')\n", "elif 'JUPYTER_IMAGE' in os.environ and '.arm.gov' in os.environ['JUPYTER_IMAGE']:\n", - " !pip --quiet install PyPartMC PySDM open-atmos-jupyter-utils\n", + " !pip --quiet install PyPartMC 'PySDM>=3.0.0' open-atmos-jupyter-utils\n", " _pypartmc_path = !pip show PyPartMC | fgrep Location | cut -f2 -d' '\n", " sys.path.extend(_pypartmc_path if _pypartmc_path[0] not in sys.path else [])" ] @@ -54,14 +54,13 @@ "import ipywidgets as widgets\n", "import numpy as np\n", "from PySDM.environments import Box\n", - "from PySDM import Builder\n", "from PySDM.backends import CPU\n", - "from PySDM.initialisation import equilibrate_wet_radii\n", + "from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii\n", "from matplotlib import pyplot\n", "from IPython.display import display, clear_output\n", "from open_atmos_jupyter_utils import show_plot\n", "from PyPartMC import si\n", - "import PyPartMC as ppmc\n" + "import PyPartMC as ppmc" ] }, { @@ -234,16 +233,14 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 17, "id": "4d9881b4", "metadata": {}, "outputs": [], "source": [ "def pysdm(dry_diameters, temp, rel_humid, kappa):\n", " r_dry = dry_diameters / 2\n", - " builder = Builder(n_sd=0, backend=CPU())\n", - " environment = Box(dt=np.nan, dv=np.nan)\n", - " environment.register(builder)\n", + " environment = Box(dt=np.nan, dv=np.nan, backend=CPU())\n", " environment['T'] = temp\n", " environment['RH'] = rel_humid\n", " kappa_times_dry_volume = kappa * (np.pi / 6) * dry_diameters**3\n", @@ -256,7 +253,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 18, "id": "ee131ad8", "metadata": {}, "outputs": [], @@ -269,14 +266,14 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 19, "id": "56fd1c71", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "8413898068594929b63f25b06825f179", + "model_id": "2a2407370aa24bde8fb70b9e8ec5d5ac", "version_major": 2, "version_minor": 0 }, @@ -290,7 +287,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "a6ae915030f7410292141561b880b036", + "model_id": "69d5191b28fe45ed8c16fb1dce09e4a3", "version_major": 2, "version_minor": 0 }, @@ -304,7 +301,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ccef33aae7484b94bf014631e97a5ce7", + "model_id": "5b841ee13d2f414781d71e9d5ada2662", "version_major": 2, "version_minor": 0 }, @@ -318,7 +315,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "210dc96de734412e969291856705b875", + "model_id": "2de009dab50a484f852ef4bc0953b394", "version_major": 2, "version_minor": 0 }, @@ -332,7 +329,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "b367f53a27d14a5fa96b7c44f342877f", + "model_id": "5e013b0a4d7d4d428fdf12121f7f32d2", "version_major": 2, "version_minor": 0 }, @@ -346,7 +343,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "52c9258492e24a4581b6621f148818c5", + "model_id": "43c68b80d3374c3aa17998bd2c057515", "version_major": 2, "version_minor": 0 }, @@ -360,12 +357,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f6339251b9dd4d819c1d00fc965e6a64", + "model_id": "dd4071d070b544b496984f5001b54d10", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "HBox(children=(Label(value='Mode 1 Geometric Mean Diameter [microns]:'), FloatSlider(value=0.9, max=10.0, min=…" + "HBox(children=(Label(value='Mode 1 Geometric Mean Diameter [microns]:'), FloatSlider(value=0.901, max=10.0, mi…" ] }, "metadata": {}, @@ -374,7 +371,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "074657939eee416eaad66e74a0db2041", + "model_id": "2dc027992cc748719d3f979e145014c9", "version_major": 2, "version_minor": 0 }, @@ -388,7 +385,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0774b29ac6384f1eb7d9b4ba4e519a88", + "model_id": "6f697a6bb1f943d6b8c1b6866ae40481", "version_major": 2, "version_minor": 0 }, @@ -402,12 +399,12 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "d31861643fad408cbdc92b9ac5a4f1ce", + "model_id": "041227f317094df7b9cb3b1b60dc5de0", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "HBox(children=(Label(value='Mode 2 Geometric Mean Diameter [microns]:'), FloatSlider(value=5.8, max=10.0, min=…" + "HBox(children=(Label(value='Mode 2 Geometric Mean Diameter [microns]:'), FloatSlider(value=5.801, max=10.0, mi…" ] }, "metadata": {}, @@ -416,7 +413,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "df434a9a1721464b95c35ce8ee79a0cc", + "model_id": "44b2228c41c244e9b513d786025f9151", "version_major": 2, "version_minor": 0 }, @@ -430,7 +427,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "fa23ee7ebb8c4defbe29892795ed986a", + "model_id": "2be546cfe3fc4125a420832b2c6ef335", "version_major": 2, "version_minor": 0 }, @@ -444,7 +441,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "b709cc132b6f4d60af07434521f4abcc", + "model_id": "03ae40fabaa04509993c675ad0ad02cd", "version_major": 2, "version_minor": 0 }, @@ -549,7 +546,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.5" }, "vscode": { "interpreter": { diff --git a/examples/terminal_velocities.ipynb b/examples/terminal_velocities.ipynb index 63458ef3..b43fd836 100644 --- a/examples/terminal_velocities.ipynb +++ b/examples/terminal_velocities.ipynb @@ -1,5 +1,36 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "9238ec23-e4f8-40cc-80f1-23b404fb42e5", + "metadata": {}, + "source": [ + "[![View notebook](https://img.shields.io/static/v1?label=render%20on&logo=github&color=87ce3e&message=GitHub)](https://github.com/open-atmos/PyPartMC/blob/main/examples/terminal_velocities.ipynb) \n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/open-atmos/PyPartMC/blob/main/examples/terminal_velocities.ipynb) \n", + "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/open-atmos/PyPartMC.git/main?urlpath=lab/tree/examples/terminal_velocities.ipynb) \n", + "[![ARM JupyterHub](https://img.shields.io/static/v1?label=launch%20in&logo=jupyter&color=lightblue&message=ARM+JupyterHub)](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/open-atmos/PyPartMC&branch=main&urlPath=) (requires [logging in with ARM account](https://www.arm.gov/capabilities/computing-resources) and directing Jupyter to a notebook within the cloned repo) \n", + "[![Voila](https://img.shields.io/static/v1?label=Voil%C3%A0&logo=jupyter&color=teal&message=web+app)](https://mybinder.org/v2/gh/open-atmos/PyPartMC/main?urlpath=voila%2Frender%2Fexamples%2Fterminal_velocities.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cc5b5985-f650-4f98-8016-bcee208ab905", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "import os\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>=3.0.0')\n", + "elif 'JUPYTER_IMAGE' in os.environ and '.arm.gov' in os.environ['JUPYTER_IMAGE']:\n", + " !pip --quiet install 'PySDM>=3.0.0' open_atmos_jupyter_utils\n", + " _pypartmc_path = !pip show PyPartMC | fgrep Location | cut -f2 -d' '\n", + " sys.path.extend(_pypartmc_path if _pypartmc_path[0] not in sys.path else [])" + ] + }, { "cell_type": "code", "execution_count": 1, @@ -7,7 +38,7 @@ "metadata": {}, "outputs": [], "source": [ - "from PySDM import Builder\n", + "from PySDM import Particulator\n", "from PySDM.backends import CPU\n", "from PySDM.physics import si\n", "from PySDM.environments import Box\n", @@ -28,20 +59,23 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "id": "ad975357", "metadata": {}, "outputs": [], "source": [ - "builder = Builder(n_sd=100, backend=CPU())\n", - "builder.set_environment(Box(dt=np.nan, dv=np.nan))\n", - "builder.request_attribute('terminal velocity')\n", - "particulator = builder.build(attributes={'n': np.ones_like(volumes), 'volume': volumes})" + "box = Box(dt=np.nan, dv=np.nan, backend=CPU())\n", + "particulator = Particulator(\n", + " n_sd=100,\n", + " environment=box,\n", + " attributes={'multiplicity': np.ones_like(volumes), 'volume': volumes},\n", + " requested_attributes=('terminal velocity',)\n", + ")" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "id": "c8a54ad2", "metadata": {}, "outputs": [], @@ -51,7 +85,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "id": "ed9ca429", "metadata": {}, "outputs": [ @@ -61,16 +95,16 @@ "\n", "\n", - "\n", + "\n", " \n", " \n", " \n", " \n", - " 2022-07-26T15:21:03.094638\n", + " 2026-10-15T00:15:32.748681\n", " image/svg+xml\n", " \n", " \n", - " Matplotlib v3.5.2, https://matplotlib.org/\n", + " Matplotlib v3.10.8, https://matplotlib.org/\n", " \n", " \n", " \n", @@ -81,19 +115,18 @@ " \n", " \n", " \n", - " \n", + "\" style=\"fill: #ffffff\"/>\n", " \n", " \n", " \n", - " \n", @@ -101,23 +134,23 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -651,83 +684,83 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", - " \n", " \n", " \n", - " \n", " \n", " \n", - " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", + " \n", + " \n", " \n", " \n", "\n" ], "text/plain": [ - "
" + "
" ] }, - "metadata": { - "needs_background": "light" - }, + "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "d184a6cdf7154a21b395cce92f3857e5", + "model_id": "169be05838fd457e92993a04ae59d091", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "HTML(value=\"./terminal_velocities.pdf

\")" + "HBox(children=(HTML(value=\"./terminal_velocities.pdf= 3.0.0", "PyMieScatt", "SciPy", "dustpy; platform_system != 'Windows'" diff --git a/tests/test_loss_rate.py b/tests/test_loss_rate.py index 96b49dc6..676077c0 100644 --- a/tests/test_loss_rate.py +++ b/tests/test_loss_rate.py @@ -4,7 +4,7 @@ # Authors: https://github.com/open-atmos/PyPartMC/graphs/contributors # #################################################################################################### -from cmath import nan +import math import numpy as np import pytest @@ -57,7 +57,7 @@ def test_loss_rate(loss_function_params: str): rate = ppmc.loss_rate(scenario, vol, density, aero_data, env_state) # assert - assert rate is not nan + assert not math.isnan(rate) @staticmethod def test_loss_rate_dry_dep(): @@ -77,4 +77,4 @@ def test_loss_rate_dry_dep(): rate = ppmc.loss_rate_dry_dep(vol, density, aero_data, env_state) # assert - assert rate is not nan + assert not math.isnan(rate)