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