star-phor

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

commit 59f137b6fa631401044c07d5e0ba340a510d6c24
parent ae8ff2a771b302742beedb1eeff88a7201ac56f0
Author: Eduardo Fontana Lazzari <edufonlaz@gmail.com>
Date:   Wed, 30 Apr 2025 18:14:23 +0200

Add associative structure linking triangles to physical properties

This commit introduces a system that maps each triangle of the input
geometries to its corresponding physical context (front/back volumes and
surfaces).

Motivation and rationale:

Once a photoreactive system configuration is loaded by star-phor-input,
there is a direct correspondence between volumes and surfaces on one
hand, and geometry files on the other. This correspondence stems from a
deliberate choice in the way such systems are described in
star-phor-input files, which aim to provide a syntax allowing for a
clear (scientifically readable) description of a well-posed radiative
transfer problem applied to photoreactors.

However, during the computation phase, this existing correspondence
proves insufficient. In a Monte Carlo calculation, the focus shifts to
querying the physical properties of the geometry (requiring a reversal
of the correspondence direction previously defined in the input file).
Moreover, the geometry must be handled more granularly: we consider
individual triangles rather than whole geometry files. Therefore, in
order to access the physical properties of a given surface or volume
during a Monte Carlo calculation, it is necessary to associate the
triangles composing the geometry of a given photoreactive system with
the volumes and surfaces each triangle belongs to.

Strategy:

We first define a structure called interface which holds the identifiers
of the front and back volumes, as well as the list of surface
identifiers the triangle belongs to (on both sides). Each interface
structure represents a single triangle, and each triangle is represented
by a unique interface (there is a bijective relationship: triangle <->
interface). To describe the entire photoreactive system, we use a
dynamic array that contains all interfaces corresponding to all unique
triangles from the geometries in the input file (see darray_interface).

Note that the coordinates of each triangle's vertices are not included
in this structure. During computation, that information is managed by
star-3d, which is responsible for building the acceleration structure
used for intersection queries during ray tracing and already possesses
all necessary geometric data. To avoid storing the same data multiple
times in memory during the same computation stage, star-3d remains the
sole owner of this data. However, a temporary dynamic array of triangles
with vertex coordinates does exist in star-phor during the construction
of the associative structure (see darray_triangle). This array is
discarded once the coordinates are transferred to star-3d (i.e.,
ownership of the geometric data passes from star-phor to star-3d once
the correspondance triangle->physical properties has been built).

To establish the equivalence between the dynamic triangle and interface
arrays, we use a simple strategy: a triangle in the darray_triangle
array and its corresponding interface in the darray_interface array
share the same index i. This means that to access the physical
properties of a given triangle, one simply retrieves its index in the
triangle array and accesses the equivalent element in the interface
array. Moreover, since S3D preserves triangle indices during geometry
structuring, this approach ensures we can retrieve triangle indices
during the Monte Carlo calculation.

Two challenges arise during this phase:

  1.How can we efficiently retrieve the index of a triangle in the
    triangle array, considering that a linear search can become
    expensive as the number of triangles grows?

  2.How can we ensure the uniqueness of a triangle, given that the same
    triangle can have 3! valid representations depending on the vertex
    order?

To answer question 2, we adopt a convention for triangle representation
that normalizes all possible descriptions to a single canonical one: we
arrange its vertices in lexicographic order
(https://en.wikipedia.org/wiki/Lexicographic_order, see the
triangle_key_setup function). Once a triangle is reduced to its ordered
representation, we use the hash table tri2id (see rsys/hash_table.h) to
map vertex sets to indices in the triangle array. This structure enables
key-value access (key: three lexicographically sorted coordinates,
value: index of the corresponding interface in the dynamic interface
array - and index of triangle in the darray_triangle ; and later
triangle id in s3d). Accordingly, we define the function
register_triangle, which both registers a triangle in the tri2id hash
table and retrieves the triangle’s index based on its coordinates.

Just like the triangles dynamic array, this hash table is temporary at
this stage of the computation. Once the mapping from geometric data to
physical data has been established for all triangles in the scene, the
table is discarded.

