commit 5e365c2c362833832829289c92a23fa25b41deb9
parent f519ed6cab752075a1749e6656a2a75e59743deb
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Wed, 17 Feb 2016 10:02:01 +0100
Big refactor of the estimator code
Add the Monte-Carlo estimation of the diffusion cross section and its
cumulative. Make optical properties constants for a geometry
distribution.
Handle Ray-Tracing self intersection on primitive edges and fix an issue
in the rejection of rays that stopped the integration process.
Diffstat:
5 files changed, 790 insertions(+), 354 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -41,11 +41,13 @@ find_package(RSys 0.3 REQUIRED)
find_package(StarSP 0.3 REQUIRED)
find_package(Star3D 0.3 REQUIRED)
find_package(OpenMP 1.2 REQUIRED)
+find_package(GSL REQUIRED)
include_directories(
${RSys_INCLUDE_DIR}
${StarSP_INCLUDE_DIR}
- ${Star3D_INCLUDE_DIR})
+ ${Star3D_INCLUDE_DIR}
+ ${GSL_INCLUDE_DIRS})
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR})
include(rcmake)
@@ -79,7 +81,7 @@ set_target_properties(sschiff PROPERTIES
VERSION ${VERSION}
SOVERSION ${VERSION_MAJOR})
-target_link_libraries(sschiff RSys Star3D StarSP)
+target_link_libraries(sschiff RSys Star3D StarSP GSL::gsl)
if(CMAKE_COMPILER_IS_GNUCC)
target_link_libraries(sschiff m)
endif()
diff --git a/src/sschiff.h b/src/sschiff.h
@@ -58,15 +58,6 @@ struct s3d_device;
struct s3d_shape;
struct ssp_rng;
-/* Type of estimated data */
-enum sschiff_data {
- SSCHIFF_EXTINCTION_CROSS_SECTION,
- SSCHIFF_ABSORPTION_CROSS_SECTION,
- SSCHIFF_SCATTERING_CROSS_SECTION,
- SSCHIFF_AVERAGE_PROJECTED_AREA,
- SSCHIFF_DATA_COUNT__
-};
-
/* Optical properties of a material handled by the Schiff integrator */
struct sschiff_material_properties {
double medium_refractive_index;
@@ -89,25 +80,30 @@ static const struct sschiff_material SSCHIFF_NULL_MATERIAL = { NULL, NULL };
/* User defined distribution of the geometries. The unit of the geometry is the
* micron, i.e. 1.0f == 1 micron */
struct sschiff_geometry_distribution {
+ struct sschiff_material material; /* Material of the geometry distribution */
+ double caracteristic_length;
res_T (*sample) /* Sample a geometry i.e. a shape and its material */
(struct ssp_rng* rng,
- struct sschiff_material* mtl, /* Sampled material */
struct s3d_shape* shape, /* Sampled shape */
void* context);
void* context;
};
static const struct sschiff_geometry_distribution
-SSCHIFF_NULL_GEOMETRY_DISTRIBUTION = { NULL, NULL };
+SSCHIFF_NULL_GEOMETRY_DISTRIBUTION = { {NULL, NULL}, -1.0, NULL, NULL };
/* State of the Monte Carlo estimation */
-struct sschiff_estimator_state {
- double wavelength; /* Estimated wavelength in micron */
- struct sschiff_estimator_value { /* Values of the estimation */
- double E; /* Expected value */
- double V; /* Variance */
- double SE; /* Standard error */
- } values[SSCHIFF_DATA_COUNT__];
+struct sschiff_state {
+ double E; /* Expected value */
+ double V; /* Variance */
+ double SE; /* Standard error */
+};
+
+struct sschiff_cross_section {
+ struct sschiff_state extinction;
+ struct sschiff_state absorption;
+ struct sschiff_state scattering;
+ struct sschiff_state average_projected_area;
};
/* Type of the function use to distribute the scattering angles in [0, PI].
@@ -155,7 +151,7 @@ sschiff_integrate
const double* wavelengths, /* list of wavelengths to estimate in micron */
const size_t wavelengths_count, /* # wavelengths to estimate */
const sschiff_scattering_angles_distribution_T angles, /* angles distrib */
- const size_t scattering_angles_count, /* # scattering angles. Must be >= 2 */
+ const size_t scattering_angles_count, /* # scattering angles. Must be >= 3 */
const size_t sampled_geometries_count, /* # geometries to sample */
const size_t sampled_directions_count, /* # directions to sample per geometry */
struct sschiff_estimator** estimator);
@@ -182,19 +178,19 @@ sschiff_estimator_get_realisations_count
/* Retrieve the estimation state of a given wavelength */
SSCHIFF_API res_T
-sschiff_estimator_get_wavelength_state
+sschiff_estimator_get_wavelength_cross_section
(const struct sschiff_estimator* estimator,
const double wavelength, /* In micron */
- struct sschiff_estimator_state* state);
+ struct sschiff_cross_section* cross_section);
-/* Retrieve the estimation state of all wavelengths. The length of `states'
- * must be at least equal to the count of integrated wavelengths. One can use
- * the sschiff_estimator_get_wavelengths_count function to retrieve this
- * information. */
+/* Retrieve the estimated cross sections of all wavelengths. The length of
+ * `cross_sections' must be at least equal to the count of integrated
+ * wavelengths. One can use the sschiff_estimator_get_wavelengths_count
+ * function to retrieve this information. */
SSCHIFF_API res_T
-sschiff_estimator_get_states
+sschiff_estimator_get_cross_sections
(const struct sschiff_estimator* estimator,
- struct sschiff_estimator_state states[]);
+ struct sschiff_cross_section cross_sections[]);
END_DECLS
diff --git a/src/sschiff_estimator.c b/src/sschiff_estimator.c
@@ -31,6 +31,7 @@
#include "sschiff.h"
#include "sschiff_device.h"
+#include <rsys/algorithm.h>
#include <rsys/float2.h>
#include <rsys/float3.h>
#include <rsys/image.h>
@@ -45,25 +46,75 @@
#include <math.h>
#include <omp.h>
+#include <gsl/gsl_sf.h>
+
#define MAX_FAILURES 10
/* Accumulator of double precision values */
-struct accum { double weights[SSCHIFF_DATA_COUNT__]; };
+struct accum {
+ double extinction_cross_section;
+ double absorption_cross_section;
+ double scattering_cross_section;
+ double avg_projected_area;
+
+ /* Per scattering angles weights */
+ double* differential_cross_section;
+ double* differential_cross_section_cumulative;
+};
/* Monte carlo data */
struct mc_data { double weight; double sqr_weight; };
-
static const struct mc_data MC_DATA_NULL = { 0.0, 0.0 };
/* Accumulator of Monte Carlo data */
-struct mc_accum { struct mc_data mc_data[SSCHIFF_DATA_COUNT__]; };
+struct mc_accum {
+ struct mc_data extinction_cross_section;
+ struct mc_data absorption_cross_section;
+ struct mc_data scattering_cross_section;
+ struct mc_data avg_projected_area;
+
+ /* Per scattering angles weight */
+ struct mc_data* differential_cross_section;
+ struct mc_data* differential_cross_section_cumulative;
+};
+
+/* The geometry context stores data that are constants for a given geometry
+ * distribution. It stores limit scattering angles as well as geometry material
+ * since the particle of a distribution shared the same optical properties.
+ * Precomputed values relying onto these data are also stored in order to
+ * improve the integration efficiency */
+struct geometry_context {
+ const double* angles; /* Pointer toward the estimated scattering angles */
+
+ /* Per wavelength index of the scattering angle from which the MC estimation
+ * of the phase function is no more valid. Must be < nangles. */
+ size_t* limit_angles;
+
+ double* medium_refractive_indices;
+ struct sschiff_material_properties* properties; /* Material properties */
+};
+
+/* The integrator context stores per thread data */
+struct integrator_context {
+ /* Geometry of the micro particle */
+ struct s3d_shape* shape;
+ struct s3d_scene* scene;
+
+ struct ssp_rng* rng;
+ const struct geometry_context* geom_ctx; /* Pointer onto the constant data */
-static const struct mc_accum MC_ACCUM_NULL =
-{{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}};
+ size_t nwavelengths; /* #wavelengths */
+ size_t nangles; /* #scattering angles */
+
+ /* Per wavelengths data */
+ struct accum* accums; /* Temporary accumulators */
+ struct mc_accum* mc_accums; /* Monte Carlo accumulators */
+};
struct sschiff_estimator {
- double* wavelengths;
- struct mc_accum* accums;
+ double* wavelengths; /* List of wavelengths in vacuum */
+ double* angles; /* List of scattering angles */
+ struct mc_accum* accums; /* Monte Carlo estimation */
struct sschiff_device* dev;
size_t nrealisations;
ref_T ref;
@@ -214,6 +265,38 @@ draw_thumbnail
#undef DEFINITION
}
+static FINLINE int
+hit_on_edge(const struct s3d_hit* hit)
+{
+ const float on_edge_eps = 1.e-4f;
+ float w;
+ ASSERT(hit && !S3D_HIT_NONE(hit));
+ w = 1.f - hit->uv[0] - hit->uv[1];
+ return eq_epsf(hit->uv[0], 0.f, on_edge_eps)
+ || eq_epsf(hit->uv[0], 1.f, on_edge_eps)
+ || eq_epsf(hit->uv[1], 0.f, on_edge_eps)
+ || eq_epsf(hit->uv[1], 1.f, on_edge_eps)
+ || eq_epsf(w, 0.f, on_edge_eps)
+ || eq_epsf(w, 1.f, on_edge_eps);
+}
+
+static FINLINE int
+self_hit
+ (const struct s3d_hit* hit_from,
+ const struct s3d_hit* hit_to,
+ const float epsilon)
+{
+ ASSERT(hit_from && hit_to);
+
+ if(S3D_HIT_NONE(hit_from) || S3D_HIT_NONE(hit_to))
+ return 0;
+ if(S3D_PRIMITIVE_EQ(&hit_from->prim, &hit_to->prim))
+ return 1;
+ if(eq_epsf(hit_to->distance - hit_from->distance, 0.f, epsilon))
+ return hit_on_edge(hit_from) && hit_on_edge(hit_to);
+ return 0;
+}
+
/* Compute the length in micron of the ray part that traverses the scene */
static res_T
compute_path_length
@@ -225,38 +308,38 @@ compute_path_length
double* length)
{
float range[2] = { 0, FLT_MAX };
- struct s3d_primitive prim_from;
- struct s3d_hit hit;
+ struct s3d_hit hit_from, hit;
double len = 0;
float dst;
ASSERT(scn && first_hit && ray_org && ray_dir && !S3D_HIT_NONE(first_hit));
dst = range[0] = first_hit->distance;
- prim_from = first_hit->prim;
+ hit_from = *first_hit;
+
do {
do {
range[0] = nextafterf(range[0], range[1]);
S3D(scene_trace_ray(scn, ray_org, ray_dir, range, &hit));
- } while(S3D_PRIMITIVE_EQ(&hit.prim, &prim_from) /* Self-intersection */
- || hit.distance < range[0]); /* Precision issue */
+ } while(hit.distance < range[0] || self_hit(&hit_from, &hit, 1.e-6f));
if(S3D_HIT_NONE(&hit)) {
- log_error(dev, "Error in computing the radiative path length.\n");
- return RES_BAD_ARG;
+ log_error(dev,
+"Error in computing the radiative path length. This may be due to numerical\n"
+"imprecisions or to a geometry that does not define a closed volume.\n");
+ return RES_BAD_OP;
}
len += hit.distance - dst;
dst = range[0] = hit.distance;
- prim_from = hit.prim;
+ hit_from = hit;
do {
range[0] = nextafterf(range[0], range[1]);
S3D(scene_trace_ray(scn, ray_org, ray_dir, range, &hit));
- } while(S3D_PRIMITIVE_EQ(&hit.prim, &prim_from) /* Self-intersection */
- || hit.distance < range[0]); /* Precision issue */
+ } while(hit.distance < range[0] || self_hit(&hit_from, &hit, 1.e-6f));
range[0] = hit.distance;
- prim_from = hit.prim;
+ hit_from = hit;
} while(!S3D_HIT_NONE(&hit));
@@ -264,63 +347,211 @@ compute_path_length
return RES_OK;
}
-static FINLINE void
+static INLINE void
accum_cross_section
- (const struct sschiff_material_properties* props,
- const double* wavelengths,
- const size_t nwavelengths,
- const double rcp_pdf,
- const double path_length,
- struct accum* accums)
+ (struct integrator_context* ctx,
+ const double plane_area,
+ const double path_length)
{
size_t iwlen;
- ASSERT(props && wavelengths && nwavelengths && rcp_pdf>0 && path_length>0);
+ ASSERT(ctx && plane_area > 0 && path_length > 0);
- FOR_EACH(iwlen, 0, nwavelengths) {
+ FOR_EACH(iwlen, 0, ctx->nwavelengths) {
double n_r, k_r;
- double n_e, k_e, lambda_e;
+ double k_e;
double w_extinction, w_absorption, w_scattering;
- n_e = props[iwlen].medium_refractive_index;
- n_r = props[iwlen].relative_real_refractive_index;
- k_r = props[iwlen].relative_imaginary_refractive_index;
-
- lambda_e = wavelengths[iwlen] / n_e;
- k_e = 2.0 * PI / lambda_e;
+ k_e = ctx->geom_ctx->medium_refractive_indices[iwlen];
+ n_r = ctx->geom_ctx->properties[iwlen].relative_real_refractive_index;
+ k_r = ctx->geom_ctx->properties[iwlen].relative_imaginary_refractive_index;
/* Extinction cross section */
- w_extinction = 2.0 * rcp_pdf *
+ w_extinction = 2.0 * plane_area *
(1.0 - exp(-k_e*k_r*path_length) * cos(k_e*(n_r - 1.0)*path_length));
- accums[iwlen].weights[SSCHIFF_EXTINCTION_CROSS_SECTION] += w_extinction;
+ ctx->accums[iwlen].extinction_cross_section += w_extinction;
/* Absorption cross section */
- w_absorption = rcp_pdf * (1.0 - exp(-2.0*k_e*k_r*path_length));
- accums[iwlen].weights[SSCHIFF_ABSORPTION_CROSS_SECTION] += w_absorption;
+ w_absorption = plane_area * (1.0 - exp(-2.0*k_e*k_r*path_length));
+ ctx->accums[iwlen].absorption_cross_section += w_absorption;
/* Scattering cross section */
w_scattering = w_extinction - w_absorption;
- accums[iwlen].weights[SSCHIFF_SCATTERING_CROSS_SECTION] += w_scattering;
+ ctx->accums[iwlen].scattering_cross_section += w_scattering;
/* Average projected area */
- accums[iwlen].weights[SSCHIFF_AVERAGE_PROJECTED_AREA] += rcp_pdf;
+ ctx->accums[iwlen].avg_projected_area += plane_area;
}
}
+static INLINE void
+accum_differential_cross_section
+ (struct integrator_context* ctx,
+ const double plane_area,
+ const double path_length[2],
+ float ray_org[2][3])
+{
+ double delta_r;
+ size_t iwlen;
+ float vec[3];
+
+ /* Compute the length between the 2 ray starting points in footprint space */
+ delta_r = f3_len(f3_sub(vec, ray_org[0], ray_org[1]));
+
+ FOR_EACH(iwlen, 0, ctx->nwavelengths) {
+ double k_e_delta_r;
+ double n_r, k_r;
+ double k_e;
+ double beta_r[2];
+ double beta_i[2];
+ double tmp;
+ size_t iangle;
+
+ /* Fetch optical properties */
+ k_e = ctx->geom_ctx->medium_refractive_indices[iwlen];
+ n_r = ctx->geom_ctx->properties[iwlen].relative_real_refractive_index;
+ k_r = ctx->geom_ctx->properties[iwlen].relative_imaginary_refractive_index;
+
+ /* Precompute some values. TODO can be stored on the geometry context at
+ * its initialisation. This should improve performances*/
+ k_e_delta_r = k_e * delta_r;
+ beta_r[0] = k_e * (n_r - 1) * path_length[0];
+ beta_r[1] = k_e * (n_r - 1) * path_length[1];
+ beta_i[0] = k_e * k_r * path_length[0];
+ beta_i[1] = k_e * k_r * path_length[1];
+ tmp = (k_e * plane_area) / (2*PI);
+ tmp *= tmp;
+ tmp *= 1
+ + exp(-(beta_i[0] + beta_i[1])) * cos(beta_r[0] - beta_r[1])
+ - exp(-beta_i[0]) * cos(beta_r[0])
+ - exp(-beta_i[1]) * cos(beta_r[1]);
+
+ /* Compute and accumulate the MC weights of the differential cross section
+ * and its cumulative. Note that ctx->limit_angles store the index of the
+ * first scattering angle whos phase function is not estimated by
+ * Monte-Carlo */
+ FOR_EACH(iangle, 0, ctx->geom_ctx->limit_angles[iwlen]) {
+ double bessel_j0, bessel_j1;
+ double weight;
+ double k_e_angle_delta_r;
+
+ k_e_angle_delta_r = ctx->geom_ctx->angles[iangle] * k_e_delta_r;
+
+ bessel_j0 = gsl_sf_bessel_j0(k_e_angle_delta_r);
+ weight = bessel_j0 * tmp;
+ ctx->accums[iwlen].differential_cross_section[iangle] += weight;
+
+ bessel_j1 = gsl_sf_bessel_j1(k_e_angle_delta_r);
+ weight = 2*PI * ctx->geom_ctx->angles[iangle] * bessel_j1 / k_e_delta_r * tmp;
+ ctx->accums[iwlen].differential_cross_section_cumulative[iangle] += weight;
+ }
+ }
+}
+
+#if 0
+struct genevieve_arg {
+ double limit_angle;
+ double scattering_cross_section;
+ double limit_differential_cross_section;
+ double limit_differential_cross_section_cumulative;
+};
+
+int
+genevieve(const gsl_vector* x, void* params, gsl_vector* f)
+{
+ struct genevieve_arg* arg = params;
+ const double x = gsl_vector_get(x, 0);
+ double f0;
+ double square_cos_limit_angle;
+ double square_half_sin_limit_angle;
+
+ if(x==2 || x==4 || x==6)
+ return GSL_EDOM;
+
+ square_cos_limit_angle = cos(limit_angle);
+ square_cos_limit_angle *= square_cos_limit_angle;
+
+ square_half_sin_limit_angle = sin(limit_angle) / 2;
+ square_half_sin_limit_angle *= square_half_sin_limit_angle;
+
+ f0 = arg->limit_differential_cross_section_cumulative
+ - arg->scattering_cross_section
+ + (4*PI * arg->limit_differential_cross_section) / (1+square_cos_limit_angle)
+ * (square_half_sin_limit_angle * ((x*x - 6*x + 8) * cos(2*arg->limit_angle) + 3*x*x - 8 * (x - 2) * cos(arg->limit_angle) - 26*x + 72) - pow(sin(arg->limit_angle/2.0), x) * (4*x*x - 24*x + 40))
+ / ((x-2)*(x-4)*(x-6));
+
+ return GSL_SUCCESS;
+}
+
+static void
+phase_function_post_process()
+{
+ const gsl_multiroot_fsolver* solver = NULL;
+ const gsl_multiroot_function func;
+ double n;
+ double r;
+ struct genevieve_param* arg;
+ struct gsl_vector* x = NULL;
+ struct gsl_vector* root;
+ res_T res = RES_OK;
+
+ solver = gsl_multiroot_fsolver_alloc(gsl_multiroot_fsolver_dnewton, 1);
+ if(!solver) {
+ log_error(dev,
+ "Not enough memory. Couldn't allocate the GSL Newton-Raphson solver.\n");
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ x = gsl_vector_alloc(1);
+ if(!x) {
+ log_error(dev,
+ "Not enough memory. Couldn't allocat the initial GSL vector.\n");
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ /* TODO fill arg */
+ func.f = genevieve;
+ func.n = 1;
+ func.params = &arg;
+
+ gsl_vector_set(x, 0, 3.0);
+ gsl_multiroot_fsolver_set(solver, f, x);
+
+ FOR_EACH(iwlen, 0, nwavelengths) {
+ int status;
+ int iter;
+ do {
+ status = gsl_multiroot_fsolver_iterate(solver);
+ if(states != GSL_SUCCESS) break; /* ERROR */
+
+ status = gsl_multiroot_test_residual(solver->f, 1.e-3);
+ } while(status == GSL_CONTINUE && iter < 1000);
+ }
+
+ root = gsl_multiroot_fsolver_root(solver->x);
+ n = gsl_vector_get(root, 0);
+
+ r = 2 * limit_differential_cross_section * pow(sin(limit_angle/2), n)
+ / (1 + pow(cos(limit_angle), 2));
+
+exit:
+ if(solver) gsl_multiroot_fsolver_free(solver);
+ if(x) gsl_vector_free(x);
+ return res;
+error:
+ goto exit;
+}
+#endif
+
static res_T
accum_monte_carlo_weight
(struct sschiff_device* dev,
- struct s3d_scene* scn,
- struct ssp_rng* rng,
- const struct sschiff_material_properties* props,
- const double* wavelengths, /* In micron */
- const size_t nwavelengths,
- const double* angles, /* Scattering angles */
- const size_t nangles,
+ struct integrator_context* ctx,
const float basis[9], /* World space basis of the RT volume */
const float pos[3], /* World space centroid of the RT volume */
const float lower[3], /* Lower boundary of the RT volume */
- const float upper[3], /* Upper boundary of the RT volume */
- struct accum* accums)
+ const float upper[3]) /* Upper boundary of the RT volume */
{
struct s3d_hit hit;
double sample[2];
@@ -328,16 +559,15 @@ accum_monte_carlo_weight
size_t nfailures = 0;
float axis_x[3], axis_y[3], axis_z[3];
float plane_size[2];
- float ray_org[3];
+ float ray_org[2][3];
float org[3];
float x[3], y[3];
- float rcp_pdf;
+ float plane_area;
res_T res = RES_OK;
- ASSERT(scn && rng && props && wavelengths && nwavelengths && angles && nangles);
- ASSERT(accums && basis && pos && lower && upper);
+ ASSERT(dev && ctx && basis && pos && lower && upper);
f2_sub(plane_size, upper, lower); /* In micron */
- rcp_pdf = plane_size[0] * plane_size[1];
+ plane_area = plane_size[0] * plane_size[1];
/* Define the projection axis */
f3_mulf(axis_x, basis + 0, plane_size[0] * 0.5f);
@@ -348,36 +578,57 @@ accum_monte_carlo_weight
f3_add(org, pos, f3_mulf(org, axis_z, lower[2]));
do {
- double path_length;
+ double path_length[2];
/* Uniformly sample a position onto the projection plane and use it as the
- * origin of the ray to trace */
- sample[0] = ssp_rng_uniform_double(rng, -1.0, 1.0);
- sample[1] = ssp_rng_uniform_double(rng, -1.0, 1.0);
+ * origin of the 1st ray to trace */
+ sample[0] = ssp_rng_uniform_double(ctx->rng, -1.0, 1.0);
+ sample[1] = ssp_rng_uniform_double(ctx->rng, -1.0, 1.0);
f3_mulf(x, axis_x, (float)sample[0]);
f3_mulf(y, axis_y, (float)sample[1]);
- f3_add(ray_org, f3_add(ray_org, x, y), org);
+ f3_add(ray_org[0], f3_add(ray_org[0], x, y), org);
- S3D(scene_trace_ray(scn, ray_org, axis_z, range, &hit));
-
- if(S3D_HIT_NONE(&hit)) /* NULL cross section and phase function weight */
+ S3D(scene_trace_ray(ctx->scene, ray_org[0], axis_z, range, &hit));
+ /* NULL cross section and differential cross section weight */
+ if(S3D_HIT_NONE(&hit))
break;
-
- res = compute_path_length(dev, scn, &hit, ray_org, axis_z, &path_length);
+ res = compute_path_length
+ (dev, ctx->scene, &hit, ray_org[0], axis_z, &path_length[0]);
if(res != RES_OK) { /* Handle numerical/geometry issues */
++nfailures;
continue;
}
- accum_cross_section
- (props, wavelengths, nwavelengths, rcp_pdf, path_length, accums);
+ /* Compute and accumulate the cross section weight */
+ accum_cross_section(ctx, plane_area, path_length[0]);
- /* TODO phase function integration */
- (void)angles, (void)nangles;
+ /* Uniformly sample a position onto the projection plane and use it as the
+ * origin of the 2nd ray to trace */
+ sample[0] = ssp_rng_uniform_double(ctx->rng, -1.0, 1.0);
+ sample[1] = ssp_rng_uniform_double(ctx->rng, -1.0, 1.0);
+ f3_mulf(x, axis_x, (float)sample[0]);
+ f3_mulf(y, axis_y, (float)sample[1]);
+ f3_add(ray_org[1], f3_add(ray_org[1], x, y), org);
+
+ S3D(scene_trace_ray(ctx->scene, ray_org[1], axis_z, range, &hit));
+ if(S3D_HIT_NONE(&hit)) /* NULL differential cross section weight */
+ break;
+
+ res = compute_path_length
+ (dev, ctx->scene, &hit, ray_org[1], axis_z, &path_length[1]);
+ if(res != RES_OK) { /* Handle numerical/geometry issues */
+ ++nfailures;
+ continue;
+ }
+ /* Compute and accumulate the per scattering angle differential cross
+ * section weight */
+ accum_differential_cross_section(ctx, plane_area, path_length, ray_org);
} while(res != RES_OK && nfailures < MAX_FAILURES);
- if(res != RES_OK) {
+ if(nfailures < MAX_FAILURES) {
+ res = RES_OK;
+ } else {
log_error(dev,
"Too many failures in computing the radiative path length. The sampled geometry\n"
"might not defined a closed volume.\n");
@@ -388,17 +639,9 @@ accum_monte_carlo_weight
static res_T
radiative_properties
(struct sschiff_device* dev,
- struct ssp_rng* rng,
const int istep,
- const double* wavelengths, /* Sorted in ascending order */
- const size_t nwavelengths,
- const double* angles, /* Sorted in ascending order in [0, PI] */
- const size_t nangles,
const size_t ndirs,
- struct s3d_scene* scene,
- const struct sschiff_material_properties* props,
- struct mc_accum* mc_accums,
- struct accum* accums)
+ struct integrator_context* ctx)
{
float lower[3], upper[3];
float aabb_pt[8][3];
@@ -407,11 +650,10 @@ radiative_properties
size_t iwlen;
int i;
res_T res = RES_OK;
- ASSERT(dev && rng && wavelengths && nwavelengths && ndirs && props);
- ASSERT(mc_accums && accums);
+ ASSERT(dev && ndirs && ctx);
(void)istep;
- S3D(scene_get_aabb(scene, lower, upper));
+ S3D(scene_get_aabb(ctx->scene, lower, upper));
/* AABB vertex layout
* 6-------7
@@ -431,14 +673,27 @@ radiative_properties
f3_mulf(centroid, f3_add(centroid, lower, upper), 0.5f);
- memset(accums, 0, sizeof(struct accum)*nwavelengths);
+ /* Clear direction accumulators */
+ FOR_EACH(iwlen, 0, ctx->nwavelengths) {
+ ctx->accums[iwlen].extinction_cross_section = 0;
+ ctx->accums[iwlen].absorption_cross_section = 0;
+ ctx->accums[iwlen].scattering_cross_section = 0;
+ ctx->accums[iwlen].avg_projected_area = 0;
+
+ /* Clean up to "limit_angle" since great angles are analytically computed */
+ memset(ctx->accums[iwlen].differential_cross_section, 0,
+ sizeof(double)*ctx->geom_ctx->limit_angles[iwlen]);
+ memset(ctx->accums[iwlen].differential_cross_section_cumulative, 0,
+ sizeof(double)*ctx->geom_ctx->limit_angles[iwlen]);
+ }
+
FOR_EACH(idir, 0, ndirs) {
float dir[4];
float basis[9];
float transform[12];
float pt[8][3];
- ssp_ran_sphere_uniform(rng, dir);
+ ssp_ran_sphere_uniform(ctx->rng, dir);
/* Build the transformation matrix from world to footprint space. Use the
* AABB centroid as the origin of the footprint space. */
@@ -457,8 +712,7 @@ radiative_properties
f3_max(upper, upper, pt[i]);
}
- res = accum_monte_carlo_weight(dev, scene, rng, props, wavelengths,
- nwavelengths, angles, nangles, basis, centroid, lower, upper, accums);
+ res = accum_monte_carlo_weight(dev, ctx, basis, centroid, lower, upper);
if(res != RES_OK) goto error;
#if 0
@@ -470,18 +724,42 @@ radiative_properties
}
#endif
}
- FOR_EACH(iwlen, 0, nwavelengths) {
- int iweight;
- FOR_EACH(iweight, 0, SSCHIFF_DATA_COUNT__) {
- const double w = accums[iwlen].weights[iweight] / (double)ndirs;
- mc_accums[iwlen].mc_data[iweight].weight += w;
- mc_accums[iwlen].mc_data[iweight].sqr_weight += w * w;
+ /* Compute the Monte Carlo weight of the temporary accumulator */
+ FOR_EACH(iwlen, 0, ctx->nwavelengths) {
+ size_t iangle;
+
+ #define MC_ACCUM(Data) { \
+ const double w = ctx->accums[iwlen].Data / (double)ndirs; \
+ ctx->mc_accums[iwlen].Data.weight += w; \
+ ctx->mc_accums[iwlen].Data.sqr_weight += w*w; \
+ } (void)0
+
+ MC_ACCUM(extinction_cross_section);
+ MC_ACCUM(absorption_cross_section);
+ MC_ACCUM(scattering_cross_section);
+ MC_ACCUM(avg_projected_area);
+
+ /* Accum up to "limit angle" since great angles are analitically computed */
+ FOR_EACH(iangle, 0, ctx->geom_ctx->limit_angles[iwlen]) {
+ MC_ACCUM(differential_cross_section[iangle]);
+ MC_ACCUM(differential_cross_section_cumulative[iangle]);
}
+
+ #undef MC_ACCUM
}
exit:
return res;
error:
- memset(mc_accums, 0, sizeof(struct mc_accum)*nwavelengths);
+ FOR_EACH(iwlen, 0, ctx->nwavelengths) {
+ ctx->mc_accums[iwlen].extinction_cross_section = MC_DATA_NULL;
+ ctx->mc_accums[iwlen].absorption_cross_section = MC_DATA_NULL;
+ ctx->mc_accums[iwlen].scattering_cross_section = MC_DATA_NULL;
+ ctx->mc_accums[iwlen].avg_projected_area = MC_DATA_NULL;
+ memset(ctx->mc_accums[iwlen].differential_cross_section, 0,
+ sizeof(double)*ctx->nangles);
+ memset(ctx->mc_accums[iwlen].differential_cross_section_cumulative, 0,
+ sizeof(double)*ctx->nangles);
+ }
goto exit;
}
@@ -489,7 +767,7 @@ static void
get_mc_value
(const struct mc_data* data,
const size_t nrealisations,
- struct sschiff_estimator_value* value)
+ struct sschiff_state* value)
{
ASSERT(data && value);
value->E = data->weight / (double)nrealisations;
@@ -502,7 +780,208 @@ static char
check_distribution(struct sschiff_geometry_distribution* distrib)
{
ASSERT(distrib);
- return distrib->sample != NULL;
+ return distrib->sample != NULL && distrib->caracteristic_length > 0;
+}
+
+static void
+geometry_context_release(struct geometry_context* ctx)
+{
+ ASSERT(ctx);
+ if(ctx->limit_angles) sa_release(ctx->limit_angles);
+ if(ctx->medium_refractive_indices) sa_release(ctx->medium_refractive_indices);
+ if(ctx->properties) sa_release(ctx->properties);
+}
+
+static res_T
+geometry_context_init
+ (struct sschiff_device* dev,
+ struct sschiff_geometry_distribution* distrib,
+ const double* wavelengths, /* In the vacuum. Sorted in ascending order */
+ const size_t nwavelengths,
+ const double* angles, /* In radian. Sorted in ascending order in [0, PI] */
+ const size_t nangles,
+ struct geometry_context* ctx)
+{
+ double rcp_PI_Lc;
+ size_t iwlen;
+ res_T res = RES_OK;
+ ASSERT(dev && distrib && wavelengths && nwavelengths && angles && nangles && ctx);
+ memset(ctx, 0, sizeof(struct geometry_context));
+
+ /* Precomputed valut */
+ rcp_PI_Lc = 1.0 / (PI*distrib->caracteristic_length);
+
+ if(!sa_add(ctx->limit_angles, nwavelengths)) {
+ log_error(dev,
+"Couldn't allocate the per wavelength indices of the limit scattering angle.\n");
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ if(!sa_add(ctx->medium_refractive_indices, nwavelengths)) {
+ log_error(dev,
+"Couldn't allocate the per wavelength refractive indices of the medium.\n");
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ if(!sa_add(ctx->properties, nwavelengths)) {
+ log_error(dev, "Couldn't allocate the per wavelength optical properties.\n");
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ FOR_EACH(iwlen, 0, nwavelengths) {
+ double lambda_e;
+ double theta_l;
+ double* angle;
+
+ /* Fetch the particle optical properties */
+ distrib->material.get_property
+ (distrib->material.material, wavelengths[iwlen], ctx->properties + iwlen);
+
+ /* Precompute the refractive index in the medium */
+ lambda_e = wavelengths[iwlen] / ctx->properties[iwlen].medium_refractive_index;
+ ctx->medium_refractive_indices[iwlen] = 2.0*PI / lambda_e;
+
+ /* Search the limit angle */
+ theta_l = sqrt(lambda_e * rcp_PI_Lc);
+ angle = search_lower_bound
+ (&theta_l, angles, nangles, sizeof(double), cmp_double);
+ if(!angle || eq_eps(*angle, PI, 1.e-6)) {
+ log_error(dev, "Invalid limit angle \"%g\".\n", *angle);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* Define the index of the angle that *follows* the limit angle */
+ ctx->limit_angles[iwlen] = (size_t)(angle - angles) + 1;
+ ASSERT(ctx->limit_angles[iwlen] < nangles);
+ }
+
+ ctx->angles = angles;
+
+exit:
+ return res;
+error:
+ geometry_context_release(ctx);
+ goto exit;
+}
+
+static void
+integrator_context_release(struct integrator_context* ctx)
+{
+ size_t iwlen;
+ ASSERT(ctx);
+
+ if(ctx->rng) SSP(rng_ref_put(ctx->rng));
+ if(ctx->shape) S3D(shape_ref_put(ctx->shape));
+ if(ctx->scene) S3D(scene_ref_put(ctx->scene));
+
+ #define CTXFREE(Data) if(Data) sa_release(Data)
+ if(ctx->accums) {
+ FOR_EACH(iwlen, 0, ctx->nwavelengths) {
+ CTXFREE(ctx->accums[iwlen].differential_cross_section);
+ CTXFREE(ctx->accums[iwlen].differential_cross_section_cumulative);
+ }
+ sa_release(ctx->accums);
+ }
+ if(ctx->mc_accums) {
+ FOR_EACH(iwlen, 0, ctx->nwavelengths) {
+ CTXFREE(ctx->mc_accums[iwlen].differential_cross_section);
+ CTXFREE(ctx->mc_accums[iwlen].differential_cross_section_cumulative);
+ }
+ sa_release(ctx->mc_accums);
+ }
+ #undef CTXFREE
+}
+
+static res_T
+integrator_context_init
+ (struct integrator_context* ctx,
+ struct s3d_device* s3d,
+ struct ssp_rng* rng,
+ const struct geometry_context* geom_ctx,
+ const size_t nwavelengths,
+ const size_t nangles)
+{
+ size_t iwlen;
+ res_T res = RES_OK;
+ ASSERT(ctx && s3d && rng && geom_ctx && nwavelengths && nangles >= 3);
+
+ memset(ctx, 0, sizeof(struct integrator_context));
+
+ if(RES_OK!=(res = s3d_shape_create_mesh(s3d, &ctx->shape))) goto error;
+ if(RES_OK!=(res = s3d_scene_create(s3d, &ctx->scene))) goto error;
+ if(RES_OK!=(res = s3d_scene_attach_shape(ctx->scene, ctx->shape))) goto error;
+
+ #define CTXALLOC(Data, N) { \
+ if(!sa_add((Data), (N))) { \
+ res = RES_MEM_ERR; \
+ goto error; \
+ } \
+ memset((Data), 0, sizeof((Data)[0])*(N)); \
+ } (void)0
+ CTXALLOC(ctx->accums, nwavelengths);
+ FOR_EACH(iwlen, 0, nwavelengths) {
+ CTXALLOC(ctx->accums[iwlen].differential_cross_section, nangles);
+ CTXALLOC(ctx->accums[iwlen].differential_cross_section_cumulative, nangles);
+ }
+ CTXALLOC(ctx->mc_accums, nwavelengths);
+ FOR_EACH(iwlen, 0, nwavelengths) {
+ CTXALLOC(ctx->mc_accums[iwlen].differential_cross_section, nangles);
+ CTXALLOC(ctx->mc_accums[iwlen].differential_cross_section_cumulative, nangles);
+ }
+ #undef CTXALLOC
+
+ SSP(rng_ref_get(rng));
+ ctx->rng = rng;
+ ctx->geom_ctx = geom_ctx;
+ ctx->nwavelengths = nwavelengths;
+ ctx->nangles = nangles;
+
+exit:
+ return res;
+error:
+ integrator_context_release(ctx);
+ goto exit;
+}
+
+static res_T
+begin_realisation
+ (struct sschiff_device* dev,
+ struct sschiff_geometry_distribution* distrib,
+ struct integrator_context* ctx)
+{
+ res_T res = RES_OK;
+ ASSERT(dev && distrib && ctx);
+
+ /* Sample a particle */
+ res = distrib->sample(ctx->rng, ctx->shape, distrib->context);
+ if(res != RES_OK) {
+ log_error(dev, "Error in sampling a particle.\n");
+ goto error;
+ }
+
+ /* Build the Star-3D representation of the sampled shape */
+ S3D(scene_begin_session(ctx->scene, S3D_TRACE));
+
+exit:
+ return res;
+error:
+ {
+ /* Disable active session if necessary */
+ int mask;
+ if(S3D(scene_get_session_mask(ctx->scene, &mask)), mask) {
+ S3D(scene_end_session(ctx->scene));
+ }
+ }
+ goto exit;
+}
+
+static FINLINE void
+end_realisation(struct integrator_context* ctx)
+{
+ ASSERT(ctx);
+ S3D(scene_end_session(ctx->scene));
}
static void
@@ -514,7 +993,18 @@ estimator_release(ref_T* ref)
estimator = CONTAINER_OF(ref, struct sschiff_estimator, ref);
dev = estimator->dev;
if(estimator->wavelengths) sa_release(estimator->wavelengths);
- if(estimator->accums) sa_release(estimator->accums);
+ if(estimator->angles) sa_release(estimator->angles);
+ if(estimator->accums) {
+ const size_t nwavelengths = sa_size(estimator->accums);
+ size_t i;
+ FOR_EACH(i, 0, nwavelengths) {
+ if(estimator->accums[i].differential_cross_section)
+ sa_release(estimator->accums[i].differential_cross_section);
+ if(estimator->accums[i].differential_cross_section_cumulative)
+ sa_release(estimator->accums[i].differential_cross_section_cumulative);
+ }
+ sa_release(estimator->accums);
+ }
MEM_RM(dev->allocator, estimator);
SSCHIFF(device_ref_put(dev));
}
@@ -522,17 +1012,22 @@ estimator_release(ref_T* ref)
static res_T
estimator_create
(struct sschiff_device* dev,
+ const double* wavelengths,
const size_t nwavelengths,
+ sschiff_scattering_angles_distribution_T angles_distrib,
+ const size_t nangles,
struct sschiff_estimator** out_estimator)
{
struct sschiff_estimator* estimator = NULL;
+ size_t i;
res_T res = RES_OK;
- ASSERT(dev && out_estimator);
+ ASSERT(dev && wavelengths && nwavelengths && angles_distrib && nangles);
+ ASSERT(out_estimator);
estimator = MEM_CALLOC(dev->allocator, 1, sizeof(struct sschiff_estimator));
if(!estimator) {
log_error
- (dev, "Not enough memory: couldn't allocate the Star-Schiff estimator.\n");
+ (dev, "Couldn't allocate the Star-Schiff estimator.\n");
res = RES_MEM_ERR;
goto error;
}
@@ -540,19 +1035,63 @@ estimator_create
SSCHIFF(device_ref_get(dev));
estimator->dev = dev;
+ /* Setup and sort the wavelengths */
if(!sa_add(estimator->wavelengths, nwavelengths)) {
log_error(dev,
- "Not enough memory: couldn't allocate the list of estimated wavelengths.\n");
+ "Couldn't allocate the list of estimated wavelengths.\n");
res = RES_MEM_ERR;
goto error;
}
- if(!sa_add(estimator->accums, nwavelengths)) {
+ FOR_EACH(i, 0, nwavelengths) estimator->wavelengths[i] = wavelengths[i];
+ qsort(estimator->wavelengths, nwavelengths, sizeof(double), cmp_double);
+
+ /* Generate the scattering angles */
+ if(!sa_add(estimator->angles, nangles)) {
log_error(dev,
- "Not enough memory: couldn't allocate the Monte Carlo accumulator.\n");
+ "Couldn't allocate the list of scattering angles.\n");
res = RES_MEM_ERR;
goto error;
}
+ angles_distrib(estimator->angles, nangles);
+ if(estimator->angles[0] != 0.f
+ || !eq_eps(estimator->angles[nangles-1], PI, 1.e-6)) {
+ log_error(dev, "Invalid scattering angle distribution.\n");
+ log_error(dev, "The first and last angles must be 0 and PI, respectively.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ FOR_EACH(i, 1, nangles) {
+ if(estimator->angles[i] <= estimator->angles[i-1]) {
+ log_error(dev, "Invalid scattering angle distribution.\n");
+ log_error(dev, "Angles must be sorted in ascending order in [0, PI]\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ }
+ /* Allocate the estimator accumulators */
+ if(!sa_add(estimator->accums, nwavelengths)) {
+ log_error(dev,
+ "Couldn't allocate the Monte Carlo accumulator of the estimator.\n");
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memset(estimator->accums, 0, sizeof(estimator->accums[0])*nwavelengths);
+ FOR_EACH(i, 0, nwavelengths) {
+ /* FIXME rename the following estimation */
+ if(!sa_add(estimator->accums[i].differential_cross_section, nangles)) {
+ log_error(dev,
+ "Couldn't allocate the differential cross sections to estimate.\n");
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ if(!sa_add(estimator->accums[i].differential_cross_section_cumulative, nangles)) {
+ log_error(dev,
+"Couldn't allocate the cumulative differential cross sections to estimate.\n");
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ }
exit:
*out_estimator = estimator;
return res;
@@ -580,30 +1119,34 @@ sschiff_integrate
const size_t ndirs,
struct sschiff_estimator** out_estimator)
{
- struct sschiff_material_properties** mtls = NULL;
- struct accum** accums = NULL;
- struct mc_accum** mc_accums = NULL;
+ struct geometry_context geom_ctx;
+ struct integrator_context* ctxs = NULL;
struct sschiff_estimator* estimator = NULL;
- struct s3d_shape** shapes = NULL;
- struct s3d_scene** scenes = NULL;
struct ssp_rng** rngs = NULL;
struct ssp_rng_proxy* rng_proxy = NULL;
- double* angles = NULL;
size_t i;
int igeom;
ATOMIC res = (ATOMIC)RES_OK;
- (void)nangles;
+ memset(&geom_ctx, 0, sizeof(geom_ctx));
if(!dev || !rng || !distrib || !wavelengths || !nwavelengths
- || !angles_distrib || nangles < 2 || !ngeoms || !ndirs || !out_estimator
+ || !angles_distrib || nangles < 3 || !ngeoms || !ndirs || !out_estimator
|| !check_distribution(distrib)) {
return RES_BAD_ARG;
}
- res = estimator_create(dev, nwavelengths, &estimator);
+ /* Create the Schiff estimator */
+ res = estimator_create
+ (dev, wavelengths, nwavelengths, angles_distrib, nangles, &estimator);
if(res != RES_OK) goto error;
estimator->nrealisations = ngeoms;
+ /* Initialise the geometry context */
+ res = geometry_context_init(dev, distrib, estimator->wavelengths,
+ nwavelengths, estimator->angles, nangles, &geom_ctx);
+ if(res != RES_OK) goto error;
+
+ /* Create a RNG proxy from the submitted RNG state */
res = ssp_rng_proxy_create_from_rng
(dev->allocator, rng, dev->nthreads, &rng_proxy);
if(res != RES_OK) {
@@ -611,152 +1154,58 @@ sschiff_integrate
goto error;
}
- /* Setup the scattering angles */
- angles = sa_add(angles, nangles);
- if(!angles) {
- log_error(dev,
- "Not enough memory: couldn't allocate the list of scattering angles.\n");
- res = RES_MEM_ERR;
- goto error;
- }
- angles_distrib(angles, nangles);
- if(angles[0] != 0.f && !eq_eps(angles[1], PI, 1.e-6)) {
- log_error(dev,
-"Invalid scattering angles distribution. The first and the last angles must be\n"
-"0 and PI, respectively\n");
- res = RES_BAD_ARG;
- goto error;
- }
-
- /* Create the containers of per thread data structures */
- if(!sa_add(mtls, dev->nthreads)) {
- log_error(dev,
- "No enough memory: couldn't allocate the list of optical properties.\n");
- res = RES_MEM_ERR;
- goto error;
- }
- if(!sa_add(accums, dev->nthreads)) {
- log_error(dev,
- "No enough memory: couldn't allocate the list of temporary accumulators.\n");
- res = RES_MEM_ERR;
- goto error;
- }
- if(!sa_add(mc_accums, dev->nthreads)) {
- log_error(dev,
- "Not enough memory: couldn't allocate the list Monte Carlo accumulators.\n");
+ /* Create per thread data structures */
+ if(!sa_add(ctxs, dev->nthreads)) {
+ log_error(dev, "Couldn't allocate the integrator contexts.\n");
res = RES_MEM_ERR;
goto error;
}
if(!sa_add(rngs, dev->nthreads)) {
- log_error(dev,
- "Not enough memory: couldn't allocate the list of RNGs.\n");
- }
- if(!sa_add(scenes, dev->nthreads)) {
- log_error(dev,
- "Not enough memory: couldn't allocate the list of Star-3D scenes.\n");
+ log_error(dev, "Couldn't allocate the list of RNGs.\n");
res = RES_MEM_ERR;
goto error;
}
- if(!sa_add(shapes, dev->nthreads)) {
- log_error(dev,
- "Not enough memory: couldn't allocate the list of Star-3D shapes.\n");
- res = RES_MEM_ERR;
- goto error;
- }
- memset(mtls, 0, sizeof(struct sschiff_material_properties*)*dev->nthreads);
- memset(accums, 0, sizeof(struct accum*)*dev->nthreads);
- memset(mc_accums, 0, sizeof(struct mc_accum*)*dev->nthreads);
- memset(rngs, 0, sizeof(struct ssp_rng*)*dev->nthreads);
- memset(scenes, 0, sizeof(struct s3d_scene*)*dev->nthreads);
- memset(shapes, 0, sizeof(struct s3d_shape*)*dev->nthreads);
+ memset(ctxs, 0, sizeof(ctxs[0])*dev->nthreads);
+ memset(rngs, 0, sizeof(rngs[0])*dev->nthreads);
- /* Create the per thread data structures */
+ /* Init the per thread data structures */
FOR_EACH(i, 0, dev->nthreads) {
- if(!sa_add(mtls[i], nwavelengths)) {
- log_error(dev,
- "Not enough memory: couldn't allocate the optical properties.\n");
- res = RES_MEM_ERR;
- goto error;
- }
- if(!sa_add(accums[i], nwavelengths)) {
- log_error(dev,
- "Not enough memory: couldn't allocate the temporary accumulator.\n");
- res = RES_MEM_ERR;
- goto error;
- }
- if(!sa_add(mc_accums[i], nwavelengths)) {
- log_error(dev,
- "Not enough memory: couldn't allocate the Monte Carlo accumulator.\n");
- res = RES_MEM_ERR;
- goto error;
- }
- memset(mc_accums[i], 0, sizeof(struct mc_accum)*nwavelengths);
-
res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs + i);
if(res != RES_OK) {
- log_error(dev, "Couldn't create the RNG.\n");
- goto error;
- }
- res = s3d_shape_create_mesh(dev->s3d[i], shapes + i);
- if(res != RES_OK) {
- log_error(dev, "Couldn't create the Star-3D shape.\n");
- goto error;
- }
- res = s3d_scene_create(dev->s3d[i], scenes + i);
- if(res != RES_OK) {
- log_error(dev, "Couldn't create the Star-3D scene.\n");
+ log_error(dev,
+ "Couldn't create the RNG of the thread \"%lu\".\n", (unsigned long)i);
goto error;
}
- res = s3d_scene_attach_shape(scenes[i], shapes[i]);
+ res = integrator_context_init(ctxs + i, dev->s3d[i], rngs[i], &geom_ctx,
+ nwavelengths, nangles);
if(res != RES_OK) {
log_error(dev,
- "Couldn't attach the Star-3D Schiff shape to the Star-3D Schiff scene.\n");
+ "Couldn't initialise the integrator context of the thread \"%lu\".\n",
+ (unsigned long)i);
goto error;
}
}
- /* Clear the accumulators */
- memset(estimator->accums, 0, sizeof(struct mc_accum)*nwavelengths);
-
- /* Setup and sort the wavelengths */
- FOR_EACH(i, 0, nwavelengths) estimator->wavelengths[i] = wavelengths[i];
- qsort(estimator->wavelengths, nwavelengths, sizeof(double), cmp_double);
-
/* Paralell Schiff integration */
#pragma omp parallel for schedule(static)
for(igeom=0; igeom < (int)ngeoms; ++igeom) {
- const double* wlengths = estimator->wavelengths;
- struct sschiff_material material = SSCHIFF_NULL_MATERIAL;
- size_t iwavelength;
const int ithread = omp_get_thread_num();
- ATOMIC res_local;
+ ATOMIC res_local = RES_OK;
if(ATOMIC_GET(&res) != RES_OK) continue;
- /* Sample a geometry, i.e. a shape and its associated material */
- res_local = distrib->sample
- (rngs[ithread], &material, shapes[ithread], distrib->context);
+ /* Setup the data for the current realisation */
+ res_local = begin_realisation(dev, distrib, ctxs + ithread);
if(res_local != RES_OK) {
- log_error(dev, "Error in sampling a Schiff geometry.\n");
- ATOMIC_SET(&res, res_local);
+ ATOMIC_CAS(&res, res_local, RES_OK);
continue;
}
- /* Fetch the per wavelength material properties */
- FOR_EACH(iwavelength, 0, nwavelengths) {
- material.get_property(material.material, wavelengths[iwavelength],
- mtls[ithread] + iwavelength);
- }
-
- /* Build the Star-3D representation of the sampled shape */
- S3D(scene_begin_session(scenes[ithread], S3D_TRACE));
/* Schiff Estimation */
- res_local = radiative_properties(dev, rngs[ithread], igeom, wlengths,
- nwavelengths, angles, nangles, ndirs, scenes[ithread], mtls[ithread],
- mc_accums[ithread], accums[ithread]);
- if(res != RES_OK) ATOMIC_SET(&res, res_local);
+ res_local = radiative_properties(dev, igeom, ndirs, ctxs+ithread);
+ if(res_local != RES_OK) ATOMIC_CAS(&res, res_local, RES_OK);
- S3D(scene_end_session(scenes[ithread]));
+ end_realisation(ctxs + ithread);
}
if(res != RES_OK) goto error; /* Handle integration error */
@@ -764,44 +1213,36 @@ sschiff_integrate
FOR_EACH(i, 0, dev->nthreads) {
size_t iwlen;
FOR_EACH(iwlen, 0, nwavelengths) {
- size_t idata;
- FOR_EACH(idata, 0, SSCHIFF_DATA_COUNT__) {
- estimator->accums[iwlen].mc_data[idata].weight +=
- mc_accums[i][iwlen].mc_data[idata].weight;
- estimator->accums[iwlen].mc_data[idata].sqr_weight +=
- mc_accums[i][iwlen].mc_data[idata].sqr_weight;
+ size_t iangle;
+ #define MC_ACCUM(Data) { \
+ const struct mc_accum* mc_accum = ctxs[i].mc_accums + iwlen; \
+ estimator->accums[iwlen].Data.weight += mc_accum->Data.weight; \
+ estimator->accums[iwlen].Data.sqr_weight += mc_accum->Data.sqr_weight; \
+ } (void)0
+ MC_ACCUM(extinction_cross_section);
+ MC_ACCUM(absorption_cross_section);
+ MC_ACCUM(scattering_cross_section);
+ MC_ACCUM(avg_projected_area);
+
+ /* Accum up to "limit angle"; Great angles are analitically computed */
+ FOR_EACH(iangle, 0, geom_ctx.limit_angles[iwlen]) {
+ MC_ACCUM(differential_cross_section[iangle]);
+ MC_ACCUM(differential_cross_section_cumulative[iangle]);
}
}
}
exit:
+ geometry_context_release(&geom_ctx);
if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy));
- if(angles) sa_release(angles);
- /* Release per thread data */
- if(mtls) {
- FOR_EACH(i, 0, dev->nthreads) if(mtls[i]) sa_release(mtls[i]);
- sa_release(mtls);
- }
- if(accums) {
- FOR_EACH(i, 0, dev->nthreads) if(accums[i]) sa_release(accums[i]);
- sa_release(accums);
- }
- if(mc_accums) {
- FOR_EACH(i, 0, dev->nthreads) if(mc_accums[i]) sa_release(mc_accums[i]);
- sa_release(mc_accums);
+ if(ctxs) {
+ FOR_EACH(i, 0, dev->nthreads) integrator_context_release(ctxs+i);
+ sa_release(ctxs);
}
if(rngs) {
FOR_EACH(i, 0, dev->nthreads) if(rngs[i]) SSP(rng_ref_put(rngs[i]));
sa_release(rngs);
}
- if(shapes) {
- FOR_EACH(i, 0, dev->nthreads) if(shapes[i]) S3D(shape_ref_put(shapes[i]));
- sa_release(shapes);
- }
- if(scenes) {
- FOR_EACH(i, 0, dev->nthreads) if(scenes[i]) S3D(scene_ref_put(scenes[i]));
- sa_release(scenes);
- }
if(out_estimator) *out_estimator = estimator;
return (res_T)res;
error:
@@ -847,19 +1288,19 @@ sschiff_estimator_get_realisations_count
}
res_T
-sschiff_estimator_get_wavelength_state
+sschiff_estimator_get_wavelength_cross_section
(const struct sschiff_estimator* estimator,
const double wavelength,
- struct sschiff_estimator_state* state)
+ struct sschiff_cross_section* cross_section)
{
- const struct mc_accum* accum;
+ const struct mc_accum* acc;
const double* wavelengths;
const double* find;
size_t iwavelength;
size_t nwavelengths;
- int i;
+ size_t N;
- if(!estimator || !state)
+ if(!estimator || !cross_section)
return RES_BAD_ARG;
wavelengths = estimator->wavelengths;
@@ -869,39 +1310,36 @@ sschiff_estimator_get_wavelength_state
if(!find) return RES_BAD_ARG;
iwavelength = (size_t)(find - wavelengths);
- accum = estimator->accums + iwavelength;
-
- FOR_EACH(i, 0, SSCHIFF_DATA_COUNT__) {
- get_mc_value(accum->mc_data+i, estimator->nrealisations, state->values+i);
- }
- state->wavelength = wavelength;
+ acc = estimator->accums + iwavelength;
+
+ N = estimator->nrealisations;
+ get_mc_value(&acc->extinction_cross_section, N, &cross_section->extinction);
+ get_mc_value(&acc->absorption_cross_section, N, &cross_section->absorption);
+ get_mc_value(&acc->scattering_cross_section, N, &cross_section->scattering);
+ get_mc_value(&acc->avg_projected_area, N,
+ &cross_section->average_projected_area);
return RES_OK;
}
res_T
-sschiff_estimator_get_states
+sschiff_estimator_get_cross_sections
(const struct sschiff_estimator* estimator,
- struct sschiff_estimator_state* states)
+ struct sschiff_cross_section* cross_sections)
{
- const double* wavelengths;
- const struct mc_accum* accums;
size_t nwavelengths;
size_t iwlen;
- int i;
- if(!estimator || !states) return RES_BAD_ARG;
+ size_t N;
+ if(!estimator || !cross_sections) return RES_BAD_ARG;
nwavelengths = sa_size(estimator->wavelengths);
- wavelengths = estimator->wavelengths;
- accums = estimator->accums;
-
+ N = estimator->nrealisations;
FOR_EACH(iwlen, 0, nwavelengths) {
- states[iwlen].wavelength = wavelengths[iwlen];
- FOR_EACH(i, 0, SSCHIFF_DATA_COUNT__) {
- get_mc_value
- (accums[iwlen].mc_data + i,
- estimator->nrealisations,
- states[iwlen].values + i);
- }
+ const struct mc_accum* acc = estimator->accums + iwlen;
+ get_mc_value(&acc->extinction_cross_section, N, &cross_sections->extinction);
+ get_mc_value(&acc->absorption_cross_section, N, &cross_sections->absorption);
+ get_mc_value(&acc->scattering_cross_section, N, &cross_sections->scattering);
+ get_mc_value(&acc->avg_projected_area, N,
+ &cross_sections->average_projected_area);
}
return RES_OK;
}
diff --git a/src/test_sschiff_estimator_cylinder.c b/src/test_sschiff_estimator_cylinder.c
@@ -128,10 +128,7 @@ dump_geometry(struct geometry* geom)
static res_T
sample_cylinder
- (struct ssp_rng* rng,
- struct sschiff_material* mtl,
- struct s3d_shape* shape,
- void* sampler_context)
+ (struct ssp_rng* rng, struct s3d_shape* shape, void* sampler_context)
{
struct s3d_vertex_data attrib;
struct sampler_context* sampler_ctx = sampler_context;
@@ -149,9 +146,6 @@ sample_cylinder
attrib.type = S3D_FLOAT3;
attrib.get = get_position;
- mtl->get_property = get_material_property;
- mtl->material = sampler_ctx;
-
nverts = sa_size(cylinder.geometry->vertices) / 3/*#coords*/;
nprims = sa_size(cylinder.geometry->indices) / 3/*#indices per prim*/;
@@ -559,7 +553,7 @@ main(int argc, char** argv)
2.010619438e+2, 2.018997018e+2, 2.027374599e+2
};
const size_t nx = sizeof(x)/sizeof(double);
- const size_t nscatt_angles = 2;
+ const size_t nscatt_angles = 3;
const size_t ngeoms = 100;
const size_t ndirs = 10;
size_t i;
@@ -575,14 +569,17 @@ main(int argc, char** argv)
FOR_EACH(i, 0, nx) {
const double wavelength = 0.6; /* In micron */
- struct sschiff_estimator_state state;
+ struct sschiff_cross_section cross_section;
double interval[2];
- struct sschiff_estimator_value* val;
+ struct sschiff_state* val;
sampler_ctx.aspect_ratio = 0.837;
sampler_ctx.mean_radius = (x[i] * 0.450) / (2*PI);
sampler_ctx.sigma = 1.18;
+ distrib.caracteristic_length = 1;
+ distrib.material.get_property = get_material_property;
+ distrib.material.material = &sampler_ctx;
distrib.sample = sample_cylinder;
distrib.context = &sampler_ctx;
@@ -594,14 +591,13 @@ main(int argc, char** argv)
time_sub(&t0, &t1, &t0);
time_dump(&t0, TIME_MIN|TIME_SEC|TIME_MSEC, NULL, buf, sizeof(buf));
- CHECK(sschiff_estimator_get_wavelength_state
- (estimator, wavelength, &state), RES_OK);
- CHECK(eq_eps(state.wavelength, wavelength, 1.e-6), 1);
+ CHECK(sschiff_estimator_get_wavelength_cross_section
+ (estimator, wavelength, &cross_section), RES_OK);
printf("%u - x = %g - Wavelength = %g micron - %s\n",
(unsigned)i, x[i], wavelength, buf);
- val = state.values + SSCHIFF_EXTINCTION_CROSS_SECTION;
+ val = &cross_section.extinction;
compute_intersection(interval, results[i].extinction_E,
results[i].extinction_SE, val->E, val->SE);
printf(" Extinction ~ %9.3g +/- %9.3g ~ %9.3g +/- %9.3g (%9.3g)\n",
@@ -609,7 +605,7 @@ main(int argc, char** argv)
val->E, val->SE, interval[1] - interval[0]);
CHECK(interval[0] <= interval[1], 1);
- val = state.values + SSCHIFF_ABSORPTION_CROSS_SECTION;
+ val = &cross_section.absorption;
compute_intersection(interval, results[i].absorption_E,
results[i].absorption_SE, val->E, val->SE);
printf(" Absorption ~ %9.3g +/- %9.3g ~ %9.3g +/- %9.3g (%9.3g)\n",
@@ -617,7 +613,7 @@ main(int argc, char** argv)
val->E, val->SE, interval[1] - interval[0]);
CHECK(interval[0] <= interval[1], 1);
- val = state.values + SSCHIFF_SCATTERING_CROSS_SECTION;
+ val = &cross_section.scattering;
compute_intersection(interval, results[i].scattering_E,
results[i].scattering_SE, val->E, val->SE);
printf(" Scattering ~ %9.3g +/- %9.3g ~ %9.3g +/- %9.3g (%9.3g)\n",
@@ -625,7 +621,7 @@ main(int argc, char** argv)
val->E, val->SE, interval[1] - interval[0]);
CHECK(interval[0] <= interval[1], 1);
- val = state.values + SSCHIFF_AVERAGE_PROJECTED_AREA;
+ val = &cross_section.average_projected_area;
compute_intersection(interval, results[i].avg_proj_area_E,
results[i].avg_proj_area_SE, val->E, val->SE);
printf(" Proj Area ~ %9.3g +/- %9.3g ~ %9.3g +/- %9.3g (%9.3g)\n",
diff --git a/src/test_sschiff_estimator_sphere.c b/src/test_sschiff_estimator_sphere.c
@@ -111,7 +111,6 @@ get_position(const unsigned ivert, float vertex[3], void* ctx)
static res_T
sample_sphere
(struct ssp_rng* rng,
- struct sschiff_material* mtl,
struct s3d_shape* shape,
void* sampler_context)
{
@@ -121,7 +120,6 @@ sample_sphere
size_t nverts, nprims;
NCHECK(rng, NULL);
- NCHECK(mtl, NULL);
NCHECK(shape, NULL);
sphere.geometry = &sampler_ctx->geometry;
@@ -132,9 +130,6 @@ sample_sphere
attrib.type = S3D_FLOAT3;
attrib.get = get_position;
- mtl->get_property = get_material_property;
- mtl->material = sampler_ctx;
-
nverts = sa_size(sphere.geometry->vertices) / 3/*#coords*/;
nprims = sa_size(sphere.geometry->indices) / 3/*#indices per prim*/;
@@ -242,11 +237,11 @@ check_schiff_estimation
char buf[64];
struct sschiff_estimator* estimator;
struct sschiff_geometry_distribution distrib = SSCHIFF_NULL_GEOMETRY_DISTRIBUTION;
- struct sschiff_estimator_state state;
+ struct sschiff_cross_section cross_section;
struct time t0, t1;
size_t i;
- const size_t nscatt_angles = 2;
+ const size_t nscatt_angles = 3;
const size_t ngeoms = 100;
const size_t ndirs = 100;
const double wavelength = sampler_ctx->wavelength;
@@ -255,6 +250,9 @@ check_schiff_estimation
NCHECK(results, NULL);
NCHECK(results, 0);
+ distrib.material.get_property = get_material_property;
+ distrib.material.material = sampler_ctx;
+ distrib.caracteristic_length = 1.0;
distrib.sample = sample_sphere;
distrib.context = sampler_ctx;
@@ -265,44 +263,40 @@ check_schiff_estimation
sampler_ctx->medium_refractive_index);
FOR_EACH(i, 0, nresults) {
- const struct sschiff_estimator_value* val;
+ const struct sschiff_state* val;
double dst;
sampler_ctx->mean_radius = results[i].mean_radius;
time_current(&t0);
- CHECK(sschiff_integrate(dev, rng, &distrib, &wavelength, 1,
+ CHECK(sschiff_integrate(dev, rng, &distrib, &wavelength, 1,
sschiff_uniform_scattering_angles, nscatt_angles, ngeoms, ndirs, &estimator), RES_OK);
time_current(&t1);
time_sub(&t0, &t1, &t0);
time_dump(&t0, TIME_MIN|TIME_SEC|TIME_MSEC, NULL, buf, sizeof(buf));
printf("%d - mean radius: %5.2f micron - %s: \n", (int)i, results[i].mean_radius, buf);
- CHECK(sschiff_estimator_get_states(estimator, &state), RES_OK);
- CHECK(eq_eps(state.wavelength, wavelength, 1.e-12), 1);
+ CHECK(sschiff_estimator_get_cross_sections(estimator, &cross_section), RES_OK);
- val = state.values + SSCHIFF_EXTINCTION_CROSS_SECTION ;
+ val = &cross_section.extinction;
dst = val->E - results[i].extinction_cross_section;
printf(" Extinction cross section = %7.2f ~ %12.7f +/- %12.7f (%6.2f)\n",
results[i].extinction_cross_section, val->E, val->SE, dst / val->SE);
CHECK(eq_eps(val->E, results[i].extinction_cross_section, 4*val->SE), 1);
- val = state.values + SSCHIFF_ABSORPTION_CROSS_SECTION ;
+ val = &cross_section.absorption;
dst = val->E - results[i].absorption_cross_section;
printf(" Absorption cross section = %7.2f ~ %12.7f +/- %12.7f (%6.2f)\n",
results[i].absorption_cross_section, val->E, val->SE, dst / val->SE);
CHECK(eq_eps(val->E, results[i].absorption_cross_section, 4*val->SE), 1);
- val = state.values + SSCHIFF_SCATTERING_CROSS_SECTION;
+ val = &cross_section.scattering;
dst = val->E - results[i].scattering_cross_section;
printf(" Scattering cross section = %7.2f ~ %12.7f +/- %12.7f (%6.2f)\n",
results[i].scattering_cross_section, val->E, val->SE, dst / val->SE);
CHECK(eq_eps(val->E, results[i].scattering_cross_section, 4*val->SE), 1);
- /*s = log(sampler_ctx->sigma);
- r = sampler_ctx->mean_radius * exp(2.5 * s * s);
- r = PI * r * r * 1.e-12 */ /* Meter^2 to micron^2*/;
- val = state.values + SSCHIFF_AVERAGE_PROJECTED_AREA;
+ val = &cross_section.average_projected_area;
printf(" Averavege projected area = %12.6g +/- %12.7g\n", val->E, val->SE);
CHECK(sschiff_estimator_ref_put(estimator), RES_OK);
@@ -317,7 +311,7 @@ main(int argc, char** argv)
struct sschiff_device* dev;
struct sschiff_geometry_distribution distrib = SSCHIFF_NULL_GEOMETRY_DISTRIBUTION;
struct sschiff_estimator* estimator;
- struct sschiff_estimator_state state;
+ struct sschiff_cross_section cross_section;
struct ssp_rng* rng;
const struct result results_n_r_1_01[] = {
{ 1.00, 0.484, 0.159, 0.643 },
@@ -362,6 +356,9 @@ main(int argc, char** argv)
sampler_ctx.mean_radius = 1.0;
sampler_ctx.sigma = 1.18;
+ distrib.material.get_property = get_material_property;
+ distrib.material.material = &sampler_ctx;
+ distrib.caracteristic_length = 1;
distrib.sample = sample_sphere;
distrib.context = &sampler_ctx;
@@ -433,7 +430,7 @@ main(int argc, char** argv)
CHECK(sschiff_integrate(dev, rng, &distrib, &wlen, 1,
sschiff_uniform_scattering_angles, 1, 1, 1, &estimator), RES_BAD_ARG);
CHECK(sschiff_integrate(dev, rng, &distrib, &wlen, 1,
- sschiff_uniform_scattering_angles, 2, 1, 1, &estimator), RES_OK);
+ sschiff_uniform_scattering_angles, 3, 1, 1, &estimator), RES_OK);
CHECK(sschiff_estimator_get_wavelengths_count(NULL, NULL), RES_BAD_ARG);
CHECK(sschiff_estimator_get_wavelengths_count(estimator, NULL), RES_BAD_ARG);
@@ -447,15 +444,22 @@ main(int argc, char** argv)
CHECK(sschiff_estimator_get_realisations_count(estimator, &count), RES_OK);
CHECK(count, 1);
- CHECK(sschiff_estimator_get_wavelength_state(NULL, 0, NULL), RES_BAD_ARG);
- CHECK(sschiff_estimator_get_wavelength_state(estimator, 0, NULL), RES_BAD_ARG);
- CHECK(sschiff_estimator_get_wavelength_state(NULL, wlen, NULL), RES_BAD_ARG);
- CHECK(sschiff_estimator_get_wavelength_state(estimator, wlen, NULL), RES_BAD_ARG);
- CHECK(sschiff_estimator_get_wavelength_state(NULL, 0, &state), RES_BAD_ARG);
- CHECK(sschiff_estimator_get_wavelength_state(estimator, 0, &state), RES_BAD_ARG);
- CHECK(sschiff_estimator_get_wavelength_state(NULL, wlen, &state), RES_BAD_ARG);
- CHECK(sschiff_estimator_get_wavelength_state(estimator, wlen, &state), RES_OK);
- CHECK(eq_eps(wlen, state.wavelength, 1.e-12), 1);
+ CHECK(sschiff_estimator_get_wavelength_cross_section
+ (NULL, 0, NULL), RES_BAD_ARG);
+ CHECK(sschiff_estimator_get_wavelength_cross_section
+ (estimator, 0, NULL), RES_BAD_ARG);
+ CHECK(sschiff_estimator_get_wavelength_cross_section
+ (NULL, wlen, NULL), RES_BAD_ARG);
+ CHECK(sschiff_estimator_get_wavelength_cross_section
+ (estimator, wlen, NULL), RES_BAD_ARG);
+ CHECK(sschiff_estimator_get_wavelength_cross_section
+ (NULL, 0, &cross_section), RES_BAD_ARG);
+ CHECK(sschiff_estimator_get_wavelength_cross_section
+ (estimator, 0, &cross_section), RES_BAD_ARG);
+ CHECK(sschiff_estimator_get_wavelength_cross_section
+ (NULL, wlen, &cross_section), RES_BAD_ARG);
+ CHECK(sschiff_estimator_get_wavelength_cross_section
+ (estimator, wlen, &cross_section), RES_OK);
CHECK(sschiff_estimator_ref_get(NULL), RES_BAD_ARG);
CHECK(sschiff_estimator_ref_get(estimator), RES_OK);
@@ -463,8 +467,8 @@ main(int argc, char** argv)
CHECK(sschiff_estimator_ref_put(estimator), RES_OK);
CHECK(sschiff_estimator_ref_put(estimator), RES_OK);
- CHECK(sschiff_integrate(dev, rng, &distrib, &wlen, 1,
- sschiff_uniform_scattering_angles, 2, 10, 1, &estimator), RES_OK);
+ CHECK(sschiff_integrate(dev, rng, &distrib, &wlen, 1,
+ sschiff_uniform_scattering_angles, 3, 10, 1, &estimator), RES_OK);
CHECK(sschiff_estimator_get_realisations_count(estimator, &count), RES_OK);
CHECK(count, 10);
CHECK(sschiff_estimator_ref_put(estimator), RES_OK);