htrdr

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

commit 61d40b8a98bb799c33765b5fa492da810f009eeb
parent 47c405f38b14b48149096f9aec5e5a934ff04ef2
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Thu, 28 Jun 2018 16:02:30 +0200

Add the sky data structure and remove the "clouds" function

Diffstat:
Mcmake/CMakeLists.txt | 4++--
Msrc/htrdr.c | 8++++----
Msrc/htrdr.h | 10++--------
Dsrc/htrdr_clouds.c | 348-------------------------------------------------------------------------------
Dsrc/htrdr_clouds.h | 35-----------------------------------
Asrc/htrdr_sky.c | 486+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/htrdr_sky.h | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/htrdr_transmission.c | 9++++++---
8 files changed, 573 insertions(+), 400 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -51,16 +51,16 @@ set(HTRDR_FILES_SRC htrdr.c htrdr_args.c htrdr_buffer.c - htrdr_clouds.c htrdr_main.c htrdr_rectangle.c + htrdr_sky.c htrdr_transmission.c) set(HTRDR_FILES_INC htrdr.h htrdr_args.h htrdr_buffer.h - htrdr_clouds.h htrdr_rectangle.h + htrdr_sky.h htrdr_solve.h) set(HTDRD_FILES_DOC COPYING README.md) diff --git a/src/htrdr.c b/src/htrdr.c @@ -18,8 +18,8 @@ #include "htrdr.h" #include "htrdr_args.h" #include "htrdr_buffer.h" -#include "htrdr_clouds.h" #include "htrdr_rectangle.h" +#include "htrdr_sky.h" #include "htrdr_solve.h" #include <rsys/clock_time.h> @@ -218,7 +218,7 @@ htrdr_init res = open_output_stream(htrdr, args); if(res != RES_OK) goto error; } - res = clouds_load(htrdr, args->verbose, args->input); + res = htrdr_sky_create(htrdr, args->input, &htrdr->sky); if(res != RES_OK) goto error; exit: @@ -233,7 +233,7 @@ htrdr_release(struct htrdr* htrdr) { ASSERT(htrdr); if(htrdr->svx) SVX(device_ref_put(htrdr->svx)); - if(htrdr->clouds) SVX(tree_ref_put(htrdr->clouds)); + if(htrdr->sky) htrdr_sky_ref_put(htrdr->sky); if(htrdr->buf) htrdr_buffer_ref_put(htrdr->buf); if(htrdr->rect) htrdr_rectangle_ref_put(htrdr->rect); logger_release(&htrdr->logger); @@ -244,7 +244,7 @@ htrdr_run(struct htrdr* htrdr) { res_T res = RES_OK; if(htrdr->dump_vtk) { - res = clouds_dump_vtk(htrdr, htrdr->output); + res = htrdr_sky_dump_clouds_vtk(htrdr->sky, htrdr->output); if(res != RES_OK) goto error; } else { struct time t0, t1; diff --git a/src/htrdr.h b/src/htrdr.h @@ -19,24 +19,18 @@ #include <rsys/logger.h> #include <rsys/ref_count.h> -enum htrdr_voxel_data { - HTRDR_K_EXT_MIN, - HTRDR_K_EXT_MAX, - HTRDR_VOXEL_DATA_COUNT__ -}; - /* Forward declaration */ struct htrdr_args; struct htrdr_buffer; +struct htrdr_sky; struct htrdr_rectangle; struct mem_allocator; struct svx_device; -struct svx_tree; struct htrdr { struct svx_device* svx; - struct svx_tree* clouds; + struct htrdr_sky* sky; struct htrdr_buffer* buf; struct htrdr_rectangle* rect; double main_dir[3]; /* Main direction */ diff --git a/src/htrdr_clouds.c b/src/htrdr_clouds.c @@ -1,348 +0,0 @@ -/* Copyright (C) 2018 Université Paul Sabatier, |Meso|Star> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#include "htrdr.h" -#include "htrdr_clouds.h" - -#include <star/svx.h> -#include <high_tune/htcp.h> - -#include <rsys/double3.h> -#include <rsys/dynamic_array_double.h> -#include <rsys/dynamic_array_size_t.h> -#include <rsys/hash_table.h> -#include <rsys/math.h> -#include <rsys/mem_allocator.h> - -struct vertex { - double x; - double y; - double z; -}; - -static char -vertex_eq(const struct vertex* v0, const struct vertex* v1) -{ - return eq_eps(v0->x, v1->x, 1.e-6) - && eq_eps(v0->y, v1->y, 1.e-6) - && eq_eps(v0->z, v1->z, 1.e-6); -} - -/* Generate the htable_vertex data structure */ -#define HTABLE_NAME vertex -#define HTABLE_KEY struct vertex -#define HTABLE_DATA size_t -#define HTABLE_KEY_FUNCTOR_EQ vertex_eq -#include <rsys/hash_table.h> - -struct build_octree_context { - struct htcp_desc* htcp_desc; - double dst_max; /* Max traversal distance */ - double tau_threshold; /* Threshold criteria for the merge process */ -}; - -struct octree_data { - struct htable_vertex vertex2id; /* Map a coordinate to its vertex id */ - struct darray_double vertices; /* Array of unique vertices */ - struct darray_double data; - struct darray_size_t cells; -}; - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static void -octree_data_init(struct octree_data* data) -{ - ASSERT(data); - htable_vertex_init(NULL, &data->vertex2id); - darray_double_init(NULL, &data->vertices); - darray_double_init(NULL, &data->data); - darray_size_t_init(NULL, &data->cells); -} - -static void -octree_data_release(struct octree_data* data) -{ - ASSERT(data); - htable_vertex_release(&data->vertex2id); - darray_double_release(&data->vertices); - darray_double_release(&data->data); - darray_size_t_release(&data->cells); -} - -static void -register_leaf - (const struct svx_voxel* leaf, - const size_t ileaf, - void* context) -{ - struct octree_data* ctx = context; - struct vertex v[8]; - const double* data; - int i; - ASSERT(leaf && ctx); - (void)ileaf; - - data = leaf->data; - - /* Compute the leaf vertices */ - v[0].x = leaf->lower[0]; v[0].y = leaf->lower[1]; v[0].z = leaf->lower[2]; - v[1].x = leaf->upper[0]; v[1].y = leaf->lower[1]; v[1].z = leaf->lower[2]; - v[2].x = leaf->lower[0]; v[2].y = leaf->upper[1]; v[2].z = leaf->lower[2]; - v[3].x = leaf->upper[0]; v[3].y = leaf->upper[1]; v[3].z = leaf->lower[2]; - v[4].x = leaf->lower[0]; v[4].y = leaf->lower[1]; v[4].z = leaf->upper[2]; - v[5].x = leaf->upper[0]; v[5].y = leaf->lower[1]; v[5].z = leaf->upper[2]; - v[6].x = leaf->lower[0]; v[6].y = leaf->upper[1]; v[6].z = leaf->upper[2]; - v[7].x = leaf->upper[0]; v[7].y = leaf->upper[1]; v[7].z = leaf->upper[2]; - - FOR_EACH(i, 0, 8) { - size_t *pid = htable_vertex_find(&ctx->vertex2id, v+i); - size_t id; - if(pid) { - id = *pid; - } else { /* Register the leaf vertex */ - id = darray_double_size_get(&ctx->vertices)/3; - CHK(RES_OK == htable_vertex_set(&ctx->vertex2id, v+i, &id)); - CHK(RES_OK == darray_double_push_back(&ctx->vertices, &v[i].x)); - CHK(RES_OK == darray_double_push_back(&ctx->vertices, &v[i].y)); - CHK(RES_OK == darray_double_push_back(&ctx->vertices, &v[i].z)); - } - /* Add the vertex id to the leaf cell */ - CHK(RES_OK == darray_size_t_push_back(&ctx->cells, &id)); - } - FOR_EACH(i, 0, HTRDR_VOXEL_DATA_COUNT__) { - CHK(RES_OK == darray_double_push_back(&ctx->data, data+i)); - } -} - -static void -vox_get(const size_t xyz[3], void* dst, void* context) -{ - struct build_octree_context* ctx = context; - const double c_ext = 6.e-10; /* Extinction cross section in m^2.particle^-1 */ - const double rho_air = 1.293; /* Volumic mass of terrestrial air in kg.m^-3 */ - const double rho_h2o = 1000; /* Volumic mass of water in kg.m^-3 */ - const double sigma = 0.1; /* Std deviation of the `r' parameter */ - const double r = 9.76617; /* Modal radius of water particles in um */ - double ql; /* Mass of the liquid water in the voxel in kg.m^-3 */ - double v; /* typical volume of a particle */ - double nv; /* Number density */ - double k_ext; /* Extinction coefficient in m^-1 */ - double* pdbl = dst; - ASSERT(xyz && dst && ctx); - - ql = htcp_desc_RCT_at(ctx->htcp_desc, xyz[0], xyz[1], xyz[2], 0/*time*/); - v = 4*PI*(r*r*r)*exp(4.5*sigma*sigma) / 3.0; - nv = 1.e18 * ql * rho_air / (v * rho_h2o); - k_ext = c_ext*nv; - - pdbl[HTRDR_K_EXT_MIN] = k_ext; - pdbl[HTRDR_K_EXT_MAX] = k_ext; -} - -static void -vox_merge(void* dst, const void* voxels[], const size_t nvoxs, void* ctx) -{ - double* pdbl = dst; - double k_ext_min = DBL_MAX; - double k_ext_max =-DBL_MAX; - size_t ivox; - ASSERT(dst && voxels && nvoxs); - (void)ctx; - - FOR_EACH(ivox, 0, nvoxs) { - const double* voxel_data = voxels[ivox]; - ASSERT(voxel_data[HTRDR_K_EXT_MIN] <= voxel_data[HTRDR_K_EXT_MAX]); - k_ext_min = MMIN(k_ext_min, voxel_data[HTRDR_K_EXT_MIN]); - k_ext_max = MMAX(k_ext_max, voxel_data[HTRDR_K_EXT_MAX]); - } - pdbl[HTRDR_K_EXT_MIN] = k_ext_min; - pdbl[HTRDR_K_EXT_MAX] = k_ext_max; -} - -static int -vox_challenge_merge(const void* voxels[], const size_t nvoxs, void* context) -{ - struct build_octree_context* ctx = context; - double k_ext_min = DBL_MAX; - double k_ext_max =-DBL_MAX; - size_t ivox; - ASSERT(voxels && nvoxs); - - FOR_EACH(ivox, 0, nvoxs) { - const double* voxel_data = voxels[ivox]; - k_ext_min = MMIN(k_ext_min, voxel_data[HTRDR_K_EXT_MIN]); - k_ext_max = MMAX(k_ext_max, voxel_data[HTRDR_K_EXT_MAX]); - } - return (k_ext_max - k_ext_min)*ctx->dst_max <= ctx->tau_threshold; -} - -/******************************************************************************* - * Local functions - ******************************************************************************/ -res_T -clouds_load(struct htrdr* htrdr, const int verbose, const char* filename) -{ - struct htcp* htcp = NULL; - struct svx_tree* clouds = NULL; - struct htcp_desc htcp_desc = HTCP_DESC_NULL; - struct svx_voxel_desc vox_desc = SVX_VOXEL_DESC_NULL; - struct build_octree_context ctx; - size_t nvoxs[3]; - double low[3], upp[3], sz[3]; - res_T res = RES_OK; - ASSERT(htrdr && filename); - - res = htcp_create(&htrdr->logger, htrdr->allocator, verbose, &htcp); - if(res != RES_OK) { - htrdr_log_err(htrdr, "could not create the loader of cloud properties.\n"); - goto error; - } - res = htcp_load(htcp, filename); - if(res != RES_OK) { - htrdr_log_err(htrdr, "error loading the cloud properties -- `%s'.\n", - filename); - goto error; - } - - /* Currently only regular Z dimension is correctly handled */ - HTCP(get_desc(htcp, &htcp_desc)); - if(htcp_desc.irregular_z) { - htrdr_log_err(htrdr, "unsupported irregular Z dimension for the clouds.\n"); - res = RES_BAD_ARG; - goto error; - } - - /* Define the number of voxels */ - nvoxs[0] = htcp_desc.spatial_definition[0]; - nvoxs[1] = htcp_desc.spatial_definition[1]; - nvoxs[2] = htcp_desc.spatial_definition[2]; - - /* Define the octree AABB */ - low[0] = htcp_desc.lower[0]; - low[1] = htcp_desc.lower[1]; - low[2] = htcp_desc.lower[2]; - upp[0] = low[0] + (double)nvoxs[0] * htcp_desc.vxsz_x; - upp[1] = low[1] + (double)nvoxs[1] * htcp_desc.vxsz_y; - upp[2] = low[2] + (double)nvoxs[2] * htcp_desc.vxsz_z[0]; - - /* Compute the size of of the AABB */ - sz[0] = upp[0] - low[0]; - sz[1] = upp[1] - low[1]; - sz[2] = upp[2] - low[2]; - - /* Setup the build context */ - ctx.htcp_desc = &htcp_desc; - ctx.dst_max = sz[2]; - ctx.tau_threshold = 0.0; - - /* Setup the voxel descriptor */ - vox_desc.get = vox_get; - vox_desc.merge = vox_merge; - vox_desc.challenge_merge = vox_challenge_merge; - vox_desc.context = &ctx; - vox_desc.size = sizeof(double)*HTRDR_VOXEL_DATA_COUNT__; - - /* Create the octree */ - res = svx_octree_create(htrdr->svx, low, upp, nvoxs, &vox_desc, &clouds); - if(res != RES_OK) { - htrdr_log_err(htrdr, - "could not create the octree of the cloud properties.\n"); - goto error; - } - -exit: - if(htcp) HTCP(ref_put(htcp)); - htrdr->clouds = clouds; - return res; -error: - if(clouds) { - SVX(tree_ref_put(clouds)); - clouds = NULL; - } - goto exit; -} - -res_T -clouds_dump_vtk(struct htrdr* htrdr, FILE* stream) -{ - struct svx_tree_desc desc; - struct octree_data data; - const double* leaf_data; - size_t nvertices; - size_t ncells; - size_t i; - ASSERT(htrdr && stream); - - octree_data_init(&data); - SVX(tree_get_desc(htrdr->clouds, &desc)); - ASSERT(desc.type == SVX_OCTREE); - - /* Register leaf data */ - SVX(tree_for_each_leaf(htrdr->clouds, register_leaf, &data)); - nvertices = darray_double_size_get(&data.vertices) / 3/*#coords per vertex*/; - ncells = darray_size_t_size_get(&data.cells)/8/*#ids per cell*/; - ASSERT(ncells == desc.nleaves); - - /* Write headers */ - fprintf(stream, "# vtk DataFile Version 2.0\n"); - fprintf(stream, "Volume\n"); - fprintf(stream, "ASCII\n"); - fprintf(stream, "DATASET UNSTRUCTURED_GRID\n"); - - /* Write vertex coordinates */ - fprintf(stream, "POINTS %lu float\n", (unsigned long)nvertices); - FOR_EACH(i, 0, nvertices) { - fprintf(stream, "%g %g %g\n", - SPLIT3(darray_double_cdata_get(&data.vertices) + i*3)); - } - - /* Write the cells */ - fprintf(stream, "CELLS %lu %lu\n", - (unsigned long)ncells, - (unsigned long)(ncells*(8/*#verts per cell*/ + 1/*1st field of a cell*/))); - FOR_EACH(i, 0, ncells) { - fprintf(stream, "8 %lu %lu %lu %lu %lu %lu %lu %lu\n", - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+0], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+1], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+2], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+3], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+4], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+5], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+6], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+7]); - } - - /* Write the cell type */ - fprintf(stream, "CELL_TYPES %lu\n", (unsigned long)ncells); - FOR_EACH(i, 0, ncells) fprintf(stream, "11\n"); - - /* Write the cell data */ - leaf_data = darray_double_cdata_get(&data.data); - fprintf(stream, "CELL_DATA %lu\n", (unsigned long)ncells); - fprintf(stream, "SCALARS Val double %d\n", HTRDR_VOXEL_DATA_COUNT__); - fprintf(stream, "LOOKUP_TABLE default\n"); - FOR_EACH(i, 0, ncells) { - size_t idata; - FOR_EACH(idata, 0, HTRDR_VOXEL_DATA_COUNT__) { - fprintf(stream, "%g ", leaf_data[i*HTRDR_VOXEL_DATA_COUNT__ + idata]); - } - fprintf(stream, "\n"); - } - octree_data_release(&data); - return RES_OK; -} - diff --git a/src/htrdr_clouds.h b/src/htrdr_clouds.h @@ -1,35 +0,0 @@ -/* Copyright (C) 2018 Université Paul Sabatier, |Meso|Star> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#ifndef HTRDR_CLOUDS_H -#define HTRDR_CLOUDS_H - -#include <rsys/rsys.h> - -struct htrdr; - -extern LOCAL_SYM res_T -clouds_load - (struct htrdr* htrdr, - const int verbose, - const char* filename); - -extern LOCAL_SYM res_T -clouds_dump_vtk - (struct htrdr* htrdr, - FILE* stream); - -#endif /* HTRDR_CLOUDS_H */ - diff --git a/src/htrdr_sky.c b/src/htrdr_sky.c @@ -0,0 +1,486 @@ +/* Copyright (C) 2018 Université Paul Sabatier, |Meso|Star> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "htrdr.h" +#include "htrdr_sky.h" + +#include <star/svx.h> +#include <high_tune/htcp.h> + +#include <rsys/dynamic_array_double.h> +#include <rsys/dynamic_array_size_t.h> +#include <rsys/hash_table.h> +#include <rsys/ref_count.h> + +struct split { + size_t index; /* Index of the current htcp voxel */ + double height; /* Absolute height where the next voxel starts */ +}; + +#define DARRAY_NAME split +#define DARRAY_DATA struct split +#include <rsys/dynamic_array.h> + +struct build_octree_context { + const struct htcp_desc* htcp_desc; + const struct darray_split* svx2htcp_z; + double dst_max; /* Max traversal distance */ + double tau_threshold; /* Threshold criteria for the merge process */ +}; + +struct vertex { + double x; + double y; + double z; +}; + +static char +vertex_eq(const struct vertex* v0, const struct vertex* v1) +{ + return eq_eps(v0->x, v1->x, 1.e-6) + && eq_eps(v0->y, v1->y, 1.e-6) + && eq_eps(v0->z, v1->z, 1.e-6); +} + +/* Generate the htable_vertex data structure */ +#define HTABLE_NAME vertex +#define HTABLE_KEY struct vertex +#define HTABLE_DATA size_t +#define HTABLE_KEY_FUNCTOR_EQ vertex_eq +#include <rsys/hash_table.h> + +struct octree_data { + struct htable_vertex vertex2id; /* Map a coordinate to its vertex id */ + struct darray_double vertices; /* Array of unique vertices */ + struct darray_double data; + struct darray_size_t cells; +}; + +struct htrdr_sky { + struct svx_tree* clouds; + struct htcp* htcp; + + struct htcp_desc htcp_desc; + + /* Map the index in Z from the regular SVX to the irregular HTCP data */ + struct darray_split svx2htcp_z; + + ref_T ref; + struct htrdr* htrdr; +}; + +/******************************************************************************* + * Helper function + ******************************************************************************/ +static INLINE void +octree_data_init(struct octree_data* data) +{ + ASSERT(data); + htable_vertex_init(NULL, &data->vertex2id); + darray_double_init(NULL, &data->vertices); + darray_double_init(NULL, &data->data); + darray_size_t_init(NULL, &data->cells); +} + +static INLINE void +octree_data_release(struct octree_data* data) +{ + ASSERT(data); + htable_vertex_release(&data->vertex2id); + darray_double_release(&data->vertices); + darray_double_release(&data->data); + darray_size_t_release(&data->cells); +} + +static INLINE void +register_leaf + (const struct svx_voxel* leaf, + const size_t ileaf, + void* context) +{ + struct octree_data* ctx = context; + struct vertex v[8]; + const double* data; + int i; + ASSERT(leaf && ctx); + (void)ileaf; + + data = leaf->data; + + /* Compute the leaf vertices */ + v[0].x = leaf->lower[0]; v[0].y = leaf->lower[1]; v[0].z = leaf->lower[2]; + v[1].x = leaf->upper[0]; v[1].y = leaf->lower[1]; v[1].z = leaf->lower[2]; + v[2].x = leaf->lower[0]; v[2].y = leaf->upper[1]; v[2].z = leaf->lower[2]; + v[3].x = leaf->upper[0]; v[3].y = leaf->upper[1]; v[3].z = leaf->lower[2]; + v[4].x = leaf->lower[0]; v[4].y = leaf->lower[1]; v[4].z = leaf->upper[2]; + v[5].x = leaf->upper[0]; v[5].y = leaf->lower[1]; v[5].z = leaf->upper[2]; + v[6].x = leaf->lower[0]; v[6].y = leaf->upper[1]; v[6].z = leaf->upper[2]; + v[7].x = leaf->upper[0]; v[7].y = leaf->upper[1]; v[7].z = leaf->upper[2]; + + FOR_EACH(i, 0, 8) { + size_t *pid = htable_vertex_find(&ctx->vertex2id, v+i); + size_t id; + if(pid) { + id = *pid; + } else { /* Register the leaf vertex */ + id = darray_double_size_get(&ctx->vertices)/3; + CHK(RES_OK == htable_vertex_set(&ctx->vertex2id, v+i, &id)); + CHK(RES_OK == darray_double_push_back(&ctx->vertices, &v[i].x)); + CHK(RES_OK == darray_double_push_back(&ctx->vertices, &v[i].y)); + CHK(RES_OK == darray_double_push_back(&ctx->vertices, &v[i].z)); + } + /* Add the vertex id to the leaf cell */ + CHK(RES_OK == darray_size_t_push_back(&ctx->cells, &id)); + } + FOR_EACH(i, 0, HTRDR_SKY_SVX_PROPS_COUNT__) { + CHK(RES_OK == darray_double_push_back(&ctx->data, data+i)); + } +} + +static double +compute_kext(const struct htcp_desc* htcp_desc, const size_t xyz[3]) +{ + const double c_ext = 6.e-10; /* Extinction cross section in m^2.particle^-1 */ + const double rho_air = 1.293; /* Volumic mass of terrestrial air in kg.m^-3 */ + const double rho_h2o = 1000; /* Volumic mass of water in kg.m^-3 */ + const double sigma = 0.1; /* Std deviation of the `r' parameter */ + const double r = 9.76617; /* Modal radius of water particles in um */ + double ql; /* Mass of the liquid water in the voxel in kg.m^-3 */ + double v; /* typical volume of a particle */ + double nv; /* Number density */ + double k_ext; /* Extinction coefficient in m^-1 */ + ASSERT(xyz && htcp_desc); + + ql = htcp_desc_RCT_at(htcp_desc, xyz[0], xyz[1], xyz[2], 0/*time*/); + v = 4*PI*(r*r*r)*exp(4.5*sigma*sigma) / 3.0; + nv = 1.e18 * ql * rho_air / (v * rho_h2o); + k_ext = c_ext*nv; + + return k_ext; +} + +static void +vox_get(const size_t xyz[3], void* dst, void* context) +{ + struct build_octree_context* ctx = context; + double kext_min; + double kext_max; + double* pdbl = dst; + ASSERT(xyz && dst && context); + + if(!ctx->htcp_desc->irregular_z) { + kext_min = kext_max = compute_kext(ctx->htcp_desc, xyz); + } else { + size_t ivox[3]; + size_t ivox_next; + ASSERT(xyz[2] < darray_split_size_get(ctx->svx2htcp_z)); + + ivox[0] = xyz[0]; + ivox[1] = xyz[1]; + ivox[2] = darray_split_cdata_get(ctx->svx2htcp_z)[xyz[2]].index; + + kext_min = kext_max = compute_kext(ctx->htcp_desc, ivox); + + /* Define if the SVX voxel is overlapped by 2 HTCP voxels */ + ivox_next = xyz[2] + 1 < darray_split_size_get(ctx->svx2htcp_z) + ? darray_split_cdata_get(ctx->svx2htcp_z)[xyz[2] + 1].index + : ivox[2]; + + if(ivox_next != ivox[2]) { + double kext; + ASSERT(ivox[2] < ivox_next); + ivox[2] = ivox_next; + kext = compute_kext(ctx->htcp_desc, ivox); + kext_min = MMIN(kext_min, kext); + kext_max = MMAX(kext_max, kext); + } + } + + pdbl[HTRDR_SKY_SVX_Kext_MIN] = kext_min; + pdbl[HTRDR_SKY_SVX_Kext_MAX] = kext_max; +} + +static void +vox_merge(void* dst, const void* voxels[], const size_t nvoxs, void* ctx) +{ + double* pdbl = dst; + double kext_min = DBL_MAX; + double kext_max =-DBL_MAX; + size_t ivox; + ASSERT(dst && voxels && nvoxs); + (void)ctx; + + FOR_EACH(ivox, 0, nvoxs) { + const double* voxel_data = voxels[ivox]; + ASSERT(voxel_data[HTRDR_SKY_SVX_Kext_MIN] + <= voxel_data[HTRDR_SKY_SVX_Kext_MAX]); + kext_min = MMIN(kext_min, voxel_data[HTRDR_SKY_SVX_Kext_MIN]); + kext_max = MMAX(kext_max, voxel_data[HTRDR_SKY_SVX_Kext_MAX]); + } + pdbl[HTRDR_SKY_SVX_Kext_MIN] = kext_min; + pdbl[HTRDR_SKY_SVX_Kext_MAX] = kext_max; +} + +static int +vox_challenge_merge(const void* voxels[], const size_t nvoxs, void* context) +{ + struct build_octree_context* ctx = context; + double kext_min = DBL_MAX; + double kext_max =-DBL_MAX; + size_t ivox; + ASSERT(voxels && nvoxs); + + FOR_EACH(ivox, 0, nvoxs) { + const double* voxel_data = voxels[ivox]; + kext_min = MMIN(kext_min, voxel_data[HTRDR_SKY_SVX_Kext_MIN]); + kext_max = MMAX(kext_max, voxel_data[HTRDR_SKY_SVX_Kext_MAX]); + } + return (kext_max - kext_min)*ctx->dst_max <= ctx->tau_threshold; +} + +static void +release_sky(ref_T* ref) +{ + struct htrdr_sky* sky; + ASSERT(ref); + sky = CONTAINER_OF(ref, struct htrdr_sky, ref); + if(sky->clouds) SVX(tree_ref_put(sky->clouds)); + if(sky->htcp) HTCP(ref_put(sky->htcp)); + darray_split_release(&sky->svx2htcp_z); + MEM_RM(sky->htrdr->allocator, sky); +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +htrdr_sky_create + (struct htrdr* htrdr, + const char* htcp_filename, + struct htrdr_sky** out_sky) +{ + struct htrdr_sky* sky = NULL; + struct svx_voxel_desc vox_desc = SVX_VOXEL_DESC_NULL; + struct build_octree_context ctx; + size_t nvoxs[3]; + double sz[3]; + double low[3]; + double upp[3]; + res_T res = RES_OK; + ASSERT(htrdr && htcp_filename && out_sky); + + sky = MEM_CALLOC(htrdr->allocator, 1, sizeof(*sky)); + if(!sky) { + res = RES_MEM_ERR; + goto error; + } + ref_init(&sky->ref); + sky->htrdr = htrdr; + darray_split_init(htrdr->allocator, &sky->svx2htcp_z); + + res = htcp_create(&htrdr->logger, htrdr->allocator, htrdr->verbose, &sky->htcp); + if(res != RES_OK) { + htrdr_log_err(htrdr, "could not create the loader of cloud properties.\n"); + goto error; + } + + res = htcp_load(sky->htcp, htcp_filename); + if(res != RES_OK) { + htrdr_log_err(htrdr, "error loading the cloud properties -- `%s'.\n", + htcp_filename); + goto error; + } + + HTCP(get_desc(sky->htcp, &sky->htcp_desc)); + + /* 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]; + + /* Define the octree AABB */ + 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; + + if(!sky->htcp_desc.irregular_z) { /* Refular voxel size in Z */ + upp[2] = low[2] + (double)nvoxs[2] * sky->htcp_desc.vxsz_z[0]; + } else { /* Irregular voxel size along Z */ + double min_vxsz_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; + min_vxsz_z = DBL_MAX; + FOR_EACH(iz, 0, sky->htcp_desc.spatial_definition[2]) { + len_z += sky->htcp_desc.vxsz_z[iz]; + min_vxsz_z = MMIN(min_vxsz_z, sky->htcp_desc.vxsz_z[iz]); + } + /* Allocate the svx2htcp LUT. The LUT is a regular table whose absolute + * size is the size of 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 / min_vxsz_z); + res = darray_split_resize(&sky->svx2htcp_z, nsplits); + if(res != RES_OK) { + htrdr_log_err(htrdr, + "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 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) * min_vxsz_z; + 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]) upp[2] += sky->htcp_desc.vxsz_z[++iz2]; + } + ASSERT(eq_eps(upp[2] - low[2], len_z, 1.e-6)); + } + + /* Compute the size of of the AABB */ + sz[0] = upp[0] - low[0]; + sz[1] = upp[1] - low[1]; + sz[2] = upp[2] - low[2]; + + /* Setup the build context */ + ctx.htcp_desc = &sky->htcp_desc; + ctx.svx2htcp_z = &sky->svx2htcp_z; + ctx.dst_max = sz[2]; + ctx.tau_threshold = 0.0; + + /* Setup the voxel descriptor */ + vox_desc.get = vox_get; + vox_desc.merge = vox_merge; + vox_desc.challenge_merge = vox_challenge_merge; + vox_desc.context = &ctx; + vox_desc.size = sizeof(double)*HTRDR_SKY_SVX_PROPS_COUNT__; + + /* Create the octree */ + res = svx_octree_create(htrdr->svx, low, upp, nvoxs, &vox_desc, &sky->clouds); + if(res != RES_OK) { + htrdr_log_err(htrdr, + "could not create the octree of the cloud properties.\n"); + goto error; + } + +exit: + *out_sky = sky; + return res; +error: + if(sky) { + htrdr_sky_ref_put(sky); + sky = NULL; + } + goto exit; +} + +void +htrdr_sky_ref_get(struct htrdr_sky* sky) +{ + ASSERT(sky); + ref_get(&sky->ref); +} + +void +htrdr_sky_ref_put(struct htrdr_sky* sky) +{ + ASSERT(sky); + ref_put(&sky->ref, release_sky); +} + +struct svx_tree* +htrdr_sky_get_svx_tree(struct htrdr_sky* sky) +{ + ASSERT(sky); + return sky->clouds; +} + +res_T +htrdr_sky_dump_clouds_vtk(const struct htrdr_sky* sky, FILE* stream) +{ + struct svx_tree_desc desc; + struct octree_data data; + const double* leaf_data; + size_t nvertices; + size_t ncells; + size_t i; + ASSERT(sky && stream); + + octree_data_init(&data); + SVX(tree_get_desc(sky->clouds, &desc)); + ASSERT(desc.type == SVX_OCTREE); + + /* Register leaf data */ + SVX(tree_for_each_leaf(sky->clouds, register_leaf, &data)); + nvertices = darray_double_size_get(&data.vertices) / 3/*#coords per vertex*/; + ncells = darray_size_t_size_get(&data.cells)/8/*#ids per cell*/; + ASSERT(ncells == desc.nleaves); + + /* Write headers */ + fprintf(stream, "# vtk DataFile Version 2.0\n"); + fprintf(stream, "Volume\n"); + fprintf(stream, "ASCII\n"); + fprintf(stream, "DATASET UNSTRUCTURED_GRID\n"); + + /* Write vertex coordinates */ + fprintf(stream, "POINTS %lu float\n", (unsigned long)nvertices); + FOR_EACH(i, 0, nvertices) { + fprintf(stream, "%g %g %g\n", + SPLIT3(darray_double_cdata_get(&data.vertices) + i*3)); + } + + /* Write the cells */ + fprintf(stream, "CELLS %lu %lu\n", + (unsigned long)ncells, + (unsigned long)(ncells*(8/*#verts per cell*/ + 1/*1st field of a cell*/))); + FOR_EACH(i, 0, ncells) { + fprintf(stream, "8 %lu %lu %lu %lu %lu %lu %lu %lu\n", + (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+0], + (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+1], + (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+2], + (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+3], + (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+4], + (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+5], + (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+6], + (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+7]); + } + + /* Write the cell type */ + fprintf(stream, "CELL_TYPES %lu\n", (unsigned long)ncells); + FOR_EACH(i, 0, ncells) fprintf(stream, "11\n"); + + /* Write the cell data */ + leaf_data = darray_double_cdata_get(&data.data); + fprintf(stream, "CELL_DATA %lu\n", (unsigned long)ncells); + fprintf(stream, "SCALARS Val double %d\n", HTRDR_SKY_SVX_PROPS_COUNT__); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(i, 0, ncells) { + size_t idata; + FOR_EACH(idata, 0, HTRDR_SKY_SVX_PROPS_COUNT__) { + fprintf(stream, "%g ", leaf_data[i*HTRDR_SKY_SVX_PROPS_COUNT__ + idata]); + } + fprintf(stream, "\n"); + } + octree_data_release(&data); + return RES_OK; +} + diff --git a/src/htrdr_sky.h b/src/htrdr_sky.h @@ -0,0 +1,73 @@ +/* Copyright (C) 2018 Université Paul Sabatier, |Meso|Star> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef HTRDR_SKY_H +#define HTRDR_SKY_H + +#include <rsys/rsys.h> + +enum htrdr_sky_property { + HTRDR_SKY_Ks, /* Scattering coefficient */ + HTRDR_SKY_Ka /* Absorption coefficient */ +}; + +enum htrdr_sky_component_flag { + HTRDR_GAZ = BIT(0), + HTRDR_PARTICLE = BIT(1) +}; + +enum htrdr_sky_svx_property { + HTRDR_SKY_SVX_Kext_MIN, + HTRDR_SKY_SVX_Kext_MAX, + HTRDR_SKY_SVX_PROPS_COUNT__ +}; + +/* Forward declaration */ +struct htrdr; +struct htrdr_sky; + +extern LOCAL_SYM res_T +htrdr_sky_create + (struct htrdr* htrdr, + const char* htcp_filename, + struct htrdr_sky** sky); + +extern LOCAL_SYM void +htrdr_sky_ref_get + (struct htrdr_sky* sky); + +extern LOCAL_SYM void +htrdr_sky_ref_put + (struct htrdr_sky* sky); + +extern LOCAL_SYM double +htrdr_sky_fetch_property + (const struct htrdr_sky* sky, + const enum htrdr_sky_property prop, + const int components, /* Combination of htrdr_sky_component_flag */ + const double wavelength, + const double pos[3]); + +extern LOCAL_SYM struct svx_tree* +htrdr_sky_get_svx_tree + (struct htrdr_sky* sky); + +extern LOCAL_SYM res_T +htrdr_sky_dump_clouds_vtk + (const struct htrdr_sky* sky, + FILE* stream); + +#endif /* HTRDR_SKY_H */ + diff --git a/src/htrdr_transmission.c b/src/htrdr_transmission.c @@ -16,6 +16,7 @@ #include "htrdr.h" #include "htrdr_buffer.h" #include "htrdr_rectangle.h" +#include "htrdr_sky.h" #include "htrdr_solve.h" #include <star/ssp.h> @@ -62,8 +63,8 @@ discard_hit (void)org, (void)dir, (void)range; vox_data = hit->voxel.data; - k_ext_min = vox_data[HTRDR_K_EXT_MIN]; - k_ext_max = vox_data[HTRDR_K_EXT_MAX]; + k_ext_min = vox_data[HTRDR_SKY_SVX_Kext_MIN]; + k_ext_max = vox_data[HTRDR_SKY_SVX_Kext_MAX]; dst = hit->distance[1] - hit->distance[0]; ASSERT(dst >= 0); @@ -81,13 +82,15 @@ transmission_realisation double *val) { struct svx_hit hit = SVX_HIT_NULL; + struct svx_tree* svx_tree = NULL; struct transmit_context ctx = TRANSMIT_CONTEXT_NULL; const double range[2] = {0, INF}; res_T res = RES_OK; ASSERT(htrdr && pos && dir && val); ctx.tau_sampled = ssp_ran_exp(rng, 1.0); - res = svx_octree_trace_ray(htrdr->clouds, pos, dir, range, NULL, + svx_tree = htrdr_sky_get_svx_tree(htrdr->sky); + res = svx_octree_trace_ray(svx_tree, pos, dir, range, NULL, discard_hit, &ctx, &hit); if(res != RES_OK) { htrdr_log_err(htrdr,