diff --git a/API.md b/API.md new file mode 100644 index 000000000..12ab443a6 --- /dev/null +++ b/API.md @@ -0,0 +1,1177 @@ +# libcloudph++ API Reference (generated by Claude 4.5; not checked for errors) + +This document provides a comprehensive reference for the libcloudph++ programming interface, covering the three microphysics schemes and their usage patterns. + +## Table of Contents + +1. [Overview](#overview) +2. [Lagrangian Microphysics (lgrngn)](#lagrangian-microphysics-lgrngn) +3. [Single-Moment Bulk Microphysics (blk_1m)](#single-moment-bulk-microphysics-blk_1m) +4. [Double-Moment Bulk Microphysics (blk_2m)](#double-moment-bulk-microphysics-blk_2m) +5. [Common Utilities](#common-utilities) + +--- + +## Overview + +libcloudph++ provides three main microphysics schemes: + +- **Lagrangian (lgrngn)**: Super-droplet method with detailed particle-scale processes +- **Single-moment bulk (blk_1m)**: Bulk scheme predicting mixing ratios only +- **Double-moment bulk (blk_2m)**: Bulk scheme predicting both mixing ratios and number concentrations + +All schemes use SI units with Boost.Units for type safety. Template parameter `real_t` can be `float` or `double`. + +--- + +## Lagrangian Microphysics (lgrngn) + +### Namespace + +```cpp +namespace libcloudphxx::lgrngn +``` + +### Backend Selection + +```cpp +enum backend_t { + undefined, + serial, // Single-threaded CPU + OpenMP, // Multi-threaded CPU + CUDA, // Single GPU + multi_CUDA // Multiple GPUs +}; +``` + +### Factory Function + +```cpp +template +particles_proto_t* factory( + const backend_t backend, + opts_init_t opts_init +); +``` + +**Description**: Creates a particle system instance with the specified backend. + +**Parameters**: +- `backend`: Computational backend (serial, OpenMP, CUDA, multi_CUDA) +- `opts_init`: Initialization options (see [USER_OPTIONS.md](USER_OPTIONS.md)) + +**Returns**: Pointer to `particles_proto_t` base class + +**Example**: +```cpp +#include + +// Configure initialization options +libcloudphxx::lgrngn::opts_init_t opts_init; +opts_init.dt = 1.0; // timestep [s] +opts_init.nx = 64; // grid cells in x +opts_init.ny = 64; // grid cells in y +opts_init.nz = 64; // grid cells in z +opts_init.dx = 100.0; // cell size x [m] +opts_init.dy = 100.0; // cell size y [m] +opts_init.dz = 100.0; // cell size z [m] +opts_init.sd_conc = 64; // super-droplets per cell + +// Create particle system +auto particles = libcloudphxx::lgrngn::factory( + libcloudphxx::lgrngn::CUDA, + opts_init +); +``` + +### Main Class: particles_t + +```cpp +template +struct particles_t : particles_proto_t +``` + +#### Constructor + +```cpp +particles_t(opts_init_t opts_init, int n_x_tot = 0); +``` + +**Parameters**: +- `opts_init`: Initialization options +- `n_x_tot`: Total number of cells in x-direction (for MPI, otherwise use `opts_init.nx`) + +#### Initialization + +```cpp +void init( + const arrinfo_t th, // potential temperature [K] + const arrinfo_t rv, // water vapor mixing ratio [kg/kg] + const arrinfo_t rhod, // dry air density [kg/m³] + const arrinfo_t p = arrinfo_t(), // pressure [Pa] + const arrinfo_t courant_x = arrinfo_t(), // Courant number in x + const arrinfo_t courant_y = arrinfo_t(), // Courant number in y + const arrinfo_t courant_z = arrinfo_t(), // Courant number in z + const std::map> ambient_chem = {} +); +``` + +**Description**: Initialize the particle system with atmospheric state and place super-droplets in the domain. + +**Parameters**: +- `th`, `rv`, `rhod`: Thermodynamic state arrays (required) +- `p`: Pressure field (required if not using anelastic approximation) +- `courant_x`, `courant_y`, `courant_z`: Courant numbers for advection (optional) +- `ambient_chem`: Ambient chemical species concentrations (for chemistry simulations) + +#### Time-Stepping Methods + +##### step_sync() + +```cpp +void step_sync( + const opts_t &opts, // runtime options + arrinfo_t th, // potential temperature [K] + arrinfo_t rv, // water vapor mixing ratio [kg/kg] + const arrinfo_t rhod = arrinfo_t(), // dry air density [kg/m³] + const arrinfo_t courant_x = arrinfo_t(), // Courant number in x + const arrinfo_t courant_y = arrinfo_t(), // Courant number in y + const arrinfo_t courant_z = arrinfo_t(), // Courant number in z + const arrinfo_t diss_rate = arrinfo_t(), // TKE dissipation [m²/s³] + std::map> ambient_chem = {} +); +``` + +**Description**: Perform a synchronous time step including all microphysics processes. Updates `th` and `rv` arrays to reflect latent heat release and phase changes. + +**Parameters**: +- `opts`: Runtime options controlling which processes to execute (see [USER_OPTIONS.md](USER_OPTIONS.md)) +- `th`, `rv`: Thermodynamic state (input/output) +- `rhod`: Dry air density (required for anelastic models) +- `courant_x`, `courant_y`, `courant_z`: Courant numbers for advection +- `diss_rate`: TKE dissipation rate for turbulent collision enhancement +- `ambient_chem`: Chemical species for aqueous chemistry + +**Processes** (controlled by `opts`): +- Advection (`opts.adve`) +- Condensation/evaporation (`opts.cond`) +- Coalescence (`opts.coal`) +- Sedimentation (`opts.sedi`) +- Subsidence (`opts.subs`) +- Chemistry (`opts.chem_switch`) +- Source/relaxation (`opts.src`, `opts.rlx`) + +##### sync_in() + +```cpp +void sync_in( + arrinfo_t th, + arrinfo_t rv, + const arrinfo_t rhod = arrinfo_t(), + const arrinfo_t courant_x = arrinfo_t(), + const arrinfo_t courant_y = arrinfo_t(), + const arrinfo_t courant_z = arrinfo_t(), + const arrinfo_t diss_rate = arrinfo_t(), + std::map> ambient_chem = {} +); +``` + +**Description**: Copy atmospheric state from host to device (for GPU backends) without performing microphysics. + +##### step_cond() + +```cpp +void step_cond( + const opts_t &opts, + arrinfo_t th, + arrinfo_t rv, + std::map> ambient_chem = {} +); +``` + +**Description**: Perform only condensation/evaporation step. Used for operator splitting. + +##### step_async() + +```cpp +void step_async(const opts_t &opts); +``` + +**Description**: Perform asynchronous processes (advection, coalescence, sedimentation) that don't require immediate synchronization with Eulerian fields. + +#### Diagnostic Methods + +##### Super-Droplet Concentration + +```cpp +void diag_sd_conc(); +``` + +**Description**: Calculate number of super-droplets per cell. Result available via `outbuf()`. + +##### Pressure and Temperature + +```cpp +void diag_pressure(); // Pressure [Pa] +void diag_temperature(); // Temperature [K] +``` + +**Description**: Diagnose pressure or temperature fields from current thermodynamic state. + +##### Relative Humidity + +```cpp +void diag_RH(); // Relative humidity [%] +``` + +**Description**: Calculate relative humidity field. + +##### Particle Size Range Selection + +```cpp +void diag_dry_rng(const real_t &r_min, const real_t &r_max); // dry radius [m] +void diag_wet_rng(const real_t &r_min, const real_t &r_max); // wet radius [m] +void diag_kappa_rng(const real_t &kappa_min, const real_t &kappa_max); // hygroscopicity +``` + +**Description**: Select super-droplets within specified size or hygroscopicity range for subsequent moment calculations. + +**Parameters**: +- `r_min`, `r_max`: Radius range (dry or wet) in meters +- `kappa_min`, `kappa_max`: Hygroscopicity parameter range + +##### Consecutive Range Selection + +```cpp +void diag_dry_rng_cons(const real_t &r_min, const real_t &r_max); +void diag_wet_rng_cons(const real_t &r_min, const real_t &r_max); +void diag_kappa_rng_cons(const real_t &kappa_min, const real_t &kappa_max); +``` + +**Description**: Further filter previously selected super-droplets. Use immediately after `diag_*_rng()` calls. + +**Example**: +```cpp +// Select droplets with wet radius 0.5-1 μm AND kappa 0.1-0.2 +particles->diag_wet_rng(0.5e-6, 1e-6); +particles->diag_kappa_rng_cons(0.1, 0.2); +particles->diag_wet_mom(3); // 3rd moment of filtered droplets +``` + +##### Moment Calculations + +```cpp +void diag_dry_mom(const int &k); // k-th moment of dry radius +void diag_wet_mom(const int &k); // k-th moment of wet radius +void diag_kappa_mom(const int &k); // k-th moment of hygroscopicity +void diag_incloud_time_mom(const int &k); // k-th moment of in-cloud time +``` + +**Description**: Calculate k-th moment of particle size distribution (or other attribute). Result in `outbuf()`. + +**Parameters**: +- `k`: Moment order (typically 0, 1, 2, 3 for concentration, mean radius, variance, mass) + +**Common moments**: +- k=0: Number concentration [#/m³] +- k=1: Mean radius [m] +- k=2: Variance-related +- k=3: Mass/volume concentration [m³/m³] or [kg/kg] + +##### Velocity Moments + +```cpp +void diag_up_mom(const int &k); // k-th moment of u-velocity component +void diag_vp_mom(const int &k); // k-th moment of v-velocity component +void diag_wp_mom(const int &k); // k-th moment of w-velocity component +``` + +##### Mass Density + +```cpp +void diag_wet_mass_dens(const real_t &r_min, const real_t &r_max); +``` + +**Description**: Calculate total mass density of droplets in specified wet radius range. + +##### Activation Diagnostics + +```cpp +void diag_rw_ge_rc(); // Select droplets with r_wet >= r_critical +void diag_RH_ge_Sc(); // Select droplets where RH >= critical supersaturation +void diag_all(); // Select all droplets +``` + +##### Precipitation + +```cpp +void diag_precip_rate(); // Precipitation rate [m/s] +void diag_max_rw(); // Maximum wet radius [m] +``` + +##### Velocity Divergence + +```cpp +void diag_vel_div(); // Velocity divergence [1/s] +``` + +##### Chemistry + +```cpp +void diag_chem(const enum common::chem::chem_species_t &species); +``` + +**Description**: Diagnose aqueous concentration of chemical species. + +##### Surface Accumulation (Puddle) + +```cpp +std::map diag_puddle(); +``` + +**Description**: Get accumulated rainfall at surface. + +**Returns**: Map with keys: +- `common::output_t::rl`: Total liquid water at surface +- `common::output_t::rc`: Cloud water at surface +- `common::output_t::rr`: Rain water at surface + +##### Direct Attribute Access + +```cpp +std::vector get_attr(const std::string &attr_name); +``` + +**Description**: Get raw super-droplet attribute array. + +**Parameters**: +- `attr_name`: Attribute name (e.g., "rd", "rw", "kpa", "x", "y", "z", "vt") + +**Returns**: Vector of attribute values for all super-droplets + +##### Output Buffer Access + +```cpp +real_t* outbuf(); +``` + +**Description**: Get pointer to output buffer containing most recent diagnostic result. + +**Returns**: Pointer to array with dimensions matching grid (nx × ny × nz) + +**Usage Pattern**: +```cpp +particles->diag_wet_mom(3); +real_t *result = particles->outbuf(); +// Copy result to your array +for (int i = 0; i < nx * ny * nz; ++i) { + liquid_water_content[i] = result[i]; +} +``` + +### Data Structures + +#### arrinfo_t + +```cpp +template +struct arrinfo_t { + real_t * const data; // Pointer to array data + const ptrdiff_t *strides; // Array strides for multidimensional indexing + const std::vector strvec; // Optional: local storage for strides + + // Constructors + arrinfo_t(); // Default: null array + arrinfo_t(real_t *data, const ptrdiff_t *strides); + arrinfo_t(real_t *data, const std::vector &strvec); + + // Methods + bool is_null() const; +}; +``` + +**Description**: Helper structure for passing multi-dimensional arrays to libcloudph++. + +**Parameters**: +- `data`: Pointer to contiguous or strided array +- `strides`: Array of stride values for each dimension (in units of `real_t`) +- `strvec`: Alternative: vector for local stride storage + +**Example**: +```cpp +// 1D array +std::vector arr_1d(nx); +arrinfo_t info_1d(arr_1d.data(), nullptr); + +// 3D array with strides +double *arr_3d = new double[nx * ny * nz]; +ptrdiff_t strides[3] = {1, nx, nx*ny}; +arrinfo_t info_3d(arr_3d, strides); + +// Using vector for strides +std::vector str = {1, nx, nx*ny}; +arrinfo_t info_3d_v(arr_3d, str); +``` + +#### opts_init_t + +See [USER_OPTIONS.md](USER_OPTIONS.md#lagrangian-lgrngn-initialization-options) for complete documentation of initialization options including: +- Domain configuration (nx, ny, nz, dx, dy, dz) +- Super-droplet configuration (sd_conc, sd_conc_mean) +- Aerosol size distributions (dry_distros, dry_sizes) +- Chemistry options (chem_switch, chem_rho) +- GPU settings (dev_count, dev_id) + +#### opts_t + +See [USER_OPTIONS.md](USER_OPTIONS.md#lagrangian-lgrngn-runtime-options) for runtime options controlling: +- Process switches (adve, sedi, cond, coal, chem_switch) +- Timestep substepping (sstp_cond, sstp_coal, sstp_chem) +- Boundary conditions (open_side_walls, periodic_topbot) + +--- + +## Single-Moment Bulk Microphysics (blk_1m) + +### Namespace + +```cpp +namespace libcloudphxx::blk_1m +``` + +### Overview + +The single-moment bulk scheme predicts mixing ratios for: +- **rc**: Cloud water mixing ratio [kg/kg] +- **rr**: Rain water mixing ratio [kg/kg] +- **ri**: Ice mixing ratio [kg/kg] +- **rs**: Snow mixing ratio [kg/kg] +- **rg**: Graupel mixing ratio [kg/kg] + +### Saturation Adjustment + +#### adj_cellwise_nwtrph() + +```cpp +template +void adj_cellwise_nwtrph( + const opts_t &opts, + const cont_t &rhod_cont, // dry air density [kg/m³] + const cont_t &p_cont, // pressure [Pa] + cont_t &th_cont, // potential temperature [K] (input/output) + cont_t &rv_cont, // water vapor mixing ratio [kg/kg] (input/output) + cont_t &rc_cont, // cloud water mixing ratio [kg/kg] (input/output) + const real_t &dt // timestep [s] +); +``` + +**Description**: Saturation adjustment using Newton-Raphson method to solve for equilibrium between vapor and cloud water, accounting for latent heat release. + +**Parameters**: +- `opts`: Options controlling adjustment (see [USER_OPTIONS.md](USER_OPTIONS.md#single-moment-bulk-blk_1m)) +- `rhod_cont`: Dry air density (constant in anelastic models) +- `p_cont`: Pressure field (required if `opts.const_p == true`) +- `th_cont`: Potential temperature (updated with latent heat effects) +- `rv_cont`: Water vapor mixing ratio (updated) +- `rc_cont`: Cloud water mixing ratio (updated) +- `dt`: Timestep duration + +**Notes**: +- Requires `opts.const_p` XOR `opts.th_dry` (exactly one must be true) +- If `opts.const_p == true`: uses "standard" potential temperature +- If `opts.th_dry == true`: uses dry potential temperature + +#### adj_cellwise_constp() + +```cpp +template +void adj_cellwise_constp( + const opts_t &opts, + cont_t &th_cont, + cont_t &rv_cont, + cont_t &rc_cont, + const cont_t &rhod_cont +); +``` + +**Description**: Saturation adjustment for constant pressure (simplified version). + +### Right-Hand Side Terms + +#### rhs_cellwise() + +```cpp +template +void rhs_cellwise( + const opts_t &opts, + cont_t &dot_rc_cont, // cloud water tendency [kg/kg/s] (output) + cont_t &dot_rr_cont, // rain water tendency [kg/kg/s] (output) + const cont_t &rc_cont, // cloud water [kg/kg] + const cont_t &rr_cont // rain water [kg/kg] +); +``` + +**Description**: Calculate microphysical tendencies for cloud and rain water using Kessler (1969) parameterization. + +**Processes**: +- **Autoconversion** (if `opts.conv == true`): Cloud water → rain water + - Formula: `rate = k_acnv * max(rc - r_c0, 0)` + - Threshold: `r_c0` (default 0.5 g/kg) + - Time scale: `1/k_acnv` (default 0.001 s) + +- **Collection/Accretion** (if `opts.accr == true`): Cloud water collected by rain + - Formula: Kessler collection formula + +**Parameters**: +- `opts`: Options controlling processes +- `dot_rc_cont`, `dot_rr_cont`: Output tendency arrays +- `rc_cont`, `rr_cont`: Input mixing ratio arrays + +**Note**: This version assumes rain evaporation is handled separately (requires `opts.adj_nwtrph == false`). + +#### rhs_cellwise_revap() + +```cpp +template +void rhs_cellwise_revap( + const opts_t &opts, + cont_t &dot_th_cont, // potential temperature tendency [K/s] (output) + cont_t &dot_rv_cont, // water vapor tendency [kg/kg/s] (output) + cont_t &dot_rc_cont, // cloud water tendency [kg/kg/s] (output) + cont_t &dot_rr_cont, // rain water tendency [kg/kg/s] (output) + const cont_t &rhod_cont, + const cont_t &p_cont, + const cont_t &th_cont, + const cont_t &rv_cont, + const cont_t &rc_cont, + const cont_t &rr_cont, + const real_t &dt +); +``` + +**Description**: Calculate tendencies including rain evaporation (to be used with Newton-Raphson saturation adjustment). + +**Additional Process**: +- **Rain evaporation**: Parameterized evaporation of falling rain droplets + +**Parameters**: +- Same as `rhs_cellwise()` plus thermodynamic state for evaporation calculation + +**Note**: Requires `opts.adj_nwtrph == true`. + +### Ice Processes + +```cpp +template +void rhs_cellwise_all( + const opts_t &opts, + cont_t &dot_th_cont, + cont_t &dot_rv_cont, + cont_t &dot_rc_cont, + cont_t &dot_rr_cont, + cont_t &dot_ri_cont, // ice mixing ratio tendency + cont_t &dot_rs_cont, // snow mixing ratio tendency + cont_t &dot_rg_cont, // graupel mixing ratio tendency + const cont_t &rhod_cont, + const cont_t &p_cont, + const cont_t &th_cont, + const cont_t &rv_cont, + const cont_t &rc_cont, + const cont_t &rr_cont, + const cont_t &ri_cont, + const cont_t &rs_cont, + const cont_t &rg_cont, + const real_t &dt +); +``` + +**Description**: Full microphysics including ice processes (Grabowski 1999). + +**Processes**: +- Ice nucleation +- Ice deposition growth +- Riming +- Melting +- All warm rain processes + +### Options Structure + +```cpp +template +struct opts_t { + bool cond; // condensation + bool conv; // autoconversion + bool accr; // accretion/collection + bool revp; // rain evaporation + bool sedi; // sedimentation + + // Additional ice options (for rhs_cellwise_all) + bool nuc; // ice nucleation + bool dep; // deposition + bool rim; // riming + bool melt; // melting + + // Thermodynamic options + bool const_p; // constant pressure (standard theta) + bool th_dry; // dry potential temperature + + // Parameters + real_t r_c0; // autoconversion threshold [kg/kg] + real_t k_acnv; // autoconversion rate [1/s] + + // Newton-Raphson options + bool adj_nwtrph; // use Newton-Raphson adjustment + real_t eps; // convergence tolerance + int iters; // max iterations +}; +``` + +See [USER_OPTIONS.md](USER_OPTIONS.md#single-moment-bulk-blk_1m) for defaults and details. + +--- + +## Double-Moment Bulk Microphysics (blk_2m) + +### Namespace + +```cpp +namespace libcloudphxx::blk_2m +``` + +### Overview + +Double-moment scheme predicts both mixing ratios and number concentrations: +- **rc**, **nc**: Cloud water mass and number +- **rr**, **nr**: Rain water mass and number + +### Right-Hand Side Calculation + +#### rhs_cellwise() + +```cpp +template +void rhs_cellwise( + const opts_t &opts, + cont_t &dot_th_cont, // potential temperature tendency [K/s] + cont_t &dot_rv_cont, // water vapor tendency [kg/kg/s] + cont_t &dot_rc_cont, // cloud water mass tendency [kg/kg/s] + cont_t &dot_nc_cont, // cloud droplet number tendency [1/kg/s] + cont_t &dot_rr_cont, // rain water mass tendency [kg/kg/s] + cont_t &dot_nr_cont, // rain drop number tendency [1/kg/s] + const cont_t &rhod_cont, // dry air density [kg/m³] + const cont_t &th_cont, // potential temperature [K] + const cont_t &rv_cont, // water vapor [kg/kg] + const cont_t &rc_cont, // cloud water [kg/kg] + const cont_t &nc_cont, // cloud droplet number [1/kg] + const cont_t &rr_cont, // rain water [kg/kg] + const cont_t &nr_cont, // rain drop number [1/kg] + const real_t &dt, // timestep [s] + const cont_t &p_cont = cont_t() // pressure [Pa] (if const_p) +); +``` + +**Description**: Calculate microphysical tendencies using double-moment parameterizations. + +**Processes** (selected by options): + +1. **Activation** (`opts.acti`): Aerosol activation to form cloud droplets + - Uses Twomey (1959) or other parameterizations + - Requires aerosol specification via `opts.dry_distros` + +2. **Autoconversion** (`opts.acnv`): Cloud droplets → rain drops + - Available formulations: Kessler, Beheng, Berry-Reinhardt + - Affects both mass (rc→rr) and number (nc→nr) + +3. **Accretion** (`opts.accr`): Rain collecting cloud water + - Separate parameterizations for mass and number + +4. **Self-collection** (`opts.slfc`): Droplet-droplet collisions within same category + - Reduces number concentration while preserving mass + +5. **Rain evaporation** (`opts.revp`): Evaporation of rain below cloud + - Ventilation effects included + +6. **Sedimentation** (`opts.sedi`): Gravitational settling + - Mass-weighted and number-weighted fall speeds + +**Parameters**: +- `opts`: See [USER_OPTIONS.md](USER_OPTIONS.md#double-moment-bulk-blk_2m) for complete options +- Thermodynamic state: `rhod`, `th`, `rv`, `p` (if `const_p`) +- Cloud state: `rc`, `nc` +- Rain state: `rr`, `nr` +- Output tendencies: `dot_th`, `dot_rv`, `dot_rc`, `dot_nc`, `dot_rr`, `dot_nr` + +**Notes**: +- Requires either `opts.const_p == true` OR `opts.th_dry == true` (not both) +- If `const_p == true`: must provide `p_cont` and use standard potential temperature +- If `th_dry == true`: uses dry potential temperature (for anelastic models) + +### Options Structure + +```cpp +template +struct opts_t { + // Process switches + bool cond; // condensation + bool acti; // activation + bool acnv; // autoconversion + bool accr; // accretion + bool slfc; // self-collection + bool revp; // rain evaporation + bool sedi; // sedimentation + + // Thermodynamics + bool const_p; // constant pressure + bool th_dry; // dry potential temperature + + // Aerosol specification (for activation) + std::map dry_distros; // {mean_radius [m]: concentration [1/kg]} + real_t kappa; // hygroscopicity parameter + + // Autoconversion parameterization + enum acnv_t { Kessler, Beheng, Berry_Reinhardt } acnv_type; + + // Accretion parameterization + enum accr_t { Khairoutdinov_Kogan, Long } accr_type; +}; +``` + +### Activation Parameterizations + +The double-moment scheme can use different aerosol activation schemes: + +#### Available Formulations + +1. **Twomey (1959)**: Simple power-law relationship + ``` + N_c = C * S^k + ``` + +2. **Abdul-Razzak and Ghan (2000)**: Physically-based with aerosol size distribution + +3. **Khvorostyanov and Curry (2006)**: Detailed kinetic approach + +**Configuration**: +```cpp +opts.acti = true; +opts.dry_distros = { + {0.02e-6, 80e6}, // mode 1: r=20 nm, N=80/cm³ + {0.075e-6, 40e6} // mode 2: r=75 nm, N=40/cm³ +}; +opts.kappa = 0.61; // ammonium sulfate +``` + +### Autoconversion Formulations + +#### Kessler Type + +```cpp +opts.acnv = true; +opts.acnv_type = opts_t::Kessler; +``` + +Classic threshold-based autoconversion. + +#### Beheng (1994) + +```cpp +opts.acnv_type = opts_t::Beheng; +``` + +Stochastic collection equation solution accounting for droplet size distribution. + +#### Berry-Reinhardt + +```cpp +opts.acnv_type = opts_t::Berry_Reinhardt; +``` + +Considers turbulence effects on collection efficiency. + +--- + +## Common Utilities + +### Namespace + +```cpp +namespace libcloudphxx::common +``` + +### Thermodynamic Functions + +Available in `#include ` + +#### Moist Air Properties + +```cpp +#include + +// Saturation vapor pressure (Tetens formula) +quantity p_vs(quantity T); + +// Saturation mixing ratio +quantity r_vs( + quantity T, + quantity p +); + +// Virtual temperature +quantity T_v( + quantity T, + quantity rv +); +``` + +#### Potential Temperature Conversions + +```cpp +#include +#include + +// Standard potential temperature +namespace theta_std { + quantity T( + quantity theta, + quantity p + ); + + real_t exner(quantity p); +} + +// Dry potential temperature +namespace theta_dry { + quantity T( + quantity theta_dry, + quantity rhod + ); + + quantity p( + quantity rhod, + quantity rv, + quantity T + ); +} +``` + +#### Terminal Velocity + +```cpp +#include + +// Terminal fall speed for droplets/particles +quantity::type, real_t> +vterm_fall( + quantity r, // particle radius + quantity rho // air density +); +``` + +### Physical Constants + +```cpp +#include + +namespace const_cp { + const quantity<...> c_pd; // specific heat of dry air + const quantity<...> c_pv; // specific heat of water vapor + const quantity<...> l_v; // latent heat of vaporization + const quantity<...> l_f; // latent heat of fusion + const quantity<...> R_d; // gas constant for dry air + const quantity<...> R_v; // gas constant for water vapor +} +``` + +### Output Enumerations + +```cpp +enum class output_t { + // Mixing ratios + rv, // water vapor + rc, // cloud water + rr, // rain water + ri, // ice + rs, // snow + rg, // graupel + + // Number concentrations (for double-moment) + nc, // cloud droplet number + nr, // rain drop number + + // Diagnostics + th, // potential temperature + rl, // total liquid water + precip_rate, // precipitation rate + + // Chemistry + chem_SO2, + chem_O3, + chem_H2O2, + // ... other species +}; +``` + +--- + +## Usage Examples + +### Example 1: Basic Lagrangian Simulation + +```cpp +#include +#include + +int main() { + // Grid setup + const int nx = 64, ny = 64, nz = 64; + const double dx = 100.0, dy = 100.0, dz = 100.0; + const double dt = 1.0; + + // Initialize options + libcloudphxx::lgrngn::opts_init_t opts_init; + opts_init.dt = dt; + opts_init.nx = nx; opts_init.ny = ny; opts_init.nz = nz; + opts_init.dx = dx; opts_init.dy = dy; opts_init.dz = dz; + opts_init.sd_conc = 64; + opts_init.dry_distros = {{0.04e-6, 60e6}}; // 40 nm mode + opts_init.kappa = 0.61; + + // Create particle system + auto particles = libcloudphxx::lgrngn::factory( + libcloudphxx::lgrngn::CUDA, opts_init + ); + + // Prepare atmospheric state + std::vector th(nx*ny*nz, 300.0); // 300 K + std::vector rv(nx*ny*nz, 0.01); // 10 g/kg + std::vector rhod(nx*ny*nz, 1.2); // 1.2 kg/m³ + + // Array info + libcloudphxx::lgrngn::arrinfo_t + th_info(th.data(), nullptr), + rv_info(rv.data(), nullptr), + rhod_info(rhod.data(), nullptr); + + // Initialize + particles->init(th_info, rv_info, rhod_info); + + // Time loop + libcloudphxx::lgrngn::opts_t opts; + opts.cond = true; + opts.coal = true; + + for (int step = 0; step < 100; ++step) { + particles->step_sync(opts, th_info, rv_info, rhod_info); + + // Diagnostics + if (step % 10 == 0) { + particles->diag_wet_mom(3); // LWC + double *lwc = particles->outbuf(); + // Process output... + } + } + + delete particles; + return 0; +} +``` + +### Example 2: Single-Moment Bulk Scheme + +```cpp +#include +#include +#include + +int main() { + const int ncells = 1000; + const double dt = 1.0; + + // Options + libcloudphxx::blk_1m::opts_t opts; + opts.cond = true; + opts.conv = true; + opts.accr = true; + opts.th_dry = true; + opts.const_p = false; + + // State variables + std::vector th(ncells, 300.0); + std::vector rv(ncells, 0.01); + std::vector rc(ncells, 0.0); + std::vector rr(ncells, 0.0); + std::vector rhod(ncells, 1.2); + std::vector p(ncells, 100000.0); + + // Tendencies + std::vector dot_rc(ncells, 0.0); + std::vector dot_rr(ncells, 0.0); + + // Time step + // 1. Calculate microphysics tendencies + libcloudphxx::blk_1m::rhs_cellwise(opts, dot_rc, dot_rr, rc, rr); + + // 2. Apply tendencies + for (int i = 0; i < ncells; ++i) { + rc[i] += dot_rc[i] * dt; + rr[i] += dot_rr[i] * dt; + } + + // 3. Saturation adjustment + libcloudphxx::blk_1m::adj_cellwise_nwtrph( + opts, rhod, p, th, rv, rc, dt + ); + + return 0; +} +``` + +### Example 3: Double-Moment Scheme with Activation + +```cpp +#include +#include + +int main() { + const int ncells = 1000; + const double dt = 1.0; + + // Options with aerosol specification + libcloudphxx::blk_2m::opts_t opts; + opts.acti = true; + opts.acnv = true; + opts.accr = true; + opts.const_p = true; + opts.th_dry = false; + opts.dry_distros = {{0.04e-6, 100e6}}; + opts.kappa = 0.61; + + // State variables + std::vector th(ncells, 300.0); + std::vector rv(ncells, 0.01); + std::vector rc(ncells, 0.0); + std::vector nc(ncells, 0.0); + std::vector rr(ncells, 0.0); + std::vector nr(ncells, 0.0); + std::vector rhod(ncells, 1.2); + std::vector p(ncells, 100000.0); + + // Tendencies + std::vector dot_th(ncells, 0.0); + std::vector dot_rv(ncells, 0.0); + std::vector dot_rc(ncells, 0.0); + std::vector dot_nc(ncells, 0.0); + std::vector dot_rr(ncells, 0.0); + std::vector dot_nr(ncells, 0.0); + + // Calculate tendencies + libcloudphxx::blk_2m::rhs_cellwise( + opts, + dot_th, dot_rv, dot_rc, dot_nc, dot_rr, dot_nr, + rhod, th, rv, rc, nc, rr, nr, dt, p + ); + + // Apply tendencies + for (int i = 0; i < ncells; ++i) { + th[i] += dot_th[i] * dt; + rv[i] += dot_rv[i] * dt; + rc[i] += dot_rc[i] * dt; + nc[i] += dot_nc[i] * dt; + rr[i] += dot_rr[i] * dt; + nr[i] += dot_nr[i] * dt; + } + + return 0; +} +``` + +--- + +## Compilation and Linking + +### CMake Integration + +```cmake +find_package(libcloudphxx REQUIRED) + +add_executable(myprogram main.cpp) +target_link_libraries(myprogram libcloudphxx::cloudphxx_lgrngn) +``` + +### Compiler Requirements + +- **C++11** or later +- **Boost** libraries (Units, OdeInt) +- **Thrust** (for GPU backends) +- **CUDA** toolkit (for GPU backends) +- **OpenMP** (for OpenMP backend) + +### Compiler Flags + +```bash +# CPU only +g++ -std=c++11 myprogram.cpp -lcloudphxx_lgrngn -lboost_system + +# With CUDA +nvcc -std=c++11 myprogram.cu -lcloudphxx_lgrngn +``` + +--- + +## Thread Safety and Performance + +### CPU Backends (serial, OpenMP) +- **Thread-safe**: No +- **Recommendation**: Use separate particle systems for different threads + +### GPU Backends (CUDA, multi_CUDA) +- **Thread-safe**: No +- **Recommendation**: One particle system per GPU stream +- **Performance**: Optimal with sd_conc ≥ 32 per cell + +### Best Practices + +1. **Minimize host-device transfers**: Use `sync_in()` and `step_async()` for better GPU utilization +2. **Batch diagnostics**: Call multiple `diag_*` methods before copying results +3. **Choose appropriate sd_conc**: Balance accuracy vs. computational cost (typically 32-128) +4. **Use operator splitting**: Separate `step_cond()` from advection/sedimentation for better integration with host model + +--- + +## Error Handling + +libcloudph++ uses assertions and exceptions for error handling: + +```cpp +try { + particles->init(th_info, rv_info, rhod_info); +} catch (const std::exception &e) { + std::cerr << "Initialization error: " << e.what() << std::endl; +} +``` + +Common errors: +- **Invalid array dimensions**: Check that nx, ny, nz match array sizes +- **Negative mixing ratios**: Ensure rv, rc, rr ≥ 0 before calling functions +- **Pressure consistency**: Verify `const_p` XOR `th_dry` in options +- **GPU memory**: Reduce sd_conc or domain size if out of memory + +--- + +## References + +For algorithm details and physics, see: + +1. Shima et al. (2009): Super-droplet method - Q. J. Roy. Meteor. Soc. 135: 1307–1320 +2. Arabas et al. (2015): libcloudph++ 1.0 - Geosci. Model Dev. 8: 1677–1707 +3. Grabowski (1999): Ice microphysics - J. Atmos. Sci. 56: 2429–2454 +4. Kessler (1969): Autoconversion parameterization +5. Khairoutdinov and Kogan (2000): Double-moment warm rain scheme - Mon. Wea. Rev. 128: 229–243 + +For user options details, see [USER_OPTIONS.md](USER_OPTIONS.md). + +--- + +**Document Version**: 1.0 +**Last Updated**: 2024 +**Library Version**: libcloudph++ 2.x diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a2b6504a..35dd2516f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -287,6 +287,8 @@ if (CMAKE_CUDA_COMPILER) target_compile_options(cloudphxx_lgrngn PRIVATE $<$: -Xcompiler -DUSE_MPI>) endif() +# target_compile_options(cloudphxx_lgrngn PRIVATE $<$: --extended-lambda>) + target_compile_definitions(cloudphxx_lgrngn PRIVATE CUDA_FOUND) # why was this added in the MPI PR? commented-out for now diff --git a/USER_OPTIONS.md b/USER_OPTIONS.md new file mode 100644 index 000000000..f65eb44c4 --- /dev/null +++ b/USER_OPTIONS.md @@ -0,0 +1,417 @@ +# libcloudph++ User Options Guide (generated by Claude 4.5; not checked for errors) + +This document describes all user-configurable options available in the libcloudph++ library for the three microphysics schemes: single-moment bulk, double-moment bulk, and Lagrangian super-droplet method. + +## Table of Contents + +1. [Single-Moment Bulk Microphysics (blk_1m)](#single-moment-bulk-microphysics-blk_1m) +2. [Double-Moment Bulk Microphysics (blk_2m)](#double-moment-bulk-microphysics-blk_2m) +3. [Lagrangian Microphysics (lgrngn)](#lagrangian-microphysics-lgrngn) + - [Initialization Options (opts_init_t)](#initialization-options-opts_init_t) + - [Runtime Options (opts_t)](#runtime-options-opts_t) + +--- + +## Single-Moment Bulk Microphysics (blk_1m) + +Defined in: `include/libcloudph++/blk_1m/options.hpp` + +The single-moment bulk scheme predicts only the mass mixing ratios of different hydrometeor species. + +### Process Control Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `cond` | `bool` | `true` | Enable condensation | +| `cevp` | `bool` | `true` | Enable evaporation of cloud droplets | +| `revp` | `bool` | `true` | Enable evaporation of rain | +| `conv` | `bool` | `true` | Enable autoconversion (cloud → rain) | +| `accr` | `bool` | `true` | Enable accretion (cloud + rain → rain) | +| `sedi` | `bool` | `true` | Enable sedimentation | +| `homA1` | `bool` | `true` | Enable homogeneous nucleation of ice A from water vapor | +| `homA2` | `bool` | `true` | Enable homogeneous nucleation of ice A from cloud droplets | +| `hetA` | `bool` | `true` | Enable heterogeneous nucleation of ice A | +| `hetB` | `bool` | `true` | Enable heterogeneous nucleation of ice B | +| `depA` | `bool` | `true` | Enable depositional growth of ice A | +| `depB` | `bool` | `true` | Enable depositional growth of ice B | +| `rimA` | `bool` | `true` | Enable growth of ice A by riming | +| `rimB` | `bool` | `true` | Enable growth of ice B by riming | +| `melA` | `bool` | `true` | Enable melting of ice A | +| `melB` | `bool` | `true` | Enable melting of ice B | + +### Physical Parameters + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `r_c0` | `real_t` | `5e-4` | Autoconversion threshold [kg/kg] | +| `k_acnv` | `real_t` | `0.001` | Kessler autoconversion coefficient (eq. 5a in Grabowski & Smolarkiewicz 1996) | +| `r_eps` | `real_t` | `2e-5` | Absolute tolerance for iterative solvers | + +### Numerical Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `adj_nwtrph` | `bool` | `true` | Use simpler Newton-Raphson iteration in saturation adjustment; otherwise use RK4 from boost.odeint | +| `nwtrph_iters` | `int` | `3` | Number of iterations in Newton-Raphson saturation adjustment | +| `th_dry` | `bool` | `true` | If `true`, input/output theta is dry-air potential temperature; if `false`, standard potential temperature | +| `const_p` | `bool` | `false` | If `true`, pressure equals supplied profile (e.g., anelastic model); if `false`, pressure from gas equation | + +**Note:** Only tested combinations are: `th_dry=true` & `const_p=false` OR `th_dry=false` & `const_p=true` + +--- + +## Double-Moment Bulk Microphysics (blk_2m) + +Defined in: `include/libcloudph++/blk_2m/options.hpp` + +The double-moment bulk scheme predicts both mass mixing ratios and number concentrations. + +### Process Control Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `acti` | `bool` | `true` | Enable activation | +| `cond` | `bool` | `true` | Enable condensation | +| `acnv` | `bool` | `true` | Enable autoconversion | +| `accr` | `bool` | `true` | Enable accretion | +| `sedi` | `bool` | `true` | Enable sedimentation | + +### Physical Parameters + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `RH_max` | `real_t` | `44` | RH limit for activation (anything > 1.1 is sufficient) | +| `acnv_A` | `real_t` | `1350` | Autoconversion parameter A (Khairoutdinov & Kogan 2000, eq. 29) | +| `acnv_b` | `real_t` | `2.47` | Autoconversion parameter b | +| `acnv_c` | `real_t` | `-1.79` | Autoconversion parameter c | + +### Aerosol Specification + +Aerosol is specified using lognormal modes: + +```cpp +struct lognormal_mode_t { + real_t mean_rd; // Mean dry radius [m] + real_t sdev_rd; // Standard deviation of dry radius [dimensionless] + real_t N_stp; // Number concentration at STP [m^-3] + real_t chem_b; // Chemical parameter [dimensionless] +}; +std::vector dry_distros; +``` + +### Thermodynamic Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `th_dry` | `bool` | `true` | If `true`, input/output theta is dry-air potential temperature; if `false`, standard potential temperature | +| `const_p` | `bool` | `false` | If `true`, pressure equals supplied profile; if `false`, pressure from gas equation | + +**Note:** Only working combinations are: `th_dry=true` & `const_p=false` OR `th_dry=false` & `const_p=true` + +--- + +## Lagrangian Microphysics (lgrngn) + +The Lagrangian super-droplet method explicitly tracks individual computational particles (super-droplets) representing groups of real droplets with identical properties. + +### Initialization Options (opts_init_t) + +Defined in: `include/libcloudph++/lgrngn/opts_init.hpp` + +These options are set once at initialization and cannot be changed during the simulation. + +#### Domain Configuration + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `nx`, `ny`, `nz` | `int` | `0` | Number of Eulerian grid cells in x, y, z directions | +| `dx`, `dy`, `dz` | `real_t` | `1` | Grid spacing [m] | +| `x0`, `y0`, `z0` | `real_t` | `0` | Lower bounds of Lagrangian domain [m] | +| `x1`, `y1`, `z1` | `real_t` | `1` | Upper bounds of Lagrangian domain [m] | +| `dt` | `real_t` | `0` | Timestep [s] | + +#### Super-Droplet Configuration + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `sd_conc` | `unsigned long long` | `0` | Number of super-droplets per cell | +| `sd_const_multi` | `unsigned long long` | `0` | Alternative to `sd_conc`: constant multiplicity for all SDs | +| `n_sd_max` | `unsigned long long` | `0` | Maximum number of super-droplets in the system (should account for sources) | +| `sd_conc_large_tail` | `bool` | `false` | Add more SDs to better represent large tail of the distribution | +| `rd_min`, `rd_max` | `real_t` | `-1` | Min/max dry radius of droplets [m]; negative = auto-detect | + +#### Aerosol Specification + +Two methods are available: + +**1. Distribution-based (recommended):** +```cpp +typedef std::unordered_map< + real_t, // kappa (hygroscopicity parameter) + std::shared_ptr> // n(ln(rd)) @ STP +> dry_distros_t; +dry_distros_t dry_distros; +``` + +**2. Size-number pairs:** +```cpp +typedef std::map< + real_t, // kappa + std::map // STP concentration [1/m^3], number of SDs + > +> dry_sizes_t; +dry_sizes_t dry_sizes; +``` + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `aerosol_independent_of_rhod` | `bool` | `false` | If `true`, aerosol concentration in cm^-3 (not at STP) | + +#### Timestep Substepping + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `sstp_cond` | `int` | `1` | Number of condensation substeps (or max if adaptive) | +| `sstp_coal` | `int` | `1` | Number of coalescence substeps | +| `sstp_chem` | `int` | `1` | Number of chemistry substeps | +| `sstp_cond_act` | `int` | `1` | Number of condensation substeps for activating/deactivating SDs (adaptive mode only) | +| `exact_sstp_cond` | `bool` | `false` | If `true`, use per-particle substepping; if `false`, per-cell | +| `sstp_cond_mix` | `bool` | `true` | If `true`, mix th/rv of all SDs in cell after each substep; if `false`, only after all substeps | +| `adaptive_sstp_cond` | `bool` | `false` | If `true`, use adaptive number of condensation substeps | +| `sstp_cond_adapt_drw2_eps` | `real_t` | `1e-4` | Tolerance for adaptive substepping: drw2_err ≤ eps × rw2 | +| `sstp_cond_adapt_drw2_max` | `real_t` | `4` | Maximum drw2: drw2 < max × rw2 | +| `variable_dt_switch` | `bool` | `false` | Allow changing dt during simulation through `opts.dt` | + +**Note:** If `dt` changes during simulation and substeps > 1, the number of substeps is adjusted to keep process timestep close to `dt/sstp`. + +#### Process Switches + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `chem_switch` | `bool` | `false` | Enable chemical reactions (affects memory allocation) | +| `coal_switch` | `bool` | `true` | Enable coalescence | +| `sedi_switch` | `bool` | `true` | Enable sedimentation | +| `subs_switch` | `bool` | `false` | Enable subsidence | +| `rlx_switch` | `bool` | `false` | Enable aerosol relaxation | +| `turb_adve_switch` | `bool` | `false` | Enable turbulent advection of SDs | +| `turb_cond_switch` | `bool` | `false` | Enable turbulent condensation | +| `turb_coal_switch` | `bool` | `false` | Enable turbulent coalescence kernels | + +#### Physical Parameterizations + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `kernel` | `kernel_t` | `undefined` | Coalescence kernel type (e.g., `geometric`, `hall`, `onishi`, `electric`) | +| `kernel_parameters` | `std::vector` | - | Parameters for selected kernel | +| `terminal_velocity` | `vt_t` | `undefined` | Terminal velocity formula (e.g., `beard`, `khvorostyanov`, `beard_relaxation`) | +| `adve_scheme` | `as_t` | `implicit` | SD advection scheme (`implicit`, `euler`, `pred_corr`) | +| `RH_formula` | `RH_formula_t` | `pv_cc` | RH formula (`pv_cc`, `rv_cc`, `pv_tet`, `rv_tet`) | +| `RH_max` | `real_t` | `0.95` | RH threshold for equilibrium at t=0 (suggested: Lebo & Seinfeld 2011) | +| `rc2_T` | `real_t` | `10` | Temperature [°C] at which critical radius is calculated | + +#### Boundary Conditions + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `open_side_walls` | `bool` | `false` | If `true`, SDs removed at side walls; if `false`, periodic | +| `periodic_topbot_walls` | `bool` | `false` | If `true`, top/bottom walls periodic; if `false`, open | + +#### Environmental Profiles + +| Option | Type | Description | +|--------|------|-------------| +| `w_LS` | `std::vector` | Subsidence rate profile [m/s], positive downwards | +| `SGS_mix_len` | `std::vector` | SGS mixing length profile [m] | +| `aerosol_conc_factor` | `std::vector` | Profile of aerosol concentration scaling factor | + +#### Chemistry Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `chem_rho` | `real_t` | `0` | Dry particle density [kg/m^3] | + +#### Diagnostics + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `diag_incloud_time` | `bool` | `false` | Track time SDs spend inside clouds | + +#### Random Number Generation + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `rng_seed` | `int` | `44` | RNG seed for stochastic processes | +| `rng_seed_init` | `int` | `44` | RNG seed for SD initialization (positions, dry sizes) | +| `rng_seed_init_switch` | `bool` | `false` | Use separate RNG seed for initialization | + +#### GPU Configuration + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `dev_count` | `int` | `0` | Number of GPUs per MPI node to use (0 = all available) | +| `dev_id` | `int` | `-1` | GPU number to use (CUDA backend only, not multi_CUDA) | + +#### Initialization Control + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `no_ccn_at_init` | `bool` | `false` | If `true`, no CCN/SD created at start of simulation | + +#### Thermodynamic Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `th_dry` | `bool` | `true` | If `true`, theta is dry-air potential temperature; if `false`, standard | +| `const_p` | `bool` | `false` | If `true`, pressure from profile; if `false`, from gas equation | + +**Note:** Only tested combinations are: `th_dry=true` & `const_p=false` OR `th_dry=false` & `const_p=true` + +#### Aerosol Source Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `src_type` | `src_t` | `off` | Type of CCN source (`off`, `constant`, etc.) | +| `src_dry_distros` | `dry_distros_t` | - | Source distribution per unit time | +| `src_dry_sizes` | `dry_sizes_t` | - | Alternative source specification using size-number pairs | +| `src_sd_conc` | `unsigned long long` | `0` | Number of SDs created per cell per source iteration | +| `src_x0`, `src_x1` | `real_t` | `0` | Source box x-boundaries [m] (rounded to cell boundaries) | +| `src_y0`, `src_y1` | `real_t` | `0` | Source box y-boundaries [m] | +| `src_z0`, `src_z1` | `real_t` | `0` | Source box z-boundaries [m] | +| `supstp_src` | `int` | `1` | Timestep interval for applying source | + +#### Aerosol Relaxation Options + +| Option | Type | Description | +|--------|------|-------------| +| `rlx_dry_distros` | `rlx_dry_distros_t` | Relaxation distributions with kappa, distribution, kappa range, and altitude range | +| `rlx_bins` | `unsigned long long` | Number of bins for dividing relaxation distribution | +| `rlx_sd_per_bin` | `real_t` | Number of SDs created per bin (float, divided by GPUs × nodes) | +| `rlx_timescale` | `real_t` | Relaxation time scale [s] | +| `supstp_rlx` | `int` | Timestep interval for applying relaxation | + +--- + +### Runtime Options (opts_t) + +Defined in: `include/libcloudph++/lgrngn/opts.hpp` + +These options can be changed at each timestep during the simulation. + +#### Process Control + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `adve` | `bool` | `true` | Enable advection | +| `sedi` | `bool` | `true` | Enable sedimentation | +| `subs` | `bool` | `false` | Enable subsidence | +| `cond` | `bool` | `true` | Enable condensation | +| `coal` | `bool` | `true` | Enable coalescence | +| `src` | `bool` | `false` | Enable aerosol source | +| `rlx` | `bool` | `false` | Enable aerosol relaxation | +| `rcyc` | `bool` | `false` | Enable recycling | +| `turb_adve` | `bool` | `false` | Enable turbulent advection | +| `turb_cond` | `bool` | `false` | Enable turbulent condensation | +| `turb_coal` | `bool` | `false` | Enable turbulent coalescence | + +#### Chemistry Process Control + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `chem_dsl` | `bool` | `false` | Enable chemical dissolution | +| `chem_dsc` | `bool` | `false` | Enable chemical dissociation | +| `chem_rct` | `bool` | `false` | Enable chemical reactions | + +#### Physical Parameters + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `RH_max` | `real_t` | `44` | RH limit for drop growth (anything > 1.1 is sufficient) | +| `dt` | `real_t` | `-1` | Override dt from `opts_init`; negative = use `opts_init.dt` | + +--- + +## Usage Examples + +### Single-Moment Bulk + +```cpp +#include + +libcloudphxx::blk_1m::opts_t opts; +opts.cond = true; // Enable condensation +opts.conv = true; // Enable autoconversion +opts.accr = true; // Enable accretion +opts.r_c0 = 5e-4; // Autoconversion threshold +opts.k_acnv = 0.001; // Kessler parameter +opts.th_dry = true; // Use dry potential temperature +opts.const_p = false; // Variable pressure +``` + +### Double-Moment Bulk + +```cpp +#include + +libcloudphxx::blk_2m::opts_t opts; +opts.acti = true; // Enable activation +opts.cond = true; // Enable condensation +opts.RH_max = 44; // RH limit + +// Define aerosol mode +libcloudphxx::blk_2m::opts_t::lognormal_mode_t mode; +mode.mean_rd = 0.04e-6; // 40 nm +mode.sdev_rd = 1.4; // Standard deviation +mode.N_stp = 60e6; // 60 cm^-3 at STP +mode.chem_b = 0.61; // Kappa +opts.dry_distros.push_back(mode); +``` + +### Lagrangian Microphysics + +```cpp +#include +#include + +// Initialization options +libcloudphxx::lgrngn::opts_init_t opts_init; +opts_init.nx = 100; +opts_init.ny = 100; +opts_init.nz = 100; +opts_init.dx = opts_init.dy = opts_init.dz = 10; // 10 m grid spacing +opts_init.dt = 1.0; // 1 s timestep +opts_init.sd_conc = 64; // 64 SDs per cell +opts_init.sstp_cond = 4; // 4 condensation substeps +opts_init.adaptive_sstp_cond = true; // Enable adaptive substepping +opts_init.exact_sstp_cond = true; // Per-particle substepping +opts_init.kernel = libcloudphxx::lgrngn::kernel_t::geometric; +opts_init.terminal_velocity = libcloudphxx::lgrngn::vt_t::beard; + +// Define aerosol distribution +auto lognormal = [](double lnr) { + double mean_r = 0.04e-6; + double stdev = 1.4; + double n_tot = 60e6; + return n_tot * exp(-pow((lnr - log(mean_r)), 2) / 2 / pow(log(stdev), 2)) + / log(stdev) / sqrt(2 * M_PI); +}; +opts_init.dry_distros[0.61] = std::make_shared(lognormal); + +// Runtime options (can change each step) +libcloudphxx::lgrngn::opts_t opts; +opts.cond = true; +opts.coal = true; +opts.sedi = true; +opts.RH_max = 1.1; +``` + +--- + +## References + +- Grabowski, W. W., & Smolarkiewicz, P. K. (1996). Two-time-level semi-Lagrangian modeling of precipitating clouds. *Monthly Weather Review*, 124(3), 487-497. +- Khairoutdinov, M., & Kogan, Y. (2000). A new cloud physics parameterization in a large-eddy simulation model of marine stratocumulus. *Monthly Weather Review*, 128(1), 229-243. +- Lebo, Z. J., & Seinfeld, J. H. (2011). Theoretical basis for convective invigoration due to increased aerosol concentration. *Atmospheric Chemistry and Physics*, 11(11), 5407-5429. +- Arabas, S., Jaruga, A., Pawlowska, H., & Grabowski, W. W. (2015). libcloudph++ 1.0: a single-moment bulk, double-moment bulk, and particle-based warm-rain microphysics library in C++. *Geoscientific Model Development*, 8(6), 1677-1707. + +For more information, see: https://github.com/igfuw/libcloudphxx/wiki diff --git a/bindings/python/lib.cpp b/bindings/python/lib.cpp index fca30884e..6719841fd 100644 --- a/bindings/python/lib.cpp +++ b/bindings/python/lib.cpp @@ -321,7 +321,10 @@ BOOST_PYTHON_MODULE(libcloudphxx) .def_readwrite("turb_cond_switch", &lgr::opts_init_t::turb_cond_switch) .def_readwrite("turb_coal_switch", &lgr::opts_init_t::turb_coal_switch) .def_readwrite("exact_sstp_cond", &lgr::opts_init_t::exact_sstp_cond) + .def_readwrite("sstp_cond_mix", &lgr::opts_init_t::sstp_cond_mix) .def_readwrite("sstp_cond", &lgr::opts_init_t::sstp_cond) + .def_readwrite("sstp_cond_act", &lgr::opts_init_t::sstp_cond_act) + .def_readwrite("rc2_T", &lgr::opts_init_t::rc2_T) .def_readwrite("sstp_coal", &lgr::opts_init_t::sstp_coal) .def_readwrite("sstp_chem", &lgr::opts_init_t::sstp_chem) .def_readwrite("supstp_rlx", &lgr::opts_init_t::supstp_rlx) @@ -352,6 +355,11 @@ BOOST_PYTHON_MODULE(libcloudphxx) .def_readwrite("time_dep_ice_nucl", &lgr::opts_init_t::time_dep_ice_nucl) .def_readwrite("const_p", &lgr::opts_init_t::const_p) .def_readwrite("th_dry", &lgr::opts_init_t::th_dry) + .def_readwrite("sstp_cond_mix", &lgr::opts_init_t::sstp_cond_mix) + .def_readwrite("adaptive_sstp_cond", &lgr::opts_init_t::adaptive_sstp_cond) + .def_readwrite("sstp_cond_adapt_drw2_eps", &lgr::opts_init_t::sstp_cond_adapt_drw2_eps) + .def_readwrite("sstp_cond_adapt_drw2_max", &lgr::opts_init_t::sstp_cond_adapt_drw2_max) + ; bp::class_/*, boost::noncopyable*/>("particles_proto_t") .add_property("opts_init", &lgrngn::get_oi) diff --git a/include/libcloudph++/lgrngn/opts_init.hpp b/include/libcloudph++/lgrngn/opts_init.hpp index ca958b93f..7acaa1322 100644 --- a/include/libcloudph++/lgrngn/opts_init.hpp +++ b/include/libcloudph++/lgrngn/opts_init.hpp @@ -38,8 +38,16 @@ namespace libcloudphxx int nx, ny, nz; real_t dx, dy, dz, dt; - // no. of substeps + // no. of substeps for condensation/coalescence. If adaptive_sstp_cond=true, sstp_cond is the maximum no. of substeps. int sstp_cond, sstp_coal; + // no. of condensation substeps for SDs that activate/deactivate in this timestep + // only work in adaptive_sstp_cond mode. + int sstp_cond_act; + // no. of substeps for chemistry + int sstp_chem; + // Note: If dt changes during simulation (by supplying opts.dt), + // no. of substeps is adjusted accordingly to keep process timestep close to dt / sstp_cond, + // but only if initially sstp_cond/coal/chem/cond_act > 1 // Lagrangian domain extents real_t x0, y0, z0, x1, y1, z1; @@ -90,12 +98,16 @@ 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 + sstp_cond_mix, // if true, th and rv of all SDs in a cell after each timestep (instant mixing at substep timescale), else update it only after all substeps + adaptive_sstp_cond, // if true, use adaptive number of substeps for condensation time_dep_ice_nucl; // it true, time-dependent freezing, if false, singular freezing + real_t sstp_cond_adapt_drw2_eps = 1e-4; // tolerance for adaptive substepping in condensation (drw2_err <= sstp_cond_adapt_eps * rw2) + real_t sstp_cond_adapt_drw2_max = 4; // tolerance for adaptive substepping in condensation (drw2 < sstp_cond_adapt_drw2_max * rw2) + // ice nucleating particles type INP_t inp_type; - int sstp_chem; real_t chem_rho; // do we want to track the time SDs spend inside clouds @@ -134,6 +146,7 @@ namespace libcloudphxx bool open_side_walls, // if true, side walls are "open", i.e. SD are removed at contact. Periodic otherwise. periodic_topbot_walls; // if true, top and bot walls are periodic. Open otherwise + real_t rc2_T = 10; // temperature [C] at which rc2 is calculated // --- aerosol source stuff --- @@ -188,7 +201,7 @@ namespace libcloudphxx aerosol_independent_of_rhod(false), sd_const_multi(0), dt(0), - sstp_cond(1), sstp_coal(1), sstp_chem(1), + sstp_cond(1), sstp_coal(1), sstp_chem(1), sstp_cond_act(1), chem_switch(false), // chemical reactions turned off by default sedi_switch(true), // sedimentation turned on by default subs_switch(false), // subsidence turned off by default @@ -199,6 +212,8 @@ namespace libcloudphxx time_dep_ice_nucl(false), inp_type(INP_t::mineral), exact_sstp_cond(false), + sstp_cond_mix(true), + adaptive_sstp_cond(false), turb_cond_switch(false), turb_adve_switch(false), turb_coal_switch(false), diff --git a/src/detail/tmp_vector_pool.hpp b/src/detail/tmp_vector_pool.hpp index f53ca9dfe..0c3bb31d5 100644 --- a/src/detail/tmp_vector_pool.hpp +++ b/src/detail/tmp_vector_pool.hpp @@ -16,15 +16,15 @@ namespace libcloudphxx entry(size_t n) : vec(n) {} }; std::vector pool; + const std::string name; + public: - tmp_vector_pool(size_t pool_size = 1): pool(pool_size, 0) {} + tmp_vector_pool(std::string name, size_t pool_size = 1): pool(pool_size, 0), name(name) {} - // Add a new vector to the pool - // void add_vector(size_t vec_size) { - // pool.emplace_back(vec_size); - // } - void add_vector() { - pool.emplace_back(0); + void add_vectors(size_t no_vectors = 1) { + for (size_t i = 0; i < no_vectors; ++i) { + pool.emplace_back(0); + } } void resize(size_t n) { @@ -41,14 +41,15 @@ namespace libcloudphxx // Acquire an available vector, returns its index size_t acquire() { -// std::cerr << "tmp_vector_pool: acquiring vector from pool of size " << pool.size() << "\n"; for (size_t i = 0; i < pool.size(); ++i) { if (!pool[i].in_use) { pool[i].in_use = true; return i; } } - assert(false && "No available temporary vectors in pool!"); + std::cerr << "LIBCLOUDPH++: tmp_vector_pool: No available temporary vectors in pool! Pool size: " << pool.size() << ". Pool name: " << name << " \n"; + // assert(false && "No available temporary vectors in pool!"); + assert(false); return size_t(-1); } diff --git a/src/impl/particles_impl_adve.ipp b/src/impl/advection/particles_impl_adve.ipp similarity index 100% rename from src/impl/particles_impl_adve.ipp rename to src/impl/advection/particles_impl_adve.ipp diff --git a/src/impl/particles_impl_turb_adve.ipp b/src/impl/advection/particles_impl_turb_adve.ipp similarity index 100% rename from src/impl/particles_impl_turb_adve.ipp rename to src/impl/advection/particles_impl_turb_adve.ipp diff --git a/src/impl/particles_impl_bcnd.ipp b/src/impl/boundary_conditions/particles_impl_bcnd.ipp similarity index 100% rename from src/impl/particles_impl_bcnd.ipp rename to src/impl/boundary_conditions/particles_impl_bcnd.ipp diff --git a/src/impl/particles_impl_chem_ante.ipp b/src/impl/chemistry/particles_impl_chem_ante.ipp similarity index 100% rename from src/impl/particles_impl_chem_ante.ipp rename to src/impl/chemistry/particles_impl_chem_ante.ipp diff --git a/src/impl/particles_impl_chem_dissoc.ipp b/src/impl/chemistry/particles_impl_chem_dissoc.ipp similarity index 100% rename from src/impl/particles_impl_chem_dissoc.ipp rename to src/impl/chemistry/particles_impl_chem_dissoc.ipp diff --git a/src/impl/particles_impl_chem_henry.ipp b/src/impl/chemistry/particles_impl_chem_henry.ipp similarity index 100% rename from src/impl/particles_impl_chem_henry.ipp rename to src/impl/chemistry/particles_impl_chem_henry.ipp diff --git a/src/impl/particles_impl_chem_react.ipp b/src/impl/chemistry/particles_impl_chem_react.ipp similarity index 100% rename from src/impl/particles_impl_chem_react.ipp rename to src/impl/chemistry/particles_impl_chem_react.ipp diff --git a/src/impl/particles_impl_chem_strength.ipp b/src/impl/chemistry/particles_impl_chem_strength.ipp similarity index 100% rename from src/impl/particles_impl_chem_strength.ipp rename to src/impl/chemistry/particles_impl_chem_strength.ipp diff --git a/src/impl/particles_impl_sstp_chem.ipp b/src/impl/chemistry/particles_impl_sstp_chem.ipp similarity index 95% rename from src/impl/particles_impl_sstp_chem.ipp rename to src/impl/chemistry/particles_impl_sstp_chem.ipp index cfc10d2e2..8d414ff14 100644 --- a/src/impl/particles_impl_sstp_chem.ipp +++ b/src/impl/chemistry/particles_impl_sstp_chem.ipp @@ -33,7 +33,7 @@ namespace libcloudphxx } template - void particles_t::impl::init_sstp_chem() + void particles_t::impl::init_percell_sstp_chem() { if (!allow_sstp_chem) return; @@ -50,7 +50,7 @@ namespace libcloudphxx } template - void particles_t::impl::sstp_step_chem( + void particles_t::impl::sstp_percell_step_chem( const int &step ) { diff --git a/src/impl/particles_impl_coal.ipp b/src/impl/coalescence/particles_impl_coal.ipp similarity index 93% rename from src/impl/particles_impl_coal.ipp rename to src/impl/coalescence/particles_impl_coal.ipp index 0da22c4ed..98943486e 100644 --- a/src/impl/particles_impl_coal.ipp +++ b/src/impl/coalescence/particles_impl_coal.ipp @@ -30,6 +30,19 @@ namespace libcloudphxx } }; + struct invalidator // set value of some parameter to invalid in the SD that had its parameters changed (other than multiplicity) + { + template + BOOST_GPU_ENABLED + void operator()(tpl_t tpl) + { + if(thrust::get<2>(tpl) <= 0) return; // do nothing if no collisions or first one passed was a SD with an uneven number in the cell + thrust::get<3>(tpl) == na_ge_nb ? // does the first SD of the pair have greater multiplicity? + thrust::get<1>(tpl) = detail::invalid: + thrust::get<0>(tpl) = detail::invalid; + } + }; + struct summator { template @@ -510,6 +523,26 @@ namespace libcloudphxx ); nancheck(incloud_time, "incloud_time - post coalescence"); } + + // invalidate rc2 due to changed rd3 and kappa + if(opts_init.sstp_cond_act > 1 && allow_sstp_cond) // rc2 only used in adaptive activation substepping + { + thrust::for_each( + thrust::make_zip_iterator(thrust::make_tuple( + thrust::make_permutation_iterator(rc2.begin(), sorted_id.begin()), + thrust::make_permutation_iterator(rc2.begin(), sorted_id.begin())+1, + col.begin(), + col.begin()+1 + )), + thrust::make_zip_iterator(thrust::make_tuple( + thrust::make_permutation_iterator(rc2.begin(), sorted_id.begin()), + thrust::make_permutation_iterator(rc2.begin(), sorted_id.begin())+1, + col.begin(), + col.begin()+1 + )) + n_part -1, + detail::invalidator() + ); + } } }; }; diff --git a/src/impl/common/calc_liq_ice_content_change.ipp b/src/impl/common/calc_liq_ice_content_change.ipp new file mode 100644 index 000000000..0fe73c37e --- /dev/null +++ b/src/impl/common/calc_liq_ice_content_change.ipp @@ -0,0 +1,50 @@ +// 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::calc_liq_ice_content_change() + { + // TODO: assert that save_liq_ice_content_before_change was called beforehand? + thrust_device::vector &drw_mom3 = drw_mom3_gp->get(); + + moms_all(); + moms_calc(rw2.begin(), real_t(3./2.)); + + // drw_mom3 = -rw_mom3_ante + rw_mom3_post + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(drw_mom3.begin(), count_ijk.begin()), // 2nd arg + thrust::make_permutation_iterator(drw_mom3.begin(), count_ijk.begin()), // output + thrust::plus() + ); + + if(opts_init.ice_switch) + { + thrust_device::vector &d_ice_mass = d_ice_mass_gp->get(); + + 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) + ); + + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(d_ice_mass.begin(), count_ijk.begin()), // 2nd arg + thrust::make_permutation_iterator(d_ice_mass.begin(), count_ijk.begin()), // output + thrust::plus() + ); + } + } + }; +}; diff --git a/src/impl/particles_impl_update_th_rv.ipp b/src/impl/common/particles_impl_update_th_rv.ipp similarity index 65% rename from src/impl/particles_impl_update_th_rv.ipp rename to src/impl/common/particles_impl_update_th_rv.ipp index 9d49647fb..36849b83f 100644 --- a/src/impl/particles_impl_update_th_rv.ipp +++ b/src/impl/common/particles_impl_update_th_rv.ipp @@ -70,94 +70,123 @@ namespace libcloudphxx // 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/dep, the default is condensation - ) +// <<<<<<< HEAD:src/impl/common/particles_impl_update_th_rv.ipp + void particles_t::impl::update_th_rv() +// ======= + // void particles_t::impl::update_th_rv( + // thrust_device::vector &drv, // change in water vapor mixing ratio + // phase_change phase // enum for cond/dep, the default is condensation + // ) +// >>>>>>> 41c117de670b134d0632608d7e48fd9050a852bc:src/impl/particles_impl_update_th_rv.ipp { if(!sorted) throw std::runtime_error("libcloudph++: update_th_rv called on an unsorted set"); - nancheck(drv, "update_th_rv: input drv"); - 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) - { - // 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 - - real_t(1) - ), - drv.begin(), // output - thrust::multiplies() - ); - } + // thrust_device::vector &drw_mom3 = drw_mom3_gp->get(); + // nancheck(drw_mom3, "update_th_rv: input drw_mom3"); + + // if (phase == phase_change::condensation) + + thrust_device::vector &drw_mom3 = drw_mom3_gp->get(); + nancheck(drw_mom3, "update_th_rv: input drw_mom3"); + + // multiplying specific 3rd moms diff by rho_w*4/3*pi to get -drv + thrust::transform( + drw_mom3.begin(), drw_mom3.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() + ), + drw_mom3.begin(), // output + thrust::multiplies() + ); + + // else if (phase == phase_change::deposition) + // if(opts_init.ice_switch) // TODO: call update_th_rv once per cond/depo + // { + // thrust_device::vector &d_ice_mass = d_ice_mass_gp->get(); + // nancheck(d_ice_mass, "update_th_rv: input d_ice_mass"); + + // thrust::transform( + // d_ice_mass.begin(), d_ice_mass.end(), // input - 1st arg + // thrust::make_constant_iterator( // input - 2nd arg + // - real_t(1) + // ), + // d_ice_mass.begin(), // output + // thrust::multiplies() + // ); + // } + + // TODO: drv = drw_mom3 or one from deposition // 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 + drw_mom3.begin(), // input - 2nd arg rv.begin(), // output - thrust::plus() + thrust::minus() ); assert(*thrust::min_element(rv.begin(), rv.end()) >= 0); - nancheck(rv, "update_th_rv: rv after update"); - - // updating th + + if(opts_init.ice_switch) { - typedef thrust::zip_iterator::iterator, - typename thrust_device::vector::iterator, - typename thrust_device::vector::iterator - > > zip_it_t; + thrust_device::vector &d_ice_mass = d_ice_mass_gp->get(); + nancheck(d_ice_mass, "update_th_rv: input d_ice_mass"); - // 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(), // - T.begin(), // dth = drv * d_th_d_rv(T, th) - th.begin() // - )), - detail::dth() - ), - th.begin(), // output - thrust::plus() + rv.begin(), rv.end(), // input - 1st arg + d_ice_mass.begin(), // input - 2nd arg + rv.begin(), // output + thrust::minus() ); + nancheck(rv, "update_th_rv: rv after update"); } - else if (phase == phase_change::deposition) + + // updating th + typedef thrust::zip_iterator::iterator, + typename thrust_device::vector::iterator, + typename thrust_device::vector::iterator + > > 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( + // TODO: which one + drw_mom3.begin(), // + T.begin(), // -dth = -drv * d_th_d_rv(T, th) + th.begin() // + )), + detail::dth() + ), + th.begin(), // output + thrust::minus() // dth = -(-dth) + ); + // } + // else if (phase == phase_change::deposition) + if(opts_init.ice_switch) { + thrust_device::vector &d_ice_mass = d_ice_mass_gp->get(); 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) + d_ice_mass.begin(), // + T.begin(), // dth = d_ice_mass * d_th_d_rv(T, th) th.begin() // )), detail::dth_dep() ), th.begin(), // output - thrust::plus() + thrust::minus() ); - } - - } + } + drw_mom3_gp.reset(); // destroy guard to tmp array that stored change in 3rd moment of rw + d_ice_mass_gp.reset(); // destroy guard to tmp array that stored change in 3rd moment of rw nancheck(th, "update_th_rv: th after update"); } @@ -260,10 +289,12 @@ namespace libcloudphxx thrust_device::vector &pstate // particle-specific cell state (same for all particles in one cell) ) { + // TODO: we copy from all SDs in a single cell to the same position; wasteful and possibly with memory races (?); do it once per cell thrust::copy( pstate.begin(), pstate.end(), thrust::make_permutation_iterator(state.begin(), ijk.begin()) ); + } }; }; diff --git a/src/impl/common/save_liq_ice_content_before_change.ipp b/src/impl/common/save_liq_ice_content_before_change.ipp new file mode 100644 index 000000000..af14a02ed --- /dev/null +++ b/src/impl/common/save_liq_ice_content_before_change.ipp @@ -0,0 +1,61 @@ +// 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::save_liq_ice_content_before_change() + { + // --- calc liquid water content before src --- + hskpng_sort(); + reset_guardp(drw_mom3_gp, tmp_device_real_cell); + thrust_device::vector &drw_mom3 = drw_mom3_gp->get(); + + moms_all(); // TODO: select water only? won't change results, because rw2=0 for non-water particles + moms_calc(rw2.begin(), real_t(3./2.)); + + // drw_mom3 = -rw_mom3 ante change + if(count_n!=n_cell) + thrust::fill(drw_mom3.begin(), drw_mom3.end(), real_t(0.)); + + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(drw_mom3.begin(), count_ijk.begin()), // output + thrust::negate() + ); + + if(opts_init.ice_switch) + { + reset_guardp(d_ice_mass_gp, tmp_device_real_cell); + thrust_device::vector &d_ice_mass = d_ice_mass_gp->get(); + + 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(d_ice_mass.begin(), d_ice_mass.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(d_ice_mass.begin(), count_ijk.begin()), + thrust::negate() + ); + } + } + }; +}; diff --git a/src/impl/condensation/common/apply_perparticle_sgs_supersat.ipp b/src/impl/condensation/common/apply_perparticle_sgs_supersat.ipp new file mode 100644 index 000000000..bce3a2800 --- /dev/null +++ b/src/impl/condensation/common/apply_perparticle_sgs_supersat.ipp @@ -0,0 +1,18 @@ +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::apply_perparticle_sgs_supersat() + { + namespace arg = thrust::placeholders; + + thrust::transform( + ssp.begin(), ssp.end(), + dot_ssp.begin(), + ssp.begin(), + arg::_1 + dt / sstp_cond * arg::_2 + ); + } + }; +}; diff --git a/src/impl/particles_impl_cond_common.ipp b/src/impl/condensation/common/particles_impl_cond_common.ipp similarity index 56% rename from src/impl/particles_impl_cond_common.ipp rename to src/impl/condensation/common/particles_impl_cond_common.ipp index d24b4861f..49b61bc9f 100644 --- a/src/impl/particles_impl_cond_common.ipp +++ b/src/impl/condensation/common/particles_impl_cond_common.ipp @@ -5,7 +5,7 @@ * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) */ -#include +// #include #include #include #include @@ -25,11 +25,13 @@ namespace libcloudphxx { real_t mlt; int n_dims; + + BOOST_GPU_ENABLED rw3diff2drv(const real_t &mlt, const int &n_dims): mlt(mlt), n_dims(n_dims) {} BOOST_GPU_ENABLED - real_t operator()(const real_t &rw3diff, const thrust::tuple &tpl) + real_t operator()(const real_t &rw3diff, const thrust::tuple &tpl) noexcept { if(n_dims > 0) return mlt * rw3diff * thrust::get<1>(tpl) / thrust::get<0>(tpl) / thrust::get<2>(tpl); @@ -38,19 +40,41 @@ namespace libcloudphxx } }; - template - struct rw2torw3 //: thrust::unary_function - { - BOOST_GPU_ENABLED - real_t operator()(const real_t &rw2) - { -#if !defined(__NVCC__) - using std::sqrt; -#endif - const real_t rw = sqrt(rw2); - return rw2 * rw; - } - }; + template + struct rw2torwX + { + BOOST_GPU_ENABLED + real_t operator()(const real_t &rw2) noexcept + { + #if !defined(__NVCC__) + using std::pow; + #endif + return pow(rw2, real_t(power) / real_t(2)); + } + }; + + template + struct rw2torwX + { + BOOST_GPU_ENABLED + real_t operator()(const real_t &rw2) noexcept + { + #if !defined(__NVCC__) + using std::sqrt; + #endif + return rw2 * sqrt(rw2); + } + }; + + template + struct rw2torwX + { + BOOST_GPU_ENABLED + real_t operator()(const real_t &rw2) noexcept + { + return rw2; + } + }; template struct advance_rw2_minfun @@ -95,7 +119,7 @@ namespace libcloudphxx {} BOOST_GPU_ENABLED - quantity::type, real_t> drw2_dt(const quantity &rw2) const + quantity::type, real_t> drw2_dt(const quantity &rw2) const noexcept { using namespace common::maxwell_mason; using namespace common::kappa_koehler; @@ -149,13 +173,15 @@ namespace libcloudphxx } }; - template + template struct advance_rw2 { - const real_t dt, RH_max; - detail::config config; + const real_t dt, RH_max, cond_mlt; + const common::detail::eps_tolerance eps_tolerance; + const uintmax_t n_iter; - advance_rw2(const real_t &dt, const real_t &RH_max) : dt(dt), RH_max(RH_max) {} + BOOST_GPU_ENABLED + advance_rw2(const real_t &dt, const real_t &RH_max, const common::detail::eps_tolerance &eps_tolerance, const real_t &cond_mlt, const uintmax_t &n_iter_) : dt(dt), RH_max(RH_max), eps_tolerance(eps_tolerance), cond_mlt(cond_mlt), n_iter(n_iter_) {} BOOST_GPU_ENABLED real_t operator()( @@ -213,17 +239,29 @@ namespace libcloudphxx } #endif - if (drw2 == 0) return rw2_old; + if (drw2 == 0) + { + if constexpr (apply) + return rw2_old; + else + return real_t(0); + } const real_t rd = cbrt(thrust::get<4>(tpl_in)); const real_t rd2 = rd*rd; const real_t - a = max(rd2, rw2_old + min(real_t(0), config.cond_mlt * drw2)), - b = rw2_old + max(real_t(0), config.cond_mlt * drw2); + a = max(rd2, rw2_old + min(real_t(0), cond_mlt * drw2)), + b = rw2_old + max(real_t(0), cond_mlt * drw2); // numerics (drw2 != 0 but a==b) - if (a == b) return rw2_old; + if (a == b) + { + if constexpr (apply) + return rw2_old; + else + return real_t(0); + } real_t fa, fb; @@ -270,11 +308,12 @@ namespace libcloudphxx // root-finding ill posed => explicit Euler if (fa * fb > 0) rw2_new = rw2_old + drw2; // otherwise implicit Euler - else + else { - uintmax_t n_iter = config.n_iter; - rw2_new = common::detail::toms748_solve(f, a, b, fa, fb, config.eps_tolerance, n_iter); + auto _n_iter = n_iter; // we need a copy because toms748_solve expects non-const ref (n_iter is modified by it) + rw2_new = common::detail::toms748_solve(f, a, b, fa, fb, eps_tolerance, _n_iter); } + // check if it doesn't evaporate too much if(rw2_new < rd2) rw2_new = rd2; @@ -291,7 +330,145 @@ namespace libcloudphxx assert(0); } #endif - return rw2_new; + if constexpr (apply) + return rw2_new; + else + return rw2_new - rw2_old; + } + }; + + 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;; + + 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 + ); + } + + 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_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> &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); + + // Skip liquid droplets + if (a_old <= 0 || c_old <= 0) + return ac_old; + + 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)) + * 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)); + 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/condensation/common/sstp_save.ipp b/src/impl/condensation/common/sstp_save.ipp new file mode 100644 index 000000000..ed4a89473 --- /dev/null +++ b/src/impl/condensation/common/sstp_save.ipp @@ -0,0 +1,32 @@ + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::sstp_save() + { + if (!allow_sstp_cond) return; + + const int n = 4; + thrust_device::vector + *fr[n] = { &rv, &th, &rhod, &p }, + *to[n] = { &sstp_tmp_rv, &sstp_tmp_th, &sstp_tmp_rh, &sstp_tmp_p }; + for (int ix = 0; ix < ( (opts_init.const_p && opts_init.exact_sstp_cond) ? n : n-1); ++ix) // TODO: var_rho + { + if(opts_init.exact_sstp_cond) // per-particle version + thrust::copy( + thrust::make_permutation_iterator(fr[ix]->begin(), ijk.begin()), + thrust::make_permutation_iterator(fr[ix]->begin(), ijk.end()), + to[ix]->begin() + ); + else // per-cell version + thrust::copy( + fr[ix]->begin(), + fr[ix]->end(), + to[ix]->begin() + ); + } + } + }; +}; \ No newline at end of file diff --git a/src/impl/particles_impl_cond.ipp b/src/impl/condensation/percell/particles_impl_cond.ipp similarity index 52% rename from src/impl/particles_impl_cond.ipp rename to src/impl/condensation/percell/particles_impl_cond.ipp index 8df6b7e5b..19d82878b 100644 --- a/src/impl/particles_impl_cond.ipp +++ b/src/impl/condensation/percell/particles_impl_cond.ipp @@ -19,43 +19,28 @@ namespace libcloudphxx 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_liq_g = tmp_device_real_cell.get_guard(); - thrust_device::vector &drv_liq = drv_liq_g.get(); + thrust_device::vector &lambda_D(lambda_D_gp->get()); + thrust_device::vector &lambda_K(lambda_K_gp->get()); + + hskpng_sort(); 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) + // drw_mom3 = -rw_mom3 assumed to be done beforehand in save_liq_ice_content_before_change + 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() - ); + if(count_n!=n_cell) // NOTE: we rely that moms_calc was called recently (e.g. in save_liq_ice_content_before_change) + thrust::fill(rw_mom3.begin(), rw_mom3.end(), real_t(0.)); } else // copy rw_mom3 from previous step { - // drv = -rw_mom3 precond + reset_guardp(drw_mom3_gp, tmp_device_real_cell); + thrust_device::vector &drw_mom3 = drw_mom3_gp->get(); + // drw_mom3 = -rw_mom3 precond thrust::transform( - rw_mom3.begin(), rw_mom3.end(), - drv_liq.begin(), + rw_mom3.begin(), rw_mom3.end(), + drw_mom3.begin(), thrust::negate() ); } @@ -96,7 +81,7 @@ namespace libcloudphxx ) ), rw2.begin(), // output - detail::advance_rw2(dt, RH_max) + detail::advance_rw2(dt / sstp_cond, RH_max, config.eps_tolerance, config.cond_mlt, config.n_iter) ); } else @@ -112,45 +97,45 @@ namespace libcloudphxx ) ), rw2.begin(), // output - detail::advance_rw2(dt, RH_max) + detail::advance_rw2(dt / sstp_cond, RH_max, config.eps_tolerance, config.cond_mlt, config.n_iter) ); - nancheck(rw2, "rw2 after condensation (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_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 - ); + // calculating the 3rd wet moment after condensation + thrust_device::vector &drw_mom3 = drw_mom3_gp->get(); + 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) after condensation"); - // 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 - } + 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 + ); - // update th and rv according to change in third specific wet moment - update_th_rv(drv_liq, impl::phase_change::condensation); + // adding the third moment after condensation to dm_3 + thrust::transform( + rw_mom3.begin(), rw_mom3.end(), + drw_mom3.begin(), + drw_mom3.begin(), + thrust::plus() + ); } + else // last step, calculate change and directly add it without storing rw_mom3 + { + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(drw_mom3.begin(), count_ijk.begin()), // input - 2nd arg + thrust::make_permutation_iterator(drw_mom3.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 changes in third specific wet moment (drw_mom3) + // update_th_rv(impl::phase_change::condensation); }; - } \ No newline at end of file + } +}; \ No newline at end of file diff --git a/src/impl/condensation/percell/sstp_percell_step.ipp b/src/impl/condensation/percell/sstp_percell_step.ipp new file mode 100644 index 000000000..4d909113a --- /dev/null +++ b/src/impl/condensation/percell/sstp_percell_step.ipp @@ -0,0 +1,50 @@ + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::sstp_percell_step( + const int &step + ) + { + if (sstp_cond == 1) return; + + namespace arg = thrust::placeholders; + + const int n = 3; + thrust_device::vector + *scl[n] = { &rv, &th, &rhod }, + *tmp[n] = { &sstp_tmp_rv, &sstp_tmp_th, &sstp_tmp_rh }; + + for (int ix = 0; ix < (var_rho ? n : n-1); ++ix) + { + const real_t sstp = sstp_cond; + if (step == 0) + { + thrust::transform( + scl[ix]->begin(), scl[ix]->end(), + tmp[ix]->begin(), + tmp[ix]->begin(), + arg::_1 - arg::_2 + ); + thrust::transform( + scl[ix]->begin(), scl[ix]->end(), + tmp[ix]->begin(), + scl[ix]->begin(), + arg::_1 - (sstp - 1) * arg::_2 / sstp + ); + } + else + { + thrust::transform( + scl[ix]->begin(), scl[ix]->end(), + tmp[ix]->begin(), + scl[ix]->begin(), + arg::_1 + arg::_2 / sstp + ); + } + } + } + }; +}; \ No newline at end of file diff --git a/src/impl/condensation/perparticle/acquire_arrays_for_perparticle_sstp.ipp b/src/impl/condensation/perparticle/acquire_arrays_for_perparticle_sstp.ipp new file mode 100644 index 000000000..1f890168f --- /dev/null +++ b/src/impl/condensation/perparticle/acquire_arrays_for_perparticle_sstp.ipp @@ -0,0 +1,28 @@ + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::acquire_arrays_for_perparticle_sstp() + { + reset_guardp(sstp_dlt_rv_gp, tmp_device_real_part); + reset_guardp(sstp_dlt_th_gp, tmp_device_real_part); + reset_guardp(sstp_dlt_rhod_gp, tmp_device_real_part); + // if(opts_init.const_p) + reset_guardp(sstp_dlt_p_gp, tmp_device_real_part); + + if(!sstp_cond_exact_nomix_adaptive) + { + reset_guardp(rwX_gp, tmp_device_real_part); + reset_guardp(drwX_gp, tmp_device_real_part); + reset_guardp(Tp_gp, tmp_device_real_part); + } + + if(opts_init.adaptive_sstp_cond) + { + reset_guardp(perparticle_sstp_cond_gp, tmp_device_n_part); + } + } + }; +}; diff --git a/src/impl/condensation/perparticle/add_perparticle_rwX_to_drwX.ipp b/src/impl/condensation/perparticle/add_perparticle_rwX_to_drwX.ipp new file mode 100644 index 000000000..7fb539b3d --- /dev/null +++ b/src/impl/condensation/perparticle/add_perparticle_rwX_to_drwX.ipp @@ -0,0 +1,40 @@ +namespace libcloudphxx +{ + namespace lgrngn + { + template + template + void particles_t::impl::add_perparticle_rwX_to_drwX(const bool store_rw3) + { + thrust_device::vector &drwX = drwX_gp->get(); + if(store_rw3) + { + assert(power == 3); + thrust_device::vector &rwX = rwX_gp->get(); + thrust::transform( + rw2.begin(), rw2.end(), + rwX.begin(), + detail::rw2torwX() + ); + + thrust::transform( + rwX.begin(), rwX.end(), + drwX.begin(), + drwX.begin(), + thrust::plus() + ); + } + else + { + thrust::transform( + thrust::make_transform_iterator(rw2.begin(), detail::rw2torwX()), + thrust::make_transform_iterator(rw2.end(), detail::rw2torwX()), + drwX.begin(), + drwX.begin(), + thrust::plus() + ); + } + } + }; +}; + diff --git a/src/impl/condensation/perparticle/apply_noncond_perparticle_sstp_delta.ipp b/src/impl/condensation/perparticle/apply_noncond_perparticle_sstp_delta.ipp new file mode 100644 index 000000000..8a13be4cc --- /dev/null +++ b/src/impl/condensation/perparticle/apply_noncond_perparticle_sstp_delta.ipp @@ -0,0 +1,34 @@ + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::apply_noncond_perparticle_sstp_delta() + { + namespace arg = thrust::placeholders; + + const int n = 4; + + thrust_device::vector + *tmp[n] = { &sstp_tmp_rv, &sstp_tmp_th, &sstp_tmp_rh, &sstp_tmp_p }, + *dlt[n]; + + dlt[0] = &(sstp_dlt_rv_gp->get()); + dlt[1] = &(sstp_dlt_th_gp->get()); + dlt[2] = &(sstp_dlt_rhod_gp->get()); + if(opts_init.const_p) + dlt[3] = &(sstp_dlt_p_gp->get()); + + for (int ix = 0; ix < (opts_init.const_p ? n : n-1); ++ix) + { + thrust::transform( + tmp[ix]->begin(), tmp[ix]->end(), + dlt[ix]->begin(), + tmp[ix]->begin(), + arg::_1 + arg::_2 / sstp_cond + ); + } + } + }; +}; \ No newline at end of file diff --git a/src/impl/condensation/perparticle/apply_perparticle_cond_change_to_percell_rv_and_th.ipp b/src/impl/condensation/perparticle/apply_perparticle_cond_change_to_percell_rv_and_th.ipp new file mode 100644 index 000000000..3d92fad73 --- /dev/null +++ b/src/impl/condensation/perparticle/apply_perparticle_cond_change_to_percell_rv_and_th.ipp @@ -0,0 +1,27 @@ +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::apply_perparticle_cond_change_to_percell_rv_and_th() + { + if(opts_init.sstp_cond_mix) + { + // with mixing, all SDs in a cell should arrive at the same sstp_tmp_rv/th; + // however, there may be some small divergences, as we see some differences in results + // of the physics/lgrngn_sstp_cond.py test between per-cell and per-particle substepping with sstp_cond_mix=True; + // these differences started to appear after changing the way how drw3 is applied to rv and th in per-particle substepping + // (commit e78392573eedc6acd74861d5a5ae57a82d2edff0). This commit did not affect per-particle substepping without mixing. + // Discussed divergences can be decreased (but not completly removed) by updating cell rv/th based on mom3, as done without mixing. + // Nevertheless, these divergences are small enough to be acceptable in most applications. + update_state(rv, sstp_tmp_rv); + update_state(th, sstp_tmp_th); + } + else + { + calc_liq_ice_content_change(); + update_th_rv(); + } + } + }; +}; \ No newline at end of file diff --git a/src/impl/condensation/perparticle/apply_perparticle_drw3_to_perparticle_rv_and_th.ipp b/src/impl/condensation/perparticle/apply_perparticle_drw3_to_perparticle_rv_and_th.ipp new file mode 100644 index 000000000..cb1343eb5 --- /dev/null +++ b/src/impl/condensation/perparticle/apply_perparticle_drw3_to_perparticle_rv_and_th.ipp @@ -0,0 +1,62 @@ +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::apply_perparticle_drw3_to_perparticle_rv_and_th() + { + namespace arg = thrust::placeholders; + thrust_device::vector &drw3 = drwX_gp->get(); + thrust_device::vector &Tp = Tp_gp->get(); + + thrust::transform( + drw3.begin(), drw3.end(), + thrust::make_zip_iterator(thrust::make_tuple( + sstp_tmp_rh.begin(), + n.begin(), + thrust::make_permutation_iterator(dv.begin(), ijk.begin()) + )), + drw3.begin(), + detail::rw3diff2drv( + - common::moist_air::rho_w() / si::kilograms * si::cubic_metres + * real_t(4./3) * pi(), n_dims + ) + ); + + if(opts_init.sstp_cond_mix) + update_pstate(sstp_tmp_rv, drw3); + else + thrust::transform( + drw3.begin(), drw3.end(), + sstp_tmp_rv.begin(), + sstp_tmp_rv.begin(), + thrust::plus() + ); + + thrust::transform( + thrust::make_zip_iterator(thrust::make_tuple( + drw3.begin(), + Tp.begin(), + sstp_tmp_th.begin() + )), + thrust::make_zip_iterator(thrust::make_tuple( + drw3.end(), + Tp.end(), + sstp_tmp_th.end() + )), + drw3.begin(), + detail::dth() + ); + + if(opts_init.sstp_cond_mix) + update_pstate(sstp_tmp_th, drw3); + else + thrust::transform( + drw3.begin(), drw3.end(), + sstp_tmp_th.begin(), + sstp_tmp_th.begin(), + thrust::plus() + ); + } + }; +}; diff --git a/src/impl/condensation/perparticle/calculate_noncond_perparticle_sstp_delta.ipp b/src/impl/condensation/perparticle/calculate_noncond_perparticle_sstp_delta.ipp new file mode 100644 index 000000000..9cf10e1c1 --- /dev/null +++ b/src/impl/condensation/perparticle/calculate_noncond_perparticle_sstp_delta.ipp @@ -0,0 +1,36 @@ + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::calculate_noncond_perparticle_sstp_delta() + { + namespace arg = thrust::placeholders; + + const int n = 4; + thrust_device::vector + *scl[n] = { &rv, &th, &rhod, &p }, + *tmp[n] = { &sstp_tmp_rv, &sstp_tmp_th, &sstp_tmp_rh, &sstp_tmp_p }, + *dlt[n]; + + dlt[0] = &(sstp_dlt_rv_gp->get()); + dlt[1] = &(sstp_dlt_th_gp->get()); + dlt[2] = &(sstp_dlt_rhod_gp->get()); + if(opts_init.const_p) + dlt[3] = &(sstp_dlt_p_gp->get()); + + for (int ix = 0; ix < (opts_init.const_p ? n : n-1); ++ix) + { + const real_t sstp = sstp_cond; + thrust::transform( + thrust::make_permutation_iterator(scl[ix]->begin(), ijk.begin()), + thrust::make_permutation_iterator(scl[ix]->begin(), ijk.end()), + tmp[ix]->begin(), + dlt[ix]->begin(), + (arg::_1 - arg::_2) // / sstp + ); + } + } + }; +}; \ No newline at end of file diff --git a/src/impl/condensation/perparticle/cond_perparticle_advance_rw2.ipp b/src/impl/condensation/perparticle/cond_perparticle_advance_rw2.ipp new file mode 100644 index 000000000..cd084e412 --- /dev/null +++ b/src/impl/condensation/perparticle/cond_perparticle_advance_rw2.ipp @@ -0,0 +1,128 @@ +namespace libcloudphxx +{ + namespace lgrngn + { + namespace detail + { + template + struct RH_sgs + { + RH resolved_RH; + + BOOST_GPU_ENABLED + RH_sgs(RH_formula_t RH_formula): + resolved_RH(RH_formula) + {} + + BOOST_GPU_ENABLED + real_t operator()(const thrust::tuple &tpl) noexcept + { + return resolved_RH(thrust::make_tuple(thrust::get<0>(tpl), thrust::get<1>(tpl), thrust::get<2>(tpl))) + thrust::get<3>(tpl); + } + }; + }; + + + template + void particles_t::impl::cond_perparticle_advance_rw2( + const real_t &RH_max, + const bool turb_cond + ) { + + namespace arg = thrust::placeholders; + + thrust_device::vector &Tp = Tp_gp->get(); + + // calculate perparticle temperature; TODO: skip it and use theta in drw2? + if(opts_init.th_dry) + { + thrust::transform( + sstp_tmp_th.begin(), sstp_tmp_th.end(), + sstp_tmp_rh.begin(), + Tp.begin(), + detail::common__theta_dry__T_rhod() + ); + } + else + { + thrust::transform( + sstp_tmp_th.begin(), sstp_tmp_th.end(), + thrust::make_zip_iterator(thrust::make_tuple( + sstp_tmp_rv.begin(), + sstp_tmp_p.begin() + )), + Tp.begin(), + detail::common__theta_std__T_p() + ); + } + + // advance rw2 + if(!opts_init.const_p) + { + auto pressure_iter = thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple( + sstp_tmp_rh.begin(), + sstp_tmp_rv.begin(), + Tp.begin() + )), + detail::common__theta_dry__p() + ); + + if(turb_cond) + perparticle_advance_rw2(RH_max, Tp, + pressure_iter, + thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple( + pressure_iter, + sstp_tmp_rv.begin(), + Tp.begin(), + ssp.begin() + )), + detail::RH_sgs(opts_init.RH_formula) + ) + ); + else + perparticle_advance_rw2 + (RH_max, Tp, + pressure_iter, + 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) + ) + ); + } + else + { + if(turb_cond) + perparticle_advance_rw2(RH_max, Tp, + sstp_tmp_p.begin(), + thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple( + sstp_tmp_p.begin(), + sstp_tmp_rv.begin(), + Tp.begin(), + ssp.begin() + )), + detail::RH_sgs(opts_init.RH_formula) + ) + ); + else + perparticle_advance_rw2(RH_max, Tp, + sstp_tmp_p.begin(), + thrust::make_transform_iterator( + thrust::make_zip_iterator(thrust::make_tuple( + sstp_tmp_p.begin(), + sstp_tmp_rv.begin(), + Tp.begin() + )), + detail::RH(opts_init.RH_formula) + ) + ); + } + } + }; +}; diff --git a/src/impl/condensation/perparticle/init_perparticle_sstp.ipp b/src/impl/condensation/perparticle/init_perparticle_sstp.ipp new file mode 100644 index 000000000..7b3a0d092 --- /dev/null +++ b/src/impl/condensation/perparticle/init_perparticle_sstp.ipp @@ -0,0 +1,25 @@ + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::init_perparticle_sstp() + { + if (!allow_sstp_cond || !opts_init.exact_sstp_cond) return; + + const int n = 4; + thrust_device::vector + *fr[n] = { &rv, &th, &rhod, &p }, + *to[n] = { &sstp_tmp_rv, &sstp_tmp_th, &sstp_tmp_rh, &sstp_tmp_p }; + for (int ix = 0; ix < ( (opts_init.const_p) ? n : n-1); ++ix) // TODO: var_rho + { + thrust::copy( + thrust::make_permutation_iterator(fr[ix]->begin(), ijk.begin()+n_part_old), + thrust::make_permutation_iterator(fr[ix]->begin(), ijk.end()), + to[ix]->begin()+n_part_old + ); + } + } + }; +}; \ No newline at end of file diff --git a/src/impl/condensation/perparticle/perparticle_advance_rw2.ipp b/src/impl/condensation/perparticle/perparticle_advance_rw2.ipp new file mode 100644 index 000000000..8386c1c55 --- /dev/null +++ b/src/impl/condensation/perparticle/perparticle_advance_rw2.ipp @@ -0,0 +1,45 @@ +namespace libcloudphxx +{ + namespace lgrngn + { + template + template + void particles_t::impl::perparticle_advance_rw2( + const real_t &RH_max, + const thrust_device::vector &Tp, + const pres_iter &pi, + const RH_iter &rhi + ) { + thrust_device::vector &lambda_D(lambda_D_gp->get()); + thrust_device::vector &lambda_K(lambda_K_gp->get()); + + namespace arg = thrust::placeholders; + + auto hlpr_zip_iter = thrust::make_zip_iterator(thrust::make_tuple( + sstp_tmp_rh.begin(), + sstp_tmp_rv.begin(), + Tp.begin(), + thrust::make_transform_iterator( + Tp.begin(), + detail::common__vterm__visc() + ), + rd3.begin(), + kpa.begin(), + vt.begin(), + thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), + thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()) + )); + + thrust::transform( + rw2.begin(), rw2.end(), + thrust::make_zip_iterator(thrust::make_tuple( + hlpr_zip_iter, + pi, + rhi + )), + rw2.begin(), + detail::advance_rw2(dt / sstp_cond, RH_max, config.eps_tolerance, config.cond_mlt, config.n_iter) + ); + } + }; +}; diff --git a/src/impl/condensation/perparticle/perparticle_nomixing_adaptive_sstp_cond.ipp b/src/impl/condensation/perparticle/perparticle_nomixing_adaptive_sstp_cond.ipp new file mode 100644 index 000000000..c1c03db58 --- /dev/null +++ b/src/impl/condensation/perparticle/perparticle_nomixing_adaptive_sstp_cond.ipp @@ -0,0 +1,331 @@ +namespace libcloudphxx +{ + namespace lgrngn + { + namespace detail + { + template + struct perparticle_nomixing_adaptive_sstp_cond_loop + { + const bool th_dry, const_p, turb_cond, adaptive_sstp_cond; + const real_t dt, RH_max, cond_mlt, sstp_cond_adapt_drw2_eps, sstp_cond_adapt_drw2_max; + const common::detail::eps_tolerance eps_tolerance; + const int n_dims, sstp_cond_max, sstp_cond_act; + const RH_formula_t RH_formula; + uintmax_t n_iter; + + perparticle_nomixing_adaptive_sstp_cond_loop( + const opts_init_t &opts_init, + const opts_t &opts, + const int n_dims, + const real_t &dt, + const int &sstp_cond_max, + const int &sstp_cond_act, + const common::detail::eps_tolerance &eps_tolerance, + const real_t &cond_mlt, + const uintmax_t &n_iter + ) : th_dry(opts_init.th_dry), + const_p(opts_init.const_p), + turb_cond(opts.turb_cond), + dt(dt), + RH_max(opts.RH_max), + n_dims(n_dims), + RH_formula(opts_init.RH_formula), + eps_tolerance(eps_tolerance), + cond_mlt(cond_mlt), + n_iter(n_iter), + adaptive_sstp_cond(opts_init.adaptive_sstp_cond), + sstp_cond_act(sstp_cond_act), + sstp_cond_max(sstp_cond_max), + sstp_cond_adapt_drw2_eps(opts_init.sstp_cond_adapt_drw2_eps), + sstp_cond_adapt_drw2_max(opts_init.sstp_cond_adapt_drw2_max) + {} + + template + BOOST_GPU_ENABLED void operator()( + tpl_t tpl + ) //noexcept + { + // copy values into local variables + // variables that are not modified + const real_t sstp_dlt_rv = thrust::get<5>(thrust::get<0>(tpl)); + const real_t sstp_dlt_th = thrust::get<6>(thrust::get<0>(tpl)); + const real_t sstp_dlt_rhod = thrust::get<7>(thrust::get<0>(tpl)); + const real_t sstp_dlt_p = thrust::get<8>(thrust::get<0>(tpl)); + const auto n = thrust::get<2>(thrust::get<1>(tpl)); + const real_t dv = thrust::get<3>(thrust::get<1>(tpl)); + const real_t lambda_D = thrust::get<4>(thrust::get<1>(tpl)); + const real_t lambda_K = thrust::get<5>(thrust::get<1>(tpl)); + const real_t rd3 = thrust::get<6>(thrust::get<1>(tpl)); + const real_t kpa = thrust::get<0>(thrust::get<2>(tpl)); + const real_t vt = thrust::get<1>(thrust::get<2>(tpl)); + const real_t dot_ssp = turb_cond ? thrust::get<0>(thrust::get<1>(tpl)) : 0; + + // variables that are modified, we make local copies regardless and copy back at the end + unsigned int sstp_cond; // its set in this function, old value not important + real_t sstp_tmp_rv = thrust::get<1>(thrust::get<0>(tpl)); + real_t sstp_tmp_th = thrust::get<2>(thrust::get<0>(tpl)); + real_t sstp_tmp_rh = thrust::get<3>(thrust::get<0>(tpl)); + real_t sstp_tmp_p = const_p ? thrust::get<4>(thrust::get<0>(tpl)) : 0; + real_t ssp = turb_cond ? thrust::get<9>(thrust::get<0>(tpl)) : 0; + real_t rw2 = thrust::get<1>(thrust::get<1>(tpl)); + + real_t drw2, Tp, RH; + + // helper functions + auto _apply_noncond_perparticle_sstp_delta = [&] (const real_t &multiplier) -> void + { + sstp_tmp_rv += sstp_dlt_rv * multiplier; + sstp_tmp_th += sstp_dlt_th * multiplier; + sstp_tmp_rh += sstp_dlt_rhod * multiplier; + if(const_p) + sstp_tmp_p += sstp_dlt_p * multiplier; + if(turb_cond) + ssp += dot_ssp * dt * multiplier; + }; + + auto _calc_Tp = [&] () -> void + { + if(th_dry) + Tp = detail::common__theta_dry__T_rhod()(sstp_tmp_th, sstp_tmp_rh); + else + Tp = detail::common__theta_std__T_p()(sstp_tmp_th, + thrust::make_tuple(sstp_tmp_rv, sstp_tmp_p) + ); + }; + + auto _calc_sstp_tmp_p = [&] () -> void + { + if(!const_p) // sstp_tmp_p needs to be allocated even without const_p! + sstp_tmp_p = detail::common__theta_dry__p()( + thrust::make_tuple(sstp_tmp_rh, sstp_tmp_rv, Tp) + ); + }; + + auto _calc_RH = [&] () -> void + { + RH = turb_cond ? + detail::RH_sgs(RH_formula)( + thrust::make_tuple(sstp_tmp_p, sstp_tmp_rv, Tp, ssp) + ) : + detail::RH(RH_formula)( + thrust::make_tuple(sstp_tmp_p, sstp_tmp_rv, Tp) + ); + }; + + // bool converged = false; + real_t delta_fraction_applied; + bool first_cond_step_done_in_adaptation = sstp_cond_max == 1 ? true : false; // actually its true if sstp_cond_max is a power of 2 (?) + // bool activates = false; + + // look for correct number of substeps + // NOTE: this function is actually only called when adaptive_sstp_cond == true, so we skip the check below + // if(adaptive_sstp_cond) + { + real_t drw2_new; + // real_t Tp; // temperature + + sstp_cond = sstp_cond_max; // start with max number of substeps, may be changed due to convergence or if droplets activate in this step + + // check drw convergence for increasing number of substeps + for(int sstp_cond_try = 1; sstp_cond_try <= sstp_cond_max; sstp_cond_try*=2) + { + delta_fraction_applied = sstp_cond_try == 1 ? 1 : -real_t(1) / sstp_cond_try; + _apply_noncond_perparticle_sstp_delta(delta_fraction_applied); + // _cond_perparticle_drw2(sstp_cond_try, sstp_cond_try == 1 ? drw2 : drw2_new); // also updates Tp! + + + _calc_Tp(); + _calc_sstp_tmp_p(); + _calc_RH(); + (sstp_cond_try == 1 ? drw2 : drw2_new) = + detail::advance_rw2(dt / sstp_cond_try, RH_max, eps_tolerance, cond_mlt, n_iter)( + rw2, + thrust::make_tuple( + thrust::make_tuple( + sstp_tmp_rh, + sstp_tmp_rv, + Tp, + detail::common__vterm__visc()(Tp), + rd3, + kpa, + vt, + lambda_D, + lambda_K + ), + sstp_tmp_p, + RH + ) + ); + + if(sstp_cond_try > 1) // check for convergence + { + if((cuda::std::abs(drw2_new * 2 - drw2) <= sstp_cond_adapt_drw2_eps * rw2) // drw2 relative to rw2 converged + && cuda::std::abs(drw2 < sstp_cond_adapt_drw2_max * rw2)) // otherwise for small droplets (near activation?) drw2_new == 2*drw already for 2 substeps, but we ativate too many droplets + // if(cuda::std::abs(drw2_new * 2 - drw2) <= tol * drw2) // drw2 converged + { + sstp_cond = sstp_cond_try / 2; + _apply_noncond_perparticle_sstp_delta(-delta_fraction_applied); // revert last addition to get to a state after one step of converged number + first_cond_step_done_in_adaptation = true; + break; + } + drw2 = drw2_new; + } + } + + // override number of substeps for SDs that de/activate in this timestep; + if(sstp_cond_act > 1) + { + const real_t rc2 = thrust::get<2>(thrust::get<2>(tpl)); + + if ( ( rw2 < rc2 && (rw2 + sstp_cond * drw2) > rc2 ) || + ( rw2 > rc2 && (rw2 + sstp_cond * drw2) < rc2 ) ) + { + sstp_cond = sstp_cond_act; + first_cond_step_done_in_adaptation = false; + } + } + if(!first_cond_step_done_in_adaptation) + { + _apply_noncond_perparticle_sstp_delta(delta_fraction_applied); // revert to state before adaptation loop (beacause sstp_cond == sstp_cond_max and sstp_cond_max may not be a power of 2) + } + } + + delta_fraction_applied = real_t(1) / sstp_cond; + auto _advance_rw2 = detail::advance_rw2(dt / sstp_cond, RH_max, eps_tolerance, cond_mlt, n_iter); + real_t &rw3 = drw2; // drw2 needed only at the start of the first step + real_t drw3; + + auto rw2torw3 = detail::rw2torwX(); + + // actual condensation substepping + for(int step = 0; step < sstp_cond; ++step) + { + drw3 = step > 0 ? -rw3 : -rw2torw3(rw2); + + if(first_cond_step_done_in_adaptation && step == 0) + { + rw2 += drw2; + } + else + { + _apply_noncond_perparticle_sstp_delta(delta_fraction_applied); + _calc_Tp(); + _calc_sstp_tmp_p(); + _calc_RH(); + + rw2 = _advance_rw2( + rw2, + thrust::make_tuple( + thrust::make_tuple( + sstp_tmp_rh, + sstp_tmp_rv, + Tp, + detail::common__vterm__visc()(Tp), + rd3, + kpa, + vt, + lambda_D, + lambda_K + ), + sstp_tmp_p, + RH + ) + ); + } + + if (step < sstp_cond - 1) + { + rw3 = rw2torw3(rw2); + drw3 += rw3; + } + else + drw3 += rw2torw3(rw2); + + drw3 = detail::rw3diff2drv( + - common::moist_air::rho_w() / si::kilograms * si::cubic_metres + * real_t(4./3) * real_t(3.14159265358979323846264338), n_dims + ) (drw3, + thrust::make_tuple(sstp_tmp_rh, n, dv) + ); + + sstp_tmp_rv += drw3; + + drw3 = detail::dth()( + thrust::make_tuple(drw3, Tp, sstp_tmp_th) + ); + + sstp_tmp_th += drw3; + } + + // copy back modified variables + thrust::get<0>(thrust::get<0>(tpl)) = sstp_cond; + thrust::get<1>(thrust::get<0>(tpl)) = sstp_tmp_rv; + thrust::get<2>(thrust::get<0>(tpl)) = sstp_tmp_th; + thrust::get<3>(thrust::get<0>(tpl)) = sstp_tmp_rh; + if(const_p) + thrust::get<4>(thrust::get<0>(tpl)) = sstp_tmp_p; + if(turb_cond) + thrust::get<9>(thrust::get<0>(tpl)) = ssp; + thrust::get<1>(thrust::get<1>(tpl)) = rw2; + } + }; + }; + + template + void particles_t::impl::perparticle_nomixing_adaptive_sstp_cond(const opts_t &opts) { + + auto &perparticle_sstp_cond = perparticle_sstp_cond_gp->get(); + auto &sstp_dlt_rv = sstp_dlt_rv_gp->get(); + auto &sstp_dlt_th = sstp_dlt_th_gp->get(); + auto &sstp_dlt_rhod = sstp_dlt_rhod_gp->get(); + auto &sstp_dlt_p = sstp_dlt_p_gp->get(); + // auto &Tp = Tp_gp->get(); + // auto &drwX = drwX_gp->get(); + // auto &rwX = rwX_gp->get(); + const auto &lambda_D = lambda_D_gp->get(); + const auto &lambda_K = lambda_K_gp->get(); + + auto pptcl_nomix_sstp_cond_args_zip = + thrust::make_zip_iterator(thrust::make_tuple( + thrust::make_zip_iterator(thrust::make_tuple( + perparticle_sstp_cond.begin(), + sstp_tmp_rv.begin(), + sstp_tmp_th.begin(), + sstp_tmp_rh.begin(), + sstp_tmp_p.begin(), + sstp_dlt_rv.begin(), + sstp_dlt_th.begin(), + sstp_dlt_rhod.begin(), + sstp_dlt_p.begin(), + ssp.begin() + )), + thrust::make_zip_iterator(thrust::make_tuple( + dot_ssp.begin(), + // Tp.begin(), + // drwX.begin(), + // rwX.begin(), + rw2.begin(), + n.begin(), + thrust::make_permutation_iterator(dv.begin(), ijk.begin()), + thrust::make_permutation_iterator(lambda_D.begin(), ijk.begin()), + thrust::make_permutation_iterator(lambda_K.begin(), ijk.begin()), + rd3.begin() + )), + thrust::make_zip_iterator(thrust::make_tuple( + kpa.begin(), + vt.begin(), + rc2.begin() + )) + )); + + thrust::for_each( + pptcl_nomix_sstp_cond_args_zip, + pptcl_nomix_sstp_cond_args_zip + n_part, + detail::perparticle_nomixing_adaptive_sstp_cond_loop( + opts_init, opts, n_dims, dt, sstp_cond, sstp_cond_act, config.eps_tolerance, config.cond_mlt, config.n_iter + ) + ); + } + }; +}; diff --git a/src/impl/condensation/perparticle/release_arrays_for_perparticle_sstp.ipp b/src/impl/condensation/perparticle/release_arrays_for_perparticle_sstp.ipp new file mode 100644 index 000000000..ea8d4de6c --- /dev/null +++ b/src/impl/condensation/perparticle/release_arrays_for_perparticle_sstp.ipp @@ -0,0 +1,28 @@ + +namespace libcloudphxx +{ + namespace lgrngn + { + template + void particles_t::impl::release_arrays_for_perparticle_sstp() + { + sstp_dlt_rv_gp.reset(); + sstp_dlt_th_gp.reset(); + sstp_dlt_rhod_gp.reset(); + // if(opts_init.const_p) + sstp_dlt_p_gp.reset(); + + if(!sstp_cond_exact_nomix_adaptive) + { + rwX_gp.reset(); + drwX_gp.reset(); + Tp_gp.reset(); + } + + if(opts_init.adaptive_sstp_cond) + { + perparticle_sstp_cond_gp.reset(); + } + } + }; +}; \ No newline at end of file diff --git a/src/impl/condensation/perparticle/set_perparticle_drwX_to_minus_rwX.ipp b/src/impl/condensation/perparticle/set_perparticle_drwX_to_minus_rwX.ipp new file mode 100644 index 000000000..d7e665162 --- /dev/null +++ b/src/impl/condensation/perparticle/set_perparticle_drwX_to_minus_rwX.ipp @@ -0,0 +1,32 @@ +namespace libcloudphxx +{ + namespace lgrngn + { + template + template + void particles_t::impl::set_perparticle_drwX_to_minus_rwX(const bool use_stored_rw3) + { + thrust_device::vector &drwX = drwX_gp->get(); + if(!use_stored_rw3) + { + thrust::transform( + thrust::make_transform_iterator(rw2.begin(), detail::rw2torwX()), + thrust::make_transform_iterator(rw2.end(), detail::rw2torwX()), + drwX.begin(), + thrust::negate() + ); + } + else + { + assert(power == 3); + thrust_device::vector &rw3 = rwX_gp->get(); + thrust::transform( + rw3.begin(), rw3.end(), + drwX.begin(), + thrust::negate() + ); + } + } + }; +}; + diff --git a/src/impl/particles_impl_fill_outbuf.ipp b/src/impl/diagnose_SD_attributes/particles_impl_fill_outbuf.ipp similarity index 100% rename from src/impl/particles_impl_fill_outbuf.ipp rename to src/impl/diagnose_SD_attributes/particles_impl_fill_outbuf.ipp diff --git a/src/impl/particles_impl_mass_dens.ipp b/src/impl/diagnose_SD_attributes/particles_impl_mass_dens.ipp similarity index 98% rename from src/impl/particles_impl_mass_dens.ipp rename to src/impl/diagnose_SD_attributes/particles_impl_mass_dens.ipp index d134b7891..9200514b8 100644 --- a/src/impl/particles_impl_mass_dens.ipp +++ b/src/impl/diagnose_SD_attributes/particles_impl_mass_dens.ipp @@ -37,7 +37,7 @@ namespace libcloudphxx // same as above // auto n_filtered_g = tmp_device_real_part.get_guard(); - thrust_device::vector &n_filtered = n_filtered_gp->get(); + auto &n_filtered = n_filtered_gp->get(); // number of SD in each cell casted to real_t auto count_num_real_t_g = tmp_device_real_cell.get_guard(); diff --git a/src/impl/particles_impl_moms.ipp b/src/impl/diagnose_SD_attributes/particles_impl_moms.ipp similarity index 95% rename from src/impl/particles_impl_moms.ipp rename to src/impl/diagnose_SD_attributes/particles_impl_moms.ipp index 502515447..1895149e0 100644 --- a/src/impl/particles_impl_moms.ipp +++ b/src/impl/diagnose_SD_attributes/particles_impl_moms.ipp @@ -52,8 +52,8 @@ namespace libcloudphxx hskpng_sort(); // reset_guardp(n_filtered_gp, tmp_device_real_part); - reset_guardp(n_filtered_gp, tmp_device_real_part); - thrust_device::vector &n_filtered = n_filtered_gp->get(); + reset_guardp(n_filtered_gp, tmp_device_real_part);; + auto &n_filtered = n_filtered_gp->get(); thrust::copy( n.begin(), n.end(), @@ -75,9 +75,9 @@ namespace libcloudphxx // transforming n -> n if within range, else 0 if(!cons) - reset_guardp(n_filtered_gp, tmp_device_real_part); + reset_guardp(n_filtered_gp, tmp_device_real_part);; - thrust_device::vector &n_filtered = n_filtered_gp->get(); + auto &n_filtered = n_filtered_gp->get(); if(!cons) thrust::transform( @@ -116,8 +116,8 @@ namespace libcloudphxx { hskpng_sort(); - reset_guardp(n_filtered_gp, tmp_device_real_part); - thrust_device::vector &n_filtered = n_filtered_gp->get(); + reset_guardp(n_filtered_gp, tmp_device_real_part);; + auto &n_filtered = n_filtered_gp->get(); thrust::transform( n.begin(), n.end(), // input - 1st arg @@ -139,8 +139,8 @@ namespace libcloudphxx { hskpng_sort(); - reset_guardp(n_filtered_gp, tmp_device_real_part); - thrust_device::vector &n_filtered = n_filtered_gp->get(); + reset_guardp(n_filtered_gp, tmp_device_real_part);; + auto &n_filtered = n_filtered_gp->get(); { namespace arg = thrust::placeholders; @@ -283,7 +283,7 @@ namespace libcloudphxx { assert(selected_before_counting); - thrust_device::vector &n_filtered = n_filtered_gp->get(); + auto &n_filtered = n_filtered_gp->get(); thrust::pair< thrust_device::vector::iterator, diff --git a/src/impl/particles_impl_update_incloud_time.ipp b/src/impl/diagnose_SD_attributes/particles_impl_update_incloud_time.ipp similarity index 100% rename from src/impl/particles_impl_update_incloud_time.ipp rename to src/impl/diagnose_SD_attributes/particles_impl_update_incloud_time.ipp diff --git a/src/impl/particles_impl_distmem_access.ipp b/src/impl/distributed_memory/particles_impl_distmem_access.ipp similarity index 100% rename from src/impl/particles_impl_distmem_access.ipp rename to src/impl/distributed_memory/particles_impl_distmem_access.ipp diff --git a/src/impl/particles_impl_mpi_exchange.ipp b/src/impl/distributed_memory/particles_impl_mpi_exchange.ipp similarity index 100% rename from src/impl/particles_impl_mpi_exchange.ipp rename to src/impl/distributed_memory/particles_impl_mpi_exchange.ipp diff --git a/src/impl/particles_impl_pack.ipp b/src/impl/distributed_memory/particles_impl_pack.ipp similarity index 100% rename from src/impl/particles_impl_pack.ipp rename to src/impl/distributed_memory/particles_impl_pack.ipp diff --git a/src/impl/particles_impl_post_copy.ipp b/src/impl/distributed_memory/particles_impl_post_copy.ipp similarity index 100% rename from src/impl/particles_impl_post_copy.ipp rename to src/impl/distributed_memory/particles_impl_post_copy.ipp diff --git a/src/impl/particles_impl_unpack.ipp b/src/impl/distributed_memory/particles_impl_unpack.ipp similarity index 100% rename from src/impl/particles_impl_unpack.ipp rename to src/impl/distributed_memory/particles_impl_unpack.ipp diff --git a/src/impl/particles_impl_xchng_courants.ipp b/src/impl/distributed_memory/particles_impl_xchng_courants.ipp similarity index 100% rename from src/impl/particles_impl_xchng_courants.ipp rename to src/impl/distributed_memory/particles_impl_xchng_courants.ipp diff --git a/src/impl/particles_impl_xchng_domains.ipp b/src/impl/distributed_memory/particles_impl_xchng_domains.ipp similarity index 100% rename from src/impl/particles_impl_xchng_domains.ipp rename to src/impl/distributed_memory/particles_impl_xchng_domains.ipp diff --git a/src/impl/particles_impl_hskpng_Tpr.ipp b/src/impl/housekeeping/particles_impl_hskpng_Tpr.ipp similarity index 99% rename from src/impl/particles_impl_hskpng_Tpr.ipp rename to src/impl/housekeeping/particles_impl_hskpng_Tpr.ipp index 3cbe9fe6e..97c735ee7 100644 --- a/src/impl/particles_impl_hskpng_Tpr.ipp +++ b/src/impl/housekeeping/particles_impl_hskpng_Tpr.ipp @@ -153,6 +153,7 @@ namespace libcloudphxx // an alternative implementation with formula choice at functor call const RH_formula_t RH_formula; // the type of formula to be used for RH + BOOST_GPU_ENABLED RH(RH_formula_t RH_formula): RH_formula(RH_formula) {} diff --git a/src/impl/particles_impl_hskpng_count.ipp b/src/impl/housekeeping/particles_impl_hskpng_count.ipp similarity index 100% rename from src/impl/particles_impl_hskpng_count.ipp rename to src/impl/housekeeping/particles_impl_hskpng_count.ipp diff --git a/src/impl/particles_impl_hskpng_ijk.ipp b/src/impl/housekeeping/particles_impl_hskpng_ijk.ipp similarity index 100% rename from src/impl/particles_impl_hskpng_ijk.ipp rename to src/impl/housekeeping/particles_impl_hskpng_ijk.ipp diff --git a/src/impl/particles_impl_hskpng_mfp.ipp b/src/impl/housekeeping/particles_impl_hskpng_mfp.ipp similarity index 100% rename from src/impl/particles_impl_hskpng_mfp.ipp rename to src/impl/housekeeping/particles_impl_hskpng_mfp.ipp diff --git a/src/impl/housekeeping/particles_impl_hskpng_rc2.ipp b/src/impl/housekeeping/particles_impl_hskpng_rc2.ipp new file mode 100644 index 000000000..c28b77063 --- /dev/null +++ b/src/impl/housekeeping/particles_impl_hskpng_rc2.ipp @@ -0,0 +1,35 @@ +// 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::hskpng_approximate_rc2_invalid() // approximated for the temperature opts_init.rc2_T + { + if(sstp_cond_act == 1 || !allow_sstp_cond) + return; + + namespace arg = thrust::placeholders; + + thrust::transform_if( + rd3.begin(), rd3.end(), // input - 1st arg + thrust::make_zip_iterator(make_tuple( + kpa.begin(), + thrust::make_constant_iterator(opts_init.rc2_T + 273.15) + )), // input - 2nd arg + rc2.begin(), // condition argument + rc2.begin(), // output + detail::rw3_cr(), // op + arg::_1 == real_t(detail::invalid) // condition + ); + } + }; +}; + diff --git a/src/impl/particles_impl_hskpng_remove.ipp b/src/impl/housekeeping/particles_impl_hskpng_remove.ipp similarity index 100% rename from src/impl/particles_impl_hskpng_remove.ipp rename to src/impl/housekeeping/particles_impl_hskpng_remove.ipp diff --git a/src/impl/particles_impl_hskpng_resize.ipp b/src/impl/housekeeping/particles_impl_hskpng_resize.ipp similarity index 100% rename from src/impl/particles_impl_hskpng_resize.ipp rename to src/impl/housekeeping/particles_impl_hskpng_resize.ipp diff --git a/src/impl/particles_impl_hskpng_sort.ipp b/src/impl/housekeeping/particles_impl_hskpng_sort.ipp similarity index 100% rename from src/impl/particles_impl_hskpng_sort.ipp rename to src/impl/housekeeping/particles_impl_hskpng_sort.ipp diff --git a/src/impl/particles_impl_hskpng_tke.ipp b/src/impl/housekeeping/particles_impl_hskpng_tke.ipp similarity index 100% rename from src/impl/particles_impl_hskpng_tke.ipp rename to src/impl/housekeeping/particles_impl_hskpng_tke.ipp diff --git a/src/impl/particles_impl_hskpng_turb_ss.ipp b/src/impl/housekeeping/particles_impl_hskpng_turb_ss.ipp similarity index 97% rename from src/impl/particles_impl_hskpng_turb_ss.ipp rename to src/impl/housekeeping/particles_impl_hskpng_turb_ss.ipp index 27ea0c815..0e4888b2a 100644 --- a/src/impl/particles_impl_hskpng_turb_ss.ipp +++ b/src/impl/housekeeping/particles_impl_hskpng_turb_ss.ipp @@ -54,6 +54,8 @@ namespace libcloudphxx // calc relaxation times (only in cells that contain any SDs) stored in count_mom moms_all(); moms_calc(rw2.begin(), real_t(1./2), false); + n_filtered_gp.reset(); // n_filtered not needed anymore + thrust::transform( count_mom.begin(), count_mom.begin() + count_n, diff --git a/src/impl/particles_impl_hskpng_turb_vel.ipp b/src/impl/housekeeping/particles_impl_hskpng_turb_vel.ipp similarity index 100% rename from src/impl/particles_impl_hskpng_turb_vel.ipp rename to src/impl/housekeeping/particles_impl_hskpng_turb_vel.ipp diff --git a/src/impl/particles_impl_hskpng_vterm.ipp b/src/impl/housekeeping/particles_impl_hskpng_vterm.ipp similarity index 100% rename from src/impl/particles_impl_hskpng_vterm.ipp rename to src/impl/housekeeping/particles_impl_hskpng_vterm.ipp diff --git a/src/impl/particles_impl_rcyc.ipp b/src/impl/housekeeping/particles_impl_rcyc.ipp similarity index 100% rename from src/impl/particles_impl_rcyc.ipp rename to src/impl/housekeeping/particles_impl_rcyc.ipp diff --git a/src/impl/ice/particles_impl_ice_dep.ipp b/src/impl/ice/particles_impl_ice_dep.ipp new file mode 100644 index 000000000..80c940dab --- /dev/null +++ b/src/impl/ice/particles_impl_ice_dep.ipp @@ -0,0 +1,144 @@ +// vim:filetype=cpp +/** @file + * @copyright University of Warsaw + * @section LICENSE + * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) + */ + +// #include +// #include +// #include +// #include +// #include +// #include + +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 d_ice_mass_g = tmp_device_real_cell.get_guard(); + // thrust_device::vector &d_ice_mass = d_ice_mass_g.get(); + if(step == 0) + reset_guardp(ice_mass_gp, tmp_device_real_cell); + thrust_device::vector &ice_mass = ice_mass_gp->get(); + + if(step == 0) + { + if(count_n!=n_cell) // NOTE: we rely that moms_calc was called recently (e.g. in save_liq_ice_content_before_change) + thrust::fill(ice_mass.begin(), ice_mass.end(), real_t(0.)); + } + else // copy ice_mass from previous step + { + reset_guardp(d_ice_mass_gp, tmp_device_real_cell); + thrust_device::vector &d_ice_mass = d_ice_mass_gp->get(); + // drv = -ice_mass precond + thrust::transform( + ice_mass.begin(), ice_mass.end(), + d_ice_mass.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 / sstp_cond, 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 d_ice_mass + if(step < sstp_cond - 1) + { + thrust_device::vector &d_ice_mass = d_ice_mass_gp->get(); + 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(), + d_ice_mass.begin(), + d_ice_mass.begin(), + thrust::plus() + ); + } + else // last step, calculate change in 3rd moment and update th and rv + { + thrust_device::vector &d_ice_mass = d_ice_mass_gp->get(); + + thrust::transform( + count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg + thrust::make_permutation_iterator(d_ice_mass.begin(), count_ijk.begin()), // input - 2nd arg + thrust::make_permutation_iterator(d_ice_mass.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(d_ice_mass, impl::phase_change::deposition); + } + }; + } \ No newline at end of file diff --git a/src/impl/particles_impl_ice_nucl_melt.ipp b/src/impl/ice/particles_impl_ice_nucl_melt.ipp similarity index 99% rename from src/impl/particles_impl_ice_nucl_melt.ipp rename to src/impl/ice/particles_impl_ice_nucl_melt.ipp index 811ee6ac7..5f7f32cdd 100644 --- a/src/impl/particles_impl_ice_nucl_melt.ipp +++ b/src/impl/ice/particles_impl_ice_nucl_melt.ipp @@ -222,9 +222,7 @@ namespace libcloudphxx ); // Update th according to the frozen volume per cell - update_th_freezing(drw); - + update_th_freezing(drw); } - } } diff --git a/src/impl/particles_impl_init_SD_with_distros.ipp b/src/impl/initialization/particles_impl_init_SD_with_distros.ipp similarity index 97% rename from src/impl/particles_impl_init_SD_with_distros.ipp rename to src/impl/initialization/particles_impl_init_SD_with_distros.ipp index 2cea4a1b3..0585bcb90 100644 --- a/src/impl/particles_impl_init_SD_with_distros.ipp +++ b/src/impl/initialization/particles_impl_init_SD_with_distros.ipp @@ -19,7 +19,7 @@ namespace libcloudphxx if(opts_init.sd_conc > 0) for (auto ddi = opts_init.dry_distros.cbegin(); ddi != opts_init.dry_distros.cend(); ++ddi) { - dist_analysis_sd_conc( + init_dist_analysis_sd_conc( *(ddi->second), opts_init.sd_conc ); @@ -80,7 +80,7 @@ namespace libcloudphxx // init for substepping for chem reactions if(opts_init.chem_switch){ - init_sstp_chem(); + init_percell_sstp_chem(); } // calculate initail volume (helper for Henry in chem) diff --git a/src/impl/particles_impl_init_SD_with_distros_const_multi.ipp b/src/impl/initialization/particles_impl_init_SD_with_distros_const_multi.ipp similarity index 96% rename from src/impl/particles_impl_init_SD_with_distros_const_multi.ipp rename to src/impl/initialization/particles_impl_init_SD_with_distros_const_multi.ipp index 31b332ab4..8e3657560 100644 --- a/src/impl/particles_impl_init_SD_with_distros_const_multi.ipp +++ b/src/impl/initialization/particles_impl_init_SD_with_distros_const_multi.ipp @@ -14,7 +14,7 @@ namespace libcloudphxx void particles_t::impl::init_SD_with_distros_const_multi(const common::unary_function &fun) { // analyze the distribution, TODO: just did it - dist_analysis_const_multi(fun); + init_dist_analysis_const_multi(fun); if(log_rd_min >= log_rd_max) throw std::runtime_error(detail::formatter() << "Distribution analysis error: rd_min(" << exp(log_rd_min) << ") >= rd_max(" << exp(log_rd_max) << ")"); diff --git a/src/impl/particles_impl_init_SD_with_distros_sd_conc.ipp b/src/impl/initialization/particles_impl_init_SD_with_distros_sd_conc.ipp similarity index 98% rename from src/impl/particles_impl_init_SD_with_distros_sd_conc.ipp rename to src/impl/initialization/particles_impl_init_SD_with_distros_sd_conc.ipp index fbdf45bdd..29921f046 100644 --- a/src/impl/particles_impl_init_SD_with_distros_sd_conc.ipp +++ b/src/impl/initialization/particles_impl_init_SD_with_distros_sd_conc.ipp @@ -14,7 +14,7 @@ namespace libcloudphxx void particles_t::impl::init_SD_with_distros_sd_conc(const common::unary_function &fun, const real_t &tot_lnrd_rng) { // analyze the distribution, TODO: just did it in init_SD_with_distros - dist_analysis_sd_conc( + init_dist_analysis_sd_conc( fun, opts_init.sd_conc ); diff --git a/src/impl/particles_impl_init_SD_with_distros_tail.ipp b/src/impl/initialization/particles_impl_init_SD_with_distros_tail.ipp similarity index 97% rename from src/impl/particles_impl_init_SD_with_distros_tail.ipp rename to src/impl/initialization/particles_impl_init_SD_with_distros_tail.ipp index 7d35d7272..e20450d56 100644 --- a/src/impl/particles_impl_init_SD_with_distros_tail.ipp +++ b/src/impl/initialization/particles_impl_init_SD_with_distros_tail.ipp @@ -13,7 +13,7 @@ namespace libcloudphxx template void particles_t::impl::init_SD_with_distros_tail(const common::unary_function &fun, const real_t log_rd_min_init) { - dist_analysis_const_multi(fun); + init_dist_analysis_const_multi(fun); // overwrite calculated log_rd_min with external one (equal to log_rd_max of sd_conc init) log_rd_min=log_rd_min_init; diff --git a/src/impl/particles_impl_init_SD_with_sizes.ipp b/src/impl/initialization/particles_impl_init_SD_with_sizes.ipp similarity index 98% rename from src/impl/particles_impl_init_SD_with_sizes.ipp rename to src/impl/initialization/particles_impl_init_SD_with_sizes.ipp index b462d4bdb..93c45049c 100644 --- a/src/impl/particles_impl_init_SD_with_sizes.ipp +++ b/src/impl/initialization/particles_impl_init_SD_with_sizes.ipp @@ -76,7 +76,7 @@ namespace libcloudphxx // init for substepping for chem reactions if(opts_init.chem_switch){ - init_sstp_chem(); + init_percell_sstp_chem(); } // calculate initail volume (helper for Henry in chem) diff --git a/src/impl/particles_impl_init_T_freeze.ipp b/src/impl/initialization/particles_impl_init_T_freeze.ipp similarity index 100% rename from src/impl/particles_impl_init_T_freeze.ipp rename to src/impl/initialization/particles_impl_init_T_freeze.ipp diff --git a/src/impl/particles_impl_init_a_c_rho_ice.ipp b/src/impl/initialization/particles_impl_init_a_c_rho_ice.ipp similarity index 100% rename from src/impl/particles_impl_init_a_c_rho_ice.ipp rename to src/impl/initialization/particles_impl_init_a_c_rho_ice.ipp diff --git a/src/impl/particles_impl_init_chem.ipp b/src/impl/initialization/particles_impl_init_chem.ipp similarity index 100% rename from src/impl/particles_impl_init_chem.ipp rename to src/impl/initialization/particles_impl_init_chem.ipp diff --git a/src/impl/particles_impl_init_count_num.ipp b/src/impl/initialization/particles_impl_init_count_num.ipp similarity index 100% rename from src/impl/particles_impl_init_count_num.ipp rename to src/impl/initialization/particles_impl_init_count_num.ipp diff --git a/src/impl/particles_impl_dist_analysis.ipp b/src/impl/initialization/particles_impl_init_dist_analysis.ipp similarity index 97% rename from src/impl/particles_impl_dist_analysis.ipp rename to src/impl/initialization/particles_impl_init_dist_analysis.ipp index ac63c9740..c7dda937d 100644 --- a/src/impl/particles_impl_dist_analysis.ipp +++ b/src/impl/initialization/particles_impl_init_dist_analysis.ipp @@ -14,7 +14,7 @@ namespace libcloudphxx { // init template - void particles_t::impl::dist_analysis_sd_conc( + void particles_t::impl::init_dist_analysis_sd_conc( const common::unary_function &n_of_lnrd_stp, const impl::n_t sd_conc, const real_t dt @@ -77,7 +77,7 @@ namespace libcloudphxx }; template - void particles_t::impl::dist_analysis_const_multi( + void particles_t::impl::init_dist_analysis_const_multi( const common::unary_function &n_of_lnrd_stp ) { diff --git a/src/impl/particles_impl_init_dry_const_multi.ipp b/src/impl/initialization/particles_impl_init_dry_const_multi.ipp similarity index 100% rename from src/impl/particles_impl_init_dry_const_multi.ipp rename to src/impl/initialization/particles_impl_init_dry_const_multi.ipp diff --git a/src/impl/particles_impl_init_dry_dry_sizes.ipp b/src/impl/initialization/particles_impl_init_dry_dry_sizes.ipp similarity index 100% rename from src/impl/particles_impl_init_dry_dry_sizes.ipp rename to src/impl/initialization/particles_impl_init_dry_dry_sizes.ipp diff --git a/src/impl/particles_impl_init_dry_sd_conc.ipp b/src/impl/initialization/particles_impl_init_dry_sd_conc.ipp similarity index 100% rename from src/impl/particles_impl_init_dry_sd_conc.ipp rename to src/impl/initialization/particles_impl_init_dry_sd_conc.ipp diff --git a/src/impl/particles_impl_init_e2l.ipp b/src/impl/initialization/particles_impl_init_e2l.ipp similarity index 100% rename from src/impl/particles_impl_init_e2l.ipp rename to src/impl/initialization/particles_impl_init_e2l.ipp diff --git a/src/impl/particles_impl_init_grid.ipp b/src/impl/initialization/particles_impl_init_grid.ipp similarity index 100% rename from src/impl/particles_impl_init_grid.ipp rename to src/impl/initialization/particles_impl_init_grid.ipp diff --git a/src/impl/particles_impl_init_hskpng_ncell.ipp b/src/impl/initialization/particles_impl_init_hskpng_ncell.ipp similarity index 85% rename from src/impl/particles_impl_init_hskpng_ncell.ipp rename to src/impl/initialization/particles_impl_init_hskpng_ncell.ipp index 951ae8054..9df14d69b 100644 --- a/src/impl/particles_impl_init_hskpng_ncell.ipp +++ b/src/impl/initialization/particles_impl_init_hskpng_ncell.ipp @@ -35,6 +35,8 @@ namespace libcloudphxx sstp_tmp_th.resize(n_cell); sstp_tmp_rh.resize(n_cell); } + // if(opts_init.adaptive_sstp_cond) + // sstp_cond_percell.resize(n_cell, opts_init.sstp_cond); // in adaptive substepping start with opts_init.sstp_cond substeps per cell } }; }; diff --git a/src/impl/particles_impl_init_ijk.ipp b/src/impl/initialization/particles_impl_init_ijk.ipp similarity index 100% rename from src/impl/particles_impl_init_ijk.ipp rename to src/impl/initialization/particles_impl_init_ijk.ipp diff --git a/src/impl/particles_impl_init_incloud_time.ipp b/src/impl/initialization/particles_impl_init_incloud_time.ipp similarity index 100% rename from src/impl/particles_impl_init_incloud_time.ipp rename to src/impl/initialization/particles_impl_init_incloud_time.ipp diff --git a/src/impl/particles_impl_init_insol_dry_sizes.ipp b/src/impl/initialization/particles_impl_init_insol_dry_sizes.ipp similarity index 100% rename from src/impl/particles_impl_init_insol_dry_sizes.ipp rename to src/impl/initialization/particles_impl_init_insol_dry_sizes.ipp diff --git a/src/impl/particles_impl_init_kappa.ipp b/src/impl/initialization/particles_impl_init_kappa.ipp similarity index 100% rename from src/impl/particles_impl_init_kappa.ipp rename to src/impl/initialization/particles_impl_init_kappa.ipp diff --git a/src/impl/particles_impl_init_kernel.ipp b/src/impl/initialization/particles_impl_init_kernel.ipp similarity index 100% rename from src/impl/particles_impl_init_kernel.ipp rename to src/impl/initialization/particles_impl_init_kernel.ipp diff --git a/src/impl/particles_impl_init_n.ipp b/src/impl/initialization/particles_impl_init_n.ipp similarity index 100% rename from src/impl/particles_impl_init_n.ipp rename to src/impl/initialization/particles_impl_init_n.ipp diff --git a/src/impl/particles_impl_init_sanity_check.ipp b/src/impl/initialization/particles_impl_init_sanity_check.ipp similarity index 84% rename from src/impl/particles_impl_init_sanity_check.ipp rename to src/impl/initialization/particles_impl_init_sanity_check.ipp index a27edf1db..ec1768ff9 100644 --- a/src/impl/particles_impl_init_sanity_check.ipp +++ b/src/impl/initialization/particles_impl_init_sanity_check.ipp @@ -147,6 +147,19 @@ namespace libcloudphxx if(!opts_init.const_p && !p.is_null()) throw std::runtime_error("libcloudph++: pressure profile was passed in init(), but the constant pressure option was not used"); + if(opts_init.sstp_cond < 1) + throw std::runtime_error("libcloudph++: opts_init.sstp_cond needs to be greater than 0"); + if(opts_init.adaptive_sstp_cond && !opts_init.exact_sstp_cond) + throw std::runtime_error("libcloudph++: Adaptive condensation substepping (opts_init.adaptive_sstp_cond) works oly for per-particle substepping (opts_init.exact_sstp_cond)"); + // if(opts_init.adaptive_sstp_cond && opts_init.sstp_cond > 1 && (opts_init.sstp_cond & (opts_init.sstp_cond-1) != 0)) + // throw std::runtime_error("libcloudph++: In adaptive condensation substepping, number of substeps has to be either 1 or a power of 2. Adjust initial number of substeps (opts_init.sstp_cond)"); + if(!opts_init.sstp_cond_mix && !opts_init.exact_sstp_cond) + throw std::runtime_error("libcloudph++: Mixing of rv and th (opts_init.sstp_cond_mix) can only be disable for per-particle substepping (opts_init.exact_sstp_cond)"); + if(opts_init.sstp_cond_mix && opts_init.adaptive_sstp_cond && opts_init.exact_sstp_cond) + throw std::runtime_error("libcloudph++: Adaptive cond substepping (opts_init.adaptive_sstp_cond) with per-particle substepping (opts_init.exact_sstp_cond) requires mixing of th and rv between subteps (opts_init.sstp_cond_mix) to be disabled"); + if(opts_init.sstp_cond_act > 1 && (opts_init.sstp_cond_mix || !opts_init.exact_sstp_cond || !opts_init.adaptive_sstp_cond)) + throw std::runtime_error("libcloudph++: number of substeps for activation (opts_init.sstp_cond_act) can be greater than 1 only if mixing of rv and th (opts_init.sstp_cond_mix) is disabled and if per-particle condensation substepping is used (opts_init.exact_sstp_cond) and if adaptive substepping is used (opts_init.adaptive_sstp_cond)"); + if(opts_init.ice_switch) { if(opts_init.coal_switch) // because we dont know what to do when ice collides with water @@ -157,6 +170,8 @@ namespace libcloudphxx 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."); + if(opts_init.exact_sstp_cond) + throw std::runtime_error("libcloudph++: deposition works only with per-cell substepping"); } } }; diff --git a/src/impl/particles_impl_init_sync.ipp b/src/impl/initialization/particles_impl_init_sync.ipp similarity index 100% rename from src/impl/particles_impl_init_sync.ipp rename to src/impl/initialization/particles_impl_init_sync.ipp diff --git a/src/impl/particles_impl_init_vterm.ipp b/src/impl/initialization/particles_impl_init_vterm.ipp similarity index 100% rename from src/impl/particles_impl_init_vterm.ipp rename to src/impl/initialization/particles_impl_init_vterm.ipp diff --git a/src/impl/particles_impl_init_wet.ipp b/src/impl/initialization/particles_impl_init_wet.ipp similarity index 100% rename from src/impl/particles_impl_init_wet.ipp rename to src/impl/initialization/particles_impl_init_wet.ipp diff --git a/src/impl/particles_impl_init_xyz.ipp b/src/impl/initialization/particles_impl_init_xyz.ipp similarity index 100% rename from src/impl/particles_impl_init_xyz.ipp rename to src/impl/initialization/particles_impl_init_xyz.ipp diff --git a/src/impl/particles_impl_reserve_hskpng_npart.ipp b/src/impl/initialization/particles_impl_reserve_hskpng_npart.ipp similarity index 97% rename from src/impl/particles_impl_reserve_hskpng_npart.ipp rename to src/impl/initialization/particles_impl_reserve_hskpng_npart.ipp index a638f08d6..aa2a9d989 100644 --- a/src/impl/particles_impl_reserve_hskpng_npart.ipp +++ b/src/impl/initialization/particles_impl_reserve_hskpng_npart.ipp @@ -33,6 +33,10 @@ namespace libcloudphxx dot_ssp.reserve(opts_init.n_sd_max); } + if(sstp_cond_act > 1 && allow_sstp_cond) + { + rc2.reserve(opts_init.n_sd_max); + } if(opts_init.ice_switch) { rd2_insol.reserve(opts_init.n_sd_max); diff --git a/src/impl/particles_impl.ipp b/src/impl/particles_impl.ipp index ef57407d5..681bf6891 100644 --- a/src/impl/particles_impl.ipp +++ b/src/impl/particles_impl.ipp @@ -11,8 +11,6 @@ #include #include -//#include -//#include #include #include @@ -83,7 +81,17 @@ namespace libcloudphxx sstp_tmp_th, // ditto for theta sstp_tmp_rh, // ditto for rho sstp_tmp_p, // ditto for pressure + sstp_tmp_chem_0, // ditto for trace gases + sstp_tmp_chem_1, // ditto for trace gases + sstp_tmp_chem_2, // ditto for trace gases + sstp_tmp_chem_3, // ditto for trace gases + sstp_tmp_chem_4, // ditto for trace gases + sstp_tmp_chem_5, // ditto for trace gases + vt, // terminal velocity + vt_0, // sea level term velocity according to Beard 1977, compute once + dv, // grid-cell volumes (per grid cell) incloud_time, // time this SD has been within a cloud + rc2, // critical radius squared (estimated for temperature from opts_init.rc2_T) rd2_insol, // dry radii squared of insoluble aerosol T_freeze, // freezing temperature ice_a, // equatorial radius of ice @@ -95,23 +103,12 @@ namespace libcloudphxx log_rd_max, // logarithm of the upper bound of the distr multiplier; // multiplier calculated for the above values - // terminal velocity (per particle) - thrust_device::vector vt; - // sea level term velocity according to Beard 1977, compute once - thrust_device::vector vt_0; - - // grid-cell volumes (per grid cell) - thrust_device::vector dv; - // housekeeping data (per particle) thrust_device::vector ijk, // Eulerian grid cell indices (always zero for 0D); i, j and k use temporary vectors from tmp_device_size_part via i_gp, j_gp and k_gp; TODO: make ijk, sorted_id and sorted_ijk also use such temporary vectors? + lft, rgt, abv, blw, fre, hnd, // Arakawa-C grid helper vars TODO: could be reused after advection! sorted_id, sorted_ijk; - // Arakawa-C grid helper vars - thrust_device::vector - lft, rgt, abv, blw, fre, hnd; // TODO: could be reused after advection! - // moment-counting stuff thrust_device::vector count_ijk; // key-value pair for sorting particles by cell index @@ -126,12 +123,6 @@ namespace libcloudphxx rhod, // dry air density th, // potential temperature (dry) rv, // water vapour mixing ratio - sstp_tmp_chem_0, // ditto for trace gases - sstp_tmp_chem_1, // ditto for trace gases - sstp_tmp_chem_2, // ditto for trace gases - sstp_tmp_chem_3, // ditto for trace gases - sstp_tmp_chem_4, // ditto for trace gases - sstp_tmp_chem_5, // ditto for trace gases courant_x, courant_y, courant_z; @@ -149,13 +140,14 @@ namespace libcloudphxx eta,// dynamic viscosity diss_rate; // turbulent kinetic energy dissipation rate - thrust_device::vector w_LS; // large-scale subsidence velocity profile - thrust_device::vector SGS_mix_len; // SGS mixing length profile - thrust_device::vector aerosol_conc_factor; // profile of aerosol concentration factor + thrust_device::vector w_LS, // large-scale subsidence velocity profile + SGS_mix_len, // SGS mixing length profile + aerosol_conc_factor; // profile of aerosol concentration factor // time steps to be used, considering that opts.dt can override opts_init.dt + // sstp_cond not used if opts_init.adaptive_sstp_cond == true, then theres sstp_cond for each SD real_t dt; - int sstp_cond, sstp_coal, sstp_chem; + int sstp_cond, sstp_coal, sstp_chem, sstp_cond_act; // sorting needed only for diagnostics and coalescence bool sorted; @@ -169,6 +161,8 @@ namespace libcloudphxx bool allow_sstp_cond, allow_sstp_chem; + bool sstp_cond_exact_nomix_adaptive; // whether per-particle substepping with no mixing and adaptive substepping is used + // timestep counter n_t src_stp_ctr, rlx_stp_ctr; @@ -219,7 +213,6 @@ namespace libcloudphxx tmp_device_size_part; // guards for temp vectors that are used in multiple functions and need to stay unchanged inbetween - // tmp_vector_pool>::guard asd; std::unique_ptr< typename tmp_vector_pool>::guard > n_filtered_gp, @@ -228,11 +221,18 @@ namespace libcloudphxx sstp_dlt_th_gp, sstp_dlt_rhod_gp, sstp_dlt_p_gp, - drv_gp, + // drv_gp, lambda_D_gp, lambda_K_gp, + drw_mom3_gp, rw_mom3_gp, - rw3_gp, + rwX_gp, + drwX_gp, + // drw2_gp, + // drw3_gp, + Tp_gp, + // rw3_gp, + d_ice_mass_gp, ice_mass_gp; std::unique_ptr< @@ -241,7 +241,8 @@ namespace libcloudphxx std::unique_ptr< typename tmp_vector_pool>::guard - > chem_flag_gp; + > chem_flag_gp, // could be bool (?) + perparticle_sstp_cond_gp; std::unique_ptr< typename tmp_vector_pool>::guard @@ -294,6 +295,8 @@ namespace libcloudphxx // ids of sds to be copied with distmem // thrust_device::vector &lft_id, &rgt_id; + // thrust_device::vector sstp_cond_percell; // number of cond substeps per cell (used in adaptive substepping) + // --- containters with vector pointers to help resize and copy vectors --- // vectors copied between distributed memories (MPI, multi_CUDA), these are SD attributes @@ -370,11 +373,26 @@ namespace libcloudphxx SGS_mix_len(_opts_init.SGS_mix_len), aerosol_conc_factor(_opts_init.aerosol_conc_factor), adve_scheme(_opts_init.adve_scheme), - allow_sstp_cond(_opts_init.sstp_cond > 1 || _opts_init.variable_dt_switch), - allow_sstp_chem(_opts_init.sstp_chem > 1 || _opts_init.variable_dt_switch), + allow_sstp_cond(_opts_init.sstp_cond > 1 || _opts_init.sstp_cond_act > 1), // || _opts_init.variable_dt_switch || _opts_init.adaptive_sstp_cond), + allow_sstp_chem(_opts_init.sstp_chem > 1), // || _opts_init.variable_dt_switch), + sstp_cond_exact_nomix_adaptive(_opts_init.exact_sstp_cond && (_opts_init.sstp_cond > 1 || _opts_init.sstp_cond_act > 1) && _opts_init.adaptive_sstp_cond), + sstp_cond(_opts_init.sstp_cond), + sstp_coal(_opts_init.sstp_coal), + sstp_chem(_opts_init.sstp_chem), + // sstp_cond_act(std::max(_opts_init.sstp_cond_act, _opts_init.sstp_cond)), + sstp_cond_act(_opts_init.sstp_cond_act), 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_host_real_part("tmp_host_real_part"), + tmp_host_real_grid("tmp_host_real_grid"), + tmp_host_real_cell("tmp_host_real_cell"), + tmp_host_size_part("tmp_host_size_part"), + tmp_host_size_cell("tmp_host_size_cell"), + tmp_device_real_part("tmp_device_real_part"), + tmp_device_real_cell("tmp_device_real_cell", 4), // 4 temporary vectors of this type; NOTE: default constructor creates 1 + tmp_device_n_part("tmp_device_n_part"), + tmp_device_size_cell("tmp_device_size_cell"), + tmp_device_size_part("tmp_device_size_part") { // set 0 dev_count to mark that its not a multi_CUDA spawn @@ -436,7 +454,8 @@ namespace libcloudphxx distmem_real_vctrs.insert({&sstp_tmp_rv, detail::no_initial_value}); distmem_real_vctrs.insert({&sstp_tmp_th, detail::no_initial_value}); distmem_real_vctrs.insert({&sstp_tmp_rh, detail::no_initial_value}); - // sstp_tmp_p needs to be added if a constant pressure profile is used, but this is only known after init - see particles_init + if(opts_init.const_p) + distmem_real_vctrs.insert({&sstp_tmp_p, detail::no_initial_value}); } if(opts_init.turb_adve_switch) @@ -466,27 +485,47 @@ namespace libcloudphxx {distmem_real_vctrs.insert({&T_freeze, detail::no_initial_value});} } + if(opts_init.sstp_cond_act > 1 && allow_sstp_cond) + { + distmem_real_vctrs.insert({&rc2, detail::invalid}); + } + // initializing distmem_n_vctrs - list of n_t vectors with properties of SDs that have to be copied/removed/recycled when a SD is copied/removed/recycled distmem_n_vctrs.insert(&n); - // init number of temporary real vctrs - if(opts_init.chem_switch || allow_sstp_cond || n_dims >= 2) - tmp_device_real_part.add_vector(); - if(opts_init.chem_switch || (allow_sstp_cond && opts_init.exact_sstp_cond) || n_dims==3 || opts_init.turb_cond_switch || distmem()) - tmp_device_real_part.add_vector(); - if(allow_sstp_cond && opts_init.exact_sstp_cond) - { - tmp_device_real_part.add_vector(); - tmp_device_real_part.add_vector(); - tmp_device_real_part.add_vector(); - 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(); - } + // number of required temporary real vectors of size npart + int tmp_drp_no = 1; + if(n_dims == 2) + tmp_drp_no = std::max(tmp_drp_no, 2); + if(allow_sstp_cond) + tmp_drp_no = std::max(tmp_drp_no, 2); + if(opts_init.ice_switch && opts_init.time_dep_ice_nucl) + tmp_drp_no = std::max(tmp_drp_no, 2); + if(opts_init.chem_switch) + tmp_drp_no = std::max(tmp_drp_no, 3); + if(n_dims == 3) + tmp_drp_no = std::max(tmp_drp_no, 3); + if(opts_init.turb_cond_switch) + tmp_drp_no = std::max(tmp_drp_no, 3); + if(distmem()) + tmp_drp_no = std::max(tmp_drp_no, 3); + // if(opts_init.ice_switch) + // tmp_drp_no = std::max(tmp_drp_no, 8); // why so many? + if(allow_sstp_cond && opts_init.exact_sstp_cond && sstp_cond_exact_nomix_adaptive) + tmp_drp_no = std::max(tmp_drp_no, 4); // why 5? not 4? + if(allow_sstp_cond && opts_init.exact_sstp_cond && !sstp_cond_exact_nomix_adaptive) + tmp_drp_no = std::max(tmp_drp_no, 7); // why 8? not 7? + // if(allow_sstp_cond && opts_init.exact_sstp_cond && opts_init.const_p) + // tmp_drp_no = std::max(tmp_drp_no, 7); + tmp_device_real_part.add_vectors(tmp_drp_no-1); // -1 because 1 is already created in the ctor + + if(opts_init.ice_switch) + tmp_device_real_cell.add_vectors(2); + + if(opts_init.exact_sstp_cond && opts_init.adaptive_sstp_cond) + tmp_device_n_part.add_vectors(2); + // init number of temporary size_t vctrs // 1 needed if n_dims == 1 (i) // 2 needed if distmem (lft_id, rgt_id) @@ -495,9 +534,9 @@ namespace libcloudphxx // 1 already initialized by default ctor; // NOTE: cell index (i,j,k) could reuse tmp_device_n_part, but then it would have to be recalculated after each random sort (maybe its not a problem?) if(n_dims>=2 || distmem()) - tmp_device_size_part.add_vector(); + tmp_device_size_part.add_vectors(1); if(n_dims==3) - tmp_device_size_part.add_vector(); + tmp_device_size_part.add_vectors(1); resize_size_vctrs.insert(&ijk); resize_size_vctrs.insert(&sorted_ijk); @@ -530,12 +569,12 @@ namespace libcloudphxx void init_n_const_multi(const thrust_size_t &); void init_n_dry_sizes(const real_t &conc, const thrust_size_t &sd_conc); - void dist_analysis_sd_conc( + void init_dist_analysis_sd_conc( const common::unary_function &n_of_lnrd, const n_t sd_conc, const real_t dt = 1. ); - void dist_analysis_const_multi( + void init_dist_analysis_const_multi( const common::unary_function &n_of_lnrd ); void reserve_hskpng_npart(); @@ -561,8 +600,8 @@ namespace libcloudphxx void init_hskpng_ncell(); void init_chem(); void init_chem_aq(); - void init_sstp(); - void init_sstp_chem(); + void init_perparticle_sstp(); + void init_percell_sstp_chem(); void init_kernel(); void init_vterm(); @@ -583,6 +622,7 @@ namespace libcloudphxx void hskpng_vterm_all(); void hskpng_vterm_invalid(); + void hskpng_approximate_rc2_invalid(); void hskpng_tke(); void hskpng_turb_vel(const real_t &dt, const bool only_vertical = false); void hskpng_turb_dot_ss(); @@ -653,18 +693,38 @@ namespace libcloudphxx void sedi(const real_t &dt); void subs(const real_t &dt); + // condensation methods + void cond(const real_t &dt, const real_t &RH_max, const bool turb_cond, const int step); + void cond_perparticle_advance_rw2(const real_t &RH_max, const bool turb_cond); + template + void perparticle_advance_rw2(const real_t &RH_max, const thrust_device::vector &Tp, const pres_iter &pi, const RH_iter &rhi); + void perparticle_nomixing_adaptive_sstp_cond(const opts_t &); + void save_liq_ice_content_before_change(); + void calc_liq_ice_content_change(); + template + void set_perparticle_drwX_to_minus_rwX(const bool use_stored_rw3); + template + void add_perparticle_rwX_to_drwX(const bool store_rw3); + + void apply_perparticle_drw3_to_perparticle_rv_and_th(); + void apply_perparticle_cond_change_to_percell_rv_and_th(); + void update_th_rv(); +// ======= 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, const int step); - void cond_sstp(const real_t &dt, const real_t &RH_max, const bool turb_cond, const int step); + // 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); 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); + // 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_freezing(thrust_device::vector &); + // void update_th_rv(thrust_device::vector &, phase_change = phase_change::condensation); +// >>>>>>> 41c117de670b134d0632608d7e48fd9050a852bc + 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); void coal(const real_t &dt, const bool &turb_coal); @@ -689,11 +749,16 @@ namespace libcloudphxx void rlx(const real_t); void rlx_dry_distros(const real_t); - void sstp_step(const int &step); - void sstp_step_exact(const int &step); - void sstp_step_ssp(const real_t &dt); + // substepping methods + void acquire_arrays_for_perparticle_sstp(); + void release_arrays_for_perparticle_sstp(); + void calculate_noncond_perparticle_sstp_delta(); + void apply_noncond_perparticle_sstp_delta(); + void apply_perparticle_sgs_supersat(); + void sstp_percell_step(const int &step); + void sstp_percell_step_exact(const int &step); void sstp_save(); - void sstp_step_chem(const int &step); + void sstp_percell_step_chem(const int &step); void sstp_save_chem(); void post_copy(const opts_t&); diff --git a/src/impl/particles_impl_adjust_timesteps.ipp b/src/impl/particles_impl_adjust_timesteps.ipp index bb4d9fa00..49226a61d 100644 --- a/src/impl/particles_impl_adjust_timesteps.ipp +++ b/src/impl/particles_impl_adjust_timesteps.ipp @@ -13,12 +13,13 @@ namespace libcloudphxx void particles_t::impl::adjust_timesteps(const real_t &_dt) { if(_dt > 0 && !opts_init.variable_dt_switch) throw std::runtime_error("libcloudph++: opts.dt specified, but opts_init.variable_dt_switch is false."); + // then, number of substeps is adjusted to get desired dt for specific processes, but only if initially they are > 1 + sstp_cond = _dt > 0 && opts_init.sstp_cond > 1 ? std::ceil(opts_init.sstp_cond * _dt / opts_init.dt) : opts_init.sstp_cond; + sstp_coal = _dt > 0 && opts_init.sstp_coal > 1 ? std::ceil(opts_init.sstp_coal * _dt / opts_init.dt) : opts_init.sstp_coal; + sstp_chem = _dt > 0 && opts_init.sstp_chem > 1 ? std::ceil(opts_init.sstp_chem * _dt / opts_init.dt) : opts_init.sstp_chem; + sstp_cond_act = _dt > 0 && opts_init.sstp_cond_act > 1 ? std::ceil(opts_init.sstp_cond_act * _dt / opts_init.dt) : opts_init.sstp_cond_act; // dt defined in opts_init can be overriden by dt passed to this function dt = _dt > 0 ? _dt : opts_init.dt; - // then, number of substeps is adjusted to get desired dt for specific processes - sstp_cond = _dt > 0 ? std::ceil(opts_init.sstp_cond * _dt / opts_init.dt) : opts_init.sstp_cond; - sstp_coal = _dt > 0 ? std::ceil(opts_init.sstp_coal * _dt / opts_init.dt) : opts_init.sstp_coal; - sstp_chem = _dt > 0 ? std::ceil(opts_init.sstp_chem * _dt / opts_init.dt) : opts_init.sstp_chem; } }; }; diff --git a/src/impl/particles_impl_cond_sstp.ipp b/src/impl/particles_impl_cond_sstp.ipp deleted file mode 100644 index a0a9ff306..000000000 --- a/src/impl/particles_impl_cond_sstp.ipp +++ /dev/null @@ -1,286 +0,0 @@ -// 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 - { - namespace detail - { - template - struct RH_sgs// : thrust::unary_function&, real_t> - { - RH resolved_RH; - - RH_sgs(RH_formula_t RH_formula): - resolved_RH(RH_formula) - {} - - 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); - } - }; - }; - - 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 - ) { - 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(), - sstp_tmp_rv.begin(), - Tp.begin(), - // particle-specific eta - thrust::make_transform_iterator( - Tp.begin(), - detail::common__vterm__visc() - ), - 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 - 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 - hlpr_zip_iter, - // particle-specific p - pi, - // particle-specific RH - rhi - )), - rw2.begin(), // output - detail::advance_rw2(dt, RH_max) - ); - } - - - template - void particles_t::impl::cond_sstp( - const real_t &dt, - const real_t &RH_max, - const bool turb_cond, - const int step - ) { - - namespace arg = thrust::placeholders; - - // prerequisite - hskpng_sort(); - // particle's local change in rv - auto pdrv_g = tmp_device_real_part.get_guard(); - thrust_device::vector &pdrv = pdrv_g.get(); - if(step == 0) - reset_guardp(rw3_gp, tmp_device_real_part); - thrust_device::vector &rw3 = rw3_gp->get(); - - // -rw3_old - if(step == 0) - { - thrust::transform( - thrust::make_transform_iterator(rw2.begin(), detail::rw2torw3()), - thrust::make_transform_iterator(rw2.end(), detail::rw2torw3()), - pdrv.begin(), - thrust::negate() - ); - } - else - { - thrust::transform( - rw3.begin(), rw3.end(), - pdrv.begin(), - thrust::negate() - ); - } - - // vector for each particle's T - auto Tp_g = tmp_device_real_part.get_guard(); - thrust_device::vector &Tp = Tp_g.get(); - - // calc Tp - if(opts_init.th_dry) - { - thrust::transform( - 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() - ); - } - else // th_std - { - // T = dry2std(th_d, rv) * exner(p_tot) - 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_p.begin() // input - third arg - )), - Tp.begin(), // output - detail::common__theta_std__T_p() - ); - } - - - // calculating drop growth in a timestep using backward Euler - if(!opts_init.const_p) - { - // particle-specific pressure iterator, used twice - // TODO: store the value somewhere? - auto pressure_iter = thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple( - sstp_tmp_rh.begin(), - sstp_tmp_rv.begin(), - Tp.begin() - )), - detail::common__theta_dry__p() - ); - - if(turb_cond) - cond_sstp_hlpr(dt, RH_max, Tp, - // particle-specific p - pressure_iter, - // particle-specific RH, resolved + SGS - thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple( - pressure_iter, - sstp_tmp_rv.begin(), - Tp.begin(), - ssp.begin() - )), - detail::RH_sgs(opts_init.RH_formula) - ) - ); - else // no RH SGS - cond_sstp_hlpr(dt, RH_max, Tp, - // particle-specific p - pressure_iter, - // particle-specific RH, 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) - ) - ); - } - else // opts_init.const_p - { - if(turb_cond) - cond_sstp_hlpr(dt, RH_max, Tp, - // particle-specific p - sstp_tmp_p.begin(), - // particle-specific RH, resolved + SGS - thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple( - sstp_tmp_p.begin(), - sstp_tmp_rv.begin(), - Tp.begin(), - ssp.begin() - )), - detail::RH_sgs(opts_init.RH_formula) - ) - ); - else // no RH SGS - cond_sstp_hlpr(dt, RH_max, Tp, - // particle-specific p - sstp_tmp_p.begin(), - // particle-specific RH, resolved - thrust::make_transform_iterator( - thrust::make_zip_iterator(thrust::make_tuple( - sstp_tmp_p.begin(), - sstp_tmp_rv.begin(), - Tp.begin() - )), - detail::RH(opts_init.RH_formula) - ) - ); - } - - // rw3_new - rw3_old - if(step < sstp_cond - 1) - { - // rw3_new - thrust::transform( - rw2.begin(), rw2.end(), - rw3.begin(), - detail::rw2torw3() - ); - - thrust::transform( - rw3.begin(), rw3.end(), - pdrv.begin(), - pdrv.begin(), - thrust::plus() - ); - } - else // last step, no need to store rw3 - { - thrust::transform( - thrust::make_transform_iterator(rw2.begin(), detail::rw2torw3()), - thrust::make_transform_iterator(rw2.end(), detail::rw2torw3()), - pdrv.begin(), - pdrv.begin(), - thrust::plus() - ); - rw3_gp.reset(); // destroy guard to tmp array that stored rw3 - } - - // calc - 4/3 * pi * rho_w * n * (rw3_new - rw3_old) / (dV * rhod) - thrust::transform( - pdrv.begin(), pdrv.end(), // input - 1st arg - thrust::make_zip_iterator(thrust::make_tuple( - sstp_tmp_rh.begin(), // rhod - n.begin(), // n - thrust::make_permutation_iterator(dv.begin(), ijk.begin()) // dv - )), - pdrv.begin(), // output - detail::rw3diff2drv( - - 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(), // - Tp.begin(), // dth = drv * d_th_d_rv(T, th) - 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() // - )), - pdrv.begin(), // in-place - detail::dth() - ); - - // 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 deleted file mode 100644 index 9e987b3b1..000000000 --- a/src/impl/particles_impl_ice_dep.ipp +++ /dev/null @@ -1,298 +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 -#include -#include -#include -#include - -namespace libcloudphxx -{ - namespace lgrngn - { - - namespace detail - { - 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;; - - 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 - ); - } - - 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_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> &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); - - // Skip liquid droplets - if (a_old <= 0 || c_old <= 0) - return ac_old; - - 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)) - * 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)); - const real_t c_new = max(c_old + dt * dc_dt, real_t(1e-9)); - - return thrust::make_tuple(a_new, c_new); - } - }; - - } - - - 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_sstp.ipp b/src/impl/particles_impl_sstp.ipp deleted file mode 100644 index 83bf7b30e..000000000 --- a/src/impl/particles_impl_sstp.ipp +++ /dev/null @@ -1,200 +0,0 @@ -// vim:filetype=cpp -/** @file - * @copyright University of Warsaw - * @section LICENSE - * GPLv3+ (see the COPYING file or http://www.gnu.org/licenses/) - */ - -#include - -namespace libcloudphxx -{ - namespace lgrngn - { - template - void particles_t::impl::sstp_save() - { - if (!allow_sstp_cond) return; - - const int n = 4; - thrust_device::vector - *fr[n] = { &rv, &th, &rhod, &p }, - *to[n] = { &sstp_tmp_rv, &sstp_tmp_th, &sstp_tmp_rh, &sstp_tmp_p }; - for (int ix = 0; ix < ( (opts_init.const_p && opts_init.exact_sstp_cond) ? n : n-1); ++ix) // TODO: var_rho - { - if(opts_init.exact_sstp_cond) // per-particle version - thrust::copy( - thrust::make_permutation_iterator(fr[ix]->begin(), ijk.begin()), - thrust::make_permutation_iterator(fr[ix]->begin(), ijk.end()), - to[ix]->begin() - ); - else // per-cell version - thrust::copy( - fr[ix]->begin(), - fr[ix]->end(), - to[ix]->begin() - ); - } - } - - // init _old of new particles, only needed in per-particle sstp cond - template - void particles_t::impl::init_sstp() - { - if (!allow_sstp_cond || !opts_init.exact_sstp_cond) return; - - const int n = 4; - thrust_device::vector - *fr[n] = { &rv, &th, &rhod, &p }, - *to[n] = { &sstp_tmp_rv, &sstp_tmp_th, &sstp_tmp_rh, &sstp_tmp_p }; - for (int ix = 0; ix < ( (opts_init.const_p) ? n : n-1); ++ix) // TODO: var_rho - { - thrust::copy( - thrust::make_permutation_iterator(fr[ix]->begin(), ijk.begin()+n_part_old), - thrust::make_permutation_iterator(fr[ix]->begin(), ijk.end()), - to[ix]->begin()+n_part_old - ); - } - } - - template - void particles_t::impl::sstp_step( - const int &step - ) - { - if (sstp_cond == 1) return; - - namespace arg = thrust::placeholders; - - const int n = 3; - thrust_device::vector - *scl[n] = { &rv, &th, &rhod }, - *tmp[n] = { &sstp_tmp_rv, &sstp_tmp_th, &sstp_tmp_rh }; - - for (int ix = 0; ix < (var_rho ? n : n-1); ++ix) - { - const real_t sstp = sstp_cond; - if (step == 0) - { - // sstp_tmp_scl = dscl_adv (i.e. delta, i.e. new - old) - thrust::transform( - scl[ix]->begin(), scl[ix]->end(), // 1st arg: rv_new - tmp[ix]->begin(), // 2nd arg: rv_old - tmp[ix]->begin(), // output (in-place) - arg::_1 - arg::_2 // op: dscl_adv = (scl_new - scl_old) - ); - // scl -= (sstp - 1) * dscl_adv / sstp - thrust::transform( - scl[ix]->begin(), scl[ix]->end(), // 1st arg - tmp[ix]->begin(), // 2nd arg - scl[ix]->begin(), // output (in-place) - arg::_1 - (sstp - 1) * arg::_2 / sstp // op: rv = rv - (sstp - 1) * dscl_adv / sstp - ); - } - else - { - // scl += dscl_adv / sstp - thrust::transform( - scl[ix]->begin(), scl[ix]->end(), // 1st arg - tmp[ix]->begin(), // 2nd arg - scl[ix]->begin(), // output (in-place) - arg::_1 + arg::_2 / sstp // op: rv = rv + drv_adv / sstp - ); - } - } - } - - template - void particles_t::impl::sstp_step_exact( - const int &step - ) - { - if (sstp_cond == 1) return; - - namespace arg = thrust::placeholders; - - const int n = 4; - thrust_device::vector - *scl[n] = { &rv, &th, &rhod, &p }, - *tmp[n] = { &sstp_tmp_rv, &sstp_tmp_th, &sstp_tmp_rh, &sstp_tmp_p }, - *dlt[n]; - - // acquire temporary arrays to store sstp_delta - if (step == 0) - { - reset_guardp(sstp_dlt_rv_gp, tmp_device_real_part); - reset_guardp(sstp_dlt_th_gp, tmp_device_real_part); - reset_guardp(sstp_dlt_rhod_gp, tmp_device_real_part); - if(opts_init.const_p) - reset_guardp(sstp_dlt_p_gp, tmp_device_real_part); - } - - dlt[0] = &(sstp_dlt_rv_gp->get()); - dlt[1] = &(sstp_dlt_th_gp->get()); - dlt[2] = &(sstp_dlt_rhod_gp->get()); - if(opts_init.const_p) - dlt[3] = &(sstp_dlt_p_gp->get()); - - for (int ix = 0; ix < (opts_init.const_p ? n : n-1); ++ix) - { - const real_t sstp = sstp_cond; - - if (step == 0) - { - // sstp_tmp_scl = dscl_adv (i.e. delta, i.e. new - old) - thrust::transform( - thrust::make_permutation_iterator(scl[ix]->begin(), ijk.begin()), // 1st arg: rv_new - thrust::make_permutation_iterator(scl[ix]->begin(), ijk.end()), // 1st arg: rv_new - tmp[ix]->begin(), // 2nd arg: rv_old - dlt[ix]->begin(), // output - (arg::_1 - arg::_2) / sstp // op: dscl_adv = (scl_new - scl_old) / sstp - ); - // scl -= (sstp - 1) * dscl_adv / sstp - thrust::transform( - thrust::make_permutation_iterator(scl[ix]->begin(), ijk.begin()), // 1st arg: rv_new - thrust::make_permutation_iterator(scl[ix]->begin(), ijk.end()), // 1st arg: rv_new - dlt[ix]->begin(), // 2nd arg - tmp[ix]->begin(), // output - arg::_1 - (sstp - 1) * arg::_2 // op: rv = rv - (sstp - 1) * dscl_adv - ); - } - else - { - // scl += dscl_adv / sstp - thrust::transform( - tmp[ix]->begin(), tmp[ix]->end(), // 1st arg - dlt[ix]->begin(), // 2nd arg - tmp[ix]->begin(), // output (in-place) - arg::_1 + arg::_2 // op: rv = rv + drv_adv - ); - } - } - - // release temporary arrays that stored sstp_delta - if (step == sstp_cond-1) - { - sstp_dlt_rv_gp.reset(); - sstp_dlt_th_gp.reset(); - sstp_dlt_rhod_gp.reset(); - if(opts_init.const_p) - sstp_dlt_p_gp.reset(); - } - } - - - template - void particles_t::impl::sstp_step_ssp( - const real_t &dt - ) - { - namespace arg = thrust::placeholders; - - thrust::transform( - ssp.begin(), ssp.end(), - dot_ssp.begin(), - ssp.begin(), - arg::_1 + arg::_2 * dt - ); - } - }; -}; diff --git a/src/impl/particles_impl_sedi.ipp b/src/impl/sedimentation/particles_impl_sedi.ipp similarity index 100% rename from src/impl/particles_impl_sedi.ipp rename to src/impl/sedimentation/particles_impl_sedi.ipp diff --git a/src/impl/particles_impl_ante_adding_SD.ipp b/src/impl/sources_and_relaxation_of_SDs/particles_impl_ante_adding_SD.ipp similarity index 56% rename from src/impl/particles_impl_ante_adding_SD.ipp rename to src/impl/sources_and_relaxation_of_SDs/particles_impl_ante_adding_SD.ipp index c791b7301..4cc2f3bf2 100644 --- a/src/impl/particles_impl_ante_adding_SD.ipp +++ b/src/impl/sources_and_relaxation_of_SDs/particles_impl_ante_adding_SD.ipp @@ -12,22 +12,7 @@ namespace libcloudphxx template void particles_t::impl::ante_adding_SD() { - // --- calc liquid water content before src --- - hskpng_sort(); - reset_guardp(drv_gp, tmp_device_real_cell); - thrust_device::vector &drv = drv_gp->get(); - - thrust::fill(drv.begin(), drv.end(), real_t(0.)); - - moms_all(); - moms_calc(rw2.begin(), real_t(3./2.)); - - // drv = - tot_vol_bfr - 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() - ); + save_liq_ice_content_before_change(); // drv = -tot_vol_bfr + dry_vol_bfr /* diff --git a/src/impl/particles_impl_post_adding_SD.ipp b/src/impl/sources_and_relaxation_of_SDs/particles_impl_post_adding_SD.ipp similarity index 62% rename from src/impl/particles_impl_post_adding_SD.ipp rename to src/impl/sources_and_relaxation_of_SDs/particles_impl_post_adding_SD.ipp index feb2b6774..df7e0bc56 100644 --- a/src/impl/particles_impl_post_adding_SD.ipp +++ b/src/impl/sources_and_relaxation_of_SDs/particles_impl_post_adding_SD.ipp @@ -12,23 +12,11 @@ namespace libcloudphxx template void particles_t::impl::post_adding_SD() { - thrust_device::vector &drv = drv_gp->get(); - // --- after source particles are no longer sorted --- sorted = false; // --- calc liquid water content after src --- - hskpng_sort(); - moms_all(); - moms_calc(rw2.begin(), real_t(3./2.)); - - // drv = tot_vol_after -tot_vol_bfr + dry_vol_bfr - thrust::transform( - count_mom.begin(), count_mom.begin() + count_n, // input - 1st arg - thrust::make_permutation_iterator(drv.begin(), count_ijk.begin()), // 2nd arg - thrust::make_permutation_iterator(drv.begin(), count_ijk.begin()), // output - thrust::plus() - ); + calc_liq_ice_content_change(); // drv = tot_vol_after - dry_vol_after - tot_vol_bfr + dry_vol_bfr /* @@ -41,8 +29,8 @@ namespace libcloudphxx ); */ - // update th and rv - update_th_rv(drv); + // update th and rv based on change in liquid water content + update_th_rv(); // update count_ijk and count_num hskpng_count(); @@ -51,9 +39,7 @@ namespace libcloudphxx hskpng_vterm_invalid(); // init _old values in per-particle substepping - init_sstp(); - - drv_gp.reset(); // release the tmp array that stored drv + init_perparticle_sstp(); } }; }; diff --git a/src/impl/particles_impl_rlx.ipp b/src/impl/sources_and_relaxation_of_SDs/particles_impl_rlx.ipp similarity index 100% rename from src/impl/particles_impl_rlx.ipp rename to src/impl/sources_and_relaxation_of_SDs/particles_impl_rlx.ipp diff --git a/src/impl/particles_impl_rlx_dry_distros.ipp b/src/impl/sources_and_relaxation_of_SDs/particles_impl_rlx_dry_distros.ipp similarity index 99% rename from src/impl/particles_impl_rlx_dry_distros.ipp rename to src/impl/sources_and_relaxation_of_SDs/particles_impl_rlx_dry_distros.ipp index 00e270427..fb1c6b143 100644 --- a/src/impl/particles_impl_rlx_dry_distros.ipp +++ b/src/impl/sources_and_relaxation_of_SDs/particles_impl_rlx_dry_distros.ipp @@ -101,7 +101,7 @@ namespace libcloudphxx real_t tot_lnrd_rng = 0.; 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) { - dist_analysis_sd_conc( + init_dist_analysis_sd_conc( *(std::get<0>(ddi->second)), opts_init.rlx_bins ); @@ -120,7 +120,7 @@ namespace libcloudphxx // analyze distribution to get rd_min and max needed for bin sizes // TODO: this was done a moment ago! // TODO2: this could be done once at the start of the simulation! - dist_analysis_sd_conc( + init_dist_analysis_sd_conc( n_of_lnrd_stp, opts_init.rlx_bins ); @@ -162,6 +162,7 @@ namespace libcloudphxx moms_rng(rd3_min, rd3_max, rd3.begin(), n_part_pre_relax, true); // calculate 0-th non-specific moment of rd3 (number of droplets in a cell) of droplets in this rd3 and kappa range moms_calc(rd3.begin(), n_part_pre_relax, 0, false); + n_filtered_gp.reset(); // n_filtered not needed anymore // horizontal sum of this moment thrust::fill(hor_sum.begin(), hor_sum.end(), 0); diff --git a/src/impl/particles_impl_src.ipp b/src/impl/sources_and_relaxation_of_SDs/particles_impl_src.ipp similarity index 100% rename from src/impl/particles_impl_src.ipp rename to src/impl/sources_and_relaxation_of_SDs/particles_impl_src.ipp diff --git a/src/impl/particles_impl_src_dry_distros.ipp b/src/impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_distros.ipp similarity index 100% rename from src/impl/particles_impl_src_dry_distros.ipp rename to src/impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_distros.ipp diff --git a/src/impl/particles_impl_src_dry_distros_matching.ipp b/src/impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_distros_matching.ipp similarity index 99% rename from src/impl/particles_impl_src_dry_distros_matching.ipp rename to src/impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_distros_matching.ipp index 9833865e7..b639154c3 100644 --- a/src/impl/particles_impl_src_dry_distros_matching.ipp +++ b/src/impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_distros_matching.ipp @@ -101,7 +101,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( + init_dist_analysis_sd_conc( *get<0>(p_sdd->second), get<1>(p_sdd->second), sup_dt diff --git a/src/impl/particles_impl_src_dry_distros_simple.ipp b/src/impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_distros_simple.ipp similarity index 98% rename from src/impl/particles_impl_src_dry_distros_simple.ipp rename to src/impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_distros_simple.ipp index 3ca1dafbd..823acdd32 100644 --- a/src/impl/particles_impl_src_dry_distros_simple.ipp +++ b/src/impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_distros_simple.ipp @@ -33,7 +33,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( + init_dist_analysis_sd_conc( *(get<0>(p_sdd->second)), get<1>(p_sdd->second), sup_dt diff --git a/src/impl/particles_impl_src_dry_sizes.ipp b/src/impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_sizes.ipp similarity index 98% rename from src/impl/particles_impl_src_dry_sizes.ipp rename to src/impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_sizes.ipp index c3d461cbd..ef204f55a 100644 --- a/src/impl/particles_impl_src_dry_sizes.ipp +++ b/src/impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_sizes.ipp @@ -79,7 +79,7 @@ namespace libcloudphxx // init for substepping for chem reactions if(opts_init.chem_switch){ - init_sstp_chem(); + init_percell_sstp_chem(); } // calculate initail volume (helper for Henry in chem) diff --git a/src/impl/particles_impl_subs.ipp b/src/impl/subsidence/particles_impl_subs.ipp similarity index 100% rename from src/impl/particles_impl_subs.ipp rename to src/impl/subsidence/particles_impl_subs.ipp diff --git a/src/particles.tpp b/src/particles.tpp index cc085c444..c7e98d1ec 100644 --- a/src/particles.tpp +++ b/src/particles.tpp @@ -53,86 +53,117 @@ // details #include "impl/particles_impl.ipp" -#include "impl/particles_impl_dist_analysis.ipp" -#include "impl/particles_impl_reserve_hskpng_npart.ipp" -#include "impl/particles_impl_init_SD_with_distros_sd_conc.ipp" -#include "impl/particles_impl_init_SD_with_distros_tail.ipp" -#include "impl/particles_impl_init_SD_with_distros_const_multi.ipp" -#include "impl/particles_impl_init_SD_with_distros.ipp" -#include "impl/particles_impl_init_SD_with_sizes.ipp" -#include "impl/particles_impl_init_dry_sd_conc.ipp" -#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_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" -#include "impl/particles_impl_init_n.ipp" -#include "impl/particles_impl_init_wet.ipp" -#include "impl/particles_impl_init_xyz.ipp" -#include "impl/particles_impl_init_ijk.ipp" -#include "impl/particles_impl_init_count_num.ipp" -#include "impl/particles_impl_init_e2l.ipp" -#include "impl/particles_impl_init_grid.ipp" -#include "impl/particles_impl_init_sync.ipp" -#include "impl/particles_impl_init_hskpng_ncell.ipp" -#include "impl/particles_impl_init_chem.ipp" -#include "impl/particles_impl_init_kernel.ipp" -#include "impl/particles_impl_post_copy.ipp" -#include "impl/particles_impl_init_vterm.ipp" -#include "impl/particles_impl_init_sanity_check.ipp" -#include "impl/particles_impl_xchng_domains.ipp" -#include "impl/particles_impl_xchng_courants.ipp" -#include "impl/particles_impl_update_th_rv.ipp" -#include "impl/particles_impl_hskpng_ijk.ipp" -#include "impl/particles_impl_hskpng_Tpr.ipp" -#include "impl/particles_impl_hskpng_mfp.ipp" -#include "impl/particles_impl_hskpng_vterm.ipp" -#include "impl/particles_impl_hskpng_turb_vel.ipp" -#include "impl/particles_impl_hskpng_turb_ss.ipp" -#include "impl/particles_impl_hskpng_tke.ipp" -#include "impl/particles_impl_hskpng_sort.ipp" -#include "impl/particles_impl_hskpng_count.ipp" -#include "impl/particles_impl_hskpng_remove.ipp" -#include "impl/particles_impl_hskpng_resize.ipp" -#include "impl/particles_impl_moms.ipp" -#include "impl/particles_impl_mass_dens.ipp" -#include "impl/particles_impl_fill_outbuf.ipp" -#include "impl/particles_impl_distmem_access.ipp" -#include "impl/particles_impl_sync.ipp" -#include "impl/particles_impl_bcnd.ipp" // bcnd has to be b4 adve for periodic struct; move it to separate file in detail... -#include "impl/particles_impl_adve.ipp" -#include "impl/particles_impl_turb_adve.ipp" -#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" -#include "impl/particles_impl_coal.ipp" -#include "impl/particles_impl_chem_ante.ipp" -#include "impl/particles_impl_chem_henry.ipp" -#include "impl/particles_impl_chem_dissoc.ipp" -#include "impl/particles_impl_chem_strength.ipp" -#include "impl/particles_impl_chem_react.ipp" -#include "impl/particles_impl_rcyc.ipp" -#include "impl/particles_impl_sstp.ipp" -#include "impl/particles_impl_sstp_chem.ipp" -#include "impl/particles_impl_ante_adding_SD.ipp" -#include "impl/particles_impl_post_adding_SD.ipp" -#include "impl/particles_impl_src.ipp" -#include "impl/particles_impl_src_dry_distros_simple.ipp" -#include "impl/particles_impl_src_dry_distros_matching.ipp" -#include "impl/particles_impl_src_dry_distros.ipp" -#include "impl/particles_impl_src_dry_sizes.ipp" -#include "impl/particles_impl_rlx.ipp" -#include "impl/particles_impl_rlx_dry_distros.ipp" -#include "impl/particles_impl_update_incloud_time.ipp" +// TODO: move these to folders +#include "impl/particles_impl_sync.ipp" #include "impl/particles_impl_adjust_timesteps.ipp" -// MPI copy -#include "impl/particles_impl_pack.ipp" -#include "impl/particles_impl_unpack.ipp" -#include "impl/particles_impl_mpi_exchange.ipp" +#include "impl/initialization/particles_impl_init_dist_analysis.ipp" +#include "impl/initialization/particles_impl_reserve_hskpng_npart.ipp" +#include "impl/initialization/particles_impl_init_SD_with_distros_sd_conc.ipp" +#include "impl/initialization/particles_impl_init_SD_with_distros_tail.ipp" +#include "impl/initialization/particles_impl_init_SD_with_distros_const_multi.ipp" +#include "impl/initialization/particles_impl_init_SD_with_distros.ipp" +#include "impl/initialization/particles_impl_init_SD_with_sizes.ipp" +#include "impl/initialization/particles_impl_init_dry_sd_conc.ipp" +#include "impl/initialization/particles_impl_init_dry_const_multi.ipp" +#include "impl/initialization/particles_impl_init_dry_dry_sizes.ipp" +#include "impl/initialization/particles_impl_init_kappa.ipp" +#include "impl/initialization/particles_impl_init_incloud_time.ipp" +#include "impl/initialization/particles_impl_init_n.ipp" +#include "impl/initialization/particles_impl_init_wet.ipp" +#include "impl/initialization/particles_impl_init_xyz.ipp" +#include "impl/initialization/particles_impl_init_ijk.ipp" +#include "impl/initialization/particles_impl_init_count_num.ipp" +#include "impl/initialization/particles_impl_init_e2l.ipp" +#include "impl/initialization/particles_impl_init_grid.ipp" +#include "impl/initialization/particles_impl_init_sync.ipp" +#include "impl/initialization/particles_impl_init_hskpng_ncell.ipp" +#include "impl/initialization/particles_impl_init_chem.ipp" +#include "impl/initialization/particles_impl_init_kernel.ipp" +#include "impl/initialization/particles_impl_init_vterm.ipp" +#include "impl/initialization/particles_impl_init_sanity_check.ipp" +#include "impl/initialization/particles_impl_init_insol_dry_sizes.ipp" +#include "impl/initialization/particles_impl_init_T_freeze.ipp" +#include "impl/initialization/particles_impl_init_a_c_rho_ice.ipp" + +#include "impl/distributed_memory/particles_impl_xchng_domains.ipp" +#include "impl/distributed_memory/particles_impl_xchng_courants.ipp" +#include "impl/distributed_memory/particles_impl_distmem_access.ipp" +#include "impl/distributed_memory/particles_impl_pack.ipp" +#include "impl/distributed_memory/particles_impl_unpack.ipp" +#include "impl/distributed_memory/particles_impl_mpi_exchange.ipp" +#include "impl/distributed_memory/particles_impl_post_copy.ipp" + +#include "impl/housekeeping/particles_impl_hskpng_ijk.ipp" +#include "impl/housekeeping/particles_impl_hskpng_Tpr.ipp" +#include "impl/housekeeping/particles_impl_hskpng_mfp.ipp" +#include "impl/housekeeping/particles_impl_hskpng_vterm.ipp" +#include "impl/housekeeping/particles_impl_hskpng_turb_vel.ipp" +#include "impl/housekeeping/particles_impl_hskpng_turb_ss.ipp" +#include "impl/housekeeping/particles_impl_hskpng_tke.ipp" +#include "impl/housekeeping/particles_impl_hskpng_sort.ipp" +#include "impl/housekeeping/particles_impl_hskpng_count.ipp" +#include "impl/housekeeping/particles_impl_hskpng_remove.ipp" +#include "impl/housekeeping/particles_impl_hskpng_resize.ipp" +#include "impl/housekeeping/particles_impl_hskpng_rc2.ipp" +#include "impl/housekeeping/particles_impl_rcyc.ipp" + +#include "impl/diagnose_SD_attributes/particles_impl_moms.ipp" +#include "impl/diagnose_SD_attributes/particles_impl_mass_dens.ipp" +#include "impl/diagnose_SD_attributes/particles_impl_fill_outbuf.ipp" +#include "impl/diagnose_SD_attributes/particles_impl_update_incloud_time.ipp" + +#include "impl/common/particles_impl_update_th_rv.ipp" +#include "impl/common/save_liq_ice_content_before_change.ipp" +#include "impl/common/calc_liq_ice_content_change.ipp" + +#include "impl/boundary_conditions/particles_impl_bcnd.ipp" // bcnd has to be b4 adve for periodic struct; move it to separate file in detail... + +#include "impl/advection/particles_impl_adve.ipp" +#include "impl/advection/particles_impl_turb_adve.ipp" + +#include "impl/condensation/common/apply_perparticle_sgs_supersat.ipp" +#include "impl/condensation/common/particles_impl_cond_common.ipp" +#include "impl/condensation/common/sstp_save.ipp" +#include "impl/condensation/percell/sstp_percell_step.ipp" +#include "impl/condensation/percell/particles_impl_cond.ipp" +#include "impl/condensation/perparticle/init_perparticle_sstp.ipp" +#include "impl/condensation/perparticle/acquire_arrays_for_perparticle_sstp.ipp" +#include "impl/condensation/perparticle/release_arrays_for_perparticle_sstp.ipp" +#include "impl/condensation/perparticle/calculate_noncond_perparticle_sstp_delta.ipp" +#include "impl/condensation/perparticle/apply_noncond_perparticle_sstp_delta.ipp" +#include "impl/condensation/perparticle/perparticle_advance_rw2.ipp" +#include "impl/condensation/perparticle/cond_perparticle_advance_rw2.ipp" +#include "impl/condensation/perparticle/apply_perparticle_drw3_to_perparticle_rv_and_th.ipp" +#include "impl/condensation/perparticle/apply_perparticle_cond_change_to_percell_rv_and_th.ipp" +#include "impl/condensation/perparticle/perparticle_nomixing_adaptive_sstp_cond.ipp" +#include "impl/condensation/perparticle/set_perparticle_drwX_to_minus_rwX.ipp" +#include "impl/condensation/perparticle/add_perparticle_rwX_to_drwX.ipp" + +#include "impl/sedimentation/particles_impl_sedi.ipp" + +#include "impl/subsidence/particles_impl_subs.ipp" + +#include "impl/coalescence/particles_impl_coal.ipp" + +#include "impl/chemistry/particles_impl_chem_ante.ipp" +#include "impl/chemistry/particles_impl_chem_henry.ipp" +#include "impl/chemistry/particles_impl_chem_dissoc.ipp" +#include "impl/chemistry/particles_impl_chem_strength.ipp" +#include "impl/chemistry/particles_impl_chem_react.ipp" +#include "impl/chemistry/particles_impl_sstp_chem.ipp" + +#include "impl/sources_and_relaxation_of_SDs/particles_impl_ante_adding_SD.ipp" +#include "impl/sources_and_relaxation_of_SDs/particles_impl_post_adding_SD.ipp" +#include "impl/sources_and_relaxation_of_SDs/particles_impl_src.ipp" +#include "impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_distros_simple.ipp" +#include "impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_distros_matching.ipp" +#include "impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_distros.ipp" +#include "impl/sources_and_relaxation_of_SDs/particles_impl_src_dry_sizes.ipp" +#include "impl/sources_and_relaxation_of_SDs/particles_impl_rlx.ipp" +#include "impl/sources_and_relaxation_of_SDs/particles_impl_rlx_dry_distros.ipp" + +#include "impl/ice/particles_impl_ice_dep.ipp" +#include "impl/ice/particles_impl_ice_nucl_melt.ipp" + + diff --git a/src/particles_diag.ipp b/src/particles_diag.ipp index 369592b99..c991fd69c 100644 --- a/src/particles_diag.ipp +++ b/src/particles_diag.ipp @@ -194,14 +194,14 @@ namespace libcloudphxx thrust::sequence(pimpl->count_ijk.begin(), pimpl->count_ijk.end()); } - // records super-droplet concentration per grid cell + // records super-droplet concentration (of selected SDs!) per grid cell template void particles_t::diag_sd_conc() { namespace arg = thrust::placeholders; assert(pimpl->selected_before_counting); - thrust_device::vector &n_filtered = pimpl->n_filtered_gp->get(); + auto &n_filtered = pimpl->n_filtered_gp->get(); // similar to hskpng_count pimpl->hskpng_sort(); @@ -537,11 +537,13 @@ namespace libcloudphxx thrust::sequence(pimpl->count_ijk.begin(), pimpl->count_ijk.end()); } - // compute 1st (non-specific) moment of rw^3 * vt of all SDs + // compute 1st (non-specific) moment of rw^3 * vt of selected SDs // TODO: replace it with simple diag vt? template void particles_t::diag_precip_rate() { + assert(pimpl->selected_before_counting); + // updating terminal velocities pimpl->hskpng_vterm_all(); @@ -558,7 +560,7 @@ namespace libcloudphxx detail::precip_rate() ); - 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_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(pimpl->vt.begin(), 1., false); // copy back stored vterm diff --git a/src/particles_init.ipp b/src/particles_init.ipp index 5460e87d0..35bf79add 100644 --- a/src/particles_init.ipp +++ b/src/particles_init.ipp @@ -31,10 +31,6 @@ namespace libcloudphxx if(pimpl->opts_init.rng_seed_init_switch) pimpl->rng.reseed(pimpl->opts_init.rng_seed_init); - // if pressure comes from a profile, sstp_tmp_p also needs to be copied between distributed memories - if(pimpl->opts_init.const_p && pimpl->allow_sstp_cond && pimpl->opts_init.exact_sstp_cond) - pimpl->distmem_real_vctrs.insert({&pimpl->sstp_tmp_p, detail::no_initial_value}); - // initialising Eulerian-Lagrangian coupling pimpl->init_sync(); // also, init of ambient_chem vectors pimpl->init_e2l(th, &pimpl->th); @@ -117,6 +113,9 @@ namespace libcloudphxx pimpl->init_vterm(); // init cached vt0 for the Beard fast vt formula pimpl->hskpng_vterm_invalid(); // init vt of SD + // initialising rc2, needed for cond with sstp_cond_act > 1 + pimpl->hskpng_approximate_rc2_invalid(); + // save _old for cond substepping pimpl->sstp_save(); diff --git a/src/particles_step.ipp b/src/particles_step.ipp index db530eaf6..5e5a9f233 100644 --- a/src/particles_step.ipp +++ b/src/particles_step.ipp @@ -187,44 +187,70 @@ namespace libcloudphxx // condensation/evaporation if (opts.cond) { + // prerequisite + pimpl->hskpng_sort(); + // calculate mean free path needed for molecular correction // NOTE: this should be don per each substep, but right now there is no logic // that would make it easy to do in exact (per-cell) substepping - pimpl->hskpng_mfp(); + pimpl->hskpng_mfp(); - if(pimpl->opts_init.exact_sstp_cond && pimpl->sstp_cond > 1) // apply substeps per-particle logic - { - if (pimpl->opts_init.ice_switch) - throw std::runtime_error("libcloudph++: deposition works only with per-cell substepping"); + if(pimpl->opts_init.exact_sstp_cond && (pimpl->sstp_cond > 1 || pimpl->sstp_cond_act > 1)) + { + if(!pimpl->opts_init.sstp_cond_mix) + { + pimpl->save_liq_ice_content_before_change(); // in drw_mom3_gp and d_ice_mass_gp + pimpl->n_filtered_gp.reset(); // n_filtered, acquired and filled in rw_mom3_ante_change, not needed anymore + } - for (int step = 0; step < pimpl->sstp_cond; ++step) - { - pimpl->sstp_step_exact(step); - if(opts.turb_cond) - pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); - pimpl->cond_sstp(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond, step); + pimpl->acquire_arrays_for_perparticle_sstp(); // sstp_dlt_rv_gp, etc. ; as in sstp_percell_step_exact() + pimpl->calculate_noncond_perparticle_sstp_delta(); // sstp_dlt_rv_gp, etc. ; as in sstp_percell_step_exact(); returns change / sstp_count; make it just change and multiply afterwards? + + // adaptive per-particle substepping + if(pimpl->opts_init.adaptive_sstp_cond) + { + // Method 1: Do condensation in one loop over SDs, since each SD can have different numbr of steps. + // One loop avoids synchronization between SDs after each substep. + pimpl->perparticle_nomixing_adaptive_sstp_cond(opts); + + // Method 2: loop over substeps. code removed after commit 14dca57a637369a46fb4c89fc0941122bfbfcf52 } - // copy sstp_tmp_rv and th to rv and th - pimpl->update_state(pimpl->rv, pimpl->sstp_tmp_rv); - pimpl->update_state(pimpl->th, pimpl->sstp_tmp_th); + else // per-particle substepping with same sstp_cond for all SDs (no adaptation, nosstp_cond_act, but mixing can be done) + { + for (int step = 0; step < pimpl->sstp_cond; ++step) + { + pimpl->apply_noncond_perparticle_sstp_delta(); + if(opts.turb_cond) + pimpl->apply_perparticle_sgs_supersat(); + + pimpl->template set_perparticle_drwX_to_minus_rwX<3>(/*use_stored_rw3=*/ step>0); + pimpl->cond_perparticle_advance_rw2(opts.RH_max, opts.turb_cond); + pimpl->template add_perparticle_rwX_to_drwX<3>(/*store_rw3=*/ step < pimpl->sstp_cond - 1); + pimpl->apply_perparticle_drw3_to_perparticle_rv_and_th(); + } + } + pimpl->release_arrays_for_perparticle_sstp(); + pimpl->apply_perparticle_cond_change_to_percell_rv_and_th(); } - else - // apply per-cell sstp logic + else // apply per-cell sstp logic, always with mixing (sstp_cond_mix==true required) { for (int step = 0; step < pimpl->sstp_cond; ++step) - { - pimpl->sstp_step(step); + { + pimpl->sstp_percell_step(step); if(opts.turb_cond) - pimpl->sstp_step_ssp(pimpl->dt / pimpl->sstp_cond); - pimpl->hskpng_Tpr(); - pimpl->cond(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond, step); + pimpl->apply_perparticle_sgs_supersat(); + pimpl->hskpng_Tpr(); + if(step == 0) + pimpl->save_liq_ice_content_before_change(); // in drw_mom3_gp and d_ice_mass_gp + pimpl->cond(pimpl->dt, opts.RH_max, opts.turb_cond, step); + // pimpl->cond(pimpl->dt / pimpl->sstp_cond, opts.RH_max, opts.turb_cond, step); if (pimpl->opts_init.ice_switch) { - if (opts.turb_cond) - throw std::runtime_error("libcloudph++:deposition doesnt work with turb_cond"); - pimpl->ice_dep(pimpl->dt / pimpl->sstp_cond, opts.RH_max, step); + // pimpl->ice_dep(pimpl->dt / pimpl->sstp_cond, opts.RH_max, step); + pimpl->ice_dep(pimpl->dt, opts.RH_max, step); } + pimpl->update_th_rv(); } } @@ -254,7 +280,7 @@ namespace libcloudphxx if (opts.chem_dsl) { //adjust trace gases to substepping - pimpl->sstp_step_chem(step); + pimpl->sstp_percell_step_chem(step); //dissolving trace gases (Henrys law) pimpl->chem_henry(pimpl->dt / pimpl->sstp_chem); @@ -368,9 +394,12 @@ namespace libcloudphxx // done if number of collisions > 1 in const_multi mode if(*(pimpl->increase_sstp_coal)) { - ++(pimpl->opts_init.sstp_coal); + ++(pimpl->sstp_coal); *(pimpl->increase_sstp_coal) = false; } + + // update rc2 (due to kappa and rd3 changes) + pimpl->hskpng_approximate_rc2_invalid(); } if (opts.turb_adve || opts.turb_cond) diff --git a/tests/python/physics/CMakeLists.txt b/tests/python/physics/CMakeLists.txt index 0247e4c9c..d9a82306e 100644 --- a/tests/python/physics/CMakeLists.txt +++ b/tests/python/physics/CMakeLists.txt @@ -1,7 +1,7 @@ #TODO: search scipy? # non-pytest tests -foreach(test test_coal coalescence_golovin coalescence_hall_davis_no_waals lgrngn_cond lgrngn_cond_substepping puddle) # the coalescence_onishi_hall test is turned off as not specific enough +foreach(test test_coal coalescence_golovin coalescence_hall_davis_no_waals lgrngn_cond lgrngn_cond_substepping lgrngn_cond_substepping_test lgrngn_cond_substepping_plot puddle) # the coalescence_onishi_hall test is turned off as not specific enough #TODO: indicate that tests depend on the lib add_test( NAME ${test} @@ -10,3 +10,5 @@ foreach(test test_coal coalescence_golovin coalescence_hall_davis_no_waals lgrng ) endforeach() +set_tests_properties(lgrngn_cond_substepping_test PROPERTIES DEPENDS lgrngn_cond_substepping) +set_tests_properties(lgrngn_cond_substepping_plot PROPERTIES DEPENDS lgrngn_cond_substepping) diff --git a/tests/python/physics/README_cond_substepping.md b/tests/python/physics/README_cond_substepping.md new file mode 100644 index 000000000..1a34e2e13 --- /dev/null +++ b/tests/python/physics/README_cond_substepping.md @@ -0,0 +1,146 @@ +# Condensation Substepping Tests + +This directory contains tests for condensation with various substepping configurations. + +## Files + +- `lgrngn_cond_substepping.py` - Main test script that runs condensation simulations +- `lgrngn_cond_substepping_test.py` - Comparison script that validates results against reference data +- `lgrngn_cond_substepping_plot.py` - Plotting script to visualize results + +## Running Tests + +### From the source directory + +```bash +cd tests/python/physics + +# Run the test and generate results +python lgrngn_cond_substepping.py + +# Compare with reference data +python lgrngn_cond_substepping_test.py + +# Generate plots +python lgrngn_cond_substepping_plot.py +``` + +### From the build directory (CMake test suite) + +```bash +cd build + +# Run all tests (includes lgrngn_cond_substepping and lgrngn_cond_substepping_test) +ctest + +# Run only the condensation substepping tests +ctest -R lgrngn_cond_substepping + +# Run with verbose output +ctest -R lgrngn_cond_substepping -V +``` + +The CMake test suite automatically runs both: +1. `lgrngn_cond_substepping` - generates results +2. `lgrngn_cond_substepping_test` - compares results with reference data (depends on the first test) + +## Workflow + +### 1. Run the test and generate results + +```bash +python lgrngn_cond_substepping.py +``` + +This will: +- Run condensation tests with various configurations (mixing, const_p, exact substepping, adaptive, etc.) +- Test different RH formulas (pv_cc, rv_cc, pv_tet, rv_tet) +- Test different numbers of substeps +- Save results to `test_results/lgrngn_cond_substepping_results.csv` + +### 2. Create reference data (first time only) + +After verifying that the results are correct, save them as reference data: + +```bash +python lgrngn_cond_substepping.py --save-ref +``` + +This creates `test_results/lgrngn_cond_substepping_refdata.csv` which will be used for future comparisons. + +### 3. Compare results with reference data + +On subsequent runs, compare new results against the reference: + +```bash +python lgrngn_cond_substepping_test.py +``` + +This will: +- Load both the results and reference data +- Compare each variable with appropriate tolerances +- Report any differences that exceed tolerances +- Exit with code 0 if all tests pass, 1 if any fail + +### 4. Visualize results + +Generate plots of the results: + +```bash +python lgrngn_cond_substepping_plot.py +``` + +This creates plots in `test_results/plots/` showing: +- Supersaturation vs. number of substeps +- Theta and rv leakage vs. number of substeps +- Activated concentration vs. number of substeps +- Radius moments vs. number of substeps +- Execution time vs. number of substeps + +Each variable is plotted for all RH formulas with logarithmic x-axis. + +## Test Tolerances + +The comparison script uses the following default tolerances: + +- `ss` (supersaturation): 1% relative tolerance +- `th_diff`, `rv_diff` (leakage): Absolute tolerances (1e-5, 1e-6) +- `act`, `mr`, `sr`, `tr` (concentration and moments): 2% relative tolerance +- `exectime`: Not compared (machine-dependent) + +## Configuration Options Tested + +- **mixing**: Communication of rv/th changes between SDs after each substep +- **constp**: Constant pressure vs. variable pressure +- **exact_sstp**: Per-particle vs. per-cell substepping +- **adaptive**: Adaptive determination of substep count +- **sstp_cond**: Number of condensation substeps (1, 2, 3, 4, 6, 8, 32) +- **sstp_cond_act**: Number of substeps for activating droplets (1, 8) +- **RH_formula**: Relative humidity formula (pv_cc, rv_cc, pv_tet, rv_tet) + +## Example Complete Workflow + +```bash +# Initial run - establish reference data +python lgrngn_cond_substepping.py --save-ref + +# Generate initial plots +python lgrngn_cond_substepping_plot.py + +# After making code changes, run test again +python lgrngn_cond_substepping.py + +# Compare with reference +python lgrngn_cond_substepping_test.py + +# Generate new plots to compare visually +python lgrngn_cond_substepping_plot.py +``` + +## Updating Reference Data + +If intentional changes are made that affect results: + +1. Verify the new results are correct +2. Update reference data: `python lgrngn_cond_substepping.py --save-ref` +3. Document the reason for the change in git commit message diff --git a/tests/python/physics/lgrngn_cond.py b/tests/python/physics/lgrngn_cond.py index 5eef756f7..ede8292c7 100644 --- a/tests/python/physics/lgrngn_cond.py +++ b/tests/python/physics/lgrngn_cond.py @@ -1,4 +1,5 @@ import sys +sys.path.insert(0, "../../../build/bindings/python/") sys.path.insert(0, "../../bindings/python/") from numpy import array as arr_t # ndarray dtype default to float64, while array's is int64! @@ -155,22 +156,28 @@ def test(RH_formula, _step_count, substep_count, exact_substep, constp, opts_dt) ss, th_diff_1 , rv_diff = test(RH_formula, 40, 1, exact_sstp, constp, opts_dt) print(ss, th_diff_1 , rv_diff) assert(abs(ss) < 4.5e-3) +# assert(abs(rv_diff) < 2.5e-5) assert(abs(rv_diff) < 1e-9) ss, th_diff_10 , rv_diff = test(RH_formula, 40, 10, exact_sstp, constp, opts_dt) print(ss, th_diff_10 , rv_diff) assert(abs(ss) < 4.5e-3) +# assert(abs(rv_diff) < 2.5e-5) assert(abs(rv_diff) < 1e-9) ss, th_diff_100, rv_diff = test(RH_formula, 40, 100, exact_sstp, constp, opts_dt) print(ss, th_diff_100, rv_diff) assert(abs(ss) < 4.5e-3) +# assert(abs(rv_diff) < 2.5e-5) assert(abs(rv_diff) < 1e-9) if constp == False: +# assert(abs(th_diff_1) < 8e-2) +# assert(abs(th_diff_10) < 8e-3) +# assert(abs(th_diff_100) < 6e-3) # little gain assert(abs(th_diff_1) < 4.2e-2) assert(abs(th_diff_10) < 4.2e-3) - assert(abs(th_diff_100) < 4.2e-4) + assert(abs(th_diff_100) < 4.2e-4) else : # TODO: why with constant pressure the error doesn't scale so well? # is there a systematic error caused by the fact that with constant pressure, diff --git a/tests/python/physics/lgrngn_cond_substepping.py b/tests/python/physics/lgrngn_cond_substepping.py index 0b453f9b8..2bd85adba 100644 --- a/tests/python/physics/lgrngn_cond_substepping.py +++ b/tests/python/physics/lgrngn_cond_substepping.py @@ -2,8 +2,14 @@ # and low RH_max to see how substepping affects activation # it's with GCCNs - large differences between Tetens and Clau-Clap in this case! # also makes substepping important! +# +# Usage: +# python lgrngn_cond_substepping.py # Run tests and save results +# python lgrngn_cond_substepping.py --save-ref # Also save results as reference data import sys +import os +sys.path.insert(0, "../../../build/bindings/python/") sys.path.insert(0, "../../bindings/python/") from numpy import array as arr_t # ndarray dtype default to float64, while array's is int64! @@ -15,6 +21,8 @@ from libcloudphxx import lgrngn from libcloudphxx import common +import pandas as pd + # wrapper for timing excecution time def wrapper(func, opts, th, rv, rhod): def wrapped(): @@ -44,19 +52,29 @@ def lognormal2(lnr): 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.RH_max = 1.0001 +opts_init.RH_max = 0.95 opts_init.ice_switch = False -opts_init.RH_max = 1.0001 opts_init.dt = 1 opts_init.sd_conc = int(1e3) opts_init.n_sd_max = opts_init.sd_conc -backend = lgrngn.backend_t.serial +opts_init.rc2_T = 10 # results are the same for 0C to 100C +opts_init.sstp_cond_adapt_drw2_eps = 1e-3 #1e-3 +opts_init.sstp_cond_adapt_drw2_max = 2 #2 + + +# backend = lgrngn.backend_t.CUDA +backend = lgrngn.backend_t.OpenMP +# backend = lgrngn.backend_t.serial opts.adve = False opts.sedi = False opts.cond = True opts.coal = False opts.chem = False +opts.RH_max = 1.005 +# opts.RH_max = 0.9 opts.ice_nucl = False #opts.RH_max = 1.005 @@ -132,13 +150,19 @@ def supersat_state(): return rhod, th, rv, p -def test(RH_formula, step_count, substep_count, exact_substep, constp): - print("[RH_formula = ", RH_formula,"]") - print("step_count = ", step_count, " substep_count = ", substep_count, "exact substepping = ", exact_substep, "constp = ", constp) +def test(RH_formula, step_count, substep_count, exact_substep, constp, mixing, adaptive, sstp_cond_act): + print("[RH_formula = ", RH_formula,"]", flush=True) + print("step_count = ", step_count, " substep_count = ", substep_count, "exact substepping = ", exact_substep, "constp = ", constp, "mixing per substep = ", mixing, "adaptive substep no. = ", adaptive, "sstp_cond_act = ", sstp_cond_act, flush=True) opts_init.sstp_cond=substep_count opts_init.exact_sstp_cond=exact_substep opts_init.RH_formula = RH_formula + opts_init.sstp_cond_mix = mixing + opts_init.adaptive_sstp_cond=adaptive + opts_init.sstp_cond_act=sstp_cond_act + + # opts_init.rd_min=1e-10 + # opts_init.rd_max=1e-5 rhod, th, rv, p = initial_state() rhod_ss, th_ss, rv_ss, p_ss = supersat_state() @@ -151,6 +175,9 @@ def test(RH_formula, step_count, substep_count, exact_substep, constp): th_ss[0] = common.th_dry2std(th_ss[0], rv_ss[0]) opts_init.const_p = True opts_init.th_dry = False + else: + opts_init.const_p = False + opts_init.th_dry = True prtcls = lgrngn.factory(backend, opts_init) @@ -160,15 +187,16 @@ def test(RH_formula, step_count, substep_count, exact_substep, constp): prtcls.init(th, rv, rhod, p_ss) # go to supersaturated air, density changes to test density substepping too - rhod[0] = rhod_ss - th[0] = th_ss - rv[0] = rv_ss + rhod[0] = rhod_ss[0] + th[0] = th_ss[0] + rv[0] = rv_ss[0] rv_init = rv.copy() th_init = th.copy() exectime = 0 opts.cond = 0 for step in arange(step_count): + # print("step ", step, flush=True) wrapped = wrapper(prtcls.step_sync, opts, th, rv, rhod) exectime += timeit.timeit(wrapped, number=1) prtcls.step_async(opts) @@ -181,13 +209,13 @@ def test(RH_formula, step_count, substep_count, exact_substep, constp): if step == 0: print("initial supersaturation", supersaturation(prtcls)) opts.cond = 1 -# print step, supersaturation(prtcls), th[0], rv[0], mean_r(prtcls), second_r(prtcls), third_r(prtcls), act_conc(prtcls) ss_post_cond = supersaturation(prtcls) print("supersaturation after condensation", ss_post_cond, th[0], rv[0], mean_r(prtcls), second_r(prtcls), third_r(prtcls), act_conc(prtcls)) - assert(abs(th[0] - exp_th[constp]) < 1e-4 * exp_th[constp]) - assert(abs(rv[0] - exp_rv[constp]) < 1e-3 * exp_rv[constp]) + th_post_cond = th[0] + rv_post_cond = rv[0] + rv_diff = rv_init.copy() - rv[0].copy() th_diff = th_init.copy() - th[0].copy() @@ -202,228 +230,78 @@ def test(RH_formula, step_count, substep_count, exact_substep, constp): wrapped = wrapper(prtcls.step_sync, opts, th, rv, rhod) exectime += timeit.timeit(wrapped, number=1) prtcls.step_async(opts) - # print step, supersaturation(prtcls), th[0], rv[0], mean_r(prtcls), second_r(prtcls), third_r(prtcls), act_conc(prtcls) ss_post_evap = supersaturation(prtcls) print("supersaturation after evaporation", ss_post_evap, th[0], rv[0], mean_r(prtcls), second_r(prtcls), third_r(prtcls), act_conc(prtcls), gccn_conc(prtcls)) - # after evaporation, only larger mode particles should have r > 0.5 microns - assert(act_conc(prtcls) == gccn_conc(prtcls)) print('execution time: ', exectime) - - return ss_post_cond, th[0] - th_init[0] - th_diff[0], rv[0] - rv_init[0] - rv_diff[0], act_conc_post_cond, mean_r_post_cond, second_r_post_cond, third_r_post_cond - - -for constp in [False, True]: - for exact_sstp in [False, True]: - for RH_formula in [lgrngn.RH_formula_t.pv_cc, lgrngn.RH_formula_t.rv_cc, lgrngn.RH_formula_t.pv_tet, lgrngn.RH_formula_t.rv_tet]: - - ss, th_diff_1 , rv_diff, act, mr, sr, tr = test(RH_formula, 100, 1, exact_sstp, constp) - print(ss, th_diff_1 , rv_diff, act, mr, sr, tr) - assert(ss_min < ss < ss_max) # GCCNs condensate even at ss<0 - assert(abs(rv_diff) < exp_rv_diff[constp]) - assert(abs(th_diff_1) < 7e-3) - - # expected concentration of droplets with r>0.5um - exp_act ={ True: { # constp - lgrngn.RH_formula_t.pv_cc : 40809, - lgrngn.RH_formula_t.rv_cc : 40809, - lgrngn.RH_formula_t.pv_tet : 40809, - lgrngn.RH_formula_t.rv_tet : 8128, - }, - False: { # varp - lgrngn.RH_formula_t.pv_cc : 40809, - lgrngn.RH_formula_t.rv_cc : 40809, - lgrngn.RH_formula_t.pv_tet : 40809, - lgrngn.RH_formula_t.rv_tet : 8125, - } - } - # expected mean radius of droplets with r>0.5um - exp_mr ={ True: { # constp - lgrngn.RH_formula_t.pv_cc : 3.141, - lgrngn.RH_formula_t.rv_cc : 3.156, - lgrngn.RH_formula_t.pv_tet : 2.767, - lgrngn.RH_formula_t.rv_tet : 8.170, - }, - False: { # varp - lgrngn.RH_formula_t.pv_cc : 2.905, - lgrngn.RH_formula_t.rv_cc : 2.92, - lgrngn.RH_formula_t.pv_tet : 2.417, - lgrngn.RH_formula_t.rv_tet : 8.099, - } - } - - # expected second moment of droplets with r>0.5um - exp_sr ={ True: { # constp - lgrngn.RH_formula_t.pv_cc : 1.662e-11, - lgrngn.RH_formula_t.rv_cc : 1.668e-11, - lgrngn.RH_formula_t.pv_tet : 1.528e-11, - lgrngn.RH_formula_t.rv_tet : 6.760e-11, - }, - False: { # varp - lgrngn.RH_formula_t.pv_cc : 1.559e-11, - lgrngn.RH_formula_t.rv_cc : 1.564e-11, - lgrngn.RH_formula_t.pv_tet : 1.421e-11, - lgrngn.RH_formula_t.rv_tet : 6.641e-11, - } - } - - # expected third moment of droplets with r>0.5um - exp_tr ={ True: { # constp - lgrngn.RH_formula_t.pv_cc : 1.226e-16, - lgrngn.RH_formula_t.rv_cc : 1.228e-16, - lgrngn.RH_formula_t.pv_tet : 1.179e-16, - lgrngn.RH_formula_t.rv_tet : 5.662e-16, - }, - False: { # varp - lgrngn.RH_formula_t.pv_cc : 1.173e-16, - lgrngn.RH_formula_t.rv_cc : 1.174e-16, - lgrngn.RH_formula_t.pv_tet : 1.131e-16, - lgrngn.RH_formula_t.rv_tet : 5.513e-16, - } - } - - assert(abs(act - exp_act[constp][RH_formula]) < 1e-3 * exp_act[constp][RH_formula]) - assert(abs(mr - exp_mr[constp][RH_formula]) < 1e-3 * exp_mr[constp][RH_formula]) - assert(abs(sr - exp_sr[constp][RH_formula]) < 1.2e-3 * exp_sr[constp][RH_formula]) - assert(abs(tr - exp_tr[constp][RH_formula]) < 2e-3 * exp_tr[constp][RH_formula]) - - - ss, th_diff_10 , rv_diff, act, mr, sr, tr = test(RH_formula, 100, 10, exact_sstp, constp) - print(ss, th_diff_10 , rv_diff, act, mr, sr, tr) - assert(ss_min < ss < ss_max) # GCCNs condensate even at ss<0 - assert(abs(rv_diff) < exp_rv_diff[constp]) - assert(abs(th_diff_10) < 5e-3) - - - exp_act ={ True: { # constp - lgrngn.RH_formula_t.pv_cc : 16561, - lgrngn.RH_formula_t.rv_cc : 17036, - lgrngn.RH_formula_t.pv_tet : 9485, - lgrngn.RH_formula_t.rv_tet : 8125, - }, - False: { # varp - lgrngn.RH_formula_t.pv_cc : 14931, - lgrngn.RH_formula_t.rv_cc : 15502, - lgrngn.RH_formula_t.pv_tet : 8131, - lgrngn.RH_formula_t.rv_tet : 8125, - } - } - - exp_mr ={ True: { # constp - lgrngn.RH_formula_t.pv_cc : 4.726, - lgrngn.RH_formula_t.rv_cc : 4.642, - lgrngn.RH_formula_t.pv_tet : 7.200, - lgrngn.RH_formula_t.rv_tet : 8.212, - }, - False: { # varp - lgrngn.RH_formula_t.pv_cc : 4.878, - lgrngn.RH_formula_t.rv_cc : 4.743, - lgrngn.RH_formula_t.pv_tet : 8.191, - lgrngn.RH_formula_t.rv_tet : 8.133, - } - } - - exp_sr ={ True: { # constp - lgrngn.RH_formula_t.pv_cc : 3.526e-11, - lgrngn.RH_formula_t.rv_cc : 3.435e-11, - lgrngn.RH_formula_t.pv_tet : 5.961e-11, - lgrngn.RH_formula_t.rv_tet : 6.828e-11, - }, - False: { # varp - lgrngn.RH_formula_t.pv_cc : 3.78e-11, - lgrngn.RH_formula_t.rv_cc : 3.646e-11, - lgrngn.RH_formula_t.pv_tet : 6.8e-11, - lgrngn.RH_formula_t.rv_tet : 6.699e-11, - } - } - - exp_tr ={ True: { # constp - lgrngn.RH_formula_t.pv_cc : 2.948e-16, - lgrngn.RH_formula_t.rv_cc : 2.867e-16, - lgrngn.RH_formula_t.pv_tet : 5.054e-16, - lgrngn.RH_formula_t.rv_tet : 5.749e-16, - }, - False: { # varp - lgrngn.RH_formula_t.pv_cc : 3.166e-16, - lgrngn.RH_formula_t.rv_cc : 3.051e-16, - lgrngn.RH_formula_t.pv_tet : 5.72e-16, - lgrngn.RH_formula_t.rv_tet : 5.585e-16, - } - } - - assert(abs(act - exp_act[constp][RH_formula]) < 1.5e-2 * exp_act[constp][RH_formula]) - assert(abs(mr - exp_mr[constp][RH_formula]) < 1.5e-2 * exp_mr[constp][RH_formula]) - assert(abs(sr - exp_sr[constp][RH_formula]) < 1.5e-2 * exp_sr[constp][RH_formula]) - assert(abs(tr - exp_tr[constp][RH_formula]) < 1.5e-2 * exp_tr[constp][RH_formula]) - - ss, th_diff_100 , rv_diff, act, mr, sr, tr = test(RH_formula, 100, 100, exact_sstp, constp) - print(ss, th_diff_100 , rv_diff, act, mr, sr, tr) - assert(ss_min < ss < ss_max) # GCCNs condensate even at ss<0 - assert(abs(rv_diff) < exp_rv_diff[constp]) - assert(abs(th_diff_100) < 5e-3) - - exp_act ={ True: { # constp - lgrngn.RH_formula_t.pv_cc : 16097, - lgrngn.RH_formula_t.rv_cc : 16404, - lgrngn.RH_formula_t.pv_tet : 8979, - lgrngn.RH_formula_t.rv_tet : 8125, - }, - False: { # varp - lgrngn.RH_formula_t.pv_cc : 13040, - lgrngn.RH_formula_t.rv_cc : 13751, - lgrngn.RH_formula_t.pv_tet : 8123, - lgrngn.RH_formula_t.rv_tet : 8125, - } - } - - exp_mr ={ True: { # constp - lgrngn.RH_formula_t.pv_cc : 4.792, - lgrngn.RH_formula_t.rv_cc : 4.741, - lgrngn.RH_formula_t.pv_tet : 7.572, - lgrngn.RH_formula_t.rv_tet : 8.222, - }, - False: { # varp - lgrngn.RH_formula_t.pv_cc : 5.438, - lgrngn.RH_formula_t.rv_cc : 5.203, - lgrngn.RH_formula_t.pv_tet : 8.2095, - lgrngn.RH_formula_t.rv_tet : 8.145, - } - } - - exp_sr ={ True: { # constp - lgrngn.RH_formula_t.pv_cc : 3.622e-11, - lgrngn.RH_formula_t.rv_cc : 3.560e-11, - lgrngn.RH_formula_t.pv_tet : 6.307e-11, - lgrngn.RH_formula_t.rv_tet : 6.844e-11, - }, - False: { # varp - lgrngn.RH_formula_t.pv_cc : 4.324e-11, - lgrngn.RH_formula_t.rv_cc : 4.105e-11, - lgrngn.RH_formula_t.pv_tet : 6.8239e-11, - lgrngn.RH_formula_t.rv_tet : 6.717e-11, - } - } - - exp_tr ={ True: { # constp - lgrngn.RH_formula_t.pv_cc : 3.041e-16, - lgrngn.RH_formula_t.rv_cc : 2.985e-16, - lgrngn.RH_formula_t.pv_tet : 5.357e-16, - lgrngn.RH_formula_t.rv_tet : 5.768e-16, - }, - False: { # varp - lgrngn.RH_formula_t.pv_cc : 3.64e-16, - lgrngn.RH_formula_t.rv_cc : 3.452e-16, - lgrngn.RH_formula_t.pv_tet : 5.7412e-16, - lgrngn.RH_formula_t.rv_tet : 5.609e-16, - } - } - - # reduced precision due to differences in results between Linux and OSX - assert(abs(act - exp_act[constp][RH_formula]) < 1.5e-2 * exp_act[constp][RH_formula]) - assert(abs(mr - exp_mr[constp][RH_formula]) < 1.5e-2 * exp_mr[constp][RH_formula]) - assert(abs(sr - exp_sr[constp][RH_formula]) < 1.5e-2 * exp_sr[constp][RH_formula]) - assert(abs(tr - exp_tr[constp][RH_formula]) < 1.5e-2 * exp_tr[constp][RH_formula]) - - + results = { + 'ss': ss_post_cond, + 'th_diff': th[0] - th_init[0] - th_diff[0], + 'rv_diff': rv[0] - rv_init[0] - rv_diff[0], + 'act': act_conc_post_cond, + 'mr': mean_r_post_cond, + 'sr': second_r_post_cond, + 'tr': third_r_post_cond, + 'exectime': exectime, + 'act_post_evap': act_conc(prtcls), + 'gccn_post_evap': gccn_conc(prtcls), + 'th_post_cond': th_post_cond, + 'rv_post_cond': rv_post_cond, + } + + return results + +records = [] + +for adaptive in [True, False]: # adaptive condensation substepping? + for mixing in [False, True]: # communicate changes in rv an theta between SDs after each substep? + for constp in [False, True]: + for exact_sstp in [False, True]: + # for RH_formula in [lgrngn.RH_formula_t.pv_cc]: + for RH_formula in [lgrngn.RH_formula_t.pv_cc, lgrngn.RH_formula_t.rv_cc, lgrngn.RH_formula_t.pv_tet, lgrngn.RH_formula_t.rv_tet]: + for sstp_cond in [1, 2, 3, 4, 6, 8, 32]: + for sstp_cond_act in [1, 8]: + if(mixing == False and exact_sstp == False): + continue # mixing can be turned off only with exact substepping + if(exact_sstp == False and adaptive == True): + continue # adaptive substepping requires exact substepping + if(sstp_cond_act > 1 and adaptive == False): + continue # sstp_cond_act > 1 requires adaptive substepping + if(adaptive == True and exact_sstp == True and mixing == True): + continue # adaptive perparticle doesnt work with mixing yet + + results = test(RH_formula, 100, sstp_cond, exact_sstp, constp, mixing, adaptive, sstp_cond_act) + print(results) + results['mixing'] = mixing + results['adaptive'] = adaptive + results['constp'] = constp + results['exact_sstp'] = exact_sstp + results['RH_formula'] = RH_formula + results['sstp_cond'] = sstp_cond + results['sstp_cond_act'] = sstp_cond_act + results['sstp_cond_adapt_drw2_eps'] = opts_init.sstp_cond_adapt_drw2_eps + results['sstp_cond_adapt_drw2_max'] = opts_init.sstp_cond_adapt_drw2_max + records.append(results) + +# save results to a CSV file for refdata comparison and for plotting +df = pd.DataFrame(records) +df['sd_conc'] = opts_init.sd_conc # Add the column to all rows at once +df['RH_max'] = opts_init.RH_max +df['dt'] = 1 +os.makedirs("test_results", exist_ok=True) +df.to_csv("test_results/lgrngn_cond_substepping_results.csv", index=False) + +# Optionally save as reference data +if '--save-ref' in sys.argv: + print("\nSaving results as reference data...") +# Get the directory where this script is located + script_dir = os.path.dirname(os.path.abspath(__file__)) + refdata_dir = os.path.join(script_dir, "refdata") + os.makedirs(refdata_dir, exist_ok=True) + refdata_file = os.path.join(refdata_dir, "lgrngn_cond_substepping_refdata.csv") + df.to_csv(refdata_file, index=False) + print("Reference data saved to: test_results/lgrngn_cond_substepping_refdata.csv") + print("Future runs can be compared against this reference using:") + print(" python lgrngn_cond_substepping_test.py") diff --git a/tests/python/physics/lgrngn_cond_substepping_plot.py b/tests/python/physics/lgrngn_cond_substepping_plot.py new file mode 100644 index 000000000..d76d5c554 --- /dev/null +++ b/tests/python/physics/lgrngn_cond_substepping_plot.py @@ -0,0 +1,46 @@ +import pandas as pd +import matplotlib.pyplot as plt +import os + +# Load the results +df = pd.read_csv("test_results/lgrngn_cond_substepping_results.csv") + +variables = [ + ('ss', 'Supersaturation [%]'), + ('th_diff', 'theta leaked [K]'), + ('rv_diff', 'rv leaked [1]'), + ('act', 'Activated concentration [1/g]'), + ('mr', 'Mean radius [um]'), + ('sr', 'Second moment of radius [m^2]'), + ('tr', 'Third moment of radius [m^3]'), + ('exectime', 'Execution time [s]'), +] + +RH_formula_names = { + 0 : "pv_cc", 1: "rv_cc", 2 :"pv_tet", 3 : "rv_tet" +} + +group_cols = ['mixing', 'constp', 'exact_sstp', 'adaptive', 'sstp_cond_act'] +RH_formulas = sorted(df['RH_formula'].unique()) + +os.makedirs("test_results/plots", exist_ok=True) + +for var, ylabel in variables: + fig, axs = plt.subplots(2, 2, figsize=(14, 10), sharex=True) + axs = axs.flatten() + for i, RH_formula in enumerate(RH_formulas): + ax = axs[i] + subdf = df[df['RH_formula'] == RH_formula] + for name, group in subdf.groupby(group_cols): + label = ', '.join(f"{col}={val}" for col, val in zip(group_cols, name)) + ax.plot(group['sstp_cond'], group[var], marker='o', label=label, linestyle='--') + ax.set_title(f'RH_formula={RH_formula_names[RH_formula]}') + ax.set_xlabel('Number of substeps') + ax.set_ylabel(ylabel) + ax.set_xscale('log') + if var == 'exectime': + ax.set_yscale('log') + ax.legend(fontsize='small', loc='best') + plt.tight_layout() + plt.savefig(f"test_results/plots/{var}_vs_substeps.png") + plt.close() \ No newline at end of file diff --git a/tests/python/physics/lgrngn_cond_substepping_test.py b/tests/python/physics/lgrngn_cond_substepping_test.py new file mode 100644 index 000000000..df4623b8c --- /dev/null +++ b/tests/python/physics/lgrngn_cond_substepping_test.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 +""" +Test script to compare condensation substepping results against reference data. +This script runs the lgrngn_cond_substepping.py test and compares the results +with previously stored reference data. +""" + +import sys +import os +import pandas as pd +import numpy as np + +def compare_results(results_file, refdata_file, tolerances=None): + """ + Compare results CSV with reference data CSV. + + Parameters: + ----------- + results_file : str + Path to the results CSV file + refdata_file : str + Path to the reference data CSV file + tolerances : dict, optional + Dictionary mapping column names to absolute or relative tolerances. + Example: {'ss': {'rtol': 0.01}, 'exectime': {'atol': 0.1}} + + Returns: + -------- + bool : True if all comparisons pass, False otherwise + """ + + # Default tolerances + if tolerances is None: + tolerances = { + 'ss': {'rtol': 1.5e-2}, # 1% relative tolerance for supersaturation + 'th_diff': {'atol': 1e-5}, # absolute tolerance for theta leak + 'rv_diff': {'atol': 1e-6}, # absolute tolerance for rv leak + 'act': {'rtol': 1.5e-2}, # relative tolerance for activated concentration + 'mr': {'rtol': 1.5e-2}, # relative tolerance for mean radius + 'sr': {'rtol': 1.5e-2}, # relative tolerance for second moment + 'tr': {'rtol': 1.5e-2}, # relative tolerance for third moment + 'act_post_evap': {'rtol': 1.5e-2}, + 'gccn_post_evap': {'rtol': 1.5e-2}, + 'th_post_cond': {'rtol': 1e-4}, + 'rv_post_cond': {'rtol': 1e-3}, + } + + # Load data + try: + results = pd.read_csv(results_file) + refdata = pd.read_csv(refdata_file) + except FileNotFoundError as e: + print(f"Error: Could not find file: {e}") + return False + + # Check if both dataframes have the same shape + if results.shape != refdata.shape: + print(f"Error: Shape mismatch. Results: {results.shape}, Reference: {refdata.shape}") + return False + + # Check if both dataframes have the same columns + if not set(results.columns) == set(refdata.columns): + print(f"Error: Column mismatch.") + print(f"Results columns: {sorted(results.columns)}") + print(f"Reference columns: {sorted(refdata.columns)}") + return False + + # Merge on configuration columns to align rows + config_cols = ['mixing', 'constp', 'exact_sstp', 'adaptive', 'RH_formula', 'sstp_cond', 'sstp_cond_act'] + merged = results.merge(refdata, on=config_cols, suffixes=('_result', '_ref')) + + if len(merged) != len(results): + print(f"Error: Not all configurations matched. {len(merged)} out of {len(results)} matched.") + return False + + # Compare each variable + all_pass = True + comparison_cols = [col for col in tolerances.keys() if col in results.columns] + + print("\n" + "="*80) + print("COMPARISON RESULTS") + print("="*80) + + for col in comparison_cols: + result_col = f"{col}_result" + ref_col = f"{col}_ref" + + if result_col not in merged.columns or ref_col not in merged.columns: + continue + + tol = tolerances[col] + + # Calculate differences + if 'rtol' in tol: + # Relative tolerance + rtol = tol['rtol'] + # Avoid division by zero + ref_values = merged[ref_col].replace(0, np.nan) + rel_diff = np.abs((merged[result_col] - merged[ref_col]) / ref_values) + max_rel_diff = rel_diff.max() + passed = (rel_diff <= rtol).all() or np.isnan(max_rel_diff) + + print(f"\n{col}:") + print(f" Max relative difference: {max_rel_diff:.6e} (tolerance: {rtol:.6e})") + + if not passed: + print(f" FAILED: Relative difference exceeds tolerance") + failing_rows = merged[rel_diff > rtol] + print(f" Number of failing configurations: {len(failing_rows)}") + print(f" Example failing case:") + if len(failing_rows) > 0: + idx = rel_diff.idxmax() + print(f" Config: {merged.loc[idx, config_cols].to_dict()}") + print(f" Result: {merged.loc[idx, result_col]:.6e}") + print(f" Reference: {merged.loc[idx, ref_col]:.6e}") + print(f" Relative diff: {rel_diff.loc[idx]:.6e}") + all_pass = False + else: + print(f" PASSED") + + elif 'atol' in tol: + # Special handling for rv_diff and th_diff: check if |result| <= |ref| + tolerance + if col in ['rv_diff', 'th_diff']: + # For leakage variables, we want to verify that the absolute value doesn't increase + # It's acceptable if |result| <= |ref| * (1 + 0.015), i.e., within 1.5% increase + ref_abs = np.abs(merged[ref_col]) + result_abs = np.abs(merged[result_col]) + tolerance_margin = 0.015 # 1.5% + max_allowed = ref_abs * (1 + tolerance_margin) + passed = (result_abs <= max_allowed).all() + max_result = result_abs.max() + max_ref = ref_abs.max() + + print(f"\n{col}:") + print(f" Max |result|: {max_result:.6e}") + print(f" Max |reference|: {max_ref:.6e}") + print(f" Tolerance: |ref| * (1 + {tolerance_margin})") + + if not passed: + print(f" FAILED: |result| exceeds |reference| * (1 + {tolerance_margin}) for some configurations") + failing_mask = result_abs > max_allowed + failing_rows = merged[failing_mask] + print(f" Number of failing configurations: {len(failing_rows)}") + print(f" Example failing case:") + if len(failing_rows) > 0: + idx = (result_abs - max_allowed).idxmax() + print(f" Config: {merged.loc[idx, config_cols].to_dict()}") + print(f" |result|: {result_abs.loc[idx]:.6e}") + print(f" |reference|: {ref_abs.loc[idx]:.6e}") + print(f" Max allowed: {max_allowed.loc[idx]:.6e}") + print(f" Excess: {(result_abs.loc[idx] - max_allowed.loc[idx]):.6e}") + all_pass = False + else: + print(f" PASSED: |result| <= |reference| * (1 + {tolerance_margin}) for all configurations") + else: + # Standard absolute tolerance for other variables + atol = tol['atol'] + abs_diff = np.abs(merged[result_col] - merged[ref_col]) + max_abs_diff = abs_diff.max() + passed = (abs_diff <= atol).all() + + print(f"\n{col}:") + print(f" Max absolute difference: {max_abs_diff:.6e} (tolerance: {atol:.6e})") + + if not passed: + print(f" FAILED: Absolute difference exceeds tolerance") + failing_rows = merged[abs_diff > atol] + print(f" Number of failing configurations: {len(failing_rows)}") + print(f" Example failing case:") + if len(failing_rows) > 0: + idx = abs_diff.idxmax() + print(f" Config: {merged.loc[idx, config_cols].to_dict()}") + print(f" Result: {merged.loc[idx, result_col]:.6e}") + print(f" Reference: {merged.loc[idx, ref_col]:.6e}") + print(f" Absolute diff: {abs_diff.loc[idx]:.6e}") + all_pass = False + else: + print(f" PASSED") + + # Additional check: act_post_evap should equal gccn_post_evap + # After evaporation, only larger mode particles should have r > 0.5 microns + if 'act_post_evap' in results.columns and 'gccn_post_evap' in results.columns: + print("\n" + "-"*80) + print("ADDITIONAL CHECK: act_post_evap == gccn_post_evap") + print("-"*80) + + act_post_evap = merged['act_post_evap_result'] + gccn_post_evap = merged['gccn_post_evap_result'] + + # Allow small tolerance for floating point comparison + equality_tol = 1e-10 + abs_diff = np.abs(act_post_evap - gccn_post_evap) + max_diff = abs_diff.max() + equality_check = (abs_diff <= equality_tol).all() + + print(f"Max absolute difference: {max_diff:.6e} (tolerance: {equality_tol:.6e})") + + if not equality_check: + print(f"FAILED: act_post_evap != gccn_post_evap for some configurations") + failing_rows = merged[abs_diff > equality_tol] + print(f"Number of failing configurations: {len(failing_rows)}") + print(f"Example failing case:") + if len(failing_rows) > 0: + idx = abs_diff.idxmax() + print(f" Config: {merged.loc[idx, config_cols].to_dict()}") + print(f" act_post_evap: {act_post_evap.loc[idx]:.6e}") + print(f" gccn_post_evap: {gccn_post_evap.loc[idx]:.6e}") + print(f" Difference: {abs_diff.loc[idx]:.6e}") + all_pass = False + else: + print(f"PASSED: act_post_evap == gccn_post_evap for all configurations") + + # Additional check: supersaturation should be in expected range + # GCCNs condense even at ss<0 + if 'ss' in results.columns: + print("\n" + "-"*80) + print("ADDITIONAL CHECK: ss within expected range") + print("-"*80) + + ss_min = -0.96 + ss_max = -0.71 + ss_values = merged['ss_result'] + + min_ss = ss_values.min() + max_ss = ss_values.max() + range_check = (ss_values >= ss_min).all() and (ss_values <= ss_max).all() + + print(f"Expected range: [{ss_min}, {ss_max}]") + print(f"Actual range: [{min_ss:.3f}, {max_ss:.3f}]") + + if not range_check: + print(f"FAILED: ss outside expected range for some configurations") + failing_rows_low = merged[ss_values < ss_min] + failing_rows_high = merged[ss_values > ss_max] + failing_count = len(failing_rows_low) + len(failing_rows_high) + print(f"Number of failing configurations: {failing_count}") + + if len(failing_rows_low) > 0: + idx = ss_values.idxmin() + print(f"Example case with ss too low:") + print(f" Config: {merged.loc[idx, config_cols].to_dict()}") + print(f" ss: {ss_values.loc[idx]:.6f} (min allowed: {ss_min})") + + if len(failing_rows_high) > 0: + idx = ss_values.idxmax() + print(f"Example case with ss too high:") + print(f" Config: {merged.loc[idx, config_cols].to_dict()}") + print(f" ss: {ss_values.loc[idx]:.6f} (max allowed: {ss_max})") + + all_pass = False + else: + print(f"PASSED: ss within expected range for all configurations") + + print("\n" + "="*80) + if all_pass: + print("ALL TESTS PASSED") + else: + print("SOME TESTS FAILED") + print("="*80 + "\n") + + return all_pass + + +def main(): + """Main test function""" + + # Define file paths + results_file = "test_results/lgrngn_cond_substepping_results.csv" + + # Get the directory where this script is located + script_dir = os.path.dirname(os.path.abspath(__file__)) + # Define file paths relative to script location + refdata_file = os.path.join(script_dir, "refdata", "lgrngn_cond_substepping_refdata.csv") + + + # Check if reference data exists + if not os.path.exists(refdata_file): + print(f"Warning: Reference data file not found: {refdata_file}") + print("To create reference data, run:") + print(f" cp {results_file} {refdata_file}") + print("\nThis will use the current results as the reference for future tests.") + return 1 + + # Check if results file exists + if not os.path.exists(results_file): + print(f"Error: Results file not found: {results_file}") + print("Please run lgrngn_cond_substepping.py first to generate results.") + return 1 + + # Run comparison + success = compare_results(results_file, refdata_file) + + return 0 if success else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/python/physics/refdata/lgrngn_cond_substepping_refdata.csv b/tests/python/physics/refdata/lgrngn_cond_substepping_refdata.csv new file mode 100644 index 000000000..c99b609a5 --- /dev/null +++ b/tests/python/physics/refdata/lgrngn_cond_substepping_refdata.csv @@ -0,0 +1,281 @@ +ss,th_diff,rv_diff,act,mr,sr,tr,exectime,act_post_evap,gccn_post_evap,th_post_cond,rv_post_cond,mixing,adaptive,constp,exact_sstp,RH_formula,sstp_cond,sstp_cond_act,sstp_cond_adapt_drw2_eps,sstp_cond_adapt_drw2_max,sd_conc,RH_max,dt +-0.854065527837522,0.006928489700214868,-4.81408036698272e-09,40795.684,2.8450377641897684,1.5404078731569696e-11,1.168321069365263e-16,0.04100339399519726,8122.147,8122.147,300.1155050089574,0.009055862985424531,False,True,False,True,0,1,1,0.001,2.0,1000,0.95,1 +-0.8540942285198416,0.008735536725680504,-3.841756266860855e-06,15066.523,4.965715208974107,3.7458790351007073e-11,3.1034254104384423e-16,0.04868583899587975,8122.147,8122.147,300.11550730290844,0.009055862104925635,False,True,False,True,0,1,8,0.001,2.0,1000,0.95,1 +-0.8541517390907472,0.008669645224699707,-3.703218914623274e-06,19236.17,4.263105600428615,2.9984425210959605e-11,2.4485998290853223e-16,0.08388675501191756,8122.147,8122.147,300.11551190621924,0.00905586034564531,False,True,False,True,0,2,1,0.001,2.0,1000,0.95,1 +-0.8541519816353871,0.008669647075237208,-3.7032199068538774e-06,19236.17,4.263086038565672,2.998436862421631e-11,2.4485986835481803e-16,0.08554636002372717,8122.147,8122.147,300.11551192563303,0.009055860338225575,False,True,False,True,0,2,8,0.001,2.0,1000,0.95,1 +-0.8541997010627078,0.008623405271066531,-3.6058245376606457e-06,15208.69,4.949445411328254,3.732452114985539e-11,3.0995930614533274e-16,0.09116806100792019,8122.147,8122.147,300.1155157470654,0.009055858879850573,False,True,False,True,0,3,1,0.001,2.0,1000,0.95,1 +-0.8541994951821952,0.008623403904778115,-3.605824280290132e-06,16091.858,4.742172193587966,3.5347875821051276e-11,2.930203192713486e-16,0.09315697301644832,8122.147,8122.147,300.11551573062246,0.00905585888617617,False,True,False,True,0,3,8,0.001,2.0,1000,0.95,1 +-0.8541978852116716,0.008592020776802656,-3.5400193779382833e-06,15352.28,4.912556756847279,3.698863735013777e-11,3.0720245855796556e-16,0.11154457100928994,8122.147,8122.147,300.1155156031578,0.009055858936490224,False,True,False,True,0,4,1,0.001,2.0,1000,0.95,1 +-0.8541974841572708,0.00859201837852197,-3.540018821714813e-06,16091.858,4.719541972479719,3.530504545913659e-11,2.9307874184620377e-16,0.10873610400085454,8122.147,8122.147,300.11551557105224,0.009055858948755657,False,True,False,True,0,4,8,0.001,2.0,1000,0.95,1 +-0.8542204609234383,0.008553731485903882,-3.459444026418859e-06,16091.858,4.7101409729022805,3.5335174471558694e-11,2.9378227880764975e-16,0.11561881498346338,8122.147,8122.147,300.1155174122116,0.009055858247424999,False,True,False,True,0,6,1,0.001,2.0,1000,0.95,1 +-0.8542212287082629,0.008553736591807137,-3.459446082638637e-06,15941.245,4.7406786070324065,3.5649893757299746e-11,2.965345706990643e-16,0.11710508197938907,8122.147,8122.147,300.115517473654,0.009055858223927874,False,True,False,True,0,6,8,0.001,2.0,1000,0.95,1 +-0.8542241434910669,0.008531479271141507,-3.4126874029326987e-06,15791.776,4.7691924636754335,3.5969712846975266e-11,2.9940594564187133e-16,0.1340020999814442,8122.147,8122.147,300.1155177079386,0.009055858135504299,False,True,False,True,0,8,1,0.001,2.0,1000,0.95,1 +-0.8542249241887623,0.008531484321565586,-3.4126893337094177e-06,15791.776,4.770005812299355,3.5971154695679413e-11,2.994077311230615e-16,0.13935085698540206,8122.147,8122.147,300.1155177704287,0.009055858111622883,False,True,False,True,0,8,8,0.001,2.0,1000,0.95,1 +-0.8542385647618111,0.008472736279259152,-3.2891317995383668e-06,15497.363,4.821281320286963,3.663571716708637e-11,3.05608417337699e-16,0.240850859980128,8122.147,8122.147,300.115518864864,0.00905585769632578,False,True,False,True,0,32,1,0.001,2.0,1000,0.95,1 +-0.8542382473777033,0.00847273484470179,-3.289131516816604e-06,15791.776,4.755183197412429,3.598220816433419e-11,2.9994397902966773e-16,0.271330344992748,8122.147,8122.147,300.11551883947953,0.009055857706049823,False,True,False,True,0,32,8,0.001,2.0,1000,0.95,1 +-0.8593812972994663,0.006894635549542727,-4.682724369722835e-09,40795.684,2.8563514578517646,1.5439403865963113e-11,1.1692361936205755e-16,0.042593360027240124,8122.147,8122.147,300.11494232915334,0.009056078030657869,False,True,False,True,1,1,1,0.001,2.0,1000,0.95,1 +-0.8594083685614429,0.008697576420900077,-3.833625151009046e-06,15497.363,4.869453553753891,3.648130252871394e-11,3.018539938519351e-16,0.05055574499783688,8122.147,8122.147,300.1149444614298,0.009056077211869304,False,True,False,True,1,1,8,0.001,2.0,1000,0.95,1 +-0.8594649903536311,0.008636517484148953,-3.7052069215021827e-06,19236.17,4.270538867053194,3.0007054725211306e-11,2.449321635864008e-16,0.09084904099654523,8122.147,8122.147,300.11494892836333,0.009056075504692661,False,True,False,True,1,2,1,0.001,2.0,1000,0.95,1 +-0.8594650204068022,0.008636518157743467,-3.705207361082846e-06,19236.17,4.270519521929804,3.000699835994194e-11,2.449320488200294e-16,0.09215536799456459,8122.147,8122.147,300.1149489307341,0.009056075503786409,False,True,False,True,1,2,8,0.001,2.0,1000,0.95,1 +-0.85951324567759,0.008592926910864662,-3.6133629075906643e-06,15352.28,4.919285346557226,3.700389492730014e-11,3.0713818653618097e-16,0.10416941699804738,8122.147,8122.147,300.1149527371171,0.009056074051176703,False,True,False,True,1,3,1,0.001,2.0,1000,0.95,1 +-0.8595130422990982,0.008592925827031195,-3.6133625419230986e-06,16243.832,4.711659595887908,3.504277346697174e-11,2.9035255152310104e-16,0.09523946101762704,8122.147,8122.147,300.1149527211083,0.009056074057335908,False,True,False,True,1,3,8,0.001,2.0,1000,0.95,1 +-0.8595113489751061,0.008563064926534025,-3.550749840633763e-06,15352.28,4.920680604710064,3.701048163139061e-11,3.072755058887798e-16,0.11009852400820819,8122.147,8122.147,300.11495258893336,0.009056074109462879,False,True,False,True,1,4,1,0.001,2.0,1000,0.95,1 +-0.8595117766093674,0.008563067664510982,-3.5507507259897875e-06,16091.858,4.728731550334803,3.53283558165294e-11,2.931512786108746e-16,0.10839191699778894,8122.147,8122.147,300.11495262266635,0.009056074096566913,False,True,False,True,1,4,8,0.001,2.0,1000,0.95,1 +-0.8595347679342447,0.008526394448040264,-3.4735647591044305e-06,16091.858,4.7195362757394745,3.535854884541157e-11,2.9385516044701606e-16,0.11592192703756155,8122.147,8122.147,300.11495443852294,0.009056073404924252,False,True,False,True,1,6,1,0.001,2.0,1000,0.95,1 +-0.8595339081022058,0.008526389515964183,-3.4735632702519864e-06,15941.245,4.750102531724496,3.567322327357403e-11,2.9660751352416195e-16,0.11921269400045276,8122.147,8122.147,300.11495437067686,0.00905607343083861,False,True,False,True,1,6,8,0.001,2.0,1000,0.95,1 +-0.859536363077873,0.008504950605754402,-3.428527036646728e-06,15791.776,4.778689836987993,3.599306075377012e-11,2.994798961905673e-16,0.13698792198556475,8122.147,8122.147,300.11495456533726,0.009056073357567787,False,True,False,True,1,8,1,0.001,2.0,1000,0.95,1 +-0.8595367190717074,0.008504952674400101,-3.42852767234135e-06,15791.776,4.779448436994434,3.5994445146753276e-11,2.994816589792006e-16,0.1398603820198332,8122.147,8122.147,300.11495459342285,0.009056073346835078,False,True,False,True,1,8,8,0.001,2.0,1000,0.95,1 +-0.8595517487091531,0.00844798301073979,-3.30869567291385e-06,15497.363,4.831175270943777,3.6659437137364815e-11,3.0568787227162623e-16,0.2562548689893447,8122.147,8122.147,300.1149557817529,0.009056072895679543,False,True,False,True,1,32,1,0.001,2.0,1000,0.95,1 +-0.8595518192155094,0.008447983995893082,-3.3086959592386334e-06,15791.776,4.7651043978593,3.600606374377877e-11,3.000230545006832e-16,0.23901726702752057,8122.147,8122.147,300.1149557873351,0.009056072893568816,False,True,False,True,1,32,8,0.001,2.0,1000,0.95,1 +-0.8919125740436185,0.006662583444494885,-4.6022673658163704e-09,40795.684,2.409809715492447,1.4199775825683754e-11,1.1307222646741993e-16,0.04371557202102849,8122.147,8122.147,300.1110616911817,0.009057561116618257,False,True,False,True,2,1,1,0.001,2.0,1000,0.95,1 +-0.8919225569715139,0.008400607861915432,-3.7006544895586135e-06,10268.547,6.610673851568431,5.351790295023763e-11,4.468668882539824e-16,0.05432991798079456,8122.147,8122.147,300.1110624875176,0.00905756081016668,False,True,False,True,2,1,8,0.001,2.0,1000,0.95,1 +-0.8919793147428523,0.008342619270706564,-3.578436440934349e-06,19236.17,3.813545555662916,2.881727170507402e-11,2.396642514939636e-16,0.0826483449927764,8122.147,8122.147,300.11106702517736,0.009057559075528252,False,True,False,True,2,2,1,0.001,2.0,1000,0.95,1 +-0.8919807906652033,0.008342627869581065,-3.5784401859334514e-06,19236.17,3.813519264815387,2.881722310734679e-11,2.3966419032587333e-16,0.0833111579377146,8122.147,8122.147,300.11106714318436,0.009057559030428645,False,True,False,True,2,2,8,0.001,2.0,1000,0.95,1 +-0.8920227244334833,0.0083013401927019,-3.4913262851044014e-06,9208.318,7.298416917690385,5.993830572604306e-11,5.02495746993963e-16,0.08955018099732115,8122.147,8122.147,300.1110704980141,0.009057557750604092,False,True,False,True,2,3,1,0.001,2.0,1000,0.95,1 +-0.8920220000816936,0.008301336246177016,-3.491324715002714e-06,9208.318,7.297708357692227,5.993732024525119e-11,5.024947127847212e-16,0.10270784300155356,8122.147,8122.147,300.1110704400993,0.009057557772738475,False,True,False,True,2,3,8,0.001,2.0,1000,0.95,1 +-0.8920198262817647,0.00827307347964279,-3.431968743109956e-06,8622.953,7.752963934040574,6.400215088741572e-11,5.368667306682894e-16,0.13422023996099597,8122.147,8122.147,300.1110702677383,0.009057557840262236,False,True,False,True,2,4,1,0.001,2.0,1000,0.95,1 +-0.8920208405877705,0.00827307967898605,-3.431971104236875e-06,8569.845,7.7970393857337985,6.439635140132371e-11,5.401938073345892e-16,0.10548655704042176,8122.147,8122.147,300.1110703488332,0.009057557809265395,False,True,False,True,2,4,8,0.001,2.0,1000,0.95,1 +-0.8920416050999691,0.008238362746567418,-3.358784628674713e-06,8391.151,7.95652733284563,6.587234058687033e-11,5.5311798099957805e-16,0.11470512600135407,8122.147,8122.147,300.1110720110935,0.009057557176317099,False,True,False,True,2,6,1,0.001,2.0,1000,0.95,1 +-0.8920418729451041,0.008238364223814187,-3.3587857370606655e-06,8380.352,7.96606045053125,6.595688602767168e-11,5.538314078570179e-16,0.1151404080192151,8122.147,8122.147,300.1110720325059,0.009057557168130233,False,True,False,True,2,6,8,0.001,2.0,1000,0.95,1 +-0.8920450468329233,0.008218065184280476,-3.316064527691953e-06,8341.374,8.001053624923298,6.627783692622875e-11,5.566026376826655e-16,0.13247900398346246,8122.147,8122.147,300.11107228726604,0.009057557071901286,False,True,False,True,2,8,1,0.001,2.0,1000,0.95,1 +-0.8920443335995398,0.00821806113231105,-3.3160631571060167e-06,8332.444,8.009081811181387,6.634858905933007e-11,5.571990159894519e-16,0.1450596669856168,8122.147,8122.147,300.11107223023964,0.009057557093695472,False,True,False,True,2,8,8,0.001,2.0,1000,0.95,1 +-0.8920581109955417,0.008164051536994066,-3.2022713115243545e-06,8265.412,8.074538245831045,6.69729455699625e-11,5.628249534581766e-16,0.2396942499835859,8122.147,8122.147,300.11107333446154,0.009057556674719445,False,True,False,True,2,32,1,0.001,2.0,1000,0.95,1 +-0.8920579000123663,0.008164050770233189,-3.2022712743093318e-06,8265.412,8.074448495453927,6.697275787451784e-11,5.628239048294569e-16,0.24229215101877344,8122.147,8122.147,300.11107331759604,0.009057556681169202,False,True,False,True,2,32,8,0.001,2.0,1000,0.95,1 +-0.9537034564724101,0.006270988086612306,-4.218641198525108e-09,8122.323,8.099027434188596,6.640913665655331e-11,5.512853670683755e-16,0.040652274030435365,8122.147,8122.147,300.10452207944803,0.009060060367762472,False,True,False,True,3,1,1,0.001,2.0,1000,0.95,1 +-0.9537029769909999,0.007910485970171521,-3.49847225844524e-06,8122.323,8.099034863756366,6.640925547712199e-11,5.512868101289963e-16,0.04953606297567603,8122.147,8122.147,300.1045220416298,0.009060060382204143,False,True,False,True,3,1,8,0.001,2.0,1000,0.95,1 +-0.9537562271965339,0.00786571277717485,-3.4037630130042545e-06,8122.243,8.1120205135549,6.662340935351154e-11,5.539724589092764e-16,0.08300491100817453,8122.147,8122.147,300.10452623981075,0.009060058776945788,False,True,False,True,3,2,1,0.001,2.0,1000,0.95,1 +-0.9537580402383172,0.007865723034910843,-3.4037674144036384e-06,8122.243,8.112020697489065,6.662341230368184e-11,5.5397249484506905e-16,0.08349446798820281,8122.147,8122.147,300.10452638277354,0.009060058722309406,False,True,False,True,3,2,8,0.001,2.0,1000,0.95,1 +-0.9537937435983745,0.007832720290025463,-3.3339314994915853e-06,8122.229,8.123022066591524,6.68051469124673e-11,5.562523337226788e-16,0.08879035396967083,8122.147,8122.147,300.1045292003296,0.009060057648100434,False,True,False,True,3,3,1,0.001,2.0,1000,0.95,1 +-0.9537948844234534,0.007832727007951235,-3.3339344672981414e-06,8122.229,8.12302210712023,6.680514756188179e-11,5.562523416246969e-16,0.09710145503049716,8122.147,8122.147,300.10452929028656,0.009060057613721528,False,True,False,True,3,3,8,0.001,2.0,1000,0.95,1 +-0.9537933001049437,0.007809689251587315,-3.2854259357541216e-06,8122.217,8.124415713332569,6.682822141276429e-11,5.565428040351333e-16,0.11337913798342925,8122.147,8122.147,300.10452916678145,0.009060057662547952,False,True,False,True,3,4,1,0.001,2.0,1000,0.95,1 +-0.9537940789800747,0.007809693860394873,-3.2854279444320678e-06,8122.217,8.124415724333375,6.682822158954451e-11,5.565428061958281e-16,0.11045583301529405,8122.147,8122.147,300.10452922819775,0.009060057639076448,False,True,False,True,3,4,8,0.001,2.0,1000,0.95,1 +-0.9538124769709833,0.007780956616215917,-3.224694071511658e-06,8122.211,8.13142787573724,6.694363066355273e-11,5.579850936969387e-16,0.11245527401842992,8122.147,8122.147,300.1045306809455,0.009060057086187168,False,True,False,True,3,6,1,0.001,2.0,1000,0.95,1 +-0.953813953398952,0.007780965333779477,-3.2246977085467754e-06,8122.211,8.13142786848757,6.694363054661832e-11,5.579850922643466e-16,0.11251360003006994,8122.147,8122.147,300.1045307973658,0.009060057041694904,False,True,False,True,3,6,8,0.001,2.0,1000,0.95,1 +-0.9538149783658301,0.007763940428048954,-3.1887999814156354e-06,8122.211,8.13234150334549,6.695847604952394e-11,5.581681248296636e-16,0.12879453300047317,8122.147,8122.147,300.10453087917637,0.009060057011560404,False,True,False,True,3,8,1,0.001,2.0,1000,0.95,1 +-0.9538154041735081,0.007763943352301794,-3.1888011786766757e-06,8122.211,8.132341505594736,6.695847608563995e-11,5.581681252677634e-16,0.14220855198800564,8122.147,8122.147,300.10453091275247,0.009060056998728692,False,True,False,True,3,8,8,0.001,2.0,1000,0.95,1 +-0.9538262296209798,0.007717952276152573,-3.0917011094363372e-06,8122.201,8.137875327619982,6.70484266035735e-11,5.592775909949195e-16,0.2422090030340769,8122.147,8122.147,300.10453176901336,0.009060056674516714,False,True,False,True,3,32,1,0.001,2.0,1000,0.95,1 +-0.9538272340177789,0.007717957565660072,-3.091702671362273e-06,8122.201,8.13787540068592,6.704842777799318e-11,5.59277605328574e-16,0.22747121700740536,8122.147,8122.147,300.1045318482124,0.009060056644248832,False,True,False,True,3,32,8,0.001,2.0,1000,0.95,1 +-0.7410217546677589,0.0003405582369282456,1.4212895386479796e-07,40795.684,3.087865943338912,1.6421866024522423e-11,1.2208823783467527e-16,0.04031327103075455,8122.147,8122.147,298.8844137397728,0.009051087041052992,False,True,True,True,0,1,1,0.001,2.0,1000,0.95,1 +-0.7411065705848374,0.006146188316847656,-2.890821922070519e-06,16554.822,4.884650027001804,3.548705475020148e-11,2.922712956024227e-16,0.05104148701502709,8122.147,8122.147,298.8844218559164,0.009051083925135327,False,True,True,True,0,1,8,0.001,2.0,1000,0.95,1 +-0.7412090532469606,0.005109442950356424,-2.3494715621543566e-06,19236.17,4.47042377861932,3.1114354707432864e-11,2.538118271798519e-16,0.09222832099840161,8122.147,8122.147,298.88443166487525,0.009051080161497627,False,True,True,True,0,2,1,0.001,2.0,1000,0.95,1 +-0.7412085495409393,0.005109443259357249,-2.3494716666558335e-06,19236.17,4.470408615648916,3.1114302592101154e-11,2.538116995044781e-16,0.08652871901358594,8122.147,8122.147,298.88443161666424,0.009051080179996297,False,True,True,True,0,2,8,0.001,2.0,1000,0.95,1 +-0.7412852780333878,0.004693096724452062,-2.1320131933302844e-06,16243.832,4.899965144040243,3.6166681718920655e-11,3.004708939775314e-16,0.09077166600400233,8122.147,8122.147,298.8844389609012,0.009051077362337803,False,True,True,True,0,3,1,0.001,2.0,1000,0.95,1 +-0.7412852287509208,0.0046930972622476474,-2.1320134295649273e-06,16554.822,4.850266314227991,3.556308946995757e-11,2.949450614914942e-16,0.0916612079818151,8122.147,8122.147,298.88443895620577,0.009051077364160257,False,True,True,True,0,3,8,0.001,2.0,1000,0.95,1 +-0.7412834324604933,0.0044720412992660385,-2.016557635218033e-06,16243.832,4.8974148016025785,3.6166537656472675e-11,3.005937320715642e-16,0.10654829497434548,8122.147,8122.147,298.8844387845613,0.009051077430294372,False,True,True,True,0,4,1,0.001,2.0,1000,0.95,1 +-0.7412824460067857,0.0044720394637920435,-2.016556886185253e-06,16554.822,4.830770831694685,3.5518534238557083e-11,2.94978997853074e-16,0.11317926801348221,8122.147,8122.147,298.88443869014714,0.009051077466523135,False,True,True,True,0,4,8,0.001,2.0,1000,0.95,1 +-0.7413111189403465,0.004242807181128683,-1.8968138126487094e-06,16243.832,4.9015381784070255,3.6211949792197494e-11,3.012317242127516e-16,0.13143545895218267,8122.147,8122.147,298.88444143498924,0.009051076413784912,False,True,True,True,0,6,1,0.001,2.0,1000,0.95,1 +-0.7413112263900401,0.004242806464105797,-1.896813535413877e-06,16398.686,4.86001743197353,3.586655492161322e-11,2.983705642533962e-16,0.11953251900922623,8122.147,8122.147,298.884441445269,0.009051076409836146,False,True,True,True,0,6,8,0.001,2.0,1000,0.95,1 +-0.7413114543781574,0.004125187171837297,-1.8353768348033234e-06,16398.686,4.853523640762945,3.5851301923820943e-11,2.9836735866135924e-16,0.1374830940112588,8122.147,8122.147,298.88444146728415,0.009051076401576232,False,True,True,True,0,8,1,0.001,2.0,1000,0.95,1 +-0.7413108536067248,0.004125186558724181,-1.835376577385972e-06,16398.686,4.853523050948532,3.5851299936367514e-11,2.983673538797665e-16,0.1377466719786753,8122.147,8122.147,298.88444140978254,0.009051076423639547,False,True,True,True,0,8,8,0.001,2.0,1000,0.95,1 +-0.7413177488238443,0.003882863844012263,-1.7087949095769689e-06,16243.832,4.873009126658442,3.6150520179365374e-11,3.013104939009668e-16,0.24080044797665323,8122.147,8122.147,298.8844420702028,0.009051076170680137,False,True,True,True,0,32,1,0.001,2.0,1000,0.95,1 +-0.7413184724632926,0.003882864288868859,-1.7087950600781082e-06,16398.686,4.84188830066469,3.5836167669903205e-11,2.985114274468285e-16,0.24006885100607178,8122.147,8122.147,298.88444213947434,0.009051076144110238,False,True,True,True,0,32,8,0.001,2.0,1000,0.95,1 +-0.7458751435198807,0.00034197774033373207,1.4042214812985676e-07,40795.684,3.096804271096643,1.6454675783917072e-11,1.2218304386377538e-16,0.039792741041310364,8122.147,8122.147,298.8838532436859,0.009051302103648547,False,True,True,True,1,1,1,0.001,2.0,1000,0.95,1 +-0.7459612468395216,0.006193665571686324,-2.9166857972648397e-06,16554.822,4.892864671335222,3.551506121876084e-11,2.923684595201495e-16,0.04667067897753441,8122.147,8122.147,298.88386136461577,0.009051298985865385,False,True,True,True,1,1,8,0.001,2.0,1000,0.95,1 +-0.746060855771491,0.005160286398961489,-2.3770846210875024e-06,19236.17,4.478396117162974,3.114329860294227e-11,2.539102513514529e-16,0.08287641999777406,8122.147,8122.147,298.88387076155516,0.009051295380329323,False,True,True,True,1,2,1,0.001,2.0,1000,0.95,1 +-0.7460615028086259,0.005160286376451495,-2.377084669250365e-06,19236.17,4.4783812157232035,3.114324701809132e-11,2.5391012423675153e-16,0.08337412999026128,8122.147,8122.147,298.88387082259504,0.00905129535690823,False,True,True,True,1,2,8,0.001,2.0,1000,0.95,1 +-0.7461371583584953,0.00474262323888297,-2.1589348078287357e-06,16243.832,4.909365195411371,3.619618262153584e-11,3.005722545313203e-16,0.09046655899146572,8122.147,8122.147,298.88387796010267,0.009051292618574422,False,True,True,True,1,3,1,0.001,2.0,1000,0.95,1 +-0.7461371827337637,0.0047426242594497126,-2.1589352680786295e-06,16554.822,4.858928508322602,3.559191780033133e-11,2.9504604593769056e-16,0.09162956899308483,8122.147,8122.147,298.88387796242426,0.009051292617704959,False,True,True,True,1,3,8,0.001,2.0,1000,0.95,1 +-0.7461350048744153,0.004520407638437973,-2.0428704609401493e-06,16243.832,4.906513409232752,3.6195821122938e-11,3.006973350601178e-16,0.10584920704059186,8122.147,8122.147,298.88387775725664,0.009051292696704844,False,True,True,True,1,4,1,0.001,2.0,1000,0.95,1 +-0.7461342958076878,0.004520405996288446,-2.042869838583816e-06,16554.822,4.841787782531172,3.5551124022132095e-11,2.9508606370496595e-16,0.10749932496764814,8122.147,8122.147,298.88387769036774,0.009051292722372867,False,True,True,True,1,4,8,0.001,2.0,1000,0.95,1 +-0.7461626064498406,0.004289679543660441,-1.92234358754699e-06,16243.832,4.91119931093228,3.62434335320047e-11,3.0134295395479824e-16,0.11749049898207886,8122.147,8122.147,298.8838803616078,0.009051291697880917,False,True,True,True,1,6,1,0.001,2.0,1000,0.95,1 +-0.7461627980805963,0.0042896800327412166,-1.922343808972299e-06,16554.822,4.828902578861074,3.556095728337045e-11,2.956640623877003e-16,0.11813567296485417,8122.147,8122.147,298.8838803796805,0.00905129169094124,False,True,True,True,1,6,8,0.001,2.0,1000,0.95,1 +-0.7461620704461436,0.004171195997173527,-1.8604534476971551e-06,16398.686,4.8626003715667085,3.5880907507493824e-11,2.984763672499699e-16,0.1360002220098977,8122.147,8122.147,298.8838803112332,0.009051291717394026,False,True,True,True,1,8,1,0.001,2.0,1000,0.95,1 +-0.7461632919382732,0.004171199070469811,-1.860454670286893e-06,16398.686,4.862599614811463,3.5880905108190935e-11,2.9847636188541753e-16,0.1372597720255726,8122.147,8122.147,298.8838804264659,0.009051291673179192,False,True,True,True,1,8,8,0.001,2.0,1000,0.95,1 +-0.746168561586158,0.003925165903922334,-1.7319316662749856e-06,16398.686,4.8427476484427645,3.5844612471884124e-11,2.985843154985787e-16,0.24273383601757814,8122.147,8122.147,298.88388092405734,0.009051291482702648,False,True,True,True,1,32,1,0.001,2.0,1000,0.95,1 +-0.746170167006166,0.003925169137346529,-1.731932900140426e-06,16398.686,4.853151975775282,3.586920361836847e-11,2.9862848860647865e-16,0.2395426100010809,8122.147,8122.147,298.88388107551845,0.009051291424596168,False,True,True,True,1,32,8,0.001,2.0,1000,0.95,1 +-0.7784580996355261,0.00031068495064801027,1.4243433543725814e-07,40795.684,2.7623001784708734,1.526348882049979e-11,1.178276035895305e-16,0.03992280601232778,8122.147,8122.147,298.87909358474076,0.00905312837809255,False,True,True,True,2,1,1,0.001,2.0,1000,0.95,1 +-0.7784833194332319,0.005965894622875112,-2.8128834248866696e-06,14385.414,5.188177307383091,3.953203987967907e-11,3.2920939666917785e-16,0.04804832503577927,8122.147,8122.147,298.87909599552523,0.009053127452115919,False,True,True,True,2,1,8,0.001,2.0,1000,0.95,1 +-0.7785862913984087,0.004980898189671734,-2.2983908095266375e-06,19236.17,4.1805783918543185,3.001828647815735e-11,2.481063414949966e-16,0.08347915401100181,8122.147,8122.147,298.879105842701,0.009053123673695183,False,True,True,True,2,2,1,0.001,2.0,1000,0.95,1 +-0.7785854388597024,0.004980894719267326,-2.298389403954798e-06,19236.17,4.180561529233623,3.00182419587119e-11,2.4810625845469203e-16,0.08431879802810727,8122.147,8122.147,298.8791057611723,0.00905312370497761,False,True,True,True,2,2,8,0.001,2.0,1000,0.95,1 +-0.7786493456590327,0.004580146324769885,-2.0890146408530236e-06,12285.962,5.802795708427433,4.6182993606818726e-11,3.8904165647935757e-16,0.09017067703098292,8122.147,8122.147,298.87911187303456,0.009053121360275324,False,True,True,True,2,3,1,0.001,2.0,1000,0.95,1 +-0.7786484357784396,0.004580142619090566,-2.0890131323756528e-06,12405.92,5.751855277812674,4.5738784339751665e-11,3.8527946149889323e-16,0.09041249902657,8122.147,8122.147,298.87911178602366,0.009053121393662685,False,True,True,True,2,3,8,0.001,2.0,1000,0.95,1 +-0.7786457891135412,0.004366377867370375,-1.977331160684173e-06,12388.668,5.778238380358208,4.5840596392924344e-11,3.8602816650962242e-16,0.10469334498338867,8122.147,8122.147,298.8791115332302,0.009053121490957735,False,True,True,True,2,4,1,0.001,2.0,1000,0.95,1 +-0.7786454040980662,0.004366374824428476,-1.9773299494412616e-06,13149.629,5.473201232992257,4.319588912782083e-11,3.6367934780947777e-16,0.10642243898109882,8122.147,8122.147,298.8791114964124,0.009053121505086071,False,True,True,True,2,4,8,0.001,2.0,1000,0.95,1 +-0.778671397793318,0.004144062270654558,-1.8611657585827779e-06,13036.078,5.500384295759952,4.360054819273577e-11,3.676240021679942e-16,0.12152104998676805,8122.147,8122.147,298.8791139826779,0.00905312055156929,False,True,True,True,2,6,1,0.001,2.0,1000,0.95,1 +-0.778670631130074,0.004144059203724737,-1.861164578852853e-06,12813.125,5.581789827395866,4.4346303619263556e-11,3.740093713750825e-16,0.11845125902982545,8122.147,8122.147,298.8791139093594,0.00905312057969946,False,True,True,True,2,6,8,0.001,2.0,1000,0.95,1 +-0.7786703169484599,0.004029759943648514,-1.8014433015179493e-06,12813.125,5.572294582332121,4.433409091732123e-11,3.740300966026282e-16,0.1357301660173107,8122.147,8122.147,298.8791138795135,0.009053120591344386,False,True,True,True,2,8,1,0.001,2.0,1000,0.95,1 +-0.77867051299636,0.004029760789592274,-1.8014436029261532e-06,12813.125,5.57228823871305,4.433407674026284e-11,3.7403007421057695e-16,0.1355631379992701,8122.147,8122.147,298.8791138982616,0.009053120584150691,False,True,True,True,2,8,8,0.001,2.0,1000,0.95,1 +-0.7786762510176848,0.0037914998362680308,-1.6769444970924502e-06,12285.962,5.770914266033675,4.621577532992967e-11,3.9030268365329947e-16,0.2388946300598036,8122.147,8122.147,298.879114447471,0.009053120373883877,False,True,True,True,2,32,1,0.001,2.0,1000,0.95,1 +-0.7786757824013169,0.0037914975433182008,-1.6769436444376978e-06,12597.638,5.647597665081201,4.508895059760202e-11,3.806601740638572e-16,0.2534399729920551,8122.147,8122.147,298.87911440265935,0.009053120391080362,False,True,True,True,2,32,8,0.001,2.0,1000,0.95,1 +-0.8394806653812537,0.0002711266791948219,1.4133053139082918e-07,8127.038,8.173483436288809,6.766667621683392e-11,5.671542091495478e-16,0.04365204797795741,8122.147,8122.147,298.8713413413251,0.009056102887603463,False,True,True,True,3,1,1,0.001,2.0,1000,0.95,1 +-0.8394802602433749,0.005742332238753534,-2.7191462830828544e-06,8131.002,8.169733450248453,6.763352689087883e-11,5.668742017975856e-16,0.04818620302830823,8122.147,8122.147,298.8713413031185,0.009056102902268106,False,True,True,True,3,1,8,0.001,2.0,1000,0.95,1 +-0.839573712089503,0.004835720864150517,-2.245385100428654e-06,8123.823,8.194750212414995,6.799752479816428e-11,5.712232147595953e-16,0.08378924896896933,8122.147,8122.147,298.87135011707323,0.009056099520186435,False,True,True,True,3,2,1,0.001,2.0,1000,0.95,1 +-0.83957321683924,0.004835720339542604,-2.2453848086863293e-06,8123.823,8.194750445914286,6.799753152496719e-11,5.71223299956941e-16,0.08485502399344114,8122.147,8122.147,298.8713500703622,0.00905609953810914,False,True,True,True,3,2,8,0.001,2.0,1000,0.95,1 +-0.8396267240421751,0.004457344556954013,-2.0476137955350443e-06,8123.298,8.207320834946108,6.820210100319895e-11,5.737787032399928e-16,0.08893255700240843,8122.147,8122.147,298.8713551176238,0.009056097602058248,False,True,True,True,3,3,1,0.001,2.0,1000,0.95,1 +-0.8396262621577222,0.004457342902526307,-2.047613094843803e-06,8123.374,8.207249005536053,6.820146694984608e-11,5.737733548382205e-16,0.08905042102560401,8122.147,8122.147,298.8713550740598,0.009056097618773495,False,True,True,True,3,3,8,0.001,2.0,1000,0.95,1 +-0.8396235168357413,0.004253638323575615,-1.9411394786318648e-06,8123.228,8.20885755055972,6.8226634335782e-11,5.740789534414395e-16,0.1065902699738217,8122.147,8122.147,298.87135481543214,0.009056097718303272,False,True,True,True,3,4,1,0.001,2.0,1000,0.95,1 +-0.8396223462404873,0.004253635032910097,-1.941138153290986e-06,8123.163,8.208919222156283,6.822717859193668e-11,5.740835505076792e-16,0.10672056801558938,8122.147,8122.147,298.87135470502403,0.00905609776066627,False,True,True,True,3,4,8,0.001,2.0,1000,0.95,1 +-0.8396450340053097,0.0040405344070109095,-1.8297397237996421e-06,8123.042,8.215318091924596,6.833199173395024e-11,5.753931750302727e-16,0.11535176099278033,8122.147,8122.147,298.8713568453576,0.009056096939887416,False,True,True,True,3,6,1,0.001,2.0,1000,0.95,1 +-0.8396449654833438,0.004040533928275636,-1.829739528945093e-06,8123.042,8.215318133309989,6.833199162274984e-11,5.753931730260627e-16,0.11556687398115173,8122.147,8122.147,298.87135683889477,0.009056096942367193,False,True,True,True,3,6,8,0.001,2.0,1000,0.95,1 +-0.8396445962419063,0.003930525892712922,-1.7722347494149304e-06,8122.987,8.215778225930334,6.833861524795773e-11,5.754662110237841e-16,0.13347180401615333,8122.147,8122.147,298.8713568042706,0.009056096955847795,False,True,True,True,3,8,1,0.001,2.0,1000,0.95,1 +-0.8396447739581703,0.003930525924374706,-1.7722347970660496e-06,8122.987,8.2157782048376,6.833861525535886e-11,5.754662113846923e-16,0.13329951100604376,8122.147,8122.147,298.8713568210324,0.009056096949416384,False,True,True,True,3,8,8,0.001,2.0,1000,0.95,1 +-0.8396488151197978,0.0036952498948039647,-1.649240806797514e-06,8122.987,8.217836289753034,6.837173796264377e-11,5.758700775731885e-16,0.23584762704194873,8122.147,8122.147,298.8713572026684,0.009056096803451364,False,True,True,True,3,32,1,0.001,2.0,1000,0.95,1 +-0.8396490258712141,0.0036952520909494524,-1.649241660997905e-06,8122.935,8.217885360980103,6.837217524822147e-11,5.75873783566299e-16,0.23566658097843174,8122.147,8122.147,298.87135722254595,0.009056096795824364,False,True,True,True,3,32,8,0.001,2.0,1000,0.95,1 +-0.854065527837522,0.006928489700214868,-4.81408036698272e-09,40795.684,2.8450377641897684,1.5404078731569696e-11,1.168321069365263e-16,0.04127290699034347,8122.147,8122.147,300.1155050089574,0.009055862985424531,False,False,False,True,0,1,1,0.001,2.0,1000,0.95,1 +-0.8543932235894003,0.008670891165650119,-3.7033898055531533e-06,19236.17,4.263167322437524,2.998459711233623e-11,2.448603561254164e-16,0.06858780703259981,8122.147,8122.147,300.11553123564784,0.009055852958710352,False,False,False,True,0,2,1,0.001,2.0,1000,0.95,1 +-0.8544593738624928,0.008624611527466186,-3.605726882530136e-06,15941.245,4.76141262884219,3.56420029762473e-11,2.957400830075612e-16,0.09445254200909403,8122.147,8122.147,300.1155365324762,0.009055850936615319,False,False,False,True,0,3,1,0.001,2.0,1000,0.95,1 +-0.8544252988424983,0.00859291826094477,-3.5396015433692557e-06,15791.776,4.796777484596231,3.6021837047697906e-11,2.99315860614542e-16,0.10848541500672582,8122.147,8122.147,300.11553380681454,0.009055851980365,False,False,False,True,0,4,1,0.001,2.0,1000,0.95,1 +-0.8541923610704383,0.008553043911774694,-3.458276925648665e-06,16243.832,4.676128738118671,3.5074962264309845e-11,2.9188719099662425e-16,0.14278580101017724,8122.147,8122.147,300.1155151640225,0.009055859107778806,False,False,False,True,0,6,1,0.001,2.0,1000,0.95,1 +-0.8538002550605661,0.008528471880254074,-3.4106493519408393e-06,16243.832,4.660039798917527,3.5080324594552625e-11,2.923756765911583e-16,0.18462127899329062,8122.147,8122.147,300.11548378011514,0.009055871103524255,False,False,False,True,0,8,1,0.001,2.0,1000,0.95,1 +-0.8474037920206734,0.008435092642514519,-3.279132518768657e-06,15791.776,4.742244135930334,3.6108841087433774e-11,3.02058533963632e-16,0.6283180640384671,8122.147,8122.147,300.1149718106653,0.009056066771933699,False,False,False,True,0,32,1,0.001,2.0,1000,0.95,1 +-0.8593812972994663,0.006894635549542727,-4.682724369722835e-09,40795.684,2.8563514578517646,1.5439403865963113e-11,1.1692361936205755e-16,0.04617450297882897,8122.147,8122.147,300.11494232915334,0.009056078030657869,False,False,False,True,1,1,1,0.001,2.0,1000,0.95,1 +-0.8597008731084599,0.008637721639047413,-3.70537909351841e-06,19236.17,4.270657229960692,3.000738242554173e-11,2.4493285954597677e-16,0.07687813102165819,8122.147,8122.147,300.11496753770746,0.009056068392967475,False,False,False,True,1,2,1,0.001,2.0,1000,0.95,1 +-0.8597651442854626,0.00859408609574075,-3.613282280767738e-06,15941.245,4.770947305066299,3.566572960677667e-11,2.958132974999622e-16,0.08745478397395345,8122.147,8122.147,300.11497261010436,0.00905606645665824,False,False,False,True,1,3,1,0.001,2.0,1000,0.95,1 +-0.8597308888827371,0.008563928541263977,-3.5503725818800125e-06,15943.75,4.765408918186384,3.5704157958493686e-11,2.965359703481053e-16,0.10228205499515752,8122.147,8122.147,300.1149699094737,0.009056067490853813,False,False,False,True,1,4,1,0.001,2.0,1000,0.95,1 +-0.8594857428303126,0.008525628308916566,-3.4724387766287695e-06,16243.832,4.68580603925844,3.5098597077840873e-11,2.919594080387772e-16,0.13867293798466562,8122.147,8122.147,300.11495057188637,0.00905607488381046,False,False,False,True,1,6,1,0.001,2.0,1000,0.95,1 +-0.8590790612122756,0.008501845305943334,-3.4265531594513537e-06,16243.832,4.67052090754501,3.51046297840153e-11,2.9244835060369016e-16,0.1759267880115658,8122.147,8122.147,300.11491848976345,0.009056087146392252,False,False,False,True,1,8,1,0.001,2.0,1000,0.95,1 +-0.8527317166789894,0.008411021518668349,-3.298993084996718e-06,15941.245,4.7139923755012845,3.579744059332671e-11,2.9930237195779556e-16,0.5976823810196947,8122.147,8122.147,300.11441776271215,0.009056278517921415,False,False,False,True,1,32,1,0.001,2.0,1000,0.95,1 +-0.8919125740436185,0.006662583444494885,-4.6022673658163704e-09,40795.684,2.409809715492447,1.4199775825683754e-11,1.1307222646741993e-16,0.039783364030881785,8122.147,8122.147,300.1110616911817,0.009057561116618257,False,False,False,True,2,1,1,0.001,2.0,1000,0.95,1 +-0.8922076309320626,0.008343802661556765,-3.5786126892714543e-06,19236.17,3.81369662534737,2.8817466464444735e-11,2.39664429639056e-16,0.062080551993858535,8122.147,8122.147,300.11108528050977,0.009057552099156046,False,False,False,True,2,2,1,0.001,2.0,1000,0.95,1 +-0.8922717637431021,0.008302517144329613,-3.4912761445252127e-06,9208.318,7.298279712336855,5.993754937667316e-11,5.024877732328519e-16,0.08225381400916376,8122.147,8122.147,300.1110904104085,0.009057550141098599,False,False,False,True,2,3,1,0.001,2.0,1000,0.95,1 +-0.8922361577648141,0.008273954562355357,-3.4316273379818296e-06,8622.953,7.758512135406238,6.409681263039465e-11,5.38062655524534e-16,0.09935766901253373,8122.147,8122.147,300.11108756530865,0.009057551230459203,False,False,False,True,2,4,1,0.001,2.0,1000,0.95,1 +-0.8919894745009427,0.008237597800587082,-3.3576979675750684e-06,8380.352,7.973846707832085,6.608668240225622e-11,5.554701013605092e-16,0.13601231000575353,8122.147,8122.147,300.1110678439525,0.009057558769988422,False,False,False,True,2,6,1,0.001,2.0,1000,0.95,1 +-0.8916046467801197,0.008215040979507648,-3.31414992134349e-06,8315.624,8.036044432121951,6.668124490177068e-11,5.608409071694912e-16,0.17149258797144284,8122.147,8122.147,300.11103707643514,0.009057570530040651,False,False,False,True,2,8,1,0.001,2.0,1000,0.95,1 +-0.8856229659669812,0.008128782590006267,-3.193143262190598e-06,8232.021,8.12426351844934,6.756884783417572e-11,5.692340506674292e-16,0.5852318969809858,8122.147,8122.147,300.1105588307103,0.009057753307845936,False,False,False,True,2,32,1,0.001,2.0,1000,0.95,1 +-0.9537034564724101,0.006270988086612306,-4.218641198525108e-09,8122.323,8.099027434188596,6.640913665655331e-11,5.512853670683755e-16,0.042959646008966956,8122.147,8122.147,300.10452207944803,0.009060060367762472,False,False,False,True,3,1,1,0.001,2.0,1000,0.95,1 +-0.9539678671590335,0.007866813598013778,-3.403963088869852e-06,8122.243,8.112020512807623,6.662340934698895e-11,5.539724588276711e-16,0.06712400499236537,8122.147,8122.147,300.1045429284933,0.00906005239938414,False,False,False,True,3,2,1,0.001,2.0,1000,0.95,1 +-0.9540064616464061,0.007833709154795088,-3.333886566134203e-06,8122.229,8.12302206565063,6.680514690610985e-11,5.562523336693523e-16,0.0841784220210684,8122.147,8122.147,300.1045459741288,0.009060051238124976,False,False,False,True,3,3,1,0.001,2.0,1000,0.95,1 +-0.9539699748145503,0.007810373827908279,-3.285096593899986e-06,8122.217,8.130503862226915,6.692855203790697e-11,5.5779821040293165e-16,0.10641499098346685,8122.147,8122.147,300.10454309879617,0.009060052338997024,False,False,False,True,3,4,1,0.001,2.0,1000,0.95,1 +-0.9537142477370919,0.007779973023559705,-3.223595175200905e-06,8122.211,8.139510357832044,6.707696368379806e-11,5.596551609170176e-16,0.15077214802659,8122.147,8122.147,300.10452293619994,0.009060060047007143,False,False,False,True,3,6,1,0.001,2.0,1000,0.95,1 +-0.9533221129951253,0.007760680378908091,-3.186843210396112e-06,8122.206,8.144613557411148,6.716097581510993e-11,5.607054734972859e-16,0.17209568800171837,8122.147,8122.147,300.1044920166726,0.009060071864941469,False,False,False,True,3,8,1,0.001,2.0,1000,0.95,1 +-0.9480123625978942,0.00768652397812275,-3.083542998483929e-06,8122.201,8.157603110676334,6.737479099232428e-11,5.633778902870057e-16,0.5730330719998165,8122.147,8122.147,300.10407334444324,0.009060231872446776,False,False,False,True,3,32,1,0.001,2.0,1000,0.95,1 +-0.7410217546677589,0.0003405582369282456,1.4212895386479796e-07,40795.684,3.087865943338912,1.6421866024522423e-11,1.2208823783467527e-16,0.04205984102009097,8122.147,8122.147,298.8844137397728,0.009051087041052992,False,False,True,True,0,1,1,0.001,2.0,1000,0.95,1 +-0.7415489054838798,0.005109782783108585,-2.3495483684899537e-06,19236.17,4.470212113176858,3.1113652338858374e-11,2.538100937545341e-16,0.06667922602355247,8122.147,8122.147,298.88446419327613,0.009051067680491574,False,False,True,True,0,2,1,0.001,2.0,1000,0.95,1 +-0.741685676744408,0.0046934138654251,-2.1320608708207123e-06,16243.832,4.910725079474244,3.618987097079345e-11,3.005072806065959e-16,0.08157168099205592,8122.147,8122.147,298.88447728447767,0.00905106265777961,False,False,True,True,0,3,1,0.001,2.0,1000,0.95,1 +-0.7416992698215941,0.004472267630944771,-2.016553616687733e-06,16398.686,4.872145318025956,3.590176309404751e-11,2.9841398080227364e-16,0.09933710298355436,8122.147,8122.147,298.8844785859727,0.00905106215883878,False,False,True,True,0,4,1,0.001,2.0,1000,0.95,1 +-0.7415274683219675,0.004242506871662499,-1.8965938376590968e-06,16554.822,4.833834011106676,3.561747042408534e-11,2.964131652067365e-16,0.13606735700159334,8122.147,8122.147,298.88446214286506,0.009051068468592846,False,False,True,True,0,6,1,0.001,2.0,1000,0.95,1 +-0.7412061551736904,0.004124176688662828,-1.8348776827797897e-06,16554.822,4.826021206214765,3.5625501114798276e-11,2.9682273925586264e-16,0.17170706300385064,8122.147,8122.147,298.88443138936196,0.009051080269019,False,False,True,True,0,8,1,0.001,2.0,1000,0.95,1 +-0.7343317459500787,0.0038408051482861083,-1.688846069365299e-06,16554.822,4.806568099339362,3.5649168605976567e-11,2.9784437858907615e-16,0.5908433520089602,8122.147,8122.147,298.88377344159244,0.009051332724558735,False,False,True,True,0,32,1,0.001,2.0,1000,0.95,1 +-0.7458751435198807,0.00034197774033373207,1.4042214812985676e-07,40795.684,3.096804271096643,1.6454675783917072e-11,1.2218304386377538e-16,0.04280960300457082,8122.147,8122.147,298.8838532436859,0.009051302103648547,False,False,True,True,1,1,1,0.001,2.0,1000,0.95,1 +-0.7463954619439495,0.0051606001119353095,-2.3771510424264175e-06,19236.17,4.478184447825517,3.114259052933184e-11,2.539084898818042e-16,0.06278649401065195,8122.147,8122.147,298.8839023277106,0.00905128326854233,False,False,True,True,1,2,1,0.001,2.0,1000,0.95,1 +-0.7465299123190605,0.004742907315460343,-2.158969251266421e-06,16243.832,4.919370648816297,3.621864252110552e-11,3.006086037924953e-16,0.08252018998246058,8122.147,8122.147,298.8839150118861,0.009051278402010114,False,False,True,True,1,3,1,0.001,2.0,1000,0.95,1 +-0.7465310062067632,0.0045205743234078,-2.042843000843453e-06,16398.686,4.881433457582969,3.5931723780694566e-11,2.9851874845224267e-16,0.09897951499078772,8122.147,8122.147,298.88391511554397,0.00905127836268381,False,False,True,True,1,4,1,0.001,2.0,1000,0.95,1 +-0.7463764535617812,0.0042893612063039654,-1.9221155991844263e-06,16554.822,4.84294893359376,3.564729456231739e-11,2.965202177870228e-16,0.13568622097955085,8122.147,8122.147,298.8839005359134,0.009051283957421712,False,False,True,True,1,6,1,0.001,2.0,1000,0.95,1 +-0.7460210245413545,0.0041700986076307345,-1.8599192891845945e-06,16712.481,4.7978437891600665,3.532622602652535e-11,2.9413596869027654e-16,0.1780289719972643,8122.147,8122.147,298.8838670058527,0.009051296823210781,False,False,True,True,1,8,1,0.001,2.0,1000,0.95,1 +-0.7391632363424439,0.0038845880490043783,-1.7127332960931857e-06,16554.822,4.8168414440685,3.568092274534987e-11,2.9795872388807486e-16,0.5848433900209784,8122.147,8122.147,298.883220079081,0.00905154504986953,False,False,True,True,1,32,1,0.001,2.0,1000,0.95,1 +-0.7784580996355261,0.00031068495064801027,1.4243433543725814e-07,40795.684,2.7623001784708734,1.526348882049979e-11,1.178276035895305e-16,0.039946535962371854,8122.147,8122.147,298.87909358474076,0.00905312837809255,False,False,True,True,2,1,1,0.001,2.0,1000,0.95,1 +-0.7789071303067274,0.004981205011517886,-2.298456045005115e-06,19236.17,4.180557562313765,3.0018237038051485e-11,2.4810626158234013e-16,0.062254932989162626,8122.147,8122.147,298.87913652486986,0.009053111901139398,False,False,True,True,2,2,1,0.001,2.0,1000,0.95,1 +-0.7790289991540522,0.004580420518379924,-2.089047168965172e-06,13149.629,5.507153160116777,4.32489900315634e-11,3.6359123339289724e-16,0.0869002560248191,8122.147,8122.147,298.87914817977645,0.009053107429645959,False,False,True,True,2,3,1,0.001,2.0,1000,0.95,1 +-0.7790321249373178,0.004366552007525115,-1.977308693041843e-06,13383.719,5.407122714164309,4.253211593673818e-11,3.581082420126434e-16,0.1042794320055691,8122.147,8122.147,298.8791484791445,0.009053107315211351,False,False,True,True,2,4,1,0.001,2.0,1000,0.95,1 +-0.7788626675445998,0.004143714912743235,-1.8609282791196002e-06,13036.078,5.504509447322222,4.36803474924132e-11,3.6865455111477145e-16,0.13476509601605358,8122.147,8122.147,298.8791322743415,0.00905311353349098,False,False,True,True,2,6,1,0.001,2.0,1000,0.95,1 +-0.7785371169597766,0.004028689206336367,-1.8009207612013772e-06,12813.125,5.577774354291847,4.4456561515122564e-11,3.7563217084395234e-16,0.16907955399074126,8122.147,8122.147,298.87910114206704,0.009053125479198771,False,False,True,True,2,8,1,0.001,2.0,1000,0.95,1 +-0.7720376846771182,0.0037529697572153964,-1.6587329663298594e-06,12186.532,5.8165863708938925,4.6805039946142804e-11,3.964247019085643e-16,0.5696649579767836,8122.147,8122.147,298.8784796173632,0.00905336395806597,False,False,True,True,2,32,1,0.001,2.0,1000,0.95,1 +-0.8394806653812537,0.0002711266791948219,1.4133053139082918e-07,8127.038,8.173483436288809,6.766667621683392e-11,5.671542091495478e-16,0.039799266967747826,8122.147,8122.147,298.8713413413251,0.009056102887603463,False,False,True,True,3,1,1,0.001,2.0,1000,0.95,1 +-0.8398679913381502,0.004835982578299536,-2.2454356654356966e-06,8123.823,8.194750197065114,6.799752476616417e-11,5.712232145720693e-16,0.06109971901969402,8122.147,8122.147,298.8713778730098,0.009056088870484827,False,False,True,True,3,2,1,0.001,2.0,1000,0.95,1 +-0.8399662103291683,0.00445754564100298,-2.0476202415818656e-06,8123.298,8.207320834259926,6.82021010193135e-11,5.737787034616475e-16,0.07962448600665084,8122.147,8122.147,298.8713871374605,0.009056085316360794,False,False,True,True,3,3,1,0.001,2.0,1000,0.95,1 +-0.8399545341420178,0.004253697106150867,-1.941074704055537e-06,8123.163,8.214774111303552,6.832455637213244e-11,5.753131496743095e-16,0.09795833500174922,8122.147,8122.147,298.8713860366286,0.00905608573917511,False,False,True,True,3,4,1,0.001,2.0,1000,0.95,1 +-0.8397742499905214,0.004040057058887214,-1.8294527183177617e-06,8123.042,8.223101117203809,6.846156219889896e-11,5.770308252693295e-16,0.1338848640116339,8122.147,8122.147,298.87136903312484,0.009056092263869475,False,False,True,True,3,6,1,0.001,2.0,1000,0.95,1 +-0.8394495624146736,0.003929310796365826,-1.771654054840907e-06,8122.987,8.227602905485012,6.853571537321405e-11,5.779610082091133e-16,0.1700088330071594,8122.147,8122.147,298.87133840961747,0.0090561040142805,False,False,True,True,3,8,1,0.001,2.0,1000,0.95,1 +-0.8334381136718805,0.003663212318258502,-1.6342686488170916e-06,8122.84,8.238629699180429,6.871731399386846e-11,5.802394124889766e-16,0.5750325610133586,8122.147,8122.147,298.87077143977325,0.009056321559006517,False,False,True,True,3,32,1,0.001,2.0,1000,0.95,1 +-0.854065527837522,0.006928489700214868,-4.81408036698272e-09,40795.684,2.8450377641897684,1.5404078731569696e-11,1.168321069365263e-16,0.04012730401518638,8122.147,8122.147,300.1155050089574,0.009055862985424531,True,False,False,False,0,1,1,0.001,2.0,1000,0.95,1 +-0.85397759330752,0.005277383315728912,-4.861749780538105e-09,19236.17,4.206267712689862,2.978946668937453e-11,2.4391944158696024e-16,0.06422125497920206,8122.147,8122.147,300.1154896656188,0.009055859372481925,True,False,False,False,0,2,1,0.001,2.0,1000,0.95,1 +-0.8538880491359535,0.00499165281206615,-4.869587190078906e-09,14652.832999999999,5.023357573208805,3.8454336576628945e-11,3.202622180498223e-16,0.08574199699796736,8122.147,8122.147,300.1154780437841,0.00905585873097397,True,False,False,False,0,3,1,0.001,2.0,1000,0.95,1 +-0.8537817757786614,0.004888884341369248,-4.872284449161657e-09,14926.292,4.936316986878289,3.778539759072343e-11,3.1506372002328807e-16,0.10801939000884886,8122.147,8122.147,300.1154669582332,0.009055860024485664,True,False,False,False,0,4,1,0.001,2.0,1000,0.95,1 +-0.8534626172733262,0.004810857242659949,-4.8729713406775454e-09,14926.292,4.895614541758751,3.777096700700179e-11,3.158407940435375e-16,0.15040485299323336,8122.147,8122.147,300.11543868335445,0.009055867716767476,True,False,False,False,0,6,1,0.001,2.0,1000,0.95,1 +-0.8530351056978192,0.004780298887737899,-4.87240999456906e-09,14385.414,5.031404270551974,3.917284360990369e-11,3.2818890896163453e-16,0.19620993000353337,8122.147,8122.147,300.1154030734426,0.009055879738796879,True,False,False,False,0,8,1,0.001,2.0,1000,0.95,1 +-0.8465860133298198,0.004711949931163417,-4.852220344270242e-09,12924.37,5.480818609067985,4.358964034082067e-11,3.6670836906991706e-16,0.66656154597149,8122.147,8122.147,300.11488376389127,0.009056074638525783,True,False,False,False,0,32,1,0.001,2.0,1000,0.95,1 +-0.8593812972994663,0.006894635549542727,-4.682724369722835e-09,40795.684,2.8563514578517646,1.5439403865963113e-11,1.1692361936205755e-16,0.040071938004984986,8122.147,8122.147,300.11494232915334,0.009056078030657869,True,False,False,False,1,1,1,0.001,2.0,1000,0.95,1 +-0.8592859424956356,0.005224467143023048,-4.729685207718881e-09,19236.17,4.215575195373292,2.981545442256357e-11,2.4399385562595313e-16,0.06387289102713112,8122.147,8122.147,300.1149265950706,0.00905607467263364,True,False,False,False,1,2,1,0.001,2.0,1000,0.95,1 +-0.8592067092522249,0.0049368341842637165,-4.737023601500412e-09,15066.522999999997,4.913474116494496,3.74342692534822e-11,3.1154642969280455e-16,0.08569221895231749,8122.147,8122.147,300.11491592143915,0.00905607370431758,True,False,False,False,1,3,1,0.001,2.0,1000,0.95,1 +-0.8590911870315021,0.004833353888727743,-4.739753211441267e-09,15066.523,4.908390806102794,3.7463494997431676e-11,3.1220138555380646e-16,0.10715442498258199,8122.147,8122.147,300.1149042399396,0.009056075238170001,True,False,False,False,1,4,1,0.001,2.0,1000,0.95,1 +-0.8587651292616627,0.004754770678175646,-4.740996517246798e-09,15208.69,4.83083256069568,3.710515524685994e-11,3.1004835610370356e-16,0.14958150301026762,8122.147,8122.147,300.1148757959133,0.00905608300334527,True,False,False,False,1,6,1,0.001,2.0,1000,0.95,1 +-0.8583312895478867,0.00472399462694284,-4.740954922047291e-09,14788.758,4.924711399837384,3.814083304560081e-11,3.1930851479535767e-16,0.18978308896839735,8122.147,8122.147,300.11484018188077,0.009056095029827071,True,False,False,False,1,8,1,0.001,2.0,1000,0.95,1 +-0.8519107616701938,0.004655826123382667,-4.720766109619912e-09,13383.719,5.32626889692327,4.212929612500952e-11,3.541940494380971e-16,0.6667919289684505,8122.147,8122.147,300.11433055813734,0.009056286232237804,True,False,False,False,1,32,1,0.001,2.0,1000,0.95,1 +-0.8919125740436185,0.006662583444494885,-4.6022673658163704e-09,40795.684,2.409809715492447,1.4199775825683754e-11,1.1307222646741993e-16,0.039811369962990284,8122.147,8122.147,300.1110616911817,0.009057561116618257,True,False,False,False,2,1,1,0.001,2.0,1000,0.95,1 +-0.891825278119418,0.005040871203732422,-4.6493658506507884e-09,8681.922,7.683555448842054,6.325103053213975e-11,5.29259626861283e-16,0.06376895200810395,8122.147,8122.147,300.11104646136477,0.009057557506981562,True,False,False,False,2,2,1,0.001,2.0,1000,0.95,1 +-0.891744874049305,0.004761052712467517,-4.6564541267973825e-09,8174.459999999999,8.131788519329275,6.729538073137609e-11,5.638899635589589e-16,0.086276510006428,8122.147,8122.147,300.1110356059494,0.00905755659576853,True,False,False,False,2,3,1,0.001,2.0,1000,0.95,1 +-0.8916302287996558,0.004660136452457664,-4.658752999694982e-09,8144.592,8.165448871052796,6.764247980832495e-11,5.672469904877468e-16,0.10669715898256982,8122.147,8122.147,300.111023875301,0.0090575581479408,True,False,False,False,2,4,1,0.001,2.0,1000,0.95,1 +-0.8913127633318751,0.004583324681959766,-4.660359454247698e-09,8132.849,8.184000312498986,6.786696359316411e-11,5.696759070441737e-16,0.14821639802175923,8122.147,8122.147,300.1109957785894,0.009057565783700533,True,False,False,False,2,6,1,0.001,2.0,1000,0.95,1 +-0.8908993239027607,0.004553190325736978,-4.659332831016827e-09,8129.818,8.191253063817285,6.796578464857448e-11,5.708178351946427e-16,0.18921179102108,8122.147,8122.147,300.11096133852215,0.009057577364045742,True,False,False,False,2,8,1,0.001,2.0,1000,0.95,1 +-0.8848544526558255,0.004486782384731214,-4.640584676945947e-09,8126.02,8.206301665685281,6.81884606623814e-11,5.734933486225361e-16,0.6701163099787664,8122.147,8122.147,300.1104749279258,0.009057759700120223,True,False,False,False,2,32,1,0.001,2.0,1000,0.95,1 +-0.9537034564724101,0.006270988086612306,-4.218641198525108e-09,8122.323,8.099027434188596,6.640913665655331e-11,5.512853670683755e-16,0.0398822229981306,8122.147,8122.147,300.10452207944803,0.009060060367762472,True,False,False,False,3,1,1,0.001,2.0,1000,0.95,1 +-0.9536296405989519,0.004677068500768655,-4.263685146263518e-09,8122.211,8.104117542544563,6.649660137902777e-11,5.524289590588424e-16,0.06332436399679864,8122.147,8122.147,300.10450813817954,0.009060056410676084,True,False,False,False,3,2,1,0.001,2.0,1000,0.95,1 +-0.9535497810652882,0.004404351537630191,-4.270231276221059e-09,8122.200999999999,8.11243368532328,6.663513925369864e-11,5.541808587062679e-16,0.08708992999527254,8122.147,8122.147,300.10449746674044,0.009060055487484417,True,False,False,False,3,3,1,0.001,2.0,1000,0.95,1 +-0.9534362841961741,0.00430562762039699,-4.273039182906002e-09,8122.189,8.118569625084401,6.673679369311584e-11,5.554597870126126e-16,0.10637249999126652,8122.147,8122.147,300.1044859763621,0.009060056973575713,True,False,False,False,3,4,1,0.001,2.0,1000,0.95,1 +-0.9531200550377705,0.004230132276575205,-4.273420273898099e-09,8122.185,8.126223797674914,6.686328904661758e-11,5.570469144162407e-16,0.14830731200345326,8122.147,8122.147,300.104458346828,0.009060064452297678,True,False,False,False,3,6,1,0.001,2.0,1000,0.95,1 +-0.9526927128131635,0.004200317578636259,-4.273376331617729e-09,8122.181,8.130648400799211,6.693627172440373e-11,5.579610546937059e-16,0.18961887903060415,8122.147,8122.147,300.10442327485475,0.009060076283350545,True,False,False,False,3,8,1,0.001,2.0,1000,0.95,1 +-0.9473115689070832,0.004136962241489073,-4.257229742343771e-09,8122.178,8.142102700605193,6.712504462943192e-11,5.603232336558546e-16,0.6822816320054699,8122.147,8122.147,300.1039958770679,0.009060236082014084,True,False,False,False,3,32,1,0.001,2.0,1000,0.95,1 +-0.854065527837522,0.006928489700214868,-4.81408036698272e-09,40795.684,2.8450377641897684,1.5404078731569696e-11,1.168321069365263e-16,0.04078578699773061,8122.147,8122.147,300.1155050089574,0.009055862985424531,True,False,False,True,0,1,1,0.001,2.0,1000,0.95,1 +-0.8539765592589177,0.005277379583617403,-4.8619137587441186e-09,19236.17,4.206267680481297,2.978946662787135e-11,2.4391944168162266e-16,0.06146311098927981,8122.147,8122.147,300.11548958285107,0.009055859404114243,True,False,False,True,0,2,1,0.001,2.0,1000,0.95,1 +-0.8538915837205008,0.004991666934756722,-4.8694381599850844e-09,14652.833,5.023357554107498,3.8454336544989145e-11,3.202622181169792e-16,0.08469632698688656,8122.147,8122.147,300.1154783267008,0.009055858622848708,True,False,False,True,0,3,1,0.001,2.0,1000,0.95,1 +-0.8537904870857815,0.004888918664960329,-4.871891163063524e-09,14926.292,4.936317007334076,3.778539772421441e-11,3.150637203599407e-16,0.10504583401052514,8122.147,8122.147,300.1154676555062,0.009055859758001163,True,False,False,True,0,4,1,0.001,2.0,1000,0.95,1 +-0.8534643062408143,0.004810863479519867,-4.87356794504068e-09,14926.292,4.895614404029423,3.7770966795055326e-11,3.158407937926818e-16,0.14601595602289308,8122.147,8122.147,300.115438818543,0.009055867665101061,True,False,False,True,0,6,1,0.001,2.0,1000,0.95,1 +-0.8530275310945412,0.004780262391193446,-4.872069789274569e-09,14385.414,5.031404372314367,3.9172843782349406e-11,3.281889091672474e-16,0.1896473689666891,8122.147,8122.147,300.1154024671583,0.009055879970507089,True,False,False,True,0,8,1,0.001,2.0,1000,0.95,1 +-0.8465642171676224,0.004711857098698147,-4.852095293259029e-09,12924.37,5.480818611386663,4.358964028041561e-11,3.6670836902912413e-16,0.6607497919721936,8122.147,8122.147,300.11488201939505,0.00905607530523822,True,False,False,True,0,32,1,0.001,2.0,1000,0.95,1 +-0.8593812972994663,0.006894635549542727,-4.682724369722835e-09,40795.684,2.8563514578517646,1.5439403865963113e-11,1.1692361936205755e-16,0.03994330697605619,8122.147,8122.147,300.11494232915334,0.009056078030657869,True,False,False,True,1,1,1,0.001,2.0,1000,0.95,1 +-0.8592886340193617,0.0052244777126588815,-4.729463749450491e-09,19236.17,4.215575176751906,2.9815454536918987e-11,2.439938578451056e-16,0.06038396798976464,8122.147,8122.147,300.11492680740656,0.009056074591483007,True,False,False,True,1,2,1,0.001,2.0,1000,0.95,1 +-0.8591965112693778,0.004936793322769972,-4.737132569890279e-09,15066.523,4.913474113556729,3.7434269261696574e-11,3.115464297982898e-16,0.08268082798167598,8122.147,8122.147,300.11491511691565,0.00905607401179166,True,False,False,True,1,3,1,0.001,2.0,1000,0.95,1 +-0.8590862966046386,0.004833333780254634,-4.7395265889010885e-09,15066.523,4.908390824553053,3.7463495030411296e-11,3.122013855282914e-16,0.10416755900223507,8122.147,8122.147,300.11490385413174,0.009056075385618272,True,False,False,True,1,4,1,0.001,2.0,1000,0.95,1 +-0.8587673879905888,0.004754776316303833,-4.740758667576284e-09,15208.69,4.830832592543682,3.7105155306484385e-11,3.1004835628146473e-16,0.1464302069725818,8122.147,8122.147,300.1148759741047,0.00905608293524377,True,False,False,True,1,6,1,0.001,2.0,1000,0.95,1 +-0.858341400953444,0.004724027801273678,-4.740088157054179e-09,14788.758,4.924711374654811,3.814083300598104e-11,3.1930851476508307e-16,0.19714073202339932,8122.147,8122.147,300.11484097956844,0.00905609472496661,True,False,False,True,1,8,1,0.001,2.0,1000,0.95,1 +-0.8519016883690611,0.0046557834308487145,-4.720760017271064e-09,13383.719,5.3262689019140375,4.212929596816454e-11,3.541940486341891e-16,0.6679337360001227,8122.147,8122.147,300.11432984238866,0.009056286505782378,True,False,False,True,1,32,1,0.001,2.0,1000,0.95,1 +-0.8919125740436185,0.006662583444494885,-4.6022673658163704e-09,40795.684,2.409809715492447,1.4199775825683754e-11,1.1307222646741993e-16,0.04097304199603968,8122.147,8122.147,300.1110616911817,0.009057561116618257,True,False,False,True,2,1,1,0.001,2.0,1000,0.95,1 +-0.8918230082343714,0.005040861693998977,-4.649281083388135e-09,8681.922,7.683555462418683,6.325103049275286e-11,5.29259626225929e-16,0.06621697700757068,8122.147,8122.147,300.11104627987703,0.009057557576342079,True,False,False,True,2,2,1,0.001,2.0,1000,0.95,1 +-0.8917410242001433,0.00476103675248396,-4.656400013833273e-09,8174.46,8.131788516030314,6.729538067487898e-11,5.638899629237827e-16,0.08277017696309485,8122.147,8122.147,300.11103529813766,0.009057556713408485,True,False,False,True,2,3,1,0.001,2.0,1000,0.95,1 +-0.8916328142836383,0.004660145441391705,-4.658480334124304e-09,8144.592,8.165448870510824,6.764247980706418e-11,5.67246990483383e-16,0.10237160701217363,8122.147,8122.147,300.1110240820221,0.009057558068936848,True,False,False,True,2,4,1,0.001,2.0,1000,0.95,1 +-0.8913177635952341,0.004583346159108714,-4.65994076832843e-09,8132.849,8.184000312600693,6.786696359585422e-11,5.696759070847304e-16,0.14432841200687108,8122.147,8122.147,300.11099617838164,0.009057565630909245,True,False,False,True,2,6,1,0.001,2.0,1000,0.95,1 +-0.8908862953668328,0.004553138587311878,-4.659953348543078e-09,8129.818,8.191253063371436,6.796578464079738e-11,5.708178350944527e-16,0.1858539289678447,8122.147,8122.147,300.11096029683966,0.009057577762152304,True,False,False,True,2,8,1,0.001,2.0,1000,0.95,1 +-0.884872664898606,0.004486865573596788,-4.640649054268864e-09,8126.02,8.206301665741787,6.81884606559587e-11,5.734933485308148e-16,0.6612561890360666,8122.147,8122.147,300.1104763839839,0.00905775914365053,True,False,False,True,2,32,1,0.001,2.0,1000,0.95,1 +-0.9537034564724101,0.006270988086612306,-4.218641198525108e-09,8122.323,8.099027434188596,6.640913665655331e-11,5.512853670683755e-16,0.041294372003903845,8122.147,8122.147,300.10452207944803,0.009060060367762472,True,False,False,True,3,1,1,0.001,2.0,1000,0.95,1 +-0.953630416361384,0.004677071027742841,-4.263467768064744e-09,8122.211,8.104117543686396,6.649660139835625e-11,5.524289593066173e-16,0.0598370180123311,8122.147,8122.147,300.1045081993503,0.009060056387298404,True,False,False,True,3,2,1,0.001,2.0,1000,0.95,1 +-0.9535521604758346,0.00440436228626595,-4.2703125688325905e-09,8122.201,8.112433683207806,6.663513922183745e-11,5.541808583455724e-16,0.0848681649986247,8122.147,8122.147,300.10449765436374,0.009060055415781637,True,False,False,True,3,3,1,0.001,2.0,1000,0.95,1 +-0.953429444504994,0.0043056015012439275,-4.272732253077227e-09,8122.189,8.11856962510055,6.673679369474691e-11,5.554597870506167e-16,0.10247288801474497,8122.147,8122.147,300.1044854370369,0.009060057179689787,True,False,False,True,3,4,1,0.001,2.0,1000,0.95,1 +-0.9531016481341692,0.004230064982152726,-4.274077187657599e-09,8122.185,8.126223797743464,6.686328904777208e-11,5.570469144302759e-16,0.1437850119691575,8122.147,8122.147,300.10445689540546,0.009060065006987406,True,False,False,True,3,6,1,0.001,2.0,1000,0.95,1 +-0.9526867843199782,0.0042002932906370916,-4.27239525875589e-09,8122.181,8.130648400746242,6.693627172346415e-11,5.579610546814809e-16,0.18278805300360546,8122.147,8122.147,300.1044228073827,0.009060076462004246,True,False,False,True,3,8,1,0.001,2.0,1000,0.95,1 +-0.9473188722224135,0.004136992408177775,-4.257201818499978e-09,8122.178,8.14210270240729,6.71250446603213e-11,5.603232340563574e-16,0.6452996499974688,8122.147,8122.147,300.1039964529175,0.009060235861941457,True,False,False,True,3,32,1,0.001,2.0,1000,0.95,1 +-0.7410217546677589,0.0003405582369282456,1.4212895386479796e-07,40795.684,3.087865943338912,1.6421866024522423e-11,1.2208823783467527e-16,0.040530407983169425,8122.147,8122.147,298.8844137397728,0.009051087041052992,True,False,True,False,0,1,1,0.001,2.0,1000,0.95,1 +-0.7410762250354863,0.0002603175888680198,1.4211931995654348e-07,19236.17,4.4349625978467575,3.0961680064119095e-11,2.529779336412409e-16,0.06533671299621346,8122.147,8122.147,298.88441743339655,0.009051084154708611,True,False,True,False,0,2,1,0.001,2.0,1000,0.95,1 +-0.7410640763027554,0.000245007805062869,1.4211664188350237e-07,16091.857999999998,4.89350495062536,3.630922616584525e-11,3.021223684934235e-16,0.08589818201653543,8122.147,8122.147,298.88441569329257,0.009051084264358071,True,False,True,False,0,3,1,0.001,2.0,1000,0.95,1 +-0.7409996033853838,0.00023935437252475822,1.4211822986845424e-07,16093.219,4.879186990385302,3.631523467960219e-11,3.026776022773419e-16,0.10707638302119449,8122.147,8122.147,298.8844092341772,0.00905108646410798,True,False,True,False,0,4,1,0.001,2.0,1000,0.95,1 +-0.7407661231827012,0.0002349563622487949,1.4212236125986377e-07,16243.832,4.8365590343371165,3.6009253642884e-11,3.005480471072555e-16,0.14917621999848052,8122.147,8122.147,298.88438660333344,0.009051094873177786,True,False,True,False,0,6,1,0.001,2.0,1000,0.95,1 +-0.7404090474158687,0.00023313221595344658,1.4212864038291861e-07,16243.832,4.818171134978476,3.599260049388651e-11,3.008761065563107e-16,0.1903543059816002,8122.147,8122.147,298.88435228725666,0.009051107905367978,True,False,True,False,0,8,1,0.001,2.0,1000,0.95,1 +-0.7334558452840922,0.00022690365221933462,1.42274018073707e-07,15791.776,4.897686547254465,3.699006434119306e-11,3.103634840027041e-16,0.6682000549772056,8122.147,8122.147,298.8836864959558,0.009051363073457509,True,False,True,False,0,32,1,0.001,2.0,1000,0.95,1 +-0.7458751435198807,0.00034197774033373207,1.4042214812985676e-07,40795.684,3.096804271096643,1.6454675783917072e-11,1.2218304386377538e-16,0.03987507799320156,8122.147,8122.147,298.8838532436859,0.009051302103648547,True,False,True,False,1,1,1,0.001,2.0,1000,0.95,1 +-0.7459270974521481,0.0002607641500844693,1.4041216225661735e-07,19236.17,4.444188771428596,3.099382132497921e-11,2.530821040218188e-16,0.06427222102502128,8122.147,8122.147,298.88385663349214,0.00905129934195699,True,False,True,False,1,2,1,0.001,2.0,1000,0.95,1 +-0.7459104340797662,0.0002452801508070479,1.404117368555835e-07,16243.831999999999,4.866296035375643,3.600861883083785e-11,2.994009503513498e-16,0.08704274496994913,8122.147,8122.147,298.88385448455074,0.00905129960877581,True,False,True,False,1,3,1,0.001,2.0,1000,0.95,1 +-0.7458433929450781,0.00023956810787240101,1.404129252556563e-07,16243.832,4.85663292665413,3.602396567903486e-11,2.999867511727744e-16,0.10934745497434051,8122.147,8122.147,298.8838478715124,0.009051301867269263,True,False,True,False,1,4,1,0.001,2.0,1000,0.95,1 +-0.7456050342368736,0.000235128574445298,1.4041606128875617e-07,16243.832,4.848686248146957,3.6044384894879914e-11,3.0065602643569506e-16,0.15540685602172744,8122.147,8122.147,298.8838251009271,0.009051310329367653,True,False,True,False,1,6,1,0.001,2.0,1000,0.95,1 +-0.7452294419126981,0.0002332800095246057,1.4042311463624557e-07,16243.832,4.8336052970773595,3.6032991322688765e-11,3.009906894291137e-16,0.19034688101601205,8122.147,8122.147,298.8837895286902,0.009051323843150827,True,False,True,False,1,8,1,0.001,2.0,1000,0.95,1 +-0.7382936840490406,0.00022710164694217383,1.4056335263107345e-07,16091.858,4.835952022971144,3.6350995504124787e-11,3.0469997206486374e-16,0.6704673469721456,8122.147,8122.147,298.8831349431532,0.009051574710360258,True,False,True,False,1,32,1,0.001,2.0,1000,0.95,1 +-0.7784580996355261,0.00031068495064801027,1.4243433543725814e-07,40795.684,2.7623001784708734,1.526348882049979e-11,1.178276035895305e-16,0.04031859502720181,8122.147,8122.147,298.87909358474076,0.00905312837809255,True,False,True,False,2,1,1,0.001,2.0,1000,0.95,1 +-0.7784893246792102,0.00023159488614510337,1.424249838118813e-07,15791.776,4.776347967483913,3.6080015105176695e-11,3.009579068149502e-16,0.06378799899903242,8122.147,8122.147,298.87909506393424,0.00905312635186642,True,False,True,False,2,2,1,0.001,2.0,1000,0.95,1 +-0.7784674876391673,0.00021650095817449255,1.424242379085422e-07,11803.137,5.9658244364814825,4.7865146378544494e-11,4.035046415486963e-16,0.08650401799968677,8122.147,8122.147,298.8790924024524,0.009053126818212855,True,False,True,False,2,3,1,0.001,2.0,1000,0.95,1 +-0.7784057780317433,0.00021093628981816437,1.424232563742439e-07,11277.413,6.177410956986151,5.008499560990155e-11,4.231586426075177e-16,0.11424281601648545,8122.147,8122.147,298.879086214755,0.009053128915197793,True,False,True,False,2,4,1,0.001,2.0,1000,0.95,1 +-0.7781647012794379,0.00020659276259493708,1.424279698295644e-07,10088.745,6.812234382929128,5.599592253727342e-11,4.741411120946812e-16,0.150226598016161,8122.147,8122.147,298.8790628783901,0.009053137596205676,True,False,True,False,2,6,1,0.001,2.0,1000,0.95,1 +-0.777814088820683,0.0002047907313453834,1.4243543039124673e-07,9665.738,7.07879765229069,5.847223395883687e-11,4.955541407503413e-16,0.19086077899919474,8122.147,8122.147,298.8790292105897,0.009053150380180062,True,False,True,False,2,8,1,0.001,2.0,1000,0.95,1 +-0.7712437461396715,0.0001988044779750453,1.425715011951767e-07,9071.819,7.499916529508921,6.240287853127371e-11,5.297768594442806e-16,0.6682160330019542,8122.147,8122.147,298.8784006035861,0.009053391280690342,True,False,True,False,2,32,1,0.001,2.0,1000,0.95,1 +-0.8394806653812537,0.0002711266791948219,1.4133053139082918e-07,8127.038,8.173483436288809,6.766667621683392e-11,5.671542091495478e-16,0.04004608702234691,8122.147,8122.147,298.8713413413251,0.009056102887603463,True,False,True,False,3,1,1,0.001,2.0,1000,0.95,1 +-0.8395154157481266,0.00019294405041136997,1.4131904636262294e-07,8122.935,8.188666137322269,6.789297339740366e-11,5.699092363026365e-16,0.0629537240274658,8122.147,8122.147,298.8713431340637,0.009056100761972257,True,False,True,False,3,2,1,0.001,2.0,1000,0.95,1 +-0.8395044296337528,0.00017804704219770429,1.4131808246872768e-07,8122.644999999999,8.198678377498124,6.805752631653067e-11,5.719776079345605e-16,0.08558659500340582,8122.147,8122.147,298.8713415295092,0.009056100827281754,True,False,True,False,3,3,1,0.001,2.0,1000,0.95,1 +-0.8394406533764665,0.00017254599845273333,1.4131852632895403e-07,8122.554,8.2049165533172,6.816042938046318e-11,5.732709868042136e-16,0.10982421899825567,8122.147,8122.147,298.87133522966644,0.009056102968913102,True,False,True,False,3,4,1,0.001,2.0,1000,0.95,1 +-0.8392018312779803,0.00016825285604227247,1.4132374926041635e-07,8122.458,8.21204249788138,6.827792801746586e-11,5.747469142271741e-16,0.159171121998952,8122.147,8122.147,298.8713124237683,0.009056111447557868,True,False,True,False,3,6,1,0.001,2.0,1000,0.95,1 +-0.8388437950363148,0.00016647217199761144,1.4132930995468251e-07,8122.437,8.21592171582459,6.834210065571454e-11,5.755538655698706e-16,0.1902614200480457,8122.147,8122.147,298.8712785165372,0.009056124323780891,True,False,True,False,3,8,1,0.001,2.0,1000,0.95,1 +-0.8328074388600393,0.00016084983082009785,1.414494286474527e-07,8122.366,8.225543522965749,6.850111833063824e-11,5.775527452327138e-16,0.6636090240208432,8122.147,8122.147,298.8707088959438,0.009056342590469958,True,False,True,False,3,32,1,0.001,2.0,1000,0.95,1 +-0.7410217546677589,0.0003405582369282456,1.4212895386479796e-07,40795.684,3.087865943338912,1.6421866024522423e-11,1.2208823783467527e-16,0.039605123005458154,8122.147,8122.147,298.8844137397728,0.009051087041052992,True,False,True,True,0,1,1,0.001,2.0,1000,0.95,1 +-0.7410799755956154,0.0002603185270686481,1.4211967223377864e-07,19236.17,4.434962665731253,3.0961680270346066e-11,2.529779339976533e-16,0.05972958898928482,8122.147,8122.147,298.8844177923727,0.009051084016969425,True,False,True,True,0,2,1,0.001,2.0,1000,0.95,1 +-0.741065346488412,0.0002450086720386935,1.4211655659929212e-07,16091.858,4.893504956896024,3.6309226181942765e-11,3.021223684935367e-16,0.08133636799902888,8122.147,8122.147,298.88441581486524,0.00905108421771046,True,False,True,True,0,3,1,0.001,2.0,1000,0.95,1 +-0.7409976874246604,0.00023935330335689287,1.4211829268799547e-07,16093.219,4.879186780323223,3.6315234132875856e-11,3.0267760128549954e-16,0.10339968397602206,8122.147,8122.147,298.88440905079557,0.009051086534471448,True,False,True,True,0,4,1,0.001,2.0,1000,0.95,1 +-0.7407665186927148,0.00023495842083320895,1.421216548978116e-07,16243.832,4.8365589783491325,3.6009253548776416e-11,3.005480470151276e-16,0.14795719700850896,8122.147,8122.147,298.8843866411884,0.009051094858652608,True,False,True,True,0,6,1,0.001,2.0,1000,0.95,1 +-0.7404076180302588,0.00023313149108616926,1.4212865755668103e-07,16243.832,4.818171101855258,3.5992600408289356e-11,3.0087610638810926e-16,0.1900326999857498,8122.147,8122.147,298.88435215044717,0.00905110795786178,True,False,True,True,0,8,1,0.001,2.0,1000,0.95,1 +-0.7334708136922274,0.0002269117352966532,1.422737039898786e-07,15791.776,4.897686294027621,3.699006366485282e-11,3.103634827103352e-16,0.6552906710167008,8122.147,8122.147,298.8836879285242,0.009051362523780008,True,False,True,True,0,32,1,0.001,2.0,1000,0.95,1 +-0.7458751435198807,0.00034197774033373207,1.4042214812985676e-07,40795.684,3.096804271096643,1.6454675783917072e-11,1.2218304386377538e-16,0.03976848698948743,8122.147,8122.147,298.8838532436859,0.009051302103648547,True,False,True,True,1,1,1,0.001,2.0,1000,0.95,1 +-0.7459270217434311,0.00026076620025605735,1.4041135716971742e-07,19236.17,4.444188943804098,3.0993821868317206e-11,2.5308210519219233e-16,0.05994173600629438,8122.147,8122.147,298.88385662635017,0.009051299344697547,True,False,True,True,1,2,1,0.001,2.0,1000,0.95,1 +-0.7459102852649946,0.0002452838921840339,1.404102650797573e-07,16243.832,4.866295991611225,3.6008618820294635e-11,2.994009511051197e-16,0.08165271203688462,8122.147,8122.147,298.8838544705119,0.009051299614162477,True,False,True,True,1,3,1,0.001,2.0,1000,0.95,1 +-0.745847048495496,0.00023957308297894997,1.4041169177879398e-07,16243.832,4.8566329657447955,3.6023965791843505e-11,2.9998675143600587e-16,0.10304640003596433,8122.147,8122.147,298.8838482163676,0.00905130173494824,True,False,True,True,1,4,1,0.001,2.0,1000,0.95,1 +-0.7456020814046949,0.00023512662858138356,1.4041627682988278e-07,16243.832,4.84868633920026,3.604438514457969e-11,3.0065602690636946e-16,0.14460193600461935,8122.147,8122.147,298.88382482236494,0.009051310436251852,True,False,True,True,1,6,1,0.001,2.0,1000,0.95,1 +-0.7452257507629123,0.00023327876556322735,1.404229526408285e-07,16243.832,4.833605216357535,3.603299115051768e-11,3.0099068924511273e-16,0.1860115929848689,8122.147,8122.147,298.88378918047806,0.009051323976759456,True,False,True,True,1,8,1,0.001,2.0,1000,0.95,1 +-0.7382821129383377,0.000227095470279437,1.4056358789427126e-07,16091.858,4.835951683576172,3.6350994522142505e-11,3.0469997013503815e-16,0.6763165630072763,8122.147,8122.147,298.883133851637,0.009051575129174802,True,False,True,True,1,32,1,0.001,2.0,1000,0.95,1 +-0.7784580996355261,0.00031068495064801027,1.4243433543725814e-07,40795.684,2.7623001784708734,1.526348882049979e-11,1.178276035895305e-16,0.03964167800586438,8122.147,8122.147,298.87909358474076,0.00905312837809255,True,False,True,True,2,1,1,0.001,2.0,1000,0.95,1 +-0.778488143656364,0.0002315938699553044,1.4242515010767787e-07,15791.776,4.7763479673630505,3.60800151036095e-11,3.0095790679552847e-16,0.05926679900585441,8122.147,8122.147,298.87909495099245,0.009053126395201906,True,False,True,True,2,2,1,0.001,2.0,1000,0.95,1 +-0.7784700680422807,0.00021650555731866916,1.4242294663936728e-07,11803.137,5.9658244108559515,4.7865146281963396e-11,4.0350464091329814e-16,0.08085001499057398,8122.147,8122.147,298.8790926492168,0.009053126723529264,True,False,True,True,2,3,1,0.001,2.0,1000,0.95,1 +-0.7784037653197573,0.0002109319026430967,1.4242458115827639e-07,11277.413,6.177411038579738,5.0084995706695434e-11,4.2315864261588486e-16,0.10432892200333299,8122.147,8122.147,298.8790860222789,0.009053128989050767,True,False,True,True,2,4,1,0.001,2.0,1000,0.95,1 +-0.7781711785413647,0.00020659677534240473,1.424276598761126e-07,10088.745,6.81223436155108,5.5995922503820026e-11,4.741411120330361e-16,0.14397190194722498,8122.147,8122.147,298.8790634978114,0.009053137358534755,True,False,True,True,2,6,1,0.001,2.0,1000,0.95,1 +-0.7778255595597305,0.00020479673634099527,1.4243528103849434e-07,9665.738,7.078797563019889,5.847223384474482e-11,4.955541406033531e-16,0.18518801602476742,8122.147,8122.147,298.87903030753375,0.009053149959283815,True,False,True,True,2,8,1,0.001,2.0,1000,0.95,1 +-0.771234497802098,0.00019879958375668139,1.4257169331753639e-07,9071.819,7.499916471226885,6.240287841045898e-11,5.29776858782717e-16,0.6463369330267597,8122.147,8122.147,298.8783997192217,0.009053391620020194,True,False,True,True,2,32,1,0.001,2.0,1000,0.95,1 +-0.8394806653812537,0.0002711266791948219,1.4133053139082918e-07,8127.038,8.173483436288809,6.766667621683392e-11,5.671542091495478e-16,0.04069735999655677,8122.147,8122.147,298.8713413413251,0.009056102887603463,True,False,True,True,3,1,1,0.001,2.0,1000,0.95,1 +-0.8395153121799948,0.00019294052350460333,1.4132038927451573e-07,8122.935,8.188666137322269,6.789297339740366e-11,5.699092363026365e-16,0.05972153899710975,8122.147,8122.147,298.8713431242953,0.009056100765720282,True,False,True,True,3,2,1,0.001,2.0,1000,0.95,1 +-0.8395006343194455,0.00017804304991386743,1.413189185239111e-07,8122.645,8.198678377698402,6.80575263191268e-11,5.719776079558722e-16,0.0805348570102069,8122.147,8122.147,298.871341171544,0.009056100964631577,True,False,True,True,3,3,1,0.001,2.0,1000,0.95,1 +-0.8394509820760621,0.00017254957106160873,1.4131905437878012e-07,8122.554,8.20491655421516,6.816042939406229e-11,5.732709869634596e-16,0.10164281597462832,8122.147,8122.147,298.8713362038448,0.009056102595124625,True,False,True,True,3,4,1,0.001,2.0,1000,0.95,1 +-0.8392002123274178,0.00016825260911446094,1.4132357044338573e-07,8122.458,8.21204249845451,6.827792802718971e-11,5.747469143505379e-16,0.1424070780230977,8122.147,8122.147,298.8713122710734,0.009056111506146644,True,False,True,True,3,6,1,0.001,2.0,1000,0.95,1 +-0.8388371737814926,0.0001664684455704446,1.4132948778985965e-07,8122.437,8.215921715933582,6.834210065749042e-11,5.755538655913225e-16,0.18297209300362738,8122.147,8122.147,298.8712778920396,0.009056124563398204,True,False,True,True,3,8,1,0.001,2.0,1000,0.95,1 +-0.8328230864900643,0.00016085831526879701,1.4144911633477697e-07,8122.366,8.225543526491549,6.850111838526893e-11,5.7755274586941e-16,0.6545607550178829,8122.147,8122.147,298.87071037170483,0.009056342024226928,True,False,True,True,3,32,1,0.001,2.0,1000,0.95,1 diff --git a/tests/python/physics/refdata/plots_ref/act_vs_substeps.png b/tests/python/physics/refdata/plots_ref/act_vs_substeps.png new file mode 100644 index 000000000..a428b24d7 Binary files /dev/null and b/tests/python/physics/refdata/plots_ref/act_vs_substeps.png differ diff --git a/tests/python/physics/refdata/plots_ref/exectime_vs_substeps.png b/tests/python/physics/refdata/plots_ref/exectime_vs_substeps.png new file mode 100644 index 000000000..0aecf44d2 Binary files /dev/null and b/tests/python/physics/refdata/plots_ref/exectime_vs_substeps.png differ diff --git a/tests/python/physics/refdata/plots_ref/mr_vs_substeps.png b/tests/python/physics/refdata/plots_ref/mr_vs_substeps.png new file mode 100644 index 000000000..93b044f81 Binary files /dev/null and b/tests/python/physics/refdata/plots_ref/mr_vs_substeps.png differ diff --git a/tests/python/physics/refdata/plots_ref/rv_diff_vs_substeps.png b/tests/python/physics/refdata/plots_ref/rv_diff_vs_substeps.png new file mode 100644 index 000000000..c30d46722 Binary files /dev/null and b/tests/python/physics/refdata/plots_ref/rv_diff_vs_substeps.png differ diff --git a/tests/python/physics/refdata/plots_ref/sr_vs_substeps.png b/tests/python/physics/refdata/plots_ref/sr_vs_substeps.png new file mode 100644 index 000000000..d64cbea00 Binary files /dev/null and b/tests/python/physics/refdata/plots_ref/sr_vs_substeps.png differ diff --git a/tests/python/physics/refdata/plots_ref/ss_vs_substeps.png b/tests/python/physics/refdata/plots_ref/ss_vs_substeps.png new file mode 100644 index 000000000..1388a54a6 Binary files /dev/null and b/tests/python/physics/refdata/plots_ref/ss_vs_substeps.png differ diff --git a/tests/python/physics/refdata/plots_ref/th_diff_vs_substeps.png b/tests/python/physics/refdata/plots_ref/th_diff_vs_substeps.png new file mode 100644 index 000000000..62e2b0454 Binary files /dev/null and b/tests/python/physics/refdata/plots_ref/th_diff_vs_substeps.png differ diff --git a/tests/python/physics/refdata/plots_ref/tr_vs_substeps.png b/tests/python/physics/refdata/plots_ref/tr_vs_substeps.png new file mode 100644 index 000000000..fc4f8c375 Binary files /dev/null and b/tests/python/physics/refdata/plots_ref/tr_vs_substeps.png differ diff --git a/tests/python/unit/sstp_cond.py b/tests/python/unit/sstp_cond.py index 583a2ce52..a2149a21c 100644 --- a/tests/python/unit/sstp_cond.py +++ b/tests/python/unit/sstp_cond.py @@ -32,6 +32,8 @@ def test(turb_cond): opts_init.exact_sstp_cond = True # test would fail with per-cell sstp logic opts_init.turb_cond_switch = turb_cond spinup = 20 + + # opts_init.sstp_cond_mix = False backend = lgrngn.backend_t.serial @@ -66,11 +68,15 @@ def test(turb_cond): rv[1,0] = 0.0095 prtcls.init(th, rv, rhod, Cx=Cx, Cz=Cz) - #equilibrium wet moment post spinup + #wet moment post init prtcls.diag_all() prtcls.diag_wet_mom(3); wet_post_init = copy(frombuffer(prtcls.outbuf()).reshape(opts_init.nx, opts_init.nz)) water_post_init = 1000. * 4./3. * pi * wet_post_init + rv + # print("liquid post init:", 1000. * 4./3. * pi * wet_post_init) + # print("rv post init:", rv) + # print("total post init:", water_post_init) + #spinup to get equilibrium if(turb_cond): @@ -84,14 +90,14 @@ def test(turb_cond): else: prtcls.step_sync(opts, th, rv) - #equilibrium wet moment post spinup + #wet moment post spinup prtcls.diag_all() 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 + assert allclose(water_post_spin, water_post_init, atol=0, rtol=1e-10) #advect SDs opts.adve=1