rnatm

Load and structure data describing an atmosphere
git clone git://git.meso-star.fr/rnatm.git
Log | Files | Refs | README | LICENSE

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:
Mcmake/CMakeLists.txt | 7+++++--
Msrc/rnatm.h | 12++++++++++++
Asrc/rnatm_voxel.h | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/rnatm_voxel_partition.c | 270+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/rnatm_voxel_partition.h | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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 */