solstice-solver

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

commit 2a6b0445ced27282e89e8133299826c1ed47231f
parent ef5867b7e4d669bc6f198a23c13646b1f66a8eec
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Thu, 21 Sep 2017 15:00:42 +0200

Merge branch 'release_0.5'

Diffstat:
MREADME.md | 7+++++++
Mcmake/CMakeLists.txt | 10+++++-----
Msrc/ssol_device.c | 23+++++++++++++++++++++--
Msrc/ssol_device_c.h | 1+
Msrc/ssol_draw_pt.c | 23++++++++---------------
Msrc/ssol_material.c | 98++++++++++++++++++++++++++++++++++---------------------------------------------
Msrc/ssol_material_c.h | 4++--
Msrc/ssol_solver.c | 45++++++++++++++++++---------------------------
Msrc/test_ssol_by_receiver_integration.c | 12++++++++----
9 files changed, 112 insertions(+), 111 deletions(-)

diff --git a/README.md b/README.md @@ -26,6 +26,13 @@ variable the install directories of its dependencies. ## Release notes +### Version 0.5 + +- Improve the performances up to 50% by optimising the allocation of the BSDF + during the radiative random walk. Performance gains are mainly observed in + situations where the radiative paths are deep, i.e. when they bounce on many + surfaces. + ### Version 0.4.2 - Energy conservation property might not be ensured when the radiative paths diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -24,11 +24,11 @@ option(NO_TEST "Do not build tests" OFF) # Check dependencies ################################################################################ find_package(RCMake 0.3 REQUIRED) -find_package(RSys 0.4 REQUIRED) +find_package(RSys 0.5 REQUIRED) find_package(Star3D 0.4.1 REQUIRED) find_package(Star3DUT 0.2 REQUIRED) find_package(StarCPR 0.1 REQUIRED) -find_package(StarSF 0.1 REQUIRED) +find_package(StarSF 0.3 REQUIRED) find_package(StarSP 0.4 REQUIRED) find_package(OpenMP 1.2 REQUIRED) @@ -50,8 +50,8 @@ rcmake_append_runtime_dirs(_runtime_dirs RSys Star3D Star3DUT StarCPR StarSF Sta # Configure and define targets ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 4) -set(VERSION_PATCH 2) +set(VERSION_MINOR 5) +set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SSOL_FILES_SRC @@ -157,7 +157,7 @@ if(NOT NO_TEST) build_test(${_name}) register_test(${_name} ${_name}) endfunction() - + new_test(test_ssol_atmosphere) new_test(test_ssol_by_receiver_integration) new_test(test_ssol_camera) diff --git a/src/ssol_device.c b/src/ssol_device.c @@ -49,6 +49,13 @@ device_release(ref_T* ref) ASSERT(ref); dev = CONTAINER_OF(ref, struct ssol_device, ref); darray_tile_release(&dev->tiles); + if(dev->bsdf_allocators) { + unsigned i; + FOR_EACH(i, 0, dev->nthreads) { + mem_shutdown_lifo_allocator(&dev->bsdf_allocators[i]); + } + MEM_RM(dev->allocator, dev->bsdf_allocators); + } if(dev->s3d) S3D(device_ref_put(dev->s3d)); if(dev->scpr_mesh) SCPR(mesh_ref_put(dev->scpr_mesh)); MEM_RM(dev->allocator, dev); @@ -67,6 +74,7 @@ ssol_device_create { struct ssol_device* dev = NULL; struct mem_allocator* allocator; + unsigned i; res_T res = RES_OK; if(nthreads_hint == 0 || !out_dev) { @@ -88,12 +96,23 @@ ssol_device_create dev->nthreads = MMIN(nthreads_hint, (unsigned)omp_get_num_procs()); omp_set_num_threads((int)dev->nthreads); + dev->bsdf_allocators = MEM_CALLOC + (dev->allocator, dev->nthreads, sizeof(struct mem_allocator)); + if(!dev->bsdf_allocators) { + res = RES_MEM_ERR; + goto error; + } + + FOR_EACH(i, 0, dev->nthreads) { + res = mem_init_lifo_allocator + (&dev->bsdf_allocators[i], dev->allocator, 4096); + if(res != RES_OK) goto error; + } + res = darray_tile_resize(&dev->tiles, dev->nthreads); if(res != RES_OK) goto error; - res = s3d_device_create(logger, mem_allocator, 0, &dev->s3d); if(res != RES_OK) goto error; - res = scpr_mesh_create(mem_allocator, &dev->scpr_mesh); if(res != RES_OK) goto error; diff --git a/src/ssol_device_c.h b/src/ssol_device_c.h @@ -39,6 +39,7 @@ struct s3d_device; struct ssol_device { struct logger* logger; struct mem_allocator* allocator; + struct mem_allocator* bsdf_allocators; /* Per thread allocator */ unsigned nthreads; int verbose; diff --git a/src/ssol_draw_pt.c b/src/ssol_draw_pt.c @@ -38,7 +38,6 @@ ******************************************************************************/ struct thread_context { struct ssp_rng* rng; - struct ssf_bsdf* bsdf; struct ranst_sun_wl* ran_wl; float up[3]; }; @@ -48,7 +47,6 @@ thread_context_release(struct thread_context* ctx) { ASSERT(ctx); if(ctx->rng) SSP(rng_ref_put(ctx->rng)); - if(ctx->bsdf) SSF(bsdf_ref_put(ctx->bsdf)); if(ctx->ran_wl) ranst_sun_wl_ref_put(ctx->ran_wl); } @@ -57,16 +55,10 @@ thread_context_init (struct mem_allocator* allocator, struct thread_context* ctx) { - res_T res = RES_OK; ASSERT(ctx); + (void)allocator; memset(ctx, 0, sizeof(ctx[0])); - res = ssf_bsdf_create(allocator, &ctx->bsdf); - if(res != RES_OK) goto error; -exit: - return res; -error: - thread_context_release(ctx); - goto exit; + return RES_OK; } static void @@ -143,6 +135,7 @@ Li(struct ssol_scene* scn, struct ray_data ray_data = RAY_DATA_NULL; struct ssol_instance* inst; struct ssol_material* mtl; + struct ssf_bsdf* bsdf = NULL; const struct shaded_shape* sshape; struct ssol_surface_fragment frag; size_t isshape; @@ -229,9 +222,8 @@ Li(struct ssol_scene* scn, /* Shaded normal may look backward the outgoing direction */ if(d3_dot(N, wo) > 0) break; - SSF(bsdf_clear(ctx->bsdf)); - res = material_setup_bsdf - (mtl, &frag, wl, &medium, 1/*Rendering*/, ctx->bsdf); + if(bsdf) SSF(bsdf_ref_put(bsdf)), bsdf = NULL; + res = material_create_bsdf(mtl, &frag, wl, &medium, 1/*Rendering*/, &bsdf); if(res != RES_OK) goto error; /* Update the ray */ @@ -248,11 +240,11 @@ Li(struct ssol_scene* scn, d3_minus(wo, wo); if(scn->sun) { L += throughput * sun_lighting - (scn->sun, view, &ray_data, ctx->bsdf, wo, N, ray_org); + (scn->sun, view, &ray_data, bsdf, wo, N, ray_org); } /* Sampling a bounce direction */ - R = ssf_bsdf_sample(ctx->bsdf, ctx->rng, wo, N, wi, &type, &pdf); + R = ssf_bsdf_sample(bsdf, ctx->rng, wo, N, wi, &type, &pdf); ASSERT(0 <= R && R <= 1); /* Due to the shading normal, the sampled direction may point in the wrong @@ -282,6 +274,7 @@ Li(struct ssol_scene* scn, d3_splat(val, L); exit: + if(bsdf) SSF(bsdf_ref_put(bsdf)); ssol_medium_clear(&medium); return res; error: diff --git a/src/ssol_material.c b/src/ssol_material.c @@ -32,6 +32,7 @@ #include <star/ssf.h> #include <math.h> +#include <omp.h> /******************************************************************************* * Helper functions @@ -78,15 +79,15 @@ shade_normal_default } static res_T -setup_dielectric_bsdf +create_dielectric_bsdf (const struct ssol_material* mtl, const struct ssol_surface_fragment* fragment, const double wavelength, /* In nanometer */ const struct ssol_medium* medium, - struct ssf_bsdf* bsdf) + struct ssf_bsdf** bsdf) { - struct ssf_bxdf* bxdf = NULL; double eta_i, eta_t; + const int ithread = omp_get_thread_num(); res_T res = RES_OK; ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_DIELECTRIC); ASSERT(medium && bsdf); @@ -101,33 +102,28 @@ 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); - 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; + #define CALL(Func) { res = Func; if(res != RES_OK) goto error; } (void)0 + CALL(ssf_bsdf_create(&mtl->dev->bsdf_allocators[ithread], + &ssf_specular_dielectric_dielectric_interface, bsdf)); + CALL(ssf_specular_dielectric_dielectric_interface_setup(*bsdf, eta_i, eta_t)); + #undef CALL exit: - if(bxdf) SSF(bxdf_ref_put(bxdf)); return res; error: + if(*bsdf) SSF(bsdf_ref_put(*bsdf)), bsdf = NULL; goto exit; } static res_T -setup_matte_bsdf +create_matte_bsdf (const struct ssol_material* mtl, const struct ssol_surface_fragment* fragment, const double wavelength, /* In nanometer */ - struct ssf_bsdf* bsdf) + struct ssf_bsdf** bsdf) { - struct ssf_bxdf* brdf = NULL; double reflectivity; + const int ithread = omp_get_thread_num(); res_T res; ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_MATTE); ASSERT(bsdf); @@ -137,35 +133,32 @@ setup_matte_bsdf (mtl->dev, mtl->buf, wavelength, fragment, &reflectivity); /* Setup the BRDF */ - res = ssf_bxdf_create(mtl->dev->allocator, &ssf_lambertian_reflection, &brdf); + res = ssf_bsdf_create + (&mtl->dev->bsdf_allocators[ithread], &ssf_lambertian_reflection, bsdf); if(res != RES_OK) goto error; - res = ssf_lambertian_reflection_setup(brdf, reflectivity); - if(res != RES_OK) goto error; - - /* Setup the BSDF */ - res = ssf_bsdf_add(bsdf, brdf, 1.0); + res = ssf_lambertian_reflection_setup(*bsdf, reflectivity); if(res != RES_OK) goto error; exit: - if(brdf) SSF(bxdf_ref_put(brdf)); return res; error: + if(*bsdf) SSF(bsdf_ref_put(*bsdf)), bsdf = NULL; goto exit; } static res_T -setup_mirror_bsdf +create_mirror_bsdf (const struct ssol_material* mtl, const struct ssol_surface_fragment* fragment, const double wavelength, /* In nanometer */ const int rendering, - struct ssf_bsdf* bsdf) + struct ssf_bsdf** bsdf) { - struct ssf_bxdf* brdf = NULL; struct ssf_fresnel* fresnel = NULL; struct ssf_microfacet_distribution* distrib = NULL; double roughness; double reflectivity; + const int ithread = omp_get_thread_num(); res_T res; ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_MIRROR); ASSERT(bsdf); @@ -184,9 +177,10 @@ setup_mirror_bsdf /* Setup the BRDF */ if(roughness == 0) { /* Purely specular reflection */ - res = ssf_bxdf_create(mtl->dev->allocator, &ssf_specular_reflection, &brdf); + res = ssf_bsdf_create + (&mtl->dev->bsdf_allocators[ithread], &ssf_specular_reflection, bsdf); if(res != RES_OK) goto error; - res = ssf_specular_reflection_setup(brdf, fresnel); + res = ssf_specular_reflection_setup(*bsdf, fresnel); if(res != RES_OK) goto error; } else { /* Glossy reflection */ res = ssf_microfacet_distribution_create @@ -199,42 +193,38 @@ setup_mirror_bsdf * evaluated and consequently it returns an invalid result for direct * lighting. */ if(rendering) { - res = ssf_bxdf_create - (mtl->dev->allocator, &ssf_microfacet_reflection, &brdf); + res = ssf_bsdf_create + (&mtl->dev->bsdf_allocators[ithread], &ssf_microfacet_reflection, bsdf); } else { - res = ssf_bxdf_create - (mtl->dev->allocator, &ssf_microfacet2_reflection, &brdf); + res = ssf_bsdf_create + (&mtl->dev->bsdf_allocators[ithread], &ssf_microfacet2_reflection, bsdf); } if(res != RES_OK) goto error; - res = ssf_microfacet_reflection_setup(brdf, fresnel, distrib); + res = ssf_microfacet_reflection_setup(*bsdf, fresnel, distrib); if(res != RES_OK) goto error; } - /* Setup the BSDF */ - res = ssf_bsdf_add(bsdf, brdf, 1.0); - if(res != RES_OK) goto error; - exit: - if(brdf) SSF(bxdf_ref_put(brdf)); if(fresnel) SSF(fresnel_ref_put(fresnel)); if(distrib) SSF(microfacet_distribution_ref_put(distrib)); return res; error: + if(*bsdf) SSF(bsdf_ref_put(*bsdf)), bsdf = NULL; goto exit; } static res_T -setup_thin_dielectric_bsdf +create_thin_dielectric_bsdf (const struct ssol_material* mtl, const struct ssol_surface_fragment* fragment, const double wavelength, /* In nanometer */ - struct ssf_bsdf* bsdf) + struct ssf_bsdf** bsdf) { - struct ssf_bxdf* bxdf = NULL; double thickness; double absorption; double eta_i; double eta_t; + const int ithread = omp_get_thread_num(); res_T res = RES_OK; ASSERT(mtl && fragment && mtl->type == SSOL_MATERIAL_THIN_DIELECTRIC); ASSERT(bsdf); @@ -248,21 +238,17 @@ setup_thin_dielectric_bsdf thickness = mtl->data.thin_dielectric.thickness; /* Setup the BxDF */ - res = ssf_bxdf_create - (mtl->dev->allocator, &ssf_thin_specular_dielectric, &bxdf); + res = ssf_bsdf_create + (&mtl->dev->bsdf_allocators[ithread], &ssf_thin_specular_dielectric, bsdf); if(res != RES_OK) goto error; res = ssf_thin_specular_dielectric_setup - (bxdf, absorption, eta_i, eta_t, thickness); - if(res != RES_OK) goto error; - - /* Setup the BSDF */ - res = ssf_bsdf_add(bsdf, bxdf, 1.0); + (*bsdf, absorption, eta_i, eta_t, thickness); if(res != RES_OK) goto error; exit: - if(bxdf) SSF(bxdf_ref_put(bxdf)); return res; error: + if(*bsdf) SSF(bsdf_ref_put(*bsdf)), bsdf = NULL; goto exit; } @@ -661,30 +647,30 @@ material_shade_normal } res_T -material_setup_bsdf +material_create_bsdf (const struct ssol_material* mtl, const struct ssol_surface_fragment* fragment, const double wavelength, /* In nanometer */ const struct ssol_medium* medium, const int rendering, /* Is BSDF used for rendering */ - struct ssf_bsdf* bsdf) + struct ssf_bsdf** bsdf) { res_T res = RES_OK; ASSERT(mtl); switch(mtl->type) { case SSOL_MATERIAL_DIELECTRIC: - res = setup_dielectric_bsdf + res = create_dielectric_bsdf (mtl, fragment, wavelength, medium, bsdf); break; case SSOL_MATERIAL_MATTE: - res = setup_matte_bsdf(mtl, fragment, wavelength, bsdf); + res = create_matte_bsdf(mtl, fragment, wavelength, bsdf); break; case SSOL_MATERIAL_MIRROR: - res = setup_mirror_bsdf(mtl, fragment, wavelength, rendering, bsdf); + res = create_mirror_bsdf(mtl, fragment, wavelength, rendering, bsdf); break; case SSOL_MATERIAL_THIN_DIELECTRIC: - res = setup_thin_dielectric_bsdf(mtl, fragment, wavelength, bsdf); + res = create_thin_dielectric_bsdf(mtl, fragment, wavelength, bsdf); break; case SSOL_MATERIAL_VIRTUAL: /* Nothing to shade */ break; default: FATAL("Unreachable code\n"); break; diff --git a/src/ssol_material_c.h b/src/ssol_material_c.h @@ -78,13 +78,13 @@ material_shade_normal double N[3]); extern LOCAL_SYM res_T -material_setup_bsdf +material_create_bsdf (const struct ssol_material* mtl, const struct ssol_surface_fragment* fragment, const double wavelength, /* In nanometer */ const struct ssol_medium* medium, /* Current medium */ const int rendering, /* Is material used for rendering purposes */ - struct ssf_bsdf* bsdf); /* Bidirectional Scattering Distribution Function */ + struct ssf_bsdf** bsdf); /* Bidirectional Scattering Distribution Function */ extern LOCAL_SYM res_T material_get_next_medium diff --git a/src/ssol_solver.c b/src/ssol_solver.c @@ -51,7 +51,6 @@ ******************************************************************************/ struct thread_context { struct ssp_rng* rng; - struct ssf_bsdf* bsdf; struct mc_data cos_factor; struct mc_data absorbed_by_receivers; struct mc_data shadowed; @@ -69,7 +68,6 @@ thread_context_release(struct thread_context* ctx) { ASSERT(ctx); if(ctx->rng) SSP(rng_ref_put(ctx->rng)); - if(ctx->bsdf) SSF(bsdf_ref_put(ctx->bsdf)); htable_receiver_release(&ctx->mc_rcvs); htable_sampled_release(&ctx->mc_samps); darray_path_release(&ctx->paths); @@ -78,22 +76,12 @@ thread_context_release(struct thread_context* ctx) static res_T thread_context_init(struct mem_allocator* allocator, struct thread_context* ctx) { - res_T res = RES_OK; ASSERT(ctx); - memset(ctx, 0, sizeof(ctx[0])); htable_receiver_init(allocator, &ctx->mc_rcvs); htable_sampled_init(allocator, &ctx->mc_samps); darray_path_init(allocator, &ctx->paths); - - res = ssf_bsdf_create(allocator, &ctx->bsdf); - if(res != RES_OK) goto error; - -exit: - return res; -error: - thread_context_release(ctx); - goto exit; + return RES_OK; } /* Define a copy functor only for consistency since this function will not be @@ -105,7 +93,6 @@ thread_context_copy res_T res = RES_OK; ASSERT(dst && src); dst->rng = src->rng; - dst->bsdf = src->bsdf; dst->cos_factor = src->cos_factor; dst->absorbed_by_receivers = src->absorbed_by_receivers; dst->shadowed = src->shadowed; @@ -272,6 +259,9 @@ point_init /* Sample a sun direction */ ranst_sun_dir_get(ran_sun_dir, rng, pt->dir); + + /* Sample a wavelength */ + pt->wl = ranst_sun_wl_get(ran_sun_wl, rng); if(pt->sshape->shape->type != SHAPE_PUNCHED) { d3_set(N, pt->N); @@ -356,9 +346,6 @@ point_init f3_set_d3(pos, pt->pos); S3D(scene_view_trace_ray(view_rt, pos, dir, range, &ray_data, &hit)); *is_lit = S3D_HIT_NONE(&hit); - if(*is_lit) { - pt->wl = ranst_sun_wl_get(ran_sun_wl, rng); /* Sample a wavelength */ - } exit: return res; @@ -428,7 +415,6 @@ point_is_receiver(const struct point* pt) static FINLINE res_T point_shade (struct point* pt, - struct ssf_bsdf* bsdf, const struct ssol_medium* in_medium, struct ssol_medium* out_medium, struct ssp_rng* rng, @@ -436,11 +422,12 @@ point_shade { struct ssol_material* mtl; struct ssol_surface_fragment frag; + struct ssf_bsdf* bsdf = NULL; double propagated = 0; double wi[3], N[3], pdf; int type = 0; res_T res; - ASSERT(pt && bsdf && in_medium && out_medium && rng && dir); + ASSERT(pt && in_medium && out_medium && rng && dir); /* TODO ensure that if `prim' was sampled, then the surface fragment setup * remains valid in *all* situations, i.e. even though the point primitive @@ -457,9 +444,9 @@ point_shade /* Shade the surface fragment */ mtl = point_get_material(pt); - SSF(bsdf_clear(bsdf)); - res = material_setup_bsdf(mtl, &frag, pt->wl, in_medium, 0, bsdf); - if(res != RES_OK) return res; + + res = material_create_bsdf(mtl, &frag, pt->wl, in_medium, 0, &bsdf); + if(res != RES_OK) goto error; /* Perturbe the normal */ material_shade_normal(mtl, &frag, pt->wl, N); @@ -494,7 +481,12 @@ point_shade } else { ssol_medium_copy(out_medium, in_medium); } - return RES_OK; + +exit: + if(bsdf) SSF(bsdf_ref_put(bsdf)); + return res; +error: + goto exit; } static FINLINE void @@ -930,8 +922,7 @@ trace_radiative_path } else { /* Modulate the point weights wrt its scattering functions and generate * an outgoing direction and set out_medium accordingly */ - res = point_shade(&pt, thread_ctx->bsdf, &in_medium, &out_medium, - thread_ctx->rng, pt.dir); + res = point_shade(&pt, &in_medium, &out_medium, thread_ctx->rng, pt.dir); if(res != RES_OK) goto error; } @@ -1033,10 +1024,10 @@ trace_radiative_path double p = ssp_rng_canonical(thread_ctx->rng); if(p > 0.5) { pt.survivor_score += 1; /* This path survived once more */ + roulette_interval = typical_max_depth; } else { cancel_mc(thread_ctx, irealisation); killed_by_roulette = 1; - roulette_interval = typical_max_depth; goto exit; /* break is not enough */ } } @@ -1068,7 +1059,7 @@ trace_radiative_path /* Check conservation of energy at the realisation level */ ASSERT((double)depth*DBL_EPSILON*pt.initial_flux >= fabs(pt.energy_loss)); - /* this realisation count account for many that where canceled */ + /* this realisation accounts for many that where canceled */ if(pt.survivor_score) { const double factor = (double)(1 << pt.survivor_score); apply_factor_mc(thread_ctx, irealisation, factor); diff --git a/src/test_ssol_by_receiver_integration.c b/src/test_ssol_by_receiver_integration.c @@ -139,22 +139,26 @@ main(int argc, char** argv) printf("Ir(target) = %g +/- %g\n", mc_rcv.incoming_flux.E, mc_rcv.incoming_flux.SE); CHECK(ssol_instance_set_receiver(heliostat, SSOL_FRONT, 0), RES_OK); - CHECK(eq_eps(mc_rcv.incoming_flux.E, S_DNI_cos, S_DNI_cos * 2e-1), 1); + CHECK(eq_eps + (mc_rcv.incoming_flux.E, S_DNI_cos, + mc_rcv.incoming_flux.SE*3), 1); CHECK(ssol_solve(scene, rng, 8 * N__, NULL, &estimator2), RES_OK); CHECK(GET_MC_RCV(estimator2, target, SSOL_FRONT, &mc_rcv), RES_OK); printf("Ir(target) = %g +/- %g\n", mc_rcv.incoming_flux.E, mc_rcv.incoming_flux.SE); - CHECK(eq_eps(mc_rcv.incoming_flux.E, S_DNI_cos, S_DNI_cos * 5e-2), 1); + CHECK(eq_eps(mc_rcv.incoming_flux.E, S_DNI_cos, mc_rcv.incoming_flux.SE*3), 1); CHECK(ssol_estimator_ref_put(estimator1), RES_OK); CHECK(ssol_solve(scene, rng, 3 * N__, NULL, &estimator1), RES_OK); CHECK(GET_MC_RCV(estimator1, target, SSOL_FRONT, &mc_rcv), RES_OK); printf("Ir(target) = %g +/- %g\n", mc_rcv.incoming_flux.E, mc_rcv.incoming_flux.SE); - CHECK(eq_eps(mc_rcv.incoming_flux.E, S_DNI_cos, S_DNI_cos * 1e-1), 1); + CHECK(eq_eps + (mc_rcv.incoming_flux.E, S_DNI_cos, + mc_rcv.incoming_flux.SE*3), 1); CHECK(GET_MC_SAMP_X_RCV(estimator1, heliostat, target, SSOL_FRONT, &mc_rcv), RES_OK); printf("Ir(heliostat=>target) = %g +/- %g\n", mc_rcv.incoming_flux.E, mc_rcv.incoming_flux.SE); - CHECK(eq_eps(mc_rcv.incoming_flux.E, S_DNI_cos, S_DNI_cos * 1e-1), 1); + CHECK(eq_eps(mc_rcv.incoming_flux.E, S_DNI_cos, mc_rcv.incoming_flux.SE*3), 1); /* Free data */ CHECK(ssol_instance_ref_put(heliostat), RES_OK);