htrdr

Solving radiative transfer in heterogeneous media
git clone git://git.meso-star.fr/htrdr.git
Log | Files | Refs | README | LICENSE

commit 5791e9f3560e9217b401bed5f68643a69a319732
parent 63ff572ea9c269fbf20929c0e83664e0cfa66e68
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon, 24 Feb 2020 14:16:49 +0100

Merge branch 'release_0.3'

Diffstat:
MREADME.md | 24++++++++++++++++++------
Mcmake/CMakeLists.txt | 17++++++-----------
Mdoc/htrdr-image.5.txt | 3++-
Mdoc/htrdr.1.txt.in | 46+++++++++++++++++++++++++---------------------
Msrc/htrdr.c | 253+++++++------------------------------------------------------------------------
Msrc/htrdr.h | 15+++++++--------
Msrc/htrdr_args.c | 19++++++++++---------
Msrc/htrdr_args.h.in | 4++--
Msrc/htrdr_buffer.c | 3++-
Msrc/htrdr_buffer.h | 3++-
Msrc/htrdr_c.h | 19++-----------------
Msrc/htrdr_camera.c | 3++-
Msrc/htrdr_camera.h | 3++-
Msrc/htrdr_compute_radiance_sw.c | 56+++++++++++++++++++++++++++++---------------------------
Msrc/htrdr_draw_radiance_sw.c | 21+++++++++++++--------
Msrc/htrdr_grid.c | 3++-
Msrc/htrdr_grid.h | 3++-
Msrc/htrdr_ground.c | 3++-
Msrc/htrdr_ground.h | 3++-
Msrc/htrdr_main.c | 3++-
Msrc/htrdr_rectangle.c | 3++-
Msrc/htrdr_rectangle.h | 3++-
Dsrc/htrdr_sky.c | 2485-------------------------------------------------------------------------------
Dsrc/htrdr_sky.h | 177-------------------------------------------------------------------------------
Msrc/htrdr_slab.c | 3++-
Msrc/htrdr_slab.h | 3++-
Msrc/htrdr_solve.h | 3++-
Msrc/htrdr_sun.c | 3++-
Msrc/htrdr_sun.h | 3++-
29 files changed, 168 insertions(+), 3019 deletions(-)

