commit 1d0d72bb32afc9da50b7c325365227d55dfcdf06
parent df91dc5f2ed1653e9e03e836c17e57999439677d
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Thu, 21 Jul 2022 14:08:20 +0200
Adds the partition of voxels and the pool of partitions
These data structures will be used during the parallel construction of
the octrees.
Diffstat:
5 files changed, 497 insertions(+), 2 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -62,10 +62,13 @@ set(RNATM_FILES_SRC
rnatm.c
rnatm_log.c
rnatm_mesh.c
- rnatm_properties.c)
+ rnatm_properties.c
+ rnatm_voxel_partition.c)
set(RNATM_FILES_INC
rnatm_c.h
- rnatm_log.h)
+ rnatm_log.h
+ rnatm_voxel.h
+ rnatm_voxel_partition.h)
set(RNATM_FILES_INC_API rnatm.h)
set(RNATM_FILES_DOC COPYING README.md)
diff --git a/src/rnatm.h b/src/rnatm.h
@@ -47,6 +47,18 @@
struct logger;
struct mem_allocator;
+enum rnatm_radcoef {
+ RNATM_RADCOEF_Ka, /* Absorption coefficient */
+ RNATM_RADCOEF_Ks, /* Scattering coefficient */
+ RNATM_RADCOEFS_COUNT__
+};
+
+enum rnatm_svx_op {
+ RNATM_SVX_OP_MIN,
+ RNATM_SVX_OP_MAX,
+ RNATM_SVX_OPS_COUNT__
+};
+
struct rnatm_gas_args {
const char* smsh_filename; /* Geometry */
const char* sck_filename; /* Radiative properties */
diff --git a/src/rnatm_voxel.h b/src/rnatm_voxel.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2022 Centre National de la Recherche Scientifique
+ * Copyright (C) 2022 Institut de Physique du Globe de Paris
+ * Copyright (C) 2022 |Méso|Star> (contact@meso-star.com)
+ * Copyright (C) 2022 Université de Reims Champagne-Ardenne
+ * Copyright (C) 2022 Université de Versaille Saint-Quentin
+ * Copyright (C) 2022 Université Paul Sabatier (contact@laplace.univ-tlse.fr)
+ *
+ * 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 RNATM_VOXEL_H
+#define RNATM_VOXEL_H
+
+#include "rnatm.h"
+
+/*
+ * Memory layout of a voxel
+ * ------------------------
+ *
+ * The data of a voxel is stored in a list of N single-precision floating-point
+ * numbers, with N defined as below:
+ *
+ * N = RNATM_RADCOEFS_COUNT__ * RNATM_SVX_OPS_COUNT__
+ *
+ * RNATM_RADCOEFS_COUNT__ is the number of radiative coefficients to be stored
+ * per voxel and RNATM_SVX_OPS_COUNT__ the number of operations for these
+ * coefficients. For a given voxel, the data corresponding to the operation 'O'
+ * on the coefficient 'C' are stored at the index 'id' between [0, N-1] and
+ * calculated as follows:
+ *
+ * id = C * RNATM_SVX_OPS_COUNT__ + O
+ */
+
+/* Total number of floating-point numbers per voxel */
+#define NFLOATS_PER_VOXEL (RNATM_RADCOEFS_COUNT__ * RNATM_SVX_OPS_COUNT__)
+
+/* Calculate the data index of voxel */
+static FINLINE size_t
+voxel_idata
+ (const enum rnatm_radcoef radcoef,
+ const enum rnatm_svx_op op)
+{
+ ASSERT((unsigned)radcoef < RNATM_RADCOEFS_COUNT__);
+ ASSERT((unsigned)op < RNATM_SVX_OPS_COUNT__);
+ return radcoef*RNATM_RADCOEFS_COUNT__ + op;
+}
+
+static FINLINE void
+voxel_clear(float* voxel)
+{
+ int radcoef, op;
+
+ /* Make degenerated the radcoef range */
+ FOR_EACH(radcoef, 0, RNATM_RADCOEFS_COUNT__) {
+ FOR_EACH(op, 0, RNATM_SVX_OPS_COUNT__) {
+ float val = 0;
+ switch(op) {
+ case RNATM_SVX_OP_MIN: val = FLT_MAX; break;
+ case RNATM_SVX_OP_MAX: val = FLT_MIN; break;
+ default: FATAL("Unreachable code\n"); break;
+ }
+ voxel[voxel_idata(radcoef, op)] = val;
+ }
+ }
+}
+
+#endif /* RNATM_VOXEL_H */
diff --git a/src/rnatm_voxel_partition.c b/src/rnatm_voxel_partition.c
@@ -0,0 +1,270 @@
+/* Copyright (C) 2022 Centre National de la Recherche Scientifique
+ * Copyright (C) 2022 Institut de Physique du Globe de Paris
+ * Copyright (C) 2022 |Méso|Star> (contact@meso-star.com)
+ * Copyright (C) 2022 Université de Reims Champagne-Ardenne
+ * Copyright (C) 2022 Université de Versaille Saint-Quentin
+ * Copyright (C) 2022 Université Paul Sabatier (contact@laplace.univ-tlse.fr)
+ *
+ * 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 "rnatm_log.h"
+#include "rnatm_voxel_partition.h"
+
+#include <rsys/condition.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/mutex.h>
+#include <rsys/ref_count.h>
+
+struct pool {
+ /* Linked list of pre-allocated partitions */
+ struct list_node parts_free;
+
+ /* List of available partition sorted in ascending order wrt partition id */
+ struct list_node parts_full;
+
+ struct mutex* mutex;
+ struct cond* cond_new;
+ struct cond* cond_fetch;
+
+ size_t next_part_id; /* Indentifier of the next partition */
+
+ struct mem_allocator* allocator;
+ ATOMIC error; /* Is the pool not valid? */
+ ref_T ref;
+};
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static void
+release_pool(ref_T* ref)
+{
+ struct pool* pool = CONTAINER_OF(ref, struct pool, ref);
+ struct list_node* node = NULL;
+ struct list_node* tmp_node = NULL;
+ ASSERT(ref);
+
+ if(pool->mutex) mutex_destroy(pool->mutex);
+ if(pool->cond_new) cond_destroy(pool->cond_new);
+ if(pool->cond_fetch) cond_destroy(pool->cond_fetch);
+
+ LIST_FOR_EACH_SAFE(node, tmp_node, &pool->parts_free) {
+ struct partition* partition = CONTAINER_OF(node, struct partition, node);
+ list_del(node);
+ partition_release(partition);
+ MEM_RM(pool->allocator, partition);
+ }
+
+ LIST_FOR_EACH_SAFE(node, tmp_node, &pool->parts_full) {
+ struct partition* partition = CONTAINER_OF(node, struct partition, node);
+ list_del(node);
+ partition_release(partition);
+ MEM_RM(pool->allocator, partition);
+ }
+
+ ASSERT(is_list_empty(&pool->parts_free));
+ ASSERT(is_list_empty(&pool->parts_full));
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+res_T
+pool_create
+ (struct mem_allocator* mem_allocator,
+ const size_t npartitions,
+ struct pool** out_pool)
+{
+ struct mem_allocator* allocator = NULL;
+ struct pool* pool = NULL;
+ size_t ipartition = 0;
+ res_T res = RES_OK;
+ ASSERT(out_pool && npartitions);
+
+ allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
+ pool = MEM_CALLOC(allocator, 1, sizeof(*pool));
+ if(!pool) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ pool->allocator = allocator;
+ ref_init(&pool->ref);
+ list_init(&pool->parts_free);
+ list_init(&pool->parts_full);
+
+ /* Create the mutex and condition variables used to synchronize threads */
+ pool->mutex = mutex_create();
+ if(!pool->mutex) { res = RES_UNKNOWN_ERR; goto error; }
+ pool->cond_new = cond_create();
+ if(!pool->cond_new) { res = RES_UNKNOWN_ERR; goto error; }
+ pool->cond_fetch = cond_create();
+ if(!pool->cond_fetch) { res = RES_UNKNOWN_ERR; goto error; }
+
+ /* Pre-allocate the partitions */
+ FOR_EACH(ipartition, 0, npartitions) {
+ struct partition* partition = NULL;
+
+ /* Aligns the partition on 64 Bytes (i.e. the size of a cache line) */
+ partition = MEM_ALLOC_ALIGNED(pool->allocator, sizeof(*partition), 64);
+ if(!partition) { res = RES_MEM_ERR; goto error; }
+
+ partition_init(partition);
+ list_add(&pool->parts_free, &partition->node);
+ }
+
+exit:
+ *out_pool = pool;
+ return res;
+error:
+ if(pool) { pool_ref_put(pool); pool = NULL; }
+ goto exit;
+}
+
+void
+pool_ref_get(struct pool* pool)
+{
+ ASSERT(pool);
+ ref_get(&pool->ref);
+}
+
+void
+pool_ref_put(struct pool* pool)
+{
+ ASSERT(pool);
+ ref_put(&pool->ref, release_pool);
+}
+
+struct partition*
+pool_next_partition(struct pool* pool)
+{
+ struct list_node* node = NULL;
+ struct partition* partition = NULL;
+ ASSERT(pool);
+
+ mutex_lock(pool->mutex);
+
+ /* Waits for a free partition */
+ while(is_list_empty(&pool->parts_free) && !pool->error) {
+ cond_wait(pool->cond_new, pool->mutex);
+ }
+
+ if(pool->error) {
+ /* An error occurs */
+ partition = NULL;
+ mutex_unlock(pool->mutex);
+
+ } else {
+ size_t ipartition;
+
+ /* Retrieve the next available partition */
+ node = list_head(&pool->parts_free);
+ list_del(node);
+ ipartition = pool->next_part_id++;
+ mutex_unlock(pool->mutex);
+
+ partition = CONTAINER_OF(node, struct partition, node);
+ partition->id = ipartition;
+ }
+ return partition;
+}
+
+void
+pool_free_partition(struct pool* pool, struct partition* partition)
+{
+ ASSERT(pool && partition);
+
+ /* Register the partition as a free partition */
+ mutex_lock(pool->mutex);
+ list_move_tail(&partition->node, &pool->parts_free);
+ mutex_unlock(pool->mutex);
+
+ /* Notify a thread waiting for a free partition that we just registered one */
+ cond_signal(pool->cond_new);
+}
+
+void
+pool_commit_partition(struct pool* pool, struct partition* partition)
+{
+ struct list_node* node = NULL;
+ ASSERT(pool && partition);
+
+ /* Committed partitions are sorted in ascending order of their id. We are
+ * therefore looking for the partition whose id is less than the id of the
+ * partition to be committed in order to add the latter in the right place */
+ mutex_lock(pool->mutex);
+ LIST_FOR_EACH_REVERSE(node, &pool->parts_full) {
+ struct partition* partition2 = CONTAINER_OF(node, struct partition, node);
+ if(partition2->id < partition->id) break;
+ }
+ list_add(node, &partition->node);
+ mutex_unlock(pool->mutex);
+
+ /* Notify a thread waiting for a valid partition that we just registered one */
+ cond_signal(pool->cond_fetch);
+}
+
+struct partition*
+pool_fetch_partition(struct pool* pool, const size_t ipartition)
+{
+ struct partition* found_partition = NULL;
+ struct list_node* node = NULL;
+ ASSERT(pool);
+
+ mutex_lock(pool->mutex);
+ while(!pool->error) {
+
+ /* Finds the partition that matches the submitted id */
+ LIST_FOR_EACH(node, &pool->parts_full) {
+ struct partition* partition = CONTAINER_OF(node, struct partition, node);
+
+ /* The partition to fetch has been found */
+ if(partition->id == ipartition) {
+ found_partition = partition;
+ break;
+ }
+
+ /* Partitions are sorted in ascending order. Stop the linear search if the
+ * current id is greater than the submitted one */
+ if(partition->id > ipartition) break;
+ }
+
+ if(!found_partition) {
+ /* The partition is still not committed. The thread is waiting for it */
+ cond_wait(pool->cond_fetch, pool->mutex);
+
+ } else {
+ /* Remove the found partition from the list of committed partition */
+ list_del(&found_partition->node);
+ break;
+ }
+ }
+ mutex_unlock(pool->mutex);
+
+ return found_partition;
+}
+
+void
+pool_invalidate(struct pool* pool)
+{
+ ASSERT(pool);
+
+ /* Notifies that an error has occurred */
+ mutex_lock(pool->mutex);
+ pool->error = 1;
+ mutex_unlock(pool->mutex);
+
+ /* Wakes up all the waiting threads */
+ cond_broadcast(pool->cond_new);
+ cond_broadcast(pool->cond_fetch);
+}
diff --git a/src/rnatm_voxel_partition.h b/src/rnatm_voxel_partition.h
@@ -0,0 +1,133 @@
+/* Copyright (C) 2022 Centre National de la Recherche Scientifique
+ * Copyright (C) 2022 Institut de Physique du Globe de Paris
+ * Copyright (C) 2022 |Méso|Star> (contact@meso-star.com)
+ * Copyright (C) 2022 Université de Reims Champagne-Ardenne
+ * Copyright (C) 2022 Université de Versaille Saint-Quentin
+ * Copyright (C) 2022 Université Paul Sabatier (contact@laplace.univ-tlse.fr)
+ *
+ * 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 RNATM_VOXEL_PARTITION_H
+#define RNATM_VOXEL_PARTITION_H
+
+#include "rnatm_voxel.h"
+
+#include <rsys/list.h>
+#include <rsys/rsys.h>
+
+/* Definition of a partition along the 3 axes */
+#define PARTITION_LOG2_DEFINITION 5 /* 5 */
+#define PARTITION_DEFINITION BIT(PARTITION_LOG2_DEFINITION) /* 2^5 = 32 */
+#define PARTITION_NVOXELS \
+ ( PARTITION_DEFINITION \
+ * PARTITION_DEFINITION \
+ * PARTITION_DEFINITION)
+
+/******************************************************************************
+ * Partition of voxels, i.e. collection of three-dimensional arrays of voxels
+ ******************************************************************************/
+struct partition {
+ /* Voxels ordered wrt their Morton code */
+ float voxels[PARTITION_NVOXELS * NFLOATS_PER_VOXEL];
+
+ struct list_node node;
+ size_t id; /* Unique identifier of the partition */
+};
+
+static INLINE void
+partition_init(struct partition* partition)
+{
+ ASSERT(partition);
+ list_init(&partition->node);
+ partition->id = SIZE_MAX;
+}
+
+static INLINE void
+partition_release(struct partition* partition)
+{
+ ASSERT(partition && is_list_empty(&partition->node));
+ (void)partition; /* Nothing to do */
+}
+
+static FINLINE float*
+partition_get_voxel
+ (struct partition* partition,
+ const size_t ivoxel) /* Morton code of the voxel */
+{
+ ASSERT(partition && ivoxel < PARTITION_NVOXELS);
+ return partition->voxels + ivoxel * NFLOATS_PER_VOXEL;
+}
+
+static INLINE void
+partition_clear_voxels(struct partition* partition)
+{
+ size_t ivoxel;
+ FOR_EACH(ivoxel, 0, PARTITION_NVOXELS) {
+ float* voxel = partition_get_voxel(partition, ivoxel);
+ voxel_clear(voxel);
+ }
+}
+
+/******************************************************************************
+ * Partition pool, i.e. collection of partitions of voxels that accessible
+ * concurrently by several threads
+ ******************************************************************************/
+struct pool;
+
+extern LOCAL_SYM res_T
+pool_create
+ (struct mem_allocator* allocator, /* May be NULL <=> default allocator */
+ const size_t npartitions, /* #partitions managed by the pool */
+ struct pool** pool);
+
+extern LOCAL_SYM void
+pool_ref_get
+ (struct pool* pool);
+
+extern LOCAL_SYM void
+pool_ref_put
+ (struct pool* pool);
+
+/* Returns a free partition. Waits for a free partition to be available.
+ * Returns NULL if an error occurs */
+extern LOCAL_SYM struct partition*
+pool_next_partition
+ (struct pool* pool);
+
+/* Saves the partition in the pool as a free partition */
+extern LOCAL_SYM void
+pool_free_partition
+ (struct pool* pool,
+ struct partition* partition);
+
+/* Save partition as a valid recoverable partition, i.e. it can be fetched */
+extern LOCAL_SYM void
+pool_commit_partition
+ (struct pool* pool,
+ struct partition* partition);
+
+/* Returns the partition with the identifier 'ipartition'. Waits for the
+ * partition to be available. Returns NULL if an error occurs */
+extern LOCAL_SYM struct partition*
+pool_fetch_partition
+ (struct pool* pool,
+ const size_t ipartition);
+
+/* Makes the pool invalid. Once invalidated, the 'next' and 'fetch'
+ * functions return NULL */
+extern LOCAL_SYM void
+pool_invalidate
+ (struct pool* pool);
+
+#endif /* RNATM_VOXEL_PARTITION_H */