star-schiff

Library for estimating radiative properties
git clone git://git.meso-star.com/star-schiff.git
Log | Files | Refs | README | LICENSE

commit c97cb074257507fb1fc001790b6dd9cee31d7270
parent 8e8d90d630d74ec8aab18acaf0c50ca7b0395b5e
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon,  7 Mar 2016 11:12:36 +0100

Make parallel the computation of the phase_functions

The per wavelength phase function can be computed in parallel.

Diffstat:
Msrc/sschiff_estimator.c | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/test_sschiff_estimator_rhodo.c | 4++--
2 files changed, 87 insertions(+), 43 deletions(-)

diff --git a/src/sschiff_estimator.c b/src/sschiff_estimator.c @@ -105,6 +105,12 @@ struct integrator_context { struct sschiff_estimator* estimator; }; +/* Store per thread data of the post integration process */ +struct solver_context { + gsl_multiroot_fdfsolver* solver; + gsl_vector* init_val; +}; + struct sschiff_estimator { double* wavelengths; /* List of wavelengths in vacuum */ double* angles; /* List of scattering angles */ @@ -285,12 +291,8 @@ compute_path_length S3D(scene_trace_ray(scn, ray_org, ray_dir, range, &hit)); } while(hit.distance < range[0] || self_hit(&hit_from, &hit, 1.e-6f)); - if(S3D_HIT_NONE(&hit)) { -/* log_error(dev, -"Error in computing the radiative path length. This may be due to numerical\n" -"imprecisions or to a geometry that does not define a closed volume.\n");*/ + if(S3D_HIT_NONE(&hit)) return RES_BAD_OP; - } len += hit.distance - dst; dst = range[0] = hit.distance; @@ -697,6 +699,7 @@ function_fdf(const gsl_vector* vec, void* params, gsl_vector* res, gsl_matrix* J static res_T solve_wide_angles_phase_function_model_parameters (struct sschiff_device* dev, + struct solver_context* ctx, const double angle, const double sin_half_angle, const double cos_angle, @@ -707,9 +710,7 @@ solve_wide_angles_phase_function_model_parameters double* n, double* r) { - gsl_multiroot_fdfsolver* solver = NULL; gsl_multiroot_function_fdf func; - gsl_vector* init_val = NULL; gsl_vector* root = NULL; struct function_arg arg; int status; @@ -721,25 +722,7 @@ solve_wide_angles_phase_function_model_parameters ASSERT(eq_eps(cos(angle), cos_angle, 1.e-6)); ASSERT(eq_eps(cos(2.0*angle), cos_2_angle, 1.e-6)); - /* Create a Newton-Raphson nonlinear solver without derivatives */ - solver = gsl_multiroot_fdfsolver_alloc(gsl_multiroot_fdfsolver_newton, 1); - if(!solver) { - log_error(dev, - "Not enough memory. Couldn't allocate the GSL Newton solver.\n"); - res = RES_MEM_ERR; - goto error; - } - - /* Allocate the result vector. */ - init_val = gsl_vector_alloc(1); - if(!init_val) { - log_error(dev, - "Not enough memory. Couldn't allocate the GSL init vector.\n"); - res = RES_MEM_ERR; - goto error; - } - - /* Fill system arguments */ + /* Fill system arguments */ arg.angle = angle; arg.scattering_cross_section = scattering_cross_section; arg.differential_cross_section = differential_cross_section; @@ -762,23 +745,21 @@ solve_wide_angles_phase_function_model_parameters /* Initialise the solver. TODO perform this in an initialisation step of a * phase_function context */ - gsl_vector_set(init_val, 0, 3); - gsl_multiroot_fdfsolver_set(solver, &func, init_val); + gsl_multiroot_fdfsolver_set(ctx->solver, &func, ctx->init_val); gsl_set_error_handler_off(); /* Launch the Newton estimation. Iterate up to `max_iter'or until the * estimation is "good enough". */ iter = 0; - do { - status = gsl_multiroot_fdfsolver_iterate(solver); + status = gsl_multiroot_fdfsolver_iterate(ctx->solver); if(status == GSL_SUCCESS) { - status = gsl_multiroot_test_residual(solver->f, 1.e-6); + status = gsl_multiroot_test_residual(ctx->solver->f, 1.e-6); } } while(status == GSL_CONTINUE && ++iter < max_iter); /* Retrieve the estimated "n" value */ - root = gsl_multiroot_fdfsolver_root(solver); + root = gsl_multiroot_fdfsolver_root(ctx->solver); *n = gsl_vector_get(root, 0); if(status != GSL_SUCCESS) { @@ -804,8 +785,6 @@ solve_wide_angles_phase_function_model_parameters / (1 + arg.sqr_cos_angle); exit: - if(solver) gsl_multiroot_fdfsolver_free(solver); - if(init_val) gsl_vector_free(init_val); return res; error: goto exit; @@ -829,6 +808,7 @@ compute_differential_cross_section_cumulative_term static res_T compute_phase_function (struct sschiff_device* dev, + struct solver_context* ctx, const size_t iwlen, const size_t nangles, struct sschiff_estimator* estimator) @@ -847,7 +827,7 @@ compute_phase_function size_t ilimit_angle; /* Index of the limit angle of the current wavelength */ size_t iangle; res_T res = RES_OK; - ASSERT(dev && estimator); + ASSERT(dev && estimator && iwlen < sa_size(estimator->wavelengths) && ctx); /* Fetch the limit angle and precompute some values */ ilimit_angle = estimator->limit_angles[iwlen]-1; @@ -856,7 +836,6 @@ compute_phase_function cos_limit_angle = cos(limit_angle); cos_2_limit_angle = cos(2.0*limit_angle); - /* Find the connector values between small and wide angles */ get_mc_value (&estimator->accums[iwlen].scattering_cross_section, estimator->nrealisations, @@ -870,8 +849,10 @@ compute_phase_function estimator->nrealisations, &limit_differential_cross_section_cumulative); + /* Find the connector values between small and wide angles */ res = solve_wide_angles_phase_function_model_parameters (dev, + ctx, limit_angle, sin_half_limit_angle, cos_limit_angle, @@ -1143,6 +1124,46 @@ error: goto exit; } +static void +solver_context_release(struct solver_context* ctx) +{ + ASSERT(ctx); + if(ctx->solver) gsl_multiroot_fdfsolver_free(ctx->solver); + if(ctx->init_val) gsl_vector_free(ctx->init_val); +} + +static res_T +solver_context_init(struct sschiff_device* dev, struct solver_context* ctx) +{ + res_T res = RES_OK; + ASSERT(ctx); + memset(ctx, 0, sizeof(*ctx)); + + /* Create a Newton-Raphson nonlinear solver with derivatives */ + ctx->solver = gsl_multiroot_fdfsolver_alloc(gsl_multiroot_fdfsolver_newton, 1); + if(!ctx->solver) { + log_error(dev, + "Not enough memory. Couldn't allocate the GSL Newton solver.\n"); + res = RES_MEM_ERR; + goto error; + } + + /* Allocate the initial value vector */ + ctx->init_val = gsl_vector_alloc(1); + if(!ctx->init_val) { + log_error(dev, + "Not enough memory. Couldn't allocate the GSL init vector.\n"); + res = RES_MEM_ERR; + goto error; + } + +exit: + return res; +error: + solver_context_release(ctx); + goto exit; +} + static res_T begin_realisation (struct sschiff_device* dev, @@ -1418,11 +1439,12 @@ sschiff_integrate struct sschiff_estimator** out_estimator) { struct integrator_context* ctxs = NULL; + struct solver_context* solver_ctxs = NULL; struct sschiff_estimator* estimator = NULL; struct ssp_rng** rngs = NULL; struct ssp_rng_proxy* rng_proxy = NULL; size_t i; - size_t iwlen; + int iwlen; int igeom; ATOMIC res = (ATOMIC)RES_OK; @@ -1452,12 +1474,18 @@ sschiff_integrate res = RES_MEM_ERR; goto error; } + if(!sa_add(solver_ctxs, dev->nthreads)) { + log_error(dev, "Couldn'nt allocate the solver contexts.\n"); + res = RES_MEM_ERR; + goto error; + } if(!sa_add(rngs, dev->nthreads)) { log_error(dev, "Couldn't allocate the list of RNGs.\n"); res = RES_MEM_ERR; goto error; } memset(ctxs, 0, sizeof(ctxs[0])*dev->nthreads); + memset(solver_ctxs, 0, sizeof(solver_ctxs[0])*dev->nthreads); memset(rngs, 0, sizeof(rngs[0])*dev->nthreads); /* Init the per thread data structures */ @@ -1476,6 +1504,13 @@ sschiff_integrate (unsigned long)i); goto error; } + res = solver_context_init(dev, solver_ctxs + i); + if(res != RES_OK) { + log_error(dev, + "Couldn't initialise the solver context of the thread \"%lu\".\n", + (unsigned long)i); + goto error; + } } /* Paralell Schiff integration */ @@ -1503,6 +1538,7 @@ sschiff_integrate /* Merge the per thread integration ressults */ FOR_EACH(i, 0, dev->nthreads) { + size_t iwlen; FOR_EACH(iwlen, 0, nwavelengths) { size_t iangle; #define MC_ACCUM(Data) { \ @@ -1526,11 +1562,15 @@ sschiff_integrate } } - /* TODO use parallelism */ - FOR_EACH(i, 0, nwavelengths) { - if(estimator->limit_angles[i] == INVALID_LIMIT_ANGLE) continue; + /* Static scheduling is not necessary to ensure the reproductability; + * computations are purely analytics */ + #pragma omp parallel for + for(iwlen=0; iwlen < (int)nwavelengths; ++iwlen) { + const int ithread = omp_get_thread_num(); + if(estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE) continue; /* Do not handle phase function errors */ - compute_phase_function(dev, i, nangles, estimator); + compute_phase_function + (dev, &solver_ctxs[ithread], (size_t)iwlen, nangles, estimator); } exit: @@ -1539,6 +1579,10 @@ exit: FOR_EACH(i, 0, dev->nthreads) integrator_context_release(ctxs+i); sa_release(ctxs); } + if(solver_ctxs) { + FOR_EACH(i, 0, dev->nthreads) solver_context_release(solver_ctxs+i); + sa_release(solver_ctxs); + } if(rngs) { FOR_EACH(i, 0, dev->nthreads) if(rngs[i]) SSP(rng_ref_put(rngs[i])); sa_release(rngs); diff --git a/src/test_sschiff_estimator_rhodo.c b/src/test_sschiff_estimator_rhodo.c @@ -602,8 +602,8 @@ main(int argc, char** argv) const double* angles = NULL; const double wavelength = 0.4; /* In micron */ const size_t nscatt_angles = 1000; - const size_t ngeoms = 10000000; - const size_t ndirs = 10; + const size_t ngeoms = 10000; + const size_t ndirs = 100; size_t count; size_t i; (void)argc, (void)argv;