From c43adb036d776967df4797adcdb07c338c0a6817 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Thu, 14 Dec 2023 09:38:22 +0100 Subject: [PATCH 01/97] simple ice: formulas and calculate RH_i --- include/libcloudph++/common/const_cp.hpp | 44 ++++++++++-- include/libcloudph++/common/maxwell-mason.hpp | 67 ++++++++++++++----- include/libcloudph++/common/moist_air.hpp | 3 + src/impl/particles_impl.ipp | 9 +-- src/impl/particles_impl_cond_common.ipp | 2 + src/impl/particles_impl_hskpng_Tpr.ipp | 53 +++++++++++++++ src/impl/particles_impl_init_hskpng_ncell.ipp | 1 + 7 files changed, 156 insertions(+), 23 deletions(-) diff --git a/include/libcloudph++/common/const_cp.hpp b/include/libcloudph++/common/const_cp.hpp index 1029ba3de..aabcc51c6 100644 --- a/include/libcloudph++/common/const_cp.hpp +++ b/include/libcloudph++/common/const_cp.hpp @@ -11,6 +11,7 @@ namespace libcloudphxx namespace const_cp { using moist_air::c_pw; + using moist_air::c_pi; using moist_air::c_pv; using moist_air::R_v; using moist_air::eps; @@ -20,7 +21,8 @@ namespace libcloudphxx // water triple point parameters libcloudphxx_const(si::pressure, p_tri, 611.73, si::pascals) // pressure libcloudphxx_const(si::temperature, T_tri, 273.16, si::kelvins) // temperature - libcloudphxx_const(energy_over_mass, l_tri, 2.5e6, si::joules / si::kilograms) // latent heat of evaporation + libcloudphxx_const(energy_over_mass, lv_tri, 2.5e6, si::joules / si::kilograms) // latent heat of evaporation + libcloudphxx_const(energy_over_mass, ls_tri, 2.834e6, si::joules / si::kilograms) // latent heat of sublimation // saturation vapour pressure for water assuming constant c_p_v and c_p_w // with constants taken at triple point @@ -34,11 +36,26 @@ namespace libcloudphxx // { return p_tri() * exp( - (l_tri() + (c_pw() - c_pv()) * T_tri()) / R_v() * (real_t(1) / T_tri() - real_t(1) / T) + (lv_tri() + (c_pw() - c_pv()) * T_tri()) / R_v() * (real_t(1) / T_tri() - real_t(1) / T) - (c_pw() - c_pv()) / R_v() * std::log(T / T_tri()) ); } + // saturation vapour pressure for ice assuming constant c_p_v and c_p_i + // with constants taken at triple point + // (solution to the Clausius-Clapeyron equation assuming rho_vapour << rho_solid) + template + BOOST_GPU_ENABLED + quantity p_vsi( + const quantity &T + ) + { + return p_tri() * exp( + (ls_tri() + (c_pi() - c_pv()) * T_tri()) / R_v() * (real_t(1) / T_tri() - real_t(1) / T) + - (c_pi() - c_pv()) / R_v() * std::log(T / T_tri()) + ); + } + // saturation vapour mixing ratio for water as a function of pressure and temperature template BOOST_GPU_ENABLED @@ -49,13 +66,32 @@ namespace libcloudphxx return eps() / (p / p_vs(T) - 1); } - // latent heat for constant c_p + // saturation vapour mixing ratio for ice as a function of pressure and temperature + template + BOOST_GPU_ENABLED + quantity r_vsi( + const quantity &T, + const quantity &p + ) { + return eps() / (p / p_vsi(T) - 1); + } + + // latent heat of evaporation for constant c_p template BOOST_GPU_ENABLED quantity::type , real_t> l_v( const quantity &T ) { - return l_tri() + (c_pv() - c_pw()) * (T - T_tri()); + return lv_tri() + (c_pv() - c_pw()) * (T - T_tri()); + } + + // latent heat sublimation for constant c_p + template + BOOST_GPU_ENABLED + quantity::type , real_t> l_s( + const quantity &T + ) { + return ls_tri() + (c_pv() - c_pi()) * (T - T_tri()); } }; }; diff --git a/include/libcloudph++/common/maxwell-mason.hpp b/include/libcloudph++/common/maxwell-mason.hpp index c86de2371..4145570c0 100644 --- a/include/libcloudph++/common/maxwell-mason.hpp +++ b/include/libcloudph++/common/maxwell-mason.hpp @@ -15,34 +15,71 @@ namespace libcloudphxx quantity::type, real_t> rdrdt( const quantity D, // D const quantity K, // K - const quantity rho_v, // ambient water vapour density - const quantity T, // ambient temperature - const quantity p, // ambient pressure - const quantity RH, // p_v/p_vs = relative humidity - const quantity a_w, // water activity - const quantity klvntrm // the Kelvin term + const quantity rho_v, // ambient water vapour density + const quantity T, // ambient temperature + const quantity p, // ambient pressure + const quantity RH, // p_v/p_vs = relative humidity + const quantity a_w, // water activity + const quantity klvntrm // the Kelvin term ) { using moist_air::rho_w; + using moist_air::rho_i; using moist_air::R_v; - quantity::type, real_t> + quantity::type, real_t> l_v = const_cp::l_v(T); - return (real_t(1) - a_w * klvntrm / RH) - / rho_w() - / ( - real_t(1) + return (real_t(1) - a_w * klvntrm / RH) + / rho_w() + / ( + real_t(1) / D / rho_v - + - l_v + + + l_v / K / RH / T * (l_v / R_v() / T - real_t(1)) - ) - ; + ) + ; + } + + // for ice + // mass accommodation coeff = 1 + // no solute nor curvature effects + template + BOOST_GPU_ENABLED + quantity::type, real_t> rdrdt_i( + const quantity D, // D + const quantity K, // K + const quantity rho_v, // ambient water vapour density + const quantity T, // ambient temperature + const quantity p, // ambient pressure + const quantity RH_i // p_v/p_vsi = relative humidity w.r.t. ice + ) + { + using moist_air::rho_i; + using moist_air::R_v; + + quantity::type, real_t> + l_s = const_cp::l_s(T); + + return (real_t(1) - real_t(1) / RH_i) + / rho_i() + / ( + real_t(1) + / D + / rho_v + + + l_s + / K + / RH_i + / T + * (l_s / R_v() / T - real_t(1)) + ) + ; } }; }; diff --git a/include/libcloudph++/common/moist_air.hpp b/include/libcloudph++/common/moist_air.hpp index 536f33616..fa62367c7 100644 --- a/include/libcloudph++/common/moist_air.hpp +++ b/include/libcloudph++/common/moist_air.hpp @@ -26,6 +26,7 @@ namespace libcloudphxx libcloudphxx_const(energy_over_temperature_mass, c_pd, 1005, si::joules / si::kilograms / si::kelvins) // dry air libcloudphxx_const(energy_over_temperature_mass, c_pv, 1850, si::joules / si::kilograms / si::kelvins) // water vapour libcloudphxx_const(energy_over_temperature_mass, c_pw, 4218, si::joules / si::kilograms / si::kelvins) // liquid water + libcloudphxx_const(energy_over_temperature_mass, c_pi, 2114, si::joules / si::kilograms / si::kelvins) // ice // molar masses libcloudphxx_const(mass_over_amount, M_d, 0.02897, si::kilograms / si::moles) // dry air (Curry & Webster / Seinfeld & Pandis) @@ -47,6 +48,8 @@ namespace libcloudphxx // water density libcloudphxx_const(si::mass_density, rho_w, 1e3, si::kilograms / si::cubic_metres) + // ice density + libcloudphxx_const(si::mass_density, rho_i, 910, si::kilograms / si::cubic_metres) // mixing rule for extensive quantitites (i.e. using mass mixing ratio) template diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index eed117466..9e1517762 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -143,10 +143,11 @@ namespace libcloudphxx std::map output_puddle; thrust_device::vector - T, // temperature [K] - p, // pressure [Pa] - RH, // relative humisity - eta,// dynamic viscosity + T, // temperature [K] + p, // pressure [Pa] + RH, // relative humisity + RH_i, // relative humisity w.r.t. ice + eta, // dynamic viscosity diss_rate; // turbulent kinetic energy dissipation rate thrust_device::vector w_LS; // large-scale subsidence velocity profile diff --git a/src/impl/particles_impl_cond_common.ipp b/src/impl/particles_impl_cond_common.ipp index f64a59946..e8c04f897 100644 --- a/src/impl/particles_impl_cond_common.ipp +++ b/src/impl/particles_impl_cond_common.ipp @@ -136,6 +136,8 @@ namespace libcloudphxx a_w(rw3, rd3, kpa), klvntrm(rw, T) ); + + // TODO: rdrdt_i if ice } // backward Euler scheme: diff --git a/src/impl/particles_impl_hskpng_Tpr.ipp b/src/impl/particles_impl_hskpng_Tpr.ipp index 38433bcad..a869dd5ed 100644 --- a/src/impl/particles_impl_hskpng_Tpr.ipp +++ b/src/impl/particles_impl_hskpng_Tpr.ipp @@ -102,6 +102,24 @@ namespace libcloudphxx / common::tetens::r_vs(T * si::kelvins, p * si::pascals)); } + template + BOOST_GPU_ENABLED + real_t RHi_pv_cc(const real_t &p, const real_t &rv, const real_t &T) + { + return real_t( + common::moist_air::p_v(p * si::pascals, quantity(rv)) + / common::const_cp::p_vsi(T * si::kelvins)); + } + + template + BOOST_GPU_ENABLED + real_t RHi_rv_cc(const real_t &p, const real_t &rv, const real_t &T) + { + return real_t( + rv + / common::const_cp::r_vsi(T * si::kelvins, p * si::pascals)); + } + template struct RH : thrust::unary_function&, real_t> { @@ -158,6 +176,32 @@ namespace libcloudphxx } } }; + + template + struct RH_i : thrust::unary_function&, real_t> + { + const RH_formula_t RH_formula; + // the type of formula to be used for RH + RH(RH_formula_t RH_formula): + RH_formula(RH_formula) + {} + + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) // p, rv, T + { + switch (RH_formula) + { + case RH_formula_t::pv_cc: + return RHi_pv_cc(thrust::get<0>(tpl), thrust::get<1>(tpl), thrust::get<2>(tpl)); + case RH_formula_t::rv_cc: + return RHi_rv_cc(thrust::get<0>(tpl), thrust::get<1>(tpl), thrust::get<2>(tpl)); + // NOTE: no Tetens formulas for ice + default: + assert(0); + return 0.; + } + } + }; template struct common__vterm__visc : thrust::unary_function// TODO: rename it! (vterm) visc_eta? @@ -214,12 +258,21 @@ namespace libcloudphxx ); } + // RH thrust::transform( zip_it_t(thrust::make_tuple(p.begin(), rv.begin(), T.begin())), // input - begin zip_it_t(thrust::make_tuple(p.end(), rv.end(), T.end() )), // input - end RH.begin(), // output detail::RH(opts_init.RH_formula) ); + + // RH_i + thrust::transform( + zip_it_t(thrust::make_tuple(p.begin(), rv.begin(), T.begin())), // input - begin + zip_it_t(thrust::make_tuple(p.end(), rv.end(), T.end() )), // input - end + RH_i.begin(), // output + detail::RH_i(opts_init.RH_formula) + ); } // dynamic viscosity diff --git a/src/impl/particles_impl_init_hskpng_ncell.ipp b/src/impl/particles_impl_init_hskpng_ncell.ipp index 2b92d6601..42806acf4 100644 --- a/src/impl/particles_impl_init_hskpng_ncell.ipp +++ b/src/impl/particles_impl_init_hskpng_ncell.ipp @@ -15,6 +15,7 @@ namespace libcloudphxx // memory allocation, p already resized in init_sync() T.resize(n_cell); RH.resize(n_cell); + RH_i.resize(n_cell); eta.resize(n_cell); count_ijk.resize(n_cell); From aa2d33ed9a6d6a9defa37f63ad69b6c8e61321fe Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Thu, 14 Dec 2023 10:35:57 +0100 Subject: [PATCH 02/97] add ice to dry_distros/sizes --- include/libcloudph++/lgrngn/opts_init.hpp | 10 ++++++---- src/impl/particles_impl.ipp | 7 ++++++- src/impl/particles_impl_hskpng_resize.ipp | 2 ++ src/impl/particles_impl_init_SD_with_distros.ipp | 7 +++++-- src/impl/particles_impl_init_SD_with_sizes.ipp | 11 ++++++----- src/impl/particles_impl_init_sanity_check.ipp | 10 ++++++++++ src/impl/particles_impl_reserve_hskpng_npart.ipp | 3 +++ src/impl/particles_impl_rlx_dry_distros.ipp | 2 +- src/impl/particles_impl_src_dry_distros_simple.ipp | 5 ++++- src/impl/particles_impl_src_dry_sizes.ipp | 12 +++++++----- 10 files changed, 50 insertions(+), 19 deletions(-) diff --git a/include/libcloudph++/lgrngn/opts_init.hpp b/include/libcloudph++/lgrngn/opts_init.hpp index 504e5f8bc..493c3d326 100644 --- a/include/libcloudph++/lgrngn/opts_init.hpp +++ b/include/libcloudph++/lgrngn/opts_init.hpp @@ -29,16 +29,16 @@ namespace libcloudphxx // defined with a distribution // uses shared_ptr to make opts_init copyable typedef std::unordered_map< - real_t, // kappa + std::pair, // (kappa, ice) std::shared_ptr> // n(ln(rd)) @ STP; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true > dry_distros_t; dry_distros_t dry_distros; // defined with a size-number pair typedef std::map< - real_t, // kappa - std::map // STP_concentration [1/m^3], number of SD that represent this radius kappa and concentration + std::pair, // (kappa, ice) + std::map // STP_concentration [1/m^3], number of SD that represent this radius kappa and concentration > > dry_sizes_t; dry_sizes_t dry_sizes; @@ -96,6 +96,7 @@ namespace libcloudphxx turb_adve_switch, // if true, turbulent motion of SDs is modeled turb_cond_switch, // if true, turbulent condensation of SDs is modeled turb_coal_switch, // if true, turbulent coalescence kernels can be used + ice_switch, // if true, ice is allowed exact_sstp_cond; // if true, use per-particle sstp_cond logic, if false, use per-cell int sstp_chem; @@ -203,6 +204,7 @@ namespace libcloudphxx coal_switch(true), // coalescence turned on by default src_type(src_t::off), // source turned off by default rlx_switch(false), + ice_switch(false), exact_sstp_cond(false), turb_cond_switch(false), turb_adve_switch(false), diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 9e1517762..54529a96c 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -89,7 +89,8 @@ namespace libcloudphxx sstp_tmp_th, // ditto for theta sstp_tmp_rh, // ditto for rho sstp_tmp_p, // ditto for pressure - incloud_time; // time this SD has been within a cloud + incloud_time, // time this SD has been within a cloud + ice; // 0 - water 1 - ice; bool would suffice, but we are lazy // dry radii distribution characteristics real_t log_rd_min, // logarithm of the lower bound of the distr @@ -419,6 +420,9 @@ namespace libcloudphxx if(opts_init.diag_incloud_time) distmem_real_vctrs.insert(&incloud_time); + + if(opts_init.ice_switch) + distmem_real_vctrs.insert(&ice); } void sanity_checks(); @@ -459,6 +463,7 @@ namespace libcloudphxx void init_ijk(); void init_xyz(); void init_kappa(const real_t &); + void init_ice(const real_t &); void init_incloud_time(); void init_count_num_sd_conc(const real_t & = 1); void init_count_num_const_multi(const common::unary_function &); diff --git a/src/impl/particles_impl_hskpng_resize.ipp b/src/impl/particles_impl_hskpng_resize.ipp index d9f494bb7..43cc80361 100644 --- a/src/impl/particles_impl_hskpng_resize.ipp +++ b/src/impl/particles_impl_hskpng_resize.ipp @@ -49,6 +49,8 @@ namespace libcloudphxx dot_ssp.resize(n_part, 0); } + if(opts_init.ice_switch) ice.resize(n_part); + if(opts_init.chem_switch || allow_sstp_cond || n_dims >= 2) { tmp_device_real_part1.resize(n_part); diff --git a/src/impl/particles_impl_init_SD_with_distros.ipp b/src/impl/particles_impl_init_SD_with_distros.ipp index 0a20ba6c1..0f4f46c78 100644 --- a/src/impl/particles_impl_init_SD_with_distros.ipp +++ b/src/impl/particles_impl_init_SD_with_distros.ipp @@ -50,10 +50,13 @@ namespace libcloudphxx // final inits common for tail/sd_conc/const_multi template - void particles_t::impl::init_SD_with_distros_finalize(const real_t &kappa, const bool unravel_ijk_switch) + void particles_t::impl::init_SD_with_distros_finalize(const std::pair &kpa_ice, const bool unravel_ijk_switch) { // init kappa - init_kappa(kappa); + init_kappa(kpa_ice.first); + + // init ice + init_ice(kpa_ice.second); // initialising wet radii init_wet(); diff --git a/src/impl/particles_impl_init_SD_with_sizes.ipp b/src/impl/particles_impl_init_SD_with_sizes.ipp index d09dee3cd..6ab048fc8 100644 --- a/src/impl/particles_impl_init_SD_with_sizes.ipp +++ b/src/impl/particles_impl_init_SD_with_sizes.ipp @@ -15,18 +15,18 @@ namespace libcloudphxx void particles_t::impl::init_SD_with_sizes() { using dry_sizes_t = typename opts_init_t::dry_sizes_t; - using kappa_t = typename dry_sizes_t::key_type; using size_number_t = typename dry_sizes_t::mapped_type; //using conc_multi_t = typename size_number_t::mapped_type; - // loop over kappas + // loop over (kappa, ice) pairs for (typename dry_sizes_t::const_iterator dsi = opts_init.dry_sizes.begin(); dsi != opts_init.dry_sizes.end(); ++dsi) { - const kappa_t &kappa(dsi->first); + const real_t &kappa(dsi->first.first); + const real_t &ice(dsi->first.second); const size_number_t &size_number_map(dsi->second); - // loop over the "size : {concentration, count}" pairs for this kappa + // loop over the "size : {concentration, count}" pairs for this (kappa, ice) pair for (typename size_number_t::const_iterator sni = size_number_map.begin(); sni != size_number_map.end(); ++sni) { // init number of SDs of this kappa in cells @@ -45,8 +45,9 @@ namespace libcloudphxx // initialising dry radii (needs ijk) init_dry_dry_sizes(sni->first); - // init kappa + // init kappa and ice init_kappa(kappa); + init_ice(ice); // init multiplicities init_n_dry_sizes(sni->second.first, sni->second.second); diff --git a/src/impl/particles_impl_init_sanity_check.ipp b/src/impl/particles_impl_init_sanity_check.ipp index 6f5877712..139258467 100644 --- a/src/impl/particles_impl_init_sanity_check.ipp +++ b/src/impl/particles_impl_init_sanity_check.ipp @@ -141,6 +141,16 @@ namespace libcloudphxx throw std::runtime_error("libcloudph++: rlx_timescale <= 0"); if(opts_init.rlx_switch && opts_init.chem_switch) throw std::runtime_error("libcloudph++: CCN relaxation does not work with chemistry"); + + if(opts_init.ice_switch) + { + if(opts_init.coal_switch) // because we dont know what to do when ice collides with water + throw std::runtime_error("libcloudph++: coalescence does not work with ice (turn off ice_switch or coal_switch)."); + if(opts_init.rlx_switch) // because we dont account for ice/water when matching and initializing aerosols from relaxation + throw std::runtime_error("libcloudph++: relaxation does not work with ice."); + if(opts_init.src_type==src_t::matching) // because we dont account for ice/water when matching and initializing aerosols from this type of source + throw std::runtime_error("libcloudph++: 'matching' source type does not work with ice."); + } } }; }; diff --git a/src/impl/particles_impl_reserve_hskpng_npart.ipp b/src/impl/particles_impl_reserve_hskpng_npart.ipp index 063969e7f..0a3134787 100644 --- a/src/impl/particles_impl_reserve_hskpng_npart.ipp +++ b/src/impl/particles_impl_reserve_hskpng_npart.ipp @@ -36,6 +36,9 @@ namespace libcloudphxx dot_ssp.reserve(opts_init.n_sd_max); } + if(opts_init.ice_switch) + ice.reserve(opts_init.n_sd_max); + vt.reserve(opts_init.n_sd_max); sorted_id.reserve(opts_init.n_sd_max); diff --git a/src/impl/particles_impl_rlx_dry_distros.ipp b/src/impl/particles_impl_rlx_dry_distros.ipp index 528b9eca7..631a81590 100644 --- a/src/impl/particles_impl_rlx_dry_distros.ipp +++ b/src/impl/particles_impl_rlx_dry_distros.ipp @@ -113,7 +113,7 @@ namespace libcloudphxx // initialize SDs of each kappa-type for (typename opts_init_t::rlx_dry_distros_t::const_iterator ddi = opts_init.rlx_dry_distros.begin(); ddi != opts_init.rlx_dry_distros.end(); ++ddi) { - const auto &kappa(ddi->first); + const auto &kappa(ddi->first.first); assert(kappa >= 0); const auto &n_of_lnrd_stp(*(std::get<0>(ddi->second))); diff --git a/src/impl/particles_impl_src_dry_distros_simple.ipp b/src/impl/particles_impl_src_dry_distros_simple.ipp index 8b7d733b8..74221b100 100644 --- a/src/impl/particles_impl_src_dry_distros_simple.ipp +++ b/src/impl/particles_impl_src_dry_distros_simple.ipp @@ -44,7 +44,10 @@ namespace libcloudphxx // init other peoperties of SDs that didnt have a match init_kappa( - opts_init.src_dry_distros.begin()->first + opts_init.src_dry_distros.begin()->first.first + ); + init_ice( + opts_init.src_dry_distros.begin()->first.second ); if(opts_init.diag_incloud_time) diff --git a/src/impl/particles_impl_src_dry_sizes.ipp b/src/impl/particles_impl_src_dry_sizes.ipp index 8b44eee62..948e91fc6 100644 --- a/src/impl/particles_impl_src_dry_sizes.ipp +++ b/src/impl/particles_impl_src_dry_sizes.ipp @@ -15,18 +15,19 @@ namespace libcloudphxx void particles_t::impl::src_dry_sizes(const real_t &dt) { using dry_sizes_t = typename opts_init_t::dry_sizes_t; - using kappa_t = typename dry_sizes_t::key_type; + using real_t = typename dry_sizes_t::key_type; using size_number_t = typename dry_sizes_t::mapped_type; //using conc_multi_t = typename size_number_t::mapped_type; - // loop over kappas + // loop over (kappa, ice) pairs for (typename dry_sizes_t::const_iterator dsi = opts_init.src_dry_sizes.begin(); dsi != opts_init.src_dry_sizes.end(); ++dsi) { - const kappa_t &kappa(dsi->first); + const real_t &kappa(dsi->first.first); + const real_t &ice(dsi->first.second); const size_number_t &size_number_map(dsi->second); - // loop over the "size : {concentration per second, multiplicity}" pairs for this kappa + // loop over the "size : {concentration per second, multiplicity}" pairs for this (kappa, ice) pair for (typename size_number_t::const_iterator sni = size_number_map.begin(); sni != size_number_map.end(); ++sni) { // init number of SDs of this kappa in cells @@ -45,8 +46,9 @@ namespace libcloudphxx // initialising dry radii (needs ijk) init_dry_dry_sizes(sni->first); - // init kappa + // init kappa and ice init_kappa(kappa); + init_ice(ice); // init multiplicities init_n_dry_sizes(sni->second.first*dt, sni->second.second); From 54d2eda46f72adb47d7f499a4e3a9007c98e8b37 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Thu, 14 Dec 2023 14:53:36 +0100 Subject: [PATCH 03/97] use different growth equation iif ice --- src/impl/particles_impl_cond.ipp | 10 ++-- src/impl/particles_impl_cond_common.ipp | 46 +++++++++++++------ src/impl/particles_impl_cond_sstp.ipp | 32 +++++++++++-- src/impl/particles_impl_init_sanity_check.ipp | 2 + 4 files changed, 68 insertions(+), 22 deletions(-) diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index c3fdfa618..0aaf1adc5 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -48,7 +48,8 @@ namespace libcloudphxx kpa.begin(), vt.begin(), thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), - thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) + thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()), + ice.begin() )); // calculating drop growth in a timestep using backward Euler @@ -63,13 +64,15 @@ namespace libcloudphxx arg::_1 + arg::_2 ); + // no RH_i, because we dont allow ice with turb_cond thrust::transform( rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() thrust::make_zip_iterator( // input - 2nd arg thrust::make_tuple( hlpr_zip_iter, thrust::make_permutation_iterator(p.begin(), ijk.begin()), - RH_plus_ssp.begin() + RH_plus_ssp.begin(), + thrust::make_constant_iterator(0) // dummy RH_i just to make it compile ) ), rw2.begin(), // output @@ -83,7 +86,8 @@ namespace libcloudphxx thrust::make_tuple( hlpr_zip_iter, thrust::make_permutation_iterator(p.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH.begin(), ijk.begin()) + thrust::make_permutation_iterator(RH.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH_i.begin(), ijk.begin()) ) ), rw2.begin(), // output diff --git a/src/impl/particles_impl_cond_common.ipp b/src/impl/particles_impl_cond_common.ipp index e8c04f897..688eb2983 100644 --- a/src/impl/particles_impl_cond_common.ipp +++ b/src/impl/particles_impl_cond_common.ipp @@ -69,13 +69,15 @@ namespace libcloudphxx const quantity RH_max; const quantity lambda_D; const quantity lambda_K; + const bool ice; + const quantity RH_i; // ctor BOOST_GPU_ENABLED advance_rw2_minfun( const real_t &dt, const real_t &rw2, - const thrust::tuple, real_t, real_t> &tpl, + const thrust::tuple, real_t, real_t, real_t> &tpl, const real_t &RH_max ) : dt(dt * si::seconds), @@ -91,6 +93,8 @@ namespace libcloudphxx RH( thrust::get<2>(tpl)), lambda_D(thrust::get<7>(thrust::get<0>(tpl)) * si::metres), lambda_K(thrust::get<8>(thrust::get<0>(tpl)) * si::metres), + ice(thrust::get<9>(thrust::get<0>(tpl))), + RH_i( thrust::get<3>(tpl)), RH_max(RH_max) {} @@ -126,18 +130,26 @@ namespace libcloudphxx const quantity K = K_0() * beta(lambda_K / rw) * (Nu(Pr, Re) / 2); - return real_t(2) * rdrdt( - D, - K, - rhod * rv, - T, - p, - RH > RH_max ? RH_max : RH, - a_w(rw3, rd3, kpa), - klvntrm(rw, T) - ); - - // TODO: rdrdt_i if ice + if(!ice) + return real_t(2) * rdrdt( + D, + K, + rhod * rv, + T, + p, + RH > RH_max ? RH_max : RH, + a_w(rw3, rd3, kpa), + klvntrm(rw, T) + ); + else + return real_t(2) * rdrdt_i( + D, + K, + rhod * rv, + T, + p, + RH_i > RH_max ? RH_max : RH_i + ); } // backward Euler scheme: @@ -194,7 +206,9 @@ namespace libcloudphxx "kpa: %g " "vt: %g " "lambda_D: %g " - "lambda_K: %g\n", + "lambda_K: %g " + "ice: %g " + "RH_i: %g\n", drw2, rw2_old, dt, RH_max, thrust::get<0>(tpl_in), // rhod thrust::get<1>(tpl_in), // rv @@ -206,7 +220,9 @@ namespace libcloudphxx thrust::get<5>(tpl_in), // kpa thrust::get<6>(tpl_in), // vt thrust::get<7>(tpl_in), // lambda_D - thrust::get<8>(tpl_in) // lambda_K + thrust::get<8>(tpl_in), // lambda_K + thrust::get<9>(tpl_in), // ice + thrust::get<3>(tpl) // RH_i ); assert(0); } diff --git a/src/impl/particles_impl_cond_sstp.ipp b/src/impl/particles_impl_cond_sstp.ipp index 01c145724..27b4a69dd 100644 --- a/src/impl/particles_impl_cond_sstp.ipp +++ b/src/impl/particles_impl_cond_sstp.ipp @@ -35,7 +35,8 @@ namespace libcloudphxx const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, - const RH_iter &rhi + const RH_iter &rhi, + const RH_iter &rhii ) { thrust_device::vector &lambda_D(tmp_device_real_cell1); // real_cell used in cond.ipp @@ -54,7 +55,8 @@ namespace libcloudphxx kpa.begin(), vt.begin(), thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), - thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) + thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()), + ice.begin() // ice can be nonsense if ice_swtich==0, because it is not assigned... )); // calculating drop growth in a timestep using backward Euler @@ -149,7 +151,9 @@ namespace libcloudphxx ssp.begin() )), detail::RH_sgs(opts_init.RH_formula) - ) + ), + // particle-specific RH_i, resolved + SGS + thrust::make_constant_iterator(0) // dummy, ice is not allowed with turb_cond ); else // no RH SGS cond_sstp_hlpr(dt, RH_max, Tp, @@ -163,6 +167,15 @@ namespace libcloudphxx Tp.begin() )), detail::RH(opts_init.RH_formula) + ), + // particle-specific RH_i, resolved + thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple( + pressure_iter, + sstp_tmp_rv.begin(), + Tp.begin() + )), + detail::RH_i(opts_init.RH_formula) ) ); } @@ -181,7 +194,9 @@ namespace libcloudphxx ssp.begin() )), detail::RH_sgs(opts_init.RH_formula) - ) + ), + // particle-specific RH_i, resolved + SGS + thrust::make_constant_iterator(0) // dummy, ice is not allowed with turb_cond ); else // no RH SGS cond_sstp_hlpr(dt, RH_max, Tp, @@ -195,6 +210,15 @@ namespace libcloudphxx Tp.begin() )), detail::RH(opts_init.RH_formula) + ), + // particle-specific RH_i, resolved + thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple( + sstp_tmp_p.begin(), + sstp_tmp_rv.begin(), + Tp.begin() + )), + detail::RH_i(opts_init.RH_formula) ) ); } diff --git a/src/impl/particles_impl_init_sanity_check.ipp b/src/impl/particles_impl_init_sanity_check.ipp index 139258467..87ec99ccc 100644 --- a/src/impl/particles_impl_init_sanity_check.ipp +++ b/src/impl/particles_impl_init_sanity_check.ipp @@ -150,6 +150,8 @@ namespace libcloudphxx throw std::runtime_error("libcloudph++: relaxation does not work with ice."); if(opts_init.src_type==src_t::matching) // because we dont account for ice/water when matching and initializing aerosols from this type of source throw std::runtime_error("libcloudph++: 'matching' source type does not work with ice."); + if(opts_init.turb_cond_switch) // because we dont want to add SGS RH to RH_i + throw std::runtime_error("libcloudph++: SGS condensation does not work with ice."); } } }; From a5aee6dbd244e60869df7257c3bb359c5e0d4ec6 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Mon, 18 Dec 2023 13:05:18 +0100 Subject: [PATCH 04/97] move src_dry_sizes/distros to opts --- include/libcloudph++/lgrngn/distro_t.hpp | 25 ++++++++++++++ include/libcloudph++/lgrngn/opts.hpp | 7 ++++ include/libcloudph++/lgrngn/opts_init.hpp | 33 ++++++++++--------- .../particles_impl_init_SD_with_distros.ipp | 4 +-- .../particles_impl_init_SD_with_sizes.ipp | 10 +++--- src/impl/particles_impl_init_sanity_check.ipp | 14 ++++---- src/impl/particles_impl_src.ipp | 4 +-- src/impl/particles_impl_src_dry_distros.ipp | 6 ++++ src/impl/particles_impl_src_dry_sizes.ipp | 11 ++++--- 9 files changed, 77 insertions(+), 37 deletions(-) create mode 100644 include/libcloudph++/lgrngn/distro_t.hpp diff --git a/include/libcloudph++/lgrngn/distro_t.hpp b/include/libcloudph++/lgrngn/distro_t.hpp new file mode 100644 index 000000000..004b7709d --- /dev/null +++ b/include/libcloudph++/lgrngn/distro_t.hpp @@ -0,0 +1,25 @@ +#pragma once + +namespace libcloudphxx +{ + namespace lgrngn + { + // initial dry sizes of aerosol + // defined with a distribution + // uses shared_ptr to make opts_init copyable + template + using dry_distros_t = std::unordered_map< + std::pair, // (kappa, ice) + std::shared_ptr> // n(ln(rd)) @ STP; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true + >; + + // defined with a size-number pair + template + using dry_sizes_t = std::map< + std::pair, // (kappa, ice) + std::map // STP_concentration [1/m^3], number of SD that represent this radius kappa and concentration + > + >; + }; +}; diff --git a/include/libcloudph++/lgrngn/opts.hpp b/include/libcloudph++/lgrngn/opts.hpp index 8a32a7e25..a92649282 100644 --- a/include/libcloudph++/lgrngn/opts.hpp +++ b/include/libcloudph++/lgrngn/opts.hpp @@ -8,6 +8,7 @@ #pragma once #include "extincl.hpp" +#include "distro_t.hpp" #include "../common/chem.hpp" namespace libcloudphxx @@ -31,6 +32,12 @@ namespace libcloudphxx // overriding dt from opts_init real_t dt; + // aerosol source distro per unit time + dry_distros_t src_dry_distros; + + // dry sizes of droplets added from the source, STP_concentration created per unit time instead of the STP_concentration + dry_sizes_t src_dry_sizes; + // ctor with defaults (C++03 compliant) ... opts_t() : adve(true), sedi(true), subs(false), cond(true), coal(true), src(false), rlx(false), rcyc(false), diff --git a/include/libcloudph++/lgrngn/opts_init.hpp b/include/libcloudph++/lgrngn/opts_init.hpp index 493c3d326..6bcef176e 100644 --- a/include/libcloudph++/lgrngn/opts_init.hpp +++ b/include/libcloudph++/lgrngn/opts_init.hpp @@ -13,6 +13,7 @@ #include "advection_scheme.hpp" #include "RH_formula.hpp" #include "ccn_source.hpp" +#include "distro_t.hpp" #include "../common/chem.hpp" namespace libcloudphxx @@ -28,20 +29,20 @@ namespace libcloudphxx // initial dry sizes of aerosol // defined with a distribution // uses shared_ptr to make opts_init copyable - typedef std::unordered_map< - std::pair, // (kappa, ice) - std::shared_ptr> // n(ln(rd)) @ STP; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true - > dry_distros_t; - dry_distros_t dry_distros; - - // defined with a size-number pair - typedef std::map< - std::pair, // (kappa, ice) - std::map // STP_concentration [1/m^3], number of SD that represent this radius kappa and concentration - > - > dry_sizes_t; - dry_sizes_t dry_sizes; +// typedef std::unordered_map< +// std::pair, // (kappa, ice) +// std::shared_ptr> // n(ln(rd)) @ STP; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true +// > dry_distros_t; + dry_distros_t dry_distros; +// +// // defined with a size-number pair +// typedef std::map< +// std::pair, // (kappa, ice) +// std::map // STP_concentration [1/m^3], number of SD that represent this radius kappa and concentration +// > +// > dry_sizes_t; + dry_sizes_t dry_sizes; // Eulerian component parameters int nx, ny, nz; @@ -141,13 +142,13 @@ namespace libcloudphxx src_t src_type; // source distro per unit time - dry_distros_t src_dry_distros; +// dry_distros_t src_dry_distros; // number of SDs created from src_dry_distros per cell per source iteration unsigned long long src_sd_conc; // dry sizes of droplets added from the source, STP_concentration created per unit time instead of the STP_concentration - dry_sizes_t src_dry_sizes; + // dry_sizes_t src_dry_sizes; // box in which aerosol from source will be created // will be rounded to cell number - cells are supposed to be uniform diff --git a/src/impl/particles_impl_init_SD_with_distros.ipp b/src/impl/particles_impl_init_SD_with_distros.ipp index 0f4f46c78..cbd61065f 100644 --- a/src/impl/particles_impl_init_SD_with_distros.ipp +++ b/src/impl/particles_impl_init_SD_with_distros.ipp @@ -17,7 +17,7 @@ namespace libcloudphxx // calc sum of ln(rd) ranges of all distributions real_t tot_lnrd_rng = 0.; if(opts_init.sd_conc > 0) - for (typename opts_init_t::dry_distros_t::const_iterator ddi = opts_init.dry_distros.begin(); ddi != opts_init.dry_distros.end(); ++ddi) + for (auto ddi = opts_init.dry_distros.cbegin(); ddi != opts_init.dry_distros.cend(); ++ddi) { dist_analysis_sd_conc( *(ddi->second), @@ -27,7 +27,7 @@ namespace libcloudphxx } // initialize SDs of each kappa-type - for (typename opts_init_t::dry_distros_t::const_iterator ddi = opts_init.dry_distros.begin(); ddi != opts_init.dry_distros.end(); ++ddi) + for (auto ddi = opts_init.dry_distros.cbegin(); ddi != opts_init.dry_distros.cend(); ++ddi) { if(opts_init.sd_conc > 0) { diff --git a/src/impl/particles_impl_init_SD_with_sizes.ipp b/src/impl/particles_impl_init_SD_with_sizes.ipp index 6ab048fc8..3a6d77528 100644 --- a/src/impl/particles_impl_init_SD_with_sizes.ipp +++ b/src/impl/particles_impl_init_SD_with_sizes.ipp @@ -14,20 +14,20 @@ namespace libcloudphxx template void particles_t::impl::init_SD_with_sizes() { - using dry_sizes_t = typename opts_init_t::dry_sizes_t; - using size_number_t = typename dry_sizes_t::mapped_type; +// using dry_sizes_t = typename opts_init_t::dry_sizes_t; + // using size_number_t = typename dry_sizes_t::mapped_type; //using conc_multi_t = typename size_number_t::mapped_type; // loop over (kappa, ice) pairs - for (typename dry_sizes_t::const_iterator dsi = opts_init.dry_sizes.begin(); dsi != opts_init.dry_sizes.end(); ++dsi) + for (auto dsi = opts_init.dry_sizes.cbegin(); dsi != opts_init.dry_sizes.cend(); ++dsi) { const real_t &kappa(dsi->first.first); const real_t &ice(dsi->first.second); - const size_number_t &size_number_map(dsi->second); + const auto &size_number_map(dsi->second); // loop over the "size : {concentration, count}" pairs for this (kappa, ice) pair - for (typename size_number_t::const_iterator sni = size_number_map.begin(); sni != size_number_map.end(); ++sni) + for (auto sni = size_number_map.cbegin(); sni != size_number_map.cend(); ++sni) { // init number of SDs of this kappa in cells init_count_num_dry_sizes(sni->second); diff --git a/src/impl/particles_impl_init_sanity_check.ipp b/src/impl/particles_impl_init_sanity_check.ipp index 87ec99ccc..89de098d3 100644 --- a/src/impl/particles_impl_init_sanity_check.ipp +++ b/src/impl/particles_impl_init_sanity_check.ipp @@ -57,11 +57,11 @@ namespace libcloudphxx if (opts_init.chem_switch && opts_init.src_type!=src_t::off) throw std::runtime_error("libcloudph++: chemistry and aerosol source are not compatible"); - if (opts_init.src_type!=src_t::off && opts_init.src_dry_distros.empty() && opts_init.src_dry_sizes.empty()) - throw std::runtime_error("libcloudph++: CCN source enabled, but src_dry_distros and src_dry_sizes are empty"); - - if (opts_init.src_type!=src_t::off && opts_init.src_dry_distros.size() > 1) - throw std::runtime_error("libcloudph++: src_dry_distros can only have a single kappa value."); +// if (opts_init.src_type!=src_t::off && opts_init.src_dry_distros.empty() && opts_init.src_dry_sizes.empty()) +// throw std::runtime_error("libcloudph++: CCN source enabled, but src_dry_distros and src_dry_sizes are empty"); +// +// if (opts_init.src_type!=src_t::off && opts_init.src_dry_distros.size() > 1) +// throw std::runtime_error("libcloudph++: src_dry_distros can only have a single kappa value."); if (opts_init.src_type==src_t::matching && opts_init.dry_distros.size() > 1) throw std::runtime_error("libcloudph++: For 'matching' CCN source, the initial aerosol distribution can only have one kappa value (na kappa matching done)."); @@ -69,8 +69,8 @@ namespace libcloudphxx if (opts_init.src_type!=src_t::off && n_dims<2) throw std::runtime_error("libcloudph++: CCN source works in 2D and 3D only."); - if (opts_init.src_type==src_t::matching && !opts_init.src_dry_distros.empty() && - opts_init.src_dry_distros.begin()->first != opts_init.dry_distros.begin()->first) throw std::runtime_error("libcloudph++: For 'matching' CCN source, kappa of the source has to be the same as that of the initial profile (no kappa matching done)"); +// if (opts_init.src_type==src_t::matching && !opts_init.src_dry_distros.empty() && +// opts_init.src_dry_distros.begin()->first != opts_init.dry_distros.begin()->first) throw std::runtime_error("libcloudph++: For 'matching' CCN source, kappa of the source has to be the same as that of the initial profile (no kappa matching done)"); if(opts_init.dry_distros.size() > 1 && opts_init.chem_switch) throw std::runtime_error("libcloudph++: chemistry and multiple kappa distributions are not compatible"); diff --git a/src/impl/particles_impl_src.ipp b/src/impl/particles_impl_src.ipp index 4044b5cab..5520497f0 100644 --- a/src/impl/particles_impl_src.ipp +++ b/src/impl/particles_impl_src.ipp @@ -14,10 +14,10 @@ namespace libcloudphxx { // ante_adding_SD(); - if(!opts_init.src_dry_distros.empty()) + if(!opts.src_dry_distros.empty()) src_dry_distros(dt); - if(!opts_init.src_dry_sizes.empty()) + if(!opts.src_dry_sizes.empty()) src_dry_sizes(dt); // post_adding_SD(); diff --git a/src/impl/particles_impl_src_dry_distros.ipp b/src/impl/particles_impl_src_dry_distros.ipp index ffd4ea560..017157ca5 100644 --- a/src/impl/particles_impl_src_dry_distros.ipp +++ b/src/impl/particles_impl_src_dry_distros.ipp @@ -16,6 +16,12 @@ namespace libcloudphxx template void particles_t::impl::src_dry_distros(const real_t &dt) { + if (opts.src_dry_distros.size() > 1) + throw std::runtime_error("libcloudph++: src_dry_distros can only have a single kappa value."); + + if (opts_init.src_type == src_t::matching && !opts.src_dry_distros.empty() && + opts.src_dry_distros.begin()->first != opts_init.dry_distros.begin()->first) throw std::runtime_error("libcloudph++: For 'matching' CCN source, kappa of the source has to be the same as that of the initial profile (no kappa matching done)"); + if(opts_init.src_type == src_t::matching) src_dry_distros_matching(dt); if(opts_init.src_type == src_t::simple) diff --git a/src/impl/particles_impl_src_dry_sizes.ipp b/src/impl/particles_impl_src_dry_sizes.ipp index 948e91fc6..4a25ca9e1 100644 --- a/src/impl/particles_impl_src_dry_sizes.ipp +++ b/src/impl/particles_impl_src_dry_sizes.ipp @@ -14,21 +14,22 @@ namespace libcloudphxx template void particles_t::impl::src_dry_sizes(const real_t &dt) { - using dry_sizes_t = typename opts_init_t::dry_sizes_t; +// using dry_sizes_t = typename opts_t::dry_sizes_t; using real_t = typename dry_sizes_t::key_type; - using size_number_t = typename dry_sizes_t::mapped_type; +// using size_number_t = typename dry_sizes_t::mapped_type; //using conc_multi_t = typename size_number_t::mapped_type; // loop over (kappa, ice) pairs - for (typename dry_sizes_t::const_iterator dsi = opts_init.src_dry_sizes.begin(); dsi != opts_init.src_dry_sizes.end(); ++dsi) + // for (typename dry_sizes_t::const_iterator dsi = opts.src_dry_sizes.begin(); dsi != opts.src_dry_sizes.end(); ++dsi) + for (auto dsi = opts.src_dry_sizes.cbegin(); dsi != opts.src_dry_sizes.cend(); ++dsi) { const real_t &kappa(dsi->first.first); const real_t &ice(dsi->first.second); - const size_number_t &size_number_map(dsi->second); + const auto &size_number_map(dsi->second); // loop over the "size : {concentration per second, multiplicity}" pairs for this (kappa, ice) pair - for (typename size_number_t::const_iterator sni = size_number_map.begin(); sni != size_number_map.end(); ++sni) + for (auto sni = size_number_map.cbegin(); sni != size_number_map.cend(); ++sni) { // init number of SDs of this kappa in cells init_count_num_src(sni->second.second); From 8282a2434dcf74c6ef5c34133585d20c9c60ede6 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Tue, 19 Dec 2023 16:02:22 +0100 Subject: [PATCH 05/97] compling version --- CMakeLists.txt | 11 +++++++++- include/libcloudph++/lgrngn/distro_t.hpp | 6 +++++- include/libcloudph++/lgrngn/opts_init.hpp | 2 +- src/impl/particles_impl.ipp | 16 +++++++-------- src/impl/particles_impl_cond.ipp | 2 ++ src/impl/particles_impl_cond_common.ipp | 2 +- src/impl/particles_impl_cond_sstp.ipp | 20 +++++++++++++++---- src/impl/particles_impl_hskpng_Tpr.ipp | 2 +- src/impl/particles_impl_rlx_dry_distros.ipp | 5 +++-- src/impl/particles_impl_src.ipp | 10 +++++----- src/impl/particles_impl_src_dry_distros.ipp | 12 +++++------ ...articles_impl_src_dry_distros_matching.ipp | 10 +++++----- .../particles_impl_src_dry_distros_simple.ipp | 10 +++++----- src/impl/particles_impl_src_dry_sizes.ipp | 6 +++--- src/particles_step.ipp | 2 +- 15 files changed, 72 insertions(+), 44 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 00d9fa58a..6fe2170c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -241,7 +241,7 @@ if (CMAKE_CUDA_COMPILER) target_compile_options(cloudphxx_lgrngn PRIVATE $<$: -O3 -use_fast_math -Xcompiler=-Ofast,-march=native,-fopenmp>) # Debug mode cuda flags elseif(CMAKE_BUILD_TYPE STREQUAL "Debug") - target_compile_options(cloudphxx_lgrngn PRIVATE $<$: -g -DTHRUST_DEBUG -lineinfo -Xcompiler=-fopenmp,-g,-Og,-rdynamic>) + target_compile_options(cloudphxx_lgrngn PRIVATE -Wfatal-errors $<$: -g -DTHRUST_DEBUG -lineinfo -Xcompiler=-fopenmp,-g,-Og,-rdynamic>) # Release mode cuda flags else() target_compile_options(cloudphxx_lgrngn PRIVATE $<$: -DNDEBUG -O3 -use_fast_math -Xcompiler=-Ofast,-march=native,-DNDEBUG,-fopenmp>) @@ -259,6 +259,8 @@ if (CMAKE_CUDA_COMPILER) endif() ############################################################################################ +# find Thrust using libcloudphxx/cmake/obsolete/FindThrust.cmake + # Thrust, location of Thrust can be hinted by setting THRUST_INCLUDE_DIR find_package(Thrust) @@ -275,6 +277,13 @@ endif() # include thrust found here instead of the one shipped with cuda target_include_directories(cloudphxx_lgrngn PRIVATE ${THRUST_INCLUDE_DIR}) +## alternative based on libcloudphxx/cmake/alternative/thrust-config.cmake +## TODO: doesn't work +#SET(Thrust_DIR ${PROJECT_SOURCE_DIR}/cmake) +#find_package(Thrust REQUIRED) +#thrust_create_target(Thrust) +#target_link_libraries(cloudphxx_lgrngn Thrust) + ############################################################################################ # Boost libraries find_package(Boost COMPONENTS) # we only need header-only boost libs diff --git a/include/libcloudph++/lgrngn/distro_t.hpp b/include/libcloudph++/lgrngn/distro_t.hpp index 004b7709d..32dcac2de 100644 --- a/include/libcloudph++/lgrngn/distro_t.hpp +++ b/include/libcloudph++/lgrngn/distro_t.hpp @@ -4,11 +4,14 @@ namespace libcloudphxx { namespace lgrngn { + using common::unary_function; + // initial dry sizes of aerosol // defined with a distribution // uses shared_ptr to make opts_init copyable template - using dry_distros_t = std::unordered_map< + using dry_distros_t = std::map< +// real_t, std::pair, // (kappa, ice) std::shared_ptr> // n(ln(rd)) @ STP; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true >; @@ -16,6 +19,7 @@ namespace libcloudphxx // defined with a size-number pair template using dry_sizes_t = std::map< +// real_t, std::pair, // (kappa, ice) std::map // STP_concentration [1/m^3], number of SD that represent this radius kappa and concentration diff --git a/include/libcloudph++/lgrngn/opts_init.hpp b/include/libcloudph++/lgrngn/opts_init.hpp index 6bcef176e..4ad18ed7b 100644 --- a/include/libcloudph++/lgrngn/opts_init.hpp +++ b/include/libcloudph++/lgrngn/opts_init.hpp @@ -34,7 +34,7 @@ namespace libcloudphxx // std::shared_ptr> // n(ln(rd)) @ STP; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true // > dry_distros_t; dry_distros_t dry_distros; -// + // // defined with a size-number pair // typedef std::map< // std::pair, // (kappa, ice) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 54529a96c..6044e6652 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -430,7 +430,7 @@ namespace libcloudphxx void init_SD_with_distros_sd_conc(const common::unary_function &, const real_t &); void init_SD_with_distros_tail(const common::unary_function &, const real_t); void init_SD_with_distros_const_multi(const common::unary_function &); - void init_SD_with_distros_finalize(const real_t &, const bool unravel_ijk = true); + void init_SD_with_distros_finalize(const std::pair &, const bool unravel_ijk = true); void init_SD_with_sizes(); void init_sanity_check( const arrinfo_t, const arrinfo_t, const arrinfo_t, @@ -565,8 +565,8 @@ namespace libcloudphxx void cond_dm3_helper(); void cond(const real_t &dt, const real_t &RH_max, const bool turb_cond); void cond_sstp(const real_t &dt, const real_t &RH_max, const bool turb_cond); - template - void cond_sstp_hlpr(const real_t &dt, const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, const RH_iter &rhi); + template + void cond_sstp_hlpr(const real_t &dt, const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, const RH_iter &rhi, const RHi_iter &rhii); void update_th_rv(thrust_device::vector &); void update_state(thrust_device::vector &, thrust_device::vector &); void update_pstate(thrust_device::vector &, thrust_device::vector &); @@ -584,11 +584,11 @@ namespace libcloudphxx thrust_size_t rcyc(); void bcnd(); - void src(const real_t &dt); - void src_dry_distros_simple(const real_t &dt); - void src_dry_distros_matching(const real_t &dt); - void src_dry_distros(const real_t &dt); - void src_dry_sizes(const real_t &dt); + void src(const real_t &dt, const dry_distros_t &, const dry_sizes_t &); + void src_dry_distros_simple(const real_t &dt, const dry_distros_t &); + void src_dry_distros_matching(const real_t &dt, const dry_distros_t &); + void src_dry_distros(const real_t &dt, const dry_distros_t &); + void src_dry_sizes(const real_t &dt, const dry_sizes_t &); void rlx(const real_t); void rlx_dry_distros(const real_t); diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index 0aaf1adc5..60801f54a 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -65,6 +65,7 @@ namespace libcloudphxx ); // no RH_i, because we dont allow ice with turb_cond + /* thrust::transform( rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() thrust::make_zip_iterator( // input - 2nd arg @@ -78,6 +79,7 @@ namespace libcloudphxx rw2.begin(), // output detail::advance_rw2(dt, RH_max) ); + */ } else thrust::transform( diff --git a/src/impl/particles_impl_cond_common.ipp b/src/impl/particles_impl_cond_common.ipp index 688eb2983..ab79692f9 100644 --- a/src/impl/particles_impl_cond_common.ipp +++ b/src/impl/particles_impl_cond_common.ipp @@ -174,7 +174,7 @@ namespace libcloudphxx BOOST_GPU_ENABLED real_t operator()( const real_t &rw2_old, - const thrust::tuple, real_t, real_t> &tpl + const thrust::tuple, real_t, real_t, real_t> &tpl ) const { #if !defined(__NVCC__) using std::min; diff --git a/src/impl/particles_impl_cond_sstp.ipp b/src/impl/particles_impl_cond_sstp.ipp index 27b4a69dd..412e22f74 100644 --- a/src/impl/particles_impl_cond_sstp.ipp +++ b/src/impl/particles_impl_cond_sstp.ipp @@ -29,14 +29,14 @@ namespace libcloudphxx }; template - template + template void particles_t::impl::cond_sstp_hlpr( const real_t &dt, const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, const RH_iter &rhi, - const RH_iter &rhii + const RHi_iter &rhii ) { thrust_device::vector &lambda_D(tmp_device_real_cell1); // real_cell used in cond.ipp @@ -67,7 +67,9 @@ namespace libcloudphxx // particle-specific p pi, // particle-specific RH - rhi + rhi, + // particle-specific RH_i + rhii )), rw2.begin(), // output detail::advance_rw2(dt, RH_max) @@ -139,6 +141,8 @@ namespace libcloudphxx ); if(turb_cond) + ; + /* cond_sstp_hlpr(dt, RH_max, Tp, // particle-specific p pressure_iter, @@ -155,6 +159,7 @@ namespace libcloudphxx // particle-specific RH_i, resolved + SGS thrust::make_constant_iterator(0) // dummy, ice is not allowed with turb_cond ); + */ else // no RH SGS cond_sstp_hlpr(dt, RH_max, Tp, // particle-specific p @@ -175,13 +180,16 @@ namespace libcloudphxx sstp_tmp_rv.begin(), Tp.begin() )), - detail::RH_i(opts_init.RH_formula) + detail::RH(opts_init.RH_formula) + //detail::RH_i(opts_init.RH_formula) ) ); } else // const_p { if(turb_cond) + ; + /* cond_sstp_hlpr(dt, RH_max, Tp, // particle-specific p sstp_tmp_p.begin(), @@ -198,7 +206,10 @@ namespace libcloudphxx // particle-specific RH_i, resolved + SGS thrust::make_constant_iterator(0) // dummy, ice is not allowed with turb_cond ); + */ else // no RH SGS + ; + /* cond_sstp_hlpr(dt, RH_max, Tp, // particle-specific p sstp_tmp_p.begin(), @@ -221,6 +232,7 @@ namespace libcloudphxx detail::RH_i(opts_init.RH_formula) ) ); + */ } // calc rw3_new - rw3_old diff --git a/src/impl/particles_impl_hskpng_Tpr.ipp b/src/impl/particles_impl_hskpng_Tpr.ipp index a869dd5ed..8eae81387 100644 --- a/src/impl/particles_impl_hskpng_Tpr.ipp +++ b/src/impl/particles_impl_hskpng_Tpr.ipp @@ -182,7 +182,7 @@ namespace libcloudphxx { const RH_formula_t RH_formula; // the type of formula to be used for RH - RH(RH_formula_t RH_formula): + RH_i(RH_formula_t RH_formula): RH_formula(RH_formula) {} diff --git a/src/impl/particles_impl_rlx_dry_distros.ipp b/src/impl/particles_impl_rlx_dry_distros.ipp index 631a81590..04436b1f3 100644 --- a/src/impl/particles_impl_rlx_dry_distros.ipp +++ b/src/impl/particles_impl_rlx_dry_distros.ipp @@ -113,7 +113,7 @@ namespace libcloudphxx // initialize SDs of each kappa-type for (typename opts_init_t::rlx_dry_distros_t::const_iterator ddi = opts_init.rlx_dry_distros.begin(); ddi != opts_init.rlx_dry_distros.end(); ++ddi) { - const auto &kappa(ddi->first.first); + const auto &kappa(ddi->first); assert(kappa >= 0); const auto &n_of_lnrd_stp(*(std::get<0>(ddi->second))); @@ -292,7 +292,8 @@ namespace libcloudphxx n_part_to_init = n_part - n_part_old; hskpng_resize_npart(); - init_SD_with_distros_finalize(kappa, false); // no need to unravel ijk there, becaues i j k are already initialized + init_SD_with_distros_finalize(std::make_pair(kappa, real_t(0)), false); // no need to unravel ijk there, becaues i j k are already initialized + // TODO: we assume that relaxation produces water (not ice) // TODO: asserts of newly added SD parameters? e.g. how many SD, how big is multiplicity etc. } // end of the distros loop diff --git a/src/impl/particles_impl_src.ipp b/src/impl/particles_impl_src.ipp index 5520497f0..d7f565396 100644 --- a/src/impl/particles_impl_src.ipp +++ b/src/impl/particles_impl_src.ipp @@ -10,15 +10,15 @@ namespace libcloudphxx namespace lgrngn { template - void particles_t::impl::src(const real_t &dt) + void particles_t::impl::src(const real_t &dt, const dry_distros_t &sdd, const dry_sizes_t &sds) { // ante_adding_SD(); - if(!opts.src_dry_distros.empty()) - src_dry_distros(dt); + if(!sdd.empty()) + src_dry_distros(dt, sdd); - if(!opts.src_dry_sizes.empty()) - src_dry_sizes(dt); + if(!sds.empty()) + src_dry_sizes(dt, sds); // post_adding_SD(); } diff --git a/src/impl/particles_impl_src_dry_distros.ipp b/src/impl/particles_impl_src_dry_distros.ipp index 017157ca5..b6b9eeda2 100644 --- a/src/impl/particles_impl_src_dry_distros.ipp +++ b/src/impl/particles_impl_src_dry_distros.ipp @@ -14,18 +14,18 @@ namespace libcloudphxx { // create new aerosol particles based on a size distribution template - void particles_t::impl::src_dry_distros(const real_t &dt) + void particles_t::impl::src_dry_distros(const real_t &dt, const dry_distros_t &sdd) { - if (opts.src_dry_distros.size() > 1) + if (sdd.size() > 1) throw std::runtime_error("libcloudph++: src_dry_distros can only have a single kappa value."); - if (opts_init.src_type == src_t::matching && !opts.src_dry_distros.empty() && - opts.src_dry_distros.begin()->first != opts_init.dry_distros.begin()->first) throw std::runtime_error("libcloudph++: For 'matching' CCN source, kappa of the source has to be the same as that of the initial profile (no kappa matching done)"); + if (opts_init.src_type == src_t::matching && !sdd.empty() && + sdd.begin()->first != opts_init.dry_distros.begin()->first) throw std::runtime_error("libcloudph++: For 'matching' CCN source, kappa of the source has to be the same as that of the initial profile (no kappa matching done)"); if(opts_init.src_type == src_t::matching) - src_dry_distros_matching(dt); + src_dry_distros_matching(dt, sdd); if(opts_init.src_type == src_t::simple) - src_dry_distros_simple(dt); + src_dry_distros_simple(dt, sdd); } }; }; diff --git a/src/impl/particles_impl_src_dry_distros_matching.ipp b/src/impl/particles_impl_src_dry_distros_matching.ipp index 61faa20bb..a79cf1ad6 100644 --- a/src/impl/particles_impl_src_dry_distros_matching.ipp +++ b/src/impl/particles_impl_src_dry_distros_matching.ipp @@ -46,7 +46,7 @@ namespace libcloudphxx // if any SDs with dry radius similar to the one to be added are present, // we increase their multiplicity instead of adding new SDs template - void particles_t::impl::src_dry_distros_matching(const real_t &dt) + void particles_t::impl::src_dry_distros_matching(const real_t &dt, const dry_distros_t &sdd) { // set number of SDs to init; use count_num as storage init_count_num_src(opts_init.src_sd_conc); @@ -91,7 +91,7 @@ namespace libcloudphxx // analyze distribution to get rd_min and max needed for bin sizes // TODO: this could be done once at the beginning of the simulation dist_analysis_sd_conc( - *(opts_init.src_dry_distros.begin()->second), + *(sdd.begin()->second), opts_init.src_sd_conc, dt ); @@ -209,14 +209,14 @@ namespace libcloudphxx // init other peoperties of SDs that didnt have a match init_kappa( - opts_init.src_dry_distros.begin()->first + sdd.begin()->first.first ); if(opts_init.diag_incloud_time) init_incloud_time(); init_n_sd_conc( - *(opts_init.src_dry_distros.begin()->second) + *(sdd.begin()->second) ); // TODO: document that n_of_lnrd_stp is expected! // init rw @@ -366,7 +366,7 @@ namespace libcloudphxx // init n of the copied SDs, but using the src distribution init_n_sd_conc( - *(opts_init.src_dry_distros.begin()->second) + *(sdd.begin()->second) ); // TODO: document that n_of_lnrd_stp is expected! // add the just-initialized multiplicities to the old ones diff --git a/src/impl/particles_impl_src_dry_distros_simple.ipp b/src/impl/particles_impl_src_dry_distros_simple.ipp index 74221b100..69af3e586 100644 --- a/src/impl/particles_impl_src_dry_distros_simple.ipp +++ b/src/impl/particles_impl_src_dry_distros_simple.ipp @@ -14,7 +14,7 @@ namespace libcloudphxx { // create new aerosol particles based on a size distribution template - void particles_t::impl::src_dry_distros_simple(const real_t &dt) + void particles_t::impl::src_dry_distros_simple(const real_t &dt, const dry_distros_t &sdd) { // set number of SDs to init; use count_num as storage init_count_num_src(opts_init.src_sd_conc); @@ -22,7 +22,7 @@ namespace libcloudphxx // analyze distribution to get rd_min and max needed for bin sizes // TODO: this could be done once at the beginning of the simulation dist_analysis_sd_conc( - *(opts_init.src_dry_distros.begin()->second), + *(sdd.begin()->second), opts_init.src_sd_conc, dt ); @@ -44,17 +44,17 @@ namespace libcloudphxx // init other peoperties of SDs that didnt have a match init_kappa( - opts_init.src_dry_distros.begin()->first.first + sdd.begin()->first.first ); init_ice( - opts_init.src_dry_distros.begin()->first.second + sdd.begin()->first.second ); if(opts_init.diag_incloud_time) init_incloud_time(); init_n_sd_conc( - *(opts_init.src_dry_distros.begin()->second) + *(sdd.begin()->second) ); // TODO: document that n_of_lnrd_stp is expected! // init rw diff --git a/src/impl/particles_impl_src_dry_sizes.ipp b/src/impl/particles_impl_src_dry_sizes.ipp index 4a25ca9e1..0d8c4dc58 100644 --- a/src/impl/particles_impl_src_dry_sizes.ipp +++ b/src/impl/particles_impl_src_dry_sizes.ipp @@ -12,17 +12,17 @@ namespace libcloudphxx { // initialize SD parameters with dry_radius-concentration pairs (i.e. dry_sizes) template - void particles_t::impl::src_dry_sizes(const real_t &dt) + void particles_t::impl::src_dry_sizes(const real_t &dt, const dry_sizes_t &sds) { // using dry_sizes_t = typename opts_t::dry_sizes_t; - using real_t = typename dry_sizes_t::key_type; +// using real_t = typename dry_sizes_t::key_type; // using size_number_t = typename dry_sizes_t::mapped_type; //using conc_multi_t = typename size_number_t::mapped_type; // loop over (kappa, ice) pairs // for (typename dry_sizes_t::const_iterator dsi = opts.src_dry_sizes.begin(); dsi != opts.src_dry_sizes.end(); ++dsi) - for (auto dsi = opts.src_dry_sizes.cbegin(); dsi != opts.src_dry_sizes.cend(); ++dsi) + for (auto dsi = sds.cbegin(); dsi != sds.cend(); ++dsi) { const real_t &kappa(dsi->first.first); const real_t &ice(dsi->first.second); diff --git a/src/particles_step.ipp b/src/particles_step.ipp index 689ce8a21..6d18f0e8e 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -406,7 +406,7 @@ namespace libcloudphxx // introduce new particles with the given time interval if(pimpl->src_stp_ctr % pimpl->opts_init.supstp_src == 0) { - pimpl->src(pimpl->opts_init.supstp_src * pimpl->dt); + pimpl->src(pimpl->opts_init.supstp_src * pimpl->dt, opts.src_dry_distros, opts.src_dry_sizes); } } From 3f5496bd90368deaabf7046509015779723d4240 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Tue, 19 Dec 2023 21:34:02 +0100 Subject: [PATCH 06/97] terminal velocity of ice --- CMakeLists.txt | 2 +- src/impl/particles_impl_hskpng_vterm.ipp | 68 ++++++++++++++---------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6fe2170c4..3b579d6e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -241,7 +241,7 @@ if (CMAKE_CUDA_COMPILER) target_compile_options(cloudphxx_lgrngn PRIVATE $<$: -O3 -use_fast_math -Xcompiler=-Ofast,-march=native,-fopenmp>) # Debug mode cuda flags elseif(CMAKE_BUILD_TYPE STREQUAL "Debug") - target_compile_options(cloudphxx_lgrngn PRIVATE -Wfatal-errors $<$: -g -DTHRUST_DEBUG -lineinfo -Xcompiler=-fopenmp,-g,-Og,-rdynamic>) + target_compile_options(cloudphxx_lgrngn PRIVATE $<$: -g -DTHRUST_DEBUG -lineinfo -Xcompiler=-fopenmp,-g,-Og,-rdynamic,-Wfatal-errors>) # Release mode cuda flags else() target_compile_options(cloudphxx_lgrngn PRIVATE $<$: -DNDEBUG -O3 -use_fast_math -Xcompiler=-Ofast,-march=native,-DNDEBUG,-fopenmp>) diff --git a/src/impl/particles_impl_hskpng_vterm.ipp b/src/impl/particles_impl_hskpng_vterm.ipp index 765d7768c..6d511aed2 100644 --- a/src/impl/particles_impl_hskpng_vterm.ipp +++ b/src/impl/particles_impl_hskpng_vterm.ipp @@ -45,17 +45,20 @@ namespace libcloudphxx BOOST_GPU_ENABLED real_t operator()( const real_t &rw2, - const thrust::tuple &tpl + const thrust::tuple &tpl ) { #if !defined(__NVCC__) using std::sqrt; #endif + + real_t vt; + // TODO: move the formula selection to the functor's constructor to avoid the switch in each function call, // see how it is done in RH calculation in hskpng_Tpr switch(vt_eq) { case(vt_t::beard76): - return common::vterm::vt_beard76( + vt = common::vterm::vt_beard76( sqrt(rw2) * si::metres, // TODO: consider caching rw? thrust::get<0>(tpl) * si::kelvins, thrust::get<1>(tpl) * si::pascals, @@ -64,7 +67,7 @@ namespace libcloudphxx ) / si::metres_per_second; case(vt_t::beard77): - return + vt = common::vterm::vt_beard77_fact( sqrt(rw2) * si::metres, // TODO: consider caching rw? thrust::get<1>(tpl) * si::pascals, @@ -73,7 +76,7 @@ namespace libcloudphxx ) * (common::vterm::vt_beard77_v0(sqrt(rw2) * si::metres) / si::metres_per_second); case(vt_t::khvorostyanov_spherical): - return common::vterm::vt_khvorostyanov( + vt = common::vterm::vt_khvorostyanov( sqrt(rw2) * si::metres, // TODO: consider caching rw? thrust::get<0>(tpl) * si::kelvins, thrust::get<2>(tpl) * si::kilograms / si::cubic_metres, @@ -82,7 +85,7 @@ namespace libcloudphxx ) / si::metres_per_second; case(vt_t::khvorostyanov_nonspherical): - return common::vterm::vt_khvorostyanov( + vt = common::vterm::vt_khvorostyanov( sqrt(rw2) * si::metres, // TODO: consider caching rw? thrust::get<0>(tpl) * si::kelvins, thrust::get<2>(tpl) * si::kilograms / si::cubic_metres, @@ -90,8 +93,14 @@ namespace libcloudphxx false ) / si::metres_per_second; default: - return 0.; //sanity checks done in pimpl constructor + vt = 0.; //sanity checks done in pimpl constructor } + + // ice terminal velocity = droplet terminal velocity * rho_ice/rho_water + if(thrust::get<4>(tpl) == 1) + vt *= real_t(common::moist_air::rho_i() / common::moist_air::rho_w()); + + return vt; } }; @@ -106,15 +115,18 @@ namespace libcloudphxx BOOST_GPU_ENABLED real_t operator()( const real_t &rw2, - const thrust::tuple &tpl + const thrust::tuple &tpl ) { #if !defined(__NVCC__) using std::sqrt; #endif + + real_t vt; + switch(vt_eq) { case(vt_t::beard77fast): - return + vt = common::vterm::vt_beard77_fact( sqrt(rw2) * si::metres, thrust::get<1>(tpl) * si::pascals, @@ -123,8 +135,14 @@ namespace libcloudphxx ) * thrust::get<0>(tpl); // cached vt_0 default: - return 0.; //sanity checks done in pimpl constructor + vt = 0.; //sanity checks done in pimpl constructor } + + // ice terminal velocity = droplet terminal velocity * rho_ice/rho_water + if(thrust::get<4>(tpl) == 1) + vt *= real_t(common::moist_air::rho_i() / common::moist_air::rho_w()); + + return vt; } }; }; @@ -133,12 +151,6 @@ namespace libcloudphxx template void particles_t::impl::hskpng_vterm_invalid() { - typedef thrust::permutation_iterator< - typename thrust_device::vector::iterator, - typename thrust_device::vector::iterator - > pi_t; - typedef thrust::zip_iterator > zip_it_t; - namespace arg = thrust::placeholders; if(opts_init.terminal_velocity == vt_t::beard77fast) //use cached vt at sea level @@ -155,11 +167,12 @@ namespace libcloudphxx // calc the vt thrust::transform_if( rw2.begin(), rw2.end(), // input - 1st arg - zip_it_t(thrust::make_tuple( + thrust::make_zip_iterator(thrust::make_tuple( thrust::make_permutation_iterator(vt_0.begin(), vt0_bin.begin()), thrust::make_permutation_iterator(p.begin(), ijk.begin()), thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()) + thrust::make_permutation_iterator(eta.begin(), ijk.begin()), + ice.begin() )), // input - 2nd arg vt.begin(), // condition argument vt.begin(), // output @@ -171,11 +184,12 @@ namespace libcloudphxx else thrust::transform_if( rw2.begin(), rw2.end(), // input - 1st arg - zip_it_t(thrust::make_tuple( + thrust::make_zip_iterator(thrust::make_tuple( thrust::make_permutation_iterator(T.begin(), ijk.begin()), thrust::make_permutation_iterator(p.begin(), ijk.begin()), thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()) + thrust::make_permutation_iterator(eta.begin(), ijk.begin()), + ice.begin() )), // input - 2nd arg vt.begin(), // condition argument vt.begin(), // output @@ -187,12 +201,6 @@ namespace libcloudphxx template void particles_t::impl::hskpng_vterm_all() { - typedef thrust::permutation_iterator< - typename thrust_device::vector::iterator, - typename thrust_device::vector::iterator - > pi_t; - typedef thrust::zip_iterator > zip_it_t; - if(opts_init.terminal_velocity == vt_t::beard77fast) //use cached vt at sea level { thrust_device::vector &vt0_bin(tmp_device_size_part); @@ -205,11 +213,12 @@ namespace libcloudphxx // calc the vt thrust::transform( rw2.begin(), rw2.end(), // input - 1st arg - zip_it_t(thrust::make_tuple( + thrust::make_zip_iterator(thrust::make_tuple( thrust::make_permutation_iterator(vt_0.begin(), vt0_bin.begin()), thrust::make_permutation_iterator(p.begin(), ijk.begin()), thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()) + thrust::make_permutation_iterator(eta.begin(), ijk.begin()), + ice.begin() )), // input - 2nd arg vt.begin(), // output detail::common__vterm__vt__cached(opts_init.terminal_velocity) @@ -218,11 +227,12 @@ namespace libcloudphxx else thrust::transform( rw2.begin(), rw2.end(), // input - 1st arg - zip_it_t(thrust::make_tuple( + thrust::make_zip_iterator(thrust::make_tuple( thrust::make_permutation_iterator(T.begin(), ijk.begin()), thrust::make_permutation_iterator(p.begin(), ijk.begin()), thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()) + thrust::make_permutation_iterator(eta.begin(), ijk.begin()), + ice.begin() )), // input - 2nd arg vt.begin(), // output detail::common__vterm__vt(opts_init.terminal_velocity) From 041893678fabc08cb4acf7bf932a71cfa28d70ec Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Wed, 20 Dec 2023 12:43:29 +0100 Subject: [PATCH 07/97] diag_ice --- include/libcloudph++/lgrngn/particles.hpp | 14 +++++++++++- src/particles_diag.ipp | 28 +++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/include/libcloudph++/lgrngn/particles.hpp b/include/libcloudph++/lgrngn/particles.hpp index 41c0e3da4..f6350ff1c 100644 --- a/include/libcloudph++/lgrngn/particles.hpp +++ b/include/libcloudph++/lgrngn/particles.hpp @@ -85,7 +85,9 @@ namespace libcloudphxx virtual void diag_dry_rng(const real_t&, const real_t&) { assert(false); } virtual void diag_wet_rng(const real_t&, const real_t&) { assert(false); } virtual void diag_kappa_rng(const real_t&, const real_t&) { assert(false); } - // The 3 following functions are for consecutive selection of SDs. + virtual void diag_ice() { assert(false); } + virtual void diag_water() { assert(false); } + // The following functions are for consecutive selection of SDs. // It allows the user to select SDs based on multiple characteristics, e.g. wet radius (0.5, 1) and kappa (0.1, 0.2): // diag_wet_rng(0.5, 1); diag_kappa_rng_cons(0.1, 0.2); // NOTE: the call with "cons" needs to be right after the previous call to diag_X_rng! @@ -95,6 +97,8 @@ namespace libcloudphxx virtual void diag_dry_rng_cons(const real_t&, const real_t&) { assert(false); } virtual void diag_wet_rng_cons(const real_t&, const real_t&) { assert(false); } virtual void diag_kappa_rng_cons(const real_t&, const real_t&) { assert(false); } + virtual void diag_ice_cons() { assert(false); } + virtual void diag_water_cons() { assert(false); } virtual void diag_dry_mom(const int&) { assert(false); } virtual void diag_wet_mom(const int&) { assert(false); } @@ -179,9 +183,13 @@ namespace libcloudphxx void diag_dry_rng(const real_t &r_mi, const real_t &r_mx); void diag_wet_rng(const real_t &r_mi, const real_t &r_mx); void diag_kappa_rng(const real_t &r_mi, const real_t &r_mx); + void diag_ice(); + void diag_water(); void diag_dry_rng_cons(const real_t &r_mi, const real_t &r_mx); void diag_wet_rng_cons(const real_t &r_mi, const real_t &r_mx); void diag_kappa_rng_cons(const real_t &r_mi, const real_t &r_mx); + void diag_ice_cons(); + void diag_water_cons(); void diag_dry_mom(const int &k); void diag_wet_mom(const int &k); void diag_kappa_mom(const int &k); @@ -276,9 +284,13 @@ namespace libcloudphxx void diag_dry_rng(const real_t &r_mi, const real_t &r_mx); void diag_wet_rng(const real_t &r_mi, const real_t &r_mx); void diag_kappa_rng(const real_t &r_mi, const real_t &r_mx); + void diag_ice(); + void diag_water(); void diag_dry_rng_cons(const real_t &r_mi, const real_t &r_mx); void diag_wet_rng_cons(const real_t &r_mi, const real_t &r_mx); void diag_kappa_rng_cons(const real_t &r_mi, const real_t &r_mx); + void diag_ice_cons(); + void diag_water_cons(); void diag_dry_mom(const int &k); void diag_wet_mom(const int &k); void diag_kappa_mom(const int&); diff --git a/src/particles_diag.ipp b/src/particles_diag.ipp index e900e8be1..202df8823 100644 --- a/src/particles_diag.ipp +++ b/src/particles_diag.ipp @@ -216,6 +216,20 @@ namespace libcloudphxx pimpl->moms_rng(kpa_min, kpa_max, pimpl->kpa.begin(), false); } + // selects ice particles + template + void particles_t::diag_ice() + { + pimpl->moms_rng(1., 1.0001, pimpl->ice.begin(), false); // TODO: nextafter instead of 1.0001 + } + + // selects water particles + template + void particles_t::diag_water() + { + pimpl->moms_rng(0., 0.0001, pimpl->ice.begin(), false); + } + // selects particles with (r_d >= r_min && r_d < r_max) from particles previously selected template void particles_t::diag_dry_rng_cons(const real_t &r_min, const real_t &r_max) @@ -243,6 +257,20 @@ namespace libcloudphxx pimpl->moms_rng(kpa_min, kpa_max, pimpl->kpa.begin(), true); } + // selects ice particles from particles previously selected + template + void particles_t::diag_ice_cons() + { + pimpl->moms_rng(1., 1.0001, pimpl->ice.begin(), true); + } + + // selects water particles from particles previously selected + template + void particles_t::diag_water_cons() + { + pimpl->moms_rng(0., 0.0001, pimpl->ice.begin(), true); + } + // selects particles with RH >= Sc (Sc - critical supersaturation) template void particles_t::diag_RH_ge_Sc() From 71d8adc7a58a1f08ef14618ab20583e57b722310 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Wed, 20 Dec 2023 12:43:48 +0100 Subject: [PATCH 08/97] disable bindings --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b579d6e4..fd0d3e9d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -371,7 +371,7 @@ enable_testing() add_subdirectory(tests) add_subdirectory(include) -add_subdirectory(bindings) +#add_subdirectory(bindings) ############################################################################################ From 0bf1d246d7d86adb408b4eb1ed63ebbdecfdfd60 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Thu, 21 Dec 2023 10:21:04 +0100 Subject: [PATCH 09/97] init_ice.ipp --- src/impl/particles_impl_init_ice.ipp | 24 ++++++++++++++++++++++++ src/particles.tpp | 1 + 2 files changed, 25 insertions(+) create mode 100644 src/impl/particles_impl_init_ice.ipp diff --git a/src/impl/particles_impl_init_ice.ipp b/src/impl/particles_impl_init_ice.ipp new file mode 100644 index 000000000..25aaa19d2 --- /dev/null +++ b/src/impl/particles_impl_init_ice.ipp @@ -0,0 +1,24 @@ +// vim:filetype=cpp +/** @file + * @copyright University of Warsaw + * @section LICENSE + * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) + * @brief initialisation routine for super droplets + */ + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::init_ice( + const real_t &val + ) + { + assert(val==0 || val==1); + // filling ice flag + thrust::fill(ice.begin() + n_part_old, ice.end(), val); + } + }; +}; + diff --git a/src/particles.tpp b/src/particles.tpp index 477f7a295..3a9f0f131 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -63,6 +63,7 @@ #include "impl/particles_impl_init_dry_const_multi.ipp" #include "impl/particles_impl_init_dry_dry_sizes.ipp" #include "impl/particles_impl_init_kappa.ipp" +#include "impl/particles_impl_init_ice.ipp" #include "impl/particles_impl_init_incloud_time.ipp" #include "impl/particles_impl_init_n.ipp" #include "impl/particles_impl_init_wet.ipp" From 89710111cff78b2835628b5c538c8f4c2fe33b20 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Thu, 21 Dec 2023 11:38:29 +0100 Subject: [PATCH 10/97] python bindings for ice --- CMakeLists.txt | 2 +- bindings/python/common.hpp | 6 ++++++ bindings/python/lgrngn.hpp | 14 ++++++++++---- bindings/python/lib.cpp | 5 +++-- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd0d3e9d7..3b579d6e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -371,7 +371,7 @@ enable_testing() add_subdirectory(tests) add_subdirectory(include) -#add_subdirectory(bindings) +add_subdirectory(bindings) ############################################################################################ diff --git a/bindings/python/common.hpp b/bindings/python/common.hpp index 58c174c18..238337837 100644 --- a/bindings/python/common.hpp +++ b/bindings/python/common.hpp @@ -69,6 +69,12 @@ namespace libcloudphxx { return cmn::const_cp::r_vs(T * si::kelvins, p * si::pascals); } + + template + real_t r_vsi(const real_t &T, const real_t &p) + { + return cmn::const_cp::r_vsi(T * si::kelvins, p * si::pascals); + } template real_t l_v(const real_t &T) diff --git a/bindings/python/lgrngn.hpp b/bindings/python/lgrngn.hpp index 693d13663..7dc895040 100644 --- a/bindings/python/lgrngn.hpp +++ b/bindings/python/lgrngn.hpp @@ -242,11 +242,13 @@ namespace libcloudphxx arg->dry_distros.clear(); for (int i = 0; i < len(kappa_func.keys()); ++i) arg->dry_distros.emplace( - bp::extract(kappa_func.keys()[i]), + std::make_pair(real_t(bp::extract(kappa_func.keys()[i])), 0), // assume ice=0 std::make_shared>(kappa_func.values()[i]) ); } +// src_dry_distros moved from opts_init to opts +/* template void set_sdd( // src_dry_distro lgr::opts_init_t *arg, @@ -255,10 +257,11 @@ namespace libcloudphxx arg->src_dry_distros.clear(); for (int i = 0; i < len(kappa_func.keys()); ++i) arg->src_dry_distros.emplace( - bp::extract(kappa_func.keys()[i]), + std::make_pair(bp::extract(kappa_func.keys()[i]), 0), // assume ice=0 std::make_shared>(kappa_func.values()[i]) ); } + */ template void set_ds( // dry_sizes @@ -283,13 +286,15 @@ namespace libcloudphxx assert(len(conc_count_list) == 2); const real_t conc = bp::extract(conc_count_list[0]); const int count = bp::extract (conc_count_list[1]); - size_conc_map[bp::extract(size_conc.keys()[i])] = std::make_pair(conc, count); + size_conc_map[bp::extract(size_conc.keys()[i])] = std::make_pair(conc, count); } const real_t kappa = bp::extract(kappa_func.keys()[j]); - arg->dry_sizes[kappa] = size_conc_map; + arg->dry_sizes[std::make_pair(kappa, 0)] = size_conc_map; // assume ice=0 } } +// src_dry_sizes moved from opts_init to opts +/* template void set_sds( // src_dry_sizes lgr::opts_init_t *arg, @@ -319,6 +324,7 @@ namespace libcloudphxx arg->src_dry_sizes[kappa] = size_conc_map; } } + */ template void set_rdd( // rlx_dry_distros diff --git a/bindings/python/lib.cpp b/bindings/python/lib.cpp index aaed65dd2..a16e62118 100644 --- a/bindings/python/lib.cpp +++ b/bindings/python/lib.cpp @@ -131,6 +131,7 @@ BOOST_PYTHON_MODULE(libcloudphxx) bp::def("p_v", &common::p_v); bp::def("p_vs", &common::p_vs); bp::def("r_vs", &common::r_vs); + bp::def("r_vsi", &common::r_vsi); bp::def("p_vs_tet", &common::p_vs_tet); bp::def("l_v", &common::l_v); bp::def("T", &common::T); @@ -264,8 +265,8 @@ BOOST_PYTHON_MODULE(libcloudphxx) bp::class_>("opts_init_t") .add_property("dry_distros", &lgrngn::get_dd, &lgrngn::set_dd) .add_property("dry_sizes", &lgrngn::get_ds, &lgrngn::set_ds) - .add_property("src_dry_distros", &lgrngn::get_sdd, &lgrngn::set_sdd) - .add_property("src_dry_sizes", &lgrngn::get_ds, &lgrngn::set_sds) + // .add_property("src_dry_distros", &lgrngn::get_sdd, &lgrngn::set_sdd) + // .add_property("src_dry_sizes", &lgrngn::get_ds, &lgrngn::set_sds) .add_property("rlx_dry_distros", &lgrngn::get_rdd, &lgrngn::set_rdd) .def_readwrite("nx", &lgr::opts_init_t::nx) .def_readwrite("ny", &lgr::opts_init_t::ny) From 3a2947a5b3cdf317b3e540e903eba410763336d6 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Fri, 22 Dec 2023 16:02:18 +0100 Subject: [PATCH 11/97] ice in puddle --- include/libcloudph++/common/output.hpp | 10 ++-- src/impl/particles_impl_bcnd.ipp | 72 ++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/include/libcloudph++/common/output.hpp b/include/libcloudph++/common/output.hpp index 46245208d..d54cc4ed1 100644 --- a/include/libcloudph++/common/output.hpp +++ b/include/libcloudph++/common/output.hpp @@ -17,7 +17,9 @@ namespace libcloudphxx outH = chem::H, outliq_vol, outdry_vol, - outprtcl_num + outice_vol, + outliq_num, + outice_num }; const std::map output_names @@ -30,9 +32,11 @@ namespace libcloudphxx {outO3 , "O3"}, {outS_VI , "S_VI"}, {outH , "H"}, - {outliq_vol, "liquid_volume"}, + {outliq_vol, "water_volume"}, {outdry_vol, "dry_volume"}, - {outprtcl_num, "particle_number"} + {outice_vol, "ice_volume"}, + {outliq_num, "water_number"}, + {outice_num, "ice_number"} }; }; }; diff --git a/src/impl/particles_impl_bcnd.ipp b/src/impl/particles_impl_bcnd.ipp index 6eebda9ee..43c8ea8b1 100644 --- a/src/impl/particles_impl_bcnd.ipp +++ b/src/impl/particles_impl_bcnd.ipp @@ -25,11 +25,14 @@ namespace libcloudphxx struct count_vol { real_t exponent; - count_vol(real_t exponent) : exponent(exponent){}; + int ice; // 0 - water only, 1 - ice only, 2 - both + count_vol(real_t exponent, int ice) : exponent(exponent), ice(ice){assert(ice == 0 || ice == 1 || ice == 2);}; template BOOST_GPU_ENABLED real_t operator()(const tuple &tup) { + if(ice != 2 && thrust::get<2>(tup) != ice) return 0.; + #if !defined(__NVCC__) using std::pow; #endif @@ -46,6 +49,20 @@ namespace libcloudphxx } }; + template + struct count_num + { + int ice; // 0 - water only, 1 - ice only, 2 - both + count_num(int ice) : ice(ice){assert(ice == 0 || ice == 1 || ice == 2);}; // TODO: ice enum + template + BOOST_GPU_ENABLED + real_t operator()(const tuple &tup) + { + if(ice != 2 && thrust::get<1>(tup) != ice) return 0.; + return thrust::get<0>(tup); + } + }; + template struct count_mass { @@ -211,10 +228,22 @@ namespace libcloudphxx output_puddle[common::outliq_vol] += thrust::transform_reduce( thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), rw2.begin())), // input start + n_filtered.begin(), rw2.begin(), ice.begin())), // input start thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), rw2.begin())) + n_part, // input end - detail::count_vol(3./2.), // operation + n_filtered.begin(), rw2.begin(), ice.begin())) + n_part, // input end + detail::count_vol(3./2., 0), // operation + real_t(0), // init val + thrust::plus() + ); + + // add total ice volume that fell out in this step + output_puddle[common::outliq_vol] += + thrust::transform_reduce( + thrust::make_zip_iterator(thrust::make_tuple( + n_filtered.begin(), rw2.begin(), ice.begin())), // input start + thrust::make_zip_iterator(thrust::make_tuple( + n_filtered.begin(), rw2.begin(), ice.begin())) + n_part, // input end + detail::count_vol(3./2., 1), // operation real_t(0), // init val thrust::plus() ); @@ -223,20 +252,45 @@ namespace libcloudphxx output_puddle[common::outdry_vol] += thrust::transform_reduce( thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), rd3.begin())), // input start + n_filtered.begin(), rd3.begin(), ice.begin())), // input start thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), rd3.begin())) + n_part, // input end - detail::count_vol(1.), // operation + n_filtered.begin(), rd3.begin(), ice.begin())) + n_part, // input end + detail::count_vol(1., 2), // operation real_t(0), // init val thrust::plus() ); - // add total number of particles that fell out in this step - output_puddle[common::outprtcl_num] += + // add total number of water droplets that fell out in this step + output_puddle[common::outliq_num] += + thrust::transform_reduce( + thrust::make_zip_iterator(thrust::make_tuple( + n_filtered.begin(), ice.begin())), // input start + thrust::make_zip_iterator(thrust::make_tuple( + n_filtered.begin(), ice.begin())) + n_part, // input end + detail::count_num(0), // operation + real_t(0), // init val + thrust::plus() + ); + + // add total number of ice droplets that fell out in this step + output_puddle[common::outice_num] += + thrust::transform_reduce( + thrust::make_zip_iterator(thrust::make_tuple( + n_filtered.begin(), ice.begin())), // input start + thrust::make_zip_iterator(thrust::make_tuple( + n_filtered.begin(), ice.begin())) + n_part, // input end + detail::count_num(1), // operation + real_t(0), // init val + thrust::plus() + ); + + /* + output_puddle[common::outliq_num] += thrust::reduce( n_filtered.begin(), // input start n_filtered.begin() + n_part // input end ); + */ if(opts_init.chem_switch) { From e441d2a3e962c07a47c15a7eb6e0b3ecdfed0d4a Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Thu, 29 Feb 2024 12:42:04 +0100 Subject: [PATCH 12/97] diag ice/water in multi_cuda --- src/particles_multi_gpu_diag.ipp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/particles_multi_gpu_diag.ipp b/src/particles_multi_gpu_diag.ipp index b3dad92cc..305f8aaa9 100644 --- a/src/particles_multi_gpu_diag.ipp +++ b/src/particles_multi_gpu_diag.ipp @@ -79,6 +79,18 @@ namespace libcloudphxx pimpl->mcuda_run(&particles_t::diag_kappa_rng, r_mi, r_mx); } + template + void particles_t::diag_ice() + { + pimpl->mcuda_run(&particles_t::diag_ice); + } + + template + void particles_t::diag_water() + { + pimpl->mcuda_run(&particles_t::diag_water); + } + template void particles_t::diag_dry_rng_cons( const real_t &r_mi, const real_t &r_mx @@ -103,6 +115,18 @@ namespace libcloudphxx pimpl->mcuda_run(&particles_t::diag_kappa_rng_cons, r_mi, r_mx); } + template + void particles_t::diag_ice_cons() + { + pimpl->mcuda_run(&particles_t::diag_ice_cons); + } + + template + void particles_t::diag_water_cons() + { + pimpl->mcuda_run(&particles_t::diag_water_cons); + } + template void particles_t::diag_dry_mom(const int &k) { From 74822eb70ffa8d80c6e2a330a62d635f992632f5 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Fri, 15 Mar 2024 14:51:27 +0100 Subject: [PATCH 13/97] allow kappa=0, for which rw=rd; condensation of water drops will fail for kappa=0 --- include/libcloudph++/common/detail/toms748.hpp | 4 ++-- include/libcloudph++/common/kappa_koehler.hpp | 12 +++++++----- src/impl/particles_impl_init_wet.ipp | 1 + 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/include/libcloudph++/common/detail/toms748.hpp b/include/libcloudph++/common/detail/toms748.hpp index b9893fcfc..b3af5dad1 100644 --- a/include/libcloudph++/common/detail/toms748.hpp +++ b/include/libcloudph++/common/detail/toms748.hpp @@ -301,9 +301,9 @@ T toms748_solve(F f, const T& ax, const T& bx, const T& fax, const T& fbx, Tol t fb = fbx; #if !defined(NDEBUG) - if(a >= b) + if(a > b) { - printf("toms a >= b; a = %g b = %g fa = %g fb = %g\n", a, b, fa, fb); + printf("toms a > b; a = %g b = %g fa = %g fb = %g\n", a, b, fa, fb); assert(0); } #endif diff --git a/include/libcloudph++/common/kappa_koehler.hpp b/include/libcloudph++/common/kappa_koehler.hpp index f548bd1e9..4292937c1 100644 --- a/include/libcloudph++/common/kappa_koehler.hpp +++ b/include/libcloudph++/common/kappa_koehler.hpp @@ -36,7 +36,7 @@ namespace libcloudphxx { assert(RH > .03); // kappa-koehler assumes well dissolved matter assert(RH < 1.); // no equilibrium over RH=100% - assert(kappa > 0); // pure-water case left out + assert(kappa >= 0); return rd3 * (1 - RH * (1 - kappa)) / (1 - RH); } @@ -49,7 +49,7 @@ namespace libcloudphxx quantity kappa ) { - assert(kappa > 0); // pure-water case left out + assert(kappa > 0); return (rw3 - rd3) / (rw3 - rd3 * (real_t(1) - kappa)); } @@ -134,7 +134,9 @@ namespace libcloudphxx ) { assert(RH < 1); // no equilibrium over RH=100% - assert(kappa > 0); // pure-water case left out + assert(kappa >= 0); + + if(kappa == 0) return rd3; return common::detail::toms748_solve( detail::rw3_eq_minfun(RH, rd3, kappa, T), // the above-defined functor @@ -154,7 +156,7 @@ namespace libcloudphxx quantity T ) { - assert(kappa > 0); // pure-water case left out + assert(kappa > 0); quantity rd3_dbl = static_cast >(rd3); quantity T_dbl = static_cast >(T); @@ -175,7 +177,7 @@ namespace libcloudphxx quantity T ) { - assert(kappa > 0); // pure-water case left out + assert(kappa > 0); #if !defined(__NVCC__) using std::pow; using std::cbrt; diff --git a/src/impl/particles_impl_init_wet.ipp b/src/impl/particles_impl_init_wet.ipp index 77646cd47..85dd96071 100644 --- a/src/impl/particles_impl_init_wet.ipp +++ b/src/impl/particles_impl_init_wet.ipp @@ -32,6 +32,7 @@ namespace libcloudphxx const quantity kpa = thrust::get<1>(tpl); const quantity RH = min(thrust::get<2>(tpl), RH_max); const quantity T = thrust::get<3>(tpl) * si::kelvins; + return pow(common::kappa_koehler::rw3_eq( rd3, kpa, RH, T ) / si::cubic_metres, real_t(2./3)); From 4e81ff6dd5757c0849abe64e6b4202e4cf65beb1 Mon Sep 17 00:00:00 2001 From: pdziekan Date: Mon, 18 Mar 2024 14:50:52 +0100 Subject: [PATCH 14/97] src individual supstp: WIP (TODO: src dry sizes) --- include/libcloudph++/lgrngn/distro_t.hpp | 18 ++++++++++-- include/libcloudph++/lgrngn/opts.hpp | 6 ++-- include/libcloudph++/lgrngn/opts_init.hpp | 4 --- src/impl/particles_impl_src.ipp | 6 ++-- src/impl/particles_impl_src_dry_distros.ipp | 6 ++-- ...articles_impl_src_dry_distros_matching.ipp | 25 +++++++++++----- .../particles_impl_src_dry_distros_simple.ipp | 29 +++++++++++++------ src/particles_step.ipp | 7 ++--- 8 files changed, 64 insertions(+), 37 deletions(-) diff --git a/include/libcloudph++/lgrngn/distro_t.hpp b/include/libcloudph++/lgrngn/distro_t.hpp index 32dcac2de..5fd26a082 100644 --- a/include/libcloudph++/lgrngn/distro_t.hpp +++ b/include/libcloudph++/lgrngn/distro_t.hpp @@ -11,7 +11,6 @@ namespace libcloudphxx // uses shared_ptr to make opts_init copyable template using dry_distros_t = std::map< -// real_t, std::pair, // (kappa, ice) std::shared_ptr> // n(ln(rd)) @ STP; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true >; @@ -19,11 +18,26 @@ namespace libcloudphxx // defined with a size-number pair template using dry_sizes_t = std::map< -// real_t, std::pair, // (kappa, ice) std::map // STP_concentration [1/m^3], number of SD that represent this radius kappa and concentration > >; + + // similar, but for sources of aerosols after initialization + template + using src_dry_distros_t = std::map< + std::pair, // (kappa, ice) + std::tuple, int, int>> // 1st: n(ln(rd)) @ STP created per second; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true; 2nd: sd_conc for this distribution ; 3rd: supstp for this aerosol (interval in timesteps beween addition of these aerosols) + >; + + // defined with a size-number pair + template + using src_dry_sizes_t = std::map< + std::pair, // (kappa, ice) + std::map // STP_concentration [1/m^3] created per second, number of SD that represent this radius kappa and concentration, supstp + > + >; }; }; diff --git a/include/libcloudph++/lgrngn/opts.hpp b/include/libcloudph++/lgrngn/opts.hpp index a92649282..d01cdd30a 100644 --- a/include/libcloudph++/lgrngn/opts.hpp +++ b/include/libcloudph++/lgrngn/opts.hpp @@ -33,10 +33,10 @@ namespace libcloudphxx real_t dt; // aerosol source distro per unit time - dry_distros_t src_dry_distros; + src_dry_distros_t src_dry_distros; - // dry sizes of droplets added from the source, STP_concentration created per unit time instead of the STP_concentration - dry_sizes_t src_dry_sizes; + // dry sizes of droplets added from the source + src_dry_sizes_t src_dry_sizes; // ctor with defaults (C++03 compliant) ... opts_t() : diff --git a/include/libcloudph++/lgrngn/opts_init.hpp b/include/libcloudph++/lgrngn/opts_init.hpp index 4ad18ed7b..95738e3b7 100644 --- a/include/libcloudph++/lgrngn/opts_init.hpp +++ b/include/libcloudph++/lgrngn/opts_init.hpp @@ -153,9 +153,6 @@ namespace libcloudphxx // box in which aerosol from source will be created // will be rounded to cell number - cells are supposed to be uniform real_t src_x0, src_y0, src_z0, src_x1, src_y1, src_z1; - - // timestep interval at which source will be applied - int supstp_src; // --- aerosol relaxation stuff --- @@ -228,7 +225,6 @@ namespace libcloudphxx src_y1(0), src_z0(0), src_z1(0), - supstp_src(1), rlx_bins(0), rlx_timescale(1), rlx_sd_per_bin(0), diff --git a/src/impl/particles_impl_src.ipp b/src/impl/particles_impl_src.ipp index d7f565396..35512bd8e 100644 --- a/src/impl/particles_impl_src.ipp +++ b/src/impl/particles_impl_src.ipp @@ -10,15 +10,15 @@ namespace libcloudphxx namespace lgrngn { template - void particles_t::impl::src(const real_t &dt, const dry_distros_t &sdd, const dry_sizes_t &sds) + void particles_t::impl::src(const src_dry_distros_t &sdd, const src_dry_sizes_t &sds) { // ante_adding_SD(); if(!sdd.empty()) - src_dry_distros(dt, sdd); + src_dry_distros(sdd); if(!sds.empty()) - src_dry_sizes(dt, sds); + src_dry_sizes(sds); // post_adding_SD(); } diff --git a/src/impl/particles_impl_src_dry_distros.ipp b/src/impl/particles_impl_src_dry_distros.ipp index b6b9eeda2..a3c37d150 100644 --- a/src/impl/particles_impl_src_dry_distros.ipp +++ b/src/impl/particles_impl_src_dry_distros.ipp @@ -14,7 +14,7 @@ namespace libcloudphxx { // create new aerosol particles based on a size distribution template - void particles_t::impl::src_dry_distros(const real_t &dt, const dry_distros_t &sdd) + void particles_t::impl::src_dry_distros(const src_dry_distros_t &sdd) { if (sdd.size() > 1) throw std::runtime_error("libcloudph++: src_dry_distros can only have a single kappa value."); @@ -23,9 +23,9 @@ namespace libcloudphxx sdd.begin()->first != opts_init.dry_distros.begin()->first) throw std::runtime_error("libcloudph++: For 'matching' CCN source, kappa of the source has to be the same as that of the initial profile (no kappa matching done)"); if(opts_init.src_type == src_t::matching) - src_dry_distros_matching(dt, sdd); + src_dry_distros_matching(sdd); if(opts_init.src_type == src_t::simple) - src_dry_distros_simple(dt, sdd); + src_dry_distros_simple(sdd); } }; }; diff --git a/src/impl/particles_impl_src_dry_distros_matching.ipp b/src/impl/particles_impl_src_dry_distros_matching.ipp index a79cf1ad6..d777bd400 100644 --- a/src/impl/particles_impl_src_dry_distros_matching.ipp +++ b/src/impl/particles_impl_src_dry_distros_matching.ipp @@ -45,11 +45,20 @@ namespace libcloudphxx // create new aerosol particles based on a size distribution // if any SDs with dry radius similar to the one to be added are present, // we increase their multiplicity instead of adding new SDs + // TODO: make it work for sdd.size()>1 template - void particles_t::impl::src_dry_distros_matching(const real_t &dt, const dry_distros_t &sdd) + void particles_t::impl::src_dry_distros_matching(const dry_distros_t &sdd) { + const auto p_sdd = sdd.begin() + + // add the source only once every number of steps + if(src_stp_ctr % get<2>(p_sdd->second) != 0) return; + + const real_t sup_dt = get<2>(p_sdd->second) * opts_init.dt; + // set number of SDs to init; use count_num as storage - init_count_num_src(opts_init.src_sd_conc); + init_count_num_src(get<1>(p_sdd->second)); + // -------- TODO: match not only sizes of old particles, but also kappas and chemical composition... -------- @@ -91,9 +100,9 @@ namespace libcloudphxx // analyze distribution to get rd_min and max needed for bin sizes // TODO: this could be done once at the beginning of the simulation dist_analysis_sd_conc( - *(sdd.begin()->second), - opts_init.src_sd_conc, - dt + *get<0>(p_sdd->second), + get<1>(p_sdd->second), + sup_dt ); // --- see how many already existing SDs match size bins --- @@ -209,14 +218,14 @@ namespace libcloudphxx // init other peoperties of SDs that didnt have a match init_kappa( - sdd.begin()->first.first + p_sdd->first.first ); if(opts_init.diag_incloud_time) init_incloud_time(); init_n_sd_conc( - *(sdd.begin()->second) + *get<0>(p_sdd->second) ); // TODO: document that n_of_lnrd_stp is expected! // init rw @@ -366,7 +375,7 @@ namespace libcloudphxx // init n of the copied SDs, but using the src distribution init_n_sd_conc( - *(sdd.begin()->second) + *get<0>(p_sdd->second) ); // TODO: document that n_of_lnrd_stp is expected! // add the just-initialized multiplicities to the old ones diff --git a/src/impl/particles_impl_src_dry_distros_simple.ipp b/src/impl/particles_impl_src_dry_distros_simple.ipp index 69af3e586..1c2be98f2 100644 --- a/src/impl/particles_impl_src_dry_distros_simple.ipp +++ b/src/impl/particles_impl_src_dry_distros_simple.ipp @@ -12,19 +12,30 @@ namespace libcloudphxx { namespace lgrngn { + using std::get; + // create new aerosol particles based on a size distribution template - void particles_t::impl::src_dry_distros_simple(const real_t &dt, const dry_distros_t &sdd) + void particles_t::impl::src_dry_distros_simple(const src_dry_distros_t &sdd) { + // We assume that sdd size is 1 + // TODO: add a loop to allow sdd.size>1 + const auto p_sdd = sdd.begin() + + // add the source only once every number of steps + if(src_stp_ctr % get<2>(p_sdd->second) != 0) return; + + const real_t sup_dt = get<2>(p_sdd->second) * opts_init.dt; + // set number of SDs to init; use count_num as storage - init_count_num_src(opts_init.src_sd_conc); + init_count_num_src(get<1>(p_sdd->second)); // analyze distribution to get rd_min and max needed for bin sizes // TODO: this could be done once at the beginning of the simulation dist_analysis_sd_conc( - *(sdd.begin()->second), - opts_init.src_sd_conc, - dt + *(get<0>(p_sdd->second)), + get<1>(p_sdd->second), + sup_dt ); namespace arg = thrust::placeholders; @@ -44,18 +55,18 @@ namespace libcloudphxx // init other peoperties of SDs that didnt have a match init_kappa( - sdd.begin()->first.first + p_sdd->first.first ); init_ice( - sdd.begin()->first.second + p_sdd->first.second ); if(opts_init.diag_incloud_time) init_incloud_time(); init_n_sd_conc( - *(sdd.begin()->second) - ); // TODO: document that n_of_lnrd_stp is expected! + *get<0>(p_sdd->second) + ); // init rw init_wet(); diff --git a/src/particles_step.ipp b/src/particles_step.ipp index 6d18f0e8e..9e0b38e14 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -403,11 +403,8 @@ namespace libcloudphxx // sanity check if (pimpl->opts_init.src_type == src_t::off) throw std::runtime_error("libcloudph++: aerosol source was switched off in opts_init"); - // introduce new particles with the given time interval - if(pimpl->src_stp_ctr % pimpl->opts_init.supstp_src == 0) - { - pimpl->src(pimpl->opts_init.supstp_src * pimpl->dt, opts.src_dry_distros, opts.src_dry_sizes); - } + // introduce new particles + pimpl->src(opts.src_dry_distros, opts.src_dry_sizes); } // aerosol relaxation, in sync since it changes th/rv From c6f843b1938b55de02946fb3f9f4c0ae4bf6c284 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Mon, 18 Mar 2024 16:57:24 +0100 Subject: [PATCH 15/97] sources: separate supstp for each --- include/libcloudph++/lgrngn/distro_t.hpp | 2 +- include/libcloudph++/lgrngn/opts_init.hpp | 10 ---------- src/impl/particles_impl.ipp | 10 +++++----- .../particles_impl_src_dry_distros_matching.ipp | 5 +++-- src/impl/particles_impl_src_dry_distros_simple.ipp | 3 ++- src/impl/particles_impl_src_dry_sizes.ipp | 14 ++++++++++---- 6 files changed, 21 insertions(+), 23 deletions(-) diff --git a/include/libcloudph++/lgrngn/distro_t.hpp b/include/libcloudph++/lgrngn/distro_t.hpp index 5fd26a082..b072b9d1c 100644 --- a/include/libcloudph++/lgrngn/distro_t.hpp +++ b/include/libcloudph++/lgrngn/distro_t.hpp @@ -28,7 +28,7 @@ namespace libcloudphxx template using src_dry_distros_t = std::map< std::pair, // (kappa, ice) - std::tuple, int, int>> // 1st: n(ln(rd)) @ STP created per second; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true; 2nd: sd_conc for this distribution ; 3rd: supstp for this aerosol (interval in timesteps beween addition of these aerosols) + std::tuple>, int, int> // 1st: n(ln(rd)) @ STP created per second; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true; 2nd: sd_conc for this distribution ; 3rd: supstp for this aerosol (interval in timesteps beween addition of these aerosols) >; // defined with a size-number pair diff --git a/include/libcloudph++/lgrngn/opts_init.hpp b/include/libcloudph++/lgrngn/opts_init.hpp index 95738e3b7..47ad06112 100644 --- a/include/libcloudph++/lgrngn/opts_init.hpp +++ b/include/libcloudph++/lgrngn/opts_init.hpp @@ -141,15 +141,6 @@ namespace libcloudphxx // type of CCN source src_t src_type; - // source distro per unit time -// dry_distros_t src_dry_distros; - - // number of SDs created from src_dry_distros per cell per source iteration - unsigned long long src_sd_conc; - - // dry sizes of droplets added from the source, STP_concentration created per unit time instead of the STP_concentration - // dry_sizes_t src_dry_sizes; - // box in which aerosol from source will be created // will be rounded to cell number - cells are supposed to be uniform real_t src_x0, src_y0, src_z0, src_x1, src_y1, src_z1; @@ -218,7 +209,6 @@ namespace libcloudphxx dev_count(0), dev_id(-1), n_sd_max(0), - src_sd_conc(0), src_x0(0), src_x1(0), src_y0(0), diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 6044e6652..6b10865cc 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -584,11 +584,11 @@ namespace libcloudphxx thrust_size_t rcyc(); void bcnd(); - void src(const real_t &dt, const dry_distros_t &, const dry_sizes_t &); - void src_dry_distros_simple(const real_t &dt, const dry_distros_t &); - void src_dry_distros_matching(const real_t &dt, const dry_distros_t &); - void src_dry_distros(const real_t &dt, const dry_distros_t &); - void src_dry_sizes(const real_t &dt, const dry_sizes_t &); + void src(const src_dry_distros_t &, const src_dry_sizes_t &); + void src_dry_distros_simple(const src_dry_distros_t &); + void src_dry_distros_matching(const src_dry_distros_t &); + void src_dry_distros(const src_dry_distros_t &); + void src_dry_sizes( const src_dry_sizes_t &); void rlx(const real_t); void rlx_dry_distros(const real_t); diff --git a/src/impl/particles_impl_src_dry_distros_matching.ipp b/src/impl/particles_impl_src_dry_distros_matching.ipp index d777bd400..0821a004b 100644 --- a/src/impl/particles_impl_src_dry_distros_matching.ipp +++ b/src/impl/particles_impl_src_dry_distros_matching.ipp @@ -47,11 +47,12 @@ namespace libcloudphxx // we increase their multiplicity instead of adding new SDs // TODO: make it work for sdd.size()>1 template - void particles_t::impl::src_dry_distros_matching(const dry_distros_t &sdd) + void particles_t::impl::src_dry_distros_matching(const src_dry_distros_t &sdd) { - const auto p_sdd = sdd.begin() + auto p_sdd = sdd.cbegin(); // add the source only once every number of steps + assert(get<2>(p_sdd->second) > 0); if(src_stp_ctr % get<2>(p_sdd->second) != 0) return; const real_t sup_dt = get<2>(p_sdd->second) * opts_init.dt; diff --git a/src/impl/particles_impl_src_dry_distros_simple.ipp b/src/impl/particles_impl_src_dry_distros_simple.ipp index 1c2be98f2..fee9d33ba 100644 --- a/src/impl/particles_impl_src_dry_distros_simple.ipp +++ b/src/impl/particles_impl_src_dry_distros_simple.ipp @@ -20,9 +20,10 @@ namespace libcloudphxx { // We assume that sdd size is 1 // TODO: add a loop to allow sdd.size>1 - const auto p_sdd = sdd.begin() + auto p_sdd = sdd.cbegin(); // add the source only once every number of steps + assert(get<2>(p_sdd->second) > 0); if(src_stp_ctr % get<2>(p_sdd->second) != 0) return; const real_t sup_dt = get<2>(p_sdd->second) * opts_init.dt; diff --git a/src/impl/particles_impl_src_dry_sizes.ipp b/src/impl/particles_impl_src_dry_sizes.ipp index 0d8c4dc58..f299d0bee 100644 --- a/src/impl/particles_impl_src_dry_sizes.ipp +++ b/src/impl/particles_impl_src_dry_sizes.ipp @@ -12,7 +12,7 @@ namespace libcloudphxx { // initialize SD parameters with dry_radius-concentration pairs (i.e. dry_sizes) template - void particles_t::impl::src_dry_sizes(const real_t &dt, const dry_sizes_t &sds) + void particles_t::impl::src_dry_sizes(const src_dry_sizes_t &sds) { // using dry_sizes_t = typename opts_t::dry_sizes_t; // using real_t = typename dry_sizes_t::key_type; @@ -28,11 +28,17 @@ namespace libcloudphxx const real_t &ice(dsi->first.second); const auto &size_number_map(dsi->second); - // loop over the "size : {concentration per second, multiplicity}" pairs for this (kappa, ice) pair + // loop over the "size : {concentration per second, multiplicity, supstp}" for this (kappa, ice) pair for (auto sni = size_number_map.cbegin(); sni != size_number_map.cend(); ++sni) { + // add the source only once every number of steps + assert(get<2>(sni->second) > 0); + if(src_stp_ctr % get<2>(sni->second) != 0) continue; + + const real_t sup_dt = get<2>(sni->second) * opts_init.dt; + // init number of SDs of this kappa in cells - init_count_num_src(sni->second.second); + init_count_num_src(get<1>(sni->second)); // update no of particles // TODO: move to a separate function @@ -52,7 +58,7 @@ namespace libcloudphxx init_ice(ice); // init multiplicities - init_n_dry_sizes(sni->second.first*dt, sni->second.second); + init_n_dry_sizes(get<0>(sni->second)*sup_dt, get<1>(sni->second)); // initialising wet radii init_wet(); From 0dafbb29399f1cd7052cedd4167048783885b1ba Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Thu, 4 Apr 2024 15:48:56 +0200 Subject: [PATCH 16/97] ice that fell out is now correctly stored in outice_vol, previously it was added to outliq_vol --- src/impl/particles_impl_bcnd.ipp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impl/particles_impl_bcnd.ipp b/src/impl/particles_impl_bcnd.ipp index 43c8ea8b1..9eb3c1844 100644 --- a/src/impl/particles_impl_bcnd.ipp +++ b/src/impl/particles_impl_bcnd.ipp @@ -237,7 +237,7 @@ namespace libcloudphxx ); // add total ice volume that fell out in this step - output_puddle[common::outliq_vol] += + output_puddle[common::outice_vol] += thrust::transform_reduce( thrust::make_zip_iterator(thrust::make_tuple( n_filtered.begin(), rw2.begin(), ice.begin())), // input start From b1131cda4576da94a600d1c37086457704242478 Mon Sep 17 00:00:00 2001 From: Piotr Dziekan Date: Fri, 26 Apr 2024 14:21:22 +0200 Subject: [PATCH 17/97] vterm: add break in switch case to fix vt --- src/impl/particles_impl_hskpng_vterm.ipp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/impl/particles_impl_hskpng_vterm.ipp b/src/impl/particles_impl_hskpng_vterm.ipp index 6d511aed2..1a2586f2e 100644 --- a/src/impl/particles_impl_hskpng_vterm.ipp +++ b/src/impl/particles_impl_hskpng_vterm.ipp @@ -65,6 +65,7 @@ namespace libcloudphxx thrust::get<2>(tpl) * si::kilograms / si::cubic_metres, thrust::get<3>(tpl) * si::pascals * si::seconds ) / si::metres_per_second; + break; case(vt_t::beard77): vt = @@ -74,6 +75,7 @@ namespace libcloudphxx thrust::get<2>(tpl) * si::kilograms / si::cubic_metres, thrust::get<3>(tpl) * si::pascals * si::seconds ) * (common::vterm::vt_beard77_v0(sqrt(rw2) * si::metres) / si::metres_per_second); + break; case(vt_t::khvorostyanov_spherical): vt = common::vterm::vt_khvorostyanov( @@ -83,6 +85,7 @@ namespace libcloudphxx thrust::get<3>(tpl) * si::pascals * si::seconds, true ) / si::metres_per_second; + break; case(vt_t::khvorostyanov_nonspherical): vt = common::vterm::vt_khvorostyanov( @@ -92,8 +95,11 @@ namespace libcloudphxx thrust::get<3>(tpl) * si::pascals * si::seconds, false ) / si::metres_per_second; + break; + default: vt = 0.; //sanity checks done in pimpl constructor + break; } // ice terminal velocity = droplet terminal velocity * rho_ice/rho_water @@ -133,6 +139,7 @@ namespace libcloudphxx thrust::get<2>(tpl) * si::kilograms / si::cubic_metres, thrust::get<3>(tpl) * si::pascals * si::seconds ) * thrust::get<0>(tpl); // cached vt_0 + break; default: vt = 0.; //sanity checks done in pimpl constructor From 21717e8d689efc92d7045cc50586301d9421658f Mon Sep 17 00:00:00 2001 From: Agnieszka Makulska Date: Thu, 29 May 2025 13:14:38 +0200 Subject: [PATCH 18/97] INAS density formulas --- .../libcloudph++/common/ice_nucleation.hpp | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 include/libcloudph++/common/ice_nucleation.hpp diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp new file mode 100644 index 000000000..4b14408bd --- /dev/null +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "units.hpp" + +namespace libcloudphxx +{ + namespace common + { + namespace ice_nucleation + { + enum class INP_t {mineral, soot}; // types of ice nucleating particles + + template + BOOST_GPU_ENABLED + quantity::type, real_t> n_s( + const quantity T, // ambient temperature + const INP_t& INP_type // type of ice nucleating particle + ) { + + if (INP_type == INP_t::mineral) // valid between 237 - 261 K + { + return std::exp(-real_t(0.517) * (T/si::kelvins - real_t(273.15)) + real_t(8.934)) / si::square_meters; + } + if (INP_type == INP_t::soot) // valid between 239 - 255 K + { + return real_t(7.463)*std::exp(-real_t(0.0101) * std::pow(T/si::kelvins - real_t(273.15) , 2) - real_t(0.8525) * (T/si::kelvins - real_t(273.15)) + real_t(0.7667)) / si::square_meters; + } + } + + using dn_dT_dimension = boost::units::divide_typeof_helper< + boost::units::divide_typeof_helper::type, + boost::units::si::temperature + >::type; // m^-2 / K + + template + BOOST_GPU_ENABLED + quantity dn_dT( + const quantity T, // ambient temperature + const INP_t& INP_type // type of ice nucleating particle + ) { + + if (INP_type == INP_t::mineral) // valid between 237 - 261 K + { + return -real_t(0.517) * n_s(T, INP_type) / si::kelvins; + } + if (INP_type == INP_t::soot) // valid between 239 - 255 K + { + return (-real_t(0.0202) * T/si::kelvins - real_t(0.8525)) * n_s(T, INP_type) / si::kelvins; + } + } + + template + BOOST_GPU_ENABLED + quantity::type, real_t> p( + const quantity T, // temperature + const INP_t& INP_type, // type of ice nucleating particle + const quantity rd_insol // radius of ice nucleating (insoluble) particle + ) { + const quantity A = real_t(4) * pi() * rd_insol * rd_insol; // surface area of the insoluble particle + return -A * dn_dT(T, INP_type) * std::exp(-A * n_s(T, INP_type)); + } + + }; + }; +}; From f0e4c95b4f2d13a134f91c5218b0409e4ac1ec9f Mon Sep 17 00:00:00 2001 From: Agnieszka Makulska Date: Mon, 2 Jun 2025 20:38:56 +0200 Subject: [PATCH 19/97] sampling T_freez from CFD --- .../libcloudph++/common/ice_nucleation.hpp | 61 +++++-------------- 1 file changed, 16 insertions(+), 45 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index 4b14408bd..8a22f26d4 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -8,58 +8,29 @@ namespace libcloudphxx { namespace ice_nucleation { - enum class INP_t {mineral, soot}; // types of ice nucleating particles + enum class INP_t {mineral}; // types of ice nucleating particles, TODO: add more types + // Freezing temperature as defined in Shima et al., 2020 template BOOST_GPU_ENABLED - quantity::type, real_t> n_s( - const quantity T, // ambient temperature - const INP_t& INP_type // type of ice nucleating particle - ) { - - if (INP_type == INP_t::mineral) // valid between 237 - 261 K - { - return std::exp(-real_t(0.517) * (T/si::kelvins - real_t(273.15)) + real_t(8.934)) / si::square_meters; - } - if (INP_type == INP_t::soot) // valid between 239 - 255 K + quantity T_freez( + const INP_t& INP_type, // type of ice nucleating particle + const quantity rd_insol, // radius of ice nucleating (insoluble) particle + int rng_seed // seed for random number generator + ) { + real_t A = real_t(4) * pi() * rd_insol * rd_insol / si::square_meters; // surface area of the insoluble particle + + std::mt19937 gen(rng_seed); // random number generator + std::uniform_real_distribution<> dis(0, 1); + real_t Y = dis(gen); // random number between [0, 1] + + // Sampling T_freez from its CDF using inverse transform sampling + if (INP_type == INP_t::mineral) { - return real_t(7.463)*std::exp(-real_t(0.0101) * std::pow(T/si::kelvins - real_t(273.15) , 2) - real_t(0.8525) * (T/si::kelvins - real_t(273.15)) + real_t(0.7667)) / si::square_meters; + return real_t(273.15) + (real_t(8.934) - std::log(- std::log(1-Y) / A) ) / real_t(0.517); } } - using dn_dT_dimension = boost::units::divide_typeof_helper< - boost::units::divide_typeof_helper::type, - boost::units::si::temperature - >::type; // m^-2 / K - - template - BOOST_GPU_ENABLED - quantity dn_dT( - const quantity T, // ambient temperature - const INP_t& INP_type // type of ice nucleating particle - ) { - - if (INP_type == INP_t::mineral) // valid between 237 - 261 K - { - return -real_t(0.517) * n_s(T, INP_type) / si::kelvins; - } - if (INP_type == INP_t::soot) // valid between 239 - 255 K - { - return (-real_t(0.0202) * T/si::kelvins - real_t(0.8525)) * n_s(T, INP_type) / si::kelvins; - } - } - - template - BOOST_GPU_ENABLED - quantity::type, real_t> p( - const quantity T, // temperature - const INP_t& INP_type, // type of ice nucleating particle - const quantity rd_insol // radius of ice nucleating (insoluble) particle - ) { - const quantity A = real_t(4) * pi() * rd_insol * rd_insol; // surface area of the insoluble particle - return -A * dn_dT(T, INP_type) * std::exp(-A * n_s(T, INP_type)); - } - }; }; }; From 57a15ca7ddb86abf2f05299620d7ab8c218bc741 Mon Sep 17 00:00:00 2001 From: pdziekan Date: Mon, 9 Jun 2025 15:42:42 +0200 Subject: [PATCH 20/97] add rd3_insol and T_freeze; init rd3_insol --- include/libcloudph++/lgrngn/distro_t.hpp | 8 ++++---- src/impl/particles_impl.ipp | 11 +++++++++-- src/impl/particles_impl_hskpng_resize.ipp | 7 ++++++- src/impl/particles_impl_init_SD_with_distros.ipp | 8 ++++---- src/impl/particles_impl_init_SD_with_sizes.ipp | 2 +- src/impl/particles_impl_reserve_hskpng_npart.ipp | 4 ++++ src/impl/particles_impl_src_dry_distros_simple.ipp | 2 +- src/impl/particles_impl_src_dry_sizes.ipp | 2 +- src/particles.tpp | 3 ++- 9 files changed, 32 insertions(+), 15 deletions(-) diff --git a/include/libcloudph++/lgrngn/distro_t.hpp b/include/libcloudph++/lgrngn/distro_t.hpp index b072b9d1c..c45825def 100644 --- a/include/libcloudph++/lgrngn/distro_t.hpp +++ b/include/libcloudph++/lgrngn/distro_t.hpp @@ -11,14 +11,14 @@ namespace libcloudphxx // uses shared_ptr to make opts_init copyable template using dry_distros_t = std::map< - std::pair, // (kappa, ice) + std::pair, // (kappa, rd_insol) std::shared_ptr> // n(ln(rd)) @ STP; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true >; // defined with a size-number pair template using dry_sizes_t = std::map< - std::pair, // (kappa, ice) + std::pair, // (kappa, rd_insol) std::map // STP_concentration [1/m^3], number of SD that represent this radius kappa and concentration > @@ -27,14 +27,14 @@ namespace libcloudphxx // similar, but for sources of aerosols after initialization template using src_dry_distros_t = std::map< - std::pair, // (kappa, ice) + std::pair, // (kappa, rd_insol) std::tuple>, int, int> // 1st: n(ln(rd)) @ STP created per second; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true; 2nd: sd_conc for this distribution ; 3rd: supstp for this aerosol (interval in timesteps beween addition of these aerosols) >; // defined with a size-number pair template using src_dry_sizes_t = std::map< - std::pair, // (kappa, ice) + std::pair, // (kappa, rd_insol) std::map // STP_concentration [1/m^3] created per second, number of SD that represent this radius kappa and concentration, supstp > diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 6b10865cc..ee1fe02c9 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -90,7 +90,9 @@ namespace libcloudphxx sstp_tmp_rh, // ditto for rho sstp_tmp_p, // ditto for pressure incloud_time, // time this SD has been within a cloud - ice; // 0 - water 1 - ice; bool would suffice, but we are lazy + ice, // 0 - water 1 - ice; bool would suffice, but we are lazy + rd3_insol, // dry radii of insoluble aerosol cubed + T_freeze; // freezing temperature // dry radii distribution characteristics real_t log_rd_min, // logarithm of the lower bound of the distr @@ -422,7 +424,11 @@ namespace libcloudphxx distmem_real_vctrs.insert(&incloud_time); if(opts_init.ice_switch) + { distmem_real_vctrs.insert(&ice); + distmem_real_vctrs.insert(&rd3_insol); + distmem_real_vctrs.insert(&T_freeze); + } } void sanity_checks(); @@ -463,7 +469,8 @@ namespace libcloudphxx void init_ijk(); void init_xyz(); void init_kappa(const real_t &); - void init_ice(const real_t &); +// void init_ice(const real_t &); + void init_rd3_insol(const real_t &); void init_incloud_time(); void init_count_num_sd_conc(const real_t & = 1); void init_count_num_const_multi(const common::unary_function &); diff --git a/src/impl/particles_impl_hskpng_resize.ipp b/src/impl/particles_impl_hskpng_resize.ipp index 43cc80361..3d2e52799 100644 --- a/src/impl/particles_impl_hskpng_resize.ipp +++ b/src/impl/particles_impl_hskpng_resize.ipp @@ -49,7 +49,12 @@ namespace libcloudphxx dot_ssp.resize(n_part, 0); } - if(opts_init.ice_switch) ice.resize(n_part); + if(opts_init.ice_switch) + { + ice.resize(n_part); + rd3_insol.resize(n_part); + T_freeze.resize(n_part); + } if(opts_init.chem_switch || allow_sstp_cond || n_dims >= 2) { diff --git a/src/impl/particles_impl_init_SD_with_distros.ipp b/src/impl/particles_impl_init_SD_with_distros.ipp index cbd61065f..5e6ee4262 100644 --- a/src/impl/particles_impl_init_SD_with_distros.ipp +++ b/src/impl/particles_impl_init_SD_with_distros.ipp @@ -50,13 +50,13 @@ namespace libcloudphxx // final inits common for tail/sd_conc/const_multi template - void particles_t::impl::init_SD_with_distros_finalize(const std::pair &kpa_ice, const bool unravel_ijk_switch) + void particles_t::impl::init_SD_with_distros_finalize(const std::pair &kpa_rd3_insol, const bool unravel_ijk_switch) { // init kappa - init_kappa(kpa_ice.first); + init_kappa(kpa_rd3_insol.first); - // init ice - init_ice(kpa_ice.second); + // init rd3_insol + init_rd3_insol(kpa_rd3_insol.second); // initialising wet radii init_wet(); diff --git a/src/impl/particles_impl_init_SD_with_sizes.ipp b/src/impl/particles_impl_init_SD_with_sizes.ipp index 3a6d77528..200c5e9ff 100644 --- a/src/impl/particles_impl_init_SD_with_sizes.ipp +++ b/src/impl/particles_impl_init_SD_with_sizes.ipp @@ -47,7 +47,7 @@ namespace libcloudphxx // init kappa and ice init_kappa(kappa); - init_ice(ice); + init_rd3_insol(ice); // init multiplicities init_n_dry_sizes(sni->second.first, sni->second.second); diff --git a/src/impl/particles_impl_reserve_hskpng_npart.ipp b/src/impl/particles_impl_reserve_hskpng_npart.ipp index 0a3134787..474a9eba7 100644 --- a/src/impl/particles_impl_reserve_hskpng_npart.ipp +++ b/src/impl/particles_impl_reserve_hskpng_npart.ipp @@ -37,7 +37,11 @@ namespace libcloudphxx } if(opts_init.ice_switch) + { ice.reserve(opts_init.n_sd_max); + rd3_insol.reserve(opts_init.n_sd_max); + T_freeze.reserve(opts_init.n_sd_max); + } vt.reserve(opts_init.n_sd_max); diff --git a/src/impl/particles_impl_src_dry_distros_simple.ipp b/src/impl/particles_impl_src_dry_distros_simple.ipp index fee9d33ba..dddf5e913 100644 --- a/src/impl/particles_impl_src_dry_distros_simple.ipp +++ b/src/impl/particles_impl_src_dry_distros_simple.ipp @@ -58,7 +58,7 @@ namespace libcloudphxx init_kappa( p_sdd->first.first ); - init_ice( + init_rd3_insol( p_sdd->first.second ); diff --git a/src/impl/particles_impl_src_dry_sizes.ipp b/src/impl/particles_impl_src_dry_sizes.ipp index f299d0bee..d2c4f9904 100644 --- a/src/impl/particles_impl_src_dry_sizes.ipp +++ b/src/impl/particles_impl_src_dry_sizes.ipp @@ -55,7 +55,7 @@ namespace libcloudphxx // init kappa and ice init_kappa(kappa); - init_ice(ice); + init_rd3_insol(ice); // init multiplicities init_n_dry_sizes(get<0>(sni->second)*sup_dt, get<1>(sni->second)); diff --git a/src/particles.tpp b/src/particles.tpp index 3a9f0f131..d31696315 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -63,7 +63,8 @@ #include "impl/particles_impl_init_dry_const_multi.ipp" #include "impl/particles_impl_init_dry_dry_sizes.ipp" #include "impl/particles_impl_init_kappa.ipp" -#include "impl/particles_impl_init_ice.ipp" +//#include "impl/particles_impl_init_ice.ipp" +#include "impl/particles_impl_init_rd3_insol.ipp" #include "impl/particles_impl_init_incloud_time.ipp" #include "impl/particles_impl_init_n.ipp" #include "impl/particles_impl_init_wet.ipp" From a5a618f2f1905106d803d5ea13314b961e7ac97d Mon Sep 17 00:00:00 2001 From: Agnieszka Makulska Date: Fri, 13 Jun 2025 16:40:16 +0200 Subject: [PATCH 21/97] ice nucleation --- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_fill_outbuf.ipp | 6 ++- src/impl/particles_impl_hskpng_resize.ipp | 4 +- src/impl/particles_impl_nucleation.ipp | 45 +++++++++++++++++++++++ src/particles_step.ipp | 4 +- 5 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 src/impl/particles_impl_nucleation.ipp diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index ee1fe02c9..153e92cdf 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -391,7 +391,7 @@ namespace libcloudphxx // TODO: add to that list vectors of other types (e.g integer pimpl->n) // NOTE: this does not include chemical stuff due to the way chem vctrs are organized! multi_CUDA / MPI does not work with chemistry as of now typedef thrust_device::vector* ptr_t; - ptr_t arr[] = {&rd3, &rw2, &kpa, &vt}; + ptr_t arr[] = {&rd3, &rw2, &kpa, &vt, &rd3_insol, &T_freeze}; distmem_real_vctrs = std::set(arr, arr + sizeof(arr) / sizeof(ptr_t) ); if (opts_init.nx != 0) distmem_real_vctrs.insert(&x); diff --git a/src/impl/particles_impl_fill_outbuf.ipp b/src/impl/particles_impl_fill_outbuf.ipp index ecb71bca9..500f1300d 100644 --- a/src/impl/particles_impl_fill_outbuf.ipp +++ b/src/impl/particles_impl_fill_outbuf.ipp @@ -40,14 +40,16 @@ namespace libcloudphxx template std::vector particles_t::impl::fill_attr_outbuf(const std::string &name) { - const std::set attr_names = {"rw2", "rd3", "kappa", "x", "y", "z"}; // TODO implement "n" - it is n_t type and others are real_t + const std::set attr_names = {"rw2", "rd3", "kappa", "rd3_insol", "T_freeze", "x", "y", "z"}; // TODO implement "n" - it is n_t type and others are real_t if (std::find(std::begin(attr_names), std::end(attr_names), name) == std::end(attr_names)) throw std::runtime_error("Unknown attribute name passed to get_attr."); const thrust_device::vector &dv( name == "rw2" ? rw2 : name == "rd3" ? rd3 : - name == "kappa" ? kpa : + name == "kappa" ? kpa : + name == "rd3_insol" ? rd3_insol : + name == "T_freeze" ? T_freeze : name == "x" ? x : name == "y" ? y : z); diff --git a/src/impl/particles_impl_hskpng_resize.ipp b/src/impl/particles_impl_hskpng_resize.ipp index 3d2e52799..4f0c4ccaf 100644 --- a/src/impl/particles_impl_hskpng_resize.ipp +++ b/src/impl/particles_impl_hskpng_resize.ipp @@ -8,8 +8,8 @@ namespace libcloudphxx { if(n_part > opts_init.n_sd_max) throw std::runtime_error(detail::formatter() << "n_sd_max (" << opts_init.n_sd_max << ") < n_part (" << n_part << ")"); { - thrust_device::vector *vec[] = {&rw2, &rd3, &kpa, &tmp_device_real_part}; - for(int i=0; i<4; ++i) + thrust_device::vector *vec[] = {&rw2, &rd3, &kpa, &rd3_insol, &T_freeze, &tmp_device_real_part}; + for(int i=0; i<6; ++i) { vec[i]->resize(n_part); } diff --git a/src/impl/particles_impl_nucleation.ipp b/src/impl/particles_impl_nucleation.ipp new file mode 100644 index 000000000..cc6b15333 --- /dev/null +++ b/src/impl/particles_impl_nucleation.ipp @@ -0,0 +1,45 @@ +// vim:filetype=cpp +/** @file + * @copyright University of Warsaw + * @section LICENSE + * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) + */ + +#include +#include + +namespace libcloudphxx +{ + namespace lgrngn + { + namespace detail + { + template + class immersion_freeze_cond + { + BOOST_GPU_ENABLED + bool operator()(const auto &tpl) + { + if (thrust::get<0>(tpl) >= thrust::get<1>(tpl) && thrust::get<2>(tpl) >= real_t(1)) + return true; + else + return false; + }; + }; + }; + + template + void particles_t::impl::nucleation() { + thrust::replace_if(ice.begin(), ice.end(), + thrust::make_zip_iterator( + thrust::make_tuple( + T_freeze.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH.begin(), ijk.begin()) + ) + ), + detail::immersion_freeze_cond(), + real_t(1) + ); + } +}; diff --git a/src/particles_step.ipp b/src/particles_step.ipp index 9e0b38e14..9520fa677 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -194,6 +194,7 @@ namespace libcloudphxx pimpl->sstp_step_exact(step); if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); + // pimpl->freez pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } // copy sstp_tmp_rv and th to rv and th @@ -208,7 +209,8 @@ namespace libcloudphxx pimpl->sstp_step(step); if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); - pimpl->hskpng_Tpr(); + pimpl->hskpng_Tpr(); + // pimpl->freez pimpl->cond(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } } From 591e5b27f5932e658ed08173e207877e9411296c Mon Sep 17 00:00:00 2001 From: Agnieszka Makulska Date: Tue, 1 Jul 2025 13:47:18 +0200 Subject: [PATCH 22/97] rd3_insol init --- src/impl/particles_impl.ipp | 4 ++- src/impl/particles_impl_init_ice.ipp | 24 ---------------- src/impl/particles_impl_init_rd3_insol.ipp | 32 ++++++++++++++++++++++ src/particles.tpp | 3 +- 4 files changed, 37 insertions(+), 26 deletions(-) delete mode 100644 src/impl/particles_impl_init_ice.ipp create mode 100644 src/impl/particles_impl_init_rd3_insol.ipp diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 153e92cdf..96abc426f 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -469,8 +469,8 @@ namespace libcloudphxx void init_ijk(); void init_xyz(); void init_kappa(const real_t &); -// void init_ice(const real_t &); void init_rd3_insol(const real_t &); + void init_T_freeze(const int &); void init_incloud_time(); void init_count_num_sd_conc(const real_t & = 1); void init_count_num_const_multi(const common::unary_function &); @@ -569,6 +569,8 @@ namespace libcloudphxx void sedi(const real_t &dt); void subs(const real_t &dt); + void nucleation(); + void cond_dm3_helper(); void cond(const real_t &dt, const real_t &RH_max, const bool turb_cond); void cond_sstp(const real_t &dt, const real_t &RH_max, const bool turb_cond); diff --git a/src/impl/particles_impl_init_ice.ipp b/src/impl/particles_impl_init_ice.ipp deleted file mode 100644 index 25aaa19d2..000000000 --- a/src/impl/particles_impl_init_ice.ipp +++ /dev/null @@ -1,24 +0,0 @@ -// vim:filetype=cpp -/** @file - * @copyright University of Warsaw - * @section LICENSE - * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) - * @brief initialisation routine for super droplets - */ - -namespace libcloudphxx -{ - namespace lgrngn - { - template - void particles_t::impl::init_ice( - const real_t &val - ) - { - assert(val==0 || val==1); - // filling ice flag - thrust::fill(ice.begin() + n_part_old, ice.end(), val); - } - }; -}; - diff --git a/src/impl/particles_impl_init_rd3_insol.ipp b/src/impl/particles_impl_init_rd3_insol.ipp new file mode 100644 index 000000000..7ebfab43c --- /dev/null +++ b/src/impl/particles_impl_init_rd3_insol.ipp @@ -0,0 +1,32 @@ +// vim:filetype=cpp +/** @file + * @copyright University of Warsaw + * @section LICENSE + * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) + * @brief initialisation routine for super droplets + */ + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::init_rd3_insol( + const real_t &rd_insol // fixed rd of insoluble particles + ) + { + // half of super-particles contain insoluble substance + thrust::fill(rd3_insol.begin() + n_part_old, + rd3_insol.begin() + n_part_old + (rd3_insol.size() - n_part_old) / 2, + rd_insol * rd_insol * rd_insol); + + // the other half contain soluble substances (rd_insol = 0) + thrust::fill(rd3_insol.begin() + n_part_old + (rd3_insol.size() - n_part_old) / 2, + rd3_insol.end(), + real_t(0)); + } + }; +}; + +//TODO: 0.05 of insoluble particles should be inactive (for inactive the freezing temperature is -38) + diff --git a/src/particles.tpp b/src/particles.tpp index d31696315..91a2ad402 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -63,8 +63,8 @@ #include "impl/particles_impl_init_dry_const_multi.ipp" #include "impl/particles_impl_init_dry_dry_sizes.ipp" #include "impl/particles_impl_init_kappa.ipp" -//#include "impl/particles_impl_init_ice.ipp" #include "impl/particles_impl_init_rd3_insol.ipp" +#include "impl/particles_impl_init_T_freeze.ipp" #include "impl/particles_impl_init_incloud_time.ipp" #include "impl/particles_impl_init_n.ipp" #include "impl/particles_impl_init_wet.ipp" @@ -105,6 +105,7 @@ #include "impl/particles_impl_cond_common.ipp" #include "impl/particles_impl_cond.ipp" #include "impl/particles_impl_cond_sstp.ipp" +#include "impl/particles_impl_nucleation.ipp" #include "impl/particles_impl_sedi.ipp" #include "impl/particles_impl_subs.ipp" #include "impl/particles_impl_coal.ipp" From db62b5566a8f06c2321493f812ac5bcfbae9ee15 Mon Sep 17 00:00:00 2001 From: Agnieszka Makulska Date: Tue, 1 Jul 2025 13:50:39 +0200 Subject: [PATCH 23/97] comments in impl_nucleation --- src/impl/particles_impl_nucleation.ipp | 36 ++++++++++++++------------ src/particles_step.ipp | 4 +-- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/impl/particles_impl_nucleation.ipp b/src/impl/particles_impl_nucleation.ipp index cc6b15333..4616c7f27 100644 --- a/src/impl/particles_impl_nucleation.ipp +++ b/src/impl/particles_impl_nucleation.ipp @@ -14,13 +14,15 @@ namespace libcloudphxx { namespace detail { + // The condition for immersion freezing template class immersion_freeze_cond { + public: BOOST_GPU_ENABLED - bool operator()(const auto &tpl) + bool operator()(const thrust::tuple &tpl) // tpl is a tuple of 3 elements: (T_freeze, ambient T, ambient RH) { - if (thrust::get<0>(tpl) >= thrust::get<1>(tpl) && thrust::get<2>(tpl) >= real_t(1)) + if (thrust::get<0>(tpl) >= thrust::get<1>(tpl) && thrust::get<2>(tpl) >= real_t(1)) // returns true if T_freeze >= ambient T and ambient RH >= 1 return true; else return false; @@ -28,18 +30,20 @@ namespace libcloudphxx }; }; - template - void particles_t::impl::nucleation() { - thrust::replace_if(ice.begin(), ice.end(), - thrust::make_zip_iterator( - thrust::make_tuple( - T_freeze.begin(), - thrust::make_permutation_iterator(T.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH.begin(), ijk.begin()) - ) - ), - detail::immersion_freeze_cond(), - real_t(1) - ); + // Immersion freezing + template + void particles_t::impl::nucleation() { + thrust::replace_if(ice.begin(), ice.end(), // Replacing values of ice with 1 if immersion_freeze_cond is satisfied. + thrust::make_zip_iterator( + thrust::make_tuple( // Creating a zip iterator to access multiple vectors: + T_freeze.begin(), // freezing temperature for each droplet + thrust::make_permutation_iterator(T.begin(), ijk.begin()), // ambient temperature + thrust::make_permutation_iterator(RH.begin(), ijk.begin()) // ambient RH + ) + ), + detail::immersion_freeze_cond(), + real_t(1) + ); + } } -}; +} diff --git a/src/particles_step.ipp b/src/particles_step.ipp index 9520fa677..52a1afd1b 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -194,7 +194,7 @@ namespace libcloudphxx pimpl->sstp_step_exact(step); if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); - // pimpl->freez + //TODO: pimpl->nucleation_sstp(); pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } // copy sstp_tmp_rv and th to rv and th @@ -210,7 +210,7 @@ namespace libcloudphxx if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); pimpl->hskpng_Tpr(); - // pimpl->freez + pimpl->nucleation(); pimpl->cond(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } } From 909b33c45d2c333ad09df585e592319f7933ab0b Mon Sep 17 00:00:00 2001 From: Agnieszka Makulska Date: Tue, 1 Jul 2025 14:59:30 +0200 Subject: [PATCH 24/97] T_freeze initialization --- .../libcloudph++/common/ice_nucleation.hpp | 29 +++++++++++++--- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_init_T_freeze.ipp | 33 +++++++++++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 src/impl/particles_impl_init_T_freeze.ipp diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index 8a22f26d4..b929c14b6 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -13,24 +13,43 @@ namespace libcloudphxx // Freezing temperature as defined in Shima et al., 2020 template BOOST_GPU_ENABLED - quantity T_freez( + quantity T_freeze( const INP_t& INP_type, // type of ice nucleating particle - const quantity rd_insol, // radius of ice nucleating (insoluble) particle + const quantity rd3_insol, // radius cubed of ice nucleating (insoluble) particle int rng_seed // seed for random number generator ) { - real_t A = real_t(4) * pi() * rd_insol * rd_insol / si::square_meters; // surface area of the insoluble particle + real_t A = real_t(4) * pi() * std::pow(rd3_insol/si::meters, 2/3); // surface area of the insoluble particle std::mt19937 gen(rng_seed); // random number generator std::uniform_real_distribution<> dis(0, 1); real_t Y = dis(gen); // random number between [0, 1] // Sampling T_freez from its CDF using inverse transform sampling - if (INP_type == INP_t::mineral) + if (A > std::numeric_limits::epsilon && INP_type == INP_t::mineral) { - return real_t(273.15) + (real_t(8.934) - std::log(- std::log(1-Y) / A) ) / real_t(0.517); + return (real_t(273.15) + (real_t(8.934) - std::log(- std::log(1-Y) / A) ) / real_t(0.517)) * si::kelvins; + } + else + { + return real_t(235.15) * si::kelvin; // if rd_insol = 0 or INP type is unknown, the default freezing temperature is -38 C } } + + template + struct T_freeze_functor + { + INP_t INP_type; + int rng_seed; + T_freeze_functor(INP_t INP_type, int rng_seed) + : INP_type(INP_type), rng_seed(rng_seed) {} + BOOST_GPU_ENABLED + real_t operator()(const real_t &rd3_insol) const + { + return ice_nucleation::T_freeze(INP_type, rd3_insol * si::meters, rng_seed).value(); + } + }; + }; }; }; diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 96abc426f..81a7dc829 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -470,7 +470,7 @@ namespace libcloudphxx void init_xyz(); void init_kappa(const real_t &); void init_rd3_insol(const real_t &); - void init_T_freeze(const int &); + void init_T_freeze(int rng_seed); void init_incloud_time(); void init_count_num_sd_conc(const real_t & = 1); void init_count_num_const_multi(const common::unary_function &); diff --git a/src/impl/particles_impl_init_T_freeze.ipp b/src/impl/particles_impl_init_T_freeze.ipp new file mode 100644 index 000000000..865fe0156 --- /dev/null +++ b/src/impl/particles_impl_init_T_freeze.ipp @@ -0,0 +1,33 @@ +// vim:filetype=cpp +/** @file + * @copyright University of Warsaw + * @section LICENSE + * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) + * @brief initialisation routine for super droplets + */ + +#include "../../include/libcloudph++/common/ice_nucleation.hpp" + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::init_T_freeze( + const int rng_seed + ) + { + using namespace libcloudphxx::common::ice_nucleation; + + const INP_t inp_type = INP_t::mineral; //TODO: INP type as argument, to support different partcle types + + thrust::transform( + rd3_insol.begin() + n_part_old, + rd3_insol.end(), + T_freeze.begin() + n_part_old, + T_freeze_functor(inp_type, rng_seed) + ); + } + }; +}; + From 5469206269cdb33933b7ae05cb4bdb0dfde66966 Mon Sep 17 00:00:00 2001 From: Agnieszka Makulska Date: Tue, 1 Jul 2025 15:22:42 +0200 Subject: [PATCH 25/97] 5% of mineral dust inactive --- include/libcloudph++/common/ice_nucleation.hpp | 4 ++-- src/impl/particles_impl_init_rd3_insol.ipp | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index b929c14b6..a9547d71b 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -25,13 +25,13 @@ namespace libcloudphxx real_t Y = dis(gen); // random number between [0, 1] // Sampling T_freez from its CDF using inverse transform sampling - if (A > std::numeric_limits::epsilon && INP_type == INP_t::mineral) + if (INP_type == INP_t::mineral && A > std::numeric_limits::epsilon() && dis(gen) > 0.05) // 5% of mineral dust particles are inactive { return (real_t(273.15) + (real_t(8.934) - std::log(- std::log(1-Y) / A) ) / real_t(0.517)) * si::kelvins; } else { - return real_t(235.15) * si::kelvin; // if rd_insol = 0 or INP type is unknown, the default freezing temperature is -38 C + return real_t(235.15) * si::kelvin; // the default freezing temperature is -38 C } } diff --git a/src/impl/particles_impl_init_rd3_insol.ipp b/src/impl/particles_impl_init_rd3_insol.ipp index 7ebfab43c..e0361cad2 100644 --- a/src/impl/particles_impl_init_rd3_insol.ipp +++ b/src/impl/particles_impl_init_rd3_insol.ipp @@ -28,5 +28,3 @@ namespace libcloudphxx }; }; -//TODO: 0.05 of insoluble particles should be inactive (for inactive the freezing temperature is -38) - From 5cbe83b91482ab19fabdfa4519e6234ff289a650 Mon Sep 17 00:00:00 2001 From: Agnieszka Makulska Date: Wed, 2 Jul 2025 14:42:35 +0200 Subject: [PATCH 26/97] adding opts.ice_nucl and renaming --- CMakeLists.txt | 2 +- include/libcloudph++/lgrngn/opts.hpp | 4 ++-- src/impl/particles_impl.ipp | 1 + ...eation.ipp => particles_impl_ice_nucl.ipp} | 2 ++ src/impl/particles_impl_init_ice.ipp | 23 +++++++++++++++++++ src/particles.tpp | 3 ++- src/particles_step.ipp | 6 +++-- 7 files changed, 35 insertions(+), 6 deletions(-) rename src/impl/{particles_impl_nucleation.ipp => particles_impl_ice_nucl.ipp} (97%) create mode 100644 src/impl/particles_impl_init_ice.ipp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b579d6e4..0eaf1d559 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -371,7 +371,7 @@ enable_testing() add_subdirectory(tests) add_subdirectory(include) -add_subdirectory(bindings) +#add_subdirectory(bindings) ############################################################################################ diff --git a/include/libcloudph++/lgrngn/opts.hpp b/include/libcloudph++/lgrngn/opts.hpp index d01cdd30a..d01181769 100644 --- a/include/libcloudph++/lgrngn/opts.hpp +++ b/include/libcloudph++/lgrngn/opts.hpp @@ -20,7 +20,7 @@ namespace libcloudphxx struct opts_t { // process toggling - bool adve, sedi, subs, cond, coal, src, rlx, rcyc, turb_adve, turb_cond, turb_coal; + bool adve, sedi, subs, cond, coal, src, rlx, rcyc, turb_adve, turb_cond, turb_coal, ice_nucl; // RH limit for drop growth real_t RH_max; @@ -42,7 +42,7 @@ namespace libcloudphxx opts_t() : adve(true), sedi(true), subs(false), cond(true), coal(true), src(false), rlx(false), rcyc(false), chem_dsl(false), chem_dsc(false), chem_rct(false), - turb_adve(false), turb_cond(false), turb_coal(false), + turb_adve(false), turb_cond(false), turb_coal(false), ice_nucl(false), RH_max(44), // :) (anything greater than 1.1 would be enough dt(-1) // negative means that we do not override dt in this step { diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 81a7dc829..1885f864d 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -469,6 +469,7 @@ namespace libcloudphxx void init_ijk(); void init_xyz(); void init_kappa(const real_t &); + void init_ice(const real_t &); void init_rd3_insol(const real_t &); void init_T_freeze(int rng_seed); void init_incloud_time(); diff --git a/src/impl/particles_impl_nucleation.ipp b/src/impl/particles_impl_ice_nucl.ipp similarity index 97% rename from src/impl/particles_impl_nucleation.ipp rename to src/impl/particles_impl_ice_nucl.ipp index 4616c7f27..d93f77150 100644 --- a/src/impl/particles_impl_nucleation.ipp +++ b/src/impl/particles_impl_ice_nucl.ipp @@ -45,5 +45,7 @@ namespace libcloudphxx real_t(1) ); } + + //TODO: latent heat of freezing } } diff --git a/src/impl/particles_impl_init_ice.ipp b/src/impl/particles_impl_init_ice.ipp new file mode 100644 index 000000000..bd465f32c --- /dev/null +++ b/src/impl/particles_impl_init_ice.ipp @@ -0,0 +1,23 @@ +// vim:filetype=cppAdd commentMore actions +/** @file + * @copyright University of Warsaw + * @section LICENSE + * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) + * @brief initialisation routine for super droplets + */ + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::init_ice( + const real_t &val + ) + { + assert(val==0 || val==1); + // filling ice flag + thrust::fill(ice.begin() + n_part_old, ice.end(), val); + } + }; +}; \ No newline at end of file diff --git a/src/particles.tpp b/src/particles.tpp index 91a2ad402..086cd0c41 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -63,6 +63,7 @@ #include "impl/particles_impl_init_dry_const_multi.ipp" #include "impl/particles_impl_init_dry_dry_sizes.ipp" #include "impl/particles_impl_init_kappa.ipp" +#include "impl/particles_impl_init_ice.ipp" #include "impl/particles_impl_init_rd3_insol.ipp" #include "impl/particles_impl_init_T_freeze.ipp" #include "impl/particles_impl_init_incloud_time.ipp" @@ -105,7 +106,7 @@ #include "impl/particles_impl_cond_common.ipp" #include "impl/particles_impl_cond.ipp" #include "impl/particles_impl_cond_sstp.ipp" -#include "impl/particles_impl_nucleation.ipp" +#include "impl/particles_impl_ice_nucl.ipp" #include "impl/particles_impl_sedi.ipp" #include "impl/particles_impl_subs.ipp" #include "impl/particles_impl_coal.ipp" diff --git a/src/particles_step.ipp b/src/particles_step.ipp index 52a1afd1b..9103d9338 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -194,7 +194,8 @@ namespace libcloudphxx pimpl->sstp_step_exact(step); if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); - //TODO: pimpl->nucleation_sstp(); + if (opts.ice_nucl) + pimpl->ice_nucl_sstp(); pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } // copy sstp_tmp_rv and th to rv and th @@ -210,7 +211,8 @@ namespace libcloudphxx if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); pimpl->hskpng_Tpr(); - pimpl->nucleation(); + if (opts.ice_nucl) + pimpl->ice_nucl(); pimpl->cond(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } } From d4722731246cd4613b9ba821e078126d0807a638 Mon Sep 17 00:00:00 2001 From: Agnieszka Makulska Date: Wed, 2 Jul 2025 14:54:50 +0200 Subject: [PATCH 27/97] code cleanup --- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_ice_nucl.ipp | 2 +- src/impl/particles_impl_init_ice.ipp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 1885f864d..9c3bfd87e 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -570,7 +570,7 @@ namespace libcloudphxx void sedi(const real_t &dt); void subs(const real_t &dt); - void nucleation(); + void ice_nucl(); void cond_dm3_helper(); void cond(const real_t &dt, const real_t &RH_max, const bool turb_cond); diff --git a/src/impl/particles_impl_ice_nucl.ipp b/src/impl/particles_impl_ice_nucl.ipp index d93f77150..e46eba0d2 100644 --- a/src/impl/particles_impl_ice_nucl.ipp +++ b/src/impl/particles_impl_ice_nucl.ipp @@ -32,7 +32,7 @@ namespace libcloudphxx // Immersion freezing template - void particles_t::impl::nucleation() { + void particles_t::impl::ice_nucl() { thrust::replace_if(ice.begin(), ice.end(), // Replacing values of ice with 1 if immersion_freeze_cond is satisfied. thrust::make_zip_iterator( thrust::make_tuple( // Creating a zip iterator to access multiple vectors: diff --git a/src/impl/particles_impl_init_ice.ipp b/src/impl/particles_impl_init_ice.ipp index bd465f32c..75e978419 100644 --- a/src/impl/particles_impl_init_ice.ipp +++ b/src/impl/particles_impl_init_ice.ipp @@ -1,4 +1,4 @@ -// vim:filetype=cppAdd commentMore actions +// vim:filetype=cpp /** @file * @copyright University of Warsaw * @section LICENSE From e5b41e63786e51e3ee289d356fe122c1e00a39fe Mon Sep 17 00:00:00 2001 From: Agnieszka Makulska Date: Mon, 14 Jul 2025 11:39:31 +0200 Subject: [PATCH 28/97] sampling in src/ with multiple random numbers --- .../libcloudph++/common/ice_nucleation.hpp | 35 ++++++++++--------- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_init_T_freeze.ipp | 17 ++++----- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index a9547d71b..3e01de18b 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -10,24 +10,19 @@ namespace libcloudphxx { enum class INP_t {mineral}; // types of ice nucleating particles, TODO: add more types - // Freezing temperature as defined in Shima et al., 2020 + // Inverse CDF for freezing temperature as defined in eq. 1 in Shima et al., 2020 template BOOST_GPU_ENABLED - quantity T_freeze( + quantity T_freeze_CDF_inv( const INP_t& INP_type, // type of ice nucleating particle const quantity rd3_insol, // radius cubed of ice nucleating (insoluble) particle - int rng_seed // seed for random number generator + const real_t rand // random number between [0, 1] ) { real_t A = real_t(4) * pi() * std::pow(rd3_insol/si::meters, 2/3); // surface area of the insoluble particle - std::mt19937 gen(rng_seed); // random number generator - std::uniform_real_distribution<> dis(0, 1); - real_t Y = dis(gen); // random number between [0, 1] - - // Sampling T_freez from its CDF using inverse transform sampling - if (INP_type == INP_t::mineral && A > std::numeric_limits::epsilon() && dis(gen) > 0.05) // 5% of mineral dust particles are inactive + if (INP_type == INP_t::mineral && A > std::numeric_limits::epsilon()) { - return (real_t(273.15) + (real_t(8.934) - std::log(- std::log(1-Y) / A) ) / real_t(0.517)) * si::kelvins; + return (real_t(273.15) + (real_t(8.934) - std::log(- std::log(1 - rand) / A) ) / real_t(0.517)) * si::kelvins; } else { @@ -37,16 +32,24 @@ namespace libcloudphxx template - struct T_freeze_functor + struct T_freeze_CDF_inv_functor { INP_t INP_type; - int rng_seed; - T_freeze_functor(INP_t INP_type, int rng_seed) - : INP_type(INP_type), rng_seed(rng_seed) {} + + T_freeze_CDF_inv_functor(INP_t INP_type) + : INP_type(INP_type) {} + BOOST_GPU_ENABLED - real_t operator()(const real_t &rd3_insol) const + real_t operator()(const thrust::tuple &tpl) const { - return ice_nucleation::T_freeze(INP_type, rd3_insol * si::meters, rng_seed).value(); + const real_t &rd3_insol = thrust::get<0>(tpl); // from rd3 vector + const real_t &rand = thrust::get<1>(tpl); // from rand vector + + return ice_nucleation::T_freeze_CDF_inv( + INP_type, + rd3_insol * si::meters, + rand + ).value(); } }; diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 9c3bfd87e..a36ec6e0b 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -471,7 +471,7 @@ namespace libcloudphxx void init_kappa(const real_t &); void init_ice(const real_t &); void init_rd3_insol(const real_t &); - void init_T_freeze(int rng_seed); + void init_T_freeze(); void init_incloud_time(); void init_count_num_sd_conc(const real_t & = 1); void init_count_num_const_multi(const common::unary_function &); diff --git a/src/impl/particles_impl_init_T_freeze.ipp b/src/impl/particles_impl_init_T_freeze.ipp index 865fe0156..b7b6eed9b 100644 --- a/src/impl/particles_impl_init_T_freeze.ipp +++ b/src/impl/particles_impl_init_T_freeze.ipp @@ -6,26 +6,27 @@ * @brief initialisation routine for super droplets */ -#include "../../include/libcloudph++/common/ice_nucleation.hpp" +#include namespace libcloudphxx { namespace lgrngn { template - void particles_t::impl::init_T_freeze( - const int rng_seed - ) - { + void particles_t::impl::init_T_freeze() { + using namespace libcloudphxx::common::ice_nucleation; const INP_t inp_type = INP_t::mineral; //TODO: INP type as argument, to support different partcle types + // random numbers between [0,1] for sampling + rand_u01(n_part_to_init); + thrust::transform( - rd3_insol.begin() + n_part_old, - rd3_insol.end(), + thrust::make_zip_iterator(thrust::make_tuple(rd3_insol.begin() + n_part_old, u01.begin() + n_part_old)), + thrust::make_zip_iterator(thrust::make_tuple(rd3_insol.end(), u01.end())), T_freeze.begin() + n_part_old, - T_freeze_functor(inp_type, rng_seed) + T_freeze_CDF_inv_functor(inp_type) ); } }; From 8ff2239282b9454337332017df56ba0d2509c595 Mon Sep 17 00:00:00 2001 From: Agnieszka Makulska Date: Mon, 14 Jul 2025 11:57:13 +0200 Subject: [PATCH 29/97] latent heat of freezing/sublimation --- include/libcloudph++/common/const_cp.hpp | 10 ++++++++++ include/libcloudph++/common/theta_dry.hpp | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/include/libcloudph++/common/const_cp.hpp b/include/libcloudph++/common/const_cp.hpp index aabcc51c6..397711891 100644 --- a/include/libcloudph++/common/const_cp.hpp +++ b/include/libcloudph++/common/const_cp.hpp @@ -23,6 +23,7 @@ namespace libcloudphxx libcloudphxx_const(si::temperature, T_tri, 273.16, si::kelvins) // temperature libcloudphxx_const(energy_over_mass, lv_tri, 2.5e6, si::joules / si::kilograms) // latent heat of evaporation libcloudphxx_const(energy_over_mass, ls_tri, 2.834e6, si::joules / si::kilograms) // latent heat of sublimation + libcloudphxx_const(energy_over_mass, lf_tri, 3.34e5, si::joules / si::kilograms) // latent heat of freezing // saturation vapour pressure for water assuming constant c_p_v and c_p_w // with constants taken at triple point @@ -93,6 +94,15 @@ namespace libcloudphxx ) { return ls_tri() + (c_pv() - c_pi()) * (T - T_tri()); } + + // latent heat of freezing for constant c_p + template + BOOST_GPU_ENABLED + quantity::type , real_t> l_f( + const quantity &T + ) { + return lf_tri() + (c_pw() - c_pi()) * (T - T_tri()); + } }; }; }; diff --git a/include/libcloudph++/common/theta_dry.hpp b/include/libcloudph++/common/theta_dry.hpp index db5da170a..32c5e6378 100644 --- a/include/libcloudph++/common/theta_dry.hpp +++ b/include/libcloudph++/common/theta_dry.hpp @@ -54,6 +54,7 @@ namespace libcloudphxx // rho/(1+r) R(r)*(1+r) } + // heat of condensation template BOOST_GPU_ENABLED quantity d_th_d_rv( @@ -63,6 +64,26 @@ namespace libcloudphxx return - th / T * const_cp::l_v(T) / c_pd(); } + // heat of sublimation + template + BOOST_GPU_ENABLED + quantity d_th_d_rv_subl( + const quantity &T, + const quantity &th // theta dry!!! + ) { + return - th / T * const_cp::l_s(T) / c_pd(); + } + + // heat of freezing + template + BOOST_GPU_ENABLED + quantity d_th_d_ri_freeze( + const quantity &T, + const quantity &th // theta dry!!! + ) { + return th / T * const_cp::l_f(T) / c_pd(); + } + template BOOST_GPU_ENABLED quantity std2dry( From aa22224ce999459b0200ea3def2c16fd9c4cc58f Mon Sep 17 00:00:00 2001 From: Agnieszka Makulska Date: Fri, 18 Jul 2025 11:49:35 +0200 Subject: [PATCH 30/97] dth freezing --- src/impl/particles_impl.ipp | 1 + src/impl/particles_impl_update_th_rv.ipp | 82 ++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index a36ec6e0b..3a7a3d2a8 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -578,6 +578,7 @@ namespace libcloudphxx template void cond_sstp_hlpr(const real_t &dt, const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, const RH_iter &rhi, const RHi_iter &rhii); void update_th_rv(thrust_device::vector &); + void update_th_freezing(thrust_device::vector &); void update_state(thrust_device::vector &, thrust_device::vector &); void update_pstate(thrust_device::vector &, thrust_device::vector &); void update_incloud_time(const real_t &dt); diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/particles_impl_update_th_rv.ipp index a3ea44ee3..00b079a92 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/particles_impl_update_th_rv.ipp @@ -29,6 +29,41 @@ namespace libcloudphxx return drv * common::theta_dry::d_th_d_rv(T, th) / si::kelvins; } }; + + template + struct dth_subl : thrust::unary_function&, real_t> + { + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) const + { + const quantity + drv = thrust::get<0>(tpl); + const quantity + T = thrust::get<1>(tpl) * si::kelvins; + const quantity + th = thrust::get<2>(tpl) * si::kelvins; + + return drv * common::theta_dry::d_th_d_rv_subl(T, th) / si::kelvins; + } + }; + + + template + struct dth_freezing : thrust::unary_function&, real_t> + { + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) const + { + const quantity + dri = thrust::get<0>(tpl); + const quantity + T = thrust::get<1>(tpl) * si::kelvins; + const quantity + th = thrust::get<2>(tpl) * si::kelvins; + + return dri * common::theta_dry::d_th_d_ri(T, th) / si::kelvins; + } + }; }; // update th and rv according to change in 3rd specific wet moments @@ -89,6 +124,53 @@ namespace libcloudphxx nancheck(th, "update_th_rv: th after update"); } + // update th for freezing + // particles have to be sorted + template + void particles_t::impl::update_th_freezing( + thrust_device::vector &dri // change in ice mixing ratio + ) + { + if(!sorted) throw std::runtime_error("libcloudph++: update_th_freezing called on an unsorted set"); + nancheck(dri, "update_th_freezing: input dri"); + + // multiplying specific 3rd moms diff by -rho_w*4/3*pi + thrust::transform( + dri.begin(), dri.end(), // input - 1st arg + thrust::make_constant_iterator( // input - 2nd arg + - common::moist_air::rho_w() / si::kilograms * si::cubic_metres + * real_t(4./3) * pi() + ), + dri.begin(), // output + thrust::multiplies() + ); + + // updating th + { + typedef thrust::zip_iterator::iterator, + typename thrust_device::vector::iterator, + typename thrust_device::vector::iterator + > > zip_it_t; + + // apply dth + thrust::transform( + th.begin(), th.end(), // input - 1st arg + thrust::make_transform_iterator( + zip_it_t(thrust::make_tuple( + dri.begin(), // + T.begin(), // dth = drv * d_th_d_rv(T, th) + th.begin() // + )), + detail::dth_freezing() + ), + th.begin(), // output + thrust::plus() + ); + } + nancheck(th, "update_th_freezing: th after update"); + } + // update particle-specific cell state // particles have to be sorted template From 118c97aa8f7b7d04a6a7553bf7aa61dc0285da3b Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Mon, 8 Sep 2025 16:09:13 +0200 Subject: [PATCH 31/97] 3rd moment for frozen droplets --- src/impl/particles_impl_ice_nucl.ipp | 49 +++++++++++++++++++++++- src/impl/particles_impl_update_th_rv.ipp | 2 +- src/particles_step.ipp | 2 +- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/impl/particles_impl_ice_nucl.ipp b/src/impl/particles_impl_ice_nucl.ipp index e46eba0d2..da0e98bb6 100644 --- a/src/impl/particles_impl_ice_nucl.ipp +++ b/src/impl/particles_impl_ice_nucl.ipp @@ -28,11 +28,27 @@ namespace libcloudphxx return false; }; }; + // Functor to compute r^3 only for ice + template + class compute_r3_if_ice + { + public: + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 2 elements: (r2, ice) + { + real_t r2 = thrust::get<0>(tpl); + int ice_flag = thrust::get<1>(tpl); + return ice_flag == 1 ? pow(r2, real_t(1.5)) : real_t(0); + } + }; + }; // Immersion freezing template void particles_t::impl::ice_nucl() { + + // Change liquid droplets to ice under the freezing condition thrust::replace_if(ice.begin(), ice.end(), // Replacing values of ice with 1 if immersion_freeze_cond is satisfied. thrust::make_zip_iterator( thrust::make_tuple( // Creating a zip iterator to access multiple vectors: @@ -44,8 +60,39 @@ namespace libcloudphxx detail::immersion_freeze_cond(), real_t(1) ); + + // Temporary vector for 3rd moment contribution of each droplet + thrust_device::vector mom3(count_n); + + // Compute r^3 only for newly frozen droplets + thrust::transform( + thrust::make_zip_iterator(thrust::make_tuple( // first input + rw2.begin(), // droplet radius squared + ice.begin() // ice flag + )), + thrust::make_zip_iterator(thrust::make_tuple( // last input + rw2.end(), + ice.end() + )), + mom3.begin(), // output + detail::compute_r3_if_ice() // functor for computing r3 of newly frozen droplets + ); + + // Reuse a temporary device vector for cell-wise 3rd moment + thrust_device::vector &dri(tmp_device_real_cell); + thrust::fill(dri.begin(), dri.end(), real_t(0)); // reset to 0 + + // add contributions to cell-wise 3rd moment + thrust::transform( + mom3.begin(), mom3.end(), // input + thrust::make_permutation_iterator(dri.begin(), count_ijk.begin()), // output per cell + thrust::make_permutation_iterator(dri.begin(), count_ijk.begin()), + thrust::plus() + ); + + // update th according to changes in ri + update_th_freezing(dri); } - //TODO: latent heat of freezing } } diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/particles_impl_update_th_rv.ipp index 00b079a92..5d44d678b 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/particles_impl_update_th_rv.ipp @@ -61,7 +61,7 @@ namespace libcloudphxx const quantity th = thrust::get<2>(tpl) * si::kelvins; - return dri * common::theta_dry::d_th_d_ri(T, th) / si::kelvins; + return dri * common::theta_dry::d_th_d_ri_freeze(T, th) / si::kelvins; } }; }; diff --git a/src/particles_step.ipp b/src/particles_step.ipp index 9103d9338..f5f2f9f49 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -195,7 +195,7 @@ namespace libcloudphxx if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); if (opts.ice_nucl) - pimpl->ice_nucl_sstp(); + pimpl->ice_nucl(); pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } // copy sstp_tmp_rv and th to rv and th From 4394428201e70fbc8248c94e5cd7d99aaff3d49e Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Mon, 8 Sep 2025 16:18:02 +0200 Subject: [PATCH 32/97] select newly frozen droplets --- src/impl/particles_impl_ice_nucl.ipp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/impl/particles_impl_ice_nucl.ipp b/src/impl/particles_impl_ice_nucl.ipp index da0e98bb6..6829ce42d 100644 --- a/src/impl/particles_impl_ice_nucl.ipp +++ b/src/impl/particles_impl_ice_nucl.ipp @@ -28,17 +28,18 @@ namespace libcloudphxx return false; }; }; - // Functor to compute r^3 only for ice + // Functor to compute r^3 only for newly frozen particles template - class compute_r3_if_ice + class compute_r3_if_frozen { public: BOOST_GPU_ENABLED - real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 2 elements: (r2, ice) + real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 3 elements: (r2, ice, ice_old) { real_t r2 = thrust::get<0>(tpl); int ice_flag = thrust::get<1>(tpl); - return ice_flag == 1 ? pow(r2, real_t(1.5)) : real_t(0); + int ice_flag_old = thrust::get<2>(tpl); + return (ice_flag==1 && ice_flag_old==0) ? pow(r2, real_t(1.5)) : real_t(0); } }; @@ -48,6 +49,9 @@ namespace libcloudphxx template void particles_t::impl::ice_nucl() { + // Copy current ice flags + thrust_device::vector ice_old = ice; + // Change liquid droplets to ice under the freezing condition thrust::replace_if(ice.begin(), ice.end(), // Replacing values of ice with 1 if immersion_freeze_cond is satisfied. thrust::make_zip_iterator( @@ -67,15 +71,17 @@ namespace libcloudphxx // Compute r^3 only for newly frozen droplets thrust::transform( thrust::make_zip_iterator(thrust::make_tuple( // first input - rw2.begin(), // droplet radius squared - ice.begin() // ice flag + rw2.begin(), // droplet radius squared + ice.begin(), // ice flag + ice_old.begin() // old ice flag )), thrust::make_zip_iterator(thrust::make_tuple( // last input rw2.end(), - ice.end() + ice.end(), + ice_old.end() )), mom3.begin(), // output - detail::compute_r3_if_ice() // functor for computing r3 of newly frozen droplets + detail::compute_r3_if_frozen() // functor for computing r3 of newly frozen droplets ); // Reuse a temporary device vector for cell-wise 3rd moment From 5d277650ef021055a45d44184bce21c83ccbfed5 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 9 Sep 2025 16:13:26 +0200 Subject: [PATCH 33/97] fixing update_th_freezing --- src/impl/particles_impl_update_th_rv.ipp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/particles_impl_update_th_rv.ipp index 5d44d678b..a5a2fd86e 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/particles_impl_update_th_rv.ipp @@ -128,20 +128,20 @@ namespace libcloudphxx // particles have to be sorted template void particles_t::impl::update_th_freezing( - thrust_device::vector &dri // change in ice mixing ratio + thrust_device::vector &drw // change in 3rd mom of liquid ) { if(!sorted) throw std::runtime_error("libcloudph++: update_th_freezing called on an unsorted set"); - nancheck(dri, "update_th_freezing: input dri"); + nancheck(drw, "update_th_freezing: input drw"); // multiplying specific 3rd moms diff by -rho_w*4/3*pi thrust::transform( - dri.begin(), dri.end(), // input - 1st arg + drw.begin(), drw.end(), // input - 1st arg thrust::make_constant_iterator( // input - 2nd arg - common::moist_air::rho_w() / si::kilograms * si::cubic_metres * real_t(4./3) * pi() ), - dri.begin(), // output + drw.begin(), // output thrust::multiplies() ); @@ -158,9 +158,9 @@ namespace libcloudphxx th.begin(), th.end(), // input - 1st arg thrust::make_transform_iterator( zip_it_t(thrust::make_tuple( - dri.begin(), // - T.begin(), // dth = drv * d_th_d_rv(T, th) - th.begin() // + drw.begin(), + T.begin(), + th.begin() )), detail::dth_freezing() ), From b5158a9bc48bbbedb9cf4fba1b18c7ba2397b7fb Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 9 Sep 2025 16:57:17 +0200 Subject: [PATCH 34/97] fixing merge conflict --- src/impl/particles_impl.ipp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index d7bcce812..873902c87 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -23,11 +23,11 @@ namespace libcloudphxx { namespace lgrngn { - namespace detail - { - enum { invalid = -1 }; - - }; + // namespace detail + // { + // enum { invalid = -1 }; + // + // }; // pimpl stuff template @@ -148,7 +148,8 @@ namespace libcloudphxx thrust_device::vector T, // temperature [K] p, // pressure [Pa] - RH, // relative humisity + RH, // relative humisity + RH_i, // relative humisity w.r.t. ice eta,// dynamic viscosity diss_rate; // turbulent kinetic energy dissipation rate @@ -274,9 +275,6 @@ namespace libcloudphxx // ids of sds to be copied with distmem thrust_device::vector &lft_id, &rgt_id; - // real_t vectors copied in distributed memory case - std::set*> distmem_real_vctrs; - // vectors copied between distributed memories (MPI, multi_CUDA), these are SD attributes std::set*, real_t>> distmem_real_vctrs; // pair of vector and its initial value std::set*> distmem_n_vctrs; @@ -461,9 +459,9 @@ namespace libcloudphxx if(opts_init.ice_switch) { - distmem_real_vctrs.insert(&ice); - distmem_real_vctrs.insert(&rd3_insol); - distmem_real_vctrs.insert(&T_freeze); + distmem_real_vctrs.insert({&ice, detail::no_initial_value}); + distmem_real_vctrs.insert({&rd3_insol, detail::no_initial_value}); + distmem_real_vctrs.insert({&T_freeze, detail::no_initial_value}); } } From a9fc994398ed8e7047c862dfaa26e73981d0fdd9 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 10 Sep 2025 14:08:10 +0200 Subject: [PATCH 35/97] output name fix --- include/libcloudph++/common/output.hpp | 2 +- include/libcloudph++/lgrngn/opts_init.hpp | 5 +++++ src/impl/particles_impl.ipp | 5 ----- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/libcloudph++/common/output.hpp b/include/libcloudph++/common/output.hpp index 22d3f6581..f1e4b6e23 100644 --- a/include/libcloudph++/common/output.hpp +++ b/include/libcloudph++/common/output.hpp @@ -37,7 +37,7 @@ namespace libcloudphxx {outdry_vol, "dry_volume"}, {outprtcl_num, "particle_number"}, {outice_vol, "ice_volume"}, - {outliq_num, "water_number"}, + {outliq_num, "liquid_number"}, {outice_num, "ice_number"} }; diff --git a/include/libcloudph++/lgrngn/opts_init.hpp b/include/libcloudph++/lgrngn/opts_init.hpp index 442e7e87e..ad436b944 100644 --- a/include/libcloudph++/lgrngn/opts_init.hpp +++ b/include/libcloudph++/lgrngn/opts_init.hpp @@ -146,10 +146,15 @@ namespace libcloudphxx // type of CCN source src_t src_type; + // number of SDs created from src_dry_distros per cell per source iteration + //unsigned long long src_sd_conc; + // box in which aerosol from source will be created // will be rounded to cell number - cells are supposed to be uniform real_t src_x0, src_y0, src_z0, src_x1, src_y1, src_z1; + // timestep interval at which source will be applied + //int supstp_src; // --- aerosol relaxation stuff --- // initial dry sizes of aerosol diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 873902c87..2dcd2f892 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -23,11 +23,6 @@ namespace libcloudphxx { namespace lgrngn { - // namespace detail - // { - // enum { invalid = -1 }; - // - // }; // pimpl stuff template From 6aad57c39de7db03bf367e88172e9d9847aad584 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 10 Sep 2025 16:22:23 +0200 Subject: [PATCH 36/97] heat of freezing --- src/impl/particles_impl_ice_nucl.ipp | 45 +++++++++++++----------- src/impl/particles_impl_update_th_rv.ipp | 18 +++++----- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/impl/particles_impl_ice_nucl.ipp b/src/impl/particles_impl_ice_nucl.ipp index 6829ce42d..eca8539b5 100644 --- a/src/impl/particles_impl_ice_nucl.ipp +++ b/src/impl/particles_impl_ice_nucl.ipp @@ -28,18 +28,18 @@ namespace libcloudphxx return false; }; }; - // Functor to compute r^3 only for newly frozen particles + // Functor to return rw2 of newly frozen droplets, otherwise 0 template - class compute_r3_if_frozen + class rw2_if_newfrozen { public: BOOST_GPU_ENABLED - real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 3 elements: (r2, ice, ice_old) + real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 3 elements: (rw2, ice, ice_old) { - real_t r2 = thrust::get<0>(tpl); + real_t rw2 = thrust::get<0>(tpl); int ice_flag = thrust::get<1>(tpl); int ice_flag_old = thrust::get<2>(tpl); - return (ice_flag==1 && ice_flag_old==0) ? pow(r2, real_t(1.5)) : real_t(0); + return (ice_flag==1 && ice_flag_old==0) ? rw2 : real_t(0); } }; @@ -49,6 +49,8 @@ namespace libcloudphxx template void particles_t::impl::ice_nucl() { + hskpng_sort(); + // Copy current ice flags thrust_device::vector ice_old = ice; @@ -65,10 +67,9 @@ namespace libcloudphxx real_t(1) ); - // Temporary vector for 3rd moment contribution of each droplet - thrust_device::vector mom3(count_n); + // Temporary vector for rw2 of newly frozen droplets + thrust_device::vector rw2_frozen(count_n); - // Compute r^3 only for newly frozen droplets thrust::transform( thrust::make_zip_iterator(thrust::make_tuple( // first input rw2.begin(), // droplet radius squared @@ -80,24 +81,28 @@ namespace libcloudphxx ice.end(), ice_old.end() )), - mom3.begin(), // output - detail::compute_r3_if_frozen() // functor for computing r3 of newly frozen droplets + rw2_frozen.begin(), // output + detail::rw2_if_newfrozen() // functor for r2 of newly frozen droplets ); - // Reuse a temporary device vector for cell-wise 3rd moment - thrust_device::vector &dri(tmp_device_real_cell); - thrust::fill(dri.begin(), dri.end(), real_t(0)); // reset to 0 + // Compute per-cell 3rd moment of newly frozen droplets (sum of n*r^3). It is stored in count_mom + moms_all(); + moms_calc(rw2_frozen.begin(), count_n, real_t(1.5), true); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of newly frozen droplets"); + + // Copy to frozen_mom3 array + thrust_device::vector &frozen_mom3(tmp_device_real_cell); + if (count_n != n_cell) + thrust::fill(frozen_mom3.begin(), frozen_mom3.end(), real_t(0.)); - // add contributions to cell-wise 3rd moment thrust::transform( - mom3.begin(), mom3.end(), // input - thrust::make_permutation_iterator(dri.begin(), count_ijk.begin()), // output per cell - thrust::make_permutation_iterator(dri.begin(), count_ijk.begin()), - thrust::plus() + count_mom.begin(), count_mom.begin() + count_n, + thrust::make_permutation_iterator(frozen_mom3.begin(), count_ijk.begin()), + thrust::identity() // just copy values ); - // update th according to changes in ri - update_th_freezing(dri); + // update th according to the frozen volume per cell + update_th_freezing(frozen_mom3); } } diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/particles_impl_update_th_rv.ipp index e45c635f0..5b2fa94fa 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/particles_impl_update_th_rv.ipp @@ -31,7 +31,7 @@ namespace libcloudphxx }; template - struct dth_subl : thrust::unary_function&, real_t> + struct dth_subl //: thrust::unary_function&, real_t> { BOOST_GPU_ENABLED real_t operator()(const thrust::tuple &tpl) const @@ -49,7 +49,7 @@ namespace libcloudphxx template - struct dth_freezing : thrust::unary_function&, real_t> + struct dth_freezing //: thrust::unary_function&, real_t> { BOOST_GPU_ENABLED real_t operator()(const thrust::tuple &tpl) const @@ -128,20 +128,20 @@ namespace libcloudphxx // particles have to be sorted template void particles_t::impl::update_th_freezing( - thrust_device::vector &drw // change in 3rd mom of liquid + thrust_device::vector &frozen_mom3 // specific 3rd moment of newly frozen droplets per cell ) { if(!sorted) throw std::runtime_error("libcloudph++: update_th_freezing called on an unsorted set"); - nancheck(drw, "update_th_freezing: input drw"); + nancheck(frozen_mom3, "update_th_freezing: input frozen_mom3"); - // multiplying specific 3rd moms diff by -rho_w*4/3*pi + // Calculating the mixing ratio of frozen water per cell (multiplying specific 3rd mom by rho_w*4/3*pi) thrust::transform( - drw.begin(), drw.end(), // input - 1st arg + frozen_mom3.begin(), frozen_mom3.end(), // input - 1st arg thrust::make_constant_iterator( // input - 2nd arg - - common::moist_air::rho_w() / si::kilograms * si::cubic_metres + common::moist_air::rho_w() / si::kilograms * si::cubic_metres * real_t(4./3) * pi() ), - drw.begin(), // output + frozen_mom3.begin(), // output thrust::multiplies() ); @@ -158,7 +158,7 @@ namespace libcloudphxx th.begin(), th.end(), // input - 1st arg thrust::make_transform_iterator( zip_it_t(thrust::make_tuple( - drw.begin(), + frozen_mom3.begin(), T.begin(), th.begin() )), From 2e9de600af1a95bca96851a9c24f739f1a97b75c Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 11 Sep 2025 11:55:39 +0200 Subject: [PATCH 37/97] new attributes --- src/impl/particles_impl.ipp | 11 ++++++++++- src/impl/particles_impl_fill_outbuf.ipp | 9 ++++++--- src/impl/particles_impl_reserve_hskpng_npart.ipp | 3 +++ src/particles.tpp | 3 +++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 2dcd2f892..d7358ed33 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -87,7 +87,10 @@ namespace libcloudphxx incloud_time, // time this SD has been within a cloud ice, // 0 - water 1 - ice; bool would suffice, but we are lazy rd3_insol, // dry radii of insoluble aerosol cubed - T_freeze; // freezing temperature + T_freeze, // freezing temperature + a_ice, // equatorial radius of ice + c_ice, // polar radius of ice + rho_i; // ice apparent density // dry radii distribution characteristics real_t log_rd_min, // logarithm of the lower bound of the distr @@ -457,6 +460,9 @@ namespace libcloudphxx distmem_real_vctrs.insert({&ice, detail::no_initial_value}); distmem_real_vctrs.insert({&rd3_insol, detail::no_initial_value}); distmem_real_vctrs.insert({&T_freeze, detail::no_initial_value}); + distmem_real_vctrs.insert({&a_ice, detail::no_initial_value}); + distmem_real_vctrs.insert({&c_ice, detail::no_initial_value}); + distmem_real_vctrs.insert({&rho_i, detail::no_initial_value}); } } @@ -501,6 +507,9 @@ namespace libcloudphxx void init_ice(const real_t &); void init_rd3_insol(const real_t &); void init_T_freeze(); + void init_a_ice(); + void init_c_ice(); + void init_rho_i(); void init_incloud_time(); void init_count_num_sd_conc(const real_t & = 1); void init_count_num_const_multi(const common::unary_function &); diff --git a/src/impl/particles_impl_fill_outbuf.ipp b/src/impl/particles_impl_fill_outbuf.ipp index 500f1300d..c6743a025 100644 --- a/src/impl/particles_impl_fill_outbuf.ipp +++ b/src/impl/particles_impl_fill_outbuf.ipp @@ -40,7 +40,7 @@ namespace libcloudphxx template std::vector particles_t::impl::fill_attr_outbuf(const std::string &name) { - const std::set attr_names = {"rw2", "rd3", "kappa", "rd3_insol", "T_freeze", "x", "y", "z"}; // TODO implement "n" - it is n_t type and others are real_t + const std::set attr_names = {"rw2", "rd3", "kappa", "rd3_insol", "T_freeze", "a_ice", "c_ice", "rho_i", "x", "y", "z"}; // TODO implement "n" - it is n_t type and others are real_t if (std::find(std::begin(attr_names), std::end(attr_names), name) == std::end(attr_names)) throw std::runtime_error("Unknown attribute name passed to get_attr."); @@ -50,8 +50,11 @@ namespace libcloudphxx name == "kappa" ? kpa : name == "rd3_insol" ? rd3_insol : name == "T_freeze" ? T_freeze : - name == "x" ? x : - name == "y" ? y : + name == "a_ice" ? a_ice : + name == "c_ice" ? c_ice : + name == "rho_i" ? rho_i : + name == "x" ? x : + name == "y" ? y : z); // NOTE: for host backends (i.e. undefined __NVCC__) we could return the vector directly, without a copy; diff --git a/src/impl/particles_impl_reserve_hskpng_npart.ipp b/src/impl/particles_impl_reserve_hskpng_npart.ipp index 85a02f4b0..60e4bc1e7 100644 --- a/src/impl/particles_impl_reserve_hskpng_npart.ipp +++ b/src/impl/particles_impl_reserve_hskpng_npart.ipp @@ -41,6 +41,9 @@ namespace libcloudphxx ice.reserve(opts_init.n_sd_max); rd3_insol.reserve(opts_init.n_sd_max); T_freeze.reserve(opts_init.n_sd_max); + a_ice.reserve(opts_init.n_sd_max); + c_ice.reserve(opts_init.n_sd_max); + rho_i.reserve(opts_init.n_sd_max); } vt.reserve(opts_init.n_sd_max); diff --git a/src/particles.tpp b/src/particles.tpp index 086cd0c41..2fe6ebaf8 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -66,6 +66,9 @@ #include "impl/particles_impl_init_ice.ipp" #include "impl/particles_impl_init_rd3_insol.ipp" #include "impl/particles_impl_init_T_freeze.ipp" +#include "impl/particles_impl_init_a_c_ice.ipp" +#include "impl/particles_impl_init_rho_ice.ipp" +#include "impl/particles_impl_init_T_freeze.ipp" #include "impl/particles_impl_init_incloud_time.ipp" #include "impl/particles_impl_init_n.ipp" #include "impl/particles_impl_init_wet.ipp" From af5e74f7ce48e348c5ab7ea7c34c9282d5caee7c Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 11 Sep 2025 14:03:54 +0200 Subject: [PATCH 38/97] melting --- src/impl/particles_impl_melt.ipp | 102 +++++++++++++++++++++++++++++++ src/particles.tpp | 2 +- 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 src/impl/particles_impl_melt.ipp diff --git a/src/impl/particles_impl_melt.ipp b/src/impl/particles_impl_melt.ipp new file mode 100644 index 000000000..dbfbe41f9 --- /dev/null +++ b/src/impl/particles_impl_melt.ipp @@ -0,0 +1,102 @@ +// vim:filetype=cpp +/** @file + * @copyright University of Warsaw + * @section LICENSE + * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) + */ + +#include +#include + +namespace libcloudphxx +{ + namespace lgrngn + { + namespace detail + { + // The condition for melting + template + class melting_cond + { + public: + BOOST_GPU_ENABLED + bool operator()(const real_t &T) const + { + return (T > real_t(273.15)); + }; + }; + + // Functor to return rw2 of newly melted particles, otherwise 0 + template + class rw2_if_newmelted + { + public: + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 3 elements: (rw2, ice, ice_old) + { + real_t rw2 = thrust::get<0>(tpl); + int ice_flag = thrust::get<1>(tpl); + int ice_flag_old = thrust::get<2>(tpl); + return (ice_flag==0 && ice_flag_old==1) ? rw2 : real_t(0); + } + }; + + }; + + // Melting + template + void particles_t::impl::melt() { + + hskpng_sort(); + + // Copy current ice flags + thrust_device::vector ice_old = ice; + + // Change ice to liquid under the melting condition + thrust::replace_if(ice.begin(), ice.end(), // Replacing values of ice with 0 if melting_cond is satisfied. + thrust::make_permutation_iterator(T.begin(), ijk.begin()), // ambient temperature for each particle + detail::melting_cond(), + real_t(0) + ); + + // Temporary vector for rw2 of newly melted particles + thrust_device::vector rw2_melted(count_n); + + thrust::transform( + thrust::make_zip_iterator(thrust::make_tuple( // first input + rw2.begin(), // droplet radius squared + ice.begin(), // ice flag + ice_old.begin() // old ice flag + )), + thrust::make_zip_iterator(thrust::make_tuple( // last input + rw2.end(), + ice.end(), + ice_old.end() + )), + rw2_melted.begin(), // output + detail::rw2_if_newmelted() // functor for r2 of newly melted particles + ); + + // Compute per-cell 3rd moment of newly melted particles (sum of n*r^3). It is stored in count_mom + moms_all(); + moms_calc(rw2_melted.begin(), count_n, real_t(1.5), true); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of newly melted particles"); + + // Copy to melted_mom3 array + thrust_device::vector &melted_mom3(tmp_device_real_cell); + if (count_n != n_cell) + thrust::fill(melted_mom3.begin(), melted_mom3.end(), real_t(0.)); + + // multiplying by -1 (dth < 0 for melting) + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, + thrust::make_permutation_iterator(melted_mom3.begin(), count_ijk.begin()), + thrust::negate() + ); + + // update th according to the melted volume per cell + update_th_freezing(melted_mom3); + } + + } +} diff --git a/src/particles.tpp b/src/particles.tpp index 2fe6ebaf8..12ff9ae65 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -68,7 +68,6 @@ #include "impl/particles_impl_init_T_freeze.ipp" #include "impl/particles_impl_init_a_c_ice.ipp" #include "impl/particles_impl_init_rho_ice.ipp" -#include "impl/particles_impl_init_T_freeze.ipp" #include "impl/particles_impl_init_incloud_time.ipp" #include "impl/particles_impl_init_n.ipp" #include "impl/particles_impl_init_wet.ipp" @@ -110,6 +109,7 @@ #include "impl/particles_impl_cond.ipp" #include "impl/particles_impl_cond_sstp.ipp" #include "impl/particles_impl_ice_nucl.ipp" +#include "impl/particles_impl_melt.ipp" #include "impl/particles_impl_sedi.ipp" #include "impl/particles_impl_subs.ipp" #include "impl/particles_impl_coal.ipp" From 1ee1311388e5a37329b35f5fb3d2c265600971bb Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 11 Sep 2025 14:27:52 +0200 Subject: [PATCH 39/97] freezing changes the radius --- src/impl/particles_impl.ipp | 1 + src/impl/particles_impl_ice_nucl.ipp | 34 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index d7358ed33..73b1c5839 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -609,6 +609,7 @@ namespace libcloudphxx void subs(const real_t &dt); void ice_nucl(); + void melt(); void cond_dm3_helper(); void cond(const real_t &dt, const real_t &RH_max, const bool turb_cond); diff --git a/src/impl/particles_impl_ice_nucl.ipp b/src/impl/particles_impl_ice_nucl.ipp index eca8539b5..02709031f 100644 --- a/src/impl/particles_impl_ice_nucl.ipp +++ b/src/impl/particles_impl_ice_nucl.ipp @@ -43,6 +43,22 @@ namespace libcloudphxx } }; + // Functor to update r2 of newly frozen droplets, because ice has different density + template + class rw2_update + { + public: + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 3 elements: (rw2, ice, ice_old) + { + real_t rw2 = thrust::get<0>(tpl); + int ice_flag = thrust::get<1>(tpl); + int ice_flag_old = thrust::get<2>(tpl); + return (ice_flag==1 && ice_flag_old==0) ? + rw2 * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(2/3)) : rw2; + } + }; + }; // Immersion freezing @@ -103,6 +119,24 @@ namespace libcloudphxx // update th according to the frozen volume per cell update_th_freezing(frozen_mom3); + + + // update the radius of newly frozen particles - taking into account different density of ice + thrust::transform( + thrust::make_zip_iterator(thrust::make_tuple( // first input + rw2.begin(), // droplet radius squared + ice.begin(), // ice flag + ice_old.begin() // old ice flag + )), + thrust::make_zip_iterator(thrust::make_tuple( // last input + rw2.end(), + ice.end(), + ice_old.end() + )), + rw2.begin(), // output + detail::rw2_update() // functor for updating rw2 + ); + } } From 1a8a244a0ca26ba60b7ae79310d00fd7b372117c Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 12 Sep 2025 16:07:28 +0200 Subject: [PATCH 40/97] heat of sublimation --- src/impl/particles_impl.ipp | 2 + src/impl/particles_impl_cond.ipp | 120 ++++++++++++++++++++--- src/impl/particles_impl_update_th_rv.ipp | 61 +++++++++++- 3 files changed, 169 insertions(+), 14 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 73b1c5839..325191fca 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -222,6 +222,7 @@ namespace libcloudphxx tmp_device_real_cell, tmp_device_real_cell1, tmp_device_real_cell2, + tmp_device_real_cell3, &u01; // uniform random numbers between 0 and 1 // TODO: use the tmp array as rand argument? thrust_device::vector tmp_device_n_part, @@ -617,6 +618,7 @@ namespace libcloudphxx template void cond_sstp_hlpr(const real_t &dt, const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, const RH_iter &rhi, const RHi_iter &rhii); void update_th_rv(thrust_device::vector &); + void update_th_rv_subl(thrust_device::vector &); void update_th_freezing(thrust_device::vector &); void update_state(thrust_device::vector &, thrust_device::vector &); void update_pstate(thrust_device::vector &, thrust_device::vector &); diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index 60801f54a..280b2e4dd 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -9,6 +9,40 @@ namespace libcloudphxx { namespace lgrngn { + + namespace detail + { + // Functor to select moment of only liquid droplets + template + class select_liq + { + public: + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 2 elements: (count_mom, ice) + { + real_t count_mom = thrust::get<0>(tpl); + int ice_flag = thrust::get<1>(tpl); + return (ice_flag==0) ? count_mom : real_t(0); + } + }; + + // Functor to select moment of only ice particles + template + class select_ice + { + public: + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 2 elements: (count_mom, ice) + { + real_t count_mom = thrust::get<0>(tpl); + int ice_flag = thrust::get<1>(tpl); + return (ice_flag==1) ? count_mom : real_t(0); + } + }; + + } + + template void particles_t::impl::cond( const real_t &dt, @@ -23,22 +57,44 @@ namespace libcloudphxx // --- calc liquid water content before cond --- hskpng_sort(); - thrust_device::vector &drv(tmp_device_real_cell); + //thrust_device::vector &drv(tmp_device_real_cell); + thrust_device::vector &drv_liq(tmp_device_real_cell); + thrust_device::vector &drv_ice(tmp_device_real_cell3); - // calculating the 3rd wet moment before condensation + // Compute per-cell 3rd wet moment before condensation (sum of n*r^3). It is stored in count_mom moms_all(); moms_calc(rw2.begin(), real_t(3./2.)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) before condensation"); // permute-copying the result to -dm_3 // fill with 0s if not all cells will be updated in the following transform - if(count_n!=n_cell) thrust::fill(drv.begin(), drv.end(), real_t(0.)); + if(count_n!=n_cell) { + thrust::fill(drv_liq.begin(), drv_liq.end(), real_t(0.)); + thrust::fill(drv_ice.begin(), drv_ice.end(), real_t(0.)); + } + + // fill drv_liq with liquid moment before conde thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - thrust::make_permutation_iterator(drv.begin(), count_ijk.begin()), // output - thrust::negate() + thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin(), ice.begin())), + thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin() + count_n, ice.begin() + count_n)), // input - 1st arg + thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // output + detail::select_liq() ); + // fill drv_ice with ice moment before cond + thrust::transform( + thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin(), ice.begin())), + thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin() + count_n, ice.begin() + count_n)), // input - 1st arg + thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // output + detail::select_ice() + ); + + // thrust::transform( + // count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + // thrust::make_permutation_iterator(drv.begin(), count_ijk.begin()), // output + // thrust::negate() + // ); + auto hlpr_zip_iter = thrust::make_zip_iterator(thrust::make_tuple( thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), thrust::make_permutation_iterator(rv.begin(), ijk.begin()), @@ -97,20 +153,58 @@ namespace libcloudphxx ); nancheck(rw2, "rw2 after condensation (no sub-steps"); - // calculating the 3rd wet moment after condensation + // Compute per-cell 3rd wet moment after condensation. It is stored in count_mom moms_calc(rw2.begin(), real_t(3./2.)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) after condensation"); - // adding the third moment after condensation to dm_3 + // fill count_mom with liquid only + thrust::transform( + thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin(), ice.begin())), + thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin() + count_n, ice.begin() + count_n)), // input - 1st arg + thrust::make_permutation_iterator(count_mom.begin(), count_ijk.begin()), // output + detail::select_liq() + ); + + // calculating the change in liquid moment thrust::transform( count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - thrust::make_permutation_iterator(drv.begin(), count_ijk.begin()), // input - 2nd arg - thrust::make_permutation_iterator(drv.begin(), count_ijk.begin()), // output - thrust::plus() + thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // input - 2nd arg + thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // output + thrust::minus() ); - // update th and rv according to changes in third specific wet moment - update_th_rv(drv); + + // Compute again the per-cell 3rd wet moment after condensation. It is stored in count_mom + moms_calc(rw2.begin(), real_t(3./2.)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) after condensation"); + + // fill count_mom_ with ice only + thrust::transform( + thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin(), ice.begin())), + thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin() + count_n, ice.begin() + count_n)), // input - 1st arg + thrust::make_permutation_iterator(count_mom.begin(), count_ijk.begin()), // output + detail::select_ice() + ); + + // calculating the change in ice moment + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // input - 2nd arg + thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // output + thrust::minus() + ); + + // // adding the third moment after condensation to dm_3 + // thrust::transform( + // count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + // thrust::make_permutation_iterator(drv.begin(), count_ijk.begin()), // input - 2nd arg + // thrust::make_permutation_iterator(drv.begin(), count_ijk.begin()), // output + // thrust::plus() + // ); + + // update th and rv according to changes in third specific wet moments + update_th_rv(drv_liq); + update_th_rv_subl(drv_ice); } }; }; diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/particles_impl_update_th_rv.ipp index 5b2fa94fa..67fecf743 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/particles_impl_update_th_rv.ipp @@ -66,7 +66,7 @@ namespace libcloudphxx }; }; - // update th and rv according to change in 3rd specific wet moments + // update th and rv after condensation according to change in 3rd specific wet moments // particles have to be sorted template void particles_t::impl::update_th_rv( @@ -124,6 +124,65 @@ namespace libcloudphxx nancheck(th, "update_th_rv: th after update"); } + + // update th and rv after sublimation according to change in 3rd specific wet moments + // particles have to be sorted + template + void particles_t::impl::update_th_rv_subl( + thrust_device::vector &drv // change in water vapor mixing ratio + ) + { + if(!sorted) throw std::runtime_error("libcloudph++: update_th_rv called on an unsorted set"); + nancheck(drv, "update_th_rv: input drv"); + + // multiplying specific 3rd moms diff by -rho_w*4/3*pi + thrust::transform( + drv.begin(), drv.end(), // input - 1st arg + thrust::make_constant_iterator( // input - 2nd arg + - common::moist_air::rho_i() / si::kilograms * si::cubic_metres + * real_t(4./3) * pi() + ), + drv.begin(), // output + thrust::multiplies() + ); + + // updating rv + assert(*thrust::min_element(rv.begin(), rv.end()) >= 0); + thrust::transform( + rv.begin(), rv.end(), // input - 1st arg + drv.begin(), // input - 2nd arg + rv.begin(), // output + thrust::plus() + ); + assert(*thrust::min_element(rv.begin(), rv.end()) >= 0); + nancheck(rv, "update_th_rv: rv after update"); + + // updating th + { + typedef thrust::zip_iterator::iterator, + typename thrust_device::vector::iterator, + typename thrust_device::vector::iterator + > > zip_it_t; + + // apply dth + thrust::transform( + th.begin(), th.end(), // input - 1st arg + thrust::make_transform_iterator( + zip_it_t(thrust::make_tuple( + drv.begin(), // + T.begin(), // dth = drv * d_th_d_rv(T, th) + th.begin() // + )), + detail::dth_subl() + ), + th.begin(), // output + thrust::plus() + ); + } + nancheck(th, "update_th_rv: th after update"); + } + // update th for freezing // particles have to be sorted template From 471999b4b3303f0ab98a7f10a18adaa207f1a9f5 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Mon, 15 Sep 2025 16:51:18 +0200 Subject: [PATCH 41/97] initialize a,c,rho as zero --- src/impl/particles_impl.ipp | 4 +--- src/impl/particles_impl_init_a_c_rho_ice.ipp | 22 ++++++++++++++++++++ src/particles.tpp | 3 +-- 3 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 src/impl/particles_impl_init_a_c_rho_ice.ipp diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 325191fca..fe1c1f085 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -508,9 +508,7 @@ namespace libcloudphxx void init_ice(const real_t &); void init_rd3_insol(const real_t &); void init_T_freeze(); - void init_a_ice(); - void init_c_ice(); - void init_rho_i(); + void init_a_c_rho_ice(); void init_incloud_time(); void init_count_num_sd_conc(const real_t & = 1); void init_count_num_const_multi(const common::unary_function &); diff --git a/src/impl/particles_impl_init_a_c_rho_ice.ipp b/src/impl/particles_impl_init_a_c_rho_ice.ipp new file mode 100644 index 000000000..b4c027931 --- /dev/null +++ b/src/impl/particles_impl_init_a_c_rho_ice.ipp @@ -0,0 +1,22 @@ +// vim:filetype=cpp +/** @file + * @copyright University of Warsaw + * @section LICENSE + * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) + * @brief initialisation routine for super droplets + */ + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::init_a_c_rho_ice() + { + // filling a and c with zeros initially + thrust::fill(a_ice.begin() + n_part_old, a_ice.end(), real_t(0)); + thrust::fill(c_ice.begin() + n_part_old, c_ice.end(), real_t(0)); + thrust::fill(rho_i.begin() + n_part_old, rho_i.end(), real_t(0)); + } + }; +}; \ No newline at end of file diff --git a/src/particles.tpp b/src/particles.tpp index 12ff9ae65..dc2e9ee27 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -66,8 +66,7 @@ #include "impl/particles_impl_init_ice.ipp" #include "impl/particles_impl_init_rd3_insol.ipp" #include "impl/particles_impl_init_T_freeze.ipp" -#include "impl/particles_impl_init_a_c_ice.ipp" -#include "impl/particles_impl_init_rho_ice.ipp" +#include "impl/particles_impl_init_a_c_rho_ice.ipp" #include "impl/particles_impl_init_incloud_time.ipp" #include "impl/particles_impl_init_n.ipp" #include "impl/particles_impl_init_wet.ipp" From fd9c2062ca153cb14d36bb15f5dc6baa598c3504 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 16 Sep 2025 11:53:48 +0200 Subject: [PATCH 42/97] fixing ice_nucl --- include/libcloudph++/common/theta_dry.hpp | 4 +- src/impl/particles_impl.ipp | 6 + src/impl/particles_impl_ice_nucl.ipp | 153 ++++++++++------------ src/impl/particles_impl_moms.ipp | 44 +++++++ src/impl/particles_impl_update_th_rv.ipp | 18 +-- src/particles_diag.ipp | 4 +- 6 files changed, 134 insertions(+), 95 deletions(-) diff --git a/include/libcloudph++/common/theta_dry.hpp b/include/libcloudph++/common/theta_dry.hpp index 32c5e6378..c704843fa 100644 --- a/include/libcloudph++/common/theta_dry.hpp +++ b/include/libcloudph++/common/theta_dry.hpp @@ -77,11 +77,11 @@ namespace libcloudphxx // heat of freezing template BOOST_GPU_ENABLED - quantity d_th_d_ri_freeze( + quantity d_th_d_rw_freeze( const quantity &T, const quantity &th // theta dry!!! ) { - return th / T * const_cp::l_f(T) / c_pd(); + return - th / T * const_cp::l_f(T) / c_pd(); } template diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index fe1c1f085..22a9e0c02 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -562,6 +562,12 @@ namespace libcloudphxx void moms_ge0( const typename thrust_device::vector::iterator &vec_bgn ); + void moms_gt0( + const typename thrust_device::vector::iterator &vec_bgn + ); + void moms_eq0( + const typename thrust_device::vector::iterator &vec_bgn + ); void moms_rng( const real_t &min, const real_t &max, const typename thrust_device::vector::iterator &vec_bgn, diff --git a/src/impl/particles_impl_ice_nucl.ipp b/src/impl/particles_impl_ice_nucl.ipp index 02709031f..13cb069c0 100644 --- a/src/impl/particles_impl_ice_nucl.ipp +++ b/src/impl/particles_impl_ice_nucl.ipp @@ -28,38 +28,40 @@ namespace libcloudphxx return false; }; }; - // Functor to return rw2 of newly frozen droplets, otherwise 0 - template - class rw2_if_newfrozen - { - public: - BOOST_GPU_ENABLED - real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 3 elements: (rw2, ice, ice_old) - { - real_t rw2 = thrust::get<0>(tpl); - int ice_flag = thrust::get<1>(tpl); - int ice_flag_old = thrust::get<2>(tpl); - return (ice_flag==1 && ice_flag_old==0) ? rw2 : real_t(0); - } - }; - // Functor to update r2 of newly frozen droplets, because ice has different density + // Functor to update ice flag, rw2, a, c, rho_i, of frozen droplets template - class rw2_update + class freezing_update { public: BOOST_GPU_ENABLED - real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 3 elements: (rw2, ice, ice_old) + void operator()(thrust::tuple< + int&, real_t&, real_t&, real_t&, real_t&, // to be updated (ice, rw2, a, c, rho_i) + const real_t&, const real_t&, const real_t& // T_freeze, T, RH + > tpl) const { - real_t rw2 = thrust::get<0>(tpl); - int ice_flag = thrust::get<1>(tpl); - int ice_flag_old = thrust::get<2>(tpl); - return (ice_flag==1 && ice_flag_old==0) ? - rw2 * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(2/3)) : rw2; + auto& ice = thrust::get<0>(tpl); + auto& rw2 = thrust::get<1>(tpl); + auto& a = thrust::get<2>(tpl); + auto& c = thrust::get<3>(tpl); + auto& rho_i = thrust::get<4>(tpl); + + const real_t T_freeze = thrust::get<5>(tpl); + const real_t T = thrust::get<6>(tpl); + const real_t RH = thrust::get<7>(tpl); + + if (detail::immersion_freeze_cond()(thrust::make_tuple(T_freeze, T, RH))) + { + ice = 1; + rw2 = real_t(0); + rho_i = common::moist_air::rho_i(); + a = rw2 * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(2/3)); + c = rw2 * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(2/3)); + } } }; - }; + } // Immersion freezing template @@ -67,75 +69,62 @@ namespace libcloudphxx hskpng_sort(); - // Copy current ice flags - thrust_device::vector ice_old = ice; - - // Change liquid droplets to ice under the freezing condition - thrust::replace_if(ice.begin(), ice.end(), // Replacing values of ice with 1 if immersion_freeze_cond is satisfied. - thrust::make_zip_iterator( - thrust::make_tuple( // Creating a zip iterator to access multiple vectors: - T_freeze.begin(), // freezing temperature for each droplet - thrust::make_permutation_iterator(T.begin(), ijk.begin()), // ambient temperature - thrust::make_permutation_iterator(RH.begin(), ijk.begin()) // ambient RH - ) - ), - detail::immersion_freeze_cond(), - real_t(1) - ); + // A vector to store liquid 3rd moments + thrust_device::vector &drw(tmp_device_real_cell); + if (count_n != n_cell) + thrust::fill(drw.begin(), drw.end(), real_t(0.)); - // Temporary vector for rw2 of newly frozen droplets - thrust_device::vector rw2_frozen(count_n); + // Compute per-cell 3rd moment of liquid droplets (sum of n*r^3) before freezing. It is stored in count_mom + moms_eq0(ice.begin()); // choose particles with ice=0 + moms_calc(rw2.begin(), real_t(1.5)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of droplets before freezing"); thrust::transform( - thrust::make_zip_iterator(thrust::make_tuple( // first input - rw2.begin(), // droplet radius squared - ice.begin(), // ice flag - ice_old.begin() // old ice flag - )), - thrust::make_zip_iterator(thrust::make_tuple( // last input - rw2.end(), - ice.end(), - ice_old.end() - )), - rw2_frozen.begin(), // output - detail::rw2_if_newfrozen() // functor for r2 of newly frozen droplets + count_mom.begin(), count_mom.begin() + count_n, + thrust::make_permutation_iterator(drw.begin(), count_ijk.begin()), + thrust::negate() ); - // Compute per-cell 3rd moment of newly frozen droplets (sum of n*r^3). It is stored in count_mom - moms_all(); - moms_calc(rw2_frozen.begin(), count_n, real_t(1.5), true); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of newly frozen droplets"); + // Change liquid droplets to ice under the freezing condition + thrust::for_each( + thrust::make_zip_iterator(thrust::make_tuple( + ice.begin(), + rw2.begin(), + a_ice.begin(), + c_ice.begin(), + rho_i.begin(), + T_freeze.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH.begin(), ijk.begin()) + )), + thrust::make_zip_iterator(thrust::make_tuple( + ice.begin(), + rw2.begin(), + a_ice.begin(), + c_ice.begin(), + rho_i.begin(), + T_freeze.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH.begin(), ijk.begin()) + )) + n_part, + detail::freezing_update() // functor for updating (ice, rw2, a, c, rho_i) if freezing condition satisfied + ); - // Copy to frozen_mom3 array - thrust_device::vector &frozen_mom3(tmp_device_real_cell); - if (count_n != n_cell) - thrust::fill(frozen_mom3.begin(), frozen_mom3.end(), real_t(0.)); + // Compute per-cell 3rd moment of liquid droplets after freezing. It is stored in count_mom + moms_eq0(ice.begin()); // choose particles with ice=0 + moms_calc(rw2.begin(), real_t(1.5)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of droplets after freezing"); + // Compute the difference between liquid moments before and after freezing thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, - thrust::make_permutation_iterator(frozen_mom3.begin(), count_ijk.begin()), - thrust::identity() // just copy values + count_mom.begin(), count_mom.begin() + count_n, + thrust::make_permutation_iterator(drw.begin(), count_ijk.begin()), + thrust::make_permutation_iterator(drw.begin(), count_ijk.begin()), + thrust::plus() ); - // update th according to the frozen volume per cell - update_th_freezing(frozen_mom3); - - - // update the radius of newly frozen particles - taking into account different density of ice - thrust::transform( - thrust::make_zip_iterator(thrust::make_tuple( // first input - rw2.begin(), // droplet radius squared - ice.begin(), // ice flag - ice_old.begin() // old ice flag - )), - thrust::make_zip_iterator(thrust::make_tuple( // last input - rw2.end(), - ice.end(), - ice_old.end() - )), - rw2.begin(), // output - detail::rw2_update() // functor for updating rw2 - ); + // Update th according to the frozen volume per cell + update_th_freezing(drw); } diff --git a/src/impl/particles_impl_moms.ipp b/src/impl/particles_impl_moms.ipp index 38d9b87dc..2a07773e3 100644 --- a/src/impl/particles_impl_moms.ipp +++ b/src/impl/particles_impl_moms.ipp @@ -144,7 +144,51 @@ namespace libcloudphxx arg::_1 * (arg::_2 >= 0) // op ); } + selected_before_counting = true; + } + + + // selects particles for which vec[i] > 0 + template + void particles_t::impl::moms_gt0( + const typename thrust_device::vector::iterator &vec_bgn + ) + { + hskpng_sort(); + + thrust_device::vector &n_filtered(tmp_device_real_part); + + { + namespace arg = thrust::placeholders; + thrust::transform( + n.begin(), n.end(), // input - 1st arg + vec_bgn, // input - 2nd arg + n_filtered.begin(), // output + arg::_1 * (arg::_2 > 0) // op + ); + } + selected_before_counting = true; + } + + // selects particles for which vec[i] = 0 + template + void particles_t::impl::moms_eq0( + const typename thrust_device::vector::iterator &vec_bgn + ) + { + hskpng_sort(); + thrust_device::vector &n_filtered(tmp_device_real_part); + + { + namespace arg = thrust::placeholders; + thrust::transform( + n.begin(), n.end(), // input - 1st arg + vec_bgn, // input - 2nd arg + n_filtered.begin(), // output + arg::_1 * (arg::_2 == 0) // op + ); + } selected_before_counting = true; } diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/particles_impl_update_th_rv.ipp index 67fecf743..24882b55d 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/particles_impl_update_th_rv.ipp @@ -55,13 +55,13 @@ namespace libcloudphxx real_t operator()(const thrust::tuple &tpl) const { const quantity - dri = thrust::get<0>(tpl); + drw = thrust::get<0>(tpl); const quantity T = thrust::get<1>(tpl) * si::kelvins; const quantity th = thrust::get<2>(tpl) * si::kelvins; - return dri * common::theta_dry::d_th_d_ri_freeze(T, th) / si::kelvins; + return drw * common::theta_dry::d_th_d_rw_freeze(T, th) / si::kelvins; } }; }; @@ -183,24 +183,24 @@ namespace libcloudphxx nancheck(th, "update_th_rv: th after update"); } - // update th for freezing + // update th for freezing // particles have to be sorted template void particles_t::impl::update_th_freezing( - thrust_device::vector &frozen_mom3 // specific 3rd moment of newly frozen droplets per cell + thrust_device::vector &drw // change of specific 3rd moment of liquid per cell ) { if(!sorted) throw std::runtime_error("libcloudph++: update_th_freezing called on an unsorted set"); - nancheck(frozen_mom3, "update_th_freezing: input frozen_mom3"); + nancheck(drw, "update_th_freezing: input drw"); - // Calculating the mixing ratio of frozen water per cell (multiplying specific 3rd mom by rho_w*4/3*pi) + // Calculating the change of liquid mixing ratio per cell (multiplying specific 3rd mom by rho_w*4/3*pi) thrust::transform( - frozen_mom3.begin(), frozen_mom3.end(), // input - 1st arg + drw.begin(), drw.end(), // input - 1st arg thrust::make_constant_iterator( // input - 2nd arg common::moist_air::rho_w() / si::kilograms * si::cubic_metres * real_t(4./3) * pi() ), - frozen_mom3.begin(), // output + drw.begin(), // output thrust::multiplies() ); @@ -217,7 +217,7 @@ namespace libcloudphxx th.begin(), th.end(), // input - 1st arg thrust::make_transform_iterator( zip_it_t(thrust::make_tuple( - frozen_mom3.begin(), + drw.begin(), T.begin(), th.begin() )), diff --git a/src/particles_diag.ipp b/src/particles_diag.ipp index 02503be24..63df19f97 100644 --- a/src/particles_diag.ipp +++ b/src/particles_diag.ipp @@ -220,14 +220,14 @@ namespace libcloudphxx template void particles_t::diag_ice() { - pimpl->moms_rng(1., 1.0001, pimpl->ice.begin(), false); // TODO: nextafter instead of 1.0001 + pimpl->moms_gt0(pimpl->ice.begin()); // ice flag greater than 0 } // selects water particles template void particles_t::diag_water() { - pimpl->moms_rng(0., 0.0001, pimpl->ice.begin(), false); + pimpl->moms_eq0(pimpl->ice.begin()); // ice flag equal to 0 } // selects particles with (r_d >= r_min && r_d < r_max) from particles previously selected From 2e1bddb6bc96d526b11fd587c60441e37981eb93 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 16 Sep 2025 12:56:00 +0200 Subject: [PATCH 43/97] melting --- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_ice_melt.ipp | 121 +++++++++++++++++++++++++++ src/impl/particles_impl_ice_nucl.ipp | 10 +-- src/impl/particles_impl_melt.ipp | 102 ---------------------- src/particles.tpp | 2 +- src/particles_step.ipp | 2 + 6 files changed, 130 insertions(+), 109 deletions(-) create mode 100644 src/impl/particles_impl_ice_melt.ipp delete mode 100644 src/impl/particles_impl_melt.ipp diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 22a9e0c02..22aeb2530 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -614,7 +614,7 @@ namespace libcloudphxx void subs(const real_t &dt); void ice_nucl(); - void melt(); + void ice_melt(); void cond_dm3_helper(); void cond(const real_t &dt, const real_t &RH_max, const bool turb_cond); diff --git a/src/impl/particles_impl_ice_melt.ipp b/src/impl/particles_impl_ice_melt.ipp new file mode 100644 index 000000000..cb9ed323e --- /dev/null +++ b/src/impl/particles_impl_ice_melt.ipp @@ -0,0 +1,121 @@ +// vim:filetype=cpp +/** @file + * @copyright University of Warsaw + * @section LICENSE + * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) + */ + +#include +#include + +namespace libcloudphxx +{ + namespace lgrngn + { + namespace detail + { + // The condition for melting + template + class melting_cond + { + public: + BOOST_GPU_ENABLED + bool operator()(const real_t &T) const + { + return (T > real_t(273.15)); + }; + }; + + // Functor to update ice flag, rw2, a, c, rho_i, of melted ice + template + class melting_update + { + public: + BOOST_GPU_ENABLED + void operator()(thrust::tuple< + real_t&, real_t&, real_t&, real_t&, real_t&, // to be updated (ice, rw2, a, c, rho_i) + const real_t& // ambient T + > tpl) const + { + auto& ice = thrust::get<0>(tpl); + auto& rw2 = thrust::get<1>(tpl); + auto& a = thrust::get<2>(tpl); + auto& c = thrust::get<3>(tpl); + auto& rho_i = thrust::get<4>(tpl); + + if (detail::melting_cond()(thrust::get<5>(tpl))) + { + ice = real_t(0); + rw2 = pow(common::moist_air::rho_i() / common::moist_air::rho_w() * c , real_t(2./3.)) * pow(a , real_t(4./3.)); + rho_i = real_t(0); + a = real_t(0); + c = real_t(0); + } + } + }; + + }; + + // Melting + template + void particles_t::impl::ice_melt() { + + hskpng_sort(); + + // A vector to store liquid 3rd moments + thrust_device::vector &drw(tmp_device_real_cell); + if (count_n != n_cell) + thrust::fill(drw.begin(), drw.end(), real_t(0.)); + + // Compute per-cell 3rd moment of liquid droplets (sum of n*r^3) before melting. It is stored in count_mom + moms_eq0(ice.begin()); // choose particles with ice=0 + moms_calc(rw2.begin(), real_t(1.5)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of droplets before melting"); + + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, + thrust::make_permutation_iterator(drw.begin(), count_ijk.begin()), + thrust::negate() + ); + + // Change ice to liquid droplets under the melting condition + thrust::for_each( + thrust::make_zip_iterator(thrust::make_tuple( + ice.begin(), + rw2.begin(), + a_ice.begin(), + c_ice.begin(), + rho_i.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()) + )), + thrust::make_zip_iterator(thrust::make_tuple( + ice.begin(), + rw2.begin(), + a_ice.begin(), + c_ice.begin(), + rho_i.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()) + )) + n_part, + detail::melting_update() // functor for updating (ice, rw2, a, c, rho_i) if melting condition satisfied + ); + + // Compute per-cell 3rd moment of liquid droplets after melting. It is stored in count_mom + moms_eq0(ice.begin()); // choose particles with ice=0 + moms_calc(rw2.begin(), real_t(1.5)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of droplets after melting"); + + // Compute the difference between liquid moments before and after melting + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, + thrust::make_permutation_iterator(drw.begin(), count_ijk.begin()), + thrust::make_permutation_iterator(drw.begin(), count_ijk.begin()), + thrust::plus() + ); + + // Update th according to the melted volume per cell + update_th_freezing(drw); + + } + + } +} diff --git a/src/impl/particles_impl_ice_nucl.ipp b/src/impl/particles_impl_ice_nucl.ipp index 13cb069c0..25e221ded 100644 --- a/src/impl/particles_impl_ice_nucl.ipp +++ b/src/impl/particles_impl_ice_nucl.ipp @@ -36,7 +36,7 @@ namespace libcloudphxx public: BOOST_GPU_ENABLED void operator()(thrust::tuple< - int&, real_t&, real_t&, real_t&, real_t&, // to be updated (ice, rw2, a, c, rho_i) + real_t&, real_t&, real_t&, real_t&, real_t&, // to be updated (ice, rw2, a, c, rho_i) const real_t&, const real_t&, const real_t& // T_freeze, T, RH > tpl) const { @@ -52,11 +52,11 @@ namespace libcloudphxx if (detail::immersion_freeze_cond()(thrust::make_tuple(T_freeze, T, RH))) { - ice = 1; + ice = real_t(1); rw2 = real_t(0); - rho_i = common::moist_air::rho_i(); - a = rw2 * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(2/3)); - c = rw2 * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(2/3)); + rho_i = common::moist_air::rho_i().value(); + a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); + c = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); } } }; diff --git a/src/impl/particles_impl_melt.ipp b/src/impl/particles_impl_melt.ipp deleted file mode 100644 index dbfbe41f9..000000000 --- a/src/impl/particles_impl_melt.ipp +++ /dev/null @@ -1,102 +0,0 @@ -// vim:filetype=cpp -/** @file - * @copyright University of Warsaw - * @section LICENSE - * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) - */ - -#include -#include - -namespace libcloudphxx -{ - namespace lgrngn - { - namespace detail - { - // The condition for melting - template - class melting_cond - { - public: - BOOST_GPU_ENABLED - bool operator()(const real_t &T) const - { - return (T > real_t(273.15)); - }; - }; - - // Functor to return rw2 of newly melted particles, otherwise 0 - template - class rw2_if_newmelted - { - public: - BOOST_GPU_ENABLED - real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 3 elements: (rw2, ice, ice_old) - { - real_t rw2 = thrust::get<0>(tpl); - int ice_flag = thrust::get<1>(tpl); - int ice_flag_old = thrust::get<2>(tpl); - return (ice_flag==0 && ice_flag_old==1) ? rw2 : real_t(0); - } - }; - - }; - - // Melting - template - void particles_t::impl::melt() { - - hskpng_sort(); - - // Copy current ice flags - thrust_device::vector ice_old = ice; - - // Change ice to liquid under the melting condition - thrust::replace_if(ice.begin(), ice.end(), // Replacing values of ice with 0 if melting_cond is satisfied. - thrust::make_permutation_iterator(T.begin(), ijk.begin()), // ambient temperature for each particle - detail::melting_cond(), - real_t(0) - ); - - // Temporary vector for rw2 of newly melted particles - thrust_device::vector rw2_melted(count_n); - - thrust::transform( - thrust::make_zip_iterator(thrust::make_tuple( // first input - rw2.begin(), // droplet radius squared - ice.begin(), // ice flag - ice_old.begin() // old ice flag - )), - thrust::make_zip_iterator(thrust::make_tuple( // last input - rw2.end(), - ice.end(), - ice_old.end() - )), - rw2_melted.begin(), // output - detail::rw2_if_newmelted() // functor for r2 of newly melted particles - ); - - // Compute per-cell 3rd moment of newly melted particles (sum of n*r^3). It is stored in count_mom - moms_all(); - moms_calc(rw2_melted.begin(), count_n, real_t(1.5), true); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of newly melted particles"); - - // Copy to melted_mom3 array - thrust_device::vector &melted_mom3(tmp_device_real_cell); - if (count_n != n_cell) - thrust::fill(melted_mom3.begin(), melted_mom3.end(), real_t(0.)); - - // multiplying by -1 (dth < 0 for melting) - thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, - thrust::make_permutation_iterator(melted_mom3.begin(), count_ijk.begin()), - thrust::negate() - ); - - // update th according to the melted volume per cell - update_th_freezing(melted_mom3); - } - - } -} diff --git a/src/particles.tpp b/src/particles.tpp index dc2e9ee27..001edd14c 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -108,7 +108,7 @@ #include "impl/particles_impl_cond.ipp" #include "impl/particles_impl_cond_sstp.ipp" #include "impl/particles_impl_ice_nucl.ipp" -#include "impl/particles_impl_melt.ipp" +#include "impl/particles_impl_ice_melt.ipp" #include "impl/particles_impl_sedi.ipp" #include "impl/particles_impl_subs.ipp" #include "impl/particles_impl_coal.ipp" diff --git a/src/particles_step.ipp b/src/particles_step.ipp index f5f2f9f49..59da15cc8 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -196,6 +196,7 @@ namespace libcloudphxx pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); if (opts.ice_nucl) pimpl->ice_nucl(); + pimpl->ice_melt(); pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } // copy sstp_tmp_rv and th to rv and th @@ -213,6 +214,7 @@ namespace libcloudphxx pimpl->hskpng_Tpr(); if (opts.ice_nucl) pimpl->ice_nucl(); + pimpl->ice_melt(); pimpl->cond(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } } From 15f011147ac3213bf71fa380f7763a9de9b3df6a Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 16 Sep 2025 13:29:02 +0200 Subject: [PATCH 44/97] fixing impl_cond --- src/impl/particles_impl_cond.ipp | 130 ++++++++----------------------- 1 file changed, 34 insertions(+), 96 deletions(-) diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index 280b2e4dd..c645ae832 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -9,91 +9,51 @@ namespace libcloudphxx { namespace lgrngn { - - namespace detail - { - // Functor to select moment of only liquid droplets - template - class select_liq - { - public: - BOOST_GPU_ENABLED - real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 2 elements: (count_mom, ice) - { - real_t count_mom = thrust::get<0>(tpl); - int ice_flag = thrust::get<1>(tpl); - return (ice_flag==0) ? count_mom : real_t(0); - } - }; - - // Functor to select moment of only ice particles - template - class select_ice - { - public: - BOOST_GPU_ENABLED - real_t operator()(const thrust::tuple &tpl) const // tpl is a tuple of 2 elements: (count_mom, ice) - { - real_t count_mom = thrust::get<0>(tpl); - int ice_flag = thrust::get<1>(tpl); - return (ice_flag==1) ? count_mom : real_t(0); - } - }; - - } - - template void particles_t::impl::cond( const real_t &dt, const real_t &RH_max, const bool turb_cond - ) { + ) { namespace arg = thrust::placeholders; thrust_device::vector &lambda_D(tmp_device_real_cell1); // real_cell used in cond.ipp thrust_device::vector &lambda_K(tmp_device_real_cell2); // real_cell used in cond.ipp - // --- calc liquid water content before cond --- - hskpng_sort(); - //thrust_device::vector &drv(tmp_device_real_cell); + hskpng_sort(); + + // Vectors to store 3rd moments thrust_device::vector &drv_liq(tmp_device_real_cell); thrust_device::vector &drv_ice(tmp_device_real_cell3); - - // Compute per-cell 3rd wet moment before condensation (sum of n*r^3). It is stored in count_mom - moms_all(); - moms_calc(rw2.begin(), real_t(3./2.)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) before condensation"); - - // permute-copying the result to -dm_3 - // fill with 0s if not all cells will be updated in the following transform if(count_n!=n_cell) { thrust::fill(drv_liq.begin(), drv_liq.end(), real_t(0.)); thrust::fill(drv_ice.begin(), drv_ice.end(), real_t(0.)); } - // fill drv_liq with liquid moment before conde + // Compute per-cell 3rd moment of liquid droplets before condensation. It is stored in count_mom + moms_eq0(ice.begin()); // choose particles with ice=0 + moms_calc(rw2.begin(), real_t(3./2.)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) before condensation"); + thrust::transform( - thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin(), ice.begin())), - thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin() + count_n, ice.begin() + count_n)), // input - 1st arg - thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // output - detail::select_liq() + count_mom.begin(), count_mom.begin() + count_n, + thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), + thrust::negate() ); - // fill drv_ice with ice moment before cond + // TODO: how to calculate 3rd ice moment + // Compute per-cell 3rd moment of ice before sublimation. It is stored in count_mom + moms_gt0(ice.begin()); // choose particles with ice=1 + moms_calc(rw2.begin(), real_t(3./2.)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) before sublimation"); + thrust::transform( - thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin(), ice.begin())), - thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin() + count_n, ice.begin() + count_n)), // input - 1st arg - thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // output - detail::select_ice() + count_mom.begin(), count_mom.begin() + count_n, + thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), + thrust::negate() ); - // thrust::transform( - // count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - // thrust::make_permutation_iterator(drv.begin(), count_ijk.begin()), // output - // thrust::negate() - // ); auto hlpr_zip_iter = thrust::make_zip_iterator(thrust::make_tuple( thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), @@ -108,7 +68,7 @@ namespace libcloudphxx ice.begin() )); - // calculating drop growth in a timestep using backward Euler + // calculating drop growth in a timestep using backward Euler // TODO: both calls almost identical, use std::bind or sth? if(turb_cond) { @@ -153,58 +113,36 @@ namespace libcloudphxx ); nancheck(rw2, "rw2 after condensation (no sub-steps"); - // Compute per-cell 3rd wet moment after condensation. It is stored in count_mom + // Compute per-cell 3rd moment of liquid droplets after condensation. It is stored in count_mom + moms_eq0(ice.begin()); // choose particles with ice=0 moms_calc(rw2.begin(), real_t(3./2.)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) after condensation"); - // fill count_mom with liquid only - thrust::transform( - thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin(), ice.begin())), - thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin() + count_n, ice.begin() + count_n)), // input - 1st arg - thrust::make_permutation_iterator(count_mom.begin(), count_ijk.begin()), // output - detail::select_liq() - ); - - // calculating the change in liquid moment + // Adding the third liquid moment after condensation to drv_liq thrust::transform( count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // input - 2nd arg thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // output - thrust::minus() + thrust::plus() ); - - // Compute again the per-cell 3rd wet moment after condensation. It is stored in count_mom + // TODO: how to calculate 3rd ice moment + // Compute per-cell 3rd moment of ice after sublimation. It is stored in count_mom + moms_gt0(ice.begin()); // choose particles with ice=1 moms_calc(rw2.begin(), real_t(3./2.)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) after condensation"); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) after sublimation"); - // fill count_mom_ with ice only - thrust::transform( - thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin(), ice.begin())), - thrust::make_zip_iterator(thrust::make_tuple(count_mom.begin() + count_n, ice.begin() + count_n)), // input - 1st arg - thrust::make_permutation_iterator(count_mom.begin(), count_ijk.begin()), // output - detail::select_ice() - ); - - // calculating the change in ice moment + // Adding the third ice moment after sublimation to drv_ice thrust::transform( count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // input - 2nd arg thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // output - thrust::minus() + thrust::plus() ); - // // adding the third moment after condensation to dm_3 - // thrust::transform( - // count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - // thrust::make_permutation_iterator(drv.begin(), count_ijk.begin()), // input - 2nd arg - // thrust::make_permutation_iterator(drv.begin(), count_ijk.begin()), // output - // thrust::plus() - // ); - // update th and rv according to changes in third specific wet moments update_th_rv(drv_liq); update_th_rv_subl(drv_ice); } - }; -}; + }; +}; \ No newline at end of file From cdc7b54b8c0e89aafdfd9269292b370dca3c0b69 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 16 Sep 2025 15:35:43 +0200 Subject: [PATCH 45/97] ice 3rd moment --- src/impl/particles_impl.ipp | 12 ++--- src/impl/particles_impl_cond.ipp | 45 +++++++++++++++---- src/impl/particles_impl_fill_outbuf.ipp | 6 +-- src/impl/particles_impl_ice_melt.ipp | 12 ++--- src/impl/particles_impl_ice_nucl.ipp | 12 ++--- src/impl/particles_impl_init_a_c_rho_ice.ipp | 6 +-- src/impl/particles_impl_init_hskpng_ncell.ipp | 1 + .../particles_impl_reserve_hskpng_npart.ipp | 6 +-- src/impl/particles_impl_update_th_rv.ipp | 2 +- 9 files changed, 65 insertions(+), 37 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 22aeb2530..9a99b526a 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -88,9 +88,9 @@ namespace libcloudphxx ice, // 0 - water 1 - ice; bool would suffice, but we are lazy rd3_insol, // dry radii of insoluble aerosol cubed T_freeze, // freezing temperature - a_ice, // equatorial radius of ice - c_ice, // polar radius of ice - rho_i; // ice apparent density + ice_a, // equatorial radius of ice + ice_c, // polar radius of ice + ice_rho; // ice apparent density // dry radii distribution characteristics real_t log_rd_min, // logarithm of the lower bound of the distr @@ -461,9 +461,9 @@ namespace libcloudphxx distmem_real_vctrs.insert({&ice, detail::no_initial_value}); distmem_real_vctrs.insert({&rd3_insol, detail::no_initial_value}); distmem_real_vctrs.insert({&T_freeze, detail::no_initial_value}); - distmem_real_vctrs.insert({&a_ice, detail::no_initial_value}); - distmem_real_vctrs.insert({&c_ice, detail::no_initial_value}); - distmem_real_vctrs.insert({&rho_i, detail::no_initial_value}); + distmem_real_vctrs.insert({&ice_a, detail::no_initial_value}); + distmem_real_vctrs.insert({&ice_c, detail::no_initial_value}); + distmem_real_vctrs.insert({&ice_rho, detail::no_initial_value}); } } diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index c645ae832..bd5e2ca51 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -9,6 +9,27 @@ namespace libcloudphxx { namespace lgrngn { + + namespace detail + { + + template + class ice_vol + { + public: + using result_type = real_t; + template + BOOST_GPU_ENABLED + real_t operator()(Tuple const &tpl) const + { + real_t a = thrust::get<0>(tpl); + real_t c = thrust::get<1>(tpl); + return a * a * c; + } + }; + } + + template void particles_t::impl::cond( const real_t &dt, @@ -26,15 +47,14 @@ namespace libcloudphxx // Vectors to store 3rd moments thrust_device::vector &drv_liq(tmp_device_real_cell); thrust_device::vector &drv_ice(tmp_device_real_cell3); - if(count_n!=n_cell) { - thrust::fill(drv_liq.begin(), drv_liq.end(), real_t(0.)); - thrust::fill(drv_ice.begin(), drv_ice.end(), real_t(0.)); - } // Compute per-cell 3rd moment of liquid droplets before condensation. It is stored in count_mom moms_eq0(ice.begin()); // choose particles with ice=0 moms_calc(rw2.begin(), real_t(3./2.)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) before condensation"); + if(count_n!=n_cell) { + thrust::fill(drv_liq.begin(), drv_liq.end(), real_t(0.)); + } thrust::transform( count_mom.begin(), count_mom.begin() + count_n, @@ -42,11 +62,17 @@ namespace libcloudphxx thrust::negate() ); - // TODO: how to calculate 3rd ice moment // Compute per-cell 3rd moment of ice before sublimation. It is stored in count_mom moms_gt0(ice.begin()); // choose particles with ice=1 - moms_calc(rw2.begin(), real_t(3./2.)); + moms_calc(thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() + ), + real_t(1)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) before sublimation"); + if(count_n!=n_cell) { + thrust::fill(drv_ice.begin(), drv_ice.end(), real_t(0.)); + } thrust::transform( count_mom.begin(), count_mom.begin() + count_n, @@ -126,11 +152,12 @@ namespace libcloudphxx thrust::plus() ); - // TODO: how to calculate 3rd ice moment // Compute per-cell 3rd moment of ice after sublimation. It is stored in count_mom moms_gt0(ice.begin()); // choose particles with ice=1 - moms_calc(rw2.begin(), real_t(3./2.)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) after sublimation"); + moms_calc(thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() + ), + real_t(1)); // Adding the third ice moment after sublimation to drv_ice thrust::transform( diff --git a/src/impl/particles_impl_fill_outbuf.ipp b/src/impl/particles_impl_fill_outbuf.ipp index c6743a025..833a60fa4 100644 --- a/src/impl/particles_impl_fill_outbuf.ipp +++ b/src/impl/particles_impl_fill_outbuf.ipp @@ -50,9 +50,9 @@ namespace libcloudphxx name == "kappa" ? kpa : name == "rd3_insol" ? rd3_insol : name == "T_freeze" ? T_freeze : - name == "a_ice" ? a_ice : - name == "c_ice" ? c_ice : - name == "rho_i" ? rho_i : + name == "ice_a" ? ice_a : + name == "ice_c" ? ice_c : + name == "ice_rho" ? ice_rho : name == "x" ? x : name == "y" ? y : z); diff --git a/src/impl/particles_impl_ice_melt.ipp b/src/impl/particles_impl_ice_melt.ipp index cb9ed323e..a4fc8cdc3 100644 --- a/src/impl/particles_impl_ice_melt.ipp +++ b/src/impl/particles_impl_ice_melt.ipp @@ -83,17 +83,17 @@ namespace libcloudphxx thrust::make_zip_iterator(thrust::make_tuple( ice.begin(), rw2.begin(), - a_ice.begin(), - c_ice.begin(), - rho_i.begin(), + ice_a.begin(), + ice_c.begin(), + ice_rho.begin(), thrust::make_permutation_iterator(T.begin(), ijk.begin()) )), thrust::make_zip_iterator(thrust::make_tuple( ice.begin(), rw2.begin(), - a_ice.begin(), - c_ice.begin(), - rho_i.begin(), + ice_a.begin(), + ice_c.begin(), + ice_rho.begin(), thrust::make_permutation_iterator(T.begin(), ijk.begin()) )) + n_part, detail::melting_update() // functor for updating (ice, rw2, a, c, rho_i) if melting condition satisfied diff --git a/src/impl/particles_impl_ice_nucl.ipp b/src/impl/particles_impl_ice_nucl.ipp index 25e221ded..f60bbe792 100644 --- a/src/impl/particles_impl_ice_nucl.ipp +++ b/src/impl/particles_impl_ice_nucl.ipp @@ -90,9 +90,9 @@ namespace libcloudphxx thrust::make_zip_iterator(thrust::make_tuple( ice.begin(), rw2.begin(), - a_ice.begin(), - c_ice.begin(), - rho_i.begin(), + ice_a.begin(), + ice_c.begin(), + ice_rho.begin(), T_freeze.begin(), thrust::make_permutation_iterator(T.begin(), ijk.begin()), thrust::make_permutation_iterator(RH.begin(), ijk.begin()) @@ -100,9 +100,9 @@ namespace libcloudphxx thrust::make_zip_iterator(thrust::make_tuple( ice.begin(), rw2.begin(), - a_ice.begin(), - c_ice.begin(), - rho_i.begin(), + ice_a.begin(), + ice_c.begin(), + ice_rho.begin(), T_freeze.begin(), thrust::make_permutation_iterator(T.begin(), ijk.begin()), thrust::make_permutation_iterator(RH.begin(), ijk.begin()) diff --git a/src/impl/particles_impl_init_a_c_rho_ice.ipp b/src/impl/particles_impl_init_a_c_rho_ice.ipp index b4c027931..bf9e4949d 100644 --- a/src/impl/particles_impl_init_a_c_rho_ice.ipp +++ b/src/impl/particles_impl_init_a_c_rho_ice.ipp @@ -14,9 +14,9 @@ namespace libcloudphxx void particles_t::impl::init_a_c_rho_ice() { // filling a and c with zeros initially - thrust::fill(a_ice.begin() + n_part_old, a_ice.end(), real_t(0)); - thrust::fill(c_ice.begin() + n_part_old, c_ice.end(), real_t(0)); - thrust::fill(rho_i.begin() + n_part_old, rho_i.end(), real_t(0)); + thrust::fill(ice_a.begin() + n_part_old, ice_a.end(), real_t(0)); + thrust::fill(ice_c.begin() + n_part_old, ice_c.end(), real_t(0)); + thrust::fill(ice_rho.begin() + n_part_old, ice_rho.end(), real_t(0)); } }; }; \ No newline at end of file diff --git a/src/impl/particles_impl_init_hskpng_ncell.ipp b/src/impl/particles_impl_init_hskpng_ncell.ipp index 42806acf4..6cbced800 100644 --- a/src/impl/particles_impl_init_hskpng_ncell.ipp +++ b/src/impl/particles_impl_init_hskpng_ncell.ipp @@ -27,6 +27,7 @@ namespace libcloudphxx tmp_device_real_cell.resize(n_cell); tmp_device_real_cell1.resize(n_cell); tmp_device_real_cell2.resize(n_cell); + tmp_device_real_cell3.resize(n_cell); tmp_device_size_cell.resize(n_cell); tmp_host_size_cell.resize(n_cell); tmp_host_real_cell.resize(n_cell); diff --git a/src/impl/particles_impl_reserve_hskpng_npart.ipp b/src/impl/particles_impl_reserve_hskpng_npart.ipp index 60e4bc1e7..a2b78f48a 100644 --- a/src/impl/particles_impl_reserve_hskpng_npart.ipp +++ b/src/impl/particles_impl_reserve_hskpng_npart.ipp @@ -41,9 +41,9 @@ namespace libcloudphxx ice.reserve(opts_init.n_sd_max); rd3_insol.reserve(opts_init.n_sd_max); T_freeze.reserve(opts_init.n_sd_max); - a_ice.reserve(opts_init.n_sd_max); - c_ice.reserve(opts_init.n_sd_max); - rho_i.reserve(opts_init.n_sd_max); + ice_a.reserve(opts_init.n_sd_max); + ice_c.reserve(opts_init.n_sd_max); + ice_rho.reserve(opts_init.n_sd_max); } vt.reserve(opts_init.n_sd_max); diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/particles_impl_update_th_rv.ipp index 24882b55d..eb8c7db05 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/particles_impl_update_th_rv.ipp @@ -229,7 +229,7 @@ namespace libcloudphxx } nancheck(th, "update_th_freezing: th after update"); } - + // update particle-specific cell state // particles have to be sorted template From f980f562c89b6e37cc0e13b8966a032b51e83b2f Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 16 Sep 2025 15:49:26 +0200 Subject: [PATCH 46/97] combine nucl and melt --- src/impl/particles_impl.ipp | 3 +- src/impl/particles_impl_fill_outbuf.ipp | 2 +- src/impl/particles_impl_ice_nucl.ipp | 132 ------------------ ...t.ipp => particles_impl_ice_nucl_melt.ipp} | 96 +++++++++++-- src/particles.tpp | 3 +- 5 files changed, 87 insertions(+), 149 deletions(-) delete mode 100644 src/impl/particles_impl_ice_nucl.ipp rename src/impl/{particles_impl_ice_melt.ipp => particles_impl_ice_nucl_melt.ipp} (51%) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 9a99b526a..de332a78e 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -613,8 +613,7 @@ namespace libcloudphxx void sedi(const real_t &dt); void subs(const real_t &dt); - void ice_nucl(); - void ice_melt(); + void ice_nucl_melt(); void cond_dm3_helper(); void cond(const real_t &dt, const real_t &RH_max, const bool turb_cond); diff --git a/src/impl/particles_impl_fill_outbuf.ipp b/src/impl/particles_impl_fill_outbuf.ipp index 833a60fa4..9af0b039f 100644 --- a/src/impl/particles_impl_fill_outbuf.ipp +++ b/src/impl/particles_impl_fill_outbuf.ipp @@ -40,7 +40,7 @@ namespace libcloudphxx template std::vector particles_t::impl::fill_attr_outbuf(const std::string &name) { - const std::set attr_names = {"rw2", "rd3", "kappa", "rd3_insol", "T_freeze", "a_ice", "c_ice", "rho_i", "x", "y", "z"}; // TODO implement "n" - it is n_t type and others are real_t + const std::set attr_names = {"rw2", "rd3", "kappa", "rd3_insol", "T_freeze", "ice_a", "ice_c", "ice_rho", "x", "y", "z"}; // TODO implement "n" - it is n_t type and others are real_t if (std::find(std::begin(attr_names), std::end(attr_names), name) == std::end(attr_names)) throw std::runtime_error("Unknown attribute name passed to get_attr."); diff --git a/src/impl/particles_impl_ice_nucl.ipp b/src/impl/particles_impl_ice_nucl.ipp deleted file mode 100644 index f60bbe792..000000000 --- a/src/impl/particles_impl_ice_nucl.ipp +++ /dev/null @@ -1,132 +0,0 @@ -// vim:filetype=cpp -/** @file - * @copyright University of Warsaw - * @section LICENSE - * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) - */ - -#include -#include - -namespace libcloudphxx -{ - namespace lgrngn - { - namespace detail - { - // The condition for immersion freezing - template - class immersion_freeze_cond - { - public: - BOOST_GPU_ENABLED - bool operator()(const thrust::tuple &tpl) // tpl is a tuple of 3 elements: (T_freeze, ambient T, ambient RH) - { - if (thrust::get<0>(tpl) >= thrust::get<1>(tpl) && thrust::get<2>(tpl) >= real_t(1)) // returns true if T_freeze >= ambient T and ambient RH >= 1 - return true; - else - return false; - }; - }; - - // Functor to update ice flag, rw2, a, c, rho_i, of frozen droplets - template - class freezing_update - { - public: - BOOST_GPU_ENABLED - void operator()(thrust::tuple< - real_t&, real_t&, real_t&, real_t&, real_t&, // to be updated (ice, rw2, a, c, rho_i) - const real_t&, const real_t&, const real_t& // T_freeze, T, RH - > tpl) const - { - auto& ice = thrust::get<0>(tpl); - auto& rw2 = thrust::get<1>(tpl); - auto& a = thrust::get<2>(tpl); - auto& c = thrust::get<3>(tpl); - auto& rho_i = thrust::get<4>(tpl); - - const real_t T_freeze = thrust::get<5>(tpl); - const real_t T = thrust::get<6>(tpl); - const real_t RH = thrust::get<7>(tpl); - - if (detail::immersion_freeze_cond()(thrust::make_tuple(T_freeze, T, RH))) - { - ice = real_t(1); - rw2 = real_t(0); - rho_i = common::moist_air::rho_i().value(); - a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); - c = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); - } - } - }; - - } - - // Immersion freezing - template - void particles_t::impl::ice_nucl() { - - hskpng_sort(); - - // A vector to store liquid 3rd moments - thrust_device::vector &drw(tmp_device_real_cell); - if (count_n != n_cell) - thrust::fill(drw.begin(), drw.end(), real_t(0.)); - - // Compute per-cell 3rd moment of liquid droplets (sum of n*r^3) before freezing. It is stored in count_mom - moms_eq0(ice.begin()); // choose particles with ice=0 - moms_calc(rw2.begin(), real_t(1.5)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of droplets before freezing"); - - thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, - thrust::make_permutation_iterator(drw.begin(), count_ijk.begin()), - thrust::negate() - ); - - // Change liquid droplets to ice under the freezing condition - thrust::for_each( - thrust::make_zip_iterator(thrust::make_tuple( - ice.begin(), - rw2.begin(), - ice_a.begin(), - ice_c.begin(), - ice_rho.begin(), - T_freeze.begin(), - thrust::make_permutation_iterator(T.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH.begin(), ijk.begin()) - )), - thrust::make_zip_iterator(thrust::make_tuple( - ice.begin(), - rw2.begin(), - ice_a.begin(), - ice_c.begin(), - ice_rho.begin(), - T_freeze.begin(), - thrust::make_permutation_iterator(T.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH.begin(), ijk.begin()) - )) + n_part, - detail::freezing_update() // functor for updating (ice, rw2, a, c, rho_i) if freezing condition satisfied - ); - - // Compute per-cell 3rd moment of liquid droplets after freezing. It is stored in count_mom - moms_eq0(ice.begin()); // choose particles with ice=0 - moms_calc(rw2.begin(), real_t(1.5)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of droplets after freezing"); - - // Compute the difference between liquid moments before and after freezing - thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, - thrust::make_permutation_iterator(drw.begin(), count_ijk.begin()), - thrust::make_permutation_iterator(drw.begin(), count_ijk.begin()), - thrust::plus() - ); - - // Update th according to the frozen volume per cell - update_th_freezing(drw); - - } - - } -} diff --git a/src/impl/particles_impl_ice_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp similarity index 51% rename from src/impl/particles_impl_ice_melt.ipp rename to src/impl/particles_impl_ice_nucl_melt.ipp index a4fc8cdc3..5ffb58361 100644 --- a/src/impl/particles_impl_ice_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -14,6 +14,21 @@ namespace libcloudphxx { namespace detail { + // The condition for immersion freezing + template + class immersion_freeze_cond + { + public: + BOOST_GPU_ENABLED + bool operator()(const thrust::tuple &tpl) // tpl is a tuple of 3 elements: (T_freeze, ambient T, ambient RH) + { + if (thrust::get<0>(tpl) >= thrust::get<1>(tpl) && thrust::get<2>(tpl) >= real_t(1)) // returns true if T_freeze >= ambient T and ambient RH >= 1 + return true; + else + return false; + }; + }; + // The condition for melting template class melting_cond @@ -26,6 +41,38 @@ namespace libcloudphxx }; }; + // Functor to update ice flag, rw2, a, c, rho_i, of frozen droplets + template + class freezing_update + { + public: + BOOST_GPU_ENABLED + void operator()(thrust::tuple< + real_t&, real_t&, real_t&, real_t&, real_t&, // to be updated (ice, rw2, a, c, rho_i) + const real_t&, const real_t&, const real_t& // T_freeze, T, RH + > tpl) const + { + auto& ice = thrust::get<0>(tpl); + auto& rw2 = thrust::get<1>(tpl); + auto& a = thrust::get<2>(tpl); + auto& c = thrust::get<3>(tpl); + auto& rho_i = thrust::get<4>(tpl); + + const real_t T_freeze = thrust::get<5>(tpl); + const real_t T = thrust::get<6>(tpl); + const real_t RH = thrust::get<7>(tpl); + + if (detail::immersion_freeze_cond()(thrust::make_tuple(T_freeze, T, RH))) + { + ice = real_t(1); + rw2 = real_t(0); + rho_i = common::moist_air::rho_i().value(); + a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); + c = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); + } + } + }; + // Functor to update ice flag, rw2, a, c, rho_i, of melted ice template class melting_update @@ -54,23 +101,23 @@ namespace libcloudphxx } }; - }; + } - // Melting + // Immersion freezing template - void particles_t::impl::ice_melt() { + void particles_t::impl::ice_nucl_melt() { - hskpng_sort(); + hskpng_sort(); // A vector to store liquid 3rd moments thrust_device::vector &drw(tmp_device_real_cell); - if (count_n != n_cell) - thrust::fill(drw.begin(), drw.end(), real_t(0.)); - // Compute per-cell 3rd moment of liquid droplets (sum of n*r^3) before melting. It is stored in count_mom + // Compute per-cell 3rd moment of liquid droplets (sum of n*r^3) before freezing/melting. It is stored in count_mom moms_eq0(ice.begin()); // choose particles with ice=0 moms_calc(rw2.begin(), real_t(1.5)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of droplets before melting"); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of droplets before freezing/melting"); + if (count_n != n_cell) + thrust::fill(drw.begin(), drw.end(), real_t(0.)); thrust::transform( count_mom.begin(), count_mom.begin() + count_n, @@ -78,6 +125,31 @@ namespace libcloudphxx thrust::negate() ); + // Change liquid droplets to ice under the freezing condition + thrust::for_each( + thrust::make_zip_iterator(thrust::make_tuple( + ice.begin(), + rw2.begin(), + ice_a.begin(), + ice_c.begin(), + ice_rho.begin(), + T_freeze.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH.begin(), ijk.begin()) + )), + thrust::make_zip_iterator(thrust::make_tuple( + ice.begin(), + rw2.begin(), + ice_a.begin(), + ice_c.begin(), + ice_rho.begin(), + T_freeze.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH.begin(), ijk.begin()) + )) + n_part, + detail::freezing_update() // functor for updating (ice, rw2, a, c, rho_i) if freezing condition satisfied + ); + // Change ice to liquid droplets under the melting condition thrust::for_each( thrust::make_zip_iterator(thrust::make_tuple( @@ -99,12 +171,12 @@ namespace libcloudphxx detail::melting_update() // functor for updating (ice, rw2, a, c, rho_i) if melting condition satisfied ); - // Compute per-cell 3rd moment of liquid droplets after melting. It is stored in count_mom + // Compute per-cell 3rd moment of liquid droplets after freezing/melting. It is stored in count_mom moms_eq0(ice.begin()); // choose particles with ice=0 moms_calc(rw2.begin(), real_t(1.5)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of droplets after melting"); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of droplets after freezing/melting"); - // Compute the difference between liquid moments before and after melting + // Compute the difference between liquid moments before and after thrust::transform( count_mom.begin(), count_mom.begin() + count_n, thrust::make_permutation_iterator(drw.begin(), count_ijk.begin()), @@ -112,7 +184,7 @@ namespace libcloudphxx thrust::plus() ); - // Update th according to the melted volume per cell + // Update th according to the frozen volume per cell update_th_freezing(drw); } diff --git a/src/particles.tpp b/src/particles.tpp index 001edd14c..34eff2240 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -107,8 +107,7 @@ #include "impl/particles_impl_cond_common.ipp" #include "impl/particles_impl_cond.ipp" #include "impl/particles_impl_cond_sstp.ipp" -#include "impl/particles_impl_ice_nucl.ipp" -#include "impl/particles_impl_ice_melt.ipp" +#include "impl/particles_impl_ice_nucl_melt.ipp" #include "impl/particles_impl_sedi.ipp" #include "impl/particles_impl_subs.ipp" #include "impl/particles_impl_coal.ipp" From 7eb1e71f8af3674118bfa072dfaf3bf1a7465ed4 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 16 Sep 2025 16:21:35 +0200 Subject: [PATCH 47/97] update_th_rv for cond and subl --- src/impl/particles_impl.ipp | 5 +- src/impl/particles_impl_cond.ipp | 4 +- src/impl/particles_impl_ice_nucl_melt.ipp | 2 +- src/impl/particles_impl_update_th_rv.ipp | 65 +++++------------------ src/particles_step.ipp | 6 +-- 5 files changed, 22 insertions(+), 60 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index de332a78e..4b5944aab 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -295,6 +295,8 @@ namespace libcloudphxx // max(1, n) int m1(int n) { return n == 0 ? 1 : n; } + enum class phase_change { condensation, sublimation }; // enum for choosing between phase change types + // ctor impl(const opts_init_t &_opts_init, const std::pair &bcond, const int &mpi_rank, const int &mpi_size, const int &n_x_tot) : init_called(false), @@ -620,8 +622,7 @@ namespace libcloudphxx void cond_sstp(const real_t &dt, const real_t &RH_max, const bool turb_cond); template void cond_sstp_hlpr(const real_t &dt, const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, const RH_iter &rhi, const RHi_iter &rhii); - void update_th_rv(thrust_device::vector &); - void update_th_rv_subl(thrust_device::vector &); + void update_th_rv(thrust_device::vector &, phase_change = phase_change::condensation); void update_th_freezing(thrust_device::vector &); void update_state(thrust_device::vector &, thrust_device::vector &); void update_pstate(thrust_device::vector &, thrust_device::vector &); diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index bd5e2ca51..c6c70cd78 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -168,8 +168,8 @@ namespace libcloudphxx ); // update th and rv according to changes in third specific wet moments - update_th_rv(drv_liq); - update_th_rv_subl(drv_ice); + update_th_rv(drv_liq, impl::phase_change::condensation); + update_th_rv(drv_ice, impl::phase_change::sublimation); } }; }; \ No newline at end of file diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp index 5ffb58361..0f4843217 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -103,7 +103,7 @@ namespace libcloudphxx } - // Immersion freezing + // Immersion freezing and melting template void particles_t::impl::ice_nucl_melt() { diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/particles_impl_update_th_rv.ipp index eb8c7db05..2c91f8d5c 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/particles_impl_update_th_rv.ipp @@ -13,6 +13,7 @@ namespace libcloudphxx { namespace detail { + // change of th during condensation template struct dth //: thrust::unary_function&, real_t> { @@ -29,7 +30,7 @@ namespace libcloudphxx return drv * common::theta_dry::d_th_d_rv(T, th) / si::kelvins; } }; - + // change of th during sublimation template struct dth_subl //: thrust::unary_function&, real_t> { @@ -47,7 +48,7 @@ namespace libcloudphxx } }; - + // change of th during freezing template struct dth_freezing //: thrust::unary_function&, real_t> { @@ -66,11 +67,12 @@ namespace libcloudphxx }; }; - // update th and rv after condensation according to change in 3rd specific wet moments + // update th and rv after condensation / sublimation according to change in 3rd specific wet moments // particles have to be sorted template void particles_t::impl::update_th_rv( - thrust_device::vector &drv // change in water vapor mixing ratio + thrust_device::vector &drv, // change in water vapor mixing ratio + phase_change phase // enum for cond/subl, the default is condensation ) { if(!sorted) throw std::runtime_error("libcloudph++: update_th_rv called on an unsorted set"); @@ -107,11 +109,13 @@ namespace libcloudphxx > > zip_it_t; // apply dth + if (phase == phase_change::condensation) + { thrust::transform( th.begin(), th.end(), // input - 1st arg thrust::make_transform_iterator( - zip_it_t(thrust::make_tuple( - drv.begin(), // + zip_it_t(thrust::make_tuple( + drv.begin(), // T.begin(), // dth = drv * d_th_d_rv(T, th) th.begin() // )), @@ -121,51 +125,8 @@ namespace libcloudphxx thrust::plus() ); } - nancheck(th, "update_th_rv: th after update"); - } - - - // update th and rv after sublimation according to change in 3rd specific wet moments - // particles have to be sorted - template - void particles_t::impl::update_th_rv_subl( - thrust_device::vector &drv // change in water vapor mixing ratio - ) - { - if(!sorted) throw std::runtime_error("libcloudph++: update_th_rv called on an unsorted set"); - nancheck(drv, "update_th_rv: input drv"); - - // multiplying specific 3rd moms diff by -rho_w*4/3*pi - thrust::transform( - drv.begin(), drv.end(), // input - 1st arg - thrust::make_constant_iterator( // input - 2nd arg - - common::moist_air::rho_i() / si::kilograms * si::cubic_metres - * real_t(4./3) * pi() - ), - drv.begin(), // output - thrust::multiplies() - ); - - // updating rv - assert(*thrust::min_element(rv.begin(), rv.end()) >= 0); - thrust::transform( - rv.begin(), rv.end(), // input - 1st arg - drv.begin(), // input - 2nd arg - rv.begin(), // output - thrust::plus() - ); - assert(*thrust::min_element(rv.begin(), rv.end()) >= 0); - nancheck(rv, "update_th_rv: rv after update"); - - // updating th + else if (phase == phase_change::sublimation) { - typedef thrust::zip_iterator::iterator, - typename thrust_device::vector::iterator, - typename thrust_device::vector::iterator - > > zip_it_t; - - // apply dth thrust::transform( th.begin(), th.end(), // input - 1st arg thrust::make_transform_iterator( @@ -179,6 +140,8 @@ namespace libcloudphxx th.begin(), // output thrust::plus() ); + } + } nancheck(th, "update_th_rv: th after update"); } @@ -229,7 +192,7 @@ namespace libcloudphxx } nancheck(th, "update_th_freezing: th after update"); } - + // update particle-specific cell state // particles have to be sorted template diff --git a/src/particles_step.ipp b/src/particles_step.ipp index 59da15cc8..58d47b1d8 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -195,8 +195,7 @@ namespace libcloudphxx if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); if (opts.ice_nucl) - pimpl->ice_nucl(); - pimpl->ice_melt(); + pimpl->ice_nucl_melt(); pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } // copy sstp_tmp_rv and th to rv and th @@ -213,8 +212,7 @@ namespace libcloudphxx pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); pimpl->hskpng_Tpr(); if (opts.ice_nucl) - pimpl->ice_nucl(); - pimpl->ice_melt(); + pimpl->ice_nucl_melt(); pimpl->cond(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } } From 4773d91ba41d54ad604abd60d752990d2ef0d3d7 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 16 Sep 2025 16:40:29 +0200 Subject: [PATCH 48/97] ice volume vector --- src/impl/particles_impl_cond.ipp | 38 +++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index c6c70cd78..a0ac04671 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -62,12 +62,24 @@ namespace libcloudphxx thrust::negate() ); + // Vector for ice volumes + thrust_device::vector ice_vols(tmp_device_real_part); + + thrust::transform( + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin() + n_part, ice_c.begin() + n_part)), + ice_vols.begin(), + detail::ice_vol() + ); + // Compute per-cell 3rd moment of ice before sublimation. It is stored in count_mom moms_gt0(ice.begin()); // choose particles with ice=1 - moms_calc(thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() - ), - real_t(1)); + moms_calc(ice_vols.begin(), real_t(1)); + + // moms_calc(thrust::make_transform_iterator( + // thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() + // ), + // real_t(1)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) before sublimation"); if(count_n!=n_cell) { @@ -152,12 +164,22 @@ namespace libcloudphxx thrust::plus() ); + // Calculate ice volumes + thrust::transform( + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin() + n_part, ice_c.begin() + n_part)), + ice_vols.begin(), + detail::ice_vol() + ); + // Compute per-cell 3rd moment of ice after sublimation. It is stored in count_mom moms_gt0(ice.begin()); // choose particles with ice=1 - moms_calc(thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() - ), - real_t(1)); + moms_calc(ice_vols.begin(), real_t(1)); + + // moms_calc(thrust::make_transform_iterator( + // thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() + // ), + // real_t(1)); // Adding the third ice moment after sublimation to drv_ice thrust::transform( From fc816177e095f4499e4c2ff284302c61e3e24731 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 19 Sep 2025 15:33:57 +0200 Subject: [PATCH 49/97] template for iterator type --- src/impl/particles_impl.ipp | 8 +++-- src/impl/particles_impl_cond.ipp | 56 +++++++++----------------------- src/impl/particles_impl_moms.ipp | 18 ++++------ 3 files changed, 27 insertions(+), 55 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 4b5944aab..09cd22405 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -580,15 +580,17 @@ namespace libcloudphxx const real_t &min, const real_t &max, const typename thrust_device::vector::iterator &vec_bgn, const bool cons - ); + ); + template // iterator type void moms_calc( - const typename thrust_device::vector::iterator &vec_bgn, + const it_t &vec_bgn, const thrust_size_t npart, const real_t power, const bool specific = true ); + template // iterator type void moms_calc( - const typename thrust_device::vector::iterator &vec_bgn, + const it_t &vec_bgn, const real_t power, const bool specific = true ); diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index a0ac04671..415d1262e 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -17,14 +17,10 @@ namespace libcloudphxx class ice_vol { public: - using result_type = real_t; - template BOOST_GPU_ENABLED - real_t operator()(Tuple const &tpl) const + real_t operator()(const thrust::tuple &tpl) // tpl is a tuple (a, c) { - real_t a = thrust::get<0>(tpl); - real_t c = thrust::get<1>(tpl); - return a * a * c; + return thrust::get<0>(tpl) * thrust::get<0>(tpl) * thrust::get<1>(tpl); // a * a * c } }; } @@ -62,26 +58,13 @@ namespace libcloudphxx thrust::negate() ); - // Vector for ice volumes - thrust_device::vector ice_vols(tmp_device_real_part); - - thrust::transform( - thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), - thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin() + n_part, ice_c.begin() + n_part)), - ice_vols.begin(), - detail::ice_vol() - ); - - // Compute per-cell 3rd moment of ice before sublimation. It is stored in count_mom + // Compute per-cell 3rd moment of ice before deposition. It is stored in count_mom moms_gt0(ice.begin()); // choose particles with ice=1 - moms_calc(ice_vols.begin(), real_t(1)); - - // moms_calc(thrust::make_transform_iterator( - // thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() - // ), - // real_t(1)); - - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) before sublimation"); + moms_calc(thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() + ), + real_t(1)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) before deposition"); if(count_n!=n_cell) { thrust::fill(drv_ice.begin(), drv_ice.end(), real_t(0.)); } @@ -164,24 +147,15 @@ namespace libcloudphxx thrust::plus() ); - // Calculate ice volumes - thrust::transform( - thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), - thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin() + n_part, ice_c.begin() + n_part)), - ice_vols.begin(), - detail::ice_vol() - ); - - // Compute per-cell 3rd moment of ice after sublimation. It is stored in count_mom + // Compute per-cell 3rd moment of ice after deposition. It is stored in count_mom moms_gt0(ice.begin()); // choose particles with ice=1 - moms_calc(ice_vols.begin(), real_t(1)); - - // moms_calc(thrust::make_transform_iterator( - // thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() - // ), - // real_t(1)); + moms_calc(thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() + ), + real_t(1)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) after deposition"); - // Adding the third ice moment after sublimation to drv_ice + // Adding the third ice moment after deposition to drv_ice thrust::transform( count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // input - 2nd arg diff --git a/src/impl/particles_impl_moms.ipp b/src/impl/particles_impl_moms.ipp index 2a07773e3..36b4ef0aa 100644 --- a/src/impl/particles_impl_moms.ipp +++ b/src/impl/particles_impl_moms.ipp @@ -232,8 +232,9 @@ namespace libcloudphxx }; template + template // iterator type void particles_t::impl::moms_calc( - const typename thrust_device::vector::iterator &vec_bgn, + const it_t &vec_bgn, const thrust_size_t npart, const real_t power, const bool specific @@ -244,12 +245,6 @@ namespace libcloudphxx // same as above thrust_device::vector &n_filtered(tmp_device_real_part); - typedef thrust::permutation_iterator< - typename thrust_device::vector::const_iterator, - typename thrust_device::vector::iterator - > pi_t; - typedef thrust::zip_iterator > zip_it_t; - thrust::pair< thrust_device::vector::iterator, typename thrust_device::vector::iterator @@ -258,9 +253,9 @@ namespace libcloudphxx sorted_ijk.begin(), sorted_ijk.begin()+npart, // input - values thrust::make_transform_iterator( - zip_it_t(thrust::make_tuple( - pi_t(n_filtered.begin(), sorted_id.begin()), - pi_t(vec_bgn, sorted_id.begin()) + thrust::make_zip_iterator(thrust::make_tuple( + thrust::make_permutation_iterator(n_filtered.begin(), sorted_id.begin()), + thrust::make_permutation_iterator(vec_bgn, sorted_id.begin()) )), detail::moment_counter(power) ), @@ -352,8 +347,9 @@ namespace libcloudphxx } template + template // iterator type void particles_t::impl::moms_calc( - const typename thrust_device::vector::iterator &vec_bgn, + const it_t &vec_bgn, const real_t power, const bool specific ) From 1714c39808374e48c7342772aaf43067761addcf Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 30 Sep 2025 09:54:04 +0200 Subject: [PATCH 50/97] init insol dry sizes --- src/impl/particles_impl.ipp | 2 +- .../particles_impl_init_insol_dry_sizes.ipp | 23 ++++++++++++++ src/impl/particles_impl_init_rd3_insol.ipp | 30 ------------------- src/particles.tpp | 2 +- 4 files changed, 25 insertions(+), 32 deletions(-) create mode 100644 src/impl/particles_impl_init_insol_dry_sizes.ipp delete mode 100644 src/impl/particles_impl_init_rd3_insol.ipp diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 09cd22405..2555d4a6b 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -508,7 +508,7 @@ namespace libcloudphxx void init_xyz(); void init_kappa(const real_t &); void init_ice(const real_t &); - void init_rd3_insol(const real_t &); + void init_insol_dry_sizes(const real_t &); void init_T_freeze(); void init_a_c_rho_ice(); void init_incloud_time(); diff --git a/src/impl/particles_impl_init_insol_dry_sizes.ipp b/src/impl/particles_impl_init_insol_dry_sizes.ipp new file mode 100644 index 000000000..074391b97 --- /dev/null +++ b/src/impl/particles_impl_init_insol_dry_sizes.ipp @@ -0,0 +1,23 @@ +// vim:filetype=cpp +/** @file + * @copyright University of Warsaw + * @section LICENSE + * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) + * @brief initialisation routine for super droplets + */ + + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::init_insol_dry_sizes( + real_t radius + ) + { + real_t rad3 = radius * radius * radius; + thrust::fill(rd3_insol.begin() + n_part_old, rd3_insol.end(), rad3); + } + }; +}; diff --git a/src/impl/particles_impl_init_rd3_insol.ipp b/src/impl/particles_impl_init_rd3_insol.ipp deleted file mode 100644 index e0361cad2..000000000 --- a/src/impl/particles_impl_init_rd3_insol.ipp +++ /dev/null @@ -1,30 +0,0 @@ -// vim:filetype=cpp -/** @file - * @copyright University of Warsaw - * @section LICENSE - * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) - * @brief initialisation routine for super droplets - */ - -namespace libcloudphxx -{ - namespace lgrngn - { - template - void particles_t::impl::init_rd3_insol( - const real_t &rd_insol // fixed rd of insoluble particles - ) - { - // half of super-particles contain insoluble substance - thrust::fill(rd3_insol.begin() + n_part_old, - rd3_insol.begin() + n_part_old + (rd3_insol.size() - n_part_old) / 2, - rd_insol * rd_insol * rd_insol); - - // the other half contain soluble substances (rd_insol = 0) - thrust::fill(rd3_insol.begin() + n_part_old + (rd3_insol.size() - n_part_old) / 2, - rd3_insol.end(), - real_t(0)); - } - }; -}; - diff --git a/src/particles.tpp b/src/particles.tpp index 34eff2240..45eae00ff 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -64,7 +64,7 @@ #include "impl/particles_impl_init_dry_dry_sizes.ipp" #include "impl/particles_impl_init_kappa.ipp" #include "impl/particles_impl_init_ice.ipp" -#include "impl/particles_impl_init_rd3_insol.ipp" +#include "impl/particles_impl_init_insol_dry_sizes.ipp" #include "impl/particles_impl_init_T_freeze.ipp" #include "impl/particles_impl_init_a_c_rho_ice.ipp" #include "impl/particles_impl_init_incloud_time.ipp" From 9215dc4e311866e1882869b7e322e12914fb8737 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 30 Sep 2025 10:08:41 +0200 Subject: [PATCH 51/97] change rd3_insol to rd2_insol --- include/libcloudph++/common/ice_nucleation.hpp | 4 ++-- src/impl/particles_impl.ipp | 4 ++-- src/impl/particles_impl_fill_outbuf.ipp | 4 ++-- src/impl/particles_impl_init_SD_with_distros.ipp | 8 ++++---- src/impl/particles_impl_init_SD_with_sizes.ipp | 2 +- src/impl/particles_impl_init_T_freeze.ipp | 4 ++-- src/impl/particles_impl_init_insol_dry_sizes.ipp | 4 ++-- src/impl/particles_impl_reserve_hskpng_npart.ipp | 2 +- src/impl/particles_impl_src_dry_distros_simple.ipp | 2 +- src/impl/particles_impl_src_dry_sizes.ipp | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index 3e01de18b..97eddb4e9 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -15,10 +15,10 @@ namespace libcloudphxx BOOST_GPU_ENABLED quantity T_freeze_CDF_inv( const INP_t& INP_type, // type of ice nucleating particle - const quantity rd3_insol, // radius cubed of ice nucleating (insoluble) particle + const quantity rd2_insol, // radius squared of ice nucleating (insoluble) particle const real_t rand // random number between [0, 1] ) { - real_t A = real_t(4) * pi() * std::pow(rd3_insol/si::meters, 2/3); // surface area of the insoluble particle + real_t A = real_t(4) * pi() * rd2_insol; // surface area of the insoluble particle if (INP_type == INP_t::mineral && A > std::numeric_limits::epsilon()) { diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 2555d4a6b..79889d2a0 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -86,7 +86,7 @@ namespace libcloudphxx sstp_tmp_p, // ditto for pressure incloud_time, // time this SD has been within a cloud ice, // 0 - water 1 - ice; bool would suffice, but we are lazy - rd3_insol, // dry radii of insoluble aerosol cubed + rd2_insol, // dry radii squared of insoluble aerosol T_freeze, // freezing temperature ice_a, // equatorial radius of ice ice_c, // polar radius of ice @@ -461,7 +461,7 @@ namespace libcloudphxx if(opts_init.ice_switch) { distmem_real_vctrs.insert({&ice, detail::no_initial_value}); - distmem_real_vctrs.insert({&rd3_insol, detail::no_initial_value}); + distmem_real_vctrs.insert({&rd2_insol, detail::no_initial_value}); distmem_real_vctrs.insert({&T_freeze, detail::no_initial_value}); distmem_real_vctrs.insert({&ice_a, detail::no_initial_value}); distmem_real_vctrs.insert({&ice_c, detail::no_initial_value}); diff --git a/src/impl/particles_impl_fill_outbuf.ipp b/src/impl/particles_impl_fill_outbuf.ipp index 9af0b039f..6c76e91ca 100644 --- a/src/impl/particles_impl_fill_outbuf.ipp +++ b/src/impl/particles_impl_fill_outbuf.ipp @@ -40,7 +40,7 @@ namespace libcloudphxx template std::vector particles_t::impl::fill_attr_outbuf(const std::string &name) { - const std::set attr_names = {"rw2", "rd3", "kappa", "rd3_insol", "T_freeze", "ice_a", "ice_c", "ice_rho", "x", "y", "z"}; // TODO implement "n" - it is n_t type and others are real_t + const std::set attr_names = {"rw2", "rd3", "kappa", "rd2_insol", "T_freeze", "ice_a", "ice_c", "ice_rho", "x", "y", "z"}; // TODO implement "n" - it is n_t type and others are real_t if (std::find(std::begin(attr_names), std::end(attr_names), name) == std::end(attr_names)) throw std::runtime_error("Unknown attribute name passed to get_attr."); @@ -48,7 +48,7 @@ namespace libcloudphxx name == "rw2" ? rw2 : name == "rd3" ? rd3 : name == "kappa" ? kpa : - name == "rd3_insol" ? rd3_insol : + name == "rd2_insol" ? rd2_insol : name == "T_freeze" ? T_freeze : name == "ice_a" ? ice_a : name == "ice_c" ? ice_c : diff --git a/src/impl/particles_impl_init_SD_with_distros.ipp b/src/impl/particles_impl_init_SD_with_distros.ipp index 5e6ee4262..ddd861243 100644 --- a/src/impl/particles_impl_init_SD_with_distros.ipp +++ b/src/impl/particles_impl_init_SD_with_distros.ipp @@ -50,13 +50,13 @@ namespace libcloudphxx // final inits common for tail/sd_conc/const_multi template - void particles_t::impl::init_SD_with_distros_finalize(const std::pair &kpa_rd3_insol, const bool unravel_ijk_switch) + void particles_t::impl::init_SD_with_distros_finalize(const std::pair &kpa_rd2_insol, const bool unravel_ijk_switch) { // init kappa - init_kappa(kpa_rd3_insol.first); + init_kappa(kpa_rd2_insol.first); - // init rd3_insol - init_rd3_insol(kpa_rd3_insol.second); + // init rd2_insol + init_insol_dry_sizes(kpa_rd2_insol.second); // initialising wet radii init_wet(); diff --git a/src/impl/particles_impl_init_SD_with_sizes.ipp b/src/impl/particles_impl_init_SD_with_sizes.ipp index 200c5e9ff..ce36847ae 100644 --- a/src/impl/particles_impl_init_SD_with_sizes.ipp +++ b/src/impl/particles_impl_init_SD_with_sizes.ipp @@ -47,7 +47,7 @@ namespace libcloudphxx // init kappa and ice init_kappa(kappa); - init_rd3_insol(ice); + init_insol_dry_sizes(ice); // init multiplicities init_n_dry_sizes(sni->second.first, sni->second.second); diff --git a/src/impl/particles_impl_init_T_freeze.ipp b/src/impl/particles_impl_init_T_freeze.ipp index b7b6eed9b..e66ea4300 100644 --- a/src/impl/particles_impl_init_T_freeze.ipp +++ b/src/impl/particles_impl_init_T_freeze.ipp @@ -23,8 +23,8 @@ namespace libcloudphxx rand_u01(n_part_to_init); thrust::transform( - thrust::make_zip_iterator(thrust::make_tuple(rd3_insol.begin() + n_part_old, u01.begin() + n_part_old)), - thrust::make_zip_iterator(thrust::make_tuple(rd3_insol.end(), u01.end())), + thrust::make_zip_iterator(thrust::make_tuple(rd2_insol.begin() + n_part_old, u01.begin() + n_part_old)), + thrust::make_zip_iterator(thrust::make_tuple(rd2_insol.end(), u01.end())), T_freeze.begin() + n_part_old, T_freeze_CDF_inv_functor(inp_type) ); diff --git a/src/impl/particles_impl_init_insol_dry_sizes.ipp b/src/impl/particles_impl_init_insol_dry_sizes.ipp index 074391b97..3facc1a97 100644 --- a/src/impl/particles_impl_init_insol_dry_sizes.ipp +++ b/src/impl/particles_impl_init_insol_dry_sizes.ipp @@ -16,8 +16,8 @@ namespace libcloudphxx real_t radius ) { - real_t rad3 = radius * radius * radius; - thrust::fill(rd3_insol.begin() + n_part_old, rd3_insol.end(), rad3); + real_t rad2 = radius * radius; + thrust::fill(rd2_insol.begin() + n_part_old, rd2_insol.end(), rad2); } }; }; diff --git a/src/impl/particles_impl_reserve_hskpng_npart.ipp b/src/impl/particles_impl_reserve_hskpng_npart.ipp index a2b78f48a..776122af8 100644 --- a/src/impl/particles_impl_reserve_hskpng_npart.ipp +++ b/src/impl/particles_impl_reserve_hskpng_npart.ipp @@ -39,7 +39,7 @@ namespace libcloudphxx if(opts_init.ice_switch) { ice.reserve(opts_init.n_sd_max); - rd3_insol.reserve(opts_init.n_sd_max); + rd2_insol.reserve(opts_init.n_sd_max); T_freeze.reserve(opts_init.n_sd_max); ice_a.reserve(opts_init.n_sd_max); ice_c.reserve(opts_init.n_sd_max); diff --git a/src/impl/particles_impl_src_dry_distros_simple.ipp b/src/impl/particles_impl_src_dry_distros_simple.ipp index dddf5e913..3e1d01dfa 100644 --- a/src/impl/particles_impl_src_dry_distros_simple.ipp +++ b/src/impl/particles_impl_src_dry_distros_simple.ipp @@ -58,7 +58,7 @@ namespace libcloudphxx init_kappa( p_sdd->first.first ); - init_rd3_insol( + init_insol_dry_sizes( p_sdd->first.second ); diff --git a/src/impl/particles_impl_src_dry_sizes.ipp b/src/impl/particles_impl_src_dry_sizes.ipp index d2c4f9904..9933b8d63 100644 --- a/src/impl/particles_impl_src_dry_sizes.ipp +++ b/src/impl/particles_impl_src_dry_sizes.ipp @@ -55,7 +55,7 @@ namespace libcloudphxx // init kappa and ice init_kappa(kappa); - init_rd3_insol(ice); + init_insol_dry_sizes(ice); // init multiplicities init_n_dry_sizes(get<0>(sni->second)*sup_dt, get<1>(sni->second)); From 11cf48976fafef2ab43da5bfbbde5e9d21f16fe0 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 30 Sep 2025 10:12:15 +0200 Subject: [PATCH 52/97] rd2_insol in T_freeze --- include/libcloudph++/common/ice_nucleation.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index 97eddb4e9..c0e2ff867 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -42,12 +42,12 @@ namespace libcloudphxx BOOST_GPU_ENABLED real_t operator()(const thrust::tuple &tpl) const { - const real_t &rd3_insol = thrust::get<0>(tpl); // from rd3 vector + const real_t &rd2_insol = thrust::get<0>(tpl); // from rd2 vector const real_t &rand = thrust::get<1>(tpl); // from rand vector return ice_nucleation::T_freeze_CDF_inv( INP_type, - rd3_insol * si::meters, + rd2_insol * si::meters, rand ).value(); } From e7681067df3cbf11eca84000b5a17c8b5f12a8fa Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 1 Oct 2025 04:03:45 +0200 Subject: [PATCH 53/97] fixing rd_insol init --- include/libcloudph++/common/ice_nucleation.hpp | 4 ++-- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_init_SD_with_sizes.ipp | 10 +++++----- src/impl/particles_impl_src_dry_sizes.ipp | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index c0e2ff867..befa90e65 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -15,7 +15,7 @@ namespace libcloudphxx BOOST_GPU_ENABLED quantity T_freeze_CDF_inv( const INP_t& INP_type, // type of ice nucleating particle - const quantity rd2_insol, // radius squared of ice nucleating (insoluble) particle + const real_t rd2_insol, // radius squared of insoluble particle in meters const real_t rand // random number between [0, 1] ) { real_t A = real_t(4) * pi() * rd2_insol; // surface area of the insoluble particle @@ -47,7 +47,7 @@ namespace libcloudphxx return ice_nucleation::T_freeze_CDF_inv( INP_type, - rd2_insol * si::meters, + rd2_insol, rand ).value(); } diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 79889d2a0..6232a4e50 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -508,7 +508,7 @@ namespace libcloudphxx void init_xyz(); void init_kappa(const real_t &); void init_ice(const real_t &); - void init_insol_dry_sizes(const real_t &); + void init_insol_dry_sizes(real_t); void init_T_freeze(); void init_a_c_rho_ice(); void init_incloud_time(); diff --git a/src/impl/particles_impl_init_SD_with_sizes.ipp b/src/impl/particles_impl_init_SD_with_sizes.ipp index ce36847ae..345285669 100644 --- a/src/impl/particles_impl_init_SD_with_sizes.ipp +++ b/src/impl/particles_impl_init_SD_with_sizes.ipp @@ -19,14 +19,14 @@ namespace libcloudphxx //using conc_multi_t = typename size_number_t::mapped_type; - // loop over (kappa, ice) pairs + // loop over (kappa, rd_insol) pairs for (auto dsi = opts_init.dry_sizes.cbegin(); dsi != opts_init.dry_sizes.cend(); ++dsi) { const real_t &kappa(dsi->first.first); - const real_t &ice(dsi->first.second); + const real_t &rd_insol(dsi->first.second); const auto &size_number_map(dsi->second); - // loop over the "size : {concentration, count}" pairs for this (kappa, ice) pair + // loop over the "size : {concentration, count}" pairs for this (kappa, rd_insol) pair for (auto sni = size_number_map.cbegin(); sni != size_number_map.cend(); ++sni) { // init number of SDs of this kappa in cells @@ -45,9 +45,9 @@ namespace libcloudphxx // initialising dry radii (needs ijk) init_dry_dry_sizes(sni->first); - // init kappa and ice + // init kappa and rd_insol init_kappa(kappa); - init_insol_dry_sizes(ice); + init_insol_dry_sizes(rd_insol); // init multiplicities init_n_dry_sizes(sni->second.first, sni->second.second); diff --git a/src/impl/particles_impl_src_dry_sizes.ipp b/src/impl/particles_impl_src_dry_sizes.ipp index 9933b8d63..122f8c206 100644 --- a/src/impl/particles_impl_src_dry_sizes.ipp +++ b/src/impl/particles_impl_src_dry_sizes.ipp @@ -20,15 +20,15 @@ namespace libcloudphxx //using conc_multi_t = typename size_number_t::mapped_type; - // loop over (kappa, ice) pairs + // loop over (kappa, rd_insol) pairs // for (typename dry_sizes_t::const_iterator dsi = opts.src_dry_sizes.begin(); dsi != opts.src_dry_sizes.end(); ++dsi) for (auto dsi = sds.cbegin(); dsi != sds.cend(); ++dsi) { const real_t &kappa(dsi->first.first); - const real_t &ice(dsi->first.second); + const real_t &rd_insol(dsi->first.second); const auto &size_number_map(dsi->second); - // loop over the "size : {concentration per second, multiplicity, supstp}" for this (kappa, ice) pair + // loop over the "size : {concentration per second, multiplicity, supstp}" for this (kappa, rd_insol) pair for (auto sni = size_number_map.cbegin(); sni != size_number_map.cend(); ++sni) { // add the source only once every number of steps @@ -53,9 +53,9 @@ namespace libcloudphxx // initialising dry radii (needs ijk) init_dry_dry_sizes(sni->first); - // init kappa and ice + // init kappa and rd_insol init_kappa(kappa); - init_insol_dry_sizes(ice); + init_insol_dry_sizes(rd_insol); // init multiplicities init_n_dry_sizes(get<0>(sni->second)*sup_dt, get<1>(sni->second)); From 39f101c337713045cc8053686265b47d0f7dd2b4 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 14 Oct 2025 16:15:33 +0900 Subject: [PATCH 54/97] probability for time-dependent freezing --- .../libcloudph++/common/ice_nucleation.hpp | 56 +++++++++++++++++-- src/impl/particles_impl_ice_nucl_melt.ipp | 6 +- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index befa90e65..eb2a9cb19 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -1,6 +1,7 @@ #pragma once #include "units.hpp" +#include "const_cp.hpp" namespace libcloudphxx { @@ -10,13 +11,13 @@ namespace libcloudphxx { enum class INP_t {mineral}; // types of ice nucleating particles, TODO: add more types - // Inverse CDF for freezing temperature as defined in eq. 1 in Shima et al., 2020 + // Inverse CDF for singular freezing temperature as defined in eq. 1 in Shima et al., 2020 template BOOST_GPU_ENABLED quantity T_freeze_CDF_inv( - const INP_t& INP_type, // type of ice nucleating particle - const real_t rd2_insol, // radius squared of insoluble particle in meters - const real_t rand // random number between [0, 1] + const INP_t& INP_type, // type of ice nucleating particle + const real_t rd2_insol, // radius squared of insoluble particle in m^2 + const real_t rand // random number between [0, 1] ) { real_t A = real_t(4) * pi() * rd2_insol; // surface area of the insoluble particle @@ -53,6 +54,53 @@ namespace libcloudphxx } }; + // Probability of time-dependent freezing as in Arabas et al., 2025 + template + BOOST_GPU_ENABLED + real_t p_freeze( + const INP_t& INP_type, // type of ice nucleating particle + const real_t rd2_insol, // radius squared of insoluble particle in m^2 + const real_t T, // temperature in kelvin + const real_t dt // time step in seconds + ) { + + real_t A = real_t(4) * pi() * rd2_insol; // surface area of the insoluble particle + real_t d_aw = real_t(1) - const_cp::p_vsi(T)/ const_cp::p_vs(T); // water activity + + if (INP_type == INP_t::mineral) + { + real_t J = std::pow(real_t(10), real_t(-1.35) + real_t(22.62) * d_aw); // nucleation rate + return 1 - std::exp(- J * A * dt); + } + else + throw std::runtime_error("INP type not implemented"); + } + + + template + struct p_freeze_functor + { + INP_t INP_type; + real_t dt; + + p_freeze_functor(INP_t INP_type, real_t dt) + : INP_type(INP_type), dt(dt) {} + + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) const + { + const real_t &rd2_insol = thrust::get<0>(tpl); // radius squared of insoluble particle + const real_t &T = thrust::get<1>(tpl); // temperature in kelvin + + return ice_nucleation::p_freeze( + INP_type, + rd2_insol, + T, + dt + ); + } + }; + }; }; }; diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp index 0f4843217..d1871c21e 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -14,9 +14,9 @@ namespace libcloudphxx { namespace detail { - // The condition for immersion freezing + // The condition for singular immersion freezing (Shima et al., 2020) template - class immersion_freeze_cond + class singular_freeze_cond { public: BOOST_GPU_ENABLED @@ -62,7 +62,7 @@ namespace libcloudphxx const real_t T = thrust::get<6>(tpl); const real_t RH = thrust::get<7>(tpl); - if (detail::immersion_freeze_cond()(thrust::make_tuple(T_freeze, T, RH))) + if (detail::singular_freeze_cond()(thrust::make_tuple(T_freeze, T, RH))) { ice = real_t(1); rw2 = real_t(0); From 4684e3832bf7bce0376187f177ace0523ee3d85d Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 14 Oct 2025 16:40:46 +0900 Subject: [PATCH 55/97] time dep freeze condition --- src/impl/particles_impl_ice_nucl_melt.ipp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp index d1871c21e..e9c1829aa 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -29,6 +29,28 @@ namespace libcloudphxx }; }; + // The condition for time-dependent immersion freezing (Arabas et al., 2025) + template + struct time_dep_freeze_cond + { + common::ice_nucleation::INP_t INP_type; + real_t dt; + + time_dep_freeze_cond(common::ice_nucleation::INP_t INP_type, real_t dt) + : INP_type(INP_type), dt(dt) {} + + BOOST_GPU_ENABLED + bool operator()(const thrust::tuple &tpl) const + { + const real_t &rnd = thrust::get<0>(tpl); // random number between [0, 1] + const real_t &rd2_insol = thrust::get<1>(tpl); // radius squared of insoluble particle + const real_t &T = thrust::get<2>(tpl); // temperature + + return rnd < common::ice_nucleation::p_freeze(INP_type, rd2_insol, T, dt); + } + }; + + // The condition for melting template class melting_cond From 0e649f94bca5b62ba58b0577a1d3ba5c5f22ab1f Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 15 Oct 2025 14:09:36 +0900 Subject: [PATCH 56/97] time dependent freezing option --- include/libcloudph++/lgrngn/opts.hpp | 4 +- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_ice_nucl_melt.ipp | 165 ++++++++++++---------- src/particles_step.ipp | 4 +- 4 files changed, 99 insertions(+), 76 deletions(-) diff --git a/include/libcloudph++/lgrngn/opts.hpp b/include/libcloudph++/lgrngn/opts.hpp index d01181769..b5b993f10 100644 --- a/include/libcloudph++/lgrngn/opts.hpp +++ b/include/libcloudph++/lgrngn/opts.hpp @@ -20,7 +20,7 @@ namespace libcloudphxx struct opts_t { // process toggling - bool adve, sedi, subs, cond, coal, src, rlx, rcyc, turb_adve, turb_cond, turb_coal, ice_nucl; + bool adve, sedi, subs, cond, coal, src, rlx, rcyc, turb_adve, turb_cond, turb_coal, ice_nucl, time_dep_ice_nucl; // RH limit for drop growth real_t RH_max; @@ -42,7 +42,7 @@ namespace libcloudphxx opts_t() : adve(true), sedi(true), subs(false), cond(true), coal(true), src(false), rlx(false), rcyc(false), chem_dsl(false), chem_dsc(false), chem_rct(false), - turb_adve(false), turb_cond(false), turb_coal(false), ice_nucl(false), + turb_adve(false), turb_cond(false), turb_coal(false), ice_nucl(false), time_dep_ice_nucl(false), RH_max(44), // :) (anything greater than 1.1 would be enough dt(-1) // negative means that we do not override dt in this step { diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 6232a4e50..31098b97c 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -617,7 +617,7 @@ namespace libcloudphxx void sedi(const real_t &dt); void subs(const real_t &dt); - void ice_nucl_melt(); + void ice_nucl_melt(const bool time_dep_ice_nucl); // 0 for singular freezing, 1 for time-dependent void cond_dm3_helper(); void cond(const real_t &dt, const real_t &RH_max, const bool turb_cond); diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp index e9c1829aa..42ff01de9 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -14,64 +14,56 @@ namespace libcloudphxx { namespace detail { - // The condition for singular immersion freezing (Shima et al., 2020) + // Singular immersion freezing (Shima et al., 2020) + // Functor to update ice flag, rw2, a, c, rho_i, of frozen droplets template - class singular_freeze_cond + class singular_freeze { public: BOOST_GPU_ENABLED - bool operator()(const thrust::tuple &tpl) // tpl is a tuple of 3 elements: (T_freeze, ambient T, ambient RH) + void operator()(thrust::tuple< + real_t&, real_t&, real_t&, real_t&, real_t&, // to be updated (ice, rw2, a, c, rho_i) + const real_t&, const real_t&, const real_t& // T_freeze, T, RH + > tpl) const { - if (thrust::get<0>(tpl) >= thrust::get<1>(tpl) && thrust::get<2>(tpl) >= real_t(1)) // returns true if T_freeze >= ambient T and ambient RH >= 1 - return true; - else - return false; - }; - }; - - // The condition for time-dependent immersion freezing (Arabas et al., 2025) - template - struct time_dep_freeze_cond - { - common::ice_nucleation::INP_t INP_type; - real_t dt; - - time_dep_freeze_cond(common::ice_nucleation::INP_t INP_type, real_t dt) - : INP_type(INP_type), dt(dt) {} + auto& ice = thrust::get<0>(tpl); + auto& rw2 = thrust::get<1>(tpl); + auto& a = thrust::get<2>(tpl); + auto& c = thrust::get<3>(tpl); + auto& rho_i = thrust::get<4>(tpl); - BOOST_GPU_ENABLED - bool operator()(const thrust::tuple &tpl) const - { - const real_t &rnd = thrust::get<0>(tpl); // random number between [0, 1] - const real_t &rd2_insol = thrust::get<1>(tpl); // radius squared of insoluble particle - const real_t &T = thrust::get<2>(tpl); // temperature + const real_t T_freeze = thrust::get<5>(tpl); + const real_t T = thrust::get<6>(tpl); + const real_t RH = thrust::get<7>(tpl); - return rnd < common::ice_nucleation::p_freeze(INP_type, rd2_insol, T, dt); + if (T_freeze >= T && RH >= real_t(1)) // condition for freezing + { + ice = real_t(1); + rw2 = real_t(0); + rho_i = common::moist_air::rho_i().value(); + a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); + c = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); + } } }; - - // The condition for melting + // Time-dependent immersion freezing (Arabas et al., 2025) + // Functor to update ice flag, rw2, a, c, rho_i, of frozen droplets template - class melting_cond + class time_dep_freeze { + const real_t dt; + //const common::ice_nucleation::INP_t INP_type; + public: BOOST_GPU_ENABLED - bool operator()(const real_t &T) const - { - return (T > real_t(273.15)); - }; - }; + time_dep_freeze(const real_t &dt) : dt(dt) + {} - // Functor to update ice flag, rw2, a, c, rho_i, of frozen droplets - template - class freezing_update - { - public: BOOST_GPU_ENABLED void operator()(thrust::tuple< real_t&, real_t&, real_t&, real_t&, real_t&, // to be updated (ice, rw2, a, c, rho_i) - const real_t&, const real_t&, const real_t& // T_freeze, T, RH + const real_t&, const real_t&, const real_t& // rd2_insol, u01, T > tpl) const { auto& ice = thrust::get<0>(tpl); @@ -80,11 +72,11 @@ namespace libcloudphxx auto& c = thrust::get<3>(tpl); auto& rho_i = thrust::get<4>(tpl); - const real_t T_freeze = thrust::get<5>(tpl); - const real_t T = thrust::get<6>(tpl); - const real_t RH = thrust::get<7>(tpl); + const real_t rd2_insol = thrust::get<5>(tpl); + const real_t u01 = thrust::get<6>(tpl); + const real_t T = thrust::get<7>(tpl); - if (detail::singular_freeze_cond()(thrust::make_tuple(T_freeze, T, RH))) + if (u01 < common::ice_nucleation::p_freeze(common::ice_nucleation::INP_t::mineral, rd2_insol, T, dt)) { ice = real_t(1); rw2 = real_t(0); @@ -97,7 +89,7 @@ namespace libcloudphxx // Functor to update ice flag, rw2, a, c, rho_i, of melted ice template - class melting_update + class melt { public: BOOST_GPU_ENABLED @@ -112,7 +104,7 @@ namespace libcloudphxx auto& c = thrust::get<3>(tpl); auto& rho_i = thrust::get<4>(tpl); - if (detail::melting_cond()(thrust::get<5>(tpl))) + if (thrust::get<5>(tpl) > real_t(273.15)) // if T > 0 C { ice = real_t(0); rw2 = pow(common::moist_air::rho_i() / common::moist_air::rho_w() * c , real_t(2./3.)) * pow(a , real_t(4./3.)); @@ -127,7 +119,7 @@ namespace libcloudphxx // Immersion freezing and melting template - void particles_t::impl::ice_nucl_melt() { + void particles_t::impl::ice_nucl_melt(const bool time_dep_ice_nucl) { hskpng_sort(); @@ -148,29 +140,60 @@ namespace libcloudphxx ); // Change liquid droplets to ice under the freezing condition - thrust::for_each( - thrust::make_zip_iterator(thrust::make_tuple( - ice.begin(), - rw2.begin(), - ice_a.begin(), - ice_c.begin(), - ice_rho.begin(), - T_freeze.begin(), - thrust::make_permutation_iterator(T.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH.begin(), ijk.begin()) - )), - thrust::make_zip_iterator(thrust::make_tuple( - ice.begin(), - rw2.begin(), - ice_a.begin(), - ice_c.begin(), - ice_rho.begin(), - T_freeze.begin(), - thrust::make_permutation_iterator(T.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH.begin(), ijk.begin()) - )) + n_part, - detail::freezing_update() // functor for updating (ice, rw2, a, c, rho_i) if freezing condition satisfied - ); + + if (time_dep_ice_nucl) // time dependent freezing based on Arabas et al., 2025 + { + rand_u01(n_part); // random numbers between [0,1] for each particle + thrust::for_each( + thrust::make_zip_iterator(thrust::make_tuple( + ice.begin(), + rw2.begin(), + ice_a.begin(), + ice_c.begin(), + ice_rho.begin(), + rd2_insol.begin(), + u01.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()) + )), + thrust::make_zip_iterator(thrust::make_tuple( + ice.begin(), + rw2.begin(), + ice_a.begin(), + ice_c.begin(), + ice_rho.begin(), + rd2_insol.begin(), + u01.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()) + )) + n_part, + detail::time_dep_freeze(dt) // functor for updating (ice, rw2, a, c, rho_i) if freezing condition satisfied + ); + } + else // singular freezing based on Shima et al., 2020 + { + thrust::for_each( + thrust::make_zip_iterator(thrust::make_tuple( + ice.begin(), + rw2.begin(), + ice_a.begin(), + ice_c.begin(), + ice_rho.begin(), + T_freeze.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH.begin(), ijk.begin()) + )), + thrust::make_zip_iterator(thrust::make_tuple( + ice.begin(), + rw2.begin(), + ice_a.begin(), + ice_c.begin(), + ice_rho.begin(), + T_freeze.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH.begin(), ijk.begin()) + )) + n_part, + detail::singular_freeze() // functor for updating (ice, rw2, a, c, rho_i) if freezing condition satisfied + ); + } // Change ice to liquid droplets under the melting condition thrust::for_each( @@ -190,7 +213,7 @@ namespace libcloudphxx ice_rho.begin(), thrust::make_permutation_iterator(T.begin(), ijk.begin()) )) + n_part, - detail::melting_update() // functor for updating (ice, rw2, a, c, rho_i) if melting condition satisfied + detail::melt() // functor for updating (ice, rw2, a, c, rho_i) if melting condition satisfied ); // Compute per-cell 3rd moment of liquid droplets after freezing/melting. It is stored in count_mom diff --git a/src/particles_step.ipp b/src/particles_step.ipp index 58d47b1d8..468c23da9 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -195,7 +195,7 @@ namespace libcloudphxx if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); if (opts.ice_nucl) - pimpl->ice_nucl_melt(); + pimpl->ice_nucl_melt(opts.time_dep_ice_nucl); pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } // copy sstp_tmp_rv and th to rv and th @@ -212,7 +212,7 @@ namespace libcloudphxx pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); pimpl->hskpng_Tpr(); if (opts.ice_nucl) - pimpl->ice_nucl_melt(); + pimpl->ice_nucl_melt(opts.time_dep_ice_nucl); pimpl->cond(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } } From 45d8dd87bd99e80a7de32f1d9cf301a2d09b3330 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 15 Oct 2025 14:45:22 +0900 Subject: [PATCH 57/97] fix units --- include/libcloudph++/common/ice_nucleation.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index eb2a9cb19..59ba2bbec 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -65,7 +65,7 @@ namespace libcloudphxx ) { real_t A = real_t(4) * pi() * rd2_insol; // surface area of the insoluble particle - real_t d_aw = real_t(1) - const_cp::p_vsi(T)/ const_cp::p_vs(T); // water activity + real_t d_aw = real_t(1) - const_cp::p_vsi(T * si::kelvins)/ const_cp::p_vs(T * si::kelvins); // water activity if (INP_type == INP_t::mineral) { From 7c6e12397c87837a217320454c1c4377c064aa05 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 16 Oct 2025 13:01:21 +0900 Subject: [PATCH 58/97] advance a c (spherical for now) --- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_cond.ipp | 34 ++++++++++++++++++++ src/impl/particles_impl_cond_common.ipp | 39 +++++++++++++++++++++++ src/impl/particles_impl_ice_nucl_melt.ipp | 2 +- src/particles_step.ipp | 6 ++-- 5 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 31098b97c..ab00654d7 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -617,7 +617,7 @@ namespace libcloudphxx void sedi(const real_t &dt); void subs(const real_t &dt); - void ice_nucl_melt(const bool time_dep_ice_nucl); // 0 for singular freezing, 1 for time-dependent + void ice_nucl_melt(const real_t &dt, const bool time_dep_ice_nucl); // 0 for singular freezing, 1 for time-dependent void cond_dm3_helper(); void cond(const real_t &dt, const real_t &RH_max, const bool turb_cond); diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index 415d1262e..633247d21 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -119,6 +119,7 @@ namespace libcloudphxx */ } else + // condensation for liquid droplets thrust::transform( rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() thrust::make_zip_iterator( // input - 2nd arg @@ -134,6 +135,39 @@ namespace libcloudphxx ); nancheck(rw2, "rw2 after condensation (no sub-steps"); + // deposition for ice crystals + thrust::transform( + thrust::make_zip_iterator( + thrust::make_tuple( + ice_a.begin(), + ice_c.begin() + ) + ), + thrust::make_zip_iterator( + thrust::make_tuple( + ice_a.end(), + ice_c.end() + ) + ), + thrust::make_zip_iterator( + thrust::make_tuple( + hlpr_zip_iter, + thrust::make_permutation_iterator(p.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH_i.begin(), ijk.begin()) + ) + ), + thrust::make_zip_iterator( + thrust::make_tuple( + ice_a.begin(), + ice_c.begin() + ) + ), + detail::advance_ice_ac(dt, RH_max) + ); + nancheck(ice_a, "ice_a after deposition (no sub-steps"); + nancheck(ice_c, "ice_c after deposition (no sub-steps"); + // Compute per-cell 3rd moment of liquid droplets after condensation. It is stored in count_mom moms_eq0(ice.begin()); // choose particles with ice=0 moms_calc(rw2.begin(), real_t(3./2.)); diff --git a/src/impl/particles_impl_cond_common.ipp b/src/impl/particles_impl_cond_common.ipp index dd075f4c6..8cd83a21e 100644 --- a/src/impl/particles_impl_cond_common.ipp +++ b/src/impl/particles_impl_cond_common.ipp @@ -309,6 +309,45 @@ namespace libcloudphxx return rw2_new; } }; + + template + struct advance_ice_ac + { + const real_t dt, RH_max; + + advance_ice_ac(const real_t &dt, const real_t &RH_max) + : dt(dt), RH_max(RH_max) {} + + BOOST_GPU_ENABLED + thrust::tuple operator()( + const thrust::tuple &ac_old, + const thrust::tuple< + thrust::tuple, + real_t, real_t, real_t> &tpl + ) const + { +#if !defined(__NVCC__) + using std::max; + using std::isnan; + using std::isinf; +#endif + const real_t a_old = thrust::get<0>(ac_old); + const real_t c_old = thrust::get<1>(ac_old); + + advance_rw2_minfun f_a(dt, a_old * a_old, tpl, RH_max); + advance_rw2_minfun f_c(dt, c_old * c_old, tpl, RH_max); + + const real_t da_dt = (f_a.drw2_dt(a_old * a_old * si::square_metres) / (2 * a_old * si::metres)).value(); + const real_t dc_dt = (f_c.drw2_dt(c_old * c_old * si::square_metres) / (2 * c_old * si::metres)).value(); + + // forward Euler for simplicity + const real_t a_new = max(a_old + dt * da_dt, real_t(1e-9)); + const real_t c_new = max(c_old + dt * dc_dt, real_t(1e-9)); + + return thrust::make_tuple(a_new, c_new); + } + }; + }; }; }; diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp index 42ff01de9..cac5e5c54 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -119,7 +119,7 @@ namespace libcloudphxx // Immersion freezing and melting template - void particles_t::impl::ice_nucl_melt(const bool time_dep_ice_nucl) { + void particles_t::impl::ice_nucl_melt(const real_t &dt, const bool time_dep_ice_nucl) { hskpng_sort(); diff --git a/src/particles_step.ipp b/src/particles_step.ipp index 468c23da9..c45266378 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -195,8 +195,8 @@ namespace libcloudphxx if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); if (opts.ice_nucl) - pimpl->ice_nucl_melt(opts.time_dep_ice_nucl); - pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); + pimpl->ice_nucl_melt(pimpl->dt / pimpl->sstp_cond, opts.time_dep_ice_nucl); + pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } // copy sstp_tmp_rv and th to rv and th pimpl->update_state(pimpl->rv, pimpl->sstp_tmp_rv); @@ -212,7 +212,7 @@ namespace libcloudphxx pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); pimpl->hskpng_Tpr(); if (opts.ice_nucl) - pimpl->ice_nucl_melt(opts.time_dep_ice_nucl); + pimpl->ice_nucl_melt(pimpl->dt / pimpl->sstp_cond, opts.time_dep_ice_nucl); pimpl->cond(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } } From 99fefaaffb38170c73c6263b705ec3b1d2831c70 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 16 Oct 2025 14:55:38 +0900 Subject: [PATCH 59/97] separate growth for ice and droplets --- src/impl/particles_impl_cond_common.ipp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/impl/particles_impl_cond_common.ipp b/src/impl/particles_impl_cond_common.ipp index 8cd83a21e..87208c867 100644 --- a/src/impl/particles_impl_cond_common.ipp +++ b/src/impl/particles_impl_cond_common.ipp @@ -184,6 +184,9 @@ namespace libcloudphxx using std::isinf; #endif + // Skip ice particles (with rw2=0) + if (rw2_old <= 0) return rw2_old; + auto& tpl_in = thrust::get<0>(tpl); const advance_rw2_minfun f(dt, rw2_old, tpl, RH_max); const real_t drw2 = dt * f.drw2_dt(rw2_old * si::square_metres) * si::seconds / si::square_metres; @@ -334,6 +337,10 @@ namespace libcloudphxx const real_t a_old = thrust::get<0>(ac_old); const real_t c_old = thrust::get<1>(ac_old); + // Skip liquid droplets + if (a_old <= 0 || c_old <= 0) + return ac_old; + advance_rw2_minfun f_a(dt, a_old * a_old, tpl, RH_max); advance_rw2_minfun f_c(dt, c_old * c_old, tpl, RH_max); From fcb8b0f84f9c99bc4b5b6e716ec119ada7c6ca47 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 17 Oct 2025 15:52:57 +0900 Subject: [PATCH 60/97] move time_dep_ice_nucl to opts_init --- include/libcloudph++/lgrngn/opts.hpp | 4 ++-- include/libcloudph++/lgrngn/opts_init.hpp | 4 +++- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_ice_nucl_melt.ipp | 4 ++-- src/particles_step.ipp | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/libcloudph++/lgrngn/opts.hpp b/include/libcloudph++/lgrngn/opts.hpp index b5b993f10..d01181769 100644 --- a/include/libcloudph++/lgrngn/opts.hpp +++ b/include/libcloudph++/lgrngn/opts.hpp @@ -20,7 +20,7 @@ namespace libcloudphxx struct opts_t { // process toggling - bool adve, sedi, subs, cond, coal, src, rlx, rcyc, turb_adve, turb_cond, turb_coal, ice_nucl, time_dep_ice_nucl; + bool adve, sedi, subs, cond, coal, src, rlx, rcyc, turb_adve, turb_cond, turb_coal, ice_nucl; // RH limit for drop growth real_t RH_max; @@ -42,7 +42,7 @@ namespace libcloudphxx opts_t() : adve(true), sedi(true), subs(false), cond(true), coal(true), src(false), rlx(false), rcyc(false), chem_dsl(false), chem_dsc(false), chem_rct(false), - turb_adve(false), turb_cond(false), turb_coal(false), ice_nucl(false), time_dep_ice_nucl(false), + turb_adve(false), turb_cond(false), turb_coal(false), ice_nucl(false), RH_max(44), // :) (anything greater than 1.1 would be enough dt(-1) // negative means that we do not override dt in this step { diff --git a/include/libcloudph++/lgrngn/opts_init.hpp b/include/libcloudph++/lgrngn/opts_init.hpp index ad436b944..3ef9fff91 100644 --- a/include/libcloudph++/lgrngn/opts_init.hpp +++ b/include/libcloudph++/lgrngn/opts_init.hpp @@ -99,7 +99,8 @@ namespace libcloudphxx turb_cond_switch, // if true, turbulent condensation of SDs is modeled turb_coal_switch, // if true, turbulent coalescence kernels can be used ice_switch, // if true, ice is allowed - exact_sstp_cond; // if true, use per-particle sstp_cond logic, if false, use per-cell + exact_sstp_cond, // if true, use per-particle sstp_cond logic, if false, use per-cell + time_dep_ice_nucl; // it true, time-dependent immersion freezing, if false, singular immersion freezing int sstp_chem; real_t chem_rho; @@ -204,6 +205,7 @@ namespace libcloudphxx src_type(src_t::off), // source turned off by default rlx_switch(false), ice_switch(false), + time_dep_ice_nucl(false), exact_sstp_cond(false), turb_cond_switch(false), turb_adve_switch(false), diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index ab00654d7..861958695 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -617,7 +617,7 @@ namespace libcloudphxx void sedi(const real_t &dt); void subs(const real_t &dt); - void ice_nucl_melt(const real_t &dt, const bool time_dep_ice_nucl); // 0 for singular freezing, 1 for time-dependent + void ice_nucl_melt(const real_t &dt); void cond_dm3_helper(); void cond(const real_t &dt, const real_t &RH_max, const bool turb_cond); diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp index cac5e5c54..5d0e482db 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -119,7 +119,7 @@ namespace libcloudphxx // Immersion freezing and melting template - void particles_t::impl::ice_nucl_melt(const real_t &dt, const bool time_dep_ice_nucl) { + void particles_t::impl::ice_nucl_melt(const real_t &dt) { hskpng_sort(); @@ -141,7 +141,7 @@ namespace libcloudphxx // Change liquid droplets to ice under the freezing condition - if (time_dep_ice_nucl) // time dependent freezing based on Arabas et al., 2025 + if (opts_init.time_dep_ice_nucl) // time dependent freezing based on Arabas et al., 2025 { rand_u01(n_part); // random numbers between [0,1] for each particle thrust::for_each( diff --git a/src/particles_step.ipp b/src/particles_step.ipp index c45266378..2cffc61da 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -195,7 +195,7 @@ namespace libcloudphxx if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); if (opts.ice_nucl) - pimpl->ice_nucl_melt(pimpl->dt / pimpl->sstp_cond, opts.time_dep_ice_nucl); + pimpl->ice_nucl_melt(pimpl->dt / pimpl->sstp_cond); pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } // copy sstp_tmp_rv and th to rv and th @@ -212,7 +212,7 @@ namespace libcloudphxx pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); pimpl->hskpng_Tpr(); if (opts.ice_nucl) - pimpl->ice_nucl_melt(pimpl->dt / pimpl->sstp_cond, opts.time_dep_ice_nucl); + pimpl->ice_nucl_melt(pimpl->dt / pimpl->sstp_cond); pimpl->cond(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } } From 746bc24a3937ed834f17fe7a690ba74e397974e8 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Mon, 20 Oct 2025 15:09:07 +0900 Subject: [PATCH 61/97] diag ice a c --- include/libcloudph++/lgrngn/opts.hpp | 2 +- include/libcloudph++/lgrngn/particles.hpp | 3 ++ src/impl/particles_impl_cond.ipp | 16 ---------- src/particles_diag.ipp | 36 +++++++++++++++++++++++ src/particles_step.ipp | 4 +-- 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/include/libcloudph++/lgrngn/opts.hpp b/include/libcloudph++/lgrngn/opts.hpp index d01181769..59ef100ea 100644 --- a/include/libcloudph++/lgrngn/opts.hpp +++ b/include/libcloudph++/lgrngn/opts.hpp @@ -42,7 +42,7 @@ namespace libcloudphxx opts_t() : adve(true), sedi(true), subs(false), cond(true), coal(true), src(false), rlx(false), rcyc(false), chem_dsl(false), chem_dsc(false), chem_rct(false), - turb_adve(false), turb_cond(false), turb_coal(false), ice_nucl(false), + turb_adve(false), turb_cond(false), turb_coal(false), ice_nucl(true), RH_max(44), // :) (anything greater than 1.1 would be enough dt(-1) // negative means that we do not override dt in this step { diff --git a/include/libcloudph++/lgrngn/particles.hpp b/include/libcloudph++/lgrngn/particles.hpp index f6350ff1c..9dd3cd121 100644 --- a/include/libcloudph++/lgrngn/particles.hpp +++ b/include/libcloudph++/lgrngn/particles.hpp @@ -198,6 +198,9 @@ namespace libcloudphxx void diag_wp_mom(const int&); void diag_incloud_time_mom(const int &k); void diag_wet_mass_dens(const real_t&, const real_t&); + void diag_ice_a_mom(const int &k); + void diag_ice_c_mom(const int &k); + void diag_ice_vol(); void diag_chem(const enum common::chem::chem_species_t&); void diag_rw_ge_rc(); diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index 633247d21..8659cce93 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -10,22 +10,6 @@ namespace libcloudphxx namespace lgrngn { - namespace detail - { - - template - class ice_vol - { - public: - BOOST_GPU_ENABLED - real_t operator()(const thrust::tuple &tpl) // tpl is a tuple (a, c) - { - return thrust::get<0>(tpl) * thrust::get<0>(tpl) * thrust::get<1>(tpl); // a * a * c - } - }; - } - - template void particles_t::impl::cond( const real_t &dt, diff --git a/src/particles_diag.ipp b/src/particles_diag.ipp index 63df19f97..435139332 100644 --- a/src/particles_diag.ipp +++ b/src/particles_diag.ipp @@ -104,6 +104,18 @@ namespace libcloudphxx return sqrt(rw2); } }; + + template + class ice_vol + { + public: + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) // tpl is a tuple (a, c) + { + return thrust::get<0>(tpl) * thrust::get<0>(tpl) * thrust::get<1>(tpl); // a * a * c + } + }; + } // records pressure @@ -339,6 +351,30 @@ namespace libcloudphxx pimpl->moms_calc(pimpl->rw2.begin(), n/2.); } + // computes n-th moment of the ice equatorial radius spectrum for the selected particles + template + void particles_t::diag_ice_a_mom(const int &n) + { + pimpl->moms_calc(pimpl->ice_a.begin(), n); + } + + // computes n-th moment of the ice polar radius spectrum for the selected particles + template + void particles_t::diag_ice_c_mom(const int &n) + { + pimpl->moms_calc(pimpl->ice_c.begin(), n); + } + + // computes ice volume + template + void particles_t::diag_ice_vol() + { + pimpl->moms_calc(thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple(pimpl->ice_a.begin(), pimpl->ice_c.begin())), detail::ice_vol() + ), + real_t(1)); + } + // compute n-th moment of kappa for selected particles template void particles_t::diag_kappa_mom(const int &n) diff --git a/src/particles_step.ipp b/src/particles_step.ipp index 2cffc61da..e1be902df 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -194,7 +194,7 @@ namespace libcloudphxx pimpl->sstp_step_exact(step); if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); - if (opts.ice_nucl) + if (pimpl->opts_init.ice_switch && opts.ice_nucl) pimpl->ice_nucl_melt(pimpl->dt / pimpl->sstp_cond); pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } @@ -211,7 +211,7 @@ namespace libcloudphxx if(opts.turb_cond) pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); pimpl->hskpng_Tpr(); - if (opts.ice_nucl) + if (pimpl->opts_init.ice_switch && opts.ice_nucl) pimpl->ice_nucl_melt(pimpl->dt / pimpl->sstp_cond); pimpl->cond(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond); } From a2d97a578d162247932104d236b3dc1df27df11a Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 22 Oct 2025 15:17:46 +0900 Subject: [PATCH 62/97] python bindinds for ice --- CMakeLists.txt | 2 +- bindings/python/common.hpp | 18 ++++++++++++++ bindings/python/lgrngn.hpp | 29 ++++++++++++++++------- bindings/python/lib.cpp | 14 +++++++++-- include/libcloudph++/lgrngn/particles.hpp | 12 +++++++--- tests/python/unit/api_lgrngn.py | 1 + 6 files changed, 62 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de156037e..2c1a7f862 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -403,7 +403,7 @@ enable_testing() add_subdirectory(tests) add_subdirectory(include) -#add_subdirectory(bindings) +add_subdirectory(bindings) ############################################################################################ diff --git a/bindings/python/common.hpp b/bindings/python/common.hpp index 238337837..c361d6f46 100644 --- a/bindings/python/common.hpp +++ b/bindings/python/common.hpp @@ -58,6 +58,12 @@ namespace libcloudphxx return cmn::const_cp::p_vs(T * si::kelvins) / si::pascals; } + template + real_t p_vsi(const real_t &T) + { + return cmn::const_cp::p_vsi(T * si::kelvins) / si::pascals; + } + template real_t p_vs_tet(const real_t &T) { @@ -82,6 +88,18 @@ namespace libcloudphxx return cmn::const_cp::l_v(T * si::kelvins) / si::joules * si::kilograms; } + template + real_t l_s(const real_t &T) + { + return cmn::const_cp::l_s(T * si::kelvins) / si::joules * si::kilograms; + } + + template + real_t l_f(const real_t &T) + { + return cmn::const_cp::l_f(T * si::kelvins) / si::joules * si::kilograms; + } + template real_t T(const real_t &th, const real_t &rhod) { diff --git a/bindings/python/lgrngn.hpp b/bindings/python/lgrngn.hpp index 7dc895040..05cfc27a1 100644 --- a/bindings/python/lgrngn.hpp +++ b/bindings/python/lgrngn.hpp @@ -234,17 +234,23 @@ namespace libcloudphxx return *arg->opts_init; } + // set dry distros from a dict with (kappa, rd_insol) as key template - void set_dd( // dry_distro + void set_dd( lgr::opts_init_t *arg, - const bp::dict &kappa_func) + const bp::dict &kappa_func) // a dict keyed by (kappa, rd_insol) { arg->dry_distros.clear(); for (int i = 0; i < len(kappa_func.keys()); ++i) + { + bp::tuple key = bp::extract(kappa_func.keys()[i]); + const real_t kappa = bp::extract(key[0]); + const real_t rd_insol = bp::extract(key[1]); arg->dry_distros.emplace( - std::make_pair(real_t(bp::extract(kappa_func.keys()[i])), 0), // assume ice=0 + std::make_pair(kappa, rd_insol), std::make_shared>(kappa_func.values()[i]) ); + } } // src_dry_distros moved from opts_init to opts @@ -263,19 +269,27 @@ namespace libcloudphxx } */ + // set dry sizes from a dict with (kappa, rd_insol) as key template - void set_ds( // dry_sizes + void set_ds( lgr::opts_init_t *arg, - const bp::dict &kappa_func + const bp::dict &kappa_func // a dict keyed by (kappa, rd_insol) ) { arg->dry_sizes.clear(); if(len(kappa_func.keys()) == 0) return; - // loop over kappas + // loop over kappas and rd_insol for (int j = 0; j < len(kappa_func.keys()); ++j) { + + // extract the key tuple (kappa, rd_insol) + const bp::tuple key = bp::extract(kappa_func.keys()[j]); + const real_t kappa = bp::extract(key[0]); + const real_t rd_insol = bp::extract(key[1]); + + // extract size : {conc, count} dict for this (kappa, rd_insol) const bp::dict size_conc = bp::extract(kappa_func.values()[j]); std::map> size_conc_map; @@ -288,8 +302,7 @@ namespace libcloudphxx const int count = bp::extract (conc_count_list[1]); size_conc_map[bp::extract(size_conc.keys()[i])] = std::make_pair(conc, count); } - const real_t kappa = bp::extract(kappa_func.keys()[j]); - arg->dry_sizes[std::make_pair(kappa, 0)] = size_conc_map; // assume ice=0 + arg->dry_sizes[std::make_pair(kappa, rd_insol)] = size_conc_map; } } diff --git a/bindings/python/lib.cpp b/bindings/python/lib.cpp index 79d4ddc42..243b33047 100644 --- a/bindings/python/lib.cpp +++ b/bindings/python/lib.cpp @@ -276,6 +276,7 @@ BOOST_PYTHON_MODULE(libcloudphxx) .def_readwrite("turb_adve", &lgr::opts_t::turb_adve) .def_readwrite("turb_cond", &lgr::opts_t::turb_cond) .def_readwrite("turb_coal", &lgr::opts_t::turb_coal) + .def_readwrite("ice_nucl", &lgr::opts_t::ice_nucl) .def_readwrite("dt", &lgr::opts_t::dt) ; bp::class_>("opts_init_t") @@ -319,7 +320,7 @@ BOOST_PYTHON_MODULE(libcloudphxx) .def_readwrite("sstp_cond", &lgr::opts_init_t::sstp_cond) .def_readwrite("sstp_coal", &lgr::opts_init_t::sstp_coal) .def_readwrite("sstp_chem", &lgr::opts_init_t::sstp_chem) - .def_readwrite("supstp_src", &lgr::opts_init_t::supstp_src) + //.def_readwrite("supstp_src", &lgr::opts_init_t::supstp_src) .def_readwrite("supstp_rlx", &lgr::opts_init_t::supstp_rlx) .def_readwrite("kernel", &lgr::opts_init_t::kernel) .def_readwrite("adve_scheme", &lgr::opts_init_t::adve_scheme) @@ -327,7 +328,7 @@ BOOST_PYTHON_MODULE(libcloudphxx) .def_readwrite("sd_conc_large_tail", &lgr::opts_init_t::sd_conc_large_tail) .def_readwrite("aerosol_independent_of_rhod", &lgr::opts_init_t::aerosol_independent_of_rhod) .def_readwrite("sd_const_multi", &lgr::opts_init_t::sd_const_multi) - .def_readwrite("src_sd_conc", &lgr::opts_init_t::src_sd_conc) + //.def_readwrite("src_sd_conc", &lgr::opts_init_t::src_sd_conc) .def_readwrite("rlx_bins", &lgr::opts_init_t::rlx_bins) .def_readwrite("rlx_sd_per_bin", &lgr::opts_init_t::rlx_sd_per_bin) .def_readwrite("rlx_timescale", &lgr::opts_init_t::rlx_timescale) @@ -345,6 +346,8 @@ BOOST_PYTHON_MODULE(libcloudphxx) .add_property("SGS_mix_len", &lgrngn::get_SGS_mix_len, &lgrngn::set_SGS_mix_len) .add_property("kernel_parameters", &lgrngn::get_kp, &lgrngn::set_kp) .def_readwrite("variable_dt_switch", &lgr::opts_init_t::variable_dt_switch) + .def_readwrite("ice_switch", &lgr::opts_init_t::ice_switch) + .def_readwrite("time_dep_ice_nucl", &lgr::opts_init_t::time_dep_ice_nucl) ; bp::class_/*, boost::noncopyable*/>("particles_proto_t") .add_property("opts_init", &lgrngn::get_oi) @@ -405,6 +408,13 @@ BOOST_PYTHON_MODULE(libcloudphxx) .def("diag_chem", &lgr::particles_proto_t::diag_chem) .def("diag_precip_rate", &lgr::particles_proto_t::diag_precip_rate) .def("diag_puddle", &lgrngn::diag_puddle) + .def("diag_ice", &lgr::particles_proto_t::diag_ice) + .def("diag_water", &lgr::particles_proto_t::diag_water) + .def("diag_ice_cons", &lgr::particles_proto_t::diag_ice_cons) + .def("diag_water_cons", &lgr::particles_proto_t::diag_water_cons) + .def("diag_ice_a_mom", &lgr::particles_proto_t::diag_ice_a_mom) + .def("diag_ice_c_mom", &lgr::particles_proto_t::diag_ice_c_mom) + .def("diag_ice_vol", &lgr::particles_proto_t::diag_ice_vol) .def("outbuf", &lgrngn::outbuf) .def("get_attr", &lgr::particles_proto_t::get_attr) ; diff --git a/include/libcloudph++/lgrngn/particles.hpp b/include/libcloudph++/lgrngn/particles.hpp index 9dd3cd121..c5d8651a7 100644 --- a/include/libcloudph++/lgrngn/particles.hpp +++ b/include/libcloudph++/lgrngn/particles.hpp @@ -102,6 +102,9 @@ namespace libcloudphxx virtual void diag_dry_mom(const int&) { assert(false); } virtual void diag_wet_mom(const int&) { assert(false); } + virtual void diag_ice_a_mom(const int&) { assert(false); } + virtual void diag_ice_c_mom(const int&) { assert(false); } + virtual void diag_ice_vol() { assert(false); } virtual void diag_wet_mass_dens(const real_t&, const real_t&) { assert(false); } virtual void diag_chem(const enum common::chem::chem_species_t&) { assert(false); } virtual void diag_precip_rate() { assert(false); } @@ -192,15 +195,15 @@ namespace libcloudphxx void diag_water_cons(); void diag_dry_mom(const int &k); void diag_wet_mom(const int &k); + void diag_ice_a_mom(const int &k); + void diag_ice_c_mom(const int &k); + void diag_ice_vol(); void diag_kappa_mom(const int &k); void diag_up_mom(const int&); void diag_vp_mom(const int&); void diag_wp_mom(const int&); void diag_incloud_time_mom(const int &k); void diag_wet_mass_dens(const real_t&, const real_t&); - void diag_ice_a_mom(const int &k); - void diag_ice_c_mom(const int &k); - void diag_ice_vol(); void diag_chem(const enum common::chem::chem_species_t&); void diag_rw_ge_rc(); @@ -296,6 +299,9 @@ namespace libcloudphxx void diag_water_cons(); void diag_dry_mom(const int &k); void diag_wet_mom(const int &k); + void diag_ice_a_mom(const int &k); + void diag_ice_c_mom(const int &k); + void diag_ice_vol(); void diag_kappa_mom(const int&); void diag_up_mom(const int&); void diag_vp_mom(const int&); diff --git a/tests/python/unit/api_lgrngn.py b/tests/python/unit/api_lgrngn.py index 40a7709e3..fef29e235 100644 --- a/tests/python/unit/api_lgrngn.py +++ b/tests/python/unit/api_lgrngn.py @@ -24,6 +24,7 @@ def lognormal(lnr): kappa1 = .61 kappa2 = 1.28 kappa3 = 0.8 +rd_insol = 0.5e-6 rho_stp = 1.2248 opts_init.dry_distros = {kappa1:lognormal, kappa2:lognormal} opts_init.kernel = lgrngn.kernel_t.geometric From 74a0b7d40d56c92a47e4ab51e2d96c7e337e5e2f Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 23 Oct 2025 13:32:54 +0900 Subject: [PATCH 63/97] kappa_rd_insol_t to fix bindings --- bindings/python/lgrngn.hpp | 4 ++-- include/libcloudph++/lgrngn/distro_t.hpp | 20 +++++++++++++++---- src/impl/particles_impl.ipp | 2 +- .../particles_impl_init_SD_with_distros.ipp | 6 +++--- .../particles_impl_init_SD_with_sizes.ipp | 4 ++-- src/impl/particles_impl_rlx_dry_distros.ipp | 2 +- src/impl/particles_impl_src_dry_distros.ipp | 3 ++- ...articles_impl_src_dry_distros_matching.ipp | 7 +++++-- .../particles_impl_src_dry_distros_simple.ipp | 4 ++-- src/impl/particles_impl_src_dry_sizes.ipp | 4 ++-- tests/python/unit/api_lgrngn.py | 2 +- 11 files changed, 37 insertions(+), 21 deletions(-) diff --git a/bindings/python/lgrngn.hpp b/bindings/python/lgrngn.hpp index 05cfc27a1..221f9e6a5 100644 --- a/bindings/python/lgrngn.hpp +++ b/bindings/python/lgrngn.hpp @@ -247,7 +247,7 @@ namespace libcloudphxx const real_t kappa = bp::extract(key[0]); const real_t rd_insol = bp::extract(key[1]); arg->dry_distros.emplace( - std::make_pair(kappa, rd_insol), + libcloudphxx::lgrngn::kappa_rd_insol_t{kappa, rd_insol}, std::make_shared>(kappa_func.values()[i]) ); } @@ -302,7 +302,7 @@ namespace libcloudphxx const int count = bp::extract (conc_count_list[1]); size_conc_map[bp::extract(size_conc.keys()[i])] = std::make_pair(conc, count); } - arg->dry_sizes[std::make_pair(kappa, rd_insol)] = size_conc_map; + arg->dry_sizes[libcloudphxx::lgrngn::kappa_rd_insol_t{kappa, rd_insol}] = size_conc_map; } } diff --git a/include/libcloudph++/lgrngn/distro_t.hpp b/include/libcloudph++/lgrngn/distro_t.hpp index c45825def..14aa2bb06 100644 --- a/include/libcloudph++/lgrngn/distro_t.hpp +++ b/include/libcloudph++/lgrngn/distro_t.hpp @@ -6,19 +6,31 @@ namespace libcloudphxx { using common::unary_function; + template + struct kappa_rd_insol_t { + real_t kappa; + real_t rd_insol; + + bool operator<(const kappa_rd_insol_t &other) const + { + if (kappa != other.kappa) return kappa < other.kappa; + return rd_insol < other.rd_insol; + } + }; + // initial dry sizes of aerosol // defined with a distribution // uses shared_ptr to make opts_init copyable template using dry_distros_t = std::map< - std::pair, // (kappa, rd_insol) + kappa_rd_insol_t, // (kappa, rd_insol) std::shared_ptr> // n(ln(rd)) @ STP; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true >; // defined with a size-number pair template using dry_sizes_t = std::map< - std::pair, // (kappa, rd_insol) + kappa_rd_insol_t, // (kappa, rd_insol) std::map // STP_concentration [1/m^3], number of SD that represent this radius kappa and concentration > @@ -27,14 +39,14 @@ namespace libcloudphxx // similar, but for sources of aerosols after initialization template using src_dry_distros_t = std::map< - std::pair, // (kappa, rd_insol) + kappa_rd_insol_t, // (kappa, rd_insol) std::tuple>, int, int> // 1st: n(ln(rd)) @ STP created per second; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true; 2nd: sd_conc for this distribution ; 3rd: supstp for this aerosol (interval in timesteps beween addition of these aerosols) >; // defined with a size-number pair template using src_dry_sizes_t = std::map< - std::pair, // (kappa, rd_insol) + kappa_rd_insol_t, // (kappa, rd_insol) std::map // STP_concentration [1/m^3] created per second, number of SD that represent this radius kappa and concentration, supstp > diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 861958695..888c48aff 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -474,7 +474,7 @@ namespace libcloudphxx void init_SD_with_distros_sd_conc(const common::unary_function &, const real_t &); void init_SD_with_distros_tail(const common::unary_function &, const real_t); void init_SD_with_distros_const_multi(const common::unary_function &); - void init_SD_with_distros_finalize(const std::pair &, const bool unravel_ijk = true); + void init_SD_with_distros_finalize(const kappa_rd_insol_t &, const bool unravel_ijk = true); void init_SD_with_sizes(); void init_sanity_check( const arrinfo_t, const arrinfo_t, const arrinfo_t, diff --git a/src/impl/particles_impl_init_SD_with_distros.ipp b/src/impl/particles_impl_init_SD_with_distros.ipp index ddd861243..74c1e860d 100644 --- a/src/impl/particles_impl_init_SD_with_distros.ipp +++ b/src/impl/particles_impl_init_SD_with_distros.ipp @@ -50,13 +50,13 @@ namespace libcloudphxx // final inits common for tail/sd_conc/const_multi template - void particles_t::impl::init_SD_with_distros_finalize(const std::pair &kpa_rd2_insol, const bool unravel_ijk_switch) + void particles_t::impl::init_SD_with_distros_finalize(const kappa_rd_insol_t &kpa_rd_insol, const bool unravel_ijk_switch) { // init kappa - init_kappa(kpa_rd2_insol.first); + init_kappa(kpa_rd_insol.kappa); // init rd2_insol - init_insol_dry_sizes(kpa_rd2_insol.second); + init_insol_dry_sizes(kpa_rd_insol.rd_insol); // initialising wet radii init_wet(); diff --git a/src/impl/particles_impl_init_SD_with_sizes.ipp b/src/impl/particles_impl_init_SD_with_sizes.ipp index 345285669..733b374b2 100644 --- a/src/impl/particles_impl_init_SD_with_sizes.ipp +++ b/src/impl/particles_impl_init_SD_with_sizes.ipp @@ -22,8 +22,8 @@ namespace libcloudphxx // loop over (kappa, rd_insol) pairs for (auto dsi = opts_init.dry_sizes.cbegin(); dsi != opts_init.dry_sizes.cend(); ++dsi) { - const real_t &kappa(dsi->first.first); - const real_t &rd_insol(dsi->first.second); + const real_t &kappa(dsi->first.kappa); + const real_t &rd_insol(dsi->first.rd_insol); const auto &size_number_map(dsi->second); // loop over the "size : {concentration, count}" pairs for this (kappa, rd_insol) pair diff --git a/src/impl/particles_impl_rlx_dry_distros.ipp b/src/impl/particles_impl_rlx_dry_distros.ipp index 04436b1f3..e05887ea0 100644 --- a/src/impl/particles_impl_rlx_dry_distros.ipp +++ b/src/impl/particles_impl_rlx_dry_distros.ipp @@ -292,7 +292,7 @@ namespace libcloudphxx n_part_to_init = n_part - n_part_old; hskpng_resize_npart(); - init_SD_with_distros_finalize(std::make_pair(kappa, real_t(0)), false); // no need to unravel ijk there, becaues i j k are already initialized + init_SD_with_distros_finalize(lgrngn::kappa_rd_insol_t{kappa, real_t(0)}, false); // no need to unravel ijk there, becaues i j k are already initialized // TODO: we assume that relaxation produces water (not ice) // TODO: asserts of newly added SD parameters? e.g. how many SD, how big is multiplicity etc. diff --git a/src/impl/particles_impl_src_dry_distros.ipp b/src/impl/particles_impl_src_dry_distros.ipp index a3c37d150..8fdaa4109 100644 --- a/src/impl/particles_impl_src_dry_distros.ipp +++ b/src/impl/particles_impl_src_dry_distros.ipp @@ -20,7 +20,8 @@ namespace libcloudphxx throw std::runtime_error("libcloudph++: src_dry_distros can only have a single kappa value."); if (opts_init.src_type == src_t::matching && !sdd.empty() && - sdd.begin()->first != opts_init.dry_distros.begin()->first) throw std::runtime_error("libcloudph++: For 'matching' CCN source, kappa of the source has to be the same as that of the initial profile (no kappa matching done)"); + sdd.begin()->first.kappa != opts_init.dry_distros.begin()->first.kappa || + sdd.begin()->first.rd_insol != opts_init.dry_distros.begin()->first.rd_insol) throw std::runtime_error("libcloudph++: For 'matching' CCN source, kappa of the source has to be the same as that of the initial profile (no kappa matching done)"); if(opts_init.src_type == src_t::matching) src_dry_distros_matching(sdd); diff --git a/src/impl/particles_impl_src_dry_distros_matching.ipp b/src/impl/particles_impl_src_dry_distros_matching.ipp index 1c4fdd8e7..30562e1bc 100644 --- a/src/impl/particles_impl_src_dry_distros_matching.ipp +++ b/src/impl/particles_impl_src_dry_distros_matching.ipp @@ -219,8 +219,11 @@ namespace libcloudphxx // init other peoperties of SDs that didnt have a match init_kappa( - p_sdd->first.first - ); + p_sdd->first.kappa + ); + init_insol_dry_sizes( + p_sdd->first.rd_insol + ); if(opts_init.diag_incloud_time) init_incloud_time(); diff --git a/src/impl/particles_impl_src_dry_distros_simple.ipp b/src/impl/particles_impl_src_dry_distros_simple.ipp index 3e1d01dfa..8247d2560 100644 --- a/src/impl/particles_impl_src_dry_distros_simple.ipp +++ b/src/impl/particles_impl_src_dry_distros_simple.ipp @@ -56,10 +56,10 @@ namespace libcloudphxx // init other peoperties of SDs that didnt have a match init_kappa( - p_sdd->first.first + p_sdd->first.kappa ); init_insol_dry_sizes( - p_sdd->first.second + p_sdd->first.rd_insol ); if(opts_init.diag_incloud_time) diff --git a/src/impl/particles_impl_src_dry_sizes.ipp b/src/impl/particles_impl_src_dry_sizes.ipp index 122f8c206..9917c6580 100644 --- a/src/impl/particles_impl_src_dry_sizes.ipp +++ b/src/impl/particles_impl_src_dry_sizes.ipp @@ -24,8 +24,8 @@ namespace libcloudphxx // for (typename dry_sizes_t::const_iterator dsi = opts.src_dry_sizes.begin(); dsi != opts.src_dry_sizes.end(); ++dsi) for (auto dsi = sds.cbegin(); dsi != sds.cend(); ++dsi) { - const real_t &kappa(dsi->first.first); - const real_t &rd_insol(dsi->first.second); + const real_t &kappa(dsi->first.kappa); + const real_t &rd_insol(dsi->first.rd_insol); const auto &size_number_map(dsi->second); // loop over the "size : {concentration per second, multiplicity, supstp}" for this (kappa, rd_insol) pair diff --git a/tests/python/unit/api_lgrngn.py b/tests/python/unit/api_lgrngn.py index fef29e235..d481de5d5 100644 --- a/tests/python/unit/api_lgrngn.py +++ b/tests/python/unit/api_lgrngn.py @@ -26,7 +26,7 @@ def lognormal(lnr): kappa3 = 0.8 rd_insol = 0.5e-6 rho_stp = 1.2248 -opts_init.dry_distros = {kappa1:lognormal, kappa2:lognormal} +opts_init.dry_distros = {(kappa1, rd_insol): lognormal} opts_init.kernel = lgrngn.kernel_t.geometric opts_init.terminal_velocity = lgrngn.vt_t.beard76 opts_init.adve_scheme = lgrngn.as_t.euler From d07d1a4fdc1622bd16e77e094fbc368bea041d99 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 30 Oct 2025 13:07:42 +0100 Subject: [PATCH 64/97] removing the ice flag --- src/impl/particles_impl.ipp | 3 - src/impl/particles_impl_bcnd.ipp | 68 ++++++--- src/impl/particles_impl_cond.ipp | 11 +- src/impl/particles_impl_cond_common.ipp | 142 ++++++++++++++---- src/impl/particles_impl_cond_sstp.ipp | 3 +- src/impl/particles_impl_hskpng_vterm.ipp | 12 +- src/impl/particles_impl_ice_nucl_melt.ipp | 72 ++++----- .../particles_impl_init_SD_with_distros.ipp | 8 +- .../particles_impl_init_SD_with_sizes.ipp | 8 +- src/impl/particles_impl_init_ice.ipp | 23 --- .../particles_impl_reserve_hskpng_npart.ipp | 1 - src/particles.tpp | 1 - src/particles_diag.ipp | 4 +- 13 files changed, 211 insertions(+), 145 deletions(-) delete mode 100644 src/impl/particles_impl_init_ice.ipp diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 888c48aff..63f550056 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -85,7 +85,6 @@ namespace libcloudphxx sstp_tmp_rh, // ditto for rho sstp_tmp_p, // ditto for pressure incloud_time, // time this SD has been within a cloud - ice, // 0 - water 1 - ice; bool would suffice, but we are lazy rd2_insol, // dry radii squared of insoluble aerosol T_freeze, // freezing temperature ice_a, // equatorial radius of ice @@ -460,7 +459,6 @@ namespace libcloudphxx if(opts_init.ice_switch) { - distmem_real_vctrs.insert({&ice, detail::no_initial_value}); distmem_real_vctrs.insert({&rd2_insol, detail::no_initial_value}); distmem_real_vctrs.insert({&T_freeze, detail::no_initial_value}); distmem_real_vctrs.insert({&ice_a, detail::no_initial_value}); @@ -507,7 +505,6 @@ namespace libcloudphxx void init_ijk(); void init_xyz(); void init_kappa(const real_t &); - void init_ice(const real_t &); void init_insol_dry_sizes(real_t); void init_T_freeze(); void init_a_c_rho_ice(); diff --git a/src/impl/particles_impl_bcnd.ipp b/src/impl/particles_impl_bcnd.ipp index 2bf4c1e2e..45d3f260d 100644 --- a/src/impl/particles_impl_bcnd.ipp +++ b/src/impl/particles_impl_bcnd.ipp @@ -23,20 +23,17 @@ namespace libcloudphxx template struct count_vol - { + { real_t exponent; - int ice; // 0 - water only, 1 - ice only, 2 - both - count_vol(real_t exponent, int ice) : exponent(exponent), ice(ice){assert(ice == 0 || ice == 1 || ice == 2);}; + count_vol(real_t exponent) : exponent(exponent){}; template BOOST_GPU_ENABLED - real_t operator()(const tuple &tup) + real_t operator()(const tuple &tup) // tup is a tuple (n, radius) { - if(ice != 2 && thrust::get<2>(tup) != ice) return 0.; - #if !defined(__NVCC__) using std::pow; #endif - return 4./3. + return 4./3. #if !defined(__NVCC__) * pi() #else @@ -47,18 +44,43 @@ namespace libcloudphxx thrust::get<1>(tup), // radius at some power exponent); } - }; + }; + + template + struct count_ice_vol + { + count_ice_vol() {} + + template + BOOST_GPU_ENABLED + real_t operator()(const tuple &tup) // tup is a tuple (n, ice_a, ice_c) + { + #if !defined(__NVCC__) + using std::pow; + #endif + return 4./3. + #if !defined(__NVCC__) + * pi() + #else + * CUDART_PI + #endif + * thrust::get<0>(tup) // n + * thrust::get<1>(tup) * thrust::get<1>(tup) // a^2 + * thrust::get<2>(tup); //c + } + }; template struct count_num { int ice; // 0 - water only, 1 - ice only, 2 - both count_num(int ice) : ice(ice){assert(ice == 0 || ice == 1 || ice == 2);}; // TODO: ice enum + template BOOST_GPU_ENABLED - real_t operator()(const tuple &tup) + real_t operator()(const tuple &tup) // tup is a tuple (n_filtered, ice_a) { - if(ice != 2 && thrust::get<1>(tup) != ice) return 0.; + if((ice==0 && thrust::get<1>(tup)>real_t(0)) || (ice==1 && thrust::get<1>(tup)==real_t(0))) return 0.; return thrust::get<0>(tup); } }; @@ -229,10 +251,10 @@ namespace libcloudphxx output_puddle[common::outliq_vol] += thrust::transform_reduce( thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), rw2.begin(), ice.begin())), // input start + n_filtered.begin(), rw2.begin())), // input start thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), rw2.begin(), ice.begin())) + n_part, // input end - detail::count_vol(3./2., 0), // operation + n_filtered.begin(), rw2.begin())) + n_part, // input end + detail::count_vol(3./2.), // operation real_t(0), // init val thrust::plus() ); @@ -241,10 +263,10 @@ namespace libcloudphxx output_puddle[common::outice_vol] += thrust::transform_reduce( thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), rw2.begin(), ice.begin())), // input start + n_filtered.begin(), ice_a.begin(), ice_c.begin())), // input start thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), rw2.begin(), ice.begin())) + n_part, // input end - detail::count_vol(3./2., 1), // operation + n_filtered.begin(), ice_a.begin(), ice_c.begin())) + n_part, // input end + detail::count_ice_vol(), // operation real_t(0), // init val thrust::plus() ); @@ -253,10 +275,10 @@ namespace libcloudphxx output_puddle[common::outdry_vol] += thrust::transform_reduce( thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), rd3.begin(), ice.begin())), // input start + n_filtered.begin(), rd3.begin())), // input start thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), rd3.begin(), ice.begin())) + n_part, // input end - detail::count_vol(1., 2), // operation + n_filtered.begin(), rd3.begin())) + n_part, // input end + detail::count_vol(1.), // operation real_t(0), // init val thrust::plus() ); @@ -265,9 +287,9 @@ namespace libcloudphxx output_puddle[common::outliq_num] += thrust::transform_reduce( thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), ice.begin())), // input start + n_filtered.begin(), ice_a.begin())), // input start thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), ice.begin())) + n_part, // input end + n_filtered.begin(), ice_a.begin())) + n_part, // input end detail::count_num(0), // operation real_t(0), // init val thrust::plus() @@ -277,9 +299,9 @@ namespace libcloudphxx output_puddle[common::outice_num] += thrust::transform_reduce( thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), ice.begin())), // input start + n_filtered.begin(), ice_a.begin())), // input start thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), ice.begin())) + n_part, // input end + n_filtered.begin(), ice_a.begin())) + n_part, // input end detail::count_num(1), // operation real_t(0), // init val thrust::plus() diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index 8659cce93..1e78c5802 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -29,7 +29,7 @@ namespace libcloudphxx thrust_device::vector &drv_ice(tmp_device_real_cell3); // Compute per-cell 3rd moment of liquid droplets before condensation. It is stored in count_mom - moms_eq0(ice.begin()); // choose particles with ice=0 + moms_eq0(ice_a.begin()); // choose liquid particles (ice_a=0) moms_calc(rw2.begin(), real_t(3./2.)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) before condensation"); if(count_n!=n_cell) { @@ -43,7 +43,7 @@ namespace libcloudphxx ); // Compute per-cell 3rd moment of ice before deposition. It is stored in count_mom - moms_gt0(ice.begin()); // choose particles with ice=1 + moms_gt0(ice_a.begin()); // choose ice particles (ice_a>0) moms_calc(thrust::make_transform_iterator( thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() ), @@ -69,8 +69,7 @@ namespace libcloudphxx kpa.begin(), vt.begin(), thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), - thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()), - ice.begin() + thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) )); // calculating drop growth in a timestep using backward Euler @@ -153,7 +152,7 @@ namespace libcloudphxx nancheck(ice_c, "ice_c after deposition (no sub-steps"); // Compute per-cell 3rd moment of liquid droplets after condensation. It is stored in count_mom - moms_eq0(ice.begin()); // choose particles with ice=0 + moms_eq0(ice_a.begin()); // choose liquid particles (ice_a=0) moms_calc(rw2.begin(), real_t(3./2.)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) after condensation"); @@ -166,7 +165,7 @@ namespace libcloudphxx ); // Compute per-cell 3rd moment of ice after deposition. It is stored in count_mom - moms_gt0(ice.begin()); // choose particles with ice=1 + moms_gt0(ice_a.begin()); // choose ice particles (ice_a>0) moms_calc(thrust::make_transform_iterator( thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() ), diff --git a/src/impl/particles_impl_cond_common.ipp b/src/impl/particles_impl_cond_common.ipp index 87208c867..951b8fd60 100644 --- a/src/impl/particles_impl_cond_common.ipp +++ b/src/impl/particles_impl_cond_common.ipp @@ -69,15 +69,13 @@ namespace libcloudphxx const quantity RH_max; const quantity lambda_D; const quantity lambda_K; - const bool ice; - const quantity RH_i; // ctor BOOST_GPU_ENABLED advance_rw2_minfun( const real_t &dt, const real_t &rw2, - const thrust::tuple, real_t, real_t, real_t> &tpl, + const thrust::tuple, real_t, real_t> &tpl, const real_t &RH_max ) : dt(dt * si::seconds), @@ -93,8 +91,6 @@ namespace libcloudphxx RH( thrust::get<2>(tpl)), lambda_D(thrust::get<7>(thrust::get<0>(tpl)) * si::metres), lambda_K(thrust::get<8>(thrust::get<0>(tpl)) * si::metres), - ice(thrust::get<9>(thrust::get<0>(tpl))), - RH_i( thrust::get<3>(tpl)), RH_max(RH_max) {} @@ -130,26 +126,16 @@ namespace libcloudphxx const quantity K = K_0() * beta(lambda_K / rw) * (Nu(Pr, Re) / 2); - if(!ice) - return real_t(2) * rdrdt( - D, - K, - rhod * rv, - T, - p, - RH > RH_max ? RH_max : RH, - a_w(rw3, rd3, kpa), - klvntrm(rw, T) - ); - else - return real_t(2) * rdrdt_i( - D, - K, - rhod * rv, - T, - p, - RH_i > RH_max ? RH_max : RH_i - ); + return real_t(2) * rdrdt( + D, + K, + rhod * rv, + T, + p, + RH > RH_max ? RH_max : RH, + a_w(rw3, rd3, kpa), + klvntrm(rw, T) + ); } // backward Euler scheme: @@ -163,6 +149,102 @@ namespace libcloudphxx } }; + template + struct advance_rw2_minfun_ice + { + const quantity r2_old; + const quantity dt; + const quantity rhod; + const quantity rv; + const quantity T; + const quantity p; + const quantity RH_i; + const quantity eta; + const quantity rd3; + const quantity kpa; + const quantity vt; + const quantity RH_max; + const quantity lambda_D; + const quantity lambda_K; + + // ctor + BOOST_GPU_ENABLED + advance_rw2_minfun_ice( + const real_t &dt, + const real_t &rw2, + const thrust::tuple, real_t, real_t> &tpl, + const real_t &RH_max + ) : + dt(dt * si::seconds), + r2_old(rw2 * si::square_metres), + rhod( thrust::get<0>(thrust::get<0>(tpl)) * si::kilograms / si::cubic_metres), + rv( thrust::get<1>(thrust::get<0>(tpl))), + T( thrust::get<2>(thrust::get<0>(tpl)) * si::kelvins), + eta( thrust::get<3>(thrust::get<0>(tpl)) * si::pascals * si::seconds), + rd3( thrust::get<4>(thrust::get<0>(tpl)) * si::cubic_metres), + kpa( thrust::get<5>(thrust::get<0>(tpl))), + vt( thrust::get<6>(thrust::get<0>(tpl)) * si::metres_per_second), + p( thrust::get<1>(tpl) * si::pascals), + RH_i( thrust::get<2>(tpl)), + lambda_D(thrust::get<7>(thrust::get<0>(tpl)) * si::metres), + lambda_K(thrust::get<8>(thrust::get<0>(tpl)) * si::metres), + RH_max(RH_max) + {} + + BOOST_GPU_ENABLED + quantity::type, real_t> drw2_dt(const quantity &rw2) const + { + using namespace common::maxwell_mason; + using namespace common::kappa_koehler; + using namespace common::kelvin; + using common::moist_air::D_0; + using common::moist_air::K_0; + using common::moist_air::c_pd; + using common::transition_regime::beta; + using common::ventil::Sh; + using common::ventil::Nu; +#if !defined(__NVCC__) + using std::sqrt; +#endif + + const quantity rw = sqrt(real_t(rw2 / si::square_metres)) * si::metres; + const quantity rw3 = rw * rw * rw;; + + // TODO: common::moist_air:: below should not be needed + // TODO: ventilation as option + const quantity + Re = common::ventil::Re(vt, rw, rhod, eta), + Sc = common::ventil::Sc(eta, rhod, D_0()), // TODO? cache + Pr = common::ventil::Pr(eta, c_pd(), K_0()); // TODO? cache + + const quantity + D = D_0() * beta(lambda_D / rw) * (Sh(Sc, Re) / 2); + + const quantity + K = K_0() * beta(lambda_K / rw) * (Nu(Pr, Re) / 2); + + return real_t(2) * rdrdt_i( + D, + K, + rhod * rv, + T, + p, + RH_i > RH_max ? RH_max : RH_i + ); + } + + // backward Euler scheme: + // rw2_new = rw2_old + f_rw2(rw2_new) * dt + // rw2_new = rw2_old + 2 * rw * f_rw(rw2_new) * dt + BOOST_GPU_ENABLED + real_t operator()(const real_t &rw2_unitless) const + { + const quantity rw2 = rw2_unitless * si::square_metres; + return (r2_old + dt * drw2_dt(rw2) - rw2) / si::square_metres; + } + }; + + template struct advance_rw2 { @@ -174,7 +256,7 @@ namespace libcloudphxx BOOST_GPU_ENABLED real_t operator()( const real_t &rw2_old, - const thrust::tuple, real_t, real_t, real_t> &tpl + const thrust::tuple, real_t, real_t, real_t> &tpl ) const { #if !defined(__NVCC__) using std::min; @@ -210,7 +292,6 @@ namespace libcloudphxx "vt: %g " "lambda_D: %g " "lambda_K: %g " - "ice: %g " "RH_i: %g\n", drw2, rw2_old, dt, RH_max, thrust::get<0>(tpl_in), // rhod @@ -224,7 +305,6 @@ namespace libcloudphxx thrust::get<6>(tpl_in), // vt thrust::get<7>(tpl_in), // lambda_D thrust::get<8>(tpl_in), // lambda_K - thrust::get<9>(tpl_in), // ice thrust::get<3>(tpl) // RH_i ); assert(0); @@ -325,7 +405,7 @@ namespace libcloudphxx thrust::tuple operator()( const thrust::tuple &ac_old, const thrust::tuple< - thrust::tuple, + thrust::tuple, real_t, real_t, real_t> &tpl ) const { @@ -341,8 +421,8 @@ namespace libcloudphxx if (a_old <= 0 || c_old <= 0) return ac_old; - advance_rw2_minfun f_a(dt, a_old * a_old, tpl, RH_max); - advance_rw2_minfun f_c(dt, c_old * c_old, tpl, RH_max); + advance_rw2_minfun_ice f_a(dt, a_old * a_old, tpl, RH_max); + advance_rw2_minfun_ice f_c(dt, c_old * c_old, tpl, RH_max); const real_t da_dt = (f_a.drw2_dt(a_old * a_old * si::square_metres) / (2 * a_old * si::metres)).value(); const real_t dc_dt = (f_c.drw2_dt(c_old * c_old * si::square_metres) / (2 * c_old * si::metres)).value(); diff --git a/src/impl/particles_impl_cond_sstp.ipp b/src/impl/particles_impl_cond_sstp.ipp index f80cbf686..b6d3dc661 100644 --- a/src/impl/particles_impl_cond_sstp.ipp +++ b/src/impl/particles_impl_cond_sstp.ipp @@ -55,8 +55,7 @@ namespace libcloudphxx kpa.begin(), vt.begin(), thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), - thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()), - ice.begin() // ice can be nonsense if ice_swtich==0, because it is not assigned... + thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) )); // calculating drop growth in a timestep using backward Euler diff --git a/src/impl/particles_impl_hskpng_vterm.ipp b/src/impl/particles_impl_hskpng_vterm.ipp index 1a2586f2e..4c55f504f 100644 --- a/src/impl/particles_impl_hskpng_vterm.ipp +++ b/src/impl/particles_impl_hskpng_vterm.ipp @@ -178,8 +178,7 @@ namespace libcloudphxx thrust::make_permutation_iterator(vt_0.begin(), vt0_bin.begin()), thrust::make_permutation_iterator(p.begin(), ijk.begin()), thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()), - ice.begin() + thrust::make_permutation_iterator(eta.begin(), ijk.begin()) )), // input - 2nd arg vt.begin(), // condition argument vt.begin(), // output @@ -195,8 +194,7 @@ namespace libcloudphxx thrust::make_permutation_iterator(T.begin(), ijk.begin()), thrust::make_permutation_iterator(p.begin(), ijk.begin()), thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()), - ice.begin() + thrust::make_permutation_iterator(eta.begin(), ijk.begin()) )), // input - 2nd arg vt.begin(), // condition argument vt.begin(), // output @@ -224,8 +222,7 @@ namespace libcloudphxx thrust::make_permutation_iterator(vt_0.begin(), vt0_bin.begin()), thrust::make_permutation_iterator(p.begin(), ijk.begin()), thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()), - ice.begin() + thrust::make_permutation_iterator(eta.begin(), ijk.begin()) )), // input - 2nd arg vt.begin(), // output detail::common__vterm__vt__cached(opts_init.terminal_velocity) @@ -238,8 +235,7 @@ namespace libcloudphxx thrust::make_permutation_iterator(T.begin(), ijk.begin()), thrust::make_permutation_iterator(p.begin(), ijk.begin()), thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()), - ice.begin() + thrust::make_permutation_iterator(eta.begin(), ijk.begin()) )), // input - 2nd arg vt.begin(), // output detail::common__vterm__vt(opts_init.terminal_velocity) diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp index 5d0e482db..f4d61333c 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -15,30 +15,28 @@ namespace libcloudphxx namespace detail { // Singular immersion freezing (Shima et al., 2020) - // Functor to update ice flag, rw2, a, c, rho_i, of frozen droplets + // Functor to update rw2, a, c, rho_i, of frozen droplets template class singular_freeze { public: BOOST_GPU_ENABLED void operator()(thrust::tuple< - real_t&, real_t&, real_t&, real_t&, real_t&, // to be updated (ice, rw2, a, c, rho_i) + real_t&, real_t&, real_t&, real_t&, // to be updated (rw2, a, c, rho_i) const real_t&, const real_t&, const real_t& // T_freeze, T, RH > tpl) const { - auto& ice = thrust::get<0>(tpl); - auto& rw2 = thrust::get<1>(tpl); - auto& a = thrust::get<2>(tpl); - auto& c = thrust::get<3>(tpl); - auto& rho_i = thrust::get<4>(tpl); + auto& rw2 = thrust::get<0>(tpl); + auto& a = thrust::get<1>(tpl); + auto& c = thrust::get<2>(tpl); + auto& rho_i = thrust::get<3>(tpl); - const real_t T_freeze = thrust::get<5>(tpl); - const real_t T = thrust::get<6>(tpl); - const real_t RH = thrust::get<7>(tpl); + const real_t T_freeze = thrust::get<4>(tpl); + const real_t T = thrust::get<5>(tpl); + const real_t RH = thrust::get<6>(tpl); if (T_freeze >= T && RH >= real_t(1)) // condition for freezing { - ice = real_t(1); rw2 = real_t(0); rho_i = common::moist_air::rho_i().value(); a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); @@ -48,7 +46,7 @@ namespace libcloudphxx }; // Time-dependent immersion freezing (Arabas et al., 2025) - // Functor to update ice flag, rw2, a, c, rho_i, of frozen droplets + // Functor to update rw2, a, c, rho_i, of frozen droplets template class time_dep_freeze { @@ -62,23 +60,21 @@ namespace libcloudphxx BOOST_GPU_ENABLED void operator()(thrust::tuple< - real_t&, real_t&, real_t&, real_t&, real_t&, // to be updated (ice, rw2, a, c, rho_i) + real_t&, real_t&, real_t&, real_t&, // to be updated (rw2, a, c, rho_i) const real_t&, const real_t&, const real_t& // rd2_insol, u01, T > tpl) const { - auto& ice = thrust::get<0>(tpl); - auto& rw2 = thrust::get<1>(tpl); - auto& a = thrust::get<2>(tpl); - auto& c = thrust::get<3>(tpl); - auto& rho_i = thrust::get<4>(tpl); + auto& rw2 = thrust::get<0>(tpl); + auto& a = thrust::get<1>(tpl); + auto& c = thrust::get<2>(tpl); + auto& rho_i = thrust::get<3>(tpl); - const real_t rd2_insol = thrust::get<5>(tpl); - const real_t u01 = thrust::get<6>(tpl); - const real_t T = thrust::get<7>(tpl); + const real_t rd2_insol = thrust::get<4>(tpl); + const real_t u01 = thrust::get<5>(tpl); + const real_t T = thrust::get<6>(tpl); if (u01 < common::ice_nucleation::p_freeze(common::ice_nucleation::INP_t::mineral, rd2_insol, T, dt)) { - ice = real_t(1); rw2 = real_t(0); rho_i = common::moist_air::rho_i().value(); a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); @@ -87,26 +83,24 @@ namespace libcloudphxx } }; - // Functor to update ice flag, rw2, a, c, rho_i, of melted ice + // Functor to update rw2, a, c, rho_i, of melted ice template class melt { public: BOOST_GPU_ENABLED void operator()(thrust::tuple< - real_t&, real_t&, real_t&, real_t&, real_t&, // to be updated (ice, rw2, a, c, rho_i) + real_t&, real_t&, real_t&, real_t&, // to be updated (rw2, a, c, rho_i) const real_t& // ambient T > tpl) const { - auto& ice = thrust::get<0>(tpl); - auto& rw2 = thrust::get<1>(tpl); - auto& a = thrust::get<2>(tpl); - auto& c = thrust::get<3>(tpl); - auto& rho_i = thrust::get<4>(tpl); + auto& rw2 = thrust::get<0>(tpl); + auto& a = thrust::get<1>(tpl); + auto& c = thrust::get<2>(tpl); + auto& rho_i = thrust::get<3>(tpl); - if (thrust::get<5>(tpl) > real_t(273.15)) // if T > 0 C + if (thrust::get<4>(tpl) > real_t(273.15)) // if T > 0 C { - ice = real_t(0); rw2 = pow(common::moist_air::rho_i() / common::moist_air::rho_w() * c , real_t(2./3.)) * pow(a , real_t(4./3.)); rho_i = real_t(0); a = real_t(0); @@ -127,7 +121,7 @@ namespace libcloudphxx thrust_device::vector &drw(tmp_device_real_cell); // Compute per-cell 3rd moment of liquid droplets (sum of n*r^3) before freezing/melting. It is stored in count_mom - moms_eq0(ice.begin()); // choose particles with ice=0 + moms_eq0(ice_a.begin()); // choose liquid particles (ice_a=0) moms_calc(rw2.begin(), real_t(1.5)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of droplets before freezing/melting"); if (count_n != n_cell) @@ -146,7 +140,6 @@ namespace libcloudphxx rand_u01(n_part); // random numbers between [0,1] for each particle thrust::for_each( thrust::make_zip_iterator(thrust::make_tuple( - ice.begin(), rw2.begin(), ice_a.begin(), ice_c.begin(), @@ -156,7 +149,6 @@ namespace libcloudphxx thrust::make_permutation_iterator(T.begin(), ijk.begin()) )), thrust::make_zip_iterator(thrust::make_tuple( - ice.begin(), rw2.begin(), ice_a.begin(), ice_c.begin(), @@ -165,14 +157,13 @@ namespace libcloudphxx u01.begin(), thrust::make_permutation_iterator(T.begin(), ijk.begin()) )) + n_part, - detail::time_dep_freeze(dt) // functor for updating (ice, rw2, a, c, rho_i) if freezing condition satisfied + detail::time_dep_freeze(dt) // functor for updating (rw2, a, c, rho_i) if freezing condition satisfied ); } else // singular freezing based on Shima et al., 2020 { thrust::for_each( thrust::make_zip_iterator(thrust::make_tuple( - ice.begin(), rw2.begin(), ice_a.begin(), ice_c.begin(), @@ -182,7 +173,6 @@ namespace libcloudphxx thrust::make_permutation_iterator(RH.begin(), ijk.begin()) )), thrust::make_zip_iterator(thrust::make_tuple( - ice.begin(), rw2.begin(), ice_a.begin(), ice_c.begin(), @@ -191,14 +181,13 @@ namespace libcloudphxx thrust::make_permutation_iterator(T.begin(), ijk.begin()), thrust::make_permutation_iterator(RH.begin(), ijk.begin()) )) + n_part, - detail::singular_freeze() // functor for updating (ice, rw2, a, c, rho_i) if freezing condition satisfied + detail::singular_freeze() // functor for updating (rw2, a, c, rho_i) if freezing condition satisfied ); } // Change ice to liquid droplets under the melting condition thrust::for_each( thrust::make_zip_iterator(thrust::make_tuple( - ice.begin(), rw2.begin(), ice_a.begin(), ice_c.begin(), @@ -206,18 +195,17 @@ namespace libcloudphxx thrust::make_permutation_iterator(T.begin(), ijk.begin()) )), thrust::make_zip_iterator(thrust::make_tuple( - ice.begin(), rw2.begin(), ice_a.begin(), ice_c.begin(), ice_rho.begin(), thrust::make_permutation_iterator(T.begin(), ijk.begin()) )) + n_part, - detail::melt() // functor for updating (ice, rw2, a, c, rho_i) if melting condition satisfied + detail::melt() // functor for updating (rw2, a, c, rho_i) if melting condition satisfied ); // Compute per-cell 3rd moment of liquid droplets after freezing/melting. It is stored in count_mom - moms_eq0(ice.begin()); // choose particles with ice=0 + moms_eq0(ice_a.begin()); // choose liquid particles (ice_a=0) moms_calc(rw2.begin(), real_t(1.5)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) of droplets after freezing/melting"); diff --git a/src/impl/particles_impl_init_SD_with_distros.ipp b/src/impl/particles_impl_init_SD_with_distros.ipp index 74c1e860d..c4f326315 100644 --- a/src/impl/particles_impl_init_SD_with_distros.ipp +++ b/src/impl/particles_impl_init_SD_with_distros.ipp @@ -55,8 +55,12 @@ namespace libcloudphxx // init kappa init_kappa(kpa_rd_insol.kappa); - // init rd2_insol - init_insol_dry_sizes(kpa_rd_insol.rd_insol); + if (opts_init.ice_switch) + { + init_ice(real_t(0)); + init_insol_dry_sizes(kpa_rd_insol.rd_insol); + init_a_c_rho_ice(); + } // initialising wet radii init_wet(); diff --git a/src/impl/particles_impl_init_SD_with_sizes.ipp b/src/impl/particles_impl_init_SD_with_sizes.ipp index 733b374b2..67bc1b3f5 100644 --- a/src/impl/particles_impl_init_SD_with_sizes.ipp +++ b/src/impl/particles_impl_init_SD_with_sizes.ipp @@ -47,7 +47,13 @@ namespace libcloudphxx // init kappa and rd_insol init_kappa(kappa); - init_insol_dry_sizes(rd_insol); + + if (opts_init.ice_switch) + { + init_ice(real_t(0)); + init_insol_dry_sizes(rd_insol); + init_a_c_rho_ice(); + } // init multiplicities init_n_dry_sizes(sni->second.first, sni->second.second); diff --git a/src/impl/particles_impl_init_ice.ipp b/src/impl/particles_impl_init_ice.ipp deleted file mode 100644 index 75e978419..000000000 --- a/src/impl/particles_impl_init_ice.ipp +++ /dev/null @@ -1,23 +0,0 @@ -// vim:filetype=cpp -/** @file - * @copyright University of Warsaw - * @section LICENSE - * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) - * @brief initialisation routine for super droplets - */ - -namespace libcloudphxx -{ - namespace lgrngn - { - template - void particles_t::impl::init_ice( - const real_t &val - ) - { - assert(val==0 || val==1); - // filling ice flag - thrust::fill(ice.begin() + n_part_old, ice.end(), val); - } - }; -}; \ No newline at end of file diff --git a/src/impl/particles_impl_reserve_hskpng_npart.ipp b/src/impl/particles_impl_reserve_hskpng_npart.ipp index 776122af8..1755c2e93 100644 --- a/src/impl/particles_impl_reserve_hskpng_npart.ipp +++ b/src/impl/particles_impl_reserve_hskpng_npart.ipp @@ -38,7 +38,6 @@ namespace libcloudphxx if(opts_init.ice_switch) { - ice.reserve(opts_init.n_sd_max); rd2_insol.reserve(opts_init.n_sd_max); T_freeze.reserve(opts_init.n_sd_max); ice_a.reserve(opts_init.n_sd_max); diff --git a/src/particles.tpp b/src/particles.tpp index 45eae00ff..a440501e6 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -63,7 +63,6 @@ #include "impl/particles_impl_init_dry_const_multi.ipp" #include "impl/particles_impl_init_dry_dry_sizes.ipp" #include "impl/particles_impl_init_kappa.ipp" -#include "impl/particles_impl_init_ice.ipp" #include "impl/particles_impl_init_insol_dry_sizes.ipp" #include "impl/particles_impl_init_T_freeze.ipp" #include "impl/particles_impl_init_a_c_rho_ice.ipp" diff --git a/src/particles_diag.ipp b/src/particles_diag.ipp index 435139332..9790a2ecd 100644 --- a/src/particles_diag.ipp +++ b/src/particles_diag.ipp @@ -232,14 +232,14 @@ namespace libcloudphxx template void particles_t::diag_ice() { - pimpl->moms_gt0(pimpl->ice.begin()); // ice flag greater than 0 + pimpl->moms_gt0(pimpl->ice_a.begin()); // ice_a greater than 0 } // selects water particles template void particles_t::diag_water() { - pimpl->moms_eq0(pimpl->ice.begin()); // ice flag equal to 0 + pimpl->moms_eq0(pimpl->ice_a.begin()); // ice_a equal to 0 } // selects particles with (r_d >= r_min && r_d < r_max) from particles previously selected From ee6b2f8bee1f1b0500bb3f64a8cf5b690be4a42b Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 30 Oct 2025 13:31:21 +0100 Subject: [PATCH 65/97] fix diag_ice_cons --- src/impl/particles_impl.ipp | 6 +- .../particles_impl_init_SD_with_distros.ipp | 1 - .../particles_impl_init_SD_with_sizes.ipp | 1 - src/impl/particles_impl_moms.ipp | 58 ++++++++++++++----- src/particles_diag.ipp | 4 +- 5 files changed, 48 insertions(+), 22 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 63f550056..52afc69c6 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -562,10 +562,12 @@ namespace libcloudphxx const typename thrust_device::vector::iterator &vec_bgn ); void moms_gt0( - const typename thrust_device::vector::iterator &vec_bgn + const typename thrust_device::vector::iterator &vec_bgn, + const bool cons = false ); void moms_eq0( - const typename thrust_device::vector::iterator &vec_bgn + const typename thrust_device::vector::iterator &vec_bgn, + const bool cons = false ); void moms_rng( const real_t &min, const real_t &max, diff --git a/src/impl/particles_impl_init_SD_with_distros.ipp b/src/impl/particles_impl_init_SD_with_distros.ipp index c4f326315..0f06d4e47 100644 --- a/src/impl/particles_impl_init_SD_with_distros.ipp +++ b/src/impl/particles_impl_init_SD_with_distros.ipp @@ -57,7 +57,6 @@ namespace libcloudphxx if (opts_init.ice_switch) { - init_ice(real_t(0)); init_insol_dry_sizes(kpa_rd_insol.rd_insol); init_a_c_rho_ice(); } diff --git a/src/impl/particles_impl_init_SD_with_sizes.ipp b/src/impl/particles_impl_init_SD_with_sizes.ipp index 67bc1b3f5..de1a1637a 100644 --- a/src/impl/particles_impl_init_SD_with_sizes.ipp +++ b/src/impl/particles_impl_init_SD_with_sizes.ipp @@ -50,7 +50,6 @@ namespace libcloudphxx if (opts_init.ice_switch) { - init_ice(real_t(0)); init_insol_dry_sizes(rd_insol); init_a_c_rho_ice(); } diff --git a/src/impl/particles_impl_moms.ipp b/src/impl/particles_impl_moms.ipp index 36b4ef0aa..823dd2282 100644 --- a/src/impl/particles_impl_moms.ipp +++ b/src/impl/particles_impl_moms.ipp @@ -151,21 +151,34 @@ namespace libcloudphxx // selects particles for which vec[i] > 0 template void particles_t::impl::moms_gt0( - const typename thrust_device::vector::iterator &vec_bgn + const typename thrust_device::vector::iterator &vec_bgn, + const bool cons ) { hskpng_sort(); - thrust_device::vector &n_filtered(tmp_device_real_part); { namespace arg = thrust::placeholders; - thrust::transform( - n.begin(), n.end(), // input - 1st arg - vec_bgn, // input - 2nd arg - n_filtered.begin(), // output - arg::_1 * (arg::_2 > 0) // op - ); + + if(!cons) + { + thrust::transform( + n.begin(), n.end(), // input - 1st arg + vec_bgn, // input - 2nd arg + n_filtered.begin(), // output + arg::_1 * (arg::_2 > 0) // op + ); + } + else + { + thrust::transform( + n_filtered.begin(), n_filtered.end(), // input - 1st arg + vec_bgn, // input - 2nd arg + n_filtered.begin(), // output + arg::_1 * (arg::_2 > 0) // op + ); + } } selected_before_counting = true; } @@ -173,21 +186,34 @@ namespace libcloudphxx // selects particles for which vec[i] = 0 template void particles_t::impl::moms_eq0( - const typename thrust_device::vector::iterator &vec_bgn + const typename thrust_device::vector::iterator &vec_bgn, + const bool cons ) { hskpng_sort(); - thrust_device::vector &n_filtered(tmp_device_real_part); { namespace arg = thrust::placeholders; - thrust::transform( - n.begin(), n.end(), // input - 1st arg - vec_bgn, // input - 2nd arg - n_filtered.begin(), // output - arg::_1 * (arg::_2 == 0) // op - ); + + if(!cons) + { + thrust::transform( + n.begin(), n.end(), // input - 1st arg + vec_bgn, // input - 2nd arg + n_filtered.begin(), // output + arg::_1 * (arg::_2 == 0) // op + ); + } + else + { + thrust::transform( + n_filtered.begin(), n_filtered.end(), // input - 1st arg + vec_bgn, // input - 2nd arg + n_filtered.begin(), // output + arg::_1 * (arg::_2 == 0) // op + ); + } } selected_before_counting = true; } diff --git a/src/particles_diag.ipp b/src/particles_diag.ipp index 9790a2ecd..de1c98ada 100644 --- a/src/particles_diag.ipp +++ b/src/particles_diag.ipp @@ -273,14 +273,14 @@ namespace libcloudphxx template void particles_t::diag_ice_cons() { - pimpl->moms_rng(1., 1.0001, pimpl->ice.begin(), true); + pimpl->moms_gt0(pimpl->ice_a.begin(), true); // ice_a greater than 0 } // selects water particles from particles previously selected template void particles_t::diag_water_cons() { - pimpl->moms_rng(0., 0.0001, pimpl->ice.begin(), true); + pimpl->moms_eq0(pimpl->ice_a.begin(), true); // ice_a equal to 0 } // selects particles with RH >= Sc (Sc - critical supersaturation) From 19519558c4538b6bffc6242ae70e6800902f556e Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 30 Oct 2025 15:39:08 +0100 Subject: [PATCH 66/97] fix condendation --- src/impl/particles_impl_cond.ipp | 4 +--- src/impl/particles_impl_cond_common.ipp | 17 ++++++----------- src/impl/particles_impl_cond_sstp.ipp | 4 +--- src/impl/particles_impl_hskpng_vterm.ipp | 13 ++----------- 4 files changed, 10 insertions(+), 28 deletions(-) diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index 1e78c5802..5bb04e225 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -109,8 +109,7 @@ namespace libcloudphxx thrust::make_tuple( hlpr_zip_iter, thrust::make_permutation_iterator(p.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH_i.begin(), ijk.begin()) + thrust::make_permutation_iterator(RH.begin(), ijk.begin()) ) ), rw2.begin(), // output @@ -136,7 +135,6 @@ namespace libcloudphxx thrust::make_tuple( hlpr_zip_iter, thrust::make_permutation_iterator(p.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH.begin(), ijk.begin()), thrust::make_permutation_iterator(RH_i.begin(), ijk.begin()) ) ), diff --git a/src/impl/particles_impl_cond_common.ipp b/src/impl/particles_impl_cond_common.ipp index 951b8fd60..edc08efdc 100644 --- a/src/impl/particles_impl_cond_common.ipp +++ b/src/impl/particles_impl_cond_common.ipp @@ -256,7 +256,7 @@ namespace libcloudphxx BOOST_GPU_ENABLED real_t operator()( const real_t &rw2_old, - const thrust::tuple, real_t, real_t, real_t> &tpl + const thrust::tuple, real_t, real_t> &tpl ) const { #if !defined(__NVCC__) using std::min; @@ -266,11 +266,8 @@ namespace libcloudphxx using std::isinf; #endif - // Skip ice particles (with rw2=0) - if (rw2_old <= 0) return rw2_old; - auto& tpl_in = thrust::get<0>(tpl); - const advance_rw2_minfun f(dt, rw2_old, tpl, RH_max); + const advance_rw2_minfun f(dt, rw2_old, tpl, RH_max); const real_t drw2 = dt * f.drw2_dt(rw2_old * si::square_metres) * si::seconds / si::square_metres; #if !defined(NDEBUG) @@ -291,9 +288,8 @@ namespace libcloudphxx "kpa: %g " "vt: %g " "lambda_D: %g " - "lambda_K: %g " - "RH_i: %g\n", - drw2, rw2_old, dt, RH_max, + "lambda_K: %g\n", + drw2, rw2_old, dt, RH_max, thrust::get<0>(tpl_in), // rhod thrust::get<1>(tpl_in), // rv thrust::get<2>(tpl_in), // T @@ -304,8 +300,7 @@ namespace libcloudphxx thrust::get<5>(tpl_in), // kpa thrust::get<6>(tpl_in), // vt thrust::get<7>(tpl_in), // lambda_D - thrust::get<8>(tpl_in), // lambda_K - thrust::get<3>(tpl) // RH_i + thrust::get<8>(tpl_in) // lambda_K ); assert(0); } @@ -406,7 +401,7 @@ namespace libcloudphxx const thrust::tuple &ac_old, const thrust::tuple< thrust::tuple, - real_t, real_t, real_t> &tpl + real_t, real_t> &tpl ) const { #if !defined(__NVCC__) diff --git a/src/impl/particles_impl_cond_sstp.ipp b/src/impl/particles_impl_cond_sstp.ipp index b6d3dc661..8d79fffb3 100644 --- a/src/impl/particles_impl_cond_sstp.ipp +++ b/src/impl/particles_impl_cond_sstp.ipp @@ -66,9 +66,7 @@ namespace libcloudphxx // particle-specific p pi, // particle-specific RH - rhi, - // particle-specific RH_i - rhii + rhi )), rw2.begin(), // output detail::advance_rw2(dt, RH_max) diff --git a/src/impl/particles_impl_hskpng_vterm.ipp b/src/impl/particles_impl_hskpng_vterm.ipp index 4c55f504f..d65f72f4d 100644 --- a/src/impl/particles_impl_hskpng_vterm.ipp +++ b/src/impl/particles_impl_hskpng_vterm.ipp @@ -45,7 +45,7 @@ namespace libcloudphxx BOOST_GPU_ENABLED real_t operator()( const real_t &rw2, - const thrust::tuple &tpl + const thrust::tuple &tpl // (T, p, rhod, eta) ) { #if !defined(__NVCC__) using std::sqrt; @@ -102,10 +102,6 @@ namespace libcloudphxx break; } - // ice terminal velocity = droplet terminal velocity * rho_ice/rho_water - if(thrust::get<4>(tpl) == 1) - vt *= real_t(common::moist_air::rho_i() / common::moist_air::rho_w()); - return vt; } }; @@ -121,7 +117,7 @@ namespace libcloudphxx BOOST_GPU_ENABLED real_t operator()( const real_t &rw2, - const thrust::tuple &tpl + const thrust::tuple &tpl // (vt_0, p, rhod, eta) ) { #if !defined(__NVCC__) using std::sqrt; @@ -144,11 +140,6 @@ namespace libcloudphxx default: vt = 0.; //sanity checks done in pimpl constructor } - - // ice terminal velocity = droplet terminal velocity * rho_ice/rho_water - if(thrust::get<4>(tpl) == 1) - vt *= real_t(common::moist_air::rho_i() / common::moist_air::rho_w()); - return vt; } }; From ac71f9d3f2949a63191d3f2eceecfadc1c3508f6 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 30 Oct 2025 16:25:10 +0100 Subject: [PATCH 67/97] vterm_ice --- src/impl/particles_impl_hskpng_vterm.ipp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/impl/particles_impl_hskpng_vterm.ipp b/src/impl/particles_impl_hskpng_vterm.ipp index d65f72f4d..f23170dad 100644 --- a/src/impl/particles_impl_hskpng_vterm.ipp +++ b/src/impl/particles_impl_hskpng_vterm.ipp @@ -101,7 +101,6 @@ namespace libcloudphxx vt = 0.; //sanity checks done in pimpl constructor break; } - return vt; } }; @@ -142,7 +141,26 @@ namespace libcloudphxx } return vt; } - }; + }; + + template + struct common__vterm__ice + { + BOOST_GPU_ENABLED + real_t operator()( + const real_t &ice_a, + const thrust::tuple &tpl // (T, p, rhod, eta) + ) { + return common::vterm::vt_beard77_fact( + sqrt(ice_a) * si::metres, // TODO: consider caching rw? + thrust::get<1>(tpl) * si::pascals, + thrust::get<2>(tpl) * si::kilograms / si::cubic_metres, + thrust::get<3>(tpl) * si::pascals * si::seconds + ) * (common::vterm::vt_beard77_v0(sqrt(ice_a) * si::metres) / si::metres_per_second) + * real_t(common::moist_air::rho_i() / common::moist_air::rho_w()); + } + }; + }; From 7f2e63c2b9c200e3455363697f256341ab3c73a2 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 4 Nov 2025 11:33:02 +0100 Subject: [PATCH 68/97] select vt formula --- src/impl/particles_impl_hskpng_vterm.ipp | 131 +++++++++++++++++------ 1 file changed, 96 insertions(+), 35 deletions(-) diff --git a/src/impl/particles_impl_hskpng_vterm.ipp b/src/impl/particles_impl_hskpng_vterm.ipp index f23170dad..bbfc6adb5 100644 --- a/src/impl/particles_impl_hskpng_vterm.ipp +++ b/src/impl/particles_impl_hskpng_vterm.ipp @@ -149,18 +149,55 @@ namespace libcloudphxx BOOST_GPU_ENABLED real_t operator()( const real_t &ice_a, + const real_t &ice_c, + const real_t &ice_rho, const thrust::tuple &tpl // (T, p, rhod, eta) ) { - return common::vterm::vt_beard77_fact( - sqrt(ice_a) * si::metres, // TODO: consider caching rw? - thrust::get<1>(tpl) * si::pascals, - thrust::get<2>(tpl) * si::kilograms / si::cubic_metres, - thrust::get<3>(tpl) * si::pascals * si::seconds - ) * (common::vterm::vt_beard77_v0(sqrt(ice_a) * si::metres) / si::metres_per_second) - * real_t(common::moist_air::rho_i() / common::moist_air::rho_w()); + return common::vterm::vt_beard76( + ice_a * si::metres, + thrust::get<0>(tpl) * si::kelvins, + thrust::get<1>(tpl) * si::pascals, + thrust::get<2>(tpl) * si::kilograms / si::cubic_metres, + thrust::get<3>(tpl) * si::pascals * si::seconds + ) / si::metres_per_second + * real_t(common::moist_air::rho_i() / common::moist_air::rho_w()); } }; + // if ice_switch=True, use this struct to select the correct formula + template + struct select_vt + { + vt_t vt_eq; + + select_vt(const vt_t &vt_eq): vt_eq(vt_eq) {} + + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple< + real_t, real_t, real_t, real_t, // rw2, ice_a, ice_c, rho_i + real_t, real_t, real_t, real_t // T, p, rhod, eta + >& tpl) const + { + const real_t &rw2 = thrust::get<0>(tpl); + const real_t &ice_a = thrust::get<1>(tpl); + const real_t &ice_c = thrust::get<2>(tpl); + const real_t &rho_i = thrust::get<3>(tpl); + thrust::tuple env( + thrust::get<4>(tpl), thrust::get<5>(tpl), thrust::get<6>(tpl), thrust::get<7>(tpl) + ); + + if(rw2 == 0 && ice_a > 0 && ice_c > 0) { + return common__vterm__ice()(ice_a, ice_c, rho_i, env); + } + else if(vt_eq == vt_t::beard77fast) { + return common__vterm__vt__cached(vt_eq)(rw2, env); + } + else { + return common__vterm__vt(vt_eq)(rw2, env); + } + } + }; + }; @@ -214,41 +251,65 @@ namespace libcloudphxx template void particles_t::impl::hskpng_vterm_all() - { - if(opts_init.terminal_velocity == vt_t::beard77fast) //use cached vt at sea level + { + if (opts_init.ice_switch) { - thrust_device::vector &vt0_bin(tmp_device_size_part); - // get cached bin number - thrust::transform( - rw2.begin(), rw2.end(), - vt0_bin.begin(), - detail::get_vt0_bin(config.vt0_ln_r_min, config.vt0_ln_r_max, config.vt0_n_bin) - ); - // calc the vt - thrust::transform( - rw2.begin(), rw2.end(), // input - 1st arg - thrust::make_zip_iterator(thrust::make_tuple( - thrust::make_permutation_iterator(vt_0.begin(), vt0_bin.begin()), + auto zipped = thrust::make_zip_iterator(thrust::make_tuple( + rw2.begin(), + ice_a.begin(), + ice_c.begin(), + ice_rho.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), thrust::make_permutation_iterator(p.begin(), ijk.begin()), thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), thrust::make_permutation_iterator(eta.begin(), ijk.begin()) - )), // input - 2nd arg - vt.begin(), // output - detail::common__vterm__vt__cached(opts_init.terminal_velocity) + )); + auto zipped_end = zipped + rw2.size(); + + thrust::transform( + zipped, + zipped_end, + vt.begin(), + detail::select_vt(opts_init.terminal_velocity) ); } else - thrust::transform( - rw2.begin(), rw2.end(), // input - 1st arg - thrust::make_zip_iterator(thrust::make_tuple( - thrust::make_permutation_iterator(T.begin(), ijk.begin()), - thrust::make_permutation_iterator(p.begin(), ijk.begin()), - thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()) - )), // input - 2nd arg - vt.begin(), // output - detail::common__vterm__vt(opts_init.terminal_velocity) - ); + { + if(opts_init.terminal_velocity == vt_t::beard77fast) //use cached vt at sea level + { + thrust_device::vector &vt0_bin(tmp_device_size_part); + // get cached bin number + thrust::transform( + rw2.begin(), rw2.end(), + vt0_bin.begin(), + detail::get_vt0_bin(config.vt0_ln_r_min, config.vt0_ln_r_max, config.vt0_n_bin) + ); + // calc the vt + thrust::transform( + rw2.begin(), rw2.end(), // input - 1st arg + thrust::make_zip_iterator(thrust::make_tuple( + thrust::make_permutation_iterator(vt_0.begin(), vt0_bin.begin()), + thrust::make_permutation_iterator(p.begin(), ijk.begin()), + thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), + thrust::make_permutation_iterator(eta.begin(), ijk.begin()) + )), // input - 2nd arg + vt.begin(), // output + detail::common__vterm__vt__cached(opts_init.terminal_velocity) + ); + } + else + thrust::transform( + rw2.begin(), rw2.end(), // input - 1st arg + thrust::make_zip_iterator(thrust::make_tuple( + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(p.begin(), ijk.begin()), + thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), + thrust::make_permutation_iterator(eta.begin(), ijk.begin()) + )), // input - 2nd arg + vt.begin(), // output + detail::common__vterm__vt(opts_init.terminal_velocity) + ); + } } }; }; From 59842e138c5b3e6adf2020f44c0e14360e81c9f4 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 5 Nov 2025 16:18:36 +0100 Subject: [PATCH 69/97] fixing errors --- include/libcloudph++/lgrngn/distro_t.hpp | 4 + src/impl/particles_impl_bcnd.ipp | 57 +++++++------- src/impl/particles_impl_hskpng_vterm.ipp | 97 +++++++++++++++--------- tests/python/unit/api_lgrngn.py | 20 ++--- 4 files changed, 105 insertions(+), 73 deletions(-) diff --git a/include/libcloudph++/lgrngn/distro_t.hpp b/include/libcloudph++/lgrngn/distro_t.hpp index 14aa2bb06..9389b5846 100644 --- a/include/libcloudph++/lgrngn/distro_t.hpp +++ b/include/libcloudph++/lgrngn/distro_t.hpp @@ -11,6 +11,10 @@ namespace libcloudphxx real_t kappa; real_t rd_insol; + kappa_rd_insol_t(real_t kappa_, real_t rd_insol_) + : kappa(kappa_), rd_insol(rd_insol_) + {} + bool operator<(const kappa_rd_insol_t &other) const { if (kappa != other.kappa) return kappa < other.kappa; diff --git a/src/impl/particles_impl_bcnd.ipp b/src/impl/particles_impl_bcnd.ipp index 45d3f260d..a30c86e12 100644 --- a/src/impl/particles_impl_bcnd.ipp +++ b/src/impl/particles_impl_bcnd.ipp @@ -78,9 +78,9 @@ namespace libcloudphxx template BOOST_GPU_ENABLED - real_t operator()(const tuple &tup) // tup is a tuple (n_filtered, ice_a) + real_t operator()(const tuple &tup) // tup is a tuple (n_filtered, rw2) { - if((ice==0 && thrust::get<1>(tup)>real_t(0)) || (ice==1 && thrust::get<1>(tup)==real_t(0))) return 0.; + if((ice==0 && thrust::get<1>(tup) == real_t(0)) || (ice==1 && thrust::get<1>(tup) > real_t(0))) return 0.; return thrust::get<0>(tup); } }; @@ -259,18 +259,6 @@ namespace libcloudphxx thrust::plus() ); - // add total ice volume that fell out in this step - output_puddle[common::outice_vol] += - thrust::transform_reduce( - thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), ice_a.begin(), ice_c.begin())), // input start - thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), ice_a.begin(), ice_c.begin())) + n_part, // input end - detail::count_ice_vol(), // operation - real_t(0), // init val - thrust::plus() - ); - // add total dry volume that fell out in this step output_puddle[common::outdry_vol] += thrust::transform_reduce( @@ -287,25 +275,40 @@ namespace libcloudphxx output_puddle[common::outliq_num] += thrust::transform_reduce( thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), ice_a.begin())), // input start + n_filtered.begin(), rw2.begin())), // input start thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), ice_a.begin())) + n_part, // input end + n_filtered.begin(), rw2.begin())) + n_part, // input end detail::count_num(0), // operation real_t(0), // init val thrust::plus() ); - // add total number of ice droplets that fell out in this step - output_puddle[common::outice_num] += - thrust::transform_reduce( - thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), ice_a.begin())), // input start - thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), ice_a.begin())) + n_part, // input end - detail::count_num(1), // operation - real_t(0), // init val - thrust::plus() - ); + if (opts_init.ice_switch) + { + // add total ice volume that fell out in this step + output_puddle[common::outice_vol] += + thrust::transform_reduce( + thrust::make_zip_iterator(thrust::make_tuple( + n_filtered.begin(), ice_a.begin(), ice_c.begin())), // input start + thrust::make_zip_iterator(thrust::make_tuple( + n_filtered.begin(), ice_a.begin(), ice_c.begin())) + n_part, // input end + detail::count_ice_vol(), // operation + real_t(0), // init val + thrust::plus() + ); + + // add total number of ice droplets that fell out in this step + output_puddle[common::outice_num] += + thrust::transform_reduce( + thrust::make_zip_iterator(thrust::make_tuple( + n_filtered.begin(), rw2.begin())), // input start + thrust::make_zip_iterator(thrust::make_tuple( + n_filtered.begin(), rw2.begin())) + n_part, // input end + detail::count_num(1), // operation + real_t(0), // init val + thrust::plus() + ); + } /* output_puddle[common::outliq_num] += diff --git a/src/impl/particles_impl_hskpng_vterm.ipp b/src/impl/particles_impl_hskpng_vterm.ipp index bbfc6adb5..292f3bd24 100644 --- a/src/impl/particles_impl_hskpng_vterm.ipp +++ b/src/impl/particles_impl_hskpng_vterm.ipp @@ -190,7 +190,7 @@ namespace libcloudphxx return common__vterm__ice()(ice_a, ice_c, rho_i, env); } else if(vt_eq == vt_t::beard77fast) { - return common__vterm__vt__cached(vt_eq)(rw2, env); + throw std::runtime_error("beard77fast doesn't work with ice switch = True"); } else { return common__vterm__vt(vt_eq)(rw2, env); @@ -203,50 +203,75 @@ namespace libcloudphxx template void particles_t::impl::hskpng_vterm_invalid() - { + { namespace arg = thrust::placeholders; - if(opts_init.terminal_velocity == vt_t::beard77fast) //use cached vt at sea level + if (opts_init.ice_switch) { - thrust_device::vector &vt0_bin(tmp_device_size_part); - // get cached bin number - thrust::transform_if( - rw2.begin(), rw2.end(), - vt.begin(), - vt0_bin.begin(), - detail::get_vt0_bin(config.vt0_ln_r_min, config.vt0_ln_r_max, config.vt0_n_bin), - arg::_1 == real_t(detail::invalid) - ); - // calc the vt - thrust::transform_if( - rw2.begin(), rw2.end(), // input - 1st arg - thrust::make_zip_iterator(thrust::make_tuple( - thrust::make_permutation_iterator(vt_0.begin(), vt0_bin.begin()), + auto zipped = thrust::make_zip_iterator(thrust::make_tuple( + rw2.begin(), + ice_a.begin(), + ice_c.begin(), + ice_rho.begin(), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), thrust::make_permutation_iterator(p.begin(), ijk.begin()), thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), thrust::make_permutation_iterator(eta.begin(), ijk.begin()) - )), // input - 2nd arg - vt.begin(), // condition argument - vt.begin(), // output - detail::common__vterm__vt__cached(opts_init.terminal_velocity), - arg::_1 == real_t(detail::invalid) + )); + auto zipped_end = zipped + rw2.size(); + + thrust::transform( + zipped, + zipped_end, + vt.begin(), + detail::select_vt(opts_init.terminal_velocity) ); } - // non-cached vt else - thrust::transform_if( - rw2.begin(), rw2.end(), // input - 1st arg - thrust::make_zip_iterator(thrust::make_tuple( - thrust::make_permutation_iterator(T.begin(), ijk.begin()), - thrust::make_permutation_iterator(p.begin(), ijk.begin()), - thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()) - )), // input - 2nd arg - vt.begin(), // condition argument - vt.begin(), // output - detail::common__vterm__vt(opts_init.terminal_velocity), - arg::_1 == real_t(detail::invalid) - ); + { + + if(opts_init.terminal_velocity == vt_t::beard77fast) //use cached vt at sea level + { + thrust_device::vector &vt0_bin(tmp_device_size_part); + // get cached bin number + thrust::transform_if( + rw2.begin(), rw2.end(), + vt.begin(), + vt0_bin.begin(), + detail::get_vt0_bin(config.vt0_ln_r_min, config.vt0_ln_r_max, config.vt0_n_bin), + arg::_1 == real_t(detail::invalid) + ); + // calc the vt + thrust::transform_if( + rw2.begin(), rw2.end(), // input - 1st arg + thrust::make_zip_iterator(thrust::make_tuple( + thrust::make_permutation_iterator(vt_0.begin(), vt0_bin.begin()), + thrust::make_permutation_iterator(p.begin(), ijk.begin()), + thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), + thrust::make_permutation_iterator(eta.begin(), ijk.begin()) + )), // input - 2nd arg + vt.begin(), // condition argument + vt.begin(), // output + detail::common__vterm__vt__cached(opts_init.terminal_velocity), + arg::_1 == real_t(detail::invalid) + ); + } + // non-cached vt + else + thrust::transform_if( + rw2.begin(), rw2.end(), // input - 1st arg + thrust::make_zip_iterator(thrust::make_tuple( + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(p.begin(), ijk.begin()), + thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), + thrust::make_permutation_iterator(eta.begin(), ijk.begin()) + )), // input - 2nd arg + vt.begin(), // condition argument + vt.begin(), // output + detail::common__vterm__vt(opts_init.terminal_velocity), + arg::_1 == real_t(detail::invalid) + ); + } } template diff --git a/tests/python/unit/api_lgrngn.py b/tests/python/unit/api_lgrngn.py index d481de5d5..fea11812c 100644 --- a/tests/python/unit/api_lgrngn.py +++ b/tests/python/unit/api_lgrngn.py @@ -242,8 +242,8 @@ def lognormal(lnr): # 0D dry_sizes init with two kappas print("0D dry sizes") opts_init.dry_distros = dict() -opts_init.dry_sizes = {kappa1 : {1.e-6 : [30. * rho_stp, 15], 15.e-6 : [10. * rho_stp, 10]}, - kappa2 : {1.2e-6 : [20. * rho_stp, 10], 12.e-6 : [15. * rho_stp, 15]}} +opts_init.dry_sizes = {(kappa1, rd_insol) : {1.e-6 : [30. * rho_stp, 15], 15.e-6 : [10. * rho_stp, 10]}, + (kappa2, rd_insol) : {1.2e-6 : [20. * rho_stp, 10], 12.e-6 : [15. * rho_stp, 15]}} sd_conc_old = opts_init.sd_conc opts_init.sd_conc = 0 @@ -302,14 +302,14 @@ def lognormal(lnr): # go back to distros init opts_init.sd_conc = sd_conc_old opts_init.dry_sizes = dict() -opts_init.dry_distros = {kappa1:lognormal, kappa2:lognormal} +opts_init.dry_distros = {(kappa1, rd_insol):lognormal, (kappa2, rd_insol):lognormal} # ---------- # 0D dry_sizes + sd_conc init print("0D dry_sizes + sd_conc") -opts_init.dry_sizes = {kappa3 : {1.e-6 : [30. * rho_stp, 15], 15.e-6 : [10. * rho_stp, 5]}} +opts_init.dry_sizes = {(kappa3, rd_insol) : {1.e-6 : [30. * rho_stp, 15], 15.e-6 : [10. * rho_stp, 5]}} prtcls = lgrngn.factory(backend, opts_init) prtcls.init(th, rv, rhod) @@ -327,7 +327,7 @@ def lognormal(lnr): # ---------- # 0D dry_sizes + sd_conc + tail print("0D dry_sizes + sd_conc + tail") -opts_init.dry_sizes = {kappa3 : {1.e-6 : [30. * rho_stp, 15], 15.e-6 : [10. * rho_stp, 5]}} +opts_init.dry_sizes = {(kappa3, rd_insol): {1.e-6 : [30. * rho_stp, 15], 15.e-6 : [10. * rho_stp, 5]}} opts_init.sd_conc_large_tail = 1 prtcls = lgrngn.factory(backend, opts_init) @@ -347,7 +347,7 @@ def lognormal(lnr): # ---------- # 0D dry_sizes + const_multi init print("0D dry_sizes + const_multi") -opts_init.dry_sizes = {kappa3 : {1.e-6 : [30. * rho_stp, 15], 15.e-6 : [10. * rho_stp, 5]}} +opts_init.dry_sizes = {(kappa3, rd_insol) : {1.e-6 : [30. * rho_stp, 15], 15.e-6 : [10. * rho_stp, 5]}} opts_init.sd_conc = 0 prtcls_per_cell = 2 * n_tot / rho_stp #rhod=1; 2* because of two distributions opts_init.sd_const_multi = int(prtcls_per_cell / 64) @@ -618,7 +618,7 @@ def lognormal(lnr): # 3D dry_sizes init print("3D dry sizes") opts_init.dry_distros = dict() -opts_init.dry_sizes = {kappa1 : {1.e-6 : [30./ cell_vol * rho_stp, 30], 15.e-6 : [10. / cell_vol * rho_stp, 10]}} +opts_init.dry_sizes = {(kappa1, rd_insol) : {1.e-6 : [30./ cell_vol * rho_stp, 30], 15.e-6 : [10. / cell_vol * rho_stp, 10]}} prtcls = lgrngn.factory(backend, opts_init) prtcls.init(th, rv, rhod) @@ -650,8 +650,8 @@ def lognormal(lnr): # ---------- # 3D dry_sizes + sd_conc init print("3D dry_sizes + sd_conc") -opts_init.dry_distros = {kappa1:lognormal, kappa2:lognormal} -opts_init.dry_sizes = {kappa1 : {1.e-6 : [30./ cell_vol * rho_stp, 15], 15.e-6 : [10. / cell_vol * rho_stp, 5]}} +opts_init.dry_distros = {(kappa1, rd_insol):lognormal, (kappa2, rd_insol):lognormal} +opts_init.dry_sizes = {(kappa1, rd_insol) : {1.e-6 : [30./ cell_vol * rho_stp, 15], 15.e-6 : [10. / cell_vol * rho_stp, 5]}} opts_init.sd_conc = sd_conc_old opts_init.sd_const_multi = 0 @@ -693,7 +693,7 @@ def lognormal(lnr): # ---------- # 3D dry_sizes + const_multi init print("3D dry_sizes + const_multi") -opts_init.dry_sizes = {kappa1 : {1.e-6 : [30./ cell_vol * rho_stp, 15], 15.e-6 : [10. / cell_vol * rho_stp, 5]}} +opts_init.dry_sizes = {(kappa1, rd_insol) : {1.e-6 : [30./ cell_vol * rho_stp, 15], 15.e-6 : [10. / cell_vol * rho_stp, 5]}} opts_init.sd_conc = 0 prtcls_per_cell = 2 * n_tot * cell_vol / rho_stp #rhod=1; 2* because of two distributions opts_init.sd_const_multi = int(prtcls_per_cell / 64) From a581bbd2a483c5ebda2ff0887d5f26f5b487a5ae Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 6 Nov 2025 12:55:19 +0100 Subject: [PATCH 70/97] use ice.switch in cond --- src/impl/particles_impl.ipp | 6 +- src/impl/particles_impl_cond.ipp | 158 ++++++++++++------------ src/impl/particles_impl_cond_common.ipp | 3 + 3 files changed, 89 insertions(+), 78 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 52afc69c6..93ae50a1f 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -457,13 +457,15 @@ namespace libcloudphxx if (opts_init.ny != 0) resize_size_vctrs.insert(&j); if (opts_init.nz != 0) resize_size_vctrs.insert(&k); + distmem_real_vctrs.insert({&rd2_insol, detail::no_initial_value}); + if(opts_init.ice_switch) { - distmem_real_vctrs.insert({&rd2_insol, detail::no_initial_value}); - distmem_real_vctrs.insert({&T_freeze, detail::no_initial_value}); distmem_real_vctrs.insert({&ice_a, detail::no_initial_value}); distmem_real_vctrs.insert({&ice_c, detail::no_initial_value}); distmem_real_vctrs.insert({&ice_rho, detail::no_initial_value}); + if (opts_init.time_dep_ice_nucl == false) + {distmem_real_vctrs.insert({&T_freeze, detail::no_initial_value});} } } diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index 5bb04e225..9332cc23d 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -24,12 +24,11 @@ namespace libcloudphxx hskpng_sort(); - // Vectors to store 3rd moments + // Vector to store 3rd moment thrust_device::vector &drv_liq(tmp_device_real_cell); - thrust_device::vector &drv_ice(tmp_device_real_cell3); // Compute per-cell 3rd moment of liquid droplets before condensation. It is stored in count_mom - moms_eq0(ice_a.begin()); // choose liquid particles (ice_a=0) + moms_gt0(rw2.begin()); // choose liquid particles (rw2>0) moms_calc(rw2.begin(), real_t(3./2.)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) before condensation"); if(count_n!=n_cell) { @@ -42,23 +41,6 @@ namespace libcloudphxx thrust::negate() ); - // Compute per-cell 3rd moment of ice before deposition. It is stored in count_mom - moms_gt0(ice_a.begin()); // choose ice particles (ice_a>0) - moms_calc(thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() - ), - real_t(1)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) before deposition"); - if(count_n!=n_cell) { - thrust::fill(drv_ice.begin(), drv_ice.end(), real_t(0.)); - } - - thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, - thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), - thrust::negate() - ); - auto hlpr_zip_iter = thrust::make_zip_iterator(thrust::make_tuple( thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), @@ -84,24 +66,22 @@ namespace libcloudphxx arg::_1 + arg::_2 ); - // no RH_i, because we dont allow ice with turb_cond - /* + // condensation for liquid droplets thrust::transform( rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() thrust::make_zip_iterator( // input - 2nd arg thrust::make_tuple( hlpr_zip_iter, thrust::make_permutation_iterator(p.begin(), ijk.begin()), - RH_plus_ssp.begin(), - thrust::make_constant_iterator(0) // dummy RH_i just to make it compile + RH_plus_ssp.begin() ) ), rw2.begin(), // output detail::advance_rw2(dt, RH_max) ); - */ } else + { // condensation for liquid droplets thrust::transform( rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() @@ -115,42 +95,11 @@ namespace libcloudphxx rw2.begin(), // output detail::advance_rw2(dt, RH_max) ); - nancheck(rw2, "rw2 after condensation (no sub-steps"); - - // deposition for ice crystals - thrust::transform( - thrust::make_zip_iterator( - thrust::make_tuple( - ice_a.begin(), - ice_c.begin() - ) - ), - thrust::make_zip_iterator( - thrust::make_tuple( - ice_a.end(), - ice_c.end() - ) - ), - thrust::make_zip_iterator( - thrust::make_tuple( - hlpr_zip_iter, - thrust::make_permutation_iterator(p.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH_i.begin(), ijk.begin()) - ) - ), - thrust::make_zip_iterator( - thrust::make_tuple( - ice_a.begin(), - ice_c.begin() - ) - ), - detail::advance_ice_ac(dt, RH_max) - ); - nancheck(ice_a, "ice_a after deposition (no sub-steps"); - nancheck(ice_c, "ice_c after deposition (no sub-steps"); + nancheck(rw2, "rw2 after condensation (no sub-steps"); + } // Compute per-cell 3rd moment of liquid droplets after condensation. It is stored in count_mom - moms_eq0(ice_a.begin()); // choose liquid particles (ice_a=0) + moms_gt0(rw2.begin()); // choose liquid particles (rw2>0) moms_calc(rw2.begin(), real_t(3./2.)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) after condensation"); @@ -162,25 +111,82 @@ namespace libcloudphxx thrust::plus() ); - // Compute per-cell 3rd moment of ice after deposition. It is stored in count_mom - moms_gt0(ice_a.begin()); // choose ice particles (ice_a>0) - moms_calc(thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() - ), - real_t(1)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) after deposition"); + // update th and rv according to change in third specific wet moment + update_th_rv(drv_liq, impl::phase_change::condensation); - // Adding the third ice moment after deposition to drv_ice - thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // input - 2nd arg - thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // output - thrust::plus() - ); - // update th and rv according to changes in third specific wet moments - update_th_rv(drv_liq, impl::phase_change::condensation); - update_th_rv(drv_ice, impl::phase_change::sublimation); + if (opts_init.ice_switch) + { + thrust_device::vector &drv_ice(tmp_device_real_cell3); + + // Compute per-cell 3rd moment of ice before deposition. It is stored in count_mom + moms_gt0(ice_a.begin()); // choose ice particles (ice_a>0) + moms_calc(thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() + ), + real_t(1)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) before deposition"); + if(count_n!=n_cell) { + thrust::fill(drv_ice.begin(), drv_ice.end(), real_t(0.)); + } + + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, + thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), + thrust::negate() + ); + + // deposition for ice crystals + thrust::transform( + thrust::make_zip_iterator( + thrust::make_tuple( + ice_a.begin(), + ice_c.begin() + ) + ), + thrust::make_zip_iterator( + thrust::make_tuple( + ice_a.end(), + ice_c.end() + ) + ), + thrust::make_zip_iterator( + thrust::make_tuple( + hlpr_zip_iter, + thrust::make_permutation_iterator(p.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH_i.begin(), ijk.begin()) + ) + ), + thrust::make_zip_iterator( + thrust::make_tuple( + ice_a.begin(), + ice_c.begin() + ) + ), + detail::advance_ice_ac(dt, RH_max) + ); + nancheck(ice_a, "ice_a after deposition (no sub-steps"); + nancheck(ice_c, "ice_c after deposition (no sub-steps"); + + // Compute per-cell 3rd moment of ice after deposition. It is stored in count_mom + moms_gt0(ice_a.begin()); // choose ice particles (ice_a>0) + moms_calc(thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() + ), + real_t(1)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) after deposition"); + + // Adding the third ice moment after deposition to drv_ice + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // input - 2nd arg + thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // output + thrust::plus() + ); + + // update th and rv according to change in third specific wet moment + update_th_rv(drv_ice, impl::phase_change::sublimation); + } } }; }; \ No newline at end of file diff --git a/src/impl/particles_impl_cond_common.ipp b/src/impl/particles_impl_cond_common.ipp index edc08efdc..9ec475599 100644 --- a/src/impl/particles_impl_cond_common.ipp +++ b/src/impl/particles_impl_cond_common.ipp @@ -266,6 +266,9 @@ namespace libcloudphxx using std::isinf; #endif + // Skip ice particles + if (rw2_old <= 0) return rw2_old; + auto& tpl_in = thrust::get<0>(tpl); const advance_rw2_minfun f(dt, rw2_old, tpl, RH_max); const real_t drw2 = dt * f.drw2_dt(rw2_old * si::square_metres) * si::seconds / si::square_metres; From 3f049aa739ee199f97c3f91602bdc6977cc26613 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 6 Nov 2025 13:41:36 +0100 Subject: [PATCH 71/97] rd2_insol only if ice_switch --- src/impl/particles_impl.ipp | 4 ++-- src/impl/particles_impl_fill_outbuf.ipp | 10 ++++++++++ src/impl/particles_impl_reserve_hskpng_npart.ipp | 5 ++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 93ae50a1f..cc71888f6 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -457,14 +457,14 @@ namespace libcloudphxx if (opts_init.ny != 0) resize_size_vctrs.insert(&j); if (opts_init.nz != 0) resize_size_vctrs.insert(&k); - distmem_real_vctrs.insert({&rd2_insol, detail::no_initial_value}); if(opts_init.ice_switch) { + distmem_real_vctrs.insert({&rd2_insol, detail::no_initial_value}); distmem_real_vctrs.insert({&ice_a, detail::no_initial_value}); distmem_real_vctrs.insert({&ice_c, detail::no_initial_value}); distmem_real_vctrs.insert({&ice_rho, detail::no_initial_value}); - if (opts_init.time_dep_ice_nucl == false) + if (! opts_init.time_dep_ice_nucl) {distmem_real_vctrs.insert({&T_freeze, detail::no_initial_value});} } } diff --git a/src/impl/particles_impl_fill_outbuf.ipp b/src/impl/particles_impl_fill_outbuf.ipp index 6c76e91ca..f94361884 100644 --- a/src/impl/particles_impl_fill_outbuf.ipp +++ b/src/impl/particles_impl_fill_outbuf.ipp @@ -44,6 +44,16 @@ namespace libcloudphxx if (std::find(std::begin(attr_names), std::end(attr_names), name) == std::end(attr_names)) throw std::runtime_error("Unknown attribute name passed to get_attr."); + if (!opts_init.ice_switch && + (name == "ice_a" || name == "ice_c" || name == "ice_rho" || name == "rd2_insol" || name == "T_freeze")) + { + throw std::runtime_error("Requested ice attribute '" + name + "' but ice_switch is off."); + } + if (opts_init.time_dep_ice_nucl && name == "T_freeze") + { + throw std::runtime_error("Requested T_freeze but singular ice nucleation is off."); + } + const thrust_device::vector &dv( name == "rw2" ? rw2 : name == "rd3" ? rd3 : diff --git a/src/impl/particles_impl_reserve_hskpng_npart.ipp b/src/impl/particles_impl_reserve_hskpng_npart.ipp index 1755c2e93..26a20682f 100644 --- a/src/impl/particles_impl_reserve_hskpng_npart.ipp +++ b/src/impl/particles_impl_reserve_hskpng_npart.ipp @@ -39,10 +39,13 @@ namespace libcloudphxx if(opts_init.ice_switch) { rd2_insol.reserve(opts_init.n_sd_max); - T_freeze.reserve(opts_init.n_sd_max); ice_a.reserve(opts_init.n_sd_max); ice_c.reserve(opts_init.n_sd_max); ice_rho.reserve(opts_init.n_sd_max); + if (! opts_init.time_dep_ice_nucl) + { + T_freeze.reserve(opts_init.n_sd_max); + } } vt.reserve(opts_init.n_sd_max); From e353e8fd906d49f3cbd3a1de9488361bd1a8a825 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 6 Nov 2025 16:34:36 +0100 Subject: [PATCH 72/97] python tests with new dry_distros --- bindings/python/CMakeLists.txt | 2 +- bindings/python/lib.cpp | 4 ++-- include/libcloudph++/lgrngn/opts.hpp | 2 +- tests/python/unit/SD_removal.py | 5 +++-- tests/python/unit/adve_scheme.py | 3 ++- tests/python/unit/chem_coal.py | 3 ++- tests/python/unit/col_kernels.py | 3 ++- tests/python/unit/diag_incloud_time.py | 2 +- tests/python/unit/lgrngn_adve.py | 3 ++- tests/python/unit/lgrngn_subsidence.py | 3 ++- tests/python/unit/multiple_kappas.py | 3 ++- tests/python/unit/relax.py | 3 ++- tests/python/unit/segfault_20150216.py | 3 ++- tests/python/unit/sstp_cond.py | 3 ++- tests/python/unit/terminal_velocities.py | 3 ++- tests/python/unit/uniform_init.py | 3 ++- 16 files changed, 30 insertions(+), 18 deletions(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index ddcbc64e2..5e3905ba8 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -93,6 +93,6 @@ set_property(TARGET cloudphxx PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE) # Python_SITEARCH is an absolute path, we manually prepend install prefix install ( TARGETS cloudphxx LIBRARY - DESTINATION "${CMAKE_INSTALL_PREFIX}${Python3_SITEARCH}" + DESTINATION "${Python3_SITEARCH}" COMPONENT library ) diff --git a/bindings/python/lib.cpp b/bindings/python/lib.cpp index 243b33047..c1d7b93f2 100644 --- a/bindings/python/lib.cpp +++ b/bindings/python/lib.cpp @@ -278,12 +278,12 @@ BOOST_PYTHON_MODULE(libcloudphxx) .def_readwrite("turb_coal", &lgr::opts_t::turb_coal) .def_readwrite("ice_nucl", &lgr::opts_t::ice_nucl) .def_readwrite("dt", &lgr::opts_t::dt) + .add_property("src_dry_distros", &lgrngn::get_sdd, &lgrngn::set_sdd) + .add_property("src_dry_sizes", &lgrngn::get_ds, &lgrngn::set_sds) ; bp::class_>("opts_init_t") .add_property("dry_distros", &lgrngn::get_dd, &lgrngn::set_dd) .add_property("dry_sizes", &lgrngn::get_ds, &lgrngn::set_ds) - // .add_property("src_dry_distros", &lgrngn::get_sdd, &lgrngn::set_sdd) - // .add_property("src_dry_sizes", &lgrngn::get_ds, &lgrngn::set_sds) .add_property("rlx_dry_distros", &lgrngn::get_rdd, &lgrngn::set_rdd) .def_readwrite("nx", &lgr::opts_init_t::nx) .def_readwrite("ny", &lgr::opts_init_t::ny) diff --git a/include/libcloudph++/lgrngn/opts.hpp b/include/libcloudph++/lgrngn/opts.hpp index 59ef100ea..d01181769 100644 --- a/include/libcloudph++/lgrngn/opts.hpp +++ b/include/libcloudph++/lgrngn/opts.hpp @@ -42,7 +42,7 @@ namespace libcloudphxx opts_t() : adve(true), sedi(true), subs(false), cond(true), coal(true), src(false), rlx(false), rcyc(false), chem_dsl(false), chem_dsc(false), chem_rct(false), - turb_adve(false), turb_cond(false), turb_coal(false), ice_nucl(true), + turb_adve(false), turb_cond(false), turb_coal(false), ice_nucl(false), RH_max(44), // :) (anything greater than 1.1 would be enough dt(-1) // negative means that we do not override dt in this step { diff --git a/tests/python/unit/SD_removal.py b/tests/python/unit/SD_removal.py index b09b5f61c..5abba2b91 100755 --- a/tests/python/unit/SD_removal.py +++ b/tests/python/unit/SD_removal.py @@ -36,9 +36,10 @@ def expvolumelnr(lnr): r=np.exp(lnr) return n_zero * 3.*np.power(r,3)/np.power(r_zero,3)*np.exp(- np.power((r/r_zero),3)); -kappa = .01 +kappa = .01 +rd_insol = 0. -opts_init.dry_distros = {kappa:expvolumelnr} +opts_init.dry_distros = {(kappa, rd_insol):expvolumelnr} opts_init.sd_conc = 64 opts_init.n_sd_max = 64 diff --git a/tests/python/unit/adve_scheme.py b/tests/python/unit/adve_scheme.py index 9448d7fad..8c01a560a 100644 --- a/tests/python/unit/adve_scheme.py +++ b/tests/python/unit/adve_scheme.py @@ -21,8 +21,9 @@ def lognormal(lnr): ) / log(stdev) / sqrt(2*pi); kappa = .61 +rd_insol = 0. -opts_init.dry_distros = {kappa:lognormal} +opts_init.dry_distros = {(kappa, rd_insol):lognormal} opts_init.sd_conc = 50 opts_init.n_sd_max = 50 diff --git a/tests/python/unit/chem_coal.py b/tests/python/unit/chem_coal.py index 84bd1d9d0..bb0be47b8 100755 --- a/tests/python/unit/chem_coal.py +++ b/tests/python/unit/chem_coal.py @@ -34,8 +34,9 @@ def expvolumelnr(lnr): return n_zero * 3.*np.power(r,3)/np.power(r_zero,3)*np.exp(- np.power((r/r_zero),3)); kappa = 0.1 +rd_insol = 0. -opts_init.dry_distros = {kappa:expvolumelnr} +opts_init.dry_distros = {(kappa, rd_insol):expvolumelnr} opts_init.sd_conc = 64 opts_init.n_sd_max = 64 diff --git a/tests/python/unit/col_kernels.py b/tests/python/unit/col_kernels.py index 9f3c1ba1b..7a433012b 100644 --- a/tests/python/unit/col_kernels.py +++ b/tests/python/unit/col_kernels.py @@ -24,13 +24,14 @@ def lognormal(lnr): ) / log(stdev) / sqrt(2*pi); kappa = .61 +rd_insol = 0. count = 0 for kernel in [lgrngn.kernel_t.geometric, lgrngn.kernel_t.geometric, lgrngn.kernel_t.long, lgrngn.kernel_t.hall, lgrngn.kernel_t.hall_davis_no_waals, lgrngn.kernel_t.golovin, lgrngn.kernel_t.onishi_hall, lgrngn.kernel_t.onishi_hall_davis_no_waals, lgrngn.kernel_t.vohl_davis_no_waals, lgrngn.kernel_t.hall_pinsky_cumulonimbus, lgrngn.kernel_t.hall_pinsky_stratocumulus]: print(kernel) opts_init = lgrngn.opts_init_t() opts_init.dt = 1 - opts_init.dry_distros = {kappa:lognormal} + opts_init.dry_distros = {(kappa, rd_insol):lognormal} opts_init.sd_conc = 50 opts_init.n_sd_max = 50 opts_init.terminal_velocity=lgrngn.vt_t.beard76 diff --git a/tests/python/unit/diag_incloud_time.py b/tests/python/unit/diag_incloud_time.py index 64a2a4f6d..a848f38df 100644 --- a/tests/python/unit/diag_incloud_time.py +++ b/tests/python/unit/diag_incloud_time.py @@ -20,7 +20,7 @@ def lognormal(lnr): opts = lgrngn.opts_t() opts_init = lgrngn.opts_init_t() -opts_init.dry_distros = {.61:lognormal, 1.28:lognormal} +opts_init.dry_distros = {(.61, 0.):lognormal, (1.28, 0.):lognormal} opts_init.coal_switch = False opts_init.sedi_switch = False opts_init.RH_max = 0.999 # to comply with the assert(RH<1) at init diff --git a/tests/python/unit/lgrngn_adve.py b/tests/python/unit/lgrngn_adve.py index 509cfbf90..b3c518742 100644 --- a/tests/python/unit/lgrngn_adve.py +++ b/tests/python/unit/lgrngn_adve.py @@ -24,7 +24,8 @@ def lognormal(lnr): Opts_init = lgrngn.opts_init_t() kappa = .61 -Opts_init.dry_distros = {kappa:lognormal} +rd_insol = 0. +Opts_init.dry_distros = {(kappa, rd_insol):lognormal} Opts_init.coal_switch = False Opts_init.sedi_switch = False diff --git a/tests/python/unit/lgrngn_subsidence.py b/tests/python/unit/lgrngn_subsidence.py index 59e0cfccb..c55da7e32 100644 --- a/tests/python/unit/lgrngn_subsidence.py +++ b/tests/python/unit/lgrngn_subsidence.py @@ -17,7 +17,8 @@ def lognormal(lnr): Opts_init = lgrngn.opts_init_t() kappa = .61 -Opts_init.dry_distros = {kappa:lognormal} +rd_insol = 0. +Opts_init.dry_distros = {(kappa, rd_insol):lognormal} Opts_init.coal_switch = False Opts_init.sedi_switch = False Opts_init.subs_switch = True diff --git a/tests/python/unit/multiple_kappas.py b/tests/python/unit/multiple_kappas.py index 44d41f137..287b751c2 100644 --- a/tests/python/unit/multiple_kappas.py +++ b/tests/python/unit/multiple_kappas.py @@ -33,8 +33,9 @@ def check_kappa_conc(prtcls, eps): opts_init = lgrngn.opts_init_t() kappa1 = .61 kappa2 = 1.28 +rd_insol = 0. rho_stp = 1.2248 -opts_init.dry_distros = {kappa1:lognormal, kappa2:lognormal} +opts_init.dry_distros = {(kappa1, rd_insol):lognormal, (kappa2, rd_insol):lognormal} opts_init.kernel = lgrngn.kernel_t.geometric opts_init.terminal_velocity = lgrngn.vt_t.beard76 opts_init.dt = 1 diff --git a/tests/python/unit/relax.py b/tests/python/unit/relax.py index ee0289794..3f27547a4 100644 --- a/tests/python/unit/relax.py +++ b/tests/python/unit/relax.py @@ -103,8 +103,9 @@ def test(opts_init): # test source with dry_distros kappa = .61 +rd_insol = 0. opts_init = lgrngn.opts_init_t() -opts_init.dry_distros = {kappa:lognormal} +opts_init.dry_distros = {(kappa, rd_insol):lognormal} opts_init.rlx_dry_distros = {kappa: [lognormal_rlx, [0,2],[0,opts_init.dz]]} opts_init.sd_conc = 1024 opts_init.rlx_bins = 1024 diff --git a/tests/python/unit/segfault_20150216.py b/tests/python/unit/segfault_20150216.py index 1d60ee6ad..7e0b3b23b 100644 --- a/tests/python/unit/segfault_20150216.py +++ b/tests/python/unit/segfault_20150216.py @@ -24,8 +24,9 @@ def lognormal(lnr): -pow((lnr - log(mean_r)), 2) / 2 / pow(log(stdev),2) ) / log(stdev) / sqrt(2*pi); kappa = .61 +rd_insol = 0. -opts_init.dry_distros = {kappa:lognormal} +opts_init.dry_distros = {(kappa, rd_insol):lognormal} opts_init.sd_conc = 50 opts_init.n_sd_max = 50 diff --git a/tests/python/unit/sstp_cond.py b/tests/python/unit/sstp_cond.py index 7ac0295a9..5d3be9f2a 100644 --- a/tests/python/unit/sstp_cond.py +++ b/tests/python/unit/sstp_cond.py @@ -19,7 +19,8 @@ def test(turb_cond): print('turb_cond = ', turb_cond) opts_init = lgrngn.opts_init_t() kappa = .61 - opts_init.dry_distros = {kappa:lognormal} + rd_insol = 0. + opts_init.dry_distros = {(kappa, rd_insol):lognormal} opts_init.coal_switch=0 opts_init.sedi_switch=0 opts_init.dt = 1 diff --git a/tests/python/unit/terminal_velocities.py b/tests/python/unit/terminal_velocities.py index 470a64f1b..4b300c917 100644 --- a/tests/python/unit/terminal_velocities.py +++ b/tests/python/unit/terminal_velocities.py @@ -26,8 +26,9 @@ def lognormal(lnr): ) / log(stdev) / sqrt(2*pi); kappa = .61 +rd_insol = 0. -opts_init.dry_distros = {kappa:lognormal} +opts_init.dry_distros = {(kappa, rd_insol):lognormal} opts_init.sd_conc = 50 opts_init.n_sd_max = 50 diff --git a/tests/python/unit/uniform_init.py b/tests/python/unit/uniform_init.py index c053ecd13..6f7c9f0dc 100644 --- a/tests/python/unit/uniform_init.py +++ b/tests/python/unit/uniform_init.py @@ -42,8 +42,9 @@ def expvolumelnr(lnr): rhod = 1. * np.ones((opts_init.nx, opts_init.ny, opts_init.nz)) + .1 * np.mgrid[1:1+opts_init.nx, 1:1+opts_init.ny, 1:1+opts_init.nz][1] # different densities, hence different water content kappa = 1e-6 +rd_insol = 0. -opts_init.dry_distros = {kappa:expvolumelnr} +opts_init.dry_distros = {(kappa, rd_insol):expvolumelnr} opts_init.sd_conc = 64 opts_init.n_sd_max = opts_init.sd_conc * opts_init.nx * opts_init.ny * opts_init.nz From f879ada2c0714d28ba5ae0019ce2318be6899a43 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 7 Nov 2025 12:25:23 +0100 Subject: [PATCH 73/97] fix src in bindings --- bindings/python/lgrngn.hpp | 50 ++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/bindings/python/lgrngn.hpp b/bindings/python/lgrngn.hpp index 221f9e6a5..7727960ae 100644 --- a/bindings/python/lgrngn.hpp +++ b/bindings/python/lgrngn.hpp @@ -253,21 +253,30 @@ namespace libcloudphxx } } -// src_dry_distros moved from opts_init to opts -/* + // src_dry_distros moved from opts_init to opts template void set_sdd( // src_dry_distro - lgr::opts_init_t *arg, - const bp::dict &kappa_func) + lgr::opts_t *arg, + const bp::dict &kappa_func) // a dict keyed by (kappa, rd_insol) { arg->src_dry_distros.clear(); for (int i = 0; i < len(kappa_func.keys()); ++i) + { + bp::tuple key = bp::extract(kappa_func.keys()[i]); + bp::tuple val = bp::extract(kappa_func.values()[i]); + const real_t kappa = bp::extract(key[0]); + const real_t rd_insol = bp::extract(key[1]); + + const int sd_conc = bp::extract(val[1]); + const int supstp = bp::extract(val[2]); arg->src_dry_distros.emplace( - std::make_pair(bp::extract(kappa_func.keys()[i]), 0), // assume ice=0 - std::make_shared>(kappa_func.values()[i]) + libcloudphxx::lgrngn::kappa_rd_insol_t{kappa, rd_insol}, + std::make_tuple(std::static_pointer_cast>( + std::make_shared>(val[0])), + sd_conc, supstp) ); + } } - */ // set dry sizes from a dict with (kappa, rd_insol) as key template @@ -306,38 +315,43 @@ namespace libcloudphxx } } -// src_dry_sizes moved from opts_init to opts -/* + // src_dry_sizes moved from opts_init to opts template void set_sds( // src_dry_sizes - lgr::opts_init_t *arg, - const bp::dict &kappa_func + lgr::opts_t *arg, + const bp::dict &kappa_func // a dict keyed by (kappa, rd_insol) ) { arg->src_dry_sizes.clear(); if(len(kappa_func.keys()) == 0) return; - // loop over kappas + // loop over kappas and rd_insol for (int j = 0; j < len(kappa_func.keys()); ++j) { + // extract the key tuple (kappa, rd_insol) + const bp::tuple key = bp::extract(kappa_func.keys()[j]); + const real_t kappa = bp::extract(key[0]); + const real_t rd_insol = bp::extract(key[1]); + + // extract size : {conc, count} dict for this (kappa, rd_insol) const bp::dict size_conc = bp::extract(kappa_func.values()[j]); - std::map> size_conc_map; + std::map> size_conc_map; // turn the size : {conc, count} dict into a size : {conc, count} map for (int i = 0; i < len(size_conc.keys()); ++i) { const bp::list conc_count_list = bp::extract(size_conc.values()[i]); - assert(len(conc_count_list) == 2); + assert(len(conc_count_list) == 3); const real_t conc = bp::extract(conc_count_list[0]); const int count = bp::extract (conc_count_list[1]); - size_conc_map[bp::extract(size_conc.keys()[i])] = std::make_pair(conc, count); + const int supstp = bp::extract (conc_count_list[2]); + size_conc_map[bp::extract(size_conc.keys()[i])] = std::make_tuple(conc, count, supstp); } - const real_t kappa = bp::extract(kappa_func.keys()[j]); - arg->src_dry_sizes[kappa] = size_conc_map; + arg->src_dry_sizes[libcloudphxx::lgrngn::kappa_rd_insol_t{kappa, rd_insol}] = size_conc_map; } } - */ + template void set_rdd( // rlx_dry_distros From 37bc8f7b3c89b0edd6227741064bc2fcdfb98ed4 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 7 Nov 2025 13:33:04 +0100 Subject: [PATCH 74/97] test ice api --- tests/python/physics/coalescence_golovin.py | 3 +- .../coalescence_hall_davis_no_waals.py | 3 +- tests/python/physics/lgrngn_cond.py | 5 +- .../python/physics/lgrngn_cond_substepping.py | 4 +- tests/python/physics/puddle.py | 3 +- tests/python/physics/test_coal.py | 3 +- tests/python/unit/api_lgrngn.py | 57 ++++++++++++++++++- 7 files changed, 70 insertions(+), 8 deletions(-) diff --git a/tests/python/physics/coalescence_golovin.py b/tests/python/physics/coalescence_golovin.py index 9576da3eb..ff35efe92 100644 --- a/tests/python/physics/coalescence_golovin.py +++ b/tests/python/physics/coalescence_golovin.py @@ -68,8 +68,9 @@ def golovin(v,t,n0,v0,b): rv = 0.01 * np.ones((1,)) kappa = 1e-10 +rd_insol = 0. -opts_init.dry_distros = {kappa:expvolumelnr} +opts_init.dry_distros = {(kappa, rd_insol):expvolumelnr} opts_init.kernel = lgrngn.kernel_t.golovin opts_init.terminal_velocity = lgrngn.vt_t.beard77 diff --git a/tests/python/physics/coalescence_hall_davis_no_waals.py b/tests/python/physics/coalescence_hall_davis_no_waals.py index 66f66b9f6..e28db195c 100644 --- a/tests/python/physics/coalescence_hall_davis_no_waals.py +++ b/tests/python/physics/coalescence_hall_davis_no_waals.py @@ -52,8 +52,9 @@ def expvolumelnr(lnr): rv = 0.01 * np.ones((opts_init.nx, opts_init.nz)) kappa = 0 #1e-10 +rd_insol = 0. -opts_init.dry_distros = {kappa:expvolumelnr} +opts_init.dry_distros = {(kappa, rd_insol):expvolumelnr} opts_init.sd_conc = pow(2,14) opts_init.n_sd_max = opts_init.sd_conc diff --git a/tests/python/physics/lgrngn_cond.py b/tests/python/physics/lgrngn_cond.py index 604bb51cd..799900b36 100644 --- a/tests/python/physics/lgrngn_cond.py +++ b/tests/python/physics/lgrngn_cond.py @@ -29,9 +29,11 @@ def lognormal(lnr): opts_init = lgrngn.opts_init_t() kappa = .61 -opts_init.dry_distros = {kappa:lognormal} +rd_insol = 0. +opts_init.dry_distros = {(kappa, rd_insol):lognormal} opts_init.coal_switch = False opts_init.sedi_switch = False +opts_init.ice_switch = False opts_init.RH_max = 0.999 # to comply with the assert(RH<1) at init opts_init.dt = 1 opts_init.sd_conc = int(1e2) @@ -44,6 +46,7 @@ def lognormal(lnr): opts.cond = True opts.coal = False opts.chem = False +opts.ice_nucl = False #expected theta and rv after condensation: exp_th = { True : 306.9, # constp diff --git a/tests/python/physics/lgrngn_cond_substepping.py b/tests/python/physics/lgrngn_cond_substepping.py index e0728b7b4..9e2286ff2 100644 --- a/tests/python/physics/lgrngn_cond_substepping.py +++ b/tests/python/physics/lgrngn_cond_substepping.py @@ -41,9 +41,10 @@ def lognormal2(lnr): opts = lgrngn.opts_t() opts_init = lgrngn.opts_init_t() -opts_init.dry_distros = {.61:lognormal, 1.28:lognormal2} # normal mode + GCCNs +opts_init.dry_distros = {(.61,0.):lognormal, (1.28,0.):lognormal2} # normal mode + GCCNs opts_init.coal_switch = False opts_init.sedi_switch = False +opts_init.ice_switch = False opts_init.RH_max = 1.0001 opts_init.dt = 1 opts_init.sd_conc = int(1e3) @@ -56,6 +57,7 @@ def lognormal2(lnr): opts.cond = True opts.coal = False opts.chem = False +opts.ice_nucl = False #opts.RH_max = 1.005 #expected theta and rv after condensation: diff --git a/tests/python/physics/puddle.py b/tests/python/physics/puddle.py index 4ddfe5055..b6281440d 100644 --- a/tests/python/physics/puddle.py +++ b/tests/python/physics/puddle.py @@ -17,7 +17,8 @@ def lognormal(lnr): Opts_init = lgrngn.opts_init_t() kappa = .61 -Opts_init.dry_distros = {kappa:lognormal} +rd_insol = 0. +Opts_init.dry_distros = {(kappa, rd_insol):lognormal} Opts_init.coal_switch = False Opts_init.sedi_switch = True Opts_init.terminal_velocity = lgrngn.vt_t.beard76 diff --git a/tests/python/physics/test_coal.py b/tests/python/physics/test_coal.py index f841a083a..8c2273a5a 100644 --- a/tests/python/physics/test_coal.py +++ b/tests/python/physics/test_coal.py @@ -29,8 +29,9 @@ def expvolumelnr(lnr): kappa1 = 0.1 kappa2 = 0.9 +rd_insol = 0. -opts_init.dry_distros = {kappa1:expvolumelnr, kappa2:expvolumelnr} +opts_init.dry_distros = {(kappa1, rd_insol):expvolumelnr, (kappa2, rd_insol):expvolumelnr} opts_init.sd_conc = pow(2,14) opts_init.n_sd_max = pow(2,14) diff --git a/tests/python/unit/api_lgrngn.py b/tests/python/unit/api_lgrngn.py index fea11812c..a6548ece4 100644 --- a/tests/python/unit/api_lgrngn.py +++ b/tests/python/unit/api_lgrngn.py @@ -70,6 +70,7 @@ def lognormal(lnr): print("coal_switch = ", opts_init.coal_switch) print("sedi_switch = ", opts_init.sedi_switch) print("subs_switch = ", opts_init.subs_switch) +print("ice_switch = ", opts_init.ice_switch) print("src_type = ", opts_init.src_type) print("exact_sstp_cond = ", opts_init.exact_sstp_cond) @@ -387,6 +388,29 @@ def lognormal(lnr): opts_init.turb_coal_switch=False opts.turb_coal=False +# ---------- +# 0D ice +print("0D ice") +th = arr_t([263.]) +opts_init.ice_switch = True +opts_init.coal_switch = False +opts.ice_nucl = True +prtcls = lgrngn.factory(backend, opts_init) +prtcls.init(th, rv, rhod) +prtcls.step_sync(opts, th, rv) +prtcls.step_async(opts) + +prtcls.diag_all() +prtcls.diag_sd_conc() +assert len(frombuffer(prtcls.outbuf())) == 1 +print(frombuffer(prtcls.outbuf())) +assert (frombuffer(prtcls.outbuf()) > 0).all() +assert sum(frombuffer(prtcls.outbuf())) == 64 + +opts_init.ice_switch=False +opts.ice_nucl=False +opts_init.coal_switch = True + # ---------- @@ -665,8 +689,12 @@ def lognormal(lnr): # test if get_attr work and if kappas are set correctly kappa = asarray(prtcls.get_attr("kappa")) -assert (kappa[:(32*opts_init.nx*opts_init.ny*opts_init.nz)] == kappa2).all() -assert (kappa[(32*opts_init.nx*opts_init.ny*opts_init.nz):] == kappa1).all() +# assert (kappa[:(32*opts_init.nx*opts_init.ny*opts_init.nz)] == kappa2).all() +# assert (kappa[(32*opts_init.nx*opts_init.ny*opts_init.nz):] == kappa1).all() +n = 32 * opts_init.nx * opts_init.ny * opts_init.nz +assert (kappa[:n] == kappa1).all() +assert (kappa[n:2*n] == kappa2).all() +assert (kappa[2*n:] == kappa1).all() # ---------- @@ -710,3 +738,28 @@ def lognormal(lnr): opts_init.sd_conc = sd_conc_old opts_init.sd_const_multi = 0 opts_init.dry_sizes = dict() + + +# ---------- +# 3D ice +print("3D ice") +th_arr = arr_t([[263., 263. ], [263., 263. ]]) +th = arr_t([th_arr, th_arr ]) +opts_init.ice_switch = True +opts_init.coal_switch = False +opts.ice_nucl = True +prtcls = lgrngn.factory(backend, opts_init) +prtcls.init(th, rv, rhod) +prtcls.step_sync(opts, th, rv) +prtcls.step_async(opts) + +prtcls.diag_all() +prtcls.diag_sd_conc() +assert len(frombuffer(prtcls.outbuf())) == opts_init.nz * opts_init.nx * opts_init.ny +print(frombuffer(prtcls.outbuf())) +assert (frombuffer(prtcls.outbuf()) > 0).all() +assert sum(frombuffer(prtcls.outbuf())) == opts_init.nz * opts_init.nx * opts_init.ny * opts_init.sd_conc + +opts_init.ice_switch = False +opts_init.coal_switch = True +opts.ice_nucl = False From 76ac5d56969917ed5742a9f79c30a77b16297694 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 7 Nov 2025 16:39:28 +0100 Subject: [PATCH 75/97] fixing problems after merge --- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_ice_nucl_melt.ipp | 7 +++++-- src/impl/particles_impl_init_T_freeze.ipp | 4 +++- src/impl/particles_impl_moms.ipp | 12 ++++++++++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index db3a8e22e..b2ebfbb5d 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -640,7 +640,7 @@ namespace libcloudphxx void cond(const real_t &dt, const real_t &RH_max, const bool turb_cond, const int step); void cond_sstp(const real_t &dt, const real_t &RH_max, const bool turb_cond, const int step); template - void cond_sstp_hlpr(const real_t &dt, const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, const RH_iter &rhi); + void cond_sstp_hlpr(const real_t &dt, const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, const RH_iter &rhi, const RHi_iter &rhii); void update_th_rv(thrust_device::vector &, phase_change = phase_change::condensation); void update_th_freezing(thrust_device::vector &); void update_state(thrust_device::vector &, thrust_device::vector &); diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp index f4d61333c..6b6c9caee 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -118,7 +118,8 @@ namespace libcloudphxx hskpng_sort(); // A vector to store liquid 3rd moments - thrust_device::vector &drw(tmp_device_real_cell); + auto drw_g = tmp_device_real_cell.get_guard(); + thrust_device::vector &drw = drw_g.get(); // Compute per-cell 3rd moment of liquid droplets (sum of n*r^3) before freezing/melting. It is stored in count_mom moms_eq0(ice_a.begin()); // choose liquid particles (ice_a=0) @@ -137,7 +138,9 @@ namespace libcloudphxx if (opts_init.time_dep_ice_nucl) // time dependent freezing based on Arabas et al., 2025 { - rand_u01(n_part); // random numbers between [0,1] for each particle + auto u01g = tmp_device_real_part.get_guard(); + thrust_device::vector &u01 = u01g.get(); + rand_u01(u01, n_part); // random numbers between [0,1] for each particle thrust::for_each( thrust::make_zip_iterator(thrust::make_tuple( rw2.begin(), diff --git a/src/impl/particles_impl_init_T_freeze.ipp b/src/impl/particles_impl_init_T_freeze.ipp index e66ea4300..06c559834 100644 --- a/src/impl/particles_impl_init_T_freeze.ipp +++ b/src/impl/particles_impl_init_T_freeze.ipp @@ -20,7 +20,9 @@ namespace libcloudphxx const INP_t inp_type = INP_t::mineral; //TODO: INP type as argument, to support different partcle types // random numbers between [0,1] for sampling - rand_u01(n_part_to_init); + auto u01g = tmp_device_real_part.get_guard(); + thrust_device::vector &u01 = u01g.get(); + rand_u01(u01, n_part_to_init); thrust::transform( thrust::make_zip_iterator(thrust::make_tuple(rd2_insol.begin() + n_part_old, u01.begin() + n_part_old)), diff --git a/src/impl/particles_impl_moms.ipp b/src/impl/particles_impl_moms.ipp index b4b711c44..502515447 100644 --- a/src/impl/particles_impl_moms.ipp +++ b/src/impl/particles_impl_moms.ipp @@ -163,7 +163,11 @@ namespace libcloudphxx ) { hskpng_sort(); - thrust_device::vector &n_filtered(tmp_device_real_part); + + if(!cons) + reset_guardp(n_filtered_gp, tmp_device_real_part); + + thrust_device::vector &n_filtered = n_filtered_gp->get(); { namespace arg = thrust::placeholders; @@ -198,7 +202,11 @@ namespace libcloudphxx ) { hskpng_sort(); - thrust_device::vector &n_filtered(tmp_device_real_part); + + if(!cons) + reset_guardp(n_filtered_gp, tmp_device_real_part); + + thrust_device::vector &n_filtered = n_filtered_gp->get(); { namespace arg = thrust::placeholders; From 880d93b6f86e98fa34c8c7d19e91bbb4bb6e72ef Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 12 Nov 2025 20:30:36 +0100 Subject: [PATCH 76/97] RHi formula --- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_hskpng_Tpr.ipp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index b2ebfbb5d..b851abcbd 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -367,7 +367,7 @@ namespace libcloudphxx allow_sstp_chem(_opts_init.sstp_chem > 1 || _opts_init.variable_dt_switch), pure_const_multi (((_opts_init.sd_conc) == 0) && (_opts_init.sd_const_multi > 0 || _opts_init.dry_sizes.size() > 0)), // coal prob can be greater than one only in sd_conc simulations //tmp_device_real_part(6), - tmp_device_real_cell(4) // 4 temporary vectors of this type; NOTE: default constructor creates 1 + tmp_device_real_cell(5) // 5 temporary vectors of this type; NOTE: default constructor creates 1 { // set 0 dev_count to mark that its not a multi_CUDA spawn diff --git a/src/impl/particles_impl_hskpng_Tpr.ipp b/src/impl/particles_impl_hskpng_Tpr.ipp index 9b206a9ab..34e7f8d05 100644 --- a/src/impl/particles_impl_hskpng_Tpr.ipp +++ b/src/impl/particles_impl_hskpng_Tpr.ipp @@ -271,7 +271,11 @@ namespace libcloudphxx zip_it_t(thrust::make_tuple(p.begin(), rv.begin(), T.begin())), // input - begin zip_it_t(thrust::make_tuple(p.end(), rv.end(), T.end() )), // input - end RH_i.begin(), // output - detail::RH_i(opts_init.RH_formula) + detail::RH_i( + opts_init.RH_formula == RH_formula_t::pv_tet ? RH_formula_t::pv_cc : + opts_init.RH_formula == RH_formula_t::rv_tet ? RH_formula_t::rv_cc : + opts_init.RH_formula + ) ); } From 4169ea2ec8ac10f77cad9dce8364c7a7c4d3c596 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 14 Nov 2025 16:01:39 +0100 Subject: [PATCH 77/97] deposotion --- include/libcloudph++/common/theta_dry.hpp | 4 +- src/impl/particles_impl.ipp | 5 ++- src/impl/particles_impl_cond.ipp | 48 +++++++++++++++------ src/impl/particles_impl_update_th_rv.ipp | 51 +++++++++++++++-------- 4 files changed, 74 insertions(+), 34 deletions(-) diff --git a/include/libcloudph++/common/theta_dry.hpp b/include/libcloudph++/common/theta_dry.hpp index 3fad401bb..49f77c07c 100644 --- a/include/libcloudph++/common/theta_dry.hpp +++ b/include/libcloudph++/common/theta_dry.hpp @@ -64,10 +64,10 @@ namespace libcloudphxx return - th / T * const_cp::l_v(T) / c_pd(); } - // heat of sublimation + // heat of deposition template BOOST_GPU_ENABLED - quantity d_th_d_rv_subl( + quantity d_th_d_rv_dep( const quantity &T, const quantity &th // theta dry!!! ) { diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index b851abcbd..23379deff 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -234,7 +234,8 @@ namespace libcloudphxx lambda_D_gp, lambda_K_gp, rw_mom3_gp, - rw3_gp; + rw3_gp, + ice_vol_gp; std::unique_ptr< typename tmp_vector_pool>::guard @@ -311,7 +312,7 @@ namespace libcloudphxx // max(1, n) int m1(int n) { return n == 0 ? 1 : n; } - enum class phase_change { condensation, sublimation }; // enum for choosing between phase change types + enum class phase_change { condensation, deposition }; // enum for choosing between phase change types // ctor impl(const opts_init_t &_opts_init, const std::pair &bcond, const int &mpi_rank, const int &mpi_size, const int &n_x_tot) : diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index ed1979696..2f6ddb8af 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -158,8 +158,8 @@ namespace libcloudphxx auto drv_ice_g = tmp_device_real_cell.get_guard(); thrust_device::vector &drv_ice = drv_ice_g.get(); if(step == 0) - reset_guardp(rw_mom3_gp, tmp_device_real_cell); - thrust_device::vector &rw_mom3 = rw_mom3_gp->get(); + reset_guardp(ice_vol_gp, tmp_device_real_cell); + thrust_device::vector &ice_vol = ice_vol_gp->get(); // Compute per-cell 3rd moment of ice before deposition. It is stored in count_mom if(step == 0) @@ -170,8 +170,11 @@ namespace libcloudphxx ), real_t(1)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) before deposition"); + + // fill with 0s if not all cells have particles if(count_n!=n_cell) { thrust::fill(drv_ice.begin(), drv_ice.end(), real_t(0.)); + thrust::fill(ice_vol.begin(), ice_vol.end(), real_t(0.)); } thrust::transform( @@ -180,11 +183,11 @@ namespace libcloudphxx thrust::negate() ); } - else // copy rw_mom3 from previous step + else // copy ice_vol from previous step { - // drv = -rw_mom3 precond + // drv = -ice_vol precond thrust::transform( - rw_mom3.begin(), rw_mom3.end(), + ice_vol.begin(), ice_vol.end(), drv_ice.begin(), thrust::negate() ); @@ -230,16 +233,35 @@ namespace libcloudphxx real_t(1)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) after deposition"); - // Adding the third ice moment after deposition to drv_ice - thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // input - 2nd arg - thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // output - thrust::plus() - ); + // Adding the ice volume after deposition to drv_ice + if(step < sstp_cond - 1) + { + thrust::copy( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(ice_vol.begin(), count_ijk.begin()) // output + ); + + // adding the third moment after deposition to ice_vol + thrust::transform( + ice_vol.begin(), ice_vol.end(), + drv_ice.begin(), + drv_ice.begin(), + thrust::plus() + ); + } + else // last step, calculate change in 3rd moment and update th and rv + { + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // input - 2nd arg + thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // output + thrust::plus() + ); + ice_vol_gp.reset(); // destroy guard to tmp array that stored ice_vol + } // update th and rv according to change in third specific wet moment - update_th_rv(drv_ice, impl::phase_change::sublimation); + update_th_rv(drv_ice, impl::phase_change::deposition); } } }; diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/particles_impl_update_th_rv.ipp index d69735b70..0764c66a3 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/particles_impl_update_th_rv.ipp @@ -30,9 +30,9 @@ namespace libcloudphxx return drv * common::theta_dry::d_th_d_rv(T, th) / si::kelvins; } }; - // change of th during sublimation + // change of th during deposition template - struct dth_subl //: thrust::unary_function&, real_t> + struct dth_dep //: thrust::unary_function&, real_t> { BOOST_GPU_ENABLED real_t operator()(const thrust::tuple &tpl) const @@ -44,7 +44,7 @@ namespace libcloudphxx const quantity th = thrust::get<2>(tpl) * si::kelvins; - return drv * common::theta_dry::d_th_d_rv_subl(T, th) / si::kelvins; + return drv * common::theta_dry::d_th_d_rv_dep(T, th) / si::kelvins; } }; @@ -67,27 +67,44 @@ namespace libcloudphxx }; }; - // update th and rv after condensation / sublimation according to change in 3rd specific wet moments + // update th and rv after condensation / deposition according to change in 3rd specific wet moments // particles have to be sorted template void particles_t::impl::update_th_rv( thrust_device::vector &drv, // change in water vapor mixing ratio - phase_change phase // enum for cond/subl, the default is condensation + phase_change phase // enum for cond/dep, the default is condensation ) { if(!sorted) throw std::runtime_error("libcloudph++: update_th_rv called on an unsorted set"); nancheck(drv, "update_th_rv: input drv"); - // multiplying specific 3rd moms diff by -rho_w*4/3*pi - thrust::transform( - drv.begin(), drv.end(), // input - 1st arg - thrust::make_constant_iterator( // input - 2nd arg - - common::moist_air::rho_w() / si::kilograms * si::cubic_metres - * real_t(4./3) * pi() - ), - drv.begin(), // output - thrust::multiplies() - ); + if (phase == phase_change::condensation) + { + // multiplying specific 3rd moms diff by -rho_w*4/3*pi + thrust::transform( + drv.begin(), drv.end(), // input - 1st arg + thrust::make_constant_iterator( // input - 2nd arg + - common::moist_air::rho_w() / si::kilograms * si::cubic_metres + * real_t(4./3) * pi() + ), + drv.begin(), // output + thrust::multiplies() + ); + } + else if (phase == phase_change::deposition) + { + // multiplying specific 3rd moms diff by -rho_i*4/3*pi + // TODO: use the effective ice density here? + thrust::transform( + drv.begin(), drv.end(), // input - 1st arg + thrust::make_constant_iterator( // input - 2nd arg + - common::moist_air::rho_i() / si::kilograms * si::cubic_metres + * real_t(4./3) * pi() + ), + drv.begin(), // output + thrust::multiplies() + ); + } // updating rv assert(*thrust::min_element(rv.begin(), rv.end()) >= 0); @@ -125,7 +142,7 @@ namespace libcloudphxx thrust::plus() ); } - else if (phase == phase_change::sublimation) + else if (phase == phase_change::deposition) { thrust::transform( th.begin(), th.end(), // input - 1st arg @@ -135,7 +152,7 @@ namespace libcloudphxx T.begin(), // dth = drv * d_th_d_rv(T, th) th.begin() // )), - detail::dth_subl() + detail::dth_dep() ), th.begin(), // output thrust::plus() From a6fdc158cbd69ab142f7d490c9b1125f5aa4c462 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 14 Nov 2025 16:48:07 +0100 Subject: [PATCH 78/97] free drv vector --- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_cond.ipp | 237 ++++++++++++++++--------------- 2 files changed, 126 insertions(+), 113 deletions(-) diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 23379deff..5f24afb3b 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -368,7 +368,7 @@ namespace libcloudphxx allow_sstp_chem(_opts_init.sstp_chem > 1 || _opts_init.variable_dt_switch), pure_const_multi (((_opts_init.sd_conc) == 0) && (_opts_init.sd_const_multi > 0 || _opts_init.dry_sizes.size() > 0)), // coal prob can be greater than one only in sd_conc simulations //tmp_device_real_part(6), - tmp_device_real_cell(5) // 5 temporary vectors of this type; NOTE: default constructor creates 1 + tmp_device_real_cell(4) // 4 temporary vectors of this type; NOTE: default constructor creates 1 { // set 0 dev_count to mark that its not a multi_CUDA spawn diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index 2f6ddb8af..1535f73c9 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -24,133 +24,135 @@ namespace libcloudphxx hskpng_sort(); - // Vector to store 3rd moment - auto drv_liq_g = tmp_device_real_cell.get_guard(); - thrust_device::vector &drv_liq = drv_liq_g.get(); - if(step == 0) - reset_guardp(rw_mom3_gp, tmp_device_real_cell); - thrust_device::vector &rw_mom3 = rw_mom3_gp->get(); - - // Compute per-cell 3rd moment of liquid droplets before condensation. It is stored in count_mom - if(step == 0) - { - moms_gt0(rw2.begin()); // choose liquid particles (rw2>0) - moms_calc(rw2.begin(), real_t(3./2.)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) before condensation"); - - // fill with 0s if not all cells have particles - if(count_n!=n_cell) { - thrust::fill(drv_liq.begin(), drv_liq.end(), real_t(0.)); - thrust::fill(rw_mom3.begin(), rw_mom3.end(), real_t(0.)); - } - - thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, - thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), - thrust::negate() - ); - } - else // copy rw_mom3 from previous step { - // drv = -rw_mom3 precond - thrust::transform( - rw_mom3.begin(), rw_mom3.end(), - drv_liq.begin(), - thrust::negate() - ); - } + // Vector to store 3rd moment + auto drv_liq_g = tmp_device_real_cell.get_guard(); + thrust_device::vector &drv_liq = drv_liq_g.get(); + if(step == 0) + reset_guardp(rw_mom3_gp, tmp_device_real_cell); + thrust_device::vector &rw_mom3 = rw_mom3_gp->get(); - auto hlpr_zip_iter = thrust::make_zip_iterator(thrust::make_tuple( - thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(rv.begin(), ijk.begin()), - thrust::make_permutation_iterator(T.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()), - rd3.begin(), - kpa.begin(), - vt.begin(), - thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), - thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) - )); + // Compute per-cell 3rd moment of liquid droplets before condensation. It is stored in count_mom + if(step == 0) + { + moms_gt0(rw2.begin()); // choose liquid particles (rw2>0) + moms_calc(rw2.begin(), real_t(3./2.)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) before condensation"); - // calculating drop growth in a timestep using backward Euler - // TODO: both calls almost identical, use std::bind or sth? - if(turb_cond) - { - auto RH_plus_ssp_g = tmp_device_real_part.get_guard(); - thrust_device::vector &RH_plus_ssp = RH_plus_ssp_g.get(); - thrust::transform( - ssp.begin(), ssp.end(), - thrust::make_permutation_iterator(RH.begin(), ijk.begin()), - RH_plus_ssp.begin(), - arg::_1 + arg::_2 - ); + // fill with 0s if not all cells have particles + if(count_n!=n_cell) { + thrust::fill(drv_liq.begin(), drv_liq.end(), real_t(0.)); + thrust::fill(rw_mom3.begin(), rw_mom3.end(), real_t(0.)); + } - // condensation for liquid droplets - thrust::transform( - rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() - thrust::make_zip_iterator( // input - 2nd arg - thrust::make_tuple( - hlpr_zip_iter, - thrust::make_permutation_iterator(p.begin(), ijk.begin()), - RH_plus_ssp.begin() - ) - ), - rw2.begin(), // output - detail::advance_rw2(dt, RH_max) - ); - } - else - { - // condensation for liquid droplets - thrust::transform( - rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() - thrust::make_zip_iterator( // input - 2nd arg - thrust::make_tuple( - hlpr_zip_iter, - thrust::make_permutation_iterator(p.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH.begin(), ijk.begin()) - ) - ), - rw2.begin(), // output - detail::advance_rw2(dt, RH_max) - ); - nancheck(rw2, "rw2 after condensation (no sub-steps"); + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, + thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), + thrust::negate() + ); + } + else // copy rw_mom3 from previous step + { + // drv = -rw_mom3 precond + thrust::transform( + rw_mom3.begin(), rw_mom3.end(), + drv_liq.begin(), + thrust::negate() + ); + } - // Compute per-cell 3rd moment of liquid droplets after condensation. It is stored in count_mom - moms_gt0(rw2.begin()); // choose liquid particles (rw2>0) - moms_calc(rw2.begin(), real_t(3./2.)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) after condensation"); + auto hlpr_zip_iter = thrust::make_zip_iterator(thrust::make_tuple( + thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), + thrust::make_permutation_iterator(rv.begin(), ijk.begin()), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(eta.begin(), ijk.begin()), + rd3.begin(), + kpa.begin(), + vt.begin(), + thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), + thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) + )); - // Adding the third liquid moment after condensation to drv_liq - if(step < sstp_cond - 1) + // calculating drop growth in a timestep using backward Euler + // TODO: both calls almost identical, use std::bind or sth? + if(turb_cond) { - thrust::copy( - count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - thrust::make_permutation_iterator(rw_mom3.begin(), count_ijk.begin()) // output + auto RH_plus_ssp_g = tmp_device_real_part.get_guard(); + thrust_device::vector &RH_plus_ssp = RH_plus_ssp_g.get(); + thrust::transform( + ssp.begin(), ssp.end(), + thrust::make_permutation_iterator(RH.begin(), ijk.begin()), + RH_plus_ssp.begin(), + arg::_1 + arg::_2 ); - // adding the third moment after condensation to dm_3 + // condensation for liquid droplets thrust::transform( - rw_mom3.begin(), rw_mom3.end(), - drv_liq.begin(), - drv_liq.begin(), - thrust::plus() + rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() + thrust::make_zip_iterator( // input - 2nd arg + thrust::make_tuple( + hlpr_zip_iter, + thrust::make_permutation_iterator(p.begin(), ijk.begin()), + RH_plus_ssp.begin() + ) + ), + rw2.begin(), // output + detail::advance_rw2(dt, RH_max) ); } - else // last step, calculate change in 3rd moment and update th and rv + else { + // condensation for liquid droplets thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // input - 2nd arg - thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // output - thrust::plus() + rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() + thrust::make_zip_iterator( // input - 2nd arg + thrust::make_tuple( + hlpr_zip_iter, + thrust::make_permutation_iterator(p.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH.begin(), ijk.begin()) + ) + ), + rw2.begin(), // output + detail::advance_rw2(dt, RH_max) ); - rw_mom3_gp.reset(); // destroy guard to tmp array that stored 3rd moment of rw - } + nancheck(rw2, "rw2 after condensation (no sub-steps"); - // update th and rv according to change in third specific wet moment - update_th_rv(drv_liq, impl::phase_change::condensation); + // Compute per-cell 3rd moment of liquid droplets after condensation. It is stored in count_mom + moms_gt0(rw2.begin()); // choose liquid particles (rw2>0) + moms_calc(rw2.begin(), real_t(3./2.)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) after condensation"); + // Adding the third liquid moment after condensation to drv_liq + if(step < sstp_cond - 1) + { + thrust::copy( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(rw_mom3.begin(), count_ijk.begin()) // output + ); + + // adding the third moment after condensation to dm_3 + thrust::transform( + rw_mom3.begin(), rw_mom3.end(), + drv_liq.begin(), + drv_liq.begin(), + thrust::plus() + ); + } + else // last step, calculate change in 3rd moment and update th and rv + { + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // input - 2nd arg + thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // output + thrust::plus() + ); + rw_mom3_gp.reset(); // destroy guard to tmp array that stored 3rd moment of rw + } + + // update th and rv according to change in third specific wet moment + update_th_rv(drv_liq, impl::phase_change::condensation); + } + } if (opts_init.ice_switch) { @@ -193,6 +195,18 @@ namespace libcloudphxx ); } + auto hlpr_zip_iter = thrust::make_zip_iterator(thrust::make_tuple( + thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), + thrust::make_permutation_iterator(rv.begin(), ijk.begin()), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(eta.begin(), ijk.begin()), + rd3.begin(), + kpa.begin(), + vt.begin(), + thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), + thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) + )); + // deposition for ice crystals thrust::transform( thrust::make_zip_iterator( @@ -265,5 +279,4 @@ namespace libcloudphxx } } }; - } -} \ No newline at end of file + } \ No newline at end of file From 73360fe5967f122aa89f40436fde5deeff7de020 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 21 Nov 2025 14:02:58 +0100 Subject: [PATCH 79/97] initialize Tfreez, correct Jhet --- include/libcloudph++/common/ice_nucleation.hpp | 2 +- src/impl/particles_impl.ipp | 4 ++++ src/impl/particles_impl_ice_nucl_melt.ipp | 4 ++-- src/impl/particles_impl_init_SD_with_distros.ipp | 4 ++++ src/impl/particles_impl_init_SD_with_sizes.ipp | 4 ++++ .../particles_impl_src_dry_distros_matching.ipp | 12 +++++++++--- src/impl/particles_impl_src_dry_distros_simple.ipp | 14 ++++++++++---- src/impl/particles_impl_src_dry_sizes.ipp | 11 ++++++++++- 8 files changed, 44 insertions(+), 11 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index 59ba2bbec..134162a28 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -69,7 +69,7 @@ namespace libcloudphxx if (INP_type == INP_t::mineral) { - real_t J = std::pow(real_t(10), real_t(-1.35) + real_t(22.62) * d_aw); // nucleation rate + real_t J = std::pow(real_t(10), real_t(-1.35) + real_t(22.62) * d_aw) * real_t(1e4); // nucleation rate return 1 - std::exp(- J * A * dt); } else diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 5f24afb3b..2e58364bb 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -466,6 +466,10 @@ namespace libcloudphxx if(opts_init.const_p) tmp_device_real_part.add_vector(); } + if (opts_init.ice_switch && opts_init.time_dep_ice_nucl) + { + tmp_device_real_part.add_vector(); + } resize_size_vctrs.insert(&ijk); resize_size_vctrs.insert(&sorted_ijk); diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp index 6b6c9caee..688788832 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -37,10 +37,10 @@ namespace libcloudphxx if (T_freeze >= T && RH >= real_t(1)) // condition for freezing { - rw2 = real_t(0); rho_i = common::moist_air::rho_i().value(); a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); c = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); + rw2 = real_t(0); } } }; @@ -75,10 +75,10 @@ namespace libcloudphxx if (u01 < common::ice_nucleation::p_freeze(common::ice_nucleation::INP_t::mineral, rd2_insol, T, dt)) { - rw2 = real_t(0); rho_i = common::moist_air::rho_i().value(); a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); c = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); + rw2 = real_t(0); } } }; diff --git a/src/impl/particles_impl_init_SD_with_distros.ipp b/src/impl/particles_impl_init_SD_with_distros.ipp index 0f06d4e47..2cea4a1b3 100644 --- a/src/impl/particles_impl_init_SD_with_distros.ipp +++ b/src/impl/particles_impl_init_SD_with_distros.ipp @@ -59,6 +59,10 @@ namespace libcloudphxx { init_insol_dry_sizes(kpa_rd_insol.rd_insol); init_a_c_rho_ice(); + if (! opts_init.time_dep_ice_nucl) + { + init_T_freeze(); + } } // initialising wet radii diff --git a/src/impl/particles_impl_init_SD_with_sizes.ipp b/src/impl/particles_impl_init_SD_with_sizes.ipp index de1a1637a..b462d4bdb 100644 --- a/src/impl/particles_impl_init_SD_with_sizes.ipp +++ b/src/impl/particles_impl_init_SD_with_sizes.ipp @@ -52,6 +52,10 @@ namespace libcloudphxx { init_insol_dry_sizes(rd_insol); init_a_c_rho_ice(); + if (! opts_init.time_dep_ice_nucl) + { + init_T_freeze(); + } } // init multiplicities diff --git a/src/impl/particles_impl_src_dry_distros_matching.ipp b/src/impl/particles_impl_src_dry_distros_matching.ipp index b82232c98..699206880 100644 --- a/src/impl/particles_impl_src_dry_distros_matching.ipp +++ b/src/impl/particles_impl_src_dry_distros_matching.ipp @@ -222,9 +222,15 @@ namespace libcloudphxx init_kappa( p_sdd->first.kappa ); - init_insol_dry_sizes( - p_sdd->first.rd_insol - ); + if (opts_init.ice_switch) + { + init_insol_dry_sizes(p_sdd->first.rd_insol); + init_a_c_rho_ice(); + if (! opts_init.time_dep_ice_nucl) + { + init_T_freeze(); + } + } if(opts_init.diag_incloud_time) init_incloud_time(); diff --git a/src/impl/particles_impl_src_dry_distros_simple.ipp b/src/impl/particles_impl_src_dry_distros_simple.ipp index 8247d2560..fe941dc09 100644 --- a/src/impl/particles_impl_src_dry_distros_simple.ipp +++ b/src/impl/particles_impl_src_dry_distros_simple.ipp @@ -57,10 +57,16 @@ namespace libcloudphxx // init other peoperties of SDs that didnt have a match init_kappa( p_sdd->first.kappa - ); - init_insol_dry_sizes( - p_sdd->first.rd_insol - ); + ); + if (opts_init.ice_switch) + { + init_insol_dry_sizes(p_sdd->first.rd_insol); + init_a_c_rho_ice(); + if (! opts_init.time_dep_ice_nucl) + { + init_T_freeze(); + } + } if(opts_init.diag_incloud_time) init_incloud_time(); diff --git a/src/impl/particles_impl_src_dry_sizes.ipp b/src/impl/particles_impl_src_dry_sizes.ipp index 9917c6580..f32dd6ad3 100644 --- a/src/impl/particles_impl_src_dry_sizes.ipp +++ b/src/impl/particles_impl_src_dry_sizes.ipp @@ -55,7 +55,16 @@ namespace libcloudphxx // init kappa and rd_insol init_kappa(kappa); - init_insol_dry_sizes(rd_insol); + + if (opts_init.ice_switch) + { + init_insol_dry_sizes(rd_insol); + init_a_c_rho_ice(); + if (! opts_init.time_dep_ice_nucl) + { + init_T_freeze(); + } + } // init multiplicities init_n_dry_sizes(get<0>(sni->second)*sup_dt, get<1>(sni->second)); From 6e7212d3185b1001700cc8d573b7ca757c2db14d Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 21 Nov 2025 15:04:28 +0100 Subject: [PATCH 80/97] debug freezing/melting --- src/impl/particles_impl_ice_nucl_melt.ipp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp index 688788832..d1064173a 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -35,7 +35,7 @@ namespace libcloudphxx const real_t T = thrust::get<5>(tpl); const real_t RH = thrust::get<6>(tpl); - if (T_freeze >= T && RH >= real_t(1)) // condition for freezing + if (rw2 > real_t(0) && T_freeze >= T && RH >= real_t(1)) // condition for freezing { rho_i = common::moist_air::rho_i().value(); a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); @@ -73,7 +73,7 @@ namespace libcloudphxx const real_t u01 = thrust::get<5>(tpl); const real_t T = thrust::get<6>(tpl); - if (u01 < common::ice_nucleation::p_freeze(common::ice_nucleation::INP_t::mineral, rd2_insol, T, dt)) + if (rw2 > real_t(0) && u01 < common::ice_nucleation::p_freeze(common::ice_nucleation::INP_t::mineral, rd2_insol, T, dt)) { rho_i = common::moist_air::rho_i().value(); a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); @@ -99,7 +99,7 @@ namespace libcloudphxx auto& c = thrust::get<2>(tpl); auto& rho_i = thrust::get<3>(tpl); - if (thrust::get<4>(tpl) > real_t(273.15)) // if T > 0 C + if (a*c > real_t(0) && thrust::get<4>(tpl) > real_t(273.15)) // if T > 0 C { rw2 = pow(common::moist_air::rho_i() / common::moist_air::rho_w() * c , real_t(2./3.)) * pow(a , real_t(4./3.)); rho_i = real_t(0); From 45cfa14388b549f2b1c50d7c44445e799e596241 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 25 Nov 2025 15:38:38 +0100 Subject: [PATCH 81/97] 4/3 pi --- src/impl/particles_impl_update_th_rv.ipp | 1 - src/particles_diag.ipp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/particles_impl_update_th_rv.ipp index 0764c66a3..2f71685cd 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/particles_impl_update_th_rv.ipp @@ -99,7 +99,6 @@ namespace libcloudphxx drv.begin(), drv.end(), // input - 1st arg thrust::make_constant_iterator( // input - 2nd arg - common::moist_air::rho_i() / si::kilograms * si::cubic_metres - * real_t(4./3) * pi() ), drv.begin(), // output thrust::multiplies() diff --git a/src/particles_diag.ipp b/src/particles_diag.ipp index a7c0ba039..da318d8d8 100644 --- a/src/particles_diag.ipp +++ b/src/particles_diag.ipp @@ -112,7 +112,7 @@ namespace libcloudphxx BOOST_GPU_ENABLED real_t operator()(const thrust::tuple &tpl) // tpl is a tuple (a, c) { - return thrust::get<0>(tpl) * thrust::get<0>(tpl) * thrust::get<1>(tpl); // a * a * c + return real_t(4./3) * pi() * thrust::get<0>(tpl) * thrust::get<0>(tpl) * thrust::get<1>(tpl); // a * a * c } }; From 2084d1b2dd346d10ed85f081fe692ef62d51c3f7 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 27 Nov 2025 12:14:49 +0100 Subject: [PATCH 82/97] change ice_vol to ice_mass --- bindings/python/lib.cpp | 3 ++- include/libcloudph++/common/output.hpp | 4 ++-- include/libcloudph++/lgrngn/particles.hpp | 6 ++--- src/impl/particles_impl.ipp | 2 +- src/impl/particles_impl_bcnd.ipp | 22 ++++++++---------- src/impl/particles_impl_cond.ipp | 28 ++++++++++++----------- src/impl/particles_impl_update_th_rv.ipp | 5 ++-- src/particles_diag.ipp | 13 +++++++---- 8 files changed, 43 insertions(+), 40 deletions(-) diff --git a/bindings/python/lib.cpp b/bindings/python/lib.cpp index 4f789d007..3d076da27 100644 --- a/bindings/python/lib.cpp +++ b/bindings/python/lib.cpp @@ -63,6 +63,7 @@ BOOST_PYTHON_MODULE(libcloudphxx) bp::scope().attr("eps") = (real_t) (cmn::moist_air::eps()); bp::scope().attr("rho_stp") = (real_t) (cmn::earth::rho_stp() * si::cubic_metres / si::kilograms); bp::scope().attr("rho_w") = (real_t) (cmn::moist_air::rho_w() * si::cubic_metres / si::kilograms); + bp::scope().attr("rho_i") = (real_t) (cmn::moist_air::rho_i() * si::cubic_metres / si::kilograms); //molar mass of trace gases bp::scope().attr("M_SO2") = (real_t) (cmn::molar_mass::M_SO2() * si::moles / si::kilograms); bp::scope().attr("M_H2O2") = (real_t) (cmn::molar_mass::M_H2O2() * si::moles / si::kilograms); @@ -419,7 +420,7 @@ BOOST_PYTHON_MODULE(libcloudphxx) .def("diag_water_cons", &lgr::particles_proto_t::diag_water_cons) .def("diag_ice_a_mom", &lgr::particles_proto_t::diag_ice_a_mom) .def("diag_ice_c_mom", &lgr::particles_proto_t::diag_ice_c_mom) - .def("diag_ice_vol", &lgr::particles_proto_t::diag_ice_vol) + .def("diag_ice_mass", &lgr::particles_proto_t::diag_ice_mass) .def("outbuf", &lgrngn::outbuf) .def("get_attr", &lgr::particles_proto_t::get_attr) ; diff --git a/include/libcloudph++/common/output.hpp b/include/libcloudph++/common/output.hpp index f1e4b6e23..a15253017 100644 --- a/include/libcloudph++/common/output.hpp +++ b/include/libcloudph++/common/output.hpp @@ -18,7 +18,7 @@ namespace libcloudphxx outliq_vol, outdry_vol, outprtcl_num, - outice_vol, + outice_mass, outliq_num, outice_num }; @@ -36,7 +36,7 @@ namespace libcloudphxx {outliq_vol, "liquid_volume"}, {outdry_vol, "dry_volume"}, {outprtcl_num, "particle_number"}, - {outice_vol, "ice_volume"}, + {outice_mass, "ice_mass"}, {outliq_num, "liquid_number"}, {outice_num, "ice_number"} }; diff --git a/include/libcloudph++/lgrngn/particles.hpp b/include/libcloudph++/lgrngn/particles.hpp index c5d8651a7..cb784e5cb 100644 --- a/include/libcloudph++/lgrngn/particles.hpp +++ b/include/libcloudph++/lgrngn/particles.hpp @@ -104,7 +104,7 @@ namespace libcloudphxx virtual void diag_wet_mom(const int&) { assert(false); } virtual void diag_ice_a_mom(const int&) { assert(false); } virtual void diag_ice_c_mom(const int&) { assert(false); } - virtual void diag_ice_vol() { assert(false); } + virtual void diag_ice_mass() { assert(false); } virtual void diag_wet_mass_dens(const real_t&, const real_t&) { assert(false); } virtual void diag_chem(const enum common::chem::chem_species_t&) { assert(false); } virtual void diag_precip_rate() { assert(false); } @@ -197,7 +197,7 @@ namespace libcloudphxx void diag_wet_mom(const int &k); void diag_ice_a_mom(const int &k); void diag_ice_c_mom(const int &k); - void diag_ice_vol(); + void diag_ice_mass(); void diag_kappa_mom(const int &k); void diag_up_mom(const int&); void diag_vp_mom(const int&); @@ -301,7 +301,7 @@ namespace libcloudphxx void diag_wet_mom(const int &k); void diag_ice_a_mom(const int &k); void diag_ice_c_mom(const int &k); - void diag_ice_vol(); + void diag_ice_mass(); void diag_kappa_mom(const int&); void diag_up_mom(const int&); void diag_vp_mom(const int&); diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 2e58364bb..5a6f19d37 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -235,7 +235,7 @@ namespace libcloudphxx lambda_K_gp, rw_mom3_gp, rw3_gp, - ice_vol_gp; + ice_mass_gp; std::unique_ptr< typename tmp_vector_pool>::guard diff --git a/src/impl/particles_impl_bcnd.ipp b/src/impl/particles_impl_bcnd.ipp index 7fafdaf3d..4d4554b64 100644 --- a/src/impl/particles_impl_bcnd.ipp +++ b/src/impl/particles_impl_bcnd.ipp @@ -47,17 +47,14 @@ namespace libcloudphxx }; template - struct count_ice_vol + struct count_ice_mass { - count_ice_vol() {} + count_ice_mass() {} template BOOST_GPU_ENABLED - real_t operator()(const tuple &tup) // tup is a tuple (n, ice_a, ice_c) + real_t operator()(const tuple &tup) // tup is a tuple (n, ice_a, ice_c, ice_rho) { - #if !defined(__NVCC__) - using std::pow; - #endif return 4./3. #if !defined(__NVCC__) * pi() @@ -66,7 +63,8 @@ namespace libcloudphxx #endif * thrust::get<0>(tup) // n * thrust::get<1>(tup) * thrust::get<1>(tup) // a^2 - * thrust::get<2>(tup); //c + * thrust::get<2>(tup) //c + * thrust::get<3>(tup); //rho_i } }; @@ -291,14 +289,14 @@ namespace libcloudphxx if (opts_init.ice_switch) { - // add total ice volume that fell out in this step - output_puddle[common::outice_vol] += + // add total ice mass that fell out in this step + output_puddle[common::outice_mass] += thrust::transform_reduce( thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), ice_a.begin(), ice_c.begin())), // input start + n_filtered.begin(), ice_a.begin(), ice_c.begin(), ice_rho.begin())), // input start thrust::make_zip_iterator(thrust::make_tuple( - n_filtered.begin(), ice_a.begin(), ice_c.begin())) + n_part, // input end - detail::count_ice_vol(), // operation + n_filtered.begin(), ice_a.begin(), ice_c.begin(), ice_rho.begin())) + n_part, // input end + detail::count_ice_mass(), // operation real_t(0), // init val thrust::plus() ); diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index 1535f73c9..75bee0c42 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -160,15 +160,16 @@ namespace libcloudphxx auto drv_ice_g = tmp_device_real_cell.get_guard(); thrust_device::vector &drv_ice = drv_ice_g.get(); if(step == 0) - reset_guardp(ice_vol_gp, tmp_device_real_cell); - thrust_device::vector &ice_vol = ice_vol_gp->get(); + reset_guardp(ice_mass_gp, tmp_device_real_cell); + thrust_device::vector &ice_mass = ice_mass_gp->get(); // Compute per-cell 3rd moment of ice before deposition. It is stored in count_mom if(step == 0) { moms_gt0(ice_a.begin()); // choose ice particles (ice_a>0) moms_calc(thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin(), ice_rho.begin())), + detail::ice_mass() ), real_t(1)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) before deposition"); @@ -176,7 +177,7 @@ namespace libcloudphxx // fill with 0s if not all cells have particles if(count_n!=n_cell) { thrust::fill(drv_ice.begin(), drv_ice.end(), real_t(0.)); - thrust::fill(ice_vol.begin(), ice_vol.end(), real_t(0.)); + thrust::fill(ice_mass.begin(), ice_mass.end(), real_t(0.)); } thrust::transform( @@ -185,11 +186,11 @@ namespace libcloudphxx thrust::negate() ); } - else // copy ice_vol from previous step + else // copy ice_mass from previous step { - // drv = -ice_vol precond + // drv = -ice_mass precond thrust::transform( - ice_vol.begin(), ice_vol.end(), + ice_mass.begin(), ice_mass.end(), drv_ice.begin(), thrust::negate() ); @@ -242,7 +243,8 @@ namespace libcloudphxx // Compute per-cell 3rd moment of ice after deposition. It is stored in count_mom moms_gt0(ice_a.begin()); // choose ice particles (ice_a>0) moms_calc(thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin())), detail::ice_vol() + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin(), ice_rho.begin())), + detail::ice_mass() ), real_t(1)); nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) after deposition"); @@ -252,12 +254,12 @@ namespace libcloudphxx { thrust::copy( count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - thrust::make_permutation_iterator(ice_vol.begin(), count_ijk.begin()) // output + thrust::make_permutation_iterator(ice_mass.begin(), count_ijk.begin()) // output ); - // adding the third moment after deposition to ice_vol + // adding the third moment after deposition to ice_mass thrust::transform( - ice_vol.begin(), ice_vol.end(), + ice_mass.begin(), ice_mass.end(), drv_ice.begin(), drv_ice.begin(), thrust::plus() @@ -271,10 +273,10 @@ namespace libcloudphxx thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // output thrust::plus() ); - ice_vol_gp.reset(); // destroy guard to tmp array that stored ice_vol + ice_mass_gp.reset(); // destroy guard to tmp array that stored ice_mass } - // update th and rv according to change in third specific wet moment + // update th and rv according to change in third specific moment update_th_rv(drv_ice, impl::phase_change::deposition); } } diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/particles_impl_update_th_rv.ipp index 2f71685cd..bd7007208 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/particles_impl_update_th_rv.ipp @@ -93,12 +93,11 @@ namespace libcloudphxx } else if (phase == phase_change::deposition) { - // multiplying specific 3rd moms diff by -rho_i*4/3*pi - // TODO: use the effective ice density here? + // drv is already multiplied by 4/3 * pi * rho_i thrust::transform( drv.begin(), drv.end(), // input - 1st arg thrust::make_constant_iterator( // input - 2nd arg - - common::moist_air::rho_i() / si::kilograms * si::cubic_metres + - si::cubic_metres / si::kilograms ), drv.begin(), // output thrust::multiplies() diff --git a/src/particles_diag.ipp b/src/particles_diag.ipp index da318d8d8..cc71859e2 100644 --- a/src/particles_diag.ipp +++ b/src/particles_diag.ipp @@ -106,13 +106,15 @@ namespace libcloudphxx }; template - class ice_vol + class ice_mass { public: BOOST_GPU_ENABLED - real_t operator()(const thrust::tuple &tpl) // tpl is a tuple (a, c) + real_t operator()(const thrust::tuple &tpl) // tpl is a tuple (a, c, rho) { - return real_t(4./3) * pi() * thrust::get<0>(tpl) * thrust::get<0>(tpl) * thrust::get<1>(tpl); // a * a * c + return real_t(4./3) * pi() + * thrust::get<0>(tpl) * thrust::get<0>(tpl) * thrust::get<1>(tpl) // a^2 * c + * thrust::get<2>(tpl); // rho } }; @@ -371,10 +373,11 @@ namespace libcloudphxx // computes ice volume template - void particles_t::diag_ice_vol() + void particles_t::diag_ice_mass() { pimpl->moms_calc(thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple(pimpl->ice_a.begin(), pimpl->ice_c.begin())), detail::ice_vol() + thrust::make_zip_iterator(thrust::make_tuple(pimpl->ice_a.begin(), pimpl->ice_c.begin(), pimpl->ice_rho.begin())), + detail::ice_mass() ), real_t(1)); } From 6d14b05c84a3ebde1853e0aeca9ba4abc02f6bb4 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 27 Nov 2025 13:20:03 +0100 Subject: [PATCH 83/97] correcting units and tests --- src/impl/particles_impl_update_th_rv.ipp | 2 +- src/particles_diag.ipp | 7 ++++++- tests/python/unit/CMakeLists.txt | 2 +- tests/python/unit/api_blk_1m.py | 2 +- tests/python/unit/source.py | 15 +++++++++------ tests/python/unit/sstp_cond.py | 5 ++++- 6 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/particles_impl_update_th_rv.ipp index bd7007208..9d49647fb 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/particles_impl_update_th_rv.ipp @@ -97,7 +97,7 @@ namespace libcloudphxx thrust::transform( drv.begin(), drv.end(), // input - 1st arg thrust::make_constant_iterator( // input - 2nd arg - - si::cubic_metres / si::kilograms + - real_t(1) ), drv.begin(), // output thrust::multiplies() diff --git a/src/particles_diag.ipp b/src/particles_diag.ipp index cc71859e2..6b8ddc25f 100644 --- a/src/particles_diag.ipp +++ b/src/particles_diag.ipp @@ -112,7 +112,12 @@ namespace libcloudphxx BOOST_GPU_ENABLED real_t operator()(const thrust::tuple &tpl) // tpl is a tuple (a, c, rho) { - return real_t(4./3) * pi() + return real_t(4./3) + #if !defined(__NVCC__) + * pi() + #else + * CUDART_PI + #endif * thrust::get<0>(tpl) * thrust::get<0>(tpl) * thrust::get<1>(tpl) // a^2 * c * thrust::get<2>(tpl); // rho } diff --git a/tests/python/unit/CMakeLists.txt b/tests/python/unit/CMakeLists.txt index 14bf67684..badc92dbc 100644 --- a/tests/python/unit/CMakeLists.txt +++ b/tests/python/unit/CMakeLists.txt @@ -1,5 +1,5 @@ # non-pytest tests -foreach(test api_blk_1m api_blk_2m api_lgrngn api_common segfault_20150216 col_kernels terminal_velocities uniform_init source sstp_cond multiple_kappas adve_scheme lgrngn_subsidence sat_adj_blk_1m diag_incloud_time relax blk_1m_ice) +foreach(test api_blk_1m api_blk_2m api_lgrngn api_common segfault_20150216 col_kernels terminal_velocities uniform_init source sstp_cond multiple_kappas adve_scheme lgrngn_subsidence sat_adj_blk_1m diag_incloud_time relax blk_1m_ice ice_SD) #TODO: indicate that tests depend on the lib add_test( diff --git a/tests/python/unit/api_blk_1m.py b/tests/python/unit/api_blk_1m.py index 5b466a50e..e3e78fa3a 100644 --- a/tests/python/unit/api_blk_1m.py +++ b/tests/python/unit/api_blk_1m.py @@ -97,7 +97,7 @@ def test_rhs_cell(opts, name): assert flux == 0 assert dot_rr == dot_rr_old # no rain water -> no precip -#test ice, TODO: separate file/test? +#test RHS cellwise ice def test_rhs_cell_ice(opts, name): print("Testing RHS cellwise ice with " + name) th = arr_t([230.]) #testing ice physics diff --git a/tests/python/unit/source.py b/tests/python/unit/source.py index 2b12e5ff5..f64273a53 100644 --- a/tests/python/unit/source.py +++ b/tests/python/unit/source.py @@ -51,6 +51,7 @@ def test(opts_init): opts_init.adve_switch = 0; opts_init.cond_switch = 0; opts_init.sedi_switch = 0; + opts_init.ice_switch = 0; opts = lgrngn.opts_t() @@ -59,6 +60,7 @@ def test(opts_init): opts.sedi = 0; opts.coal = 0; opts.cond = 0; + opts.ice_nucl = 0; rhod = arr_t([[ 1., 1. ],[ 1., 1. ]]) th = arr_t([[300., 300. ],[ 300., 300. ]]) @@ -92,12 +94,13 @@ def test(opts_init): return sd_conc, wet_mom0, wet_mom1 kappa = .61 +rd_insol = 0.5e-6 # ----------- test source with dry_distros simple ------------------ print(' --- dry_distros simple src ---') opts_init = lgrngn.opts_init_t() -opts_init.dry_distros = {kappa:lognormal} -opts_init.src_dry_distros = {kappa:lognormal_src} +opts_init.dry_distros = {(kappa, rd_insol):lognormal} +opts_init.src_dry_distros = {(kappa, rd_insol):lognormal_src} opts_init.sd_conc = 1024 opts_init.src_sd_conc = 512 opts_init.n_sd_max = int((opts_init.sd_conc * 2 + opts_init.src_sd_conc * 2) * 2) # assuming nx=nz=2 @@ -122,8 +125,8 @@ def test(opts_init): # --------------- test source with dry_distros matching ------------------ print(' --- dry_distros matching src ---') opts_init = lgrngn.opts_init_t() -opts_init.dry_distros = {kappa:lognormal} -opts_init.src_dry_distros = {kappa:lognormal_src} +opts_init.dry_distros = {(kappa, rd_insol):lognormal} +opts_init.src_dry_distros = {(kappa, rd_insol):lognormal_src} opts_init.sd_conc = 1024 opts_init.src_sd_conc = 512 opts_init.n_sd_max = int((opts_init.sd_conc * 2 + opts_init.src_sd_conc * 2) * 2) # assuming nx=nz=2 @@ -148,8 +151,8 @@ def test(opts_init): # --------- test source with dry_sizes ------------ print(' --- dry_sizes src ---') opts_init = lgrngn.opts_init_t() -opts_init.dry_sizes = {kappa : {1.e-6 : [30., 20], 15.e-6 : [10., 10]}} -opts_init.src_dry_sizes = {kappa : {1.e-6 : [0.3, 10], 15.e-6 : [0.1, 5]}} +opts_init.dry_sizes = {(kappa, rd_insol) : {1.e-6 : [30., 20], 15.e-6 : [10., 10]}} +opts_init.src_dry_sizes = {(kappa, rd_insol) : {1.e-6 : [0.3, 10], 15.e-6 : [0.1, 5]}} opts_init.n_sd_max=240 opts_init.src_type = lgrngn.src_t.simple; # dry sizes works the same for simple and matching (no matching done) diff --git a/tests/python/unit/sstp_cond.py b/tests/python/unit/sstp_cond.py index 22f92b41c..583a2ce52 100644 --- a/tests/python/unit/sstp_cond.py +++ b/tests/python/unit/sstp_cond.py @@ -24,6 +24,7 @@ def test(turb_cond): opts_init.dry_distros = {(kappa, rd_insol):lognormal} opts_init.coal_switch=0 opts_init.sedi_switch=0 + opts_init.ice_switch=0 opts_init.dt = 1 opts_init.sd_conc = 64 opts_init.n_sd_max = 512 @@ -88,6 +89,8 @@ def test(turb_cond): prtcls.diag_wet_mom(3); wet_post_spin = copy(frombuffer(prtcls.outbuf()).reshape(opts_init.nx, opts_init.nz)) water_post_spin = 1000. * 4./3. * pi * wet_post_spin + rv + print("water post spin: ", water_post_spin) + print("water post init: ", water_post_init) assert allclose(water_post_spin, water_post_init, atol=0, rtol=1e-10) #some discrepancy due to water density #advect SDs @@ -120,7 +123,7 @@ def test(turb_cond): prtcls.diag_all() prtcls.diag_wet_mom(3); wet_post_adve_cond = copy(frombuffer(prtcls.outbuf()).reshape(opts_init.nx, opts_init.nz)) - print(wet_post_adve, wet_post_adve_cond) + #print(wet_post_adve, wet_post_adve_cond) assert allclose(wet_post_adve, wet_post_adve_cond, atol=0, rtol=5e-2) test(False) From 5f226ea0255087302ca4d520f507d90d5b1b5d5a Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 28 Nov 2025 16:02:32 +0100 Subject: [PATCH 84/97] homogeneous freezing at -38C --- .../libcloudph++/common/ice_nucleation.hpp | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index 134162a28..6d813b745 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -62,18 +62,22 @@ namespace libcloudphxx const real_t rd2_insol, // radius squared of insoluble particle in m^2 const real_t T, // temperature in kelvin const real_t dt // time step in seconds - ) { - - real_t A = real_t(4) * pi() * rd2_insol; // surface area of the insoluble particle - real_t d_aw = real_t(1) - const_cp::p_vsi(T * si::kelvins)/ const_cp::p_vs(T * si::kelvins); // water activity - - if (INP_type == INP_t::mineral) + ) + { + if (rd2_insol > real_t(0)) { - real_t J = std::pow(real_t(10), real_t(-1.35) + real_t(22.62) * d_aw) * real_t(1e4); // nucleation rate - return 1 - std::exp(- J * A * dt); + real_t A = real_t(4) * pi() * rd2_insol; // surface area of the insoluble particle + real_t d_aw = real_t(1) - const_cp::p_vsi(T * si::kelvins)/ const_cp::p_vs(T * si::kelvins); // water activity + if (INP_type == INP_t::mineral) + { + real_t J = std::pow(real_t(10), real_t(-1.35) + real_t(22.62) * d_aw) * real_t(1e4); // nucleation rate + return 1 - std::exp(- J * A * dt); + } + else + throw std::runtime_error("INP type not implemented"); } else - throw std::runtime_error("INP type not implemented"); + return T > real_t(235.15) ? real_t(0) : real_t(1); // homogeneous freezing at -38 C } From e47c973d09fc45b41c287b0c4fb78c20c7c442fd Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 2 Dec 2025 15:36:17 +0100 Subject: [PATCH 85/97] ice test --- src/impl/particles_impl_cond.ipp | 3 +- tests/python/unit/ice_SD.py | 63 ++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/python/unit/ice_SD.py diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index 75bee0c42..882219495 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -116,6 +116,7 @@ namespace libcloudphxx detail::advance_rw2(dt, RH_max) ); nancheck(rw2, "rw2 after condensation (no sub-steps"); + } // Compute per-cell 3rd moment of liquid droplets after condensation. It is stored in count_mom moms_gt0(rw2.begin()); // choose liquid particles (rw2>0) @@ -152,7 +153,7 @@ namespace libcloudphxx // update th and rv according to change in third specific wet moment update_th_rv(drv_liq, impl::phase_change::condensation); } - } + if (opts_init.ice_switch) { diff --git a/tests/python/unit/ice_SD.py b/tests/python/unit/ice_SD.py new file mode 100644 index 000000000..b5cda6c35 --- /dev/null +++ b/tests/python/unit/ice_SD.py @@ -0,0 +1,63 @@ +import sys + +sys.path.insert(0, "../../bindings/python/") +sys.path.insert(0, "../../../build/bindings/python/") + +from libcloudphxx import lgrngn, common +import numpy as np +from math import exp, log, sqrt, pi + +n_tot = 60e6 +def lognormal(lnr): + mean_r = .04e-6 / 2 + stdev = 1.4 + return n_tot * exp( + -pow((lnr - log(mean_r)), 2) / 2 / pow(log(stdev),2) + ) / log(stdev) / sqrt(2*pi); + +opts_init = lgrngn.opts_init_t() +backend = lgrngn.backend_t.serial +opts = lgrngn.opts_t() + +kappa = .61 +rd_insol = 0.5e-6 +opts_init.dry_distros = {(kappa, rd_insol): lognormal} +opts_init.dt = 0.1 +opts_init.sd_conc = 100 +opts_init.n_sd_max = 1000 +opts_init.RH_max = 0.95 + +opts_init.ice_switch = True +opts_init.coal_switch = False +opts_init.sedi_switch = False +opts.ice_nucl = True +opts.cond = True +opts.coal = False +opts.adve = False +opts.sedi = False + + +for time_dep_switch in [True, False]: + print("time dependent ice nucleation = ", time_dep_switch) + p = 80000. + T = 243. + RH = 1. + rv = np.array([RH * common.r_vs(T, p)]) + th = np.array([T / common.exner(p)]) + rhod = np.array([common.rhod(p, th[0], rv[0])]) + + opts_init.time_dep_ice_nucl = time_dep_switch + prtcls = lgrngn.factory(backend, opts_init) + prtcls.init(th, rv, rhod) + for _ in range(500): + prtcls.step_sync(opts, th, rv, rhod) + prtcls.step_async(opts) + prtcls.diag_all() + prtcls.diag_ice_mass() + ri = np.frombuffer(prtcls.outbuf()) + print("ice mixing ratio ", ri[0] * 1e3, "g/kg") + print("water vapor mixing ratio ", rv[0] * 1e3, "g/kg") + assert np.isnan(ri[0]) == False + assert np.isnan(rv[0]) == False + assert rv[0] >= 0 + assert ri[0] >= 0 \ No newline at end of file From ebf9a2f7aea9f73e3ff7b0bad20abe0c6282c45c Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 2 Dec 2025 16:03:37 +0100 Subject: [PATCH 86/97] source test --- bindings/python/lgrngn.hpp | 4 ++-- bindings/python/lib.cpp | 2 -- tests/python/unit/source.py | 43 ++++++++++++++++++++----------------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/bindings/python/lgrngn.hpp b/bindings/python/lgrngn.hpp index 7727960ae..7b1148e69 100644 --- a/bindings/python/lgrngn.hpp +++ b/bindings/python/lgrngn.hpp @@ -334,11 +334,11 @@ namespace libcloudphxx const real_t kappa = bp::extract(key[0]); const real_t rd_insol = bp::extract(key[1]); - // extract size : {conc, count} dict for this (kappa, rd_insol) + // extract size : {conc, count, supstp_src} dict for this (kappa, rd_insol) const bp::dict size_conc = bp::extract(kappa_func.values()[j]); std::map> size_conc_map; - // turn the size : {conc, count} dict into a size : {conc, count} map + // turn the size : {conc, count, supstp_src} dict into a size : {conc, count, supstp_src} map for (int i = 0; i < len(size_conc.keys()); ++i) { const bp::list conc_count_list = bp::extract(size_conc.values()[i]); diff --git a/bindings/python/lib.cpp b/bindings/python/lib.cpp index 3d076da27..9975fe48f 100644 --- a/bindings/python/lib.cpp +++ b/bindings/python/lib.cpp @@ -324,7 +324,6 @@ BOOST_PYTHON_MODULE(libcloudphxx) .def_readwrite("sstp_cond", &lgr::opts_init_t::sstp_cond) .def_readwrite("sstp_coal", &lgr::opts_init_t::sstp_coal) .def_readwrite("sstp_chem", &lgr::opts_init_t::sstp_chem) - //.def_readwrite("supstp_src", &lgr::opts_init_t::supstp_src) .def_readwrite("supstp_rlx", &lgr::opts_init_t::supstp_rlx) .def_readwrite("kernel", &lgr::opts_init_t::kernel) .def_readwrite("adve_scheme", &lgr::opts_init_t::adve_scheme) @@ -332,7 +331,6 @@ BOOST_PYTHON_MODULE(libcloudphxx) .def_readwrite("sd_conc_large_tail", &lgr::opts_init_t::sd_conc_large_tail) .def_readwrite("aerosol_independent_of_rhod", &lgr::opts_init_t::aerosol_independent_of_rhod) .def_readwrite("sd_const_multi", &lgr::opts_init_t::sd_const_multi) - //.def_readwrite("src_sd_conc", &lgr::opts_init_t::src_sd_conc) .def_readwrite("rlx_bins", &lgr::opts_init_t::rlx_bins) .def_readwrite("rlx_sd_per_bin", &lgr::opts_init_t::rlx_sd_per_bin) .def_readwrite("rlx_timescale", &lgr::opts_init_t::rlx_timescale) diff --git a/tests/python/unit/source.py b/tests/python/unit/source.py index f64273a53..04f306609 100644 --- a/tests/python/unit/source.py +++ b/tests/python/unit/source.py @@ -29,8 +29,10 @@ def lognormal_src(lnr): -pow((lnr - log(mean_r)), 2) / 2 / pow(log(stdev),2) ) / log(stdev) / sqrt(2*pi); -def test(opts_init): - opts_init.supstp_src = 50 +kappa = .61 +rd_insol = 0.5e-6 + +def test(opts_init, opts): opts_init.rng_seed = int(time()) opts_init.dt = 1 opts_init.nx = 2; @@ -53,8 +55,6 @@ def test(opts_init): opts_init.sedi_switch = 0; opts_init.ice_switch = 0; - opts = lgrngn.opts_t() - opts.adve = 0; opts.chem = 0; opts.sedi = 0; @@ -93,20 +93,19 @@ def test(opts_init): return sd_conc, wet_mom0, wet_mom1 -kappa = .61 -rd_insol = 0.5e-6 - # ----------- test source with dry_distros simple ------------------ print(' --- dry_distros simple src ---') opts_init = lgrngn.opts_init_t() +opts = lgrngn.opts_t() opts_init.dry_distros = {(kappa, rd_insol):lognormal} -opts_init.src_dry_distros = {(kappa, rd_insol):lognormal_src} opts_init.sd_conc = 1024 -opts_init.src_sd_conc = 512 -opts_init.n_sd_max = int((opts_init.sd_conc * 2 + opts_init.src_sd_conc * 2) * 2) # assuming nx=nz=2 -opts_init.src_type = lgrngn.src_t.simple; +src_sd_conc = 512 +supstp_src = 50 +opts.src_dry_distros = {(kappa, rd_insol):(lognormal_src, src_sd_conc, supstp_src)} +opts_init.n_sd_max = int((opts_init.sd_conc * 2 + src_sd_conc * 2) * 2) # assuming nx=nz=2 +opts_init.src_type = lgrngn.src_t.simple -sd_conc, wet_mom0, wet_mom1 = test(opts_init) +sd_conc, wet_mom0, wet_mom1 = test(opts_init, opts) print('diag_sd_conc', sd_conc) if not(sd_conc[0] == 2048 and sd_conc[2] == 2048): @@ -125,14 +124,16 @@ def test(opts_init): # --------------- test source with dry_distros matching ------------------ print(' --- dry_distros matching src ---') opts_init = lgrngn.opts_init_t() +opts = lgrngn.opts_t() opts_init.dry_distros = {(kappa, rd_insol):lognormal} -opts_init.src_dry_distros = {(kappa, rd_insol):lognormal_src} opts_init.sd_conc = 1024 -opts_init.src_sd_conc = 512 -opts_init.n_sd_max = int((opts_init.sd_conc * 2 + opts_init.src_sd_conc * 2) * 2) # assuming nx=nz=2 -opts_init.src_type = lgrngn.src_t.matching; +src_sd_conc = 512 +supstp_src = 50 +opts.src_dry_distros = {(kappa, rd_insol):(lognormal_src, src_sd_conc, supstp_src)} +opts_init.n_sd_max = int((opts_init.sd_conc * 2 + src_sd_conc * 2) * 2) # assuming nx=nz=2 +opts_init.src_type = lgrngn.src_t.matching -sd_conc, wet_mom0, wet_mom1 = test(opts_init) +sd_conc, wet_mom0, wet_mom1 = test(opts_init, opts) print('diag_sd_conc', sd_conc) if not((sd_conc[0] == 1164 or sd_conc[0] == 1165) and (sd_conc[2] == 1164 or sd_conc[2] == 1165)): @@ -151,13 +152,15 @@ def test(opts_init): # --------- test source with dry_sizes ------------ print(' --- dry_sizes src ---') opts_init = lgrngn.opts_init_t() +opts = lgrngn.opts_t() +supstp_src = 50 opts_init.dry_sizes = {(kappa, rd_insol) : {1.e-6 : [30., 20], 15.e-6 : [10., 10]}} -opts_init.src_dry_sizes = {(kappa, rd_insol) : {1.e-6 : [0.3, 10], 15.e-6 : [0.1, 5]}} +opts.src_dry_sizes = {(kappa, rd_insol) : {1.e-6 : [0.3, 10, supstp_src], 15.e-6 : [0.1, 5, supstp_src]}} opts_init.n_sd_max=240 -opts_init.src_type = lgrngn.src_t.simple; # dry sizes works the same for simple and matching (no matching done) +opts_init.src_type = lgrngn.src_t.simple # dry sizes works the same for simple and matching (no matching done) -sd_conc, wet_mom0, wet_mom1 = test(opts_init) +sd_conc, wet_mom0, wet_mom1 = test(opts_init, opts) print('diag_sd_conc', sd_conc) if not((sd_conc[0] == 60) and (sd_conc[2] == 60)): From 1cea336d292c1105ebd27645b4cb40a1ce551820 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 2 Dec 2025 16:16:00 +0100 Subject: [PATCH 87/97] rd_insol in tests --- tests/mpi/mpi_adve_test.cpp | 2 +- tests/python/unit/parcel/test.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/mpi/mpi_adve_test.cpp b/tests/mpi/mpi_adve_test.cpp index 7f07a2060..d21b0b47e 100644 --- a/tests/mpi/mpi_adve_test.cpp +++ b/tests/mpi/mpi_adve_test.cpp @@ -121,7 +121,7 @@ void test(backend_t backend, std::string back_name, int ndims, bool dir, int n_d */ opts_init.dry_distros.emplace( - 0.001, // kappa + (0.001, 0.), // kappa, rd_insol std::make_shared>() // distribution ); diff --git a/tests/python/unit/parcel/test.py b/tests/python/unit/parcel/test.py index b2fc56f19..291633601 100644 --- a/tests/python/unit/parcel/test.py +++ b/tests/python/unit/parcel/test.py @@ -35,7 +35,8 @@ def lognormal(lnr): return n_tot * exp( -pow((lnr - log(mean_r)), 2) / 2 / pow(log(stdev),2) ) / log(stdev) / sqrt(2*pi); -kappa = .61 #TODO - check +kappa = .61 +rd_insol = 0. # rho = 1.8 # TODO chem_gas = { @@ -47,7 +48,7 @@ def lognormal(lnr): # running all three for rhs in [ rhs_blk_2m(dt), - rhs_lgrngn(dt, sd_conc, {kappa:lognormal}, chem_gas) + rhs_lgrngn(dt, sd_conc, {(kappa, rd_insol):lognormal}, chem_gas) ]: parcel(p_d, th_d, r_v, w, dt, nt, rhs) print(p_d, arr_t([th_dry2std(th_d[0], r_v[0])]), r_v) From 62cee1413d43c2aa1c7a5091112ff1e06c2fda28 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 3 Dec 2025 12:42:56 +0100 Subject: [PATCH 88/97] fixing CUDA compilation errors --- .../libcloudph++/common/ice_nucleation.hpp | 30 +++++++++++++------ models/kinematic_2D/src/opts_lgrngn.hpp | 2 +- src/impl/particles_impl_hskpng_vterm.ipp | 6 +++- tests/mpi/mpi_adve_test.cpp | 2 +- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index 6d813b745..c0a33eb1c 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -19,11 +19,17 @@ namespace libcloudphxx const real_t rd2_insol, // radius squared of insoluble particle in m^2 const real_t rand // random number between [0, 1] ) { - real_t A = real_t(4) * pi() * rd2_insol; // surface area of the insoluble particle - - if (INP_type == INP_t::mineral && A > std::numeric_limits::epsilon()) + real_t A = real_t(4) + #if !defined(__NVCC__) + * pi() + #else + * CUDART_PI + #endif + * rd2_insol; // surface area of the insoluble particle + + if (INP_type == INP_t::mineral && A > real_t(1e-20)) { - return (real_t(273.15) + (real_t(8.934) - std::log(- std::log(1 - rand) / A) ) / real_t(0.517)) * si::kelvins; + return (real_t(273.15) + (real_t(8.934) - log(- log(real_t(1.) - rand) / A) ) / real_t(0.517)) * si::kelvin; } else { @@ -66,15 +72,21 @@ namespace libcloudphxx { if (rd2_insol > real_t(0)) { - real_t A = real_t(4) * pi() * rd2_insol; // surface area of the insoluble particle - real_t d_aw = real_t(1) - const_cp::p_vsi(T * si::kelvins)/ const_cp::p_vs(T * si::kelvins); // water activity + real_t A = real_t(4) + #if !defined(__NVCC__) + * pi() + #else + * CUDART_PI + #endif + * rd2_insol; // surface area of the insoluble particle + real_t d_aw = real_t(1) - const_cp::p_vsi(T * si::kelvin)/ const_cp::p_vs(T * si::kelvin); // water activity if (INP_type == INP_t::mineral) { - real_t J = std::pow(real_t(10), real_t(-1.35) + real_t(22.62) * d_aw) * real_t(1e4); // nucleation rate - return 1 - std::exp(- J * A * dt); + real_t J = pow(real_t(10), real_t(-1.35) + real_t(22.62) * d_aw) * real_t(1e4); // nucleation rate + return 1 - exp(- J * A * dt); } else - throw std::runtime_error("INP type not implemented"); + return real_t(0.); // TODO: other INP types } else return T > real_t(235.15) ? real_t(0) : real_t(1); // homogeneous freezing at -38 C diff --git a/models/kinematic_2D/src/opts_lgrngn.hpp b/models/kinematic_2D/src/opts_lgrngn.hpp index ddae0d1aa..479bc7cbb 100644 --- a/models/kinematic_2D/src/opts_lgrngn.hpp +++ b/models/kinematic_2D/src/opts_lgrngn.hpp @@ -297,7 +297,7 @@ void setopts_micro( */ rt_params.cloudph_opts_init.dry_distros.emplace( - setup.kappa, // key + libcloudphxx::lgrngn::kappa_rd_insol_t{setup.kappa, 0.}, // kappa, rd_insol std::make_shared> (setup) ); diff --git a/src/impl/particles_impl_hskpng_vterm.ipp b/src/impl/particles_impl_hskpng_vterm.ipp index 9d3f8c7a6..44cdf3055 100644 --- a/src/impl/particles_impl_hskpng_vterm.ipp +++ b/src/impl/particles_impl_hskpng_vterm.ipp @@ -175,7 +175,11 @@ namespace libcloudphxx return common__vterm__ice()(ice_a, ice_c, rho_i, env); } else if(vt_eq == vt_t::beard77fast) { - throw std::runtime_error("beard77fast doesn't work with ice switch = True"); + #ifndef __CUDA_ARCH__ + throw std::runtime_error("beard77fast doesn't work with ice switch = True"); + #else + assert(false && "beard77fast doesn't work with ice switch = True"); + #endif } else { return common__vterm__vt(vt_eq)(rw2, env); diff --git a/tests/mpi/mpi_adve_test.cpp b/tests/mpi/mpi_adve_test.cpp index d21b0b47e..06813b7d7 100644 --- a/tests/mpi/mpi_adve_test.cpp +++ b/tests/mpi/mpi_adve_test.cpp @@ -121,7 +121,7 @@ void test(backend_t backend, std::string back_name, int ndims, bool dir, int n_d */ opts_init.dry_distros.emplace( - (0.001, 0.), // kappa, rd_insol + libcloudphxx::lgrngn::kappa_rd_insol_t{0.001, 0.}, // kappa, rd_insol std::make_shared>() // distribution ); From e57ba43dea351817590003157a7da71cb6321465 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Wed, 3 Dec 2025 16:10:21 +0100 Subject: [PATCH 89/97] debuging --- include/libcloudph++/common/ice_nucleation.hpp | 2 +- src/impl/particles_impl_hskpng_vterm.ipp | 6 ++---- tests/mpi/mpi_adve_test.cpp | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index c0a33eb1c..381d9b2f2 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -52,7 +52,7 @@ namespace libcloudphxx const real_t &rd2_insol = thrust::get<0>(tpl); // from rd2 vector const real_t &rand = thrust::get<1>(tpl); // from rand vector - return ice_nucleation::T_freeze_CDF_inv( + return ice_nucleation::template T_freeze_CDF_inv( INP_type, rd2_insol, rand diff --git a/src/impl/particles_impl_hskpng_vterm.ipp b/src/impl/particles_impl_hskpng_vterm.ipp index 44cdf3055..d85c6eee1 100644 --- a/src/impl/particles_impl_hskpng_vterm.ipp +++ b/src/impl/particles_impl_hskpng_vterm.ipp @@ -174,16 +174,14 @@ namespace libcloudphxx if(rw2 == 0 && ice_a > 0 && ice_c > 0) { return common__vterm__ice()(ice_a, ice_c, rho_i, env); } - else if(vt_eq == vt_t::beard77fast) { + if(vt_eq == vt_t::beard77fast) { #ifndef __CUDA_ARCH__ throw std::runtime_error("beard77fast doesn't work with ice switch = True"); #else assert(false && "beard77fast doesn't work with ice switch = True"); #endif } - else { - return common__vterm__vt(vt_eq)(rw2, env); - } + return common__vterm__vt(vt_eq)(rw2, env); } }; diff --git a/tests/mpi/mpi_adve_test.cpp b/tests/mpi/mpi_adve_test.cpp index 06813b7d7..b1f2c92f3 100644 --- a/tests/mpi/mpi_adve_test.cpp +++ b/tests/mpi/mpi_adve_test.cpp @@ -121,7 +121,7 @@ void test(backend_t backend, std::string back_name, int ndims, bool dir, int n_d */ opts_init.dry_distros.emplace( - libcloudphxx::lgrngn::kappa_rd_insol_t{0.001, 0.}, // kappa, rd_insol + libcloudphxx::lgrngn::kappa_rd_insol_t{double(0.001), double(0.)}, // kappa, rd_insol std::make_shared>() // distribution ); From 73cbbc18183fb8704d34d7d49ae67f875cb8a3f6 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 4 Dec 2025 11:44:31 +0100 Subject: [PATCH 90/97] more debugging --- bindings/python/lgrngn.hpp | 2 +- models/kinematic_2D/src/opts_lgrngn.hpp | 2 +- src/impl/particles_impl_bcnd.ipp | 7 +++++++ src/impl/particles_impl_cond_common.ipp | 6 ++++-- tests/python/physics/puddle.py | 7 ++++++- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/bindings/python/lgrngn.hpp b/bindings/python/lgrngn.hpp index 7b1148e69..9223cd6e3 100644 --- a/bindings/python/lgrngn.hpp +++ b/bindings/python/lgrngn.hpp @@ -222,7 +222,7 @@ namespace libcloudphxx map_t map = arg->diag_puddle(); bp::dict dict; for(auto& x : map) - dict[static_cast(x.first)] = x.second; + dict[cmn::output_names.at(x.first)] = x.second; return dict; } diff --git a/models/kinematic_2D/src/opts_lgrngn.hpp b/models/kinematic_2D/src/opts_lgrngn.hpp index 479bc7cbb..924921d24 100644 --- a/models/kinematic_2D/src/opts_lgrngn.hpp +++ b/models/kinematic_2D/src/opts_lgrngn.hpp @@ -297,7 +297,7 @@ void setopts_micro( */ rt_params.cloudph_opts_init.dry_distros.emplace( - libcloudphxx::lgrngn::kappa_rd_insol_t{setup.kappa, 0.}, // kappa, rd_insol + libcloudphxx::lgrngn::kappa_rd_insol_t{setup.kappa, config::real_t(0.)}, // kappa, rd_insol std::make_shared> (setup) ); diff --git a/src/impl/particles_impl_bcnd.ipp b/src/impl/particles_impl_bcnd.ipp index 4d4554b64..d3119b9a9 100644 --- a/src/impl/particles_impl_bcnd.ipp +++ b/src/impl/particles_impl_bcnd.ipp @@ -287,6 +287,13 @@ namespace libcloudphxx thrust::plus() ); + // add total number of particles that fell out in this step + output_puddle[common::outprtcl_num] += + thrust::reduce( + n_filtered.begin(), // input start + n_filtered.begin() + n_part // input end + ); + if (opts_init.ice_switch) { // add total ice mass that fell out in this step diff --git a/src/impl/particles_impl_cond_common.ipp b/src/impl/particles_impl_cond_common.ipp index 9ec475599..bc5591643 100644 --- a/src/impl/particles_impl_cond_common.ipp +++ b/src/impl/particles_impl_cond_common.ipp @@ -422,8 +422,10 @@ namespace libcloudphxx advance_rw2_minfun_ice f_a(dt, a_old * a_old, tpl, RH_max); advance_rw2_minfun_ice f_c(dt, c_old * c_old, tpl, RH_max); - const real_t da_dt = (f_a.drw2_dt(a_old * a_old * si::square_metres) / (2 * a_old * si::metres)).value(); - const real_t dc_dt = (f_c.drw2_dt(c_old * c_old * si::square_metres) / (2 * c_old * si::metres)).value(); + const real_t da_dt = (f_a.drw2_dt(a_old * a_old * si::square_metres) / (2 * a_old * si::metres)) + * si::seconds / si::metres; + const real_t dc_dt = (f_c.drw2_dt(c_old * c_old * si::square_metres) / (2 * c_old * si::metres)) + * si::seconds / si::metres; // forward Euler for simplicity const real_t a_new = max(a_old + dt * da_dt, real_t(1e-9)); diff --git a/tests/python/physics/puddle.py b/tests/python/physics/puddle.py index 252972f83..e80b93525 100644 --- a/tests/python/physics/puddle.py +++ b/tests/python/physics/puddle.py @@ -21,6 +21,7 @@ def lognormal(lnr): Opts_init.dry_distros = {(kappa, rd_insol):lognormal} Opts_init.coal_switch = False Opts_init.sedi_switch = True +Opts_init.ice_switch = False Opts_init.terminal_velocity = lgrngn.vt_t.beard76 Opts_init.dt = 1 @@ -45,6 +46,7 @@ def lognormal(lnr): Opts.coal = False Opts.chem = False Opts.rcyc = False +Opts.ice_nucl = False Rhod = 1. * np.ones((Opts_init.nx, Opts_init.nz)) Th = 300. * np.ones((Opts_init.nx, Opts_init.nz)) @@ -69,7 +71,10 @@ def lognormal(lnr): assert(tab_out[0][0] == 0.) -puddle_expected_per_cell = {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0, 5: 0.0, 6: 0.0, 7: 0.0, 8: 7.087802417148837e-05, 9: 5.630090090571395e-06, 10: 815411.5, 11: 0.0} +puddle_expected_per_cell = {'HNO3': 0.0, 'NH3': 0.0, 'CO2': 0.0, 'SO2': 0.0, 'H2O2': 0.0, 'O3': 0.0, 'S_VI': 0.0, 'H': 0.0, + 'liquid_volume': 7.087802417148837e-05, 'dry_volume': 5.630090090571395e-06, + 'particle_number': 815411.5, 'liquid_number': 815411.5, + 'ice_mass': 0.0, 'ice_number': 0.0} for a in puddle: print(puddle[a], Opts_init.nx * puddle_expected_per_cell[a]) From d532356f8adf6291b53fa3774681893ac9d9a9f1 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Thu, 4 Dec 2025 15:16:12 +0100 Subject: [PATCH 91/97] debugging for CUDA --- include/libcloudph++/common/ice_nucleation.hpp | 2 +- src/impl/particles_impl_ice_nucl_melt.ipp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index 381d9b2f2..9287b4a47 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -56,7 +56,7 @@ namespace libcloudphxx INP_type, rd2_insol, rand - ).value(); + ) / si::kelvin; } }; diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp index d1064173a..2e6898354 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -37,7 +37,7 @@ namespace libcloudphxx if (rw2 > real_t(0) && T_freeze >= T && RH >= real_t(1)) // condition for freezing { - rho_i = common::moist_air::rho_i().value(); + rho_i = common::moist_air::rho_i() * si::cubic_metres / si::kilograms; a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); c = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); rw2 = real_t(0); @@ -75,7 +75,7 @@ namespace libcloudphxx if (rw2 > real_t(0) && u01 < common::ice_nucleation::p_freeze(common::ice_nucleation::INP_t::mineral, rd2_insol, T, dt)) { - rho_i = common::moist_air::rho_i().value(); + rho_i = common::moist_air::rho_i() * si::cubic_metres / si::kilograms; a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); c = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); rw2 = real_t(0); From 30c83a929d5df1f3a042bac9ad3dbe466be1d21b Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Fri, 5 Dec 2025 16:53:57 +0100 Subject: [PATCH 92/97] time dep homogeneous freezing --- .../libcloudph++/common/ice_nucleation.hpp | 31 +++++++++++++++---- src/impl/particles_impl_ice_nucl_melt.ipp | 2 +- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/include/libcloudph++/common/ice_nucleation.hpp b/include/libcloudph++/common/ice_nucleation.hpp index 9287b4a47..7c11f63e6 100644 --- a/include/libcloudph++/common/ice_nucleation.hpp +++ b/include/libcloudph++/common/ice_nucleation.hpp @@ -60,12 +60,14 @@ namespace libcloudphxx } }; - // Probability of time-dependent freezing as in Arabas et al., 2025 + // Probability of time-dependent freezing, + // heterogeneous as in Arabas et al., 2025 and homogeneous as in Koop & Murray, 2016 template BOOST_GPU_ENABLED real_t p_freeze( const INP_t& INP_type, // type of ice nucleating particle const real_t rd2_insol, // radius squared of insoluble particle in m^2 + const real_t rw2, // wet radius squared in m^2 const real_t T, // temperature in kelvin const real_t dt // time step in seconds ) @@ -82,14 +84,29 @@ namespace libcloudphxx real_t d_aw = real_t(1) - const_cp::p_vsi(T * si::kelvin)/ const_cp::p_vs(T * si::kelvin); // water activity if (INP_type == INP_t::mineral) { - real_t J = pow(real_t(10), real_t(-1.35) + real_t(22.62) * d_aw) * real_t(1e4); // nucleation rate - return 1 - exp(- J * A * dt); + real_t J_het = pow(real_t(10), real_t(-1.35) + real_t(22.62) * d_aw) * real_t(1e4); // nucleation rate + return 1 - exp(- J_het * A * dt); } else return real_t(0.); // TODO: other INP types } else - return T > real_t(235.15) ? real_t(0) : real_t(1); // homogeneous freezing at -38 C + { + real_t V = real_t(4/3) + #if !defined(__NVCC__) + * pi() + #else + * CUDART_PI + #endif + * pow(rw2, real_t(3/2)); // droplet volume + + real_t dT = T - real_t(273.15); + real_t x = - real_t(3020.684) - real_t(425.921)*pow(dT,real_t(1)) - real_t(25.9779)*pow(dT,real_t(2)) + - real_t(0.868451)*pow(dT,real_t(3)) - real_t(0.0166203)*pow(dT,real_t(4)) + - real_t(0.000171736)*pow(dT,real_t(5)) - real_t(0.000000746953)*pow(dT,real_t(6)); + real_t J_hom = pow(real_t(10), x) * real_t(1e6); // nucleation rate + return 1 - exp(- J_hom * V * dt); + } } @@ -103,14 +120,16 @@ namespace libcloudphxx : INP_type(INP_type), dt(dt) {} BOOST_GPU_ENABLED - real_t operator()(const thrust::tuple &tpl) const + real_t operator()(const thrust::tuple &tpl) const { const real_t &rd2_insol = thrust::get<0>(tpl); // radius squared of insoluble particle - const real_t &T = thrust::get<1>(tpl); // temperature in kelvin + const real_t &rw2 = thrust::get<1>(tpl); // wet radius squared + const real_t &T = thrust::get<2>(tpl); // temperature in kelvin return ice_nucleation::p_freeze( INP_type, rd2_insol, + rw2, T, dt ); diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/particles_impl_ice_nucl_melt.ipp index 2e6898354..81bb0bd67 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/particles_impl_ice_nucl_melt.ipp @@ -73,7 +73,7 @@ namespace libcloudphxx const real_t u01 = thrust::get<5>(tpl); const real_t T = thrust::get<6>(tpl); - if (rw2 > real_t(0) && u01 < common::ice_nucleation::p_freeze(common::ice_nucleation::INP_t::mineral, rd2_insol, T, dt)) + if (rw2 > real_t(0) && u01 < common::ice_nucleation::p_freeze(common::ice_nucleation::INP_t::mineral, rd2_insol, rw2, T, dt)) { rho_i = common::moist_air::rho_i() * si::cubic_metres / si::kilograms; a = pow(rw2, real_t(0.5)) * pow(common::moist_air::rho_w() / common::moist_air::rho_i(), real_t(1./3.)); From f482ed078404b9f5d830dc37390a4fb5812b7c19 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Mon, 8 Dec 2025 14:50:44 +0100 Subject: [PATCH 93/97] ice a,c rng --- include/libcloudph++/lgrngn/particles.hpp | 12 ++++++++++ src/particles_diag.ipp | 28 +++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/libcloudph++/lgrngn/particles.hpp b/include/libcloudph++/lgrngn/particles.hpp index cb784e5cb..e06dcdec6 100644 --- a/include/libcloudph++/lgrngn/particles.hpp +++ b/include/libcloudph++/lgrngn/particles.hpp @@ -84,6 +84,8 @@ namespace libcloudphxx virtual void diag_RH_ge_Sc() { assert(false); } virtual void diag_dry_rng(const real_t&, const real_t&) { assert(false); } virtual void diag_wet_rng(const real_t&, const real_t&) { assert(false); } + virtual void diag_ice_a_rng(const real_t&, const real_t&) { assert(false); } + virtual void diag_ice_c_rng(const real_t&, const real_t&) { assert(false); } virtual void diag_kappa_rng(const real_t&, const real_t&) { assert(false); } virtual void diag_ice() { assert(false); } virtual void diag_water() { assert(false); } @@ -96,6 +98,8 @@ namespace libcloudphxx // and Boost Python does not work well with virtual member functions that have default arguments virtual void diag_dry_rng_cons(const real_t&, const real_t&) { assert(false); } virtual void diag_wet_rng_cons(const real_t&, const real_t&) { assert(false); } + virtual void diag_ice_a_rng_cons(const real_t&, const real_t&) { assert(false); } + virtual void diag_ice_c_rng_cons(const real_t&, const real_t&) { assert(false); } virtual void diag_kappa_rng_cons(const real_t&, const real_t&) { assert(false); } virtual void diag_ice_cons() { assert(false); } virtual void diag_water_cons() { assert(false); } @@ -185,11 +189,15 @@ namespace libcloudphxx void diag_RH(); void diag_dry_rng(const real_t &r_mi, const real_t &r_mx); void diag_wet_rng(const real_t &r_mi, const real_t &r_mx); + void diag_ice_a_rng(const real_t &a_mi, const real_t &a_mx); + void diag_ice_c_rng(const real_t &c_mi, const real_t &c_mx); void diag_kappa_rng(const real_t &r_mi, const real_t &r_mx); void diag_ice(); void diag_water(); void diag_dry_rng_cons(const real_t &r_mi, const real_t &r_mx); void diag_wet_rng_cons(const real_t &r_mi, const real_t &r_mx); + void diag_ice_a_rng_cons(const real_t &a_mi, const real_t &a_mx); + void diag_ice_c_rng_cons(const real_t &c_mi, const real_t &c_mx); void diag_kappa_rng_cons(const real_t &r_mi, const real_t &r_mx); void diag_ice_cons(); void diag_water_cons(); @@ -289,11 +297,15 @@ namespace libcloudphxx void diag_RH(); void diag_dry_rng(const real_t &r_mi, const real_t &r_mx); void diag_wet_rng(const real_t &r_mi, const real_t &r_mx); + void diag_ice_a_rng(const real_t &a_mi, const real_t &a_mx); + void diag_ice_c_rng(const real_t &c_mi, const real_t &c_mx); void diag_kappa_rng(const real_t &r_mi, const real_t &r_mx); void diag_ice(); void diag_water(); void diag_dry_rng_cons(const real_t &r_mi, const real_t &r_mx); void diag_wet_rng_cons(const real_t &r_mi, const real_t &r_mx); + void diag_ice_a_rng_cons(const real_t &a_mi, const real_t &a_mx); + void diag_ice_c_rng_cons(const real_t &c_mi, const real_t &c_mx); void diag_kappa_rng_cons(const real_t &r_mi, const real_t &r_mx); void diag_ice_cons(); void diag_water_cons(); diff --git a/src/particles_diag.ipp b/src/particles_diag.ipp index 6b8ddc25f..3a08fef37 100644 --- a/src/particles_diag.ipp +++ b/src/particles_diag.ipp @@ -228,6 +228,20 @@ namespace libcloudphxx pimpl->moms_rng(pow(r_min, 2), pow(r_max, 2), pimpl->rw2.begin(), false); } + // selects particles with (ice_a >= a_min && ice_a < a_max) + template + void particles_t::diag_ice_a_rng(const real_t &a_min, const real_t &a_max) + { + pimpl->moms_rng(a_min, a_max, pimpl->ice_a.begin(), false); + } + + // selects particles with (ice_c >= c_min && ice_c < c_max) + template + void particles_t::diag_ice_c_rng(const real_t &c_min, const real_t &c_max) + { + pimpl->moms_rng(c_min, c_max, pimpl->ice_c.begin(), false); + } + // selects particles with (kpa >= kpa_min && kpa < kpa_max) template void particles_t::diag_kappa_rng(const real_t &kpa_min, const real_t &kpa_max) @@ -269,6 +283,20 @@ namespace libcloudphxx pimpl->moms_rng(pow(r_min, 2), pow(r_max, 2), pimpl->rw2.begin(), true); } + // selects particles with (ice_a >= a_min && ice_a < a_max) from particles previously selected + template + void particles_t::diag_ice_a_rng_cons(const real_t &a_min, const real_t &a_max) + { + pimpl->moms_rng(a_min, a_max, pimpl->ice_a.begin(), true); + } + + // selects particles with (ice_c >= c_min && ice_c < c_max) from particles previously selected + template + void particles_t::diag_ice_c_rng_cons(const real_t &c_min, const real_t &c_max) + { + pimpl->moms_rng(c_min, c_max, pimpl->ice_c.begin(), true); + } + // selects particles with (kpa >= kpa_min && kpa < kpa_max) from particles previously selected template void particles_t::diag_kappa_rng_cons(const real_t &kpa_min, const real_t &kpa_max) From 7a9e0091e6a61edd12b1246c00ed2fab574d6a74 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Mon, 8 Dec 2025 15:42:04 +0100 Subject: [PATCH 94/97] precip rate ice --- include/libcloudph++/lgrngn/particles.hpp | 3 ++ src/particles_diag.ipp | 36 ++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/include/libcloudph++/lgrngn/particles.hpp b/include/libcloudph++/lgrngn/particles.hpp index e06dcdec6..0da464b51 100644 --- a/include/libcloudph++/lgrngn/particles.hpp +++ b/include/libcloudph++/lgrngn/particles.hpp @@ -112,6 +112,7 @@ namespace libcloudphxx virtual void diag_wet_mass_dens(const real_t&, const real_t&) { assert(false); } virtual void diag_chem(const enum common::chem::chem_species_t&) { assert(false); } virtual void diag_precip_rate() { assert(false); } + virtual void diag_precip_rate_ice_mass() { assert(false); } virtual void diag_kappa_mom(const int&) { assert(false); } virtual void diag_up_mom(const int&) { assert(false); } virtual void diag_vp_mom(const int&) { assert(false); } @@ -218,6 +219,7 @@ namespace libcloudphxx void diag_RH_ge_Sc(); void diag_all(); void diag_precip_rate(); + void diag_precip_rate_ice_mass(); void diag_max_rw(); void diag_vel_div(); std::map diag_puddle(); @@ -328,6 +330,7 @@ namespace libcloudphxx void diag_RH_ge_Sc(); void diag_all(); void diag_precip_rate(); + void diag_precip_rate_ice_mass(); void diag_max_rw(); void diag_vel_div(); std::map diag_puddle(); diff --git a/src/particles_diag.ipp b/src/particles_diag.ipp index 3a08fef37..0171a6299 100644 --- a/src/particles_diag.ipp +++ b/src/particles_diag.ipp @@ -74,6 +74,24 @@ namespace libcloudphxx } }; + template + struct precip_rate_ice + { + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) // tpl is a tuple (a, c, rho, vt) + { + return real_t(4./3) + #if !defined(__NVCC__) + * pi() + #else + * CUDART_PI + #endif + * thrust::get<0>(tpl) * thrust::get<0>(tpl) * thrust::get<1>(tpl) // a^2 * c + * thrust::get<2>(tpl) // rho + * thrust::get<3>(tpl); // vt + } + }; + template struct RH_minus_Sc { @@ -545,7 +563,23 @@ namespace libcloudphxx // copy back stored vterm thrust::copy(tmp_vt.begin(), tmp_vt.end(), pimpl->vt.begin()); - } + } + + // compute 1st (non-specific) moment of ice_mass * vt of all SDs + template + void particles_t::diag_precip_rate_ice_mass() + { + // updating terminal velocities + pimpl->hskpng_vterm_all(); + + pimpl->moms_all(); // we need this here, because hskpng_vterm modifies tmp_device_real_part, which is used as n_filtered in moms_calc + pimpl->moms_calc(thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple(pimpl->ice_a.begin(), pimpl->ice_c.begin(), + pimpl->ice_rho.begin(), pimpl->vt.begin())), + detail::precip_rate_ice() + ), + real_t(1), false); + } // get max rw in each cell template From 1d9a6e39980dc6be929754a3db0a489db4528858 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Mon, 8 Dec 2025 17:03:38 +0100 Subject: [PATCH 95/97] code cleanup --- include/libcloudph++/lgrngn/opts.hpp | 4 ++-- include/libcloudph++/lgrngn/opts_init.hpp | 24 +++-------------------- tests/python/unit/api_lgrngn.py | 4 ++-- tests/python/unit/multiple_kappas.py | 1 - 4 files changed, 7 insertions(+), 26 deletions(-) diff --git a/include/libcloudph++/lgrngn/opts.hpp b/include/libcloudph++/lgrngn/opts.hpp index d01181769..8bf6b59f9 100644 --- a/include/libcloudph++/lgrngn/opts.hpp +++ b/include/libcloudph++/lgrngn/opts.hpp @@ -32,10 +32,10 @@ namespace libcloudphxx // overriding dt from opts_init real_t dt; - // aerosol source distro per unit time + // aerosol source dry sizes defined with a distribution, number of SD and supstp src_dry_distros_t src_dry_distros; - // dry sizes of droplets added from the source + // aerosol source dry sizes defined with a size-number pair, number of SD and supstp src_dry_sizes_t src_dry_sizes; // ctor with defaults (C++03 compliant) ... diff --git a/include/libcloudph++/lgrngn/opts_init.hpp b/include/libcloudph++/lgrngn/opts_init.hpp index 5676af2dc..7bb700772 100644 --- a/include/libcloudph++/lgrngn/opts_init.hpp +++ b/include/libcloudph++/lgrngn/opts_init.hpp @@ -26,22 +26,10 @@ namespace libcloudphxx template struct opts_init_t { - // initial dry sizes of aerosol - // defined with a distribution - // uses shared_ptr to make opts_init copyable -// typedef std::unordered_map< -// std::pair, // (kappa, ice) -// std::shared_ptr> // n(ln(rd)) @ STP; alternatively it's n(ln(rd)) independent of rhod if aerosol_independent_of_rhod=true -// > dry_distros_t; + // initial dry sizes of aerosol defined with a distribution dry_distros_t dry_distros; -// // defined with a size-number pair -// typedef std::map< -// std::pair, // (kappa, ice) -// std::map // STP_concentration [1/m^3], number of SD that represent this radius kappa and concentration -// > -// > dry_sizes_t; + // initial dry sizes of aerosol defined with a size-number pair dry_sizes_t dry_sizes; // Eulerian component parameters @@ -100,7 +88,7 @@ namespace libcloudphxx turb_coal_switch, // if true, turbulent coalescence kernels can be used ice_switch, // if true, ice is allowed exact_sstp_cond, // if true, use per-particle sstp_cond logic, if false, use per-cell - time_dep_ice_nucl; // it true, time-dependent immersion freezing, if false, singular immersion freezing + time_dep_ice_nucl; // it true, time-dependent freezing, if false, singular freezing int sstp_chem; real_t chem_rho; @@ -147,16 +135,10 @@ namespace libcloudphxx // type of CCN source src_t src_type; - // number of SDs created from src_dry_distros per cell per source iteration - //unsigned long long src_sd_conc; - // box in which aerosol from source will be created // will be rounded to cell number - cells are supposed to be uniform real_t src_x0, src_y0, src_z0, src_x1, src_y1, src_z1; - // timestep interval at which source will be applied - //int supstp_src; - // --- aerosol relaxation stuff --- // initial dry sizes of aerosol // defined with a distribution diff --git a/tests/python/unit/api_lgrngn.py b/tests/python/unit/api_lgrngn.py index a6548ece4..4da852d4c 100644 --- a/tests/python/unit/api_lgrngn.py +++ b/tests/python/unit/api_lgrngn.py @@ -36,9 +36,7 @@ def lognormal(lnr): opts_init.rng_seed = 396 opts_init.rng_seed_init = 456 opts_init.rng_seed_init_switch = True -opts_init.src_dry_distros = {kappa1:lognormal} opts_init.rlx_dry_distros = {kappa1:[lognormal, [0,2], [0,opts_init.dz]]} -opts_init.src_sd_conc = 64 opts_init.src_z1 = opts_init.dz opts_init.diag_incloud_time = True @@ -108,6 +106,8 @@ def lognormal(lnr): print("chem_gas[SO2] = ", opts.chem_gas[lgrngn.chem_species_t.SO2]) print("chem_gas = ", opts.chem_gas) +opts.src_dry_distros = {(kappa1, rd_insol):(lognormal, 64, 1)} + # --------- test runs ----------- # ---------- diff --git a/tests/python/unit/multiple_kappas.py b/tests/python/unit/multiple_kappas.py index 287b751c2..5f332fbac 100644 --- a/tests/python/unit/multiple_kappas.py +++ b/tests/python/unit/multiple_kappas.py @@ -42,7 +42,6 @@ def check_kappa_conc(prtcls, eps): opts_init.sd_conc = 64 opts_init.n_sd_max = 512 opts_init.rng_seed = 396 -opts_init.src_sd_conc = 64 opts_init.src_z1 = opts_init.dz opts_init.sedi_switch = False From 88669538a17504b188bd64a6a31f3dab7feef279 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 9 Dec 2025 13:15:02 +0100 Subject: [PATCH 96/97] python path --- bindings/python/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 5e3905ba8..ddcbc64e2 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -93,6 +93,6 @@ set_property(TARGET cloudphxx PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE) # Python_SITEARCH is an absolute path, we manually prepend install prefix install ( TARGETS cloudphxx LIBRARY - DESTINATION "${Python3_SITEARCH}" + DESTINATION "${CMAKE_INSTALL_PREFIX}${Python3_SITEARCH}" COMPONENT library ) From 5d4b07c7d28f1265692c380fd0be511d86c20290 Mon Sep 17 00:00:00 2001 From: AgnieszkaMakulska Date: Tue, 9 Dec 2025 15:04:34 +0100 Subject: [PATCH 97/97] move deposition to separate file --- src/impl/particles_impl.ipp | 5 +- src/impl/particles_impl_bcnd.ipp | 9 +- src/impl/particles_impl_cond.ipp | 351 ++++++------------ src/impl/particles_impl_cond_sstp.ipp | 105 ++---- src/impl/particles_impl_ice_dep.ipp | 150 ++++++++ src/impl/particles_impl_init_sanity_check.ipp | 8 - src/impl/particles_impl_src_dry_sizes.ipp | 5 - src/particles.tpp | 1 + src/particles_step.ipp | 4 + 9 files changed, 306 insertions(+), 332 deletions(-) create mode 100644 src/impl/particles_impl_ice_dep.ipp diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index 5a6f19d37..7f0f8e358 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -644,8 +644,9 @@ namespace libcloudphxx void cond_dm3_helper(); void cond(const real_t &dt, const real_t &RH_max, const bool turb_cond, const int step); void cond_sstp(const real_t &dt, const real_t &RH_max, const bool turb_cond, const int step); - template - void cond_sstp_hlpr(const real_t &dt, const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, const RH_iter &rhi, const RHi_iter &rhii); + void ice_dep(const real_t &dt, const real_t &RH_max, const int step); + template + void cond_sstp_hlpr(const real_t &dt, const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, const RH_iter &rhi); void update_th_rv(thrust_device::vector &, phase_change = phase_change::condensation); void update_th_freezing(thrust_device::vector &); void update_state(thrust_device::vector &, thrust_device::vector &); diff --git a/src/impl/particles_impl_bcnd.ipp b/src/impl/particles_impl_bcnd.ipp index d3119b9a9..4080a653e 100644 --- a/src/impl/particles_impl_bcnd.ipp +++ b/src/impl/particles_impl_bcnd.ipp @@ -320,14 +320,7 @@ namespace libcloudphxx thrust::plus() ); } - - /* - output_puddle[common::outliq_num] += - thrust::reduce( - n_filtered.begin(), // input start - n_filtered.begin() + n_part // input end - ); - */ + if(opts_init.chem_switch) { diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/particles_impl_cond.ipp index 882219495..8df6b7e5b 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/particles_impl_cond.ipp @@ -24,262 +24,133 @@ namespace libcloudphxx hskpng_sort(); + // Vector to store 3rd moment + auto drv_liq_g = tmp_device_real_cell.get_guard(); + thrust_device::vector &drv_liq = drv_liq_g.get(); + if(step == 0) + reset_guardp(rw_mom3_gp, tmp_device_real_cell); + thrust_device::vector &rw_mom3 = rw_mom3_gp->get(); + + // Compute per-cell 3rd moment of liquid droplets before condensation. It is stored in count_mom + if(step == 0) { - // Vector to store 3rd moment - auto drv_liq_g = tmp_device_real_cell.get_guard(); - thrust_device::vector &drv_liq = drv_liq_g.get(); - if(step == 0) - reset_guardp(rw_mom3_gp, tmp_device_real_cell); - thrust_device::vector &rw_mom3 = rw_mom3_gp->get(); - - // Compute per-cell 3rd moment of liquid droplets before condensation. It is stored in count_mom - if(step == 0) - { - moms_gt0(rw2.begin()); // choose liquid particles (rw2>0) - moms_calc(rw2.begin(), real_t(3./2.)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) before condensation"); + moms_gt0(rw2.begin()); // choose liquid particles (rw2>0) + moms_calc(rw2.begin(), real_t(3./2.)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) before condensation"); + + // fill with 0s if not all cells have particles + if(count_n!=n_cell) { + thrust::fill(drv_liq.begin(), drv_liq.end(), real_t(0.)); + thrust::fill(rw_mom3.begin(), rw_mom3.end(), real_t(0.)); + } - // fill with 0s if not all cells have particles - if(count_n!=n_cell) { - thrust::fill(drv_liq.begin(), drv_liq.end(), real_t(0.)); - thrust::fill(rw_mom3.begin(), rw_mom3.end(), real_t(0.)); - } + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, + thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), + thrust::negate() + ); + } + else // copy rw_mom3 from previous step + { + // drv = -rw_mom3 precond + thrust::transform( + rw_mom3.begin(), rw_mom3.end(), + drv_liq.begin(), + thrust::negate() + ); + } - thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, - thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), - thrust::negate() - ); - } - else // copy rw_mom3 from previous step - { - // drv = -rw_mom3 precond - thrust::transform( - rw_mom3.begin(), rw_mom3.end(), - drv_liq.begin(), - thrust::negate() - ); - } + auto hlpr_zip_iter = thrust::make_zip_iterator(thrust::make_tuple( + thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), + thrust::make_permutation_iterator(rv.begin(), ijk.begin()), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(eta.begin(), ijk.begin()), + rd3.begin(), + kpa.begin(), + vt.begin(), + thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), + thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) + )); + + // calculating drop growth in a timestep using backward Euler + // TODO: both calls almost identical, use std::bind or sth? + if(turb_cond) + { + auto RH_plus_ssp_g = tmp_device_real_part.get_guard(); + thrust_device::vector &RH_plus_ssp = RH_plus_ssp_g.get(); + thrust::transform( + ssp.begin(), ssp.end(), + thrust::make_permutation_iterator(RH.begin(), ijk.begin()), + RH_plus_ssp.begin(), + arg::_1 + arg::_2 + ); + + // condensation for liquid droplets + thrust::transform( + rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() + thrust::make_zip_iterator( // input - 2nd arg + thrust::make_tuple( + hlpr_zip_iter, + thrust::make_permutation_iterator(p.begin(), ijk.begin()), + RH_plus_ssp.begin() + ) + ), + rw2.begin(), // output + detail::advance_rw2(dt, RH_max) + ); + } + else + { + // condensation for liquid droplets + thrust::transform( + rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() + thrust::make_zip_iterator( // input - 2nd arg + thrust::make_tuple( + hlpr_zip_iter, + thrust::make_permutation_iterator(p.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH.begin(), ijk.begin()) + ) + ), + rw2.begin(), // output + detail::advance_rw2(dt, RH_max) + ); + nancheck(rw2, "rw2 after condensation (no sub-steps"); + } - auto hlpr_zip_iter = thrust::make_zip_iterator(thrust::make_tuple( - thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(rv.begin(), ijk.begin()), - thrust::make_permutation_iterator(T.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()), - rd3.begin(), - kpa.begin(), - vt.begin(), - thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), - thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) - )); + // Compute per-cell 3rd moment of liquid droplets after condensation. It is stored in count_mom + moms_gt0(rw2.begin()); // choose liquid particles (rw2>0) + moms_calc(rw2.begin(), real_t(3./2.)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) after condensation"); - // calculating drop growth in a timestep using backward Euler - // TODO: both calls almost identical, use std::bind or sth? - if(turb_cond) + // Adding the third liquid moment after condensation to drv_liq + if(step < sstp_cond - 1) { - auto RH_plus_ssp_g = tmp_device_real_part.get_guard(); - thrust_device::vector &RH_plus_ssp = RH_plus_ssp_g.get(); - thrust::transform( - ssp.begin(), ssp.end(), - thrust::make_permutation_iterator(RH.begin(), ijk.begin()), - RH_plus_ssp.begin(), - arg::_1 + arg::_2 + thrust::copy( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(rw_mom3.begin(), count_ijk.begin()) // output ); - // condensation for liquid droplets + // adding the third moment after condensation to dm_3 thrust::transform( - rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() - thrust::make_zip_iterator( // input - 2nd arg - thrust::make_tuple( - hlpr_zip_iter, - thrust::make_permutation_iterator(p.begin(), ijk.begin()), - RH_plus_ssp.begin() - ) - ), - rw2.begin(), // output - detail::advance_rw2(dt, RH_max) + rw_mom3.begin(), rw_mom3.end(), + drv_liq.begin(), + drv_liq.begin(), + thrust::plus() ); } - else + else // last step, calculate change in 3rd moment and update th and rv { - // condensation for liquid droplets thrust::transform( - rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() - thrust::make_zip_iterator( // input - 2nd arg - thrust::make_tuple( - hlpr_zip_iter, - thrust::make_permutation_iterator(p.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH.begin(), ijk.begin()) - ) - ), - rw2.begin(), // output - detail::advance_rw2(dt, RH_max) + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // input - 2nd arg + thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // output + thrust::plus() ); - nancheck(rw2, "rw2 after condensation (no sub-steps"); + rw_mom3_gp.reset(); // destroy guard to tmp array that stored 3rd moment of rw } - // Compute per-cell 3rd moment of liquid droplets after condensation. It is stored in count_mom - moms_gt0(rw2.begin()); // choose liquid particles (rw2>0) - moms_calc(rw2.begin(), real_t(3./2.)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd wet moment) after condensation"); - - // Adding the third liquid moment after condensation to drv_liq - if(step < sstp_cond - 1) - { - thrust::copy( - count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - thrust::make_permutation_iterator(rw_mom3.begin(), count_ijk.begin()) // output - ); - - // adding the third moment after condensation to dm_3 - thrust::transform( - rw_mom3.begin(), rw_mom3.end(), - drv_liq.begin(), - drv_liq.begin(), - thrust::plus() - ); - } - else // last step, calculate change in 3rd moment and update th and rv - { - thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // input - 2nd arg - thrust::make_permutation_iterator(drv_liq.begin(), count_ijk.begin()), // output - thrust::plus() - ); - rw_mom3_gp.reset(); // destroy guard to tmp array that stored 3rd moment of rw - } - - // update th and rv according to change in third specific wet moment - update_th_rv(drv_liq, impl::phase_change::condensation); - } - - - if (opts_init.ice_switch) - { - // Vector to store 3rd moment - auto drv_ice_g = tmp_device_real_cell.get_guard(); - thrust_device::vector &drv_ice = drv_ice_g.get(); - if(step == 0) - reset_guardp(ice_mass_gp, tmp_device_real_cell); - thrust_device::vector &ice_mass = ice_mass_gp->get(); - - // Compute per-cell 3rd moment of ice before deposition. It is stored in count_mom - if(step == 0) - { - moms_gt0(ice_a.begin()); // choose ice particles (ice_a>0) - moms_calc(thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin(), ice_rho.begin())), - detail::ice_mass() - ), - real_t(1)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) before deposition"); - - // fill with 0s if not all cells have particles - if(count_n!=n_cell) { - thrust::fill(drv_ice.begin(), drv_ice.end(), real_t(0.)); - thrust::fill(ice_mass.begin(), ice_mass.end(), real_t(0.)); - } - - thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, - thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), - thrust::negate() - ); - } - else // copy ice_mass from previous step - { - // drv = -ice_mass precond - thrust::transform( - ice_mass.begin(), ice_mass.end(), - drv_ice.begin(), - thrust::negate() - ); - } - - auto hlpr_zip_iter = thrust::make_zip_iterator(thrust::make_tuple( - thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), - thrust::make_permutation_iterator(rv.begin(), ijk.begin()), - thrust::make_permutation_iterator(T.begin(), ijk.begin()), - thrust::make_permutation_iterator(eta.begin(), ijk.begin()), - rd3.begin(), - kpa.begin(), - vt.begin(), - thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), - thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) - )); - - // deposition for ice crystals - thrust::transform( - thrust::make_zip_iterator( - thrust::make_tuple( - ice_a.begin(), - ice_c.begin() - ) - ), - thrust::make_zip_iterator( - thrust::make_tuple( - ice_a.end(), - ice_c.end() - ) - ), - thrust::make_zip_iterator( - thrust::make_tuple( - hlpr_zip_iter, - thrust::make_permutation_iterator(p.begin(), ijk.begin()), - thrust::make_permutation_iterator(RH_i.begin(), ijk.begin()) - ) - ), - thrust::make_zip_iterator( - thrust::make_tuple( - ice_a.begin(), - ice_c.begin() - ) - ), - detail::advance_ice_ac(dt, RH_max) - ); - nancheck(ice_a, "ice_a after deposition (no sub-steps"); - nancheck(ice_c, "ice_c after deposition (no sub-steps"); - - // Compute per-cell 3rd moment of ice after deposition. It is stored in count_mom - moms_gt0(ice_a.begin()); // choose ice particles (ice_a>0) - moms_calc(thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin(), ice_rho.begin())), - detail::ice_mass() - ), - real_t(1)); - nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) after deposition"); - - // Adding the ice volume after deposition to drv_ice - if(step < sstp_cond - 1) - { - thrust::copy( - count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - thrust::make_permutation_iterator(ice_mass.begin(), count_ijk.begin()) // output - ); - - // adding the third moment after deposition to ice_mass - thrust::transform( - ice_mass.begin(), ice_mass.end(), - drv_ice.begin(), - drv_ice.begin(), - thrust::plus() - ); - } - else // last step, calculate change in 3rd moment and update th and rv - { - thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // input - 2nd arg - thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // output - thrust::plus() - ); - ice_mass_gp.reset(); // destroy guard to tmp array that stored ice_mass - } - - // update th and rv according to change in third specific moment - update_th_rv(drv_ice, impl::phase_change::deposition); - } + // update th and rv according to change in third specific wet moment + update_th_rv(drv_liq, impl::phase_change::condensation); } }; } \ No newline at end of file diff --git a/src/impl/particles_impl_cond_sstp.ipp b/src/impl/particles_impl_cond_sstp.ipp index 72dbc4f6d..a0a9ff306 100644 --- a/src/impl/particles_impl_cond_sstp.ipp +++ b/src/impl/particles_impl_cond_sstp.ipp @@ -20,8 +20,8 @@ namespace libcloudphxx resolved_RH(RH_formula) {} - BOOST_GPU_ENABLED - real_t operator()(const thrust::tuple &tpl) + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) { return resolved_RH(thrust::make_tuple(thrust::get<0>(tpl), thrust::get<1>(tpl), thrust::get<2>(tpl))) + thrust::get<3>(tpl); } @@ -29,17 +29,16 @@ namespace libcloudphxx }; template - template + template void particles_t::impl::cond_sstp_hlpr( const real_t &dt, const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, - const RH_iter &rhi, - const RHi_iter &rhii - ) { - thrust_device::vector &lambda_D(lambda_D_gp->get()); - thrust_device::vector &lambda_K(lambda_K_gp->get()); + const RH_iter &rhi + ) { + thrust_device::vector &lambda_D(lambda_D_gp->get()); + thrust_device::vector &lambda_K(lambda_K_gp->get()); auto hlpr_zip_iter = thrust::make_zip_iterator(thrust::make_tuple( sstp_tmp_rh.begin(), @@ -57,7 +56,7 @@ namespace libcloudphxx thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) )); - // calculating drop growth in a timestep using backward Euler + // calculating drop growth in a timestep using backward Euler thrust::transform( rw2.begin(), rw2.end(), // input - 1st arg (zip not as 1st arg not to write zip.end() thrust::make_zip_iterator(thrust::make_tuple( // input - 2nd arg @@ -66,7 +65,7 @@ namespace libcloudphxx pi, // particle-specific RH rhi - )), + )), rw2.begin(), // output detail::advance_rw2(dt, RH_max) ); @@ -79,12 +78,12 @@ namespace libcloudphxx const real_t &RH_max, const bool turb_cond, const int step - ) { + ) { namespace arg = thrust::placeholders; // prerequisite - hskpng_sort(); + hskpng_sort(); // particle's local change in rv auto pdrv_g = tmp_device_real_part.get_guard(); thrust_device::vector &pdrv = pdrv_g.get(); @@ -122,8 +121,8 @@ namespace libcloudphxx sstp_tmp_th.begin(), sstp_tmp_th.end(), // input - first arg sstp_tmp_rh.begin(), // input - second arg Tp.begin(), // output - detail::common__theta_dry__T_rhod() - ); + detail::common__theta_dry__T_rhod() + ); } else // th_std { @@ -131,16 +130,16 @@ namespace libcloudphxx thrust::transform( sstp_tmp_th.begin(), sstp_tmp_th.end(), // input - first arg thrust::make_zip_iterator(thrust::make_tuple( - sstp_tmp_rv.begin(), // input - second arg + sstp_tmp_rv.begin(), // input - second arg sstp_tmp_p.begin() // input - third arg )), Tp.begin(), // output - detail::common__theta_std__T_p() + detail::common__theta_std__T_p() ); } - // calculating drop growth in a timestep using backward Euler + // calculating drop growth in a timestep using backward Euler if(!opts_init.const_p) { // particle-specific pressure iterator, used twice @@ -155,9 +154,7 @@ namespace libcloudphxx ); if(turb_cond) - ; - /* - cond_sstp_hlpr(dt, RH_max, Tp, + cond_sstp_hlpr(dt, RH_max, Tp, // particle-specific p pressure_iter, // particle-specific RH, resolved + SGS @@ -169,11 +166,8 @@ namespace libcloudphxx ssp.begin() )), detail::RH_sgs(opts_init.RH_formula) - ), - // particle-specific RH_i, resolved + SGS - thrust::make_constant_iterator(0) // dummy, ice is not allowed with turb_cond - ); - */ + ) + ); else // no RH SGS cond_sstp_hlpr(dt, RH_max, Tp, // particle-specific p @@ -186,24 +180,12 @@ namespace libcloudphxx Tp.begin() )), detail::RH(opts_init.RH_formula) - ), - // particle-specific RH_i, resolved - thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple( - pressure_iter, - sstp_tmp_rv.begin(), - Tp.begin() - )), - detail::RH(opts_init.RH_formula) - //detail::RH_i(opts_init.RH_formula) - ) - ); + ) + ); } else // opts_init.const_p { if(turb_cond) - ; - /* cond_sstp_hlpr(dt, RH_max, Tp, // particle-specific p sstp_tmp_p.begin(), @@ -216,14 +198,9 @@ namespace libcloudphxx ssp.begin() )), detail::RH_sgs(opts_init.RH_formula) - ), - // particle-specific RH_i, resolved + SGS - thrust::make_constant_iterator(0) // dummy, ice is not allowed with turb_cond - ); - */ + ) + ); else // no RH SGS - ; - /* cond_sstp_hlpr(dt, RH_max, Tp, // particle-specific p sstp_tmp_p.begin(), @@ -235,18 +212,8 @@ namespace libcloudphxx Tp.begin() )), detail::RH(opts_init.RH_formula) - ), - // particle-specific RH_i, resolved - thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple( - sstp_tmp_p.begin(), - sstp_tmp_rv.begin(), - Tp.begin() - )), - detail::RH_i(opts_init.RH_formula) - ) - ); - */ + ) + ); } // rw3_new - rw3_old @@ -291,23 +258,23 @@ namespace libcloudphxx - common::moist_air::rho_w() / si::kilograms * si::cubic_metres * real_t(4./3) * pi(), n_dims ) - ); + ); // apply change in rv to sstp_tmp_rv update_pstate(sstp_tmp_rv, pdrv); // calc particle-specific change in th based on pdrv thrust::transform( - thrust::make_zip_iterator(thrust::make_tuple( - pdrv.begin(), // + thrust::make_zip_iterator(thrust::make_tuple( + pdrv.begin(), // Tp.begin(), // dth = drv * d_th_d_rv(T, th) - sstp_tmp_th.begin() // - )), - thrust::make_zip_iterator(thrust::make_tuple( - pdrv.end(), // + sstp_tmp_th.begin() // + )), + thrust::make_zip_iterator(thrust::make_tuple( + pdrv.end(), // Tp.end(), // dth = drv * d_th_d_rv(T, th) - sstp_tmp_th.end() // - )), + sstp_tmp_th.end() // + )), pdrv.begin(), // in-place detail::dth() ); @@ -315,5 +282,5 @@ namespace libcloudphxx // apply change in th to sstp_tmp_th update_pstate(sstp_tmp_th, pdrv); } - }; -}; + }; +}; \ No newline at end of file diff --git a/src/impl/particles_impl_ice_dep.ipp b/src/impl/particles_impl_ice_dep.ipp new file mode 100644 index 000000000..bfb41deae --- /dev/null +++ b/src/impl/particles_impl_ice_dep.ipp @@ -0,0 +1,150 @@ +// vim:filetype=cpp +/** @file + * @copyright University of Warsaw + * @section LICENSE + * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) + */ + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::ice_dep( + const real_t &dt, + const real_t &RH_max, + const int step + ) { + + namespace arg = thrust::placeholders; + + thrust_device::vector &lambda_D(lambda_D_gp->get()); + thrust_device::vector &lambda_K(lambda_K_gp->get()); + + hskpng_sort(); + + // Vector to store 3rd moment + auto drv_ice_g = tmp_device_real_cell.get_guard(); + thrust_device::vector &drv_ice = drv_ice_g.get(); + if(step == 0) + reset_guardp(ice_mass_gp, tmp_device_real_cell); + thrust_device::vector &ice_mass = ice_mass_gp->get(); + + // Compute per-cell 3rd moment of ice before deposition. It is stored in count_mom + if(step == 0) + { + moms_gt0(ice_a.begin()); // choose ice particles (ice_a>0) + moms_calc(thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin(), ice_rho.begin())), + detail::ice_mass() + ), + real_t(1)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) before deposition"); + + // fill with 0s if not all cells have particles + if(count_n!=n_cell) { + thrust::fill(drv_ice.begin(), drv_ice.end(), real_t(0.)); + thrust::fill(ice_mass.begin(), ice_mass.end(), real_t(0.)); + } + + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, + thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), + thrust::negate() + ); + } + else // copy ice_mass from previous step + { + // drv = -ice_mass precond + thrust::transform( + ice_mass.begin(), ice_mass.end(), + drv_ice.begin(), + thrust::negate() + ); + } + + auto hlpr_zip_iter = thrust::make_zip_iterator(thrust::make_tuple( + thrust::make_permutation_iterator(rhod.begin(), ijk.begin()), + thrust::make_permutation_iterator(rv.begin(), ijk.begin()), + thrust::make_permutation_iterator(T.begin(), ijk.begin()), + thrust::make_permutation_iterator(eta.begin(), ijk.begin()), + rd3.begin(), + kpa.begin(), + vt.begin(), + thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), + thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) + )); + + // deposition for ice crystals + thrust::transform( + thrust::make_zip_iterator( + thrust::make_tuple( + ice_a.begin(), + ice_c.begin() + ) + ), + thrust::make_zip_iterator( + thrust::make_tuple( + ice_a.end(), + ice_c.end() + ) + ), + thrust::make_zip_iterator( + thrust::make_tuple( + hlpr_zip_iter, + thrust::make_permutation_iterator(p.begin(), ijk.begin()), + thrust::make_permutation_iterator(RH_i.begin(), ijk.begin()) + ) + ), + thrust::make_zip_iterator( + thrust::make_tuple( + ice_a.begin(), + ice_c.begin() + ) + ), + detail::advance_ice_ac(dt, RH_max) + ); + nancheck(ice_a, "ice_a after deposition (no sub-steps"); + nancheck(ice_c, "ice_c after deposition (no sub-steps"); + + // Compute per-cell 3rd moment of ice after deposition. It is stored in count_mom + moms_gt0(ice_a.begin()); // choose ice particles (ice_a>0) + moms_calc(thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple(ice_a.begin(), ice_c.begin(), ice_rho.begin())), + detail::ice_mass() + ), + real_t(1)); + nancheck_range(count_mom.begin(), count_mom.begin() + count_n, "count_mom (3rd ice moment) after deposition"); + + // Adding the ice volume after deposition to drv_ice + if(step < sstp_cond - 1) + { + thrust::copy( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(ice_mass.begin(), count_ijk.begin()) // output + ); + + // adding the third moment after deposition to ice_mass + thrust::transform( + ice_mass.begin(), ice_mass.end(), + drv_ice.begin(), + drv_ice.begin(), + thrust::plus() + ); + } + else // last step, calculate change in 3rd moment and update th and rv + { + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // input - 2nd arg + thrust::make_permutation_iterator(drv_ice.begin(), count_ijk.begin()), // output + thrust::plus() + ); + ice_mass_gp.reset(); // destroy guard to tmp array that stored ice_mass + } + + // update th and rv according to change in third specific moment + update_th_rv(drv_ice, impl::phase_change::deposition); + } + }; + } \ No newline at end of file diff --git a/src/impl/particles_impl_init_sanity_check.ipp b/src/impl/particles_impl_init_sanity_check.ipp index e0e0c750c..a27edf1db 100644 --- a/src/impl/particles_impl_init_sanity_check.ipp +++ b/src/impl/particles_impl_init_sanity_check.ipp @@ -57,20 +57,12 @@ namespace libcloudphxx if (opts_init.chem_switch && opts_init.src_type!=src_t::off) throw std::runtime_error("libcloudph++: chemistry and aerosol source are not compatible"); - // if (opts_init.src_type!=src_t::off && opts_init.src_dry_distros.empty() && opts_init.src_dry_sizes.empty())// throw std::runtime_error("libcloudph++: CCN source enabled, but src_dry_distros and src_dry_sizes are empty"); -// -// if (opts_init.src_type!=src_t::off && opts_init.src_dry_distros.size() > 1) -// throw std::runtime_error("libcloudph++: src_dry_distros can only have a single kappa value."); - if (opts_init.src_type==src_t::matching && opts_init.dry_distros.size() > 1) throw std::runtime_error("libcloudph++: For 'matching' CCN source, the initial aerosol distribution can only have one kappa value (na kappa matching done)."); if (opts_init.src_type!=src_t::off && n_dims<2) throw std::runtime_error("libcloudph++: CCN source works in 2D and 3D only."); -// if (opts_init.src_type==src_t::matching && !opts_init.src_dry_distros.empty() && -// opts_init.src_dry_distros.begin()->first != opts_init.dry_distros.begin()->first) throw std::runtime_error("libcloudph++: For 'matching' CCN source, kappa of the source has to be the same as that of the initial profile (no kappa matching done)"); - if(opts_init.dry_distros.size() > 1 && opts_init.chem_switch) throw std::runtime_error("libcloudph++: chemistry and multiple kappa distributions are not compatible"); diff --git a/src/impl/particles_impl_src_dry_sizes.ipp b/src/impl/particles_impl_src_dry_sizes.ipp index f32dd6ad3..c3d461cbd 100644 --- a/src/impl/particles_impl_src_dry_sizes.ipp +++ b/src/impl/particles_impl_src_dry_sizes.ipp @@ -14,11 +14,6 @@ namespace libcloudphxx template void particles_t::impl::src_dry_sizes(const src_dry_sizes_t &sds) { -// using dry_sizes_t = typename opts_t::dry_sizes_t; -// using real_t = typename dry_sizes_t::key_type; -// using size_number_t = typename dry_sizes_t::mapped_type; - //using conc_multi_t = typename size_number_t::mapped_type; - // loop over (kappa, rd_insol) pairs // for (typename dry_sizes_t::const_iterator dsi = opts.src_dry_sizes.begin(); dsi != opts.src_dry_sizes.end(); ++dsi) diff --git a/src/particles.tpp b/src/particles.tpp index 213f09e74..cc085c444 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -107,6 +107,7 @@ #include "impl/particles_impl_cond_common.ipp" #include "impl/particles_impl_cond.ipp" #include "impl/particles_impl_cond_sstp.ipp" +#include "impl/particles_impl_ice_dep.ipp" #include "impl/particles_impl_ice_nucl_melt.ipp" #include "impl/particles_impl_sedi.ipp" #include "impl/particles_impl_subs.ipp" diff --git a/src/particles_step.ipp b/src/particles_step.ipp index 8097e85b1..1805d6b00 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -199,6 +199,8 @@ namespace libcloudphxx if (pimpl->opts_init.ice_switch && opts.ice_nucl) pimpl->ice_nucl_melt(pimpl->dt / pimpl->sstp_cond); pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond, step); + if (pimpl->opts_init.ice_switch) + pimpl->ice_dep(pimpl->dt / pimpl->sstp_cond, opts.RH_max, step); } // copy sstp_tmp_rv and th to rv and th pimpl->update_state(pimpl->rv, pimpl->sstp_tmp_rv); @@ -216,6 +218,8 @@ namespace libcloudphxx if (pimpl->opts_init.ice_switch && opts.ice_nucl) pimpl->ice_nucl_melt(pimpl->dt / pimpl->sstp_cond); pimpl->cond(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond, step); + if (pimpl->opts_init.ice_switch) + pimpl->ice_dep(pimpl->dt / pimpl->sstp_cond, opts.RH_max, step); } }