stardis-solver

Solve coupled heat transfers
git clone git://git.meso-star.fr/stardis-solver.git
Log | Files | Refs | README | LICENSE

commit cdc5ce0ec54aae65d91f333af868b5120e6643dc
parent 4f7a34d95ab77a42ec48189d3197ebc7b8e5d371
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed,  5 Jan 2022 14:26:21 +0100

Merge branch 'feature_mpi' into develop

Diffstat:
MREADME.md | 2+-
Mcmake/CMakeLists.txt | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Asrc/sdis.c | 869+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis.h | 46++++++++++++++++++++++++++++++++++++++++------
Msrc/sdis_Xd_begin.h | 2+-
Msrc/sdis_Xd_end.h | 2+-
Asrc/sdis_c.h | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_camera.c | 2+-
Msrc/sdis_camera.h | 2+-
Msrc/sdis_data.c | 2+-
Msrc/sdis_device.c | 321++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/sdis_device_c.h | 21++++++++++++++++++++-
Msrc/sdis_estimator.c | 2+-
Msrc/sdis_estimator_buffer.c | 4++--
Msrc/sdis_estimator_buffer_c.h | 2+-
Msrc/sdis_estimator_c.h | 2+-
Msrc/sdis_green.c | 2+-
Msrc/sdis_green.h | 2+-
Msrc/sdis_heat_path.c | 2+-
Msrc/sdis_heat_path.h | 2+-
Msrc/sdis_heat_path_boundary.c | 2+-
Msrc/sdis_heat_path_boundary_Xd.h | 2+-
Msrc/sdis_heat_path_boundary_Xd_c.h | 2+-
Msrc/sdis_heat_path_boundary_Xd_fixed_flux.h | 2+-
Msrc/sdis_heat_path_boundary_Xd_solid_fluid_picard1.h | 6+++---
Msrc/sdis_heat_path_boundary_Xd_solid_fluid_picardN.h | 6+++---
Msrc/sdis_heat_path_boundary_Xd_solid_solid.h | 2+-
Msrc/sdis_heat_path_boundary_c.h | 2+-
Msrc/sdis_heat_path_conductive_Xd.h | 2+-
Msrc/sdis_heat_path_convective_Xd.h | 2+-
Msrc/sdis_heat_path_radiative_Xd.h | 2+-
Msrc/sdis_interface.c | 6+++---
Msrc/sdis_interface_c.h | 2+-
Msrc/sdis_log.c | 38++++++++++++++++++++++++++++----------
Msrc/sdis_log.h | 2+-
Msrc/sdis_medium.c | 2+-
Msrc/sdis_medium_c.h | 2+-
Msrc/sdis_misc.c | 50+++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/sdis_misc.h | 19++++++++++++++++++-
Msrc/sdis_misc_Xd.h | 2+-
Asrc/sdis_mpi.c | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_mpi.h | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_realisation.c | 2+-
Msrc/sdis_realisation.h | 2+-
Msrc/sdis_realisation_Xd.h | 6+++---
Msrc/sdis_scene.c | 62+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/sdis_scene_Xd.h | 2+-
Msrc/sdis_scene_c.h | 25++++++++++++++++++++++---
Msrc/sdis_solve.c | 359+------------------------------------------------------------------------------
Msrc/sdis_solve_boundary_Xd.h | 569+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Asrc/sdis_solve_camera.c | 699+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_solve_medium_Xd.h | 461+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/sdis_solve_probe_Xd.h | 245+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/sdis_solve_probe_boundary_Xd.h | 608+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Asrc/sdis_tile.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_tile.h | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis.c | 34++++++++++++++++++++++++++++++++++
Msrc/test_sdis_accum_buffer.c | 2+-
Msrc/test_sdis_camera.c | 9++-------
Msrc/test_sdis_compute_power.c | 92++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/test_sdis_conducto_radiative.c | 6++++--
Msrc/test_sdis_conducto_radiative_2d.c | 4++--
Msrc/test_sdis_contact_resistance.c | 10+++-------
Msrc/test_sdis_contact_resistance.h | 2+-
Msrc/test_sdis_contact_resistance_2.c | 10+++-------
Msrc/test_sdis_convection.c | 8++------
Msrc/test_sdis_convection_non_uniform.c | 8++------
Msrc/test_sdis_data.c | 7++-----
Msrc/test_sdis_device.c | 54+++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/test_sdis_flux.c | 10+++-------
Msrc/test_sdis_interface.c | 8++------
Msrc/test_sdis_medium.c | 8++------
Msrc/test_sdis_picard.c | 8++------
Msrc/test_sdis_scene.c | 8++------
Msrc/test_sdis_solid_random_walk_robustness.c | 10+++-------
Msrc/test_sdis_solve_boundary.c | 191+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/test_sdis_solve_boundary_flux.c | 63++++++++++++++++++++++++++++++++++++---------------------------
Msrc/test_sdis_solve_camera.c | 95++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/test_sdis_solve_medium.c | 103++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/test_sdis_solve_medium_2d.c | 98+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/test_sdis_solve_probe.c | 6++++--
Msrc/test_sdis_solve_probe2.c | 68++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/test_sdis_solve_probe2_2d.c | 8++------
Msrc/test_sdis_solve_probe3.c | 10+++-------
Msrc/test_sdis_solve_probe3_2d.c | 8++------
Msrc/test_sdis_solve_probe_2d.c | 8++------
Msrc/test_sdis_transcient.c | 6+-----
Msrc/test_sdis_unstationary_atm.c | 10+++-------
Msrc/test_sdis_utils.c | 3+--
Msrc/test_sdis_utils.h | 67++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/test_sdis_volumic_power.c | 10+++-------
Msrc/test_sdis_volumic_power2.c | 5++---
Msrc/test_sdis_volumic_power2_2d.c | 8++------
Msrc/test_sdis_volumic_power3_2d.c | 8++------
Msrc/test_sdis_volumic_power4.c | 8++------
95 files changed, 4344 insertions(+), 1785 deletions(-)

