rnatm

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

commit 9157d07191280f7915ec2a6947b44d00f864ba77
parent c91f8ebe300d244447dc828766a411c06d0e3002
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 30 Aug 2022 16:18:05 +0200

Kext<min|max> are no longer stored in voxels

They are calculated at runtime from ka<min|max> and ks<min|max> to
reduce voxel memory space.

Diffstat:
Msrc/rnatm.c | 35+++++++++++++++++++++++++----------
Msrc/rnatm_octree.c | 73+++++++++++++++++++++++--------------------------------------------------
Msrc/rnatm_voxel.h | 36++++++++++--------------------------
Msrc/rnatm_write_vtk_octree.c | 1+
4 files changed, 59 insertions(+), 86 deletions(-)

diff --git a/src/rnatm.c b/src/rnatm.c @@ -259,24 +259,39 @@ rnatm_get_k_svx_voxel const enum rnatm_svx_op op) { const float* vx; - float k_min; - float k_max; ASSERT(atm && voxel && radcoef); ASSERT((unsigned)radcoef < RNATM_RADCOEFS_COUNT__); ASSERT((unsigned)op < RNATM_SVX_OPS_COUNT__); (void)atm; vx = voxel->data; - k_min = vx[voxel_idata(radcoef, RNATM_SVX_OP_MIN)]; - k_max = vx[voxel_idata(radcoef, RNATM_SVX_OP_MAX)]; - /* Return a zero radiative coefficient for empty voxels */ - if(k_min > k_max) return 0; + /* Absorption/Scattering coefficient */ + if(radcoef != RNATM_RADCOEF_Kext) { + const float k_min = vx[voxel_idata(radcoef, RNATM_SVX_OP_MIN)]; + const float k_max = vx[voxel_idata(radcoef, RNATM_SVX_OP_MAX)]; + if(k_min > k_max) return 0; /* Empty voxel => null radiative coefficient */ + switch(op) { + case RNATM_SVX_OP_MIN: return k_min; + case RNATM_SVX_OP_MAX: return k_max; + default: FATAL("Unreachable code\n"); break; + } - switch(op) { - case RNATM_SVX_OP_MIN: return k_min; - case RNATM_SVX_OP_MAX: return k_max; - default: FATAL("Unreachable code\n"); break; + /* Extinction coefficient */ + } else { + const float ka_min = vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MIN)]; + const float ka_max = vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MAX)]; + const float ks_min = vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MIN)]; + const float ks_max = vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MAX)]; + + /* Empty voxel => null radiative coefficient */ + if(ka_min > ka_max) { ASSERT(ks_min > ks_max); return 0; } + + switch(op) { + case RNATM_SVX_OP_MIN: return ka_min + ks_min; + case RNATM_SVX_OP_MAX: return ka_max + ks_max; + default: FATAL("Unreachable code\n"); break; + } } } diff --git a/src/rnatm_octree.c b/src/rnatm_octree.c @@ -70,7 +70,6 @@ static const struct build_octree_context BUILD_OCTREE_CONTEXT_NULL = struct radcoefs { float ka_min, ka_max; float ks_min, ks_max; - float kext_min, kext_max; }; #define RADCOEFS_NULL__ { \ FLT_MAX, -FLT_MAX, \ @@ -266,20 +265,20 @@ find_band_range nbands = sck_get_bands_count(atm->gas.ck); - /* Find the lower bound */ + /* Find the lower bound (inclusive) */ FOR_EACH(ilow, 0, nbands) { SCK(get_band(atm->gas.ck, ilow, &band_low)); if(band_low.upper >= range[0]) break; } - /* Find the upper bound */ + /* Find the upper bound (inclusive) */ FOR_EACH(iupp, ilow, nbands) { SCK(get_band(atm->gas.ck, iupp, &band_upp)); - if(band_upp.lower > range[1]) break; + if(band_upp.upper > range[1]) break; } bands[0] = ilow; - bands[1] = iupp - 1; /* Make the boundary inclusive */ + bands[1] = iupp; /* Make the boundary inclusive */ if(bands[0] > bands[1]) { log_err(atm, @@ -462,7 +461,6 @@ setup_tetra_radcoefs_gas { float ka[4]; float ks[4]; - float kext[4]; struct sck_band band; struct sck_quad_pt quad_pt; ASSERT(atm && tetra && radcoefs); @@ -485,14 +483,6 @@ setup_tetra_radcoefs_gas ka[3] = quad_pt.ka_list[tetra->indices[3]]; radcoefs->ka_min = MMIN(MMIN(ka[0], ka[1]), MMIN(ka[2], ka[3])); radcoefs->ka_max = MMAX(MMAX(ka[0], ka[1]), MMAX(ka[2], ka[3])); - - /* Compute the extinction coefficient range of the tetrahedron */ - kext[0] = ka[0] + ks[0]; - kext[1] = ka[1] + ks[1]; - kext[2] = ka[2] + ks[2]; - kext[3] = ka[3] + ks[3]; - radcoefs->kext_min = MMIN(MMIN(kext[0], kext[1]), MMIN(kext[2], kext[3])); - radcoefs->kext_max = MMAX(MMAX(kext[0], kext[1]), MMAX(kext[2], kext[3])); } static void @@ -507,7 +497,7 @@ setup_tetra_radcoefs_aerosol struct sars_band ars_band; double gas_spectral_range[2]; /* In nm */ size_t ars_ibands[2]; - float ka[4], ks[4], kext[4]; + float ka[4], ks[4]; ASSERT(atm && aerosol && tetra && radcoefs); /* Look for the aerosol bands covered by the gas band */ @@ -522,8 +512,6 @@ setup_tetra_radcoefs_aerosol radcoefs->ks_max =-FLT_MAX; radcoefs->ka_min = FLT_MAX; radcoefs->ka_max =-FLT_MAX; - radcoefs->kext_min = FLT_MAX; - radcoefs->kext_max =-FLT_MAX; return; } @@ -551,29 +539,22 @@ setup_tetra_radcoefs_aerosol radcoefs->ka_min = MMIN(MMIN(ka[0], ka[1]), MMIN(ka[2], ka[3])); radcoefs->ka_max = MMAX(MMAX(ka[0], ka[1]), MMAX(ka[2], ka[3])); - /* Compute the extinction coefficient range of the tetrahedron */ - kext[0] = ka[0] + ks[0]; - kext[1] = ka[1] + ks[1]; - kext[2] = ka[2] + ks[2]; - kext[3] = ka[3] + ks[3]; - radcoefs->kext_min = MMIN(MMIN(kext[0], kext[1]), MMIN(kext[2], kext[3])); - radcoefs->kext_max = MMAX(MMAX(kext[0], kext[1]), MMAX(kext[2], kext[3])); - /* The gas band overlaid N aerosol bands (N >= 1) */ } else { float tau_ka[4] = {0, 0, 0, 0}; float tau_ks[4] = {0, 0, 0, 0}; + double lambda_min; + double lambda_max; float rcp_gas_band_len; size_t iars_band; FOR_EACH(iars_band, ars_ibands[0], ars_ibands[1]+1) { - double lambda_min; - double lambda_max; - float lambda_len; + float lambda_len; SARS(get_band(aerosol->sars, iars_band, &ars_band)); lambda_min = MMAX(gas_band.lower, ars_band.lower); - lambda_max = MMIN(gas_band.upper, ars_band.upper); + lambda_max = MMIN(gas_band.upper, ars_band.upper); /* exclusive */ + lambda_max = nextafter(lambda_max, 0); /* inclusive */ lambda_len = (float)(lambda_max - lambda_min); ASSERT(lambda_len > 0); @@ -584,24 +565,22 @@ setup_tetra_radcoefs_aerosol tau_ka[0] += ars_band.ka_list[tetra->indices[0]] * lambda_len; tau_ka[1] += ars_band.ka_list[tetra->indices[1]] * lambda_len; - tau_ka[2] += ars_band.ka_list[tetra->indices[2]] * lambda_len; - tau_ka[3] += ars_band.ka_list[tetra->indices[3]] * lambda_len; + tau_ka[2] += ars_band.ka_list[tetra->indices[2]] * lambda_len; + tau_ka[3] += ars_band.ka_list[tetra->indices[3]] * lambda_len; } /* Compute the radiative coefficients of the tetrahedron */ - ASSERT(gas_band.upper > gas_band.lower); - rcp_gas_band_len = 1.f / (float)(gas_band.upper - gas_band.lower); + lambda_min = gas_band.lower; + lambda_max = nextafter(gas_band.upper, 0); /* inclusive */ + rcp_gas_band_len = 1.f/(float)(lambda_max - lambda_min); f4_mulf(ks, tau_ks, rcp_gas_band_len); f4_mulf(ka, tau_ka, rcp_gas_band_len); - f4_add(kext, ka, ks); /* Compute the radiative coefficients range of the tetrahedron */ radcoefs->ks_min = MMIN(MMIN(ks[0], ks[1]), MMIN(ks[2], ks[3])); radcoefs->ks_max = MMAX(MMAX(ks[0], ks[1]), MMAX(ks[2], ks[3])); radcoefs->ka_min = MMIN(MMIN(ka[0], ka[1]), MMIN(ka[2], ka[3])); radcoefs->ka_max = MMAX(MMAX(ka[0], ka[1]), MMAX(ka[2], ka[3])); - radcoefs->kext_min = MMIN(MMIN(kext[0], kext[1]), MMIN(kext[2], kext[3])); - radcoefs->kext_max = MMAX(MMAX(kext[0], kext[1]), MMAX(kext[2], kext[3])); } } @@ -635,7 +614,6 @@ update_voxel { float vx_ka_min, vx_ka_max; float vx_ks_min, vx_ks_max; - float vx_kext_min, vx_kext_max; float* vx = NULL; ASSERT(atm && radcoefs && part); @@ -648,20 +626,14 @@ update_voxel 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, radcoefs->ka_min); vx_ka_max = MMAX(vx_ka_max, radcoefs->ka_max); vx_ks_min = MMIN(vx_ks_min, radcoefs->ks_min); vx_ks_max = MMAX(vx_ks_max, radcoefs->ks_max); - vx_kext_min = MMIN(vx_kext_min, radcoefs->kext_min); - vx_kext_max = MMAX(vx_kext_max, radcoefs->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; } static res_T @@ -1122,7 +1094,6 @@ vx_merge(void* dst, const void* vxs[], const size_t nvxs, void* ctx) { float ka_min = FLT_MAX, ka_max = -FLT_MAX; float ks_min = FLT_MAX, ks_max = -FLT_MAX; - float kext_min = FLT_MAX, kext_max = -FLT_MAX; float* merged_vx = dst; size_t ivx; ASSERT(dst && vxs && nvxs); @@ -1134,15 +1105,12 @@ vx_merge(void* dst, const void* vxs[], const size_t nvxs, void* ctx) ka_max = MMAX(ka_max, vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MAX)]); ks_min = MMIN(ks_min, vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MIN)]); ks_max = MMAX(ks_max, vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MAX)]); - kext_min = MMIN(kext_min, vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MIN)]); - kext_max = MMAX(kext_max, vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MAX)]); } + merged_vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MIN)] = ka_min; merged_vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MAX)] = ka_max; merged_vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MIN)] = ks_min; merged_vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MAX)] = ks_max; - merged_vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MIN)] = kext_min; - merged_vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MAX)] = kext_max; } static int @@ -1163,8 +1131,13 @@ vx_challenge_merge /* Compute the range of the extinction coefficients of the submitted voxels */ FOR_EACH(ivx, 0, nvxs) { const float* vx = vxs[ivx].data; - kext_min = MMIN(kext_min, vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MIN)]); - kext_max = MMAX(kext_max, vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MAX)]); + const float ka_min = vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MIN)]; + const float ka_max = vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MAX)]; + const float ks_min = vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MIN)]; + const float ks_max = vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MAX)]; + if(ka_min > ka_max) { ASSERT(ks_min > ks_max); continue; } /* Empty voxel */ + kext_min = MMIN(kext_min, ka_min + ks_min); + kext_max = MMAX(kext_max, ka_max + ks_max); } /* Always merge empty voxels */ diff --git a/src/rnatm_voxel.h b/src/rnatm_voxel.h @@ -27,22 +27,16 @@ * Memory layout of a voxel * ------------------------ * - * The data of a voxel are 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: + * The data of a voxel are stored in a list of 4 single-precision floating-point + * numbers ka_min, ka_max, ks_min, ks_max. 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__) +#define NFLOATS_PER_VOXEL 4 /* Calculate the data index of voxel */ static FINLINE size_t @@ -50,7 +44,7 @@ voxel_idata (const enum rnatm_radcoef radcoef, const enum rnatm_svx_op op) { - ASSERT((unsigned)radcoef < RNATM_RADCOEFS_COUNT__); + ASSERT(radcoef == RNATM_RADCOEF_Ka || radcoef == RNATM_RADCOEF_Ks); ASSERT((unsigned)op < RNATM_SVX_OPS_COUNT__); return radcoef*RNATM_SVX_OPS_COUNT__ + op; } @@ -58,20 +52,10 @@ voxel_idata 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_MAX; break; - default: FATAL("Unreachable code\n"); break; - } - voxel[voxel_idata(radcoef, op)] = val; - } - } + voxel[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MIN)] = FLT_MAX; + voxel[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MAX)] =-FLT_MAX; + voxel[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MIN)] = FLT_MAX; + voxel[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MAX)] =-FLT_MAX; } #endif /* RNATM_VOXEL_H */ diff --git a/src/rnatm_write_vtk_octree.c b/src/rnatm_write_vtk_octree.c @@ -139,6 +139,7 @@ register_leaf /* Register leaf data */ kext_min = rnatm_get_k_svx_voxel(ctx->atm, leaf, RNATM_RADCOEF_Kext, RNATM_SVX_OP_MIN); kext_max = rnatm_get_k_svx_voxel(ctx->atm, leaf, RNATM_RADCOEF_Kext, RNATM_SVX_OP_MAX); + ASSERT(kext_min <= kext_max); CHK(RES_OK == darray_double_push_back(&ctx->data, &kext_min)); CHK(RES_OK == darray_double_push_back(&ctx->data, &kext_max)); }