solstice-solver

Solver library of the solstice app
git clone git://git.meso-star.com/solstice-solver.git
Log | Files | Refs | README | LICENSE

commit a97269bdcdb8fc0c1f51f61a39a535ceed7c1018
parent 5f442b20030f7c3ee7bae09f4fa14069d49c06ab
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri,  8 Jul 2016 11:21:39 +0200

Add first statistical distributions, used for the sun.

Diffstat:
Mcmake/CMakeLists.txt | 9++++++---
Asrc/ssol_distributions.c | 338+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ssol_distributions_c.h | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 410 insertions(+), 3 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -26,6 +26,7 @@ option(NO_TEST "Do not build tests" OFF) find_package(RCMake 0.2.3 REQUIRED) find_package(RSys 0.3 REQUIRED) find_package(Star3D 0.3 REQUIRED) +find_package(StarSP 0.4 REQUIRED) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) include(rcmake) @@ -48,6 +49,7 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SSOL_FILES_SRC ssol_device.c + ssol_distributions.c ssol_image.c ssol_material.c ssol_object.c @@ -63,6 +65,7 @@ set(SSOL_FILES_INC_API set(SSOL_FILES_INC ssol_device_c.h + ssol_distributions_c.h ssol_image_c.h ssol_material_c.h ssol_object_c.h @@ -82,7 +85,7 @@ rcmake_prepend_path(SSOL_FILES_INC_API ${SSOL_SOURCE_DIR}) rcmake_prepend_path(SSOL_FILES_DOC ${PROJECT_SOURCE_DIR}/../) add_library(solstice-solver SHARED ${SSOL_FILES_SRC} ${SSOL_FILES_INC} ${SSOL_FILES_INC_API}) -target_link_libraries(solstice-solver RSys Star3D) +target_link_libraries(solstice-solver RSys Star3D StarSP) if(CMAKE_COMPILER_IS_GNUCC) target_link_libraries(solstice-solver m) @@ -103,7 +106,7 @@ if(NOT NO_TEST) add_executable(${_name} ${SSOL_SOURCE_DIR}/test_ssol_utils.h ${SSOL_SOURCE_DIR}/${_name}.c) - target_link_libraries(${_name} solstice-solver RSys Star3D) + target_link_libraries(${_name} solstice-solver RSys Star3D StarSP) set(_libraries ${ARGN}) foreach(_lib ${_libraries}) target_link_libraries(${_name} ${_lib}) @@ -119,7 +122,7 @@ if(NOT NO_TEST) build_test(${_name} ${ARGN}) register_test(${_name} ${_name}) endfunction() - + new_test(test_ssol_device) new_test(test_ssol_image) new_test(test_ssol_material) diff --git a/src/ssol_distributions.c b/src/ssol_distributions.c @@ -0,0 +1,337 @@ +/* Copyright (C) CNRS 2016 +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "ssol.h" +#include "ssol_distributions_c.h" + +#include <star/ssp.h> + +#include <rsys/rsys.h> +#include <rsys/ref_count.h> +#include <rsys/mem_allocator.h> +#include <rsys/math.h> +#include <rsys/double33.h> + +/******************************************************************************* +* distributions types +******************************************************************************/ + +struct ran_buie_state +{ + double thetaSD; + double deltaThetaCSSD; + double gamma; + double etokTimes1000toGamma; + double alpha; + double hRect1; + double hRect2; + double probaRect1; + double basis[9]; +}; + +struct ran_pillbox_state +{ + double radius; + double basis[9]; +}; + +struct ran_dirac_state +{ + double dir[3]; +}; + +/* one single type for all distributions +* only the state type depends on the distribution type */ +struct ssol_ran_sun_dir { + double*(*get)(const struct ssol_ran_sun_dir* ran, struct ssp_rng* rng, double dir[3]); + ref_T ref; + struct mem_allocator* allocator; + union { + struct ran_buie_state buie; + struct ran_pillbox_state pillbox; + struct ran_dirac_state dirac; + } state; +}; + +/******************************************************************************* +* sun direction distribution +******************************************************************************/ + +static void +distrib_sun_release(ref_T* ref) +{ + struct ssol_ran_sun_dir* ran; + ASSERT(ref); + ran = CONTAINER_OF(ref, struct ssol_ran_sun_dir, ref); + MEM_RM(ran->allocator, ran); +} + +res_T +ssol_ran_sun_dir_create + (struct mem_allocator* allocator, + struct ssol_ran_sun_dir** out_ran) +{ + struct ssol_ran_sun_dir* ran = NULL; + + if (!out_ran) return RES_BAD_ARG; + + allocator = allocator ? allocator : &mem_default_allocator; + + ran = MEM_CALLOC(allocator, 1, sizeof(struct ssol_ran_sun_dir)); + if (!ran) return RES_MEM_ERR; + + ref_init(&ran->ref); + ran->allocator = allocator; + *out_ran = ran; + + return RES_OK; +} + +res_T +ssol_ran_sun_dir_ref_get + (struct ssol_ran_sun_dir* ran) +{ + if (!ran) return RES_BAD_ARG; + ref_get(&ran->ref); + return RES_OK; +} + +res_T +ssol_ran_sun_dir_ref_put + (struct ssol_ran_sun_dir* ran) +{ + if (!ran) return RES_BAD_ARG; + ref_put(&ran->ref, distrib_sun_release); + return RES_OK; +} + +double* +ssol_ran_sun_dir_get + (const struct ssol_ran_sun_dir* ran, + struct ssp_rng* rng, + double dir[3]) +{ + return ran->get(ran, rng, dir); +} + +/******************************************************************************* +* Buie distribution +******************************************************************************/ + +double chiValue(double csr) +{ + if (csr > 0.145) + return -0.04419909985804843 + csr * (1.401323894233574 + csr * (-0.3639746714505299 + csr * (-0.9579768560161194 + 1.1550475450828657 * csr))); + + if (csr > 0.035) + return 0.022652077593662934 + csr * (0.5252380349996234 + (2.5484334534423887 - 0.8763755326550412 * csr) * csr); + + return 0.004733749294807862 + csr * (4.716738065192151 + csr * (-463.506669149804 + csr * (24745.88727411664 + csr * (-606122.7511711778 + 5521693.445014727 * csr)))); +} + +double phiSolarDisk(double theta) +{ + // The parameter theta is the zenith angle in radians + return cos(326 * theta) / cos(308 * theta); +} + +double phiCircumSolarRegion(double theta, const struct ran_buie_state* state) +{ + // The parameter theta is the zenith angle in radians + return state->etokTimes1000toGamma * pow(theta, state->gamma); +} + +double phi(double theta, const struct ran_buie_state* state) +{ + // The parameter theta is the zenith angle in radians + if (theta < state->thetaSD) return phiSolarDisk(theta); + else return phiCircumSolarRegion(theta, state); +} + +double pdfTheta(double theta, const struct ran_buie_state* state) +{ + // The parameter theta is the zenith angle in radians + return state->alpha * phi(theta, state) * sin(theta); +} + +double gammaValue(double chi) +{ + /* gamma is the gradient of the 2nd part of the curve in log/log space*/ + return 2.2 * log(0.52 * chi) * pow(chi, 0.43) - 0.1; +} + +double kValue(double chi) +{ + /* k is the intercept of the 2nd part of the + curve at an angular displacement of zero */ + return 0.9 * log(13.5 * chi) * pow(chi, -0.3); +} + +double intregralB(double k, double gamma, double thetaCS, double thetaSD) +{ + double g2 = gamma + 2.0; + return exp(k) * pow(1000, gamma) / g2 * (pow(thetaCS, g2) - pow(thetaSD, g2)); +} + +double probaRect1(double widthR1, double heightR1, double widthR2, double heightR2) +{ + double areaR1 = widthR1 * heightR1; + double areaR2 = widthR2 * heightR2; + return areaR1 / (areaR1 + areaR2); +} + +double zenithAngle(struct ssp_rng* rng, const struct ran_buie_state* state) +{ + double theta; + double value; + + do { + if (ssp_rng_canonical(rng) < state->probaRect1) { + theta = ssp_rng_canonical(rng) * state->thetaSD; + value = ssp_rng_canonical(rng) * state->hRect1; + } + else { + theta = state->thetaSD + ssp_rng_canonical(rng) * state->deltaThetaCSSD; + value = ssp_rng_canonical(rng) * state->hRect2; + } + } while (value > pdfTheta(theta, state)); + + return theta; +} + +void fill_buie_state(struct ran_buie_state* s, double p) { + double thetaCS, integralA, chi, k, integralB; + ASSERT(s); + ASSERT(0 <= p && p < 1); + s->thetaSD = 0.00465; + thetaCS = 0.0436; + s->deltaThetaCSSD = thetaCS - s->thetaSD; + integralA = 9.224724736098827E-6; + chi = chiValue(p); + k = kValue(chi); + s->gamma = gammaValue(chi); + s->etokTimes1000toGamma = exp(k) * pow(1000, s->gamma); + integralB = intregralB(k, s->gamma, thetaCS, s->thetaSD); + s->alpha = 1 / (integralA + integralB); + /* + The value 0.0038915695846209047 radians is the value for which the + probability density function is a maximum. The value of the maximum + varies with the circumsolar ratio, but the value of theta where the + maximum is obtained remains fixed. + */ + s->hRect1 = 1.001 * pdfTheta(0.0038915695846209047, s); + s->hRect2 = pdfTheta(s->thetaSD, s); + s->probaRect1 = probaRect1(s->thetaSD, s->hRect1, s->deltaThetaCSSD, s->hRect2); +} + +double* +ssol_ran_buie_get + (const struct ssol_ran_sun_dir* ran, struct ssp_rng* rng, double dir[3]) +{ + double phi, theta, sinTheta, cosTheta, cosPhi, sinPhi; + ASSERT(ran->state.buie.thetaSD > 0); + phi = ssp_rng_uniform_double(rng, 0, 2 * PI); + theta = zenithAngle(rng, &ran->state.buie); + sinTheta = sin(theta); + cosTheta = cos(theta); + cosPhi = cos(phi); + sinPhi = sin(phi); + dir[0] = sinTheta * sinPhi; + dir[1] = -cosTheta; + dir[2] = sinTheta * cosPhi; + d33_muld3(dir, ran->state.buie.basis, dir); + return dir; +} + +res_T +ssol_ran_sun_dir_buie_setup + (struct ssol_ran_sun_dir* ran, + double param, + const double dir[3]) +{ + const double minCRSValue = 1E-6; + const double maxCRSValue = 0.849; + if (!ran || !dir || param < minCRSValue || param > maxCRSValue) + return RES_BAD_ARG; + + ran->get = ssol_ran_buie_get; + d33_basis(ran->state.buie.basis, dir); + fill_buie_state(&ran->state.buie, param); + return RES_OK; +} + +/******************************************************************************* +* Pillbox distribution +******************************************************************************/ + +double* +ssol_ran_pillbox_get +(const struct ssol_ran_sun_dir* ran, + struct ssp_rng* rng, + double dir[3]) +{ + double pt[3]; + ASSERT(ran->state.pillbox.radius > 0); + ssp_ran_uniform_disk(rng, ran->state.pillbox.radius, pt); + pt[2] = 1; + d33_muld3(dir, ran->state.pillbox.basis, pt); + d3_normalize(dir, pt); + return dir; +} + +res_T +ssol_ran_sun_dir_pillbox_setup + (struct ssol_ran_sun_dir* ran, + double aperture, + const double dir[3]) +{ + double radius; + if (!ran || !dir || aperture <= 0 || aperture >= PI ) + return RES_BAD_ARG; + radius = tan(0.5 * aperture); + ran->get = ssol_ran_pillbox_get; + ran->state.pillbox.radius = radius; + d33_basis(ran->state.pillbox.basis, dir); + return RES_OK; +} + +/******************************************************************************* +* Dirac distribution +******************************************************************************/ + +double* +ssol_ran_dirac_get +(const struct ssol_ran_sun_dir* ran, + struct ssp_rng* rng, + double dir[3]) +{ + (void) rng; + ASSERT(d3_is_normalized(ran->state.dirac.dir)); + d3_set(dir, ran->state.dirac.dir); + return dir; +} + +res_T +ssol_ran_sun_dir_dirac_setup + (struct ssol_ran_sun_dir* ran, + const double dir[3]) +{ + if (!ran || !dir) return RES_BAD_ARG; + if (0 == d3_normalize(ran->state.dirac.dir, dir)) + /* zero vector */ + return RES_BAD_ARG; + ran->get = ssol_ran_dirac_get; + return RES_OK; +} +\ No newline at end of file diff --git a/src/ssol_distributions_c.h b/src/ssol_distributions_c.h @@ -0,0 +1,66 @@ +/* Copyright (C) CNRS 2016 +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef SSOL_DISTRIBUTIONS_C_H +#define SSOL_DISTRIBUTIONS_C_H + +/******************************************************************************* +* Sun distributions +******************************************************************************/ + +/* Forward declaration of opaque types */ +struct ssol_ran_sun_dir; + +/* Forward declaration of external types */ +struct ssp_rng; +struct mem_allocator; + +res_T +ssol_ran_sun_dir_create + (struct mem_allocator* allocator, + struct ssol_ran_sun_dir** ran); + +res_T +ssol_ran_sun_dir_buie_setup + (struct ssol_ran_sun_dir* ran, + double param, + const double dir[3]); + +res_T +ssol_ran_sun_dir_pillbox_setup +(struct ssol_ran_sun_dir* ran, + double aperture, /* apparent size in radians */ + const double dir[3]); + +res_T +ssol_ran_sun_dir_dirac_setup +(struct ssol_ran_sun_dir* ran, + const double dir[3]); + +res_T +ssol_ran_sun_dir_ref_get + (struct ssol_ran_sun_dir* ran); + +res_T +ssol_ran_sun_dir_ref_put + (struct ssol_ran_sun_dir* ran); + +double* +ssol_ran_sun_dir_get + (const struct ssol_ran_sun_dir* ran, + struct ssp_rng* rng, + double dir[3]); + +#endif /* SSOL_DISTRIBUTIONS_C_H */