star-gf

Compute Gebhart factors
git clone git://git.meso-star.fr/star-gf.git
Log | Files | Refs | README | LICENSE

commit df29d4ea741ce6653129dfc6b32d9e955cd2e268
parent 5301884f22d0bb7a8730624f6f399eeb28051748
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri, 25 Sep 2015 09:19:54 +0200

Code refactoring

Split the source in 2 parts: the sgf_device implementation and the
sgf_estimator one. Add comments.

Diffstat:
Mcmake/CMakeLists.txt | 8+++++---
Dsrc/sgf.c | 459-------------------------------------------------------------------------------
Msrc/sgf.h | 6++++++
Asrc/sgf_device.c | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sgf_device_c.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sgf_estimator.c | 373+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sgf_cube.c | 2+-
7 files changed, 544 insertions(+), 463 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -40,7 +40,7 @@ find_package(RCMake 0.1 REQUIRED) find_package(RSys 0.2 REQUIRED) find_package(StarSP 0.1 REQUIRED) find_package(Star3D 0.2 REQUIRED) -find_package(OpenMP) +find_package(OpenMP REQUIRED) include_directories( ${RSys_INCLUDE_DIR} @@ -59,16 +59,18 @@ set(VERSION_MINOR 0) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) -set(SGF_FILES_SRC sgf.c) +set(SGF_FILES_SRC sgf_device.c sgf_estimator.c) set(SGF_FILES_INC_API sgf.h) +set(SGF_FILES_INC sgf_device_c.h) set(SGF_FILES_DOC COPYING.en COPYING.fr README.md) # Prepend each file in the `SGF_FILES_<SRC|INC>' list by `SGF_SOURCE_DIR' rcmake_prepend_path(SGF_FILES_SRC ${SGF_SOURCE_DIR}) rcmake_prepend_path(SGF_FILES_INC_API ${SGF_SOURCE_DIR}) +rcmake_prepend_path(SGF_FILES_INC ${SGF_SOURCE_DIR}) rcmake_prepend_path(SGF_FILES_DOC ${PROJECT_SOURCE_DIR}/../) -add_library(sgf SHARED ${SGF_FILES_SRC} ${SGF_FILES_INC_API}) +add_library(sgf SHARED ${SGF_FILES_SRC} ${SGF_FILES_INC_API} ${SGF_FILES_INC}) set_target_properties(sgf PROPERTIES DEFINE_SYMBOL SGF_SHARED_BUILD VERSION ${VERSION} diff --git a/src/sgf.c b/src/sgf.c @@ -1,459 +0,0 @@ -/* Copyright (C) |Meso|Star> 2015 (contact@meso-star.com) - * - * This software is governed by the CeCILL license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/or redistribute the software under the terms of the CeCILL - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL license and that you accept its terms. */ - -#include "sgf.h" - -#include <star/s3d.h> -#include <star/ssp.h> - -#include <rsys/dynamic_array.h> -#include <rsys/logger.h> -#include <rsys/mem_allocator.h> -#include <rsys/ref_count.h> - -struct accum { - double radiative_flux; - double sqr_radiative_flux; -}; - -#define DARRAY_NAME accum -#define DARRAY_DATA struct accum -#include <rsys/dynamic_array.h> - -struct sgf_device { - int verbose; - struct logger* logger; - - struct mem_allocator* allocator; - ref_T ref; -}; - -struct sgf_estimator { - struct darray_accum buf; - size_t nsteps; - - struct sgf_device* dev; - ref_T ref; -}; - -/* Define how many failures are handled until an error occurs */ -#define MAX_FAILURES 10 - -/******************************************************************************* - * Helper function - ******************************************************************************/ -static res_T -estimator_create(struct sgf_device* dev, struct sgf_estimator** out_estimator) -{ - struct sgf_estimator* estimator = NULL; - res_T res = RES_OK; - ASSERT(dev && out_estimator); - - estimator = MEM_CALLOC(dev->allocator, 1, sizeof(struct sgf_estimator)); - if(!estimator) { - if(dev->verbose) { - logger_print(dev->logger, LOG_ERROR, -"Not enough memory space: couldn't allocato the Gebhart Factor estimator.\n"); - } - res = RES_MEM_ERR; - goto error; - } - ref_init(&estimator->ref); - SGF(device_ref_get(dev)); - estimator->dev = dev; - darray_accum_init(dev->allocator, &estimator->buf); - -exit: - *out_estimator = estimator; - return res; - -error: - if(estimator) { - SGF(estimator_ref_put(estimator)); - estimator = NULL; - } - goto exit; -} - -static INLINE char -check_scene_desc(struct sgf_scene_desc* desc) -{ - ASSERT(desc); - return desc->get_material_property && desc->scene; -} - -static FINLINE void -accum_weight - (struct sgf_estimator* estimator, - const size_t iface, - const double weight) -{ - struct accum* acc; - ASSERT(estimator); - acc = darray_accum_data_get(&estimator->buf) + iface; - acc->radiative_flux += weight; - acc->sqr_radiative_flux += weight * weight; -} - -/* Return RES_BAD_OP on numerical issue. i.e. the radiative path is skipped */ -static res_T -gebhart_radiative_path - (struct sgf_estimator* estimator, - struct ssp_rng* rng, - const size_t primitive_id, - struct sgf_scene_desc* desc) -{ - struct s3d_hit hit; - struct s3d_primitive prim_from, prim; - struct s3d_attrib attrib; - float st[2] = {0.f, 0.f}; - double sky_gebhart = 0.0; - double transmissivity = 1.0; - double proba_reflec_spec; - double emissivity; - double reflectivity; - double specularity; -#ifndef NDEBUG - double sum_radiative_flux = 0.f; -#endif - const double trans_min = 1.e-8; - size_t nprims; - float vec0[3]; - float normal[3]; - float pos[3]; - float dir[4]; - float range[2]; - - /* Discard faces with no emissivity */ - emissivity = desc->get_material_property(desc->material, - SGF_MATERIAL_EMISSIVITY, primitive_id, 0); - if(emissivity <= 0.0) - return RES_OK; - - S3D(scene_primitives_count(desc->scene, &nprims)); - S3D(scene_get_primitive(desc->scene, (unsigned)primitive_id, &prim)); - - /* Compute the geometric normal of the primitive */ - S3D(primitive_get_attrib(&prim, S3D_GEOMETRY_NORMAL, st, &attrib)); - f3_normalize(normal, attrib.value); - - /* Uniformly sample the primitive to define the starting position of the - * radiative path */ - S3D(primitive_sample - (&prim, (float)ssp_rng_canonical(rng), (float)ssp_rng_canonical(rng), st)); - S3D(primitive_get_attrib(&prim, S3D_POSITION, st, &attrib)); - f3_set(pos, attrib.value); - - /* Cosine weighted sampling of the starting radiative path direction around - * `normal' */ - ssp_ran_hemisphere_cos(rng, normal, dir); - - transmissivity = 1.0; - sky_gebhart = 0.0; - - for(;;) { /* Here we go */ - prim_from = prim; - - range[0] = 1.e-6f, range[1] = FLT_MAX; - for(;;) { - S3D(scene_trace_ray(desc->scene, pos, dir, range, &hit)); - if(S3D_PRIMITIVE_EQ(&hit.prim, &prim_from)) range[0] += 1.e-6f; /* Self hit */ - else break; - } - - if(S3D_HIT_NONE(&hit)) { - sky_gebhart += transmissivity; - break; - } - f3_normalize(normal, hit.normal); - - /* Retrieve the hit position and the hit primitive id */ - f3_mulf(vec0, dir, hit.distance); - f3_add(pos, vec0, pos); - prim = hit.prim; - - /* Fetch material property */ - emissivity = desc->get_material_property(desc->material, - SGF_MATERIAL_EMISSIVITY, prim.scene_prim_id, 0); - specularity = desc->get_material_property(desc->material, - SGF_MATERIAL_SPECULARITY, prim.scene_prim_id, 0); - reflectivity = desc->get_material_property(desc->material, - SGF_MATERIAL_REFLECTIVITY, prim.scene_prim_id, 0); - - if(transmissivity > trans_min) { - const double weight = transmissivity * emissivity; - accum_weight(estimator, prim.scene_prim_id, weight); - transmissivity = transmissivity * (1.0 - emissivity); -#ifndef NDEBUG - sum_radiative_flux += weight; -#endif - } else { - /* Russian roulette */ - if(ssp_rng_canonical(rng) < emissivity) { - const double weight = transmissivity; - accum_weight(estimator, prim.scene_prim_id, weight); -#ifndef NDEBUG - sum_radiative_flux += weight; -#endif - break; - } - } - - if(reflectivity > 0.0) { - proba_reflec_spec = specularity / reflectivity; - } else { - break; - } - if(ssp_rng_canonical(rng) >= proba_reflec_spec) { /* Diffuse reflection */ - ssp_ran_hemisphere_cos(rng, normal, dir); - ASSERT(f3_dot(normal, dir) > 0); - } else { /* Specular reflection */ - const float tmp = -2.f * f3_dot(dir, normal); - ASSERT(0); - f3_mulf(vec0, normal, tmp); - f3_add(dir, dir, vec0); - f3_normalize(dir, dir); - } - } -#if !defined(NDEBUG) - /* Check the energy conservation property */ - ASSERT(eq_eps(sum_radiative_flux + sky_gebhart, 1.0, 1.e6)); -#endif - - return RES_OK; -} - -static void -device_release(ref_T* ref) -{ - struct sgf_device* dev; - ASSERT(ref); - dev = CONTAINER_OF(ref, struct sgf_device, ref); - MEM_RM(dev->allocator, dev); -} - -static void -estimator_release(ref_T* ref) -{ - struct sgf_device* dev; - struct sgf_estimator* estimator; - ASSERT(ref); - estimator = CONTAINER_OF(ref, struct sgf_estimator, ref); - darray_accum_release(&estimator->buf); - dev = estimator->dev; - MEM_RM(dev->allocator, estimator); - SGF(device_ref_put(dev)); -} - -/******************************************************************************* - * API functions - ******************************************************************************/ -res_T -sgf_device_create - (struct logger* log, - struct mem_allocator* allocator, - const int verbose, - struct sgf_device** out_dev) -{ - struct mem_allocator* mem_allocator; - struct sgf_device* dev = NULL; - struct logger* logger = NULL; - res_T res = RES_OK; - - if(!out_dev) { - res = RES_BAD_ARG; - goto error; - } - - mem_allocator = allocator ? allocator : &mem_default_allocator; - logger = log ? log : LOGGER_DEFAULT; - - dev = MEM_CALLOC(mem_allocator, 1, sizeof(struct sgf_device)); - if(!dev) { - if(verbose) { - logger_print(logger, LOG_ERROR, - "Couldn't allocate the Star-Gebhart-Factor device.\n"); - } - res = RES_MEM_ERR; - goto error; - } - ref_init(&dev->ref); - dev->allocator = mem_allocator; - dev->logger = logger; - dev->verbose = verbose; - -exit: - if(out_dev) *out_dev = dev; - return res; -error: - if(dev) { - SGF(device_ref_put(dev)); - dev = NULL; - } - goto exit; -} - -res_T -sgf_device_ref_get(struct sgf_device* dev) -{ - if(!dev) return RES_BAD_ARG; - ref_get(&dev->ref); - return RES_OK; -} - -res_T -sgf_device_ref_put(struct sgf_device* dev) -{ - if(!dev) return RES_BAD_ARG; - ref_put(&dev->ref, device_release); - return RES_OK; -} - -res_T -sgf_integrate - (struct sgf_device* dev, - struct ssp_rng* rng, - const size_t primitive_id, - struct sgf_scene_desc* desc, - const size_t steps_count, - struct sgf_estimator** out_estimator) -{ - struct sgf_estimator* estimator = NULL; - size_t istep = 0; - size_t nprims; - int mask; - res_T res = RES_OK; - - if(!dev || !steps_count || !rng || !desc || !check_scene_desc(desc) - || !out_estimator) { - res = RES_BAD_ARG; - goto error; - } - - res = estimator_create(dev, &estimator); - if(res != RES_OK) goto error; - - /* Check scene active sessions */ - S3D(scene_get_session_mask(desc->scene, &mask)); - if(!(mask & S3D_TRACE) || !(mask & S3D_GET_PRIMITIVE)) { - if(dev->verbose) { - logger_print(dev->logger, LOG_ERROR, - "No active trace/get_primitive session on the Star-3D scene\n"); - } - res = RES_BAD_ARG; - goto error; - } - - /* Check submitted primitive_id */ - S3D(scene_primitives_count(desc->scene, &nprims)); - if(primitive_id >= nprims) { - if(dev->verbose) { - logger_print(dev->logger, LOG_ERROR, - "Invalid primitive index `%lu'\n", (unsigned long)primitive_id); - } - res = RES_BAD_ARG; - goto error; - } - - /* Allocate internal memory space */ - res = darray_accum_resize(&estimator->buf, nprims*desc->spectral_bands_count); - if(res != RES_OK) { - if(dev->verbose) { - logger_print(dev->logger, LOG_ERROR, -"Couldn't allocate the Gebhart Factor integration data structure.\n"); - } - goto error; - } - memset(darray_accum_data_get(&estimator->buf), 0, - darray_accum_size_get(&estimator->buf)*sizeof(struct accum)); - - /* Here we go ! Perform the Gebhart factor integration. - * TODO interate per spectral bands */ - FOR_EACH(istep, 0, steps_count) { - res = gebhart_radiative_path(estimator, rng, primitive_id, desc); - if(res != RES_OK) goto error; - } - estimator->nsteps = steps_count; - -exit: - if(out_estimator) *out_estimator = estimator; - return res; -error: - if(estimator) SGF(estimator_ref_put(estimator)); - goto exit; -} - -res_T -sgf_estimator_ref_get(struct sgf_estimator* estimator) -{ - if(!estimator) return RES_BAD_ARG; - ref_get(&estimator->ref); - return RES_OK; -} - -res_T -sgf_estimator_ref_put(struct sgf_estimator* estimator) -{ - if(!estimator) return RES_BAD_ARG; - ref_put(&estimator->ref, estimator_release); - return RES_OK; -} - -res_T -sgf_estimator_get_status - (struct sgf_estimator* estimator, - const size_t iprimitive, - struct sgf_status* status) -{ - struct accum* acc; - size_t nsteps; - - if(!estimator || !status) - return RES_BAD_ARG; - - if(iprimitive >= darray_accum_size_get(&estimator->buf)) { - if(estimator->dev->verbose) { - logger_print(estimator->dev->logger, LOG_ERROR, - "Invalid primitive index `%lu'\n", iprimitive); - } - return RES_BAD_ARG; - } - - acc = darray_accum_data_get(&estimator->buf) + iprimitive; - nsteps = estimator->nsteps; - - status->E = acc->radiative_flux / (double)nsteps; - status->V = - acc->sqr_radiative_flux / (double)nsteps - - (acc->radiative_flux * acc->radiative_flux) / (double)(nsteps * nsteps); - status->SE = sqrt(status->V / (double)nsteps); - status->nsteps = nsteps; - - return RES_OK; -} - diff --git a/src/sgf.h b/src/sgf.h @@ -78,6 +78,7 @@ static const struct sgf_scene_desc SGF_SCENE_DESC_NULL = { NULL, NULL, 0, 0 }; +/* Estimated Gebart Factor between 2 faces */ struct sgf_status { double E; /* Expected value */ double V; /* Variance */ @@ -85,9 +86,13 @@ struct sgf_status { size_t nsteps; /* #realisations */ }; +/* Opaque types */ struct sgf_device; struct sgf_estimator; +/******************************************************************************* + * Star Gebhart Factor API + ******************************************************************************/ BEGIN_DECLS SGF_API res_T @@ -105,6 +110,7 @@ SGF_API res_T sgf_device_ref_put (struct sgf_device* dev); +/* TODO comment it */ SGF_API res_T sgf_integrate (struct sgf_device* dev, diff --git a/src/sgf_device.c b/src/sgf_device.c @@ -0,0 +1,110 @@ +/* Copyright (C) |Meso|Star> 2015 (contact@meso-star.com) + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "sgf.h" +#include "sgf_device_c.h" + +#include <rsys/logger.h> +#include <rsys/mem_allocator.h> + +/******************************************************************************* + * Helper function + ******************************************************************************/ +static void +device_release(ref_T* ref) +{ + struct sgf_device* dev; + ASSERT(ref); + dev = CONTAINER_OF(ref, struct sgf_device, ref); + MEM_RM(dev->allocator, dev); +} + +/******************************************************************************* + * API functions + ******************************************************************************/ +res_T +sgf_device_create + (struct logger* log, + struct mem_allocator* allocator, + const int verbose, + struct sgf_device** out_dev) +{ + struct mem_allocator* mem_allocator; + struct sgf_device* dev = NULL; + struct logger* logger = NULL; + res_T res = RES_OK; + + if(!out_dev) { + res = RES_BAD_ARG; + goto error; + } + + mem_allocator = allocator ? allocator : &mem_default_allocator; + logger = log ? log : LOGGER_DEFAULT; + + dev = MEM_CALLOC(mem_allocator, 1, sizeof(struct sgf_device)); + if(!dev) { + if(verbose) { + logger_print(logger, LOG_ERROR, + "Couldn't allocate the Star-Gebhart-Factor device.\n"); + } + res = RES_MEM_ERR; + goto error; + } + ref_init(&dev->ref); + dev->allocator = mem_allocator; + dev->logger = logger; + dev->verbose = verbose; + +exit: + if(out_dev) *out_dev = dev; + return res; +error: + if(dev) { + SGF(device_ref_put(dev)); + dev = NULL; + } + goto exit; +} + +res_T +sgf_device_ref_get(struct sgf_device* dev) +{ + if(!dev) return RES_BAD_ARG; + ref_get(&dev->ref); + return RES_OK; +} + +res_T +sgf_device_ref_put(struct sgf_device* dev) +{ + if(!dev) return RES_BAD_ARG; + ref_put(&dev->ref, device_release); + return RES_OK; +} + diff --git a/src/sgf_device_c.h b/src/sgf_device_c.h @@ -0,0 +1,49 @@ +/* Copyright (C) |Meso|Star> 2015 (contact@meso-star.com) + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#ifndef SGF_DEVICE_C_H +#define SGF_DEVICE_C_H + +#include <rsys/ref_count.h> + +struct logger; +struct mem_allocator; + +struct sgf_device { + int verbose; + struct logger* logger; + + struct mem_allocator* allocator; + ref_T ref; +}; + +/* TODO implement a print function that print messages with respect to the + * device verbosity */ + +#endif /* SGF_DEVICE_C_H */ + diff --git a/src/sgf_estimator.c b/src/sgf_estimator.c @@ -0,0 +1,373 @@ +/* Copyright (C) |Meso|Star> 2015 (contact@meso-star.com) + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "sgf.h" +#include "sgf_device_c.h" + +#include <star/s3d.h> +#include <star/ssp.h> + +#include <rsys/dynamic_array.h> +#include <rsys/logger.h> +#include <rsys/mem_allocator.h> +#include <rsys/ref_count.h> + +struct accum { + double radiative_flux; + double sqr_radiative_flux; +}; + +#define DARRAY_NAME accum +#define DARRAY_DATA struct accum +#include <rsys/dynamic_array.h> + +/* Estimator of the Gebhart Factor of a given face to all the other ones */ +struct sgf_estimator { + struct darray_accum buf; /* Integrated radiative flux of each primitive */ + size_t nsteps; /* Number of realisations */ + + struct sgf_device* dev; + ref_T ref; +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE char +check_scene_desc(struct sgf_scene_desc* desc) +{ + ASSERT(desc); + return desc->get_material_property && desc->scene; +} + +static FINLINE void +accum_weight + (struct sgf_estimator* estimator, + const size_t iface, + const double weight) +{ + struct accum* acc; + ASSERT(estimator); + acc = darray_accum_data_get(&estimator->buf) + iface; + acc->radiative_flux += weight; + acc->sqr_radiative_flux += weight * weight; +} + +/* Return RES_BAD_OP on numerical issue. i.e. the radiative path is skipped */ +static res_T +gebhart_radiative_path + (struct sgf_estimator* estimator, + struct ssp_rng* rng, + const size_t primitive_id, + struct sgf_scene_desc* desc) +{ + struct s3d_hit hit; + struct s3d_primitive prim_from, prim; + struct s3d_attrib attrib; + + const double trans_min = 1.e-8; /* Minimum transmissivity threshold */ + double outside_radiative_flux = 0.0; + double proba_reflec_spec; + double transmissivity, emissivity, reflectivity, specularity; +#ifndef NDEBUG + double sum_radiative_flux = 0.f; +#endif + + float st[2] = {0.f, 0.f}; /* Parametric coordinate of the sampled position */ + float vec0[3]; /* Temporary vector */ + float normal[3]; /* Geometric normal */ + float pos[3]; /* Radiative path position */ + float dir[4]; /* Radiative path direction. dir[3] <=> sampled dir pdf */ + float range[2]; /* Traced ray range */ + + /* Discard faces with no emissivity */ + emissivity = desc->get_material_property(desc->material, + SGF_MATERIAL_EMISSIVITY, primitive_id, 0); + if(emissivity <= 0.0) + return RES_OK; + + /* Get the geometric normal of the input primitive */ + S3D(scene_get_primitive(desc->scene, (unsigned)primitive_id, &prim)); + S3D(primitive_get_attrib(&prim, S3D_GEOMETRY_NORMAL, st, &attrib)); + f3_normalize(normal, attrib.value); + + /* Uniformly sample the primitive to define the starting position of the + * radiative path */ + S3D(primitive_sample(&prim, + (float)ssp_rng_canonical(rng), (float)ssp_rng_canonical(rng), st)); + S3D(primitive_get_attrib(&prim, S3D_POSITION, st, &attrib)); + f3_set(pos, attrib.value); + + /* Cosine weighted sampling of the radiative path direction around `normal' */ + ssp_ran_hemisphere_cos(rng, normal, dir); + + transmissivity = 1.0; + outside_radiative_flux = 0.0; + for(;;) { /* Here we go */ + + prim_from = prim; + range[0] = 1.e-6f, range[1] = FLT_MAX; + + for(;;) { + S3D(scene_trace_ray(desc->scene, pos, dir, range, &hit)); + if(S3D_PRIMITIVE_EQ(&hit.prim, &prim_from)) range[0] += 1.e-6f; /* Self hit */ + else break; + } + + if(S3D_HIT_NONE(&hit)) { /* The ray is outside the volume */ + outside_radiative_flux += transmissivity; + break; + } + + /* Retrieve the hit position and the hit primitive id */ + f3_mulf(vec0, dir, hit.distance); + f3_add(pos, vec0, pos); + prim = hit.prim; + + /* Fetch material property */ + emissivity = desc->get_material_property(desc->material, + SGF_MATERIAL_EMISSIVITY, prim.scene_prim_id, 0); + specularity = desc->get_material_property(desc->material, + SGF_MATERIAL_SPECULARITY, prim.scene_prim_id, 0); + reflectivity = desc->get_material_property(desc->material, + SGF_MATERIAL_REFLECTIVITY, prim.scene_prim_id, 0); + + if(transmissivity > trans_min) { + const double weight = transmissivity * emissivity; + accum_weight(estimator, prim.scene_prim_id, weight); + transmissivity = transmissivity * (1.0 - emissivity); +#ifndef NDEBUG + sum_radiative_flux += weight; +#endif + } else { + /* Russian roulette */ + if(ssp_rng_canonical(rng) < emissivity) { + const double weight = transmissivity; + accum_weight(estimator, prim.scene_prim_id, weight); +#ifndef NDEBUG + sum_radiative_flux += weight; +#endif + break; + } + } + + if(reflectivity <= 0.0) + break; + + proba_reflec_spec = specularity / reflectivity; + f3_normalize(normal, hit.normal); + + if(ssp_rng_canonical(rng) >= proba_reflec_spec) { /* Diffuse reflection */ + ssp_ran_hemisphere_cos(rng, normal, dir); + ASSERT(f3_dot(normal, dir) > 0); + } else { /* Specular reflection */ + const float tmp = -2.f * f3_dot(dir, normal); + ASSERT(0); + f3_mulf(vec0, normal, tmp); + f3_add(dir, dir, vec0); + f3_normalize(dir, dir); + } + } +#if !defined(NDEBUG) + /* Check the energy conservation property */ + ASSERT(eq_eps(sum_radiative_flux + outside_radiative_flux, 1.0, 1.e6)); +#endif + return RES_OK; +} + +static res_T +estimator_create(struct sgf_device* dev, struct sgf_estimator** out_estimator) +{ + struct sgf_estimator* estimator = NULL; + res_T res = RES_OK; + ASSERT(dev && out_estimator); + + estimator = MEM_CALLOC(dev->allocator, 1, sizeof(struct sgf_estimator)); + if(!estimator) { + if(dev->verbose) { + logger_print(dev->logger, LOG_ERROR, +"Not enough memory space: couldn't allocato the Gebhart Factor estimator.\n"); + } + res = RES_MEM_ERR; + goto error; + } + ref_init(&estimator->ref); + SGF(device_ref_get(dev)); + estimator->dev = dev; + darray_accum_init(dev->allocator, &estimator->buf); + +exit: + *out_estimator = estimator; + return res; + +error: + if(estimator) { + SGF(estimator_ref_put(estimator)); + estimator = NULL; + } + goto exit; +} + +static void +estimator_release(ref_T* ref) +{ + struct sgf_device* dev; + struct sgf_estimator* estimator; + ASSERT(ref); + estimator = CONTAINER_OF(ref, struct sgf_estimator, ref); + darray_accum_release(&estimator->buf); + dev = estimator->dev; + MEM_RM(dev->allocator, estimator); + SGF(device_ref_put(dev)); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +sgf_integrate + (struct sgf_device* dev, + struct ssp_rng* rng, + const size_t primitive_id, + struct sgf_scene_desc* desc, + const size_t steps_count, + struct sgf_estimator** out_estimator) +{ + struct sgf_estimator* estimator = NULL; + size_t istep = 0; + size_t nprims; + int mask; + res_T res = RES_OK; + + if(!dev || !steps_count || !rng || !desc || !check_scene_desc(desc) + || !out_estimator) { + res = RES_BAD_ARG; + goto error; + } + + res = estimator_create(dev, &estimator); + if(res != RES_OK) goto error; + + /* Check scene active sessions */ + S3D(scene_get_session_mask(desc->scene, &mask)); + if(!(mask & S3D_TRACE) || !(mask & S3D_GET_PRIMITIVE)) { + if(dev->verbose) { + logger_print(dev->logger, LOG_ERROR, + "No active trace/get_primitive session on the Star-3D scene\n"); + } + res = RES_BAD_ARG; + goto error; + } + + /* Check submitted primitive_id */ + S3D(scene_primitives_count(desc->scene, &nprims)); + if(primitive_id >= nprims) { + if(dev->verbose) { + logger_print(dev->logger, LOG_ERROR, + "Invalid primitive index `%lu'\n", (unsigned long)primitive_id); + } + res = RES_BAD_ARG; + goto error; + } + + /* Allocate internal memory space */ + res = darray_accum_resize(&estimator->buf, nprims*desc->spectral_bands_count); + if(res != RES_OK) { + if(dev->verbose) { + logger_print(dev->logger, LOG_ERROR, +"Couldn't allocate the Gebhart Factor integration data structure.\n"); + } + goto error; + } + memset(darray_accum_data_get(&estimator->buf), 0, + darray_accum_size_get(&estimator->buf)*sizeof(struct accum)); + + /* Invoke the Gebhart factor integration. TODO interate per spectral bands */ + FOR_EACH(istep, 0, steps_count) { + res = gebhart_radiative_path(estimator, rng, primitive_id, desc); + if(res != RES_OK) goto error; + } + estimator->nsteps = steps_count; + +exit: + if(out_estimator) *out_estimator = estimator; + return res; +error: + if(estimator) SGF(estimator_ref_put(estimator)); + goto exit; +} + +res_T +sgf_estimator_ref_get(struct sgf_estimator* estimator) +{ + if(!estimator) return RES_BAD_ARG; + ref_get(&estimator->ref); + return RES_OK; +} + +res_T +sgf_estimator_ref_put(struct sgf_estimator* estimator) +{ + if(!estimator) return RES_BAD_ARG; + ref_put(&estimator->ref, estimator_release); + return RES_OK; +} + +res_T +sgf_estimator_get_status + (struct sgf_estimator* estimator, + const size_t iprimitive, + struct sgf_status* status) +{ + struct accum* acc; + size_t nsteps; + + if(!estimator || !status) + return RES_BAD_ARG; + + if(iprimitive >= darray_accum_size_get(&estimator->buf)) { + if(estimator->dev->verbose) { + logger_print(estimator->dev->logger, LOG_ERROR, + "Invalid primitive index `%lu'\n", iprimitive); + } + return RES_BAD_ARG; + } + + acc = darray_accum_data_get(&estimator->buf) + iprimitive; + nsteps = estimator->nsteps; + + status->E = acc->radiative_flux / (double)nsteps; /* Expected value */ + status->V = /* Variance */ + acc->sqr_radiative_flux / (double)nsteps + - (acc->radiative_flux * acc->radiative_flux) / (double)(nsteps * nsteps); + status->SE = sqrt(status->V / (double)nsteps); /* Standard error */ + status->nsteps = nsteps; /* # realisations */ + + return RES_OK; +} + diff --git a/src/test_sgf_cube.c b/src/test_sgf_cube.c @@ -37,7 +37,7 @@ #include <omp.h> -#define NSTEPS 100000L +#define NSTEPS 10000L /* * Analytic Gebhart Factor matrix