Once this structure is in place, we iterate over all surfaces and
volumes in the configuration, then over each geometry of each surface
and volume, and then over each triangle of each geometry, decorating the
corresponding interface with its front volume, back volume, and lists of
front and back surfaces.

This association step has a subtle complexity: a triangle may be defined
multiple times with different orientations in the star-phor input, each
time following the valid left-hand orientation convention. For instance,
a triangle might initially have a front facing one direction, and later
be redefined with an opposite vertex order, reversing the front
direction — both valid interpretations.

Thus, if a previously defined triangle is encountered during the
geometric-to-physical data association, we check the already registered
interface description and compare it to the one at the triangle level.
If the triangle normals point in opposite directions, this must be
handled before recording the volume or surface in the corresponding
interface.

Diffstat:
Msrc/sphor.c | 2++
Msrc/sphor_c.h | 48++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sphor_config.c | 543+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sphor_interface.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 642 insertions(+), 0 deletions(-)

diff --git a/src/sphor.c b/src/sphor.c @@ -49,6 +49,7 @@ release_sphor ASSERT(NULL != address); sphor = CONTAINER_OF(address, struct sphor, ref); + darray_interface_release(&sphor->interfaces); MEM_RM(sphor->allocator, sphor); } @@ -89,6 +90,7 @@ sphor_create } else { sphor->logger = args->logger; } + darray_interface_init(sphor->allocator, &sphor->interfaces); /* TODO load the system and build data structure for Monte Carlo */ res = setup_config(sphor, args); diff --git a/src/sphor_c.h b/src/sphor_c.h @@ -25,6 +25,11 @@ #ifndef SPHOR_C_H #define SPHOR_C_H +#include "sphor_interface.h" + +#include <rsys/double3.h> +#include <rsys/dynamic_array.h> +#include <rsys/hash_table.h> #include <rsys/logger.h> #include <rsys/ref_count.h> @@ -46,6 +51,7 @@ struct sphor_create_args; struct sphor { /* Physical configuration */ struct sphin_config* config; + struct darray_interface interfaces; /* Log */ struct logger* logger; @@ -56,6 +62,48 @@ struct sphor { ref_T ref; }; +struct triangle { + double v0[3]; + double v1[3]; + double v2[3]; +}; +#define TRIANGLE_NULL__ {0} +static const struct triangle TRIANGLE_NULL = TRIANGLE_NULL__; + +/* Define the dynamic array for the struct triangle as a type */ +#define DARRAY_NAME triangle +#define DARRAY_DATA struct triangle +#include <rsys/dynamic_array.h> + +struct triangle_key { + double v0[3]; + double v1[3]; + double v2[3]; +}; +#define TRIANGLE_KEY_NULL__ {0} +static const struct triangle_key TRIANGLE_KEY_NULL = TRIANGLE_KEY_NULL__; + +static INLINE char +triangle_key_eq + (const struct triangle_key* a, + const struct triangle_key* b) +{ + ASSERT(NULL != a); + ASSERT(NULL != b); + + return d3_eq(a->v0, b->v0) + && d3_eq(a->v1, b->v1) + && d3_eq(a->v2, b->v2); +} + +/* Define the hash table that maps triangle_key to an index used to access the + * triangle's properties (coordinates and interface) */ +#define HTABLE_NAME tri2id +#define HTABLE_KEY struct triangle_key +#define HTABLE_DATA size_t +#define HTABLE_KEY_FUNCTOR_EQ triangle_key_eq +#include <rsys/hash_table.h> + extern LOCAL_SYM res_T setup_config (struct sphor* sphor, diff --git a/src/sphor_config.c b/src/sphor_config.c @@ -30,6 +30,537 @@ /******************************************************************************* * Helper functions ******************************************************************************/ +/* Compare the vector "a" to the vector "b". It returns an integer indicating + * the result of the comparison, as follows: + * - 0, if the "a" and "b" are equal; + * - a negative value if "a" is less than "b"; + * - a positive value if "a" is greater than "b". */ +static INLINE int +vec3_cmp + (const double a[3], + const double b[3]) +{ + int i; + ASSERT(NULL != a); + ASSERT(NULL != b); + + FOR_EACH(i, 0, 3) { + if (a[i] < b[i]) { return -1; } + else if (a[i] > b[i]) { return +1; } + } + return 0; +} + +static INLINE void +vec3_swap + (double v0[3], + double v1[3]) +{ + double v[3] = {0}; + ASSERT(NULL != v0); + ASSERT(NULL != v1); + + d3_set(v, v0); + d3_set(v0,v1); + d3_set(v1, v); +} + +static void +triangle_key_setup + (struct triangle_key* key, + const struct triangle* tri) +{ + ASSERT(NULL != key); + ASSERT(NULL != tri); + + d3_set(key->v0, tri->v0); + d3_set(key->v1, tri->v1); + d3_set(key->v2, tri->v2); + + /* Sort tri vertices lexicographically using the bubble sort algorithm + * (https://en.wikipedia.org/wiki/Bubble_sort) */ + if (vec3_cmp(key->v0, key->v1) < 0) vec3_swap(key->v0, key->v1); + if (vec3_cmp(key->v1, key->v2) < 0) vec3_swap(key->v1, key->v2); + if (vec3_cmp(key->v0, key->v1) < 0) vec3_swap(key->v0, key->v1); +} + +static int +are_triangles_reversed + (const struct triangle* tri0, + const struct triangle* tri1) +{ + const double* tri1_verts[3] = { NULL, NULL, NULL}; + int i = 0; + + ASSERT(NULL != tri0); + ASSERT(NULL != tri1); + +#ifndef NDEBUG + /* In debug check that the triangles are geometrically the same */ + { + struct triangle_key k0 = TRIANGLE_KEY_NULL; + struct triangle_key k1 = TRIANGLE_KEY_NULL; + + triangle_key_setup(&k0, tri0); + triangle_key_setup(&k1, tri1); + ASSERT(triangle_key_eq(&k0, &k1)); + } +#endif /* NDEBUG */ + + /* Match tri0->v0 with one of the vertices of tri1. */ + tri1_verts[0] = tri1->v0; + tri1_verts[1] = tri1->v1; + tri1_verts[2] = tri1->v2; + + FOR_EACH(i, 0, 3) { + if(0 == vec3_cmp(tri0->v0, tri1_verts[i])) { break; } + } + ASSERT(i<3); + /* Check the order of the next vertex: + * If tri0->v1 matches the next vertex in tri1 (cyclically), + * the triangles have the same orientation (not reversed). + * Otherwise, the vertex order has been reversed + * (due to lexicographical sorting in the key struct). */ + if(0 == vec3_cmp(tri0->v1, tri1_verts[(i+1)%3])) { + return 0; /* Same orientation */ + } else { + return 1; /* Reversed orientation */ + } +} + +/* Return the triangle id if it already exists, append it to the dynamic array + * and create an equivalent interface in the interface dynamic array otherwise + * and return the newly created id */ +static res_T +register_triangle + (struct sphor* sphor, + const struct triangle* triangle, + struct htable_tri2id* tri2id, + struct darray_triangle* triangles, + size_t* out_id) +{ + size_t* p_id = NULL; + struct triangle_key tri_key = TRIANGLE_KEY_NULL; + res_T res = RES_OK; + + /* Reorder tri vertices to get the tri_key */ + triangle_key_setup(&tri_key, triangle); + + /* Get the triangle id */ + p_id = htable_tri2id_find(tri2id, &tri_key); + + /* If not in the array, add it */ + if (NULL == p_id) { + *p_id = darray_triangle_size_get(triangles); + res = darray_triangle_push_back(triangles, triangle); + if (RES_OK != res) { goto error; } + res = darray_interface_push_back(&sphor->interfaces, &INTERFACE_NULL); + if (RES_OK != res) { goto error; } + res = htable_tri2id_set(tri2id, &tri_key, p_id); + if (RES_OK != res) { goto error; } + } + +exit: + *out_id = *p_id; + return res; +error: + goto exit; +} + +/******************************************************************************* + * Volume setup + ******************************************************************************/ +static res_T +setup_interface_volume_geometry_triangle + (struct sphor* sphor, + struct htable_tri2id* tri2id, + struct darray_triangle* triangles, + const struct sphin_geometry_descriptor* geom_desc, + const size_t ivolume, + const size_t itri) +{ + size_t itri_sphor = 0; /* Triangle index used internally by sphor */ + struct triangle tri = TRIANGLE_NULL; + const struct triangle* tri_sphor = NULL; + struct interface* interface = NULL; + size_t ids[3] = {0, 0, 0}; + const double* coords[3] = {NULL, NULL, NULL}; + size_t* pvolume_id = NULL; + int flip = 0; + res_T res = RES_OK; + + ASSERT(NULL != sphor); + ASSERT(NULL != tri2id); + ASSERT(NULL != triangles); + ASSERT(NULL != geom_desc); + ASSERT(itri < geom_desc->mesh.triangle_count); + + /* Get the indices of the triangle vertices. They are stored in a one + * dimensional array. So, to retrieve the vertex indices of the i^th triangle, + * one has to multiply i by 3, i.e. the number of vertex per triangle : + * + * 0 1 2 3 4 5 6 7 8 + * +---+---+---+---+---+---+---+---+---+----- + * indices |id0|id1|id2|id0|id1|id2|id0|id1|id2| ... + * +---+---+---+---+---+---+---+---+---+----- + * \__tri 0__/ \__tri 1__/ \__tri 2__/ */ + ids[0] = geom_desc->mesh.indices[itri*3/*#indices per triangle*/+0]; + ids[1] = geom_desc->mesh.indices[itri*3/*#indices per triangle*/+1]; + ids[2] = geom_desc->mesh.indices[itri*3/*#indices per triangle*/+2]; + + /* Get coords for the triangle corresponding to the vertices retrieved. Like + * the indices, the coords are stored in an one dimensional array. + * + * 0 1 2 3 4 5 6 7 8 + * +---+---+---+---+---+---+---+---+---+----- + * coords | x | y | z | x | y | z | x | y | z | ... + * +---+---+---+---+---+---+---+---+---+----- + * \__vert0__/ \__vert1__/ \__vert2__/ */ + coords[0] = geom_desc->mesh.coords + ids[0]*3/*#coords per vertex*/; + coords[1] = geom_desc->mesh.coords + ids[1]*3/*#coords per vertex*/; + coords[2] = geom_desc->mesh.coords + ids[2]*3/*#coords per vertex*/; + + /* Set the retrieved coords to the triangle struct */ + d3_set(tri.v0, coords[0]); + d3_set(tri.v1, coords[1]); + d3_set(tri.v2, coords[2]); + + res = register_triangle(sphor, &tri, tri2id, triangles, &itri_sphor); + if (RES_OK != res) { goto error; } + + /* Retrieve the registered triangle and its properties */ + tri_sphor = darray_triangle_cdata_get(triangles) + itri_sphor; + interface = darray_interface_data_get(&sphor->interfaces) + itri_sphor; + + flip = are_triangles_reversed(&tri, tri_sphor); + + switch (geom_desc->side) { + case SPHIN_SIDE_FRONT: + pvolume_id = flip ? &interface->volume_back : &interface->volume_front; + break; + case SPHIN_SIDE_BACK: + pvolume_id = flip ? &interface->volume_front : &interface->volume_back; + break; + default: FATAL("Unreachable code\n"); break; + } + + if(*pvolume_id != INVALID_ID) { + /* TODO return an error and log a message */ + } + + *pvolume_id = ivolume; + +exit: + return res; +error: + goto exit; +} + +static res_T +setup_interfaces_volume_geometry + (struct sphor* sphor, + struct htable_tri2id* tri2id, + struct darray_triangle* triangles, + const size_t ivolume, + const size_t igeom) +{ + struct sphin_geometry* geom = NULL; + struct sphin_geometry_descriptor geom_desc = SPHIN_GEOMETRY_DESCRIPTOR_NULL; + struct sphin_volume* volume = NULL; + size_t itri = 0; + size_t n = 0; + res_T res = RES_OK; + + ASSERT(NULL != sphor); + ASSERT(NULL != tri2id); + ASSERT(NULL != triangles); + ASSERT(RES_OK == sphin_config_get_volume_count(sphor->config, &n)); + ASSERT(ivolume < n); + + res = sphin_config_get_volume(sphor->config, ivolume, &volume); + if (RES_OK != res) { goto error; } + + ASSERT(RES_OK == sphin_volume_get_geometry_count(volume, &n)); + ASSERT(igeom < n); + + res = sphin_volume_get_geometry(volume, igeom, &geom); + if (RES_OK != res) { goto error; } + + res = sphin_geometry_get_desc(geom, &geom_desc); + if (RES_OK != res) { goto error; } + + n = geom_desc.mesh.triangle_count; + FOR_EACH(itri, 0, n) { + res = setup_interface_volume_geometry_triangle + (sphor, tri2id, triangles, &geom_desc, ivolume, itri); + if (RES_OK != res) { goto error; } + } + +exit: + return res; +error: + goto exit; +} + +static res_T +setup_interfaces_volume + (struct sphor* sphor, + struct htable_tri2id* tri2id, + struct darray_triangle* triangles, + const size_t ivolume) +{ + struct sphin_volume* volume = NULL; + size_t n = 0; + size_t igeom = 0; + res_T res = RES_OK; + + ASSERT(NULL != sphor); + ASSERT(NULL != tri2id); + ASSERT(NULL != triangles); + ASSERT(RES_OK == sphin_config_get_volume_count(sphor->config, &n)); + ASSERT(ivolume < n); + + res = sphin_config_get_volume(sphor->config, ivolume, &volume); + if (RES_OK != res) { goto error; } + + res = sphin_volume_get_geometry_count(volume, &n); + if (RES_OK != res) { goto error; } + + FOR_EACH(igeom, 0, n) { + res = setup_interfaces_volume_geometry + (sphor, tri2id, triangles, ivolume, igeom); + if (RES_OK != res) { goto error; } + } +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Surface setup + ******************************************************************************/ +static res_T +setup_interface_surface_geometry_triangle + (struct sphor* sphor, + struct htable_tri2id* tri2id, + struct darray_triangle* triangles, + const struct sphin_geometry_descriptor* geom_desc, + const size_t isurface, + const size_t itri) +{ + size_t itri_sphor = 0; /* Triangle index used internally by sphor */ + struct triangle tri = TRIANGLE_NULL; + const struct triangle* tri_sphor = NULL; + struct interface* interface = NULL; + size_t ids[3] = {0, 0, 0}; + const double* coords[3] = {NULL, NULL, NULL}; + int flip = 0; + int is_back = 0; + res_T res = RES_OK; + + ASSERT(NULL != sphor); + ASSERT(NULL != tri2id); + ASSERT(NULL != triangles); + ASSERT(NULL != geom_desc); + ASSERT(itri < geom_desc->mesh.triangle_count); + + /* Get the indices of the triangle vertices. They are stored in a one + * dimensional array. So, to retrieve the vertex indices of the i^th triangle, + * one has to multiply i by 3, i.e. the number of vertex per triangle : + * + * 0 1 2 3 4 5 6 7 8 + * +---+---+---+---+---+---+---+---+---+----- + * indices |id0|id1|id2|id0|id1|id2|id0|id1|id2| ... + * +---+---+---+---+---+---+---+---+---+----- + * \__tri 0__/ \__tri 1__/ \__tri 2__/ */ + ids[0] = geom_desc->mesh.indices[itri*3/*#indices per triangle*/+0]; + ids[1] = geom_desc->mesh.indices[itri*3/*#indices per triangle*/+1]; + ids[2] = geom_desc->mesh.indices[itri*3/*#indices per triangle*/+2]; + + /* Get coords for the triangle corresponding to the vertices retrieved. Like + * the indices, the coords are stored in an one dimensional array. + * + * 0 1 2 3 4 5 6 7 8 + * +---+---+---+---+---+---+---+---+---+----- + * coords | x | y | z | x | y | z | x | y | z | ... + * +---+---+---+---+---+---+---+---+---+----- + * \__vert0__/ \__vert1__/ \__vert2__/ */ + coords[0] = geom_desc->mesh.coords + ids[0]*3/*#coords per vertex*/; + coords[1] = geom_desc->mesh.coords + ids[1]*3/*#coords per vertex*/; + coords[2] = geom_desc->mesh.coords + ids[2]*3/*#coords per vertex*/; + + /* Set the retrieved coords to the triangle struct */ + d3_set(tri.v0, coords[0]); + d3_set(tri.v1, coords[1]); + d3_set(tri.v2, coords[2]); + + res = register_triangle(sphor, &tri, tri2id, triangles, &itri_sphor); + if (RES_OK != res) { goto error; } + + /* Retrieve the registered triangle and its properties */ + tri_sphor = darray_triangle_cdata_get(triangles) + itri_sphor; + interface = darray_interface_data_get(&sphor->interfaces) + itri_sphor; + + /* Determine the side to append the surface based on whether the geometry is + * flipped and knowing the input side using the xor (^) operator. + * The value of the is_back variable is given by the truth table: + * + * +-----------+ + * | flip | + * +-----+-----+ + * | 0 | 1 | + * +------+-----+-----+-----+ + * | side | 0 | 0 | 1 | + * | == |-----+-----+-----+ + * | back | 1 | 1 | 0 | + * +------+-----+-----+-----+ */ + + flip = are_triangles_reversed(&tri, tri_sphor); + is_back = flip ^ (geom_desc->side == SPHIN_SIDE_BACK); + + if (is_back) { + if ( MAX_SURFACE_COUNT <= interface->surface_back_count+1){ + /* TODO Error message and exit */ + } + interface->surfaces_back[interface->surface_back_count] = isurface; + interface->surface_back_count++; + } else { + if ( MAX_SURFACE_COUNT <= interface->surface_front_count+1){ + /* TODO Error message and exit */ + } + interface->surfaces_front[interface->surface_front_count] = isurface; + interface->surface_front_count++; + } + +exit: + return res; +error: + goto exit; +} + +static res_T +setup_interfaces_surface_geometry + (struct sphor* sphor, + struct htable_tri2id* tri2id, + struct darray_triangle* triangles, + const size_t isurface, + const size_t igeom) +{ + struct sphin_geometry* geom = NULL; + struct sphin_geometry_descriptor geom_desc = SPHIN_GEOMETRY_DESCRIPTOR_NULL; + struct sphin_surface* surface = NULL; + size_t itri = 0; + size_t n = 0; + res_T res = RES_OK; + + ASSERT(NULL != sphor); + ASSERT(NULL != tri2id); + ASSERT(NULL != triangles); + ASSERT(RES_OK == sphin_config_get_surface_count(sphor->config, &n)); + ASSERT(isurface < n); + + res = sphin_config_get_surface(sphor->config, isurface, &surface); + if (RES_OK != res) { goto error; } + + ASSERT(RES_OK == sphin_surface_get_geometry_count(surface, &n)); + ASSERT(igeom < n); + + res = sphin_surface_get_geometry(surface, igeom, &geom); + if (RES_OK != res) { goto error; } + + res = sphin_geometry_get_desc(geom, &geom_desc); + if (RES_OK != res) { goto error; } + + n = geom_desc.mesh.triangle_count; + FOR_EACH(itri, 0, n) { + res = setup_interface_surface_geometry_triangle + (sphor, tri2id, triangles, &geom_desc, isurface, itri); + if (RES_OK != res) { goto error; } + } + +exit: + return res; +error: + goto exit; +} + +static res_T +setup_interfaces_surface + (struct sphor* sphor, + struct htable_tri2id* tri2id, + struct darray_triangle* triangles, + const size_t isurface) +{ + struct sphin_surface* surface = NULL; + size_t n = 0; + size_t igeom = 0; + res_T res = RES_OK; + + ASSERT(NULL != sphor); + ASSERT(NULL != tri2id); + ASSERT(NULL != triangles); + ASSERT(RES_OK == sphin_config_get_surface_count(sphor->config, &n)); + ASSERT(isurface < n); + + res = sphin_config_get_surface(sphor->config, isurface, &surface); + if (RES_OK != res) { goto error; } + + res = sphin_surface_get_geometry_count(surface, &n); + if (RES_OK != res) { goto error; } + + FOR_EACH(igeom, 0, n) { + res = setup_interfaces_surface_geometry + (sphor, tri2id, triangles, isurface, igeom); + if (RES_OK != res) { goto error; } + } +exit: + return res; +error: + goto exit; +} + +static res_T +setup_interfaces + (struct sphor* sphor, + struct htable_tri2id* tri2id, + struct darray_triangle* triangles) +{ + size_t id = 0; + size_t volume_count = 0; + size_t surface_count = 0; + res_T res = RES_OK; + + (void) triangles; + + ASSERT(NULL != sphor); + ASSERT(NULL != tri2id); + ASSERT(NULL != triangles); + /* Assumes that config is already loaded */ + ASSERT(NULL != sphor->config); + + res = sphin_config_get_volume_count(sphor->config, &volume_count); + if (RES_OK != res) { goto error; } + res = sphin_config_get_surface_count(sphor->config, &surface_count); + if (RES_OK != res) { goto error; } + + FOR_EACH(id, 0, volume_count) { + res = setup_interfaces_volume(sphor, tri2id, triangles, id); + if (RES_OK != res) { goto error; } + } + + FOR_EACH(id, 0, surface_count) { + res = setup_interfaces_surface(sphor, tri2id, triangles, id); + if (RES_OK != res) { goto error; } + } + +exit: + return res; + +error: + goto exit; +} + static res_T load_config (struct sphor* sphor, @@ -80,15 +611,27 @@ setup_config (struct sphor* sphor, const struct sphor_create_args* args) { + struct darray_triangle triangles; + struct htable_tri2id tri2id; res_T res = RES_OK; ASSERT(NULL != sphor); ASSERT(NULL != args); + darray_triangle_init(sphor->allocator, &triangles); + htable_tri2id_init(sphor->allocator, &tri2id); + + /* Parse input file */ res = load_config(sphor, args); if (RES_OK != res) { goto error; } + /* Attach physical properties to primitives */ + res = setup_interfaces(sphor, &tri2id, &triangles); + if (RES_OK != res) { goto error; } + exit: + darray_triangle_release(&triangles); + htable_tri2id_release(&tri2id); return res; error: goto exit; diff --git a/src/sphor_interface.h b/src/sphor_interface.h @@ -0,0 +1,49 @@ +/* Copyright (C) 2024-2025 Centre National de la Recherche Scientifique + * Copyright (C) 2024-2025 Clermont Auvergne INP + * Copyright (C) 2024-2025 INSA Lyon + * Copyright (C) 2024-2025 Institut Mines Télécom Albi-Carmaux + * Copyright (C) 2024-2025 Institut National Polytechnique de Toulouse + * Copyright (C) 2024-2025 |Méso|Star> (contact@meso-star.com) + * Copyright (C) 2024-2025 PhotonLyX (info@photonlyx.com) + * Copyright (C) 2024-2025 Université de Lorraine + * Copyright (C) 2024-2025 Université Paul Sabatier + * Copyright (C) 2024-2025 Université Toulouse - Jean Jaurès + * + * 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 SPHOR_INTERFACE_H +#define SPHOR_INTERFACE_H + +#include <rsys/dynamic_array.h> + +/* Maximal amount of surfaces that the same triangle can be part of. */ +#define MAX_SURFACE_COUNT 4 +#define INVALID_ID SIZE_MAX + +struct interface { + size_t volume_front; + size_t volume_back; + size_t surfaces_back[MAX_SURFACE_COUNT]; + size_t surfaces_front[MAX_SURFACE_COUNT]; + short surface_back_count; + short surface_front_count; +}; +#define INTERFACE_NULL__ {INVALID_ID, INVALID_ID, {INVALID_ID}, {INVALID_ID}, 0, 0} +static const struct interface INTERFACE_NULL = INTERFACE_NULL__; + +#define DARRAY_NAME interface +#define DARRAY_DATA struct interface +#include <rsys/dynamic_array.h> + +#endif /* SPHOR_INTERFACE_H */