From a3b8f21a8b7be670785a685dfeb784754c286441 Mon Sep 17 00:00:00 2001 From: Tim Luettmer Date: Fri, 14 Mar 2025 18:45:48 +0100 Subject: [PATCH 1/2] initial commit to demonstrate idea for new implementation of shape depended particle attributes --- PySDM/formulae.py | 2 + .../liquid_spheres.py | 3 - .../physics/particle_shape_common/__init__.py | 5 ++ .../physics/particle_shape_common/spheres.py | 56 +++++++++++++++++++ PySDM/physics/particle_shape_ice/__init__.py | 5 ++ PySDM/physics/particle_shape_ice/columnar.py | 12 ++++ .../physics/particle_shape_ice/ice_spheres.py | 14 +++++ .../physics/particle_shape_liquid/__init__.py | 6 ++ .../particle_shape_liquid/liquid_spheres.py | 12 ++++ .../particle_shape_liquid/porous_spheroids.py | 10 ++++ PySDM/physics/trivia.py | 21 ++++++- 11 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 PySDM/physics/particle_shape_common/__init__.py create mode 100644 PySDM/physics/particle_shape_common/spheres.py create mode 100644 PySDM/physics/particle_shape_ice/__init__.py create mode 100644 PySDM/physics/particle_shape_ice/columnar.py create mode 100644 PySDM/physics/particle_shape_ice/ice_spheres.py create mode 100644 PySDM/physics/particle_shape_liquid/__init__.py create mode 100644 PySDM/physics/particle_shape_liquid/liquid_spheres.py create mode 100644 PySDM/physics/particle_shape_liquid/porous_spheroids.py diff --git a/PySDM/formulae.py b/PySDM/formulae.py index 853000eb7d..14e5d99ffb 100644 --- a/PySDM/formulae.py +++ b/PySDM/formulae.py @@ -54,6 +54,8 @@ def __init__( # pylint: disable=too-many-locals optical_albedo: str = "Null", optical_depth: str = "Null", particle_shape_and_density: str = "LiquidSpheres", + liquid_particle_shape: str = "Spheres", + ice_particle_shape: str = "Spheres", terminal_velocity: str = "GunnKinzer1949", air_dynamic_viscosity: str = "ZografosEtAl1987", bulk_phase_partitioning: str = "Null", diff --git a/PySDM/physics/particle_shape_and_density/liquid_spheres.py b/PySDM/physics/particle_shape_and_density/liquid_spheres.py index 8b31b56824..61eee9d57e 100644 --- a/PySDM/physics/particle_shape_and_density/liquid_spheres.py +++ b/PySDM/physics/particle_shape_and_density/liquid_spheres.py @@ -26,6 +26,3 @@ def volume_to_mass(const, volume): def radius_to_mass(const, radius): return const.rho_w * const.PI_4_3 * np.power(radius, const.THREE) - @staticmethod - def reynolds_number(_, radius, velocity_wrt_air, dynamic_viscosity, density): - return 2 * radius * velocity_wrt_air * density / dynamic_viscosity diff --git a/PySDM/physics/particle_shape_common/__init__.py b/PySDM/physics/particle_shape_common/__init__.py new file mode 100644 index 0000000000..6dc65b7d3b --- /dev/null +++ b/PySDM/physics/particle_shape_common/__init__.py @@ -0,0 +1,5 @@ +""" +general particle shape relationships +""" + +from .spheres import Spheres \ No newline at end of file diff --git a/PySDM/physics/particle_shape_common/spheres.py b/PySDM/physics/particle_shape_common/spheres.py new file mode 100644 index 0000000000..4ef9e4f898 --- /dev/null +++ b/PySDM/physics/particle_shape_common/spheres.py @@ -0,0 +1,56 @@ +""" +spherical particles with constant density +""" +from PySDM.physics.trivia import Trivia + +class Spheres: + def __init__(self, _): + pass + + @staticmethod + def mass_to_volume(const, mass): + return ( + Trivia.volume_of_density_mass(self.mass_density, mass) + ) + + @staticmethod + def volume_to_mass(const, volume): + return ( + Trivia.mass_of_density_volume(self.mass_density, volume) + ) + + @staticmethod + def volume_to_radius(const, volume): + return ( + Trivia.radius(const, volume) + ) + + @staticmethod + def radius_to_volume(const, radius): + return ( + Trivia.sphere_radius_to_volume(const, radius) + ) + + @staticmethod + def radius_to_mass(const, radius): + return ( + self.sphere_radius_to_volumeconst, radius) * self.mass_density + ) + + @staticmethod + def radius_to_area(const, radius): + return( + Trivia.area(const, radius) + ) + + @staticmethod + def mass_to_capacity(const, mass): + return( + Trivia.sphere_mass_to_radius(const, mass, self.mass_density) + ) + + @staticmethod + def maximum_diameter(const, mass): + return( + Trivia.sphere_mass_to_radius(const, mass, self.mass_density) * 2. + ) diff --git a/PySDM/physics/particle_shape_ice/__init__.py b/PySDM/physics/particle_shape_ice/__init__.py new file mode 100644 index 0000000000..53f1e28962 --- /dev/null +++ b/PySDM/physics/particle_shape_ice/__init__.py @@ -0,0 +1,5 @@ +""" +particle shape and density relationships for ice particles +""" + +from .ice_spheres import IceSpheres diff --git a/PySDM/physics/particle_shape_ice/columnar.py b/PySDM/physics/particle_shape_ice/columnar.py new file mode 100644 index 0000000000..31a5b37c9f --- /dev/null +++ b/PySDM/physics/particle_shape_ice/columnar.py @@ -0,0 +1,12 @@ +""" +columnar particles with constant density of ice +""" + +class Columnar: + def __init__(self, const, _): + self.mass_density = const.rho_i + + @staticmethod + def supports_mixed_phase(_=None): + return True + diff --git a/PySDM/physics/particle_shape_ice/ice_spheres.py b/PySDM/physics/particle_shape_ice/ice_spheres.py new file mode 100644 index 0000000000..66dc167821 --- /dev/null +++ b/PySDM/physics/particle_shape_ice/ice_spheres.py @@ -0,0 +1,14 @@ +""" +spherical particles with constant density of ice +""" +from PySDM.physics.particle_shape_common import Spheres + +class IceSpheres(Spheres): + def __init__(self, const, _): + self.mass_density = const.rho_i + + @staticmethod + def supports_mixed_phase(_=None): + return True + + diff --git a/PySDM/physics/particle_shape_liquid/__init__.py b/PySDM/physics/particle_shape_liquid/__init__.py new file mode 100644 index 0000000000..a35c5f0c4d --- /dev/null +++ b/PySDM/physics/particle_shape_liquid/__init__.py @@ -0,0 +1,6 @@ +""" +particle shape and density relationships for liquid particles +""" + +from .liquid_spheres import LiquidSpheres +from .porous_spheroids import PorousSpheroid diff --git a/PySDM/physics/particle_shape_liquid/liquid_spheres.py b/PySDM/physics/particle_shape_liquid/liquid_spheres.py new file mode 100644 index 0000000000..fa57f6b4ef --- /dev/null +++ b/PySDM/physics/particle_shape_liquid/liquid_spheres.py @@ -0,0 +1,12 @@ +""" +spherical particles with constant density of ice +""" +from PySDM.physics.particle_shape_common import Spheres + +class LiquidSpheres(Spheres): + def __init__(self, const, _): + self.mass_density = const.rho_w + + @staticmethod + def reynolds_number(_, radius, velocity_wrt_air, dynamic_viscosity, density): + return 2 * radius * velocity_wrt_air * density / dynamic_viscosity \ No newline at end of file diff --git a/PySDM/physics/particle_shape_liquid/porous_spheroids.py b/PySDM/physics/particle_shape_liquid/porous_spheroids.py new file mode 100644 index 0000000000..94d6cf3d6a --- /dev/null +++ b/PySDM/physics/particle_shape_liquid/porous_spheroids.py @@ -0,0 +1,10 @@ +""" +for mixed-phase microphysics as in +[Shima et al. 2020](https://doi.org/10.5194/gmd-13-4107-2020) +""" + + +class PorousSpheroid: # pylint: disable=too-few-public-methods + @staticmethod + def supports_mixed_phase(_=None): + return True diff --git a/PySDM/physics/trivia.py b/PySDM/physics/trivia.py index 8bfb0f7716..97852e3dbf 100644 --- a/PySDM/physics/trivia.py +++ b/PySDM/physics/trivia.py @@ -12,21 +12,36 @@ def __init__(self, _): pass @staticmethod - def volume_of_density_mass(rho, m): - return m / rho + def volume_of_density_mass(rho, mass): + return mass / rho + @staticmethod + def mass_of_density_volume(rho, volume): + return volume * rho + + # TODO: change name to sphere_volume_to_radius @staticmethod def radius(const, volume): return np.power(volume / const.PI_4_3, const.ONE_THIRD) + # TODO: change name to sphere_radius_to_area @staticmethod def area(const, radius): return const.PI * const.FOUR * np.power(radius, const.TWO) @staticmethod - def volume(const, radius): + def sphere_radius_to_volume(const, radius): return const.PI_4_3 * np.power(radius, const.THREE) + @staticmethod + def sphere_radius_to_mass(const, radius, density): + return const.PI_4_3 * np.power(radius, const.THREE) * density + + @staticmethod + def sphere_mass_to_radius(const, mass, density): + return const.PI_4_3 * np.power(radius, const.THREE) * density + + # TODO: remove this. There is already sphere_radius_to_area @staticmethod def sphere_surface(const, diameter): return const.PI * diameter**2 From f4b6c1e14f427b39aa6f94c95e9db5730f12c55e Mon Sep 17 00:00:00 2001 From: Tim Luettmer Date: Mon, 17 Mar 2025 11:31:47 +0100 Subject: [PATCH 2/2] removed general spheres class --- PySDM/formulae.py | 4 +- .../physics/particle_shape_common/__init__.py | 5 -- .../physics/particle_shape_common/spheres.py | 56 ------------------- .../physics/particle_shape_ice/ice_spheres.py | 55 +++++++++++++++++- .../particle_shape_liquid/liquid_spheres.py | 54 ++++++++++++++++-- 5 files changed, 103 insertions(+), 71 deletions(-) delete mode 100644 PySDM/physics/particle_shape_common/__init__.py delete mode 100644 PySDM/physics/particle_shape_common/spheres.py diff --git a/PySDM/formulae.py b/PySDM/formulae.py index 14e5d99ffb..c867638325 100644 --- a/PySDM/formulae.py +++ b/PySDM/formulae.py @@ -54,8 +54,8 @@ def __init__( # pylint: disable=too-many-locals optical_albedo: str = "Null", optical_depth: str = "Null", particle_shape_and_density: str = "LiquidSpheres", - liquid_particle_shape: str = "Spheres", - ice_particle_shape: str = "Spheres", + liquid_particle_shape: str = "LiquidSpheres", + ice_particle_shape: str = "IceSpheres", terminal_velocity: str = "GunnKinzer1949", air_dynamic_viscosity: str = "ZografosEtAl1987", bulk_phase_partitioning: str = "Null", diff --git a/PySDM/physics/particle_shape_common/__init__.py b/PySDM/physics/particle_shape_common/__init__.py deleted file mode 100644 index 6dc65b7d3b..0000000000 --- a/PySDM/physics/particle_shape_common/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -""" -general particle shape relationships -""" - -from .spheres import Spheres \ No newline at end of file diff --git a/PySDM/physics/particle_shape_common/spheres.py b/PySDM/physics/particle_shape_common/spheres.py deleted file mode 100644 index 4ef9e4f898..0000000000 --- a/PySDM/physics/particle_shape_common/spheres.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -spherical particles with constant density -""" -from PySDM.physics.trivia import Trivia - -class Spheres: - def __init__(self, _): - pass - - @staticmethod - def mass_to_volume(const, mass): - return ( - Trivia.volume_of_density_mass(self.mass_density, mass) - ) - - @staticmethod - def volume_to_mass(const, volume): - return ( - Trivia.mass_of_density_volume(self.mass_density, volume) - ) - - @staticmethod - def volume_to_radius(const, volume): - return ( - Trivia.radius(const, volume) - ) - - @staticmethod - def radius_to_volume(const, radius): - return ( - Trivia.sphere_radius_to_volume(const, radius) - ) - - @staticmethod - def radius_to_mass(const, radius): - return ( - self.sphere_radius_to_volumeconst, radius) * self.mass_density - ) - - @staticmethod - def radius_to_area(const, radius): - return( - Trivia.area(const, radius) - ) - - @staticmethod - def mass_to_capacity(const, mass): - return( - Trivia.sphere_mass_to_radius(const, mass, self.mass_density) - ) - - @staticmethod - def maximum_diameter(const, mass): - return( - Trivia.sphere_mass_to_radius(const, mass, self.mass_density) * 2. - ) diff --git a/PySDM/physics/particle_shape_ice/ice_spheres.py b/PySDM/physics/particle_shape_ice/ice_spheres.py index 66dc167821..b7ecc450c1 100644 --- a/PySDM/physics/particle_shape_ice/ice_spheres.py +++ b/PySDM/physics/particle_shape_ice/ice_spheres.py @@ -1,14 +1,63 @@ """ spherical particles with constant density of ice """ -from PySDM.physics.particle_shape_common import Spheres +from PySDM.physics.trivia import Trivia -class IceSpheres(Spheres): +class IceSpheres: def __init__(self, const, _): - self.mass_density = const.rho_i + pass @staticmethod def supports_mixed_phase(_=None): return True + @staticmethod + def mass_to_volume(const, mass): + return ( + Trivia.volume_of_density_mass(const, const.rho_i, mass) + ) + + @staticmethod + def volume_to_mass(const, volume): + return ( + Trivia.mass_of_density_volume(const.rho_i, volume) + ) + + @staticmethod + def volume_to_radius(const, volume): + return ( + Trivia.radius(const, volume) + ) + + @staticmethod + def radius_to_volume(const, radius): + return ( + Trivia.sphere_radius_to_volume(const, radius) + ) + + @staticmethod + def radius_to_mass(const, radius, mass_density): + return ( + Trivia.sphere_radius_to_volume(const, radius) * const.rho_i + ) + + @staticmethod + def radius_to_area(const, radius): + return( + Trivia.area(const, radius) + ) + + @staticmethod + def mass_to_capacity(const, mass): + return( + Trivia.sphere_mass_to_radius(const, mass, const.rho_i,) + ) + + @staticmethod + def maximum_diameter(const, mass): + return( + Trivia.sphere_mass_to_radius(const, mass, const.rho_i) * 2 + ) + + diff --git a/PySDM/physics/particle_shape_liquid/liquid_spheres.py b/PySDM/physics/particle_shape_liquid/liquid_spheres.py index fa57f6b4ef..3351bc2539 100644 --- a/PySDM/physics/particle_shape_liquid/liquid_spheres.py +++ b/PySDM/physics/particle_shape_liquid/liquid_spheres.py @@ -1,12 +1,56 @@ """ spherical particles with constant density of ice """ -from PySDM.physics.particle_shape_common import Spheres +from PySDM.physics.trivia import Trivia -class LiquidSpheres(Spheres): +class LiquidSpheres: def __init__(self, const, _): - self.mass_density = const.rho_w + pass @staticmethod - def reynolds_number(_, radius, velocity_wrt_air, dynamic_viscosity, density): - return 2 * radius * velocity_wrt_air * density / dynamic_viscosity \ No newline at end of file + def mass_to_volume(const, mass): + return ( + Trivia.volume_of_density_mass(const, const.rho_w, mass) + ) + + @staticmethod + def volume_to_mass(const, volume): + return ( + Trivia.mass_of_density_volume(const.rho_w, volume) + ) + + @staticmethod + def volume_to_radius(const, volume): + return ( + Trivia.radius(const, volume) + ) + + @staticmethod + def radius_to_volume(const, radius): + return ( + Trivia.sphere_radius_to_volume(const, radius) + ) + + @staticmethod + def radius_to_mass(const, radius, mass_density): + return ( + Trivia.sphere_radius_to_volume(const, radius) * const.rho_w + ) + + @staticmethod + def radius_to_area(const, radius): + return ( + Trivia.area(const, radius) + ) + + @staticmethod + def mass_to_capacity(const, mass): + return ( + Trivia.sphere_mass_to_radius(const, mass, const.rho_w, ) + ) + + @staticmethod + def maximum_diameter(const, mass): + return ( + Trivia.sphere_mass_to_radius(const, mass, const.rho_w) * 2 + ) \ No newline at end of file