Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
77bea54
Add functionality for Long 1974
ekdejong Jan 8, 2026
0f41bf5
Add unit tests
ekdejong Jan 8, 2026
6f838ca
linting
ekdejong Jan 9, 2026
b482c85
fix output size in test
ekdejong Jan 9, 2026
02cedda
fix unit test again
ekdejong Jan 9, 2026
ed76d2d
run pre-commit (Black)
ekdejong Jan 10, 2026
c5163dc
Merge branch 'main' of https://github.com/open-atmos/PySDM into ej/ad…
ekdejong Jan 10, 2026
d98a620
fix paper link, add entry to docs bib file
slayoo Jan 10, 2026
babf080
missing https:// added
slayoo Jan 10, 2026
b082716
add equation #; remove extraneous array allocation
ekdejong Jan 16, 2026
5489b83
Change condition array to bool
ekdejong Jan 16, 2026
c2cfc8d
add GPU storage methods; switch to bool condition
ekdejong Jan 16, 2026
c516183
add GPU testing to kernel unit test
ekdejong Jan 16, 2026
2d32565
black
ekdejong Jan 16, 2026
c6c4ce1
more black
ekdejong Jan 16, 2026
3e6865c
add example for Long kernel
ekdejong Jan 16, 2026
820a89b
black & badges
ekdejong Jan 16, 2026
4ac08bf
parameterize backend for long unit test
ekdejong Jan 16, 2026
593a009
fix the backend pytest oops
ekdejong Jan 16, 2026
ec94f73
add long to bib
ekdejong Jan 16, 2026
c86207e
Merge branch 'main' of https://github.com/open-atmos/PySDM into ej/ad…
ekdejong Jan 16, 2026
f8b462e
add missing comma
ekdejong Jan 16, 2026
5ad9b13
update link in example init
ekdejong Jan 16, 2026
36785c1
remove unused import
ekdejong Jan 18, 2026
0695067
Merge branch 'main' of https://github.com/open-atmos/PySDM into ej/ad…
ekdejong Mar 18, 2026
155dbfa
black
ekdejong Mar 18, 2026
acab174
update url to match bibliography
ekdejong Mar 18, 2026
16f6d73
linting
ekdejong Mar 18, 2026
347b352
update bib
ekdejong Mar 18, 2026
f1fbd59
lint
ekdejong Mar 18, 2026
5a91044
fix where, isless Thrust backends
ekdejong Mar 18, 2026
2b30ba5
black
ekdejong Mar 18, 2026
aefa58a
add small tolerance to test for float conversion on GPU
ekdejong Mar 18, 2026
f7c3b68
add new Long_1974 dir to conftest.py
slayoo Mar 19, 2026
debdd7c
refactor: reduce code repetitions in the notebook
slayoo Mar 19, 2026
a2efc23
remove unused import
slayoo Mar 19, 2026
f8030ee
remove unused vars
slayoo Mar 19, 2026
fddd1b8
fix k specification
ekdejong Mar 24, 2026
e9120fc
add smoke test for Long's
ekdejong Mar 24, 2026
e32cf11
black
ekdejong Mar 24, 2026
cb52e0f
Fix notebook path in test_collisions.py
slayoo Mar 24, 2026
66eb701
adapt smoke-test tolerances, reduce n_sd for CI runs, fine-tune plot …
slayoo Mar 24, 2026
9a4c747
code formatting
slayoo Mar 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions PySDM/backends/impl_numba/storage.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# pylint: disable=too-many-public-methods
"""
CPU Numpy-based implementation of Storage class
"""
Expand Down Expand Up @@ -181,6 +182,13 @@ def divide_if_not_zero(self, divisor):
impl.divide_if_not_zero(self.data, divisor.data)
return self

def where(self, condition, true_value, false_value):
impl.where(self.data, condition.data, true_value.data, false_value.data)
return self

def isless(self, comparison, value):
impl.isless(self.data, comparison.data, value)

def sum(self, arg_a, arg_b):
impl.sum_out_of_place(self.data, arg_a.data, arg_b.data)
return self
Expand Down
18 changes: 18 additions & 0 deletions PySDM/backends/impl_numba/storage_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@ def divide_if_not_zero(output, divisor):
output[i] /= divisor[i]


@numba.njit(**conf.JIT_FLAGS)
def where(output, condition, true_value, false_value):
for i in numba.prange(output.shape[0]): # pylint: disable=not-an-iterable
if condition[i]:
output[i] = true_value[i]
else:
output[i] = false_value[i]


@numba.njit(**conf.JIT_FLAGS)
def isless(output, comparison, value):
for i in numba.prange(output.shape[0]): # pylint: disable=not-an-iterable
if comparison[i] < value:
output[i] = True
else:
output[i] = False


@numba.njit(**conf.JIT_FLAGS)
def floor(output):
output[:] = np.floor(output)
Expand Down
49 changes: 49 additions & 0 deletions PySDM/backends/impl_thrust_rtc/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,47 @@ def divide_if_not_zero(output, divisor):
n=(output.shape[0]), args=(Impl.thrust((output, divisor)))
)

__where_body = trtc.For(
param_names=("output", "condition", "t_value", "f_value"),
name_iter="i",
body="""
if (condition[i]) {
output[i] = t_value[i]
}
else {
output[i] = f_value[i]
}
""",
)

