star-schiff

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

sschiff_estimator.c (65003B)


      1 /* Copyright (C) 2015, 2016, 2026 Centre National de la Recherche Scientifique
      2  * Copyright (C) 2026 Clermont Auvergne INP
      3  * Copyright (C) 2026 Institut Mines Télécom Albi-Carmaux
      4  * Copyright (C) 2020, 2021, 2023, 2026 |Méso|Star> (contact@meso-star.com)
      5  * Copyright (C) 2026 Université de Lorraine
      6  * Copyright (C) 2026 Université de Toulouse
      7  *
      8  * This program is free software: you can redistribute it and/or modify
      9  * it under the terms of the GNU General Public License as published by
     10  * the Free Software Foundation, either version 3 of the License, or
     11  * (at your option) any later version.
     12  *
     13  * This program is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     16  * GNU General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU General Public License
     19  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     20 
     21 #define _POSIX_C_SOURCE 200112L /* nextafterf support */
     22 
     23 #include "sschiff.h"
     24 #include "sschiff_device.h"
     25 
     26 #include <rsys/algorithm.h>
     27 #include <rsys/float2.h>
     28 #include <rsys/float3.h>
     29 #include <rsys/image.h>
     30 #include <rsys/logger.h>
     31 #include <rsys/math.h>
     32 #include <rsys/mem_allocator.h>
     33 #include <rsys/stretchy_array.h>
     34 
     35 #include <star/s3d.h>
     36 #include <star/ssp.h>
     37 
     38 #include <math.h>
     39 #include <omp.h>
     40 
     41 #include <gsl/gsl_sf.h>
     42 #include <gsl/gsl_multiroots.h>
     43 
     44 #define MAX_FAILURES 10
     45 #define INVALID_LIMIT_ANGLE SIZE_MAX
     46 
     47 /* Accumulator of double precision values */
     48 struct accum {
     49   double extinction_cross_section;
     50   double absorption_cross_section;
     51   double scattering_cross_section;
     52   double avg_projected_area;
     53 
     54   /* Per scattering angles weights */
     55   double* differential_cross_section;
     56   double* differential_cross_section_cumulative;
     57 };
     58 
     59 /* Monte carlo data */
     60 struct mc_data { double weight; double sqr_weight; };
     61 static const struct mc_data MC_DATA_NULL = { 0.0, 0.0 };
     62 
     63 /* Accumulator of Monte Carlo data */
     64 struct mc_accum {
     65   struct mc_data extinction_cross_section;
     66   struct mc_data absorption_cross_section;
     67   struct mc_data scattering_cross_section;
     68   struct mc_data avg_projected_area;
     69 
     70   /* Per scattering angles weight */
     71   struct mc_data* differential_cross_section;
     72   struct mc_data* differential_cross_section_cumulative;
     73 };
     74 
     75 /* Estimated phase function */
     76 struct phase_function {
     77   /* Per angle values */
     78   struct sschiff_state* values;
     79   struct sschiff_state* cumulative;
     80 };
     81 
     82 /* The integrator context stores per thread data */
     83 struct integrator_context {
     84   /* Geometry of the micro particle */
     85   struct s3d_shape* shape;
     86   struct s3d_scene* scene;
     87   struct s3d_scene_view* view;
     88   void* shape_data; /* Client side data of the shape */
     89 
     90   struct ssp_rng* rng; /* Random Number Generator */
     91   size_t nwavelengths; /* #wavelengths */
     92   size_t nangles; /* #scattering angles */
     93 
     94    /* Per wavelengths data */
     95   struct accum* accums; /* Temporary accumulators */
     96   struct mc_accum* mc_accums; /* Monte Carlo accumulators */
     97 
     98   /* Pointer toward the estimator */
     99   struct sschiff_estimator* estimator;
    100 };
    101 
    102 /* Store per thread data of the post integration process */
    103 struct solver_context {
    104   gsl_multiroot_fdfsolver* solver;
    105   gsl_vector* init_val;
    106 };
    107 
    108 struct sschiff_estimator {
    109   double* wavelengths; /* List of wavelengths in vacuum */
    110   double* angles; /* List of scattering angles */
    111 
    112   /* Per wavelength index of the scattering angle from which the MC estimation
    113    * of the phase function is no more valid. Must be < nangles. */
    114   size_t* limit_angles;
    115   double* wide_angles_parameter;
    116 
    117   double* wavenumbers;
    118   struct sschiff_material_properties* properties; /* Material properties */
    119 
    120   /* Per wavelength results */
    121   struct phase_function* phase_functions;
    122   struct mc_accum* accums; /* Monte Carlo estimation */
    123 
    124   size_t nrealisations; /* # realisation used by MC estimations */
    125   int no_phase_function; /* Set to 1 when no phase function is computed */
    126   int discard_large_angles; /* Avoid analytic model for wide angles */
    127 
    128   struct sschiff_device* dev;
    129   ref_T ref;
    130 };
    131 
    132 /*******************************************************************************
    133  * Helper functions
    134  ******************************************************************************/
    135 static FINLINE int
    136 cmp_double(const void* a, const void* b)
    137 {
    138   const double* op0 = a;
    139   const double* op1 = b;
    140   const int i = *op0 < *op1 ? -1 : (*op0 > *op1 ? 1 : 0);
    141   return i;
    142 }
    143 
    144 static FINLINE int
    145 cmp_schiff_state(const void* a, const void* b)
    146 {
    147   const struct sschiff_state* op0 = a;
    148   const struct sschiff_state* op1 = b;
    149   const int i = op0->E < op1->E ? -1 : (op0->E > op1->E ? 1 : 0);
    150   return i;
    151 }
    152 
    153 /* Compute an orthonormal basis where `dir' is the Z axis. */
    154 static void
    155 build_basis(const float dir[3], float basis[9])
    156 {
    157   float dir_abs[3];
    158   float axis_min[3];
    159   int i;
    160   ASSERT(dir && basis && f3_is_normalized(dir));
    161 
    162   /* Define which axis direction is minimal and use its unit vector to compute
    163    * a vector ortogonal to `dir'. This ensures that the unit vector and `dir'
    164    * are not colinear and thus that their cross product is not a zero vector. */
    165   FOR_EACH(i, 0, 3) dir_abs[i] = absf(dir[i]);
    166   if(dir_abs[0] < dir_abs[1]) {
    167     if(dir_abs[0] < dir_abs[2]) f3(axis_min, 1.f, 0.f, 0.f);
    168     else f3(axis_min, 0.f, 0.f, 1.f);
    169   } else {
    170     if(dir_abs[1] < dir_abs[2]) f3(axis_min, 0.f, 1.f, 0.f);
    171     else f3(axis_min, 0.f, 0.f, 1.f);
    172   }
    173 
    174   f3_normalize(basis + 0, f3_cross(basis + 0, dir, axis_min));
    175   f3_cross(basis + 3, dir, basis + 0);
    176   f3_set(basis + 6, dir);
    177 }
    178 
    179 /* Compute a 3x4 affine transformation from a orthonormal basis and the
    180  * position of the origin */
    181 static FINLINE void
    182 build_transform
    183   (const float pos[3], /* Position of the origin */
    184    const float basis[9], /* Column major */
    185    float transform[12]) /* Column major */
    186 {
    187   ASSERT(pos && basis && transform);
    188 
    189    /* The linear part of the transform matrix is the inverse of the
    190     * orthonormal basis (i.e. its transpose) */
    191   f3(transform + 0, basis[0], basis[3], basis[6]);
    192   f3(transform + 3, basis[1], basis[4], basis[7]);
    193   f3(transform + 6, basis[2], basis[5], basis[8]);
    194 
    195   /* Affine part of the transform matrix */
    196   transform[9 ] = -f3_dot(basis+0, pos);
    197   transform[10] = -f3_dot(basis+3, pos);
    198   transform[11] = -f3_dot(basis+6, pos);
    199 }
    200 
    201 static FINLINE double
    202 expected_value(const struct mc_data* data, const size_t nrealisations)
    203 {
    204   return data->weight / (double)nrealisations;
    205 }
    206 
    207 static INLINE void
    208 get_mc_value
    209   (const struct mc_data* data,
    210    const size_t nrealisations,
    211    struct sschiff_state* value)
    212 {
    213   ASSERT(data && value);
    214   value->E = expected_value(data, nrealisations);
    215   value->V = data->sqr_weight / (double)nrealisations
    216     - (data->weight * data->weight) / (double)(nrealisations * nrealisations);
    217   value->SE = sqrt(value->V / (double)nrealisations);
    218 }
    219 
    220 static FINLINE int
    221 hit_on_edge(const struct s3d_hit* hit)
    222 {
    223   const float on_edge_eps = 1.e-4f;
    224   float w;
    225   ASSERT(hit && !S3D_HIT_NONE(hit));
    226   w = 1.f - hit->uv[0] - hit->uv[1];
    227   return eq_epsf(hit->uv[0], 0.f, on_edge_eps)
    228       || eq_epsf(hit->uv[0], 1.f, on_edge_eps)
    229       || eq_epsf(hit->uv[1], 0.f, on_edge_eps)
    230       || eq_epsf(hit->uv[1], 1.f, on_edge_eps)
    231       || eq_epsf(w, 0.f, on_edge_eps)
    232       || eq_epsf(w, 1.f, on_edge_eps);
    233 }
    234 
    235 static FINLINE int
    236 self_hit
    237   (const struct s3d_hit* hit_from,
    238    const struct s3d_hit* hit_to,
    239    const float epsilon)
    240 {
    241   ASSERT(hit_from && hit_to);
    242 
    243   if(S3D_HIT_NONE(hit_from) || S3D_HIT_NONE(hit_to))
    244     return 0;
    245   if(S3D_PRIMITIVE_EQ(&hit_from->prim, &hit_to->prim))
    246     return 1;
    247   if(eq_epsf(hit_to->distance - hit_from->distance, 0.f, epsilon))
    248     return hit_on_edge(hit_from) && hit_on_edge(hit_to);
    249   return 0;
    250 }
    251 
    252 /* Compute the length in micron of the ray part that traverses the scene */
    253 static res_T
    254 compute_path_length
    255   (struct sschiff_device* dev,
    256    struct s3d_scene_view* view,
    257    const struct s3d_hit* first_hit,
    258    const float ray_org[3],
    259    const float ray_dir[3],
    260    const double scale,
    261    double* length)
    262 {
    263   float range[2] = { 0, FLT_MAX };
    264   struct s3d_hit hit_from, hit;
    265   double len = 0;
    266   float dst;
    267   ASSERT(view && first_hit && ray_org && ray_dir && !S3D_HIT_NONE(first_hit));
    268   ASSERT(scale > 0);
    269   (void)dev;
    270 
    271   dst = range[0] = first_hit->distance;
    272   hit_from = *first_hit;
    273 
    274   do {
    275     do {
    276       range[0] = nextafterf(range[0], range[1]);
    277       S3D(scene_view_trace_ray(view, ray_org, ray_dir, range, NULL, &hit));
    278     } while(hit.distance < range[0] || self_hit(&hit_from, &hit, 1.e-6f));
    279 
    280     if(S3D_HIT_NONE(&hit))
    281       return RES_BAD_OP;
    282 
    283     len += hit.distance - dst;
    284     dst = range[0] = hit.distance;
    285     hit_from = hit;
    286 
    287     do {
    288       range[0] = nextafterf(range[0], range[1]);
    289       S3D(scene_view_trace_ray(view, ray_org, ray_dir, range, NULL, &hit));
    290     } while(hit.distance < range[0] || self_hit(&hit_from, &hit, 1.e-6f));
    291 
    292     range[0] = hit.distance;
    293     hit_from = hit;
    294 
    295   } while(!S3D_HIT_NONE(&hit));
    296 
    297   *length = len * scale;
    298   return RES_OK;
    299 }
    300 
    301 static INLINE void
    302 accum_cross_section
    303   (struct integrator_context* ctx,
    304    const double plane_area,
    305    const double path_length)
    306 {
    307   size_t iwlen;
    308   ASSERT(ctx && plane_area > 0 && path_length > 0);
    309 
    310   FOR_EACH(iwlen, 0, ctx->nwavelengths) {
    311     double n_r, k_r;
    312     double k_e;
    313     double w_extinction, w_absorption, w_scattering;
    314 
    315     k_e = ctx->estimator->wavenumbers[iwlen];
    316     n_r = ctx->estimator->properties[iwlen].relative_real_refractive_index;
    317     k_r = ctx->estimator->properties[iwlen].relative_imaginary_refractive_index;
    318 
    319     /* Extinction cross section */
    320     w_extinction = 2.0 * plane_area *
    321       (1.0 - exp(-k_e*k_r*path_length) * cos(k_e*(n_r - 1.0)*path_length));
    322     ctx->accums[iwlen].extinction_cross_section += w_extinction;
    323 
    324     /* Absorption cross section */
    325     w_absorption = plane_area * (1.0 - exp(-2.0*k_e*k_r*path_length));
    326     ctx->accums[iwlen].absorption_cross_section += w_absorption;
    327 
    328     /* Scattering cross section */
    329     w_scattering = w_extinction - w_absorption;
    330     ctx->accums[iwlen].scattering_cross_section += w_scattering;
    331 
    332     /* Average projected area */
    333     ctx->accums[iwlen].avg_projected_area += plane_area;
    334   }
    335 }
    336 
    337 static INLINE void
    338 accum_differential_cross_section
    339   (struct integrator_context* ctx,
    340    const double plane_area,
    341    const double path_length[2],
    342    const double delta_r)/* Length between the 2 ray origins in footprint space */
    343 {
    344   size_t iwlen;
    345 
    346   FOR_EACH(iwlen, 0, ctx->nwavelengths) {
    347     double k_e_delta_r;
    348     double n_r, k_r;
    349     double k_e;
    350     double beta_r[2];
    351     double beta_i[2];
    352     double tmp;
    353     size_t iangle;
    354 
    355     /* Avoid evaluating the differential cross sections for wavelengths with no
    356      * valid limit scattering angle */
    357     if(ctx->estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE) continue;
    358 
    359     /* Fetch optical properties */
    360     k_e = ctx->estimator->wavenumbers[iwlen];
    361     n_r = ctx->estimator->properties[iwlen].relative_real_refractive_index;
    362     k_r = ctx->estimator->properties[iwlen].relative_imaginary_refractive_index;
    363 
    364     /* Precompute some values. TODO can be stored on the context */
    365     k_e_delta_r = k_e * delta_r;
    366     beta_r[0] = k_e * (n_r - 1) * path_length[0];
    367     beta_r[1] = k_e * (n_r - 1) * path_length[1];
    368     beta_i[0] = k_e * k_r * path_length[0];
    369     beta_i[1] = k_e * k_r * path_length[1];
    370     tmp  = (k_e * plane_area) / (2*PI);
    371     tmp *= tmp;
    372     tmp *= (1
    373          + exp(-(beta_i[0] + beta_i[1])) * cos(beta_r[0] - beta_r[1])
    374          - exp(-beta_i[0]) * cos(beta_r[0])
    375          - exp(-beta_i[1]) * cos(beta_r[1]));
    376 
    377     /* Compute and accumulate the MC weights of the differential cross section
    378      * and its cumulative. Note that ctx->limit_angles store the index of the
    379      * first scattering angle whose phase function is not estimated by
    380      * Monte-Carlo */
    381     FOR_EACH(iangle, 0, ctx->estimator->limit_angles[iwlen]) {
    382       double bessel_j0, bessel_j1;
    383       double weight;
    384       double k_e_angle_delta_r;
    385 
    386       k_e_angle_delta_r = ctx->estimator->angles[iangle] * k_e_delta_r;
    387 
    388       bessel_j0 = gsl_sf_bessel_J0(k_e_angle_delta_r);
    389       weight = bessel_j0 * tmp;
    390       ctx->accums[iwlen].differential_cross_section[iangle] += weight;
    391 
    392       bessel_j1 = gsl_sf_bessel_J1(k_e_angle_delta_r);
    393       weight = 2*PI*ctx->estimator->angles[iangle]*bessel_j1/k_e_delta_r*tmp;
    394       ctx->accums[iwlen].differential_cross_section_cumulative[iangle] += weight;
    395     }
    396   }
    397 }
    398 
    399 static res_T
    400 accum_monte_carlo_weight
    401   (struct sschiff_device* dev,
    402    struct integrator_context* ctx,
    403    const float basis[9], /* World space basis of the RT volume */
    404    const float pos[3], /* World space centroid of the RT volume */
    405    const float lower[3], /* Lower boundary of the RT volume */
    406    const float upper[3], /* Upper boundary of the RT volume */
    407    const double area_scaling, /* Scale factor to apply to the areas */
    408    const double dist_scaling) /* Scale factor to apply to the distances */
    409 {
    410   struct s3d_hit hit;
    411   double sample[2];
    412   const float range[2] = { 0, FLT_MAX };
    413   size_t nfailures = 0;
    414   float axis_x[3], axis_y[3], axis_z[3];
    415   float plane_size[2];
    416   float ray_org[2][3];
    417   float org[3];
    418   float x[3], y[3];
    419   float plane_area;
    420   res_T res = RES_OK;
    421   ASSERT(dev && ctx &&  basis && pos && lower && upper);
    422   ASSERT(area_scaling > 0 && dist_scaling > 0);
    423 
    424   f2_sub(plane_size, upper, lower); /* In micron */
    425   plane_area = plane_size[0] * plane_size[1] * (float)area_scaling;
    426 
    427   /* Define the projection axis */
    428   f3_mulf(axis_x, basis + 0, plane_size[0] * 0.5f);
    429   f3_mulf(axis_y, basis + 3, plane_size[1] * 0.5f);
    430   f3_set(axis_z, basis + 6);
    431 
    432   /* Compute the origin of the projection plane */
    433   f3_add(org, pos, f3_mulf(org, axis_z, lower[2]));
    434 
    435   do {
    436     float vec[3];
    437     double path_length[2];
    438     double delta_r;
    439 
    440     /* Uniformly sample a position onto the projection plane and use it as the
    441      * origin of the 1st ray to trace */
    442     sample[0] = ssp_rng_uniform_double(ctx->rng, -1.0, 1.0);
    443     sample[1] = ssp_rng_uniform_double(ctx->rng, -1.0, 1.0);
    444     f3_mulf(x, axis_x, (float)sample[0]);
    445     f3_mulf(y, axis_y, (float)sample[1]);
    446     f3_add(ray_org[0], f3_add(ray_org[0], x, y), org);
    447 
    448     S3D(scene_view_trace_ray(ctx->view, ray_org[0], axis_z, range, NULL, &hit));
    449     /* NULL cross section and differential cross section weight */
    450     if(S3D_HIT_NONE(&hit))
    451       break;
    452 
    453     res = compute_path_length(dev, ctx->view, &hit, ray_org[0], axis_z,
    454       dist_scaling, &path_length[0]);
    455     if(res != RES_OK) { /* Handle numerical/geometry issues */
    456       ++nfailures;
    457       continue;
    458     }
    459     /* Compute and accumulate the cross section weight */
    460     accum_cross_section(ctx, plane_area, path_length[0]);
    461 
    462     /* Avoid the estimation of the phase function */
    463     if(ctx->estimator->no_phase_function) break;
    464 
    465     /* Uniformly sample a position onto the projection plane and use it as the
    466      * origin of the 2nd ray to trace */
    467     sample[0] = ssp_rng_uniform_double(ctx->rng, -1.0, 1.0);
    468     sample[1] = ssp_rng_uniform_double(ctx->rng, -1.0, 1.0);
    469     f3_mulf(x, axis_x, (float)sample[0]);
    470     f3_mulf(y, axis_y, (float)sample[1]);
    471     f3_add(ray_org[1], f3_add(ray_org[1], x, y), org);
    472 
    473     S3D(scene_view_trace_ray(ctx->view, ray_org[1], axis_z, range, NULL, &hit));
    474     if(S3D_HIT_NONE(&hit)) /* NULL differential cross section weight */
    475       break;
    476 
    477     res = compute_path_length(dev, ctx->view, &hit, ray_org[1], axis_z,
    478       dist_scaling, &path_length[1]);
    479     if(res != RES_OK) { /* Handle numerical/geometry issues */
    480       ++nfailures;
    481       continue;
    482     }
    483     /* Compute and accumulate the per scattering angle differential cross
    484      * section weight */
    485     delta_r = f3_len(f3_sub(vec, ray_org[0], ray_org[1]));
    486     delta_r *= dist_scaling;
    487     accum_differential_cross_section(ctx, plane_area, path_length, delta_r);
    488 
    489   } while(res != RES_OK && nfailures < MAX_FAILURES);
    490 
    491   if(nfailures < MAX_FAILURES) {
    492     res = RES_OK;
    493   } else {
    494     log_error(dev,
    495 "Too many failures in computing the radiative path length. The sampled geometry\n"
    496 "might not defined a closed volume.\n");
    497   }
    498   return res;
    499 }
    500 
    501 static res_T
    502 radiative_properties
    503   (struct sschiff_device* dev,
    504    const int istep,
    505    const size_t ndirs,
    506    struct sschiff_geometry_distribution* distrib,
    507    struct integrator_context* ctx)
    508 {
    509   float lower[3], upper[3];
    510   float aabb_pt[8][3];
    511   float centroid[3];
    512   size_t idir;
    513   size_t iwlen;
    514   int i;
    515   res_T res = RES_OK;
    516   ASSERT(dev && ndirs && distrib && ctx);
    517   (void)istep;
    518 
    519   S3D(scene_view_get_aabb(ctx->view, lower, upper));
    520 
    521   /* AABB vertex layout
    522    *    6-------7
    523    *   /'      /|
    524    *  2-------3 |
    525    *  | 4.....|.5
    526    *  |,      |/
    527    *  0-------1 */
    528   f3(aabb_pt[0], lower[0], lower[1], lower[2]);
    529   f3(aabb_pt[1], upper[0], lower[1], lower[2]);
    530   f3(aabb_pt[2], lower[0], upper[1], lower[2]);
    531   f3(aabb_pt[3], upper[0], upper[1], lower[2]);
    532   f3(aabb_pt[4], lower[0], lower[1], upper[2]);
    533   f3(aabb_pt[5], upper[0], lower[1], upper[2]);
    534   f3(aabb_pt[6], lower[0], upper[1], upper[2]);
    535   f3(aabb_pt[7], upper[0], upper[1], upper[2]);
    536 
    537   f3_mulf(centroid, f3_add(centroid, lower, upper), 0.5f);
    538 
    539   /* Clear direction accumulators */
    540   FOR_EACH(iwlen, 0, ctx->nwavelengths) {
    541     ctx->accums[iwlen].extinction_cross_section = 0;
    542     ctx->accums[iwlen].absorption_cross_section = 0;
    543     ctx->accums[iwlen].scattering_cross_section = 0;
    544     ctx->accums[iwlen].avg_projected_area = 0;
    545 
    546     /* Do not clean up accumulators of invalid differential cross section */
    547     if(ctx->estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE) continue;
    548 
    549     /* Clean up to "limit_angle" since great angles are analytically computed */
    550     memset(ctx->accums[iwlen].differential_cross_section, 0,
    551       sizeof(double)*ctx->estimator->limit_angles[iwlen]);
    552     memset(ctx->accums[iwlen].differential_cross_section_cumulative, 0,
    553       sizeof(double)*ctx->estimator->limit_angles[iwlen]);
    554   }
    555 
    556   FOR_EACH(idir, 0, ndirs) {
    557     float dir[3];
    558     float basis[9];
    559     float transform[12];
    560     float pt[8][3];
    561     double volume_scaling = 1.0;
    562     double area_scaling = 1.0;
    563     double dist_scaling = 1.0;
    564 
    565     /* Sample a volume scaling factor if necessary */
    566     if(distrib->sample_volume_scaling) {
    567       res = distrib->sample_volume_scaling
    568         (ctx->rng, ctx->shape_data, &volume_scaling, distrib->context);
    569       if(res != RES_OK) {
    570         log_error(dev, "Error in sampling a volume scaling.\n");
    571         goto error;
    572       }
    573       if(volume_scaling <= 0) {
    574         log_error(dev, "Invalid volume scale factor `%g'.\n", volume_scaling);
    575         res = RES_BAD_ARG;
    576         goto error;
    577       }
    578 
    579       /* Define the scale factor to apply the the area and the distance from the
    580        * scale factor of the volume */
    581       area_scaling = pow(volume_scaling, 2.0/3.0);
    582       dist_scaling = pow(volume_scaling, 1.0/3.0);
    583     }
    584 
    585     /* Sample an orientation */
    586     ssp_ran_sphere_uniform_float(ctx->rng, dir, NULL);
    587 
    588     /* Build the transformation matrix from world to footprint space. Use the
    589      * AABB centroid as the origin of the footprint space. */
    590     build_basis(dir, basis);
    591     build_transform(centroid, basis, transform);
    592 
    593     /* Transform the AABB from world to footprint space */
    594     FOR_EACH(i, 0, 8) {
    595       f3_add(pt[i], f33_mulf3(pt[i], transform, aabb_pt[i]), transform + 9);
    596     }
    597 
    598     /* Compute the AABB of the footprint */
    599     f3_splat(lower, FLT_MAX); f3_splat(upper,-FLT_MAX);
    600     FOR_EACH(i, 0, 8) {
    601       f3_min(lower, lower, pt[i]);
    602       f3_max(upper, upper, pt[i]);
    603     }
    604 
    605     res = accum_monte_carlo_weight
    606       (dev, ctx, basis, centroid, lower, upper, area_scaling, dist_scaling);
    607     if(res != RES_OK) goto error;
    608   }
    609   /* Compute the Monte Carlo weight of the temporary accumulator */
    610   FOR_EACH(iwlen, 0, ctx->nwavelengths) {
    611     size_t iangle;
    612 
    613     #define MC_ACCUM(Data) {                                                   \
    614       const double w = ctx->accums[iwlen].Data / (double)ndirs;                \
    615       ctx->mc_accums[iwlen].Data.weight += w;                                  \
    616       ctx->mc_accums[iwlen].Data.sqr_weight += w*w;                            \
    617     } (void)0
    618 
    619     MC_ACCUM(extinction_cross_section);
    620     MC_ACCUM(absorption_cross_section);
    621     MC_ACCUM(scattering_cross_section);
    622     MC_ACCUM(avg_projected_area);
    623 
    624     if(ctx->estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE)
    625       continue;
    626 
    627     /* Accum up to "limit angle" since great angles are analitically computed */
    628     FOR_EACH(iangle, 0, ctx->estimator->limit_angles[iwlen]) {
    629       MC_ACCUM(differential_cross_section[iangle]);
    630       MC_ACCUM(differential_cross_section_cumulative[iangle]);
    631     }
    632 
    633     #undef MC_ACCUM
    634   }
    635 exit:
    636   return res;
    637 error:
    638   FOR_EACH(iwlen, 0, ctx->nwavelengths) {
    639     ctx->mc_accums[iwlen].extinction_cross_section = MC_DATA_NULL;
    640     ctx->mc_accums[iwlen].absorption_cross_section = MC_DATA_NULL;
    641     ctx->mc_accums[iwlen].scattering_cross_section = MC_DATA_NULL;
    642     ctx->mc_accums[iwlen].avg_projected_area = MC_DATA_NULL;
    643 
    644     if(ctx->estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE)
    645       continue;
    646 
    647     memset(ctx->mc_accums[iwlen].differential_cross_section, 0,
    648       sizeof(double)*ctx->estimator->limit_angles[iwlen]);
    649     memset(ctx->mc_accums[iwlen].differential_cross_section_cumulative, 0,
    650       sizeof(double)*ctx->estimator->limit_angles[iwlen]);
    651   }
    652   goto exit;
    653 }
    654 
    655 struct function_arg {
    656   double scattering_cross_section;
    657   double angle;
    658   double differential_cross_section;
    659   double differential_cross_section_cumulative;
    660 
    661   /* Precomputed values */
    662   double cos_2_angle;
    663   double cos_angle;
    664   double cos_half_angle;
    665   double sin_half_angle;
    666   double sqr_cos_angle;
    667   double sqr_sin_half_angle;
    668 };
    669 
    670 static int
    671 function_fdf(const gsl_vector* vec, void* params, gsl_vector* res, gsl_matrix* J)
    672 {
    673   struct function_arg* arg = params;
    674   const double n = gsl_vector_get(vec, 0);
    675   double u, u_prime;
    676   double v, v_prime;
    677   double coef;
    678   double f, df;
    679 
    680   if(n==2 || n==4 || n==6)
    681     return GSL_EDOM;
    682 
    683   coef = (4*PI * arg->differential_cross_section) / (1+arg->sqr_cos_angle);
    684 
    685   u = ( arg->sqr_sin_half_angle
    686        * ( (n*n - 6*n + 8) * arg->cos_2_angle
    687          + 3*n*n
    688          - 8*(n - 2) * arg->cos_angle
    689          - 26*n + 72 )
    690        - pow(arg->sin_half_angle, n) * (4*n*n - 24*n + 64) );
    691   v = ((n - 2) * (n - 4) * (n - 6));
    692 
    693   u_prime = arg->sqr_sin_half_angle
    694           * ((2*n - 6) * arg->cos_2_angle + 6*n - 8*arg->cos_angle - 26)
    695           - pow(arg->sin_half_angle, n)
    696           * (log(arg->sin_half_angle) * (4*n*n - 24*n + 64) + 8*n-24);
    697   v_prime = (n-4)*(n-6) + (n-2)*(n-6) + (n-2)*(n-4);
    698 
    699   f  = arg->differential_cross_section_cumulative
    700      - arg->scattering_cross_section
    701      + coef * u / v;
    702   df = (v*u_prime - u*v_prime) / (v*v);
    703   df = df * coef;
    704 
    705   gsl_vector_set(res, 0, f);
    706   gsl_matrix_set(J, 0, 0, df);
    707   return GSL_SUCCESS;
    708 }
    709 
    710 /* Solve the parameters used to connect the Monte-Carlo estimation of the
    711  * [cumulative] differential cross sections for small angles with their
    712  * analytical estimation for wide angles */
    713 static res_T
    714 solve_wide_angles_phase_function_model_parameters
    715   (struct sschiff_device* dev,
    716    struct solver_context* ctx,
    717    const double angle,
    718    const double sin_half_angle,
    719    const double cos_angle,
    720    const double cos_2_angle,
    721    const double scattering_cross_section,
    722    const double differential_cross_section,
    723    const double differential_cross_section_cumulative,
    724    double* n,
    725    double* r)
    726 {
    727   gsl_multiroot_function_fdf func;
    728   gsl_vector* root = NULL;
    729   struct function_arg arg;
    730   int status;
    731   int iter;
    732   const int max_iter = 1000; /* Maximum number of Newton iterations */
    733   res_T res = RES_OK;
    734   ASSERT(dev && n && r);
    735   ASSERT(eq_eps(sin(angle*0.5), sin_half_angle, 1.e-6));
    736   ASSERT(eq_eps(cos(angle), cos_angle, 1.e-6));
    737   ASSERT(eq_eps(cos(2.0*angle), cos_2_angle, 1.e-6));
    738 
    739     /* Fill system arguments */
    740   arg.angle = angle;
    741   arg.scattering_cross_section = scattering_cross_section;
    742   arg.differential_cross_section = differential_cross_section;
    743   arg.differential_cross_section_cumulative = differential_cross_section_cumulative;
    744 
    745   /* Precompute some values to speed up Newton iterations */
    746   arg.cos_2_angle = cos_2_angle;
    747   arg.cos_angle = cos_angle;
    748   arg.cos_half_angle = cos(angle * 0.5);
    749   arg.sin_half_angle = sin_half_angle;
    750   arg.sqr_cos_angle = arg.cos_angle * arg.cos_angle;
    751   arg.sqr_sin_half_angle = arg.sin_half_angle * arg.sin_half_angle;
    752 
    753   /* Setup system functions */
    754   func.f = NULL;
    755   func.df = NULL;
    756   func.fdf = function_fdf; /* Compute the function *and* its derivative */
    757   func.n = 1; /* Number of equations */
    758   func.params = &arg; /* System parameters */
    759 
    760   /* Initialise the solver. TODO perform this in an initialisation step of a
    761    * phase_function context */
    762   gsl_multiroot_fdfsolver_set(ctx->solver, &func, ctx->init_val);
    763   gsl_set_error_handler_off();
    764 
    765   /* Launch the Newton estimation. Iterate up to `max_iter'or until the
    766    * estimation is "good enough". */
    767   iter = 0;
    768   do {
    769     status = gsl_multiroot_fdfsolver_iterate(ctx->solver);
    770     if(status == GSL_SUCCESS) {
    771       status = gsl_multiroot_test_residual(ctx->solver->f, 1.e-6);
    772     }
    773   } while(status == GSL_CONTINUE && ++iter < max_iter);
    774 
    775   /* Retrieve the estimated "n" value */
    776   root = gsl_multiroot_fdfsolver_root(ctx->solver);
    777   *n = gsl_vector_get(root, 0);
    778 
    779   if(status != GSL_SUCCESS) {
    780     log_error(dev,
    781 "Cannot estimate the parameters of the wide scattering phase function model.\n"
    782 "GSL error: %s - %d.\n",
    783       gsl_strerror(status), iter);
    784     res = RES_BAD_OP;
    785     goto error;
    786   }
    787 
    788   if(*n < 0) {
    789     log_error(dev,
    790 "The estimated parameter `n' of the wide scattering angles phase function\n"
    791 "model cannot be negative.\n"
    792 "n = %g\n", *n);
    793     res = RES_BAD_OP;
    794     goto error;
    795   }
    796 
    797   /* Compute r from the estimated n */
    798   *r = 2 * arg.differential_cross_section * pow(arg.sin_half_angle, *n)
    799     / (1 + arg.sqr_cos_angle);
    800 
    801 exit:
    802   return res;
    803 error:
    804   goto exit;
    805 }
    806 
    807 /* Helper function used to compute one term of the analytic evaluation of the
    808  * cumulative differential cross section */
    809 static FINLINE double
    810 compute_differential_cross_section_cumulative_term
    811   (const double sin_half_angle,
    812    const double cos_angle,
    813    const double cos_2_angle,
    814    const double n)
    815 {
    816   return
    817     -pow(sin_half_angle, 2-n)
    818   * ((n*n - 6*n + 8)*cos_2_angle + 3*n*n - 8*(n-2)*cos_angle - 26*n + 72)
    819   / ((n-2)*(n-4)*(n-6));
    820 }
    821 
    822 static res_T
    823 compute_small_scattering_angles_properties
    824   (struct sschiff_device* dev,
    825    struct solver_context* ctx,
    826    const size_t iwlen,
    827    const size_t nangles,
    828    struct sschiff_estimator* estimator)
    829 {
    830   double rcp_scattering_cross_section;
    831   double rcp_sqr_scattering_cross_section;
    832   struct sschiff_state scattering_cross_section;
    833   size_t ilimit_angle;
    834   size_t iangle;
    835   ASSERT(dev && estimator && iwlen < sa_size(estimator->wavelengths) && ctx);
    836   (void)ctx, (void)dev, (void)nangles;
    837 
    838   /* Fetch the limit angle and precompute some values */
    839   ilimit_angle = estimator->limit_angles[iwlen]-1;
    840 
    841   get_mc_value
    842     (&estimator->accums[iwlen].scattering_cross_section,
    843      estimator->nrealisations,
    844      &scattering_cross_section);
    845 
    846   /* Compute the [cumulative] phase function for small angles */
    847   rcp_scattering_cross_section = 1.0/scattering_cross_section.E;
    848   rcp_sqr_scattering_cross_section =
    849     rcp_scattering_cross_section * rcp_scattering_cross_section;
    850 
    851   FOR_EACH(iangle, 0, ilimit_angle+1) {
    852     struct mc_data mc_data;
    853 
    854     mc_data = estimator->accums[iwlen].differential_cross_section[iangle];
    855     mc_data.weight *= rcp_scattering_cross_section;
    856     mc_data.sqr_weight *= rcp_sqr_scattering_cross_section;
    857     get_mc_value(&mc_data, estimator->nrealisations,
    858        &estimator->phase_functions[iwlen].values[iangle]);
    859 
    860     mc_data = estimator->accums[iwlen].differential_cross_section_cumulative[iangle];
    861     mc_data.weight *= rcp_scattering_cross_section;
    862     mc_data.sqr_weight *= rcp_sqr_scattering_cross_section;
    863     get_mc_value(&mc_data, estimator->nrealisations,
    864        &estimator->phase_functions[iwlen].cumulative[iangle]);
    865   }
    866   return RES_OK;
    867 }
    868 
    869 static res_T
    870 compute_large_scattering_angles_properties
    871   (struct sschiff_device* dev,
    872    struct solver_context* ctx,
    873    const size_t iwlen,
    874    const size_t nangles,
    875    struct sschiff_estimator* estimator)
    876 {
    877   double limit_angle;
    878   double sin_half_limit_angle;
    879   double cos_limit_angle;
    880   double cos_2_limit_angle;
    881   double coef_limit;
    882   double n, r; /* Connector values */
    883   double rcp_scattering_cross_section;
    884   struct sschiff_state scattering_cross_section;
    885   struct sschiff_state limit_differential_cross_section;
    886   struct sschiff_state limit_differential_cross_section_cumulative;
    887   size_t ilimit_angle; /* Index of the limit angle of the current wavelength */
    888   size_t iangle;
    889   res_T res = RES_OK;
    890   ASSERT(dev && estimator && iwlen < sa_size(estimator->wavelengths) && ctx);
    891 
    892   /* Fetch the limit angle and precompute some values */
    893   ilimit_angle = estimator->limit_angles[iwlen]-1;
    894   limit_angle = estimator->angles[ilimit_angle];
    895   sin_half_limit_angle = sin(0.5*limit_angle);
    896   cos_limit_angle = cos(limit_angle);
    897   cos_2_limit_angle = cos(2.0*limit_angle);
    898 
    899   get_mc_value
    900     (&estimator->accums[iwlen].scattering_cross_section,
    901      estimator->nrealisations,
    902      &scattering_cross_section);
    903   get_mc_value
    904     (&estimator->accums[iwlen].differential_cross_section[ilimit_angle],
    905      estimator->nrealisations,
    906      &limit_differential_cross_section);
    907   get_mc_value
    908     (&estimator->accums[iwlen].differential_cross_section_cumulative[ilimit_angle],
    909      estimator->nrealisations,
    910      &limit_differential_cross_section_cumulative);
    911 
    912   /* Find the connector values between small and wide angles */
    913   res = solve_wide_angles_phase_function_model_parameters
    914     (dev,
    915      ctx,
    916      limit_angle,
    917      sin_half_limit_angle,
    918      cos_limit_angle,
    919      cos_2_limit_angle,
    920      scattering_cross_section.E,
    921      limit_differential_cross_section.E,
    922      limit_differential_cross_section_cumulative.E,
    923      &n, &r);
    924   if(res != RES_OK) {
    925     log_error(estimator->dev,
    926 "Couldn't estimate the parameters of the phase function for wide scattering\n"
    927 "angles. The phase function is thus not computed.\n"
    928 "Wavelength = %g micron\n",
    929       estimator->wavelengths[iwlen]);
    930     goto error;
    931   }
    932 
    933   if(n < 2 || n > 4) {
    934     log_warning(estimator->dev,
    935 "The wide scattering angles phase function model parameter `n' is not in\n"
    936 "[2, 4]. One might increase the number of realisations and/or adjust the\n"
    937 "characteristic length of the particles (that sets the limit between small\n"
    938 "and large scattering angles) and/or increase the number of scattering\n"
    939 "angles computed.\n"
    940 "  wavelength = %g micron\n"
    941 "  n = %g\n"
    942 "  scattering cross section = %g +/- %g\n"
    943 "  differential cross section = %g +/- %g\n"
    944 "  differential cross section cumulative = %g +/- %g\n",
    945       estimator->wavelengths[iwlen],
    946       n,
    947       scattering_cross_section.E,
    948       scattering_cross_section.SE,
    949       limit_differential_cross_section.E,
    950       limit_differential_cross_section.SE,
    951       limit_differential_cross_section_cumulative.E,
    952       limit_differential_cross_section_cumulative.SE);
    953   }
    954 
    955   /* Save the `n' wide angle parameter */
    956   estimator->wide_angles_parameter[iwlen] = n;
    957 
    958   /* Precomputed vlaue  */
    959   coef_limit = compute_differential_cross_section_cumulative_term
    960     (sin_half_limit_angle, cos_limit_angle, cos_2_limit_angle, n);
    961 
    962   /* Fill the [cumulative] differential cross sections of wide angles with the
    963    * analytic model */
    964   FOR_EACH(iangle, ilimit_angle+1, nangles) {
    965     struct mc_accum* accum = estimator->accums + iwlen;
    966     double angle, sin_half_angle, cos_angle, cos_2_angle;
    967     double coef;
    968 
    969     /* Precompute some values */
    970     angle = estimator->angles[iangle];
    971     cos_angle = cos(angle);
    972     cos_2_angle = cos(2.0*angle);
    973     sin_half_angle = sin(angle*0.5);
    974 
    975     /* Analytically compute the differential cross section */
    976     accum->differential_cross_section[iangle].sqr_weight = -1.f;
    977     accum->differential_cross_section[iangle].weight =
    978       r * (1+cos_angle*cos_angle) / (2.0*pow(sin_half_angle, n));
    979 
    980     /* Analytically compute the differential cross section cumulative */
    981     coef = compute_differential_cross_section_cumulative_term
    982       (sin_half_angle, cos_angle, cos_2_angle, n);
    983     accum->differential_cross_section_cumulative[iangle].sqr_weight = -1.f;
    984     accum->differential_cross_section_cumulative[iangle].weight =
    985       limit_differential_cross_section_cumulative.E + 2*PI*r*(coef-coef_limit);
    986   }
    987 
    988   /* Compute the [cumulative] phase function for wide angles */
    989   rcp_scattering_cross_section = 1.0/scattering_cross_section.E;
    990   FOR_EACH(iangle, ilimit_angle + 1, nangles) {
    991     estimator->phase_functions[iwlen].values[iangle].E =
    992       estimator->accums[iwlen].differential_cross_section[iangle].weight
    993     * rcp_scattering_cross_section;
    994 
    995     estimator->phase_functions[iwlen].cumulative[iangle].E =
    996       estimator->accums[iwlen].differential_cross_section_cumulative[iangle].weight
    997     * rcp_scattering_cross_section;
    998 
    999     /* The phase function for wide angles is analitically computed, i.e. there
   1000      * is no variance or standard error */
   1001     estimator->phase_functions[iwlen].values[iangle].V = 0;
   1002     estimator->phase_functions[iwlen].values[iangle].SE = 0;
   1003     estimator->phase_functions[iwlen].cumulative[iangle].V = 0;
   1004     estimator->phase_functions[iwlen].cumulative[iangle].SE = 0;
   1005   }
   1006 
   1007 exit:
   1008   return res;
   1009 error:
   1010   FOR_EACH(iangle, ilimit_angle+1, nangles) {
   1011     estimator->phase_functions[iwlen].values[iangle].E = -1;
   1012     estimator->phase_functions[iwlen].values[iangle].V = -1;
   1013     estimator->phase_functions[iwlen].values[iangle].SE = -1;
   1014     estimator->phase_functions[iwlen].cumulative[iangle].E = -1;
   1015     estimator->phase_functions[iwlen].cumulative[iangle].V = -1;
   1016     estimator->phase_functions[iwlen].cumulative[iangle].SE = -1;
   1017   }
   1018   goto exit;
   1019 }
   1020 
   1021 static res_T
   1022 compute_scattering_angles_properties
   1023   (struct sschiff_device* dev,
   1024    struct solver_context* ctx,
   1025    const size_t iwlen,
   1026    const size_t nangles,
   1027    struct sschiff_estimator* estimator)
   1028 {
   1029   res_T res = RES_OK;
   1030 
   1031   res = compute_small_scattering_angles_properties
   1032     (dev, ctx, iwlen, nangles, estimator);
   1033   if(res != RES_OK) goto error;
   1034 
   1035   if(!estimator->discard_large_angles) {
   1036     res = compute_large_scattering_angles_properties
   1037       (dev, ctx, iwlen, nangles, estimator);
   1038     if(res != RES_OK) goto error;
   1039   }
   1040 
   1041 exit:
   1042   return res;
   1043 error:
   1044   goto exit;
   1045 }
   1046 
   1047 static res_T
   1048 inverse_cumulative_phase_function
   1049   (const struct sschiff_estimator* estimator,
   1050    const double* cumulative_small_angles,
   1051    const size_t iwlen,
   1052    const double cumulative,
   1053    double* theta)
   1054 {
   1055   double u, lower, upper;
   1056   size_t iangle, nsmall_angles;
   1057   ASSERT(estimator && cumulative_small_angles && theta);
   1058   ASSERT(cumulative >= 0.0 && cumulative <= 1.0);
   1059   ASSERT(iwlen < sa_size(estimator->phase_functions));
   1060 
   1061   nsmall_angles = estimator->limit_angles[iwlen];
   1062   if(cumulative < cumulative_small_angles[nsmall_angles-1]) {
   1063     /* Look for the cumulative in the filtered small angles array */
   1064     double* found = search_lower_bound(&cumulative, cumulative_small_angles,
   1065       nsmall_angles, sizeof(double), cmp_double);
   1066     if(!found) {
   1067       log_error(estimator->dev,
   1068         "Error in inverting the phase function cumulative for small angles.\n");
   1069       return RES_BAD_OP;
   1070     }
   1071     iangle = (size_t)(found - cumulative_small_angles);
   1072     upper = cumulative_small_angles[iangle];
   1073     lower = cumulative_small_angles[iangle-1];
   1074 
   1075     /* Use the cumulative bounds to linearly interpolate the angles */
   1076     u = (cumulative - lower) / (upper - lower);
   1077     *theta = u*estimator->angles[iangle] + (1.0 - u)*estimator->angles[iangle-1];
   1078   } else if(estimator->discard_large_angles) {
   1079     *theta = -1;
   1080   } else {
   1081     /* Look for the cumulative in the wide angles cumulative */
   1082     struct sschiff_state cumul;
   1083     struct sschiff_state* found = NULL;
   1084     size_t nangles;
   1085     cumul.E = cumulative;
   1086 
   1087     nangles = sa_size(estimator->angles);
   1088     found = search_lower_bound
   1089       (&cumul,
   1090        estimator->phase_functions[iwlen].cumulative + nsmall_angles,
   1091        nangles - nsmall_angles,
   1092        sizeof(struct sschiff_state), cmp_schiff_state);
   1093     if(!found) {
   1094       log_error(estimator->dev,
   1095         "Error in inverting the phase function cumulative for wide angles.\n");
   1096       return RES_BAD_OP;
   1097     }
   1098     iangle = (size_t)(found - estimator->phase_functions[iwlen].cumulative);
   1099     ASSERT(iangle >= nsmall_angles);
   1100     upper = estimator->phase_functions[iwlen].cumulative[iangle].E;
   1101     if(iangle == nsmall_angles) {
   1102       lower = cumulative_small_angles[nsmall_angles-1];
   1103     } else {
   1104       lower = estimator->phase_functions[iwlen].cumulative[iangle-1].E;
   1105     }
   1106     /* Use the cumulative bounds to linearly interpolate the angles */
   1107     u = (cumulative - lower) / (upper - lower);
   1108     *theta = u*estimator->angles[iangle] + (1.0 - u)*estimator->angles[iangle-1];
   1109   }
   1110 
   1111   return RES_OK;
   1112 }
   1113 
   1114 static char
   1115 check_distribution(struct sschiff_geometry_distribution* distrib)
   1116 {
   1117   ASSERT(distrib);
   1118   return distrib->sample != NULL
   1119       && distrib->characteristic_length > 0;
   1120 }
   1121 
   1122 static void
   1123 integrator_context_release(struct integrator_context* ctx)
   1124 {
   1125   size_t iwlen;
   1126   ASSERT(ctx);
   1127 
   1128   if(ctx->rng) SSP(rng_ref_put(ctx->rng));
   1129   if(ctx->estimator) SSCHIFF(estimator_ref_put(ctx->estimator));
   1130   if(ctx->shape) S3D(shape_ref_put(ctx->shape));
   1131   if(ctx->scene) S3D(scene_ref_put(ctx->scene));
   1132   if(ctx->view) S3D(scene_view_ref_put(ctx->view));
   1133 
   1134   #define RELEASE(Data) if(Data) sa_release(Data)
   1135   if(ctx->accums) {
   1136     FOR_EACH(iwlen, 0, ctx->nwavelengths) {
   1137       RELEASE(ctx->accums[iwlen].differential_cross_section);
   1138       RELEASE(ctx->accums[iwlen].differential_cross_section_cumulative);
   1139     }
   1140     sa_release(ctx->accums);
   1141   }
   1142   if(ctx->mc_accums) {
   1143     FOR_EACH(iwlen, 0, ctx->nwavelengths) {
   1144       RELEASE(ctx->mc_accums[iwlen].differential_cross_section);
   1145       RELEASE(ctx->mc_accums[iwlen].differential_cross_section_cumulative);
   1146     }
   1147     sa_release(ctx->mc_accums);
   1148   }
   1149   #undef RELEASE
   1150 }
   1151 
   1152 static res_T
   1153 integrator_context_init
   1154   (struct integrator_context* ctx,
   1155    struct s3d_device* s3d,
   1156    struct ssp_rng* rng,
   1157    struct sschiff_estimator* estimator,
   1158    const size_t nwavelengths,
   1159    const size_t nangles)
   1160 {
   1161   size_t iwlen;
   1162   res_T res = RES_OK;
   1163   ASSERT(ctx && s3d && rng && nwavelengths && nangles >= 2);
   1164 
   1165   memset(ctx, 0, sizeof(struct integrator_context));
   1166 
   1167   if(RES_OK!=(res = s3d_shape_create_mesh(s3d, &ctx->shape))) goto error;
   1168   if(RES_OK!=(res = s3d_scene_create(s3d, &ctx->scene))) goto error;
   1169   if(RES_OK!=(res = s3d_scene_attach_shape(ctx->scene, ctx->shape))) goto error;
   1170 
   1171   #define RESIZE(Data, N) {                                                    \
   1172     if(!sa_add((Data), (N))) {                                                 \
   1173       res = RES_MEM_ERR;                                                       \
   1174       goto error;                                                              \
   1175     }                                                                          \
   1176     memset((Data), 0, sizeof((Data)[0])*(N));                                  \
   1177   } (void)0
   1178   RESIZE(ctx->accums, nwavelengths);
   1179   FOR_EACH(iwlen, 0, nwavelengths) {
   1180     /* Do not allocate the accumulators if no limit angle was found */
   1181     if(estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE) continue;
   1182     RESIZE(ctx->accums[iwlen].differential_cross_section, nangles);
   1183     RESIZE(ctx->accums[iwlen].differential_cross_section_cumulative, nangles);
   1184   }
   1185 
   1186   RESIZE(ctx->mc_accums, nwavelengths);
   1187   FOR_EACH(iwlen, 0, nwavelengths) {
   1188     /* Do not allocate the accumulators if no limit angle was found */
   1189     if(estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE) continue;
   1190     RESIZE(ctx->mc_accums[iwlen].differential_cross_section, nangles);
   1191     RESIZE(ctx->mc_accums[iwlen].differential_cross_section_cumulative, nangles);
   1192   }
   1193   #undef RESIZE
   1194 
   1195   SSP(rng_ref_get(rng));
   1196   ctx->rng = rng;
   1197   SSCHIFF(estimator_ref_get(estimator));
   1198   ctx->estimator = estimator;
   1199   ctx->nwavelengths = nwavelengths;
   1200   ctx->nangles = nangles;
   1201 
   1202 exit:
   1203   return res;
   1204 error:
   1205   integrator_context_release(ctx);
   1206   goto exit;
   1207 }
   1208 
   1209 static void
   1210 solver_context_release(struct solver_context* ctx)
   1211 {
   1212   ASSERT(ctx);
   1213   if(ctx->solver) gsl_multiroot_fdfsolver_free(ctx->solver);
   1214   if(ctx->init_val) gsl_vector_free(ctx->init_val);
   1215 }
   1216 
   1217 static res_T
   1218 solver_context_init(struct sschiff_device* dev, struct solver_context* ctx)
   1219 {
   1220   res_T res = RES_OK;
   1221   ASSERT(ctx);
   1222   memset(ctx, 0, sizeof(*ctx));
   1223 
   1224   /* Create a Newton-Raphson nonlinear solver with derivatives */
   1225   ctx->solver = gsl_multiroot_fdfsolver_alloc(gsl_multiroot_fdfsolver_newton, 1);
   1226   if(!ctx->solver) {
   1227     log_error(dev,
   1228       "Not enough memory. Couldn't allocate the GSL Newton solver.\n");
   1229     res = RES_MEM_ERR;
   1230     goto error;
   1231   }
   1232 
   1233   /* Allocate the initial value vector */
   1234   ctx->init_val = gsl_vector_alloc(1);
   1235   if(!ctx->init_val) {
   1236     log_error(dev,
   1237       "Not enough memory. Couldn't allocate the GSL init vector.\n");
   1238     res = RES_MEM_ERR;
   1239     goto error;
   1240   }
   1241   gsl_vector_set(ctx->init_val, 0, 3);
   1242 
   1243 exit:
   1244   return res;
   1245 error:
   1246   solver_context_release(ctx);
   1247   goto exit;
   1248 }
   1249 
   1250 static res_T
   1251 begin_realisation
   1252   (struct sschiff_device* dev,
   1253    struct sschiff_geometry_distribution* distrib,
   1254    struct integrator_context* ctx)
   1255 {
   1256   struct s3d_accel_struct_conf accel_struct_conf = S3D_ACCEL_STRUCT_CONF_DEFAULT;
   1257   res_T res = RES_OK;
   1258   ASSERT(dev && distrib && ctx && !ctx->view);
   1259 
   1260   /* Sample a particle */
   1261   res = distrib->sample
   1262     (ctx->rng, &ctx->shape_data, ctx->shape, distrib->context);
   1263   if(res != RES_OK) {
   1264     log_error(dev, "Error in sampling a particle.\n");
   1265     goto error;
   1266   }
   1267 
   1268   accel_struct_conf.quality = S3D_ACCEL_STRUCT_QUALITY_LOW;
   1269   accel_struct_conf.mask =
   1270     S3D_ACCEL_STRUCT_FLAG_ROBUST
   1271   | S3D_ACCEL_STRUCT_FLAG_DYNAMIC;
   1272 
   1273   /* Build the Star-3D representation of the sampled shape */
   1274   S3D(scene_view_create2(ctx->scene, S3D_TRACE, &accel_struct_conf, &ctx->view));
   1275 
   1276 exit:
   1277   return res;
   1278 error:
   1279   if(ctx->view) {
   1280     S3D(scene_view_ref_put(ctx->view));
   1281     ctx->view = NULL;
   1282   }
   1283   goto exit;
   1284 }
   1285 
   1286 static FINLINE void
   1287 end_realisation(struct integrator_context* ctx)
   1288 {
   1289   ASSERT(ctx && ctx->view);
   1290   S3D(scene_view_ref_put(ctx->view));
   1291   ctx->view = NULL;
   1292 }
   1293 
   1294 static void
   1295 estimator_release(ref_T* ref)
   1296 {
   1297   struct sschiff_estimator* estimator;
   1298   struct sschiff_device* dev;
   1299   size_t nwavelengths;
   1300   size_t i;
   1301   ASSERT(ref);
   1302 
   1303   estimator = CONTAINER_OF(ref, struct sschiff_estimator, ref);
   1304   dev = estimator->dev;
   1305   nwavelengths = sa_size(estimator->accums);
   1306 
   1307   #define RELEASE(Data) if(Data) sa_release(Data)
   1308   RELEASE(estimator->wavelengths);
   1309   RELEASE(estimator->angles);
   1310   RELEASE(estimator->limit_angles);
   1311   RELEASE(estimator->wide_angles_parameter);
   1312   RELEASE(estimator->wavenumbers);
   1313   RELEASE(estimator->properties);
   1314   if(estimator->accums) {
   1315     FOR_EACH(i, 0, nwavelengths) {
   1316       RELEASE(estimator->accums[i].differential_cross_section);
   1317       RELEASE(estimator->accums[i].differential_cross_section_cumulative);
   1318     }
   1319     sa_release(estimator->accums);
   1320   }
   1321   if(estimator->phase_functions) {
   1322     FOR_EACH(i, 0, nwavelengths) {
   1323       RELEASE(estimator->phase_functions[i].values);
   1324       RELEASE(estimator->phase_functions[i].cumulative);
   1325     }
   1326     sa_release(estimator->phase_functions);
   1327   }
   1328   #undef RELEASE
   1329 
   1330   MEM_RM(dev->allocator, estimator);
   1331   SSCHIFF(device_ref_put(dev));
   1332 }
   1333 
   1334 static res_T
   1335 estimator_create
   1336   (struct sschiff_device* dev,
   1337    struct sschiff_geometry_distribution* distrib,
   1338    const double* wavelengths,
   1339    const size_t nwavelengths,
   1340    sschiff_scattering_angles_distribution_T angles_distrib,
   1341    const size_t nangles,
   1342    const int discard_large_angles,
   1343    struct sschiff_estimator** out_estimator)
   1344 {
   1345   struct sschiff_estimator* estimator = NULL;
   1346   double rcp_PI_Lc;
   1347   size_t i;
   1348   int hold_valid_limit_angle = 0;
   1349   res_T res = RES_OK;
   1350   ASSERT(dev && distrib && wavelengths && nwavelengths);
   1351   ASSERT(angles_distrib && nangles && out_estimator);
   1352 
   1353   estimator = MEM_CALLOC(dev->allocator, 1, sizeof(struct sschiff_estimator));
   1354   if(!estimator) {
   1355     log_error
   1356       (dev, "Couldn't allocate the Star-Schiff estimator.\n");
   1357     res = RES_MEM_ERR;
   1358     goto error;
   1359   }
   1360   ref_init(&estimator->ref);
   1361   SSCHIFF(device_ref_get(dev));
   1362   estimator->dev = dev;
   1363   estimator->discard_large_angles = discard_large_angles;
   1364 
   1365   #define RESIZE(Array, Count, ErrMsg) {                                       \
   1366     if(!sa_add(Array, Count)) {                                                \
   1367       log_error(dev, ErrMsg);                                                  \
   1368       res = RES_MEM_ERR;                                                       \
   1369       goto error;                                                              \
   1370     }                                                                          \
   1371     memset(Array, 0, sizeof(Array[0])*Count);                                  \
   1372   } (void)0
   1373 
   1374   /* Check the wavelengths */
   1375   FOR_EACH(i, 1, nwavelengths) {
   1376     if(wavelengths[i] <= wavelengths[i-1]) {
   1377       log_error(dev,
   1378         "The submitted wavelengths are not sorted in ascending order.\n");
   1379       res = RES_BAD_ARG;
   1380       goto error;
   1381     }
   1382   }
   1383   /* Copy the wavelengths */
   1384   RESIZE(estimator->wavelengths, nwavelengths,
   1385     "Couldn't allocate the list of estimated wavelengths.\n");
   1386   memcpy(estimator->wavelengths, wavelengths, nwavelengths*sizeof(double));
   1387 
   1388   /* Generate the scattering angles */
   1389   RESIZE(estimator->angles, nangles,
   1390     "Couldn't allocate the list of scattering angles.\n");
   1391   angles_distrib(estimator->angles, nangles);
   1392   if(estimator->angles[0] != 0.f
   1393   || !eq_eps(estimator->angles[nangles-1], PI, 1.e-6)) {
   1394     log_error(dev, "Invalid scattering angle distribution.\n");
   1395     log_error(dev, "The first and last angles must be 0 and PI, respectively.\n");
   1396     res = RES_BAD_ARG;
   1397     goto error;
   1398   }
   1399   FOR_EACH(i, 1, nangles) {
   1400     if(estimator->angles[i] <= estimator->angles[i-1]) {
   1401       log_error(dev, "Invalid scattering angle distribution.\n");
   1402       log_error(dev, "Angles must be sorted in ascending order in [0, PI]\n");
   1403       res = RES_BAD_ARG;
   1404       goto error;
   1405     }
   1406   }
   1407 
   1408   /* Allocate miscellaneous array of temporary/precomputed data */
   1409   RESIZE(estimator->limit_angles, nwavelengths,
   1410     "Couldn't allocate the per wavelength indices of the limit scattering angle.\n");
   1411   RESIZE(estimator->wide_angles_parameter, nwavelengths,
   1412     "Couldn't allocate the per wavelength `n' parameter of the wide scattering\n"
   1413     "angles model.\n");
   1414   RESIZE(estimator->wavenumbers, nwavelengths,
   1415     "Couldn't allocate the list of wavenumbers.\n");
   1416   RESIZE(estimator->properties, nwavelengths,
   1417     "Couldn't allocate the per wavelength optical properties.\n");
   1418 
   1419   rcp_PI_Lc = 1.0 / (PI*distrib->characteristic_length);
   1420   FOR_EACH(i, 0, nwavelengths) {
   1421     double lambda_e, theta_l;
   1422     double* angle;
   1423 
   1424     /* Fetch the particle optical properties */
   1425     distrib->material.get_property
   1426       (distrib->material.material,
   1427        estimator->wavelengths[i],
   1428        &estimator->properties[i]);
   1429 
   1430     /* Precompute the wavenumbers */
   1431     lambda_e =
   1432       estimator->wavelengths[i]
   1433     / estimator->properties[i].medium_refractive_index;
   1434     estimator->wavenumbers[i] = 2.0*PI/lambda_e;
   1435 
   1436     /* Search for limit scattering angle */
   1437     theta_l = sqrt(lambda_e * rcp_PI_Lc);
   1438     if(theta_l <= 0 || theta_l >= PI) {
   1439       log_warning(dev,
   1440 "Invalid theta limit, i.e. angle between small and wide scattering angles\n"
   1441 "`%g'. The phase function for the wavelengths `%g' and its [inverse]\n"
   1442 "cumulative will be not computed.\n",
   1443         theta_l, estimator->wavelengths[i]);
   1444       estimator->limit_angles[i] = INVALID_LIMIT_ANGLE;
   1445     } else {
   1446       angle = search_lower_bound(&theta_l, estimator->angles, nangles,
   1447         sizeof(double), cmp_double);
   1448       if(!angle) {
   1449         log_error(dev,
   1450 "Can't find a limit scattering angle for theta limit `%g'.\n", theta_l);
   1451         res = RES_BAD_ARG;
   1452         goto error;
   1453       }
   1454      /* if(eq_eps(*angle, PI, 1.e-6)) {
   1455         log_error(dev, "Invalid limit scattering angle `%g'.\n", *angle);
   1456         res = RES_BAD_ARG;
   1457         goto error;
   1458       }*/
   1459       /* Define the index of the first "wide scattering angle" */
   1460       estimator->limit_angles[i] = (size_t)(angle - estimator->angles);
   1461       ASSERT(estimator->limit_angles[i] < nangles);
   1462       hold_valid_limit_angle = 1;
   1463     }
   1464   }
   1465   estimator->no_phase_function = !hold_valid_limit_angle;
   1466   if(estimator->no_phase_function) {
   1467     /* No phase function => scattering angles and wide angle parameter are
   1468      * useless */
   1469     sa_release(estimator->angles);
   1470     sa_release(estimator->wide_angles_parameter);
   1471     estimator->angles = NULL;
   1472     estimator->wide_angles_parameter = NULL;
   1473   }
   1474 
   1475   /* Allocate the estimator accumulators */
   1476   RESIZE(estimator->accums, nwavelengths,
   1477     "Couldn't allocate the Monte Carlo accumulator of the estimator.\n");
   1478   if(!estimator->no_phase_function) {
   1479     FOR_EACH(i, 0, nwavelengths) {
   1480       /* Do not allocate the MC accumulator if no limit angle was found */
   1481       if(estimator->limit_angles[i] == INVALID_LIMIT_ANGLE) continue;
   1482       RESIZE(estimator->accums[i].differential_cross_section, nangles,
   1483         "Couldn't allocate the differential cross sections to estimate.\n");
   1484       RESIZE(estimator->accums[i].differential_cross_section_cumulative, nangles,
   1485         "Couldn't allocate the cumulative differential cross sections to estimate.\n");
   1486     }
   1487   }
   1488 
   1489   if(!estimator->no_phase_function) {
   1490     /* Allocate the phase function data */
   1491     RESIZE(estimator->phase_functions, nwavelengths,
   1492       "Couldn't allocate the per wavelength phase functions data.\n");
   1493     FOR_EACH(i, 0, nwavelengths) {
   1494       /* Do not allocate the phase functions data if no limit angle was found */
   1495       if(estimator->limit_angles[i] == INVALID_LIMIT_ANGLE) continue;
   1496       RESIZE(estimator->phase_functions[i].values, nangles,
   1497         "Couldn't allocate the per angle phase function values.\n");
   1498       RESIZE(estimator->phase_functions[i].cumulative, nangles,
   1499         "Couldn't allocate the per angle cumulative phase function.\n");
   1500     }
   1501   }
   1502   #undef RESIZE
   1503 
   1504 exit:
   1505   *out_estimator = estimator;
   1506   return res;
   1507 error:
   1508   if(estimator) {
   1509     SSCHIFF(estimator_ref_put(estimator));
   1510     estimator = NULL;
   1511   }
   1512   goto exit;
   1513 }
   1514 
   1515 /*******************************************************************************
   1516  * Exported functions
   1517  ******************************************************************************/
   1518 res_T
   1519 sschiff_integrate
   1520   (struct sschiff_device* dev,
   1521    struct ssp_rng* rng,
   1522    struct sschiff_geometry_distribution* distrib,
   1523    const double* wavelengths,
   1524    const size_t nwavelengths,
   1525    const sschiff_scattering_angles_distribution_T angles_distrib,
   1526    const size_t nangles,
   1527    const size_t ngeoms,
   1528    const size_t ndirs,
   1529    const int discard_large_angles,
   1530    struct sschiff_estimator** out_estimator)
   1531 {
   1532   struct integrator_context* ctxs = NULL;
   1533   struct solver_context* solver_ctxs = NULL;
   1534   struct sschiff_estimator* estimator = NULL;
   1535   struct ssp_rng** rngs = NULL;
   1536   struct ssp_rng_proxy* rng_proxy = NULL;
   1537   size_t i;
   1538   int iwlen;
   1539   int igeom;
   1540   ATOMIC res = (ATOMIC)RES_OK;
   1541 
   1542   if(!dev || !rng || !distrib || !wavelengths || !nwavelengths
   1543   || !angles_distrib || nangles < 2 || !ngeoms || !ndirs || !out_estimator
   1544   || !check_distribution(distrib)) {
   1545     return RES_BAD_ARG;
   1546   }
   1547 
   1548   /* Create the Schiff estimator */
   1549   res = estimator_create(dev, distrib, wavelengths, nwavelengths,
   1550     angles_distrib, nangles, discard_large_angles, &estimator);
   1551   if(res != RES_OK) goto error;
   1552   estimator->nrealisations = ngeoms;
   1553 
   1554   /* Create a RNG proxy from the submitted RNG state */
   1555   res = ssp_rng_proxy_create_from_rng
   1556     (dev->allocator, rng, dev->nthreads, &rng_proxy);
   1557   if(res != RES_OK) {
   1558     log_error(dev, "Couldn't create the proxy allocator\n");
   1559     goto error;
   1560   }
   1561 
   1562   /* Create per thread data structures */
   1563   if(!sa_add(ctxs, dev->nthreads)) {
   1564     log_error(dev, "Couldn't allocate the integrator contexts.\n");
   1565     res = RES_MEM_ERR;
   1566     goto error;
   1567   }
   1568   if(!sa_add(solver_ctxs, dev->nthreads)) {
   1569     log_error(dev, "Couldn'nt allocate the solver contexts.\n");
   1570     res = RES_MEM_ERR;
   1571     goto error;
   1572   }
   1573   if(!sa_add(rngs, dev->nthreads)) {
   1574     log_error(dev, "Couldn't allocate the list of RNGs.\n");
   1575     res = RES_MEM_ERR;
   1576     goto error;
   1577   }
   1578   memset(ctxs, 0, sizeof(ctxs[0])*dev->nthreads);
   1579   memset(solver_ctxs, 0, sizeof(solver_ctxs[0])*dev->nthreads);
   1580   memset(rngs, 0, sizeof(rngs[0])*dev->nthreads);
   1581 
   1582   /* Init the per thread data structures */
   1583   FOR_EACH(i, 0, dev->nthreads) {
   1584     res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs + i);
   1585     if(res != RES_OK) {
   1586       log_error(dev,
   1587         "Couldn't create the RNG of the thread \"%lu\".\n", (unsigned long)i);
   1588       goto error;
   1589     }
   1590     res = integrator_context_init(ctxs + i, dev->s3d[i], rngs[i], estimator,
   1591       nwavelengths, nangles);
   1592     if(res != RES_OK) {
   1593       log_error(dev,
   1594         "Couldn't initialise the integrator context of the thread \"%lu\".\n",
   1595         (unsigned long)i);
   1596       goto error;
   1597     }
   1598     res = solver_context_init(dev, solver_ctxs + i);
   1599     if(res != RES_OK) {
   1600       log_error(dev,
   1601         "Couldn't initialise the solver context of the thread \"%lu\".\n",
   1602         (unsigned long)i);
   1603       goto error;
   1604     }
   1605   }
   1606 
   1607   /* Paralell Schiff integration */
   1608   #pragma omp parallel for schedule(static)
   1609   for(igeom=0; igeom < (int)ngeoms; ++igeom) {
   1610     const int ithread = omp_get_thread_num();
   1611     ATOMIC res_local = RES_OK;
   1612 
   1613     if(ATOMIC_GET(&res) != RES_OK) continue;
   1614 
   1615     /* Setup the data for the current realisation */
   1616     res_local = begin_realisation(dev, distrib, ctxs+ithread);
   1617     if(res_local != RES_OK) {
   1618       ATOMIC_CAS(&res, res_local, RES_OK);
   1619       continue;
   1620     }
   1621 
   1622     /* Schiff Estimation */
   1623     res_local = radiative_properties(dev, igeom, ndirs, distrib, ctxs+ithread);
   1624     if(res_local != RES_OK) ATOMIC_CAS(&res, res_local, RES_OK);
   1625 
   1626     end_realisation(ctxs + ithread);
   1627   }
   1628   if(res != RES_OK) goto error; /* Handle integration error */
   1629 
   1630   /* Merge the per thread integration results */
   1631   FOR_EACH(i, 0, dev->nthreads) {
   1632     FOR_EACH(iwlen, 0, (int)nwavelengths) {
   1633       size_t iangle;
   1634       #define MC_ACCUM(Data) {                                                 \
   1635         const struct mc_accum* mc_accum = ctxs[i].mc_accums + iwlen;           \
   1636         estimator->accums[iwlen].Data.weight += mc_accum->Data.weight;         \
   1637         estimator->accums[iwlen].Data.sqr_weight += mc_accum->Data.sqr_weight; \
   1638       } (void)0
   1639       MC_ACCUM(extinction_cross_section);
   1640       MC_ACCUM(absorption_cross_section);
   1641       MC_ACCUM(scattering_cross_section);
   1642       MC_ACCUM(avg_projected_area);
   1643 
   1644       /* Discard differential cross section accumulation for invalid angle */
   1645       if(estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE) continue;
   1646 
   1647       /* Accum up to "limit angle"; Great angles are analitically computed */
   1648       FOR_EACH(iangle, 0, estimator->limit_angles[iwlen]) {
   1649         MC_ACCUM(differential_cross_section[iangle]);
   1650         MC_ACCUM(differential_cross_section_cumulative[iangle]);
   1651       }
   1652     }
   1653   }
   1654 
   1655   /* Static scheduling is not necessary to ensure the reproductability;
   1656    * computations are purely analytics */
   1657   #pragma omp parallel for
   1658   for(iwlen=0; iwlen < (int)nwavelengths; ++iwlen) {
   1659     const int ithread = omp_get_thread_num();
   1660     if(estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE) continue;
   1661     /* Do not handle phase function errors */
   1662     compute_scattering_angles_properties
   1663       (dev, &solver_ctxs[ithread], (size_t)iwlen, nangles, estimator);
   1664   }
   1665 
   1666 exit:
   1667   if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy));
   1668   if(ctxs) {
   1669     FOR_EACH(i, 0, dev->nthreads) integrator_context_release(ctxs+i);
   1670     sa_release(ctxs);
   1671   }
   1672   if(solver_ctxs) {
   1673     FOR_EACH(i, 0, dev->nthreads) solver_context_release(solver_ctxs+i);
   1674     sa_release(solver_ctxs);
   1675   }
   1676   if(rngs) {
   1677     FOR_EACH(i, 0, dev->nthreads) if(rngs[i]) SSP(rng_ref_put(rngs[i]));
   1678     sa_release(rngs);
   1679   }
   1680   if(out_estimator) *out_estimator = estimator;
   1681   return (res_T)res;
   1682 error:
   1683   if(estimator) {
   1684     SSCHIFF(estimator_ref_put(estimator));
   1685     estimator = NULL;
   1686   }
   1687   goto exit;
   1688 }
   1689 
   1690 res_T
   1691 sschiff_estimator_ref_get(struct sschiff_estimator* estimator)
   1692 {
   1693   if(!estimator) return RES_BAD_ARG;
   1694   ref_get(&estimator->ref);
   1695   return RES_OK;
   1696 }
   1697 
   1698 res_T
   1699 sschiff_estimator_ref_put(struct sschiff_estimator* estimator)
   1700 {
   1701   if(!estimator) return RES_BAD_ARG;
   1702   ref_put(&estimator->ref, estimator_release);
   1703   return RES_OK;
   1704 }
   1705 
   1706 res_T
   1707 sschiff_estimator_get_wavelengths
   1708   (const struct sschiff_estimator* estimator,
   1709    const double** wavelengths,
   1710    size_t* count)
   1711 {
   1712   if(!estimator) return RES_BAD_ARG;
   1713   if(wavelengths) *wavelengths = estimator->wavelengths;
   1714   if(count) *count = sa_size(estimator->wavelengths);
   1715   return RES_OK;
   1716 }
   1717 
   1718 res_T
   1719 sschiff_estimator_get_scattering_angles
   1720   (const struct sschiff_estimator* estimator,
   1721    const double** angles,
   1722    size_t* count)
   1723 {
   1724   if(!estimator) return RES_BAD_ARG;
   1725   if(angles) *angles = estimator->angles;
   1726   if(count) *count = sa_size(estimator->angles);
   1727   return RES_OK;
   1728 }
   1729 
   1730 res_T
   1731 sschiff_estimator_get_realisations_count
   1732   (const struct sschiff_estimator* estimator, size_t* count)
   1733 {
   1734   if(!estimator || !count) return RES_BAD_ARG;
   1735   *count = estimator->nrealisations;
   1736   return RES_OK;
   1737 }
   1738 
   1739 res_T
   1740 sschiff_estimator_get_cross_section
   1741   (const struct sschiff_estimator* estimator,
   1742    const size_t iwlen,
   1743    struct sschiff_cross_section* cross_section)
   1744 {
   1745   const struct mc_accum* acc;
   1746   size_t N;
   1747 
   1748   if(!estimator || !cross_section || iwlen >= sa_size(estimator->wavelengths))
   1749     return RES_BAD_ARG;
   1750 
   1751   acc = estimator->accums + iwlen;
   1752   N = estimator->nrealisations;
   1753   get_mc_value(&acc->extinction_cross_section, N, &cross_section->extinction);
   1754   get_mc_value(&acc->absorption_cross_section, N, &cross_section->absorption);
   1755   get_mc_value(&acc->scattering_cross_section, N, &cross_section->scattering);
   1756   get_mc_value(&acc->avg_projected_area, N,
   1757     &cross_section->average_projected_area);
   1758   return RES_OK;
   1759 }
   1760 
   1761 res_T
   1762 sschiff_estimator_get_cross_sections
   1763   (const struct sschiff_estimator* estimator,
   1764    struct sschiff_cross_section* cross_sections)
   1765 {
   1766   size_t nwavelengths;
   1767   size_t iwlen;
   1768   size_t N;
   1769   if(!estimator || !cross_sections) return RES_BAD_ARG;
   1770 
   1771   nwavelengths = sa_size(estimator->wavelengths);
   1772   N = estimator->nrealisations;
   1773   FOR_EACH(iwlen, 0, nwavelengths) {
   1774     const struct mc_accum* acc = estimator->accums + iwlen;
   1775     get_mc_value(&acc->extinction_cross_section, N, &cross_sections->extinction);
   1776     get_mc_value(&acc->absorption_cross_section, N, &cross_sections->absorption);
   1777     get_mc_value(&acc->scattering_cross_section, N, &cross_sections->scattering);
   1778     get_mc_value(&acc->avg_projected_area, N,
   1779       &cross_sections->average_projected_area);
   1780   }
   1781   return RES_OK;
   1782 }
   1783 
   1784 res_T
   1785 sschiff_estimator_get_phase_function
   1786   (const struct sschiff_estimator* estimator,
   1787    const size_t iwlen,
   1788    const struct sschiff_state** states)
   1789 {
   1790   if(!estimator || !states || iwlen >= sa_size(estimator->wavelengths))
   1791     return RES_BAD_ARG;
   1792   /* No phase function was computed */
   1793   if(estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE)
   1794     return RES_BAD_OP;
   1795   /* Invalid phase function */
   1796   if(estimator->phase_functions[iwlen].values[0].V < 0)
   1797     return RES_BAD_OP;
   1798   *states = estimator->phase_functions[iwlen].values;
   1799   return RES_OK;
   1800 }
   1801 
   1802 /* Retrieve a pointer onto the estimated phase function cumulative. */
   1803 SSCHIFF_API res_T
   1804 sschiff_estimator_get_phase_function_cumulative
   1805   (const struct sschiff_estimator* estimator,
   1806    const size_t iwlen,
   1807    const struct sschiff_state** states)
   1808 {
   1809   if(!estimator || !states || iwlen >= sa_size(estimator->wavelengths))
   1810     return RES_BAD_ARG;
   1811   /* No phase function cumulative was computed */
   1812   if(estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE)
   1813     return RES_BAD_OP;
   1814   /* Invalid cumulative phase function */
   1815   if(estimator->phase_functions[iwlen].cumulative[0].V < 0)
   1816     return RES_BAD_OP;
   1817   *states = estimator->phase_functions[iwlen].cumulative;
   1818   return RES_OK;
   1819 }
   1820 
   1821 res_T
   1822 sschiff_estimator_inverse_cumulative_phase_function
   1823   (const struct sschiff_estimator* estimator,
   1824    const size_t iwlen,
   1825    double* thetas,
   1826    const size_t nthetas)
   1827 {
   1828   struct sschiff_state* cumul[2];
   1829   double* cumulative_small_angles = NULL;
   1830   double cumulative;
   1831   double step;
   1832   size_t itheta;
   1833   size_t iangle, nsmall_angles;
   1834   res_T res = RES_OK;
   1835 
   1836   if(!estimator
   1837   || nthetas < 2
   1838   || !thetas
   1839   || iwlen >= sa_size(estimator->wavelengths)) {
   1840     res = RES_BAD_ARG;
   1841     goto error;
   1842   }
   1843 
   1844   /* No phase function cumulative is computed => nothing to inverse */
   1845   if(estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE) {
   1846     res = RES_BAD_OP;
   1847     goto error;
   1848   }
   1849 
   1850   /* Allocate the "filtered" phase function cumulative of small angles, i.e.
   1851    * the increasing cumulative defined from the Monte-Carlo estimation. */
   1852   nsmall_angles = estimator->limit_angles[iwlen];
   1853   if(!sa_add(cumulative_small_angles, nsmall_angles)) {
   1854     log_error(estimator->dev,
   1855 "Couldn't allocate the filtered phase function cumulative of small angles.\n");
   1856     res = RES_MEM_ERR;
   1857     goto error;
   1858   }
   1859 
   1860   /* Fill the phase function cumulative for small angles. Ensure that the
   1861    * resulting array is sorted in increasing order, i.e. increasing function */
   1862   cumul[0] = &estimator->phase_functions[iwlen].cumulative[0];
   1863   cumulative_small_angles[0] = cumul[0]->E;
   1864   FOR_EACH(iangle, 1, nsmall_angles) {
   1865     cumul[1] = &estimator->phase_functions[iwlen].cumulative[iangle];
   1866 
   1867     if(cumul[0]->E <= cumul[1]->E) {
   1868       cumulative_small_angles[iangle] = cumul[1]->E;
   1869     } else {
   1870       /* Numerical imprecisions may lead to a cumulative that is not an
   1871        * increasing function. In such case, check that the standard-error
   1872        * ensure at least a threshold between the decreasing entry an the
   1873        * previous one. If not, return an error */
   1874       cumulative = MMIN(cumul[1]->E + cumul[1]->SE, cumul[0]->E);
   1875       if(cumulative < cumul[0]->E) {
   1876         log_error(estimator->dev,
   1877 "The phase function cumulative of small angles is not an increasing function.\n"
   1878 "This may be due to numerical imprecisions.\n");
   1879         res = RES_BAD_OP;
   1880         goto error;
   1881       }
   1882       cumulative_small_angles[iangle] = cumulative;
   1883     }
   1884     cumul[0] = cumul[1];
   1885   }
   1886 
   1887   /* Inverse the phase function cumulative */
   1888   thetas[0] = 0.0;
   1889   thetas[nthetas-1] = PI;
   1890   cumulative = step = 1.0/(double)(nthetas-1);
   1891   FOR_EACH(itheta, 1, nthetas-1) {
   1892     /* TODO since the submitted cumulative are strictly increasing, one can
   1893      * speed up the inversion by using the previous search result to reduce the
   1894      * search domain in the cumulative arrays */
   1895     res = inverse_cumulative_phase_function
   1896       (estimator, cumulative_small_angles, iwlen, cumulative, &thetas[itheta]);
   1897     if(res != RES_OK) goto error;
   1898     cumulative += step;
   1899   }
   1900 
   1901 exit:
   1902   if(cumulative_small_angles) sa_release(cumulative_small_angles);
   1903   return res;
   1904 error:
   1905   goto exit;
   1906 }
   1907 
   1908 res_T
   1909 sschiff_estimator_get_limit_scattering_angle_index
   1910   (const struct sschiff_estimator* estimator,
   1911    const size_t iwlen,
   1912    size_t* iangle)
   1913 {
   1914   size_t limit_angle;
   1915   if(!estimator || iwlen >= sa_size(estimator->wavelengths) || !iangle)
   1916      return RES_BAD_ARG;
   1917   limit_angle = estimator->limit_angles[iwlen];
   1918   if(limit_angle == INVALID_LIMIT_ANGLE)
   1919     return RES_BAD_OP;
   1920   *iangle = limit_angle - 1/*<=>index of the last small angle*/;
   1921   return RES_OK;
   1922 }
   1923 
   1924 res_T
   1925 sschiff_estimator_get_wide_scattering_angle_model_parameter
   1926   (const struct sschiff_estimator* estimator,
   1927    const size_t iwlen,
   1928    double* out_n)
   1929 {
   1930   if(!estimator || iwlen >= sa_size(estimator->wavelengths) || !out_n)
   1931     return RES_BAD_ARG;
   1932   if(estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE)
   1933     return RES_BAD_OP;
   1934   *out_n = estimator->wide_angles_parameter[iwlen];
   1935   return RES_OK;
   1936 }
   1937 
   1938 res_T
   1939 sschiff_estimator_get_differential_cross_section
   1940   (const struct sschiff_estimator* estimator,
   1941    const size_t iwlen,
   1942    const size_t iangle,
   1943    struct sschiff_state* state)
   1944 {
   1945   if(!estimator
   1946   || iwlen >= sa_size(estimator->wavelengths)
   1947   || iangle >= sa_size(estimator->angles)
   1948   || !state)
   1949     return RES_BAD_ARG;
   1950 
   1951   if(estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE)
   1952     return RES_BAD_OP;
   1953 
   1954   get_mc_value
   1955     (&estimator->accums[iwlen].differential_cross_section[iangle],
   1956      estimator->nrealisations,
   1957      state);
   1958   return RES_OK;
   1959 }
   1960 
   1961 res_T
   1962 sschiff_estimator_get_differential_cross_section_cumulative
   1963   (const struct sschiff_estimator* estimator,
   1964    const size_t iwlen,
   1965    const size_t iangle,
   1966    struct sschiff_state* state)
   1967 {
   1968   if(!estimator
   1969   || iwlen >= sa_size(estimator->wavelengths)
   1970   || iangle >= sa_size(estimator->angles)
   1971   || !state)
   1972     return RES_BAD_ARG;
   1973 
   1974   if(estimator->limit_angles[iwlen] == INVALID_LIMIT_ANGLE)
   1975     return RES_BAD_OP;
   1976 
   1977   get_mc_value
   1978     (&estimator->accums[iwlen].differential_cross_section_cumulative[iangle],
   1979      estimator->nrealisations,
   1980      state);
   1981   return RES_OK;
   1982 }
   1983