rnatm

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

commit afcaf41d20acedd247a969b1387f38ca6807de44
parent aff0cf67939db10a2cb5261dc1eadf82c0b425a6
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri, 22 Jul 2022 17:24:30 +0200

Continue the building of the octrees

Diffstat:
Msrc/rnatm.h | 1+
Msrc/rnatm_c.h | 11++++++++++-
Msrc/rnatm_octree.c | 380+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/rnatm_voxel_partition.c | 37+++++++++++++++++++++++++++----------
Msrc/rnatm_voxel_partition.h | 25+++++++++++++------------
5 files changed, 423 insertions(+), 31 deletions(-)

diff --git a/src/rnatm.h b/src/rnatm.h @@ -50,6 +50,7 @@ struct mem_allocator; enum rnatm_radcoef { RNATM_RADCOEF_Ka, /* Absorption coefficient */ RNATM_RADCOEF_Ks, /* Scattering coefficient */ + RNATM_RADCOEF_Kext, /* Extinction coefficient */ RNATM_RADCOEFS_COUNT__ }; diff --git a/src/rnatm_c.h b/src/rnatm_c.h @@ -21,7 +21,7 @@ #ifndef RNATM_C_H #define RNATM_C_H -#include <rsys/dynamic_array.h> +#include <rsys/dynamic_array_size_t.h> #include <rsys/logger.h> #include <rsys/ref_count.h> #include <rsys/str.h> @@ -29,6 +29,15 @@ struct rnatm_create_args; struct rnsf; +/* Generate the dynamic array of dynamic array of size_t */ +#define DARRAY_NAME size_t_list +#define DARRAY_DATA struct darray_size_t +#define DARRAY_FUNCTOR_INIT darray_size_t_init +#define DARRAY_FUNCTOR_RELEASE darray_size_t_release +#define DARRAY_FUNCTOR_COPY darray_size_t_copy +#define DARRAY_FUNCTOR_COPY_AND_RELEASE darray_size_t_copy_and_release +#include <rsys/dynamic_array.h> + /******************************************************************************* * Phase function ******************************************************************************/ diff --git a/src/rnatm_octree.c b/src/rnatm_octree.c @@ -29,6 +29,7 @@ #include <rsys/cstr.h> #include <rsys/math.h> +#include <rsys/morton.h> #include <rsys/rsys.h> #include <math.h> /* lround */ @@ -59,6 +60,22 @@ round_pow2(const unsigned val) } } +static INLINE void +register_tetra + (const struct suvm_primitive* prim, + const double low[3], + const double upp[3], + void* context) +{ + struct darray_size_t* tetra_ids = context; + ASSERT(prim && low && upp && context); + ASSERT(low[0] < upp[0]); + ASSERT(low[1] < upp[1]); + ASSERT(low[2] < upp[2]); + (void)low, (void)upp; + CHK(darray_size_t_push_back(tetra_ids, &prim->iprim) == RES_OK); +} + static res_T compute_grid_definition(struct rnatm* atm, const struct rnatm_create_args* args) { @@ -114,7 +131,336 @@ compute_grid_definition(struct rnatm* atm, const struct rnatm_create_args* args) } static res_T -build_octrees(struct rnatm* atm, const struct rnatm_create_args* args) +update_voxel + (struct rnatm* atm, + const struct suvm_primitive* tetra, + struct partition* part, + const uint64_t vx_mcode) +{ + size_t iquad_pt; + size_t iband; + size_t nbands; + float* vx = NULL; + ASSERT(atm && tetra && part); + ASSERT(tetra->nvertices == 4); + + nbands = sck_get_bands_count(atm->gas.ck); + + #define REDUX_MIN(V4) MMIN(MMIN((V4)[0], (V4)[1]), MMIN((V4)[2], V4[3])) + #define REDUX_MAX(V4) MMAX(MMAX((V4)[0], (V4)[1]), MMAX((V4)[2], V4[3])) + + FOR_EACH(iband, 0, nbands) { + struct sck_band band; + float tetra_ks[4]; + float tetra_ks_min, tetra_ks_max; + + /* Compute the scattering coefficient range of the tetrahedron */ + SCK(get_band(atm->gas.ck, iband, &band)); + tetra_ks[0] = band.ks_list[tetra->indices[0]]; + tetra_ks[1] = band.ks_list[tetra->indices[1]]; + tetra_ks[2] = band.ks_list[tetra->indices[2]]; + tetra_ks[3] = band.ks_list[tetra->indices[3]]; + tetra_ks_min = REDUX_MIN(tetra_ks); + tetra_ks_max = REDUX_MAX(tetra_ks); + + FOR_EACH(iquad_pt, 0, band.quad_pts_count) { + struct sck_quad_pt quad_pt; + float tetra_ka[4]; + float tetra_kext[4]; + float tetra_ka_min, tetra_ka_max; + float tetra_kext_min, tetra_kext_max; + float vx_ka_min, vx_ka_max; + float vx_ks_min, vx_ks_max; + float vx_kext_min, vx_kext_max; + + /* Compute the absorption coefficient range of the tetrahedron */ + SCK(band_get_quad_pt(&band, iquad_pt, &quad_pt)); + tetra_ka[0] = quad_pt.ka_list[tetra->indices[0]]; + tetra_ka[1] = quad_pt.ka_list[tetra->indices[1]]; + tetra_ka[2] = quad_pt.ka_list[tetra->indices[2]]; + tetra_ka[3] = quad_pt.ka_list[tetra->indices[3]]; + tetra_ka_min = REDUX_MIN(tetra_ka); + tetra_ka_max = REDUX_MAX(tetra_ka); + + /* Compute the extinction coefficient range of the tetrahedron */ + tetra_kext[0] = tetra_ka[0] + tetra_ks[0]; + tetra_kext[1] = tetra_ka[1] + tetra_ks[1]; + tetra_kext[2] = tetra_ka[2] + tetra_ks[2]; + tetra_kext[3] = tetra_ka[3] + tetra_ks[3]; + tetra_kext_min = REDUX_MIN(tetra_kext); + tetra_kext_max = REDUX_MAX(tetra_kext); + + vx = partition_get_voxel(part, iband, iquad_pt, vx_mcode); + + /* Update the range of the radiative coefficients of the voxel */ + vx_ka_min = vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MIN)]; + vx_ka_max = vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MAX)]; + vx_ks_min = vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MIN)]; + vx_ks_max = vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MAX)]; + vx_kext_min = vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MIN)]; + vx_kext_max = vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MAX)]; + vx_ka_min = MMIN(vx_ka_min, tetra_ka_min); + vx_ka_max = MMAX(vx_ka_max, tetra_ka_max); + vx_ks_min = MMIN(vx_ks_min, tetra_ks_min); + vx_ks_max = MMAX(vx_ks_max, tetra_ks_max); + vx_kext_min = MMIN(vx_kext_min, tetra_kext_min); + vx_kext_max = MMAX(vx_kext_max, tetra_kext_max); + vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MIN)] = vx_ka_min; + vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MAX)] = vx_ka_max; + vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MIN)] = vx_ks_min; + vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MAX)] = vx_ks_max; + vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MIN)] = vx_kext_min; + vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MAX)] = vx_kext_max; + } + } + #undef REDUX_MIN + #undef REDUX_MAX + + return RES_OK; +} + +static res_T +voxelize_gas + (struct rnatm* atm, + const double part_low[3], + const double part_upp[3], + const double vxsz[3], + const struct darray_size_t* tetra_ids, + struct partition* part) +{ + size_t i; + res_T res = RES_OK; + ASSERT(atm && part_low && part_upp && tetra_ids && part); + ASSERT(vxsz[0] > 0 && vxsz[1] > 0 && vxsz[2] > 0); + ASSERT(part_low[0] < part_upp[0]); + ASSERT(part_low[1] < part_upp[1]); + ASSERT(part_low[2] < part_upp[2]); + + partition_clear_voxels(part); + + FOR_EACH(i, 0, darray_size_t_size_get(tetra_ids)) { + struct suvm_primitive tetra; + struct suvm_polyhedron poly; + double poly_low[3]; + double poly_upp[3]; + float vx_low[3]; + float vx_upp[3]; + uint32_t ivx_low[3]; + uint32_t ivx_upp[3]; + uint32_t ivx[3]; + uint64_t mcode[3]; /* Cache of 3D morton code */ + const size_t itetra = darray_size_t_cdata_get(tetra_ids)[i]; + enum suvm_intersection_type intersect; + + /* Recover the tetrahedron and setup its polyhedron */ + SUVM(volume_get_primitive(atm->gas.volume, itetra, &tetra)); + SUVM(primitive_setup_polyhedron(&tetra, &poly)); + ASSERT(poly.lower[0] <= part_upp[0] && poly.upper[0] >= part_low[0]); + ASSERT(poly.lower[1] <= part_upp[1] && poly.upper[1] >= part_low[1]); + ASSERT(poly.lower[2] <= part_upp[2] && poly.upper[2] >= part_low[2]); + + /* Clamp the AABB of the polyhedra to the partition bounds */ + poly_low[0] = MMAX(poly.lower[0], part_low[0]); + poly_low[1] = MMAX(poly.lower[1], part_low[1]); + poly_low[2] = MMAX(poly.lower[2], part_low[2]); + poly_upp[0] = MMIN(poly.upper[0], part_upp[0]); + poly_upp[1] = MMIN(poly.upper[1], part_upp[1]); + poly_upp[2] = MMIN(poly.upper[2], part_upp[2]); + + /* Transform the AABB of the polyhedron in voxel space of the partition */ + ivx_low[0] = (uint32_t)((poly_low[0] - part_low[0]) / vxsz[0]); + ivx_low[1] = (uint32_t)((poly_low[1] - part_low[1]) / vxsz[1]); + ivx_low[2] = (uint32_t)((poly_low[2] - part_low[2]) / vxsz[2]); + ivx_upp[0] = (uint32_t)ceil((poly_upp[0] - part_low[0]) / vxsz[0]); + ivx_upp[1] = (uint32_t)ceil((poly_upp[1] - part_low[1]) / vxsz[1]); + ivx_upp[2] = (uint32_t)ceil((poly_upp[2] - part_low[2]) / vxsz[2]); + ASSERT(ivx_upp[0] <= partition_get_definition(part)); + ASSERT(ivx_upp[1] <= partition_get_definition(part)); + ASSERT(ivx_upp[2] <= partition_get_definition(part)); + + /* Iterate voxels intersected by the AABB of the polyedron */ + FOR_EACH(ivx[2], ivx_low[2], ivx_upp[2]) { + vx_low[2] = (float)((double)ivx[2]*vxsz[2] + part_low[2]); + vx_upp[2] = vx_low[2] + (float)vxsz[2]; + mcode[2] = morton3D_encode_u21(ivx[2]); + + FOR_EACH(ivx[1], ivx_low[1], ivx_upp[1]) { + vx_low[1] = (float)((double)ivx[1]*vxsz[1] + part_low[1]); + vx_upp[1] = vx_low[1] + (float)vxsz[1]; + mcode[1] = (morton3D_encode_u21(ivx[1]) << 1) | mcode[2]; + + FOR_EACH(ivx[0], ivx_low[0], ivx_upp[0]) { + vx_low[0] = (float)((double)ivx[0]*vxsz[0] + part_low[0]); + vx_upp[0] = vx_low[0] + (float)vxsz[0]; + mcode[0] = (morton3D_encode_u21(ivx[0]) << 2) | mcode[1]; + + intersect = suvm_polyhedron_intersect_aabb(&poly, vx_low, vx_upp); + if(intersect == SUVM_INTERSECT_NONE) continue; + + res = update_voxel(atm, &tetra, part, mcode[0]); + if(res != RES_OK) goto error; + } + } + } + } +exit: + return res; +error: + partition_clear_voxels(part); + goto exit; +} + +static res_T +voxelize_partition + (struct rnatm* atm, + const double part_low[3], + const double part_upp[3], + const double vxsz[3], + struct darray_size_t* tetra_ids, + struct partition* part) +{ + res_T res = RES_OK; + ASSERT(atm && part_low && part_upp && tetra_ids && part); + ASSERT(part_low[0] < part_upp[0]); + ASSERT(part_low[1] < part_upp[1]); + ASSERT(part_low[2] < part_upp[2]); + + /* Find the list of gas tetrahedra that overlap the partition */ + darray_size_t_clear(tetra_ids); + res = suvm_volume_intersect_aabb + (atm->gas.volume, part_low, part_upp, register_tetra, tetra_ids); + if(res != RES_OK) goto error; + + res = voxelize_gas(atm, part_low, part_upp, vxsz, tetra_ids, part); + if(res != RES_OK) goto error; + + /* TODO voxelise aerosols */ + +exit: + return res; +error: + goto exit; +} + +static res_T +voxelize_atmosphere(struct rnatm* atm, struct pool* pool) +{ + struct darray_size_t_list per_thread_tetra_list; + double atm_low[3]; + double atm_upp[3]; + double vxsz[3]; + size_t nparts[3]; /* #partitions along the 3 axis */ + size_t nparts_adjusted; + size_t part_def; /* Definition of a partition */ + int64_t i; + int progress = 0; + ATOMIC nparts_voxelized = 0; + ATOMIC res = RES_OK; + ASSERT(atm); + + /* Allocate the per thread list of tetra list */ + darray_size_t_list_init(atm->allocator, &per_thread_tetra_list); + res = darray_size_t_list_resize(&per_thread_tetra_list, atm->nthreads); + if(res != RES_OK) goto error; + + /* Recover the AABB atmosphere and compute the size of a voxel */ + SUVM(volume_get_aabb(atm->gas.volume, atm_low, atm_upp)); + vxsz[0] = (atm_upp[0] - atm_low[0]) / (double)atm->grid_definition[0]; + vxsz[1] = (atm_upp[1] - atm_low[1]) / (double)atm->grid_definition[1]; + vxsz[2] = (atm_upp[2] - atm_low[2]) / (double)atm->grid_definition[2]; + + /* Number of partitions required to cover the entire atmosphere grid */ + part_def = pool_get_partition_definition(pool); + nparts[0] = (atm->grid_definition[0] + (part_def-1)/*ceil*/) / part_def; + nparts[1] = (atm->grid_definition[1] + (part_def-1)/*ceil*/) / part_def; + nparts[2] = (atm->grid_definition[2] + (part_def-1)/*ceil*/) / part_def; + + /* Adjust the #partitions allowing their indexing by their morton code */ + nparts_adjusted = MMAX(nparts[0], MMAX(nparts[1], nparts[2])); + nparts_adjusted = round_up_pow2(nparts_adjusted); + nparts_adjusted = nparts_adjusted * nparts_adjusted * nparts_adjusted; + + /* Print status message */ + #define LOG_MSG "Voxelization of atmosphere meshes: %3d%%\r" + log_info(atm, LOG_MSG, 0); + + /* Iterates over the partitions of the grid according to their Morton order + * and voxelizes the tetrahedrons that overlap them */ + omp_set_num_threads((int)atm->nthreads); + #pragma omp parallel for schedule(static, 1/*chunk size*/) + for(i = 0; i < (int64_t)nparts_adjusted; ++i) { + struct darray_size_t* tetra_ids = NULL; + struct partition* part = NULL; + double part_low[3]; + double part_upp[3]; + uint32_t part_ids[3]; + size_t n; + const int ithread = omp_get_thread_num(); + int pcent; + res_T res_local = RES_OK; + + if(ATOMIC_GET(&res) != RES_OK) continue; + + /* Recover current partition */ + part = pool_next_partition(pool); + if(!part) { ATOMIC_SET(&res, RES_UNKNOWN_ERR); continue; }; + + /* Is the partition out of bounds of the atmosphere grid*/ + morton_xyz_decode_u21((uint64_t)partition_get_id(part), part_ids); + if(part_ids[0] >= nparts[0] + || part_ids[1] >= nparts[1] + || part_ids[2] >= nparts[2]) { + pool_free_partition(pool, part); + continue; + } + + /* Compute the AABB of the partition */ + part_low[0] = (double)(part_ids[0] * part_def) * vxsz[0] + atm_low[0]; + part_low[1] = (double)(part_ids[1] * part_def) * vxsz[1] + atm_low[1]; + part_low[2] = (double)(part_ids[2] * part_def) * vxsz[2] + atm_low[2]; + part_upp[0] = part_low[0] + (double)part_def * vxsz[0]; + part_upp[1] = part_low[1] + (double)part_def * vxsz[1]; + part_upp[2] = part_low[2] + (double)part_def * vxsz[2]; + + /* Retrieves the array where to store the indices of tetrahedra that + * overlap the partition */ + tetra_ids = darray_size_t_list_data_get(&per_thread_tetra_list) + ithread; + + /* Voxelizes the partition and once done, commits */ + res_local = voxelize_partition + (atm, part_low, part_upp, vxsz, tetra_ids, part); + if(res_local == RES_OK) { + pool_commit_partition(pool, part); + } else { + pool_free_partition(pool, part); + ATOMIC_SET(&res, res_local); + continue; + }; + + /* Update progress bar */ + n = (size_t)ATOMIC_INCR(&nparts_voxelized); + pcent = (int)((n * 100) / (nparts[0]*nparts[1]*nparts[2])); + #pragma omp critical + if(pcent > progress) { + progress = pcent; + log_info(atm, LOG_MSG, pcent); + } + } + if(res != RES_OK) goto error; + + /* Print final status message */ + log_info(atm, LOG_MSG"\n", 100); + #undef LOG_MSG + +exit: + darray_size_t_list_release(&per_thread_tetra_list); + return (res_T)res; +error: + goto exit; +} + +static res_T +build_octrees(struct rnatm* atm) { /* Empirical constant that defines the number of voxel partitions to * pre-allocate per thread to avoid contension between the thread building the @@ -124,8 +470,8 @@ build_octrees(struct rnatm* atm, const struct rnatm_create_args* args) struct pool_create_args pool_args = POOL_CREATE_ARGS_DEFAULT; struct pool* pool = NULL; - res_T res = RES_OK; - ASSERT(atm && args); + ATOMIC res = RES_OK; + ASSERT(atm); /* Create the vortex partition pool */ pool_args.npartitions = NPARTITIONS_PER_THREAD * atm->nthreads; @@ -137,7 +483,7 @@ build_octrees(struct rnatm* atm, const struct rnatm_create_args* args) res = pool_create(&pool_args, &pool); if(res != RES_OK) { log_err(atm, "Failed to create the voxel partition pool -- %s\n", - res_to_cstr(res)); + res_to_cstr((res_T)res)); goto error; } @@ -148,12 +494,30 @@ build_octrees(struct rnatm* atm, const struct rnatm_create_args* args) * properties */ omp_set_nested(1); - /* TODO build the octrees */ - (void)args; + #pragma omp parallel sections num_threads(2) + { + #pragma omp section + { + const res_T res_local = voxelize_atmosphere(atm, pool); + if(res_local != RES_OK) { + log_err(atm, "Atmosphere voxelization error -- %s\n", + res_to_cstr(res_local)); + pool_invalidate(pool); + ATOMIC_SET(&res, res_local); + } + } + + #pragma omp section + { + /* TODO Build the octreese */ + FATAL("Not fully implemented yet\n"); + } + } + if(res != RES_OK) goto error; exit: if(pool) pool_ref_put(pool); - return res; + return (res_T)res; error: goto exit; } @@ -169,7 +533,7 @@ setup_octrees(struct rnatm* atm, const struct rnatm_create_args* args) res = compute_grid_definition(atm, args); if(res != RES_OK) goto error; - res = build_octrees(atm, args); + res = build_octrees(atm); if(res != RES_OK) goto error; exit: diff --git a/src/rnatm_voxel_partition.c b/src/rnatm_voxel_partition.c @@ -18,25 +18,18 @@ * 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_c.h" #include "rnatm_log.h" #include "rnatm_voxel_partition.h" #include <rsys/condition.h> -#include <rsys/dynamic_array_size_t.h> #include <rsys/mem_allocator.h> #include <rsys/mutex.h> #include <rsys/ref_count.h> -/* Generate the dynamic array of dynamic array of size_t */ -#define DARRAY_NAME size_t_list -#define DARRAY_DATA struct darray_size_t -#define DARRAY_FUNCTOR_INIT darray_size_t_init -#define DARRAY_FUNCTOR_RELEASE darray_size_t_release -#define DARRAY_FUNCTOR_COPY darray_size_t_copy -#define DARRAY_FUNCTOR_COPY_AND_RELEASE darray_size_t_copy_and_release -#include <rsys/dynamic_array.h> - struct partition { + size_t definition; + /* Size of a cluster in bytes. A cluster is a list of voxels for a quadrature * point of a spectral band */ size_t cluster_size; @@ -72,6 +65,7 @@ struct pool { struct cond* cond_fetch; size_t next_part_id; /* Indentifier of the next partition */ + size_t partition_definition; /* #voxels along the 3 axis */ struct mem_allocator* allocator; ATOMIC error; /* Is the pool not valid? */ @@ -118,6 +112,7 @@ setup_partition_clusters /* Compute the size of a cluster. Ensure a multiple of 64 bytes, i.e. align * the cluster of voxels on the size of a cache line */ + partition->definition = args->partition_definition; partition->cluster_nvoxels = args->partition_definition * args->partition_definition @@ -253,6 +248,20 @@ partition_ref_put(struct partition* partition) ref_put(&partition->ref, release_partition); } +size_t +partition_get_id(const struct partition* partition) +{ + ASSERT(partition); + return partition->id; +} + +size_t +partition_get_definition(const struct partition* partition) +{ + ASSERT(partition); + return partition->definition; +} + float* partition_get_voxel (struct partition* part, @@ -322,6 +331,7 @@ pool_create goto error; } pool->allocator = allocator; + pool->partition_definition = args->partition_definition; ref_init(&pool->ref); list_init(&pool->parts_free); list_init(&pool->parts_commit); @@ -366,6 +376,13 @@ pool_ref_put(struct pool* pool) ref_put(&pool->ref, release_pool); } +size_t +pool_get_partition_definition(const struct pool* pool) +{ + ASSERT(pool); + return pool->partition_definition; +} + struct partition* pool_next_partition(struct pool* pool) { diff --git a/src/rnatm_voxel_partition.h b/src/rnatm_voxel_partition.h @@ -26,17 +26,6 @@ #include <rsys/list.h> #include <rsys/rsys.h> -/* TODO remove this */ -#if 0 -/* 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) -#endif - struct pool_create_args { size_t npartitions; /* Number of partitions to preallocate */ @@ -56,7 +45,15 @@ static const struct pool_create_args POOL_CREATE_ARGS_DEFAULT = ******************************************************************************/ struct partition; -extern LOCAL_SYM FINLINE float* +extern LOCAL_SYM size_t +partition_get_id + (const struct partition* partition); + +extern LOCAL_SYM size_t +partition_get_definition + (const struct partition* partition); + +extern LOCAL_SYM float* partition_get_voxel (struct partition* partition, const size_t iband, @@ -86,6 +83,10 @@ extern LOCAL_SYM void pool_ref_put (struct pool* pool); +extern LOCAL_SYM size_t +pool_get_partition_definition + (const 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*