schiff

Estimate the radiative properties of soft particless
git clone git://git.meso-star.com/schiff.git
Log | Files | Refs | README | LICENSE

commit d95149b1c8a679f6726611c39c987bc42def30e8
parent ce30fcf5c1a2aad59475e447d9ba0c245221f807
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 21 Oct 2015 14:56:59 +0200

First draft of the "schiff sphere" computation

Implement a sub-set of the "schiff sphere" command. Output file is not
handled yet.

Diffstat:
MREADME.md | 6++++--
Mcmake/CMakeLists.txt | 8++++++--
Msrc/sbox_schiff.c | 306++++++-------------------------------------------------------------------------
Msrc/sbox_schiff.h | 2++
Asrc/sbox_schiff_sphere.c | 603+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sbox_schiff_sphere.h | 50++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 685 insertions(+), 290 deletions(-)

diff --git a/README.md b/README.md @@ -9,8 +9,10 @@ that estimates the radiative properties of micro organism with an The library relies on the [CMake](http://www.cmake.org) and the [RCMake](https://gitlab.com/vaplv/rcmake/) package to build. It also depends -on the [RSys](https://gitlab.com/vaplv/rsys/) and the -[Star-Schiff](https://gitlab.com/meso-star/star-schiff/) libraries and the +on the [RSys](https://gitlab.com/vaplv/rsys/) +[Star-3D](https://gitlab.com/meso-star/star-3d/), +[Star-Schiff](https://gitlab.com/meso-star/star-schiff/), +[Star-SP](https://gitlab.com/meso-star/star-sp/) libraries, and the [Star-Box](https://gitlab.com/meso-schiff/star-box/) software development kit. First ensure that CMake is installed on your system. Then install the RCMake diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -36,8 +36,10 @@ set(SBOX_SCHIFF_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src/) ################################################################################ find_package(RCMake 0.2 REQUIRED) find_package(RSys 0.2.1 REQUIRED) -find_package(StarSchiff 0.3 REQUIRED) +find_package(Star3D 0.3 REQUIRED) find_package(StarBox 0.3 REQUIRED) +find_package(StarSchiff 0.3 REQUIRED) +find_package(StarSP 0.3 REQUIRED) if(MSVC) find_package(MuslGetopt REQUIRED) include_directories(${MuslGetopt_INCLUDE_DIR}) @@ -62,9 +64,11 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SBOX_SCHIFF_FILES_SRC sbox_schiff.c + sbox_schiff_sphere.c sbox_schiff_optical_properties.c) set(SBOX_SCHIFF_FILES_INC sbox_schiff.h + sbox_schiff_sphere.h sbox_schiff_optical_properties.h) set(SBOX_SCHIFF_FILES_DOC COPYING.fr COPYING.en README.md) @@ -79,7 +83,7 @@ if(CMAKE_COMPILER_IS_GNUCC) endif() add_library(sbox-schiff SHARED ${SBOX_SCHIFF_FILES_SRC} ${SBOX_SCHIFF_FILES_INC}) -target_link_libraries(sbox-schiff RSys StarBox StarSchiff ${MATH_LIB}) +target_link_libraries(sbox-schiff RSys Star3D StarBox StarSchiff StarSP ${MATH_LIB}) set_target_properties(sbox-schiff PROPERTIES VERSION ${VERSION} SOVERSION ${VERSION_MAJOR}) diff --git a/src/sbox_schiff.c b/src/sbox_schiff.c @@ -26,194 +26,14 @@ * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ -#define _POSIX_C_SOURCE 2 /* getopt support */ #include "sbox_schiff.h" -#include "sbox_schiff_optical_properties.h" +#include "sbox_schiff_sphere.h" #include <star/sbox.h> -#include <star/sbox_parse.h> #include <rsys/mem_allocator.h> #include <rsys/rsys.h> -#include <rsys/stretchy_array.h> - -#ifdef COMPER_CL - #include <getopt.h> -#else - #include <unistd.h> -#endif - -#define NDIRS 100 -#define NGEOMS 1000 -#define RADIUS 1.0 -#define SIGMA 1.0 -#define NTHETAS 64 - -struct schiff_sphere_args { - const char* material; - const char* output; - struct sbox_schiff_optical_properties* props; - double* wavelengths; - double radius; - double sigma; - unsigned ndirs; - unsigned ngeoms; - unsigned nthetas; -}; - -static const struct schiff_sphere_args SCHIFF_SPHERE_ARGS_NULL = { - NULL, NULL, NULL, NULL, RADIUS, SIGMA, NDIRS, NGEOMS, NTHETAS -}; - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static char -check_wavelength(const double wavelength) -{ - ASSERT(wavelength); - return wavelength > 0.0; -} - -static int -cmp_properties(const void* op0, const void* op1) -{ - const struct sbox_schiff_optical_properties* prop0 = op0; - const struct sbox_schiff_optical_properties* prop1 = op1; - if (prop0->W < prop1->W) return -1; - else if(prop0->W > prop1->W) return 1; - else return 0; -} - -static res_T -schiff_sphere_parse_args - (struct sbox* sbox, - const int argc, - char** argv, - struct schiff_sphere_args* args) -{ - double props[4]; - double* wavelength; - int opt; - res_T res = RES_OK; - ASSERT(sbox && argc && argv && args); - - *args = SCHIFF_SPHERE_ARGS_NULL; - optind = 0; - opterr = 0; - - while((opt = getopt(argc, argv, ":d:g:i:n:p:r:s:w:")) != -1) { - res_T res = RES_OK; - switch(opt) { - case 'd': res = sbox_str_to_uint(optarg, &args->ndirs); break; - case 'g': res = sbox_str_to_uint(optarg, &args->ngeoms); break; - case 'i': args->material = optarg; break; - case 'n': res = sbox_str_to_uint(optarg, &args->nthetas); break; - case '0': args->output = optarg; break; - case 'p': - res = sbox_parse_list_double(optarg, props, 4); - if(res == RES_OK) { - struct sbox_schiff_optical_properties* opt_props = sa_add(args->props, 1); - opt_props->W = props[0]; - opt_props->Nr = props[1]; - opt_props->Kr = props[2]; - opt_props->Ne = props[3]; - if(!sbox_schiff_optical_properties_check(opt_props)) - res = RES_BAD_ARG; - } - break; - case 'r': - res = sbox_str_to_double(optarg, &args->radius); - if(res == RES_OK && args->radius < 0.0) res = RES_BAD_ARG; - break; - case 's': - res = sbox_str_to_double(optarg, &args->sigma); - if(res == RES_OK && args->sigma < 0.0) res = RES_BAD_ARG; - break; - case 'w': - wavelength = sa_add(args->wavelengths, 1); - res = sbox_str_to_double(optarg, wavelength); - if(res == RES_OK && !check_wavelength(*wavelength)) res = RES_BAD_ARG; - break; - case ':': - logger_print(&sbox->logger, LOG_ERROR, - "%s: option requires an argument -- '%c'\n", - argv[0], argv[optind-1][1]); - res = RES_BAD_ARG; - break; - case '?': - logger_print(&sbox->logger, LOG_ERROR, - "%s: invalid option -- '%c'\n", - argv[0], argv[optind-1][1]); - res = RES_BAD_ARG; - break; - default: res = RES_BAD_ARG; break; - } - if(res != RES_OK) { - if(optarg) { - logger_print(&sbox->logger, LOG_ERROR, - "%s: invalid option arguments '%s' -- '%c'\n", - argv[0], optarg, opt); - } - goto error; - } - } -exit: - return res; -error: - sa_release(args->props); - sa_release(args->wavelengths); - *args = SCHIFF_SPHERE_ARGS_NULL; - goto exit; -} - -/* Assume that props0 and props1 are sorted. If two entries have the same - * wavelength the entry of props1 is skipped. */ -static void -merge_properties - (struct sbox_schiff_optical_properties** props0, - const struct sbox_schiff_optical_properties* props1) -{ - const size_t sizeof_prop = sizeof(struct sbox_schiff_optical_properties); - const struct sbox_schiff_optical_properties* src0; - const struct sbox_schiff_optical_properties* src1; - struct sbox_schiff_optical_properties* dst = NULL; - size_t nsrc0, nsrc1; - size_t isrc0 = 0, isrc1 =0; - ASSERT(props0 && props1); - - src0 = *props0; - src1 = props1; - nsrc0 = sa_size(src0); - nsrc1 = sa_size(src1); - - while(isrc0 < nsrc0 && isrc1 < nsrc1) { - if(src0[isrc0].W < src1[isrc1].W) { - sa_push(dst, src0[isrc0]); - ++isrc0; - } else if(src1[isrc1].W < src0[isrc0].W) { - sa_push(dst, src1[isrc1]); - ++isrc1; - } else { /* src0[isrc0].W == src1[isrc1].W */ - sa_push(dst, src0[isrc0]); - ++isrc0; - ++isrc1; - } - - if(isrc0 >= nsrc0 && isrc1 < nsrc1) { - const size_t nremains = nsrc1 - isrc1; - memcpy(sa_add(dst, nremains), src1 + isrc1, sizeof_prop * nremains); - isrc1 = nsrc1; - } else if(isrc1 >= nsrc1 && isrc0 < nsrc0) { - const size_t nremains = nsrc0 - isrc0; - memcpy(sa_add(dst, nremains), src0 + isrc0, sizeof_prop * nremains); - isrc0 = nsrc0; - } - } - sa_release(*props0); - *props0 = dst; -} /******************************************************************************* * Schiff command @@ -232,71 +52,6 @@ cmd_func_schiff(struct sbox* sbox, const int argc, char** argv, void* ctx) } static res_T -cmd_func_schiff_sphere(struct sbox* sbox, const int argc, char** argv, void* ctx) -{ - struct schiff_sphere_args args = SCHIFF_SPHERE_ARGS_NULL; - struct sbox_schiff_optical_properties* mtl_props = NULL; - const size_t sizeof_prop = sizeof(struct sbox_schiff_optical_properties); - size_t i; - res_T res = RES_OK; - (void)ctx; - - res = schiff_sphere_parse_args(sbox, argc, argv, &args); - if(res != RES_OK) goto error; - - if(!args.wavelengths) { - logger_print(&sbox->logger, LOG_ERROR, - "Missing the wavelength argument(s).\n"); - res = RES_BAD_ARG; - goto error; - } - - /* Sort the submitted properties in ascending order with respect to the - * wavelength */ - if(args.props) { - qsort(args.props, sa_size(args.props), sizeof_prop, cmp_properties); - } - /* Load the material file and sort the its properties in ascending order with - * respect to the wavelength */ - if(args.material) { - res = sbox_schiff_optical_properties_load(sbox, args.material, &mtl_props); - if(res != RES_OK) goto error; - qsort(mtl_props, sa_size(mtl_props), sizeof_prop, cmp_properties); - } - - /* Define the optical properties to use */ - if(mtl_props && !args.props) { - args.props = mtl_props; - mtl_props = NULL; - } else if(args.props && mtl_props) { - merge_properties(&args.props, mtl_props); - sa_release(mtl_props); - mtl_props = NULL; - } - - if(!args.props) { - logger_print(&sbox->logger, LOG_ERROR, - "Radiative properties are not defined.\n"); - res = RES_BAD_ARG; - goto error; - } - - FOR_EACH(i, 0, sa_size(args.props)) { - printf("%g %g %g %g\n", - args.props[i].W, args.props[i].Nr, args.props[i].Kr, args.props[i].Ne); - } - -exit: - sa_release(args.props); - sa_release(args.wavelengths); - sa_release(mtl_props); - return res; -error: - goto exit; -} - - -static res_T cmd_func_help_schiff(struct sbox* sbox, const int argc, char** argv, void* ctx) { struct sbox_schiff* schiff = ctx; @@ -329,43 +84,6 @@ cmd_func_help_schiff(struct sbox* sbox, const int argc, char** argv, void* ctx) } } -static res_T -cmd_func_help_schiff_sphere - (struct sbox* sbox, const int argc, char** argv, void* ctx) -{ - static const char* help[] = { -"The geometry of the micro organisms are distributed with respect to a lognormal\n", -"distribution of spherical meshes:\n", -"P(x) dx = 1/(sigma*x*sqrt(2*PI)) * exp(-(ln(x)-radius)^2 / (2*sigma^2)) dx\n", -"\n", -"schiff sphere [OPTIONS]...\n", -"\n", -"-d DIRECTIONS Number of directions sampled for each spherical geometry.\n", -" Default is "STR(NDIRS)".\n", -"-g GEOMETRIES Number of spherical geometries sampled with the lognormal\n", -" distribution. This is actually the number of realisations of\n", -" the integration. Default is "STR(NGEOMS)".\n", -"-i PROPERTIES File that lists the radiative properties of the geometries for\n", -" a set of wavelengths. Each line must be formatted as \"W Nr Kr Ne\"\n", -" where \"W\" is the wavelength in meter, \"Nr\" and \"Kr\" are\n", -" respectively the real and imaginary parts of the relative\n", -" refractive index, and \"Ne\" the refractive index of the medium.\n", -"-n STEPS Number of points used to discretised the spherical mesh along 2PI.\n", -" Default is "STR(NTHETAS)".\n", -"-o FILENAME Write output to FILENAME.\n", -"-p W:Nr:Kr:Ne Optical properties of the geometries for a given wavelength. \"W\"\n", -" is the wavelength in meter, \"Nr\" and \"Kr\" are respectively the\n", -" real and imaginary parts of the relative refractive index and\n", -" \"Ne\" is the refractive index of the medium. Overwrite the\n", -" properties loaded with the -i option.\n", -"-r RADIUS \"radius\" argument of the lognormal distribution Default is "STR(RADIUS)".\n", -"-s SIGMA \"sigma\" argument of the lognormal distribution. Default is "STR(SIGMA)".\n", -"-w WAVELENGTH Wavelength to integrate.\n" - }; - (void)argc, (void)argv, (void)ctx; - return sbox_print_text(sbox, help, sizeof(help)/sizeof(const char*)); -} - /******************************************************************************* * Exported functions ******************************************************************************/ @@ -376,6 +94,7 @@ res_T sbox_plugin_create(struct sbox* sbox, void** out_plugin) { struct sbox_schiff* schiff = NULL; + struct mem_allocator allocator; res_T res = RES_OK; if(!sbox || !out_plugin) { @@ -383,13 +102,16 @@ sbox_plugin_create(struct sbox* sbox, void** out_plugin) goto error; } - schiff = MEM_CALLOC(sbox->allocator, 1, sizeof(struct sbox_schiff)); + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + schiff = MEM_CALLOC(&allocator, 1, sizeof(struct sbox_schiff)); if(!schiff) { logger_print(&sbox->logger, LOG_ERROR, "Not enough memory: couldn't create the Schiff Star-Box plugin.\n"); res = RES_MEM_ERR; goto error; } + schiff->allocator = allocator; SBOX(cmd_init(&schiff->cmd_schiff, "schiff", "Estimation of the radiative properties of micro organisms.", @@ -430,12 +152,24 @@ res_T sbox_plugin_release(struct sbox* sbox, void* addin) { struct sbox_schiff* schiff = addin; + struct mem_allocator allocator = schiff->allocator; + res_T res = RES_OK; + if(!sbox || !schiff) return RES_BAD_ARG; + SBOX(cmd_unregister(&schiff->cmd_schiff)); SBOX(cmd_unregister(&schiff->cmd_schiff_sphere)); SBOX(cmd_unregister(&schiff->cmd_help_schiff)); SBOX(cmd_unregister(&schiff->cmd_help_schiff_sphere)); - MEM_RM(sbox->allocator, schiff); - return RES_OK; + MEM_RM(&allocator, schiff); + + if(MEM_ALLOCATED_SIZE(&allocator)) { + char dump[512]; + MEM_DUMP(&allocator, dump, sizeof(dump)/sizeof(char)); + logger_print(&sbox->logger, LOG_ERROR, "Memory leaks:\n%s\n", dump); + res = RES_MEM_ERR; + } + mem_shutdown_proxy_allocator(&allocator); + return res; } diff --git a/src/sbox_schiff.h b/src/sbox_schiff.h @@ -36,6 +36,8 @@ struct sbox_schiff { struct sbox_cmd cmd_schiff_sphere; struct sbox_cmd cmd_help_schiff; struct sbox_cmd cmd_help_schiff_sphere; + + struct mem_allocator allocator; }; #endif /* SBOX_SCHIFF_H */ diff --git a/src/sbox_schiff_sphere.c b/src/sbox_schiff_sphere.c @@ -0,0 +1,603 @@ +/* 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. */ + +#define _POSIX_C_SOURCE 2 /* getopt support */ + +#include "sbox_schiff.h" +#include "sbox_schiff_optical_properties.h" +#include "sbox_schiff_sphere.h" + +#include <star/s3d.h> +#include <star/sbox_parse.h> +#include <star/ssp.h> +#include <star/sschiff.h> + +#include <rsys/clock_time.h> +#include <rsys/float3.h> +#include <rsys/stretchy_array.h> + +#ifdef COMPILER_CL + #include <getopt.h> +#else + #include <unistd.h> +#endif + + +/* Default values */ +#define NDIRS 100 +#define NGEOMS 1000 +#define RADIUS 1.0 +#define SIGMA 1.0 +#define NTHETAS 64 + +struct schiff_sphere_args { + const char* material; + const char* output; + struct sbox_schiff_optical_properties* props; + double* wavelengths; + double radius; + double sigma; + unsigned ndirs; + unsigned ngeoms; + unsigned nthetas; +}; + +static const struct schiff_sphere_args SCHIFF_SPHERE_ARGS_NULL = { + NULL, NULL, NULL, NULL, RADIUS, SIGMA, NDIRS, NGEOMS, NTHETAS +}; + +struct mesh { + unsigned* indices; + float* vertices; +}; + +static const struct mesh MESH_NULL = { NULL, NULL }; + +struct sphere { + struct mesh* mesh; + float radius; +}; + +struct schiff_sphere_geometry_distribution_context { + struct mesh* mesh; + struct sbox_schiff_optical_properties* properties; + double mean_radius; + double sigma; +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static char +check_wavelength(const double wavelength) +{ + ASSERT(wavelength); + return wavelength > 0.0; +} + +static int +cmp_properties(const void* op0, const void* op1) +{ + const struct sbox_schiff_optical_properties* prop0 = op0; + const struct sbox_schiff_optical_properties* prop1 = op1; + if (prop0->W < prop1->W) return -1; + else if(prop0->W > prop1->W) return 1; + else return 0; +} + +static void +get_indices(const unsigned itri, unsigned ids[3], void* ctx) +{ + struct sphere* sphere = ctx; + const size_t i = itri * 3; + ASSERT(sa_size(sphere->mesh->indices) % 3 == 0); + ASSERT(itri < sa_size(sphere->mesh->indices) / 3); + ids[0] = sphere->mesh->indices[i + 0]; + ids[1] = sphere->mesh->indices[i + 1]; + ids[2] = sphere->mesh->indices[i + 2]; +} + +static INLINE void +get_position(const unsigned ivert, float vertex[3], void* ctx) +{ + struct sphere* sphere = ctx; + const size_t i = ivert * 3; + ASSERT(sa_size(sphere->mesh->vertices) % 3 == 0); + ASSERT(ivert < sa_size(sphere->mesh->vertices) / 3); + vertex[0] = sphere->mesh->vertices[i + 0] * sphere->radius; + vertex[1] = sphere->mesh->vertices[i + 1] * sphere->radius; + vertex[2] = sphere->mesh->vertices[i + 2] * sphere->radius; +} + +static void +get_material_property + (void* mtl, + const double wavelength, + struct sschiff_material_properties* props) +{ + struct sbox_schiff_optical_properties* properties = mtl; + const size_t nproperties = sa_size(properties); + double Ne, Kr, Nr; + size_t i; + + /* Assume that properties are sorted in ascending order with respect to the + * wavelength. TODO use a binary search */ + FOR_EACH(i, 0, nproperties) { + if(properties[i].W >= wavelength) + break; + } + + if(eq_eps(properties[i].W, wavelength, 1.e-12) + || i==0 || i==nproperties) { /* Clamp to wavelength */ + Ne = properties[i].Ne; + Kr = properties[i].Kr; + Nr = properties[i].Nr; + } else { /* Linear interpolation of optical properties */ + const size_t i_prev = i - 1; + const double d = properties[i].W - properties[i_prev].W; + const double u = (wavelength - properties[i_prev].W) / d; + Ne = u * properties[i].Ne + (1-u) * properties[i_prev].Ne; + Kr = u * properties[i].Kr + (1-u) * properties[i_prev].Kr; + Nr = u * properties[i].Nr + (1-u) * properties[i_prev].Nr; + } + + NCHECK(props, NULL); + props->medium_refractive_index = Ne; + props->relative_imaginary_refractive_index = Kr; + props->relative_real_refractive_index = Nr; +} + +static void +mesh_init_sphere(struct mesh* 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; + ASSERT(sphere && ntheta); + + 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 +mesh_release(struct mesh* sphere) +{ + sa_release(sphere->vertices); + sa_release(sphere->indices); + sphere->vertices = NULL; + sphere->indices = NULL; +} + +/* Assume that props0 and props1 are sorted. If two entries have the same + * wavelength the entry of props1 is skipped. */ +static void +merge_properties + (struct sbox_schiff_optical_properties** props0, + const struct sbox_schiff_optical_properties* props1) +{ + const size_t sizeof_prop = sizeof(struct sbox_schiff_optical_properties); + const struct sbox_schiff_optical_properties* src0; + const struct sbox_schiff_optical_properties* src1; + struct sbox_schiff_optical_properties* dst = NULL; + size_t nsrc0, nsrc1; + size_t isrc0 = 0, isrc1 =0; + ASSERT(props0 && props1); + + src0 = *props0; + src1 = props1; + nsrc0 = sa_size(src0); + nsrc1 = sa_size(src1); + + while(isrc0 < nsrc0 && isrc1 < nsrc1) { + if(src0[isrc0].W < src1[isrc1].W) { + sa_push(dst, src0[isrc0]); + ++isrc0; + } else if(src1[isrc1].W < src0[isrc0].W) { + sa_push(dst, src1[isrc1]); + ++isrc1; + } else { /* src0[isrc0].W == src1[isrc1].W */ + sa_push(dst, src0[isrc0]); + ++isrc0; + ++isrc1; + } + + if(isrc0 >= nsrc0 && isrc1 < nsrc1) { + const size_t nremains = nsrc1 - isrc1; + memcpy(sa_add(dst, nremains), src1 + isrc1, sizeof_prop * nremains); + isrc1 = nsrc1; + } else if(isrc1 >= nsrc1 && isrc0 < nsrc0) { + const size_t nremains = nsrc0 - isrc0; + memcpy(sa_add(dst, nremains), src0 + isrc0, sizeof_prop * nremains); + isrc0 = nsrc0; + } + } + sa_release(*props0); + *props0 = dst; +} + + +static res_T +schiff_sphere_parse_args + (struct sbox* sbox, + const int argc, + char** argv, + struct schiff_sphere_args* args) +{ + double props[4]; + double* wavelength; + int opt; + res_T res = RES_OK; + ASSERT(sbox && argc && argv && args); + + *args = SCHIFF_SPHERE_ARGS_NULL; + optind = 0; + opterr = 0; + + while((opt = getopt(argc, argv, ":d:g:i:n:p:r:s:w:")) != -1) { + res_T res = RES_OK; + switch(opt) { + case 'd': res = sbox_str_to_uint(optarg, &args->ndirs); break; + case 'g': res = sbox_str_to_uint(optarg, &args->ngeoms); break; + case 'i': args->material = optarg; break; + case 'n': res = sbox_str_to_uint(optarg, &args->nthetas); break; + case '0': args->output = optarg; break; + case 'p': + res = sbox_parse_list_double(optarg, props, 4); + if(res == RES_OK) { + struct sbox_schiff_optical_properties* opt_props = sa_add(args->props, 1); + opt_props->W = props[0]; + opt_props->Nr = props[1]; + opt_props->Kr = props[2]; + opt_props->Ne = props[3]; + if(!sbox_schiff_optical_properties_check(opt_props)) + res = RES_BAD_ARG; + } + break; + case 'r': + res = sbox_str_to_double(optarg, &args->radius); + if(res == RES_OK && args->radius < 0.0) res = RES_BAD_ARG; + break; + case 's': + res = sbox_str_to_double(optarg, &args->sigma); + if(res == RES_OK && args->sigma < 0.0) res = RES_BAD_ARG; + break; + case 'w': + wavelength = sa_add(args->wavelengths, 1); + res = sbox_str_to_double(optarg, wavelength); + if(res == RES_OK && !check_wavelength(*wavelength)) res = RES_BAD_ARG; + break; + case ':': + logger_print(&sbox->logger, LOG_ERROR, + "%s: option requires an argument -- '%c'\n", + argv[0], argv[optind-1][1]); + res = RES_BAD_ARG; + break; + case '?': + logger_print(&sbox->logger, LOG_ERROR, + "%s: invalid option -- '%c'\n", + argv[0], argv[optind-1][1]); + res = RES_BAD_ARG; + break; + default: res = RES_BAD_ARG; break; + } + if(res != RES_OK) { + if(optarg) { + logger_print(&sbox->logger, LOG_ERROR, + "%s: invalid option arguments '%s' -- '%c'\n", + argv[0], optarg, opt); + } + goto error; + } + } +exit: + return res; +error: + sa_release(args->props); + sa_release(args->wavelengths); + *args = SCHIFF_SPHERE_ARGS_NULL; + goto exit; +} + +static res_T +schiff_sphere_sample_geometry + (struct ssp_rng* rng, + struct sschiff_material* mtl, + struct s3d_shape* shape, + void* context) +{ + struct schiff_sphere_geometry_distribution_context* ctx = context; + struct sphere sphere; + struct s3d_vertex_data attrib; + size_t nverts, nprims; + ASSERT(rng && mtl && shape && context); + + sphere.mesh = ctx->mesh; + sphere.radius = (float)ssp_ran_lognormal + (rng, log(ctx->mean_radius), log(ctx->sigma)); + + attrib.usage = S3D_POSITION; + attrib.type = S3D_FLOAT3; + attrib.get = get_position; + + mtl->get_property = get_material_property; + mtl->material = ctx->properties; + + nverts = sa_size(ctx->mesh->vertices) / 3/*#coords*/; + nprims = sa_size(ctx->mesh->indices) / 3/*#indices per prim*/; + + return s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprims, + get_indices, (unsigned)nverts, &attrib, 1, &sphere); +} + +static res_T +schiff_sphere_run + (struct sbox* sbox, + struct sbox_schiff* schiff, + struct schiff_sphere_args* args) +{ + struct sschiff_geometry_distribution distrib = SSCHIFF_NULL_GEOMETRY_DISTRIBUTION; + struct schiff_sphere_geometry_distribution_context ctx; + struct mesh mesh = MESH_NULL; + struct sschiff_device* sschiff = NULL; + struct ssp_rng* rng = NULL; + size_t iwave, nwaves; + res_T res = RES_OK; + ASSERT(sbox); + + mesh_init_sphere(&mesh, args->nthetas); + + /* FIXME use the rng type defined in sbox */ + res = ssp_rng_create(&schiff->allocator, &ssp_rng_threefry, &rng); + if(res != RES_OK) { + logger_print(&sbox->logger, LOG_ERROR, + "Couldn't create the Star Schiff Random Number Generator.\n"); + goto error; + } + + res = sschiff_device_create + (&sbox->logger, &schiff->allocator, 0, sbox->s3d, &sschiff); + if(res != RES_OK) { + logger_print(&sbox->logger, LOG_ERROR, + "Couldn't create the Star Schiff device.\n"); + goto error; + } + + ctx.mesh = &mesh; + ctx.properties = args->props; + ctx.mean_radius = args->radius; + ctx.sigma = args->sigma; + + distrib.sample = schiff_sphere_sample_geometry; + distrib.context = &ctx; + + nwaves = sa_size(args->wavelengths); + FOR_EACH(iwave, 0, nwaves) { + struct sschiff_estimator* estimator; + struct sschiff_estimator_status status; + struct time t0, t1; + char buf[64]; + + time_current(&t0); + res = sschiff_integrate(sschiff, rng, &distrib, args->wavelengths[iwave], 1, + args->ngeoms, args->ndirs, &estimator); + time_current(&t1); + if(res != RES_OK) { + logger_print(&sbox->logger, LOG_ERROR, + "Error in Schiff integration of the wavelength `%g'.\n", + args->wavelengths[iwave]); + continue; + } + + time_sub(&t0, &t1, &t0); + time_dump(&t0, TIME_MIN|TIME_SEC|TIME_MSEC, NULL, buf, sizeof(buf)); + + SSCHIFF(estimator_get_extinction_cross_section(estimator, &status)); + logger_print(&sbox->logger, LOG_OUTPUT, +"Wavelength: %7.2g - Extinction cross section ~ %12.7g +/- %12.7g - %s\n", + args->wavelengths[iwave], status.E, status.SE, buf); + + SSCHIFF(estimator_ref_put(estimator)); + } + +exit: + if(sschiff) SSCHIFF(device_ref_put(sschiff)); + if(rng) SSP(rng_ref_put(rng)); + mesh_release(&mesh); + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +cmd_func_schiff_sphere(struct sbox* sbox, const int argc, char** argv, void* ctx) +{ + struct sbox_schiff* schiff = ctx; + struct schiff_sphere_args args = SCHIFF_SPHERE_ARGS_NULL; + struct sbox_schiff_optical_properties* mtl_props = NULL; + const size_t sizeof_prop = sizeof(struct sbox_schiff_optical_properties); + res_T res = RES_OK; + (void)ctx; + + res = schiff_sphere_parse_args(sbox, argc, argv, &args); + if(res != RES_OK) goto error; + + if(!args.wavelengths) { + logger_print(&sbox->logger, LOG_ERROR, + "Missing the wavelength argument(s).\n"); + res = RES_BAD_ARG; + goto error; + } + + /* Sort the submitted properties in ascending order with respect to the + * wavelength */ + if(args.props) { + qsort(args.props, sa_size(args.props), sizeof_prop, cmp_properties); + } + /* Load the material file and sort the its properties in ascending order with + * respect to the wavelength */ + if(args.material) { + res = sbox_schiff_optical_properties_load(sbox, args.material, &mtl_props); + if(res != RES_OK) goto error; + qsort(mtl_props, sa_size(mtl_props), sizeof_prop, cmp_properties); + } + + /* Define the optical properties to use */ + if(mtl_props && !args.props) { + args.props = mtl_props; + mtl_props = NULL; + } else if(args.props && mtl_props) { + merge_properties(&args.props, mtl_props); + sa_release(mtl_props); + mtl_props = NULL; + } + + if(!args.props) { + logger_print(&sbox->logger, LOG_ERROR, + "Radiative properties are not defined.\n"); + res = RES_BAD_ARG; + goto error; + } + + res = schiff_sphere_run(sbox, schiff, &args); + if(res != RES_OK) goto error; + +exit: + sa_release(args.props); + sa_release(args.wavelengths); + sa_release(mtl_props); + return res; +error: + goto exit; +} + +res_T +cmd_func_help_schiff_sphere + (struct sbox* sbox, const int argc, char** argv, void* ctx) +{ + static const char* help[] = { +"The geometry of the micro organisms are distributed with respect to a lognormal\n", +"distribution of spherical meshes:\n", +"P(x) dx = 1/(sigma*x*sqrt(2*PI)) * exp(-(ln(x)-ln(radius))^2 / (2*ln(sigma)^2)) dx\n", +"\n", +"schiff sphere [OPTIONS]...\n", +"\n", +"-d DIRECTIONS Number of directions sampled for each spherical geometry.\n", +" Default is "STR(NDIRS)".\n", +"-g GEOMETRIES Number of spherical geometries sampled with the lognormal\n", +" distribution. This is actually the number of realisations of\n", +" the integration. Default is "STR(NGEOMS)".\n", +"-i PROPERTIES File that lists the radiative properties of the geometries for\n", +" a set of wavelengths. Each line must be formatted as \"W Nr Kr Ne\"\n", +" where \"W\" is the wavelength in meter, \"Nr\" and \"Kr\" are\n", +" respectively the real and imaginary parts of the relative\n", +" refractive index, and \"Ne\" the refractive index of the medium.\n", +"-n STEPS Number of points used to discretised the spherical mesh along 2PI.\n", +" Default is "STR(NTHETAS)".\n", +"-o FILENAME Write output to FILENAME.\n", +"-p W:Nr:Kr:Ne Optical properties of the geometries for a given wavelength. \"W\"\n", +" is the wavelength in meter, \"Nr\" and \"Kr\" are respectively the\n", +" real and imaginary parts of the relative refractive index and\n", +" \"Ne\" is the refractive index of the medium. Overwrite the\n", +" properties loaded with the -i option.\n", +"-r RADIUS \"radius\" argument of the lognormal distribution Default is "STR(RADIUS)".\n", +"-s SIGMA \"sigma\" argument of the lognormal distribution. Default is "STR(SIGMA)".\n", +"-w WAVELENGTH Wavelength to integrate.\n" + }; + (void)argc, (void)argv, (void)ctx; + return sbox_print_text(sbox, help, sizeof(help)/sizeof(const char*)); +} + diff --git a/src/sbox_schiff_sphere.h b/src/sbox_schiff_sphere.h @@ -0,0 +1,50 @@ +/* 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. */ + +#ifndef SBOX_SCHIFF_SPHERE_H +#define SBOX_SCHOFF_SPHERE_H + +#include <rsys/rsys.h> + +struct sbox; + +extern LOCAL_SYM res_T +cmd_func_schiff_sphere + (struct sbox* sbox, + const int argc, + char** argv, + void* ctx); + +extern LOCAL_SYM res_T +cmd_func_help_schiff_sphere + (struct sbox* sbox, + const int argc, + char** argv, + void* ctx); + +#endif /* SBOX_SCHIFF_SPHERE_H */