diff --git a/README.md b/README.md @@ -37,9 +37,7 @@ This program is compatible GNU/Linux 64-bits. It relies on the [CMake](http://www.cmake.org) and the [RCMake](https://gitlab.com/vaplv/rcmake/) packages to build. It also depends on the -[HTCP](https://gitlab.com/meso-star/htcp/), -[HTGOP](https://gitlab.com/meso-star/htgop/), -[HTMIE](https://gitlab.com/meso-star/htmie/), +[HTSky](https://gitlab.com/meso-star/htsky/), [RSys](https://gitlab.com/vaplv/rsys/), [Star-3D](https://gitlab.com/meso-star/star-3d/), [Star-3DAW](https://gitlab.com/meso-star/star-3daw/), @@ -60,6 +58,20 @@ informations on CMake. ## Release notes +### Version 0.3 + +- Add the `-O` option that defines the file where the sky data are cached. If + the file does not exist, the sky data structures are built from scracth and + serialized into this new file. If this file exists, these data structures are + directly read from it, leading to a huge speed up of the `htrdr` + pre-processing step. Note that if the provided file exists but is filled with + data that do not match the submitted HTGOP, HTCP and HTMie files, an error is + detected and the program stops. +- Rely on the HTSky library to manage the sky data. This library handles the + code previously defined into the `htrdr_sky.<c|h>` files. The HTCP, HTGOP, + HTMie libraries are thus no more dependencies of `htrdr` since only the + `htrdr_sky` files used them. + ### Version 0.2 - Add the `-b` option that controls the BRDF of the ground geometry. @@ -94,9 +106,9 @@ informations on CMake. ## License -Copyright (C) 2018-2019 Centre National de la Recherche -Scientifique (CNRS), [|Meso|Star](http://www.meso-star.com) -<contact@meso-star.com>, Université Paul Sabatier +Copyright (C) 2018, 2019, 2020 [|Meso|Star>](http://www.meso-star.com) +<contact@meso-star.com>. Copyright (C) 2018, 2019 Centre National de la +Recherche Scientifique (CNRS), Université Paul Sabatier <contact-edstar@laplace.univ-tlse.fr>. htrdr is free software released under the GPL v3+ license: GNU GPL version 3 or later. You are welcome to redistribute it under certain conditions; refer to the COPYING file for diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -1,4 +1,5 @@ -# Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +# Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) +# Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier # # 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 @@ -30,9 +31,7 @@ find_package(Star3DAW 0.3.1 REQUIRED) find_package(StarSF 0.6 REQUIRED) find_package(StarSP 0.8 REQUIRED) find_package(StarVX REQUIRED) -find_package(HTCP REQUIRED) -find_package(HTGOP REQUIRED) -find_package(HTMIE REQUIRED) +find_package(HTSky REQUIRED) find_package(OpenMP 1.2 REQUIRED) find_package(MPI 1 REQUIRED) @@ -49,9 +48,7 @@ include_directories( ${StarSF_INCLUDE_DIR} ${StarSP_INCLUDE_DIR} ${StarVX_INCLUDE_DIR} - ${HTCP_INCLUDE_DIR} - ${HTGOP_INCLUDE_DIR} - ${HTMIE_INCLUDE_DIR} + ${HTSky} ${HTRDR_SOURCE_DIR} ${MPI_INCLUDE_PATH} ${CMAKE_CURRENT_BINARY_DIR}) @@ -60,7 +57,7 @@ include_directories( # Generate files ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 2) +set(VERSION_MINOR 3) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) @@ -101,7 +98,6 @@ set(HTRDR_FILES_SRC htrdr_main.c htrdr_rectangle.c htrdr_slab.c - htrdr_sky.c htrdr_sun.c) set(HTRDR_FILES_INC htrdr.h @@ -112,7 +108,6 @@ set(HTRDR_FILES_INC htrdr_ground.h htrdr_rectangle.h htrdr_slab.h - htrdr_sky.h htrdr_sun.h htrdr_solve.h) set(HTRDR_FILES_INC2 # Generated files @@ -127,7 +122,7 @@ rcmake_prepend_path(HTRDR_FILES_INC2 ${CMAKE_CURRENT_BINARY_DIR}) rcmake_prepend_path(HTRDR_FILES_DOC ${PROJECT_SOURCE_DIR}/../) add_executable(htrdr ${HTRDR_FILES_SRC} ${HTRDR_FILES_INC} ${HTRDR_FILES_INC2}) -target_link_libraries(htrdr HTCP HTGOP HTMIE RSys Star3D Star3DAW StarSF StarSP StarVX) +target_link_libraries(htrdr HTSky RSys Star3D Star3DAW StarSF StarSP) if(CMAKE_COMPILER_IS_GNUCC) target_link_libraries(htrdr m) diff --git a/doc/htrdr-image.5.txt b/doc/htrdr-image.5.txt @@ -1,4 +1,5 @@ -// Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +// Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) +// Copyright (C) 2018-2019 CNRS, Université Paul Sabatier // // 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 diff --git a/doc/htrdr.1.txt.in b/doc/htrdr.1.txt.in @@ -1,4 +1,5 @@ -// Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +// Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) +// Copyright (C) 2018-2019 CNRS, Université Paul Sabatier // // 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 @@ -24,7 +25,7 @@ htrdr - image renderer of cloudy atmospheres SYNOPSIS -------- [verse] -*htrdr* [_option_]... -a _atmosphere_ -m _mie_ +*htrdr* [_option_]... -a _atmosphere_ DESCRIPTION ----------- @@ -124,7 +125,7 @@ OPTIONS *-e* _reflectivity_:: Reflectivity of the ground geometry in [0, 1]. By default it is set to @HTRDR_ARGS_DEFAULT_GROUND_REFLECTIVITY@. This parameter is fixed for the - while visible range and defines the amount of reflected energy. Refer to the + whole visible range and defines the amount of reflected energy. Refer to the *-b* option to control how the light is reflected on the ground surface. *-f*:: @@ -133,18 +134,6 @@ OPTIONS *-g* _ground_:: Path toward an OBJ file [2] representing the ground geometry. -*-G*:: - Pre-compute or use cached grids of the cloud properties built from the - _clouds_, the _atmosphere_ and the _mie_ files. If the corresponding grids - were generated in a previous run, reuse them as far as it is possible, i.e. - if the _clouds_, the _atmosphere_ and the _mie_ files were not updated. The - cached data are written in a hidden directory named *.htrdr* located in the - directory where *htrdr* is run. On platforms with an efficient hard-drive and - plenty of random access memory, this cache mechanism can significantly - speed-up the pre-computation step on _clouds_ data. Note that this option is - incompatible with a MPI execution and is thus forced to off if *htrdr* is run - through a process launcher. - *-h*:: List short help and exit. @@ -171,6 +160,19 @@ OPTIONS Path toward a *htmie*(5) file defining the optical properties of water droplets. +*-O* _cache_:: + File used to cache the sky data. If the _cache_ file does not exists, it is + created and filled with the sky data built from the _clouds_, the + _atmosphere_ and the _mie_ input files. This cached data can then be reused + in the next runs as long as the input files provided on the command are the + same of the ones used to setup the cache; leading to a significant speed-up + of the pre-processing step. If _cache_ contains data generated from input + files that are not the ones submitted on the command line, an error is + notified and the execution is stopped, avoiding the use of wrong cached data. + Note that when the cache is used, *htrdr* ignores the arguments used to + parametrise the structures partitioning the sky data, i.e. the *-T* and *-V* + options. + *-o* _output_:: File where *htrdr* writes its _output_ data. If not defined, write results to standard output. @@ -227,13 +229,14 @@ output in a regular PPM image [5]: $ htpp -o image.ppm output Move the sun by setting its azimuthal and elevation angles to *120* and *40* -degrees respectively. Use the *-G* option to enable the cache mechanism on -clouds data. Increase the image definition to *1280* by *720* and set the +degrees respectively. Use the *-O* option to enable the cache mechanism on +sky data. Increase the image definition to *1280* by *720* and set the number of samples per pixel component to *1024*. Write results on standard output and convert the resulting image in PPM before visualising it through the *feh*(1) image viewer: - $ htrdr -D120,40 -a gas.txt -m Mie.nc -g mountains.obj -R -c clouds.htcp -G \ + $ htrdr -D120,40 -a gas.txt -m Mie.nc -g mountains.obj -R -c clouds.htcp \ + -O my_cache \ -C pos=0,0,400:tgt=0,1,0:up=0,0,1 \ -i def=1280x720:spp=1024 | htpp | feh - @@ -268,9 +271,10 @@ NOTES COPYRIGHT --------- -Copyright &copy; 2018-2019 CNRS, |Meso|Star> <contact@meso-star.com>, Université -Paul Sabatier <contact-edstar@laplace.univ-tlse.fr>. *htrdr* is free software -released under the GPLv3+ license: GNU GPL version 3 or later +Copyright &copy; 2018, 2019, 2020 |Meso|Star> <contact@meso-star.com>. +Copyright &copy; 2018, 2019 CNRS, Université Paul Sabatier +<contact-edstar@laplace.univ-tlse.fr>. *htrdr* is free software released under +the GPLv3+ license: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>. You are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. diff --git a/src/htrdr.c b/src/htrdr.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 @@ -21,7 +22,6 @@ #include "htrdr_buffer.h" #include "htrdr_camera.h" #include "htrdr_ground.h" -#include "htrdr_sky.h" #include "htrdr_sun.h" #include "htrdr_solve.h" @@ -29,9 +29,10 @@ #include <rsys/mem_allocator.h> #include <rsys/str.h> +#include "high_tune/htsky.h" + #include <star/s3d.h> #include <star/ssf.h> -#include <star/svx.h> #include <errno.h> #include <fcntl.h> /* open */ @@ -152,76 +153,6 @@ error: goto exit; } -static res_T -open_file_stamp - (struct htrdr* htrdr, - const char* filename, - struct stat* out_stat, /* Stat of the submitted filename */ - int* out_fd, /* Descriptor of the opened file. Must be closed by the caller */ - struct str* stamp_filename) -{ - struct stat statbuf; - struct str str; - int err; - int fd = -1; - res_T res = RES_OK; - ASSERT(htrdr && filename && out_fd && out_stat && stamp_filename); - - str_init(htrdr->allocator, &str); - - err = stat(filename, &statbuf); - if(err) { - htrdr_log_err(htrdr, "%s: could not stat the file -- %s.\n", - filename, strerror(errno)); - res = RES_IO_ERR; - goto error; - } - - if(!S_ISREG(statbuf.st_mode)) { - htrdr_log_err(htrdr, "%s: not a regular file.\n", filename); - res = RES_IO_ERR; - goto error; - } - - res = create_directory(htrdr, ".htrdr/"); - if(res != RES_OK) goto error; - - #define CHK_STR(Func, ErrMsg) { \ - res = str_##Func; \ - if(res != RES_OK) { \ - htrdr_log_err(htrdr, "%s: "ErrMsg"\n", filename); \ - goto error; \ - } \ - } (void)0 - CHK_STR(set(&str, filename), "could not copy the filename"); - CHK_STR(set(&str, basename(str_get(&str))), "could not setup the basename"); - CHK_STR(insert(&str, 0, ".htrdr/"), "could not setup the stamp directory"); - CHK_STR(append(&str, ".stamp"), "could not setup the stamp extension"); - #undef CHK_STR - - fd = open(str_cget(&str), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); - if(fd < 0) { - htrdr_log_err(htrdr, "%s: could not open/create the file -- %s.\n", - str_cget(&str), strerror(errno)); - res = RES_IO_ERR; - goto error; - } - - CHK(str_copy_and_clear(stamp_filename, &str) == RES_OK); - -exit: - str_release(&str); - *out_fd = fd; - *out_stat = statbuf; - return res; -error: - if(fd >= 0) { - CHK(close(fd) == 0); - fd = -1; - } - goto exit; -} - static INLINE void spherical_to_cartesian_dir (const double azimuth, /* In radians */ @@ -434,6 +365,7 @@ htrdr_init const struct htrdr_args* args, struct htrdr* htrdr) { + struct htsky_args htsky_args = HTSKY_ARGS_DEFAULT; double proj_ratio; double sun_dir[3]; const char* output_name = NULL; @@ -455,7 +387,6 @@ htrdr_init nthreads_max = MMAX(omp_get_max_threads(), omp_get_num_procs()); htrdr->dump_vtk = args->dump_vtk; - htrdr->cache_grids = args->cache_grids; htrdr->verbose = args->verbose; htrdr->nthreads = MMIN(args->nthreads, (unsigned)nthreads_max); htrdr->spp = args->image.spp; @@ -465,17 +396,9 @@ htrdr_init htrdr->grid_max_definition[1] = args->grid_max_definition[1]; htrdr->grid_max_definition[2] = args->grid_max_definition[2]; - res = mem_init_regular_allocator(&htrdr->svx_allocator); - if(res != RES_OK) goto error; - res = init_mpi(htrdr); if(res != RES_OK) goto error; - if(htrdr->cache_grids && htrdr->mpi_nprocs != 1) { - htrdr_log_warn(htrdr, "cached grids are not supported in a MPI execution.\n"); - htrdr->cache_grids = 0; - } - if(!args->output) { htrdr->output = stdout; output_name = "<stdout>"; @@ -493,15 +416,6 @@ htrdr_init goto error; } - res = svx_device_create - (&htrdr->logger, &htrdr->svx_allocator, args->verbose, &htrdr->svx); - if(res != RES_OK) { - htrdr_log_err(htrdr, - "%s: could not create the Star-VoXel device -- %s.\n", - FUNC_NAME, res_to_cstr(res)); - goto error; - } - /* Disable the Star-3D verbosity since the Embree backend prints some messages * on stdout rather than stderr. This is annoying since stdout may be used by * htrdr to write output data */ @@ -547,9 +461,18 @@ htrdr_init (MDEG2RAD(args->sun_azimuth), MDEG2RAD(args->sun_elevation), sun_dir); htrdr_sun_set_direction(htrdr->sun, sun_dir); - res = htrdr_sky_create(htrdr, htrdr->sun, args->filename_les, - args->filename_gas, args->filename_mie, args->optical_thickness, - args->repeat_clouds, &htrdr->sky); + htsky_args.htcp_filename = args->filename_les; + htsky_args.htgop_filename = args->filename_gas; + htsky_args.htmie_filename = args->filename_mie; + htsky_args.cache_filename = args->cache; + htsky_args.grid_max_definition[0] = args->grid_max_definition[0]; + htsky_args.grid_max_definition[1] = args->grid_max_definition[1]; + htsky_args.grid_max_definition[2] = args->grid_max_definition[2]; + htsky_args.optical_thickness = args->optical_thickness; + htsky_args.nthreads = htrdr->nthreads; + htsky_args.repeat_clouds = args->repeat_clouds; + htsky_args.verbose = args->verbose; + res = htsky_create(&htrdr->logger, htrdr->allocator, &htsky_args, &htrdr->sky); if(res != RES_OK) goto error; htrdr->lifo_allocators = MEM_CALLOC @@ -586,9 +509,8 @@ htrdr_release(struct htrdr* htrdr) ASSERT(htrdr); release_mpi(htrdr); if(htrdr->s3d) S3D(device_ref_put(htrdr->s3d)); - if(htrdr->svx) SVX(device_ref_put(htrdr->svx)); if(htrdr->ground) htrdr_ground_ref_put(htrdr->ground); - if(htrdr->sky) htrdr_sky_ref_put(htrdr->sky); + if(htrdr->sky) HTSKY(ref_put(htrdr->sky)); if(htrdr->sun) htrdr_sun_ref_put(htrdr->sun); if(htrdr->cam) htrdr_camera_ref_put(htrdr->cam); if(htrdr->buf) htrdr_buffer_ref_put(htrdr->buf); @@ -601,9 +523,6 @@ htrdr_release(struct htrdr* htrdr) } str_release(&htrdr->output_name); logger_release(&htrdr->logger); - - ASSERT(MEM_ALLOCATED_SIZE(&htrdr->svx_allocator) == 0); - mem_shutdown_regular_allocator(&htrdr->svx_allocator); } res_T @@ -611,19 +530,19 @@ htrdr_run(struct htrdr* htrdr) { res_T res = RES_OK; if(htrdr->dump_vtk) { - const size_t nbands = htrdr_sky_get_sw_spectral_bands_count(htrdr->sky); + const size_t nbands = htsky_get_sw_spectral_bands_count(htrdr->sky); size_t i; /* Nothing to do */ if(htrdr->mpi_rank != 0) goto exit; FOR_EACH(i, 0, nbands) { - const size_t iband = htrdr_sky_get_sw_spectral_band_id(htrdr->sky, i); - const size_t nquads = htrdr_sky_get_sw_spectral_band_quadrature_length + const size_t iband = htsky_get_sw_spectral_band_id(htrdr->sky, i); + const size_t nquads = htsky_get_sw_spectral_band_quadrature_length (htrdr->sky, iband); size_t iquad; FOR_EACH(iquad, 0, nquads) { - res = htrdr_sky_dump_clouds_vtk(htrdr->sky, iband, iquad, htrdr->output); + res = htsky_dump_cloud_vtk(htrdr->sky, iband, iquad, htrdr->output); if(res != RES_OK) goto error; fprintf(htrdr->output, "---\n"); } @@ -780,134 +699,6 @@ error: goto exit; } -res_T -is_file_updated(struct htrdr* htrdr, const char* filename, int* out_upd) -{ - struct str stamp_filename; - struct stat statbuf; - ssize_t n; - off_t size; - struct timespec mtime; - int fd = -1; - int upd = 1; - res_T res = RES_OK; - ASSERT(htrdr && filename && out_upd); - - str_init(htrdr->allocator, &stamp_filename); - - res = open_file_stamp(htrdr, filename, &statbuf, &fd, &stamp_filename); - if(res != RES_OK) goto error; - - n = read(fd, &mtime, sizeof(mtime)); - if(n < 0) { - htrdr_log_err(htrdr, "%s: could not read the `mtime' data -- %s.\n", - str_cget(&stamp_filename), strerror(errno)); - res = RES_IO_ERR; - goto error; - } - - upd = (size_t)n != sizeof(mtime) - ||mtime.tv_nsec != statbuf.st_mtim.tv_nsec - ||mtime.tv_sec != statbuf.st_mtim.tv_sec; - - if(!upd) { - n = read(fd, &size, sizeof(size)); - if(n < 0) { - htrdr_log_err(htrdr, "%s: could not read the `size' data -- %s.\n", - str_cget(&stamp_filename), strerror(errno)); - res = RES_IO_ERR; - goto error; - } - upd = (size_t)n != sizeof(size) || statbuf.st_size != size; - } - -exit: - *out_upd = upd; - str_release(&stamp_filename); - if(fd >= 0) CHK(close(fd) == 0); - return res; -error: - goto exit; -} - - -res_T -update_file_stamp(struct htrdr* htrdr, const char* filename) -{ - struct str stamp_filename; - struct stat statbuf; - int fd = -1; - ssize_t n; - res_T res = RES_OK; - ASSERT(htrdr && filename); - - str_init(htrdr->allocator, &stamp_filename); - - res = open_file_stamp(htrdr, filename, &statbuf, &fd, &stamp_filename); - if(res != RES_OK) goto error; - - #define CHK_IO(Func, ErrMsg) { \ - if((Func) < 0) { \ - htrdr_log_err(htrdr, "%s: "ErrMsg" -- %s.\n", \ - str_cget(&stamp_filename), strerror(errno)); \ - res = RES_IO_ERR; \ - goto error; \ - } \ - } (void) 0 - - CHK_IO(lseek(fd, 0, SEEK_SET), "could not rewind the file descriptor"); - - /* NOTE: Ignore n >=0 but != sizeof(DATA). In such case stamp is currupted - * and on the next invocation on the same filename, this function will - * return 1 */ - n = write(fd, &statbuf.st_mtim, sizeof(statbuf.st_mtim)); - CHK_IO(n, "could not update the `mtime' data"); - n = write(fd, &statbuf.st_size, sizeof(statbuf.st_size)); - CHK_IO(n, "could not update the `size' data"); - - CHK_IO(fsync(fd), "could not sync the file with storage device"); - - #undef CHK_IO - -exit: - str_release(&stamp_filename); - if(fd >= 0) CHK(close(fd) == 0); - return res; -error: - goto exit; -} - -res_T -create_directory(struct htrdr* htrdr, const char* path) -{ - res_T res = RES_OK; - int err; - ASSERT(htrdr && path); - - err = mkdir(path, S_IRWXU); - if(!err) goto exit; - - if(errno != EEXIST) { - htrdr_log_err(htrdr, "cannot create the `%s' directory -- %s.\n", - path, strerror(errno)); - res = RES_IO_ERR; - goto error; - } else { - const int fd = open(path, O_DIRECTORY); - if(fd < -1) { - htrdr_log_err(htrdr, "cannot open the `%s' directory -- %s.\n", - path, strerror(errno)); - res = RES_IO_ERR; - goto error; - } - CHK(!close(fd)); - } -exit: - return res; -error: - goto exit; -} - void send_mpi_progress (struct htrdr* htrdr, const enum htrdr_mpi_message msg, int32_t percent) diff --git a/src/htrdr.h b/src/htrdr.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 @@ -21,7 +22,7 @@ #include <rsys/str.h> /* Helper macro that asserts if the invocation of the htrdr function `Func' - * returns an error. One should use this macro on htcp function calls for + * returns an error. One should use this macro on htrdr function calls for * which no explicit error checking is performed */ #ifndef NDEBUG #define HTRDR(Func) ASSERT(htrdr_ ## Func == RES_OK) @@ -30,9 +31,9 @@ #endif /* Forward declarations */ +struct htsky; struct htrdr_args; struct htrdr_buffer; -struct htrdr_sky; struct htrdr_rectangle; struct mem_allocator; struct mutext; @@ -40,18 +41,18 @@ struct s3d_device; struct s3d_scene; struct ssf_bsdf; struct ssf_phase; -struct svx_device; struct htrdr { - struct svx_device* svx; struct s3d_device* s3d; struct htrdr_ground* ground; - struct htrdr_sky* sky; struct htrdr_sun* sun; struct htrdr_camera* cam; struct htrdr_buffer* buf; + + struct htsky* sky; + size_t spp; /* #samples per pixel */ size_t width; /* Image width */ size_t height; /* Image height */ @@ -62,7 +63,6 @@ struct htrdr { unsigned grid_max_definition[3]; /* Max definition of the acceleration grids */ unsigned nthreads; /* #threads of the process */ int dump_vtk; /* Dump octree VTK */ - int cache_grids; /* Use/Precompute grid caches */ int verbose; /* Verbosity level */ int mpi_rank; /* Rank of the process in the MPI group */ @@ -79,7 +79,6 @@ struct htrdr { struct logger logger; struct mem_allocator* allocator; - struct mem_allocator svx_allocator; struct mem_allocator* lifo_allocators; /* Per thread lifo allocator */ }; diff --git a/src/htrdr_args.c b/src/htrdr_args.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 @@ -43,7 +44,7 @@ static void print_help(const char* cmd) { ASSERT(cmd); - printf("Usage: %s [OPION]... -a ATMOSPHERE -m MIE\n", cmd); + printf("Usage: %s [OPION]... -a ATMOSPHERE\n", cmd); printf( "Render an image in the visible part of the spectrum, for scenes composed of an\n" "atmospheric gaz mixture, clouds and a ground.\n\n"); @@ -73,8 +74,6 @@ print_help(const char* cmd) printf( " -g GROUND ground geometry.\n"); printf( -" -G precompute/use cached grids of cloud properties.\n"); - printf( " -h display this help and exit.\n"); printf( " -i <image> define the image to compute.\n"); @@ -85,6 +84,8 @@ print_help(const char* cmd) printf( " -m MIE file of Mie's data.\n"); printf( +" -O CACHE name of the cache file used to store/restore the sky data.\n"); + printf( " -o OUTPUT file where data are written. If not defined, data are\n" " written to standard output.\n"); printf( @@ -103,9 +104,9 @@ print_help(const char* cmd) " --version display version information and exit.\n"); printf("\n"); printf( -"htrdr (C) 2018-2019 CNRS, |Meso|Star> <contact@meso-star.com>, Université Paul\n" -"Sabatier <contact-edstar@laplace.univ-tlse.fr>. This is free software released\n" -"under the GNU GPL license, version 3 or later. You are free to change or\n" +"Copyright (C) 2018, 2019, 2020 |Meso|Star> <contact@meso-star.com>. Copyright\n" +"(C) 2018, 2019 CNRS, Université Paul Sabatier. htrdr is free software released\n" +"under the GNU GPL license, version 3 or later. You are free to change or\n" "redistribute it under certain conditions <http://gnu.org/licenses/gpl.html>\n"); } @@ -414,7 +415,7 @@ htrdr_args_init(struct htrdr_args* args, int argc, char** argv) } } - while((opt = getopt(argc, argv, "a:b:C:c:D:de:fGg:hi:m:o:RrT:t:V:v")) != -1) { + while((opt = getopt(argc, argv, "a:b:C:c:D:de:fg:hi:m:O:o:RrT:t:V:v")) != -1) { switch(opt) { case 'a': args->filename_gas = optarg; break; case 'b': @@ -434,7 +435,6 @@ htrdr_args_init(struct htrdr_args* args, int argc, char** argv) } break; case 'f': args->force_overwriting = 1; break; - case 'G': args->cache_grids = 1; break; case 'g': args->filename_obj = optarg; break; case 'h': print_help(argv[0]); @@ -446,6 +446,7 @@ htrdr_args_init(struct htrdr_args* args, int argc, char** argv) (args, optarg, parse_image_parameter); break; case 'm': args->filename_mie = optarg; break; + case 'O': args->cache = optarg; break; case 'o': args->output = optarg; break; case 'r': args->repeat_clouds = 1; break; case 'R': args->repeat_ground = 1; break; diff --git a/src/htrdr_args.h.in b/src/htrdr_args.h.in @@ -26,6 +26,7 @@ struct htrdr_args { const char* filename_les; /* Path of the HTCP file */ const char* filename_mie; /* Path of the Mie properties */ const char* filename_obj; /* Path of the 3D geometry */ + const char* cache; const char* output; struct { @@ -57,7 +58,6 @@ struct htrdr_args { unsigned nthreads; /* Hint on the number of threads to use */ int force_overwriting; int dump_vtk; /* Dump the loaded cloud properties in a VTK file */ - int cache_grids; /* Use grid caching mechanism */ int verbose; /* Verbosity level */ int repeat_clouds; /* Make the clouds infinite in X and Y */ int repeat_ground; /* Make the ground infinite in X and Y */ @@ -69,6 +69,7 @@ struct htrdr_args { NULL, /* LES filename */ \ NULL, /* Mie filename */ \ NULL, /* Obj filename */ \ + NULL, /* Cache filename */ \ NULL, /* Output filename */ \ { \ {0, 0, 0}, /* plane position */ \ @@ -93,7 +94,6 @@ struct htrdr_args { (unsigned)~0, /* #threads */ \ 0, /* Force overwriting */ \ 0, /* dump VTK */ \ - 0, /* Grid cache */ \ 0, /* Verbose flag */ \ 0, /* Repeat clouds */ \ 0, /* Repeat ground */ \ diff --git a/src/htrdr_buffer.c b/src/htrdr_buffer.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_buffer.h b/src/htrdr_buffer.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_c.h b/src/htrdr_c.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 @@ -110,22 +111,6 @@ open_output_stream int force_overwrite, FILE** out_fp); -extern LOCAL_SYM res_T -is_file_updated - (struct htrdr* htrdr, - const char* filename, - int* is_upd); - -extern LOCAL_SYM res_T -update_file_stamp - (struct htrdr* htrdr, - const char* filename); - -extern LOCAL_SYM res_T -create_directory - (struct htrdr* htrdt, - const char* path); - extern LOCAL_SYM void send_mpi_progress (struct htrdr* htrdr, diff --git a/src/htrdr_camera.c b/src/htrdr_camera.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_camera.h b/src/htrdr_camera.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_compute_radiance_sw.c b/src/htrdr_compute_radiance_sw.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 @@ -17,9 +18,10 @@ #include "htrdr_c.h" #include "htrdr_ground.h" #include "htrdr_solve.h" -#include "htrdr_sky.h" #include "htrdr_sun.h" +#include <high_tune/htsky.h> + #include <star/s3d.h> #include <star/ssf.h> #include <star/ssp.h> @@ -31,7 +33,7 @@ struct scattering_context { struct ssp_rng* rng; - const struct htrdr_sky* sky; + const struct htsky* sky; size_t iband; /* Index of the spectrald */ size_t iquad; /* Index of the quadrature point into the band */ @@ -44,7 +46,7 @@ static const struct scattering_context SCATTERING_CONTEXT_NULL = { struct transmissivity_context { struct ssp_rng* rng; - const struct htrdr_sky* sky; + const struct htsky* sky; size_t iband; /* Index of the spectrald */ size_t iquad; /* Index of the quadrature point into the band */ @@ -52,7 +54,7 @@ struct transmissivity_context { double Tmin; /* Minimal optical thickness */ double traversal_dst; /* Distance traversed along the ray */ - enum htrdr_sky_property prop; + enum htsky_property prop; }; static const struct transmissivity_context TRANSMISSION_CONTEXT_NULL = { NULL, NULL, 0, 0, 0, 0, 0, 0 @@ -75,8 +77,8 @@ scattering_hit_filter ASSERT(hit && ctx && !SVX_HIT_NONE(hit) && org && dir && range); (void)range; - ks_max = htrdr_sky_fetch_svx_voxel_property(ctx->sky, HTRDR_Ks, - HTRDR_SVX_MAX, HTRDR_ALL_COMPONENTS, ctx->iband, ctx->iquad, &hit->voxel); + ks_max = htsky_fetch_svx_voxel_property(ctx->sky, HTSKY_Ks, + HTSKY_SVX_MAX, HTSKY_CPNT_MASK_ALL, ctx->iband, ctx->iquad, &hit->voxel); ctx->traversal_dst = hit->distance[0]; @@ -111,8 +113,8 @@ scattering_hit_filter pos[1] = org[1] + ctx->traversal_dst * dir[1]; pos[2] = org[2] + ctx->traversal_dst * dir[2]; - ks = htrdr_sky_fetch_raw_property(ctx->sky, HTRDR_Ks, - HTRDR_ALL_COMPONENTS, ctx->iband, ctx->iquad, pos, -DBL_MAX, DBL_MAX); + ks = htsky_fetch_raw_property(ctx->sky, HTSKY_Ks, + HTSKY_CPNT_MASK_ALL, ctx->iband, ctx->iquad, pos, -DBL_MAX, DBL_MAX); /* Handle the case that ks_max is not *really* the max */ proba = ks / ks_max; @@ -137,17 +139,17 @@ transmissivity_hit_filter void* context) { struct transmissivity_context* ctx = context; - int comp_mask = HTRDR_ALL_COMPONENTS; + int comp_mask = HTSKY_CPNT_MASK_ALL; double k_max; double k_min; int pursue_traversal = 1; ASSERT(hit && ctx && !SVX_HIT_NONE(hit) && org && dir && range); (void)range; - k_min = htrdr_sky_fetch_svx_voxel_property(ctx->sky, ctx->prop, - HTRDR_SVX_MIN, comp_mask, ctx->iband, ctx->iquad, &hit->voxel); - k_max = htrdr_sky_fetch_svx_voxel_property(ctx->sky, ctx->prop, - HTRDR_SVX_MAX, comp_mask, ctx->iband, ctx->iquad, &hit->voxel); + k_min = htsky_fetch_svx_voxel_property(ctx->sky, ctx->prop, + HTSKY_SVX_MIN, comp_mask, ctx->iband, ctx->iquad, &hit->voxel); + k_max = htsky_fetch_svx_voxel_property(ctx->sky, ctx->prop, + HTSKY_SVX_MAX, comp_mask, ctx->iband, ctx->iquad, &hit->voxel); ASSERT(k_min <= k_max); ctx->Tmin += (hit->distance[1] - hit->distance[0]) * k_min; @@ -183,7 +185,7 @@ transmissivity_hit_filter x[1] = org[1] + ctx->traversal_dst * dir[1]; x[2] = org[2] + ctx->traversal_dst * dir[2]; - k = htrdr_sky_fetch_raw_property(ctx->sky, ctx->prop, + k = htsky_fetch_raw_property(ctx->sky, ctx->prop, comp_mask, ctx->iband, ctx->iquad, x, k_min, k_max); ASSERT(k >= k_min && k <= k_max); @@ -204,7 +206,7 @@ static double transmissivity (struct htrdr* htrdr, struct ssp_rng* rng, - const enum htrdr_sky_property prop, + const enum htsky_property prop, const size_t iband, const size_t iquad, const double pos[3], @@ -224,7 +226,7 @@ transmissivity transmissivity_ctx.prop = prop; /* Compute the transmissivity */ - HTRDR(sky_trace_ray(htrdr->sky, pos, dir, range, NULL, + HTSKY(trace_ray(htrdr->sky, pos, dir, range, NULL, transmissivity_hit_filter, &transmissivity_ctx, iband, iquad, &svx_hit)); if(SVX_HIT_NONE(&svx_hit)) { @@ -284,7 +286,7 @@ htrdr_compute_radiance_sw (&htrdr->lifo_allocators[ithread], &ssf_phase_rayleigh, &phase_rayleigh)); /* Setup the phase function for this spectral band & quadrature point */ - g = htrdr_sky_fetch_particle_phase_function_asymmetry_parameter + g = htsky_fetch_particle_phase_function_asymmetry_parameter (htrdr->sky, iband, iquad); SSF(phase_hg_setup(phase_hg, g)); @@ -296,7 +298,7 @@ htrdr_compute_radiance_sw * spectral data are defined by bands that, actually are the same of the SW * spectral bands defined in the default "ecrad_opt_prot.txt" file provided * by the HTGOP project. */ - htrdr_sky_get_sw_spectral_band_bounds(htrdr->sky, iband, band_bounds); + htsky_get_sw_spectral_band_bounds(htrdr->sky, iband, band_bounds); wlen = (band_bounds[0] + band_bounds[1]) * 0.5; sun_solid_angle = htrdr_sun_get_solid_angle(htrdr->sun); L_sun = htrdr_sun_get_radiance(htrdr->sun, wlen); @@ -315,7 +317,7 @@ htrdr_compute_radiance_sw Tr = 0; } else { Tr = transmissivity - (htrdr, rng, HTRDR_Kext, iband, iquad , pos, dir, range); + (htrdr, rng, HTSKY_Kext, iband, iquad , pos, dir, range); w = L_sun * Tr; } } @@ -341,7 +343,7 @@ htrdr_compute_radiance_sw /* Define if a scattering event occurs */ d2(range, 0, s3d_hit.distance); - HTRDR(sky_trace_ray(htrdr->sky, pos, dir, range, NULL, + HTSKY(trace_ray(htrdr->sky, pos, dir, range, NULL, scattering_hit_filter, &scattering_ctx, iband, iquad, &svx_hit)); /* No scattering and no surface reflection. Stop the radiative random walk */ @@ -364,7 +366,7 @@ htrdr_compute_radiance_sw * next position */ d2(range, 0, scattering_ctx.traversal_dst); Tr_abs = transmissivity - (htrdr, rng, HTRDR_Ka, iband, iquad, pos, dir, range); + (htrdr, rng, HTSKY_Ka, iband, iquad, pos, dir, range); if(Tr_abs <= 0) break; /* Sample a sun direction */ @@ -382,7 +384,7 @@ htrdr_compute_radiance_sw Tr = 0; } else { Tr = transmissivity - (htrdr, rng, HTRDR_Kext, iband, iquad, pos_next, sun_dir, range); + (htrdr, rng, HTSKY_Kext, iband, iquad, pos_next, sun_dir, range); } /* Scattering at a surface */ @@ -412,10 +414,10 @@ htrdr_compute_radiance_sw double ks_gas; /* Scattering coefficient of the gaz */ double ks; /* Overall scattering coefficient */ - ks_gas = htrdr_sky_fetch_raw_property(htrdr->sky, HTRDR_Ks, HTRDR_GAS, - iband, iquad, pos_next, -DBL_MAX, DBL_MAX); - ks_particle = htrdr_sky_fetch_raw_property(htrdr->sky, HTRDR_Ks, - HTRDR_PARTICLES, iband, iquad, pos_next, -DBL_MAX, DBL_MAX); + ks_gas = htsky_fetch_raw_property(htrdr->sky, HTSKY_Ks, + HTSKY_CPNT_FLAG_GAS, iband, iquad, pos_next, -DBL_MAX, DBL_MAX); + ks_particle = htsky_fetch_raw_property(htrdr->sky, HTSKY_Ks, + HTSKY_CPNT_FLAG_PARTICLES, iband, iquad, pos_next, -DBL_MAX, DBL_MAX); ks = ks_particle + ks_gas; r = ssp_rng_canonical(rng); diff --git a/src/htrdr_draw_radiance_sw.c b/src/htrdr_draw_radiance_sw.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 @@ -19,9 +20,10 @@ #include "htrdr_c.h" #include "htrdr_buffer.h" #include "htrdr_camera.h" -#include "htrdr_sky.h" #include "htrdr_solve.h" +#include <high_tune/htsky.h> + #include <rsys/clock_time.h> #include <rsys/cstr.h> #include <rsys/dynamic_array_u32.h> @@ -504,6 +506,7 @@ draw_tile double ray_org[3]; double ray_dir[3]; double weight; + double r0, r1; size_t iband; size_t iquad; double usec; @@ -517,18 +520,20 @@ draw_tile htrdr_camera_ray(cam, pix_samp, ray_org, ray_dir); /* Sample a spectral band and a quadrature point */ + r0 = ssp_rng_canonical(rng); + r1 = ssp_rng_canonical(rng); switch(ichannel) { case 0: - htrdr_sky_sample_sw_spectral_data_CIE_1931_X - (htrdr->sky, rng, &iband, &iquad); + htsky_sample_sw_spectral_data_CIE_1931_X + (htrdr->sky, r0, r1, &iband, &iquad); break; case 1: - htrdr_sky_sample_sw_spectral_data_CIE_1931_Y - (htrdr->sky, rng, &iband, &iquad); + htsky_sample_sw_spectral_data_CIE_1931_Y + (htrdr->sky, r0, r1, &iband, &iquad); break; case 2: - htrdr_sky_sample_sw_spectral_data_CIE_1931_Z - (htrdr->sky, rng, &iband, &iquad); + htsky_sample_sw_spectral_data_CIE_1931_Z + (htrdr->sky, r0, r1, &iband, &iquad); break; default: FATAL("Unreachable code.\n"); break; } diff --git a/src/htrdr_grid.c b/src/htrdr_grid.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_grid.h b/src/htrdr_grid.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_ground.c b/src/htrdr_ground.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_ground.h b/src/htrdr_ground.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_main.c b/src/htrdr_main.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_rectangle.c b/src/htrdr_rectangle.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_rectangle.h b/src/htrdr_rectangle.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_sky.c b/src/htrdr_sky.c @@ -1,2485 +0,0 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier - * - * 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/>. */ - -#define _POSIX_C_SOURCE 200809L /* nextafterf & O_DIRECTORY */ - -#include "htrdr.h" -#include "htrdr_c.h" -#include "htrdr_grid.h" -#include "htrdr_slab.h" -#include "htrdr_sky.h" -#include "htrdr_sun.h" - -#include <star/ssp.h> -#include <star/svx.h> -#include <high_tune/htcp.h> -#include <high_tune/htgop.h> -#include <high_tune/htmie.h> - -#include <rsys/clock_time.h> -#include <rsys/dynamic_array_double.h> -#include <rsys/dynamic_array_size_t.h> -#include <rsys/hash_table.h> -#include <rsys/ref_count.h> - -#include <errno.h> -#include <fcntl.h> /* open */ -#include <libgen.h> -#include <math.h> -#include <mpi.h> -#include <omp.h> -#include <string.h> -#include <sys/stat.h> /* mkdir */ -#include <unistd.h> - -#define DRY_AIR_MOLAR_MASS 0.0289644 /* In kg.mol^-1 */ -#define H2O_MOLAR_MASS 0.01801528 /* In kg.mol^-1 */ -#define GAS_CONSTANT 8.3144598 /* In kg.m^2.s^-2.mol^-1.K */ - -#define NFLOATS_PER_COMPONENT (HTRDR_SVX_OPS_COUNT__ * HTRDR_PROPERTIES_COUNT__) -#define NFLOATS_PER_VOXEL (NFLOATS_PER_COMPONENT * HTRDR_COMPONENTS_COUNT__) - -enum axis_flag { - AXIS_X = BIT(0), - AXIS_Y = BIT(1), - AXIS_Z = BIT(2) -}; - -struct split { - size_t index; /* Index of the current htcp voxel */ - double height; /* Absolute height where the next voxel starts */ -}; - -#define DARRAY_NAME split -#define DARRAY_DATA struct split -#include <rsys/dynamic_array.h> - -struct spectral_data { - size_t iband; /* Index of the spectral band */ - size_t iquad; /* Quadrature point into the band */ -}; - -#define DARRAY_NAME specdata -#define DARRAY_DATA struct spectral_data -#include <rsys/dynamic_array.h> - -struct build_tree_context { - const struct htrdr_sky* sky; - double vxsz[3]; - double tau_threshold; /* Threshold criteria for the merge process */ - size_t iband; /* Index of the band that overlaps the CIE XYZ color space */ - size_t quadrature_range[2]; /* Range of quadrature point indices to handle */ - - /* Precomputed voxel data of the finest level. May be NULL <=> compute the - * voxel data at runtime. This structure is not used during the construction - * of the binary tree of the atmosphere */ - struct htrdr_grid* grid; -}; - -#define BUILD_TREE_CONTEXT_NULL__ {NULL,{0,0,0},0,0,{SIZE_MAX,SIZE_MAX},NULL} -static const struct build_tree_context BUILD_TREE_CONTEXT_NULL = - BUILD_TREE_CONTEXT_NULL__; - -struct vertex { - double x; - double y; - double z; -}; - -static char -vertex_eq(const struct vertex* v0, const struct vertex* v1) -{ - return eq_eps(v0->x, v1->x, 1.e-6) - && eq_eps(v0->y, v1->y, 1.e-6) - && eq_eps(v0->z, v1->z, 1.e-6); -} - -/* Generate the htable_vertex data structure */ -#define HTABLE_NAME vertex -#define HTABLE_KEY struct vertex -#define HTABLE_DATA size_t -#define HTABLE_KEY_FUNCTOR_EQ vertex_eq -#include <rsys/hash_table.h> - -/* Temporary data structure used to dump the octree data into a VTK file */ -struct octree_data { - struct htable_vertex vertex2id; /* Map a coordinate to its vertex id */ - struct darray_double vertices; /* Array of unique vertices */ - struct darray_double data; /* List of registered leaf data */ - struct darray_size_t cells; /* Ids of the cell vertices */ - size_t iband; /* Index of the band that overlaps the CIE XYZ color space */ - size_t iquad; /* Index of the quadrature point into the band */ - const struct htrdr_sky* sky; -}; - -struct trace_cloud_context { - struct svx_tree* clouds; - struct svx_hit* hit; - svx_hit_challenge_T challenge; - svx_hit_filter_T filter; - void* context; -}; -static const struct trace_cloud_context TRACE_CLOUD_CONTEXT_NULL = { - NULL, NULL, NULL, NULL, NULL -}; - -/* Properties of a short wave spectral band */ -struct sw_band_prop { - /* Average cross section in the band */ - double Ca_avg; /* Absorption cross section */ - double Cs_avg; /* Scattering cross section */ - - /* Average asymmetry parameter the band */ - double g_avg; -}; - -/* Encompass the hierarchical data structure of the cloud/atmospheric data and - * its associated descriptor. - * - * For each SVX voxel, the data of the optical property are stored - * linearly as N single precision floating point data, with N computed as - * bellow: - * - * N = HTRDR_PROPERTIES_COUNT__ #optical properties per voxel - * * HTRDR_SVX_OPS_COUNT__ #supported operations on each properties - * * HTRDR_COMPONENTS_COUNT__; #components on which properties are defined - * - * In a given voxel, the index `id' in [0, N-1] corresponding to the optical - * property `enum htrdr_sky_property P' of the component `enum - * htrdr_sky_component C' according to the operation `enum htrdr_svx_op O' is - * then computed as bellow: - * - * id = C * NFLOATS_PER_COMPONENT + P * HTRDR_SVX_OPS_COUNT__ + O; - * NFLOATS_PER_COMPONENT = HTRDR_SVX_OPS_COUNT__ * HTRDR_PROPERTIES_COUNT__; */ -struct cloud { - struct svx_tree* octree; - struct svx_tree_desc octree_desc; -}; -struct atmosphere { - struct svx_tree* bitree; - struct svx_tree_desc bitree_desc; -}; - -struct htrdr_sky { - struct cloud** clouds; /* Per sw_band cloud data structure */ - - /* Per sw_band and per quadrature point atmosphere data structure */ - struct atmosphere** atmosphere; - - struct htrdr_sun* sun; /* Sun attached to the sky */ - - /* Loaders of... */ - struct htcp* htcp; /* ... Cloud properties */ - struct htgop* htgop; /* ... Gas optical properties */ - struct htmie* htmie; /* ... Mie's data */ - - struct htcp_desc htcp_desc; /* Descriptor of the loaded LES data */ - - /* LUT used to map the index of a Z from the regular SVX to the irregular - * HTCP data */ - struct darray_split svx2htcp_z; - double lut_cell_sz; /* Size of a svx2htcp_z cell */ - - /* Ids and optical properties of the short wave spectral bands loaded by - * HTGOP and that overlap the CIE XYZ color space */ - size_t sw_bands_range[2]; - struct sw_band_prop* sw_bands; - - int repeat_clouds; /* Make clouds infinite in X and Y */ - int is_cloudy; /* The sky has clouds */ - - ref_T ref; - struct htrdr* htrdr; -}; - -/******************************************************************************* - * Helper function - ******************************************************************************/ -static INLINE int -aabb_intersect - (const double aabb0_low[3], - const double aabb0_upp[3], - const double aabb1_low[3], - const double aabb1_upp[3]) -{ - ASSERT(aabb0_low[0] < aabb0_upp[0] && aabb1_low[0] < aabb1_upp[0]); - ASSERT(aabb0_low[1] < aabb0_upp[1] && aabb1_low[1] < aabb1_upp[1]); - ASSERT(aabb0_low[2] < aabb0_upp[2] && aabb1_low[2] < aabb1_upp[2]); - return !(aabb0_upp[0] < aabb1_low[0]) && !(aabb0_low[0] > aabb1_upp[0]) - && !(aabb0_upp[1] < aabb1_low[1]) && !(aabb0_low[1] > aabb1_upp[1]) - && !(aabb0_upp[2] < aabb1_low[2]) && !(aabb0_low[2] > aabb1_upp[2]); -} - -static void -log_svx_memory_usage(struct htrdr* htrdr) -{ - char dump[128]; - char* dst = dump; - size_t available_space = sizeof(dump); - const size_t KILO_BYTE = 1024; - const size_t MEGA_BYTE = 1024*KILO_BYTE; - const size_t GIGA_BYTE = 1024*MEGA_BYTE; - size_t ngigas, nmegas, nkilos, memsz, len; - ASSERT(htrdr); - - memsz = MEM_ALLOCATED_SIZE(&htrdr->svx_allocator); - - if((ngigas = memsz / GIGA_BYTE) != 0) { - len = (size_t)snprintf(dst, available_space, - "%lu GB ", (unsigned long)ngigas); - CHK(len < available_space); - dst += len; - available_space -= len; - memsz -= ngigas * GIGA_BYTE; - } - if((nmegas = memsz / MEGA_BYTE) != 0) { - len = (size_t)snprintf(dst, available_space, - "%lu MB ", (unsigned long)nmegas); - CHK(len < available_space); - dst += len; - available_space -= len; - memsz -= nmegas * MEGA_BYTE; - } - if((nkilos = memsz / KILO_BYTE) != 0) { - len = (size_t)snprintf(dst, available_space, - "%lu KB ", (unsigned long)nkilos); - dst += len; - available_space -= len; - memsz -= nkilos * KILO_BYTE; - } - if(memsz) { - len = (size_t)snprintf(dst, available_space, - "%lu Byte%s", (unsigned long)memsz, memsz > 1 ? "s" : ""); - CHK(len < available_space); - } - htrdr_log(htrdr, "SVX memory usage: %s\n", dump); -} - -/* Transform pos from world to cloud space */ -static INLINE double* -world_to_cloud - (const struct htrdr_sky* sky, - const double pos_ws[3], - double out_pos_cs[3]) -{ - double cloud_sz[2]; - double pos_cs[3]; - double pos_cs_n[2]; - ASSERT(sky && pos_ws && out_pos_cs); - ASSERT(pos_ws[2] >= sky->htcp_desc.lower[2]); - ASSERT(pos_ws[2] <= sky->htcp_desc.upper[2]); - - if(!sky->repeat_clouds) { /* Nothing to do */ - return d3_set(out_pos_cs, pos_ws); - } - - if(!sky->repeat_clouds /* Nothing to do */ - || ( pos_ws[0] >= sky->htcp_desc.lower[0] - && pos_ws[0] <= sky->htcp_desc.upper[0] - && pos_ws[1] >= sky->htcp_desc.lower[1] - && pos_ws[1] <= sky->htcp_desc.upper[1])) { - return d3_set(out_pos_cs, pos_ws); - } - - cloud_sz[0] = sky->htcp_desc.upper[0] - sky->htcp_desc.lower[0]; - cloud_sz[1] = sky->htcp_desc.upper[1] - sky->htcp_desc.lower[1]; - - /* Transform pos in normalize local cloud space */ - pos_cs_n[0] = (pos_ws[0] - sky->htcp_desc.lower[0]) / cloud_sz[0]; - pos_cs_n[1] = (pos_ws[1] - sky->htcp_desc.lower[1]) / cloud_sz[1]; - pos_cs_n[0] -= (int)pos_cs_n[0]; /* Keep fractional part */ - pos_cs_n[1] -= (int)pos_cs_n[1]; /* Keep fractional part */ - if(pos_cs_n[0] < 0) pos_cs_n[0] += 1; - if(pos_cs_n[1] < 0) pos_cs_n[1] += 1; - - /* Transform pos in local cloud space */ - pos_cs[0] = sky->htcp_desc.lower[0] + pos_cs_n[0] * cloud_sz[0]; - pos_cs[1] = sky->htcp_desc.lower[1] + pos_cs_n[1] * cloud_sz[1]; - pos_cs[2] = pos_ws[2]; - - ASSERT(pos_cs[0] >= sky->htcp_desc.lower[0]); - ASSERT(pos_cs[0] <= sky->htcp_desc.upper[0]); - ASSERT(pos_cs[1] >= sky->htcp_desc.lower[1]); - ASSERT(pos_cs[1] <= sky->htcp_desc.upper[1]); - - return d3_set(out_pos_cs, pos_cs); -} - -static INLINE res_T -trace_cloud - (const double org[3], - const double dir[3], - const double range[2], - void* context, - int* hit) -{ - struct trace_cloud_context* ctx = context; - res_T res = RES_OK; - ASSERT(org && dir && range && context && hit); - - res = svx_tree_trace_ray(ctx->clouds, org, dir, range, ctx->challenge, - ctx->filter, ctx->context, ctx->hit); - if(res != RES_OK) return res; - - *hit = !SVX_HIT_NONE(ctx->hit); - return RES_OK; -} - -static FINLINE float -voxel_get - (const float* data, - const enum htrdr_sky_component comp, - const enum htrdr_sky_property prop, - const enum htrdr_svx_op op) -{ - ASSERT(data); - return data[comp*NFLOATS_PER_COMPONENT+ prop*HTRDR_SVX_OPS_COUNT__ + op]; -} - -static FINLINE void -voxel_set - (float* data, - const enum htrdr_sky_component comp, - const enum htrdr_sky_property prop, - const enum htrdr_svx_op op, - const float val) -{ - ASSERT(data); - data[comp*NFLOATS_PER_COMPONENT+ prop*HTRDR_SVX_OPS_COUNT__ + op] = val; -} - -/* Compute the dry air density in the cloud */ -static FINLINE double -cloud_dry_air_density - (const struct htcp_desc* desc, - const size_t ivox[3]) /* Index of the voxel */ -{ - double P = 0; /* Pressure in Pa */ - double T = 0; /* Temperature in K */ - ASSERT(desc && ivox); - P = htcp_desc_PABST_at(desc, ivox[0], ivox[1], ivox[2], 0/*time*/); - T = htcp_desc_T_at(desc, ivox[0], ivox[1], ivox[2], 0/*time*/); - return (P*DRY_AIR_MOLAR_MASS)/(T*GAS_CONSTANT); -} - -/* Compute the water molar fraction */ -static FINLINE double -cloud_water_vapor_molar_fraction - (const struct htcp_desc* desc, - const size_t ivox[3]) -{ - double rvt = 0; - ASSERT(desc && ivox); - rvt = htcp_desc_RVT_at(desc, ivox[0], ivox[1], ivox[2], 0/*time*/); - return rvt / (rvt + H2O_MOLAR_MASS/DRY_AIR_MOLAR_MASS); -} - -/* Smits intersection: "Efficiency issues for ray tracing" */ -static FINLINE void -ray_intersect_aabb - (const double org[3], - const double dir[3], - const double range[2], - const double low[3], - const double upp[3], - const int axis_mask, - double hit_range[2]) -{ - double hit[2]; - int i; - ASSERT(org && dir && range && low && upp && hit_range); - ASSERT(low[0] < upp[0]); - ASSERT(low[1] < upp[1]); - ASSERT(low[2] < upp[2]); - - hit_range[0] = INF; - hit_range[1] =-INF; - hit[0] = range[0]; - hit[1] = range[1]; - FOR_EACH(i, 0, 3) { - double t_min, t_max; - - if(!(BIT(i) & axis_mask)) continue; - - t_min = (low[i] - org[i]) / dir[i]; - t_max = (upp[i] - org[i]) / dir[i]; - - if(t_min > t_max) SWAP(double, t_min, t_max); - hit[0] = MMAX(t_min, hit[0]); - hit[1] = MMIN(t_max, hit[1]); - if(hit[0] > hit[1]) return; - } - hit_range[0] = hit[0]; - hit_range[1] = hit[1]; -} - -static INLINE void -octree_data_init - (const struct htrdr_sky* sky, - const size_t iband, - const size_t iquad, - struct octree_data* data) -{ - ASSERT(data); - ASSERT(iband >= sky->sw_bands_range[0]); - ASSERT(iband <= sky->sw_bands_range[1]); - (void)iquad; - htable_vertex_init(sky->htrdr->allocator, &data->vertex2id); - darray_double_init(sky->htrdr->allocator, &data->vertices); - darray_double_init(sky->htrdr->allocator, &data->data); - darray_size_t_init(sky->htrdr->allocator, &data->cells); - data->sky = sky; - data->iband = iband; - data->iquad = iquad; -} - -static INLINE void -octree_data_release(struct octree_data* data) -{ - ASSERT(data); - htable_vertex_release(&data->vertex2id); - darray_double_release(&data->vertices); - darray_double_release(&data->data); - darray_size_t_release(&data->cells); -} - -static INLINE void -register_leaf - (const struct svx_voxel* leaf, - const size_t ileaf, - void* context) -{ - struct octree_data* ctx = context; - struct vertex v[8]; - double kext_min; - double kext_max; - int i; - ASSERT(leaf && ctx); - (void)ileaf; - - /* Compute the leaf vertices */ - v[0].x = leaf->lower[0]; v[0].y = leaf->lower[1]; v[0].z = leaf->lower[2]; - v[1].x = leaf->upper[0]; v[1].y = leaf->lower[1]; v[1].z = leaf->lower[2]; - v[2].x = leaf->lower[0]; v[2].y = leaf->upper[1]; v[2].z = leaf->lower[2]; - v[3].x = leaf->upper[0]; v[3].y = leaf->upper[1]; v[3].z = leaf->lower[2]; - v[4].x = leaf->lower[0]; v[4].y = leaf->lower[1]; v[4].z = leaf->upper[2]; - v[5].x = leaf->upper[0]; v[5].y = leaf->lower[1]; v[5].z = leaf->upper[2]; - v[6].x = leaf->lower[0]; v[6].y = leaf->upper[1]; v[6].z = leaf->upper[2]; - v[7].x = leaf->upper[0]; v[7].y = leaf->upper[1]; v[7].z = leaf->upper[2]; - - FOR_EACH(i, 0, 8) { - size_t *pid = htable_vertex_find(&ctx->vertex2id, v+i); - size_t id; - if(pid) { - id = *pid; - } else { /* Register the leaf vertex */ - id = darray_double_size_get(&ctx->vertices)/3; - CHK(RES_OK == htable_vertex_set(&ctx->vertex2id, v+i, &id)); - CHK(RES_OK == darray_double_push_back(&ctx->vertices, &v[i].x)); - CHK(RES_OK == darray_double_push_back(&ctx->vertices, &v[i].y)); - CHK(RES_OK == darray_double_push_back(&ctx->vertices, &v[i].z)); - } - /* Add the vertex id to the leaf cell */ - CHK(RES_OK == darray_size_t_push_back(&ctx->cells, &id)); - } - - /* Register the leaf data */ - kext_max = htrdr_sky_fetch_svx_voxel_property(ctx->sky, HTRDR_Kext, - HTRDR_SVX_MAX, HTRDR_ALL_COMPONENTS, ctx->iband, ctx->iquad, leaf); - kext_min = htrdr_sky_fetch_svx_voxel_property(ctx->sky, HTRDR_Kext, - HTRDR_SVX_MIN, HTRDR_ALL_COMPONENTS, ctx->iband, ctx->iquad, leaf); - CHK(RES_OK == darray_double_push_back(&ctx->data, &kext_min)); - CHK(RES_OK == darray_double_push_back(&ctx->data, &kext_max)); -} - -static void -clean_clouds(struct htrdr_sky* sky) -{ - size_t nbands; - size_t i; - ASSERT(sky); - - if(!sky->clouds) return; - - nbands = htrdr_sky_get_sw_spectral_bands_count(sky); - FOR_EACH(i, 0, nbands) { - struct htgop_spectral_interval band; - size_t iband; - size_t iquad; - - iband = sky->sw_bands_range[0] + i; - HTGOP(get_sw_spectral_interval(sky->htgop, iband, &band)); - - if(!sky->clouds[i]) continue; - - FOR_EACH(iquad, 0, band.quadrature_length) { - if(sky->clouds[i][iquad].octree) { - SVX(tree_ref_put(sky->clouds[i][iquad].octree)); - sky->clouds[i][iquad].octree = NULL; - } - } - MEM_RM(sky->htrdr->allocator, sky->clouds[i]); - } - MEM_RM(sky->htrdr->allocator, sky->clouds); - sky->clouds = NULL; -} - -static void -clean_atmosphere(struct htrdr_sky* sky) -{ - size_t nbands; - size_t i; - ASSERT(sky); - - if(!sky->atmosphere) return; - - nbands = htrdr_sky_get_sw_spectral_bands_count(sky); - FOR_EACH(i, 0, nbands) { - struct htgop_spectral_interval band; - size_t iband; - size_t iquad; - - iband = sky->sw_bands_range[0] + i; - HTGOP(get_sw_spectral_interval(sky->htgop, iband, &band)); - - if(!sky->atmosphere[i]) continue; - - FOR_EACH(iquad, 0, band.quadrature_length) { - if(sky->atmosphere[i][iquad].bitree) { - SVX(tree_ref_put(sky->atmosphere[i][iquad].bitree)); - sky->atmosphere[i][iquad].bitree = NULL; - } - } - MEM_RM(sky->htrdr->allocator, sky->atmosphere[i]); - } - MEM_RM(sky->htrdr->allocator, sky->atmosphere); - sky->atmosphere = NULL; -} - -static void -cloud_vox_get_particle - (const size_t xyz[3], - float vox[], - const struct build_tree_context* ctx) -{ - const struct htcp_desc* htcp_desc; - size_t ivox[3]; - size_t igrid_low[3], igrid_upp[3]; - double vox_low[3], vox_upp[3]; - double low[3], upp[3]; - double rct; - double ka, ks, kext; - double ka_min, ka_max; - double ks_min, ks_max; - double kext_min, kext_max; - double rho_da; /* Dry air density */ - double Ca_avg; - double Cs_avg; - double ipart; - size_t i; - ASSERT(xyz && vox && ctx); - - i = ctx->iband - ctx->sky->sw_bands_range[0]; - htcp_desc = &ctx->sky->htcp_desc; - - /* Fetch the optical properties of the spectral band */ - Ca_avg = ctx->sky->sw_bands[i].Ca_avg; - Cs_avg = ctx->sky->sw_bands[i].Cs_avg; - - /* Compute the AABB of the SVX voxel */ - vox_low[0] = (double)xyz[0] * ctx->vxsz[0] + htcp_desc->lower[0]; - vox_low[1] = (double)xyz[1] * ctx->vxsz[1] + htcp_desc->lower[1]; - vox_low[2] = (double)xyz[2] * ctx->vxsz[2] + htcp_desc->lower[2]; - vox_upp[0] = vox_low[0] + ctx->vxsz[0]; - vox_upp[1] = vox_low[1] + ctx->vxsz[1]; - vox_upp[2] = vox_low[2] + ctx->vxsz[2]; - - /* Compute the *inclusive* bounds of the indices of cloud grid overlapped by - * the SVX voxel in X and Y */ - low[0] = (vox_low[0]-htcp_desc->lower[0])/htcp_desc->vxsz_x; - low[1] = (vox_low[1]-htcp_desc->lower[1])/htcp_desc->vxsz_x; - upp[0] = (vox_upp[0]-htcp_desc->lower[0])/htcp_desc->vxsz_y; - upp[1] = (vox_upp[1]-htcp_desc->lower[1])/htcp_desc->vxsz_y; - igrid_low[0] = (size_t)low[0]; - igrid_low[1] = (size_t)low[1]; - igrid_upp[0] = (size_t)upp[0] - (modf(upp[0], &ipart)==0); - igrid_upp[1] = (size_t)upp[1] - (modf(upp[1], &ipart)==0); - ASSERT(igrid_low[0] <= igrid_upp[0]); - ASSERT(igrid_low[1] <= igrid_upp[1]); - - if(!ctx->sky->htcp_desc.irregular_z) { /* 1 LES voxel <=> 1 SVX voxel */ - /* Compute the *inclusive* bounds of the indices of cloud grid overlapped by - * the SVX voxel along the Z axis */ - low[2] = (vox_low[2]-htcp_desc->lower[2])/htcp_desc->vxsz_z[0]; - upp[2] = (vox_upp[2]-htcp_desc->lower[2])/htcp_desc->vxsz_z[0]; - igrid_low[2] = (size_t)low[2]; - igrid_upp[2] = (size_t)upp[2] - (modf(upp[2], &ipart)==0); - ASSERT(igrid_low[2] <= igrid_upp[2]); - - /* Prepare the iteration over the grid voxels overlapped by the SVX voxel */ - ka_min = ks_min = kext_min = DBL_MAX; - ka_max = ks_max = kext_max =-DBL_MAX; - - /* Iterate over the grid voxels overlapped by the SVX voxel */ - FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { - FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { - FOR_EACH(ivox[2], igrid_low[2], igrid_upp[2]+1) { - /* Compute the radiative properties */ - rho_da = cloud_dry_air_density(htcp_desc, ivox); - rct = htcp_desc_RCT_at(htcp_desc, ivox[0], ivox[1], ivox[2], 0); - ka = Ca_avg * rho_da * rct; - ks = Cs_avg * rho_da * rct; - kext = ka + ks; - /* Update the boundaries of the radiative properties */ - ka_min = MMIN(ka_min, ka); - ka_max = MMAX(ka_max, ka); - ks_min = MMIN(ks_min, ks); - ks_max = MMAX(ks_max, ks); - kext_min = MMIN(kext_min, kext); - kext_max = MMAX(kext_max, kext); - #ifndef NDEBUG - { - double tmp_low[3], tmp_upp[3]; - htcp_desc_get_voxel_aabb - (&ctx->sky->htcp_desc, ivox[0], ivox[1], ivox[2], tmp_low, tmp_upp); - ASSERT(aabb_intersect(tmp_low, tmp_upp, vox_low, vox_upp)); - } - #endif - } - } - } - } else { - double pos_z; - size_t ilut_low, ilut_upp; - size_t ilut; - size_t ivox_z_prev = SIZE_MAX; - - /* Compute the *inclusive* bounds of the indices of the LUT cells - * overlapped by the SVX voxel */ - ilut_low = (size_t)((vox_low[2]-htcp_desc->lower[2])/ctx->sky->lut_cell_sz); - ilut_upp = (size_t)((vox_upp[2]-htcp_desc->lower[2])/ctx->sky->lut_cell_sz); - ASSERT(ilut_low <= ilut_upp); - - /* Prepare the iteration over the LES voxels overlapped by the SVX voxel */ - ka_min = ks_min = kext_min = DBL_MAX; - ka_max = ks_max = kext_max =-DBL_MAX; - ivox_z_prev = SIZE_MAX; - pos_z = vox_low[2]; - ASSERT(pos_z >= (double)ilut_low * ctx->sky->lut_cell_sz); - ASSERT(pos_z <= (double)ilut_upp * ctx->sky->lut_cell_sz); - - /* Iterate over the LUT cells overlapped by the voxel */ - FOR_EACH(ilut, ilut_low, ilut_upp+1) { - const struct split* split = darray_split_cdata_get(&ctx->sky->svx2htcp_z)+ilut; - ASSERT(ilut < darray_split_size_get(&ctx->sky->svx2htcp_z)); - - ivox[2] = pos_z <= split->height ? split->index : split->index + 1; - if(ivox[2] >= ctx->sky->htcp_desc.spatial_definition[2] - && eq_eps(pos_z, split->height, 1.e-6)) { /* Handle numerical inaccuracy */ - ivox[2] = split->index; - } - - /* Compute the upper bound of the *next* LUT cell clamped to the voxel - * upper bound. Note that the upper bound of the current LUT cell is - * the lower bound of the next cell, i.e. (ilut+1)*lut_cell_sz. The - * upper bound of the next cell is thus the lower bound of the cell - * following the next cell, i.e. (ilut+2)*lut_cell_sz */ - pos_z = MMIN((double)(ilut+2)*ctx->sky->lut_cell_sz, vox_upp[2]); - - /* Does the LUT cell overlap an already handled LES voxel? */ - if(ivox[2] == ivox_z_prev) continue; - ivox_z_prev = ivox[2]; - - FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { - FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { - - /* Compute the radiative properties */ - rho_da = cloud_dry_air_density(htcp_desc, ivox); - rct = htcp_desc_RCT_at(htcp_desc, ivox[0], ivox[1], ivox[2], 0); - ka = Ca_avg * rho_da * rct; - ks = Cs_avg * rho_da * rct; - kext = ka + ks; - - /* Update the boundaries of the radiative properties */ - ka_min = MMIN(ka_min, ka); - ka_max = MMAX(ka_max, ka); - ks_min = MMIN(ks_min, ks); - ks_max = MMAX(ks_max, ks); - kext_min = MMIN(kext_min, kext); - kext_max = MMAX(kext_max, kext); - } - } - } - } - - /* Ensure that the single precision bounds include their double precision - * version. */ - if(ka_min != (float)ka_min) ka_min = nextafterf((float)ka_min,-FLT_MAX); - if(ka_max != (float)ka_max) ka_max = nextafterf((float)ka_max, FLT_MAX); - if(ks_min != (float)ks_min) ks_min = nextafterf((float)ks_min,-FLT_MAX); - if(ks_max != (float)ks_max) ks_max = nextafterf((float)ks_max, FLT_MAX); - if(kext_min != (float)kext_min) kext_min = nextafterf((float)kext_min,-FLT_MAX); - if(kext_max != (float)kext_max) kext_max = nextafterf((float)kext_max, FLT_MAX); - - voxel_set(vox, HTRDR_PARTICLES__, HTRDR_Ka, HTRDR_SVX_MIN, (float)ka_min); - voxel_set(vox, HTRDR_PARTICLES__, HTRDR_Ka, HTRDR_SVX_MAX, (float)ka_max); - voxel_set(vox, HTRDR_PARTICLES__, HTRDR_Ks, HTRDR_SVX_MIN, (float)ks_min); - voxel_set(vox, HTRDR_PARTICLES__, HTRDR_Ks, HTRDR_SVX_MAX, (float)ks_max); - voxel_set(vox, HTRDR_PARTICLES__, HTRDR_Kext, HTRDR_SVX_MIN, (float)kext_min); - voxel_set(vox, HTRDR_PARTICLES__, HTRDR_Kext, HTRDR_SVX_MAX, (float)kext_max); -} - -static void -cloud_vox_get_gas - (const size_t xyz[3], - float vox[], - const struct build_tree_context* ctx) -{ - const struct htcp_desc* htcp_desc; - size_t ivox[3]; - size_t igrid_low[3], igrid_upp[3]; - struct htgop_layer layer; - struct htgop_layer_sw_spectral_interval band; - size_t ilayer; - size_t layer_range[2]; - double x_h2o_range[2]; - double low[3], upp[3]; - double vox_low[3], vox_upp[3]; /* Voxel AABB */ - double ka_min, ka_max; - double ks_min, ks_max; - double kext_min, kext_max; - double x_h2o; - double ipart; - ASSERT(xyz && vox && ctx); - - htcp_desc = &ctx->sky->htcp_desc; - - /* Compute the AABB of the SVX voxel */ - vox_low[0] = (double)xyz[0] * ctx->vxsz[0] + htcp_desc->lower[0]; - vox_low[1] = (double)xyz[1] * ctx->vxsz[1] + htcp_desc->lower[1]; - vox_low[2] = (double)xyz[2] * ctx->vxsz[2] + htcp_desc->lower[2]; - vox_upp[0] = vox_low[0] + ctx->vxsz[0]; - vox_upp[1] = vox_low[1] + ctx->vxsz[1]; - vox_upp[2] = vox_low[2] + ctx->vxsz[2]; - - /* Compute the *inclusive* bounds of the indices of cloud grid overlapped by - * the SVX voxel in X and Y */ - low[0] = (vox_low[0]-htcp_desc->lower[0])/htcp_desc->vxsz_x; - low[1] = (vox_low[1]-htcp_desc->lower[1])/htcp_desc->vxsz_x; - upp[0] = (vox_upp[0]-htcp_desc->lower[0])/htcp_desc->vxsz_y; - upp[1] = (vox_upp[1]-htcp_desc->lower[1])/htcp_desc->vxsz_y; - igrid_low[0] = (size_t)low[0]; - igrid_low[1] = (size_t)low[1]; - igrid_upp[0] = (size_t)upp[0] - (modf(upp[0], &ipart)==0); - igrid_upp[1] = (size_t)upp[1] - (modf(upp[1], &ipart)==0); - ASSERT(igrid_low[0] <= igrid_upp[0]); - ASSERT(igrid_low[1] <= igrid_upp[1]); - - /* Define the xH2O range from the LES data */ - if(!ctx->sky->htcp_desc.irregular_z) { /* 1 LES voxel <=> 1 SVX voxel */ - /* Compute the *inclusive* bounds of the indices of cloud grid overlapped by - * the SVX voxel in Z */ - low[2] = (vox_low[2]-htcp_desc->lower[2])/htcp_desc->vxsz_z[0]; - upp[2] = (vox_upp[2]-htcp_desc->lower[2])/htcp_desc->vxsz_z[0]; - igrid_low[2] = (size_t)low[2]; - igrid_upp[2] = (size_t)upp[2] - (modf(upp[2], &ipart)==0); - ASSERT(igrid_low[2] <= igrid_upp[2]); - - /* Prepare the iteration overt the grid voxels overlapped by the SVX voxel */ - x_h2o_range[0] = DBL_MAX; - x_h2o_range[1] =-DBL_MAX; - - FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { - FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { - FOR_EACH(ivox[2], igrid_low[2], igrid_upp[2]+1) { - - /* Compute the xH2O for the current LES voxel */ - x_h2o = cloud_water_vapor_molar_fraction(htcp_desc, ivox); - - /* Update the xH2O range of the SVX voxel */ - x_h2o_range[0] = MMIN(x_h2o, x_h2o_range[0]); - x_h2o_range[1] = MMAX(x_h2o, x_h2o_range[1]); - #ifndef NDEBUG - { - double tmp_low[3], tmp_upp[3]; - htcp_desc_get_voxel_aabb - (&ctx->sky->htcp_desc, ivox[0], ivox[1], ivox[2], tmp_low, tmp_upp); - ASSERT(aabb_intersect(tmp_low, tmp_upp, vox_low, vox_upp)); - } - #endif - } - } - } - } else { /* A SVX voxel can be overlapped by 2 LES voxels */ - double pos_z; - size_t ilut_low, ilut_upp; - size_t ilut; - size_t ivox_z_prev; - ASSERT(xyz[2] < darray_split_size_get(&ctx->sky->svx2htcp_z)); - - /* Compute the *inclusive* bounds of the indices of the LUT cells - * overlapped by the SVX voxel */ - ilut_low = (size_t)((vox_low[2] - htcp_desc->lower[2]) / ctx->sky->lut_cell_sz); - ilut_upp = (size_t)((vox_upp[2] - htcp_desc->lower[2]) / ctx->sky->lut_cell_sz); - ASSERT(ilut_low <= ilut_upp); - - /* Prepare the iteration over the LES voxels overlapped by the SVX voxel */ - x_h2o_range[0] = DBL_MAX; - x_h2o_range[1] =-DBL_MAX; - ivox_z_prev = SIZE_MAX; - pos_z = vox_low[2]; - ASSERT(pos_z >= (double)ilut_low * ctx->sky->lut_cell_sz); - ASSERT(pos_z <= (double)ilut_upp * ctx->sky->lut_cell_sz); - - /* Iterate over the LUT cells overlapped by the voxel */ - FOR_EACH(ilut, ilut_low, ilut_upp+1) { - const struct split* split = darray_split_cdata_get(&ctx->sky->svx2htcp_z)+ilut; - ASSERT(ilut < darray_split_size_get(&ctx->sky->svx2htcp_z)); - - ivox[2] = pos_z <= split->height ? split->index : split->index + 1; - if(ivox[2] >= ctx->sky->htcp_desc.spatial_definition[2] - && eq_eps(pos_z, split->height, 1.e-6)) { /* Handle numerical inaccuracy */ - ivox[2] = split->index; - } - - /* Compute the upper bound of the *next* LUT cell clamped to the voxel - * upper bound. Note that the upper bound of the current LUT cell is - * the lower bound of the next cell, i.e. (ilut+1)*lut_cell_sz. The - * upper bound of the next cell is thus the lower bound of the cell - * following the next cell, i.e. (ilut+2)*lut_cell_sz */ - pos_z = MMIN((double)(ilut+2)*ctx->sky->lut_cell_sz, vox_upp[2]); - - /* Does the LUT voxel overlap an already handled LES voxel? */ - if(ivox[2] == ivox_z_prev) continue; - ivox_z_prev = ivox[2]; - - FOR_EACH(ivox[0], igrid_low[0], igrid_upp[0]+1) { - FOR_EACH(ivox[1], igrid_low[1], igrid_upp[1]+1) { - - /* Compute the xH2O for the current LES voxel */ - x_h2o = cloud_water_vapor_molar_fraction(&ctx->sky->htcp_desc, ivox); - - /* Update the xH2O range of the SVX voxel */ - x_h2o_range[0] = MMIN(x_h2o, x_h2o_range[0]); - x_h2o_range[1] = MMAX(x_h2o, x_h2o_range[1]); - } - } - } - } - - /* Define the atmospheric layers overlapped by the SVX voxel */ - HTGOP(position_to_layer_id(ctx->sky->htgop, vox_low[2], &layer_range[0])); - HTGOP(position_to_layer_id(ctx->sky->htgop, vox_upp[2], &layer_range[1])); - - ka_min = ks_min = kext_min = DBL_MAX; - ka_max = ks_max = kext_max =-DBL_MAX; - - /* For each atmospheric layer that overlaps the SVX voxel ... */ - FOR_EACH(ilayer, layer_range[0], layer_range[1]+1) { - double k[2]; - - HTGOP(get_layer(ctx->sky->htgop, ilayer, &layer)); - - /* ... retrieve the considered spectral interval */ - HTGOP(layer_get_sw_spectral_interval(&layer, ctx->iband, &band)); - ASSERT(ctx->quadrature_range[0] <= ctx->quadrature_range[1]); - ASSERT(ctx->quadrature_range[1] < band.quadrature_length); - - /* ... and compute the radiative properties and upd their bounds */ - HTGOP(layer_sw_spectral_interval_quadpoints_get_ka_bounds - (&band, ctx->quadrature_range, x_h2o_range, k)); - ka_min = MMIN(ka_min, k[0]); - ka_max = MMAX(ka_max, k[1]); - HTGOP(layer_sw_spectral_interval_quadpoints_get_ks_bounds - (&band, ctx->quadrature_range, x_h2o_range, k)); - ks_min = MMIN(ks_min, k[0]); - ks_max = MMAX(ks_max, k[1]); - HTGOP(layer_sw_spectral_interval_quadpoints_get_kext_bounds - (&band, ctx->quadrature_range, x_h2o_range, k)); - kext_min = MMIN(kext_min, k[0]); - kext_max = MMAX(kext_max, k[1]); - } - - /* Ensure that the single precision bounds include their double precision - * version. */ - if(ka_min != (float)ka_min) ka_min = nextafterf((float)ka_min,-FLT_MAX); - if(ka_max != (float)ka_max) ka_max = nextafterf((float)ka_max, FLT_MAX); - if(ks_min != (float)ks_min) ks_min = nextafterf((float)ks_min,-FLT_MAX); - if(ks_max != (float)ks_max) ks_max = nextafterf((float)ks_max, FLT_MAX); - if(kext_min != (float)kext_min) kext_min = nextafterf((float)kext_min,-FLT_MAX); - if(kext_max != (float)kext_max) kext_max = nextafterf((float)kext_max, FLT_MAX); - - voxel_set(vox, HTRDR_GAS__, HTRDR_Ka, HTRDR_SVX_MIN, (float)ka_min); - voxel_set(vox, HTRDR_GAS__, HTRDR_Ka, HTRDR_SVX_MAX, (float)ka_max); - voxel_set(vox, HTRDR_GAS__, HTRDR_Ks, HTRDR_SVX_MIN, (float)ks_min); - voxel_set(vox, HTRDR_GAS__, HTRDR_Ks, HTRDR_SVX_MAX, (float)ks_max); - voxel_set(vox, HTRDR_GAS__, HTRDR_Kext, HTRDR_SVX_MIN, (float)kext_min); - voxel_set(vox, HTRDR_GAS__, HTRDR_Kext, HTRDR_SVX_MAX, (float)kext_max); -} - -static void -cloud_vox_get(const size_t xyz[3], void* dst, void* context) -{ - struct build_tree_context* ctx = context; - ASSERT(context); - - if(ctx->grid) { /* Fetch voxel data from precomputed grid */ - const float* vox_data = htrdr_grid_at(ctx->grid, xyz); - memcpy(dst, vox_data, NFLOATS_PER_VOXEL * sizeof(float)); - } else { - /* No precomputed grid. Compute the voxel data at runtime */ - cloud_vox_get_particle(xyz, dst, ctx); - cloud_vox_get_gas(xyz, dst, ctx); - } -} - -static INLINE void -vox_merge_component - (float* vox_out, - const enum htrdr_sky_component comp, - const float* voxels[], - const size_t nvoxs) -{ - float ka_min = FLT_MAX; - float ka_max =-FLT_MAX; - float ks_min = FLT_MAX; - float ks_max =-FLT_MAX; - float kext_min = FLT_MAX; - float kext_max =-FLT_MAX; - size_t ivox; - ASSERT(vox_out && voxels && nvoxs); - - FOR_EACH(ivox, 0, nvoxs) { - const float* vox = voxels[ivox]; - - ka_min = MMIN(ka_min, voxel_get(vox, comp, HTRDR_Ka, HTRDR_SVX_MIN)); - ka_max = MMAX(ka_max, voxel_get(vox, comp, HTRDR_Ka, HTRDR_SVX_MAX)); - ks_min = MMIN(ks_min, voxel_get(vox, comp, HTRDR_Ks, HTRDR_SVX_MIN)); - ks_max = MMAX(ks_max, voxel_get(vox, comp, HTRDR_Ks, HTRDR_SVX_MAX)); - kext_min = MMIN(kext_min, voxel_get(vox, comp, HTRDR_Kext, HTRDR_SVX_MIN)); - kext_max = MMAX(kext_max, voxel_get(vox, comp, HTRDR_Kext, HTRDR_SVX_MAX)); - } - - voxel_set(vox_out, comp, HTRDR_Ka, HTRDR_SVX_MIN, ka_min); - voxel_set(vox_out, comp, HTRDR_Ka, HTRDR_SVX_MAX, ka_max); - voxel_set(vox_out, comp, HTRDR_Ks, HTRDR_SVX_MIN, ks_min); - voxel_set(vox_out, comp, HTRDR_Ks, HTRDR_SVX_MAX, ks_max); - voxel_set(vox_out, comp, HTRDR_Kext, HTRDR_SVX_MIN, kext_min); - voxel_set(vox_out, comp, HTRDR_Kext, HTRDR_SVX_MAX, kext_max); -} - -static void -cloud_vox_merge(void* dst, const void* voxels[], const size_t nvoxs, void* context) -{ - ASSERT(dst && voxels && nvoxs); - (void)context; - vox_merge_component(dst, HTRDR_PARTICLES__, (const float**)voxels, nvoxs); - vox_merge_component(dst, HTRDR_GAS__, (const float**)voxels, nvoxs); -} - -static INLINE int -vox_challenge_merge_component - (const enum htrdr_sky_component comp, - const struct svx_voxel voxels[], - const size_t nvoxs, - struct build_tree_context* ctx) -{ - double lower_z = DBL_MAX; - double upper_z =-DBL_MAX; - double dst; - float kext_min = FLT_MAX; - float kext_max =-FLT_MAX; - size_t ivox; - ASSERT(voxels && nvoxs && ctx); - - FOR_EACH(ivox, 0, nvoxs) { - const float* vox = voxels[ivox].data; - kext_min = MMIN(kext_min, voxel_get(vox, comp, HTRDR_Kext, HTRDR_SVX_MIN)); - kext_max = MMAX(kext_max, voxel_get(vox, comp, HTRDR_Kext, HTRDR_SVX_MAX)); - lower_z = MMIN(voxels[ivox].lower[2], lower_z); - upper_z = MMAX(voxels[ivox].upper[2], upper_z); - } - dst = upper_z - lower_z; - return (kext_max - kext_min)*dst <= ctx->tau_threshold; -} - -static int -cloud_vox_challenge_merge - (const struct svx_voxel voxels[], const size_t nvoxs, void* ctx) -{ - ASSERT(voxels); - return vox_challenge_merge_component(HTRDR_PARTICLES__, voxels, nvoxs, ctx) - && vox_challenge_merge_component(HTRDR_GAS__, voxels, nvoxs, ctx); -} - -static res_T -setup_temp_dir - (struct htrdr_sky* sky, - const char* htgop_filename, - const char* htmie_filename, - struct str* out_path) -{ - struct str path; - struct str htgop; - struct str htmie; - res_T res = RES_OK; - ASSERT(sky && htgop_filename && htmie_filename && out_path); - - str_init(sky->htrdr->allocator, &path); - str_init(sky->htrdr->allocator, &htgop); - str_init(sky->htrdr->allocator, &htmie); - - CHK(RES_OK == str_set(&htgop, htgop_filename)); - CHK(RES_OK == str_set(&htmie, htmie_filename)); - CHK(RES_OK == str_set(&htgop, basename(str_get(&htgop)))); - CHK(RES_OK == str_set(&htmie, basename(str_get(&htmie)))); - CHK(RES_OK == str_append_char(&htgop, '/')); - CHK(RES_OK == str_append_char(&htmie, '/')); - - CHK(RES_OK == str_set(&path, ".htrdr/")); - res = create_directory(sky->htrdr, str_cget(&path)); - if(res != RES_OK) goto error; - - CHK(RES_OK == str_append(&path, str_cget(&htmie))); - res = create_directory(sky->htrdr, str_cget(&path)); - if(res != RES_OK) goto error; - - CHK(RES_OK == str_append(&path, str_cget(&htgop))); - res = create_directory(sky->htrdr, str_cget(&path)); - if(res != RES_OK) goto error; - - CHK(RES_OK == str_copy_and_release(out_path, &path)); - -exit: - str_release(&htgop); - str_release(&htmie); - return res; -error: - str_release(&path); - goto exit; -} - -/* Create/load a grid of cloud data used by SVX to build the octree. The grid - * is saved in the directory where htrdr is run with a name generated from the - * "htcp_filename" path. If a grid with the same name exists, the function - * tries to load it except if the force_update flag is set. Even though the - * grid is loaded from disk, the function will recompute and store it if the - * definition of the loaded grid is different from the submitted definition. */ -static res_T -setup_cloud_grid - (struct htrdr_sky* sky, - const size_t definition[3], - const size_t iband, - const size_t iquad, - const char* htcp_filename, - const char* htgop_filename, - const char* htmie_filename, - const int force_update, - struct htrdr_grid** out_grid) -{ - struct htrdr_grid* grid = NULL; - struct str path; - struct str str; - struct build_tree_context ctx = BUILD_TREE_CONTEXT_NULL; - struct htgop_spectral_interval band; - size_t sizeof_cell; - size_t ncells; - uint64_t mcode; - uint64_t mcode_max; - char buf[16]; - size_t progress = 0; - ATOMIC ncells_computed = 0; - res_T res = RES_OK; - ASSERT(sky && definition && htcp_filename && out_grid); - ASSERT(definition[0] && definition[1] && definition[2]); - ASSERT(iband >= sky->sw_bands_range[0] && iband <= sky->sw_bands_range[1]); - - str_init(sky->htrdr->allocator, &str); - str_init(sky->htrdr->allocator, &path); - - CHK((size_t)snprintf(buf, sizeof(buf), ".%lu.%lu", iband, iquad) < sizeof(buf)); - - res = setup_temp_dir(sky, htgop_filename, htmie_filename, &path); - if(res != RES_OK) goto error; - - /* Build the grid path */ - CHK(RES_OK == str_set(&str, htcp_filename)); - CHK(RES_OK == str_set(&str, basename(str_get(&str)))); - CHK(RES_OK == str_append(&path, str_cget(&str))); - CHK(RES_OK == str_append(&path, ".grid")); - CHK(RES_OK == str_append(&path, buf)); - - if(!force_update) { - /* Try to open an already saved grid */ - res = htrdr_grid_open(sky->htrdr, str_cget(&path), &grid); - if(res != RES_OK) { - htrdr_log_warn(sky->htrdr, "cannot open the grid `%s'.\n", str_cget(&path)); - } else { - size_t grid_def[3]; - - /* Check that the definition is the loaded grid is the same of the - * submitted grid definition */ - htrdr_grid_get_definition(grid, grid_def); - if(grid_def[0] == definition[0] - && grid_def[1] == definition[1] - && grid_def[2] == definition[2]) { - htrdr_log(sky->htrdr, - "Use the precomputed grid `%s'.\n", str_cget(&path)); - goto exit; /* No more work to do. The loaded data seems valid */ - } - - /* The grid is no more valid. Update it! */ - htrdr_grid_ref_put(grid); - grid = NULL; - } - } - - sizeof_cell = NFLOATS_PER_COMPONENT * 2/*gas & particle*/ * sizeof(float); - - htrdr_log(sky->htrdr, "Compute the grid `%s'.\n", str_cget(&path)); - - res = htrdr_grid_create - (sky->htrdr, definition, sizeof_cell, str_cget(&path), 1, &grid); - if(res != RES_OK) goto error; - - ctx.sky = sky; - ctx.tau_threshold = DBL_MAX; /* Unused for grid construction */ - ctx.iband = iband; - - HTGOP(get_sw_spectral_interval(sky->htgop, ctx.iband, &band)); - ctx.quadrature_range[0] = iquad; - ctx.quadrature_range[1] = iquad; - - /* Compute the size of a SVX voxel */ - ctx.vxsz[0] = sky->htcp_desc.upper[0] - sky->htcp_desc.lower[0]; - ctx.vxsz[1] = sky->htcp_desc.upper[1] - sky->htcp_desc.lower[1]; - ctx.vxsz[2] = sky->htcp_desc.upper[2] - sky->htcp_desc.lower[2]; - ctx.vxsz[0] = ctx.vxsz[0] / (double)sky->htcp_desc.spatial_definition[0]; - ctx.vxsz[1] = ctx.vxsz[1] / (double)sky->htcp_desc.spatial_definition[1]; - ctx.vxsz[2] = ctx.vxsz[2] / (double)sky->htcp_desc.spatial_definition[2]; - ASSERT(eq_eps(ctx.vxsz[0], sky->htcp_desc.vxsz_x, 1.e-6)); - ASSERT(eq_eps(ctx.vxsz[1], sky->htcp_desc.vxsz_y, 1.e-6)); - ASSERT(eq_eps(ctx.vxsz[2], sky->htcp_desc.vxsz_z[0], 1.e-6) - || sky->htcp_desc.irregular_z); - - /* Conservatively define the maximum morton code of the htrdr_grid */ - mcode_max = MMAX(MMAX(definition[0], definition[1]), definition[2]); - mcode_max = round_up_pow2(mcode_max); - mcode_max = mcode_max*mcode_max*mcode_max; - - /* Compute the overall number of voxels in the htrdr_grid */ - ncells = definition[0] * definition[1] * definition[2]; - - htrdr_fprintf(sky->htrdr, stderr, - "Generating cloud grid %lu.%lu: %3u%%", iband, iquad, 0); - htrdr_fflush(sky->htrdr, stderr); - - omp_set_num_threads((int)sky->htrdr->nthreads); - #pragma omp parallel for - for(mcode=0; mcode<mcode_max; ++mcode) { - size_t xyz[3]; - size_t pcent; - size_t n; - - /* Discard out of bound voxels */ - if((xyz[0] = morton3D_decode_u21(mcode >> 2)) >= definition[0]) continue; - if((xyz[1] = morton3D_decode_u21(mcode >> 1)) >= definition[1]) continue; - if((xyz[2] = morton3D_decode_u21(mcode >> 0)) >= definition[2]) continue; - - /* Compute the voxel data */ - cloud_vox_get(xyz, htrdr_grid_at_mcode(grid, mcode), &ctx); - - /* Update the progress message */ - n = (size_t)ATOMIC_INCR(&ncells_computed); - pcent = n * 100 / ncells; - #pragma omp critical - if(pcent > progress) { - progress = pcent; - htrdr_fprintf(sky->htrdr, stderr, - "%c[2K\rGenerating cloud grid %lu.%lu: %3u%%", - 27, iband, iquad, (unsigned)pcent); - htrdr_fflush(sky->htrdr, stderr); - } - } - htrdr_fprintf(sky->htrdr, stderr, "\n"); - -exit: - *out_grid = grid; - str_release(&str); - str_release(&path); - return res; - return res; -error: - if(grid) { - htrdr_grid_ref_put(grid); - grid = NULL; - } - goto exit; -} - -static res_T -setup_clouds - (struct htrdr_sky* sky, - const char* htcp_filename, - const char* htgop_filename, - const char* htmie_filename, - const double optical_thickness_threshold, - const int force_cache_update) -{ - struct darray_specdata specdata; - const size_t* raw_def; - size_t nvoxs[3]; - double vxsz[3]; - double low[3]; - double upp[3]; - int64_t ispecdata; - size_t nbands; - size_t i; - ATOMIC nbuilt_octrees = 0; - ATOMIC res = RES_OK; - ASSERT(sky && sky->sw_bands && optical_thickness_threshold >= 0); - - darray_specdata_init(sky->htrdr->allocator, &specdata); - - res = htcp_get_desc(sky->htcp, &sky->htcp_desc); - if(res != RES_OK) { - htrdr_log_err(sky->htrdr, "could not retrieve the HTCP descriptor.\n"); - goto error; - } - - htrdr_log(sky->htrdr, "Clouds bounding box: {%g, %g, %g} / {%g, %g, %g}.\n", - SPLIT3(sky->htcp_desc.lower), SPLIT3(sky->htcp_desc.upper)); - - /* Define the number of voxels */ - raw_def = sky->htcp_desc.spatial_definition; - nvoxs[0] = MMIN(raw_def[0], sky->htrdr->grid_max_definition[0]); - nvoxs[1] = MMIN(raw_def[1], sky->htrdr->grid_max_definition[1]); - nvoxs[2] = MMIN(raw_def[2], sky->htrdr->grid_max_definition[2]); - - /* Define the octree AABB excepted for the Z dimension */ - low[0] = sky->htcp_desc.lower[0]; - low[1] = sky->htcp_desc.lower[1]; - low[2] = sky->htcp_desc.lower[2]; - upp[0] = low[0] + (double)raw_def[0] * sky->htcp_desc.vxsz_x; - upp[1] = low[1] + (double)raw_def[1] * sky->htcp_desc.vxsz_y; - - if(!sky->htcp_desc.irregular_z) { - /* Regular voxel size along the Z dimension: compute its upper boundary as - * the others dimensions */ - upp[2] = low[2] + (double)raw_def[2] * sky->htcp_desc.vxsz_z[0]; - } else { /* Irregular voxel size along Z */ - double len_z; - size_t nsplits; - size_t iz, iz2;; - - /* Find the min voxel size along Z and compute the length of a Z column */ - len_z = 0; - sky->lut_cell_sz = DBL_MAX; - FOR_EACH(iz, 0, sky->htcp_desc.spatial_definition[2]) { - len_z += sky->htcp_desc.vxsz_z[iz]; - sky->lut_cell_sz = MMIN(sky->lut_cell_sz, sky->htcp_desc.vxsz_z[iz]); - } - /* Allocate the svx2htcp LUT. This LUT is a regular table whose absolute - * size is greater or equal to a Z column in the htcp file. The size of its - * cells is the minimal voxel size in Z of the htcp file */ - nsplits = (size_t)ceil(len_z / sky->lut_cell_sz); - res = darray_split_resize(&sky->svx2htcp_z, nsplits); - if(res != RES_OK) { - htrdr_log_err(sky->htrdr, - "could not allocate the table mapping regular to irregular Z.\n"); - goto error; - } - /* Setup the svx2htcp LUT. Each LUT entry stores the index of the current Z - * voxel in the htcp file that overlaps the entry lower bound as well as the - * lower bound in Z of the next htcp voxel. */ - iz2 = 0; - upp[2] = low[2] + sky->htcp_desc.vxsz_z[iz2]; - FOR_EACH(iz, 0, nsplits) { - const double upp_z = (double)(iz + 1) * sky->lut_cell_sz + low[2]; - darray_split_data_get(&sky->svx2htcp_z)[iz].index = iz2; - darray_split_data_get(&sky->svx2htcp_z)[iz].height = upp[2]; - if(upp_z >= upp[2] && iz + 1 < nsplits) { - ASSERT(iz2 + 1 < sky->htcp_desc.spatial_definition[2]); - upp[2] += sky->htcp_desc.vxsz_z[++iz2]; - } - } - ASSERT(eq_eps(upp[2] - low[2], len_z, 1.e-6)); - } - - /* Setup the build context */ - vxsz[0] = sky->htcp_desc.upper[0] - sky->htcp_desc.lower[0]; - vxsz[1] = sky->htcp_desc.upper[1] - sky->htcp_desc.lower[1]; - vxsz[2] = sky->htcp_desc.upper[2] - sky->htcp_desc.lower[2]; - vxsz[0] = vxsz[0] / (double)nvoxs[0]; - vxsz[1] = vxsz[1] / (double)nvoxs[1]; - vxsz[2] = vxsz[2] / (double)nvoxs[2]; - - /* Create as many cloud data structure than considered SW spectral bands */ - nbands = htrdr_sky_get_sw_spectral_bands_count(sky); - sky->clouds = MEM_CALLOC(sky->htrdr->allocator, nbands, sizeof(*sky->clouds)); - if(!sky->clouds) { - htrdr_log_err(sky->htrdr, - "could not create the list of per SW band cloud data structure.\n"); - res = RES_MEM_ERR; - goto error; - } - - /* Compute how many octree are going to be built */ - FOR_EACH(i, 0, nbands) { - struct htgop_spectral_interval band; - const size_t iband = i + sky->sw_bands_range[0]; - size_t iquad; - - HTGOP(get_sw_spectral_interval(sky->htgop, iband, &band)); - - sky->clouds[i] = MEM_CALLOC(sky->htrdr->allocator, - band.quadrature_length, sizeof(*sky->clouds[i])); - if(!sky->clouds[i]) { - htrdr_log_err(sky->htrdr, - "could not create the list of per quadrature point cloud data " - "for the band %lu.\n", (unsigned long)iband); - res = RES_MEM_ERR; - goto error; - } - - FOR_EACH(iquad, 0, band.quadrature_length) { - struct spectral_data spectral_data; - spectral_data.iband = iband; - spectral_data.iquad = iquad; - res = darray_specdata_push_back(&specdata, &spectral_data); - if(res != RES_OK) { - htrdr_log_err(sky->htrdr, - "could not register the quadrature point %lu of the spectral band " - "%lu .\n", (unsigned long)iband, (unsigned long)iquad); - goto error; - } - } - } - - /* Make the generation of the octrees parallel or sequential whether the - * cache is disabled or not respectively: when the cache is enabled, we - * parallelize the generation of the cached grids, not the building of the - * octrees. */ - if(sky->htrdr->cache_grids) { - omp_set_num_threads(1); - } else { - omp_set_num_threads((int)sky->htrdr->nthreads); - if(sky->htrdr->mpi_rank == 0) { - fetch_mpi_progress(sky->htrdr, HTRDR_MPI_PROGRESS_BUILD_OCTREE); - print_mpi_progress(sky->htrdr, HTRDR_MPI_PROGRESS_BUILD_OCTREE); - } - } - #pragma omp parallel for schedule(dynamic, 1/*chunksize*/) - for(ispecdata=0; - (size_t)ispecdata<darray_specdata_size_get(&specdata); - ++ispecdata) { - struct svx_voxel_desc vox_desc = SVX_VOXEL_DESC_NULL; - struct build_tree_context ctx = BUILD_TREE_CONTEXT_NULL; - const size_t iband = darray_specdata_data_get(&specdata)[ispecdata].iband; - const size_t iquad = darray_specdata_data_get(&specdata)[ispecdata].iquad; - const size_t id = iband - sky->sw_bands_range[0]; - int32_t pcent; - size_t n; - res_T res_local = RES_OK; - - if(ATOMIC_GET(&res) != RES_OK) continue; - - /* Setup the build context */ - ctx.sky = sky; - ctx.vxsz[0] = vxsz[0]; - ctx.vxsz[1] = vxsz[1]; - ctx.vxsz[2] = vxsz[2]; - ctx.tau_threshold = optical_thickness_threshold; - ctx.iband = iband; - ctx.quadrature_range[0] = iquad; - ctx.quadrature_range[1] = iquad; - - /* Setup the voxel descriptor */ - vox_desc.get = cloud_vox_get; - vox_desc.get = cloud_vox_get; - vox_desc.merge = cloud_vox_merge; - vox_desc.challenge_merge = cloud_vox_challenge_merge; - vox_desc.context = &ctx; - vox_desc.size = sizeof(float) * NFLOATS_PER_VOXEL; - - /* Compute grid of voxels data */ - if(sky->htrdr->cache_grids) { - res_local = setup_cloud_grid(sky, nvoxs, iband, iquad, htcp_filename, - htgop_filename, htmie_filename, force_cache_update, &ctx.grid); - if(res_local != RES_OK) continue; - } - - /* Create the octree */ - res_local = svx_octree_create(sky->htrdr->svx, low, upp, nvoxs, &vox_desc, - &sky->clouds[id][iquad].octree); - if(ctx.grid) htrdr_grid_ref_put(ctx.grid); - if(res_local != RES_OK) { - htrdr_log_err(sky->htrdr, "could not create the octree of the cloud " - "properties for the band %lu.\n", (unsigned long)ctx.iband); - ATOMIC_SET(&res, res_local); - continue; - } - - /* Fetch the octree descriptor for future use */ - SVX(tree_get_desc - (sky->clouds[id][iquad].octree, &sky->clouds[id][iquad].octree_desc)); - - if(!sky->htrdr->cache_grids) { - /* Update the progress message */ - n = (size_t)ATOMIC_INCR(&nbuilt_octrees); - pcent = (int32_t)(n * 100 / darray_specdata_size_get(&specdata)); - - #pragma omp critical - if(pcent > sky->htrdr->mpi_progress_octree[0]) { - sky->htrdr->mpi_progress_octree[0] = pcent; - if(sky->htrdr->mpi_rank == 0) { - update_mpi_progress(sky->htrdr, HTRDR_MPI_PROGRESS_BUILD_OCTREE); - } else { /* Send the progress percentage to the master process */ - send_mpi_progress(sky->htrdr, HTRDR_MPI_PROGRESS_BUILD_OCTREE, pcent); - } - } - } - } - - if(!sky->htrdr->cache_grids && sky->htrdr->mpi_rank == 0) { - while(total_mpi_progress(sky->htrdr, HTRDR_MPI_PROGRESS_BUILD_OCTREE) != 100) { - update_mpi_progress(sky->htrdr, HTRDR_MPI_PROGRESS_BUILD_OCTREE); - sleep(1); - } - fprintf(stderr, "\n"); /* Add a new line after the progress statuses */ - } - -exit: - darray_specdata_release(&specdata); - return (res_T)res; -error: - clean_clouds(sky); - darray_split_clear(&sky->svx2htcp_z); - goto exit; -} - -static void -atmosphere_vox_get(const size_t xyz[3], void* dst, void* context) -{ - float* vox = dst; - struct build_tree_context* ctx = context; - struct htgop_level level; - size_t layer_range[2]; - size_t nlevels; - double vox_low, vox_upp; - double ka_min, ks_min, kext_min; - double ka_max, ks_max, kext_max; - size_t ilayer; - ASSERT(xyz && dst && context); - - /* Compute the boundaries of the SVX voxel */ - HTGOP(get_level(ctx->sky->htgop, 0, &level)); - vox_low = (double)xyz[2] * ctx->vxsz[2] + level.height; - HTGOP(get_levels_count(ctx->sky->htgop, &nlevels)); - HTGOP(get_level(ctx->sky->htgop, nlevels-1, &level)); - vox_upp = MMIN(vox_low + ctx->vxsz[2], level.height); /* Handle numerical issues */ - - /* Define the atmospheric layers overlapped by the SVX voxel */ - HTGOP(position_to_layer_id(ctx->sky->htgop, vox_low, &layer_range[0])); - HTGOP(position_to_layer_id(ctx->sky->htgop, vox_upp, &layer_range[1])); - - ka_min = ks_min = kext_min = DBL_MAX; - ka_max = ks_max = kext_max =-DBL_MAX; - - /* For each atmospheric layer that overlaps the SVX voxel ... */ - FOR_EACH(ilayer, layer_range[0], layer_range[1]+1) { - struct htgop_layer layer; - struct htgop_layer_sw_spectral_interval band; - size_t iquad; - - HTGOP(get_layer(ctx->sky->htgop, ilayer, &layer)); - - /* ... retrieve the considered spectral interval */ - HTGOP(layer_get_sw_spectral_interval(&layer, ctx->iband, &band)); - - /* ... and update the radiative properties bound with the per quadrature - * point nominal values */ - ASSERT(ctx->quadrature_range[0] <= ctx->quadrature_range[1]); - ASSERT(ctx->quadrature_range[1] < band.quadrature_length); - FOR_EACH(iquad, ctx->quadrature_range[0], ctx->quadrature_range[1]+1) { - ka_min = MMIN(ka_min, band.ka_nominal[iquad]); - ka_max = MMAX(ka_max, band.ka_nominal[iquad]); - ks_min = MMIN(ks_min, band.ks_nominal[iquad]); - ks_max = MMAX(ks_max, band.ks_nominal[iquad]); - kext_min = MMIN(kext_min, band.ka_nominal[iquad]+band.ks_nominal[iquad]); - kext_max = MMAX(kext_max, band.ka_nominal[iquad]+band.ks_nominal[iquad]); - } - } - - /* Ensure that the single precision bounds include their double precision - * version. */ - if(ka_min != (float)ka_min) ka_min = nextafterf((float)ka_min,-FLT_MAX); - if(ka_max != (float)ka_max) ka_max = nextafterf((float)ka_max, FLT_MAX); - if(ks_min != (float)ks_min) ks_min = nextafterf((float)ks_min,-FLT_MAX); - if(ks_max != (float)ks_max) ks_max = nextafterf((float)ks_max, FLT_MAX); - if(kext_min != (float)kext_min) kext_min = nextafterf((float)kext_min,-FLT_MAX); - if(kext_max != (float)kext_max) kext_max = nextafterf((float)kext_max, FLT_MAX); - - /* Setup gas optical properties of the voxel */ - voxel_set(vox, HTRDR_GAS__, HTRDR_Ka, HTRDR_SVX_MIN, (float)ka_min); - voxel_set(vox, HTRDR_GAS__, HTRDR_Ka, HTRDR_SVX_MAX, (float)ka_max); - voxel_set(vox, HTRDR_GAS__, HTRDR_Ks, HTRDR_SVX_MIN, (float)ks_min); - voxel_set(vox, HTRDR_GAS__, HTRDR_Ks, HTRDR_SVX_MAX, (float)ks_max); - voxel_set(vox, HTRDR_GAS__, HTRDR_Kext, HTRDR_SVX_MIN, (float)kext_min); - voxel_set(vox, HTRDR_GAS__, HTRDR_Kext, HTRDR_SVX_MAX, (float)kext_max); -} - -static void -atmosphere_vox_merge - (void* dst, const void* voxels[], const size_t nvoxs, void* context) -{ - ASSERT(dst && voxels && nvoxs); - (void)context; - vox_merge_component(dst, HTRDR_GAS__, (const float**)voxels, nvoxs); -} - -static int -atmosphere_vox_challenge_merge - (const struct svx_voxel voxels[], const size_t nvoxs, void* ctx) -{ - ASSERT(voxels); - return vox_challenge_merge_component(HTRDR_GAS__, voxels, nvoxs, ctx); -} - -static res_T -setup_atmosphere - (struct htrdr_sky* sky, const double optical_thickness_threshold) -{ - struct build_tree_context ctx = BUILD_TREE_CONTEXT_NULL; - struct htgop_level lvl_low, lvl_upp; - struct svx_voxel_desc vox_desc = SVX_VOXEL_DESC_NULL; - double low, upp; - size_t nlayers, nlevels; - size_t definition; - size_t nbands; - size_t i; - res_T res = RES_OK; - ASSERT(sky && optical_thickness_threshold >= 0); - - HTGOP(get_layers_count(sky->htgop, &nlayers)); - HTGOP(get_levels_count(sky->htgop, &nlevels)); - HTGOP(get_level(sky->htgop, 0, &lvl_low)); - HTGOP(get_level(sky->htgop, nlevels-1, &lvl_upp)); - low = lvl_low.height; - upp = lvl_upp.height; - definition = nlayers; - - /* Setup the build context */ - ctx.sky = sky; - ctx.tau_threshold = optical_thickness_threshold; - ctx.vxsz[0] = INF; - ctx.vxsz[1] = INF; - ctx.vxsz[2] = (upp-low)/(double)definition; - - /* Setup the voxel descriptor for the atmosphere. Note that in contrats with - * the clouds, the voxel contains only NFLOATS_PER_COMPONENT floats and not - * NFLOATS_PER_VOXEL. Indeed, the atmosphere has only 1 component: the gas. - * Anyway, we still rely on the memory layout of the cloud voxels excepted - * that we assume that the optical properties of the particles are never - * fetched. We thus have to ensure that the gas properties are arranged - * before the particles, i.e. HTRDR_GAS__ == 0 */ - vox_desc.get = atmosphere_vox_get; - vox_desc.merge = atmosphere_vox_merge; - vox_desc.challenge_merge = atmosphere_vox_challenge_merge; - vox_desc.context = &ctx; - vox_desc.size = sizeof(float) * NFLOATS_PER_COMPONENT; - { STATIC_ASSERT(HTRDR_GAS__ == 0, Unexpected_enum_value); } - - /* Create as many atmospheric data structure than considered SW spectral - * bands */ - nbands = htrdr_sky_get_sw_spectral_bands_count(sky); - sky->atmosphere = MEM_CALLOC - (sky->htrdr->allocator, nbands, sizeof(*sky->atmosphere)); - if(!sky->atmosphere) { - htrdr_log_err(sky->htrdr, - "could not create the list of per SW band atmospheric data structure.\n"); - res = RES_MEM_ERR; - goto error; - } - - FOR_EACH(i, 0, nbands) { - size_t iquad; - struct htgop_spectral_interval band; - ctx.iband = i + sky->sw_bands_range[0]; - - HTGOP(get_sw_spectral_interval(sky->htgop, ctx.iband, &band)); - - sky->atmosphere[i] = MEM_CALLOC(sky->htrdr->allocator, - band.quadrature_length, sizeof(*sky->atmosphere[i])); - if(!sky->atmosphere[i]) { - htrdr_log_err(sky->htrdr, - "could not create the list of per quadrature point atmospheric data " - "for the band %lu.\n", (unsigned long)ctx.iband); - res = RES_MEM_ERR; - goto error; - } - - /* Build an atmospheric binary tree for each quadrature point of the - * considered spectral band */ - FOR_EACH(iquad, 0, band.quadrature_length) { - ctx.quadrature_range[0] = iquad; - ctx.quadrature_range[1] = iquad; - - /* Create the atmospheric binary tree */ - res = svx_bintree_create(sky->htrdr->svx, low, upp, definition, SVX_AXIS_Z, - &vox_desc, &sky->atmosphere[i][iquad].bitree); - if(res != RES_OK) { - htrdr_log_err(sky->htrdr, "could not create the binary tree of the " - "atmospheric properties for the band %lu.\n", (unsigned long)ctx.iband); - goto error; - } - - /* Fetch the binary tree descriptor for future use */ - SVX(tree_get_desc(sky->atmosphere[i][iquad].bitree, - &sky->atmosphere[i][iquad].bitree_desc)); - } - } - -exit: - return res; -error: - clean_atmosphere(sky); - goto exit; -} - -static res_T -setup_sw_bands_properties(struct htrdr_sky* sky) -{ - res_T res = RES_OK; - size_t nbands; - size_t i; - ASSERT(sky); - - nbands = htrdr_sky_get_sw_spectral_bands_count(sky); - ASSERT(nbands); - sky->sw_bands = MEM_CALLOC - (sky->htrdr->allocator, nbands, sizeof(*sky->sw_bands)); - if(!sky->sw_bands) { - htrdr_log_err(sky->htrdr, - "could not allocate the list of SW band properties.\n"); - res = RES_MEM_ERR; - goto error; - } - - FOR_EACH(i, 0, nbands) { - struct htgop_spectral_interval band; - double band_wlens[2]; - - HTGOP(get_sw_spectral_interval - (sky->htgop, i+ sky->sw_bands_range[0], &band)); - band_wlens[0] = wavenumber_to_wavelength(band.wave_numbers[1]); - band_wlens[1] = wavenumber_to_wavelength(band.wave_numbers[0]); - ASSERT(band_wlens[0] < band_wlens[1]); - - sky->sw_bands[i].Ca_avg = htmie_compute_xsection_absorption_average - (sky->htmie, band_wlens, HTMIE_FILTER_LINEAR); - sky->sw_bands[i].Cs_avg = htmie_compute_xsection_scattering_average - (sky->htmie, band_wlens, HTMIE_FILTER_LINEAR); - sky->sw_bands[i].g_avg = htmie_compute_asymmetry_parameter_average - (sky->htmie, band_wlens, HTMIE_FILTER_LINEAR); - ASSERT(sky->sw_bands[i].Ca_avg > 0); - ASSERT(sky->sw_bands[i].Cs_avg > 0); - ASSERT(sky->sw_bands[i].g_avg > 0); - } - -exit: - return res; -error: - if(sky->sw_bands) { - MEM_RM(sky->htrdr->allocator, sky->sw_bands); - sky->sw_bands = NULL; - } - goto exit; -} - -static void -sample_sw_spectral_data - (struct htgop* htgop, - struct ssp_rng* rng, - res_T (*sample_sw_band)(const struct htgop*, const double, size_t*), - size_t* ispectral_band, - size_t* iquadrature_pt) -{ - struct htgop_spectral_interval specint; - double r1, r2; - res_T res = RES_OK; - ASSERT(htgop && rng && sample_sw_band && ispectral_band && iquadrature_pt); - ASSERT(ispectral_band && iquadrature_pt); - (void)res; - r1 = ssp_rng_canonical(rng); - r2 = ssp_rng_canonical(rng); - res = sample_sw_band(htgop, r1, ispectral_band); - ASSERT(res == RES_OK); - HTGOP(get_sw_spectral_interval(htgop, *ispectral_band, &specint)); - HTGOP(spectral_interval_sample_quadrature(&specint, r2, iquadrature_pt)); -} - -static void -release_sky(ref_T* ref) -{ - struct htrdr_sky* sky; - ASSERT(ref); - sky = CONTAINER_OF(ref, struct htrdr_sky, ref); - clean_clouds(sky); - clean_atmosphere(sky); - if(sky->sun) htrdr_sun_ref_put(sky->sun); - if(sky->htcp) HTCP(ref_put(sky->htcp)); - if(sky->htgop) HTGOP(ref_put(sky->htgop)); - if(sky->htmie) HTMIE(ref_put(sky->htmie)); - if(sky->sw_bands) MEM_RM(sky->htrdr->allocator, sky->sw_bands); - darray_split_release(&sky->svx2htcp_z); - MEM_RM(sky->htrdr->allocator, sky); -} - -/******************************************************************************* - * Local functions - ******************************************************************************/ -res_T -htrdr_sky_create - (struct htrdr* htrdr, - struct htrdr_sun* sun, - const char* htcp_filename, - const char* htgop_filename, - const char* htmie_filename, - const double optical_thickness_threshold, - const int repeat_clouds, - struct htrdr_sky** out_sky) -{ - struct time t0, t1; - struct htrdr_sky* sky = NULL; - char buf[128]; - int htcp_upd = 1; - int htmie_upd = 1; - int htgop_upd = 1; - int force_upd = 1; - res_T res = RES_OK; - ASSERT(htrdr && sun && htgop_filename && out_sky); - ASSERT(optical_thickness_threshold >= 0); - - sky = MEM_CALLOC(htrdr->allocator, 1, sizeof(*sky)); - if(!sky) { - htrdr_log_err(htrdr, "could not allocate the sky data structure.\n"); - res = RES_MEM_ERR; - goto error; - } - ref_init(&sky->ref); - htrdr_sun_ref_get(sun); - sky->htrdr = htrdr; - sky->sun = sun; - sky->repeat_clouds = repeat_clouds; - sky->is_cloudy = htcp_filename != NULL; - darray_split_init(htrdr->allocator, &sky->svx2htcp_z); - sky->sw_bands_range[0] = 1; - sky->sw_bands_range[1] = 0; - - /* Load the gas optical properties */ - res = htgop_create - (&htrdr->logger, htrdr->allocator, htrdr->verbose, &sky->htgop); - if(res != RES_OK) { - htrdr_log_err(htrdr, - "could not create the loader of the gas optical properties.\n"); - goto error; - } - res = htgop_load(sky->htgop, htgop_filename); - if(res != RES_OK) { - htrdr_log_err(htrdr, "error loading the gas optical properties -- `%s'.\n", - htgop_filename); - goto error; - } - - /* Fetch short wave bands range */ - res = htgop_get_sw_spectral_intervals_CIE_XYZ(sky->htgop, sky->sw_bands_range); - if(res != RES_OK) goto error; - - /* Setup the atmopshere */ - time_current(&t0); - res = setup_atmosphere(sky, optical_thickness_threshold); - if(res != RES_OK) goto error; - time_sub(&t0, time_current(&t1), &t0); - time_dump(&t0, TIME_ALL, NULL, buf, sizeof(buf)); - htrdr_log(htrdr, "Setup atmosphere in %s\n", buf); - - /* Nothing more to do */ - if(!sky->is_cloudy) goto exit; - - /* Load MIE data */ - res = htmie_create(&htrdr->logger, htrdr->allocator, htrdr->verbose, &sky->htmie); - if(res != RES_OK) { - htrdr_log_err(htrdr, "could not create the loader of Mie's data.\n"); - goto error; - } - res = htmie_load(sky->htmie, htmie_filename); - if(res != RES_OK) { - htrdr_log_err(htrdr, "error loading the Mie's data -- `%s'.\n", - htmie_filename); - goto error; - } - - res = setup_sw_bands_properties(sky); - if(res != RES_OK) goto error; - - /* Load clouds properties */ - res = htcp_create(&htrdr->logger, htrdr->allocator, htrdr->verbose, &sky->htcp); - if(res != RES_OK) { - htrdr_log_err(htrdr, "could not create the loader of cloud properties.\n"); - goto error; - } - res = htcp_load(sky->htcp, htcp_filename); - if(res != RES_OK) { - htrdr_log_err(htrdr, "error loading the cloud properties -- `%s'.\n", - htcp_filename); - goto error; - } - - /* Define if the cached grid data must be updated */ - res = is_file_updated(sky->htrdr, htcp_filename, &htcp_upd); - if(res != RES_OK) goto error; - res = is_file_updated(sky->htrdr, htmie_filename, &htmie_upd); - if(res != RES_OK) goto error; - res = is_file_updated(sky->htrdr, htgop_filename, &htgop_upd); - if(res != RES_OK) goto error; - force_upd = htcp_upd || htmie_upd || htgop_upd; - - time_current(&t0); - res = setup_clouds(sky, htcp_filename, htgop_filename, htmie_filename, - optical_thickness_threshold, force_upd); - if(res != RES_OK) goto error; - time_sub(&t0, time_current(&t1), &t0); - time_dump(&t0, TIME_ALL, NULL, buf, sizeof(buf)); - htrdr_log(htrdr, "Setup clouds in %s\n", buf); - - /* Update the file stamps */ - if(htcp_upd) { - res = update_file_stamp(sky->htrdr, htcp_filename); - if(res != RES_OK) goto error; - } - if(htmie_upd) { - res = update_file_stamp(sky->htrdr, htmie_filename); - if(res != RES_OK) goto error; - } - if(htgop_upd) { - res = update_file_stamp(sky->htrdr, htgop_filename); - if(res != RES_OK) goto error; - } - - log_svx_memory_usage(htrdr); - -exit: - *out_sky = sky; - return res; -error: - if(sky) { - htrdr_sky_ref_put(sky); - sky = NULL; - } - goto exit; -} - -void -htrdr_sky_ref_get(struct htrdr_sky* sky) -{ - ASSERT(sky); - ref_get(&sky->ref); -} - -void -htrdr_sky_ref_put(struct htrdr_sky* sky) -{ - ASSERT(sky); - ref_put(&sky->ref, release_sky); -} - -double -htrdr_sky_fetch_particle_phase_function_asymmetry_parameter - (const struct htrdr_sky* sky, - const size_t ispectral_band, - const size_t iquad) -{ - size_t i; - ASSERT(sky); - ASSERT(ispectral_band >= sky->sw_bands_range[0]); - ASSERT(ispectral_band <= sky->sw_bands_range[1]); - (void)iquad; - if(!sky->is_cloudy) { - return 0; - } else { - i = ispectral_band - sky->sw_bands_range[0]; - return sky->sw_bands[i].g_avg; - } -} - -double -htrdr_sky_fetch_raw_property - (const struct htrdr_sky* sky, - const enum htrdr_sky_property prop, - const int components_mask, /* Combination of htrdr_sky_component_flag */ - const size_t iband, /* Index of the spectral band */ - const size_t iquad, /* Index of the quadrature point in the spectral band */ - const double pos[3], - const double k_min, - const double k_max) -{ - size_t ivox[3]; - size_t i; - const struct svx_tree_desc* cloud_desc; - const struct svx_tree_desc* atmosphere_desc; - int comp_mask = components_mask; - int in_clouds; /* Defines if `pos' lies in the clouds */ - int in_atmosphere; /* Defines if `pos' lies in the atmosphere */ - double pos_cs[3]; /* Position in cloud space */ - double k_particle = 0; - double k_gas = 0; - double k = 0; - ASSERT(sky && pos); - ASSERT(iband >= sky->sw_bands_range[0]); - ASSERT(iband <= sky->sw_bands_range[1]); - ASSERT(comp_mask & HTRDR_ALL_COMPONENTS); - - i = iband - sky->sw_bands_range[0]; - cloud_desc = sky->is_cloudy ? &sky->clouds[i][iquad].octree_desc : NULL; - atmosphere_desc = &sky->atmosphere[i][iquad].bitree_desc; - ASSERT(atmosphere_desc->frame[0] == SVX_AXIS_Z); - - /* Is the position inside the clouds? */ - if(!sky->is_cloudy) { - in_clouds = 0; - } else if(sky->repeat_clouds) { - in_clouds = - pos[2] >= cloud_desc->lower[2] - && pos[2] <= cloud_desc->upper[2]; - } else { - in_clouds = - pos[0] >= cloud_desc->lower[0] - && pos[1] >= cloud_desc->lower[1] - && pos[2] >= cloud_desc->lower[2] - && pos[0] <= cloud_desc->upper[0] - && pos[1] <= cloud_desc->upper[1] - && pos[2] <= cloud_desc->upper[2]; - } - - /* Is the position inside the atmosphere? */ - ASSERT(atmosphere_desc->frame[0] == SVX_AXIS_Z); - in_atmosphere = - pos[2] >= atmosphere_desc->lower[2] - && pos[2] <= atmosphere_desc->upper[2]; - - if(!in_clouds) { - /* Make invalid the voxel index */ - ivox[0] = SIZE_MAX; - ivox[1] = SIZE_MAX; - ivox[2] = SIZE_MAX; - /* Not in clouds => No particle */ - comp_mask &= ~HTRDR_PARTICLES; - /* Not in atmopshere => No gas */ - if(!in_atmosphere) comp_mask &= ~HTRDR_GAS; - } else { - world_to_cloud(sky, pos, pos_cs); - - /* Compute the index of the voxel to fetch */ - ivox[0] = (size_t)((pos_cs[0] - cloud_desc->lower[0])/sky->htcp_desc.vxsz_x); - ivox[1] = (size_t)((pos_cs[1] - cloud_desc->lower[1])/sky->htcp_desc.vxsz_y); - if(!sky->htcp_desc.irregular_z) { - /* The voxels along the Z dimension have the same size */ - ivox[2] = (size_t)((pos_cs[2] - cloud_desc->lower[2])/sky->htcp_desc.vxsz_z[0]); - } else { - /* Irregular voxel size along the Z dimension. Compute the index of the Z - * position in the svx2htcp_z Look Up Table and use the LUT to define the - * voxel index into the HTCP descripptor */ - const struct split* splits = darray_split_cdata_get(&sky->svx2htcp_z); - const size_t ilut = (size_t) - ((pos_cs[2] - cloud_desc->lower[2]) / sky->lut_cell_sz); - ivox[2] = splits[ilut].index + (pos_cs[2] > splits[ilut].height); - } - } - - if(comp_mask & HTRDR_PARTICLES) { - double rho_da = 0; /* Dry air density */ - double rct = 0; /* #droplets in kg of water per kg of dry air */ - double ql = 0; /* Droplet density In kg.m^-3 */ - double Ca = 0; /* Massic absorption cross section in m^2.kg^-1 */ - double Cs = 0; /* Massic scattering cross section in m^2.kg^-1 */ - ASSERT(in_clouds); - - /* Compute he dry air density */ - rho_da = cloud_dry_air_density(&sky->htcp_desc, ivox); - - /* Compute the droplet density */ - rct = htcp_desc_RCT_at(&sky->htcp_desc, ivox[0], ivox[1], ivox[2], 0); - ql = rho_da * rct; - - /* Use the average cross section of the current spectral band */ - if(prop == HTRDR_Ka || prop == HTRDR_Kext) Ca = sky->sw_bands[i].Ca_avg; - if(prop == HTRDR_Ks || prop == HTRDR_Kext) Cs = sky->sw_bands[i].Cs_avg; - - k_particle = ql*(Ca + Cs); - } - - if(comp_mask & HTRDR_GAS) { - struct htgop_layer layer; - struct htgop_layer_sw_spectral_interval band; - size_t ilayer = 0; - ASSERT(in_atmosphere); - - /* Retrieve the quadrature point into the spectral band of the layer into - * which `pos' lies */ - HTGOP(position_to_layer_id(sky->htgop, pos[2], &ilayer)); - HTGOP(get_layer(sky->htgop, ilayer, &layer)); - HTGOP(layer_get_sw_spectral_interval(&layer, iband, &band)); - - if(!in_clouds) { - /* Pos is outside the clouds. Directly fetch the nominal optical - * properties */ - ASSERT(iquad < band.quadrature_length); - switch(prop) { - case HTRDR_Ka: k_gas = band.ka_nominal[iquad]; break; - case HTRDR_Ks: k_gas = band.ks_nominal[iquad]; break; - case HTRDR_Kext: - k_gas = band.ka_nominal[iquad] + band.ks_nominal[iquad]; - break; - default: FATAL("Unreachable code.\n"); break; - } - } else { - /* Pos is inside the clouds. Compute the water vapor molar fraction at - * the current voxel */ - const double x_h2o = cloud_water_vapor_molar_fraction(&sky->htcp_desc, ivox); - struct htgop_layer_sw_spectral_interval_tab tab; - - /* Retrieve the tabulated data for the quadrature point */ - HTGOP(layer_sw_spectral_interval_get_tab(&band, iquad, &tab)); - - /* Fetch the optical properties wrt x_h2o */ - switch(prop) { - case HTRDR_Ka: - HTGOP(layer_sw_spectral_interval_tab_fetch_ka(&tab, x_h2o, &k_gas)); - break; - case HTRDR_Ks: - HTGOP(layer_sw_spectral_interval_tab_fetch_ks(&tab, x_h2o, &k_gas)); - break; - case HTRDR_Kext: - HTGOP(layer_sw_spectral_interval_tab_fetch_kext(&tab, x_h2o, &k_gas)); - break; - default: FATAL("Unreachable code.\n"); break; - } - } - } - - k = k_particle + k_gas; - ASSERT(k >= k_min && k <= k_max); - (void)k_min, (void)k_max; - return k; -} - -res_T -htrdr_sky_dump_clouds_vtk - (const struct htrdr_sky* sky, - const size_t iband, /* Index of the spectral band */ - const size_t iquad, /* Index of the quadrature point */ - FILE* stream) -{ - const struct cloud* cloud; - struct htgop_spectral_interval specint; - struct octree_data data; - const double* leaf_data; - size_t nvertices; - size_t ncells; - size_t i; - ASSERT(sky && stream); - ASSERT(iband >= sky->sw_bands_range[0]); - ASSERT(iband <= sky->sw_bands_range[1]); - - if(!sky->is_cloudy) { - htrdr_log_warn(sky->htrdr, "%s: the sky has no cloud.\n", FUNC_NAME); - return RES_OK; - } - - i = iband - sky->sw_bands_range[0]; - - octree_data_init(sky, iband, iquad, &data); - cloud = &sky->clouds[i][iquad]; - - ASSERT(cloud->octree_desc.type == SVX_OCTREE); - - /* Register leaf data */ - SVX(tree_for_each_leaf(cloud->octree, register_leaf, &data)); - nvertices = darray_double_size_get(&data.vertices) / 3/*#coords per vertex*/; - ncells = darray_size_t_size_get(&data.cells)/8/*#ids per cell*/; - ASSERT(ncells == cloud->octree_desc.nleaves); - - /* Fetch the spectral interval descriptor */ - HTGOP(get_sw_spectral_interval(sky->htgop, iband, &specint)); - - /* Write headers */ - fprintf(stream, "# vtk DataFile Version 2.0\n"); - fprintf(stream, "Clouds optical properties in [%g, %g] nanometers\n", - wavenumber_to_wavelength(specint.wave_numbers[1]), - wavenumber_to_wavelength(specint.wave_numbers[0])); - fprintf(stream, "ASCII\n"); - fprintf(stream, "DATASET UNSTRUCTURED_GRID\n"); - - /* Write vertex coordinates */ - fprintf(stream, "POINTS %lu float\n", (unsigned long)nvertices); - FOR_EACH(i, 0, nvertices) { - fprintf(stream, "%g %g %g\n", - SPLIT3(darray_double_cdata_get(&data.vertices) + i*3)); - } - - /* Write the cells */ - fprintf(stream, "CELLS %lu %lu\n", - (unsigned long)ncells, - (unsigned long)(ncells*(8/*#verts per cell*/ + 1/*1st field of a cell*/))); - FOR_EACH(i, 0, ncells) { - fprintf(stream, "8 %lu %lu %lu %lu %lu %lu %lu %lu\n", - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+0], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+1], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+2], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+3], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+4], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+5], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+6], - (unsigned long)darray_size_t_cdata_get(&data.cells)[i*8+7]); - } - - /* Write the cell type */ - fprintf(stream, "CELL_TYPES %lu\n", (unsigned long)ncells); - FOR_EACH(i, 0, ncells) fprintf(stream, "11\n"); - - /* Write the cell data */ - leaf_data = darray_double_cdata_get(&data.data); - fprintf(stream, "CELL_DATA %lu\n", (unsigned long)ncells); - fprintf(stream, "SCALARS Kext double 2\n"); - fprintf(stream, "LOOKUP_TABLE default\n"); - FOR_EACH(i, 0, ncells) { - fprintf(stream, "%g %g\n", leaf_data[i*2+0], leaf_data[i*2+1]); - } - octree_data_release(&data); - return RES_OK; -} - -double -htrdr_sky_fetch_svx_property - (const struct htrdr_sky* sky, - const enum htrdr_sky_property prop, - const enum htrdr_svx_op op, - const int components_mask, /* Combination of htrdr_sky_component_flag */ - const size_t iband, /* Index of the spectral band */ - const size_t iquad, /* Index of the quadrature point in the spectral band */ - const double pos[3]) -{ - struct svx_voxel voxel = SVX_VOXEL_NULL; - struct atmosphere* atmosphere; - struct cloud* cloud; - size_t i; - int in_clouds; /* Defines if `pos' lies in the clouds */ - int in_atmosphere; /* Defines if `pos' lies in the atmosphere */ - int comp_mask = components_mask; - ASSERT(sky && pos); - ASSERT(comp_mask & HTRDR_ALL_COMPONENTS); - ASSERT(iband >= sky->sw_bands_range[0]); - ASSERT(iband <= sky->sw_bands_range[1]); - - i = iband - sky->sw_bands_range[0]; - cloud = sky->is_cloudy ? &sky->clouds[i][iquad] : NULL; - atmosphere = &sky->atmosphere[i][iquad]; - - /* Is the position inside the clouds? */ - if(sky->is_cloudy) { - in_clouds = 0; - } else if(sky->repeat_clouds) { - in_clouds = - pos[2] >= cloud->octree_desc.lower[2] - && pos[2] <= cloud->octree_desc.upper[2]; - } else { - in_clouds = - pos[0] >= cloud->octree_desc.lower[0] - && pos[1] >= cloud->octree_desc.lower[1] - && pos[2] >= cloud->octree_desc.lower[2] - && pos[0] <= cloud->octree_desc.upper[0] - && pos[1] <= cloud->octree_desc.upper[1] - && pos[2] <= cloud->octree_desc.upper[2]; - } - - ASSERT(atmosphere->bitree_desc.frame[0] == SVX_AXIS_Z); - in_atmosphere = - pos[2] >= atmosphere->bitree_desc.lower[2] - && pos[2] <= atmosphere->bitree_desc.upper[2]; - - if(!in_clouds) { /* Not in clouds => No particle */ - comp_mask &= ~HTRDR_PARTICLES; - } - if(!in_atmosphere) { /* Not in atmosphere => No gas */ - comp_mask &= ~HTRDR_GAS; - } - - if(!in_clouds && !in_atmosphere) /* In vacuum */ - return 0; - - if(!in_clouds) { - ASSERT(in_atmosphere); - SVX(tree_at(atmosphere->bitree, pos, NULL, NULL, &voxel)); - } else { - double pos_cs[3]; - world_to_cloud(sky, pos, pos_cs); - SVX(tree_at(cloud->octree, pos_cs, NULL, NULL, &voxel)); - } - - return htrdr_sky_fetch_svx_voxel_property - (sky, prop, op, comp_mask, iband, iquad, &voxel); -} - -double -htrdr_sky_fetch_svx_voxel_property - (const struct htrdr_sky* sky, - const enum htrdr_sky_property prop, - const enum htrdr_svx_op op, - const int components_mask, - const size_t ispectral_band, /* Index of the spectral band */ - const size_t iquad, /* Index of the quadrature point in the spectral band */ - const struct svx_voxel* voxel) -{ - double gas = 0; - double par = 0; - int comp_mask = components_mask; - ASSERT(sky && voxel); - ASSERT((unsigned)prop < HTRDR_PROPERTIES_COUNT__); - ASSERT((unsigned)op < HTRDR_SVX_OPS_COUNT__); - (void)sky, (void)ispectral_band, (void)iquad; - - /* Check if the voxel has infinite bounds/degenerated. In such case it is - * atmospheric voxel with only gas properties */ - if(IS_INF(voxel->upper[0]) || voxel->lower[0] > voxel->upper[0]) { - ASSERT(IS_INF(voxel->upper[1]) || voxel->lower[1] > voxel->upper[1]); - comp_mask &= ~HTRDR_PARTICLES; - } - - if(comp_mask & HTRDR_PARTICLES) { - par = voxel_get(voxel->data, HTRDR_PARTICLES__, prop, op); - } - if(comp_mask & HTRDR_GAS) { - gas = voxel_get(voxel->data, HTRDR_GAS__, prop, op); - } - /* Interval arithmetic to ensure that the returned Min/Max includes the - * Min/Max of the "gas + particles mixture" */ - return par + gas; -} - -size_t -htrdr_sky_get_sw_spectral_bands_count(const struct htrdr_sky* sky) -{ - ASSERT(sky && sky->sw_bands_range[0] <= sky->sw_bands_range[1]); - return sky->sw_bands_range[1] - sky->sw_bands_range[0] + 1; -} - -size_t -htrdr_sky_get_sw_spectral_band_id - (const struct htrdr_sky* sky, const size_t i) -{ - ASSERT(sky && i < htrdr_sky_get_sw_spectral_bands_count(sky)); - return sky->sw_bands_range[0] + i; -} - -size_t -htrdr_sky_get_sw_spectral_band_quadrature_length - (const struct htrdr_sky* sky, const size_t iband) -{ - struct htgop_spectral_interval band; - ASSERT(sky); - ASSERT(iband >= sky->sw_bands_range[0]); - ASSERT(iband <= sky->sw_bands_range[1]); - HTGOP(get_sw_spectral_interval(sky->htgop, iband, &band)); - return band.quadrature_length; -} - -res_T -htrdr_sky_get_sw_spectral_band_bounds - (const struct htrdr_sky* sky, - const size_t iband, - double wavelengths[2]) -{ - struct htgop_spectral_interval specint; - res_T res = RES_OK; - ASSERT(sky && wavelengths); - - res = htgop_get_sw_spectral_interval(sky->htgop, iband, &specint); - if(res != RES_OK) return res; - - wavelengths[0] = wavenumber_to_wavelength(specint.wave_numbers[1]); - wavelengths[1] = wavenumber_to_wavelength(specint.wave_numbers[0]); - ASSERT(wavelengths[0] < wavelengths[1]); - return RES_OK; -} - -void -htrdr_sky_sample_sw_spectral_data_CIE_1931_X - (const struct htrdr_sky* sky, - struct ssp_rng* rng, - size_t* ispectral_band, - size_t* iquadrature_pt) -{ - sample_sw_spectral_data - (sky->htgop, rng, htgop_sample_sw_spectral_interval_CIE_1931_X, - ispectral_band, iquadrature_pt); -} - -void -htrdr_sky_sample_sw_spectral_data_CIE_1931_Y - (const struct htrdr_sky* sky, - struct ssp_rng* rng, - size_t* ispectral_band, - size_t* iquadrature_pt) -{ - sample_sw_spectral_data - (sky->htgop, rng, htgop_sample_sw_spectral_interval_CIE_1931_Y, - ispectral_band, iquadrature_pt); -} - -void -htrdr_sky_sample_sw_spectral_data_CIE_1931_Z - (const struct htrdr_sky* sky, - struct ssp_rng* rng, - size_t* ispectral_band, - size_t* iquadrature_pt) -{ - sample_sw_spectral_data - (sky->htgop, rng, htgop_sample_sw_spectral_interval_CIE_1931_Z, - ispectral_band, iquadrature_pt); -} - -res_T -htrdr_sky_trace_ray - (struct htrdr_sky* sky, - const double org[3], - const double dir[3], /* Must be normalized */ - const double range[2], - const svx_hit_challenge_T challenge, /* NULL <=> Traversed up to the leaves */ - const svx_hit_filter_T filter, /* NULL <=> Stop RT at the 1st hit voxel */ - void* context, /* Data sent to the filter functor */ - const size_t ispectral_band, - const size_t iquadrature_pt, - struct svx_hit* hit) -{ - double cloud_range[2]; - struct svx_tree* clouds; - struct svx_tree* atmosphere; - size_t i; - res_T res = RES_OK; - ASSERT(sky); - ASSERT(ispectral_band >= sky->sw_bands_range[0]); - ASSERT(ispectral_band <= sky->sw_bands_range[1]); - (void)iquadrature_pt; - - /* Fetch the clouds/atmosphere corresponding to the submitted spectral data */ - i = ispectral_band - sky->sw_bands_range[0]; - clouds = sky->is_cloudy ? sky->clouds[i][iquadrature_pt].octree : NULL; - atmosphere = sky->atmosphere[i][iquadrature_pt].bitree; - - cloud_range[0] = INF; - cloud_range[1] =-INF; - - if(sky->is_cloudy) { - /* Compute the ray range, intersecting the clouds AABB */ - if(sky->repeat_clouds) { - ray_intersect_aabb(org, dir, range, sky->htcp_desc.lower, - sky->htcp_desc.upper, AXIS_Z, cloud_range); - } else { - ray_intersect_aabb(org, dir, range, sky->htcp_desc.lower, - sky->htcp_desc.upper, AXIS_X|AXIS_Y|AXIS_Z, cloud_range); - } - } - - /* Reset the hit */ - *hit = SVX_HIT_NULL; - - if(cloud_range[0] >= cloud_range[1]) { /* The ray does not traverse the clouds */ - res = svx_tree_trace_ray(atmosphere, org, dir, range, challenge, filter, - context, hit); - if(res != RES_OK) { - htrdr_log_err(sky->htrdr, - "%s: could not trace the ray in the atmosphere.\n", - FUNC_NAME); - goto error; - } - } else { /* The ray may traverse the clouds */ - double range_adjusted[2]; - - if(cloud_range[0] > range[0]) { /* The ray begins in the atmosphere */ - /* Trace a ray in the atmosphere from range[0] to cloud_range[0] */ - range_adjusted[0] = range[0]; - range_adjusted[1] = nextafter(cloud_range[0], -DBL_MAX); - res = svx_tree_trace_ray(atmosphere, org, dir, range_adjusted, challenge, - filter, context, hit); - if(res != RES_OK) { - htrdr_log_err(sky->htrdr, - "%s: could not to trace the part that begins in the atmosphere.\n", - FUNC_NAME); - goto error; - } - if(!SVX_HIT_NONE(hit)) goto exit; /* Collision */ - } - - /* Pursue ray traversal into the clouds */ - if(!sky->repeat_clouds) { - res = svx_tree_trace_ray(clouds, org, dir, cloud_range, challenge, filter, - context, hit); - if(res != RES_OK) { - htrdr_log_err(sky->htrdr, - "%s: could not trace the ray part that intersects the clouds.\n", - FUNC_NAME); - goto error; - } - if(!SVX_HIT_NONE(hit)) goto exit; /* Collision */ - - /* Clouds are infinitely repeated along the X and Y axis */ - } else { - struct trace_cloud_context slab_ctx = TRACE_CLOUD_CONTEXT_NULL; - - slab_ctx.clouds = clouds; - slab_ctx.challenge = challenge; - slab_ctx.filter = filter; - slab_ctx.context = context; - slab_ctx.hit = hit; - - res = htrdr_slab_trace_ray(sky->htrdr, org, dir, cloud_range, - sky->htcp_desc.lower, sky->htcp_desc.upper, trace_cloud, 32, - &slab_ctx); - if(res != RES_OK) goto error; - - if(!SVX_HIT_NONE(hit)) goto exit; /* Collision */ - } - - /* Pursue ray traversal into the atmosphere */ - range_adjusted[0] = nextafter(cloud_range[1], DBL_MAX); - range_adjusted[1] = range[1]; - res = svx_tree_trace_ray(atmosphere, org, dir, range_adjusted, challenge, - filter, context, hit); - if(res != RES_OK) { - htrdr_log_err(sky->htrdr, - "%s: could not trace the ray part that ends in the atmosphere.\n", - FUNC_NAME); - goto error; - } - if(!SVX_HIT_NONE(hit)) goto exit; /* Collision */ - } - -exit: - return res; -error: - goto exit; -} - diff --git a/src/htrdr_sky.h b/src/htrdr_sky.h @@ -1,177 +0,0 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier - * - * 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 HTRDR_SKY_H -#define HTRDR_SKY_H - -#include <rsys/rsys.h> -#include <star/svx.h> - -/* Properties of the sky */ -enum htrdr_sky_property { - HTRDR_Ks, /* Scattering coefficient */ - HTRDR_Ka, /* Absorption coefficient */ - HTRDR_Kext, /* Extinction coefficient = Ks + Ka */ - HTRDR_PROPERTIES_COUNT__ -}; - -/* FIXME Maybe rename this constant to avoid the confusion with the - * htrdr_sky_component_flag enumerate */ -enum htrdr_sky_component { - HTRDR_GAS__, - HTRDR_PARTICLES__, - HTRDR_COMPONENTS_COUNT__ -}; - -/* Component of the sky for which the properties are queried */ -enum htrdr_sky_component_flag { - HTRDR_GAS = BIT(HTRDR_GAS__), - HTRDR_PARTICLES = BIT(HTRDR_PARTICLES__), - HTRDR_ALL_COMPONENTS = HTRDR_GAS | HTRDR_PARTICLES -}; - -enum htrdr_svx_op { - HTRDR_SVX_MIN, - HTRDR_SVX_MAX, - HTRDR_SVX_OPS_COUNT__ -}; - -/* Forward declarations */ -struct htrdr; -struct htrdr_sky; -struct htrdr_sun; -struct ssp_rng; -struct svx_tree; -struct svx_voxel; - -extern LOCAL_SYM res_T -htrdr_sky_create - (struct htrdr* htrdr, - struct htrdr_sun* sun, - const char* htcp_filename, - const char* htgop_filename, - const char* htmie_filename, - const double optical_thickness, /* Threshold used during octree building */ - const int repeat_clouds, /* Infinitely repeat the clouds in X and Y */ - struct htrdr_sky** sky); - -extern LOCAL_SYM void -htrdr_sky_ref_get - (struct htrdr_sky* sky); - -extern LOCAL_SYM void -htrdr_sky_ref_put - (struct htrdr_sky* sky); - -extern LOCAL_SYM double -htrdr_sky_fetch_particle_phase_function_asymmetry_parameter - (const struct htrdr_sky* sky, - const size_t ispectral_band, - const size_t iquadrature_pt); - -extern LOCAL_SYM double -htrdr_sky_fetch_raw_property - (const struct htrdr_sky* sky, - const enum htrdr_sky_property prop, - const int comp_mask, /* Combination of htrdr_sky_component_flag */ - const size_t ispectral_band, - const size_t iquadrature_pt, - const double pos[3], - const double k_min, /* For debug only */ - const double k_max); /* For debug only */ - -extern LOCAL_SYM double -htrdr_sky_fetch_svx_property - (const struct htrdr_sky* sky, - const enum htrdr_sky_property prop, - const enum htrdr_svx_op op, - const int comp_mask, /* Combination of htrdr_sky_component_flag */ - const size_t ispectral_band, - const size_t iquadrature_pt, - const double pos[3]); - -extern LOCAL_SYM double -htrdr_sky_fetch_svx_voxel_property - (const struct htrdr_sky* sky, - const enum htrdr_sky_property prop, - const enum htrdr_svx_op op, - const int comp_mask, /* Combination of htrdr_sky_component_flag */ - const size_t ispectral_band, - const size_t iquadrature_pt, - const struct svx_voxel* voxel); - -extern LOCAL_SYM res_T -htrdr_sky_dump_clouds_vtk - (const struct htrdr_sky* sky, - const size_t ispectral_band, - const size_t iquadrature_pt, - FILE* stream); - -extern LOCAL_SYM size_t -htrdr_sky_get_sw_spectral_bands_count - (const struct htrdr_sky* sky); - -extern LOCAL_SYM size_t -htrdr_sky_get_sw_spectral_band_id - (const struct htrdr_sky* sky, - const size_t i); /* in [0, htrdr_sky_get_sw_spectral_bands_count[ */ - -extern LOCAL_SYM size_t -htrdr_sky_get_sw_spectral_band_quadrature_length - (const struct htrdr_sky* sky, - const size_t iband); - -extern LOCAL_SYM res_T -htrdr_sky_get_sw_spectral_band_bounds - (const struct htrdr_sky* sky, - const size_t iband, - double wavelengths[2]); - -extern LOCAL_SYM void -htrdr_sky_sample_sw_spectral_data_CIE_1931_X - (const struct htrdr_sky* sky, - struct ssp_rng* rng, - size_t* ispectral_band, - size_t* iquadrature_pt); - -extern LOCAL_SYM void -htrdr_sky_sample_sw_spectral_data_CIE_1931_Y - (const struct htrdr_sky* sky, - struct ssp_rng* rng, - size_t* ispectral_band, - size_t* iquadrature_pt); - -extern LOCAL_SYM void -htrdr_sky_sample_sw_spectral_data_CIE_1931_Z - (const struct htrdr_sky* sky, - struct ssp_rng* rng, - size_t* ispectral_band, - size_t* iquadrature_pt); - -extern LOCAL_SYM res_T -htrdr_sky_trace_ray - (struct htrdr_sky* sky, - const double ray_origin[3], - const double ray_direction[3], /* Must be normalized */ - const double ray_range[2], - const svx_hit_challenge_T challenge, /* NULL <=> Traversed up to the leaves */ - const svx_hit_filter_T filter, /* NULL <=> Stop RT at the 1st hit voxel */ - void* context, /* Data sent to the filter functor */ - const size_t ispectral_band, - const size_t iquadrature_pt, - struct svx_hit* hit); - -#endif /* HTRDR_SKY_H */ - diff --git a/src/htrdr_slab.c b/src/htrdr_slab.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_slab.h b/src/htrdr_slab.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_solve.h b/src/htrdr_solve.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_sun.c b/src/htrdr_sun.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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 diff --git a/src/htrdr_sun.h b/src/htrdr_sun.h @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 CNRS, |Meso|Star>, Université Paul Sabatier +/* Copyright (C) 2018, 2019, 2020 |Meso|Star> (contact@meso-star.com) + * Copyright (C) 2018, 2019 CNRS, Université Paul Sabatier * * 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