stardis-solver

Solve coupled heat transfers
git clone git://git.meso-star.fr/stardis-solver.git
Log | Files | Refs | README | LICENSE

sdis_scene.c (17766B)


      1 /* Copyright (C) 2016-2025 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #include "sdis.h"
     17 #include "sdis_interface_c.h"
     18 #include "sdis_scene_c.h"
     19 #include "sdis_source_c.h"
     20 
     21 #include <float.h>
     22 #include <limits.h>
     23 
     24 /* Generate the Generic functions of the scene */
     25 #define SDIS_XD_DIMENSION 2
     26 #include "sdis_scene_Xd.h"
     27 #define SDIS_XD_DIMENSION 3
     28 #include "sdis_scene_Xd.h"
     29 
     30 /*******************************************************************************
     31  * Helper function
     32  ******************************************************************************/
     33 static void
     34 project_position
     35   (const double V0[3],
     36    const double E0[3],
     37    const double N[3],
     38    const double NxE1[3],
     39    const double rcp_det,
     40    const double pos[3],
     41    double uvw[3])
     42 {
     43   double T[3], Q[3], k;
     44   ASSERT(V0 && E0 && N && NxE1 && pos && uvw);
     45 
     46   /* Use Moller/Trumbore intersection test the compute the parametric
     47    * coordinates of the intersection between the triangle and the ray
     48    * `r = pos + N*d' */
     49   d3_sub(T, pos, V0);
     50   uvw[0] = d3_dot(T, NxE1) * rcp_det;
     51   d3_cross(Q, T, E0);
     52   uvw[1] = d3_dot(Q, N) * rcp_det;
     53   uvw[2] = 1.0 - uvw[0] - uvw[1];
     54 
     55   if(uvw[0] >= 0 && uvw[1] >= 0 && uvw[2] >= 0) {/* The ray hits the triangle */
     56     ASSERT(eq_eps(uvw[0] + uvw[1] + uvw[2], 1.0, 1.e-6));
     57     return;
     58   }
     59 
     60   /* Clamp barycentric coordinates to triangle edges */
     61   if(uvw[0] >= 0) {
     62     if(uvw[1] >= 0) {
     63       k = 1.0 / (uvw[0] + uvw[1]);
     64       uvw[0] *= k;
     65       uvw[1] *= k;
     66       uvw[2] = 0;
     67     } else if( uvw[2] >= 0) {
     68       k = 1.0 / (uvw[0] + uvw[2]);
     69       uvw[0] *= k;
     70       uvw[1] = 0;
     71       uvw[2] *= k;
     72     } else {
     73       ASSERT(uvw[0] >= 1.f);
     74       d3(uvw, 1, 0, 0);
     75     }
     76   } else if(uvw[1] >= 0) {
     77     if(uvw[2] >= 0) {
     78       k = 1.0 / (uvw[1] + uvw[2]);
     79       uvw[0] = 0;
     80       uvw[1] *= k;
     81       uvw[2] *= k;
     82     } else {
     83       ASSERT(uvw[1] >= 1);
     84       d3(uvw, 0, 1, 0);
     85     }
     86   } else {
     87     ASSERT(uvw[2] >= 1);
     88     d3(uvw, 0, 0, 1);
     89   }
     90 }
     91 
     92 static void
     93 scene_release(ref_T * ref)
     94 {
     95   struct sdis_device* dev = NULL;
     96   struct sdis_scene* scn = NULL;
     97   ASSERT(ref);
     98   scn = CONTAINER_OF(ref, struct sdis_scene, ref);
     99   dev = scn->dev;
    100   clear_properties(scn);
    101   darray_interf_release(&scn->interfaces);
    102   darray_medium_release(&scn->media);
    103   darray_prim_prop_release(&scn->prim_props);
    104   htable_enclosure_release(&scn->enclosures);
    105   htable_d_release(&scn->tmp_hc_ub);
    106   htable_key2prim2d_release(&scn->key2prim2d);
    107   htable_key2prim3d_release(&scn->key2prim3d);
    108   if(scn->s2d_view) S2D(scene_view_ref_put(scn->s2d_view));
    109   if(scn->s3d_view) S3D(scene_view_ref_put(scn->s3d_view));
    110   if(scn->senc2d_scn) SENC2D(scene_ref_put(scn->senc2d_scn));
    111   if(scn->senc3d_scn) SENC3D(scene_ref_put(scn->senc3d_scn));
    112   if(scn->source) SDIS(source_ref_put(scn->source));
    113   if(scn->radenv) SDIS(radiative_env_ref_put(scn->radenv));
    114   MEM_RM(dev->allocator, scn);
    115   SDIS(device_ref_put(dev));
    116 }
    117 
    118 /*******************************************************************************
    119  * Exported functions
    120  ******************************************************************************/
    121 res_T
    122 sdis_scene_create
    123   (struct sdis_device* dev,
    124    const struct sdis_scene_create_args* args,
    125    struct sdis_scene** out_scn)
    126 {
    127   return scene_create_3d(dev, args, out_scn);
    128 }
    129 
    130 res_T
    131 sdis_scene_2d_create
    132   (struct sdis_device* dev,
    133    const struct sdis_scene_create_args* args,
    134    struct sdis_scene** out_scn)
    135 {
    136   return scene_create_2d(dev, args, out_scn);
    137 }
    138 
    139 res_T
    140 sdis_scene_ref_get(struct sdis_scene* scn)
    141 {
    142   if(!scn) return RES_BAD_ARG;
    143   ref_get(&scn->ref);
    144   return RES_OK;
    145 }
    146 
    147 res_T
    148 sdis_scene_ref_put(struct sdis_scene* scn)
    149 {
    150   if(!scn) return RES_BAD_ARG;
    151   ref_put(&scn->ref, scene_release);
    152   return RES_OK;
    153 }
    154 
    155 res_T
    156 sdis_scene_get_aabb
    157   (const struct sdis_scene* scn,
    158    double lower[],
    159    double upper[])
    160 {
    161   float low[3], upp[3];
    162   res_T res = RES_OK;
    163   if(!scn || !lower || !upper) return RES_BAD_ARG;
    164 
    165   if(scene_is_2d(scn)) {
    166     res = s2d_scene_view_get_aabb(scn->s2d_view, low, upp);
    167     if(res != RES_OK) return res;
    168     d2_set_f2(lower, low);
    169     d2_set_f2(upper, upp);
    170   } else {
    171     res = s3d_scene_view_get_aabb(scn->s3d_view, low, upp);
    172     if(res != RES_OK) return res;
    173     d3_set_f3(lower, low);
    174     d3_set_f3(upper, upp);
    175   }
    176   return RES_OK;
    177 }
    178 
    179 res_T
    180 sdis_scene_get_fp_to_meter
    181   (const struct sdis_scene* scn,
    182    double* fp_to_meter)
    183 {
    184   if(!scn || !fp_to_meter) return RES_BAD_ARG;
    185   *fp_to_meter = scn->fp_to_meter;
    186   return RES_OK;
    187 }
    188 
    189 res_T
    190 sdis_scene_set_fp_to_meter
    191   (struct sdis_scene* scn,
    192    const double fp_to_meter)
    193 {
    194   if(!scn || fp_to_meter <= 0) return RES_BAD_ARG;
    195   scn->fp_to_meter = fp_to_meter;
    196   return RES_OK;
    197 }
    198 
    199 res_T
    200 sdis_scene_get_temperature_range
    201   (const struct sdis_scene* scn,
    202    double t_range[2])
    203 {
    204   if(!scn || !t_range) return RES_BAD_ARG;
    205   t_range[0]  = scn->tmin;
    206   t_range[1]  = scn->tmax;
    207   return RES_OK;
    208 }
    209 
    210 res_T
    211 sdis_scene_set_temperature_range
    212   (struct sdis_scene* scn,
    213    const double t_range[2])
    214 {
    215   if(!scn || !t_range) return RES_BAD_ARG;
    216   scn->tmin = t_range[0];
    217   scn->tmax = t_range[1];
    218   return RES_OK;
    219 }
    220 
    221 res_T
    222 sdis_scene_find_closest_point
    223   (const struct sdis_scene* scn,
    224    const struct sdis_scene_find_closest_point_args* args,
    225    size_t* iprim,
    226    double uv[])
    227 {
    228   if(!scn) return RES_BAD_ARG;
    229   if(scene_is_2d(scn)) {
    230     return scene_find_closest_point_2d(scn, args, iprim, uv);
    231   } else {
    232     return scene_find_closest_point_3d(scn, args, iprim, uv);
    233   }
    234 }
    235 
    236 res_T
    237 sdis_scene_get_boundary_position
    238   (const struct sdis_scene* scn,
    239    const size_t iprim,
    240    const double uv[],
    241    double pos[])
    242 {
    243   if(!scn || !uv || !pos) return RES_BAD_ARG;
    244   if(iprim >= scene_get_primitives_count(scn)) return RES_BAD_ARG;
    245 
    246   if(scene_is_2d(scn)) {
    247     struct s2d_primitive prim;
    248     struct s2d_attrib attr;
    249     float s = (float)uv[0];
    250 
    251     S2D(scene_view_get_primitive(scn->s2d_view, (unsigned int)iprim, &prim));
    252     S2D(primitive_get_attrib(&prim, S2D_POSITION, s, &attr));
    253     d2_set_f2(pos, attr.value);
    254   } else {
    255     struct s3d_primitive prim;
    256     struct s3d_attrib attr;
    257     float st[2];
    258 
    259     f2_set_d2(st, uv);
    260     S3D(scene_view_get_primitive(scn->s3d_view, (unsigned int)iprim, &prim));
    261     S3D(primitive_get_attrib(&prim, S3D_POSITION, st, &attr));
    262     d3_set_f3(pos, attr.value);
    263   }
    264   return RES_OK;
    265 }
    266 
    267 res_T
    268 sdis_scene_boundary_project_position
    269   (const struct sdis_scene* scn,
    270    const size_t iprim,
    271    const double pos[],
    272    double uv[])
    273 {
    274   if(!scn || !pos || !uv) return RES_BAD_ARG;
    275   if(iprim >= scene_get_primitives_count(scn)) return RES_BAD_ARG;
    276 
    277   if(scene_is_2d(scn)) {
    278     struct s2d_primitive prim;
    279     struct s2d_attrib a;
    280     double V[2][2]; /* Vertices */
    281     double E[2][3]; /* V0->V1 and V0->pos */
    282     double proj;
    283 
    284     /* Retrieve the segment vertices */
    285     S2D(scene_view_get_primitive(scn->s2d_view, (unsigned int)iprim, &prim));
    286     S2D(segment_get_vertex_attrib(&prim, 0, S2D_POSITION, &a)); d2_set_f2(V[0], a.value);
    287     S2D(segment_get_vertex_attrib(&prim, 1, S2D_POSITION, &a)); d2_set_f2(V[1], a.value);
    288 
    289     /* Compute the parametric coordinate of the project of `pos' onto the
    290      * segment.*/
    291     d2_sub(E[0], V[1], V[0]);
    292     d2_normalize(E[0], E[0]);
    293     d2_sub(E[1], pos,  V[0]);
    294     proj = d2_dot(E[0], E[1]);
    295 
    296     uv[0] = CLAMP(proj, 0, 1); /* Clamp the parametric coordinate in [0, 1] */
    297 
    298   } else {
    299     struct s3d_primitive prim;
    300     struct s3d_attrib a;
    301     double V[3][3]; /* Vertices */
    302     double E[2][3]; /* V0->V1 and V0->V2 edges */
    303     double N[3]; /* Normal */
    304     double NxE1[3], rcp_det; /* Muller/Trumboer triangle parameters */
    305     double uvw[3];
    306 
    307     S3D(scene_view_get_primitive(scn->s3d_view, (unsigned int)iprim, &prim));
    308     S3D(triangle_get_vertex_attrib(&prim, 0, S3D_POSITION, &a)); d3_set_f3(V[0], a.value);
    309     S3D(triangle_get_vertex_attrib(&prim, 1, S3D_POSITION, &a)); d3_set_f3(V[1], a.value);
    310     S3D(triangle_get_vertex_attrib(&prim, 2, S3D_POSITION, &a)); d3_set_f3(V[2], a.value);
    311     d3_sub(E[0], V[1], V[0]);
    312     d3_sub(E[1], V[2], V[0]);
    313     d3_cross(N, E[0], E[1]);
    314 
    315     /* Muller/Trumbore triangle parameters */
    316     d3_cross(NxE1, N, E[1]);
    317     rcp_det = 1.0 / d3_dot(NxE1, E[0]);
    318 
    319     /* Use the Muller/Trumbore intersection test to project `pos' onto the
    320      * triangle and to retrieve the parametric coordinates of the projection
    321      * point */
    322     project_position(V[0], E[0], N, NxE1, rcp_det, pos, uvw);
    323 
    324     uv[0] = uvw[2];
    325     uv[1] = uvw[0];
    326   }
    327   return RES_OK;
    328 }
    329 
    330 res_T
    331 sdis_scene_get_senc2d_scene
    332   (struct sdis_scene* scn,
    333    struct senc2d_scene** senc2d_scn)
    334 {
    335   if(!scn || !senc2d_scn) return RES_BAD_ARG;
    336   if(!scn->senc2d_scn) return RES_BAD_ARG; /* Scene is 3D */
    337   *senc2d_scn = scn->senc2d_scn;
    338   return RES_OK;
    339 }
    340 
    341 res_T
    342 sdis_scene_get_senc3d_scene
    343   (struct sdis_scene* scn,
    344    struct senc3d_scene** senc3d_scn)
    345 {
    346   if(!scn || !senc3d_scn) return RES_BAD_ARG;
    347   if(!scn->senc3d_scn) return RES_BAD_ARG; /* Scene is 2D */
    348   *senc3d_scn = scn->senc3d_scn;
    349   return RES_OK;
    350 }
    351 
    352 res_T
    353 sdis_scene_get_s2d_scene_view
    354   (struct sdis_scene* scn,
    355    struct s2d_scene_view** s2d_view)
    356 {
    357   if(!scn || !s2d_view) return RES_BAD_ARG;
    358   if(!scn->s2d_view) return RES_BAD_ARG; /* Scene is 3D */
    359   *s2d_view = scn->s2d_view;
    360   return RES_OK;
    361 }
    362 
    363 res_T
    364 sdis_scene_get_s3d_scene_view
    365   (struct sdis_scene* scn,
    366    struct s3d_scene_view** s3d_view)
    367 {
    368   if(!scn || !s3d_view) return RES_BAD_ARG;
    369   if(!scn->s3d_view) return RES_BAD_ARG; /* Scene is 2D */
    370   *s3d_view = scn->s3d_view;
    371   return RES_OK;
    372 }
    373 
    374 res_T
    375 sdis_scene_get_dimension
    376   (const struct sdis_scene* scn, enum sdis_scene_dimension* dim)
    377 {
    378   if(!scn || !dim) return RES_BAD_ARG;
    379   *dim = scene_is_2d(scn) ? SDIS_SCENE_2D : SDIS_SCENE_3D;
    380   return RES_OK;
    381 }
    382 
    383 res_T
    384 sdis_scene_get_medium_spread
    385   (struct sdis_scene* scn,
    386    const struct sdis_medium* mdm,
    387    double* out_spread)
    388 {
    389   struct htable_enclosure_iterator it, end;
    390   double spread = 0;
    391   res_T res = RES_OK;
    392 
    393   if(!scn || !mdm || !out_spread) {
    394     res = RES_BAD_ARG;
    395     goto error;
    396   }
    397 
    398   htable_enclosure_begin(&scn->enclosures, &it);
    399   htable_enclosure_end(&scn->enclosures, &end);
    400   while(!htable_enclosure_iterator_eq(&it, &end)) {
    401     const struct enclosure* enc = htable_enclosure_iterator_data_get(&it);
    402     htable_enclosure_iterator_next(&it);
    403     if(sdis_medium_get_id(mdm) == enc->medium_id) {
    404       spread += enc->V;
    405     }
    406   }
    407   *out_spread = spread;
    408 
    409 exit:
    410   return res;
    411 error:
    412   goto exit;
    413 }
    414 
    415 res_T
    416 sdis_scene_get_device(struct sdis_scene* scn, struct sdis_device** device)
    417 {
    418   if(!scn || !device) return RES_BAD_ARG;
    419   *device = scn->dev;
    420   return RES_OK;
    421 }
    422 
    423 res_T
    424 sdis_scene_get_source(struct sdis_scene* scn, struct sdis_source** source)
    425 {
    426   if(!scn || !source) return RES_BAD_ARG;
    427   *source = scn->source;
    428   return RES_OK;
    429 }
    430 
    431 res_T
    432 sdis_scene_get_radiative_env
    433   (struct sdis_scene* scn,
    434    struct sdis_radiative_env** radenv)
    435 {
    436   if(!scn || !radenv) return RES_BAD_ARG;
    437   *radenv = scn->radenv;
    438   return RES_OK;
    439 }
    440 
    441 res_T
    442 sdis_scene_get_s2d_primitive
    443   (struct sdis_scene* scn,
    444    const struct sdis_primkey* key,
    445    struct s2d_primitive* out_prim)
    446 {
    447   struct s2d_primitive* prim = NULL;
    448 
    449   if(!scn || !key || !out_prim || !scene_is_2d(scn)) return RES_BAD_ARG;
    450 
    451   if((prim = htable_key2prim2d_find(&scn->key2prim2d, key)) == NULL)
    452     return RES_BAD_ARG;
    453   *out_prim = *prim;
    454   return RES_OK;
    455 }
    456 
    457 res_T
    458 sdis_scene_get_s3d_primitive
    459   (struct sdis_scene* scn,
    460    const struct sdis_primkey* key,
    461    struct s3d_primitive* out_prim)
    462 {
    463   struct s3d_primitive* prim = NULL;
    464 
    465   if(!scn || !key || !out_prim || scene_is_2d(scn)) return RES_BAD_ARG;
    466 
    467   if((prim = htable_key2prim3d_find(&scn->key2prim3d, key)) == NULL)
    468     return RES_BAD_ARG;
    469   *out_prim = *prim;
    470   return RES_OK;
    471 }
    472 
    473 /*******************************************************************************
    474  * Local miscellaneous function
    475  ******************************************************************************/
    476 struct sdis_interface*
    477 scene_get_interface(const struct sdis_scene* scn, const unsigned iprim)
    478 {
    479   ASSERT(scn && iprim < darray_prim_prop_size_get(&scn->prim_props));
    480   return darray_prim_prop_cdata_get(&scn->prim_props)[iprim].interf;
    481 }
    482 
    483 res_T
    484 scene_get_enclosure_id
    485   (struct sdis_scene* scn,
    486    const double pos[],
    487    unsigned* enc_id)
    488 {
    489   return scene_is_2d(scn)
    490     ? scene_get_enclosure_id_2d(scn, pos, enc_id)
    491     : scene_get_enclosure_id_3d(scn, pos, enc_id);
    492 }
    493 
    494 res_T
    495 scene_get_enclosure_id_in_closed_boundaries
    496   (struct sdis_scene* scn,
    497    const double pos[],
    498    unsigned* enc_id)
    499 {
    500   return scene_is_2d(scn)
    501     ? scene_get_enclosure_id_in_closed_boundaries_2d(scn, pos, enc_id)
    502     : scene_get_enclosure_id_in_closed_boundaries_3d(scn, pos, enc_id);
    503 }
    504 
    505 res_T
    506 scene_get_enclosure_medium
    507   (struct sdis_scene* scn,
    508    const struct enclosure* enc,
    509    struct sdis_medium** out_mdm)
    510 {
    511   struct sdis_medium* mdm = NULL;
    512   res_T res = RES_OK;
    513 
    514   ASSERT(scn && enc && out_mdm);
    515 
    516   /* Check that the enclosure doesn't surround multiple media */
    517   if(enc->medium_id == MEDIUM_ID_MULTI) {
    518     log_warn(scn->dev,
    519        "%s: invalid medium request. The enclosure includes several media.\n",
    520        FUNC_NAME);
    521     res = RES_BAD_OP;
    522     goto error;
    523   }
    524 
    525   /* Obtain enclosure medium */
    526   ASSERT(enc->medium_id < darray_medium_size_get(&scn->media));
    527   mdm = darray_medium_data_get(&scn->media)[enc->medium_id];
    528 
    529 error:
    530   *out_mdm = mdm;
    531   goto exit;
    532 exit:
    533   mdm = NULL;
    534   return res;
    535 }
    536 
    537 res_T
    538 scene_compute_hash(const struct sdis_scene* scn, hash256_T hash)
    539 {
    540   struct sha256_ctx sha256_ctx;
    541   size_t iprim, nprims;
    542   int has_radenv = 0;
    543   res_T res = RES_OK;
    544   ASSERT(scn && hash);
    545 
    546   sha256_ctx_init(&sha256_ctx);
    547 
    548   if(scene_is_2d(scn)) {
    549     S2D(scene_view_primitives_count(scn->s2d_view, &nprims));
    550   } else {
    551     S3D(scene_view_primitives_count(scn->s3d_view, &nprims));
    552   }
    553   #define SHA256_UPD(Var, Nb) \
    554     sha256_ctx_update(&sha256_ctx, (const char*)(Var), sizeof(*Var)*(Nb))
    555 
    556   has_radenv = scn->radenv != NULL;
    557 
    558   SHA256_UPD(&has_radenv, 1);
    559   SHA256_UPD(&scn->tmax, 1);
    560   SHA256_UPD(&scn->fp_to_meter, 1);
    561 
    562   if(scn->source) {
    563     hash256_T src_hash;
    564     source_compute_signature(scn->source, src_hash);
    565     sha256_ctx_update(&sha256_ctx, src_hash, sizeof(hash256_T));
    566   }
    567 
    568   FOR_EACH(iprim, 0, nprims) {
    569     struct sdis_interface* interf = NULL;
    570     size_t ivert;
    571 
    572     if(scene_is_2d(scn)) {
    573       struct s2d_primitive prim;
    574       S2D(scene_view_get_primitive(scn->s2d_view, (unsigned)iprim, &prim));
    575       FOR_EACH(ivert, 0, 2) {
    576         struct s2d_attrib attr;
    577         S2D(segment_get_vertex_attrib(&prim, ivert, S2D_POSITION, &attr));
    578         SHA256_UPD(attr.value, 2);
    579       }
    580     } else {
    581       struct s3d_primitive prim;
    582       S3D(scene_view_get_primitive(scn->s3d_view, (unsigned)iprim, &prim));
    583       FOR_EACH(ivert, 0, 3) {
    584         struct s3d_attrib attr;
    585         S3D(triangle_get_vertex_attrib(&prim, ivert, S3D_POSITION, &attr));
    586         SHA256_UPD(attr.value, 3);
    587       }
    588     }
    589 
    590     interf = scene_get_interface(scn, (unsigned)iprim);
    591     SHA256_UPD(&interf->medium_front->type, 1);
    592     SHA256_UPD(&interf->medium_front->id, 1);
    593     SHA256_UPD(&interf->medium_back->type, 1);
    594     SHA256_UPD(&interf->medium_back->id, 1);
    595   }
    596   #undef SHA256_UPD
    597   sha256_ctx_finalize(&sha256_ctx, hash);
    598 
    599   return res;
    600 }
    601 
    602 res_T
    603 scene_check_primitive_index(const struct sdis_scene* scn, const size_t iprim)
    604 {
    605   res_T res = RES_OK;
    606   ASSERT(scn);
    607 
    608   if(iprim >= scene_get_primitives_count(scn)) {
    609     log_err(scn->dev,
    610       "%s: invalid primitive identifier `%lu'. "
    611       "It must be in the [0 %lu] range.\n",
    612       FUNC_NAME,
    613       (unsigned long)iprim,
    614       (unsigned long)scene_get_primitives_count(scn)-1);
    615     res = RES_BAD_ARG;
    616     goto error;
    617   }
    618 
    619 exit:
    620   return res;
    621 error:
    622   goto exit;
    623 }
    624 
    625 res_T
    626 scene_check_dimensionality_2d(const struct sdis_scene* scn)
    627 {
    628   res_T res = RES_OK;
    629   ASSERT(scn);
    630   if(scene_is_2d(scn) == 0) {
    631     log_err(scn->dev,
    632       "%s: expects a 2D scene while the input scene is 3D.\n",
    633       FUNC_NAME);
    634     res = RES_BAD_ARG;
    635     goto error;
    636   }
    637 exit:
    638   return res;
    639 error:
    640   goto exit;
    641 }
    642 
    643 res_T
    644 scene_check_dimensionality_3d(const struct sdis_scene* scn)
    645 {
    646   res_T res = RES_OK;
    647   ASSERT(scn);
    648   if(scene_is_2d(scn) != 0) {
    649     log_err(scn->dev,
    650       "%s: expects a 3D scene while the input scene is 2D.\n",
    651       FUNC_NAME);
    652     res = RES_BAD_ARG;
    653     goto error;
    654   }
    655 exit:
    656   return res;
    657 error:
    658   goto exit;
    659 }
    660 
    661 res_T
    662 scene_check_temperature_range(const struct sdis_scene* scn)
    663 {
    664   res_T res = RES_OK;
    665   ASSERT(scn);
    666 
    667   if(SDIS_TEMPERATURE_IS_UNKNOWN(scn->tmin)) {
    668     log_err(scn->dev,
    669       "%s the defined minimum temperature is unknown "
    670       "when it is expected to be known.\n",
    671       FUNC_NAME);
    672     res = RES_BAD_ARG;
    673     goto error;
    674   }
    675 
    676   if(SDIS_TEMPERATURE_IS_UNKNOWN(scn->tmax)) {
    677     log_err(scn->dev,
    678       "%s the defined maximum temperature is unknown "
    679       "when it is expected to be known.\n",
    680       FUNC_NAME);
    681     res = RES_BAD_ARG;
    682     goto error;
    683   }
    684 
    685   if(scn->tmin > scn->tmax) {
    686     log_err(scn->dev,
    687       "%s: defined temperature range degenerated -- [%g, %g] K\n",
    688       FUNC_NAME, scn->tmin, scn->tmax);
    689     res = RES_BAD_ARG;
    690     goto error;
    691   }
    692 
    693 exit:
    694   return res;
    695 error:
    696   goto exit;
    697 }