diff --git a/README.md b/README.md @@ -374,7 +374,7 @@ First version and implementation of the Stardis-Solver API. ## License -Copyright (C) 2016-2021 |Meso|Star> (<contact@meso-star.com>). Stardis-Solver +Copyright (C) 2016-2022 |Meso|Star> (<contact@meso-star.com>). Stardis-Solver is free software released under the GPLv3+ license: GNU GPL version 3 or later. You are welcome to redistribute it under certain conditions; refer to the COPYING files for details. diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +# Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) # # 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,6 +21,9 @@ include(CMakeDependentOption) set(SDIS_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src) option(NO_TEST "Do not build tests" OFF) +option(ENABLE_MPI + "Enable the support of distributed parallelism \ +using the Message Passing Interface specification." ON) CMAKE_DEPENDENT_OPTION(ALL_TESTS "Perform basic and advanced tests" OFF "NOT NO_TEST" OFF) @@ -31,7 +34,7 @@ CMAKE_DEPENDENT_OPTION(ALL_TESTS find_package(RCMake 0.4 REQUIRED) find_package(Star2D 0.5 REQUIRED) find_package(Star3D 0.8 REQUIRED) -find_package(StarSP 0.12 REQUIRED) +find_package(StarSP 0.13 REQUIRED) find_package(StarEnc2D 0.5 REQUIRED) find_package(StarEnc3D 0.5 REQUIRED) find_package(RSys 0.12 REQUIRED) @@ -49,6 +52,12 @@ include_directories( ${StarEnc3D_INCLUDE_DIR} ${RSys_INCLUDE_DIR}) +if(ENABLE_MPI) + find_package(MPI 2 REQUIRED) + set(CMAKE_C_COMPILER ${MPI_C_COMPILER}) + include_directories(${MPI_INCLUDE_PATH}) +endif() + rcmake_append_runtime_dirs(_runtime_dirs RSys Star2D Star3D StarSP StarEnc2D StarEnc3D) @@ -61,6 +70,7 @@ set(VERSION_PATCH 3) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SDIS_FILES_SRC + sdis.c sdis_camera.c sdis_data.c sdis_device.c @@ -75,12 +85,15 @@ set(SDIS_FILES_SRC sdis_misc.c sdis_realisation.c sdis_scene.c - sdis_solve.c) + sdis_solve.c + sdis_solve_camera.c + sdis_tile.c) set(SDIS_FILES_INC_API sdis.h) set(SDIS_FILES_INC + sdis_c.h sdis_camera.h sdis_device_c.h sdis_estimator_c.h @@ -107,9 +120,15 @@ set(SDIS_FILES_INC sdis_solve_medium_Xd.h sdis_solve_probe_Xd.h sdis_solve_probe_boundary_Xd.h + sdis_tile.h sdis_Xd_begin.h sdis_Xd_end.h) +if(ENABLE_MPI) + set(SDIS_FILES_SRC ${SDIS_FILES_SRC} sdis_mpi.c) + set(SDIS_FILES_INC ${SDIS_FILES_INC} sdis_mpi.h) +endif() + set(SDIS_FILES_DOC COPYING README.md) # Prepend each file by `SDIS_SOURCE_DIR' @@ -152,6 +171,10 @@ if(CMAKE_COMPILER_IS_GNUCC) set_target_properties(sdis PROPERTIES LINK_FLAGS "${OpenMP_C_FLAGS}") endif() +if(ENABLE_MPI) + set_target_properties(sdis PROPERTIES COMPILE_DEFINITIONS "SDIS_ENABLE_MPI") +endif() + rcmake_setup_devel(sdis Stardis ${VERSION} sdis_version.h) ############################################################################### @@ -180,7 +203,6 @@ if(NOT NO_TEST) endfunction() new_test(test_sdis_camera) - new_test(test_sdis_compute_power) new_test(test_sdis_conducto_radiative) new_test(test_sdis_conducto_radiative_2d) new_test(test_sdis_contact_resistance) @@ -195,22 +217,25 @@ if(NOT NO_TEST) new_test(test_sdis_picard) new_test(test_sdis_scene) new_test(test_sdis_solid_random_walk_robustness) - new_test(test_sdis_solve_camera) new_test(test_sdis_solve_probe) - new_test(test_sdis_solve_probe2) new_test(test_sdis_solve_probe3) new_test(test_sdis_solve_probe_2d) new_test(test_sdis_solve_probe2_2d) new_test(test_sdis_solve_probe3_2d) - new_test(test_sdis_solve_boundary) - new_test(test_sdis_solve_boundary_flux) - new_test(test_sdis_solve_medium) - new_test(test_sdis_solve_medium_2d) new_test(test_sdis_transcient) new_test(test_sdis_unstationary_atm) new_test(test_sdis_volumic_power) new_test(test_sdis_volumic_power4) + build_test(test_sdis) + build_test(test_sdis_compute_power) + build_test(test_sdis_solve_camera) + build_test(test_sdis_solve_medium) + build_test(test_sdis_solve_medium_2d) + build_test(test_sdis_solve_boundary) + build_test(test_sdis_solve_boundary_flux) + build_test(test_sdis_solve_probe2) + # Additionnal tests build_test(test_sdis_volumic_power2) build_test(test_sdis_volumic_power2_2d) @@ -228,7 +253,35 @@ if(NOT NO_TEST) target_link_libraries(test_sdis_solve_probe3 Star3DUT) target_link_libraries(test_sdis_solve_probe3_2d ${MATH_LIB}) target_link_libraries(test_sdis_solve_camera Star3DUT) - + + set(_mpi_tests + test_sdis + test_sdis_compute_power + test_sdis_solve_camera + test_sdis_solve_medium + test_sdis_solve_medium_2d + test_sdis_solve_boundary + test_sdis_solve_boundary_flux + test_sdis_solve_probe2) + + if(NOT ENABLE_MPI) + foreach(_test ${_mpi_tests}) + add_test(${_test} ${_test}) + endforeach() + else() + set_target_properties(test_sdis PROPERTIES + COMPILE_DEFINITIONS "SDIS_ENABLE_MPI") + set_target_properties(test_sdis_device PROPERTIES + COMPILE_DEFINITIONS "SDIS_ENABLE_MPI") + + foreach(_test ${_mpi_tests}) + set_target_properties(${_test} PROPERTIES + COMPILE_DEFINITIONS "SDIS_ENABLE_MPI") + add_test(${_test}_mpi_on mpirun -n 2 ${_test} mpi) + add_test(${_test}_no_mpi ${_test}) + endforeach() + endif() + rcmake_copy_runtime_libraries(test_sdis_solid_random_walk_robustness) endif() @@ -242,4 +295,3 @@ install(TARGETS sdis RUNTIME DESTINATION bin) install(FILES ${SDIS_FILES_INC_API} DESTINATION include/) install(FILES ${SDIS_FILES_DOC} DESTINATION share/doc/stardis-solver) - diff --git a/src/sdis.c b/src/sdis.c @@ -0,0 +1,869 @@ +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) + * + * 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 200112L + +#include "sdis.h" +#include "sdis_c.h" +#include "sdis_device_c.h" +#include "sdis_estimator_c.h" +#include "sdis_green.h" +#include "sdis_log.h" +#include "sdis_misc.h" +#include "sdis_scene_c.h" +#ifdef SDIS_ENABLE_MPI + #include "sdis_mpi.h" +#endif + +#include <star/ssp.h> + +#include <rsys/cstr.h> +#include <rsys/clock_time.h> +#include <rsys/mem_allocator.h> + +#include <errno.h> + +/* Number random numbers in a sequence, i.e. number of consecutive random + * numbers that can be used by a thread */ +#define RNG_SEQUENCE_SIZE 100000 + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE int +check_accum_message(const enum mpi_sdis_message msg) +{ + return msg == MPI_SDIS_MSG_ACCUM_TEMP + || msg == MPI_SDIS_MSG_ACCUM_TIME + || msg == MPI_SDIS_MSG_ACCUM_FLUX_CONVECTIVE + || msg == MPI_SDIS_MSG_ACCUM_FLUX_IMPOSED + || msg == MPI_SDIS_MSG_ACCUM_FLUX_RADIATIVE + || msg == MPI_SDIS_MSG_ACCUM_FLUX_TOTAL + || msg == MPI_SDIS_MSG_ACCUM_MEAN_POWER; +} + +static res_T +gather_green_functions_no_mpi + (struct sdis_scene* scn, + struct ssp_rng_proxy* rng_proxy, + struct sdis_green_function* per_thread_green[], + const struct accum* per_thread_acc_time, + struct sdis_green_function** out_green) +{ + struct sdis_green_function* green = NULL; + struct accum acc_time = ACCUM_NULL; + res_T res = RES_OK; + ASSERT(scn && rng_proxy && per_thread_green && per_thread_acc_time); + ASSERT(out_green); + + /* Redux the per thread green function into the green function of the 1st + * thread */ + res = green_function_redux_and_clear + (per_thread_green[0], per_thread_green+1, scn->dev->nthreads-1); + if(res != RES_OK) goto error; + + /* Return the green of the 1st thread */ + SDIS(green_function_ref_get(per_thread_green[0])); + green = per_thread_green[0]; + + res = gather_accumulators + (scn->dev, MPI_SDIS_MSG_ACCUM_TIME, per_thread_acc_time, &acc_time); + if(res != RES_OK) goto error; + + /* Finalize the estimated green */ + res = green_function_finalize(green, rng_proxy, &acc_time); + if(res != RES_OK) goto error; + +exit: + *out_green = green; + return res; +error: + if(green) { SDIS(green_function_ref_put(green)); green = NULL; } + goto exit; +} + +#ifdef SDIS_ENABLE_MPI +static void +rewind_progress_printing(struct sdis_device* dev) +{ + size_t i; + if(!dev->use_mpi || dev->mpi_nprocs == 1) return; + FOR_EACH(i, 0, dev->mpi_nprocs-1) { + log_info(dev, "\033[1A\r"); /* Move up */ + } +} + +static res_T +send_green_function_to_master_process + (struct sdis_device* dev, + struct sdis_green_function* green) +{ + char buf[128]; + FILE* stream = NULL; /* Temp file that stores the serialized green function */ + void* data = NULL; /* Pointer to serialized green function data */ + long sz = 0; /* Size in Bytes of the serialized green function data */ + res_T res = RES_OK; + ASSERT(dev && green && dev->mpi_rank != 0); + + /* Open a stream to store the serialized green function */ + stream = tmpfile(); + if(!stream) { + log_err(dev, + "Could not open the stream used to temporary store the green function " + "before it is sent to the master process.\n"); + res = RES_IO_ERR; + goto error; + } + + /* Write the green function into the stream */ + res = sdis_green_function_write(green, stream); + if(res != RES_OK) goto error; + + /* Fetch the size of the serialized data */ + sz = ftell(stream); + if(sz == -1) { + strerror_r(errno, buf, sizeof(buf)); + log_err(dev, + "Could not query the size of the serialized green function data to sent " + "to the master process -- %s.\n", buf); + res = RES_IO_ERR; + goto error; + } + if(sz > INT_MAX) { + log_err(dev, + "The size of the green function data is too large. It must be less than " + "%d Mebabytes while it is of %ld MegabBytes.\n", + INT_MAX / (1024*1024), sz/(1024*1024)); + res = RES_MEM_ERR; + goto error; + } + + data = MEM_CALLOC(dev->allocator, 1, (size_t)sz); + if(!data) { + log_err(dev, "Could not allocate the memory to store the serialized green " + "function before it is sent to the master process.\n"); + res = RES_MEM_ERR; + goto error; + } + + /* Load in memory the serialized data */ + rewind(stream); + if(fread(data, (size_t)sz, 1, stream) != 1) { + log_err(dev, + "Could not read the serialized green function data from the temporary " + "stream before it is sent to the master process.\n"); + res = RES_IO_ERR; + goto error; + } + + /* Send the serialized data to the master process */ + mutex_lock(dev->mpi_mutex); + MPI(Send(data, (int)sz, MPI_CHAR, 0/*Dst*/, MPI_SDIS_MSG_GREEN_FUNCTION, + MPI_COMM_WORLD)); + mutex_unlock(dev->mpi_mutex); + +exit: + if(stream) CHK(fclose(stream) == 0); + if(data) MEM_RM(dev->allocator, data); + return RES_OK; +error: + goto exit; +} + +static res_T +gather_green_functions_from_non_master_process + (struct sdis_scene* scn, + struct sdis_green_function* greens[]) +{ + void* data = NULL; /* Pointer to gathered serialized green function data */ + FILE* stream = NULL; /* Temp file that stores the serialized green function */ + int iproc; + res_T res = RES_OK; + ASSERT(scn->dev && greens && scn->dev->mpi_rank == 0); + + FOR_EACH(iproc, 1, scn->dev->mpi_nprocs) { + MPI_Request req; + MPI_Status status; + int count; + + /* Waiting for the serialized green function sent by the process `iproc'*/ + mpi_waiting_for_message + (scn->dev, iproc, MPI_SDIS_MSG_GREEN_FUNCTION, &status); + + /* Fetch the sizeof the green function sent by the process `iproc' */ + mutex_lock(scn->dev->mpi_mutex); + MPI(Get_count(&status, MPI_CHAR, &count)); + mutex_unlock(scn->dev->mpi_mutex); + + /* Allocate the memory to store the serialized green function sent by the + * process `iproc' */ + data = MEM_REALLOC(scn->dev->allocator, data, (size_t)count); + if(!data) { + log_err(scn->dev, + "Could not allocate %d bytes to store the serialized green function " + "sent by the process %d.\n", + count, iproc); + res = RES_MEM_ERR; + goto error; + } + + /* Asynchronously receive the green function */ + mutex_lock(scn->dev->mpi_mutex); + MPI(Irecv(data, count, MPI_CHAR, iproc, MPI_SDIS_MSG_GREEN_FUNCTION, + MPI_COMM_WORLD, &req)); + mutex_unlock(scn->dev->mpi_mutex); + mpi_waiting_for_request(scn->dev, &req); + + /* Open a stream to store the serialized green function */ + stream = tmpfile(); + if(!stream) { + log_err(scn->dev, + "Could not open the stream used to temporary store the green function " + "sent by the process %d.\n", iproc); + res = RES_IO_ERR; + goto error; + } + + if(fwrite(data, (size_t)count, 1, stream) != 1) { + log_err(scn->dev, + "Could not write the green function sent by the process %d into the " + "temporary stream.\n", iproc); + res = RES_IO_ERR; + goto error; + } + + /* Deserialized the green function of the process `iproc'. Note that the + * number of green functions to output is #procs - 1. Since we + * iterate over the indices of non master processes in [1, #procs], + * the index the green function to deserialized is iproc - 1 */ + rewind(stream); + res = sdis_green_function_create_from_stream(scn, stream, &greens[iproc-1]); + if(res != RES_OK) { + log_err(scn->dev, + "Error deserializing the green function sent by the process %d -- %s.\n", + iproc, res_to_cstr(res)); + goto error; + } + + CHK(fclose(stream) == 0); + stream = NULL; + } + +exit: + if(data) MEM_RM(scn->dev->allocator, data); + if(stream) CHK(fclose(stream) == 0); + return res; +error: + goto exit; +} +#endif + +/******************************************************************************* + * Exported function + ******************************************************************************/ +res_T +sdis_get_info(struct sdis_info* info) +{ + if(!info) return RES_BAD_ARG; + *info = SDIS_INFO_NULL; +#ifdef SDIS_ENABLE_MPI + info->mpi_enabled = 1; +#else + info->mpi_enabled = 0; +#endif + return RES_OK; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +create_per_thread_rng + (struct sdis_device* dev, + struct ssp_rng* rng_state, + struct ssp_rng_proxy** out_proxy, + struct ssp_rng** out_rngs[]) +{ + struct ssp_rng_proxy_create2_args proxy_args = SSP_RNG_PROXY_CREATE2_ARGS_NULL; + struct ssp_rng_proxy* proxy = NULL; + struct ssp_rng** rngs = NULL; + size_t i; + res_T res = RES_OK; + ASSERT(dev && out_proxy && out_rngs); + + rngs = MEM_CALLOC(dev->allocator, dev->nthreads, sizeof(*rngs)); + if(!rngs) { + log_err(dev, "Could not allocate the list of per thread RNG.\n"); + res = RES_MEM_ERR; + goto error; + } + + /* Create the RNG proxy */ + proxy_args.rng= rng_state; + proxy_args.type = SSP_RNG_MT19937_64; + proxy_args.nbuckets = dev->nthreads; +#ifdef SDIS_ENABLE_MPI + if(dev->use_mpi) { + proxy_args.sequence_size = RNG_SEQUENCE_SIZE; + proxy_args.sequence_offset = RNG_SEQUENCE_SIZE * (size_t)dev->mpi_rank; + proxy_args.sequence_pitch = RNG_SEQUENCE_SIZE * (size_t)dev->mpi_nprocs; + } else +#endif + { + proxy_args.sequence_size = RNG_SEQUENCE_SIZE; + proxy_args.sequence_offset = 0; + proxy_args.sequence_pitch = RNG_SEQUENCE_SIZE; + } + res = ssp_rng_proxy_create2(dev->allocator, &proxy_args, &proxy); + if(res != RES_OK) goto error; + + /* Query the RNG proxy to create the per thread RNGs */ + FOR_EACH(i, 0, dev->nthreads) { + res = ssp_rng_proxy_create_rng(proxy, i, &rngs[i]); + if(res != RES_OK) goto error; + } + +exit: + *out_rngs = rngs; + *out_proxy = proxy; + return res; +error: + if(rngs) { release_per_thread_rng(dev, rngs); rngs = NULL; } + if(proxy) { SSP(rng_proxy_ref_put(proxy)); proxy = NULL; } + goto exit; +} + +void +release_per_thread_rng(struct sdis_device* dev, struct ssp_rng* rngs[]) +{ + size_t i; + ASSERT(dev); + if(!rngs) return; + FOR_EACH(i, 0, dev->nthreads) { if(rngs[i]) SSP(rng_ref_put(rngs[i])); } + MEM_RM(dev->allocator, rngs); +} + +res_T +create_per_thread_green_function + (struct sdis_scene* scn, + struct sdis_green_function** out_greens[]) +{ + struct sdis_green_function** greens = NULL; + size_t i; + res_T res = RES_OK; + ASSERT(scn && out_greens); + + greens = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*greens)); + if(!greens) { + log_err(scn->dev, + "Could not allocate the list of per thread green function.\n"); + res = RES_MEM_ERR; + goto error; + } + + FOR_EACH(i, 0, scn->dev->nthreads) { + res = green_function_create(scn, &greens[i]); + if(res != RES_OK) goto error; + } + +exit: + *out_greens = greens; + return res; +error: + if(greens) { + release_per_thread_green_function(scn, greens); + greens = NULL; + } + goto exit; +} + +void +release_per_thread_green_function + (struct sdis_scene* scn, + struct sdis_green_function* greens[]) +{ + size_t i; + ASSERT(greens); + FOR_EACH(i, 0, scn->dev->nthreads) { + if(greens[i]) SDIS(green_function_ref_put(greens[i])); + } + MEM_RM(scn->dev->allocator, greens); +} + +res_T +alloc_process_progress(struct sdis_device* dev, int32_t** out_progress) +{ + int32_t* progress = NULL; + size_t nprocs; + res_T res = RES_OK; + ASSERT(dev && out_progress); + +#ifdef SDIS_ENABLE_MPI + if(dev->use_mpi) { + nprocs = (size_t)dev->mpi_nprocs; + } else +#endif + { + nprocs = 1; + } + progress = MEM_CALLOC(dev->allocator, nprocs, sizeof(*progress)); + if(!progress) { + log_err(dev,"Could not allocate the list of per process progress status.\n"); + res = RES_MEM_ERR; + goto error; + } + +exit: + *out_progress = progress; + return res; +error: + if(progress) { MEM_RM(dev->allocator, progress); progress = NULL; } + goto exit; +} + +void +free_process_progress(struct sdis_device* dev, int32_t progress[]) +{ + ASSERT(dev && progress); + MEM_RM(dev->allocator, progress); +} + +size_t +compute_process_realisations_count + (const struct sdis_device* dev, + const size_t nrealisations) +{ +#ifndef SDIS_ENABLE_MPI + (void)dev, (void)nrealisations; + return nrealisations; +#else + size_t per_process_nrealisations = 0; + size_t remaining_nrealisations = 0; + ASSERT(dev); + + if(!dev->use_mpi) return nrealisations; + + /* Compute minimum the number of realisations on each process */ + per_process_nrealisations = nrealisations / (size_t)dev->mpi_nprocs; + + /* Define the remaining number of realisations that are not handle by one + * process */ + remaining_nrealisations = + nrealisations + - per_process_nrealisations * (size_t)dev->mpi_nprocs; + + /* Distribute the remaining realisations onto the processes */ + if((size_t)dev->mpi_rank >= remaining_nrealisations) { + return per_process_nrealisations; + } else { + return per_process_nrealisations + 1; + } +#endif +} + +#ifndef SDIS_ENABLE_MPI +res_T +gather_accumulators + (struct sdis_device* dev, + const enum mpi_sdis_message msg, + const struct accum* per_thread_acc, + struct accum* acc) +{ + (void)msg; + ASSERT(dev); + /* Gather thread accumulators */ + sum_accums(per_thread_acc, dev->nthreads, acc); + return RES_OK; +} +#endif + +#ifdef SDIS_ENABLE_MPI +res_T +gather_accumulators + (struct sdis_device* dev, + const enum mpi_sdis_message msg, + const struct accum* per_thread_acc, + struct accum* acc) +{ + struct accum* per_proc_acc = NULL; + size_t nprocs = 0; + res_T res = RES_OK; + ASSERT(dev && per_thread_acc && acc && check_accum_message(msg)); + + if(!dev->use_mpi) { + /* Gather thread accumulators */ + sum_accums(per_thread_acc, dev->nthreads, acc); + goto exit; + } + + nprocs = dev->mpi_rank == 0 ? (size_t)dev->mpi_nprocs : 1; + per_proc_acc = MEM_CALLOC(dev->allocator, nprocs, sizeof(struct accum)); + if(!per_proc_acc) { res = RES_MEM_ERR; goto error; } + + /* Gather thread accumulators */ + sum_accums(per_thread_acc, dev->nthreads, &per_proc_acc[0]); + + /* Non master process */ + if(dev->mpi_rank != 0) { + + /* Send the accumulator to the master process */ + mutex_lock(dev->mpi_mutex); + MPI(Send(&per_proc_acc[0], sizeof(per_proc_acc[0]), MPI_CHAR, 0/*Dst*/, + msg, MPI_COMM_WORLD)); + mutex_unlock(dev->mpi_mutex); + + *acc = per_proc_acc[0]; + + /* Master process */ + } else { + int iproc; + + /* Gather process accumulators */ + FOR_EACH(iproc, 1, dev->mpi_nprocs) { + MPI_Request req; + + /* Asynchronously receive the accumulator of `iproc' */ + mutex_lock(dev->mpi_mutex); + MPI(Irecv(&per_proc_acc[iproc], sizeof(per_proc_acc[iproc]), MPI_CHAR, + iproc, msg, MPI_COMM_WORLD, &req)); + mutex_unlock(dev->mpi_mutex); + mpi_waiting_for_request(dev, &req); + } + + /* Sum the process accumulators */ + sum_accums(per_proc_acc, (size_t)dev->mpi_nprocs, acc); + } + +exit: + if(per_proc_acc) MEM_RM(dev->allocator, per_proc_acc); + return res; +error: + goto exit; +} +#endif /* SDIS_ENABLE_MPI */ + +#ifndef SDIS_ENABLE_MPI +res_T +gather_green_functions + (struct sdis_scene* scn, + struct ssp_rng_proxy* rng_proxy, + struct sdis_green_function* per_thread_green[], + const struct accum* per_thread_acc_time, + struct sdis_green_function** out_green) +{ + return gather_green_functions_no_mpi + (scn, rng_proxy, per_thread_green, per_thread_acc_time, out_green); +} +#else +res_T +gather_green_functions + (struct sdis_scene* scn, + struct ssp_rng_proxy* rng_proxy, + struct sdis_green_function* per_thread_green[], + const struct accum* per_thread_acc_time, + struct sdis_green_function** out_green) +{ + struct accum acc_time = ACCUM_NULL; + struct sdis_green_function* green = NULL; + struct sdis_green_function** per_proc_green = NULL; + unsigned ithread; + int iproc; + res_T res = RES_OK; + ASSERT(scn && per_thread_green && out_green); + + if(!scn->dev->use_mpi) { + return gather_green_functions_no_mpi + (scn, rng_proxy, per_thread_green, per_thread_acc_time, out_green); + goto exit; + } + + /* Redux the per thread green function into the green function of the 1st + * thread */ + res = green_function_redux_and_clear + (per_thread_green[0], per_thread_green+1, scn->dev->nthreads-1); + if(res != RES_OK) goto error; + + /* Gather the accumulators. The master process gathers all accumulators and + * non master process gather their per thread accumulators only that is + * sent to the master process */ + res = gather_accumulators + (scn->dev, MPI_SDIS_MSG_ACCUM_TIME, per_thread_acc_time, &acc_time); + if(res != RES_OK) goto error; + + /* Non master process */ + if(scn->dev->mpi_rank != 0) { + /* Return the green of the 1st thread */ + SDIS(green_function_ref_get(per_thread_green[0])); + green = per_thread_green[0]; + + /* We have to finalize the green function priorly to its sent to the master + * process. Its serialization failed without it. */ + res = green_function_finalize(green, rng_proxy, &acc_time); + if(res != RES_OK) goto error; + + res = send_green_function_to_master_process(scn->dev, green); + if(res != RES_OK) goto error; + + /* Master process */ + } else { + /* Allocate the list of per process green functions */ + per_proc_green = MEM_CALLOC(scn->dev->allocator, + (size_t)scn->dev->mpi_nprocs, sizeof(*per_proc_green)); + if(!per_proc_green) { + log_err(scn->dev, + "Could not allocate the temporary list of per process " + "green functions.\n"); + res = RES_MEM_ERR; + goto error; + } + + /* Set the gathered per thread green function stores on thread 0 at the + * green function for master process */ + SDIS(green_function_ref_get(per_thread_green[0])); + per_proc_green[0] = per_thread_green[0]; + + /* Release per thread green functions */ + FOR_EACH(ithread, 0, scn->dev->nthreads) { + SDIS(green_function_ref_put(per_thread_green[ithread])); + per_thread_green[ithread] = NULL; + } + + res = gather_green_functions_from_non_master_process(scn, per_proc_green+1); + if(res != RES_OK) goto error; + + /* Redux the per proc green function into the green function of the master + * process */ + res = green_function_redux_and_clear + (per_proc_green[0], per_proc_green+1, (size_t)scn->dev->mpi_nprocs-1); + if(res != RES_OK) goto error; + + /* Return the gatherd green function of the master process */ + SDIS(green_function_ref_get(per_proc_green[0])); + green = per_proc_green[0]; + + /* Finalize the green function */ + res = green_function_finalize(green, rng_proxy, &acc_time); + if(res != RES_OK) goto error; + } + +exit: + if(per_proc_green) { + FOR_EACH(iproc, 0, scn->dev->mpi_nprocs) { + if(per_proc_green[iproc]) { + SDIS(green_function_ref_put(per_proc_green[iproc])); + } + } + MEM_RM(scn->dev->allocator, per_proc_green); + } + *out_green = green; + return res; +error: + if(green) { SDIS(green_function_ref_put(green)); green = NULL; } + goto exit; +} + +#endif + +#ifndef SDIS_ENABLE_MPI +res_T +gather_rng_proxy_sequence_id + (struct sdis_device* dev, + struct ssp_rng_proxy* proxy) +{ + ASSERT(dev && proxy); + (void)dev, (void)proxy; + return RES_OK; +} +#else + +res_T +gather_rng_proxy_sequence_id + (struct sdis_device* dev, + struct ssp_rng_proxy* proxy) +{ + unsigned long proc_seq_id = 0; + size_t seq_id = SSP_SEQUENCE_ID_NONE; + res_T res = RES_OK; + ASSERT(dev && proxy); + + if(!dev->use_mpi) goto exit; + + /* Retrieve the sequence id of the process */ + SSP(rng_proxy_get_sequence_id(proxy, &seq_id)); + CHK(seq_id <= ULONG_MAX); + proc_seq_id = (unsigned long)seq_id; + + /* Non master process */ + if(dev->mpi_rank != 0) { + + /* Send the sequence id to the master process */ + mutex_lock(dev->mpi_mutex); + MPI(Send(&proc_seq_id, 1, MPI_UNSIGNED_LONG, 0/*Dst*/, + MPI_SDIS_MSG_RNG_PROXY_SEQUENCE_ID, MPI_COMM_WORLD)); + mutex_unlock(dev->mpi_mutex); + + /* Master process */ + } else { + size_t nseqs_to_flush = 0; + unsigned long max_seq_id = 0; + int iproc; + + max_seq_id = proc_seq_id; + + /* Gather per process sequence id and defined the maximum sequence id */ + FOR_EACH(iproc, 1, dev->mpi_nprocs) { + MPI_Request req; + unsigned long tmp_seq_id; + + /* Asynchronously receive the sequence id of `iproc' */ + mutex_lock(dev->mpi_mutex); + MPI(Irecv(&tmp_seq_id, 1, MPI_UNSIGNED_LONG, iproc, + MPI_SDIS_MSG_RNG_PROXY_SEQUENCE_ID, MPI_COMM_WORLD, &req)); + mutex_unlock(dev->mpi_mutex); + mpi_waiting_for_request(dev, &req); + + /* Define the maximum sequence id between all processes */ + max_seq_id = MMAX(max_seq_id, tmp_seq_id); + } + + /* Flush the current sequence that is already consumed in addition to the + * sequences queried by the other processes */ + nseqs_to_flush = 1/*Current sequence*/ + max_seq_id - proc_seq_id; + res = ssp_rng_proxy_flush_sequences(proxy, nseqs_to_flush); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} +#endif + +#ifndef SDIS_ENABLE_MPI +res_T +gather_res_T(struct sdis_device* dev, const res_T res) +{ + (void)dev; + return res; +} +#else +res_T +gather_res_T(struct sdis_device* dev, const res_T proc_res) +{ + int32_t status; + res_T res = proc_res; + int iproc; + ASSERT(dev); + + if(!dev->use_mpi) return proc_res; + + status = (int32_t)(proc_res); + + /* Send the local res status to all other processes */ + FOR_EACH(iproc, 0, dev->mpi_nprocs) { + /* Do not send the res status to yourself */ + if(iproc == dev->mpi_rank) continue; + + mutex_lock(dev->mpi_mutex); + MPI(Send(&status, 1, MPI_INT32_T, iproc, MPI_SDIS_MSG_RES_T, + MPI_COMM_WORLD)); + mutex_unlock(dev->mpi_mutex); + } + + /* Receive the res status of all other processes */ + res = proc_res; + FOR_EACH(iproc, 0, dev->mpi_nprocs) { + MPI_Request req; + + /* Do not receive the res status from yourself */ + if(iproc == dev->mpi_rank) continue; + + mutex_lock(dev->mpi_mutex); + MPI(Irecv(&status, 1, MPI_INT32_T, iproc, MPI_SDIS_MSG_RES_T, + MPI_COMM_WORLD, &req)); + mutex_unlock(dev->mpi_mutex); + mpi_waiting_for_request(dev, &req); + + if(res == RES_OK && status != RES_OK) { + res = (res_T)status; + } + } + + return res; +} + +#endif + +void +print_progress + (struct sdis_device* dev, + int32_t progress[], + const char* label) +{ + ASSERT(dev && label); +#ifndef SDIS_ENABLE_MPI + log_info(dev, "%s%3d%%\r", label, progress[0]); +#else + if(!dev->use_mpi) { + log_info(dev, "%s%3d%%\r", label, progress[0]); + } else { + int i; + if(dev->mpi_rank != 0) return; + mpi_fetch_progress(dev, progress); + FOR_EACH(i, 0, dev->mpi_nprocs) { + log_info(dev, "Process %d -- %s%3d%%%c", + i, label, progress[i], i == dev->mpi_nprocs - 1 ? '\r' : '\n'); + } + } +#endif +} + +void +print_progress_update + (struct sdis_device* dev, + int32_t progress[], + const char* label) +{ + ASSERT(dev); +#ifndef SDIS_ENABLE_MPI + print_progress(dev, progress, label); +#else + if(!dev->use_mpi) { + print_progress(dev, progress, label); + } else { + if(dev->mpi_rank != 0) { + mpi_send_progress(dev, progress[0]); + } else { + mpi_fetch_progress(dev, progress); + rewind_progress_printing(dev); + print_progress(dev, progress, label); + } + } +#endif +} + +void +process_barrier(struct sdis_device* dev) +{ +#ifndef SDIS_ENABLE_MPI + (void)dev; + return; +#else + if(dev->use_mpi) { + mpi_barrier(dev); + } +#endif +} diff --git a/src/sdis.h b/src/sdis.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -130,6 +130,31 @@ struct sdis_mc { #define SDIS_MC_NULL__ {0, 0, 0} static const struct sdis_mc SDIS_MC_NULL = SDIS_MC_NULL__; +/* Input arguments of the sdis_device_create function */ +struct sdis_device_create_args { + struct logger* logger; /* NULL <=> default logger */ + struct mem_allocator* allocator; /* NULL <=> default allocator */ + unsigned nthreads_hint; /* Hint on the number of threads to use */ + int verbosity; /* Verbosity level */ + + /* Use the Message Passing Interface to distribute work between processes. + * This option is taken into account only if Stardis-Solver is compiled with + * MPI support */ + int use_mpi; +}; +#define SDIS_DEVICE_CREATE_ARGS_DEFAULT__ { \ + NULL, NULL, SDIS_NTHREADS_DEFAULT, 1, 0 \ +} +static const struct sdis_device_create_args SDIS_DEVICE_CREATE_ARGS_DEFAULT = + SDIS_DEVICE_CREATE_ARGS_DEFAULT__; + +/* Informations on the Stardis-Solver library */ +struct sdis_info { + int mpi_enabled; /* Define if Stardis-Solver was built with MPI support */ +}; +#define SDIS_INFO_NULL__ {0} +static const struct sdis_info SDIS_INFO_NULL = SDIS_INFO_NULL__; + /******************************************************************************* * Data type used to describe physical properties ******************************************************************************/ @@ -570,7 +595,7 @@ struct sdis_solve_camera_args { * higher orders allow the estimation of the T4 radiative transfer. */ size_t picard_order; - size_t image_resolution[2]; /* Image resolution */ + size_t image_definition[2]; /* Image definition */ size_t spp; /* #samples per pixel */ int register_paths; /* Combination of enum sdis_heat_path_flag */ }; @@ -608,10 +633,7 @@ BEGIN_DECLS ******************************************************************************/ SDIS_API res_T sdis_device_create - (struct logger* logger, /* May be NULL <=> use default logger */ - struct mem_allocator* allocator, /* May be NULL <=> use default allocator */ - const unsigned nthreads_hint, /* Hint on the number of threads to use */ - const int verbose, /* Verbosity level */ + (const struct sdis_device_create_args* args, struct sdis_device** dev); SDIS_API res_T @@ -622,6 +644,11 @@ SDIS_API res_T sdis_device_ref_put (struct sdis_device* dev); +SDIS_API res_T +sdis_device_get_mpi_rank + (struct sdis_device* dev, + int* rank); + /******************************************************************************* * A data stores in the Stardis memory space a set of user defined data. It can * be seen as a ref counted memory space allocated by Stardis. It is used to @@ -1317,6 +1344,13 @@ sdis_solve_medium_green_function const struct sdis_solve_medium_args* args, struct sdis_green_function** green); +/******************************************************************************* + * Retrieve infos from the Stardis-Solver library + ******************************************************************************/ +SDIS_API res_T +sdis_get_info + (struct sdis_info* info); + END_DECLS #endif /* SDIS_H */ diff --git a/src/sdis_Xd_begin.h b/src/sdis_Xd_begin.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_Xd_end.h b/src/sdis_Xd_end.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_c.h b/src/sdis_c.h @@ -0,0 +1,156 @@ +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) + * + * 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 SDIS_C_H +#define SDIS_C_H + +#include <rsys/rsys.h> + +/* Id of the messages sent between processes */ +enum mpi_sdis_message { + MPI_SDIS_MSG_ACCUM_TEMP, /* Temperature accumulator */ + MPI_SDIS_MSG_ACCUM_TIME, /* Time accumulator */ + MPI_SDIS_MSG_ACCUM_FLUX_CONVECTIVE, /* Convective flux accumulator */ + MPI_SDIS_MSG_ACCUM_FLUX_IMPOSED, /* Imposed flux accumulator */ + MPI_SDIS_MSG_ACCUM_FLUX_RADIATIVE, /* Radiative flux accumulator */ + MPI_SDIS_MSG_ACCUM_FLUX_TOTAL, /* Total flux accumulator */ + MPI_SDIS_MSG_ACCUM_MEAN_POWER, /* Mean power accumulator */ + MPI_SDIS_MSG_GREEN_FUNCTION, /* Serialized green function */ + MPI_SDIS_MSG_PROGRESS, /* Progress status */ + MPI_SDIS_MSG_RES_T, /* Result status */ + MPI_SDIS_MSG_RNG_PROXY_SEQUENCE_ID, /* Index of the current RNG sequence */ + MPI_SDIS_MSG_TILE, /* 2D Tile of row ordered accumulators */ + MPI_SDIS_MSG_COUNT__ +}; + +/* Forward declarations */ +struct accum; +struct sdis_device; +struct sdis_estimator; +struct sdis_green_function; +struct sdis_scene; +struct ssp_rng; +struct ssp_rng_proxy; + +extern LOCAL_SYM res_T +create_per_thread_rng + (struct sdis_device* dev, + struct ssp_rng* rng_state, + struct ssp_rng_proxy** rng_proxy, + struct ssp_rng** rngs[]); + +extern LOCAL_SYM void +release_per_thread_rng + (struct sdis_device* dev, + struct ssp_rng* rngs[]); + +extern LOCAL_SYM res_T +create_per_thread_green_function + (struct sdis_scene* scene, + struct sdis_green_function** greens[]); + +extern LOCAL_SYM void +release_per_thread_green_function + (struct sdis_scene* scn, + struct sdis_green_function* greens[]); + +/* Allocate the progress status list for the current process. Without MPI, the + * length of the progress list is 1. With MPI, the length is also 1 except for + * the master process for which the length of the list is equal to the number + * of MPI processes. For this process the list will be used to gather the + * progress status of the other processes. */ +extern LOCAL_SYM res_T +alloc_process_progress + (struct sdis_device* dev, + int32_t* progress[]); + +extern LOCAL_SYM void +free_process_progress + (struct sdis_device* dev, + int32_t progress[]); + +/* Compute the number of realisations for the current process */ +extern LOCAL_SYM size_t +compute_process_realisations_count + (const struct sdis_device* dev, + const size_t overall_realisations_count); + +/* Gather the accumulators and sum them in acc. With MPI, non master processes + * store in acc the gathering of their per thread accumulators that are sent to + * the master process. The master process gathers the per thread accumulators + * and the per process ones and save the result in acc */ +extern LOCAL_SYM res_T +gather_accumulators + (struct sdis_device* dev, + const enum mpi_sdis_message msg, + const struct accum* per_thread_acc, + struct accum* acc); + +/* Gather the green functions. With MPI, non master processes store in green + * the gathering of their per thread green functions and sent the result to the + * master process. The master process gathers both per thread green functions + * and per process ones and finally save the result in green */ +extern LOCAL_SYM res_T +gather_green_functions + (struct sdis_scene* scn, + struct ssp_rng_proxy* proxy, + struct sdis_green_function* per_thread_green[], + const struct accum* acc_time, + struct sdis_green_function** green); + +/* Gather the sequence IDs of the proxy RNGs. Without MPI, nothing happens. + * With MPI, non-master processes send the sequence ID of their proxy RNG to + * the master process. The master process updates its proxy RNG to ensure that + * its state is greater than the state of all other proxies, that is, its + * sequence ID is greater than the sequence IDs received. */ +extern LOCAL_SYM res_T +gather_rng_proxy_sequence_id + (struct sdis_device* dev, + struct ssp_rng_proxy* proxy); + +/* Gather `res' from all other processes. Without MPI, the function simply + * returns `res'. With MPI, each process sends `res' to the other processes and + * retrieves the `res' sent by the other processes. The function then returns + * RES_OK if all the results collected are RES_OK. Otherwise, it returns the + * first error received. */ +extern LOCAL_SYM res_T +gather_res_T + (struct sdis_device* dev, + const res_T res); + +/* Print the progress status. With MPI, the master process print the progress + * of all processes stored in the progress list. Non master processes do not + * print anything */ +extern LOCAL_SYM void +print_progress + (struct sdis_device* dev, + int32_t progress[], + const char* label); /* Text preceding the progress status */ + +/* Update the printed progress status, i.e. rewind the printing and print the + * new status */ +extern LOCAL_SYM void +print_progress_update + (struct sdis_device* dev, + int32_t progress[], + const char* label); /* Text preceding the progress status */ + +/* Waiting for all processes. Without MPI this function does nothing. With MPI + * it waits for MPI process synchronisation */ +extern LOCAL_SYM void +process_barrier + (struct sdis_device* dev); + +#endif /* SDIS_C_H */ diff --git a/src/sdis_camera.c b/src/sdis_camera.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_camera.h b/src/sdis_camera.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_data.c b/src/sdis_data.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_device.c b/src/sdis_device.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -20,6 +20,7 @@ #include <rsys/cstr.h> #include <rsys/logger.h> #include <rsys/mem_allocator.h> +#include <rsys/mutex.h> #include <star/s2d.h> #include <star/s3d.h> @@ -27,9 +28,249 @@ #include <omp.h> +#ifdef SDIS_ENABLE_MPI + #include <mpi.h> +#endif + /******************************************************************************* * Helper functions ******************************************************************************/ +#ifdef SDIS_ENABLE_MPI + +static const char* +mpi_error_string(struct sdis_device* dev, const int mpi_err) +{ + int res_mpi = MPI_SUCCESS; + int len; + ASSERT(dev); + + res_mpi = MPI_Error_string(mpi_err, str_get(&dev->mpi_err_str), &len); + return res_mpi == MPI_SUCCESS + ? str_get(&dev->mpi_err_str) : "Invalid MPI error"; +} + +static const char* +mpi_thread_support_string(const int val) +{ + switch(val) { + case MPI_THREAD_SINGLE: return "MPI_THREAD_SINGLE"; + case MPI_THREAD_FUNNELED: return "MPI_THREAD_FUNNELED"; + case MPI_THREAD_SERIALIZED: return "MPI_THREAD_SERIALIZED"; + case MPI_THREAD_MULTIPLE: return "MPI_THREAD_MULTIPLE"; + default: FATAL("Unreachable code.\n"); break; + } +} + +static res_T +mpi_print_proc_info(struct sdis_device* dev) +{ + char proc_name[MPI_MAX_PROCESSOR_NAME]; + int proc_name_len; + char* proc_names = NULL; + uint32_t* proc_nthreads = NULL; + uint32_t nthreads = 0; + int iproc; + res_T res = RES_OK; + ASSERT(dev); + + /* On process 0, allocate the arrays to stored gathered data */ + if(dev->mpi_rank == 0) { + + /* Allocate the array to store the per process name */ + proc_names = MEM_CALLOC(dev->allocator, (size_t)dev->mpi_nprocs, + MPI_MAX_PROCESSOR_NAME*sizeof(*proc_names)); + if(!proc_names) { + res = RES_MEM_ERR; + log_err(dev, + "Could not allocate the temporary memory for MPI process names -- " + "%s.\n", res_to_cstr(res)); + goto error; + } + + /* Allocate the array to store the per process #threads */ + proc_nthreads = MEM_CALLOC(dev->allocator, (size_t)dev->mpi_nprocs, + sizeof(*proc_nthreads)); + if(!proc_nthreads) { + res = RES_MEM_ERR; + log_err(dev, + "Could not allocate the temporary memory for the #threads of the MPI " + "processes -- %s.\n", res_to_cstr(res)); + goto error; + } + } + + /* Gather the process name to the process 0 */ + MPI(Get_processor_name(proc_name, &proc_name_len)); + MPI(Gather(proc_name, MPI_MAX_PROCESSOR_NAME, MPI_CHAR, proc_names, + MPI_MAX_PROCESSOR_NAME, MPI_CHAR, 0, MPI_COMM_WORLD)); + + /* Gather the #threads to process 0*/ + nthreads = (uint32_t)dev->nthreads; + MPI(Gather(&nthreads, 1, MPI_UINT32_T, proc_nthreads, 1, MPI_UINT32_T, 0, + MPI_COMM_WORLD)); + + if(dev->mpi_rank == 0) { + FOR_EACH(iproc, 0, dev->mpi_nprocs) { + log_info(dev, "Process %d -- %s; #threads: %u\n", + iproc, proc_names + iproc*MPI_MAX_PROCESSOR_NAME, proc_nthreads[iproc]); + } + } + +exit: + if(proc_names) MEM_RM(dev->allocator, proc_names); + if(proc_nthreads) MEM_RM(dev->allocator, proc_nthreads); + return res; +error: + goto exit; +} + +static res_T +mpi_init(struct sdis_device* dev) +{ + int res_mpi = MPI_SUCCESS; + int is_init = 0; + int thread_support = 0; + res_T res = RES_OK; + ASSERT(dev); + + #define CALL_MPI(Func, ErrMsg) { \ + res_mpi = MPI_##Func; \ + if(res_mpi != MPI_SUCCESS) { \ + log_err(dev, ErrMsg" - %s\n", mpi_error_string(dev, res_mpi)); \ + res = RES_UNKNOWN_ERR; \ + goto error; \ + } \ + } (void)0 + + CALL_MPI(Initialized(&is_init), + "Error querying the MPI init state"); + + if(!is_init) { + log_err(dev, + "MPI is not initialized. The MPI_Init[_thread] function must be called " + "priorly to the creation of the Stardis device.\n"); + res = RES_BAD_OP; + goto error; + } + + CALL_MPI(Query_thread(&thread_support), + "Error querying the MPI thread support"); + + if(thread_support < MPI_THREAD_SERIALIZED) { + log_err(dev, + "The provided MPI implementation does not support serialized API calls " + "from multiple threads. The thread support is limited to %s.\n", + mpi_thread_support_string(thread_support)); + res = RES_BAD_OP; + goto error; + } + + CALL_MPI(Comm_rank(MPI_COMM_WORLD, &dev->mpi_rank), + "Error retrieving the MPI rank"); + CALL_MPI(Comm_size(MPI_COMM_WORLD, &dev->mpi_nprocs), + "Error retrieving the size of the MPI group"); + + #undef CALL_MPI + + dev->mpi_mutex = mutex_create(); + if(!dev->mpi_mutex) { + log_err(dev, + "Error creating the mutex used to protect the MPI calls.\n"); + res = RES_MEM_ERR; + goto error; + } + + mpi_print_proc_info(dev); + +exit: + return res; +error: + if(dev->mpi_mutex) { + mutex_destroy(dev->mpi_mutex); + dev->mpi_mutex = NULL; + } + goto exit; +} + +#endif /* SDIS_ENABLE_MPI */ + +static INLINE int +check_sdis_device_create_args(const struct sdis_device_create_args* args) +{ + return args && args->nthreads_hint != 0; +} + +static INLINE res_T +setup_logger + (struct sdis_device* dev, + const struct sdis_device_create_args* args) +{ + ASSERT(dev && args); + if(args->logger) { + dev->logger = args->logger; + } else { + setup_log_default(dev); + } + return RES_OK; +} + +static INLINE res_T +setup_star2d(struct sdis_device* dev) +{ + res_T res = RES_OK; + ASSERT(dev); + res = s2d_device_create(dev->logger, dev->allocator, 0, &dev->s2d_dev); + if(res != RES_OK) { + log_err(dev, + "Could not create the Star-2D device for Stardis-Solver -- %s.\n", + res_to_cstr(res)); + goto error; + } +exit: + return res; +error: + goto exit; +} + +static INLINE res_T +setup_star3d(struct sdis_device* dev) +{ + res_T res = RES_OK; + ASSERT(dev); + res = s3d_device_create(dev->logger, dev->allocator, 0, &dev->s3d_dev); + if(res != RES_OK) { + log_err(dev, + "Could not create the Star-3D device for Stardis-Solver -- %s.\n", + res_to_cstr(res)); + goto error; + } +exit: + return res; +error: + goto exit; +} + +static INLINE res_T +setup_mpi(struct sdis_device* dev, const struct sdis_device_create_args* args) +{ + ASSERT(dev && args); +#ifdef SDIS_ENABLE_MPI + dev->use_mpi = args->use_mpi; + if(args->use_mpi) { + const res_T res = mpi_init(dev); + if(res != RES_OK) return res; + } +#else + if(args->use_mpi) { + log_warn(dev, + "Stardis-Solver is built without the support of the Message Passing " + "Interface. MPI cannot be used for parallel computations.\n"); + } +#endif + return RES_OK; + +} + static void device_release(ref_T* ref) { @@ -43,6 +284,10 @@ device_release(ref_T* ref) ASSERT(flist_name_is_empty(&dev->media_names)); flist_name_release(&dev->interfaces_names); flist_name_release(&dev->media_names); +#ifdef SDIS_ENABLE_MPI + if(dev->mpi_mutex) mutex_destroy(dev->mpi_mutex); + str_release(&dev->mpi_err_str); +#endif MEM_RM(dev->allocator, dev); } @@ -51,29 +296,26 @@ device_release(ref_T* ref) ******************************************************************************/ res_T sdis_device_create - (struct logger* logger, - struct mem_allocator* mem_allocator, - const unsigned nthreads_hint, - const int verbose, + (const struct sdis_device_create_args* args, struct sdis_device** out_dev) { - struct logger* log = NULL; struct sdis_device* dev = NULL; struct mem_allocator* allocator = NULL; + unsigned nthreads_max = 0; res_T res = RES_OK; - if(nthreads_hint == 0 || !out_dev) { + if(!check_sdis_device_create_args(args) || !out_dev) { res = RES_BAD_ARG; goto error; } - allocator = mem_allocator ? mem_allocator : &mem_default_allocator; + allocator = args->allocator ? args->allocator : &mem_default_allocator; dev = MEM_CALLOC(allocator, 1, sizeof(struct sdis_device)); if(!dev) { - if(verbose) { + if(args->verbosity) { #define ERR_STR STR(FUNC_NAME)": could not allocate the Stardis device -- %s." - if(logger) { - logger_print(logger, LOG_ERROR, ERR_STR, res_to_cstr(res)); + if(args->logger) { + logger_print(args->logger, LOG_ERROR, ERR_STR, res_to_cstr(res)); } else { fprintf(stderr, MSG_ERROR_PREFIX ERR_STR, res_to_cstr(res)); } @@ -82,44 +324,34 @@ sdis_device_create res = RES_MEM_ERR; goto error; } + nthreads_max = (unsigned)MMAX(omp_get_max_threads(), omp_get_num_procs()); dev->allocator = allocator; - dev->verbose = verbose; - dev->nthreads = MMIN(nthreads_hint, (unsigned)omp_get_num_procs()); + dev->verbose = args->verbosity; + dev->nthreads = MMIN(args->nthreads_hint, nthreads_max); ref_init(&dev->ref); flist_name_init(allocator, &dev->interfaces_names); flist_name_init(allocator, &dev->media_names); +#ifdef SDIS_ENABLE_MPI + str_init(allocator, &dev->mpi_err_str); +#endif + + res = setup_logger(dev, args); + if(res != RES_OK) goto error; + res = setup_star2d(dev); + if(res != RES_OK) goto error; + res = setup_star3d(dev); + if(res != RES_OK) goto error; + res = setup_mpi(dev, args); + if(res != RES_OK) goto error; - if(logger) { - dev->logger = logger; - } else { - setup_log_default(dev); - } log_info(dev, "Use %lu %s.\n", (unsigned long)dev->nthreads, dev->nthreads == 1 ? "thread" : "threads"); - res = s2d_device_create(log, allocator, 0, &dev->s2d_dev); - if(res != RES_OK) { - log_err(dev, - "%s: could not create the Star-2D device on Stardis -- %s.\n", - FUNC_NAME, res_to_cstr(res)); - } - - res = s3d_device_create(log, allocator, 0, &dev->s3d_dev); - if(res != RES_OK) { - log_err(dev, - "%s: could not create the Star-3D device on Stardis -- %s.\n", - FUNC_NAME, res_to_cstr(res)); - goto error; - } - exit: if(out_dev) *out_dev = dev; return res; error: - if(dev) { - SDIS(device_ref_put(dev)); - dev = NULL; - } + if(dev) { SDIS(device_ref_put(dev)); dev = NULL; } goto exit; } @@ -139,6 +371,21 @@ sdis_device_ref_put(struct sdis_device* dev) return RES_OK; } +res_T +sdis_device_get_mpi_rank(struct sdis_device* dev, int* rank) +{ +#ifndef SDIS_ENABLE_MPI + (void)dev, (void)rank; + return RES_BAD_OP; +#else + if(!dev || !rank) return RES_BAD_ARG; + if(!dev->use_mpi) return RES_BAD_OP; + ASSERT(dev->mpi_rank >= 0); + *rank = dev->mpi_rank; + return RES_OK; +#endif +} + /******************************************************************************* * Local functions ******************************************************************************/ diff --git a/src/sdis_device_c.h b/src/sdis_device_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -22,8 +22,18 @@ #include <rsys/free_list.h> #include <rsys/logger.h> #include <rsys/ref_count.h> +#include <rsys/str.h> + +#ifdef SDIS_ENABLE_MPI + #ifndef NDEBUG + #define MPI(Func) ASSERT(MPI_##Func == MPI_SUCCESS) + #else + #define MPI(Func) MPI_##Func + #endif +#endif /* Forward declarations */ +struct mutex; struct ssp_rng; struct ssp_rng_proxy; @@ -38,6 +48,15 @@ struct sdis_device { unsigned nthreads; int verbose; +#ifdef SDIS_ENABLE_MPI + int mpi_rank; /* Rank of the process in the MPI group */ + int mpi_nprocs; /* Overall #processes in the MPI group */ + struct str mpi_err_str; /* String used to store the MPI error string */ + + struct mutex* mpi_mutex; /* Protect MPI calls from concurrent threads */ + int use_mpi; +#endif + struct flist_name interfaces_names; struct flist_name media_names; diff --git a/src/sdis_estimator.c b/src/sdis_estimator.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_estimator_buffer.c b/src/sdis_estimator_buffer.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -22,7 +22,7 @@ #include <star/ssp.h> struct sdis_estimator_buffer { - struct sdis_estimator** estimators; /* Row major per pixe lestimators */ + struct sdis_estimator** estimators; /* Row major per pixel estimators */ size_t width; size_t height; diff --git a/src/sdis_estimator_buffer_c.h b/src/sdis_estimator_buffer_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_estimator_c.h b/src/sdis_estimator_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_green.c b/src/sdis_green.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_green.h b/src/sdis_green.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_heat_path.c b/src/sdis_heat_path.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_heat_path.h b/src/sdis_heat_path.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_heat_path_boundary.c b/src/sdis_heat_path_boundary.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_heat_path_boundary_Xd.h b/src/sdis_heat_path_boundary_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_heat_path_boundary_Xd_c.h b/src/sdis_heat_path_boundary_Xd_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_heat_path_boundary_Xd_fixed_flux.h b/src/sdis_heat_path_boundary_Xd_fixed_flux.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_heat_path_boundary_Xd_solid_fluid_picard1.h b/src/sdis_heat_path_boundary_Xd_solid_fluid_picard1.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -231,8 +231,8 @@ XD(solid_fluid_boundary_picard1_path) double p_radi; /* Radiative proba */ /* Indices of the registered vertex of the sampled radiative path */ - size_t ihvtx_radi_begin; - size_t ihvtx_radi_end; + size_t ihvtx_radi_begin = 0; + size_t ihvtx_radi_end = 0; r = ssp_rng_canonical(rng); diff --git a/src/sdis_heat_path_boundary_Xd_solid_fluid_picardN.h b/src/sdis_heat_path_boundary_Xd_solid_fluid_picardN.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -214,8 +214,8 @@ XD(solid_fluid_boundary_picardN_path) double T0, T1, T2, T3, T4, T5; /* Computed temperatures */ /* Indices of the registered vertex of the sampled radiative path */ - size_t ihvtx_radi_begin; - size_t ihvtx_radi_end; + size_t ihvtx_radi_begin = 0; + size_t ihvtx_radi_end = 0; r = ssp_rng_canonical(rng); diff --git a/src/sdis_heat_path_boundary_Xd_solid_solid.h b/src/sdis_heat_path_boundary_Xd_solid_solid.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_heat_path_boundary_c.h b/src/sdis_heat_path_boundary_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_heat_path_conductive_Xd.h b/src/sdis_heat_path_conductive_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_heat_path_convective_Xd.h b/src/sdis_heat_path_convective_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_heat_path_radiative_Xd.h b/src/sdis_heat_path_radiative_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_interface.c b/src/sdis_interface.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -53,7 +53,7 @@ check_interface_shader && shader->convection_coef) { log_warn(dev, "%s: a solid/solid interface can't have a convection coefficient. The " - " shader's pointer function for this attribute should be NULL.\n", + "shader's pointer function for this attribute should be NULL.\n", caller_name); } if(shader->convection_coef_upper_bound < 0) { @@ -67,7 +67,7 @@ check_interface_shader && shader->thermal_contact_resistance) { log_warn(dev, "%s: only solid/solid interface can have a thermal contact resistance. The " - " shader's pointer function for this attribute should be NULL.\n", + "shader's pointer function for this attribute should be NULL.\n", caller_name); } diff --git a/src/sdis_interface_c.h b/src/sdis_interface_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_log.c b/src/sdis_log.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -90,9 +90,15 @@ log_info(const struct sdis_device* dev, const char* msg, ...) va_list vargs_list; ASSERT(dev && msg); - va_start(vargs_list, msg); - log_msg(dev, LOG_OUTPUT, msg, vargs_list); - va_end(vargs_list); +#ifdef SDIS_ENABLE_MPI + /* Log standard messages only on master process */ + if(dev->mpi_rank == 0) +#endif + { + va_start(vargs_list, msg); + log_msg(dev, LOG_OUTPUT, msg, vargs_list); + va_end(vargs_list); + } } void @@ -101,9 +107,15 @@ log_err(const struct sdis_device* dev, const char* msg, ...) va_list vargs_list; ASSERT(dev && msg); - va_start(vargs_list, msg); - log_msg(dev, LOG_ERROR, msg, vargs_list); - va_end(vargs_list); +#ifdef SDIS_ENABLE_MPI + /* Log error messages only on master process */ + if(dev->mpi_rank == 0) +#endif + { + va_start(vargs_list, msg); + log_msg(dev, LOG_ERROR, msg, vargs_list); + va_end(vargs_list); + } } void @@ -112,8 +124,14 @@ log_warn(const struct sdis_device* dev, const char* msg, ...) va_list vargs_list; ASSERT(dev && msg); - va_start(vargs_list, msg); - log_msg(dev, LOG_WARNING, msg, vargs_list); - va_end(vargs_list); +#ifdef SDIS_ENABLE_MPI + /* Log warnings only on master process */ + if(dev->mpi_rank == 0) +#endif + { + va_start(vargs_list, msg); + log_msg(dev, LOG_WARNING, msg, vargs_list); + va_end(vargs_list); + } } diff --git a/src/sdis_log.h b/src/sdis_log.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_medium.c b/src/sdis_medium.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_medium_c.h b/src/sdis_medium_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_misc.c b/src/sdis_misc.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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,3 +21,51 @@ #define SDIS_XD_DIMENSION 3 #include "sdis_misc_Xd.h" +res_T +check_primitive_uv_2d(struct sdis_device* dev, const double param_coord[]) +{ + double u; + res_T res = RES_OK; + ASSERT(dev && param_coord); + + u = param_coord[0]; + + if(u < 0 || 1 < u) { + log_err(dev, + "%s: invalid parametric coordinates u=%g; it must be in [0, 1].\n", + FUNC_NAME, u); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +res_T +check_primitive_uv_3d(struct sdis_device* dev, const double param_coords[]) +{ + double u, v, w; + res_T res = RES_OK; + ASSERT(dev && param_coords); + + u = param_coords[0]; + v = param_coords[1]; + w = CLAMP(1 - u - v, 0, 1); + + if(u < 0 || 1 < u || v < 0 || 1 < v || !eq_eps(u + v + w, 1, 1.e-6)) { + log_err(dev, + "%s: invalid parametric coordinates u=%g; v=%g. " + "u + v + (1-u-v) must be equal to 1 with u and v in [0, 1].\n", + FUNC_NAME, u, v); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + goto exit; +} diff --git a/src/sdis_misc.h b/src/sdis_misc.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -16,6 +16,8 @@ #ifndef SDIS_MISC_H #define SDIS_MISC_H +#include "sdis_heat_path.h" + #include <rsys/float2.h> #include <rsys/float3.h> #include <star/ssp.h> @@ -163,4 +165,19 @@ time_rewind_3d struct rwalk_3d* rwalk, struct temperature_3d* T); +/* Check the validity of the parametric coordinate onto a 2D primitive. If it + * is invalid, the function prints an error message and return RES_BAD_ARG. */ +extern LOCAL_SYM res_T +check_primitive_uv_2d + (struct sdis_device* dev, + const double u[]); + +/* Check the validity of the parametric coordinates onto a 3D primitive. If + * they are invalid, the function prints an error message and return + * RES_BAD_ARG. */ +extern LOCAL_SYM res_T +check_primitive_uv_3d + (struct sdis_device* dev, + const double uv[]); + #endif /* SDIS_MISC_H */ diff --git a/src/sdis_misc_Xd.h b/src/sdis_misc_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_mpi.c b/src/sdis_mpi.c @@ -0,0 +1,138 @@ +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "sdis_c.h" +#include "sdis_device_c.h" +#include "sdis_mpi.h" + +#include <rsys/mutex.h> + +#include <time.h> /* nanosleep */ +#include <sys/time.h> /* struct timespec */ + +/******************************************************************************* + * Local functions + ******************************************************************************/ +void +mpi_send_progress(struct sdis_device* dev, const int32_t progress) +{ + ASSERT(dev && dev->use_mpi); + (void)dev; + + mutex_lock(dev->mpi_mutex); + MPI(Send(&progress, 1/*#data*/, MPI_INT32_T, 0/*dst rank*/, + MPI_SDIS_MSG_PROGRESS, MPI_COMM_WORLD)); + mutex_unlock(dev->mpi_mutex); +} + +void +mpi_fetch_progress(struct sdis_device* dev, int32_t progress[]) +{ + int iproc; + ASSERT(dev && dev->use_mpi && dev->mpi_rank == 0); + + FOR_EACH(iproc, 1, dev->mpi_nprocs) { + /* Flush all progress messages sent by the process `iproc' */ + for(;;) { + MPI_Request req; + int flag; + + /* Query for a progress message */ + mutex_lock(dev->mpi_mutex); + MPI(Iprobe(iproc, MPI_SDIS_MSG_PROGRESS, MPI_COMM_WORLD, &flag, + MPI_STATUS_IGNORE)); + mutex_unlock(dev->mpi_mutex); + + if(flag == 0) break; /* No more progress status to receive */ + + /* Asynchronously receive the progress status */ + mutex_lock(dev->mpi_mutex); + MPI(Irecv(&progress[iproc], 1/*count*/, MPI_INT32_T, iproc, + MPI_SDIS_MSG_PROGRESS, MPI_COMM_WORLD, &req)); + mutex_unlock(dev->mpi_mutex); + + /* Actively wait for the completion of the receive procedure */ + mpi_waiting_for_request(dev, &req); + } + } +} + +void +mpi_waiting_for_request(struct sdis_device* dev, MPI_Request* req) +{ + struct timespec t; + ASSERT(dev && dev->use_mpi && req); + + /* Setup the suspend time of the process while waiting for a request */ + t.tv_sec = 0; + t.tv_nsec = 10000000; /* 10ms */ + + /* Wait for process synchronisation */ + for(;;) { + int complete; + + mutex_lock(dev->mpi_mutex); + MPI(Test(req, &complete, MPI_STATUS_IGNORE)); + mutex_unlock(dev->mpi_mutex); + + if(complete) break; + nanosleep(&t, NULL); + } +} + +void +mpi_waiting_for_message + (struct sdis_device* dev, + const int iproc, + const enum mpi_sdis_message msg, + MPI_Status* status) +{ + struct timespec t; + ASSERT(dev && dev->use_mpi && status); + + /* Setup the suspend time of the process while waiting for a message */ + t.tv_sec = 0; + t.tv_nsec = 10000000; /* 10ms */ + + /* Wait for process synchronisation */ + for(;;) { + int flag; + + /* Asynchronously probe for green function data */ + mutex_lock(dev->mpi_mutex); + MPI(Iprobe(iproc, msg, MPI_COMM_WORLD, &flag, status)); + mutex_unlock(dev->mpi_mutex); + + if(flag) break; + nanosleep(&t, NULL); + } +} + +void +mpi_barrier(struct sdis_device* dev) +{ + MPI_Request req; + ASSERT(dev && dev->use_mpi); + + /* Asynchronously wait for process completion. Use an asynchronous barrier to + * avoid a dead lock if another thread on the same process queries the + * mpi_mutex */ + + mutex_lock(dev->mpi_mutex); + MPI(Ibarrier(MPI_COMM_WORLD, &req)); + mutex_unlock(dev->mpi_mutex); + + mpi_waiting_for_request(dev, &req); +} diff --git a/src/sdis_mpi.h b/src/sdis_mpi.h @@ -0,0 +1,64 @@ +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) + * + * 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 SDIS_MPI_H +#define SDIS_MPI_H + +#ifndef SDIS_ENABLE_MPI + #error "Invalid inclusion. Stardis-Solver is compiled without MPI support" +#endif + +#include "sdis_c.h" + +#include <rsys/rsys.h> +#include <mpi.h> + +/* Forward declarations */ +struct sdis_device; + +/* Send the progress status `progress' to the master process */ +extern LOCAL_SYM void +mpi_send_progress + (struct sdis_device* dev, + const int32_t progress); + +/* Fetch the progress status of non master processes into `progress'. The + * progress of the i-th process is stored in progress[i], meaning that the + * length of progress must be at least equal to the number of MPI processes */ +extern LOCAL_SYM void +mpi_fetch_progress + (struct sdis_device* dev, + int32_t progress[]); + +/* Actively wait for the completion of the MPI request `req' */ +extern LOCAL_SYM void +mpi_waiting_for_request + (struct sdis_device* dev, + MPI_Request* req); + +/* Actively wait for a message from the process iproc */ +extern LOCAL_SYM void +mpi_waiting_for_message + (struct sdis_device* dev, + const int iproc, + const enum mpi_sdis_message msg, + MPI_Status* status); + +/* Waiting for all processes */ +extern LOCAL_SYM void +mpi_barrier + (struct sdis_device* dev); + +#endif /* SDIS_MPI_H */ diff --git a/src/sdis_realisation.c b/src/sdis_realisation.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_realisation.h b/src/sdis_realisation.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_realisation_Xd.h b/src/sdis_realisation_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -49,7 +49,7 @@ check_boundary_realisation_args(const struct boundary_realisation_args* args) && args->rng && args->uv[0] >= 0 && args->uv[0] <= 1 - && args->uv[1] >= 0 + && args->uv[1] >= 0 && args->uv[1] <= 1 && args->time >= 0 && args->picard_order > 0 @@ -64,7 +64,7 @@ check_boundary_flux_realisation_args && args->rng && args->uv[0] >= 0 && args->uv[0] <= 1 - && args->uv[1] >= 0 + && args->uv[1] >= 0 && args->uv[1] <= 1 && args->time >= 0 && args->picard_order > 0 diff --git a/src/sdis_scene.c b/src/sdis_scene.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -542,3 +542,63 @@ exit: error: goto exit; } + +res_T +scene_check_primitive_index(const struct sdis_scene* scn, const size_t iprim) +{ + res_T res = RES_OK; + ASSERT(scn); + + if(iprim >= scene_get_primitives_count(scn)) { + log_err(scn->dev, + "%s: invalid primitive identifier `%lu'. " + "It must be in the [0 %lu] range.\n", + FUNC_NAME, + (unsigned long)iprim, + (unsigned long)scene_get_primitives_count(scn)-1); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +res_T +scene_check_dimensionality_2d(const struct sdis_scene* scn) +{ + res_T res = RES_OK; + ASSERT(scn); + if(scene_is_2d(scn) == 0) { + log_err(scn->dev, + "%s: expects a 2D scene while the input scene is 3D.\n", + FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } +exit: + return res; +error: + goto exit; +} + +res_T +scene_check_dimensionality_3d(const struct sdis_scene* scn) +{ + res_T res = RES_OK; + ASSERT(scn); + if(scene_is_2d(scn) != 0) { + log_err(scn->dev, + "%s: expects a 3D scene while the input scene is 2D.\n", + FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } +exit: + return res; +error: + goto exit; +} + diff --git a/src/sdis_scene_Xd.h b/src/sdis_scene_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/sdis_scene_c.h b/src/sdis_scene_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -243,7 +243,7 @@ scene_get_medium * time consuming. * * Note that actually, the function internally calls scene_get_medium if no - * valid medium is found with the regular procedure. This may be due to + * valid medium is found with the regular procedure. This may be due to * numerical issues or wrong assumptions on the current medium (its boundaries * are opened to infinity). */ extern LOCAL_SYM res_T @@ -257,6 +257,25 @@ scene_compute_hash (const struct sdis_scene* scn, hash256_T hash); +/* Check that the primitive identifier is valid wrt the scene. If not, the + * function prints an error message and returns RES_BAD_ARG. */ +extern LOCAL_SYM res_T +scene_check_primitive_index + (const struct sdis_scene* scn, + const size_t iprim); + +/* Check that the scene is 2D. If not, the function prints an error message and + * returns RES_BAD_ARG */ +extern LOCAL_SYM res_T +scene_check_dimensionality_2d + (const struct sdis_scene* scn); + +/* Check that the scene is 3D. If not, the function prints an error message and + * returns RES_BAD_ARG */ +extern LOCAL_SYM res_T +scene_check_dimensionality_3d + (const struct sdis_scene* scn); + static INLINE void scene_get_enclosure_ids (const struct sdis_scene* scn, @@ -290,7 +309,7 @@ scene_get_enclosure(struct sdis_scene* scn, const unsigned ienc) return enc; } -static FINLINE int +static INLINE int scene_is_2d(const struct sdis_scene* scn) { ASSERT(scn && (scn->s2d_view || scn->s3d_view)); diff --git a/src/sdis_solve.c b/src/sdis_solve.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -14,14 +14,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "sdis.h" -#include "sdis_camera.h" -#include "sdis_device_c.h" -#include "sdis_estimator_c.h" -#include "sdis_estimator_buffer_c.h" -#include "sdis_interface_c.h" - -#include <star/ssp.h> -#include <omp.h> /* Generate the probe solvers */ #define SDIS_XD_DIMENSION 2 @@ -48,179 +40,6 @@ #include "sdis_solve_medium_Xd.h" /******************************************************************************* - * Helper functions - ******************************************************************************/ -static FINLINE uint16_t -morton2D_decode(const uint32_t u32) -{ - uint32_t x = u32 & 0x55555555; - x = (x | (x >> 1)) & 0x33333333; - x = (x | (x >> 2)) & 0x0F0F0F0F; - x = (x | (x >> 4)) & 0x00FF00FF; - x = (x | (x >> 8)) & 0x0000FFFF; - return (uint16_t)x; -} - -static res_T -solve_pixel - (struct sdis_scene* scn, - struct ssp_rng* rng, - struct sdis_medium* mdm, - const struct sdis_camera* cam, - const double time_range[2], /* Observation time */ - const size_t ipix[2], /* Pixel coordinate in the image plane */ - const size_t nrealisations, - const int register_paths, /* Combination of enum sdis_heat_path_flag */ - const double pix_sz[2], /* Pixel size in the normalized image plane */ - const size_t picard_order, - struct sdis_estimator* estimator) -{ - struct accum acc_temp = ACCUM_NULL; - struct accum acc_time = ACCUM_NULL; - struct sdis_heat_path* pheat_path = NULL; - size_t irealisation; - res_T res = RES_OK; - ASSERT(scn && mdm && rng && cam && ipix && nrealisations); - ASSERT(pix_sz && pix_sz[0] > 0 && pix_sz[1] > 0); - ASSERT(estimator && time_range); - - FOR_EACH(irealisation, 0, nrealisations) { - struct ray_realisation_args realis_args = RAY_REALISATION_ARGS_NULL; - struct time t0, t1; - double samp[2]; /* Pixel sample */ - double ray_pos[3]; - double ray_dir[3]; - double w = 0; - struct sdis_heat_path heat_path; - double time; - res_T res_simul = RES_OK; - - /* Begin time registration */ - time_current(&t0); - - time = sample_time(rng, time_range); - if(register_paths) { - heat_path_init(scn->dev->allocator, &heat_path); - pheat_path = &heat_path; - } - - /* Generate a sample into the pixel to estimate */ - samp[0] = ((double)ipix[0] + ssp_rng_canonical(rng)) * pix_sz[0]; - samp[1] = ((double)ipix[1] + ssp_rng_canonical(rng)) * pix_sz[1]; - - /* Generate a ray starting from the camera position and passing through - * pixel sample */ - camera_ray(cam, samp, ray_pos, ray_dir); - - /* Launch the realisation */ - realis_args.rng = rng; - realis_args.medium = mdm; - realis_args.time = time; - realis_args.picard_order = picard_order; - realis_args.heat_path = pheat_path; - d3_set(realis_args.position, ray_pos); - d3_set(realis_args.direction, ray_dir); - res_simul = ray_realisation_3d(scn, &realis_args, &w); - - /* Handle fatal error */ - if(res_simul != RES_OK && res_simul != RES_BAD_OP) { - res = res_simul; - goto error; - } - - if(pheat_path) { - pheat_path->status = res_simul == RES_OK - ? SDIS_HEAT_PATH_SUCCESS - : SDIS_HEAT_PATH_FAILURE; - - /* Check if the path must be saved regarding the register_paths mask */ - if(!(register_paths & (int)pheat_path->status)) { - heat_path_release(pheat_path); - pheat_path = NULL; - } else { /* Register the sampled path */ - res = estimator_add_and_release_heat_path(estimator, pheat_path); - if(res != RES_OK) goto error; - pheat_path = NULL; - } - } - - /* Stop time registration */ - time_sub(&t0, time_current(&t1), &t0); - - if(res_simul == RES_OK) { - /* Update global accumulators */ - const double usec = (double)time_val(&t0, TIME_NSEC) * 0.001; - acc_temp.sum += w; acc_temp.sum2 += w*w; ++acc_temp.count; - acc_time.sum += usec; acc_time.sum2 += usec*usec; ++acc_time.count; - } - } - - /* Setup the pixel estimator */ - ASSERT(acc_temp.count == acc_time.count); - estimator_setup_realisations_count(estimator, nrealisations, acc_temp.count); - estimator_setup_temperature(estimator, acc_temp.sum, acc_temp.sum2); - estimator_setup_realisation_time(estimator, acc_time.sum, acc_time.sum2); - -exit: - if(pheat_path) heat_path_release(pheat_path); - return res; -error: - goto exit; -} - -static res_T -solve_tile - (struct sdis_scene* scn, - struct ssp_rng* rng, - struct sdis_medium* mdm, - const struct sdis_camera* cam, - const double time_range[2], - const size_t origin[2], /* Tile origin in image plane */ - const size_t size[2], /* #pixels in X and Y */ - const size_t spp, /* #samples per pixel */ - const int register_paths, /* Combination of enum sdis_heat_path_flag */ - const double pix_sz[2], /* Pixel size in the normalized image plane */ - const size_t picard_order, - struct sdis_estimator_buffer* buf) -{ - size_t mcode; /* Morton code of the tile pixel */ - size_t npixels; - res_T res = RES_OK; - ASSERT(scn && rng && mdm && cam && spp && origin); - ASSERT(size &&size[0] && size[1] && buf); - ASSERT(pix_sz && pix_sz[0] > 0 && pix_sz[1] > 0 && time_range); - - /* Adjust the #pixels to process them wrt a morton order */ - npixels = round_up_pow2(MMAX(size[0], size[1])); - npixels *= npixels; - - FOR_EACH(mcode, 0, npixels) { - size_t ipix[2]; - struct sdis_estimator* estimator; - - ipix[0] = morton2D_decode((uint32_t)(mcode>>0)); - if(ipix[0] >= size[0]) continue; - ipix[1] = morton2D_decode((uint32_t)(mcode>>1)); - if(ipix[1] >= size[1]) continue; - - ipix[0] = ipix[0] + origin[0]; - ipix[1] = ipix[1] + origin[1]; - - /* Fetch the pixel estimator */ - estimator = estimator_buffer_grab(buf, ipix[0], ipix[1]); - - res = solve_pixel(scn, rng, mdm, cam, time_range, - ipix, spp, register_paths, pix_sz, picard_order, estimator); - if(res != RES_OK) goto error; - } - -exit: - return res; -error: - goto exit; -} - -/******************************************************************************* * Exported functions ******************************************************************************/ res_T @@ -336,182 +155,6 @@ sdis_solve_boundary_flux } res_T -sdis_solve_camera - (struct sdis_scene* scn, - const struct sdis_solve_camera_args* args, - struct sdis_estimator_buffer** out_buf) -{ - #define TILE_SIZE 32 /* definition in X & Y of a tile */ - STATIC_ASSERT(IS_POW2(TILE_SIZE), TILE_SIZE_must_be_a_power_of_2); - - struct sdis_estimator_buffer* buf = NULL; - struct sdis_medium* medium = NULL; - struct accum acc_temp = ACCUM_NULL; - struct accum acc_time = ACCUM_NULL; - struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - size_t ntiles_x, ntiles_y, ntiles; - double pix_sz[2]; /* Size of a pixel in the normalized image plane */ - int64_t mcode; /* Morton code of a tile */ - size_t nrealisations; - size_t nsuccesses; - size_t ix, iy; - size_t i; - int progress = 0; - ATOMIC nsolved_tiles = 0; - ATOMIC res = RES_OK; - - if(!scn - || !args - || !out_buf - || !args->cam - || !args->image_resolution[0] - || !args->image_resolution[1] - || !args->spp - || args->time_range[0] < 0 - || args->time_range[1] < args->time_range[0] - || ( args->time_range[1] > DBL_MAX - && args->time_range[0] != args->time_range[1])) { - res = RES_BAD_ARG; - goto error; - } - - if(scene_is_2d(scn)) { - log_err(scn->dev, "%s: 2D scene are not supported.\n", FUNC_NAME); - goto error; - } - - /* Retrieve the medium in which the submitted position lies */ - res = scene_get_medium(scn, args->cam->position, NULL, &medium); - if(res != RES_OK) goto error; - - if(medium->type != SDIS_FLUID) { - log_err(scn->dev, "%s: the camera position `%g %g %g' is not in a fluid.\n", - FUNC_NAME, SPLIT3(args->cam->position)); - res = RES_BAD_ARG; - goto error; - } - - /* Create the proxy RNG */ - res = ssp_rng_proxy_create(scn->dev->allocator, SSP_RNG_MT19937_64, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - - /* Create the per thread RNG */ - rngs = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(struct ssp_rng*)); - if(!rngs) { - res = RES_MEM_ERR; - goto error; - } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs+i); - if(res != RES_OK) goto error; - } - - ntiles_x = (args->image_resolution[0] + (TILE_SIZE-1)/*ceil*/)/TILE_SIZE; - ntiles_y = (args->image_resolution[1] + (TILE_SIZE-1)/*ceil*/)/TILE_SIZE; - ntiles = round_up_pow2(MMAX(ntiles_x, ntiles_y)); - ntiles *= ntiles; - - pix_sz[0] = 1.0 / (double)args->image_resolution[0]; - pix_sz[1] = 1.0 / (double)args->image_resolution[1]; - - /* Create the global estimator */ - res = estimator_buffer_create - (scn->dev, args->image_resolution[0], args->image_resolution[1], &buf); - if(res != RES_OK) goto error; - - omp_set_num_threads((int)scn->dev->nthreads); - #pragma omp parallel for schedule(static, 1/*chunk size*/) - for(mcode = 0; mcode < (int64_t)ntiles; ++mcode) { - size_t tile_org[2] = {0, 0}; - size_t tile_sz[2] = {0, 0}; - const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - size_t n; - int pcent; - res_T res_local = RES_OK; - - if(ATOMIC_GET(&res) != RES_OK) continue; - - tile_org[0] = morton2D_decode((uint32_t)(mcode>>0)); - if(tile_org[0] >= ntiles_x) continue; /* Discard tile */ - tile_org[1] = morton2D_decode((uint32_t)(mcode>>1)); - if(tile_org[1] >= ntiles_y) continue; /* Disaard tile */ - - /* Setup the tile coordinates in the image plane */ - tile_org[0] *= TILE_SIZE; - tile_org[1] *= TILE_SIZE; - tile_sz[0] = MMIN(TILE_SIZE, args->image_resolution[0] - tile_org[0]); - tile_sz[1] = MMIN(TILE_SIZE, args->image_resolution[1] - tile_org[1]); - - /* Draw the tile */ - res_local = solve_tile(scn, rng, medium, args->cam, args->time_range, - tile_org, tile_sz, args->spp, args->register_paths, pix_sz, - args->picard_order, buf); - if(res_local != RES_OK) { - ATOMIC_SET(&res, res_local); - continue; - } - - /* Update progress */ - n = (size_t)ATOMIC_INCR(&nsolved_tiles); - pcent = (int)((double)n*100.0 / (double)(ntiles_x*ntiles_y) + 0.5/*round*/); - #pragma omp critical - if(pcent > progress) { - progress = pcent; - log_info(scn->dev, "Infrared rendering: %3d%%\r", progress); - } - } - if(res != RES_OK) goto error; - - /* Add a new line after the progress status */ - log_info(scn->dev, "Infrared rendering: %3d%%\n", progress); - - /* Setup the accumulators of the whole estimator buffer */ - acc_temp = ACCUM_NULL; - acc_time = ACCUM_NULL; - nsuccesses = 0; - FOR_EACH(iy, 0, args->image_resolution[1]) { - FOR_EACH(ix, 0, args->image_resolution[0]) { - const struct sdis_estimator* estimator; - SDIS(estimator_buffer_at(buf, ix, iy, &estimator)); - acc_temp.sum += estimator->temperature.sum; - acc_temp.sum2 += estimator->temperature.sum2; - acc_temp.count += estimator->temperature.count; - acc_time.sum += estimator->realisation_time.sum; - acc_time.sum2 += estimator->realisation_time.sum2; - acc_time.count += estimator->realisation_time.count; - nsuccesses += estimator->nrealisations; - } - } - - nrealisations = args->image_resolution[0]*args->image_resolution[1]*args->spp; - ASSERT(acc_temp.count == acc_time.count); - ASSERT(acc_temp.count == nsuccesses); - estimator_buffer_setup_realisations_count(buf, nrealisations, nsuccesses); - estimator_buffer_setup_temperature(buf, acc_temp.sum, acc_temp.sum2); - estimator_buffer_setup_realisation_time(buf, acc_time.sum, acc_time.sum2); - res = estimator_buffer_save_rng_state(buf, rng_proxy); - if(res != RES_OK) goto error; - -exit: - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(rngs[i]) SSP(rng_ref_put(rngs[i])); - } - MEM_RM(scn->dev->allocator, rngs); - } - if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); - if(out_buf) *out_buf = buf; - return (res_T)res; -error: - if(buf) SDIS(estimator_buffer_ref_put(buf)); - goto exit; -} - -res_T sdis_solve_medium (struct sdis_scene* scn, const struct sdis_solve_medium_args* args, diff --git a/src/sdis_solve_boundary_Xd.h b/src/sdis_solve_boundary_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -13,16 +13,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "sdis_c.h" #include "sdis_device_c.h" #include "sdis_estimator_c.h" #include "sdis_interface_c.h" +#include "sdis_log.h" +#include "sdis_green.h" #include "sdis_medium_c.h" #include "sdis_misc.h" #include "sdis_realisation.h" #include "sdis_scene_c.h" #include <rsys/clock_time.h> -#include <rsys/rsys.h> #include <star/ssp.h> #include <omp.h> @@ -37,7 +39,7 @@ static const struct XD(boundary_context) XD(BOUNDARY_CONTEXT_NULL) = { }; /******************************************************************************* - * Help functions + * Non generic helper functions ******************************************************************************/ #ifndef SDIS_SOLVE_BOUNDARY_XD #define SDIS_SOLVE_BOUNDARY_XD @@ -74,8 +76,43 @@ check_solve_boundary_args(const struct sdis_solve_boundary_args* args) return RES_OK; } +static INLINE res_T +check_solve_boundary_flux_args(const struct sdis_solve_boundary_flux_args* args) +{ + if(!args) return RES_BAD_ARG; + + /* Check #realisations */ + if(!args->nrealisations || args->nrealisations > INT64_MAX) { + return RES_BAD_ARG; + } + + /* Check the list of primitives */ + if(!args->primitives || !args->nprimitives) { + return RES_BAD_ARG; + } + + /* Check time range */ + if(args->time_range[0] < 0 || args->time_range[1] < args->time_range[0]) { + return RES_BAD_ARG; + } + if(args->time_range[1] > DBL_MAX + && args->time_range[0] != args->time_range[1]) { + return RES_BAD_ARG; + } + + /* Check picard order */ + if(args->picard_order < 1) { + return RES_BAD_ARG; + } + + return RES_OK; +} + #endif /* SDIS_SOLVE_BOUNDARY_XD */ +/******************************************************************************* + * Helper functions + ******************************************************************************/ static INLINE void XD(boundary_get_indices)(const unsigned iprim, unsigned ids[DIM], void* context) { @@ -117,66 +154,57 @@ XD(solve_boundary) struct sdis_green_function** out_green, struct sdis_estimator** out_estimator) { + /* Time registration */ + struct time time0, time1; + char buf[128]; /* Temporary buffer used to store formated time */ + + /* Device variables */ + struct mem_allocator* allocator = NULL; + size_t nthreads = 0; + + /* Stardis variables */ + struct sdis_estimator* estimator = NULL; + struct sdis_green_function* green = NULL; + struct sdis_green_function** per_thread_green = NULL; + + /* Random number generator */ + struct ssp_rng_proxy* rng_proxy = NULL; + struct ssp_rng** per_thread_rng = NULL; + + /* XD variables */ struct XD(boundary_context) ctx = XD(BOUNDARY_CONTEXT_NULL); struct sXd(vertex_data) vdata = SXD_VERTEX_DATA_NULL; struct sXd(scene)* scene = NULL; struct sXd(shape)* shape = NULL; struct sXd(scene_view)* view = NULL; - struct sdis_estimator* estimator = NULL; - struct sdis_green_function* green = NULL; - struct sdis_green_function** greens = NULL; - struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - struct accum* acc_temps = NULL; - struct accum* acc_times = NULL; + + /* Miscellaneous */ + struct accum* per_thread_acc_temp = NULL; + struct accum* per_thread_acc_time = NULL; size_t nrealisations = 0; int64_t irealisation = 0; size_t i; - size_t view_nprims; - int progress = 0; + int32_t* progress = NULL; /* Per process progress bar */ int register_paths = SDIS_HEAT_PATH_NONE; + int is_master_process = 1; ATOMIC nsolved_realisations = 0; ATOMIC res = RES_OK; - if(!scn) { - res = RES_BAD_ARG; - goto error; - } - + if(!scn) { res = RES_BAD_ARG; goto error; } + if(!out_estimator && !out_green) { res = RES_BAD_ARG; goto error; } res = check_solve_boundary_args(args); if(res != RES_OK) goto error; + res = XD(scene_check_dimensionality)(scn); + if(res != RES_OK) goto error; - if(!out_estimator && !out_green) { - res = RES_BAD_ARG; - goto error; - } - - if(out_green && args->picard_order != 1) { - log_err(scn->dev, "%s: the evaluation of the green function does not make " - "sense when dealing with the non-linearities of the system; i.e. picard " - "order must be set to 1 while it is currently set to %lu.\n", - FUNC_NAME, (unsigned long)args->picard_order); - res = RES_BAD_ARG; - goto error; + /* Check the submitted primitive indices */ + FOR_EACH(i, 0, args->nprimitives) { + res = scene_check_primitive_index(scn, args->primitives[i]); + if(res != RES_OK) goto error; } -#if SDIS_XD_DIMENSION == 2 - if(scene_is_2d(scn) == 0) { res = RES_BAD_ARG; goto error; } -#else - if(scene_is_2d(scn) != 0) { res = RES_BAD_ARG; goto error; } -#endif - - SXD(scene_view_primitives_count(scn->sXd(view), &view_nprims)); + /* Check the submitted primitive sides */ FOR_EACH(i, 0, args->nprimitives) { - if(args->primitives[i] >= view_nprims) { - log_err(scn->dev, - "%s: invalid primitive identifier `%lu'. It must be in the [0 %lu] range.\n", - FUNC_NAME, - (unsigned long)args->primitives[i], - (unsigned long)scene_get_primitives_count(scn)-1); - res = RES_BAD_ARG; - goto error; - } if((unsigned)args->sides[i] >= SDIS_SIDE_NULL__) { log_err(scn->dev, "%s: invalid side for the primitive `%lu'.\n", @@ -186,6 +214,19 @@ XD(solve_boundary) } } + if(out_green && args->picard_order != 1) { + log_err(scn->dev, "%s: the evaluation of the green function does not make " + "sense when dealing with the non-linearities of the system; i.e. picard " + "order must be set to 1 while it is currently set to %lu.\n", + FUNC_NAME, (unsigned long)args->picard_order); + res = RES_BAD_ARG; + goto error; + } + +#ifdef SDIS_ENABLE_MPI + is_master_process = !scn->dev->use_mpi || scn->dev->mpi_rank == 0; +#endif + /* Create the Star-XD shape of the boundary */ #if SDIS_XD_DIMENSION == 2 res = s2d_shape_create_line_segments(scn->dev->sXd(dev), &shape); @@ -221,60 +262,59 @@ XD(solve_boundary) res = sXd(scene_view_create)(scene, SXD_SAMPLE, &view); if(res != RES_OK) goto error; - /* Create the proxy RNG */ - if(args->rng_state) { - res = ssp_rng_proxy_create_from_rng(scn->dev->allocator, args->rng_state, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } else { - res = ssp_rng_proxy_create(scn->dev->allocator, SSP_RNG_MT19937_64, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } + nthreads = scn->dev->nthreads; + allocator = scn->dev->allocator; - /* Create the per thread RNG */ - rngs = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*rngs)); - if(!rngs) { res = RES_MEM_ERR; goto error; } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs+i); - if(res != RES_OK) goto error; - } + /* Create the per thread RNGs */ + res = create_per_thread_rng + (scn->dev, args->rng_state, &rng_proxy, &per_thread_rng); + if(res != RES_OK) goto error; + + /* Allocate the per process progress status */ + res = alloc_process_progress(scn->dev, &progress); + if(res != RES_OK) goto error; /* Create the per thread accumulators */ - acc_temps = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(*acc_temps)); - if(!acc_temps) { res = RES_MEM_ERR; goto error; } - acc_times = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(*acc_times)); - if(!acc_times) { res = RES_MEM_ERR; goto error; } + per_thread_acc_temp = MEM_CALLOC(allocator, nthreads, sizeof(struct accum)); + per_thread_acc_time = MEM_CALLOC(allocator, nthreads, sizeof(struct accum)); + if(!per_thread_acc_temp) { res = RES_MEM_ERR; goto error; } + if(!per_thread_acc_time) { res = RES_MEM_ERR; goto error; } /* Create the per thread green function */ if(out_green) { - greens = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*greens)); - if(!greens) { res = RES_MEM_ERR; goto error; } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = green_function_create(scn, &greens[i]); - if(res != RES_OK) goto error; - } + res = create_per_thread_green_function(scn, &per_thread_green); + if(res != RES_OK) goto error; } - /* Create the estimator */ - if(out_estimator) { + /* Create the estimator on the master process only. No estimator is needed + * for non master process */ + if(out_estimator && is_master_process) { res = estimator_create(scn->dev, SDIS_ESTIMATOR_TEMPERATURE, &estimator); if(res != RES_OK) goto error; } - nrealisations = args->nrealisations; - register_paths = out_estimator ? args->register_paths : SDIS_HEAT_PATH_NONE; + /* Synchronise the processes */ + process_barrier(scn->dev); + + #define PROGRESS_MSG "Solving surface temperature: " + print_progress(scn->dev, progress, PROGRESS_MSG); + + /* Begin time registration of the computation */ + time_current(&time0); + + /* Here we go! Launch the Monte Carlo estimation */ + nrealisations = compute_process_realisations_count(scn->dev, args->nrealisations); + register_paths = out_estimator && is_master_process + ? args->register_paths : SDIS_HEAT_PATH_NONE; omp_set_num_threads((int)scn->dev->nthreads); #pragma omp parallel for schedule(static) for(irealisation=0; irealisation<(int64_t)nrealisations; ++irealisation) { struct boundary_realisation_args realis_args = BOUNDARY_REALISATION_ARGS_NULL; struct time t0, t1; const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - struct accum* acc_temp = &acc_temps[ithread]; - struct accum* acc_time = &acc_times[ithread]; + struct ssp_rng* rng = per_thread_rng[ithread]; + struct accum* acc_temp = &per_thread_acc_temp[ithread]; + struct accum* acc_time = &per_thread_acc_time[ithread]; struct green_path_handle* pgreen_path = NULL; struct green_path_handle green_path = GREEN_PATH_HANDLE_NULL; struct sdis_heat_path* pheat_path = NULL; @@ -298,7 +338,8 @@ XD(solve_boundary) time = sample_time(rng, args->time_range); if(out_green) { - res_local = green_function_create_path(greens[ithread], &green_path); + res_local = green_function_create_path + (per_thread_green[ithread], &green_path); if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); goto error_it; @@ -392,9 +433,9 @@ XD(solve_boundary) n = (size_t)ATOMIC_INCR(&nsolved_realisations); pcent = (int)((double)n * 100.0 / (double)nrealisations + 0.5/*round*/); #pragma omp critical - if(pcent > progress) { - progress = pcent; - log_info(scn->dev, "Solving boundary temperature: %3d%%\r", progress); + if(pcent > progress[0]) { + progress[0] = pcent; + print_progress_update(scn->dev, progress, PROGRESS_MSG); } exit_it: if(pheat_path) heat_path_release(pheat_path); @@ -402,58 +443,82 @@ XD(solve_boundary) error_it: goto exit_it; } + /* Synchronise processes */ + process_barrier(scn->dev); + + res = gather_res_T(scn->dev, (res_T)res); if(res != RES_OK) goto error; - /* Add a new line after the progress status */ - log_info(scn->dev, "Solving boundary temperature: %3d%%\n", progress); + print_progress_update(scn->dev, progress, PROGRESS_MSG); + log_info(scn->dev, "\n"); + #undef PROGRESS_MSG + + /* Report computation time */ + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Surface probe temperature solved in %s.\n", buf); + + /* Gather the RNG proxy sequence IDs and ensure that the RNG proxy state of + * the master process is greater than the RNG proxy state of all other + * processes */ + res = gather_rng_proxy_sequence_id(scn->dev, rng_proxy); + if(res != RES_OK) goto error; /* Setup the estimated temperature */ if(out_estimator) { struct accum acc_temp; struct accum acc_time; - sum_accums(acc_temps, scn->dev->nthreads, &acc_temp); - sum_accums(acc_times, scn->dev->nthreads, &acc_time); - ASSERT(acc_temp.count == acc_time.count); - - estimator_setup_realisations_count(estimator, nrealisations, acc_temp.count); - estimator_setup_temperature(estimator, acc_temp.sum, acc_temp.sum2); - estimator_setup_realisation_time(estimator, acc_time.sum, acc_time.sum2); - res = estimator_save_rng_state(estimator, rng_proxy); - if(res != RES_OK) goto error; + time_current(&time0); + + #define GATHER_ACCUMS(Msg, Acc) { \ + res = gather_accumulators(scn->dev, Msg, per_thread_##Acc, &Acc); \ + if(res != RES_OK) goto error; \ + } (void)0 + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TEMP, acc_temp); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TIME, acc_time); + #undef GATHER_ACCUMS + + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Accumulators gathered in %s.\n", buf); + + /* Return an estimator only on master process */ + if(is_master_process) { + ASSERT(acc_temp.count == acc_time.count); + estimator_setup_realisations_count(estimator, args->nrealisations, acc_temp.count); + estimator_setup_temperature(estimator, acc_temp.sum, acc_temp.sum2); + estimator_setup_realisation_time(estimator, acc_time.sum, acc_time.sum2); + res = estimator_save_rng_state(estimator, rng_proxy); + if(res != RES_OK) goto error; + } } /* Setup the green function */ if(out_green) { - struct accum acc_time; + time_current(&time0); - /* Redux the per thread green function into the green of the 1st thread */ - green = greens[0]; /* Return the green of the 1st thread */ - greens[0] = NULL; /* Make invalid the 1st green for 'on exit' clean up*/ - res = green_function_redux_and_clear(green, greens+1, scn->dev->nthreads-1); + res = gather_green_functions + (scn, rng_proxy, per_thread_green, per_thread_acc_time, &green); if(res != RES_OK) goto error; - /* Finalize the estimated green */ - sum_accums(acc_times, scn->dev->nthreads, &acc_time); - res = green_function_finalize(green, rng_proxy, &acc_time); - if(res != RES_OK) goto error; - } + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Green functions gathered in %s.\n", buf); -exit: - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(rngs[i]) SSP(rng_ref_put(rngs[i])); + /* Return a green function only on master process */ + if(!is_master_process) { + SDIS(green_function_ref_put(green)); + green = NULL; } - MEM_RM(scn->dev->allocator, rngs); } - if(greens) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(greens[i]) SDIS(green_function_ref_put(greens[i])); - } - MEM_RM(scn->dev->allocator, greens); - } - if(acc_temps) MEM_RM(scn->dev->allocator, acc_temps); - if(acc_times) MEM_RM(scn->dev->allocator, acc_times); + +exit: + if(per_thread_rng) release_per_thread_rng(scn->dev, per_thread_rng); + if(per_thread_green) release_per_thread_green_function(scn, per_thread_green); + if(progress) free_process_progress(scn->dev, progress); + if(per_thread_acc_temp) MEM_RM(scn->dev->allocator, per_thread_acc_temp); + if(per_thread_acc_time) MEM_RM(scn->dev->allocator, per_thread_acc_time); if(scene) SXD(scene_ref_put(scene)); if(shape) SXD(shape_ref_put(shape)); if(view) SXD(scene_view_ref_put(view)); @@ -462,14 +527,8 @@ exit: if(out_estimator) *out_estimator = estimator; return (res_T)res; error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } - if(green) { - SDIS(green_function_ref_put(green)); - green = NULL; - } + if(estimator) { SDIS(estimator_ref_put(estimator)); estimator = NULL; } + if(green) { SDIS(green_function_ref_put(green)); green = NULL; } goto exit; } @@ -479,59 +538,59 @@ XD(solve_boundary_flux) const struct sdis_solve_boundary_flux_args* args, struct sdis_estimator** out_estimator) { + /* Time registration */ + struct time time0, time1; + char buf[128]; /* Temporary buffer used to store formated time */ + + /* Stardis variables */ + struct sdis_estimator* estimator = NULL; + + /* XD variables */ struct XD(boundary_context) ctx = XD(BOUNDARY_CONTEXT_NULL); struct sXd(vertex_data) vdata = SXD_VERTEX_DATA_NULL; struct sXd(scene)* scene = NULL; struct sXd(shape)* shape = NULL; struct sXd(scene_view)* view = NULL; - struct sdis_estimator* estimator = NULL; + + /* Random number generator */ struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - struct accum* acc_tp = NULL; /* Per thread temperature accumulator */ - struct accum* acc_ti = NULL; /* Per thread realisation time accumulator */ - struct accum* acc_fl = NULL; /* Per thread flux accumulator */ - struct accum* acc_fc = NULL; /* Per thread convective flux accumulator */ - struct accum* acc_fr = NULL; /* Per thread radiative flux accumulator */ - struct accum* acc_fi = NULL; /* Per thread imposed flux accumulator */ + struct ssp_rng** per_thread_rng = NULL; + + /* Per thread accumulators */ + struct accum* per_thread_acc_tp = NULL; /* Temperature accumulator */ + struct accum* per_thread_acc_ti = NULL; /* Realisation time accumulator */ + struct accum* per_thread_acc_fl = NULL; /* Flux accumulator */ + struct accum* per_thread_acc_fc = NULL; /* Convective flux accumulator */ + struct accum* per_thread_acc_fr = NULL; /* Radiative flux accumulator */ + struct accum* per_thread_acc_fi = NULL; /* Imposed flux accumulator */ + + /* Gathered accumulators */ + struct accum acc_tp = ACCUM_NULL; + struct accum acc_ti = ACCUM_NULL; + struct accum acc_fl = ACCUM_NULL; + struct accum acc_fc = ACCUM_NULL; + struct accum acc_fr = ACCUM_NULL; + struct accum acc_fi = ACCUM_NULL; + + /* Miscellaneous */ size_t nrealisations = 0; int64_t irealisation; size_t i; - size_t view_nprims; - int progress = 0; + int32_t* progress = NULL; /* Per process progress bar */ + int is_master_process = 1; ATOMIC nsolved_realisations = 0; ATOMIC res = RES_OK; - if(!scn - || !args - || !args->nrealisations - || args->nrealisations > INT64_MAX - || !args->primitives - || args->time_range[0] < 0 - || args->time_range[1] < args->time_range[0] - || (args->time_range[1] > DBL_MAX && args->time_range[0] != args->time_range[1]) - || !args->nprimitives - || !out_estimator) { - res = RES_BAD_ARG; - goto error; - } + if(!scn || !out_estimator) { res = RES_BAD_ARG; goto error; } -#if SDIS_XD_DIMENSION == 2 - if(scene_is_2d(scn) == 0) { res = RES_BAD_ARG; goto error; } -#else - if(scene_is_2d(scn) != 0) { res = RES_BAD_ARG; goto error; } -#endif + res = check_solve_boundary_flux_args(args); + if(res != RES_OK) goto error; + res = XD(scene_check_dimensionality)(scn); + if(res != RES_OK) goto error; - SXD(scene_view_primitives_count(scn->sXd(view), &view_nprims)); FOR_EACH(i, 0, args->nprimitives) { - if(args->primitives[i] >= view_nprims) { - log_err(scn->dev, - "%s: invalid primitive identifier `%lu'. It must be in the [0 %lu] range.\n", - FUNC_NAME, - (unsigned long)args->primitives[i], - (unsigned long)scene_get_primitives_count(scn)-1); - res = RES_BAD_ARG; - goto error; - } + res = scene_check_primitive_index(scn, args->primitives[i]); + if(res != RES_OK) goto error; } /* Create the Star-XD shape of the boundary */ @@ -569,43 +628,49 @@ XD(solve_boundary_flux) res = sXd(scene_view_create)(scene, SXD_SAMPLE, &view); if(res != RES_OK) goto error; - /* Create the proxy RNG */ - if(args->rng_state) { - res = ssp_rng_proxy_create_from_rng(scn->dev->allocator, args->rng_state, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } else { - res = ssp_rng_proxy_create(scn->dev->allocator, SSP_RNG_MT19937_64, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } +#ifdef SDIS_ENABLE_MPI + is_master_process = !scn->dev->use_mpi || scn->dev->mpi_rank == 0; +#endif - /* Create the per thread RNG */ - rngs = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*rngs)); - if(!rngs) { res = RES_MEM_ERR; goto error; } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs + i); - if(res != RES_OK) goto error; - } + /* Create the per thread RNGs */ + res = create_per_thread_rng + (scn->dev, args->rng_state, &rng_proxy, &per_thread_rng); + if(res != RES_OK) goto error; + + /* Allocate the per process progress status */ + res = alloc_process_progress(scn->dev, &progress); + if(res != RES_OK) goto error; /* Create the per thread accumulator */ #define ALLOC_ACCUMS(Dst) { \ Dst = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*Dst)); \ if(!Dst) { res = RES_MEM_ERR; goto error; } \ } (void)0 - ALLOC_ACCUMS(acc_tp); - ALLOC_ACCUMS(acc_ti); - ALLOC_ACCUMS(acc_fc); - ALLOC_ACCUMS(acc_fl); - ALLOC_ACCUMS(acc_fr); - ALLOC_ACCUMS(acc_fi); + ALLOC_ACCUMS(per_thread_acc_tp); + ALLOC_ACCUMS(per_thread_acc_ti); + ALLOC_ACCUMS(per_thread_acc_fc); + ALLOC_ACCUMS(per_thread_acc_fl); + ALLOC_ACCUMS(per_thread_acc_fr); + ALLOC_ACCUMS(per_thread_acc_fi); #undef ALLOC_ACCUMS - /* Create the estimator */ - res = estimator_create(scn->dev, SDIS_ESTIMATOR_FLUX, &estimator); - if(res != RES_OK) goto error; + if(is_master_process) { + /* Create the estimator */ + res = estimator_create(scn->dev, SDIS_ESTIMATOR_FLUX, &estimator); + if(res != RES_OK) goto error; + } + + /* Synchronise the processes */ + process_barrier(scn->dev); - nrealisations = args->nrealisations; + #define PROGRESS_MSG "Solving surface flux: " + print_progress(scn->dev, progress, PROGRESS_MSG); + + /* Begin time registration of the computation */ + time_current(&time0); + + /* Here we go! Launch the Monte Carlo estimation */ + nrealisations = compute_process_realisations_count(scn->dev, args->nrealisations); omp_set_num_threads((int)scn->dev->nthreads); #pragma omp parallel for schedule(static) for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { @@ -613,13 +678,13 @@ XD(solve_boundary_flux) BOUNDARY_FLUX_REALISATION_ARGS_NULL; struct time t0, t1; const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - struct accum* acc_temp = &acc_tp[ithread]; - struct accum* acc_time = &acc_ti[ithread]; - struct accum* acc_flux = &acc_fl[ithread]; - struct accum* acc_fcon = &acc_fc[ithread]; - struct accum* acc_frad = &acc_fr[ithread]; - struct accum* acc_fimp = &acc_fi[ithread]; + struct ssp_rng* rng = per_thread_rng[ithread]; + struct accum* acc_temp = &per_thread_acc_tp[ithread]; + struct accum* acc_time = &per_thread_acc_ti[ithread]; + struct accum* acc_flux = &per_thread_acc_fl[ithread]; + struct accum* acc_fcon = &per_thread_acc_fc[ithread]; + struct accum* acc_frad = &per_thread_acc_fr[ithread]; + struct accum* acc_fimp = &per_thread_acc_fi[ithread]; struct sXd(primitive) prim; struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; const struct sdis_interface* interf; @@ -765,52 +830,77 @@ XD(solve_boundary_flux) n = (size_t)ATOMIC_INCR(&nsolved_realisations); pcent = (int)((double)n * 100.0 / (double)nrealisations + 0.5/*round*/); #pragma omp critical - if(pcent > progress) { - progress = pcent; - log_info(scn->dev, "Solving boundary flux: %3d%%\r", progress); + if(pcent > progress[0]) { + progress[0] = pcent; + print_progress_update(scn->dev, progress, PROGRESS_MSG); } } + /* Synchronise processes */ + process_barrier(scn->dev); + + res = gather_res_T(scn->dev, (res_T)res); if(res != RES_OK) goto error; - /* Add a new line after the progress status */ - log_info(scn->dev, "Solving boundary flux: %3d%%\n", progress); - - /* Redux the per thread accumulators */ - sum_accums(acc_tp, scn->dev->nthreads, &acc_tp[0]); - sum_accums(acc_ti, scn->dev->nthreads, &acc_ti[0]); - sum_accums(acc_fc, scn->dev->nthreads, &acc_fc[0]); - sum_accums(acc_fr, scn->dev->nthreads, &acc_fr[0]); - sum_accums(acc_fl, scn->dev->nthreads, &acc_fl[0]); - sum_accums(acc_fi, scn->dev->nthreads, &acc_fi[0]); - ASSERT(acc_tp[0].count == acc_fl[0].count); - ASSERT(acc_tp[0].count == acc_ti[0].count); - ASSERT(acc_tp[0].count == acc_fr[0].count); - ASSERT(acc_tp[0].count == acc_fc[0].count); - ASSERT(acc_tp[0].count == acc_fi[0].count); + print_progress_update(scn->dev, progress, PROGRESS_MSG); + log_info(scn->dev, "\n"); + #undef PROGRESS_MSG - /* Setup the estimated values */ - estimator_setup_realisations_count(estimator, nrealisations, acc_tp[0].count); - estimator_setup_temperature(estimator, acc_tp[0].sum, acc_tp[0].sum2); - estimator_setup_realisation_time(estimator, acc_ti[0].sum, acc_ti[0].sum2); - estimator_setup_flux(estimator, FLUX_CONVECTIVE, acc_fc[0].sum, acc_fc[0].sum2); - estimator_setup_flux(estimator, FLUX_RADIATIVE, acc_fr[0].sum, acc_fr[0].sum2); - estimator_setup_flux(estimator, FLUX_IMPOSED, acc_fi[0].sum, acc_fi[0].sum2); - estimator_setup_flux(estimator, FLUX_TOTAL, acc_fl[0].sum, acc_fl[0].sum2); - - res = estimator_save_rng_state(estimator, rng_proxy); + /* Report computation time */ + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Surface flux solved in %s.\n", buf); + + /* Gather the RNG proxy sequence IDs and ensure that the RNG proxy state of + * the master process is greater than the RNG proxy state of all other + * processes */ + res = gather_rng_proxy_sequence_id(scn->dev, rng_proxy); if(res != RES_OK) goto error; -exit: - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) { if(rngs[i]) SSP(rng_ref_put(rngs[i])); } - MEM_RM(scn->dev->allocator, rngs); + time_current(&time0); + #define GATHER_ACCUMS(Msg, Acc) { \ + res = gather_accumulators(scn->dev, Msg, per_thread_##Acc, &Acc); \ + if(res != RES_OK) goto error; \ + } (void)0 + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TEMP, acc_tp); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TIME, acc_ti); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_FLUX_CONVECTIVE, acc_fc); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_FLUX_IMPOSED, acc_fi); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_FLUX_RADIATIVE, acc_fr); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_FLUX_TOTAL, acc_fl); + #undef GATHER_ACCUMS + + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Accumulators gathered in %s.\n", buf); + + /* Setup the estimated values */ + if(is_master_process) { + ASSERT(acc_tp.count == acc_fl.count); + ASSERT(acc_tp.count == acc_ti.count); + ASSERT(acc_tp.count == acc_fr.count); + ASSERT(acc_tp.count == acc_fc.count); + ASSERT(acc_tp.count == acc_fi.count); + estimator_setup_realisations_count(estimator, args->nrealisations, acc_tp.count); + estimator_setup_temperature(estimator, acc_tp.sum, acc_tp.sum2); + estimator_setup_realisation_time(estimator, acc_ti.sum, acc_ti.sum2); + estimator_setup_flux(estimator, FLUX_CONVECTIVE, acc_fc.sum, acc_fc.sum2); + estimator_setup_flux(estimator, FLUX_RADIATIVE, acc_fr.sum, acc_fr.sum2); + estimator_setup_flux(estimator, FLUX_IMPOSED, acc_fi.sum, acc_fi.sum2); + estimator_setup_flux(estimator, FLUX_TOTAL, acc_fl.sum, acc_fl.sum2); + + res = estimator_save_rng_state(estimator, rng_proxy); + if(res != RES_OK) goto error; } - if(acc_tp) MEM_RM(scn->dev->allocator, acc_tp); - if(acc_ti) MEM_RM(scn->dev->allocator, acc_ti); - if(acc_fc) MEM_RM(scn->dev->allocator, acc_fc); - if(acc_fr) MEM_RM(scn->dev->allocator, acc_fr); - if(acc_fl) MEM_RM(scn->dev->allocator, acc_fl); - if(acc_fi) MEM_RM(scn->dev->allocator, acc_fi); + +exit: + if(per_thread_rng) release_per_thread_rng(scn->dev, per_thread_rng); + if(per_thread_acc_tp) MEM_RM(scn->dev->allocator, per_thread_acc_tp); + if(per_thread_acc_ti) MEM_RM(scn->dev->allocator, per_thread_acc_ti); + if(per_thread_acc_fc) MEM_RM(scn->dev->allocator, per_thread_acc_fc); + if(per_thread_acc_fr) MEM_RM(scn->dev->allocator, per_thread_acc_fr); + if(per_thread_acc_fl) MEM_RM(scn->dev->allocator, per_thread_acc_fl); + if(per_thread_acc_fi) MEM_RM(scn->dev->allocator, per_thread_acc_fi); + if(progress) free_process_progress(scn->dev, progress); if(scene) SXD(scene_ref_put(scene)); if(shape) SXD(shape_ref_put(shape)); if(view) SXD(scene_view_ref_put(view)); @@ -818,10 +908,7 @@ exit: if(out_estimator) *out_estimator = estimator; return (res_T)res; error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } + if(estimator) { SDIS(estimator_ref_put(estimator)); estimator = NULL; } goto exit; } diff --git a/src/sdis_solve_camera.c b/src/sdis_solve_camera.c @@ -0,0 +1,699 @@ +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "sdis.h" +#include "sdis_c.h" +#include "sdis_camera.h" +#include "sdis_device_c.h" +#include "sdis_estimator_buffer_c.h" +#include "sdis_log.h" +#include "sdis_medium_c.h" +#include "sdis_realisation.h" +#include "sdis_scene_c.h" +#include "sdis_tile.h" +#ifdef SDIS_ENABLE_MPI + #include "sdis_mpi.h" +#endif + +#include <rsys/clock_time.h> +#include <rsys/cstr.h> +#include <rsys/list.h> +#include <rsys/morton.h> + +#include <omp.h> + +/******************************************************************************* + * Helper function + ******************************************************************************/ +static res_T +check_solve_camera_args(const struct sdis_solve_camera_args* args) +{ + if(!args) return RES_BAD_ARG; + if(!args->cam) return RES_BAD_ARG; + + /* Check the image resolution */ + if(!args->image_definition[0] || !args->image_definition[1]) { + return RES_BAD_ARG; + } + + /* Check the number of samples per pixel */ + if(!args->spp) { + return RES_BAD_ARG; + } + + /* Check the time range */ + if(args->time_range[0] < 0 || args->time_range[1] < args->time_range[0]) { + return RES_BAD_ARG; + } + if(args->time_range[1] > DBL_MAX + && args->time_range[0] != args->time_range[1]) { + return RES_BAD_ARG; + } + + /* Check the picard order */ + if(args->picard_order < 1) { + return RES_BAD_ARG; + } + + return RES_OK; +} + +static res_T +solve_pixel + (struct sdis_scene* scn, + struct ssp_rng* rng, + struct sdis_medium* mdm, + const struct sdis_camera* cam, + const double time_range[2], /* Observation time */ + const size_t ipix[2], /* Pixel coordinate in the image space */ + const size_t nrealisations, + const int register_paths, /* Combination of enum sdis_heat_path_flag */ + const double pix_sz[2], /* Pixel size in the normalized image plane */ + const size_t picard_order, + struct sdis_estimator* estimator, + struct pixel* pixel) +{ + struct sdis_heat_path* pheat_path = NULL; + size_t irealisation; + res_T res = RES_OK; + ASSERT(scn && mdm && rng && cam && ipix && nrealisations); + ASSERT(pix_sz && pix_sz[0] > 0 && pix_sz[1] > 0); + ASSERT(pixel && time_range); + + pixel->acc_temp = ACCUM_NULL; + pixel->acc_time = ACCUM_NULL; + + FOR_EACH(irealisation, 0, nrealisations) { + struct ray_realisation_args realis_args = RAY_REALISATION_ARGS_NULL; + struct time t0, t1; + double samp[2]; /* Pixel sample */ + double ray_pos[3]; + double ray_dir[3]; + double w = 0; + struct sdis_heat_path heat_path; + double time; + res_T res_simul = RES_OK; + + /* Begin time registration */ + time_current(&t0); + + time = sample_time(rng, time_range); + if(register_paths) { + heat_path_init(scn->dev->allocator, &heat_path); + pheat_path = &heat_path; + } + + /* Generate a sample into the pixel to estimate */ + samp[0] = ((double)ipix[0] + ssp_rng_canonical(rng)) * pix_sz[0]; + samp[1] = ((double)ipix[1] + ssp_rng_canonical(rng)) * pix_sz[1]; + + /* Generate a ray starting from the camera position and passing through + * pixel sample */ + camera_ray(cam, samp, ray_pos, ray_dir); + + /* Launch the realisation */ + realis_args.rng = rng; + realis_args.medium = mdm; + realis_args.time = time; + realis_args.picard_order = picard_order; + realis_args.heat_path = pheat_path; + d3_set(realis_args.position, ray_pos); + d3_set(realis_args.direction, ray_dir); + res_simul = ray_realisation_3d(scn, &realis_args, &w); + + /* Handle fatal error */ + if(res_simul != RES_OK && res_simul != RES_BAD_OP) { + res = res_simul; + goto error; + } + + if(pheat_path) { + pheat_path->status = res_simul == RES_OK + ? SDIS_HEAT_PATH_SUCCESS + : SDIS_HEAT_PATH_FAILURE; + + /* Check if the path must be saved regarding the register_paths mask */ + if(!(register_paths & (int)pheat_path->status)) { + heat_path_release(pheat_path); + pheat_path = NULL; + } else { /* Register the sampled path */ + ASSERT(estimator); + res = estimator_add_and_release_heat_path(estimator, pheat_path); + if(res != RES_OK) goto error; + pheat_path = NULL; + } + } + + /* Stop time registration */ + time_sub(&t0, time_current(&t1), &t0); + + if(res_simul == RES_OK) { + /* Update pixel accumulators */ + const double usec = (double)time_val(&t0, TIME_NSEC) * 0.001; + pixel->acc_temp.sum += w; + pixel->acc_temp.sum2 += w*w; + pixel->acc_temp.count += 1; + pixel->acc_time.sum += usec; + pixel->acc_time.sum2 += usec*usec; + pixel->acc_time.count += 1; + } + } + +exit: + if(pheat_path) heat_path_release(pheat_path); + return res; +error: + goto exit; +} + +static res_T +solve_tile + (struct sdis_scene* scn, + struct ssp_rng* rng, + struct sdis_medium* mdm, + const struct sdis_camera* cam, + const double time_range[2], + const size_t tile_org[2], /* Origin of the tile in pixel space */ + const size_t tile_size[2], /* #pixels in the tile in X and Y */ + const size_t spp, /* #samples per pixel */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ + const double pix_sz[2], /* Pixel size in the normalized image plane */ + const size_t picard_order, + struct sdis_estimator_buffer* buf, + struct tile* tile) +{ + size_t mcode; /* Morton code of the tile pixel */ + size_t npixels; + res_T res = RES_OK; + ASSERT(scn && rng && mdm && cam && spp); + ASSERT(tile_size && tile_size[0] && tile_size[1]); + ASSERT(pix_sz && pix_sz[0] > 0 && pix_sz[1] > 0 && time_range); + + /* Adjust the #pixels to process them wrt a morton order */ + npixels = round_up_pow2(MMAX(tile_size[0], tile_size[1])); + npixels *= npixels; + + FOR_EACH(mcode, 0, npixels) { + struct pixel* pixel = NULL; + struct sdis_estimator* estimator = NULL; + uint16_t ipix_tile[2]; + size_t ipix_image[2]; + + ipix_tile[0] = morton2D_decode_u16((uint32_t)(mcode>>0)); + if(ipix_tile[0] >= tile_size[0]) continue; + ipix_tile[1] = morton2D_decode_u16((uint32_t)(mcode>>1)); + if(ipix_tile[1] >= tile_size[1]) continue; + + pixel = tile_at(tile, ipix_tile[0], ipix_tile[1]); + + /* Compute the pixel coordinates in image space */ + ipix_image[0] = ipix_tile[0] + tile_org[0]; + ipix_image[1] = ipix_tile[1] + tile_org[1]; + + if(register_paths != SDIS_HEAT_PATH_NONE) { + ASSERT(buf); + estimator = estimator_buffer_grab(buf, ipix_image[0], ipix_image[1]); + } + res = solve_pixel + (scn, rng, mdm, cam, time_range, ipix_image, spp, register_paths, pix_sz, + picard_order, estimator, pixel); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + +static INLINE res_T +setup_estimator_from_pixel + (struct sdis_estimator* estimator, + const size_t spp, /* #samples per pixel */ + struct pixel* pixel) +{ + ASSERT(estimator && spp && pixel); + ASSERT(pixel->acc_temp.count == pixel->acc_time.count); + estimator_setup_realisations_count + (estimator, spp, pixel->acc_temp.count); + estimator_setup_temperature + (estimator, pixel->acc_temp.sum, pixel->acc_temp.sum2); + estimator_setup_realisation_time + (estimator, pixel->acc_time.sum, pixel->acc_time.sum2); + return RES_OK; +} + +static res_T +write_tile + (struct sdis_estimator_buffer* buf, + const size_t spp, /* #samples per pixel */ + struct tile* tile) +{ + res_T res = RES_OK; + size_t tile_org[2]; + uint16_t x, y; + ASSERT(buf && spp && tile); + + tile_org[0] = tile->data.x * TILE_SIZE; + tile_org[1] = tile->data.y * TILE_SIZE; + + FOR_EACH(y, 0, TILE_SIZE) { + const size_t pix_y = tile_org[1] + y; + FOR_EACH(x, 0, TILE_SIZE) { + const size_t pix_x = tile_org[0] + x; + struct sdis_estimator* estimator = NULL; + struct pixel* pixel = NULL; + + estimator = estimator_buffer_grab(buf, pix_x, pix_y); + pixel = tile_at(tile, x, y); + + res = setup_estimator_from_pixel(estimator, spp, pixel); + if(res != RES_OK) goto error; + } + } + +exit: + return res; +error: + goto exit; +} + +static res_T +write_list_of_tiles + (struct sdis_estimator_buffer* buf, + const size_t spp, /* #samples per pixel */ + struct list_node* tiles) /* Tiles to write */ +{ + struct list_node* node = NULL; + res_T res = RES_OK; + ASSERT(buf && spp && tiles); + + LIST_FOR_EACH(node, tiles) { + struct tile* tile = CONTAINER_OF(node, struct tile, node); + res = write_tile(buf, spp, tile); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + +#ifndef SDIS_ENABLE_MPI +static INLINE res_T +gather_tiles + (struct sdis_device* dev, + struct sdis_estimator_buffer* buf, /* NULL on non master processes */ + const size_t spp, /* #realisations per pixel */ + const size_t ntiles, /* Overall #tiles that must be written into `buf' */ + struct list_node* tiles) /* List of tiles of the current process */ +{ + (void)dev, (void)ntiles; + return write_list_of_tiles(buf, spp, tiles); +} +#else +/* Gather the tiles and write them into the estimator buffer. The master process + * write its own tiles into the estimator buffer. Then, it gathers the tiles + * send by non master processes and write them too into the estimator buffer */ +static res_T +gather_tiles + (struct sdis_device* dev, + struct sdis_estimator_buffer* buf, /* NULL on non master processes */ + const size_t spp, /* #realisations per pixel */ + const size_t ntiles, /* Overall #tiles that must be written into `buf' */ + struct list_node* tiles) /* List of tiles of the current process */ +{ + struct tile* tile_temp = NULL; + struct list_node* node = NULL; + res_T res = RES_OK; + ASSERT(dev && spp && tiles); + + if(!dev->use_mpi) { + res = write_list_of_tiles(buf, spp, tiles); + if(res != RES_OK) goto error; + goto exit; /* No more to do */ + } + + /* Non master process */ + if(dev->mpi_rank != 0) { + + /* Send to the master process the list of tiles solved by the current + * process */ + LIST_FOR_EACH(node, tiles) { + struct tile* tile = CONTAINER_OF(node, struct tile, node); + mutex_lock(dev->mpi_mutex); + MPI(Send(&tile->data, sizeof(tile->data), MPI_CHAR, 0, MPI_SDIS_MSG_TILE, + MPI_COMM_WORLD)); + mutex_unlock(dev->mpi_mutex); + } + + /* Master process */ + } else { + size_t itile; + size_t ntiles_master; + + /* Write into the buffer the tiles solved by the master process itself */ + res = write_list_of_tiles(buf, spp, tiles); + if(res != RES_OK) goto error; + + /* Create a temporary tile to store the tile sent by the non master + * processes */ + res = tile_create(dev->allocator, &tile_temp); + if(res != RES_OK) { + log_err(dev, + "Could not allocate the tile to temporary store the tiles sent by " + "the non master processes -- %s", res_to_cstr(res)); + goto error; + } + + /* Count the number of tiles rendered onto the master process */ + ntiles_master = 0; + LIST_FOR_EACH(node, tiles) ++ntiles_master; + ASSERT(ntiles_master <= ntiles); + + /* Receive the remaining tiles sent by the non master processes */ + FOR_EACH(itile, ntiles_master, ntiles) { + MPI_Request req; + + /* Asynchronously receive a tile */ + mutex_lock(dev->mpi_mutex); + MPI(Irecv(&tile_temp->data, sizeof(tile_temp->data), MPI_CHAR, + MPI_ANY_SOURCE, MPI_SDIS_MSG_TILE, MPI_COMM_WORLD, &req)); + mutex_unlock(dev->mpi_mutex); + mpi_waiting_for_request(dev, &req); + + /* Write the tile into the estimator buffer */ + res = write_tile(buf, spp, tile_temp); + if(res != RES_OK) goto error; + } + } + +exit: + if(tile_temp) tile_ref_put(tile_temp); + return res; +error: + goto exit; +} +#endif + +/* Setup the accumulators of the whole estimator buffer */ +static res_T +finalize_estimator_buffer + (struct sdis_estimator_buffer* buf, + struct ssp_rng_proxy* rng_proxy, + const size_t spp) /* #samples per pixel */ +{ + struct accum acc_temp = ACCUM_NULL; + struct accum acc_time = ACCUM_NULL; + size_t definition[2]; + size_t x, y; + size_t nrealisations = 0; + size_t nsuccesses = 0; + res_T res = RES_OK; + ASSERT(buf && rng_proxy && spp); + + SDIS(estimator_buffer_get_definition(buf, definition)); + + FOR_EACH(y, 0, definition[1]) { + FOR_EACH(x, 0, definition[0]) { + const struct sdis_estimator* estimator; + SDIS(estimator_buffer_at(buf, x, y, &estimator)); + acc_temp.sum += estimator->temperature.sum; + acc_temp.sum2 += estimator->temperature.sum2; + acc_temp.count += estimator->temperature.count; + acc_time.sum += estimator->realisation_time.sum; + acc_time.sum2 += estimator->realisation_time.sum2; + acc_time.count += estimator->realisation_time.count; + nsuccesses += estimator->nrealisations; + } + } + + nrealisations = definition[0]*definition[1]*spp; + ASSERT(acc_temp.count == acc_time.count); + ASSERT(acc_temp.count == nsuccesses); + estimator_buffer_setup_realisations_count(buf, nrealisations, nsuccesses); + estimator_buffer_setup_temperature(buf, acc_temp.sum, acc_temp.sum2); + estimator_buffer_setup_realisation_time(buf, acc_time.sum, acc_time.sum2); + res = estimator_buffer_save_rng_state(buf, rng_proxy); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +static void +free_rendered_tiles(struct list_node* tiles) +{ + struct list_node* node; + struct list_node* tmp; + ASSERT(tiles); + LIST_FOR_EACH_SAFE(node, tmp, tiles) { + struct tile* tile = CONTAINER_OF(node, struct tile, node); + list_del(node); + tile_ref_put(tile); + } +} + +/******************************************************************************* + * Exported function + ******************************************************************************/ +res_T +sdis_solve_camera + (struct sdis_scene* scn, + const struct sdis_solve_camera_args* args, + struct sdis_estimator_buffer** out_buf) +{ + /* Time registration */ + struct time time0, time1; + char buffer[128]; /* Temporary buffer used to store formated time */ + + /* Stardis variables */ + struct sdis_estimator_buffer* buf= NULL; + struct sdis_medium* medium = NULL; + + /* Random number generators */ + struct ssp_rng_proxy* rng_proxy = NULL; + struct ssp_rng** per_thread_rng = NULL; + + /* Miscellaneous */ + size_t ntiles_x, ntiles_y, ntiles, ntiles_adjusted; + size_t ntiles_proc; /* #tiles for the current proc */ + struct list_node tiles; /* List of tiles rendered by the process */ + double pix_sz[2]; /* Size of a pixel in the normalized image plane */ + int64_t mcode; /* Morton code of a tile */ + int64_t mcode_1st; /* morton code of the 1st tile computed by the process */ + int64_t mcode_incr; /* Increment toward the next morton code */ + int32_t* progress = NULL; /* Per process progress bar */ + int register_paths = SDIS_HEAT_PATH_NONE; + int is_master_process = 1; + ATOMIC nsolved_tiles = 0; + ATOMIC res = RES_OK; + + list_init(&tiles); + + if(!scn || !out_buf) { res = RES_BAD_ARG; goto error; } + res = check_solve_camera_args(args); + if(res != RES_OK) goto error; + + if(scene_is_2d(scn)) { + log_err(scn->dev, "%s: 2D scenes are not supported.\n", FUNC_NAME); + goto error; + } + + /* Retrieve the medium in which the submitted position lies */ + res = scene_get_medium(scn, args->cam->position, NULL, &medium); + if(res != RES_OK) goto error; + + if(medium->type != SDIS_FLUID) { + log_err(scn->dev, + "%s: the camera position `%g %g %g' must be in a fluid medium.\n", + FUNC_NAME, SPLIT3(args->cam->position)); + res = RES_BAD_ARG; + goto error; + } + + /* Create the per thread RNGs */ + res = create_per_thread_rng + (scn->dev, NULL, &rng_proxy, &per_thread_rng); + if(res != RES_OK) goto error; + + /* Allocate the per process progress status */ + res = alloc_process_progress(scn->dev, &progress); + if(res != RES_OK) goto error; + + /* Compute the overall number of tiles */ + ntiles_x = (args->image_definition[0] + (TILE_SIZE-1)/*ceil*/)/TILE_SIZE; + ntiles_y = (args->image_definition[1] + (TILE_SIZE-1)/*ceil*/)/TILE_SIZE; + ntiles = ntiles_x * ntiles_y; + ntiles_adjusted = round_up_pow2(MMAX(ntiles_x, ntiles_y)); + ntiles_adjusted *= ntiles_adjusted; + +#ifdef SDIS_ENABLE_MPI + is_master_process = !scn->dev->use_mpi || scn->dev->mpi_rank == 0; + if(scn->dev->use_mpi) { + mcode_1st = scn->dev->mpi_rank; + mcode_incr = scn->dev->mpi_nprocs; + + /* Compute the #tiles of the current proc */ + ntiles_proc = ntiles / (size_t)scn->dev->mpi_nprocs; + if(ntiles % (size_t)scn->dev->mpi_nprocs > (size_t)scn->dev->mpi_rank) { + ++ntiles_proc; + } + } else +#endif + { + is_master_process = 1; + mcode_1st = 0; + mcode_incr = 1; + ntiles_proc = ntiles; + } + + /* Compute the normalized pixel size */ + pix_sz[0] = 1.0 / (double)args->image_definition[0]; + pix_sz[1] = 1.0 / (double)args->image_definition[1]; + + /* Create the global estimator on the master process only */ + if(is_master_process) { + res = estimator_buffer_create + (scn->dev, args->image_definition[0], args->image_definition[1], &buf); + if(res != RES_OK) goto error; + } + + /* Synchronise the processes */ + process_barrier(scn->dev); + + #define PROGRESS_MSG "Rendering: " + print_progress(scn->dev, progress, PROGRESS_MSG); + + /* Begin time registration of the computation */ + time_current(&time0); + + /* Here we go! Launch the Monte Carlo estimation */ + omp_set_num_threads((int)scn->dev->nthreads); + register_paths = is_master_process + ? args->register_paths : SDIS_HEAT_PATH_NONE; + #pragma omp parallel for schedule(static, 1/*chunk size*/) + for(mcode = mcode_1st; mcode < (int64_t)ntiles_adjusted; mcode+=mcode_incr) { + size_t tile_org[2] = {0, 0}; + size_t tile_sz[2] = {0, 0}; + struct tile* tile = NULL; + const int ithread = omp_get_thread_num(); + struct ssp_rng* rng = per_thread_rng[ithread]; + size_t n; + int pcent; + res_T res_local = RES_OK; + + if(ATOMIC_GET(&res) != RES_OK) continue; + + tile_org[0] = morton2D_decode_u16((uint32_t)(mcode>>0)); + if(tile_org[0] >= ntiles_x) continue; /* Discard tile */ + tile_org[1] = morton2D_decode_u16((uint32_t)(mcode>>1)); + if(tile_org[1] >= ntiles_y) continue; /* Disaard tile */ + + res_local = tile_create(scn->dev->allocator, &tile); + if(tile == NULL) { + log_err(scn->dev, "%s: error allocating the tile (%lu, %lu) -- %s\n", + FUNC_NAME, + (unsigned long)tile_org[0], + (unsigned long)tile_org[1], + res_to_cstr(res_local)); + ATOMIC_SET(&res, res_local); + continue; + } + + /* Register the tile */ + #pragma omp critical + list_add_tail(&tiles, &tile->node); + + /* Setup the tile coordinates */ + tile->data.x = (uint16_t)tile_org[0]; + tile->data.y = (uint16_t)tile_org[1]; + + /* Setup the tile coordinates in the image plane */ + tile_org[0] *= TILE_SIZE; + tile_org[1] *= TILE_SIZE; + tile_sz[0] = MMIN(TILE_SIZE, args->image_definition[0] - tile_org[0]); + tile_sz[1] = MMIN(TILE_SIZE, args->image_definition[1] - tile_org[1]); + + /* Draw the tile */ + res_local = solve_tile + (scn, rng, medium, args->cam, args->time_range, tile_org, tile_sz, + args->spp, register_paths, pix_sz, args->picard_order, buf, tile); + if(res_local != RES_OK) { + ATOMIC_SET(&res, res_local); + continue; + } + + /* Update progress */ + n = (size_t)ATOMIC_INCR(&nsolved_tiles); + pcent = (int)((double)n*100.0 / (double)ntiles_proc + 0.5/*round*/); + #pragma omp critical + if(pcent > progress[0]) { + progress[0] = pcent; + print_progress_update(scn->dev, progress, PROGRESS_MSG); + } + } + + /* Synchronise the processes */ + process_barrier(scn->dev); + + res = gather_res_T(scn->dev, (res_T)res); + if(res != RES_OK) goto error; + + print_progress_update(scn->dev, progress, PROGRESS_MSG); + log_info(scn->dev, "\n"); + #undef PROGRESS_MSG + + /* Report computation time */ + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buffer, sizeof(buffer)); + log_info(scn->dev, "Image rendered in %s.\n", buffer); + + /* Gather the RNG proxy sequence IDs and ensure that the RNG proxy state of + * the master process is greater than the RNG proxy state of all other + * processes */ + res = gather_rng_proxy_sequence_id(scn->dev, rng_proxy); + if(res != RES_OK) goto error; + + time_current(&time0); + + res = gather_tiles(scn->dev, buf, args->spp, ntiles, &tiles); + if(res != RES_OK) goto error; + + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buffer, sizeof(buffer)); + log_info(scn->dev, "Image tiles gathered in %s.\n", buffer); + + if(is_master_process) { + res = finalize_estimator_buffer(buf, rng_proxy, args->spp); + if(res != RES_OK) goto error; + } + +exit: + free_rendered_tiles(&tiles); + if(per_thread_rng)release_per_thread_rng(scn->dev, per_thread_rng); + if(progress) free_process_progress(scn->dev, progress); + if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); + if(out_buf) *out_buf = buf; + return (res_T)res; +error: + if(buf) { SDIS(estimator_buffer_ref_put(buf)); buf = NULL; } + goto exit; +} + + diff --git a/src/sdis_solve_medium_Xd.h b/src/sdis_solve_medium_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -13,8 +13,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "sdis_c.h" #include "sdis_device_c.h" #include "sdis_estimator_c.h" +#include "sdis_interface_c.h" +#include "sdis_log.h" #include "sdis_green.h" #include "sdis_realisation.h" #include "sdis_scene_c.h" @@ -23,6 +26,8 @@ #include <rsys/clock_time.h> #include <rsys/dynamic_array.h> +#include <omp.h> + #include "sdis_Xd_begin.h" #ifndef SDIS_SOLVE_MEDIUM_XD_H @@ -165,6 +170,31 @@ check_solve_medium_args(const struct sdis_solve_medium_args* args) return RES_OK; } +static INLINE res_T +check_compute_power_args(const struct sdis_compute_power_args* args) +{ + if(!args) return RES_BAD_ARG; + + /* Check the medium */ + if(!args->medium) return RES_BAD_ARG; + + /* Check #realisations */ + if(!args->nrealisations || args->nrealisations > INT64_MAX) { + return RES_BAD_ARG; + } + + /* Check time range */ + if(args->time_range[0] < 0 || args->time_range[1] < args->time_range[0]) { + return RES_BAD_ARG; + } + if(args->time_range[1] > DBL_MAX + && args->time_range[0] != args->time_range[1]) { + return RES_BAD_ARG; + } + + return RES_OK; +} + #endif /* !SDIS_SOLVE_MEDIUM_XD_H */ /******************************************************************************* @@ -234,35 +264,42 @@ XD(solve_medium) struct sdis_green_function** out_green, /* May be NULL <=> No green func */ struct sdis_estimator** out_estimator) /* May be NULL <=> No estimator */ { - struct darray_enclosure_cumul cumul; + /* Time registration */ + struct time time0, time1; + char buf[128]; /* Temporary buffer used to store formated time */ + + /* Device variables */ + struct mem_allocator* allocator = NULL; + size_t nthreads = 0; + + /* Stardis variables */ + struct sdis_estimator* estimator = NULL; struct sdis_green_function* green = NULL; - struct sdis_green_function** greens = NULL; + struct sdis_green_function** per_thread_green = NULL; + + /* Random number generator */ struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - struct sdis_estimator* estimator = NULL; - struct accum* acc_temps = NULL; - struct accum* acc_times = NULL; + struct ssp_rng** per_thread_rng = NULL; + + /* Miscellaneous */ + struct darray_enclosure_cumul cumul; + struct accum* per_thread_acc_temp = NULL; + struct accum* per_thread_acc_time = NULL; size_t nrealisations = 0; int64_t irealisation; + int32_t* progress = NULL; /* Per process progress bar */ + int is_master_process = 1; int cumul_is_init = 0; - size_t i; - int progress = 0; int register_paths = SDIS_HEAT_PATH_NONE; ATOMIC nsolved_realisations = 0; ATOMIC res = RES_OK; - if(!scn) { - res = RES_BAD_ARG; - goto error; - } - + if(!scn) { res = RES_BAD_ARG; goto error; } + if(!out_estimator && !out_green) { res = RES_BAD_ARG; goto error; } res = check_solve_medium_args(args); if(res != RES_OK) goto error; - - if(!out_estimator && !out_green) { - res = RES_BAD_ARG; - goto error; - } + res = XD(scene_check_dimensionality)(scn); + if(res != RES_OK) goto error; if(out_green && args->picard_order != 1) { log_err(scn->dev, "%s: the evaluation of the green function does not make " @@ -273,38 +310,27 @@ XD(solve_medium) goto error; } -#if SDIS_XD_DIMENSION == 2 - if(scene_is_2d(scn) == 0) { res = RES_BAD_ARG; goto error; } -#else - if(scene_is_2d(scn) != 0) { res = RES_BAD_ARG; goto error; } +#ifdef SDIS_ENABLE_MPI + is_master_process = !scn->dev->use_mpi || scn->dev->mpi_rank == 0; #endif - /* Create the proxy RNG */ - if(args->rng_state) { - res = ssp_rng_proxy_create_from_rng(scn->dev->allocator, args->rng_state, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } else { - res = ssp_rng_proxy_create(scn->dev->allocator, SSP_RNG_MT19937_64, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } + nthreads = scn->dev->nthreads; + allocator = scn->dev->allocator; - /* Create the per thread RNG */ - rngs = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*rngs)); - if(!rngs) { res = RES_MEM_ERR; goto error; } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs+i); - if(res != RES_OK) goto error; - } + /* Create the per thread RNGs */ + res = create_per_thread_rng + (scn->dev, args->rng_state, &rng_proxy, &per_thread_rng); + if(res != RES_OK) goto error; + + /* Allocate the per process progress status */ + res = alloc_process_progress(scn->dev, &progress); + if(res != RES_OK) goto error; /* Create the per thread accumulators */ - acc_temps = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(*acc_temps)); - if(!acc_temps) { res = RES_MEM_ERR; goto error; } - acc_times = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(*acc_times)); - if(!acc_times) { res = RES_MEM_ERR; goto error; } + per_thread_acc_temp = MEM_CALLOC(allocator, nthreads, sizeof(struct accum)); + per_thread_acc_time = MEM_CALLOC(allocator, nthreads, sizeof(struct accum)); + if(!per_thread_acc_temp) { res = RES_MEM_ERR; goto error; } + if(!per_thread_acc_time) { res = RES_MEM_ERR; goto error; } /* Compute the enclosure cumulative */ darray_enclosure_cumul_init(scn->dev->allocator, &cumul); @@ -313,32 +339,39 @@ XD(solve_medium) if(res != RES_OK) goto error; if(out_green) { - /* Create the per thread green function */ - greens = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*greens)); - if(!greens) { res = RES_MEM_ERR; goto error; } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = green_function_create(scn, &greens[i]); - if(res != RES_OK) goto error; - } + res = create_per_thread_green_function(scn, &per_thread_green); + if(res != RES_OK) goto error; } - /* Create the estimator */ - if(out_estimator) { + /* Create the estimator on the master process only. No estimator is needed + * for non master process */ + if(out_estimator && is_master_process) { res = estimator_create(scn->dev, SDIS_ESTIMATOR_TEMPERATURE, &estimator); if(res != RES_OK) goto error; } - nrealisations = args->nrealisations; - register_paths = out_estimator ? args->register_paths : SDIS_HEAT_PATH_NONE; + /* Synchronise the processes */ + process_barrier(scn->dev); + + #define PROGRESS_MSG "Solving medium temperature: " + print_progress(scn->dev, progress, PROGRESS_MSG); + + /* Begin time registration of the computation */ + time_current(&time0); + + /* Here we go! Launch the Monte Carlo estimation */ + nrealisations = compute_process_realisations_count(scn->dev, args->nrealisations); + register_paths = out_estimator && is_master_process + ? args->register_paths : SDIS_HEAT_PATH_NONE; omp_set_num_threads((int)scn->dev->nthreads); #pragma omp parallel for schedule(static) for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { struct probe_realisation_args realis_args = PROBE_REALISATION_ARGS_NULL; struct time t0, t1; const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - struct accum* acc_temp = &acc_temps[ithread]; - struct accum* acc_time = &acc_times[ithread]; + struct ssp_rng* rng = per_thread_rng[ithread]; + struct accum* acc_temp = &per_thread_acc_temp[ithread]; + struct accum* acc_time = &per_thread_acc_time[ithread]; struct green_path_handle* pgreen_path = NULL; struct green_path_handle green_path = GREEN_PATH_HANDLE_NULL; const struct enclosure* enc = NULL; @@ -358,11 +391,8 @@ XD(solve_medium) time = sample_time(rng, args->time_range); if(out_green) { - res_local = green_function_create_path(greens[ithread], &green_path); - if(res_local != RES_OK) { - ATOMIC_SET(&res, res_local); - goto error_it; - } + res_local = green_function_create_path(per_thread_green[ithread], &green_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); goto error_it; } pgreen_path = &green_path; } @@ -430,9 +460,9 @@ XD(solve_medium) n = (size_t)ATOMIC_INCR(&nsolved_realisations); pcent = (int)((double)n * 100.0 / (double)nrealisations + 0.5/*round*/); #pragma omp critical - if(pcent > progress) { - progress = pcent; - log_info(scn->dev, "Solving medium temperature: %3d%%\r", progress); + if(pcent > progress[0]) { + progress[0] = pcent; + print_progress_update(scn->dev, progress, PROGRESS_MSG); } exit_it: if(pheat_path) heat_path_release(pheat_path); @@ -440,71 +470,89 @@ XD(solve_medium) error_it: goto exit_it; } + /* Synchronise processes */ + process_barrier(scn->dev); + + res = gather_res_T(scn->dev, (res_T)res); if(res != RES_OK) goto error; - /* Add a new line after the progress status */ - log_info(scn->dev, "Solving medium temperature: %3d%%\n", progress); + print_progress_update(scn->dev, progress, PROGRESS_MSG); + log_info(scn->dev, "\n"); + #undef PROGRESS_MSG + + /* Report computation time */ + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Medium temperature solved in %s.\n", buf); + + /* Gather the RNG proxy sequence IDs and ensure that the RNG proxy state of + * the master process is greater than the RNG proxy state of all other + * processes */ + res = gather_rng_proxy_sequence_id(scn->dev, rng_proxy); + if(res != RES_OK) goto error; /* Setup the estimated temperature */ if(out_estimator) { struct accum acc_temp; struct accum acc_time; - sum_accums(acc_temps, scn->dev->nthreads, &acc_temp); - sum_accums(acc_times, scn->dev->nthreads, &acc_time); - ASSERT(acc_temp.count == acc_time.count); - - estimator_setup_realisations_count(estimator, nrealisations, acc_temp.count); - estimator_setup_temperature(estimator, acc_temp.sum, acc_temp.sum2); - estimator_setup_realisation_time(estimator, acc_time.sum, acc_time.sum2); - res = estimator_save_rng_state(estimator, rng_proxy); - if(res != RES_OK) goto error; + time_current(&time0); + + #define GATHER_ACCUMS(Msg, Acc) { \ + res = gather_accumulators(scn->dev, Msg, per_thread_##Acc, &Acc); \ + if(res != RES_OK) goto error; \ + } (void)0 + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TEMP, acc_temp); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TIME, acc_time); + #undef GATHER_ACCUMS + + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Accumulators gathered in %s.\n", buf); + + /* Return an estimator only on master process */ + if(is_master_process) { + ASSERT(acc_temp.count == acc_time.count); + estimator_setup_realisations_count(estimator, args->nrealisations, acc_temp.count); + estimator_setup_temperature(estimator, acc_temp.sum, acc_temp.sum2); + estimator_setup_realisation_time(estimator, acc_time.sum, acc_time.sum2); + res = estimator_save_rng_state(estimator, rng_proxy); + if(res != RES_OK) goto error; + } } if(out_green) { - struct accum acc_time; + time_current(&time0); - /* Redux the per thread green function into the green of the 1st thread */ - green = greens[0]; /* Return the green of the 1st thread */ - greens[0] = NULL; /* Make invalid the 1st green for 'on exit' clean up*/ - res = green_function_redux_and_clear(green, greens+1, scn->dev->nthreads-1); + res = gather_green_functions + (scn, rng_proxy, per_thread_green, per_thread_acc_time, &green); if(res != RES_OK) goto error; - /* Finalize the estimated green */ - sum_accums(acc_times, scn->dev->nthreads, &acc_time); - res = green_function_finalize(green, rng_proxy, &acc_time); - if(res != RES_OK) goto error; - } + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Green functions gathered in %s.\n", buf); -exit: - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(rngs[i]) SSP(rng_ref_put(rngs[i])); + /* Return a green function only on master process */ + if(!is_master_process) { + SDIS(green_function_ref_put(green)); + green = NULL; } - MEM_RM(scn->dev->allocator, rngs); } - if(greens) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(greens[i]) SDIS(green_function_ref_put(greens[i])); - } - MEM_RM(scn->dev->allocator, greens); - } - if(acc_temps) MEM_RM(scn->dev->allocator, acc_temps); - if(acc_times) MEM_RM(scn->dev->allocator, acc_times); + +exit: + if(per_thread_rng) release_per_thread_rng(scn->dev, per_thread_rng); + if(per_thread_green) release_per_thread_green_function(scn, per_thread_green); + if(progress) free_process_progress(scn->dev, progress); + if(per_thread_acc_temp) MEM_RM(scn->dev->allocator, per_thread_acc_temp); + if(per_thread_acc_time) MEM_RM(scn->dev->allocator, per_thread_acc_time); if(cumul_is_init) darray_enclosure_cumul_release(&cumul); if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); if(out_estimator) *out_estimator = estimator; if(out_green) *out_green = green; return (res_T)res; error: - if(green) { - SDIS(green_function_ref_put(green)); - green = NULL; - } - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } + if(green) { SDIS(green_function_ref_put(green)); green = NULL; } + if(estimator) { SDIS(estimator_ref_put(estimator)); estimator = NULL; } goto exit; } @@ -514,39 +562,40 @@ XD(compute_power) const struct sdis_compute_power_args* args, struct sdis_estimator** out_estimator) { - struct darray_enclosure_cumul cumul; + /* Time registration */ + struct time time0, time1; + char buf[128]; /* Temporary buffer used to store formated time */ + + /* Device variables */ + struct mem_allocator* allocator = NULL; + size_t nthreads = 0; + + /* Stardis variables */ struct sdis_estimator* estimator = NULL; + + /* Random number generator */ struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - struct accum* acc_mpows = NULL; - struct accum* acc_times = NULL; + struct ssp_rng** per_thread_rng = NULL; + + /* Miscellaneous */ + struct darray_enclosure_cumul cumul; + struct accum* per_thread_acc_mpow = NULL; + struct accum* per_thread_acc_time = NULL; double spread = 0; - size_t i = 0; size_t nrealisations = 0; int64_t irealisation = 0; + int32_t* progress = NULL; /* Per process progress bar */ int cumul_is_init = 0; - int progress = 0; + int is_master_process = 1; ATOMIC nsolved_realisations = 0; ATOMIC res = RES_OK; - if(!scn - || !args - || !out_estimator - || !args->medium - || !args->nrealisations - || args->nrealisations > INT64_MAX - || args->time_range[0] < 0 - || args->time_range[0] > args->time_range[1] - || ( args->time_range[1] > DBL_MAX - && args->time_range[0] != args->time_range[1])) { - res = RES_BAD_ARG; - goto error; - } - - if(scene_is_2d(scn) != (SDIS_XD_DIMENSION==2)) { - res = RES_BAD_ARG; - goto error; - } + if(!scn) { res = RES_BAD_ARG; goto error; } + if(!out_estimator) { res = RES_BAD_ARG; goto error; } + res = check_compute_power_args(args); + if(res != RES_OK) goto error; + res = XD(scene_check_dimensionality)(scn); + if(res != RES_OK) goto error; if(sdis_medium_get_type(args->medium) != SDIS_SOLID) { log_err(scn->dev, "Could not compute mean power on a non solid medium.\n"); @@ -554,32 +603,27 @@ XD(compute_power) goto error; } - /* Create the RNG proxy */ - if(args->rng_state) { - res = ssp_rng_proxy_create_from_rng(scn->dev->allocator, args->rng_state, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } else { - res = ssp_rng_proxy_create(scn->dev->allocator, SSP_RNG_MT19937_64, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } +#ifdef SDIS_ENABLE_MPI + is_master_process = !scn->dev->use_mpi || scn->dev->mpi_rank == 0; +#endif - /* Create the per thread RNG */ - rngs = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*rngs)); - if(!rngs) { res = RES_MEM_ERR; goto error; } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs+i); - if(res != RES_OK) goto error; - } + nthreads = scn->dev->nthreads; + allocator = scn->dev->allocator; + + /* Create the per thread RNGs */ + res = create_per_thread_rng + (scn->dev, args->rng_state, &rng_proxy, &per_thread_rng); + if(res != RES_OK) goto error; + + /* Allocate the per process progress status */ + res = alloc_process_progress(scn->dev, &progress); + if(res != RES_OK) goto error; /* Create the per thread accumulators */ - acc_mpows = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(*acc_mpows)); - if(!acc_mpows) { res = RES_MEM_ERR; goto error; } - acc_times = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(*acc_times)); - if(!acc_times) { res = RES_MEM_ERR; goto error; } + per_thread_acc_mpow = MEM_CALLOC(allocator, nthreads, sizeof(struct accum)); + per_thread_acc_time = MEM_CALLOC(allocator, nthreads, sizeof(struct accum)); + if(!per_thread_acc_mpow) { res = RES_MEM_ERR; goto error; } + if(!per_thread_acc_time) { res = RES_MEM_ERR; goto error; } /* Compute the cumulative of the spreads of the enclosures surrounding the * submitted medium */ @@ -592,20 +636,33 @@ XD(compute_power) spread = darray_enclosure_cumul_cdata_get(&cumul) [darray_enclosure_cumul_size_get(&cumul)-1].cumul; - /* Create the estimator */ - res = estimator_create(scn->dev, SDIS_ESTIMATOR_POWER, &estimator); - if(res != RES_OK) goto error; + /* Create the estimator on the master process only. No estimator is needed + * for non master process */ + if(is_master_process) { + res = estimator_create(scn->dev, SDIS_ESTIMATOR_POWER, &estimator); + if(res != RES_OK) goto error; + } + + /* Synchronise the processes */ + process_barrier(scn->dev); + + #define PROGRESS_MSG "Computing mean power: " + print_progress(scn->dev, progress, PROGRESS_MSG); - nrealisations = args->nrealisations; + /* Begin time registration of the computation */ + time_current(&time0); + + /* Here we go! Launch the Monte Carlo estimation */ + nrealisations = compute_process_realisations_count(scn->dev, args->nrealisations); omp_set_num_threads((int)scn->dev->nthreads); #pragma omp parallel for schedule(static) for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { struct time t0, t1; struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - struct accum* acc_mpow = &acc_mpows[ithread]; - struct accum* acc_time = &acc_times[ithread]; + struct ssp_rng* rng = per_thread_rng[ithread]; + struct accum* acc_mpow = &per_thread_acc_mpow[ithread]; + struct accum* acc_time = &per_thread_acc_time[ithread]; const struct enclosure* enc = NULL; double power = 0; double usec = 0; @@ -646,52 +703,78 @@ XD(compute_power) n = (size_t)ATOMIC_INCR(&nsolved_realisations); pcent = (int)((double)n * 100.0 / (double)nrealisations + 0.5/*round*/); #pragma omp critical - if(pcent > progress) { - progress = pcent; - log_info(scn->dev, "Computing mean power: %3d%%\r", progress); + if(pcent > progress[0]) { + progress[0] = pcent; + print_progress_update(scn->dev, progress, PROGRESS_MSG); } exit_it: continue; error_it: goto exit_it; } + /* Synchronise the processes */ + process_barrier(scn->dev); + + res = gather_res_T(scn->dev, (res_T)res); if(res != RES_OK) goto error; - /* Add a new line after the progress status */ - log_info(scn->dev, "Computing mean power: %3d%%\n", progress); + print_progress_update(scn->dev, progress, PROGRESS_MSG); + log_info(scn->dev, "\n"); + #undef PROGRESS_MSG + + /* Report computation time */ + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Mean power computed in in %s.\n", buf); + + /* Gather the RNG proxy sequence IDs and ensure that the RNG proxy state of + * the master process is greater than the RNG proxy state of all other + * processes */ + res = gather_rng_proxy_sequence_id(scn->dev, rng_proxy); + if(res != RES_OK) goto error; /* Setup the estimated mean power */ { struct accum acc_mpow; struct accum acc_time; - sum_accums(acc_mpows, scn->dev->nthreads, &acc_mpow); - sum_accums(acc_times, scn->dev->nthreads, &acc_time); - ASSERT(acc_mpow.count == acc_time.count); - - estimator_setup_realisations_count(estimator, nrealisations, acc_mpow.count); - estimator_setup_realisation_time(estimator, acc_time.sum, acc_time.sum2); - estimator_setup_power - (estimator, acc_mpow.sum, acc_mpow.sum2, spread, args->time_range); - res = estimator_save_rng_state(estimator, rng_proxy); - if(res != RES_OK) goto error; + + time_current(&time0); + + #define GATHER_ACCUMS(Msg, Acc) { \ + res = gather_accumulators(scn->dev, Msg, per_thread_##Acc, &Acc); \ + if(res != RES_OK) goto error; \ + } (void)0 + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_MEAN_POWER, acc_mpow); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TIME, acc_time); + #undef GATHER_ACCUMS + + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Accumulators gathered in %s.\n", buf); + + /* Return an estimator only on master process */ + if(is_master_process) { + ASSERT(acc_mpow.count == acc_time.count); + estimator_setup_realisations_count(estimator, args->nrealisations, acc_mpow.count); + estimator_setup_realisation_time(estimator, acc_time.sum, acc_time.sum2); + estimator_setup_power + (estimator, acc_mpow.sum, acc_mpow.sum2, spread, args->time_range); + res = estimator_save_rng_state(estimator, rng_proxy); + if(res != RES_OK) goto error; + } } exit: - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) {if(rngs[i]) SSP(rng_ref_put(rngs[i]));} - MEM_RM(scn->dev->allocator, rngs); - } - if(acc_mpows) MEM_RM(scn->dev->allocator, acc_mpows); - if(acc_times) MEM_RM(scn->dev->allocator, acc_times); + if(per_thread_rng) release_per_thread_rng(scn->dev, per_thread_rng); + if(progress) free_process_progress(scn->dev, progress); + if(per_thread_acc_mpow) MEM_RM(scn->dev->allocator, per_thread_acc_mpow); + if(per_thread_acc_time) MEM_RM(scn->dev->allocator, per_thread_acc_time); if(cumul_is_init) darray_enclosure_cumul_release(&cumul); if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); if(out_estimator) *out_estimator = estimator; return (res_T)res; error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } + if(estimator) { SDIS(estimator_ref_put(estimator)); estimator = NULL; } goto exit; } diff --git a/src/sdis_solve_probe_Xd.h b/src/sdis_solve_probe_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -13,6 +13,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/>. */ +#include "sdis_c.h" #include "sdis_device_c.h" #include "sdis_estimator_c.h" #include "sdis_log.h" @@ -72,34 +73,41 @@ XD(solve_probe) struct sdis_green_function** out_green, /* May be NULL <=> No green func */ struct sdis_estimator** out_estimator) /* May be NULL <=> No estimator */ { + /* Time registration */ + struct time time0, time1; + char buf[128]; /* Temporary buffer used to store formated time */ + + /* Device variables */ + struct mem_allocator* allocator = NULL; + size_t nthreads = 0; + + /* Stardis variables */ struct sdis_medium* medium = NULL; struct sdis_estimator* estimator = NULL; struct sdis_green_function* green = NULL; - struct sdis_green_function** greens = NULL; + struct sdis_green_function** per_thread_green = NULL; + + /* Random Number generator */ struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - struct accum* acc_temps = NULL; - struct accum* acc_times = NULL; + struct ssp_rng** per_thread_rng = NULL; + + /* Miscellaneous */ + struct accum* per_thread_acc_temp = NULL; + struct accum* per_thread_acc_time = NULL; size_t nrealisations = 0; int64_t irealisation = 0; - size_t i; - int progress = 0; + int32_t* progress = NULL; /* Per process progress bar */ int register_paths = SDIS_HEAT_PATH_NONE; + int is_master_process = 1; ATOMIC nsolved_realisations = 0; ATOMIC res = RES_OK; - if(!scn) { - res = RES_BAD_ARG; - goto error; - } - + if(!scn) { res = RES_BAD_ARG; goto error; } + if(!out_estimator && !out_green) { res = RES_BAD_ARG; goto error; } res = check_solve_probe_args(args); if(res != RES_OK) goto error; - - if(!out_estimator && !out_green) { - res = RES_BAD_ARG; - goto error; - } + res = XD(scene_check_dimensionality)(scn); + if(res != RES_OK) goto error; if(out_green && args->picard_order != 1) { log_err(scn->dev, "%s: the evaluation of the green function does not make " @@ -110,38 +118,27 @@ XD(solve_probe) goto error; } -#if SDIS_XD_DIMENSION == 2 - if(scene_is_2d(scn) == 0) { res = RES_BAD_ARG; goto error; } -#else - if(scene_is_2d(scn) != 0) { res = RES_BAD_ARG; goto error; } +#ifdef SDIS_ENABLE_MPI + is_master_process = !scn->dev->use_mpi || scn->dev->mpi_rank == 0; #endif - /* Create the proxy RNG */ - if(args->rng_state) { - res = ssp_rng_proxy_create_from_rng(scn->dev->allocator, args->rng_state, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } else { - res = ssp_rng_proxy_create(scn->dev->allocator, SSP_RNG_MT19937_64, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } + nthreads = scn->dev->nthreads; + allocator = scn->dev->allocator; - /* Create the per thread RNG */ - rngs = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*rngs)); - if(!rngs) { res = RES_MEM_ERR; goto error; } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs+i); - if(res != RES_OK) goto error; - } + /* Create the per thread RNGs */ + res = create_per_thread_rng + (scn->dev, args->rng_state, &rng_proxy, &per_thread_rng); + if(res != RES_OK) goto error; + + /* Allocate the per process progress status */ + res = alloc_process_progress(scn->dev, &progress); + if(res != RES_OK) goto error; /* Create the per thread accumulators */ - acc_temps = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(*acc_temps)); - if(!acc_temps) { res = RES_MEM_ERR; goto error; } - acc_times = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(*acc_times)); - if(!acc_times) { res = RES_MEM_ERR; goto error; } + per_thread_acc_temp = MEM_CALLOC(allocator, nthreads, sizeof(struct accum)); + per_thread_acc_time = MEM_CALLOC(allocator, nthreads, sizeof(struct accum)); + if(!per_thread_acc_temp) { res = RES_MEM_ERR; goto error; } + if(!per_thread_acc_time) { res = RES_MEM_ERR; goto error; } /* Retrieve the medium in which the submitted position lies */ res = scene_get_medium(scn, args->position, NULL, &medium); @@ -149,32 +146,39 @@ XD(solve_probe) /* Create the per thread green function */ if(out_green) { - greens = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*greens)); - if(!greens) { res = RES_MEM_ERR; goto error; } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = green_function_create(scn, &greens[i]); - if(res != RES_OK) goto error; - } + res = create_per_thread_green_function(scn, &per_thread_green); + if(res != RES_OK) goto error; } - /* Create the estimator */ - if(out_estimator) { + /* Create the estimator on the master process only. No estimator is needed + * for non master process */ + if(out_estimator && is_master_process) { res = estimator_create(scn->dev, SDIS_ESTIMATOR_TEMPERATURE, &estimator); if(res != RES_OK) goto error; } + /* Synchronise the processes */ + process_barrier(scn->dev); + + #define PROGRESS_MSG "Solving probe temperature: " + print_progress(scn->dev, progress, PROGRESS_MSG); + + /* Begin time registration of the computation */ + time_current(&time0); + /* Here we go! Launch the Monte Carlo estimation */ - nrealisations = args->nrealisations; - register_paths = out_estimator ? args->register_paths : SDIS_HEAT_PATH_NONE; + nrealisations = compute_process_realisations_count(scn->dev, args->nrealisations); + register_paths = out_estimator && is_master_process + ? args->register_paths : SDIS_HEAT_PATH_NONE; omp_set_num_threads((int)scn->dev->nthreads); #pragma omp parallel for schedule(static) for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { struct probe_realisation_args realis_args = PROBE_REALISATION_ARGS_NULL; struct time t0, t1; const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - struct accum* acc_temp = &acc_temps[ithread]; - struct accum* acc_time = &acc_times[ithread]; + struct ssp_rng* rng = per_thread_rng[ithread]; + struct accum* acc_temp = &per_thread_acc_temp[ithread]; + struct accum* acc_time = &per_thread_acc_time[ithread]; struct green_path_handle* pgreen_path = NULL; struct green_path_handle green_path = GREEN_PATH_HANDLE_NULL; struct sdis_heat_path* pheat_path = NULL; @@ -188,19 +192,18 @@ XD(solve_probe) if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ - /* Begin time registration */ + /* Begin time registration of the realisation */ time_current(&t0); time = sample_time(rng, args->time_range); if(out_green) { - res_local = green_function_create_path(greens[ithread], &green_path); - if(res_local != RES_OK) { - ATOMIC_SET(&res, res_local); - goto error_it; - } + res_local = green_function_create_path + (per_thread_green[ithread], &green_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); goto error_it; } pgreen_path = &green_path; } + if(register_paths) { heat_path_init(scn->dev->allocator, &heat_path); pheat_path = &heat_path; @@ -252,84 +255,106 @@ XD(solve_probe) acc_time->sum += usec; acc_time->sum2 += usec*usec; ++acc_time->count; } - /* Update progress */ + /* Update the progress status */ n = (size_t)ATOMIC_INCR(&nsolved_realisations); pcent = (int)((double)n * 100.0 / (double)nrealisations + 0.5/*round*/); + #pragma omp critical - if(pcent > progress) { - progress = pcent; - log_info(scn->dev, "Solving probe temperature: %3d%%\r", progress); + if(pcent > progress[0]) { + progress[0] = pcent; + print_progress_update(scn->dev, progress, PROGRESS_MSG); } + exit_it: if(pheat_path) heat_path_release(pheat_path); continue; error_it: goto exit_it; } + + /* Synchronise processes */ + process_barrier(scn->dev); + + res = gather_res_T(scn->dev, (res_T)res); if(res != RES_OK) goto error; - /* Add a new line after the progress status */ - log_info(scn->dev, "Solving probe temperature: %3d%%\n", progress); + print_progress_update(scn->dev, progress, PROGRESS_MSG); + log_info(scn->dev, "\n"); + #undef PROGRESS_MSG + + /* Report computation time */ + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Probe temperature solved in %s.\n", buf); + + /* Gather the RNG proxy sequence IDs and ensure that the RNG proxy state of + * the master process is greater than the RNG proxy state of all other + * processes */ + res = gather_rng_proxy_sequence_id(scn->dev, rng_proxy); + if(res != RES_OK) goto error; - /* Setup the estimated temperature and per realisation time */ + /* Setup the estimated values */ if(out_estimator) { struct accum acc_temp; struct accum acc_time; - sum_accums(acc_temps, scn->dev->nthreads, &acc_temp); - sum_accums(acc_times, scn->dev->nthreads, &acc_time); - ASSERT(acc_temp.count == acc_time.count); - - estimator_setup_realisations_count(estimator, nrealisations, acc_temp.count); - estimator_setup_temperature(estimator, acc_temp.sum, acc_temp.sum2); - estimator_setup_realisation_time(estimator, acc_time.sum, acc_time.sum2); - res = estimator_save_rng_state(estimator, rng_proxy); - if(res != RES_OK) goto error; + time_current(&time0); + + #define GATHER_ACCUMS(Msg, Acc) { \ + res = gather_accumulators(scn->dev, Msg, per_thread_##Acc, &Acc); \ + if(res != RES_OK) goto error; \ + } (void)0 + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TEMP, acc_temp); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TEMP, acc_time); + #undef GATHER_ACCUMS + + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Accumulators gathered in %s.\n", buf); + + /* Return an estimator only on master process */ + if(is_master_process) { + ASSERT(acc_temp.count == acc_time.count); + estimator_setup_realisations_count(estimator, args->nrealisations, acc_temp.count); + estimator_setup_temperature(estimator, acc_temp.sum, acc_temp.sum2); + estimator_setup_realisation_time(estimator, acc_time.sum, acc_time.sum2); + res = estimator_save_rng_state(estimator, rng_proxy); + if(res != RES_OK) goto error; + } } + /* Setup the green function */ if(out_green) { - struct accum acc_time; + time_current(&time0); - /* Redux the per thread green function into the green of the 1st thread */ - green = greens[0]; /* Return the green of the 1st thread */ - greens[0] = NULL; /* Make invalid the 1st green for 'on exit' clean up*/ - res = green_function_redux_and_clear(green, greens+1, scn->dev->nthreads-1); + res = gather_green_functions + (scn, rng_proxy, per_thread_green, per_thread_acc_time, &green); if(res != RES_OK) goto error; - /* Finalize the estimated green */ - sum_accums(acc_times, scn->dev->nthreads, &acc_time); - res = green_function_finalize(green, rng_proxy, &acc_time); - if(res != RES_OK) goto error; - } + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Green functions gathered in %s.\n", buf); -exit: - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(rngs[i]) SSP(rng_ref_put(rngs[i])); + /* Return a green function only on master process */ + if(!is_master_process) { + SDIS(green_function_ref_put(green)); + green = NULL; } - MEM_RM(scn->dev->allocator, rngs); } - if(greens) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(greens[i]) SDIS(green_function_ref_put(greens[i])); - } - MEM_RM(scn->dev->allocator, greens); - } - if(acc_temps) MEM_RM(scn->dev->allocator, acc_temps); - if(acc_times) MEM_RM(scn->dev->allocator, acc_times); + +exit: + if(per_thread_rng) release_per_thread_rng(scn->dev, per_thread_rng); + if(per_thread_green) release_per_thread_green_function(scn, per_thread_green); + if(progress) free_process_progress(scn->dev, progress); + if(per_thread_acc_temp) MEM_RM(scn->dev->allocator, per_thread_acc_temp); + if(per_thread_acc_time) MEM_RM(scn->dev->allocator, per_thread_acc_time); if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); if(out_green) *out_green = green; if(out_estimator) *out_estimator = estimator; return (res_T)res; error: - if(green) { - SDIS(green_function_ref_put(green)); - green = NULL; - } - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } + if(estimator) { SDIS(estimator_ref_put(estimator)); estimator = NULL; } + if(green) { SDIS(green_function_ref_put(green)); green = NULL; } goto exit; } diff --git a/src/sdis_solve_probe_boundary_Xd.h b/src/sdis_solve_probe_boundary_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -13,9 +13,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "sdis_c.h" #include "sdis_device_c.h" #include "sdis_estimator_c.h" +#include "sdis_interface_c.h" #include "sdis_log.h" +#include "sdis_green.h" #include "sdis_medium_c.h" #include "sdis_misc.h" #include "sdis_realisation.h" @@ -66,6 +69,34 @@ check_solve_probe_boundary_args return RES_OK; } +static INLINE res_T +check_solve_probe_boundary_flux_args + (const struct sdis_solve_probe_boundary_flux_args* args) +{ + if(!args) return RES_BAD_ARG; + + /* Check #realisations */ + if(!args->nrealisations || args->nrealisations > INT64_MAX) { + return RES_BAD_ARG; + } + + /* Check time range */ + if(args->time_range[0] < 0 || args->time_range[1] < args->time_range[0]) { + return RES_BAD_ARG; + } + if(args->time_range[1] > DBL_MAX + && args->time_range[0] != args->time_range[1]) { + return RES_BAD_ARG; + } + + /* Check picard order */ + if(args->picard_order < 1) { + return RES_BAD_ARG; + } + + return RES_OK; +} + #endif /* SDIS_SOLVE_PROBE_BOUNDARY_XD_H */ /******************************************************************************* @@ -78,33 +109,44 @@ XD(solve_probe_boundary) struct sdis_green_function** out_green, struct sdis_estimator** out_estimator) { + /* Time registration */ + struct time time0, time1; + char buf[128]; /* Temporary buffer used to store formated time */ + + /* Device variables */ + struct mem_allocator* allocator = NULL; + size_t nthreads = 0; + + /* Stardis variables */ struct sdis_estimator* estimator = NULL; struct sdis_green_function* green = NULL; - struct sdis_green_function** greens = NULL; + struct sdis_green_function** per_thread_green = NULL; + + /* Random number generator */ struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - struct accum* acc_temps = NULL; - struct accum* acc_times = NULL; + struct ssp_rng** per_thread_rng = NULL; + + /* Miscellaneous */ + struct accum* per_thread_acc_temp = NULL; + struct accum* per_thread_acc_time = NULL; size_t nrealisations = 0; int64_t irealisation = 0; - size_t i; - int progress = 0; + int32_t* progress = NULL; /* Per process progress bar */ int register_paths = SDIS_HEAT_PATH_NONE; + int is_master_process = 1; ATOMIC nsolved_realisations = 0; ATOMIC res = RES_OK; - if(!scn) { - res = RES_BAD_ARG; - goto error; - } - + if(!scn) { res = RES_BAD_ARG; goto error; } + if(!out_estimator && !out_green) { res = RES_BAD_ARG; goto error; } res = check_solve_probe_boundary_args(args); if(res != RES_OK) goto error; - - if(!out_estimator && !out_green) { - res = RES_BAD_ARG; - goto error; - } + res = XD(check_primitive_uv)(scn->dev, args->uv); + if(res != RES_OK) goto error; + res = XD(scene_check_dimensionality)(scn); + if(res != RES_OK) goto error; + res = scene_check_primitive_index(scn, args->iprim); + if(res != RES_OK) goto error; if(out_green && args->picard_order != 1) { log_err(scn->dev, "%s: the evaluation of the green function does not make " @@ -115,108 +157,63 @@ XD(solve_probe_boundary) goto error; } -#if SDIS_XD_DIMENSION == 2 - if(scene_is_2d(scn) == 0) { res = RES_BAD_ARG; goto error; } -#else - if(scene_is_2d(scn) != 0) { res = RES_BAD_ARG; goto error; } +#ifdef SDIS_ENABLE_MPI + is_master_process = !scn->dev->use_mpi || scn->dev->mpi_rank == 0; #endif - /* Check the primitive identifier */ - if(args->iprim >= scene_get_primitives_count(scn)) { - log_err(scn->dev, - "%s: invalid primitive identifier `%lu'. " - "It must be in the [0 %lu] range.\n", - FUNC_NAME, - (unsigned long)args->iprim, - (unsigned long)scene_get_primitives_count(scn)-1); - res = RES_BAD_ARG; - goto error; - } + nthreads = scn->dev->nthreads; + allocator = scn->dev->allocator; - /* Check parametric coordinates */ -#if SDIS_XD_DIMENSION == 2 - { - const double v = CLAMP(1.0 - args->uv[0], 0, 1); - if(args->uv[0] < 0 || args->uv[0] > 1 || !eq_eps(args->uv[0]+v, 1, 1.e-6)) { - log_err(scn->dev, - "%s: invalid parametric coordinates %g." - "u + (1-u) must be equal to 1 with u [0, 1].\n", - FUNC_NAME, args->uv[0]); - res = RES_BAD_ARG; - goto error; - } - } -#else /* SDIS_XD_DIMENSION == 3 */ - { - const double w = CLAMP(1 - args->uv[0] - args->uv[1], 0, 1); - if(args->uv[0] < 0 || args->uv[1] < 0 || args->uv[0] > 1 || args->uv[1] > 1 - || !eq_eps(w + args->uv[0] + args->uv[1], 1, 1.e-6)) { - log_err(scn->dev, - "%s: invalid parametric coordinates [%g, %g]. " - "u + v + (1-u-v) must be equal to 1 with u and v in [0, 1].\n", - FUNC_NAME, args->uv[0], args->uv[1]); - res = RES_BAD_ARG; - goto error; - } - } -#endif - - /* Create the proxy RNG */ - if(args->rng_state) { - res = ssp_rng_proxy_create_from_rng(scn->dev->allocator, args->rng_state, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } else { - res = ssp_rng_proxy_create(scn->dev->allocator, SSP_RNG_MT19937_64, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } + /* Create the per thread RNGs */ + res = create_per_thread_rng + (scn->dev, args->rng_state, &rng_proxy, &per_thread_rng); + if(res != RES_OK) goto error; - /* Create the per thread RNG */ - rngs = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(struct ssp_rng*)); - if(!rngs) { res = RES_MEM_ERR; goto error; } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs+i); - if(res != RES_OK) goto error; - } + /* Allocate the per process progress status */ + res = alloc_process_progress(scn->dev, &progress); + if(res != RES_OK) goto error; - /* Create the per thread accumulator */ - acc_temps = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(*acc_temps)); - if(!acc_temps) { res = RES_MEM_ERR; goto error; } - acc_times = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(*acc_times)); - if(!acc_times) { res = RES_MEM_ERR; goto error; } + /* Create the per thread accumulators */ + per_thread_acc_temp = MEM_CALLOC(allocator, nthreads, sizeof(struct accum)); + per_thread_acc_time = MEM_CALLOC(allocator, nthreads, sizeof(struct accum)); + if(!per_thread_acc_temp) { res = RES_MEM_ERR; goto error; } + if(!per_thread_acc_time) { res = RES_MEM_ERR; goto error; } /* Create the per thread green function */ if(out_green) { - greens = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*greens)); - if(!greens) { res = RES_MEM_ERR; goto error; } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = green_function_create(scn, &greens[i]); - if(res != RES_OK) goto error; - } + res = create_per_thread_green_function(scn, &per_thread_green); + if(res != RES_OK) goto error; } - /* Create the estimator */ - if(out_estimator) { + /* Create the estimator on the master process only. No estimator is needed + * for non master process */ + if(out_estimator && is_master_process) { res = estimator_create(scn->dev, SDIS_ESTIMATOR_TEMPERATURE, &estimator); if(res != RES_OK) goto error; } + /* Synchronise the processes */ + process_barrier(scn->dev); + + #define PROGRESS_MSG "Solving surface probe temperature: " + print_progress(scn->dev, progress, PROGRESS_MSG); + + /* Begin time registration of the computation */ + time_current(&time0); + /* Here we go! Launch the Monte Carlo estimation */ - nrealisations = args->nrealisations; - register_paths = out_estimator ? args->register_paths : SDIS_HEAT_PATH_NONE; + nrealisations = compute_process_realisations_count(scn->dev, args->nrealisations); + register_paths = out_estimator && is_master_process + ? args->register_paths : SDIS_HEAT_PATH_NONE; omp_set_num_threads((int)scn->dev->nthreads); #pragma omp parallel for schedule(static) for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { struct boundary_realisation_args realis_args = BOUNDARY_REALISATION_ARGS_NULL; struct time t0, t1; const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - struct accum* acc_temp = &acc_temps[ithread]; - struct accum* acc_time = &acc_times[ithread]; + struct ssp_rng* rng = per_thread_rng[ithread]; + struct accum* acc_temp = &per_thread_acc_temp[ithread]; + struct accum* acc_time = &per_thread_acc_time[ithread]; struct green_path_handle* pgreen_path = NULL; struct green_path_handle green_path = GREEN_PATH_HANDLE_NULL; struct sdis_heat_path* pheat_path = NULL; @@ -235,11 +232,9 @@ XD(solve_probe_boundary) time = sample_time(rng, args->time_range); if(out_green) { - res_local = green_function_create_path(greens[ithread], &green_path); - if(res_local != RES_OK) { - ATOMIC_SET(&res, res_local); - goto error_it; - } + res_local = green_function_create_path + (per_thread_green[ithread], &green_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); goto error_it; } pgreen_path = &green_path; } @@ -302,9 +297,9 @@ XD(solve_probe_boundary) n = (size_t)ATOMIC_INCR(&nsolved_realisations); pcent = (int)((double)n * 100.0 / (double)nrealisations + 0.5/*round*/); #pragma omp critical - if(pcent > progress) { - progress = pcent; - log_info(scn->dev, "Solving probe boundary temperature: %3d%%\r", progress); + if(pcent > progress[0]) { + progress[0] = pcent; + print_progress_update(scn->dev, progress, PROGRESS_MSG); } exit_it: @@ -313,70 +308,88 @@ XD(solve_probe_boundary) error_it: goto exit_it; } + /* Synchronise processes */ + process_barrier(scn->dev); + + res = gather_res_T(scn->dev, (res_T)res); if(res != RES_OK) goto error; - /* Add a new line after the progress status */ - log_info(scn->dev, "Solving probe boundary temperature: %3d%%\n", progress); + print_progress_update(scn->dev, progress, PROGRESS_MSG); + log_info(scn->dev, "\n"); + #undef PROGRESS_MSG + + /* Report computation time */ + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Surface probe temperature solved in %s.\n", buf); + + /* Gather the RNG proxy sequence IDs and ensure that the RNG proxy state of + * the master process is greater than the RNG proxy state of all other + * processes */ + res = gather_rng_proxy_sequence_id(scn->dev, rng_proxy); + if(res != RES_OK) goto error; /* Setup the estimated temperature and per realisation time */ if(out_estimator) { struct accum acc_temp; struct accum acc_time; - sum_accums(acc_temps, scn->dev->nthreads, &acc_temp); - sum_accums(acc_times, scn->dev->nthreads, &acc_time); - ASSERT(acc_temp.count == acc_time.count); - - estimator_setup_realisations_count(estimator, nrealisations, acc_temp.count); - estimator_setup_temperature(estimator, acc_temp.sum, acc_temp.sum2); - estimator_setup_realisation_time(estimator, acc_time.sum, acc_time.sum2); - res = estimator_save_rng_state(estimator, rng_proxy); - if(res != RES_OK) goto error; + time_current(&time0); + + #define GATHER_ACCUMS(Msg, Acc) { \ + res = gather_accumulators(scn->dev, Msg, per_thread_##Acc, &Acc); \ + if(res != RES_OK) goto error; \ + } (void)0 + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TEMP, acc_temp); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TIME, acc_time); + #undef GATHER_ACCUMS + + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Accumulators gathered in %s.\n", buf); + + /* Return an estimator only on master process */ + if(is_master_process) { + ASSERT(acc_temp.count == acc_time.count); + estimator_setup_realisations_count(estimator, args->nrealisations, acc_temp.count); + estimator_setup_temperature(estimator, acc_temp.sum, acc_temp.sum2); + estimator_setup_realisation_time(estimator, acc_time.sum, acc_time.sum2); + res = estimator_save_rng_state(estimator, rng_proxy); + if(res != RES_OK) goto error; + } } if(out_green) { - struct accum acc_time; + time_current(&time0); - /* Redux the per thread green function into the green of the 1st thread */ - green = greens[0]; /* Return the green of the 1st thread */ - greens[0] = NULL; /* Make invalid the 1st green for 'on exit' clean up*/ - res = green_function_redux_and_clear(green, greens+1, scn->dev->nthreads-1); + res = gather_green_functions + (scn, rng_proxy, per_thread_green, per_thread_acc_time, &green); if(res != RES_OK) goto error; - /* Finalize the estimated green */ - sum_accums(acc_times, scn->dev->nthreads, &acc_time); - res = green_function_finalize(green, rng_proxy, &acc_time); - if(res != RES_OK) goto error; - } + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Green functions gathered in %s.\n", buf); -exit: - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(rngs[i]) SSP(rng_ref_put(rngs[i])); - } - MEM_RM(scn->dev->allocator, rngs); - } - if(greens) { - FOR_EACH(i, 0, scn->dev->nthreads) { - if(greens[i]) SDIS(green_function_ref_put(greens[i])); + /* Return a green function only on master process */ + if(!is_master_process) { + SDIS(green_function_ref_put(green)); + green = NULL; } - MEM_RM(scn->dev->allocator, greens); } - if(acc_temps) MEM_RM(scn->dev->allocator, acc_temps); - if(acc_times) MEM_RM(scn->dev->allocator, acc_times); + +exit: + if(per_thread_rng) release_per_thread_rng(scn->dev, per_thread_rng); + if(per_thread_green) release_per_thread_green_function(scn, per_thread_green); + if(progress) free_process_progress(scn->dev, progress); + if(per_thread_acc_temp) MEM_RM(scn->dev->allocator, per_thread_acc_temp); + if(per_thread_acc_time) MEM_RM(scn->dev->allocator, per_thread_acc_time); if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); if(out_green) *out_green = green; if(out_estimator) *out_estimator = estimator; return (res_T)res; error: - if(green) { - SDIS(green_function_ref_put(green)); - green = NULL; - } - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } + if(estimator) { SDIS(estimator_ref_put(estimator)); estimator = NULL; } + if(green) { SDIS(green_function_ref_put(green)); green = NULL; } goto exit; } @@ -386,86 +399,63 @@ XD(solve_probe_boundary_flux) const struct sdis_solve_probe_boundary_flux_args* args, struct sdis_estimator** out_estimator) { + /* Time registration */ + struct time time0, time1; + char buf[128]; /* Temporary buffer used to store formated time */ + + /* Stardis variables */ + const struct sdis_interface* interf = NULL; + const struct sdis_medium* fmd = NULL; + const struct sdis_medium* bmd = NULL; struct sdis_estimator* estimator = NULL; + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + enum sdis_side solid_side = SDIS_SIDE_NULL__; + enum sdis_side fluid_side = SDIS_SIDE_NULL__; + + /* Random number generator */ struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - const struct sdis_interface* interf; - const struct sdis_medium *fmd, *bmd; - enum sdis_side solid_side, fluid_side; - struct sdis_interface_fragment frag; - struct accum* acc_tp = NULL; /* Per thread temperature accumulator */ - struct accum* acc_ti = NULL; /* Per thread realisation time */ - struct accum* acc_fl = NULL; /* Per thread flux accumulator */ - struct accum* acc_fc = NULL; /* Per thread convective flux accumulator */ - struct accum* acc_fr = NULL; /* Per thread radiative flux accumulator */ - struct accum* acc_fi = NULL; /* Per thread imposed flux accumulator */ + struct ssp_rng** per_thread_rng = NULL; + + /* Per thread accumulators */ + struct accum* per_thread_acc_tp = NULL; /* Temperature accumulator */ + struct accum* per_thread_acc_ti = NULL; /* Realisation time */ + struct accum* per_thread_acc_fl = NULL; /* Flux accumulator */ + struct accum* per_thread_acc_fc = NULL; /* Convective flux accumulator */ + struct accum* per_thread_acc_fr = NULL; /* Radiative flux accumulator */ + struct accum* per_thread_acc_fi = NULL; /* Imposed flux accumulator */ + + /* Gathered accumulator */ + struct accum acc_tp = ACCUM_NULL; + struct accum acc_ti = ACCUM_NULL; + struct accum acc_fl = ACCUM_NULL; + struct accum acc_fc = ACCUM_NULL; + struct accum acc_fr = ACCUM_NULL; + struct accum acc_fi = ACCUM_NULL; + + /* Miscellaneous */ size_t nrealisations = 0; int64_t irealisation = 0; - size_t i; - int progress = 0; + int32_t* progress = NULL; /* Per process progress bar */ + int is_master_process = 1; ATOMIC nsolved_realisations = 0; ATOMIC res = RES_OK; - if(!scn || !args || !args->nrealisations || args->nrealisations > INT64_MAX - || args->time_range[0] < 0 || args->time_range[1] < args->time_range[0] - || (args->time_range[1]>DBL_MAX && args->time_range[0] != args->time_range[1]) - || !out_estimator) { - res = RES_BAD_ARG; - goto error; - } - -#if SDIS_XD_DIMENSION == 2 - if(scene_is_2d(scn) == 0) { res = RES_BAD_ARG; goto error; } -#else - if(scene_is_2d(scn) != 0) { res = RES_BAD_ARG; goto error; } -#endif - - /* Check the primitive identifier */ - if(args->iprim >= scene_get_primitives_count(scn)) { - log_err(scn->dev, - "%s: invalid primitive identifier `%lu'. " - "It must be in the [0 %lu] range.\n", - FUNC_NAME, - (unsigned long)args->iprim, - (unsigned long)scene_get_primitives_count(scn)-1); - res = RES_BAD_ARG; - goto error; - } + if(!scn) { res = RES_BAD_ARG; goto error; } + if(!out_estimator) { res = RES_BAD_ARG; goto error; } + res = check_solve_probe_boundary_flux_args(args); + if(res != RES_OK) goto error; + res = XD(check_primitive_uv)(scn->dev, args->uv); + if(res != RES_OK) goto error; + res = XD(scene_check_dimensionality)(scn); + if(res != RES_OK) goto error; + res = scene_check_primitive_index(scn, args->iprim); + if(res != RES_OK) goto error; - /* Check parametric coordinates */ - if(scene_is_2d(scn)) { - const double v = CLAMP(1.0 - args->uv[0], 0, 1); - if(args->uv[0] < 0 || args->uv[0] > 1 - || !eq_eps(args->uv[0] + v, 1, 1.e-6)) { - log_err(scn->dev, - "%s: invalid parametric coordinates %g. " - "u + (1-u) must be equal to 1 with u [0, 1].\n", - FUNC_NAME, args->uv[0]); - res = RES_BAD_ARG; - goto error; - } - } else { - const double w = CLAMP(1 - args->uv[0] - args->uv[1], 0, 1); - if(args->uv[0] < 0 - || args->uv[1] < 0 - || args->uv[0] > 1 - || args->uv[1] > 1 - || !eq_eps(w + args->uv[0] + args->uv[1], 1, 1.e-6)) { - log_err(scn->dev, - "%s: invalid parametric coordinates [%g, %g]. " - "u + v + (1-u-v) must be equal to 1 with u and v in [0, 1].\n", - FUNC_NAME, args->uv[0], args->uv[1]); - res = RES_BAD_ARG; - goto error; - } - } /* Check medium is fluid on one side and solid on the other */ interf = scene_get_interface(scn, (unsigned)args->iprim); fmd = interface_get_medium(interf, SDIS_FRONT); bmd = interface_get_medium(interf, SDIS_BACK); - if(!fmd || !bmd - || ( !(fmd->type == SDIS_FLUID && bmd->type == SDIS_SOLID) - && !(fmd->type == SDIS_SOLID && bmd->type == SDIS_FLUID))) { + if(!fmd || !bmd || fmd->type == bmd->type) { log_err(scn->dev, "%s: Attempt to compute a flux at a %s-%s interface.\n", FUNC_NAME, @@ -477,37 +467,30 @@ XD(solve_probe_boundary_flux) solid_side = (fmd->type == SDIS_SOLID) ? SDIS_FRONT : SDIS_BACK; fluid_side = (fmd->type == SDIS_FLUID) ? SDIS_FRONT : SDIS_BACK; - /* Create the proxy RNG */ - if(args->rng_state) { - res = ssp_rng_proxy_create_from_rng(scn->dev->allocator, args->rng_state, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } else { - res = ssp_rng_proxy_create(scn->dev->allocator, SSP_RNG_MT19937_64, - scn->dev->nthreads, &rng_proxy); - if(res != RES_OK) goto error; - } +#ifdef SDIS_ENABLE_MPI + is_master_process = !scn->dev->use_mpi || scn->dev->mpi_rank == 0; +#endif - /* Create the per thread RNG */ - rngs = MEM_CALLOC - (scn->dev->allocator, scn->dev->nthreads, sizeof(struct ssp_rng*)); - if(!rngs) { res = RES_MEM_ERR; goto error; } - FOR_EACH(i, 0, scn->dev->nthreads) { - res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs + i); - if(res != RES_OK) goto error; - } + /* Create the per thread RNGs */ + res = create_per_thread_rng + (scn->dev, args->rng_state, &rng_proxy, &per_thread_rng); + if(res != RES_OK) goto error; + + /* Allocate the per process progress status */ + res = alloc_process_progress(scn->dev, &progress); + if(res != RES_OK) goto error; - /* Create the per thread accumulator */ + /* Create the per thread accumulators */ #define ALLOC_ACCUMS(Dst) { \ Dst = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*Dst)); \ if(!Dst) { res = RES_MEM_ERR; goto error; } \ } (void)0 - ALLOC_ACCUMS(acc_tp); - ALLOC_ACCUMS(acc_ti); - ALLOC_ACCUMS(acc_fc); - ALLOC_ACCUMS(acc_fl); - ALLOC_ACCUMS(acc_fr); - ALLOC_ACCUMS(acc_fi); + ALLOC_ACCUMS(per_thread_acc_tp); + ALLOC_ACCUMS(per_thread_acc_ti); + ALLOC_ACCUMS(per_thread_acc_fc); + ALLOC_ACCUMS(per_thread_acc_fl); + ALLOC_ACCUMS(per_thread_acc_fr); + ALLOC_ACCUMS(per_thread_acc_fi); #undef ALLOC_ACCUMS /* Prebuild the interface fragment */ @@ -515,12 +498,23 @@ XD(solve_probe_boundary_flux) (&frag, scn, (unsigned)args->iprim, args->uv, fluid_side); if(res != RES_OK) goto error; - /* Create the estimator */ - res = estimator_create(scn->dev, SDIS_ESTIMATOR_FLUX, &estimator); - if(res != RES_OK) goto error; + if(is_master_process) { + /* Create the estimator */ + res = estimator_create(scn->dev, SDIS_ESTIMATOR_FLUX, &estimator); + if(res != RES_OK) goto error; + } + + /* Synchronise the processes */ + process_barrier(scn->dev); + + #define PROGRESS_MSG "Solving surface probe flux: " + print_progress(scn->dev, progress, PROGRESS_MSG); + + /* Begin time registration of the computation */ + time_current(&time0); /* Here we go! Launch the Monte Carlo estimation */ - nrealisations = args->nrealisations; + nrealisations = compute_process_realisations_count(scn->dev, args->nrealisations); omp_set_num_threads((int)scn->dev->nthreads); #pragma omp parallel for schedule(static) for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { @@ -528,13 +522,13 @@ XD(solve_probe_boundary_flux) BOUNDARY_FLUX_REALISATION_ARGS_NULL; struct time t0, t1; const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - struct accum* acc_temp = &acc_tp[ithread]; - struct accum* acc_time = &acc_ti[ithread]; - struct accum* acc_flux = &acc_fl[ithread]; - struct accum* acc_fcon = &acc_fc[ithread]; - struct accum* acc_frad = &acc_fr[ithread]; - struct accum* acc_fimp = &acc_fi[ithread]; + struct ssp_rng* rng = per_thread_rng[ithread]; + struct accum* acc_temp = &per_thread_acc_tp[ithread]; + struct accum* acc_time = &per_thread_acc_ti[ithread]; + struct accum* acc_flux = &per_thread_acc_fl[ithread]; + struct accum* acc_fcon = &per_thread_acc_fc[ithread]; + struct accum* acc_frad = &per_thread_acc_fr[ithread]; + struct accum* acc_fimp = &per_thread_acc_fi[ithread]; double time, epsilon, hc, hr, imposed_flux, imposed_temp; int flux_mask = 0; struct bound_flux_result result = BOUND_FLUX_RESULT_NULL__; @@ -562,7 +556,8 @@ XD(solve_probe_boundary_flux) imposed_temp = interface_side_get_temperature(interf, &frag); if(imposed_temp >= 0) { /* Flux computation on T boundaries is not supported yet */ - log_err(scn->dev,"%s: Attempt to compute a flux at a Dirichlet boundary " + log_err(scn->dev, + "%s: Attempt to compute a flux at a Dirichlet boundary " "(not available yet).\n", FUNC_NAME); ATOMIC_SET(&res, RES_BAD_ARG); continue; @@ -629,60 +624,83 @@ XD(solve_probe_boundary_flux) n = (size_t)ATOMIC_INCR(&nsolved_realisations); pcent = (int)((double)n * 100.0 / (double)nrealisations + 0.5/*round*/); #pragma omp critical - if(pcent > progress) { - progress = pcent; - log_info(scn->dev, "Solving probe boundary flux: %3d%%\r", progress); + if(pcent > progress[0]) { + progress[0] = pcent; + print_progress_update(scn->dev, progress, PROGRESS_MSG); } } + /* Synchronise processes */ + process_barrier(scn->dev); + + res = gather_res_T(scn->dev, (res_T)res); if(res != RES_OK) goto error; - /* Add a new line after the progress status */ - log_info(scn->dev, "Solving probe boundary flux: %3d%%\n", progress); - - /* Redux the per thread accumulators */ - sum_accums(acc_tp, scn->dev->nthreads, &acc_tp[0]); - sum_accums(acc_ti, scn->dev->nthreads, &acc_ti[0]); - sum_accums(acc_fc, scn->dev->nthreads, &acc_fc[0]); - sum_accums(acc_fr, scn->dev->nthreads, &acc_fr[0]); - sum_accums(acc_fl, scn->dev->nthreads, &acc_fl[0]); - sum_accums(acc_fi, scn->dev->nthreads, &acc_fi[0]); - ASSERT(acc_tp[0].count == acc_fl[0].count); - ASSERT(acc_tp[0].count == acc_ti[0].count); - ASSERT(acc_tp[0].count == acc_fr[0].count); - ASSERT(acc_tp[0].count == acc_fc[0].count); - ASSERT(acc_tp[0].count == acc_fi[0].count); + print_progress_update(scn->dev, progress, PROGRESS_MSG); + log_info(scn->dev, "\n"); + #undef PROGRESS_MSG - /* Setup the estimated values */ - estimator_setup_realisations_count(estimator, nrealisations, acc_tp[0].count); - estimator_setup_temperature(estimator, acc_tp[0].sum, acc_tp[0].sum2); - estimator_setup_realisation_time(estimator, acc_ti[0].sum, acc_ti[0].sum2); - estimator_setup_flux(estimator, FLUX_CONVECTIVE, acc_fc[0].sum, acc_fc[0].sum2); - estimator_setup_flux(estimator, FLUX_RADIATIVE, acc_fr[0].sum, acc_fr[0].sum2); - estimator_setup_flux(estimator, FLUX_IMPOSED, acc_fi[0].sum, acc_fi[0].sum2); - estimator_setup_flux(estimator, FLUX_TOTAL, acc_fl[0].sum, acc_fl[0].sum2); - - res = estimator_save_rng_state(estimator, rng_proxy); + /* Report computation time */ + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Surface probe flux solved in %s.\n", buf); + + /* Gather the RNG proxy sequence IDs and ensure that the RNG proxy state of + * the master process is greater than the RNG proxy state of all other + * processes */ + res = gather_rng_proxy_sequence_id(scn->dev, rng_proxy); if(res != RES_OK) goto error; -exit: - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) {if(rngs[i]) SSP(rng_ref_put(rngs[i]));} - MEM_RM(scn->dev->allocator, rngs); + time_current(&time0); + + #define GATHER_ACCUMS(Msg, Acc) { \ + res = gather_accumulators(scn->dev, Msg, per_thread_##Acc, &Acc); \ + if(res != RES_OK) goto error; \ + } (void)0 + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TEMP, acc_tp); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TIME, acc_ti); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_FLUX_CONVECTIVE, acc_fc); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_FLUX_IMPOSED, acc_fi); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_FLUX_RADIATIVE, acc_fr); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_FLUX_TOTAL, acc_fl); + #undef GATHER_ACCUMS + + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Accumulators gathered in %s.\n", buf); + + /* Setup the estimated values */ + if(is_master_process) { + ASSERT(acc_tp.count == acc_fl.count); + ASSERT(acc_tp.count == acc_ti.count); + ASSERT(acc_tp.count == acc_fr.count); + ASSERT(acc_tp.count == acc_fc.count); + ASSERT(acc_tp.count == acc_fi.count); + estimator_setup_realisations_count(estimator, args->nrealisations, acc_tp.count); + estimator_setup_temperature(estimator, acc_tp.sum, acc_tp.sum2); + estimator_setup_realisation_time(estimator, acc_ti.sum, acc_ti.sum2); + estimator_setup_flux(estimator, FLUX_CONVECTIVE, acc_fc.sum, acc_fc.sum2); + estimator_setup_flux(estimator, FLUX_RADIATIVE, acc_fr.sum, acc_fr.sum2); + estimator_setup_flux(estimator, FLUX_IMPOSED, acc_fi.sum, acc_fi.sum2); + estimator_setup_flux(estimator, FLUX_TOTAL, acc_fl.sum, acc_fl.sum2); + + res = estimator_save_rng_state(estimator, rng_proxy); + if(res != RES_OK) goto error; } - if(acc_tp) MEM_RM(scn->dev->allocator, acc_tp); - if(acc_ti) MEM_RM(scn->dev->allocator, acc_ti); - if(acc_fc) MEM_RM(scn->dev->allocator, acc_fc); - if(acc_fr) MEM_RM(scn->dev->allocator, acc_fr); - if(acc_fl) MEM_RM(scn->dev->allocator, acc_fl); - if(acc_fi) MEM_RM(scn->dev->allocator, acc_fi); + +exit: + if(per_thread_rng) release_per_thread_rng(scn->dev, per_thread_rng); + if(per_thread_acc_tp) MEM_RM(scn->dev->allocator, per_thread_acc_tp); + if(per_thread_acc_ti) MEM_RM(scn->dev->allocator, per_thread_acc_ti); + if(per_thread_acc_fc) MEM_RM(scn->dev->allocator, per_thread_acc_fc); + if(per_thread_acc_fr) MEM_RM(scn->dev->allocator, per_thread_acc_fr); + if(per_thread_acc_fl) MEM_RM(scn->dev->allocator, per_thread_acc_fl); + if(per_thread_acc_fi) MEM_RM(scn->dev->allocator, per_thread_acc_fi); + if(progress) free_process_progress(scn->dev, progress); if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); if(out_estimator) *out_estimator = estimator; return (res_T)res; error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } + if(estimator) { SDIS(estimator_ref_put(estimator)); estimator = NULL; } goto exit; } diff --git a/src/sdis_tile.c b/src/sdis_tile.c @@ -0,0 +1,67 @@ +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "sdis_tile.h" + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE void +release_tile(ref_T* ref) +{ + struct tile* tile = CONTAINER_OF(ref, struct tile, ref); + ASSERT(ref); + MEM_RM(tile->allocator, tile); +} + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +res_T +tile_create(struct mem_allocator* allocator, struct tile** out_tile) +{ + struct tile* tile = NULL; + res_T res = RES_OK; + ASSERT(allocator && out_tile); + + tile = MEM_ALLOC_ALIGNED(allocator, sizeof(*tile), 16); + if(!tile) { res = RES_MEM_ERR; goto error; } + + ref_init(&tile->ref); + list_init(&tile->node); + tile->allocator = allocator; + memset(&tile->data, 0, sizeof(tile->data)); +exit: + *out_tile = tile; + return res; +error: + if(tile) { tile_ref_put(tile); tile = NULL; } + goto exit; +} + +void +tile_ref_get(struct tile* tile) +{ + ASSERT(tile); + ref_get(&tile->ref); +} + +void +tile_ref_put(struct tile* tile) +{ + ASSERT(tile); + ref_put(&tile->ref, release_tile); +} + diff --git a/src/sdis_tile.h b/src/sdis_tile.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) + * + * 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 SDIS_TILE_H +#define SDIS_TILE_H + +#include "sdis_misc.h" + +#include <rsys/list.h> +#include <rsys/ref_count.h> + +/* Forward declarations */ +struct mem_allocator; + +#define TILE_SIZE 4 /* definition in X & Y of a tile */ +STATIC_ASSERT(IS_POW2(TILE_SIZE), TILE_SIZE_must_be_a_power_of_2); + +struct pixel { + struct accum acc_temp; /* Temperature accumulator */ + struct accum acc_time; /* Time accumulator */ +}; + +/* Tile of row ordered pixels */ +struct tile { + struct list_node node; + struct mem_allocator* allocator; + ref_T ref; + + struct tile_data { + uint16_t x, y; /* 2D coordinates of the tile in tile space */ + struct pixel ALIGN(16) pixels[TILE_SIZE*TILE_SIZE]; + } data; +}; + +extern LOCAL_SYM res_T +tile_create + (struct mem_allocator* allocator, + struct tile** tile); + +extern LOCAL_SYM void +tile_ref_get + (struct tile* tile); + +extern LOCAL_SYM void +tile_ref_put + (struct tile* tile); + +static INLINE struct pixel* +tile_at(struct tile* tile, const uint16_t x, const uint16_t y) +{ + ASSERT(tile && x < TILE_SIZE && y < TILE_SIZE); + return tile->data.pixels + y*TILE_SIZE + x; +} + +#endif /* SDIS_TILE_H */ diff --git a/src/test_sdis.c b/src/test_sdis.c @@ -0,0 +1,34 @@ +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "sdis.h" +#include "test_sdis_utils.h" + +int +main(int argc, char** argv) +{ + struct sdis_info info = SDIS_INFO_NULL; + (void)argc, (void)argv; + + BA(sdis_get_info(NULL)); + OK(sdis_get_info(&info)); +#ifdef SDIS_ENABLE_MPI + CHK(info.mpi_enabled); +#else + CHK(!info.mpi_enabled); +#endif + return 0; +} + diff --git a/src/test_sdis_accum_buffer.c b/src/test_sdis_accum_buffer.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/test_sdis_camera.c b/src/test_sdis_camera.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -23,15 +23,12 @@ main(int argc, char** argv) { struct sdis_device* dev; struct sdis_camera* cam; - struct mem_allocator allocator; double pos[3] = {0}; double tgt[3] = {0}; double up[3] = {0}; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); BA(sdis_camera_create(NULL, NULL)); BA(sdis_camera_create(dev, NULL)); @@ -85,8 +82,6 @@ main(int argc, char** argv) OK(sdis_device_ref_put(dev)); OK(sdis_camera_ref_put(cam)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_compute_power.c b/src/test_sdis_compute_power.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -164,7 +164,6 @@ solid_get_volumic_power int main(int argc, char** argv) { - struct mem_allocator allocator; struct context ctx; struct s3dut_mesh* sphere = NULL; struct s3dut_mesh* cylinder = NULL; @@ -187,10 +186,10 @@ main(int argc, char** argv) size_t nverts = 0; size_t ntris = 0; double ref = 0; + int is_master_process; (void)argc, (void) argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + create_default_device(&argc, &argv, &is_master_process, &dev); /* Setup the interface shader */ interf_shader.convection_coef = interface_get_convection_coef; @@ -219,8 +218,8 @@ main(int argc, char** argv) ctx.interf1 = interf1; /* Create the geometry */ - OK(s3dut_create_sphere(&allocator, 1, 512, 256, &sphere)); - OK(s3dut_create_cylinder(&allocator, 1, 10, 512, 8, &cylinder)); + OK(s3dut_create_sphere(NULL, 1, 512, 256, &sphere)); + OK(s3dut_create_cylinder(NULL, 1, 10, 512, 8, &cylinder)); OK(s3dut_mesh_get_data(sphere, &ctx.msh0)); OK(s3dut_mesh_get_data(cylinder, &ctx.msh1)); @@ -258,38 +257,48 @@ main(int argc, char** argv) args.time_range[0] = args.time_range[1] = INF; OK(sdis_compute_power(scn, &args, &estimator)); - BA(sdis_estimator_get_power(NULL, &mpow)); - BA(sdis_estimator_get_power(estimator, NULL)); - OK(sdis_estimator_get_power(estimator, &mpow)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - - /* Check results for solid 0 */ - ref = 4.0/3.0 * PI * POWER0; - printf("Mean power of the solid0 = %g ~ %g +/- %g\n", - ref, mpow.E, mpow.SE); - check_intersection(ref, 1.e-3*ref, mpow.E, 3*mpow.SE); - OK(sdis_estimator_ref_put(estimator)); + if(!is_master_process) { + CHK(estimator == NULL); + } else { + BA(sdis_estimator_get_power(NULL, &mpow)); + BA(sdis_estimator_get_power(estimator, NULL)); + OK(sdis_estimator_get_power(estimator, &mpow)); + OK(sdis_estimator_get_realisation_time(estimator, &time)); + + /* Check results for solid 0 */ + ref = 4.0/3.0 * PI * POWER0; + printf("Mean power of the solid0 = %g ~ %g +/- %g\n", + ref, mpow.E, mpow.SE); + check_intersection(ref, 1.e-3*ref, mpow.E, 3*mpow.SE); + OK(sdis_estimator_ref_put(estimator)); + } - /* Check results for solid 1 */ args.medium = solid1; OK(sdis_compute_power(scn, &args, &estimator)); - OK(sdis_estimator_get_power(estimator, &mpow)); - ref = PI * 10 * POWER1; - printf("Mean power of the solid1 = %g ~ %g +/- %g\n", - ref, mpow.E, mpow.SE); - check_intersection(ref, 1.e-3*ref, mpow.E, 3*mpow.SE); - OK(sdis_estimator_ref_put(estimator)); - - /* Check for a not null time range */ + + if(is_master_process) { + /* Check results for solid 1 */ + OK(sdis_estimator_get_power(estimator, &mpow)); + ref = PI * 10 * POWER1; + printf("Mean power of the solid1 = %g ~ %g +/- %g\n", + ref, mpow.E, mpow.SE); + check_intersection(ref, 1.e-3*ref, mpow.E, 3*mpow.SE); + OK(sdis_estimator_ref_put(estimator)); + } + args.time_range[0] = 0; args.time_range[1] = 10; OK(sdis_compute_power(scn, &args, &estimator)); - OK(sdis_estimator_get_power(estimator, &mpow)); - ref = PI * 10 * POWER1 / 10; - printf("Mean power of the solid1 in [0, 10] s = %g ~ %g +/- %g\n", - ref, mpow.E, mpow.SE); - check_intersection(ref, 1.e-3*ref, mpow.E, 3*mpow.SE); - OK(sdis_estimator_ref_put(estimator)); + + if(is_master_process) { + /* Check for a not null time range */ + OK(sdis_estimator_get_power(estimator, &mpow)); + ref = PI * 10 * POWER1 / 10; + printf("Mean power of the solid1 in [0, 10] s = %g ~ %g +/- %g\n", + ref, mpow.E, mpow.SE); + check_intersection(ref, 1.e-3*ref, mpow.E, 3*mpow.SE); + OK(sdis_estimator_ref_put(estimator)); + } /* Reset the scene with only one solid medium */ OK(sdis_scene_ref_put(scn)); @@ -305,12 +314,14 @@ main(int argc, char** argv) /* Check non constant volumic power */ args.medium = solid0; OK(sdis_compute_power(scn, &args, &estimator)); - OK(sdis_estimator_get_power(estimator, &mpow)); - ref = 4.0/3.0*PI*POWER0 + PI*10*POWER1; - printf("Mean power of the sphere+cylinder = %g ~ %g +/- %g\n", - ref, mpow.E, mpow.SE); - check_intersection(ref, 1e-3*ref, mpow.E, 3*mpow.SE); - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + OK(sdis_estimator_get_power(estimator, &mpow)); + ref = 4.0/3.0*PI*POWER0 + PI*10*POWER1; + printf("Mean power of the sphere+cylinder = %g ~ %g +/- %g\n", + ref, mpow.E, mpow.SE); + check_intersection(ref, 1e-2*ref, mpow.E, 3*mpow.SE); + OK(sdis_estimator_ref_put(estimator)); + } #if 0 { @@ -328,7 +339,6 @@ main(int argc, char** argv) #endif /* Clean up memory */ - OK(sdis_device_ref_put(dev)); OK(sdis_medium_ref_put(fluid)); OK(sdis_medium_ref_put(solid0)); OK(sdis_medium_ref_put(solid1)); @@ -338,8 +348,8 @@ main(int argc, char** argv) OK(s3dut_mesh_ref_put(sphere)); OK(s3dut_mesh_ref_put(cylinder)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); + free_default_device(dev); + CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_conducto_radiative.c b/src/test_sdis_conducto_radiative.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -342,6 +342,7 @@ main(int argc, char** argv) struct sdis_interface* interfaces[5] = {NULL}; struct sdis_interface* prim_interfaces[32/*#triangles*/]; struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + struct sdis_device_create_args dev_args = SDIS_DEVICE_CREATE_ARGS_DEFAULT; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_scene* scn = NULL; @@ -360,7 +361,8 @@ main(int argc, char** argv) (void)argc, (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + dev_args.allocator = &allocator; + OK(sdis_device_create(&dev_args, &dev)); /* Create the fluid medium */ fluid_shader.temperature = temperature_unknown; diff --git a/src/test_sdis_conducto_radiative_2d.c b/src/test_sdis_conducto_radiative_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -363,7 +363,7 @@ main(int argc, char** argv) (void)argc, (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Create the fluid medium */ fluid_shader.temperature = temperature_unknown; diff --git a/src/test_sdis_contact_resistance.c b/src/test_sdis_contact_resistance.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -251,7 +251,6 @@ solve int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_data* data = NULL; struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; @@ -275,8 +274,7 @@ main(int argc, char** argv) struct ssp_rng* rng = NULL; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); fluid_shader.temperature = fluid_get_temperature; OK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); @@ -425,7 +423,7 @@ main(int argc, char** argv) OK(sdis_interface_ref_put(interf_R)); /* Solve */ - OK(ssp_rng_create(&allocator, SSP_RNG_KISS, &rng)); + OK(ssp_rng_create(NULL, SSP_RNG_KISS, &rng)); printf(">> Box scene\n"); solve(box_scn, interf_props, rng); printf("\n>> Square scene\n"); @@ -436,8 +434,6 @@ main(int argc, char** argv) OK(sdis_device_ref_put(dev)); OK(ssp_rng_ref_put(rng)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_contact_resistance.h b/src/test_sdis_contact_resistance.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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/test_sdis_contact_resistance_2.c b/src/test_sdis_contact_resistance_2.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -345,7 +345,6 @@ solve int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_data* data = NULL; struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; @@ -369,8 +368,7 @@ main(int argc, char** argv) struct ssp_rng* rng = NULL; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); fluid_shader.temperature = fluid_get_temperature; OK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); @@ -519,7 +517,7 @@ main(int argc, char** argv) OK(sdis_interface_ref_put(interf_R)); /* Solve */ - OK(ssp_rng_create(&allocator, SSP_RNG_KISS, &rng)); + OK(ssp_rng_create(NULL, SSP_RNG_KISS, &rng)); printf(">> Box scene\n"); solve_probe(box_scn, interf_props, rng); solve(box_scn, interf_props, rng); @@ -532,8 +530,6 @@ main(int argc, char** argv) OK(sdis_device_ref_put(dev)); OK(ssp_rng_ref_put(rng)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_convection.c b/src/test_sdis_convection.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -165,7 +165,6 @@ create_interface int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_mc T = SDIS_MC_NULL; struct sdis_mc mc_time = SDIS_MC_NULL; struct sdis_device* dev = NULL; @@ -198,8 +197,7 @@ main(int argc, char** argv) int i; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Create the fluid medium */ OK(sdis_data_create(dev, sizeof(int), ALIGNOF(int), NULL, &is_stationary)); @@ -361,8 +359,6 @@ main(int argc, char** argv) OK(sdis_device_ref_put(dev)); OK(sdis_data_ref_put(is_stationary)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_convection_non_uniform.c b/src/test_sdis_convection_non_uniform.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -175,7 +175,6 @@ create_interface int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_mc T = SDIS_MC_NULL; struct sdis_mc mc_time = SDIS_MC_NULL; struct sdis_device* dev = NULL; @@ -208,8 +207,7 @@ main(int argc, char** argv) int i; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); OK(sdis_data_create(dev, sizeof(int), ALIGNOF(int), NULL, &is_stationary)); *((int*)sdis_data_get(is_stationary)) = 0; @@ -376,8 +374,6 @@ main(int argc, char** argv) OK(sdis_device_ref_put(dev)); OK(sdis_data_ref_put(is_stationary)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_data.c b/src/test_sdis_data.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -36,14 +36,12 @@ int main(int argc, char** argv) { const char* str = "Hello world!"; - struct mem_allocator allocator; struct sdis_device* dev = NULL; struct sdis_data* data = NULL; struct param* param = NULL; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); BA(sdis_data_create(NULL, 0, 0, NULL, NULL)); BA(sdis_data_create(dev, 0, 0, NULL, NULL)); BA(sdis_data_create(NULL, 8, 0, NULL, NULL)); @@ -87,7 +85,6 @@ main(int argc, char** argv) OK(sdis_data_ref_put(data)); OK(sdis_device_ref_put(dev)); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_device.c b/src/test_sdis_device.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -18,6 +18,10 @@ #include <rsys/logger.h> +#ifdef SDIS_ENABLE_MPI +#include <mpi.h> +#endif + static INLINE void log_stream(const char* msg, void* ctx) { @@ -29,14 +33,22 @@ log_stream(const char* msg, void* ctx) int main(int argc, char** argv) { + struct sdis_device_create_args args = SDIS_DEVICE_CREATE_ARGS_DEFAULT; struct logger logger; struct mem_allocator allocator; struct sdis_device* dev; +#ifdef SDIS_ENABLE_MPI + int provided; +#endif (void)argc, (void)argv; - BA(sdis_device_create(NULL, NULL, 0, 0, NULL)); - BA(sdis_device_create(NULL, NULL, 0, 0, &dev)); - OK(sdis_device_create(NULL, NULL, 1, 0, &dev)); + args.nthreads_hint = 0; + args.verbosity = 0; + + BA(sdis_device_create(&args, NULL)); + BA(sdis_device_create(&args, &dev)); + args.nthreads_hint = 1; + OK(sdis_device_create(&args, &dev)); BA(sdis_device_ref_get(NULL)); OK(sdis_device_ref_get(dev)); BA(sdis_device_ref_put(NULL)); @@ -46,8 +58,11 @@ main(int argc, char** argv) OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); CHK(MEM_ALLOCATED_SIZE(&allocator) == 0); - BA(sdis_device_create(NULL, &allocator, 1, 0, NULL)); - OK(sdis_device_create(NULL, &allocator, 1, 0, &dev)); + + args.allocator = &allocator; + args.verbosity = 0; + BA(sdis_device_create(&args, NULL)); + OK(sdis_device_create(&args, &dev)); OK(sdis_device_ref_put(dev)); CHK(MEM_ALLOCATED_SIZE(&allocator) == 0); @@ -56,16 +71,33 @@ main(int argc, char** argv) logger_set_stream(&logger, LOG_ERROR, log_stream, NULL); logger_set_stream(&logger, LOG_WARNING, log_stream, NULL); - BA(sdis_device_create(&logger, NULL, 1, 0, NULL)); - OK(sdis_device_create(&logger, NULL, 1, 0, &dev)); + args.logger = &logger; + args.allocator = NULL; + BA(sdis_device_create(&args, NULL)); + OK(sdis_device_create(&args, &dev)); OK(sdis_device_ref_put(dev)); - BA(sdis_device_create(&logger, &allocator, 1, 0, NULL)); - OK(sdis_device_create(&logger, &allocator, 1, 0, &dev)); + args.allocator = &allocator; + BA(sdis_device_create(&args, NULL)); + OK(sdis_device_create(&args, &dev)); OK(sdis_device_ref_put(dev)); - OK(sdis_device_create(&logger, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev)); + args.nthreads_hint = SDIS_NTHREADS_DEFAULT; + OK(sdis_device_create(&args, &dev)); + OK(sdis_device_ref_put(dev)); + + args.use_mpi = 1; + args.verbosity = 1; + +#ifndef SDIS_ENABLE_MPI + OK(sdis_device_create(&args, &dev)); + OK(sdis_device_ref_put(dev)); +#else + CHK(MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided) == MPI_SUCCESS); + OK(sdis_device_create(&args, &dev)); + CHK(MPI_Finalize() == MPI_SUCCESS); OK(sdis_device_ref_put(dev)); +#endif logger_release(&logger); check_memory_allocator(&allocator); diff --git a/src/test_sdis_flux.c b/src/test_sdis_flux.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -353,7 +353,6 @@ solve int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_data* data = NULL; struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; @@ -373,8 +372,7 @@ main(int argc, char** argv) struct ssp_rng* rng = NULL; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Create the dummy fluid medium */ OK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); @@ -459,7 +457,7 @@ main(int argc, char** argv) OK(sdis_interface_ref_put(interf_phi)); /* Solve */ - OK(ssp_rng_create(&allocator, SSP_RNG_KISS, &rng)); + OK(ssp_rng_create(NULL, SSP_RNG_KISS, &rng)); printf(">> Box scene\n"); solve(box_scn, rng, interf_props); printf(">> Square Scene\n"); @@ -470,8 +468,6 @@ main(int argc, char** argv) OK(sdis_device_ref_put(dev)); OK(ssp_rng_ref_put(rng)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_interface.c b/src/test_sdis_interface.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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,7 +19,6 @@ int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_data* data = NULL; struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; @@ -32,8 +31,7 @@ main(int argc, char** argv) struct sdis_interface_shader shader2 = SDIS_INTERFACE_SHADER_NULL; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); OK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -144,8 +142,6 @@ main(int argc, char** argv) OK(sdis_medium_ref_put(fluid)); OK(sdis_medium_ref_put(solid)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_medium.c b/src/test_sdis_medium.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 +21,6 @@ int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_data* data = NULL; struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; @@ -32,8 +31,7 @@ main(int argc, char** argv) struct sdis_solid_shader solid_shader2 = SDIS_SOLID_SHADER_NULL; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); BA(sdis_fluid_create(NULL, NULL, NULL, NULL)); BA(sdis_fluid_create(dev, NULL, NULL, NULL)); @@ -146,8 +144,6 @@ main(int argc, char** argv) OK(sdis_medium_ref_put(fluid)); OK(sdis_device_ref_put(dev)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; diff --git a/src/test_sdis_picard.c b/src/test_sdis_picard.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -566,7 +566,6 @@ int main(int argc, char** argv) { FILE* stream = NULL; - struct mem_allocator allocator; struct sdis_device* dev = NULL; struct sdis_scene* scn_2d = NULL; @@ -588,8 +587,7 @@ main(int argc, char** argv) size_t i; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Solid medium */ solid_props.lambda = 1.15; @@ -773,8 +771,6 @@ main(int argc, char** argv) OK(sdis_device_ref_put(dev)); CHK(fclose(stream) == 0); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_scene.c b/src/test_sdis_scene.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -468,7 +468,6 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_device* dev = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* fluid = NULL; @@ -480,8 +479,7 @@ main(int argc, char** argv) interface_shader.convection_coef = DUMMY_INTERFACE_SHADER.convection_coef; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); OK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -497,8 +495,6 @@ main(int argc, char** argv) OK(sdis_device_ref_put(dev)); OK(sdis_interface_ref_put(interf)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_solid_random_walk_robustness.c b/src/test_sdis_solid_random_walk_robustness.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -259,7 +259,6 @@ solid_get_volumetric_power int main(int argc, char** argv) { - struct mem_allocator allocator; struct s3dut_super_formula f0 = S3DUT_SUPER_FORMULA_NULL; struct s3dut_super_formula f1 = S3DUT_SUPER_FORMULA_NULL; struct s3dut_mesh* msh = NULL; @@ -284,8 +283,7 @@ main(int argc, char** argv) double spread; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Create the fluid medium */ fluid_shader.temperature = fluid_get_temperature; @@ -325,7 +323,7 @@ main(int argc, char** argv) /* Create the solid super shape */ f0.A = 1; f0.B = 1; f0.M = 20; f0.N0 = 1; f0.N1 = 1; f0.N2 = 5; f1.A = 1; f1.B = 1; f1.M = 7; f1.N0 = 1; f1.N1 = 2; f1.N2 = 5; - OK(s3dut_create_super_shape(&allocator, &f0, &f1, 1, 128, 64, &msh)); + OK(s3dut_create_super_shape(NULL, &f0, &f1, 1, 128, 64, &msh)); OK(s3dut_mesh_get_data(msh, &ctx.msh)); compute_aabb(ctx.msh.positions, ctx.msh.nvertices, lower, upper); @@ -395,8 +393,6 @@ main(int argc, char** argv) OK(sdis_interface_ref_put(interf)); OK(sdis_scene_ref_put(scn)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_solve_boundary.c b/src/test_sdis_solve_boundary.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -173,7 +173,6 @@ int main(int argc, char** argv) { FILE* fp = NULL; - struct mem_allocator allocator; struct sdis_data* data = NULL; struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; @@ -201,10 +200,10 @@ main(int argc, char** argv) double ref; size_t prims[4]; enum sdis_side sides[4]; + int is_master_process = 0; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + create_default_device(&argc, &argv, &is_master_process, &dev); /* Temporary file used to dump heat paths */ CHK((fp = tmpfile()) != NULL); @@ -340,8 +339,10 @@ main(int argc, char** argv) OK(SOLVE(box_scn, &probe_args, &estimator)); OK(sdis_scene_get_boundary_position (box_scn, probe_args.iprim, probe_args.uv, pos)); - printf("Boundary temperature of the box at (%g %g %g) = ", SPLIT3(pos)); - check_estimator(estimator, N, ref); + if(is_master_process) { + printf("Boundary temperature of the box at (%g %g %g) = ", SPLIT3(pos)); + check_estimator(estimator, N, ref); + } BA(GREEN(NULL, &probe_args, &green)); BA(GREEN(box_scn, NULL, &green)); @@ -357,21 +358,28 @@ main(int argc, char** argv) probe_args.side = SDIS_FRONT; OK(GREEN(box_scn, &probe_args, &green)); - check_green_function(green); - OK(sdis_green_function_solve(green, &estimator2)); - check_estimator(estimator2, N, ref); - check_green_serialization(green, box_scn); + if(!is_master_process) { + CHK(estimator == NULL); + CHK(green == NULL); + } else { + check_green_function(green); + OK(sdis_green_function_solve(green, &estimator2)); + check_estimator(estimator2, N, ref); + check_green_serialization(green, box_scn); - OK(sdis_green_function_ref_put(green)); - OK(sdis_estimator_ref_put(estimator)); - OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + } /* Dump paths */ probe_args.nrealisations = N_dump; probe_args.register_paths = SDIS_HEAT_PATH_ALL; OK(SOLVE(box_scn, &probe_args, &estimator)); - dump_heat_paths(fp, estimator); - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + dump_heat_paths(fp, estimator); + OK(sdis_estimator_ref_put(estimator)); + } /* The external fluid cannot have an unknown temperature */ fluid_param->temperature = UNKNOWN_TEMPERATURE; @@ -388,14 +396,16 @@ main(int argc, char** argv) OK(SOLVE(square_scn, &probe_args, &estimator)); OK(GREEN(square_scn, &probe_args, &green)); - check_green_function(green); - OK(sdis_green_function_solve(green, &estimator2)); - check_estimator(estimator2, N, ref); - check_green_serialization(green, square_scn); + if(is_master_process) { + check_green_function(green); + OK(sdis_green_function_solve(green, &estimator2)); + check_estimator(estimator2, N, ref); + check_green_serialization(green, square_scn); - OK(sdis_estimator_ref_put(estimator)); - OK(sdis_estimator_ref_put(estimator2)); - OK(sdis_green_function_ref_put(green)); + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + } /* The external fluid cannot have an unknown temperature */ fluid_param->temperature = UNKNOWN_TEMPERATURE; @@ -408,15 +418,17 @@ main(int argc, char** argv) probe_args.iprim = 6; OK(SOLVE(box_scn, &probe_args, &estimator)); - check_estimator(estimator, N, Tf); - - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + check_estimator(estimator, N, Tf); + OK(sdis_estimator_ref_put(estimator)); + } probe_args.iprim = 3; OK(SOLVE(square_scn, &probe_args, &estimator)); - check_estimator(estimator, N, Tf); - - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + check_estimator(estimator, N, Tf); + OK(sdis_estimator_ref_put(estimator)); + } #undef F #undef SOLVE @@ -473,8 +485,10 @@ main(int argc, char** argv) /* Average temperature on the right side of the box */ OK(SOLVE(box_scn, &bound_args, &estimator)); - printf("Average temperature of the right side of the box = "); - check_estimator(estimator, N, ref); + if(is_master_process) { + printf("Average temperature of the right side of the box = "); + check_estimator(estimator, N, ref); + } BA(GREEN(NULL, &bound_args, &green)); BA(GREEN(box_scn, NULL, &green)); @@ -499,14 +513,19 @@ main(int argc, char** argv) sides[0] = SDIS_FRONT; OK(GREEN(box_scn, &bound_args, &green)); - check_green_function(green); - OK(sdis_green_function_solve(green, &estimator2)); - check_estimator(estimator2, N, ref); - check_green_serialization(green, box_scn); - - OK(sdis_green_function_ref_put(green)); - OK(sdis_estimator_ref_put(estimator)); - OK(sdis_estimator_ref_put(estimator2)); + if(!is_master_process) { + CHK(estimator == NULL); + CHK(green == NULL); + } else { + check_green_function(green); + OK(sdis_green_function_solve(green, &estimator2)); + check_estimator(estimator2, N, ref); + check_green_serialization(green, box_scn); + + OK(sdis_green_function_ref_put(green)); + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + } /* Dump path */ bound_args.nrealisations = N_dump; @@ -519,8 +538,10 @@ main(int argc, char** argv) /* Dump path */ fluid_param->temperature = Tf; OK(SOLVE(box_scn, &bound_args, &estimator)); - dump_heat_paths(fp, estimator); - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + dump_heat_paths(fp, estimator); + OK(sdis_estimator_ref_put(estimator)); + } /* Switch in 2D */ bound_args.nrealisations = N; @@ -532,25 +553,31 @@ main(int argc, char** argv) /* Average temperature on the right side of the square */ prims[0] = 3; OK(SOLVE(square_scn, &bound_args, &estimator)); - printf("Average temperature of the right side of the square = "); - check_estimator(estimator, N, ref); + if(is_master_process) { + printf("Average temperature of the right side of the square = "); + check_estimator(estimator, N, ref); + } OK(GREEN(square_scn, &bound_args, &green)); - check_green_function(green); - OK(sdis_green_function_solve(green, &estimator2)); - check_estimator(estimator2, N, ref); - check_green_serialization(green, square_scn); + if(is_master_process) { + check_green_function(green); + OK(sdis_green_function_solve(green, &estimator2)); + check_estimator(estimator2, N, ref); + check_green_serialization(green, square_scn); - OK(sdis_green_function_ref_put(green)); - OK(sdis_estimator_ref_put(estimator)); - OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + } /* Dump path */ bound_args.nrealisations = N_dump; bound_args.register_paths = SDIS_HEAT_PATH_ALL; OK(SOLVE(square_scn, &bound_args, &estimator)); - dump_heat_paths(fp, estimator); - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + dump_heat_paths(fp, estimator); + OK(sdis_estimator_ref_put(estimator)); + } bound_args.register_paths = SDIS_HEAT_PATH_NONE; bound_args.nrealisations = N; @@ -565,36 +592,44 @@ main(int argc, char** argv) bound_args.nprimitives = 4; OK(SOLVE(box_scn, &bound_args, &estimator)); - printf("Average temperature of the left+right sides of the box = "); - check_estimator(estimator, N, ref); + if(is_master_process) { + printf("Average temperature of the left+right sides of the box = "); + check_estimator(estimator, N, ref); + } OK(GREEN(box_scn, &bound_args, &green)); - check_green_function(green); - OK(sdis_green_function_solve(green, &estimator2)); - check_estimator(estimator2, N, ref); - check_green_serialization(green, box_scn); + if(is_master_process) { + check_green_function(green); + OK(sdis_green_function_solve(green, &estimator2)); + check_estimator(estimator2, N, ref); + check_green_serialization(green, box_scn); - OK(sdis_green_function_ref_put(green)); - OK(sdis_estimator_ref_put(estimator)); - OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + } /* Average temperature on the left+right sides of the square */ prims[0] = 1; prims[1] = 3; bound_args.nprimitives = 2; OK(SOLVE(square_scn, &bound_args, &estimator)); - printf("Average temperature of the left+right sides of the square = "); - check_estimator(estimator, N, ref); + if(is_master_process) { + printf("Average temperature of the left+right sides of the square = "); + check_estimator(estimator, N, ref); + } OK(GREEN(square_scn, &bound_args, &green)); - check_green_function(green); - OK(sdis_green_function_solve(green, &estimator2)); - check_estimator(estimator2, N, ref); - check_green_serialization(green, square_scn); + if(is_master_process) { + check_green_function(green); + OK(sdis_green_function_solve(green, &estimator2)); + check_estimator(estimator2, N, ref); + check_green_serialization(green, square_scn); - OK(sdis_green_function_ref_put(green)); - OK(sdis_estimator_ref_put(estimator)); - OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + } /* Right-side temperature at initial time */ bound_args.time_range[0] = 0; @@ -604,28 +639,28 @@ main(int argc, char** argv) prims[1] = 7; bound_args.nprimitives = 2; OK(SOLVE(box_scn, &bound_args, &estimator)); - check_estimator(estimator, N, Tf); - - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + check_estimator(estimator, N, Tf); + OK(sdis_estimator_ref_put(estimator)); + } prims[0] = 3; bound_args.nprimitives = 1; OK(SOLVE(square_scn, &bound_args, &estimator)); - check_estimator(estimator, N, Tf); - - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + check_estimator(estimator, N, Tf); + OK(sdis_estimator_ref_put(estimator)); + } #undef SOLVE #undef GREEN OK(sdis_scene_ref_put(box_scn)); OK(sdis_scene_ref_put(square_scn)); - OK(sdis_device_ref_put(dev)); + free_default_device(dev); CHK(fclose(fp) == 0); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_solve_boundary_flux.c b/src/test_sdis_solve_boundary_flux.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -228,7 +228,6 @@ check_estimator int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_data* data = NULL; struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; @@ -255,10 +254,10 @@ main(int argc, char** argv) double pos[3]; double analyticT, analyticCF, analyticRF, analyticTF; size_t prims[2]; + int is_master_process; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + create_default_device(&argc, &argv, &is_master_process, &dev); /* Create the fluid medium */ OK(sdis_data_create @@ -405,25 +404,31 @@ main(int argc, char** argv) probe_args.time_range[0] = INF; OK(SOLVE(box_scn, &probe_args, &estimator)); - OK(sdis_estimator_get_type(estimator, &type)); - CHK(type == SDIS_ESTIMATOR_FLUX); - - OK(sdis_scene_get_boundary_position - (box_scn, probe_args.iprim, probe_args.uv, pos)); - printf("Boundary values of the box at (%g %g %g) = ", SPLIT3(pos)); - check_estimator(estimator, N, analyticT, analyticCF, analyticRF, analyticTF); - OK(sdis_estimator_ref_put(estimator)); + if(!is_master_process) { + CHK(estimator == NULL); + } else { + OK(sdis_estimator_get_type(estimator, &type)); + CHK(type == SDIS_ESTIMATOR_FLUX); + + OK(sdis_scene_get_boundary_position + (box_scn, probe_args.iprim, probe_args.uv, pos)); + printf("Boundary values of the box at (%g %g %g) = ", SPLIT3(pos)); + check_estimator(estimator, N, analyticT, analyticCF, analyticRF, analyticTF); + OK(sdis_estimator_ref_put(estimator)); + } probe_args.uv[0] = 0.5; probe_args.iprim = 4; BA(SOLVE(square_scn, &probe_args, &estimator)); probe_args.iprim = 3; OK(SOLVE(square_scn, &probe_args, &estimator)); - OK(sdis_scene_get_boundary_position - (square_scn, probe_args.iprim, probe_args.uv, pos)); - printf("Boundary values of the square at (%g %g) = ", SPLIT2(pos)); - check_estimator(estimator, N, analyticT, analyticCF, analyticRF, analyticTF); - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + OK(sdis_scene_get_boundary_position + (square_scn, probe_args.iprim, probe_args.uv, pos)); + printf("Boundary values of the square at (%g %g) = ", SPLIT2(pos)); + check_estimator(estimator, N, analyticT, analyticCF, analyticRF, analyticTF); + OK(sdis_estimator_ref_put(estimator)); + } #undef F #undef SOLVE @@ -460,10 +465,14 @@ main(int argc, char** argv) prims[0] = 6; OK(SOLVE(box_scn, &bound_args, &estimator)); - /* Average temperature on the right side of the box */ - printf("Average values of the right side of the box = "); - check_estimator(estimator, N, analyticT, analyticCF, analyticRF, analyticTF); - OK(sdis_estimator_ref_put(estimator)); + if(!is_master_process) { + CHK(estimator == NULL); + } else { + /* Average temperature on the right side of the box */ + printf("Average values of the right side of the box = "); + check_estimator(estimator, N, analyticT, analyticCF, analyticRF, analyticTF); + OK(sdis_estimator_ref_put(estimator)); + } /* Average temperature on the right side of the square */ prims[0] = 4; @@ -471,9 +480,11 @@ main(int argc, char** argv) BA(SOLVE(square_scn, &bound_args, &estimator)); prims[0] = 3; OK(SOLVE(square_scn, &bound_args, &estimator)); - printf("Average values of the right side of the square = "); - check_estimator(estimator, N, analyticT, analyticCF, analyticRF, analyticTF); - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + printf("Average values of the right side of the square = "); + check_estimator(estimator, N, analyticT, analyticCF, analyticRF, analyticTF); + OK(sdis_estimator_ref_put(estimator)); + } /* Flux computation on Dirichlet boundaries is not available yet. * Once available, the expected total flux is the same we expect on the right @@ -489,10 +500,8 @@ main(int argc, char** argv) OK(sdis_scene_ref_put(box_scn)); OK(sdis_scene_ref_put(square_scn)); - OK(sdis_device_ref_put(dev)); + free_default_device(dev); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_solve_camera.c b/src/test_sdis_solve_camera.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -540,7 +540,6 @@ dump_image(const struct sdis_estimator_buffer* buf) int main(int argc, char** argv) { - struct mem_allocator allocator; struct geometry geom = GEOMETRY_NULL; struct s3dut_mesh* msh = NULL; struct s3dut_mesh_data msh_data; @@ -570,10 +569,10 @@ main(int argc, char** argv) double pos[3]; double tgt[3]; double up[3]; + int is_master_process; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + create_default_device(&argc, &argv, &is_master_process, &dev); /* Create the fluid0 */ fluid_param.temperature = 350; @@ -611,14 +610,14 @@ main(int argc, char** argv) create_interface(dev, fluid1, solid, &interface_param, &interf1); /* Setup the cube geometry */ - OK(s3dut_create_cuboid(&allocator, 2, 2, 2, &msh)); + OK(s3dut_create_cuboid(NULL, 2, 2, 2, &msh)); OK(s3dut_mesh_get_data(msh, &msh_data)); geometry_add_shape(&geom, msh_data.positions, msh_data.nvertices, msh_data.indices, msh_data.nprimitives, NULL, interf1); OK(s3dut_mesh_ref_put(msh)); /* Setup the sphere geometry */ - OK(s3dut_create_sphere(&allocator, 0.5, 32, 16, &msh)); + OK(s3dut_create_sphere(NULL, 0.5, 32, 16, &msh)); OK(s3dut_mesh_get_data(msh, &msh_data)); geometry_add_shape(&geom, msh_data.positions, msh_data.nvertices, msh_data.indices, msh_data.nprimitives, NULL, interf0); @@ -660,8 +659,8 @@ main(int argc, char** argv) solve_args.cam = cam; solve_args.time_range[0] = INF; solve_args.time_range[0] = INF; - solve_args.image_resolution[0] = IMG_WIDTH; - solve_args.image_resolution[1] = IMG_HEIGHT; + solve_args.image_definition[0] = IMG_WIDTH; + solve_args.image_definition[1] = IMG_HEIGHT; solve_args.spp = SPP; BA(sdis_solve_camera(NULL, &solve_args, &buf)); @@ -687,42 +686,46 @@ main(int argc, char** argv) /* Launch the simulation */ OK(sdis_solve_camera(scn, &solve_args, &buf)); - BA(sdis_estimator_buffer_get_realisation_count(NULL, &nreals)); - BA(sdis_estimator_buffer_get_realisation_count(buf, NULL)); - OK(sdis_estimator_buffer_get_realisation_count(buf, &nreals)); - - BA(sdis_estimator_buffer_get_failure_count(NULL, &nfails)); - BA(sdis_estimator_buffer_get_failure_count(buf, NULL)); - OK(sdis_estimator_buffer_get_failure_count(buf, &nfails)); - - BA(sdis_estimator_buffer_get_temperature(NULL, &T)); - BA(sdis_estimator_buffer_get_temperature(buf, NULL)); - OK(sdis_estimator_buffer_get_temperature(buf, &T)); - - BA(sdis_estimator_buffer_get_realisation_time(NULL, &time)); - BA(sdis_estimator_buffer_get_realisation_time(buf, NULL)); - OK(sdis_estimator_buffer_get_realisation_time(buf, &time)); - - BA(sdis_estimator_buffer_get_rng_state(NULL, &rng_state)); - BA(sdis_estimator_buffer_get_rng_state(buf, NULL)); - OK(sdis_estimator_buffer_get_rng_state(buf, &rng_state)); - - CHK(nreals + nfails == IMG_WIDTH*IMG_HEIGHT*SPP); - - fprintf(stderr, "Overall temperature ~ %g +/- %g\n", T.E, T.SE); - fprintf(stderr, "Time per realisation (in usec) ~ %g +/- %g\n", time.E, time.SE); - fprintf(stderr, "#failures = %lu/%lu\n", - (unsigned long)nfails, (unsigned long)(IMG_WIDTH*IMG_HEIGHT*SPP)); - - BA(sdis_estimator_buffer_get_definition(NULL, definition)); - BA(sdis_estimator_buffer_get_definition(buf, NULL)); - OK(sdis_estimator_buffer_get_definition(buf, definition)); - CHK(definition[0] == IMG_WIDTH); - CHK(definition[1] == IMG_HEIGHT); - - /* Write the image */ - dump_image(buf); - OK(sdis_estimator_buffer_ref_put(buf)); + if(!is_master_process) { + CHK(buf == NULL); + } else { + BA(sdis_estimator_buffer_get_realisation_count(NULL, &nreals)); + BA(sdis_estimator_buffer_get_realisation_count(buf, NULL)); + OK(sdis_estimator_buffer_get_realisation_count(buf, &nreals)); + + BA(sdis_estimator_buffer_get_failure_count(NULL, &nfails)); + BA(sdis_estimator_buffer_get_failure_count(buf, NULL)); + OK(sdis_estimator_buffer_get_failure_count(buf, &nfails)); + + BA(sdis_estimator_buffer_get_temperature(NULL, &T)); + BA(sdis_estimator_buffer_get_temperature(buf, NULL)); + OK(sdis_estimator_buffer_get_temperature(buf, &T)); + + BA(sdis_estimator_buffer_get_realisation_time(NULL, &time)); + BA(sdis_estimator_buffer_get_realisation_time(buf, NULL)); + OK(sdis_estimator_buffer_get_realisation_time(buf, &time)); + + BA(sdis_estimator_buffer_get_rng_state(NULL, &rng_state)); + BA(sdis_estimator_buffer_get_rng_state(buf, NULL)); + OK(sdis_estimator_buffer_get_rng_state(buf, &rng_state)); + + CHK(nreals + nfails == IMG_WIDTH*IMG_HEIGHT*SPP); + + fprintf(stderr, "Overall temperature ~ %g +/- %g\n", T.E, T.SE); + fprintf(stderr, "Time per realisation (in usec) ~ %g +/- %g\n", time.E, time.SE); + fprintf(stderr, "#failures = %lu/%lu\n", + (unsigned long)nfails, (unsigned long)(IMG_WIDTH*IMG_HEIGHT*SPP)); + + BA(sdis_estimator_buffer_get_definition(NULL, definition)); + BA(sdis_estimator_buffer_get_definition(buf, NULL)); + OK(sdis_estimator_buffer_get_definition(buf, definition)); + CHK(definition[0] == IMG_WIDTH); + CHK(definition[1] == IMG_HEIGHT); + + /* Write the image */ + dump_image(buf); + OK(sdis_estimator_buffer_ref_put(buf)); + } pfluid_param = sdis_data_get(sdis_medium_get_data(fluid1)); pfluid_param->temperature = UNKOWN_TEMPERATURE; @@ -740,11 +743,9 @@ main(int argc, char** argv) OK(sdis_camera_ref_put(cam)); OK(sdis_interface_ref_put(interf0)); OK(sdis_interface_ref_put(interf1)); - OK(sdis_device_ref_put(dev)); + free_default_device(dev); geometry_release(&geom); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_solve_medium.c b/src/test_sdis_solve_medium.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -197,7 +197,6 @@ interface_get_specular_fraction int main(int argc, char** argv) { - struct mem_allocator allocator; struct s3dut_super_formula f0 = S3DUT_SUPER_FORMULA_NULL; struct s3dut_super_formula f1 = S3DUT_SUPER_FORMULA_NULL; struct s3dut_mesh* msh0 = NULL; @@ -232,10 +231,10 @@ main(int argc, char** argv) size_t nfails; size_t ntris; size_t nverts; + int is_master_process; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + create_default_device(&argc, &argv, &is_master_process, &dev); fluid_shader.temperature = fluid_get_temperature; @@ -309,13 +308,13 @@ main(int argc, char** argv) /* Create the mesh0 */ f0.A = 1; f0.B = 1; f0.M = 3; f0.N0 = 1; f0.N1 = 1; f0.N2 = 2; f1.A = 1; f1.B = 1; f1.M = 10; f1.N0 = 1; f1.N1 = 1; f1.N2 = 3; - OK(s3dut_create_super_shape(&allocator, &f0, &f1, 1, 64, 32, &msh0)); + OK(s3dut_create_super_shape(NULL, &f0, &f1, 1, 64, 32, &msh0)); OK(s3dut_mesh_get_data(msh0, &ctx.msh0)); /* Create the mesh1 */ f0.A = 1; f0.B = 1; f0.M = 10; f0.N0 = 1; f0.N1 = 1; f0.N2 = 5; f1.A = 1; f1.B = 1; f1.M = 1; f1.N0 = 1; f1.N1 = 1; f1.N2 = 1; - OK(s3dut_create_super_shape(&allocator, &f0, &f1, 1, 64, 32, &msh1)); + OK(s3dut_create_super_shape(NULL, &f0, &f1, 1, 64, 32, &msh1)); OK(s3dut_mesh_get_data(msh1, &ctx.msh1)); /* Create the scene */ @@ -381,16 +380,20 @@ main(int argc, char** argv) solve_args.time_range[0] = solve_args.time_range[1] = INF; OK(sdis_solve_medium(scn, &solve_args, &estimator)); - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - printf("Shape0 temperature = "STR(Tf0)" ~ %g +/- %g\n", T.E, T.SE); - printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); - printf("#failures = %lu/%lu\n\n", (unsigned long)nfails, N); - CHK(eq_eps(T.E, Tf0, T.SE)); - CHK(nreals + nfails == N); - OK(sdis_estimator_ref_put(estimator)); + if(!is_master_process) { + CHK(estimator == NULL); + } else { + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_time(estimator, &time)); + printf("Shape0 temperature = "STR(Tf0)" ~ %g +/- %g\n", T.E, T.SE); + printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); + printf("#failures = %lu/%lu\n\n", (unsigned long)nfails, N); + CHK(eq_eps(T.E, Tf0, T.SE)); + CHK(nreals + nfails == N); + OK(sdis_estimator_ref_put(estimator)); + } solve_args.medium = solid1; @@ -401,21 +404,25 @@ main(int argc, char** argv) BA(sdis_solve_medium(scn, &solve_args, &estimator)); fluid_param->temperature = Tf1; OK(sdis_solve_medium(scn, &solve_args, &estimator)); - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + OK(sdis_estimator_ref_put(estimator)); + } solve_args.nrealisations = N; solve_args.register_paths = SDIS_HEAT_PATH_NONE; OK(sdis_solve_medium(scn, &solve_args, &estimator)); - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - printf("Shape1 temperature = "STR(Tf1)" ~ %g +/- %g\n", T.E, T.SE); - printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); - printf("#failures = %lu/%lu\n\n", (unsigned long)nfails, N); - CHK(eq_eps(T.E, Tf1, T.SE)); - CHK(nreals + nfails == N); - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_time(estimator, &time)); + printf("Shape1 temperature = "STR(Tf1)" ~ %g +/- %g\n", T.E, T.SE); + printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); + printf("#failures = %lu/%lu\n\n", (unsigned long)nfails, N); + CHK(eq_eps(T.E, Tf1, T.SE)); + CHK(nreals + nfails == N); + OK(sdis_estimator_ref_put(estimator)); + } /* Create a new scene with the same medium in the 2 super shapes */ OK(sdis_scene_ref_put(scn)); @@ -431,15 +438,17 @@ main(int argc, char** argv) solve_args.medium = solid0; solve_args.nrealisations = Np; OK(sdis_solve_medium(scn, &solve_args, &estimator)); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - ref = Tf0 * v0/v + Tf1 * v1/v; - printf("Shape0 + Shape1 temperature = %g ~ %g +/- %g\n", ref, T.E, T.SE); - printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); - printf("#failures = %lu/%lu\n", (unsigned long)nfails, Np); - CHK(eq_eps(T.E, ref, T.SE*3)); + if(is_master_process) { + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_time(estimator, &time)); + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + ref = Tf0 * v0/v + Tf1 * v1/v; + printf("Shape0 + Shape1 temperature = %g ~ %g +/- %g\n", ref, T.E, T.SE); + printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); + printf("#failures = %lu/%lu\n", (unsigned long)nfails, Np); + CHK(eq_eps(T.E, ref, T.SE*3)); + } /* Solve green */ BA(sdis_solve_medium_green_function(NULL, &solve_args, &green)); @@ -458,20 +467,23 @@ main(int argc, char** argv) solve_args.picard_order = 1; OK(sdis_solve_medium_green_function(scn, &solve_args, &green)); - OK(sdis_green_function_solve(green, &estimator2)); - check_green_function(green); - check_estimator_eq(estimator, estimator2); - check_green_serialization(green, scn); + if(!is_master_process) { + CHK(green == NULL); + } else { + OK(sdis_green_function_solve(green, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + check_green_serialization(green, scn); - OK(sdis_green_function_ref_put(green)); + OK(sdis_green_function_ref_put(green)); - OK(sdis_estimator_ref_put(estimator)); - OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + } /* Release */ OK(s3dut_mesh_ref_put(msh0)); OK(s3dut_mesh_ref_put(msh1)); - OK(sdis_device_ref_put(dev)); OK(sdis_medium_ref_put(fluid0)); OK(sdis_medium_ref_put(fluid1)); OK(sdis_medium_ref_put(solid0)); @@ -480,9 +492,8 @@ main(int argc, char** argv) OK(sdis_interface_ref_put(solid0_fluid1)); OK(sdis_interface_ref_put(solid1_fluid1)); OK(sdis_scene_ref_put(scn)); + free_default_device(dev); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_solve_medium_2d.c b/src/test_sdis_solve_medium_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -186,7 +186,6 @@ interface_get_specular_fraction int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_mc T = SDIS_MC_NULL; struct sdis_mc time = SDIS_MC_NULL; struct sdis_device* dev = NULL; @@ -219,10 +218,10 @@ main(int argc, char** argv) size_t nreals; size_t nfails; size_t i; + int is_master_process; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + create_default_device(&argc, &argv, &is_master_process, &dev); fluid_shader.temperature = fluid_get_temperature; @@ -347,30 +346,36 @@ main(int argc, char** argv) /* Estimate the temperature of the square */ solve_args.medium = solid0; OK(sdis_solve_medium(scn, &solve_args, &estimator)); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - printf("Square temperature = "STR(Tf0)" ~ %g +/- %g\n", T.E, T.SE); - printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); - printf("#failures = %lu / %lu\n\n", (unsigned long)nfails, N); - CHK(eq_eps(T.E, Tf0, T.SE)); - CHK(nreals + nfails == N); - OK(sdis_estimator_ref_put(estimator)); + if(!is_master_process) { + CHK(estimator == NULL); + } else { + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_time(estimator, &time)); + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + printf("Square temperature = "STR(Tf0)" ~ %g +/- %g\n", T.E, T.SE); + printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); + printf("#failures = %lu / %lu\n\n", (unsigned long)nfails, N); + CHK(eq_eps(T.E, Tf0, T.SE)); + CHK(nreals + nfails == N); + OK(sdis_estimator_ref_put(estimator)); + } /* Estimate the temperature of the disk */ solve_args.medium = solid1; OK(sdis_solve_medium(scn, &solve_args, &estimator)); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - printf("Disk temperature = "STR(Tf1)" ~ %g +/- %g\n", T.E, T.SE); - printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); - printf("#failures = %lu / %lu\n\n", (unsigned long)nfails, N); - CHK(eq_eps(T.E, Tf1, T.SE)); - CHK(nreals + nfails == N); - OK(sdis_estimator_ref_put(estimator)); + if(is_master_process) { + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_time(estimator, &time)); + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + printf("Disk temperature = "STR(Tf1)" ~ %g +/- %g\n", T.E, T.SE); + printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); + printf("#failures = %lu / %lu\n\n", (unsigned long)nfails, N); + CHK(eq_eps(T.E, Tf1, T.SE)); + CHK(nreals + nfails == N); + OK(sdis_estimator_ref_put(estimator)); + } /* Create a new scene with the same medium for the disk and the square */ OK(sdis_scene_ref_put(scn)); @@ -387,16 +392,18 @@ main(int argc, char** argv) BA(sdis_solve_medium(scn, &solve_args, &estimator)); solve_args.medium = solid0; OK(sdis_solve_medium(scn, &solve_args, &estimator)); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - ref = Tf0 * a0/a + Tf1 * a1/a; - printf("Square + Disk temperature = %g ~ %g +/- %g\n", ref, T.E, T.SE); - printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); - printf("#failures = %lu / %lu\n", (unsigned long)nfails, Np); - CHK(eq_eps(T.E, ref, 3*T.SE)); - CHK(nreals + nfails == Np); + if(is_master_process) { + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_time(estimator, &time)); + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + ref = Tf0 * a0/a + Tf1 * a1/a; + printf("Square + Disk temperature = %g ~ %g +/- %g\n", ref, T.E, T.SE); + printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); + printf("#failures = %lu / %lu\n", (unsigned long)nfails, Np); + CHK(eq_eps(T.E, ref, 3*T.SE)); + CHK(nreals + nfails == Np); + } /* Solve green */ BA(sdis_solve_medium_green_function(NULL, &solve_args, &green)); @@ -404,18 +411,21 @@ main(int argc, char** argv) BA(sdis_solve_medium_green_function(scn, &solve_args, NULL)); OK(sdis_solve_medium_green_function(scn, &solve_args, &green)); - OK(sdis_green_function_solve(green, &estimator2)); - check_green_function(green); - check_estimator_eq(estimator, estimator2); - check_green_serialization(green, scn); + if(!is_master_process) { + CHK(green == NULL); + } else { + OK(sdis_green_function_solve(green, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + check_green_serialization(green, scn); - OK(sdis_green_function_ref_put(green)); + OK(sdis_green_function_ref_put(green)); - OK(sdis_estimator_ref_put(estimator)); - OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + } /* Release */ - OK(sdis_device_ref_put(dev)); OK(sdis_medium_ref_put(solid0)); OK(sdis_medium_ref_put(solid1)); OK(sdis_medium_ref_put(fluid0)); @@ -425,11 +435,11 @@ main(int argc, char** argv) OK(sdis_interface_ref_put(solid1_fluid1)); OK(sdis_scene_ref_put(scn)); + free_default_device(dev); + sa_release(positions); sa_release(indices); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_solve_probe.c b/src/test_sdis_solve_probe.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -279,6 +279,7 @@ main(int argc, char** argv) struct sdis_estimator* estimator3 = NULL; struct sdis_green_function* green = NULL; const struct sdis_heat_path* path = NULL; + struct sdis_device_create_args dev_args = SDIS_DEVICE_CREATE_ARGS_DEFAULT; struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; @@ -304,7 +305,8 @@ main(int argc, char** argv) (void)argc, (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + dev_args.allocator = &allocator; + OK(sdis_device_create(&dev_args, &dev)); /* Create the fluid medium */ OK(sdis_data_create diff --git a/src/test_sdis_solve_probe2.c b/src/test_sdis_solve_probe2.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -18,6 +18,8 @@ #include <rsys/math.h> +#include <string.h> + /* * The scene is composed of a solid cube whose temperature is unknown. The * convection coefficient with the surrounding fluid is null. The temperature @@ -145,7 +147,6 @@ interface_get_temperature int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_mc T = SDIS_MC_NULL; struct sdis_mc time = SDIS_MC_NULL; struct sdis_device* dev = NULL; @@ -171,10 +172,10 @@ main(int argc, char** argv) const size_t N = 10000; size_t nreals; size_t nfails; + int is_master_process; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + create_default_device(&argc, &argv, &is_master_process, &dev); /* Create the fluid medium */ fluid_shader.temperature = temperature_unknown; @@ -253,39 +254,46 @@ main(int argc, char** argv) solve_args.time_range[1] = INF; OK(sdis_solve_probe(scn, &solve_args, &estimator)); - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - - /* Print the estimation results */ - ref = 350 * solve_args.position[2] + (1-solve_args.position[2]) * 300; - printf("Temperature at (%g, %g, %g) = %g ~ %g +/- %g\n", - SPLIT3(solve_args.position), ref, T.E, T.SE); - printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); - printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); - - /* Check the results */ - CHK(nfails + nreals == N); - CHK(nfails < N/1000); - CHK(eq_eps(T.E, ref, 3*T.SE)); + if(!is_master_process) { + CHK(estimator == NULL); + } else { + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_time(estimator, &time)); + + /* Print the estimation results */ + ref = 350 * solve_args.position[2] + (1-solve_args.position[2]) * 300; + printf("Temperature at (%g, %g, %g) = %g ~ %g +/- %g\n", + SPLIT3(solve_args.position), ref, T.E, T.SE); + printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); + printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); + + /* Check the results */ + CHK(nfails + nreals == N); + CHK(nfails < N/1000); + CHK(eq_eps(T.E, ref, 3*T.SE)); + } /* Check green */ OK(sdis_solve_probe_green_function(scn, &solve_args, &green)); - OK(sdis_green_function_solve(green, &estimator2)); - check_green_function(green); - check_estimator_eq(estimator, estimator2); - check_green_serialization(green, scn); + if(!is_master_process) { + CHK(green == NULL); + } else { + OK(sdis_green_function_solve(green, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + check_green_serialization(green, scn); + } /* Release data */ - OK(sdis_estimator_ref_put(estimator)); - OK(sdis_estimator_ref_put(estimator2)); - OK(sdis_green_function_ref_put(green)); + if(estimator) OK(sdis_estimator_ref_put(estimator)); + if(estimator2) OK(sdis_estimator_ref_put(estimator2)); + if(green) OK(sdis_green_function_ref_put(green)); OK(sdis_scene_ref_put(scn)); - OK(sdis_device_ref_put(dev)); + free_default_device(dev); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); + return 0; } diff --git a/src/test_sdis_solve_probe2_2d.c b/src/test_sdis_solve_probe2_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -141,7 +141,6 @@ interface_get_temperature int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_mc T = SDIS_MC_NULL; struct sdis_mc time = SDIS_MC_NULL; struct sdis_device* dev = NULL; @@ -169,8 +168,7 @@ main(int argc, char** argv) size_t nfails; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Create the fluid medium */ fluid_shader.temperature = temperature_unknown; @@ -282,8 +280,6 @@ main(int argc, char** argv) OK(sdis_green_function_ref_put(green)); OK(sdis_device_ref_put(dev)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_solve_probe3.c b/src/test_sdis_solve_probe3.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -166,7 +166,6 @@ interface_get_temperature int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_mc T = SDIS_MC_NULL; struct sdis_mc time = SDIS_MC_NULL; struct sdis_device* dev = NULL; @@ -199,8 +198,7 @@ main(int argc, char** argv) size_t i; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Create the fluid medium */ fluid_shader.temperature = temperature_unknown; @@ -262,7 +260,7 @@ main(int argc, char** argv) } /* Setup a sphere at the center of the box */ - OK(s3dut_create_sphere(&allocator, 0.25, 64, 32, &msh)); + OK(s3dut_create_sphere(NULL, 0.25, 64, 32, &msh)); OK(s3dut_mesh_get_data(msh, &msh_data)); FOR_EACH(i, 0, msh_data.nvertices) { sa_push(ctx.positions, msh_data.positions[i*3+0] + 0.5); @@ -339,8 +337,6 @@ main(int argc, char** argv) OK(sdis_scene_ref_put(scn)); OK(sdis_device_ref_put(dev)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; diff --git a/src/test_sdis_solve_probe3_2d.c b/src/test_sdis_solve_probe3_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -163,7 +163,6 @@ interface_get_temperature int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_mc T = SDIS_MC_NULL; struct sdis_mc time = SDIS_MC_NULL; struct sdis_device* dev = NULL; @@ -194,8 +193,7 @@ main(int argc, char** argv) size_t i; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Create the fluid medium */ fluid_shader.temperature = temperature_unknown; @@ -331,8 +329,6 @@ main(int argc, char** argv) OK(sdis_scene_ref_put(scn)); OK(sdis_device_ref_put(dev)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_solve_probe_2d.c b/src/test_sdis_solve_probe_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -133,7 +133,6 @@ interface_get_convection_coef int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_mc T = SDIS_MC_NULL; struct sdis_mc time = SDIS_MC_NULL; struct sdis_device* dev = NULL; @@ -158,8 +157,7 @@ main(int argc, char** argv) size_t nfails; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Create the fluid medium */ OK(sdis_data_create @@ -243,8 +241,6 @@ main(int argc, char** argv) OK(sdis_scene_ref_put(scn)); OK(sdis_device_ref_put(dev)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_transcient.c b/src/test_sdis_transcient.c @@ -463,7 +463,6 @@ temperature_analytical int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_device* dev = NULL; struct sdis_scene* box_scn = NULL; struct sdis_scene* box2_scn = NULL; @@ -509,8 +508,7 @@ main(int argc, char** argv) boxsz[1] = 0.1; boxsz[2] = 0.2; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Create the fluid medium */ fluid_shader = DUMMY_FLUID_SHADER; @@ -680,8 +678,6 @@ main(int argc, char** argv) OK(sdis_scene_ref_put(box2_scn)); OK(sdis_scene_ref_put(box_matriochka_scn)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_unstationary_atm.c b/src/test_sdis_unstationary_atm.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -703,7 +703,6 @@ solve_tfluid int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_data* data = NULL; struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; @@ -728,8 +727,7 @@ main(int argc, char** argv) struct ssp_rng* rng = NULL; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Setup the solid shader */ solid_shader.calorific_capacity = solid_get_calorific_capacity; @@ -899,7 +897,7 @@ main(int argc, char** argv) OK(sdis_interface_ref_put(interf_TA)); /* Solve */ - OK(ssp_rng_create(&allocator, SSP_RNG_KISS, &rng)); + OK(ssp_rng_create(NULL, SSP_RNG_KISS, &rng)); printf(">> Box scene\n"); solve_tfluid(box_scn); solve_tbound1(box_scn, rng); @@ -916,8 +914,6 @@ main(int argc, char** argv) OK(sdis_device_ref_put(dev)); OK(ssp_rng_ref_put(rng)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_utils.c b/src/test_sdis_utils.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -480,4 +480,3 @@ check_green_serialization OK(sdis_green_function_ref_put(green2)); } - diff --git a/src/test_sdis_utils.h b/src/test_sdis_utils.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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,6 +21,11 @@ #include <rsys/double33.h> #include <rsys/mem_allocator.h> #include <stdio.h> +#include <string.h> + +#ifdef SDIS_ENABLE_MPI + #include <mpi.h> +#endif #define BOLTZMANN_CONSTANT 5.6696e-8 /* W/m^2/K^4 */ @@ -198,6 +203,66 @@ static const struct sdis_interface_shader DUMMY_INTERFACE_SHADER = { }; /******************************************************************************* + * Device creation + ******************************************************************************/ +#ifndef SDIS_ENABLE_MPI + +static INLINE void +create_default_device + (int* argc, + char*** argv, + int* is_master_process, + struct sdis_device** dev) +{ + (void)argc, (void)argv; + CHK(dev && is_master_process); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, dev)); + *is_master_process = 1; +} + +#else + +static INLINE void +create_default_device + (int* pargc, + char*** pargv, + int* is_master_process, + struct sdis_device** out_dev) +{ + struct sdis_device_create_args dev_args = SDIS_DEVICE_CREATE_ARGS_DEFAULT; + struct sdis_device* dev = NULL; + int mpi_thread_support; + int mpi_rank; + CHK(pargc && pargv && is_master_process && out_dev); + + CHK(MPI_Init_thread + (pargc, pargv, MPI_THREAD_SERIALIZED, &mpi_thread_support) == MPI_SUCCESS); + CHK(mpi_thread_support >= MPI_THREAD_SERIALIZED); + + dev_args.use_mpi = *pargc >= 2 && !strcmp((*pargv)[1], "mpi"); + OK(sdis_device_create(&dev_args, &dev)); + + if(dev_args.use_mpi) { + OK(sdis_device_get_mpi_rank(dev, &mpi_rank)); + *is_master_process = mpi_rank == 0; + } else { + CHK(sdis_device_get_mpi_rank(dev, &mpi_rank) == RES_BAD_OP); + *is_master_process = 1; + } + *out_dev = dev; +} +#endif + +static INLINE void +free_default_device(struct sdis_device* dev) +{ + OK(sdis_device_ref_put(dev)); +#ifdef SDIS_ENABLE_MPI + CHK(MPI_Finalize() == MPI_SUCCESS); +#endif +} + +/******************************************************************************* * Miscellaneous ******************************************************************************/ static INLINE void diff --git a/src/test_sdis_volumic_power.c b/src/test_sdis_volumic_power.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -379,7 +379,6 @@ solve int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_data* data = NULL; struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; @@ -399,8 +398,7 @@ main(int argc, char** argv) struct ssp_rng* rng = NULL; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); fluid_shader.temperature = fluid_get_temperature; OK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); @@ -487,7 +485,7 @@ main(int argc, char** argv) OK(sdis_interface_ref_put(interf_T0)); /* Solve */ - OK(ssp_rng_create(&allocator, SSP_RNG_KISS, &rng)); + OK(ssp_rng_create(NULL, SSP_RNG_KISS, &rng)); printf(">> Box scene\n"); solve(box_scn, rng, solid_props); printf(">> Square scene\n"); @@ -498,8 +496,6 @@ main(int argc, char** argv) OK(sdis_device_ref_put(dev)); OK(ssp_rng_ref_put(rng)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_volumic_power2.c b/src/test_sdis_volumic_power2.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -302,8 +302,7 @@ main(int argc, char** argv) }; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Setup the fluid shader */ fluid_shader.temperature = fluid_get_temperature; diff --git a/src/test_sdis_volumic_power2_2d.c b/src/test_sdis_volumic_power2_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -284,7 +284,6 @@ check(struct sdis_scene* scn, const struct reference refs[], const size_t nrefs) int main(int argc, char** argv) { - struct mem_allocator allocator; struct solid* solid_param = NULL; struct fluid* fluid_param = NULL; struct interf* interf_param = NULL; @@ -334,8 +333,7 @@ main(int argc, char** argv) }; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Setup the fluid shader */ fluid_shader.temperature = fluid_get_temperature; @@ -501,8 +499,6 @@ main(int argc, char** argv) OK(sdis_scene_ref_put(scn)); OK(sdis_device_ref_put(dev)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_volumic_power3_2d.c b/src/test_sdis_volumic_power3_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -238,7 +238,6 @@ interface_get_temperature int main(int argc, char** argv) { - struct mem_allocator allocator; struct solid* solid_param = NULL; struct fluid* fluid_param = NULL; struct interf* interf_param = NULL; @@ -271,8 +270,7 @@ main(int argc, char** argv) size_t nreals; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Create the fluid medium */ fluid_shader.temperature = fluid_get_temperature; @@ -469,8 +467,6 @@ main(int argc, char** argv) OK(sdis_scene_ref_put(scn)); OK(sdis_device_ref_put(dev)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; } diff --git a/src/test_sdis_volumic_power4.c b/src/test_sdis_volumic_power4.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2021 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2016-2022 |Meso|Star> (contact@meso-star.com) * * 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 @@ -205,7 +205,6 @@ main(int argc, char** argv) { char dump[128]; struct time t0, t1; - struct mem_allocator allocator; struct solid* solid_param = NULL; struct fluid* fluid_param = NULL; struct interf* interf_param = NULL; @@ -233,8 +232,7 @@ main(int argc, char** argv) double x; (void)argc, (void)argv; - OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); /* Create the fluid medium */ fluid_shader.temperature = fluid_get_temperature; @@ -413,8 +411,6 @@ main(int argc, char** argv) OK(sdis_scene_ref_put(scn_3d)); OK(sdis_device_ref_put(dev)); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); return 0; }