commit 27df4af5d16d6ed2fd96afbaa246d94e58a95c1a
parent 2d3d4b9b09b31b6a75327546737b39520f0bc1b8
Author: Eduardo Fontana Lazzari <edufonlaz@gmail.com>
Date: Tue, 14 Oct 2025 19:09:48 +0200
Setup accumulator data structures to manage observable-weighted sums
Up to this point, no dedicated structure was implemented in star-phor to
manage weights in algorithms. In the current implementation of
compute_MVREA, the weight is stored as a single real number. This
approach is quite limiting, since a single realization can lead to
different path endpoints that, in turn, increment distinct weight sums.
In other cases, it may be necessary to update multiple weights
in a same realization function call.
This commit introduces a more robust method for storing and managing
sums of weights. We define an elementary structure (struct accum) to store:
1. the sum of the weights (sum),
2. the square of this sum (sum2), and
3. the number of realizations of the random variable associated with the
accumulator.
Each weight is characterized by a unique triplet of keywords that
represent the physical quantity being estimated: observable, sensor, and
component. An example of possible keyword usage is as follows:
1. observable: the type of physical quantity to be estimated (e.g.,
“MVREA”);
2. sensor: where the photon was absorbed (the name of a volume or a
surface);
3. component: additional information related to photon absorption (chemical
species, wavelength interval, etc.).
The choice of which keywords to use depends on the algorithm. Defining
three levels of description reflects an attempt to make the structure as
generic as necessary for all possible applications, while staying within
the conceptual perimeter of star-phor.
This commit also introduces a dynamic array (struct darray_accum) of
these structures to manage a list of different estimators, which is
expected to be the typical use case.
While this array solves the problem of storing weights, it raises a new
question: these weights are updated many times during a simulation, so
it is necessary to establish an efficient way to access the weight
corresponding to a given physical situation using the information
available during path tracing.
Given the computational intensity of the algorithms and the need for a
general, reusable solution for future developments in star-phor, we
introduce a hash table (htable_accum2id), which accelerates the access
to the accumumulators, by establishing a constant-time
correspondence between a key and a value. It allows retrieving the
identifier of an accumulator within the dynamic array using a key.
The key (accum_key) is composed of:
1. the sensor type (either volume or surface),
2. its identifier, and
3. an optional third argument representing the component.
This design ensures key uniqueness and makes the key easily
constructible on the fly under different circumstances.
Diffstat:
| M | Makefile | | | 1 | + |
| A | src/sphor_accum.c | | | 96 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/sphor_accum.h | | | 137 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 234 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -38,6 +38,7 @@ all: executable library tests
################################################################################
SRC_LIB =\
src/sphor.c\
+ src/sphor_accum.c\
src/sphor_compute_mvrea.c\
src/sphor_config.c\
src/sphor_interface.c\
diff --git a/src/sphor_accum.c b/src/sphor_accum.c
@@ -0,0 +1,96 @@
+/* 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/>. */
+
+#include "sphor.h"
+#include "sphor_accum.h"
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+extern LOCAL_SYM void
+accum_init
+ (struct mem_allocator* allocator,
+ struct accum* accum)
+{
+ ASSERT(NULL != allocator);
+ ASSERT(NULL != accum);
+
+ *accum = ACCUM_NULL;
+ str_init(allocator, &accum->observable);
+ str_init(allocator, &accum->sensor);
+ str_init(allocator, &accum->component);
+}
+
+extern LOCAL_SYM void
+accum_release
+ (struct accum* accum)
+{
+ ASSERT(NULL != accum);
+
+ str_release(&accum->observable);
+ str_release(&accum->sensor);
+ str_release(&accum->component);
+}
+
+extern LOCAL_SYM res_T
+accum_copy
+ (struct accum* dst,
+ const struct accum* src)
+{
+ res_T res = RES_OK;
+
+ ASSERT(NULL != dst);
+ ASSERT(NULL != src);
+
+ dst->sum = src->sum;
+ dst->sum2 = src->sum2;
+ dst->n_realizations = src->n_realizations;
+ res = str_copy(&dst->observable, &src->observable);
+ if (RES_OK != res) { return res; }
+ res = str_copy(&dst->sensor, &src->sensor);
+ if (RES_OK != res) { return res; }
+ res = str_copy(&dst->component, &src->component);
+ return res;
+}
+
+extern LOCAL_SYM res_T
+accum_copy_and_release
+ (struct accum* dst,
+ struct accum* src)
+{
+ res_T res = RES_OK;
+
+ ASSERT(NULL != dst);
+ ASSERT(NULL != src);
+
+ dst->sum = src->sum;
+ dst->sum2 = src->sum2;
+ dst->n_realizations = src->n_realizations;
+ res = str_copy_and_release(&dst->observable, &src->observable);
+ if (RES_OK != res) { return res; }
+ res = str_copy_and_release(&dst->sensor, &src->sensor);
+ if (RES_OK != res) { return res; }
+ res = str_copy_and_release(&dst->component, &src->component);
+ return res;
+}
diff --git a/src/sphor_accum.h b/src/sphor_accum.h
@@ -0,0 +1,137 @@
+/* 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_ACCUM_H
+#define SPHOR_ACCUM_H
+
+#include <rsys/dynamic_array.h>
+#include <rsys/hash_table.h>
+#include <rsys/str.h>
+
+struct accum {
+ struct str observable; /* Type of observable */
+ struct str sensor; /* Volume name or surface name */
+ struct str component; /* Optional: used to discriminate part of the events (ex:
+ prop_rad name in volumes) */
+ double sum;
+ double sum2;
+ size_t n_realizations;
+};
+#define ACCUM_NULL__ {0}
+static const struct accum ACCUM_NULL = ACCUM_NULL__;
+
+enum sphor_sensor_type {
+ SPHOR_SENSOR_VOLUME,
+ SPHOR_SENSOR_SURFACE,
+ SPHOR_SENSOR_NONE
+};
+
+struct accum_key {
+ enum sphor_sensor_type sensor_type;
+ size_t sensor_id;
+ size_t component_id;
+};
+#define ACCUM_KEY_NULL__ {SPHOR_SENSOR_NONE, SIZE_MAX, SIZE_MAX}
+static const struct accum_key ACCUM_KEY_NULL = ACCUM_KEY_NULL__;
+
+extern LOCAL_SYM void
+accum_init
+ (struct mem_allocator* allocator,
+ struct accum* accum);
+
+extern LOCAL_SYM void
+accum_release
+ (struct accum* accum);
+
+extern LOCAL_SYM res_T
+accum_copy
+ (struct accum* dst,
+ const struct accum* src);
+
+extern LOCAL_SYM res_T
+accum_copy_and_release
+ (struct accum* dst,
+ struct accum* src);
+
+/* Generate darray_accum data type and API */
+#define DARRAY_NAME accum
+#define DARRAY_DATA struct accum
+#define DARRAY_FUNCTOR_INIT accum_init
+#define DARRAY_FUNCTOR_RELEASE accum_release
+#define DARRAY_FUNCTOR_COPY accum_copy
+#define DARRAY_FUNCTOR_COPY_AND_RELEASE accum_copy_and_release
+#include <rsys/dynamic_array.h>
+
+static INLINE char
+accum_key_eq
+ (const struct accum_key* a,
+ const struct accum_key* b)
+{
+ ASSERT(NULL != a);
+ ASSERT(NULL != b);
+
+ return a->sensor_type == b->sensor_type
+ && a->sensor_id == b->sensor_id
+ && a->component_id == b->component_id;
+}
+
+/* In many general cases, when using hash functions, we can directly apply the
+ * hash function to the key as is.
+ * This is not the case here, since using an enum may lead to different
+ * hashes for the same accum_key structure across different function calls,
+ * due to data alignment performed by the compiler
+ * (see https://en.wikipedia.org/wiki/Data_structure_alignment). */
+static INLINE size_t
+accum_key_hash
+ (const struct accum_key* accum_key)
+{
+ size_t key[3] = {0};
+ size_t hash = 0;
+
+ ASSERT(NULL != accum_key);
+
+ /* We convert the key into an array to ensure that all data is stored in a
+ * contiguous memory block, with no padding bytes added by the compiler.
+ * This guarantees that the same key will always produce the same hash. */
+ key[0] = accum_key->sensor_type;
+ key[1] = accum_key->sensor_id;
+ key[2] = accum_key->component_id;
+
+ hash = hash_fnv64(key, sizeof(key));
+
+ return hash;
+}
+
+/* Generate the hash table that maps accum_key to an index used to access the
+ * corresponding accum in the dynamic array */
+#define HTABLE_NAME accum2id
+#define HTABLE_KEY struct accum_key
+#define HTABLE_DATA size_t
+#define HTABLE_KEY_FUNCTOR_EQ accum_key_eq
+#define HTABLE_KEY_FUNCTOR_HASH accum_key_hash
+
+/* Include rsys/hash_table a second time to generate the hash table */
+#include <rsys/hash_table.h>
+
+#endif /* SPHOR_ACCUM_H */