star-schiff

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

commit eeee76acf3d587adfdcadd36c08444def80b684a
parent a57cf548c99f5ea0af24d4df231b45f3d14d2df9
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 13 Oct 2015 12:15:42 +0200

Refactor the Schiff estimator test

Improve the performances of the geometry sampler

Diffstat:
Mcmake/CMakeLists.txt | 2+-
Msrc/sschiff_estimator.c | 4++--
Dsrc/test_sschiff_estimator.c | 393-------------------------------------------------------------------------------
Asrc/test_sschiff_estimator_sphere.c | 386+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 389 insertions(+), 396 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -93,7 +93,7 @@ if(NOT NO_TEST) add_test(${_name} ${_name}) endfunction(new_test) new_test(test_sschiff_device) - new_test(test_sschiff_estimator) + new_test(test_sschiff_estimator_sphere) endif() ################################################################################ diff --git a/src/sschiff_estimator.c b/src/sschiff_estimator.c @@ -147,7 +147,7 @@ compute_path_length if(S3D_HIT_NONE(&hit)) return RES_BAD_ARG; - len += hit.distance; + len += hit.distance - range[0]; range[0] = hit.distance; prim_from = hit.prim; @@ -161,7 +161,7 @@ compute_path_length } while(!S3D_HIT_NONE(&hit)); - *length = (len - first_hit->distance) * 1.e-6/* Micron to meter */; + *length = len * 1.e-6/* Meter to micron */; return RES_OK; } diff --git a/src/test_sschiff_estimator.c b/src/test_sschiff_estimator.c @@ -1,393 +0,0 @@ -/* Copyright (C) |Meso|Star> 2015 (contact@meso-star.com) - * - * This software is governed by the CeCILL license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/or redistribute the software under the terms of the CeCILL - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL license and that you accept its terms. */ - -#include "sschiff.h" -#include "test_sschiff_utils.h" - -#include <rsys/clock_time.h> -#include <rsys/float3.h> -#include <rsys/stretchy_array.h> - -#include <star/s3d.h> -#include <star/ssp.h> - -#define STEP 0.1f -#define NREALISATIONS 1000 - -struct optical_properties { double m, n_r, kappa_r, n_medium; }; - -struct sphere_context { - struct optical_properties props; - double mean_radius; -}; - -struct sphere { - double radius; - size_t ntheta, nphi; -}; - -struct cylinder { - float* vertices; - unsigned* indices; -}; - -static void -get_material_property - (void* mtl, - const double wavelength, - struct sschiff_material_properties* properties) -{ - struct optical_properties* props = mtl; - - NCHECK(mtl, NULL); - NCHECK(properties, NULL); - CHECK(eq_eps(props->m, wavelength, 1.e-8), 1); - - properties->medium_refractive_index = props->n_medium; - properties->relative_real_refractive_index = props->n_r; - properties->relative_imaginary_refractive_index = props->kappa_r; -} - -/******************************************************************************* - * Sphere geometry - ******************************************************************************/ -static void -sphere_get_indices(const unsigned itri, unsigned ids[3], void* ctx) -{ - struct sphere* sphere = ctx; - const size_t iquad = itri / 2; - const size_t iquad_tri = itri % 2; - const size_t i = iquad / (sphere->nphi - 1); - const size_t j = iquad % (sphere->nphi - 1); - size_t A, B; - - B = i * sphere->nphi + j + 1; - A = ((i + 1) % sphere->ntheta) * sphere->nphi + j; - if(iquad_tri == 0) { - ids[0] = (unsigned)B - 1; - ids[1] = (unsigned)A; - ids[2] = (unsigned)B; - } else { ASSERT(iquad_tri == 1); - ids[0] = (unsigned)B; - ids[1] = (unsigned)A; - ids[2] = (unsigned)A + 1; - } -} - -static INLINE void -sphere_get_vertex_position(const unsigned ivert, float vertex[3], void* ctx) -{ - struct sphere* sphere = ctx; - const size_t itheta = ivert / sphere->nphi; - const size_t iphi = ivert % sphere->nphi; - double angle[2]; - double sin_angle[2]; - double cos_angle[2]; - - if(iphi == sphere->nphi - 1) { /* Polar vertices: Force polar coordinates */ - angle[0] = PI; - angle[1] = PI/2; - } else { - angle[0] = -PI + (double)itheta * STEP; - angle[1] = -PI/2 + (double)iphi * STEP; - } - - cos_angle[0] = cos(angle[0]); - cos_angle[1] = cos(angle[1]); - sin_angle[0] = sin(angle[0]); - sin_angle[1] = sin(angle[1]); - - vertex[0] = (float)(cos_angle[0] * cos_angle[1] * sphere->radius); - vertex[1] = (float)(sin_angle[0] * cos_angle[1] * sphere->radius); - vertex[2] = (float)(sin_angle[1] * sphere->radius); -} - -static res_T -sample_sphere - (struct ssp_rng* rng, - struct sschiff_material* mtl, - struct s3d_shape* shape, - void* sampler_context) -{ - struct s3d_vertex_data attrib; - struct sphere sphere; - struct sphere_context* ctx = sampler_context; - - const size_t ntheta = (int)((2*PI)/STEP) + 1; - const size_t nphi = (int)((PI)/STEP) + 1; - const size_t nverts = ntheta * nphi; - const size_t nprims = ntheta * (nphi - 1)/*#quads*/ * 2/*#triangles*/; - - sphere.radius = ssp_ran_lognormal(rng, log(ctx->mean_radius), log(1.18)); - sphere.ntheta = ntheta; - sphere.nphi = nphi; - - NCHECK(rng, NULL); - NCHECK(mtl, NULL); - NCHECK(shape, NULL); - - attrib.usage = S3D_POSITION; - attrib.type = S3D_FLOAT3; - attrib.get = sphere_get_vertex_position; - - mtl->get_property = get_material_property; - mtl->material = ctx; - - return s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprims, - sphere_get_indices, (unsigned)nverts, &attrib, 1, &sphere); -} - -/******************************************************************************* - * Cylinder geometry - ******************************************************************************/ -static void -cylinder_get_indices(const unsigned itri, unsigned ids[3], void* ctx) -{ - struct cylinder* cylinder = ctx; - const size_t id = itri * 3; - - CHECK(itri < sa_size(cylinder->indices) / 3, 1); - ids[0] = cylinder->indices[id+0]; - ids[1] = cylinder->indices[id+1]; - ids[2] = cylinder->indices[id+2]; -} - -static void -cylinder_get_vertex_position(const unsigned ivert, float vertex[3], void* ctx) -{ - struct cylinder* cylinder = ctx; - const size_t i = ivert * 3; - - CHECK(ivert < sa_size(cylinder->vertices) / 3, 1); - f3_set(vertex, cylinder->vertices + i); -} - -static res_T -sample_cylinder - (struct ssp_rng* rng, - struct sschiff_material* mtl, - struct s3d_shape* shape, - void* sampler_context) -{ - struct s3d_vertex_data attrib; - struct cylinder cylinder = { NULL, NULL }; - const unsigned nsteps = (unsigned)((2*PI)/STEP); - unsigned istep; - double sample; - const float aspect_ratio = 0.263f; - float radius; - float height; - res_T res = RES_OK; - (void)rng, (void)sampler_context; - - sample = ssp_ran_lognormal(rng, log(0.983), log(1.1374)); - radius = (float)(sample * pow(aspect_ratio, 1.0/3.0)); - height = 2 * radius * aspect_ratio; - - /* Generate the vertex coordinates */ - FOR_EACH(istep, 0, nsteps) { - const float theta = (float)istep * STEP; - const float x = (float)(radius * cos(theta)); - const float y = (float)(radius * sin(theta)); - f3(sa_add(cylinder.vertices, 3), x, 0.f, y); - f3(sa_add(cylinder.vertices, 3), x, height, y); - } - /* "Polar" vertices */ - f3_splat(sa_add(cylinder.vertices, 3), 0.f); - f3(sa_add(cylinder.vertices, 3), 0.f, height, 0.f); - - /* Cylinder primitives */ - FOR_EACH(istep, 0, nsteps) { - const unsigned id = istep * 2; - unsigned* iprim; - - iprim = sa_add(cylinder.indices, 3); - iprim[0] = (id + 0); - iprim[1] = (id + 1); - iprim[2] = (id + 2) % (nsteps*2); - - iprim = sa_add(cylinder.indices, 3); - iprim[0] = (id + 2) % (nsteps*2); - iprim[1] = (id + 1); - iprim[2] = (id + 3) % (nsteps*2); - } - /* Cap primitives */ - FOR_EACH(istep, 0, nsteps) { - const unsigned id = istep * 2; - unsigned* iprim; - - iprim = sa_add(cylinder.indices, 3); - iprim[0] = (nsteps * 2); - iprim[1] = (id + 0); - iprim[2] = (id + 2) % (nsteps*2); - - iprim = sa_add(cylinder.indices, 3); - iprim[0] = (nsteps * 2) + 1; - iprim[1] = (id + 3) % (nsteps*2); - iprim[2] = (id + 1); - } - - attrib.usage = S3D_POSITION; - attrib.type = S3D_FLOAT3; - attrib.get = cylinder_get_vertex_position; - - mtl->get_property = get_material_property; - mtl->material = NULL; - - res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)nsteps*4, - cylinder_get_indices, (unsigned)nsteps*2 + 2, &attrib, 1, &cylinder); - - sa_release(cylinder.vertices); - sa_release(cylinder.indices); - - return res; -} - -int -main(int argc, char** argv) -{ - char buf[64]; - struct mem_allocator allocator; - struct sphere_context sphere_ctx; - struct sschiff_device* dev; - struct sschiff_estimator* estimator; - struct sschiff_integrator_desc desc = SSCHIFF_NULL_INTEGRATOR_DESC; - struct sschiff_estimator_status status; - struct ssp_rng* rng; - struct time t0, t1; - size_t i; - const double sphere_data_1_01[][2] = { /* mean_radius, result */ - { 1.00, 6.43e-13 }, - { 3.22, 2.66e-11 }, - { 5.44, 1.34e-10 }, - { 7.67, 3.56e-10 }, - { 9.89, 6.83e-10 }, - { 12.1, 1.08e-9 }, - { 14.3, 1.53e-9 }, - { 16.6, 2.00e-9 }, - { 18.8, 2.51e-9 }, - { 21.0, 3.07e-9 } - }; - const double sphere_data_1_1[][2] = { /* mean_radius, result */ - { 1.00, 8.39e-12 }, - { 3.22, 6.93e-11 }, - { 5.44, 1.97e-10 }, - { 7.67, 3.92e-10 }, - { 9.89, 6.51e-10 }, - { 12.1, 9.75e-10 }, - { 14.3, 1.37e-9 }, - { 16.6, 1.82e-9 }, - { 18.8, 2.34e-9 }, - { 21.0, 2.93e-9 } - }; - (void)argc, (void)argv; - - mem_init_proxy_allocator(&allocator, &mem_default_allocator); - - CHECK(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng), RES_OK); - CHECK(sschiff_device_create(NULL, &allocator, 0, NULL, &dev), RES_OK); - - desc.sample_geometry = sample_sphere; - desc.scattering_angles_count = 1; - CHECK(sschiff_integrate(NULL, NULL, NULL, 0, 0, NULL), RES_BAD_ARG); - CHECK(sschiff_integrate(dev, NULL, NULL, 0, 0, NULL), RES_BAD_ARG); - CHECK(sschiff_integrate(NULL, rng, NULL, 0, 0, NULL), RES_BAD_ARG); - CHECK(sschiff_integrate(dev, rng, NULL, 0, 0, NULL), RES_BAD_ARG); - CHECK(sschiff_integrate(NULL, NULL, &desc, 0, 0, NULL), RES_BAD_ARG); - CHECK(sschiff_integrate(dev, NULL, &desc, 0, 0, NULL), RES_BAD_ARG); - CHECK(sschiff_integrate(NULL, rng, &desc, 0, 0, NULL), RES_BAD_ARG); - CHECK(sschiff_integrate(dev, rng, &desc, 0, 0, NULL), RES_BAD_ARG); - CHECK(sschiff_integrate(NULL, NULL, NULL, 0, 1, &estimator), RES_BAD_ARG); - CHECK(sschiff_integrate(dev, NULL, NULL, 0, 1, &estimator), RES_BAD_ARG); - CHECK(sschiff_integrate(NULL, rng, NULL, 0, 1, &estimator), RES_BAD_ARG); - CHECK(sschiff_integrate(dev, rng, NULL, 0, 1, &estimator), RES_BAD_ARG); - CHECK(sschiff_integrate(NULL, NULL, &desc, 0, 1, &estimator), RES_BAD_ARG); - CHECK(sschiff_integrate(dev, NULL, &desc, 0, 1, &estimator), RES_BAD_ARG); - CHECK(sschiff_integrate(NULL, rng, &desc, 0, 1, &estimator), RES_BAD_ARG); - - desc.sampler_context = &sphere_ctx; - - sphere_ctx.props.m = 0.6e-6; - sphere_ctx.props.n_r = 1.1; - sphere_ctx.props.kappa_r = 0.004; - sphere_ctx.props.n_medium = 4.0/3.0; - sphere_ctx.mean_radius = 1.0; - - time_current(&t0); - CHECK(sschiff_integrate(dev, rng, &desc, 6.e-7, NREALISATIONS, &estimator), RES_OK); - time_current(&t1); - - CHECK(sschiff_estimator_get_extinction_cross_section(NULL, NULL), RES_BAD_ARG); - CHECK(sschiff_estimator_get_extinction_cross_section(estimator, NULL), RES_BAD_ARG); - CHECK(sschiff_estimator_get_extinction_cross_section(NULL, &status), RES_BAD_ARG); - CHECK(sschiff_estimator_get_extinction_cross_section(estimator, &status), RES_OK); - - printf("Extinction cross section: %g +/- %g\n", status.E, status.SE); - CHECK(eq_eps(status.E, 8.3e-12, 2*status.SE), 1); - time_sub(&t0, &t1, &t0); - time_dump(&t0, TIME_MIN|TIME_SEC|TIME_MSEC, NULL, buf, sizeof(buf)); - printf("Elapsed time: %s\n", buf); - - CHECK(sschiff_estimator_ref_get(NULL), RES_BAD_ARG); - CHECK(sschiff_estimator_ref_get(estimator), RES_OK); - CHECK(sschiff_estimator_ref_put(NULL), RES_BAD_ARG); - CHECK(sschiff_estimator_ref_put(estimator), RES_OK); - CHECK(sschiff_estimator_ref_put(estimator), RES_OK); - - sphere_ctx.props.n_r = 1.1; - printf("\nMean radius: 1.1\n"); - FOR_EACH(i, 0, sizeof(sphere_data_1_1)/sizeof(double[2])) { - sphere_ctx.mean_radius = sphere_data_1_1[i][0]; - CHECK(sschiff_integrate(dev, rng, &desc, 6.e-7, NREALISATIONS, &estimator), RES_OK); - CHECK(sschiff_estimator_get_extinction_cross_section(estimator, &status), RES_OK); - printf("%d - Extinction cross section = %g ~ %g +/- %g\n", - (int)i, sphere_data_1_1[i][1], status.E, status.SE); - CHECK(eq_eps(status.E, sphere_data_1_1[i][1], 2*status.SE), 1); - CHECK(sschiff_estimator_ref_put(estimator), RES_OK); - } - sphere_ctx.props.n_r = 1.01; - printf("\nMean radius: 1.01\n"); - FOR_EACH(i, 0, sizeof(sphere_data_1_01)/sizeof(double[2])) { - sphere_ctx.mean_radius = sphere_data_1_01[i][0]; - CHECK(sschiff_integrate(dev, rng, &desc, 6.e-7, NREALISATIONS, &estimator), RES_OK); - CHECK(sschiff_estimator_get_extinction_cross_section(estimator, &status), RES_OK); - printf("%d - Extinction cross section = %g ~ %g +/- %g\n", - (int)i, sphere_data_1_01[i][1], status.E, status.SE); - CHECK(eq_eps(status.E, sphere_data_1_01[i][1], 2*status.SE), 1); - CHECK(sschiff_estimator_ref_put(estimator), RES_OK); - } - - CHECK(sschiff_device_ref_put(dev), RES_OK); - CHECK(ssp_rng_ref_put(rng), RES_OK); - - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); - CHECK(mem_allocated_size(), 0); - return 0; -} - diff --git a/src/test_sschiff_estimator_sphere.c b/src/test_sschiff_estimator_sphere.c @@ -0,0 +1,386 @@ +/* Copyright (C) |Meso|Star> 2015 (contact@meso-star.com) + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "sschiff.h" +#include "test_sschiff_utils.h" + +#include <rsys/clock_time.h> +#include <rsys/float3.h> +#include <rsys/stretchy_array.h> + +#include <star/s3d.h> +#include <star/ssp.h> + +#define NSTEPS 1000 + +struct result { + double mean_radius; + double extinction_cross_section; +}; + +struct sphere { + float* vertices; /* List of float[3] */ + unsigned* indices; /* List of unsigned[3] */ +}; + +struct sampler_context { + struct sphere unit_sphere; + double wavelength; + double medium_refractive_index; + double relative_real_refractive_index; + double relative_imaginary_refractive_index; + double mean_radius; +}; + +struct sphere_context { + struct sphere* unit_sphere; + float radius; +}; + +static void +get_material_property + (void* mtl, + const double wavelength, + struct sschiff_material_properties* properties) +{ + struct sampler_context* ctx = mtl; + + NCHECK(mtl, NULL); + NCHECK(properties, NULL); + CHECK(eq_eps(ctx->wavelength, wavelength, 1.e-8), 1); + + properties->medium_refractive_index = ctx->medium_refractive_index; + properties->relative_real_refractive_index = + ctx->relative_real_refractive_index; + properties->relative_imaginary_refractive_index = + ctx->relative_imaginary_refractive_index; +} + +static void +get_indices(const unsigned itri, unsigned ids[3], void* ctx) +{ + struct sphere_context* sphere = ctx; + const size_t i = itri * 3; + + CHECK(sa_size(sphere->unit_sphere->indices) % 3, 0); + CHECK(itri < sa_size(sphere->unit_sphere->indices) / 3, 1); + ids[0] = sphere->unit_sphere->indices[i + 0]; + ids[1] = sphere->unit_sphere->indices[i + 1]; + ids[2] = sphere->unit_sphere->indices[i + 2]; +} + +static INLINE void +get_position(const unsigned ivert, float vertex[3], void* ctx) +{ + struct sphere_context* sphere = ctx; + const size_t i = ivert * 3; + + CHECK(sa_size(sphere->unit_sphere->vertices) % 3, 0); + CHECK(ivert < sa_size(sphere->unit_sphere->vertices) / 3, 1); + vertex[0] = sphere->unit_sphere->vertices[i + 0] * sphere->radius; + vertex[1] = sphere->unit_sphere->vertices[i + 1] * sphere->radius; + vertex[2] = sphere->unit_sphere->vertices[i + 2] * sphere->radius; +} + +static res_T +sample_sphere + (struct ssp_rng* rng, + struct sschiff_material* mtl, + struct s3d_shape* shape, + void* sampler_context) +{ + struct s3d_vertex_data attrib; + struct sampler_context* sampler_ctx = sampler_context; + struct sphere_context sphere; + size_t nverts, nprims; + + NCHECK(rng, NULL); + NCHECK(mtl, NULL); + NCHECK(shape, NULL); + + sphere.unit_sphere = &sampler_ctx->unit_sphere; + sphere.radius = (float)ssp_ran_lognormal + (rng, log(sampler_ctx->mean_radius), log(1.18)); + + attrib.usage = S3D_POSITION; + attrib.type = S3D_FLOAT3; + attrib.get = get_position; + + mtl->get_property = get_material_property; + mtl->material = sampler_ctx; + + nverts = sa_size(sphere.unit_sphere->vertices) / 3/*#coords*/; + nprims = sa_size(sphere.unit_sphere->indices) / 3/*#indices per prim*/; + + return s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprims, + get_indices, (unsigned)nverts, &attrib, 1, &sphere); +} + +static void +unit_sphere_init(struct sphere* sphere, const unsigned ntheta) +{ + const unsigned nphi = (unsigned)(((double)ntheta + 0.5) / 2.0); + const double step_theta = 2*PI / (double)ntheta; + const double step_phi = PI / (double)nphi; + double* cos_theta = NULL; + double* sin_theta = NULL; + double* cos_phi = NULL; + double* sin_phi = NULL; + unsigned itheta, iphi; + + NCHECK(sphere, NULL); + NCHECK(ntheta, 0); + + sphere->vertices = NULL; + sphere->indices = NULL; + + /* Precompute the cosine/sinus of the theta/phi angles */ + FOR_EACH(itheta, 0, ntheta) { + const double theta = -PI + (double)itheta * step_theta; + sa_push(cos_theta, cos(theta)); + sa_push(sin_theta, sin(theta)); + } + FOR_EACH(iphi, 1, nphi) { + const double phi = -PI/2 + (double)iphi * step_phi; + sa_push(cos_phi, cos(phi)); + sa_push(sin_phi, sin(phi)); + } + /* Compute the contour vertices */ + FOR_EACH(itheta, 0, ntheta) { + FOR_EACH(iphi, 0, nphi-1) { + sa_push(sphere->vertices, (float)(cos_theta[itheta]*cos_phi[iphi])); + sa_push(sphere->vertices, (float)(sin_theta[itheta]*cos_phi[iphi])); + sa_push(sphere->vertices, (float)sin_phi[iphi]); + } + } + /* Compute the polar vertices */ + f3(sa_add(sphere->vertices, 3), 0.f, 0.f,-1.f); + f3(sa_add(sphere->vertices, 3), 0.f, 0.f, 1.f); + + /* Define the indices of the contour primitives */ + FOR_EACH(itheta, 0, ntheta) { + const unsigned itheta0 = itheta * (nphi - 1); + const unsigned itheta1 = ((itheta + 1) % ntheta) * (nphi - 1); + FOR_EACH(iphi, 0, nphi-2) { + const unsigned iphi0 = iphi + 0; + const unsigned iphi1 = iphi + 1; + + sa_push(sphere->indices, itheta0 + iphi0); + sa_push(sphere->indices, itheta0 + iphi1); + sa_push(sphere->indices, itheta1 + iphi0); + + sa_push(sphere->indices, itheta1 + iphi0); + sa_push(sphere->indices, itheta0 + iphi1); + sa_push(sphere->indices, itheta1 + iphi1); + } + } + + /* Define the indices of the polar primitives */ + FOR_EACH(itheta, 0, ntheta) { + const unsigned itheta0 = itheta * (nphi - 1); + const unsigned itheta1 = ((itheta + 1) % ntheta) * (nphi - 1); + + sa_push(sphere->indices, ntheta * (nphi - 1)); + sa_push(sphere->indices, itheta0); + sa_push(sphere->indices, itheta1); + + sa_push(sphere->indices, ntheta * (nphi - 1) + 1); + sa_push(sphere->indices, itheta1 + (nphi - 2)); + sa_push(sphere->indices, itheta0 + (nphi - 2)); + } + + /* Release the intermediary data structure */ + sa_release(cos_theta); + sa_release(sin_theta); + sa_release(cos_phi); + sa_release(sin_phi); +} + +static void +unit_sphere_release(struct sphere* sphere) +{ + sa_release(sphere->vertices); + sa_release(sphere->indices); + sphere->vertices = NULL; + sphere->indices = NULL; +} + +static void +check_schiff_estimation + (struct sschiff_device* dev, + struct ssp_rng* rng, + struct sampler_context* sampler_ctx, + const struct result* results, + const size_t nresults) +{ + char buf[64]; + struct sschiff_estimator* estimator; + struct sschiff_integrator_desc desc = SSCHIFF_NULL_INTEGRATOR_DESC; + struct sschiff_estimator_status status; + struct time t0, t1; + size_t i; + + NCHECK(sampler_ctx, NULL); + NCHECK(results, NULL); + NCHECK(results, 0); + + desc.sample_geometry = sample_sphere; + desc.scattering_angles_count = 1; + desc.sampler_context = sampler_ctx; + + printf("Schiff sphere estimation - m: %g; n_r: %g; kappa_r: %g; n_e: %g\n", + sampler_ctx->wavelength, + sampler_ctx->relative_real_refractive_index, + sampler_ctx->relative_imaginary_refractive_index, + sampler_ctx->medium_refractive_index); + + FOR_EACH(i, 0, nresults) { + sampler_ctx->mean_radius = results[i].mean_radius; + + time_current(&t0); + CHECK(sschiff_integrate(dev, rng, &desc, 6.e-7, NSTEPS, &estimator), RES_OK); + time_current(&t1); + + CHECK(sschiff_estimator_get_extinction_cross_section(estimator, &status), RES_OK); + + time_sub(&t0, &t1, &t0); + time_dump(&t0, TIME_MIN|TIME_SEC|TIME_MSEC, NULL, buf, sizeof(buf)); + printf( +"%d - mean radius: %5.2fe-6 - " +"Extinction cross section = %7.2g ~ %12.7g +/- %12.7g - " +"%s\n", + (int)i, + results[i].mean_radius, + results[i].extinction_cross_section, + status.E, status.SE, + buf); + + CHECK(eq_eps(status.E, results[i].extinction_cross_section, 2*status.SE), 1); + CHECK(sschiff_estimator_ref_put(estimator), RES_OK); + } +} + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct sampler_context sampler_ctx; + struct sschiff_device* dev; + struct sschiff_integrator_desc desc = SSCHIFF_NULL_INTEGRATOR_DESC; + struct sschiff_estimator* estimator; + struct sschiff_estimator_status status; + struct ssp_rng* rng; + const struct result results_n_r_1_01[] = { + { 1.00, 6.43e-13 }, + { 3.22, 2.66e-11 }, + { 5.44, 1.34e-10 }, + { 7.67, 3.56e-10 }, + { 9.89, 6.83e-10 }, + { 12.1, 1.08e-9 }, + { 14.3, 1.53e-9 }, + { 16.6, 2.00e-9 }, + { 18.8, 2.51e-9 }, + { 21.0, 3.07e-9 } + }; + const struct result results_n_r_1_1[] = { + { 1.00, 8.39e-12 }, + { 3.22, 6.93e-11 }, + { 5.44, 1.97e-10 }, + { 7.67, 3.92e-10 }, + { 9.89, 6.51e-10 }, + { 12.1, 9.75e-10 }, + { 14.3, 1.37e-9 }, + { 16.6, 1.82e-9 }, + { 18.8, 2.34e-9 }, + { 21.0, 2.93e-9 } + }; + (void)argc, (void)argv; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(ssp_rng_create(&allocator, &ssp_rng_threefry, &rng), RES_OK); + CHECK(sschiff_device_create(NULL, &allocator, 0, NULL, &dev), RES_OK); + + unit_sphere_init(&sampler_ctx.unit_sphere, 64); + + sampler_ctx.wavelength = 0.6e-6; + sampler_ctx.relative_real_refractive_index = 1.1; + sampler_ctx.relative_imaginary_refractive_index = 0.004; + sampler_ctx.medium_refractive_index = 4.0/3.0; + sampler_ctx.mean_radius = 1.0; + + desc.sample_geometry = sample_sphere; + desc.scattering_angles_count = 1; + desc.sampler_context = &sampler_ctx; + + CHECK(sschiff_integrate(NULL, NULL, NULL, 6e-7, 0, NULL), RES_BAD_ARG); + CHECK(sschiff_integrate(dev, NULL, NULL, 6e-7, 0, NULL), RES_BAD_ARG); + CHECK(sschiff_integrate(NULL, rng, NULL, 6e-7, 0, NULL), RES_BAD_ARG); + CHECK(sschiff_integrate(dev, rng, NULL, 6e-7, 0, NULL), RES_BAD_ARG); + CHECK(sschiff_integrate(NULL, NULL, &desc, 6e-7, 0, NULL), RES_BAD_ARG); + CHECK(sschiff_integrate(dev, NULL, &desc, 6e-7, 0, NULL), RES_BAD_ARG); + CHECK(sschiff_integrate(NULL, rng, &desc, 6e-7, 0, NULL), RES_BAD_ARG); + CHECK(sschiff_integrate(dev, rng, &desc, 6e-7, 0, NULL), RES_BAD_ARG); + CHECK(sschiff_integrate(NULL, NULL, NULL, 6e-7, 1, &estimator), RES_BAD_ARG); + CHECK(sschiff_integrate(dev, NULL, NULL, 6e-7, 1, &estimator), RES_BAD_ARG); + CHECK(sschiff_integrate(NULL, rng, NULL, 6e-7, 1, &estimator), RES_BAD_ARG); + CHECK(sschiff_integrate(dev, rng, NULL, 6e-7, 1, &estimator), RES_BAD_ARG); + CHECK(sschiff_integrate(NULL, NULL, &desc, 6e-7, 1, &estimator), RES_BAD_ARG); + CHECK(sschiff_integrate(dev, NULL, &desc, 6e-7, 1, &estimator), RES_BAD_ARG); + CHECK(sschiff_integrate(NULL, rng, &desc, 6e-7, 1, &estimator), RES_BAD_ARG); + CHECK(sschiff_integrate(dev, rng, &desc, 6e-7, 1, &estimator), RES_OK); + + CHECK(sschiff_estimator_get_extinction_cross_section(NULL, NULL), RES_BAD_ARG); + CHECK(sschiff_estimator_get_extinction_cross_section(estimator, NULL), RES_BAD_ARG); + CHECK(sschiff_estimator_get_extinction_cross_section(NULL, &status), RES_BAD_ARG); + CHECK(sschiff_estimator_get_extinction_cross_section(estimator, &status), RES_OK); + + CHECK(sschiff_estimator_ref_get(NULL), RES_BAD_ARG); + CHECK(sschiff_estimator_ref_get(estimator), RES_OK); + CHECK(sschiff_estimator_ref_put(NULL), RES_BAD_ARG); + CHECK(sschiff_estimator_ref_put(estimator), RES_OK); + CHECK(sschiff_estimator_ref_put(estimator), RES_OK); + + sampler_ctx.relative_real_refractive_index = 1.1; + check_schiff_estimation(dev, rng, &sampler_ctx, results_n_r_1_1, + sizeof(results_n_r_1_1)/sizeof(struct result)); + + sampler_ctx.relative_real_refractive_index = 1.01; + check_schiff_estimation(dev, rng, &sampler_ctx, results_n_r_1_01, + sizeof(results_n_r_1_01)/sizeof(struct result)); + + CHECK(sschiff_device_ref_put(dev), RES_OK); + CHECK(ssp_rng_ref_put(rng), RES_OK); + + unit_sphere_release(&sampler_ctx.unit_sphere); + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + return 0; +} +