star-4v_s

An invariant property of diffuse random walks
git clone git://git.meso-star.fr/star-4v_s.git
Log | Files | Refs | README | LICENSE

commit 2a7b25faa69b6261e2a04b78ec8870f731974a87
parent c19561bc509f9730aee1017b19f7f302c5fd0ece
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Thu, 31 Mar 2016 10:11:25 +0200

Move from handmade openMP parallelization to Star-MC

Stop using rsys/logger too

Diffstat:
Mcmake/CMakeLists.txt | 20+++++++-------------
Asrc/realization.c | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/realization.h | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/s4vs.c | 235++++++++++++-------------------------------------------------------------------
4 files changed, 233 insertions(+), 212 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -34,16 +34,12 @@ set(S4VS_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src/) ################################################################################ # Check dependencies ################################################################################ -find_package(OpenMP) find_package(RCMake 0.2 REQUIRED) find_package(RSys 0.2.1 REQUIRED) find_package(Star3D 0.3 REQUIRED) find_package(Star3DAW 0.1.2 REQUIRED) -find_package(StarSP 0.2 REQUIRED) - -if(NOT OPENMP_FOUND) - message(STATUS "No OpenMP support: muti-threading is disabled") -endif() +find_package(StarSP 0.3 REQUIRED) +find_package(StarMC 0.3 REQUIRED) include_directories( ${RSys_INCLUDE_DIR} @@ -62,24 +58,22 @@ set(VERSION_MINOR 1) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) -set(S4VS_FILES_SRC s4vs.c) +set(S4VS_FILES_SRC s4vs.c realization.c) +set(S4VS_FILES_INC realization.h) set(S4VS_FILES_DOC COPYING.fr COPYING.en README.md) # Prepend each file in the `S4VS_FILES_<SRC|INC>' list by the absolute # path of the directory in which they lie rcmake_prepend_path(S4VS_FILES_SRC ${S4VS_SOURCE_DIR}) +rcmake_prepend_path(S4VS_FILES_INC ${S4VS_SOURCE_DIR}) rcmake_prepend_path(S4VS_FILES_DOC ${PROJECT_SOURCE_DIR}/../) -if(OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") -endif() - if(CMAKE_COMPILER_IS_GNUCC) set(MATH_LIB m) endif() -add_executable(s4vs ${S4VS_FILES_SRC}) -target_link_libraries(s4vs RSys Star3D Star3DAW StarSP ${MATH_LIB}) +add_executable(s4vs ${S4VS_FILES_SRC} ${S4VS_FILES_INC}) +target_link_libraries(s4vs RSys Star3D Star3DAW StarSP StarMC ${MATH_LIB}) set_target_properties(s4vs PROPERTIES VERSION ${VERSION} SOVERSION ${VERSION_MAJOR}) diff --git a/src/realization.c b/src/realization.c @@ -0,0 +1,140 @@ +/* Copyright (C) |Meso|Star> 2015-2016 (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 <rsys/float3.h> + +#include <star/s3d.h> +#include <star/ssp.h> +#include <star/smc.h> + +#include "realization.h" + +/* Maximum number of failures before an error occurs */ +#define MAX_FAILURES 10 + +void +realization(void* length, struct ssp_rng* rng, void* context) +{ + struct context* ctx = (struct context*)context; + struct s3d_attrib attrib; + struct s3d_primitive prim; + double lambda; + float normal[3], direction[4], origin[3], range[2], st[2]; + struct s3d_hit hit; + int nfailures = 0; + + SMC_DOUBLE(length) = 0; + + /* sample a starting point on the scene surface to start the random walk from */ + for(;;) { + /* sample the scene */ + float u, v, w; + do { u = (float)ssp_rng_canonical(rng); } while(u >= 1); + do { v = (float)ssp_rng_canonical(rng); } while(v >= 1); + do { w = (float)ssp_rng_canonical(rng); } while(w >= 1); + /* get a location: primitive ID and parametric coordinates */ + S3D(scene_sample(ctx->scene, u, v, w, &prim, st)); + + /* retrieve the sampled geometric normal and position */ + S3D(primitive_get_attrib(&prim, S3D_GEOMETRY_NORMAL, st, &attrib)); + f3_normalize(normal, attrib.value); + S3D(primitive_get_attrib(&prim, S3D_POSITION, st, &attrib)); + f3_set(origin, attrib.value); + + /* cosine weighted sampling of the hemisphere around the sampled normal */ + ssp_ran_hemisphere_cos(rng, normal, direction); + + /* trace a ray from the sampled location */ + range[0] = 1.e-6f; range[1] = FLT_MAX; + for(;;) { + /* find first intersection with geometry to determine its distance */ + S3D(scene_trace_ray(ctx->scene, origin, direction, range, &hit)); + /* handle auto-intersection: hitting the same primitive means auto-intersection */ + if(S3D_PRIMITIVE_EQ(&hit.prim, &prim)) { + /* just increase the minimum distance and retry */ + range[0] += 1.e-6f; + } + else break; /* standard case: OK */ + } + + f3_normalize(hit.normal, hit.normal); + + /* handle precision issues (no intersection means geometry leakage: error) */ + if(S3D_HIT_NONE(&hit)) { + if(++nfailures >= MAX_FAILURES) { + /* too many errors: stop */ + ctx->exit_failure = 1; + break; + } + } + else break; /* standard case: OK */ + } + if(ctx->exit_failure) + return; + + /* here the starting point and a propagation direction have been sampled */ + /* and we have determined the distance of the geometry in this direction */ + + /* start diffuse random walk */ + for(;;) { + /* sample a free path according to ks */ + lambda = ssp_ran_exp(rng, ctx->ks); + if(lambda >= hit.distance) { + /* lambda exceeds the geometry distance: the random walk ends on geometry */ + SMC_DOUBLE(length) += hit.distance; + break; /* the standard way of leaving the loop */ + } else { + /* lambda doesn't exceed the geometry distance */ + /* the random walk can continue after a diffusion */ + SMC_DOUBLE(length) += lambda; + /* new location after lambda */ + f3_add(origin, origin, f3_mulf(direction, direction, (float)lambda)); + + do { + /* sample a new direction */ + ssp_ran_sphere_uniform(rng, direction); + range[0] = 0; + /* find the first intersection with the geometry */ + /* to refresh the geometry's distance */ + S3D(scene_trace_ray(ctx->scene, origin, direction, range, &hit)); + + /* handle precision issues (no intersection means geometry leakage: error) */ + if(S3D_HIT_NONE(&hit)) { + if(++nfailures >= MAX_FAILURES) { + /* too many errors: stop */ + ctx->exit_failure = 1; + break; + } + } + /* should be an infinite loop, or there is a geometry leakage */ + } while(S3D_HIT_NONE(&hit)); + } + } + + ATOMIC_ADD(&ctx->nfailures, nfailures); +} diff --git a/src/realization.h b/src/realization.h @@ -0,0 +1,50 @@ +/* Copyright (C) |Meso|Star> 2015-2016 (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 REALIZATION_H_ +#define REALIZATION_H_ + +/* forward definition */ +struct ssp_rng; + +struct context { + struct s3d_sampler* sampler; + struct s3d_scene* scene; + double ks; + int nfailures; + char exit_failure; +}; + +/******************************************************************************* + * MC realization function + ******************************************************************************/ + +void +realization(void* length, struct ssp_rng* rng, void* context); + +#endif diff --git a/src/s4vs.c b/src/s4vs.c @@ -28,171 +28,42 @@ #include <rsys/clock_time.h> #include <rsys/float3.h> -#include <rsys/logger.h> +#include <rsys/mem_allocator.h> #include <star/s3d.h> #include <star/s3daw.h> -#include <star/ssp.h> +#include <star/smc.h> -#include <omp.h> - -/* Maximum number of failures before an error occurs */ -#define MAX_FAILURES 10 - -/******************************************************************************* - * Helper function - ******************************************************************************/ -struct context { - struct s3d_sampler* sampler; - struct s3d_scene* scene; - double ks; - double sum; - double sum2; - int nfailures; - char exit_failure; -}; - -static void -realization(struct ssp_rng* rng, struct context* ctx) -{ - struct s3d_attrib attrib; - struct s3d_primitive prim; - double lambda, sum = 0; - float normal[3], direction[4], origin[3], range[2], st[2]; - struct s3d_hit hit; - int nfailures = 0; - - /* sample a starting point on the scene surface to start the random walk from */ - for(;;) { - /* sample the scene */ - float u, v, w; - do { u = (float)ssp_rng_canonical(rng); } while(u >= 1); - do { v = (float)ssp_rng_canonical(rng); } while(v >= 1); - do { w = (float)ssp_rng_canonical(rng); } while(w >= 1); - /* get a location: primitive ID and parametric coordinates */ - S3D(scene_sample(ctx->scene, u, v, w, &prim, st)); - - /* retrieve the sampled geometric normal and position */ - S3D(primitive_get_attrib(&prim, S3D_GEOMETRY_NORMAL, st, &attrib)); - f3_normalize(normal, attrib.value); - S3D(primitive_get_attrib(&prim, S3D_POSITION, st, &attrib)); - f3_set(origin, attrib.value); - - /* cosine weighted sampling of the hemisphere around the sampled normal */ - ssp_ran_hemisphere_cos(rng, normal, direction); - - /* trace a ray from the sampled location */ - range[0] = 1.e-6f; range[1] = FLT_MAX; - for(;;) { - /* find first intersection with geometry to determine its distance */ - S3D(scene_trace_ray(ctx->scene, origin, direction, range, &hit)); - /* handle auto-intersection: hitting the same primitive means auto-intersection */ - if(S3D_PRIMITIVE_EQ(&hit.prim, &prim)) { - /* just increase the minimum distance and retry */ - range[0] += 1.e-6f; - } - else break; /* standard case: OK */ - } - - f3_normalize(hit.normal, hit.normal); - - /* handle precision issues (no intersection means geometry leakage: error) */ - if(S3D_HIT_NONE(&hit)) { - if(++nfailures >= MAX_FAILURES) { - /* too many errors: stop */ - ctx->exit_failure = 1; - break; - } - } - else break; /* standard case: OK */ - } - if(ctx->exit_failure) - return; - - /* here the starting point and a propagation direction have been sampled */ - /* and we have determined the distance of the geometry in this direction */ - - /* start diffuse random walk */ - for(;;) { - /* sample a free path according to ks */ - lambda = ssp_ran_exp(rng, ctx->ks); - if(lambda >= hit.distance) { - /* lambda exceeds the geometry distance: the random walk ends on geometry */ - sum += hit.distance; - break; - } else { - /* lambda doesn't exceed the geometry distance */ - /* the random walk can continue after a diffusion */ - sum += lambda; - /* new location after lambda */ - f3_add(origin, origin, f3_mulf(direction, direction, (float)lambda)); - - do { - /* sample a new direction */ - ssp_ran_sphere_uniform(rng, direction); - range[0] = 0; - /* find the first intersection with the geometry */ - /* to refresh the geometry's distance */ - S3D(scene_trace_ray(ctx->scene, origin, direction, range, &hit)); - - /* handle precision issues (no intersection means geometry leakage: error) */ - if(S3D_HIT_NONE(&hit)) { - if(++nfailures >= MAX_FAILURES) { - /* too many errors: stop */ - ctx->exit_failure = 1; - break; - } - } - } while(S3D_HIT_NONE(&hit)); - } - } - /* accumulate results */ -#ifdef _OPENMP -#pragma omp atomic update - ctx->sum += sum; -#pragma omp atomic update - ctx->sum2 += sum * sum; -#pragma omp atomic update - ctx->nfailures += nfailures; -#else - ctx->sum += sum; - ctx->sum2 += sum * sum; - ctx->nfailures += nfailures; -#endif -} +#include "realization.h" static res_T compute_4v_s(struct s3d_scene* scene, const size_t max_steps, const double ks) { char buf[512]; struct time begin, end, elapsed; - struct s3d_shape* shape; struct context ctx; - double length, var, sig; float S, V, expected; - struct ssp_rng* rng; -#ifdef _OPENMP - struct ssp_rng_proxy* proxy; -#endif - unsigned i; + struct smc_device* smc; + struct smc_integrator integrator; + struct smc_estimator* estimator = NULL; + struct smc_estimator_status estimator_status; + double length; + double sig_length; res_T res = RES_OK; ASSERT(max_steps > 0 && ks > 0); - S3D(scene_instantiate(scene, &shape)); S3D(scene_begin_session(scene, S3D_SAMPLE|S3D_TRACE)); /* compute the expected result using a mesh-based method */ S3D(scene_compute_area(scene, &S)); S3D(scene_compute_volume(scene, &V)); if(eq_epsf(S, 0, 1.e-6f) || S < 0) { - logger_print(LOGGER_DEFAULT, LOG_ERROR, - "No surface to sample. Is the scene empty?\n"); + printf("No surface to sample. Is the scene empty?\n"); goto error; } if(eq_epsf(V, 0, 1.e-6f) || V < 0) { - logger_print(LOGGER_DEFAULT, LOG_ERROR, - "Invalid volume \"%.2f\". The scene might not match the prerequisites:\n" + printf("Invalid volume \"%.2f\". The scene might not match the prerequisites:\n" "it must be closed and its normals must point *into* the volume.\n", V); goto error; } @@ -203,42 +74,16 @@ compute_4v_s(struct s3d_scene* scene, const size_t max_steps, const double ks) ctx.nfailures = 0; ctx.exit_failure = 0; ctx.ks = ks; - ctx.sum = 0; - ctx.sum2 = 0; - - time_current(&begin); -#ifdef _OPENMP -#pragma omp parallel private(rng) - { - size_t me = (size_t)omp_get_thread_num(); - /* create a RNG proxy and a derived RNG by thread */ -#pragma omp single - { - size_t nb_t = (size_t)omp_get_num_threads(); - SSP(rng_proxy_create(NULL, &ssp_rng_threefry, nb_t, &proxy)); - } - SSP(rng_proxy_create_rng(proxy, me, &rng)); - -#pragma omp for - for (i = 0; i < max_steps; i++) { - realization(rng, &ctx); - } - SSP(rng_ref_put(rng)); -#pragma omp single - { - SSP(rng_proxy_ref_put(proxy)); - } - } -#else - /* single-threaded execution: create a single RNG */ - SSP(rng_create(NULL, &ssp_rng_threefry, &rng)); - for (i = 0; i < max_steps; i++) { - realization(rng, &ctx); - } - SSP(rng_ref_put(rng)); -#endif + /* setup Star-MC */ + integrator.integrand = &realization; /* realization function */ + integrator.type = &smc_double; /* realization type */ + integrator.max_steps = max_steps; /* realization count */ + time_current(&begin); + /* solve */ + SMC(device_create(NULL, NULL, 8, NULL, &smc)); + SMC(solve(smc, &integrator, &ctx, &estimator)); time_current(&end); time_sub(&elapsed, &end, &begin); time_dump(&elapsed, TIME_MIN|TIME_SEC|TIME_MSEC, NULL, buf, sizeof(buf)); @@ -246,36 +91,29 @@ compute_4v_s(struct s3d_scene* scene, const size_t max_steps, const double ks) S3D(scene_end_session(scene)); if(ctx.exit_failure) { - logger_print(LOGGER_DEFAULT, LOG_ERROR, - "Too many failures. The scene might not match the prerequisites: it must be\n" + printf("Too many failures. The scene might not match the prerequisites: it must be\n" "closed and its normals must point *into* the volume.\n"); goto error; } - /* compute mean length and standard error from MC results */ - length = ctx.sum / (double)max_steps; - var = ctx.sum2 / (double)max_steps - length * length; - /* numerical errors can trick us: if variance is negative pretend standard error is 0 */ - sig = var > 0 ? sqrt(var / (double)max_steps) : 0; - - logger_print(LOGGER_DEFAULT, LOG_OUTPUT, - "\n4V/S = %g ~ %g +/- %g\n# failures: %d/%lu\nElapsed time: %s\n", - expected, length, sig, ctx.nfailures, max_steps, buf); + /* get simulation results */ + SMC(estimator_get_status(estimator, &estimator_status)); + length = SMC_DOUBLE(estimator_status.E); + sig_length = SMC_DOUBLE(estimator_status.SE); -#ifdef _OPENMP - logger_print(LOGGER_DEFAULT, LOG_OUTPUT, - "On %d threads\n", omp_get_num_threads()); -#endif + printf("\n4V/S = %g ~ %g +/- %g\n# failures: %d/%lu\nElapsed time: %s\n", + expected, length, sig_length, ctx.nfailures, max_steps, buf); exit: /* clean data */ + SMC(device_ref_put(smc)); + SMC(estimator_ref_put(estimator)); if(scene) { int mask; S3D(scene_get_session_mask(scene, &mask)); if(mask) S3D(scene_end_session(scene)); S3D(scene_ref_put(scene)); } - if(shape) S3D(shape_ref_put(shape)); return res; error: @@ -328,18 +166,17 @@ int main(int argc, char *argv[]) { /* check command arguments */ if(argc < 2 || argc > 4) { if(argc < 2) - logger_print(LOGGER_DEFAULT, LOG_ERROR, "Argument required\n"); + printf("Argument required\n"); else - logger_print(LOGGER_DEFAULT, LOG_ERROR, "Too many arguments\n"); - logger_print(LOGGER_DEFAULT, LOG_OUTPUT, - "usage: %s OBJ_FILE [SAMPLES_COUNT [K_SCATTERING]]\n", argv[0]); + printf("Too many arguments\n"); + printf("usage: %s OBJ_FILE [SAMPLES_COUNT [K_SCATTERING]]\n", argv[0]); return RES_BAD_ARG; } /* import file's content in the scene */ res = import_obj(argv[1], &scene); if(res != RES_OK) { - logger_print(LOGGER_DEFAULT, LOG_ERROR, "Invalid file '%s'\n", argv[1]); + printf("Invalid file '%s'\n", argv[1]); return res; } @@ -350,7 +187,7 @@ int main(int argc, char *argv[]) { else { long ns = atol(argv[2]); if(ns <= 0) { - logger_print(LOGGER_DEFAULT, LOG_ERROR, "Invalid number of steps `%s'\n", argv[2]); + printf("Invalid number of steps `%s'\n", argv[2]); return RES_BAD_ARG; } nsteps = (size_t)ns; @@ -363,13 +200,13 @@ int main(int argc, char *argv[]) { else { ks = atof(argv[3]); if(ks <= 0) { - logger_print(LOGGER_DEFAULT, LOG_ERROR, - "Invalid k-scattering value `%s'\n", argv[3]); + printf("Invalid k-scattering value `%s'\n", argv[3]); return RES_BAD_ARG; } } - compute_4v_s(scene, nsteps, ks); + /* compute */ + CHECK(compute_4v_s(scene, nsteps, ks), RES_OK); /* check memory leaks */ CHECK(mem_allocated_size(), 0);