htrdr

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

commit 96dfacb7be8c728de1d05b476ba2d25a8a31e0a3
parent 83f5f871ca0a407543b75ffb0b34e197ce085f95
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 18 Mar 2025 15:36:49 +0100

planets: configure the atmosphere and multi-proc

Update to the way atmopshere is configured when using MPI. In fact,
manage the new way of configuring the acceleration structure as
documented in commit 0103104.

The analysis of the input arguments is therefore updated to take account
of these. And, by default, if octree storage is used, octrees are now
built by the master process only, assuming that storage is on a disk
shared by the different processes.

Diffstat:
Msrc/planets/htrdr_planets.c | 100++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/planets/htrdr_planets_args.c | 122++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/planets/htrdr_planets_args.h.in | 34+++++++++++++++++++++++-----------
3 files changed, 204 insertions(+), 52 deletions(-)

diff --git a/src/planets/htrdr_planets.c b/src/planets/htrdr_planets.c @@ -21,7 +21,7 @@ * 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 200112L /* fdopen, nextafter, rint */ +#define _POSIX_C_SOURCE 200112L /* fdopen, nanosleep, nextafter, rint */ #include "core/htrdr.h" #include "core/htrdr_ran_wlen_cie_xyz.h" @@ -49,6 +49,9 @@ #include <unistd.h> /* close */ #include <sys/stat.h> +#include <mpi.h> +#include <time.h> + /******************************************************************************* * Helper function ******************************************************************************/ @@ -86,9 +89,9 @@ setup_octree_storage rnatm_args->octrees_storage = NULL; rnatm_args->load_octrees_from_storage = 0; - if(!args->octrees_storage) goto exit; + if(!args->accel_struct.storage) goto exit; - fd = open(args->octrees_storage, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); + fd = open(args->accel_struct.storage, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); if(fd < 0) { res = RES_IO_ERR; goto error; } rnatm_args->octrees_storage = fdopen(fd, "w+"); @@ -98,7 +101,7 @@ setup_octree_storage * descriptor */ fd = -1; - err = stat(args->octrees_storage, &file_stat); + err = stat(args->accel_struct.storage, &file_stat); if(err < 0) { res = RES_IO_ERR; goto error; } if(file_stat.st_size != 0) { @@ -111,7 +114,7 @@ exit: return res; error: htrdr_log_err(cmd->htrdr, "error opening the octree storage `%s' -- %s\n", - args->octrees_storage, res_to_cstr(res)); + args->accel_struct.storage, res_to_cstr(res)); if(fd >= 0) CHK(close(fd) == 0); if(rnatm_args->octrees_storage) CHK(fclose(rnatm_args->octrees_storage) == 0); @@ -120,34 +123,83 @@ error: goto exit; } +static void +mpi_barrier(void) +{ + struct timespec t; + int complete = 0; + MPI_Request req; + + t.tv_sec = 0; + t.tv_nsec = 10000000; /* 10ms */ + + /* Use an asynchronous barrier to avoid active waiting, + * and therefore wasted resources */ + MPI_Ibarrier(MPI_COMM_WORLD, &req); + + do { + nanosleep(&t, NULL); + MPI_Test(&req, &complete, MPI_STATUS_IGNORE); + } while(!complete); +} + static res_T setup_atmosphere (struct htrdr_planets* cmd, const struct htrdr_planets_args* args) { - struct rnatm_create_args rnatm_args = RNATM_CREATE_ARGS_DEFAULT; + struct rnatm_create_args rnatm = RNATM_CREATE_ARGS_DEFAULT; res_T res = RES_OK; ASSERT(cmd && args); - rnatm_args.gas = args->gas; - rnatm_args.aerosols = args->aerosols; - rnatm_args.naerosols = args->naerosols; - rnatm_args.name = "atmosphere"; - rnatm_args.spectral_range[0] = args->spectral_domain.wlen_range[0]; - rnatm_args.spectral_range[1] = args->spectral_domain.wlen_range[1]; - rnatm_args.optical_thickness = args->optical_thickness; - rnatm_args.grid_definition_hint = args->octree_definition_hint; - rnatm_args.precompute_normals = args->precompute_normals; - rnatm_args.logger = htrdr_get_logger(cmd->htrdr); - rnatm_args.allocator = htrdr_get_allocator(cmd->htrdr); - rnatm_args.nthreads = args->nthreads; - rnatm_args.verbose = args->verbose; - - res = setup_octree_storage(cmd, args, &rnatm_args); - if(res != RES_OK) goto error; + rnatm.gas = args->gas; + rnatm.aerosols = args->aerosols; + rnatm.naerosols = args->naerosols; + rnatm.name = "atmosphere"; + rnatm.spectral_range[0] = args->spectral_domain.wlen_range[0]; + rnatm.spectral_range[1] = args->spectral_domain.wlen_range[1]; + rnatm.optical_thickness = args->accel_struct.optical_thickness; + rnatm.grid_definition_hint = args->accel_struct.definition_hint; + rnatm.precompute_normals = args->precompute_normals; + rnatm.logger = htrdr_get_logger(cmd->htrdr); + rnatm.allocator = htrdr_get_allocator(cmd->htrdr); + rnatm.nthreads = args->accel_struct.nthreads; + rnatm.verbose = args->verbose; + + if(!args->accel_struct.storage || !args->accel_struct.master_only) { + /* From now on, either the octrees have to be built in memory, or all the + * processes have to build them. In all cases, all processes must create + * the atmopshere data structures. */ + if((res = setup_octree_storage(cmd, args, &rnatm)) != RES_OK) goto error; + if((res = rnatm_create(&rnatm, &cmd->atmosphere)) != RES_OK) goto error; - res = rnatm_create(&rnatm_args, &cmd->atmosphere); - if(res != RES_OK) goto error; + } else { + + const int is_master_process = htrdr_get_mpi_rank(cmd->htrdr) == 0; + /* A storage space is used and only the master process must fill it with the + * octrees it builds */ + + if(is_master_process) { + /* Octrees are built only by the master process and stored on disk. + * Note that in reality, octrees may already be constructed and stored in + * the storage provided. In any case, we can treat this special case as + * the general case, and therefore simply consider that in such situation, + * "construction" by the master process is only faster */ + if((res = setup_octree_storage(cmd, args, &rnatm)) != RES_OK) goto error; + if((res = rnatm_create(&rnatm, &cmd->atmosphere)) != RES_OK) goto error; + } + + /* Wait for octrees to be built by the master process */ + mpi_barrier(); + + if(!is_master_process) { + /* Octrees have been built by the master process. + * Non master processes simply load them. */ + if((res = setup_octree_storage(cmd, args, &rnatm)) != RES_OK) goto error; + ASSERT(rnatm.load_octrees_from_storage == 1); + if((res = rnatm_create(&rnatm, &cmd->atmosphere)) != RES_OK) goto error; + } + } exit: return res; diff --git a/src/planets/htrdr_planets_args.c b/src/planets/htrdr_planets_args.c @@ -117,17 +117,32 @@ check_volrad_budget_args(const struct htrdr_planets_volrad_budget_args* args) return RES_OK; } +static INLINE res_T +check_accel_struct_build_args + (const struct htrdr_planets_accel_struct_build_args* args) +{ + if(!args) return RES_BAD_ARG; + + /* Definition and number of threads cannot be null */ + if(!args->definition_hint || !args->nthreads) return RES_BAD_ARG; + + /* Invalid threshold */ + if(args->optical_thickness < 0) return RES_BAD_ARG; + + return RES_OK; +} + static void usage(void) { printf("usage: htrdr-planets [-dfhNv] [-a aerosol_opt[:aerosol_opt ...]]\n"); + printf(" [-b accel_struct_build_opt[:accel_struct_build_opt ...]]\n"); printf(" [-C persp_camera_opt[:persp_camera_opt ...]]\n"); printf(" [-G ground_opt[:ground_opt ...]]\n"); - printf(" [-i image_opt[:image_opt ...]] [-O accel_struct_storage]\n"); - printf(" [-o output] [-r volrad_budget_opt[:volrad_budget_opt ...]]\n"); + printf(" [-i image_opt[:image_opt ...]] [-o output]\n"); + printf(" [-r volrad_budget_opt[:volrad_budget_opt ...]]\n"); printf(" [-S source_opt[:source_opt ...]]\n"); - printf(" [-s spectral_opt[:spectral_opt ...]] [-T optical_thickness]\n"); - printf(" [-t threads_count] [-V accel_struct_definition]\n"); + printf(" [-s spectral_opt[:spectral_opt ...]] [-t threads_count]\n"); printf(" -g gas_opt[:gas_opt ...]\n"); } @@ -590,6 +605,85 @@ error: goto exit; } +static res_T +parse_accel_struct_build_parameters(const char* str, void* ptr) +{ + enum { DEF, NTHREADS, PROC, STORAGE, OPTIC_THICKNESS} iparam; + char buf[BUFSIZ]; + struct htrdr_planets_args* args = ptr; + char* key; + char* val; + char* tk_ctx; + res_T res = RES_OK; + + ASSERT(str && ptr); + + if(strlen(str) >= sizeof(buf) -1/*NULL char*/) { + fprintf(stderr, + "Could not duplicate the parameters of the acceleration structures `%s'", + str); + res = RES_MEM_ERR; + goto error; + } + strncpy(buf, str, sizeof(buf)); + + key = strtok_r(buf, "=", &tk_ctx); + val = strtok_r(NULL, "", &tk_ctx); + + if(!strcmp(key, "def")) iparam = DEF; + else if(!strcmp(key, "nthreads")) iparam = NTHREADS; + else if(!strcmp(key, "proc")) iparam = PROC; + else if(!strcmp(key, "storage")) iparam = STORAGE; + else if(!strcmp(key, "tau")) iparam = OPTIC_THICKNESS; + else { + fprintf(stderr, "Invalid acceleration structure parameter `%s'\n", key); + res = RES_BAD_ARG; + goto error; + } + + switch(iparam) { + case DEF: + res = cstr_to_uint(val, &args->accel_struct.definition_hint); + if(res == RES_OK && args->accel_struct.definition_hint == 0) + res = RES_BAD_ARG; + break; + case NTHREADS: + res = cstr_to_uint(val, &args->accel_struct.nthreads); + if(res == RES_OK && args->accel_struct.nthreads == 0) res = RES_BAD_ARG; + break; + case PROC: + if(!strcmp(val, "all")) { + args->accel_struct.master_only = 0; + } else if(!strcmp(val, "master")) { + args->accel_struct.master_only = 1; + } else { + res = RES_BAD_ARG; + } + break; + case STORAGE: + if(args->accel_struct.storage) mem_rm(args->accel_struct.storage); + if(!(args->accel_struct.storage = str_dup(val))) res = RES_MEM_ERR; + break; + case OPTIC_THICKNESS: + res = cstr_to_double(val, &args->accel_struct.optical_thickness); + if(res == RES_OK && args->accel_struct.optical_thickness < 0) + res = RES_BAD_ARG; + break; + default: FATAL("Unreachable code\n"); break; + } + if(res != RES_OK) { + fprintf(stderr, + "Unable to parse the acceleration structure parameter `%s' -- %s\n", + str, res_to_cstr(res)); + goto error; + } + +exit: + return res; +error: + goto exit; +} + /******************************************************************************* * Local functions ******************************************************************************/ @@ -602,7 +696,7 @@ htrdr_planets_args_init(struct htrdr_planets_args* args, int argc, char** argv) *args = HTRDR_PLANETS_ARGS_DEFAULT; - while((opt = getopt(argc, argv, "a:C:dfG:g:hi:NO:o:r:S:s:T:t:V:v")) != -1) { + while((opt = getopt(argc, argv, "a:b:C:dfG:g:hi:No:r:S:s:t:v")) != -1) { switch(opt) { case 'a': (void)sa_add(args->aerosols, 1); @@ -613,6 +707,9 @@ htrdr_planets_args_init(struct htrdr_planets_args* args, int argc, char** argv) res = check_aerosol_args(args->aerosols+args->naerosols-1); } break; + case 'b': + res = cstr_parse_list(optarg, ':', parse_accel_struct_build_parameters, args); + break; case 'C': res = htrdr_args_camera_perspective_parse(&args->cam_persp, optarg); args->output_type = HTRDR_PLANETS_ARGS_OUTPUT_IMAGE; @@ -644,7 +741,6 @@ htrdr_planets_args_init(struct htrdr_planets_args* args, int argc, char** argv) res = htrdr_args_image_parse(&args->image, optarg); break; case 'N': args->precompute_normals = 1; break; - case 'O': args->octrees_storage = optarg; break; case 'o': args->output = optarg; break; case 'r': res = cstr_parse_list(optarg, ':', parse_volrad_budget_parameters, args); @@ -656,18 +752,10 @@ htrdr_planets_args_init(struct htrdr_planets_args* args, int argc, char** argv) case 's': res = cstr_parse_list(optarg, ':', parse_spectral_parameters, args); break; - case 'T': - res = cstr_to_double(optarg, &args->optical_thickness); - if(res == RES_OK && args->optical_thickness < 0) res = RES_BAD_ARG; - break; case 't': res = cstr_to_uint(optarg, &args->nthreads); if(res == RES_OK && !args->nthreads) res = RES_BAD_ARG; break; - case 'V': - res = cstr_to_uint(optarg, &args->octree_definition_hint); - if(res == RES_OK && !args->octree_definition_hint) res = RES_BAD_ARG; - break; case 'v': args->verbose = 1; break; default: res = RES_BAD_ARG; break; } @@ -737,6 +825,7 @@ htrdr_planets_args_release(struct htrdr_planets_args* args) if(args->ground.name) mem_rm(args->ground.name); if(args->source.rnrl_filename) mem_rm(args->source.rnrl_filename); if(args->volrad_budget.smsh_filename) mem_rm(args->volrad_budget.smsh_filename); + if(args->accel_struct.storage) mem_rm(args->accel_struct.storage); FOR_EACH(i, 0, args->naerosols) { struct rnatm_aerosol_args* aerosol = args->aerosols + i; @@ -770,9 +859,8 @@ htrdr_planets_args_check(const struct htrdr_planets_args* args) } /* Check the octree parameters */ - if(args->octree_definition_hint == 0 - || args->optical_thickness < 0) - return RES_BAD_ARG; + res = check_accel_struct_build_args(&args->accel_struct); + if(res != RES_OK) return res; /* Check the spectral domain */ res = check_spectral_args(&args->spectral_domain); diff --git a/src/planets/htrdr_planets_args.h.in b/src/planets/htrdr_planets_args.h.in @@ -82,6 +82,25 @@ static const struct htrdr_planets_volrad_budget_args HTRDR_PLANETS_VOLRAD_BUDGET_ARGS_NULL = HTRDR_PLANETS_VOLRAD_BUDGET_ARGS_NULL__; +/* Configure the building of the acceleration structure */ +struct htrdr_planets_accel_struct_build_args { + unsigned definition_hint; /* Hint on octree definition */ + unsigned nthreads; /* Hint on the number of threads to use */ + double optical_thickness; /* Threshold used during octree building */ + + /* Read/Write file where octrees are stored. May be NULL => octres are built + * at runtime and kept in memory */ + char* storage; + + /* Indicator defining whether the structure is built solely on the + * master process */ + int master_only; +}; +#define HTRDR_PLANETS_ACCEL_STRUCT_BUILD_ARGS_NULL__ { 512, 8, 1, NULL, 1 } +static const struct htrdr_planets_accel_struct_build_args +HTRDR_PLANETS_ACCEL_STRUCT_BUILD_ARGS_NULL = + HTRDR_PLANETS_ACCEL_STRUCT_BUILD_ARGS_NULL__; + struct htrdr_planets_args { /* System data */ struct rnatm_gas_args gas; @@ -89,12 +108,8 @@ struct htrdr_planets_args { size_t naerosols; struct htrdr_planets_ground_args ground; - /* Read/Write file where octrees are stored. May be NULL => octres are built - * at runtime and kept in memory */ - char* octrees_storage; - - unsigned octree_definition_hint; /* Hint on octree definition */ - double optical_thickness; /* Threshold used during octree building */ + /* Configure the building of the acceleration structure */ + struct htrdr_planets_accel_struct_build_args accel_struct; char* output; /* File where the result is written */ struct htrdr_planets_spectral_args spectral_domain; /* Integration spectral domain */ @@ -107,7 +122,7 @@ struct htrdr_planets_args { struct htrdr_planets_volrad_budget_args volrad_budget; /* Miscellaneous arguments */ - unsigned nthreads; /* Hint on the nimber of threads to use */ + unsigned nthreads; /* Hint on the number of threads to use */ enum htrdr_planets_args_output_type output_type; int precompute_normals; /* Pre-compute tetrahedron normals */ int force_output_overwrite; /* Replace output if it exists */ @@ -120,10 +135,7 @@ struct htrdr_planets_args { 0, /* Number of aerosols */ \ HTRDR_PLANETS_GROUND_ARGS_NULL__, /* Ground */ \ \ - NULL, /* File where to dump octrees */ \ - \ - @HTRDR_PLANETS_ARGS_DEFAULT_GRID_DEFINITION_HINT@, /* octree definition */ \ - @HTRDR_PLANETS_ARGS_DEFAULT_OPTICAL_THICKNESS_THRESHOLD@, \ + HTRDR_PLANETS_ACCEL_STRUCT_BUILD_ARGS_NULL__, \ \ NULL, /* Ouput file */ \ HTRDR_PLANETS_SPECTRAL_ARGS_DEFAULT__, /* Spectral domain */ \