htrdr

Solving radiative transfer in heterogeneous media
git clone git://git.meso-star.fr/htrdr.git
Log | Files | Refs | README | LICENSE

commit 9a9bd865004078cdc6af5e5b6a3b8002e2ae3abd
parent 6eb115d22ac7e26fa19d55d7787c40efde38d439
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon,  1 Apr 2019 15:40:26 +0200

Add the -V option that fixes the finest octree definition

Diffstat:
Mdoc/htrdr.1.txt.in | 7++++++-
Msrc/htrdr.c | 3+++
Msrc/htrdr.h | 1+
Msrc/htrdr_args.c | 38+++++++++++++++++++++++++++++++++++++-
Msrc/htrdr_args.h.in | 3+++
Msrc/htrdr_sky.c | 344++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
6 files changed, 277 insertions(+), 119 deletions(-)

diff --git a/doc/htrdr.1.txt.in b/doc/htrdr.1.txt.in @@ -143,7 +143,7 @@ OPTIONS *-i* <__image-parameter__:...>:: Define the image to render. Available image parameters are: - **def**=**_width_**,**_height_**;; + **def**=**_width_**x**_height_**;; Definition of the image. By default the image definition is @HTRDR_ARGS_DEFAULT_IMG_WIDTH@x@HTRDR_ARGS_DEFAULT_IMG_HEIGHT@. @@ -175,6 +175,11 @@ OPTIONS Hint on the number of threads to use. By default use as many threads as CPU cores. +*-V* **_x_**,**_y_**,**_z_**:: + Define the maximum definition of the acceleration data structure that + partitions the cloud properties. By default the finest definition is the + definition of the submitted *htcp*(5) file. + *-v*:: Make *htrdr* verbose. diff --git a/src/htrdr.c b/src/htrdr.c @@ -460,6 +460,9 @@ htrdr_init htrdr->spp = args->image.spp; htrdr->width = args->image.definition[0]; htrdr->height = args->image.definition[1]; + htrdr->grid_max_definition[0] = args->grid_max_definition[0]; + htrdr->grid_max_definition[1] = args->grid_max_definition[1]; + htrdr->grid_max_definition[2] = args->grid_max_definition[2]; res = mem_init_regular_allocator(&htrdr->svx_allocator); if(res != RES_OK) goto error; diff --git a/src/htrdr.h b/src/htrdr.h @@ -59,6 +59,7 @@ struct htrdr { FILE* output; struct str output_name; + unsigned grid_max_definition[3]; /* Max definition of the acceleration grids */ unsigned nthreads; /* #threads of the process */ int dump_vtk; /* Dump octree VTK */ int cache_grids; /* Use/Precompute grid caches */ diff --git a/src/htrdr_args.c b/src/htrdr_args.c @@ -79,6 +79,9 @@ print_help(const char* cmd) " -t THREADS hint on the number of threads to use. By default use as\n" " many threads as CPU cores.\n"); printf( +" -V X,Y,Z maximum definition of the cloud acceleration grids along\n" +" the 3 axis. By default use the definition of the clouds\n"); + printf( " -v make the program verbose.\n"); printf( " --version display version information and exit.\n"); @@ -319,6 +322,38 @@ error: goto exit; } +static res_T +parse_grid_definition(struct htrdr_args* args, const char* str) +{ + unsigned def[3]; + size_t len; + res_T res = RES_OK; + ASSERT(args && str); + + res = cstr_to_list_uint(str, ',', def, &len, 3); + if(res == RES_OK && len != 3) res = RES_BAD_ARG; + if(res != RES_OK) { + fprintf(stderr, "Invalid grid definition `%s'.\n", str); + goto error; + } + + if(!def[0] || !def[1] || !def[2]) { + fprintf(stderr, + "Invalid null grid definition {%u, %u, %u}.\n", SPLIT3(def)); + res = RES_BAD_ARG; + goto error; + } + + args->grid_max_definition[0] = def[0]; + args->grid_max_definition[1] = def[1]; + args->grid_max_definition[2] = def[2]; + +exit: + return res; +error: + goto exit; +} + /******************************************************************************* * Local functions ******************************************************************************/ @@ -343,7 +378,7 @@ htrdr_args_init(struct htrdr_args* args, int argc, char** argv) } } - while((opt = getopt(argc, argv, "a:C:c:D:de:fGg:hi:m:o:RrT:t:v")) != -1) { + while((opt = getopt(argc, argv, "a:C:c:D:de:fGg:hi:m:o:RrT:t:V:v")) != -1) { switch(opt) { case 'a': args->filename_gas = optarg; break; case 'C': @@ -383,6 +418,7 @@ htrdr_args_init(struct htrdr_args* args, int argc, char** argv) res = cstr_to_uint(optarg, &args->nthreads); if(res == RES_OK && !args->nthreads) res = RES_BAD_ARG; break; + case 'V': res = parse_grid_definition(args, optarg); break; case 'v': args->verbose = 1; break; default: res = RES_BAD_ARG; break; } diff --git a/src/htrdr_args.h.in b/src/htrdr_args.h.in @@ -16,6 +16,7 @@ #ifndef HTRDR_ARGS_H #define HTRDR_ARGS_H +#include <limits.h> #include <rsys/rsys.h> struct htrdr_args { @@ -48,6 +49,7 @@ struct htrdr_args { double sun_elevation; /* In degrees */ double optical_thickness; /* Threshold used during octree building */ double ground_reflectivity; /* Reflectivity of the ground */ + unsigned grid_max_definition[3]; /* Maximum definition of the grid */ unsigned nthreads; /* Hint on the number of threads to use */ int force_overwriting; @@ -83,6 +85,7 @@ struct htrdr_args { 90, /* Sun elevation */ \ @HTRDR_ARGS_DEFAULT_OPTICAL_THICKNESS_THRESHOLD@, /* Optical thickness */ \ @HTRDR_ARGS_DEFAULT_GROUND_REFLECTIVITY@, /* Ground reflectivity */ \ + {UINT_MAX, UINT_MAX, UINT_MAX}, /* Maximum definition of the grid */ \ (unsigned)~0, /* #threads */ \ 0, /* Force overwriting */ \ 0, /* dump VTK */ \ diff --git a/src/htrdr_sky.c b/src/htrdr_sky.c @@ -207,6 +207,21 @@ struct htrdr_sky { /******************************************************************************* * Helper function ******************************************************************************/ +static INLINE int +aabb_intersect + (const double aabb0_low[3], + const double aabb0_upp[3], + const double aabb1_low[3], + const double aabb1_upp[3]) +{ + ASSERT(aabb0_low[0] < aabb0_upp[0] && aabb1_low[0] < aabb1_upp[0]); + ASSERT(aabb0_low[1] < aabb0_upp[1] && aabb1_low[1] < aabb1_upp[1]); + ASSERT(aabb0_low[2] < aabb0_upp[2] && aabb1_low[2] < aabb1_upp[2]); + return !(aabb0_upp[0] < aabb1_low[0]) && !(aabb0_low[0] > aabb1_upp[0]) + && !(aabb0_upp[1] < aabb1_low[1]) && !(aabb0_low[1] > aabb1_upp[1]) + && !(aabb0_upp[2] < aabb1_low[2]) && !(aabb0_low[2] > aabb1_upp[2]); +} + static void log_svx_memory_usage(struct htrdr* htrdr) { @@ -222,7 +237,7 @@ log_svx_memory_usage(struct htrdr* htrdr) memsz = MEM_ALLOCATED_SIZE(&htrdr->svx_allocator); if((ngigas = memsz / GIGA_BYTE) != 0) { - len = (size_t)snprintf(dst, available_space, + len = (size_t)snprintf(dst, available_space, "%lu GB ", (unsigned long)ngigas); CHK(len < available_space); dst += len; @@ -230,7 +245,7 @@ log_svx_memory_usage(struct htrdr* htrdr) memsz -= ngigas * GIGA_BYTE; } if((nmegas = memsz / MEGA_BYTE) != 0) { - len = (size_t)snprintf(dst, available_space, + len = (size_t)snprintf(dst, available_space, "%lu MB ", (unsigned long)nmegas); CHK(len < available_space); dst += len; @@ -245,7 +260,7 @@ log_svx_memory_usage(struct htrdr* htrdr) memsz -= nkilos * KILO_BYTE; } if(memsz) { - len = (size_t)snprintf(dst, available_space, + len = (size_t)snprintf(dst, available_space, "%lu Byte%s", (unsigned long)memsz, memsz > 1 ? "s" : ""); CHK(len < available_space); } @@ -559,6 +574,11 @@ cloud_vox_get_particle float vox[], const struct build_tree_context* ctx) { + 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]; + double low[3], upp[3]; double rct; double ka, ks, kext; double ka_min, ka_max; @@ -567,42 +587,89 @@ cloud_vox_get_particle double rho_da; /* Dry air density */ double Ca_avg; double Cs_avg; + double ipart; size_t i; ASSERT(xyz && vox && ctx); i = ctx->iband - ctx->sky->sw_bands_range[0]; + htcp_desc = &ctx->sky->htcp_desc; /* 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; + /* 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_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]; + + /* 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]); + if(!ctx->sky->htcp_desc.irregular_z) { /* 1 LES voxel <=> 1 SVX voxel */ - rho_da = cloud_dry_air_density(&ctx->sky->htcp_desc, xyz); - rct = htcp_desc_RCT_at(&ctx->sky->htcp_desc, xyz[0], xyz[1], xyz[2], 0); - ka_min = ka_max = ka = Ca_avg * rho_da * rct; - ks_min = ks_max = ks = Cs_avg * rho_da * rct; - kext_min = kext_max = kext = ka + ks; - } else { /* A SVX voxel can be overlapped by 2 LES voxels */ - double vox_low[3], vox_upp[3]; + /* 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 + } + } + } + } else { double pos_z; size_t ivox_z_prev; size_t ilut_low, ilut_upp; size_t ilut; - /* Compute the AABB of the SVX voxel */ - 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]; - /* Compute the *inclusive* bounds of the indices of the LUT cells * overlapped by the SVX voxel */ - ilut_low = (size_t) - ((vox_low[2] - ctx->sky->htcp_desc.lower[2]) / ctx->sky->lut_cell_sz); - ilut_upp = (size_t) - ((vox_upp[2] - ctx->sky->htcp_desc.lower[2]) / ctx->sky->lut_cell_sz); + 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 */ @@ -614,44 +681,45 @@ cloud_vox_get_particle 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) { - size_t ivox[3]; - const struct split* split = darray_split_cdata_get(&ctx->sky->svx2htcp_z)+ilut; - ASSERT(ilut < darray_split_size_get(&ctx->sky->svx2htcp_z)); - - ivox[0] = xyz[0]; - ivox[1] = xyz[1]; - 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; + FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { + FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { + 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]; + + /* 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); + } } - - /* 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]; - - /* Compute the radiative properties */ - rho_da = cloud_dry_air_density(&ctx->sky->htcp_desc, ivox); - rct = htcp_desc_RCT_at(&ctx->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_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); } } @@ -678,39 +746,81 @@ cloud_vox_get_gas float vox[], 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; ASSERT(xyz && vox && ctx); + htcp_desc = &ctx->sky->htcp_desc; + /* Compute the AABB of the SVX voxel */ - 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_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_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]; + /* 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 */ - double x_h2o; - x_h2o = cloud_water_vapor_molar_fraction(&ctx->sky->htcp_desc, xyz); - x_h2o_range[0] = x_h2o_range[1] = x_h2o; -#ifndef NDEBUG - { - double low[3], upp[3]; - htcp_desc_get_voxel_aabb - (&ctx->sky->htcp_desc, xyz[0], xyz[1], xyz[2], low, upp); - ASSERT(d3_eq_eps(low, vox_low, 1.e-6)); - ASSERT(d3_eq_eps(upp, vox_upp, 1.e-6)); + /* 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 + } + } } -#endif } else { /* A SVX voxel can be overlapped by 2 LES voxels */ double pos_z; size_t ilut_low, ilut_upp; @@ -720,10 +830,8 @@ cloud_vox_get_gas /* Compute the *inclusive* bounds of the indices of the LUT cells * overlapped by the SVX voxel */ - ilut_low = (size_t) - ((vox_low[2] - ctx->sky->htcp_desc.lower[2]) / ctx->sky->lut_cell_sz); - ilut_upp = (size_t) - ((vox_upp[2] - ctx->sky->htcp_desc.lower[2]) / ctx->sky->lut_cell_sz); + 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 */ @@ -735,37 +843,37 @@ cloud_vox_get_gas 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; - size_t ivox[3]; - double x_h2o; - ASSERT(ilut < darray_split_size_get(&ctx->sky->svx2htcp_z)); - - ivox[0] = xyz[0]; - ivox[1] = xyz[1]; - 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; + FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { + FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { + 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]; + + /* 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 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]; - - /* 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]); } } @@ -1129,6 +1237,7 @@ setup_clouds const int force_cache_update) { struct darray_specdata specdata; + const size_t* raw_def; size_t nvoxs[3]; double vxsz[3]; double low[3]; @@ -1152,21 +1261,22 @@ setup_clouds SPLIT3(sky->htcp_desc.lower), SPLIT3(sky->htcp_desc.upper)); /* Define the number of voxels */ - nvoxs[0] = sky->htcp_desc.spatial_definition[0]; - nvoxs[1] = sky->htcp_desc.spatial_definition[1]; - nvoxs[2] = sky->htcp_desc.spatial_definition[2]; + raw_def = sky->htcp_desc.spatial_definition; + nvoxs[0] = MMIN(raw_def[0], sky->htrdr->grid_max_definition[0]); + nvoxs[1] = MMIN(raw_def[1], sky->htrdr->grid_max_definition[1]); + nvoxs[2] = MMIN(raw_def[2], sky->htrdr->grid_max_definition[2]); /* Define the octree AABB excepted for the Z dimension */ low[0] = sky->htcp_desc.lower[0]; low[1] = sky->htcp_desc.lower[1]; low[2] = sky->htcp_desc.lower[2]; - upp[0] = low[0] + (double)nvoxs[0] * sky->htcp_desc.vxsz_x; - upp[1] = low[1] + (double)nvoxs[1] * sky->htcp_desc.vxsz_y; + upp[0] = low[0] + (double)raw_def[0] * sky->htcp_desc.vxsz_x; + upp[1] = low[1] + (double)raw_def[1] * sky->htcp_desc.vxsz_y; if(!sky->htcp_desc.irregular_z) { /* Regular voxel size along the Z dimension: compute its upper boundary as * the others dimensions */ - upp[2] = low[2] + (double)nvoxs[2] * sky->htcp_desc.vxsz_z[0]; + upp[2] = low[2] + (double)raw_def[2] * sky->htcp_desc.vxsz_z[0]; } else { /* Irregular voxel size along Z */ double len_z; size_t nsplits; @@ -1191,7 +1301,7 @@ setup_clouds } /* 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 the next htcp voxel. */ + * 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) { @@ -1210,9 +1320,9 @@ setup_clouds vxsz[0] = sky->htcp_desc.upper[0] - sky->htcp_desc.lower[0]; vxsz[1] = sky->htcp_desc.upper[1] - sky->htcp_desc.lower[1]; vxsz[2] = sky->htcp_desc.upper[2] - sky->htcp_desc.lower[2]; - vxsz[0] = vxsz[0] / (double)sky->htcp_desc.spatial_definition[0]; - vxsz[1] = vxsz[1] / (double)sky->htcp_desc.spatial_definition[1]; - vxsz[2] = vxsz[2] / (double)sky->htcp_desc.spatial_definition[2]; + vxsz[0] = vxsz[0] / (double)nvoxs[0]; + vxsz[1] = vxsz[1] / (double)nvoxs[1]; + vxsz[2] = vxsz[2] / (double)nvoxs[2]; /* Create as many cloud data structure than considered SW spectral bands */ nbands = htrdr_sky_get_sw_spectral_bands_count(sky);