star-phor

Radiative transfer solver for photoreactors.
git clone https://www.edstar.cnrs.fr/git/star-phor.git
Log | Files | Refs | README | LICENSE

sphor_ran_source.c (8806B)


      1 /* Copyright (C) 2024-2026 Centre National de la Recherche Scientifique
      2  * Copyright (C) 2024-2026 Clermont Auvergne INP
      3  * Copyright (C) 2024-2026 INSA Lyon
      4  * Copyright (C) 2024-2026 Institut Mines Télécom Albi-Carmaux
      5  * Copyright (C) 2024-2026 Institut National Polytechnique de Toulouse
      6  * Copyright (C) 2024-2026 |Méso|Star> (contact@meso-star.com)
      7  * Copyright (C) 2024-2026 PhotonLyX (info@photonlyx.com)
      8  * Copyright (C) 2024-2026 Université de Lorraine
      9  * Copyright (C) 2024-2026 Université Paul Sabatier
     10  * Copyright (C) 2024-2026 Université Toulouse - Jean Jaurès
     11  *
     12  * This program is free software: you can redistribute it and/or modify
     13  * it under the terms of the GNU General Public License as published by
     14  * the Free Software Foundation, either version 3 of the License, or
     15  * (at your option) any later version.
     16  *
     17  * This program is distributed in the hope that it will be useful,
     18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     20  * GNU General Public License for more details.
     21  *
     22  * You should have received a copy of the GNU General Public License
     23  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     24 
     25 #include "sphor_c.h"
     26 #include "sphor_interface.h"
     27 #include "sphor_ran_geometry.h"
     28 #include "sphor_ran_source.h"
     29 #include "sphor_sources.h"
     30 
     31 #include <rsys/double3.h>
     32 #include <rsys/float3.h>
     33 #include <rsys/math.h>
     34 #include <rsys/rsys.h>
     35 #include <star/s3d.h>
     36 #include <star/sphin.h>
     37 #include <star/ssp.h>
     38 
     39 #include <math.h>
     40 
     41 static double*
     42 ran_hemisphere_cos_pow_n
     43   (struct ssp_rng* rng,
     44    double n,
     45    double normal[3],
     46    double sample[3],
     47    double* pdf)
     48 {
     49   double phi = 0;
     50   double tmp = 0;
     51   double cos_theta = 0;
     52   double sin_theta = 0;
     53   double basis[9] = {0};
     54   double sample_local[3] = {0};
     55 
     56   ASSERT(NULL != rng);
     57   ASSERT(NULL != normal);
     58   ASSERT(NULL != sample);
     59   ASSERT(d3_is_normalized(normal));
     60   ASSERT(0 <= n);
     61 
     62   phi = ssp_rng_uniform_double(rng, 0, 2 * PI);
     63   tmp = ssp_rng_canonical(rng);
     64 
     65   cos_theta = pow(tmp, 1 / (n + 2));
     66   sin_theta = sqrt(1- cos_theta * cos_theta);
     67 
     68   if(pdf) *pdf = (n+2) * pow(cos_theta, n + 1) / (2 * PI);
     69 
     70   sample_local[0] = sin_theta * cos(phi);
     71   sample_local[1] = sin_theta * sin(phi);
     72   sample_local[2] = cos_theta;
     73 
     74   return d33_muld3(sample, d33_basis(basis, normal), sample_local);
     75 }
     76 
     77 /*******************************************************************************
     78  * Local functions
     79  ******************************************************************************/
     80 res_T
     81 source_surface_sample_direction
     82   (struct sphor* sphor,
     83    struct ssp_rng* rng,
     84    struct primitive_pos* prim_pos,
     85    double dir[3])
     86 {
     87   struct sphin_source_surface* source = NULL;
     88   struct sphin_source_surface_direction_distribution src_dir_dist
     89     = SPHIN_SOURCE_SURFACE_DIRECTION_DISTRIBUTION_NULL;
     90   double normal[3] = {0};
     91   double sample[3] = {0};
     92   res_T res = RES_OK;
     93 
     94   res = primitive_get_source(sphor, &prim_pos->primitive, &source);
     95   if (RES_OK != res) { goto error; }
     96   if (NULL == source){ res = RES_BAD_ARG; goto error; }
     97 
     98   /* Sample a direction according to the source direction distribution */
     99   res = sphin_source_surface_get_direction_distribution(source, &src_dir_dist);
    100   if (RES_OK != res) { goto error; }
    101 
    102   /* Determine the effective emission normal.
    103    * Since scene geometry normals can be arbitrary, we use the pre-calculated
    104    * 'side' property of the primitive obtained during positiob sampling to
    105    * ensure the normal points into the emission hemisphere. */
    106   if (SPHIN_SIDE_FRONT == prim_pos->primitive.side) {
    107     /* Normal aligns with emission. Use as us */
    108     d3_set(normal, prim_pos->primitive.normal);
    109   } else if (SPHIN_SIDE_BACK == prim_pos->primitive.side) {
    110     /* Normal oposed to emission. Invert it */
    111     d3_minus(normal, prim_pos->primitive.normal);
    112   } else { res = RES_BAD_ARG; goto error; }
    113 
    114   switch (src_dir_dist.type) {
    115     case SPHIN_SOURCE_DIRECTION_COLLIM:
    116       d3_set(sample, normal);
    117       break;
    118     case SPHIN_SOURCE_DIRECTION_COS_POW_N:
    119       ran_hemisphere_cos_pow_n
    120         (rng, src_dir_dist.cos_pow_n.collimation_degree, normal, sample, NULL);
    121       break;
    122     case SPHIN_SOURCE_DIRECTION_ISOTROPIC:
    123       ssp_ran_hemisphere_cos(rng, normal, sample, NULL);
    124       break;
    125     case SPHIN_SOURCE_DIRECTION_NONE__:
    126       res = RES_BAD_ARG;
    127       goto error;
    128     default: FATAL("Unreachable code\n"); break;
    129   }
    130   d3_normalize(dir, sample);
    131 exit:
    132   return res;
    133 error:
    134   goto exit;
    135 }
    136 
    137 res_T
    138 sample_source_view
    139   (struct sphor* sphor,
    140    struct ssp_rng* rng,
    141    struct source_view** source_view)
    142 {
    143   struct source_view* source_views = NULL;
    144   size_t isource_view = 0;
    145 
    146   ASSERT(NULL != sphor);
    147   ASSERT(NULL != rng);
    148 
    149   isource_view = ssp_ranst_discrete_get(rng, sphor->source_distrib_power);
    150 
    151   source_views = darray_source_view_data_get(&sphor->source_views);
    152   *source_view = &source_views[isource_view];
    153 
    154   return RES_OK;
    155 }
    156 
    157 res_T
    158 sample_source_position
    159   (struct sphor* sphor,
    160    struct ssp_rng* rng,
    161    struct source_view** source_view,
    162    struct primitive_pos* position,
    163    double pos[3])
    164 {
    165   float st[2] = {0};
    166   size_t scn_prim_id;
    167   struct source_view* source_to_sample = NULL;
    168   struct s3d_primitive src_prim = S3D_PRIMITIVE_NULL;
    169   struct s3d_primitive scn_prim = S3D_PRIMITIVE_NULL;
    170   struct s3d_attrib src_normal;
    171   struct s3d_attrib scn_normal;
    172   struct s3d_attrib scn_pos;
    173   res_T res = RES_OK;
    174 
    175   ASSERT(NULL != sphor);
    176   ASSERT(NULL != rng);
    177 
    178   res = sample_source_view(sphor, rng, &source_to_sample);
    179   if (RES_OK != res) { goto error; }
    180 
    181   res = sample_surface_position
    182     (sphor, rng, source_to_sample->view, &src_prim, st);
    183   if (RES_OK != res) { goto error; }
    184 
    185   scn_prim_id = darray_size_t_data_get
    186     (&source_to_sample->src2scn)[src_prim.prim_id];
    187 
    188   res = s3d_scene_view_get_primitive
    189     (sphor->scene_view, (unsigned)scn_prim_id, &scn_prim);
    190   if (RES_OK != res) { goto error; }
    191 
    192   res = s3d_primitive_get_attrib
    193     (&src_prim, S3D_GEOMETRY_NORMAL, st, &src_normal);
    194   if (RES_OK != res) { goto error; }
    195   res = s3d_primitive_get_attrib
    196     (&scn_prim, S3D_GEOMETRY_NORMAL, st, &scn_normal);
    197   if (RES_OK != res) { goto error; }
    198 
    199   f3_normalize(src_normal.value, src_normal.value);
    200   f3_normalize(scn_normal.value, scn_normal.value);
    201 
    202   /* S3D and star-phor use different conventions.
    203    * Flip the normal in Star-Phor convention, i.e., right-hand rule */
    204   f3_minus(src_normal.value, src_normal.value);
    205   f3_minus(scn_normal.value, scn_normal.value);
    206 
    207   position->uv[0] = st[0];
    208   position->uv[1] = st[1];
    209   position->primitive.prim_id = scn_prim_id;
    210   position->primitive.interface =
    211       darray_interface_data_get(&sphor->interfaces) + scn_prim_id;
    212   position->primitive.normal[0] = scn_normal.value[0];
    213   position->primitive.normal[1] = scn_normal.value[1];
    214   position->primitive.normal[2] = scn_normal.value[2];
    215 
    216   /*  During setup, source geometry normals are explicitly oriented toward the
    217    * emission direction. Scene geometry normals, however, are arbitrarily
    218    * oriented based on initial triangle traversal.
    219    *
    220    * By calculating the dot product of these two normals, we determine if the
    221    * scene's face orientation aligns with the emission direction, allowing us
    222    * to label the emitting side */
    223 
    224   if (f3_dot(src_normal.value, scn_normal.value) > 0) {
    225     /* The scene normal points in the same direction of the source normal, that
    226      * points on the emission direction, i.e.,  */
    227     position->primitive.side = SPHIN_SIDE_FRONT;
    228   } else {
    229     position->primitive.side = SPHIN_SIDE_BACK;
    230   }
    231 
    232   res = s3d_primitive_get_attrib(&scn_prim, S3D_POSITION, st, &scn_pos);
    233   if (RES_OK != res) { goto error; }
    234 
    235   d3_set_f3(pos, scn_pos.value);
    236   *source_view = source_to_sample;
    237 exit:
    238   return res;
    239 error:
    240   goto exit;
    241 }
    242 
    243 res_T
    244 source_sample_wavelength
    245   (struct sphor* sphor,
    246    struct ssp_rng* rng,
    247    struct source_view* source_view,
    248    double* wavelength)
    249 {
    250   struct sphin_surface* surface = NULL;
    251   struct sphin_source_surface* source = NULL;
    252   struct sphin_source_surface_flux_density flux_density
    253     = SPHIN_SOURCE_SURFACE_FLUX_DENSITY_NULL;
    254   res_T res = RES_OK;
    255 
    256   ASSERT(NULL != sphor);
    257   ASSERT(NULL != rng);
    258   ASSERT(NULL != source_view);
    259 
    260   res = sphin_config_get_surface
    261     (sphor->config, source_view->sphin_id, &surface);
    262   if (RES_OK != res) { goto error; }
    263 
    264   res = sphin_surface_get_source(surface, &source);
    265   if (RES_OK != res) { goto error; }
    266   if (NULL == source){ res = RES_BAD_ARG; goto error; }
    267 
    268   res = sphin_source_surface_get_flux_density(source, &flux_density);
    269   if (RES_OK != res) { goto error; }
    270 
    271   if (NULL != flux_density.emission_spectrum) {
    272     *wavelength = ssp_ranst_piecewise_linear_get
    273       (source_view->emission_spectrum_pdf, rng);
    274   }
    275 exit:
    276   return res;
    277 error:
    278   goto exit;
    279 }