From e7b40ae4eecffe9dfa88c60d7f4cc89ce1a242a6 Mon Sep 17 00:00:00 2001 From: Jeff Curtis Date: Thu, 18 Jun 2026 13:51:56 -0500 Subject: [PATCH] add wrappers for particle frozen properties --- src/aero_particle.F90 | 45 +++++++++++++++++++++++++++++++ src/aero_particle.hpp | 15 +++++++++++ src/pypartmc.cpp | 6 +++++ tests/test_aero_particle.py | 53 +++++++++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+) diff --git a/src/aero_particle.F90 b/src/aero_particle.F90 index 28851d0d..ee00777f 100644 --- a/src/aero_particle.F90 +++ b/src/aero_particle.F90 @@ -536,6 +536,51 @@ subroutine f_aero_particle_frozen( & end subroutine + subroutine f_aero_particle_imf_temperature( & + aero_particle_ptr_c, & + imf_temperature & + ) bind(C) + + type(aero_particle_t), pointer :: aero_particle_ptr_f => null() + type(c_ptr), intent(in) :: aero_particle_ptr_c + real(c_double), intent(out) :: imf_temperature + + call c_f_pointer(aero_particle_ptr_c, aero_particle_ptr_f) + + imf_temperature = aero_particle_ptr_f%imf_temperature + + end subroutine + + subroutine f_aero_particle_den_ice( & + aero_particle_ptr_c, & + den_ice & + ) bind(C) + + type(aero_particle_t), pointer :: aero_particle_ptr_f => null() + type(c_ptr), intent(in) :: aero_particle_ptr_c + real(c_double), intent(out) :: den_ice + + call c_f_pointer(aero_particle_ptr_c, aero_particle_ptr_f) + + den_ice = aero_particle_ptr_f%den_ice + + end subroutine + + subroutine f_aero_particle_ice_shape_phi( & + aero_particle_ptr_c, & + ice_shape_phi & + ) bind(C) + + type(aero_particle_t), pointer :: aero_particle_ptr_f => null() + type(c_ptr), intent(in) :: aero_particle_ptr_c + real(c_double), intent(out) :: ice_shape_phi + + call c_f_pointer(aero_particle_ptr_c, aero_particle_ptr_f) + + ice_shape_phi = aero_particle_ptr_f%ice_shape_phi + + end subroutine + subroutine f_aero_particle_refract_shell( & aero_particle_ptr_c, & refract_shell, & diff --git a/src/aero_particle.hpp b/src/aero_particle.hpp index 36c21759..fede9215 100644 --- a/src/aero_particle.hpp +++ b/src/aero_particle.hpp @@ -43,6 +43,9 @@ extern "C" void f_aero_particle_least_create_time(const void *aero_particle_ptr, extern "C" void f_aero_particle_get_component_sources(const void *aero_particle_ptr, void *arr_data, const int *arr_size) noexcept; extern "C" void f_aero_particle_id(const void *aero_particle_ptr, int64_t *val) noexcept; extern "C" void f_aero_particle_frozen(const void *aero_particle_ptr, bool *val) noexcept; +extern "C" void f_aero_particle_imf_temperature(const void *aero_particle_ptr, double *val) noexcept; +extern "C" void f_aero_particle_den_ice(const void *aero_particle_ptr, double *val) noexcept; +extern "C" void f_aero_particle_ice_shape_phi(const void *aero_particle_ptr, double *val) noexcept; extern "C" void f_aero_particle_refract_shell(const void *aero_particle_ptr, std::complex *val, const int *arr_size) noexcept; extern "C" void f_aero_particle_refract_core(const void *aero_particle_ptr, std::complex *val, const int *arr_size) noexcept; extern "C" void f_aero_particle_set_weight_class(void *ptr, const int *weight_class) noexcept; @@ -243,6 +246,18 @@ struct AeroParticle { return pypartmc::get_value(self, f_aero_particle_frozen); } + static auto imf_temperature(const AeroParticle &self) { + return pypartmc::get_value(self, f_aero_particle_imf_temperature); + } + + static auto den_ice(const AeroParticle &self) { + return pypartmc::get_value(self, f_aero_particle_den_ice); + } + + static auto ice_shape_phi(const AeroParticle &self) { + return pypartmc::get_value(self, f_aero_particle_ice_shape_phi); + } + static auto refract_shell(const AeroParticle &self) { auto fn = f_aero_particle_refract_shell; return pypartmc::get_array_values_set_len>(self, fn, n_swbands); diff --git a/src/pypartmc.cpp b/src/pypartmc.cpp index 08d62bf8..44d25cc7 100644 --- a/src/pypartmc.cpp +++ b/src/pypartmc.cpp @@ -267,6 +267,12 @@ NB_MODULE(_PyPartMC, m) { "Last time a constituent was created (s).") .def_prop_ro("id", AeroParticle::id, "Unique ID number.") .def_prop_ro("is_frozen", AeroParticle::is_frozen, "Frozen status - particle is ice if 1.") + .def_prop_ro("imf_temperature", AeroParticle::imf_temperature, + "Immersion freezing temperature (K).") + .def_prop_ro("den_ice", AeroParticle::den_ice, + "Ice density (kg/m^3).") + .def_prop_ro("ice_shape_phi", AeroParticle::ice_shape_phi, + "Ice shape.") .def("mobility_diameter", AeroParticle::mobility_diameter, "Mobility diameter of the particle (m).") .def_prop_ro("density", AeroParticle::density, diff --git a/tests/test_aero_particle.py b/tests/test_aero_particle.py index 7c65cae6..18cb8cb4 100644 --- a/tests/test_aero_particle.py +++ b/tests/test_aero_particle.py @@ -614,3 +614,56 @@ def test_is_frozen(): # assert assert isinstance(frozen[0], int) assert all(x == 0 for x in frozen) + + @staticmethod + def test_imf_temperature(): + # arrange + aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL) + aero_dist = ppmc.AeroDist(aero_data, AERO_DIST_CTOR_ARG_MINIMAL) + aero_state = ppmc.AeroState(aero_data, *AERO_STATE_CTOR_ARG_MINIMAL) + _ = aero_state.dist_sample(aero_dist, 1.0, 0.0) + + # act + imf_temperature = [] + for i_part in range(len(aero_state)): + imf_temperature.append(aero_state.particle(i_part).imf_temperature) + + # assert + assert isinstance(imf_temperature[0], float) + assert all(x == 0.0 for x in imf_temperature) + + @staticmethod + def test_den_ice(): + # arrange + aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL) + aero_dist = ppmc.AeroDist(aero_data, AERO_DIST_CTOR_ARG_MINIMAL) + aero_state = ppmc.AeroState(aero_data, *AERO_STATE_CTOR_ARG_MINIMAL) + _ = aero_state.dist_sample(aero_dist, 1.0, 0.0) + + # act + den_ice = [] + for i_part in range(len(aero_state)): + den_ice.append(aero_state.particle(i_part).den_ice) + + # assert + assert isinstance(den_ice[0], float) + # unfrozen particles have an undefined ice density + assert all(x == -9999.0 for x in den_ice) + + @staticmethod + def test_ice_shape_phi(): + # arrange + aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL) + aero_dist = ppmc.AeroDist(aero_data, AERO_DIST_CTOR_ARG_MINIMAL) + aero_state = ppmc.AeroState(aero_data, *AERO_STATE_CTOR_ARG_MINIMAL) + _ = aero_state.dist_sample(aero_dist, 1.0, 0.0) + + # act + ice_shape_phi = [] + for i_part in range(len(aero_state)): + ice_shape_phi.append(aero_state.particle(i_part).ice_shape_phi) + + # assert + assert isinstance(ice_shape_phi[0], float) + # unfrozen particles have an undefined ice shape + assert all(x == -9999.0 for x in ice_shape_phi)