solstice-solver

Solver library of the solstice app
git clone git://git.meso-star.com/solstice-solver.git
Log | Files | Refs | README | LICENSE

commit 55800a0a35915ba325d91bc8f87b97af61b154a4
parent 6901f6b770584b2175eae2e8e446c3f335693176
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Sat, 16 Sep 2017 15:11:40 +0200

Merge branch 'release_0.4'

Diffstat:
MREADME.md | 9+++++++++
Mcmake/CMakeLists.txt | 19+++++++++++++++----
Msrc/ssol.h | 5+++--
Msrc/ssol_estimator_c.h | 8++++++++
Msrc/ssol_material.c | 32++++++++++++--------------------
Msrc/ssol_solver.c | 167++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/test_ssol_solver4.c | 6++----
Msrc/test_ssol_solver9.c | 5++---
8 files changed, 182 insertions(+), 69 deletions(-)

diff --git a/README.md b/README.md @@ -26,6 +26,15 @@ variable the install directories of its dependencies. ## Release notes +### Version 0.4 + +- Add the `SSOL_PATH_ERROR` type used for the paths that travel unforeseen + mediums. +- Fix the cosine factor estimation that did not take into account the + shadowed realisations. +- Ensure the energy conservation property for dielectric materials. Previously, + some energy was lost even for dielectric materials with no absorption. + ### Version 0.3 - Full rewrite of the estimated values. The global results report the cosine diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -28,7 +28,7 @@ find_package(RSys 0.4 REQUIRED) find_package(Star3D 0.4.1 REQUIRED) find_package(Star3DUT 0.2 REQUIRED) find_package(StarCPR 0.1 REQUIRED) -find_package(StarSF 0.2 REQUIRED) +find_package(StarSF 0.1 REQUIRED) find_package(StarSP 0.4 REQUIRED) find_package(OpenMP 1.2 REQUIRED) @@ -50,7 +50,7 @@ rcmake_append_runtime_dirs(_runtime_dirs RSys Star3D Star3DUT StarCPR StarSF Sta # Configure and define targets ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 3) +set(VERSION_MINOR 4) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) @@ -130,8 +130,20 @@ rcmake_setup_devel(solstice-solver SolSolver ${VERSION} solstice/ssol_version.h) # Add tests ################################################################################ if(NOT NO_TEST) + set(SSOL_TEST_FILES_INC + test_ssol_circ2D_geometry.h + test_ssol_geometries.h + test_ssol_rect_geometry.h + test_ssol_utils.h + test_ssol_cube_geometry.h + test_ssol_materials.h + test_ssol_rect2D_geometry.h + ) + + rcmake_prepend_path(SSOL_TEST_FILES_INC ${SSOL_SOURCE_DIR}) + function(build_test _name) - add_executable(${_name} ${SSOL_SOURCE_DIR}/${_name}.c) + add_executable(${_name} ${SSOL_SOURCE_DIR}/${_name}.c ${SSOL_TEST_FILES_INC}) target_link_libraries(${_name} solstice-solver RSys Star3D StarSP) endfunction() @@ -174,7 +186,6 @@ if(NOT NO_TEST) build_test(test_ssol_draw) register_test(test_ssol_draw_draft test_ssol_draw draft) register_test(test_ssol_draw_pt test_ssol_draw pt) - endif() ################################################################################ diff --git a/src/ssol.h b/src/ssol.h @@ -68,8 +68,9 @@ enum ssol_side_flag { enum ssol_path_type { SSOL_PATH_MISSING, /* The path misses the receivers */ - SSOL_PATH_SHADOW, /* The path is occluded before the sampled geometry */ - SSOL_PATH_SUCCESS /* The path contributes to at least one receiver */ + SSOL_PATH_SHADOW, /* The path is occluded before the sampled geometry */ + SSOL_PATH_SUCCESS, /* The path contributes to at least one receiver */ + SSOL_PATH_ERROR /* The path was canceled due to a path-related error */ }; enum ssol_material_type { diff --git a/src/ssol_estimator_c.h b/src/ssol_estimator_c.h @@ -101,6 +101,14 @@ mc_data_get(struct mc_data* data, double* weight, double* sqr_weight) *sqr_weight = data->sqr_weight__; } +static INLINE void +mc_data_cancel(struct mc_data* data, size_t irealisation) +{ + ASSERT(data); + if(data->irealisation!=irealisation) return; + data->tmp = 0; +} + /******************************************************************************* * One sided per shape MC data ******************************************************************************/ diff --git a/src/ssol_material.c b/src/ssol_material.c @@ -85,9 +85,7 @@ setup_dielectric_bsdf const struct ssol_medium* medium, struct ssf_bsdf* bsdf) { - struct ssf_bxdf* brdf = NULL; - struct ssf_bxdf* btdf = NULL; - struct ssf_fresnel* fresnel = NULL; + struct ssf_bxdf* bxdf = NULL; double eta_i, eta_t; res_T res = RES_OK; ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_DIELECTRIC); @@ -103,25 +101,19 @@ setup_dielectric_bsdf eta_i = ssol_data_get_value(&mtl->out_medium.refractive_index, wavelength); eta_t = ssol_data_get_value(&mtl->in_medium.refractive_index, wavelength); - #define CALL(Func) { res = Func; if(res != RES_OK) goto error; } (void)0 - /* Setup the reflective part */ - CALL(ssf_fresnel_create - (mtl->dev->allocator, &ssf_fresnel_dielectric_dielectric, &fresnel)); - CALL(ssf_fresnel_dielectric_dielectric_setup(fresnel, eta_i, eta_t)); - CALL(ssf_bxdf_create(mtl->dev->allocator, &ssf_specular_reflection, &brdf)); - CALL(ssf_specular_reflection_setup(brdf, fresnel)); - /* Setup the transmissive part */ - CALL(ssf_bxdf_create(mtl->dev->allocator, &ssf_specular_transmission, &btdf)); - CALL(ssf_specular_transmission_setup(btdf, eta_i, eta_t)); - /* Setup the scattering function */ - CALL(ssf_bsdf_add(bsdf, brdf, 0.5)); - CALL(ssf_bsdf_add(bsdf, btdf, 0.5)); - #undef CALL + res = ssf_bxdf_create + (mtl->dev->allocator, &ssf_specular_dielectric_dielectric_interface, &bxdf); + if(res != RES_OK) goto error; + + res = ssf_specular_dielectric_dielectric_interface_setup + (bxdf, eta_i, eta_t); + if(res != RES_OK) goto error; + + res = ssf_bsdf_add(bsdf, bxdf, 1.0); + if(res != RES_OK) goto error; exit: - if(brdf) SSF(bxdf_ref_put(brdf)); - if(btdf) SSF(bxdf_ref_put(btdf)); - if(fresnel) SSF(fresnel_ref_put(fresnel)); + if(bxdf) SSF(bxdf_ref_put(bxdf)); return res; error: goto exit; diff --git a/src/ssol_solver.c b/src/ssol_solver.c @@ -173,7 +173,8 @@ struct point { const struct ssol_material* material; /* tmp quantities to compute weights */ double kabs_at_pt; - double partial_atm, partial_recv, partial_other; + /* for conservation of energy check */ + double energy_loss; /* MC weights */ /* Set once */ double initial_flux; /* the initial flux*/ @@ -204,7 +205,8 @@ struct point { {0, 0}, /* UV */ \ 0, /* Wavelength */ \ NULL, /* Material */ \ - 0, 0, 0, 0, /* tmp values */ \ + 0, /* tmp values */ \ + 0, /* Energy loss */ \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* MC weights */ \ SSOL_FRONT /* Side */ \ } @@ -306,7 +308,7 @@ point_init pt->cos_factor = surface_sun_cos; } - pt->partial_atm = pt->partial_recv = pt->partial_other = 0; + pt->energy_loss = w0; pt->initial_flux = w0; pt->prev_outgoing_flux = w0; pt->prev_outgoing_if_no_atm_loss = w0; @@ -482,7 +484,7 @@ point_shade pt->kabs_at_pt = (1 - propagated); pt->outgoing_flux = pt->incoming_flux * propagated; pt->outgoing_if_no_atm_loss = pt->incoming_if_no_atm_loss * propagated; - pt->outgoing_if_no_field_loss = point_is_receiver(pt) + pt->outgoing_if_no_field_loss = point_is_receiver(pt) ? pt->incoming_if_no_field_loss*propagated : pt->incoming_if_no_field_loss; if(type & SSF_TRANSMISSION) { @@ -665,12 +667,6 @@ error: goto exit; } -/* - * FIXME Are the following accumulations OK when the radiative path bounces - * several times on the same receiver ? It seems weird to add the overall - * absorptivity and reflectivity losses to the corresponding per receiver - * accumulators since they already registered some losses. - */ static res_T update_mc (struct point* pt, @@ -682,7 +678,11 @@ update_mc res_T res = RES_OK; ASSERT(pt && thread_ctx && point_is_receiver(pt)); - pt->partial_recv += pt->incoming_flux - pt->outgoing_flux; + #define ACCUM_WEIGHT(Name, W)\ + mc_data_add_weight(&thread_ctx->Name, irealisation, W) + ACCUM_WEIGHT(absorbed_by_receivers, pt->incoming_flux - pt->outgoing_flux); + pt->energy_loss -= (pt->incoming_flux - pt->outgoing_flux); + #undef ACCUM_WEIGHT /* Per receiver MC accumulation */ res = get_mc_receiver_1side(&thread_ctx->mc_rcvs, pt->inst, pt->side, &mc_rcv1); @@ -774,7 +774,7 @@ trace_radiative_path /* Find a new starting point of the radiative random walk */ res = point_init(&pt, sampled_area_proxy, scn, &thread_ctx->mc_samps, - view_samp, view_rt, ran_sun_dir, ran_sun_wl, thread_ctx->rng, + view_samp, view_rt, ran_sun_dir, ran_sun_wl, thread_ctx->rng, &in_medium, &is_lit); if(res != RES_OK) goto error; @@ -799,6 +799,7 @@ trace_radiative_path if(!is_lit) { /* The starting point is not lit */ ACCUM_WEIGHT(pt.mc_samp->shadowed, pt.initial_flux); ACCUM_WEIGHT(thread_ctx->shadowed, pt.initial_flux); + pt.energy_loss -= pt.initial_flux; if(tracker) path.type = SSOL_PATH_SHADOW; } else { /* Setup the ray as if it starts from the current point position in order @@ -846,7 +847,9 @@ trace_radiative_path res = update_mc(&pt, irealisation, thread_ctx); if(res != RES_OK) goto error; } else { - pt.partial_other += pt.incoming_flux * pt.kabs_at_pt; + ACCUM_WEIGHT(thread_ctx->other_absorbed, + pt.incoming_flux * pt.kabs_at_pt); + pt.energy_loss -= (pt.incoming_flux * pt.kabs_at_pt); } /* Stop the radiative random walk if no more flux */ @@ -888,7 +891,11 @@ trace_radiative_path if (res != RES_OK) goto error; } last_segment = 1; /* Path reached its last segment */ - ASSERT(in_atm); + if(!in_atm) { + log_error(scn->dev, "Inconsistent medium description.\n"); + res = RES_BAD_OP; + goto error; + } } /* Don't change prev_outgoing weigths nor record segment absorption until @@ -897,10 +904,14 @@ trace_radiative_path * a non-virtual material is hit or no further hit can be found. */ if(last_segment || !hit_virtual) { if(in_atm) { - pt.partial_atm += pt.prev_outgoing_flux - pt.incoming_flux; + ACCUM_WEIGHT(thread_ctx->absorbed_by_atmosphere, + pt.prev_outgoing_flux - pt.incoming_flux); } else { - pt.partial_other += pt.prev_outgoing_flux - pt.incoming_flux; + ACCUM_WEIGHT(thread_ctx->other_absorbed, + pt.prev_outgoing_flux - pt.incoming_flux); } + pt.energy_loss -= (pt.prev_outgoing_flux - pt.incoming_flux); + if(last_segment) { break; } @@ -910,7 +921,8 @@ trace_radiative_path } depth += !hit_virtual; - ASSERT(depth < 100); /* FIXME: create a true cancel path for this MC sample */ + /* FIXME: create a true cancel path for this MC sample */ + ASSERT(depth < 100); /* Update the point */ point_update_from_hit(&pt, scn, org, dir, &hit, &ray_data); @@ -919,42 +931,122 @@ trace_radiative_path res = path_add_vertex(&path, pt.pos, pt.outgoing_flux); if (res != RES_OK) goto error; } - + ssol_medium_copy(&in_medium, &out_medium); } - /* Check conservation of energy */ - ASSERT((double)depth * pt.initial_flux * DBL_EPSILON >= - fabs(pt.initial_flux - - (pt.outgoing_flux + pt.partial_recv + pt.partial_atm + pt.partial_other))); - - /* Now that the sample ends successfully, - * record MC weights and register the remaining power as missing */ - ACCUM_WEIGHT(pt.mc_samp->cos_factor, pt.cos_factor); - ACCUM_WEIGHT(thread_ctx->cos_factor, pt.cos_factor); + + /* Register the remaining flux as missing */ ACCUM_WEIGHT(thread_ctx->missing, pt.outgoing_flux); - ACCUM_WEIGHT(thread_ctx->absorbed_by_receivers, pt.partial_recv); - ACCUM_WEIGHT(thread_ctx->absorbed_by_atmosphere, pt.partial_atm); - ACCUM_WEIGHT(thread_ctx->other_absorbed, pt.partial_other); - #undef ACCUM_WEIGHT + pt.energy_loss -= pt.outgoing_flux; if(tracker) { path.type = hit_a_receiver ? SSOL_PATH_SUCCESS : SSOL_PATH_MISSING; } } + /* Now that the sample ends successfully, record MC weights */ + ACCUM_WEIGHT(pt.mc_samp->cos_factor, pt.cos_factor); + ACCUM_WEIGHT(thread_ctx->cos_factor, pt.cos_factor); + #undef ACCUM_WEIGHT + + /* Check conservation of energy at the realisation level */ + ASSERT((double)depth*DBL_EPSILON*pt.initial_flux >= fabs(pt.energy_loss)); +exit: if(tracker) { - res = path_register_and_clear(&thread_ctx->paths, &path); - if(res != RES_OK) goto error; + res_T tmp_res = path_register_and_clear(&thread_ctx->paths, &path); + if(tmp_res != RES_OK && res == RES_OK) { + res = tmp_res; + goto error; + } } -exit: ssol_medium_clear(&in_medium); ssol_medium_clear(&out_medium); if(tracker) path_release(&path); return res; error: + if (tracker) { + path.type = SSOL_PATH_ERROR; + } goto exit; } +static void +cancel_mc_receiver_1side + (struct mc_receiver_1side* rcv, + size_t irealisation) +{ + mc_data_cancel(&rcv->incoming_flux, irealisation); + mc_data_cancel(&rcv->incoming_if_no_atm_loss, irealisation); + mc_data_cancel(&rcv->incoming_if_no_field_loss, irealisation); + mc_data_cancel(&rcv->incoming_lost_in_field, irealisation); + mc_data_cancel(&rcv->incoming_lost_in_atmosphere, irealisation); + mc_data_cancel(&rcv->absorbed_flux, irealisation); + mc_data_cancel(&rcv->absorbed_if_no_atm_loss, irealisation); + mc_data_cancel(&rcv->absorbed_if_no_field_loss, irealisation); + mc_data_cancel(&rcv->absorbed_lost_in_field, irealisation); + mc_data_cancel(&rcv->absorbed_lost_in_atmosphere, irealisation); +} + +static void +cancel_mc + (struct thread_context* thread_ctx, + size_t irealisation) +{ + struct htable_receiver_iterator r_it, r_end; + struct htable_sampled_iterator s_it, s_end; + + /* Cancel global MC estimations */ + mc_data_cancel(&thread_ctx->cos_factor, irealisation); + mc_data_cancel(&thread_ctx->absorbed_by_atmosphere, irealisation); + mc_data_cancel(&thread_ctx->absorbed_by_receivers, irealisation); + mc_data_cancel(&thread_ctx->other_absorbed, irealisation); + mc_data_cancel(&thread_ctx->missing, irealisation); + mc_data_cancel(&thread_ctx->shadowed, irealisation); + + /* Cancel receiver MC estimations */ + htable_receiver_begin(&thread_ctx->mc_rcvs, &r_it); + htable_receiver_end(&thread_ctx->mc_rcvs, &r_end); + while (!htable_receiver_iterator_eq(&r_it, &r_end)) { + struct mc_receiver* mc_rcv = htable_receiver_iterator_data_get(&r_it); + const struct ssol_instance* inst = *htable_receiver_iterator_key_get(&r_it); + + htable_receiver_iterator_next(&r_it); + + if (inst->receiver_mask & (int)SSOL_FRONT) { + cancel_mc_receiver_1side(&mc_rcv->front, irealisation); + } + if (inst->receiver_mask & (int)SSOL_BACK) { + cancel_mc_receiver_1side(&mc_rcv->back, irealisation); + } + } + /* Cancel sampled instance MC estimations */ + htable_sampled_begin(&thread_ctx->mc_samps, &s_it); + htable_sampled_end(&thread_ctx->mc_samps, &s_end); + while (!htable_sampled_iterator_eq(&s_it, &s_end)) { + struct mc_sampled* mc_samp = htable_sampled_iterator_data_get(&s_it); + htable_sampled_iterator_next(&s_it); + + mc_data_cancel(&mc_samp->cos_factor, irealisation); + mc_data_cancel(&mc_samp->shadowed, irealisation); + + /* dst->by_receiver += src->by_receiver; */ + htable_receiver_begin(&mc_samp->mc_rcvs, &r_it); + htable_receiver_end(&mc_samp->mc_rcvs, &r_end); + while (!htable_receiver_iterator_eq(&r_it, &r_end)) { + struct mc_receiver* mc_rcv = htable_receiver_iterator_data_get(&r_it); + const struct ssol_instance* inst = *htable_receiver_iterator_key_get(&r_it); + htable_receiver_iterator_next(&r_it); + + if (inst->receiver_mask & (int)SSOL_FRONT) { + cancel_mc_receiver_1side(&mc_rcv->front, irealisation); + } + if (inst->receiver_mask & (int)SSOL_BACK) { + cancel_mc_receiver_1side(&mc_rcv->back, irealisation); + } + } + } +} + /******************************************************************************* * Exported functions ******************************************************************************/ @@ -1005,7 +1097,7 @@ ssol_solve res = scene_check(scn, FUNC_NAME); if(res != RES_OK) goto error; - + /* init air properties */ if(scn->atmosphere) ssol_data_copy(&scn->air.absorption, &scn->atmosphere->absorption); @@ -1065,7 +1157,10 @@ ssol_solve /* Execute a MC experiment */ res_local = trace_radiative_path((size_t)i, sampled_area_proxy, thread_ctx, scn, view_samp, view_rt, ran_sun_dir, ran_sun_wl, path_tracker); - + if(res_local != RES_OK) { + /* Cancel partial MC results */ + cancel_mc(thread_ctx, (size_t)i); + } if(res_local == RES_BAD_OP) { if(ATOMIC_INCR(&nfailures) >= max_failures) { log_error(scn->dev, "Too many unexpected radiative paths.\n"); diff --git a/src/test_ssol_solver4.c b/src/test_ssol_solver4.c @@ -154,11 +154,9 @@ main(int argc, char** argv) m2 = m1; std2 = std1; CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); - printf("Shadows = %g +/- %g\n", mc_global.shadowed.E, mc_global.shadowed.SE); - printf("Missing = %g +/- %g\n", mc_global.missing.E, mc_global.missing.SE); - printf("Cos = %g +/- %g\n", mc_global.cos_factor.E, mc_global.cos_factor.SE); + PRINT_GLOBAL(mc_global); CHECK(eq_eps(mc_global.shadowed.E, 0, 1e-4), 1); - CHECK(eq_eps(mc_global.missing.E, 400 * DNI_cos, 1e-2), 1); /* nothing absorbed */ + CHECK(eq_eps(mc_global.missing.E, 400 * DNI_cos, 1e-2), 1); /* virtual => 100 % missing */ CHECK(GET_MC_RCV(estimator, target1, SSOL_FRONT, &mc_rcv), RES_OK); printf("Ir(target1) = %g +/- %g\n", mc_rcv.incoming_flux.E, mc_rcv.incoming_flux.SE); diff --git a/src/test_ssol_solver9.c b/src/test_ssol_solver9.c @@ -141,9 +141,8 @@ main(int argc, char** argv) #define DNI_TGT_S (DNI * TGT_X * TGT_Y) #define DNI_S (DNI * SZ * SZ) CHECK(ssol_estimator_get_mc_global(estimator, &mc_global), RES_OK); - printf("Cos = %g +/- %g\n", mc_global.cos_factor.E, mc_global.cos_factor.SE); - printf("Shadows = %g +/- %g\n", mc_global.shadowed.E, mc_global.shadowed.SE); - printf("Missing = %g +/- %g\n", mc_global.missing.E, mc_global.missing.SE); + PRINT_GLOBAL(mc_global); + CHECK(eq_eps(mc_global.cos_factor.E, 1./3., 3 * mc_global.cos_factor.SE), 1); CHECK(eq_eps(mc_global.shadowed.E, DNI_S, 3 * mc_global.shadowed.SE), 1); CHECK(eq_eps(mc_global.missing.E, MMAX(DNI_S, DNI_TGT_S), 3 * mc_global.missing.SE), 1);