htsky

Load and structure a vertically stratified atmosphere
git clone git://git.meso-star.fr/htsky.git
Log | Files | Refs | README | LICENSE

commit 351eb0beac9155956fccc84b704cc318b7dadbba
parent 94465b104794cf711ed3a42798eb6eaceea093c4
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon, 27 Jan 2020 15:06:07 +0100

Refactor of the htsky_cloud.c file

Diffstat:
Msrc/htsky_cloud.c | 724++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
1 file changed, 419 insertions(+), 305 deletions(-)

diff --git a/src/htsky_cloud.c b/src/htsky_cloud.c @@ -55,177 +55,431 @@ aabb_intersect && !(aabb0_upp[2] < aabb1_low[2]) && !(aabb0_low[2] > aabb1_upp[2]); } -static void -cloud_vox_get_particle - (const size_t xyz[3], - float vox[], - const struct build_tree_context* ctx) +static res_T +setup_svx2htcp_z_lut(struct htsky* sky, double* cloud_upp_z) +{ + double org, height; + double len_z; + size_t nsplits; + size_t iz, iz2; + res_T res = RES_OK; + ASSERT(sky && sky->htcp_desc.irregular_z && cloud_upp_z); + + /* Find the min voxel size along Z and compute the length of a Z column */ + len_z = 0; + sky->lut_cell_sz = DBL_MAX; + FOR_EACH(iz, 0, sky->htcp_desc.spatial_definition[2]) { + len_z += sky->htcp_desc.vxsz_z[iz]; + sky->lut_cell_sz = MMIN(sky->lut_cell_sz, sky->htcp_desc.vxsz_z[iz]); + } + + /* Allocate the svx2htcp LUT. This LUT is a regular table whose absolute + * size is greater or equal to a Z column in the htcp file. The size of its + * cells is the minimal voxel size in Z of the htcp file */ + nsplits = (size_t)ceil(len_z / sky->lut_cell_sz); + res = darray_split_resize(&sky->svx2htcp_z, nsplits); + if(res != RES_OK) { + log_err(sky, + "could not allocate the table mapping regular to irregular Z.\n"); + goto error; + } + + /* Setup the svx2htcp LUT. Each LUT entry stores the index of the current Z + * voxel in the htcp file that overlaps the entry lower bound as well as the + * lower bound in Z of the next htcp voxel. */ + iz2 = 0; + org = sky->htcp_desc.lower[2]; + height = org + sky->htcp_desc.vxsz_z[iz2]; + FOR_EACH(iz, 0, nsplits) { + const double upp_z = (double)(iz + 1) * sky->lut_cell_sz + org; + darray_split_data_get(&sky->svx2htcp_z)[iz].index = iz2; + darray_split_data_get(&sky->svx2htcp_z)[iz].height = height; + if(upp_z >= height && iz + 1 < nsplits) { + ASSERT(iz2 + 1 < sky->htcp_desc.spatial_definition[2]); + height += sky->htcp_desc.vxsz_z[++iz2]; + } + } + ASSERT(eq_eps(height - org, len_z, 1.e-6)); + +exit: + *cloud_upp_z = height; + return res; +error: + darray_split_clear(&sky->svx2htcp_z); + height = -1; + goto exit; +} + +static INLINE void +compute_k_bounds_regular_z + (const struct htsky* sky, + const size_t iband, + const double vox_svx_low[3], /* Lower bound of the SVX voxel */ + const double vox_svx_upp[3], /* Upper bound of the SVX voxel */ + double ka_bounds[2], + double ks_bounds[2], + double kext_bounds[2]) { - const struct htcp_desc* htcp_desc; size_t ivox[3]; size_t igrid_low[3], igrid_upp[3]; - double vox_low[3], vox_upp[3]; + size_t i; + double ka, ks, kext; double low[3], upp[3]; + double ipart; + double rho_da; /* Dry air density */ double rct; + double Ca_avg; + double Cs_avg; + ASSERT(!sky->htcp_desc.irregular_z && vox_svx_low && vox_svx_upp); + ASSERT(ka_bounds && ks_bounds && kext_bounds); + + i = iband - sky->sw_bands_range[0]; + + /* Fetch the optical properties of the spectral band */ + Ca_avg = sky->sw_bands[i].Ca_avg; + Cs_avg = sky->sw_bands[i].Cs_avg; + + /* Compute the *inclusive* bounds of the indices of cloud grid overlapped by + * the SVX voxel */ + low[0] = (vox_svx_low[0]-sky->htcp_desc.lower[0])/sky->htcp_desc.vxsz_x; + low[1] = (vox_svx_low[1]-sky->htcp_desc.lower[1])/sky->htcp_desc.vxsz_x; + low[2] = (vox_svx_low[2]-sky->htcp_desc.lower[2])/sky->htcp_desc.vxsz_z[0]; + upp[0] = (vox_svx_upp[0]-sky->htcp_desc.lower[0])/sky->htcp_desc.vxsz_y; + upp[1] = (vox_svx_upp[1]-sky->htcp_desc.lower[1])/sky->htcp_desc.vxsz_y; + upp[2] = (vox_svx_upp[2]-sky->htcp_desc.lower[2])/sky->htcp_desc.vxsz_z[0]; + igrid_low[0] = (size_t)low[0]; + igrid_low[1] = (size_t)low[1]; + igrid_low[2] = (size_t)low[2]; + igrid_upp[0] = (size_t)upp[0] - (modf(upp[0], &ipart)==0); + igrid_upp[1] = (size_t)upp[1] - (modf(upp[1], &ipart)==0); + igrid_upp[2] = (size_t)upp[2] - (modf(upp[2], &ipart)==0); + ASSERT(igrid_low[0] <= igrid_upp[0]); + ASSERT(igrid_low[1] <= igrid_upp[1]); + ASSERT(igrid_low[2] <= igrid_upp[2]); + + /* Prepare the iteration over the grid voxels overlapped by the SVX voxel */ + ka_bounds[0] = ks_bounds[0] = kext_bounds[0] = DBL_MAX; + ka_bounds[1] = ks_bounds[1] = kext_bounds[1] =-DBL_MAX; + + /* Iterate over the grid voxels overlapped by the SVX voxel */ + FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { + FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { + FOR_EACH(ivox[2], igrid_low[2], igrid_upp[2]+1) { + /* Compute the radiative properties */ + rho_da = cloud_dry_air_density(&sky->htcp_desc, ivox); + rct = htcp_desc_RCT_at(&sky->htcp_desc, ivox[0], ivox[1], ivox[2], 0); + ka = Ca_avg * rho_da * rct; + ks = Cs_avg * rho_da * rct; + kext = ka + ks; + /* Update the boundaries of the radiative properties */ + ka_bounds[0] = MMIN(ka_bounds[0], ka); + ka_bounds[1] = MMAX(ka_bounds[1], ka); + ks_bounds[0] = MMIN(ks_bounds[0], ks); + ks_bounds[1] = MMAX(ks_bounds[1], ks); + kext_bounds[0] = MMIN(kext_bounds[0], kext); + kext_bounds[1] = MMAX(kext_bounds[1], kext); + #ifndef NDEBUG + { + double tmp_low[3], tmp_upp[3]; + htcp_desc_get_voxel_aabb + (&sky->htcp_desc, ivox[0], ivox[1], ivox[2], tmp_low, tmp_upp); + ASSERT(aabb_intersect(tmp_low, tmp_upp, vox_svx_low, vox_svx_upp)); + } + #endif + } + } + } +} + + +static void +compute_k_bounds_irregular_z + (const struct htsky* sky, + const size_t iband, + const double vox_svx_low[3], /* Lower bound of the SVX voxel */ + const double vox_svx_upp[3], /* Upper bound of the SVX voxel */ + double ka_bounds[2], + double ks_bounds[2], + double kext_bounds[2]) +{ + size_t ivox[3]; + size_t igrid_low[2], igrid_upp[2]; + size_t i; double ka, ks, kext; - double ka_min, ka_max; - double ks_min, ks_max; - double kext_min, kext_max; + double low[2], upp[2]; + double ipart; double rho_da; /* Dry air density */ + double rct; double Ca_avg; double Cs_avg; - double ipart; - size_t i; - ASSERT(xyz && vox && ctx); + double pos_z; + size_t ilut_low, ilut_upp; + size_t ilut; + size_t ivox_z_prev = SIZE_MAX; + ASSERT(sky->htcp_desc.irregular_z && vox_svx_low && vox_svx_upp); + ASSERT(ka_bounds && ks_bounds && kext_bounds); - i = ctx->iband - ctx->sky->sw_bands_range[0]; - htcp_desc = &ctx->sky->htcp_desc; + i = iband - sky->sw_bands_range[0]; /* Fetch the optical properties of the spectral band */ - Ca_avg = ctx->sky->sw_bands[i].Ca_avg; - Cs_avg = ctx->sky->sw_bands[i].Cs_avg; + Ca_avg = sky->sw_bands[i].Ca_avg; + Cs_avg = sky->sw_bands[i].Cs_avg; + + /* Compute the *inclusive* bounds of the indices of cloud grid overlapped by + * the SVX voxel along the X and Y axes */ + low[0] = (vox_svx_low[0]-sky->htcp_desc.lower[0])/sky->htcp_desc.vxsz_x; + low[1] = (vox_svx_low[1]-sky->htcp_desc.lower[1])/sky->htcp_desc.vxsz_x; + upp[0] = (vox_svx_upp[0]-sky->htcp_desc.lower[0])/sky->htcp_desc.vxsz_y; + upp[1] = (vox_svx_upp[1]-sky->htcp_desc.lower[1])/sky->htcp_desc.vxsz_y; + igrid_low[0] = (size_t)low[0]; + igrid_low[1] = (size_t)low[1]; + igrid_upp[0] = (size_t)upp[0] - (modf(upp[0], &ipart)==0); + igrid_upp[1] = (size_t)upp[1] - (modf(upp[1], &ipart)==0); + ASSERT(igrid_low[0] <= igrid_upp[0]); + ASSERT(igrid_low[1] <= igrid_upp[1]); + + /* Compute the *inclusive* bounds of the indices of the LUT cells + * overlapped by the SVX voxel */ + ilut_low = (size_t)((vox_svx_low[2]-sky->htcp_desc.lower[2])/sky->lut_cell_sz); + ilut_upp = (size_t)((vox_svx_upp[2]-sky->htcp_desc.lower[2])/sky->lut_cell_sz); + ASSERT(ilut_low <= ilut_upp); + + /* Prepare the iteration over the LES voxels overlapped by the SVX voxel */ + ka_bounds[0] = ks_bounds[0] = kext_bounds[0] = DBL_MAX; + ka_bounds[1] = ks_bounds[1] = kext_bounds[1] =-DBL_MAX; + ivox_z_prev = SIZE_MAX; + pos_z = vox_svx_low[2]; + ASSERT(pos_z >= (double)ilut_low * sky->lut_cell_sz); + ASSERT(pos_z <= (double)ilut_upp * sky->lut_cell_sz); + + /* Iterate over the LUT cells overlapped by the voxel */ + FOR_EACH(ilut, ilut_low, ilut_upp+1) { + const struct split* split = darray_split_cdata_get(&sky->svx2htcp_z)+ilut; + ASSERT(ilut < darray_split_size_get(&sky->svx2htcp_z)); + + ivox[2] = pos_z <= split->height ? split->index : split->index + 1; + if(ivox[2] >= sky->htcp_desc.spatial_definition[2] + && eq_eps(pos_z, split->height, 1.e-6)) { /* Handle numerical inaccuracy */ + ivox[2] = split->index; + } + + /* Compute the upper bound of the *next* LUT cell clamped to the voxel + * upper bound. Note that the upper bound of the current LUT cell is + * the lower bound of the next cell, i.e. (ilut+1)*lut_cell_sz. The + * upper bound of the next cell is thus the lower bound of the cell + * following the next cell, i.e. (ilut+2)*lut_cell_sz */ + pos_z = MMIN((double)(ilut+2)*sky->lut_cell_sz, vox_svx_upp[2]); + + /* Does the LUT cell overlap an already handled LES voxel? */ + if(ivox[2] == ivox_z_prev) continue; + ivox_z_prev = ivox[2]; + + FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { + FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { + + /* Compute the radiative properties */ + rho_da = cloud_dry_air_density(&sky->htcp_desc, ivox); + rct = htcp_desc_RCT_at(&sky->htcp_desc, ivox[0], ivox[1], ivox[2], 0); + ka = Ca_avg * rho_da * rct; + ks = Cs_avg * rho_da * rct; + kext = ka + ks; + + /* Update the boundaries of the radiative properties */ + ka_bounds[0] = MMIN(ka_bounds[0], ka); + ka_bounds[1] = MMAX(ka_bounds[1], ka); + ks_bounds[0] = MMIN(ks_bounds[0], ks); + ks_bounds[1] = MMAX(ks_bounds[1], ks); + kext_bounds[0] = MMIN(kext_bounds[0], kext); + kext_bounds[1] = MMAX(kext_bounds[1], kext); + } + } + } +} + +static void +cloud_vox_get_particle + (const size_t xyz[3], + float vox[], + const struct build_tree_context* ctx) +{ + double vox_low[3], vox_upp[3]; + double ka[2], ks[2], kext[2]; + ASSERT(xyz && vox && ctx); /* Compute the AABB of the SVX voxel */ - vox_low[0] = (double)xyz[0] * ctx->vxsz[0] + htcp_desc->lower[0]; - vox_low[1] = (double)xyz[1] * ctx->vxsz[1] + htcp_desc->lower[1]; - vox_low[2] = (double)xyz[2] * ctx->vxsz[2] + htcp_desc->lower[2]; + vox_low[0] = (double)xyz[0] * ctx->vxsz[0] + ctx->sky->htcp_desc.lower[0]; + vox_low[1] = (double)xyz[1] * ctx->vxsz[1] + ctx->sky->htcp_desc.lower[1]; + vox_low[2] = (double)xyz[2] * ctx->vxsz[2] + ctx->sky->htcp_desc.lower[2]; vox_upp[0] = vox_low[0] + ctx->vxsz[0]; vox_upp[1] = vox_low[1] + ctx->vxsz[1]; vox_upp[2] = vox_low[2] + ctx->vxsz[2]; + if(!ctx->sky->htcp_desc.irregular_z) { /* 1 LES voxel <=> 1 SVX voxel */ + compute_k_bounds_regular_z + (ctx->sky, ctx->iband, vox_low, vox_upp, ka, ks, kext); + } else { + ASSERT(xyz[2] < darray_split_size_get(&ctx->sky->svx2htcp_z)); + compute_k_bounds_irregular_z + (ctx->sky, ctx->iband, vox_low, vox_upp, ka, ks, kext); + } + + /* Ensure that the single precision bounds include their double precision + * version. */ + if(ka[0] != (float)ka[0]) ka[0] = nextafterf((float)ka[0],-FLT_MAX); + if(ka[1] != (float)ka[1]) ka[1] = nextafterf((float)ka[1], FLT_MAX); + if(ks[0] != (float)ks[0]) ks[0] = nextafterf((float)ks[0],-FLT_MAX); + if(ks[1] != (float)ks[1]) ks[1] = nextafterf((float)ks[1], FLT_MAX); + if(kext[0] != (float)kext[0]) kext[0] = nextafterf((float)kext[0],-FLT_MAX); + if(kext[1] != (float)kext[1]) kext[1] = nextafterf((float)kext[1], FLT_MAX); + + vox_set(vox, HTSKY_CPNT_PARTICLES, HTSKY_Ka, HTSKY_SVX_MIN, (float)ka[0]); + vox_set(vox, HTSKY_CPNT_PARTICLES, HTSKY_Ka, HTSKY_SVX_MAX, (float)ka[1]); + vox_set(vox, HTSKY_CPNT_PARTICLES, HTSKY_Ks, HTSKY_SVX_MIN, (float)ks[0]); + vox_set(vox, HTSKY_CPNT_PARTICLES, HTSKY_Ks, HTSKY_SVX_MAX, (float)ks[1]); + vox_set(vox, HTSKY_CPNT_PARTICLES, HTSKY_Kext, HTSKY_SVX_MIN, (float)kext[0]); + vox_set(vox, HTSKY_CPNT_PARTICLES, HTSKY_Kext, HTSKY_SVX_MAX, (float)kext[1]); +} + +static void +compute_xh2o_range_regular_z + (const struct htsky* sky, + const double vox_svx_low[3], /* Lower bound of the SVX voxel */ + const double vox_svx_upp[3], /* Upper bound of the SVX voxel */ + double x_h2o_range[2]) +{ + size_t ivox[3]; + size_t igrid_low[3], igrid_upp[3]; + double low[3], upp[3]; + double ipart; + ASSERT(!sky->htcp_desc.irregular_z && vox_svx_low && vox_svx_upp); + ASSERT(x_h2o_range); + /* Compute the *inclusive* bounds of the indices of cloud grid overlapped by * the SVX voxel in X and Y */ - low[0] = (vox_low[0]-htcp_desc->lower[0])/htcp_desc->vxsz_x; - low[1] = (vox_low[1]-htcp_desc->lower[1])/htcp_desc->vxsz_x; - upp[0] = (vox_upp[0]-htcp_desc->lower[0])/htcp_desc->vxsz_y; - upp[1] = (vox_upp[1]-htcp_desc->lower[1])/htcp_desc->vxsz_y; + low[0] = (vox_svx_low[0]-sky->htcp_desc.lower[0])/sky->htcp_desc.vxsz_x; + low[1] = (vox_svx_low[1]-sky->htcp_desc.lower[1])/sky->htcp_desc.vxsz_x; + low[2] = (vox_svx_low[2]-sky->htcp_desc.lower[2])/sky->htcp_desc.vxsz_z[0]; + upp[0] = (vox_svx_upp[0]-sky->htcp_desc.lower[0])/sky->htcp_desc.vxsz_y; + upp[1] = (vox_svx_upp[1]-sky->htcp_desc.lower[1])/sky->htcp_desc.vxsz_y; + upp[2] = (vox_svx_upp[2]-sky->htcp_desc.lower[2])/sky->htcp_desc.vxsz_z[0]; igrid_low[0] = (size_t)low[0]; igrid_low[1] = (size_t)low[1]; + igrid_low[2] = (size_t)low[2]; igrid_upp[0] = (size_t)upp[0] - (modf(upp[0], &ipart)==0); igrid_upp[1] = (size_t)upp[1] - (modf(upp[1], &ipart)==0); + igrid_upp[2] = (size_t)upp[2] - (modf(upp[2], &ipart)==0); ASSERT(igrid_low[0] <= igrid_upp[0]); ASSERT(igrid_low[1] <= igrid_upp[1]); - - if(!ctx->sky->htcp_desc.irregular_z) { /* 1 LES voxel <=> 1 SVX voxel */ - /* Compute the *inclusive* bounds of the indices of cloud grid overlapped by - * the SVX voxel along the Z axis */ - low[2] = (vox_low[2]-htcp_desc->lower[2])/htcp_desc->vxsz_z[0]; - upp[2] = (vox_upp[2]-htcp_desc->lower[2])/htcp_desc->vxsz_z[0]; - igrid_low[2] = (size_t)low[2]; - igrid_upp[2] = (size_t)upp[2] - (modf(upp[2], &ipart)==0); - ASSERT(igrid_low[2] <= igrid_upp[2]); - - /* Prepare the iteration over the grid voxels overlapped by the SVX voxel */ - ka_min = ks_min = kext_min = DBL_MAX; - ka_max = ks_max = kext_max =-DBL_MAX; - - /* Iterate over the grid voxels overlapped by the SVX voxel */ - FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { - FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { - FOR_EACH(ivox[2], igrid_low[2], igrid_upp[2]+1) { - /* Compute the radiative properties */ - rho_da = cloud_dry_air_density(htcp_desc, ivox); - rct = htcp_desc_RCT_at(htcp_desc, ivox[0], ivox[1], ivox[2], 0); - ka = Ca_avg * rho_da * rct; - ks = Cs_avg * rho_da * rct; - kext = ka + ks; - /* Update the boundaries of the radiative properties */ - ka_min = MMIN(ka_min, ka); - ka_max = MMAX(ka_max, ka); - ks_min = MMIN(ks_min, ks); - ks_max = MMAX(ks_max, ks); - kext_min = MMIN(kext_min, kext); - kext_max = MMAX(kext_max, kext); - #ifndef NDEBUG - { - double tmp_low[3], tmp_upp[3]; - htcp_desc_get_voxel_aabb - (&ctx->sky->htcp_desc, ivox[0], ivox[1], ivox[2], tmp_low, tmp_upp); - ASSERT(aabb_intersect(tmp_low, tmp_upp, vox_low, vox_upp)); - } - #endif + ASSERT(igrid_low[2] <= igrid_upp[2]); + + /* Prepare the iteration overt the grid voxels overlapped by the SVX voxel */ + x_h2o_range[0] = DBL_MAX; + x_h2o_range[1] =-DBL_MAX; + + FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { + FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { + FOR_EACH(ivox[2], igrid_low[2], igrid_upp[2]+1) { + double x_h2o; + + /* Compute the xH2O for the current LES voxel */ + x_h2o = cloud_water_vapor_molar_fraction(&sky->htcp_desc, ivox); + + /* Update the xH2O range of the SVX voxel */ + x_h2o_range[0] = MMIN(x_h2o, x_h2o_range[0]); + x_h2o_range[1] = MMAX(x_h2o, x_h2o_range[1]); + #ifndef NDEBUG + { + double tmp_low[3], tmp_upp[3]; + htcp_desc_get_voxel_aabb + (&sky->htcp_desc, ivox[0], ivox[1], ivox[2], tmp_low, tmp_upp); + ASSERT(aabb_intersect(tmp_low, tmp_upp, vox_svx_low, vox_svx_upp)); } + #endif } } - } else { - double pos_z; - size_t ilut_low, ilut_upp; - size_t ilut; - size_t ivox_z_prev = SIZE_MAX; - - /* Compute the *inclusive* bounds of the indices of the LUT cells - * overlapped by the SVX voxel */ - ilut_low = (size_t)((vox_low[2]-htcp_desc->lower[2])/ctx->sky->lut_cell_sz); - ilut_upp = (size_t)((vox_upp[2]-htcp_desc->lower[2])/ctx->sky->lut_cell_sz); - ASSERT(ilut_low <= ilut_upp); - - /* Prepare the iteration over the LES voxels overlapped by the SVX voxel */ - ka_min = ks_min = kext_min = DBL_MAX; - ka_max = ks_max = kext_max =-DBL_MAX; - ivox_z_prev = SIZE_MAX; - pos_z = vox_low[2]; - ASSERT(pos_z >= (double)ilut_low * ctx->sky->lut_cell_sz); - ASSERT(pos_z <= (double)ilut_upp * ctx->sky->lut_cell_sz); - - /* Iterate over the LUT cells overlapped by the voxel */ - FOR_EACH(ilut, ilut_low, ilut_upp+1) { - const struct split* split = darray_split_cdata_get(&ctx->sky->svx2htcp_z)+ilut; - ASSERT(ilut < darray_split_size_get(&ctx->sky->svx2htcp_z)); - - ivox[2] = pos_z <= split->height ? split->index : split->index + 1; - if(ivox[2] >= ctx->sky->htcp_desc.spatial_definition[2] - && eq_eps(pos_z, split->height, 1.e-6)) { /* Handle numerical inaccuracy */ - ivox[2] = split->index; - } + } +} - /* Compute the upper bound of the *next* LUT cell clamped to the voxel - * upper bound. Note that the upper bound of the current LUT cell is - * the lower bound of the next cell, i.e. (ilut+1)*lut_cell_sz. The - * upper bound of the next cell is thus the lower bound of the cell - * following the next cell, i.e. (ilut+2)*lut_cell_sz */ - pos_z = MMIN((double)(ilut+2)*ctx->sky->lut_cell_sz, vox_upp[2]); - - /* Does the LUT cell overlap an already handled LES voxel? */ - if(ivox[2] == ivox_z_prev) continue; - ivox_z_prev = ivox[2]; - - FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { - FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { - - /* Compute the radiative properties */ - rho_da = cloud_dry_air_density(htcp_desc, ivox); - rct = htcp_desc_RCT_at(htcp_desc, ivox[0], ivox[1], ivox[2], 0); - ka = Ca_avg * rho_da * rct; - ks = Cs_avg * rho_da * rct; - kext = ka + ks; - - /* Update the boundaries of the radiative properties */ - ka_min = MMIN(ka_min, ka); - ka_max = MMAX(ka_max, ka); - ks_min = MMIN(ks_min, ks); - ks_max = MMAX(ks_max, ks); - kext_min = MMIN(kext_min, kext); - kext_max = MMAX(kext_max, kext); - } +static void +compute_xh2o_range_irregular_z + (const struct htsky* sky, + const double vox_svx_low[3], /* Lower bound of the SVX voxel */ + const double vox_svx_upp[3], /* Upper bound of the SVX voxel */ + double x_h2o_range[2]) +{ + size_t ivox[3]; + size_t igrid_low[2], igrid_upp[2]; + size_t ilut_low, ilut_upp; + size_t ilut; + size_t ivox_z_prev; + double low[2], upp[2]; + double pos_z; + double ipart; + ASSERT(sky->htcp_desc.irregular_z && vox_svx_low && vox_svx_upp); + ASSERT(x_h2o_range); + + /* Compute the *inclusive* bounds of the indices of cloud grid overlapped by + * the SVX voxel in X and Y */ + low[0] = (vox_svx_low[0]-sky->htcp_desc.lower[0])/sky->htcp_desc.vxsz_x; + low[1] = (vox_svx_low[1]-sky->htcp_desc.lower[1])/sky->htcp_desc.vxsz_x; + upp[0] = (vox_svx_upp[0]-sky->htcp_desc.lower[0])/sky->htcp_desc.vxsz_y; + upp[1] = (vox_svx_upp[1]-sky->htcp_desc.lower[1])/sky->htcp_desc.vxsz_y; + igrid_low[0] = (size_t)low[0]; + igrid_low[1] = (size_t)low[1]; + igrid_upp[0] = (size_t)upp[0] - (modf(upp[0], &ipart)==0); + igrid_upp[1] = (size_t)upp[1] - (modf(upp[1], &ipart)==0); + ASSERT(igrid_low[0] <= igrid_upp[0]); + ASSERT(igrid_low[1] <= igrid_upp[1]); + + /* Compute the *inclusive* bounds of the indices of the LUT cells + * overlapped by the SVX voxel */ + ilut_low = (size_t)((vox_svx_low[2]-sky->htcp_desc.lower[2])/sky->lut_cell_sz); + ilut_upp = (size_t)((vox_svx_upp[2]-sky->htcp_desc.lower[2])/sky->lut_cell_sz); + ASSERT(ilut_low <= ilut_upp); + + /* Prepare the iteration over the LES voxels overlapped by the SVX voxel */ + x_h2o_range[0] = DBL_MAX; + x_h2o_range[1] =-DBL_MAX; + ivox_z_prev = SIZE_MAX; + pos_z = vox_svx_low[2]; + ASSERT(pos_z >= (double)ilut_low * sky->lut_cell_sz); + ASSERT(pos_z <= (double)ilut_upp * sky->lut_cell_sz); + + /* Iterate over the LUT cells overlapped by the voxel */ + FOR_EACH(ilut, ilut_low, ilut_upp+1) { + const struct split* split = darray_split_cdata_get(&sky->svx2htcp_z) + ilut; + ASSERT(ilut < darray_split_size_get(&sky->svx2htcp_z)); + + ivox[2] = pos_z <= split->height ? split->index : split->index + 1; + if(ivox[2] >= sky->htcp_desc.spatial_definition[2] + && eq_eps(pos_z, split->height, 1.e-6)) { /* Handle numerical inaccuracy */ + ivox[2] = split->index; + } + + /* Compute the upper bound of the *next* LUT cell clamped to the voxel + * upper bound. Note that the upper bound of the current LUT cell is + * the lower bound of the next cell, i.e. (ilut+1)*lut_cell_sz. The + * upper bound of the next cell is thus the lower bound of the cell + * following the next cell, i.e. (ilut+2)*lut_cell_sz */ + pos_z = MMIN((double)(ilut+2)*sky->lut_cell_sz, vox_svx_upp[2]); + + /* Does the LUT voxel overlap an already handled LES voxel? */ + if(ivox[2] == ivox_z_prev) continue; + ivox_z_prev = ivox[2]; + + FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { + FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { + double x_h2o; + + /* Compute the xH2O for the current LES voxel */ + x_h2o = cloud_water_vapor_molar_fraction(&sky->htcp_desc, ivox); + + /* Update the xH2O range of the SVX voxel */ + x_h2o_range[0] = MMIN(x_h2o, x_h2o_range[0]); + x_h2o_range[1] = MMAX(x_h2o, x_h2o_range[1]); } } } - - /* Ensure that the single precision bounds include their double precision - * version. */ - if(ka_min != (float)ka_min) ka_min = nextafterf((float)ka_min,-FLT_MAX); - if(ka_max != (float)ka_max) ka_max = nextafterf((float)ka_max, FLT_MAX); - if(ks_min != (float)ks_min) ks_min = nextafterf((float)ks_min,-FLT_MAX); - if(ks_max != (float)ks_max) ks_max = nextafterf((float)ks_max, FLT_MAX); - if(kext_min != (float)kext_min) kext_min = nextafterf((float)kext_min,-FLT_MAX); - if(kext_max != (float)kext_max) kext_max = nextafterf((float)kext_max, FLT_MAX); - - vox_set(vox, HTSKY_CPNT_PARTICLES, HTSKY_Ka, HTSKY_SVX_MIN, (float)ka_min); - vox_set(vox, HTSKY_CPNT_PARTICLES, HTSKY_Ka, HTSKY_SVX_MAX, (float)ka_max); - vox_set(vox, HTSKY_CPNT_PARTICLES, HTSKY_Ks, HTSKY_SVX_MIN, (float)ks_min); - vox_set(vox, HTSKY_CPNT_PARTICLES, HTSKY_Ks, HTSKY_SVX_MAX, (float)ks_max); - vox_set(vox, HTSKY_CPNT_PARTICLES, HTSKY_Kext, HTSKY_SVX_MIN, (float)kext_min); - vox_set(vox, HTSKY_CPNT_PARTICLES, HTSKY_Kext, HTSKY_SVX_MAX, (float)kext_max); } static void @@ -235,20 +489,13 @@ cloud_vox_get_gas const struct build_tree_context* ctx) { const struct htcp_desc* htcp_desc; - size_t ivox[3]; - size_t igrid_low[3], igrid_upp[3]; struct htgop_layer layer; struct htgop_layer_sw_spectral_interval band; size_t ilayer; size_t layer_range[2]; double x_h2o_range[2]; - double low[3], upp[3]; double vox_low[3], vox_upp[3]; /* Voxel AABB */ - double ka_min, ka_max; - double ks_min, ks_max; - double kext_min, kext_max; - double x_h2o; - double ipart; + double ka[2], ks[2], kext[2]; ASSERT(xyz && vox && ctx); htcp_desc = &ctx->sky->htcp_desc; @@ -261,117 +508,20 @@ cloud_vox_get_gas vox_upp[1] = vox_low[1] + ctx->vxsz[1]; vox_upp[2] = vox_low[2] + ctx->vxsz[2]; - /* Compute the *inclusive* bounds of the indices of cloud grid overlapped by - * the SVX voxel in X and Y */ - low[0] = (vox_low[0]-htcp_desc->lower[0])/htcp_desc->vxsz_x; - low[1] = (vox_low[1]-htcp_desc->lower[1])/htcp_desc->vxsz_x; - upp[0] = (vox_upp[0]-htcp_desc->lower[0])/htcp_desc->vxsz_y; - upp[1] = (vox_upp[1]-htcp_desc->lower[1])/htcp_desc->vxsz_y; - igrid_low[0] = (size_t)low[0]; - igrid_low[1] = (size_t)low[1]; - igrid_upp[0] = (size_t)upp[0] - (modf(upp[0], &ipart)==0); - igrid_upp[1] = (size_t)upp[1] - (modf(upp[1], &ipart)==0); - ASSERT(igrid_low[0] <= igrid_upp[0]); - ASSERT(igrid_low[1] <= igrid_upp[1]); - /* Define the xH2O range from the LES data */ if(!ctx->sky->htcp_desc.irregular_z) { /* 1 LES voxel <=> 1 SVX voxel */ - /* Compute the *inclusive* bounds of the indices of cloud grid overlapped by - * the SVX voxel in Z */ - low[2] = (vox_low[2]-htcp_desc->lower[2])/htcp_desc->vxsz_z[0]; - upp[2] = (vox_upp[2]-htcp_desc->lower[2])/htcp_desc->vxsz_z[0]; - igrid_low[2] = (size_t)low[2]; - igrid_upp[2] = (size_t)upp[2] - (modf(upp[2], &ipart)==0); - ASSERT(igrid_low[2] <= igrid_upp[2]); - - /* Prepare the iteration overt the grid voxels overlapped by the SVX voxel */ - x_h2o_range[0] = DBL_MAX; - x_h2o_range[1] =-DBL_MAX; - - FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { - FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { - FOR_EACH(ivox[2], igrid_low[2], igrid_upp[2]+1) { - - /* Compute the xH2O for the current LES voxel */ - x_h2o = cloud_water_vapor_molar_fraction(htcp_desc, ivox); - - /* Update the xH2O range of the SVX voxel */ - x_h2o_range[0] = MMIN(x_h2o, x_h2o_range[0]); - x_h2o_range[1] = MMAX(x_h2o, x_h2o_range[1]); - #ifndef NDEBUG - { - double tmp_low[3], tmp_upp[3]; - htcp_desc_get_voxel_aabb - (&ctx->sky->htcp_desc, ivox[0], ivox[1], ivox[2], tmp_low, tmp_upp); - ASSERT(aabb_intersect(tmp_low, tmp_upp, vox_low, vox_upp)); - } - #endif - } - } - } + compute_xh2o_range_regular_z(ctx->sky, vox_low, vox_upp, x_h2o_range); } else { /* A SVX voxel can be overlapped by 2 LES voxels */ - double pos_z; - size_t ilut_low, ilut_upp; - size_t ilut; - size_t ivox_z_prev; ASSERT(xyz[2] < darray_split_size_get(&ctx->sky->svx2htcp_z)); - - /* Compute the *inclusive* bounds of the indices of the LUT cells - * overlapped by the SVX voxel */ - ilut_low = (size_t)((vox_low[2] - htcp_desc->lower[2]) / ctx->sky->lut_cell_sz); - ilut_upp = (size_t)((vox_upp[2] - htcp_desc->lower[2]) / ctx->sky->lut_cell_sz); - ASSERT(ilut_low <= ilut_upp); - - /* Prepare the iteration over the LES voxels overlapped by the SVX voxel */ - x_h2o_range[0] = DBL_MAX; - x_h2o_range[1] =-DBL_MAX; - ivox_z_prev = SIZE_MAX; - pos_z = vox_low[2]; - ASSERT(pos_z >= (double)ilut_low * ctx->sky->lut_cell_sz); - ASSERT(pos_z <= (double)ilut_upp * ctx->sky->lut_cell_sz); - - /* Iterate over the LUT cells overlapped by the voxel */ - FOR_EACH(ilut, ilut_low, ilut_upp+1) { - const struct split* split = darray_split_cdata_get(&ctx->sky->svx2htcp_z)+ilut; - ASSERT(ilut < darray_split_size_get(&ctx->sky->svx2htcp_z)); - - ivox[2] = pos_z <= split->height ? split->index : split->index + 1; - if(ivox[2] >= ctx->sky->htcp_desc.spatial_definition[2] - && eq_eps(pos_z, split->height, 1.e-6)) { /* Handle numerical inaccuracy */ - ivox[2] = split->index; - } - - /* Compute the upper bound of the *next* LUT cell clamped to the voxel - * upper bound. Note that the upper bound of the current LUT cell is - * the lower bound of the next cell, i.e. (ilut+1)*lut_cell_sz. The - * upper bound of the next cell is thus the lower bound of the cell - * following the next cell, i.e. (ilut+2)*lut_cell_sz */ - pos_z = MMIN((double)(ilut+2)*ctx->sky->lut_cell_sz, vox_upp[2]); - - /* Does the LUT voxel overlap an already handled LES voxel? */ - if(ivox[2] == ivox_z_prev) continue; - ivox_z_prev = ivox[2]; - - FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { - FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { - - /* Compute the xH2O for the current LES voxel */ - x_h2o = cloud_water_vapor_molar_fraction(&ctx->sky->htcp_desc, ivox); - - /* Update the xH2O range of the SVX voxel */ - x_h2o_range[0] = MMIN(x_h2o, x_h2o_range[0]); - x_h2o_range[1] = MMAX(x_h2o, x_h2o_range[1]); - } - } - } + compute_xh2o_range_irregular_z(ctx->sky, vox_low, vox_upp, x_h2o_range); } /* Define the atmospheric layers overlapped by the SVX voxel */ HTGOP(position_to_layer_id(ctx->sky->htgop, vox_low[2], &layer_range[0])); HTGOP(position_to_layer_id(ctx->sky->htgop, vox_upp[2], &layer_range[1])); - ka_min = ks_min = kext_min = DBL_MAX; - ka_max = ks_max = kext_max =-DBL_MAX; + ka[0] = ks[0] = kext[0] = DBL_MAX; + ka[1] = ks[1] = kext[1] =-DBL_MAX; /* For each atmospheric layer that overlaps the SVX voxel ... */ FOR_EACH(ilayer, layer_range[0], layer_range[1]+1) { @@ -387,33 +537,33 @@ cloud_vox_get_gas /* ... and compute the radiative properties and upd their bounds */ HTGOP(layer_sw_spectral_interval_quadpoints_get_ka_bounds (&band, ctx->quadrature_range, x_h2o_range, k)); - ka_min = MMIN(ka_min, k[0]); - ka_max = MMAX(ka_max, k[1]); + ka[0] = MMIN(ka[0], k[0]); + ka[1] = MMAX(ka[1], k[1]); HTGOP(layer_sw_spectral_interval_quadpoints_get_ks_bounds (&band, ctx->quadrature_range, x_h2o_range, k)); - ks_min = MMIN(ks_min, k[0]); - ks_max = MMAX(ks_max, k[1]); + ks[0] = MMIN(ks[0], k[0]); + ks[1] = MMAX(ks[1], k[1]); HTGOP(layer_sw_spectral_interval_quadpoints_get_kext_bounds (&band, ctx->quadrature_range, x_h2o_range, k)); - kext_min = MMIN(kext_min, k[0]); - kext_max = MMAX(kext_max, k[1]); + kext[0] = MMIN(kext[0], k[0]); + kext[1] = MMAX(kext[1], k[1]); } /* Ensure that the single precision bounds include their double precision * version. */ - if(ka_min != (float)ka_min) ka_min = nextafterf((float)ka_min,-FLT_MAX); - if(ka_max != (float)ka_max) ka_max = nextafterf((float)ka_max, FLT_MAX); - if(ks_min != (float)ks_min) ks_min = nextafterf((float)ks_min,-FLT_MAX); - if(ks_max != (float)ks_max) ks_max = nextafterf((float)ks_max, FLT_MAX); - if(kext_min != (float)kext_min) kext_min = nextafterf((float)kext_min,-FLT_MAX); - if(kext_max != (float)kext_max) kext_max = nextafterf((float)kext_max, FLT_MAX); - - vox_set(vox, HTSKY_CPNT_GAS, HTSKY_Ka, HTSKY_SVX_MIN, (float)ka_min); - vox_set(vox, HTSKY_CPNT_GAS, HTSKY_Ka, HTSKY_SVX_MAX, (float)ka_max); - vox_set(vox, HTSKY_CPNT_GAS, HTSKY_Ks, HTSKY_SVX_MIN, (float)ks_min); - vox_set(vox, HTSKY_CPNT_GAS, HTSKY_Ks, HTSKY_SVX_MAX, (float)ks_max); - vox_set(vox, HTSKY_CPNT_GAS, HTSKY_Kext, HTSKY_SVX_MIN, (float)kext_min); - vox_set(vox, HTSKY_CPNT_GAS, HTSKY_Kext, HTSKY_SVX_MAX, (float)kext_max); + if(ka[0] != (float)ka[0]) ka[0] = nextafterf((float)ka[0],-FLT_MAX); + if(ka[1] != (float)ka[1]) ka[1] = nextafterf((float)ka[1], FLT_MAX); + if(ks[0] != (float)ks[0]) ks[0] = nextafterf((float)ks[0],-FLT_MAX); + if(ks[1] != (float)ks[1]) ks[1] = nextafterf((float)ks[1], FLT_MAX); + if(kext[0] != (float)kext[0]) kext[0] = nextafterf((float)kext[0],-FLT_MAX); + if(kext[1] != (float)kext[1]) kext[1] = nextafterf((float)kext[1], FLT_MAX); + + vox_set(vox, HTSKY_CPNT_GAS, HTSKY_Ka, HTSKY_SVX_MIN, (float)ka[0]); + vox_set(vox, HTSKY_CPNT_GAS, HTSKY_Ka, HTSKY_SVX_MAX, (float)ka[1]); + vox_set(vox, HTSKY_CPNT_GAS, HTSKY_Ks, HTSKY_SVX_MIN, (float)ks[0]); + vox_set(vox, HTSKY_CPNT_GAS, HTSKY_Ks, HTSKY_SVX_MAX, (float)ks[1]); + vox_set(vox, HTSKY_CPNT_GAS, HTSKY_Kext, HTSKY_SVX_MIN, (float)kext[0]); + vox_set(vox, HTSKY_CPNT_GAS, HTSKY_Kext, HTSKY_SVX_MAX, (float)kext[1]); } static void @@ -495,45 +645,9 @@ cloud_setup /* Regular voxel size along the Z dimension: compute its upper boundary as * the others dimensions */ upp[2] = low[2] + (double)raw_def[2] * sky->htcp_desc.vxsz_z[0]; - - /* TODO move the following block in a separate function */ } else { /* Irregular voxel size along Z */ - double len_z; - size_t nsplits; - size_t iz, iz2;; - - /* Find the min voxel size along Z and compute the length of a Z column */ - len_z = 0; - sky->lut_cell_sz = DBL_MAX; - FOR_EACH(iz, 0, sky->htcp_desc.spatial_definition[2]) { - len_z += sky->htcp_desc.vxsz_z[iz]; - sky->lut_cell_sz = MMIN(sky->lut_cell_sz, sky->htcp_desc.vxsz_z[iz]); - } - /* Allocate the svx2htcp LUT. This LUT is a regular table whose absolute - * size is greater or equal to a Z column in the htcp file. The size of its - * cells is the minimal voxel size in Z of the htcp file */ - nsplits = (size_t)ceil(len_z / sky->lut_cell_sz); - res = darray_split_resize(&sky->svx2htcp_z, nsplits); - if(res != RES_OK) { - log_err(sky, - "could not allocate the table mapping regular to irregular Z.\n"); - goto error; - } - /* Setup the svx2htcp LUT. Each LUT entry stores the index of the current Z - * voxel in the htcp file that overlaps the entry lower bound as well as the - * lower bound in Z of the next htcp voxel. */ - iz2 = 0; - upp[2] = low[2] + sky->htcp_desc.vxsz_z[iz2]; - FOR_EACH(iz, 0, nsplits) { - const double upp_z = (double)(iz + 1) * sky->lut_cell_sz + low[2]; - darray_split_data_get(&sky->svx2htcp_z)[iz].index = iz2; - darray_split_data_get(&sky->svx2htcp_z)[iz].height = upp[2]; - if(upp_z >= upp[2] && iz + 1 < nsplits) { - ASSERT(iz2 + 1 < sky->htcp_desc.spatial_definition[2]); - upp[2] += sky->htcp_desc.vxsz_z[++iz2]; - } - } - ASSERT(eq_eps(upp[2] - low[2], len_z, 1.e-6)); + res = setup_svx2htcp_z_lut(sky, &upp[2]); + if(res != RES_OK) goto error; } /* Setup the build context */ @@ -554,7 +668,7 @@ cloud_setup goto error; } - /* Compute how many octree are going to be built */ + /* Compute how many octrees are going to be built */ FOR_EACH(i, 0, nbands) { struct htgop_spectral_interval band; const size_t iband = i + sky->sw_bands_range[0];