@staticmethod
@nice_thrust(**NICE_THRUST_FLAGS)
def where(output, condition, t_value, f_value):
Impl.__where_body.launch_n(
n=(output.shape[0]),
args=(Impl.thrust((output, condition, t_value, f_value))),
)

__isless_body = trtc.For(
param_names=("output", "comparison", "value"),
name_iter="i",
body="""
if (comparison[i] < value) {
output[i] = true
}
else {
output[i] = false
}
""",
)

@staticmethod
@nice_thrust(**NICE_THRUST_FLAGS)
def isless(output, comparison, value):
Impl.__isless_body.launch_n(
n=(output.shape[0]), args=(Impl.thrust((output, comparison, value)))
)

__exp_body = trtc.For(
("output",),
"i",
Expand Down Expand Up @@ -550,6 +591,14 @@ def divide_if_not_zero(self, divisor):
Impl.divide_if_not_zero(self, divisor)
return self

def where(self, condition, t_value, f_value):
Impl.where(self, condition, t_value, f_value)
return self

def isless(self, comparison, value):
Impl.isless(self, comparison, value)
return self

def fill(self, other):
if isinstance(other, Storage):
trtc.Copy(other.data, self.data)
Expand Down
1 change: 1 addition & 0 deletions PySDM/dynamics/collisions/collision_kernels/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
from .hydrodynamic import Hydrodynamic
from .linear import Linear
from .simple_geometric import SimpleGeometric
from .long1974 import Long1974
74 changes: 74 additions & 0 deletions PySDM/dynamics/collisions/collision_kernels/long1974.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""
piecewise kernel from Eq (11) in
[Long 1974](https://doi.org/10.1175/1520-0469%281974%29031%3C1040%3ASTTDCE%3E2.0.CO%3B2)
Comment thread
slayoo marked this conversation as resolved.

Default parameters are:
lin_coeff = 5.78e3 / s
sq_coeff = 9.44e15 / m^3 / s
r_thresh = 5e-5 m
"""


class Long1974:
Comment thread
slayoo marked this conversation as resolved.
def __init__(self, lin_coeff=5.78e3, sq_coeff=9.44e15, r_thres=5e-5):
self.lc = lin_coeff
self.sc = sq_coeff
self.rt = r_thres
self.particulator = None
self.largeR = None
self.arrays = {}

def register(self, builder):
self.particulator = builder.particulator
builder.request_attribute("volume")
builder.request_attribute("radius")
for key in (
Comment thread
ekdejong marked this conversation as resolved.
"r_lg",
"v_lg",
"v_sm",
"v_ratio",
"tmp",
"tmp1",
):
self.arrays[key] = self.particulator.PairwiseStorage.empty(
self.particulator.n_sd // 2, dtype=float
)
self.arrays["condition"] = self.particulator.PairwiseStorage.empty(
self.particulator.n_sd // 2, dtype=bool
)

def __call__(self, output, is_first_in_pair):
# get smaller and larger radii, volume
self.arrays["r_lg"].max(
self.particulator.attributes["radius"], is_first_in_pair
)
self.arrays["v_lg"].max(
self.particulator.attributes["volume"], is_first_in_pair
)
self.arrays["v_sm"].min(
self.particulator.attributes["volume"], is_first_in_pair
)

# compute volume ratio
self.arrays["v_ratio"].fill(self.arrays["v_sm"])
self.arrays["v_ratio"].divide_if_not_zero(self.arrays["v_lg"])

# compute small radius limit
self.arrays["tmp1"].fill(self.arrays["v_ratio"])
self.arrays["tmp1"] **= 2.0
self.arrays["tmp1"] += 1.0
self.arrays["tmp"].fill(self.arrays["v_lg"])
self.arrays["tmp"] **= 2.0
self.arrays["tmp1"] *= self.arrays["tmp"]
self.arrays["tmp1"] *= self.sc

# compute large radius (linear) limit
self.arrays["v_ratio"] += 1.0
self.arrays["v_ratio"] *= self.arrays["v_lg"]
self.arrays["v_ratio"] *= self.lc

# apply piecewise
self.arrays["condition"].isless(self.arrays["r_lg"], self.rt)
output.where(
self.arrays["condition"], self.arrays["tmp1"], self.arrays["v_ratio"]
)
9 changes: 9 additions & 0 deletions docs/bibliography.json
Original file line number Diff line number Diff line change
Expand Up @@ -1012,5 +1012,14 @@
],
"label": "Hayes 2004",
"title": "An Introduction to Isotopic Calculations"
},
"https://doi.org/10.1175/1520-0469%281974%29031%3C1040%3ASTTDCE%3E2.0.CO%3B2" : {
"usages": [
"PySDM/dynamics/collisions/collision_kernels/long1974.py",
"examples/PySDM_examples/Long_1974/__init__.py",
"examples/PySDM_examples/Long_1974/fig10_11_13_14.ipynb"
],
"label": "Long 1974 (J. Atmos. Sci. 31)",
"title": "Solutions to the Droplet Collection Equation for Polynomial Kernels"
}
}
8 changes: 8 additions & 0 deletions examples/PySDM_examples/Long_1974/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# pylint: disable=invalid-name
"""
piecewise collision-coalescence example from
[Long 1974 (JAS)](https://doi.org/10.1175/1520-0469%281974%29031%3C1040%3ASTTDCE%3E2.0.CO%3B2)
"""

from .settings import Settings
from .simulation import Simulation
Loading
Loading