stardis-solver

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

commit 65da61bc8ff2d761285fa22fb269edbcc0d71617
parent 4a4b2e47445621dcce2ceff0f06d3bf3fb0fe417
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri,  4 Mar 2022 10:06:19 +0100

Merge branch 'release_0.13'

Diffstat:
MREADME.md | 62++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mcmake/CMakeLists.txt | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Asrc/sdis.c | 870+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis.h | 204++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/sdis_Xd_begin.h | 45++++++++++++++++++++++++++++++++++++++++-----
Msrc/sdis_Xd_end.h | 2+-
Asrc/sdis_c.h | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_camera.c | 2+-
Msrc/sdis_camera.h | 2+-
Msrc/sdis_data.c | 2+-
Msrc/sdis_device.c | 323+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
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 | 16++++++++++++++--
Msrc/sdis_green.h | 6+++++-
Msrc/sdis_heat_path.c | 147++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/sdis_heat_path.h | 122++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Asrc/sdis_heat_path_boundary.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_heat_path_boundary_Xd.h | 952+------------------------------------------------------------------------------
Asrc/sdis_heat_path_boundary_Xd_c.h | 875+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_boundary_Xd_fixed_flux.h | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_heat_path_boundary_Xd_solid_fluid_picard1.h | 373+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Asrc/sdis_heat_path_boundary_Xd_solid_fluid_picardN.h | 365+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_boundary_Xd_solid_solid.h | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_boundary_c.h | 219+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_heat_path_conductive_Xd.h | 338+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/sdis_heat_path_convective_Xd.h | 326+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/sdis_heat_path_radiative_Xd.h | 26++++++++++++++++----------
Msrc/sdis_interface.c | 16+++++++++-------
Msrc/sdis_interface_c.h | 18+++++++++++++++++-
Msrc/sdis_log.c | 38++++++++++++++++++++++++++++----------
Msrc/sdis_log.h | 2+-
Msrc/sdis_medium.c | 4++--
Msrc/sdis_medium_c.h | 283+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/sdis_misc.c | 50+++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/sdis_misc.h | 37++++++++++++++++++++++++++++---------
Msrc/sdis_misc_Xd.h | 40++++++++++++++++------------------------
Asrc/sdis_mpi.c | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_mpi.h | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_realisation.c | 59++++++++++++++++++++++++++++++++++++++---------------------
Msrc/sdis_realisation.h | 143++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/sdis_realisation_Xd.h | 219++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/sdis_scene.c | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/sdis_scene_Xd.h | 7++++---
Msrc/sdis_scene_c.h | 30+++++++++++++++++++++++++-----
Msrc/sdis_solve.c | 349+------------------------------------------------------------------------------
Msrc/sdis_solve_boundary_Xd.h | 657+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Asrc/sdis_solve_camera.c | 704+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_solve_medium_Xd.h | 529+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/sdis_solve_probe_Xd.h | 310+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/sdis_solve_probe_boundary_Xd.h | 708+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
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 | 94++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/test_sdis_conducto_radiative.c | 87++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/test_sdis_conducto_radiative_2d.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/test_sdis_contact_resistance.c | 12++++--------
Msrc/test_sdis_contact_resistance.h | 19++++++++-----------
Msrc/test_sdis_contact_resistance_2.c | 16++++++----------
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 | 22++++++++++++++--------
Msrc/test_sdis_interface.c | 8++------
Msrc/test_sdis_medium.c | 16++++++----------
Asrc/test_sdis_picard.c | 776+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_scene.c | 68++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/test_sdis_solid_random_walk_robustness.c | 12++++--------
Msrc/test_sdis_solve_boundary.c | 275++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/test_sdis_solve_boundary_flux.c | 168++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/test_sdis_solve_camera.c | 163+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/test_sdis_solve_medium.c | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/test_sdis_solve_medium_2d.c | 100+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/test_sdis_solve_probe.c | 68+++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/test_sdis_solve_probe2.c | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/test_sdis_solve_probe2_2d.c | 10+++-------
Msrc/test_sdis_solve_probe3.c | 12++++--------
Msrc/test_sdis_solve_probe3_2d.c | 10+++-------
Msrc/test_sdis_solve_probe_2d.c | 10+++-------
Msrc/test_sdis_transcient.c | 20++++++++------------
Msrc/test_sdis_unstationary_atm.c | 251+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/test_sdis_utils.c | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/test_sdis_utils.h | 116++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/test_sdis_volumic_power.c | 14+++++---------
Msrc/test_sdis_volumic_power2.c | 14+++++---------
Msrc/test_sdis_volumic_power2_2d.c | 14+++++---------
Msrc/test_sdis_volumic_power3_2d.c | 14+++++---------
Msrc/test_sdis_volumic_power4.c | 10+++-------
95 files changed, 9809 insertions(+), 3755 deletions(-)

diff --git a/README.md b/README.md @@ -20,13 +20,16 @@ The hypothesis these algorithms are based upon are the following: - *convection*: fluid media are supposed to be isothermal, even if their temperature may vary with time. This hypothesis relies on the assumption of perfectly agitated fluids. -- *radiation*: local radiative transfer is linearised, i.e. instead of writing - the spectrally integrated net flux as a difference of temperatures to the - power 4, it is assumed of the same form as the convective flux (as a - difference of temperatures, multiplied by a radiative exchange coefficient). - In order to be valid, this representation of radiative transfer exchanges - requires that the temperature at any position and time is close to a known - reference temperature. +- *radiation*: local radiative transfer is solved by an [iterative numerical + method](https://hal.archives-ouvertes.fr/tel-03266863/) (Picard algorithm) + that requires the knowledge of a reference temperature field. At the basic + level (one level of recursion), and using a uniform reference temperature + field, this algorithm translates into the hypothesis of a linearized + radiative transfer. Using a higher order or recursion makes possible to + converge the result closer to the solution of a rigorous + spectrally-integrated radiative transfer (a difference of temperatures to the + power 4 when integrated over the whole spectrum). The higher the recursion + order, to better will be the convergence of the algorithm. In Stardis-Solver the system to simulate is represented by a *scene* whose geometry defines the contour of the object only: in contrast to legacy thermal @@ -58,7 +61,7 @@ The main features of the solver are currently: been reached; when internal power sources or imposed fluxes are taken into account, additional contributions to the weight must be continuously evaluated by the thermal conduction algorithm, but these contributions are - proportional to the local dissipated power/imposed flux. In any case, the + proportional to the local dissipated power/imposed flux. In any case, the position and date at the end of each thermal path (and also accumulation coefficients) can be stored during a first complete Monte-Carlo simulation. This information, known as the Green function, can then be used in (very @@ -67,7 +70,8 @@ The main features of the solver are currently: flux). Note that when using the Green function, only boundary and initial conditions (as well as internal power sources) can be modified: in particular, the geometry, thermal properties and exchange coefficients have - to remain identical. + to remain identical. Furthermore, the green function is only valid under the + assumption of linearized radiative transfer. - *path visualization*: Stardis-Solver can store the complete spatial and temporal position along a set of thermal paths, for latter visualization. In addition of their position and, each thermal path vertex register additional @@ -79,7 +83,7 @@ Stardis-Solver is currently used in two frameworks. The tools is the reference workflow of Stardis-Solver. It proposes a complete toolchain from fileformats describing the scene (geometry, thermal properties, limit and boundary conditions) to computations and post-treatments of the -results ([Stardis-Green](https://gitlab.com/meso-star/stardis-green.git). +results ([Stardis-Green](https://gitlab.com/meso-star/stardis-green.git)). Stardis-Solver is also integrated into [SYRTHES](https://www.edf.fr/en/the-edf-group/world-s-largest-power-company/activities/research-and-development/scientific-communities/simulation-softwares?logiciel=10818), the general thermal free software developed by Electricité De France (EDF). @@ -101,7 +105,9 @@ It also depends on the [Star-Enclosures-2D](https://gitlab.com/meso-star/star-enclosures-2d/) and [Star-SP](https://gitlab.com/meso-star/star-sp/) libraries as well as on the [OpenMP](http://www.openmp.org) 2.0 specification to parallelize its -computations. +computations. It may depend on [OpenMPI](https://www.open-mpi.org/) 2.0 if +distributed memory parallelism is enabled via the `ENABLE_MPI` variable of the +CMake file First ensure that CMake and a C compiler that implements the OpenMP 2.0 specification are installed on your system. Then install the RCMake package as @@ -111,6 +117,38 @@ variable the install directories of its dependencies. ## Release notes +### Version 0.13 + +#### Non linear radiative transfer + +Uses a new [iterative numerical +method](https://hal.archives-ouvertes.fr/tel-03266863/) to estimate radiative +transfer. With a recursion level of 1, this is equivalent to a linearization of +the radiative transfer but with a reference temperature that can vary in time +and space. By using a higher-order recursion, one can converge towards a +rigorous estimate that takes into account the non-linearity of the radiative +transfer; the higher the recursion order, the better the convergence, but with +the counterpart of an increase in calculation time. + +#### Distributed memory parallelism + +Uses message passing interface to distribute computation across multiple +computers. Stardis-Solver now, uses a mixed parallelism: on one computer (i.e. a +node), it uses a shared memory parallelism and relies on the message passing +interface to parallelize calculations between several nodes. + +#### Type and state of the random number generator + +Adds the member input variable `rng_type` to the solve functions. It +defines the type of random number generator to use when no generator is +defined. Note that the `sdis_solve_camera` function does not have a random +number generator as an input variable and has therefore been updated to support +it. + +#### Reading the source code + +Refactoring and deep rewriting of the source code to simplify its reading. + ### Version 0.12.3 Fix green paths ending in a fluid (transcient computation): The path's end was @@ -374,7 +412,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 |Méso|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) @@ -56,11 +65,12 @@ rcmake_append_runtime_dirs(_runtime_dirs # Configure and define targets ############################################################################### set(VERSION_MAJOR 0) -set(VERSION_MINOR 12) -set(VERSION_PATCH 3) +set(VERSION_MINOR 13) +set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SDIS_FILES_SRC + sdis.c sdis_camera.c sdis_data.c sdis_device.c @@ -68,24 +78,32 @@ set(SDIS_FILES_SRC sdis_estimator_buffer.c sdis_green.c sdis_heat_path.c + sdis_heat_path_boundary.c sdis_interface.c sdis_log.c sdis_medium.c 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 sdis_green.h sdis_heat_path.h + sdis_heat_path_boundary_c.h sdis_heat_path_boundary_Xd.h + sdis_heat_path_boundary_Xd_fixed_flux.h + sdis_heat_path_boundary_Xd_solid_fluid_picard1.h + sdis_heat_path_boundary_Xd_solid_solid.h sdis_heat_path_conductive_Xd.h sdis_heat_path_convective_Xd.h sdis_heat_path_radiative_Xd.h @@ -102,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' @@ -147,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) ############################################################################### @@ -175,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) @@ -187,24 +214,28 @@ if(NOT NO_TEST) new_test(test_sdis_flux) new_test(test_sdis_interface) new_test(test_sdis_medium) + 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) @@ -222,7 +253,40 @@ 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) - + + target_link_libraries(test_sdis_solve_boundary StarSP) + target_link_libraries(test_sdis_solve_boundary_flux StarSP) + target_link_libraries(test_sdis_solve_probe2 StarSP) + target_link_libraries(test_sdis_solve_medium StarSP) + + 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() @@ -236,4 +300,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,870 @@ +/* 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, + const enum ssp_rng_type rng_type, + 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 = rng_type; + 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 @@ -16,6 +16,8 @@ #ifndef SDIS_H #define SDIS_H +#include <star/ssp.h> + #include <rsys/rsys.h> #include <float.h> @@ -50,7 +52,6 @@ struct logger; struct mem_allocator; struct senc2d_scene; struct senc3d_scene; -struct ssp_rng; /* Forward declaration of the Stardis opaque data types. These data types are * ref counted. Once created the caller implicitly owns the created data, i.e. @@ -130,6 +131,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 ******************************************************************************/ @@ -159,7 +185,7 @@ struct sdis_solid_shader { sdis_medium_getter_T calorific_capacity; /* In J.K^-1.kg^-1 */ sdis_medium_getter_T thermal_conductivity; /* In W.m^-1.K^-1 */ sdis_medium_getter_T volumic_mass; /* In kg.m^-3 */ - sdis_medium_getter_T delta_solid; + sdis_medium_getter_T delta; /* May be NULL if there is no volumic power. One can also return * SDIS_VOLUMIC_POWER_NONE to define that there is no volumic power at the @@ -207,8 +233,11 @@ struct sdis_interface_side_shader { * interface or if the emissivity is 0 onto the whole interface. */ sdis_interface_getter_T emissivity; /* Overall emissivity. */ sdis_interface_getter_T specular_fraction; /* Specular part in [0,1] */ + + /* Reference temperature used in Picard 1 */ + sdis_interface_getter_T reference_temperature; }; -#define SDIS_INTERFACE_SIDE_SHADER_NULL__ { NULL, NULL, NULL, NULL } +#define SDIS_INTERFACE_SIDE_SHADER_NULL__ { NULL, NULL, NULL, NULL, NULL } static const struct sdis_interface_side_shader SDIS_INTERFACE_SIDE_SHADER_NULL = SDIS_INTERFACE_SIDE_SHADER_NULL__; @@ -256,8 +285,9 @@ struct sdis_heat_vertex { double time; double weight; enum sdis_heat_vertex_type type; + int branch_id; }; -#define SDIS_HEAT_VERTEX_NULL__ {{0,0,0}, 0, 0, SDIS_HEAT_VERTEX_CONDUCTION} +#define SDIS_HEAT_VERTEX_NULL__ {{0,0,0}, 0, 0, SDIS_HEAT_VERTEX_CONDUCTION, 0} static const struct sdis_heat_vertex SDIS_HEAT_VERTEX_NULL = SDIS_HEAT_VERTEX_NULL__; @@ -357,20 +387,31 @@ typedef void double pos[], /* Output list of vertex coordinates */ void* ctx); +struct sdis_ambient_radiative_temperature { + double temperature; /* In Kelvin */ + double reference; /* Used to linearise the radiative transfer */ +}; +#define SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL__ {-1, -1} +static const struct sdis_ambient_radiative_temperature +SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL = + SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL__; + struct sdis_scene_create_args { /* Functors to retrieve the geometric description */ sdis_get_primitive_indices_T get_indices; sdis_get_primitive_interface_T get_interface; sdis_get_vertex_position_T get_position; - /* Pointer toward client side sent as the last argument of the callbacks */ + /* Pointer toward client side sent as the last argument of the callbacks */ void* context; size_t nprimitives; /* #primitives, i.e. #segments or #triangles */ size_t nvertices; /* #vertices */ double fp_to_meter; /* Scale factor used to convert 1.0 in 1 meter */ - double trad; /* Ambiant radiative temperature */ - double tref; /* Temperature used to linearize the radiative temperature */ + struct sdis_ambient_radiative_temperature trad; /* Ambient radiative temp */ + + /* Min/max temperature used to linearise the radiative temperature */ + double t_range[2]; }; #define SDIS_SCENE_CREATE_ARGS_DEFAULT__ { \ @@ -381,8 +422,8 @@ struct sdis_scene_create_args { 0, /* #primitives */ \ 0, /* #vertices */ \ 1.0, /* #Floating point to meter scale factor */ \ - -1.0, /* Ambient radiative temperature */ \ - -1.0 /* Reference temperature */ \ + SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL__,/* Ambient radiative temperature */\ + {0.0, -1.0} /* Temperature range */ \ } static const struct sdis_scene_create_args SDIS_SCENE_CREATE_ARGS_DEFAULT = SDIS_SCENE_CREATE_ARGS_DEFAULT__; @@ -394,15 +435,24 @@ struct sdis_solve_probe_args { size_t nrealisations; /* #realisations */ double position[3]; /* Probe position */ double time_range[2]; /* Observation time */ + + /* Set the Picard recursion order to estimate the radiative temperature. An + * order of one means that the radiative temperature is linearized, while + * higher orders allow the estimation of the T4 radiative transfer. */ + size_t picard_order; + int register_paths; /* Combination of enum sdis_heat_path_flag */ struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ + enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ }; #define SDIS_SOLVE_PROBE_ARGS_DEFAULT__ { \ 10000, /* #realisations */ \ {0,0,0}, /* Position */ \ {DBL_MAX,DBL_MAX}, /* Time range */ \ + 1, /* Picard order */ \ SDIS_HEAT_PATH_NONE, /* Register paths mask */ \ - NULL /* RNG state */ \ + NULL, /* RNG state */ \ + SSP_RNG_THREEFRY /* RNG type */ \ } static const struct sdis_solve_probe_args SDIS_SOLVE_PROBE_ARGS_DEFAULT = SDIS_SOLVE_PROBE_ARGS_DEFAULT__; @@ -413,18 +463,27 @@ struct sdis_solve_probe_boundary_args { size_t iprim; /* Identifier of the primitive on which the probe lies */ double uv[2]; /* Parametric coordinates of the probe onto the primitve */ double time_range[2]; /* Observation time */ + + /* Set the Picard recursion order to estimate the radiative temperature. An + * order of one means that the radiative temperature is linearized, while + * higher orders allow the estimation of the T4 radiative transfer. */ + size_t picard_order; + enum sdis_side side; /* Side of iprim on which the probe lies */ int register_paths; /* Combination of enum sdis_heat_path_flag */ struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ + enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ }; #define SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT__ { \ 10000, /* #realisations */ \ 0, /* Primitive identifier */ \ {0,0}, /* UV */ \ {DBL_MAX,DBL_MAX}, /* Time range */ \ + 1, /* Picard order */ \ SDIS_SIDE_NULL__, \ SDIS_HEAT_PATH_NONE, \ - NULL /* RNG state */ \ + NULL, /* RNG state */ \ + SSP_RNG_THREEFRY /* RNG type */ \ } static const struct sdis_solve_probe_boundary_args SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT = @@ -436,8 +495,15 @@ struct sdis_solve_boundary_args { const enum sdis_side* sides; /* Per primitive side to consider */ size_t nprimitives; /* #primitives */ double time_range[2]; /* Observation time */ + + /* Set the Picard recursion order to estimate the radiative temperature. An + * order of one means that the radiative temperature is linearized, while + * higher orders allow the estimation of the T4 radiative transfer. */ + size_t picard_order; + int register_paths; /* Combination of enum sdis_heat_path_flag */ struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ + enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ }; #define SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT__ { \ 10000, /* #realisations */ \ @@ -445,8 +511,10 @@ struct sdis_solve_boundary_args { NULL, /* Per primitive side */ \ 0, /* #primitives */ \ {DBL_MAX,DBL_MAX}, /* Time range */ \ + 1, /* Picard order */ \ SDIS_HEAT_PATH_NONE, \ - NULL /* RNG state */ \ + NULL, /* RNG state */ \ + SSP_RNG_THREEFRY /* RNG type */ \ } static const struct sdis_solve_boundary_args SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT__; @@ -455,15 +523,24 @@ struct sdis_solve_medium_args { size_t nrealisations; /* #realisations */ struct sdis_medium* medium; /* Medium to solve */ double time_range[2]; /* Observation time */ + + /* Set the Picard recursion order to estimate the radiative temperature. An + * order of one means that the radiative temperature is linearized, while + * higher orders allow the estimation of the T4 radiative transfer. */ + size_t picard_order; + int register_paths; /* Combination of enum sdis_heat_path_flag */ struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ + enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ }; #define SDIS_SOLVE_MEDIUM_ARGS_DEFAULT__ { \ 10000, /* #realisations */ \ NULL, /* Medium */ \ {DBL_MAX,DBL_MAX}, /* Time range */ \ + 1, /* Picard order */ \ SDIS_HEAT_PATH_NONE, \ - NULL /* RNG state */ \ + NULL, /* RNG state */ \ + SSP_RNG_THREEFRY /* RNG type */ \ } static const struct sdis_solve_medium_args SDIS_SOLVE_MEDIUM_ARGS_DEFAULT = SDIS_SOLVE_MEDIUM_ARGS_DEFAULT__; @@ -473,14 +550,23 @@ struct sdis_solve_probe_boundary_flux_args { size_t iprim; /* Identifier of the primitive on which the probe lies */ double uv[2]; /* Parametric coordinates of the probe onto the primitve */ double time_range[2]; /* Observation time */ + + /* Set the Picard recursion order to estimate the radiative temperature. An + * order of one means that the radiative temperature is linearized, while + * higher orders allow the estimation of the T4 radiative transfer. */ + size_t picard_order; + struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ + enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ }; #define SDIS_SOLVE_PROBE_BOUNDARY_FLUX_ARGS_DEFAULT__ { \ 10000, /* #realisations */ \ 0, /* Primitive identifier */ \ {0,0}, /* UV */ \ {DBL_MAX,DBL_MAX}, /* Time range */ \ - NULL /* RNG state */ \ + 1, /* Picard order */ \ + NULL, /* RNG state */ \ + SSP_RNG_THREEFRY /* RNG type */ \ } static const struct sdis_solve_probe_boundary_flux_args SDIS_SOLVE_PROBE_BOUNDARY_FLUX_ARGS_DEFAULT = @@ -491,14 +577,23 @@ struct sdis_solve_boundary_flux_args { const size_t* primitives; /* List of boundary primitives to handle */ size_t nprimitives; /* #primitives */ double time_range[2]; /* Observation time */ + + /* Set the Picard recursion order to estimate the radiative temperature. An + * order of one means that the radiative temperature is linearized, while + * higher orders allow the estimation of the T4 radiative transfer. */ + size_t picard_order; + struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ + enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ }; #define SDIS_SOLVE_BOUNDARY_FLUX_ARGS_DEFAULT__ { \ 10000, /* #realisations */ \ NULL, /* List or primitive ids */ \ 0, /* #primitives */ \ {DBL_MAX,DBL_MAX}, /* Time range */ \ - NULL /* RNG state */ \ + 1, /* Picard order */ \ + NULL, /* RNG state */ \ + SSP_RNG_THREEFRY /* RNG type */ \ } static const struct sdis_solve_boundary_flux_args SDIS_SOLVE_BOUNDARY_FLUX_ARGS_DEFAULT = @@ -507,16 +602,28 @@ SDIS_SOLVE_BOUNDARY_FLUX_ARGS_DEFAULT = struct sdis_solve_camera_args { struct sdis_camera* cam; /* Point of view */ double time_range[2]; /* Observation time */ - size_t image_resolution[2]; /* Image resolution */ + + /* Set the Picard recursion order to estimate the radiative temperature. An + * order of one means that the radiative temperature is linearized, while + * higher orders allow the estimation of the T4 radiative transfer. */ + size_t picard_order; + + size_t image_definition[2]; /* Image definition */ size_t spp; /* #samples per pixel */ int register_paths; /* Combination of enum sdis_heat_path_flag */ + + struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ + enum ssp_rng_type rng_type; /* RNG type to use */ }; #define SDIS_SOLVE_CAMERA_ARGS_DEFAULT__ { \ NULL, /* Camera */ \ {DBL_MAX,DBL_MAX}, /* Time range */ \ + 1, /* Picard order */ \ {512,512}, /* Image resolution */ \ 256, /* #realisations per pixel */ \ - SDIS_HEAT_PATH_NONE \ + SDIS_HEAT_PATH_NONE, \ + NULL, /* RNG state */ \ + SSP_RNG_THREEFRY /* RNG type */ \ } static const struct sdis_solve_camera_args SDIS_SOLVE_CAMERA_ARGS_DEFAULT = SDIS_SOLVE_CAMERA_ARGS_DEFAULT__; @@ -526,12 +633,14 @@ struct sdis_compute_power_args { struct sdis_medium* medium; /* Medium to solve */ double time_range[2]; /* Observation time */ struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ + enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ }; #define SDIS_COMPUTE_POWER_ARGS_DEFAULT__ { \ 10000, /* #realisations */ \ NULL, /* Medium */ \ {DBL_MAX,DBL_MAX}, /* Time range */ \ - NULL /* RNG state */ \ + NULL, /* RNG state */ \ + SSP_RNG_THREEFRY /* RNG type */ \ } static const struct sdis_compute_power_args SDIS_COMPUTE_POWER_ARGS_DEFAULT = SDIS_COMPUTE_POWER_ARGS_DEFAULT__; @@ -544,10 +653,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 @@ -558,6 +664,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 @@ -826,27 +937,27 @@ sdis_scene_set_fp_to_meter SDIS_API res_T sdis_scene_get_ambient_radiative_temperature (const struct sdis_scene* scn, - double* trad); + struct sdis_ambient_radiative_temperature* trad); /* Set scene's ambient radiative temperature. If set negative, any sample * ending in ambient radiative temperature will fail */ SDIS_API res_T sdis_scene_set_ambient_radiative_temperature (struct sdis_scene* scn, - const double trad); + const struct sdis_ambient_radiative_temperature* trad); -/* Get scene's reference temperature */ +/* Get scene's minimum/maximum temperature */ SDIS_API res_T -sdis_scene_get_reference_temperature +sdis_scene_get_temperature_range (const struct sdis_scene* scn, - double* tref); + double t_range[2]); -/* Set scene's reference temperature. If set to 0, there is no radiative - * transfert in the whole system */ +/* Set scene's minimum/maximum temperature. Must be correctly defined if there + * is any radiative transfer in the scene */ SDIS_API res_T -sdis_scene_set_reference_temperature +sdis_scene_set_temperature_range (struct sdis_scene* scn, - const double tref); + const double t_range[2]); /* Search the point onto the scene geometry that is the closest of `pos'. The * `radius' parameter controls the maximum search distance around `pos'. The @@ -1130,24 +1241,32 @@ sdis_green_path_for_each_flux_term * Heat path API ******************************************************************************/ SDIS_API res_T -sdis_heat_path_get_vertices_count +sdis_heat_path_get_status (const struct sdis_heat_path* path, - size_t* nvertices); + enum sdis_heat_path_flag* status); SDIS_API res_T -sdis_heat_path_get_status +sdis_heat_path_get_line_strips_count (const struct sdis_heat_path* path, - enum sdis_heat_path_flag* status); + size_t* nstrips); + +SDIS_API res_T +sdis_heat_path_line_strip_get_vertices_count + (const struct sdis_heat_path* path, + const size_t istrip, + size_t* nvertices); SDIS_API res_T -sdis_heat_path_get_vertex +sdis_heat_path_line_strip_get_vertex (const struct sdis_heat_path* path, - const size_t ivertex, + const size_t istrip, + const size_t ivert, struct sdis_heat_vertex* vertex); SDIS_API res_T -sdis_heat_path_for_each_vertex +sdis_heat_path_line_strip_for_each_vertex (const struct sdis_heat_path* path, + const size_t istrip, sdis_process_heat_vertex_T func, void* context); @@ -1245,6 +1364,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 @@ -25,17 +25,52 @@ struct sdis_heat_path; struct rwalk_context { struct green_path_handle* green_path; struct sdis_heat_path* heat_path; - double Tarad; /* Ambient radiative temperature */ - double Tref3; /* Reference temperature ^ 3 */ + + double Tmin; /* Lower bound temperature */ + double Tmin2; /* Tmin^2 */ + double Tmin3; /* Tmin^3 */ + + double That; /* Upper bound temperature */ + double That2; /* That^2 */ + double That3; /* That^3 */ + + /* Maximum branchings i.e. the maximum number of times + * XD(compute_temperature) can be called. It controls the number of + * ramifications of the heat path and currently is correlated to the Picard + * order used to estimate the radiative temperature. max_branchings == + * picard_order-1 */ + size_t max_branchings; + + /* Number of heat path branchings */ + size_t nbranchings; }; -#define RWALK_CONTEXT_NULL__ {NULL, NULL, 0, 0} +#define RWALK_CONTEXT_NULL__ { \ + NULL, /* Green path */ \ + NULL, /* Heat path */ \ + 0, /* Tmin */ \ + 0, /* Tmin^2 */ \ + 0, /* Tmin^3 */ \ + 0, /* That */ \ + 0, /* That^2 */ \ + 0, /* That^3 */ \ + 0, /* Max #branchings */ \ + SIZE_MAX, /* #branchings */ \ +} static const struct rwalk_context RWALK_CONTEXT_NULL = RWALK_CONTEXT_NULL__; +static INLINE size_t +get_picard_order(const struct rwalk_context* ctx) +{ + ASSERT(ctx); + return ctx->max_branchings + 1; +} + #endif /* SDIS_XD_BEGIN_H */ #ifdef SDIS_XD_BEGIN_H__ #error "This header is already included without its associated sdis_Xd_end.h file." #endif + #define SDIS_XD_BEGIN_H__ /* Check prerequisite */ @@ -105,7 +140,7 @@ static const struct XD(rwalk) XD(RWALK_NULL) = { struct XD(temperature) { res_T (*func)/* Next function to invoke in order to compute the temperature */ (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct XD(rwalk)* rwalk, struct ssp_rng* rng, struct XD(temperature)* temp); 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,158 @@ +/* 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 <star/ssp.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, /* May be NULL */ + const enum ssp_rng_type rng_type, /* RNG type when `rng_state' is NULL */ + 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 - 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 = 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; - 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; - } + log_info(dev, "Using %lu %s.\n", (unsigned long)dev->nthreads, + dev->nthreads == 1 ? "thread" : "threads"); 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 @@ -387,6 +387,8 @@ green_function_solve_path const size_t ipath, double* weight) { + struct sdis_ambient_radiative_temperature trad = + SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL; const struct power_term* power_terms = NULL; const struct flux_term* flux_terms = NULL; const struct green_path* path = NULL; @@ -443,7 +445,8 @@ green_function_solve_path break; case SDIS_GREEN_PATH_END_RADIATIVE: SDIS(green_function_get_scene(green, &scn)); - SDIS(scene_get_ambient_radiative_temperature(scn, &end_temperature)); + SDIS(scene_get_ambient_radiative_temperature(scn, &trad)); + end_temperature = trad.temperature; if(end_temperature < 0) { /* Cannot be negative if used */ res = RES_BAD_ARG; goto error; @@ -1549,6 +1552,15 @@ green_path_set_limit_radiative } res_T +green_path_reset_limit(struct green_path_handle* handle) +{ + ASSERT(handle); + handle->path->elapsed_time = -INF; + handle->path->end_type = SDIS_GREEN_PATH_END_TYPES_COUNT__; + return RES_OK; +} + +res_T green_path_add_power_term (struct green_path_handle* handle, struct sdis_medium* mdm, 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 @@ -87,6 +87,10 @@ green_path_set_limit_radiative const double elapsed_time); extern LOCAL_SYM res_T +green_path_reset_limit + (struct green_path_handle* handle); + +extern LOCAL_SYM res_T green_path_add_power_term (struct green_path_handle* path, struct sdis_medium* mdm, 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 @@ -33,24 +33,28 @@ #define SDIS_XD_DIMENSION 3 #include "sdis_heat_path_conductive_Xd.h" -/* Generate the boundary path routines */ -#define SDIS_XD_DIMENSION 2 -#include "sdis_heat_path_boundary_Xd.h" -#define SDIS_XD_DIMENSION 3 -#include "sdis_heat_path_boundary_Xd.h" - /******************************************************************************* - * Exported functions + * Local functions ******************************************************************************/ -res_T -sdis_heat_path_get_vertices_count - (const struct sdis_heat_path* path, size_t* nvertices) +/* Return the offset into the list of vertices toward the first vertex of the + * line strip */ +static INLINE size_t +line_strip_vertex_offset(const struct sdis_heat_path* path, const size_t istrip) { - if(!path || !nvertices) return RES_BAD_ARG; - *nvertices = darray_heat_vertex_size_get(&path->vertices); - return RES_OK; + ASSERT(path); +#ifndef NDEBUG + { + size_t nstrips; + SDIS(heat_path_get_line_strips_count(path, &nstrips)); + ASSERT(istrip < nstrips); + } +#endif + return istrip == 0 ? 0 : darray_size_t_cdata_get(&path->breaks)[istrip-1] + 1; } +/******************************************************************************* + * Exported functions + ******************************************************************************/ res_T sdis_heat_path_get_status (const struct sdis_heat_path* path, enum sdis_heat_path_flag* status) @@ -61,28 +65,112 @@ sdis_heat_path_get_status } res_T -sdis_heat_path_get_vertex +sdis_heat_path_get_line_strips_count + (const struct sdis_heat_path* path, + size_t* nstrips) +{ + if(!path || !nstrips) return RES_BAD_ARG; + /* #strips == #breaks + 1 */ + *nstrips = darray_size_t_size_get(&path->breaks) + 1; + return RES_OK; +} + +res_T +sdis_heat_path_line_strip_get_vertices_count + (const struct sdis_heat_path* path, + const size_t istrip, + size_t* out_nvertices) +{ + size_t nstrips = 0; + size_t ivert_begin = 0; + size_t ivert_end = 0; + res_T res = RES_OK; + + if(!path || !out_nvertices) { + res = RES_BAD_ARG; + goto error; + } + + res = sdis_heat_path_get_line_strips_count(path, &nstrips); + if(res != RES_OK) goto error; + + /* Check the indices of the strip */ + if(istrip >= nstrips) { + res = RES_BAD_ARG; + goto error; + } + + if(istrip == 0) { /* First strip */ + ivert_begin = 0; + } else { + ivert_begin = line_strip_vertex_offset(path, istrip); + } + + if(istrip == nstrips-1) { /* Last strip */ + ivert_end = darray_heat_vertex_size_get(&path->vertices); + } else { + ivert_end = line_strip_vertex_offset(path, istrip+1); + } + + ASSERT(ivert_begin <= ivert_end); + *out_nvertices = ivert_end - ivert_begin; + +exit: + return res; +error: + goto exit; +} + +res_T +sdis_heat_path_line_strip_get_vertex (const struct sdis_heat_path* path, - const size_t ivertex, + const size_t istrip, + const size_t ivert, struct sdis_heat_vertex* vertex) { - if(!path || !vertex - || ivertex >= darray_heat_vertex_size_get(&path->vertices)) { - return RES_BAD_ARG; + size_t nverts = 0; + size_t ivert_adjusted = 0; + res_T res = RES_OK; + + if(!path || !vertex) { + res = RES_BAD_ARG; + goto error; } - *vertex = darray_heat_vertex_cdata_get(&path->vertices)[ivertex]; - return RES_OK; + /* By retrieving the number of vertices, we also check the validity of + * istrip: the function will return an error if istrip is invalid */ + res = sdis_heat_path_line_strip_get_vertices_count(path, istrip, &nverts); + if(res != RES_OK) goto error; + + if(ivert >= nverts) { + res = RES_BAD_ARG; + goto error; + } + + /* Compute the index into the overall list of vertices */ + ivert_adjusted = ivert + line_strip_vertex_offset(path, istrip); + ASSERT(ivert_adjusted < darray_heat_vertex_size_get(&path->vertices)); + + /* Fetch the vertex */ + *vertex = darray_heat_vertex_cdata_get(&path->vertices)[ivert_adjusted]; + +exit: + return res; +error: + goto exit; } res_T -sdis_heat_path_for_each_vertex +sdis_heat_path_line_strip_for_each_vertex (const struct sdis_heat_path* path, + const size_t istrip, sdis_process_heat_vertex_T func, void* context) { - const struct sdis_heat_vertex* vertices; - size_t i, n; + const struct sdis_heat_vertex* vertices = NULL; + size_t ivert = 0; + size_t offset = 0; + size_t nverts = 0; res_T res = RES_OK; if(!path || !func) { @@ -90,10 +178,14 @@ sdis_heat_path_for_each_vertex goto error; } - SDIS(heat_path_get_vertices_count(path, &n)); + res = sdis_heat_path_line_strip_get_vertices_count(path, istrip, &nverts); + if(res != RES_OK) goto error; + + offset = line_strip_vertex_offset(path, istrip); + vertices = darray_heat_vertex_cdata_get(&path->vertices); - FOR_EACH(i, 0, n) { - res = func(vertices+i, context); + FOR_EACH(ivert, 0, nverts) { + res = func(vertices+ivert+offset, context); if(res != RES_OK) goto error; } @@ -102,4 +194,3 @@ exit: error: goto exit; } - 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 @@ -19,6 +19,7 @@ #include "sdis.h" #include <rsys/dynamic_array.h> +#include <rsys/dynamic_array_size_t.h> #include <rsys/rsys.h> /* Forward declarations */ @@ -39,7 +40,12 @@ struct temperature_3d; * Heat path data structure ******************************************************************************/ struct sdis_heat_path { + /* List of the path vertices */ struct darray_heat_vertex vertices; + + /* Indices of the vertices that mark a break in the path */ + struct darray_size_t breaks; + enum sdis_heat_path_flag status; }; @@ -49,6 +55,7 @@ heat_path_init(struct mem_allocator* allocator, struct sdis_heat_path* path) ASSERT(path); path->status = SDIS_HEAT_PATH_NONE; darray_heat_vertex_init(allocator, &path->vertices); + darray_size_t_init(allocator, &path->breaks); } static INLINE void @@ -56,30 +63,46 @@ heat_path_release(struct sdis_heat_path* path) { ASSERT(path); darray_heat_vertex_release(&path->vertices); + darray_size_t_release(&path->breaks); } static INLINE res_T heat_path_copy(struct sdis_heat_path* dst, const struct sdis_heat_path* src) { + res_T res = RES_OK; ASSERT(dst && src); dst->status = src->status; - return darray_heat_vertex_copy(&dst->vertices, &src->vertices); + res = darray_heat_vertex_copy(&dst->vertices, &src->vertices); + if(res != RES_OK) return res; + res = darray_size_t_copy(&dst->breaks, &src->breaks); + if(res != RES_OK) return res; + return RES_OK; } static INLINE res_T heat_path_copy_and_release(struct sdis_heat_path* dst, struct sdis_heat_path* src) { + res_T res = RES_OK; ASSERT(dst && src); dst->status = src->status; - return darray_heat_vertex_copy_and_release(&dst->vertices, &src->vertices); + res = darray_heat_vertex_copy_and_release(&dst->vertices, &src->vertices); + if(res != RES_OK) return res; + res = darray_size_t_copy_and_release(&dst->breaks, &src->breaks); + if(res != RES_OK) return res; + return RES_OK; } static INLINE res_T heat_path_copy_and_clear(struct sdis_heat_path* dst, struct sdis_heat_path* src) { + res_T res = RES_OK; ASSERT(dst && src); dst->status = src->status; - return darray_heat_vertex_copy_and_clear(&dst->vertices, &src->vertices); + res = darray_heat_vertex_copy_and_clear(&dst->vertices, &src->vertices); + if(res != RES_OK) return res; + res = darray_size_t_copy_and_clear(&dst->breaks, &src->breaks); + if(res != RES_OK) return res; + return RES_OK; } static INLINE res_T @@ -89,14 +112,81 @@ heat_path_add_vertex(struct sdis_heat_path* path, const struct sdis_heat_vertex* return darray_heat_vertex_push_back(&path->vertices, vtx); } +static INLINE size_t +heat_path_get_vertices_count(const struct sdis_heat_path* path) +{ + ASSERT(path); + return darray_heat_vertex_size_get(&path->vertices); +} + +static INLINE struct sdis_heat_vertex* +heat_path_get_vertex(struct sdis_heat_path* path, const size_t ivert) +{ + ASSERT(path && ivert < heat_path_get_vertices_count(path)); + return darray_heat_vertex_data_get(&path->vertices) + ivert; +} + static INLINE struct sdis_heat_vertex* heat_path_get_last_vertex(struct sdis_heat_path* path) { size_t sz; ASSERT(path); - sz = darray_heat_vertex_size_get(&path->vertices); + sz = heat_path_get_vertices_count(path); ASSERT(sz); - return darray_heat_vertex_data_get(&path->vertices) + (sz-1); + return heat_path_get_vertex(path, sz-1); +} + +static INLINE res_T +heat_path_add_break(struct sdis_heat_path* path) +{ + size_t id; + size_t sz; + ASSERT(path); + sz = darray_heat_vertex_size_get(&path->vertices); + if(sz == 0) return RES_OK; /* Nothing to do */ + id = sz-1; + return darray_size_t_push_back(&path->breaks, &id); +} + +static INLINE res_T +heat_path_restart + (struct sdis_heat_path* path, + const struct sdis_heat_vertex* vtx) /* Vertex to restart from */ +{ + size_t nverts = 0; + size_t nbreaks = 0; + res_T res = RES_OK; + + if(!path) goto exit; + ASSERT(vtx); + + nbreaks = darray_size_t_size_get(&path->breaks); + nverts = darray_heat_vertex_size_get(&path->vertices); + + res = heat_path_add_break(path); + if(res != RES_OK) goto error; + res = heat_path_add_vertex(path, vtx); + if(res != RES_OK) goto error; + +exit: + return res; +error: + CHK(darray_size_t_resize(&path->breaks, nbreaks) == RES_OK); + CHK(darray_heat_vertex_resize(&path->vertices, nverts) == RES_OK); + goto exit; +} + +static INLINE void +heat_path_increment_sub_path_branch_id + (struct sdis_heat_path* path, + const size_t ivtx_begin, + const size_t ivtx_end) +{ + size_t ivtx; + FOR_EACH(ivtx, ivtx_begin, ivtx_end) { + struct sdis_heat_vertex* vtx = heat_path_get_vertex(path, ivtx); + vtx->branch_id += 1; + } } /* Generate the dynamic array of heat paths */ @@ -115,7 +205,7 @@ extern LOCAL_SYM res_T trace_radiative_path_2d (struct sdis_scene* scn, const float ray_dir[3], - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct rwalk_2d* rwalk, struct ssp_rng* rng, struct temperature_2d* temperature); @@ -124,7 +214,7 @@ extern LOCAL_SYM res_T trace_radiative_path_3d (struct sdis_scene* scn, const float ray_dir[3], - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct rwalk_3d* rwalk, struct ssp_rng* rng, struct temperature_3d* temperature); @@ -132,7 +222,7 @@ trace_radiative_path_3d extern LOCAL_SYM res_T radiative_path_2d (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct rwalk_2d* rwalk, struct ssp_rng* rng, struct temperature_2d* temperature); @@ -140,7 +230,7 @@ radiative_path_2d extern LOCAL_SYM res_T radiative_path_3d (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct rwalk_3d* rwalk, struct ssp_rng* rng, struct temperature_3d* temperature); @@ -151,7 +241,7 @@ radiative_path_3d extern LOCAL_SYM res_T convective_path_2d (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct rwalk_2d* rwalk, struct ssp_rng* rng, struct temperature_2d* temperature); @@ -159,7 +249,7 @@ convective_path_2d extern LOCAL_SYM res_T convective_path_3d (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct rwalk_3d* rwalk, struct ssp_rng* rng, struct temperature_3d* temperature); @@ -170,7 +260,7 @@ convective_path_3d extern LOCAL_SYM res_T conductive_path_2d (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct rwalk_2d* rwalk, struct ssp_rng* rng, struct temperature_2d* temperature); @@ -178,7 +268,7 @@ conductive_path_2d extern LOCAL_SYM res_T conductive_path_3d (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct rwalk_3d* rwalk, struct ssp_rng* rng, struct temperature_3d* temperature); @@ -189,7 +279,7 @@ conductive_path_3d extern LOCAL_SYM res_T boundary_path_2d (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct rwalk_2d* rwalk, struct ssp_rng* rng, struct temperature_2d* temperature); @@ -197,7 +287,7 @@ boundary_path_2d extern LOCAL_SYM res_T boundary_path_3d (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct rwalk_3d* rwalk, struct ssp_rng* rng, struct temperature_3d* temperature); diff --git a/src/sdis_heat_path_boundary.c b/src/sdis_heat_path_boundary.c @@ -0,0 +1,47 @@ +/* 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_heat_path.h" +#include "sdis_heat_path_boundary_c.h" + +/* Generate the helper routines */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_boundary_Xd_c.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_boundary_Xd_c.h" + +/* Generate the boundary path sub-routines */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_boundary_Xd_fixed_flux.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_boundary_Xd_fixed_flux.h" +#define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_boundary_Xd_solid_fluid_picard1.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_boundary_Xd_solid_fluid_picard1.h" +#define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_boundary_Xd_solid_fluid_picardN.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_boundary_Xd_solid_fluid_picardN.h" +#define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_boundary_Xd_solid_solid.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_boundary_Xd_solid_solid.h" + +/* Generate the boundary path routines */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_boundary_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_boundary_Xd.h" 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 @@ -16,962 +16,21 @@ #include "sdis_device_c.h" #include "sdis_green.h" #include "sdis_heat_path.h" +#include "sdis_heat_path_boundary_c.h" #include "sdis_interface_c.h" #include "sdis_medium_c.h" -#include "sdis_scene_c.h" #include <star/ssp.h> #include "sdis_Xd_begin.h" -/* Emperical scale factor applied to the challenged reinjection distance. If - * the distance to reinject is less than this adjusted value, the solver will - * try to discard the reinjection distance if possible in order to avoid - * numerical issues. */ -#define REINJECT_DST_MIN_SCALE 0.125f - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static void -XD(sample_reinjection_dir) - (const struct XD(rwalk)* rwalk, struct ssp_rng* rng, float dir[DIM]) -{ -#if DIM == 2 - /* The sampled directions is defined by rotating the normal around the Z axis - * of an angle of PI/4 or -PI/4. Let the rotation matrix defined as - * | cos(a) -sin(a) | - * | sin(a) cos(a) | - * with a = PI/4, dir = sqrt(2)/2 * | 1 -1 | . N - * | 1 1 | - * with a =-PI/4, dir = sqrt(2)/2 * | 1 1 | . N - * |-1 1 | - * Note that since the sampled direction is finally normalized, we can - * discard the sqrt(2)/2 constant. */ - const uint64_t r = ssp_rng_uniform_uint64(rng, 0, 1); - ASSERT(rwalk && dir); - if(r) { - dir[0] = rwalk->hit.normal[0] - rwalk->hit.normal[1]; - dir[1] = rwalk->hit.normal[0] + rwalk->hit.normal[1]; - } else { - dir[0] = rwalk->hit.normal[0] + rwalk->hit.normal[1]; - dir[1] =-rwalk->hit.normal[0] + rwalk->hit.normal[1]; - } - f2_normalize(dir, dir); -#else - /* Sample a random direction around the normal whose cosine is 1/sqrt(3). To - * do so we sample a position onto a cone whose height is 1/sqrt(2) and the - * radius of its base is 1. */ - float frame[9]; - ASSERT(fX(is_normalized)(rwalk->hit.normal)); - - ssp_ran_circle_uniform_float(rng, dir, NULL); - dir[2] = (float)(1.0/sqrt(2)); - - f33_basis(frame, rwalk->hit.normal); - f33_mulf3(dir, frame, dir); - f3_normalize(dir, dir); - ASSERT(eq_epsf(f3_dot(dir, rwalk->hit.normal), (float)(1.0/sqrt(3)), 1.e-4f)); -#endif -} - -#if DIM == 2 -static void -XD(move_away_primitive_boundaries) - (struct XD(rwalk)* rwalk, - const double delta) -{ - struct sXd(attrib) attr; - float pos[DIM]; - float dir[DIM]; - float len; - const float st = 0.5f; - ASSERT(rwalk && !SXD_HIT_NONE(&rwalk->hit) && delta > 0); - - SXD(primitive_get_attrib(&rwalk->hit.prim, SXD_POSITION, st, &attr)); - - fX_set_dX(pos, rwalk->vtx.P); - fX(sub)(dir, attr.value, pos); - len = fX(normalize)(dir, dir); - len = MMIN(len, (float)(delta*0.1)); - - XD(move_pos)(rwalk->vtx.P, dir, len); -} -#else -/* Move the random walk away from the primitive boundaries to avoid numerical - * issues leading to inconsistent random walks. */ -static void -XD(move_away_primitive_boundaries) - (struct XD(rwalk)* rwalk, - const double delta) -{ - struct s3d_attrib v0, v1, v2; /* Triangle vertices */ - float E[3][4]; /* 3D edge equations */ - float dst[3]; /* Distance from current position to edge equation */ - float N[3]; /* Triangle normal */ - float P[3]; /* Random walk position */ - float tmp[3]; - float min_dst, max_dst; - float cos_a1, cos_a2; - float len; - int imax = 0; - int imin = 0; - int imid = 0; - int i; - ASSERT(rwalk && delta > 0 && !S3D_HIT_NONE(&rwalk->hit)); - - fX_set_dX(P, rwalk->vtx.P); - - /* Fetch triangle vertices */ - S3D(triangle_get_vertex_attrib(&rwalk->hit.prim, 0, S3D_POSITION, &v0)); - S3D(triangle_get_vertex_attrib(&rwalk->hit.prim, 1, S3D_POSITION, &v1)); - S3D(triangle_get_vertex_attrib(&rwalk->hit.prim, 2, S3D_POSITION, &v2)); - - /* Compute the edge vector */ - f3_sub(E[0], v1.value, v0.value); - f3_sub(E[1], v2.value, v1.value); - f3_sub(E[2], v0.value, v2.value); - - /* Compute the triangle normal */ - f3_cross(N, E[1], E[0]); - - /* Compute the 3D edge equation */ - f3_normalize(E[0], f3_cross(E[0], E[0], N)); - f3_normalize(E[1], f3_cross(E[1], E[1], N)); - f3_normalize(E[2], f3_cross(E[2], E[2], N)); - E[0][3] = -f3_dot(E[0], v0.value); - E[1][3] = -f3_dot(E[1], v1.value); - E[2][3] = -f3_dot(E[2], v2.value); - - /* Compute the distance from current position to the edges */ - dst[0] = f3_dot(E[0], P) + E[0][3]; - dst[1] = f3_dot(E[1], P) + E[1][3]; - dst[2] = f3_dot(E[2], P) + E[2][3]; - - /* Retrieve the min and max distance from random walk position to triangle - * edges */ - min_dst = MMIN(MMIN(dst[0], dst[1]), dst[2]); - max_dst = MMAX(MMAX(dst[0], dst[1]), dst[2]); - - /* Sort the edges with respect to their distance to the random walk position */ - FOR_EACH(i, 0, 3) { - if(dst[i] == min_dst) { - imin = i; - } else if(dst[i] == max_dst) { - imax = i; - } else { - imid = i; - } - } - (void)imax; - - /* TODO if the current position is near a vertex, one should move toward the - * farthest edge along its normal to avoid too small displacement */ - - /* Compute the distance `dst' from the current position to the edges to move - * to, along the normal of the edge from which the random walk is the nearest - * - * +. cos(a) = d / dst => dst = d / cos_a - * / `*. - * / | `*. - * / dst| a /`*. - * / | / `*. - * / | / d `*. - * / |/ `*. - * +---------o----------------+ */ - cos_a1 = f3_dot(E[imin], f3_minus(tmp, E[imid])); - cos_a2 = f3_dot(E[imin], f3_minus(tmp, E[imax])); - dst[imid] = cos_a1 > 0 ? dst[imid] / cos_a1 : FLT_MAX; - dst[imax] = cos_a2 > 0 ? dst[imax] / cos_a2 : FLT_MAX; - - /* Compute the maximum displacement distance into the triangle along the - * normal of the edge from which the random walk is the nearest */ - len = MMIN(dst[imid], dst[imax]); - ASSERT(len != FLT_MAX); - - /* Define the displacement distance as the minimum between 10 percent of - * delta and len / 2. */ - len = MMIN(len*0.5f, (float)(delta*0.1)); - XD(move_pos)(rwalk->vtx.P, E[imin], len); -} -#endif - -static res_T -XD(select_reinjection_dir) - (const struct sdis_scene* scn, - const struct sdis_medium* mdm, /* Medium into which the reinjection occurs */ - struct XD(rwalk)* rwalk, /* Current random walk state */ - const float dir0[DIM], /* Challenged direction */ - const float dir1[DIM], /* Challanged direction */ - const double delta, /* Max reinjection distance */ - float reinject_dir[DIM], /* Selected direction */ - float* reinject_dst, /* Effective reinjection distance */ - int can_move, /* Define of the random wal pos can be moved or not */ - int* move_pos, /* Define if the current random walk was moved. May be NULL */ - struct sXd(hit)* reinject_hit) /* Hit along the reinjection dir */ -{ - struct sdis_interface* interf; - struct sdis_medium* mdm0; - struct sdis_medium* mdm1; - struct hit_filter_data filter_data; - struct sXd(hit) hit; - struct sXd(hit) hit0; - struct sXd(hit) hit1; - double tmp[DIM]; - double dst; - double dst0; - double dst1; - const double delta_adjusted = delta * RAY_RANGE_MAX_SCALE; - const float* dir; - const float reinject_threshold = (float)delta * REINJECT_DST_MIN_SCALE; - float org[DIM]; - float range[2]; - enum sdis_side side; - int iattempt = 0; - const int MAX_ATTEMPTS = can_move ? 2 : 1; - res_T res = RES_OK; - ASSERT(scn && mdm && rwalk && dir0 && dir1 && delta > 0); - ASSERT(reinject_dir && reinject_dst && reinject_hit); - - if(move_pos) *move_pos = 0; - - do { - f2(range, 0, FLT_MAX); - fX_set_dX(org, rwalk->vtx.P); - filter_data.XD(hit) = rwalk->hit; - filter_data.epsilon = delta * 0.01; - SXD(scene_view_trace_ray(scn->sXd(view), org, dir0, range, &filter_data, &hit0)); - SXD(scene_view_trace_ray(scn->sXd(view), org, dir1, range, &filter_data, &hit1)); - - /* Retrieve the medium at the reinjection pos along dir0 */ - if(SXD_HIT_NONE(&hit0)) { - XD(move_pos)(dX(set)(tmp, rwalk->vtx.P), dir0, (float)delta); - res = scene_get_medium_in_closed_boundaries(scn, tmp, &mdm0); - if(res == RES_BAD_OP) { mdm0 = NULL; res = RES_OK; } - if(res != RES_OK) goto error; - } else { - interf = scene_get_interface(scn, hit0.prim.prim_id); - side = fX(dot)(dir0, hit0.normal) < 0 ? SDIS_FRONT : SDIS_BACK; - mdm0 = interface_get_medium(interf, side); - } - - /* Retrieve the medium at the reinjection pos along dir1 */ - if(SXD_HIT_NONE(&hit1)) { - XD(move_pos)(dX(set)(tmp, rwalk->vtx.P), dir1, (float)delta); - res = scene_get_medium_in_closed_boundaries(scn, tmp, &mdm1); - if(res == RES_BAD_OP) { mdm1 = NULL; res = RES_OK; } - if(res != RES_OK) goto error; - } else { - interf = scene_get_interface(scn, hit1.prim.prim_id); - side = fX(dot)(dir1, hit1.normal) < 0 ? SDIS_FRONT : SDIS_BACK; - mdm1 = interface_get_medium(interf, side); - } - - dst0 = dst1 = -1; - if(mdm0 == mdm) { /* Check reinjection consistency */ - if(hit0.distance <= delta_adjusted) { - dst0 = hit0.distance; - } else { - dst0 = delta; - hit0 = SXD_HIT_NULL; - } - } - if(mdm1 == mdm) {/* Check reinjection consistency */ - if(hit1.distance <= delta_adjusted) { - dst1 = hit1.distance; - } else { - dst1 = delta; - hit1 = SXD_HIT_NULL; - } - } - - /* No valid reinjection. Maybe the random walk is near a sharp corner and - * thus the ray-tracing misses the enclosure geometry. Another possibility - * is that the random walk lies roughly on an edge. In this case, sampled - * reinjecton dirs can intersect the primitive on the other side of the - * edge. Normally, this primitive should be filtered by the "hit_filter" - * function but this may be not the case due to a "threshold effect". In - * both situations, try to slightly move away from the primitive boundaries - * and retry to find a valid reinjection. */ - if(dst0 == -1 && dst1 == -1) { - XD(move_away_primitive_boundaries)(rwalk, delta); - if(move_pos) *move_pos = 1; - } - } while(dst0 == -1 && dst1 == -1 && ++iattempt < MAX_ATTEMPTS); - - if(dst0 == -1 && dst1 == -1) { /* No valid reinjection */ - log_warn(scn->dev, "%s: no valid reinjection direction at {%g, %g, %g}.\n", - FUNC_NAME, SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP_IRRECOVERABLE; - goto error; - } - - if(dst0 == -1) { - /* Invalid dir0 -> move along dir1 */ - dir = dir1; - dst = dst1; - hit = hit1; - } else if(dst1 == -1) { - /* Invalid dir1 -> move along dir0 */ - dir = dir0; - dst = dst0; - hit = hit0; - } else if(dst0 < reinject_threshold && dst1 < reinject_threshold) { - /* The displacement along dir0 and dir1 are both below the reinjection - * threshold that defines a distance under which the temperature gradients - * are ignored. Move along the direction that allows the maximum - * displacement. */ - if(dst0 > dst1) { - dir = dir0; - dst = dst0; - hit = hit0; - } else { - dir = dir1; - dst = dst1; - hit = hit1; - } - } else if(dst0 < reinject_threshold) { - /* Ingore dir0 that is bellow the reinject threshold */ - dir = dir1; - dst = dst1; - hit = hit1; - } else if(dst1 < reinject_threshold) { - /* Ingore dir1 that is bellow the reinject threshold */ - dir = dir0; - dst = dst0; - hit = hit0; - } else { - /* All reinjection directions are valid. Choose the first 1 that was - * randomly selected by the sample_reinjection_dir procedure and adjust - * the displacement distance. */ - dir = dir0; - - /* Define the reinjection distance along dir0 and its corresponding hit */ - if(dst0 <= dst1) { - dst = dst0; - hit = hit0; - } else { - dst = dst1; - hit = SXD_HIT_NULL; - } - - /* If the displacement distance is too close of a boundary, move to the - * boundary in order to avoid numerical uncertainty. */ - if(!SXD_HIT_NONE(&hit0) - && dst0 != dst - && eq_eps(dst0, dst, dst0*0.1)) { - dst = dst0; - hit = hit0; - } - } - - /* Setup output variable */ - fX(set)(reinject_dir, dir); - *reinject_dst = (float)dst; - *reinject_hit = hit; - -exit: - return res; -error: - goto exit; -} - -static res_T -XD(select_reinjection_dir_and_check_validity) - (const struct sdis_scene* scn, - const struct sdis_medium* mdm, /* Medium into which the reinjection occurs */ - struct XD(rwalk)* rwalk, /* Current random walk state */ - const float dir0[DIM], /* Challenged direction */ - const float dir1[DIM], /* Challanged direction */ - const double delta, /* Max reinjection distance */ - float out_reinject_dir[DIM], /* Selected direction */ - float* out_reinject_dst, /* Effective reinjection distance */ - int can_move, /* Define of the random wal pos can be moved or not */ - int* move_pos, /* Define if the current random walk was moved. May be NULL */ - int* is_valid, /* Define if the reinjection defines a valid pos */ - struct sXd(hit)* out_reinject_hit) /* Hit along the reinjection dir */ -{ - double pos[DIM]; - struct sdis_medium* reinject_mdm; - struct sXd(hit) reinject_hit; - float reinject_dir[DIM]; - float reinject_dst; - res_T res = RES_OK; - ASSERT(is_valid && out_reinject_dir && out_reinject_dst && out_reinject_hit); - - /* Select a reinjection direction */ - res = XD(select_reinjection_dir)(scn, mdm, rwalk, dir0, dir1, delta, - reinject_dir, &reinject_dst, can_move, move_pos, &reinject_hit); - if(res != RES_OK) goto error; - - if(!SXD_HIT_NONE(&reinject_hit)) { - *is_valid = 1; - } else { - /* Check medium consistency at the reinjection position */ - XD(move_pos)(dX(set)(pos, rwalk->vtx.P), reinject_dir, reinject_dst); - res = scene_get_medium_in_closed_boundaries - (scn, pos, &reinject_mdm); - if(res == RES_BAD_OP) { reinject_mdm = NULL; res = RES_OK; } - if(res != RES_OK) goto error; - - *is_valid = reinject_mdm == mdm; - } - - if(*is_valid) { - fX(set)(out_reinject_dir, reinject_dir); - *out_reinject_dst = reinject_dst; - *out_reinject_hit = reinject_hit; - } - -exit: - return res; -error: - goto exit; -} - -/* Check that the interface fragment is consistent with the current state of - * the random walk */ -static INLINE int -XD(check_rwalk_fragment_consistency) - (const struct XD(rwalk)* rwalk, - const struct sdis_interface_fragment* frag) -{ - double N[DIM]; - double uv[2] = {0, 0}; - ASSERT(rwalk && frag); - dX(normalize)(N, dX_set_fX(N, rwalk->hit.normal)); - if( SXD_HIT_NONE(&rwalk->hit) - || !dX(eq_eps)(rwalk->vtx.P, frag->P, 1.e-6) - || !dX(eq_eps)(N, frag->Ng, 1.e-6) - || !( (IS_INF(rwalk->vtx.time) && IS_INF(frag->time)) - || eq_eps(rwalk->vtx.time, frag->time, 1.e-6))) { - return 0; - } -#if (SDIS_XD_DIMENSION == 2) - uv[0] = rwalk->hit.u; -#else - d2_set_f2(uv, rwalk->hit.uv); -#endif - return d2_eq_eps(uv, frag->uv, 1.e-6); -} - -static res_T -XD(solid_solid_boundary_path) - (const struct sdis_scene* scn, - const struct rwalk_context* ctx, - const struct sdis_interface_fragment* frag, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - struct sXd(hit) hit0, hit1; - struct sXd(hit)* hit; - struct XD(rwalk) rwalk_saved; - struct sdis_interface* interf = NULL; - struct sdis_medium* solid_front = NULL; - struct sdis_medium* solid_back = NULL; - struct sdis_medium* mdm; - double lambda_front, lambda_back; - double delta_front, delta_back; - double delta_boundary_front, delta_boundary_back; - double proba; - double tmp; - double r; - double power; - double tcr; - float dir0[DIM], dir1[DIM], dir2[DIM], dir3[DIM]; - float dir_front[DIM], dir_back[DIM]; - float* dir; - float reinject_dst_front = 0, reinject_dst_back = 0; - float reinject_dst; - /* In 2D it is useless to try to resample a reinjection direction since there - * is only one possible direction */ - const int MAX_ATTEMPTS = DIM == 2 ? 1 : 10; - int iattempt; - int move; - int reinjection_is_valid; - res_T res = RES_OK; - ASSERT(scn && ctx && frag && rwalk && rng && T); - ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); - (void)frag, (void)ctx; - - /* Retrieve the current boundary media */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - solid_front = interface_get_medium(interf, SDIS_FRONT); - solid_back = interface_get_medium(interf, SDIS_BACK); - ASSERT(solid_front->type == SDIS_SOLID); - ASSERT(solid_back->type == SDIS_SOLID); - - /* Retrieve the thermal contact resistance */ - tcr = interface_get_thermal_contact_resistance(interf, frag); - - /* Fetch the properties of the media */ - lambda_front = solid_get_thermal_conductivity(solid_front, &rwalk->vtx); - lambda_back = solid_get_thermal_conductivity(solid_back, &rwalk->vtx); - - /* Note that reinjection distance is *FIXED*. It MUST ensure that the orthogonal - * distance from the boundary to the point to challenge is equal to delta. */ - delta_front = solid_get_delta(solid_front, &rwalk->vtx); - delta_back = solid_get_delta(solid_back, &rwalk->vtx); - delta_boundary_front = delta_front*sqrt(DIM); - delta_boundary_back = delta_back *sqrt(DIM); - - rwalk_saved = *rwalk; - reinjection_is_valid = 0; - iattempt = 0; - do { - if(iattempt != 0) *rwalk = rwalk_saved; - - /* Sample a reinjection direction and reflect it around the normal. Then - * reflect them on the back side of the interface. */ - XD(sample_reinjection_dir)(rwalk, rng, dir0); - XD(reflect)(dir2, dir0, rwalk->hit.normal); - fX(minus)(dir1, dir0); - fX(minus)(dir3, dir2); - - /* Select the reinjection direction and distance for the front side */ - res = XD(select_reinjection_dir_and_check_validity)(scn, solid_front, rwalk, - dir0, dir2, delta_boundary_front, dir_front, &reinject_dst_front, 1, &move, - &reinjection_is_valid, &hit0); - if(res != RES_OK) goto error; - if(!reinjection_is_valid) continue; - - /* Select the reinjection direction and distance for the back side */ - res = XD(select_reinjection_dir_and_check_validity)(scn, solid_back, rwalk, - dir1, dir3, delta_boundary_back, dir_back, &reinject_dst_back, 1, &move, - &reinjection_is_valid, &hit1); - if(res != RES_OK) goto error; - if(!reinjection_is_valid) continue; - - /* If random walk was moved by the select_reinjection_dir on back side, one - * has to rerun the select_reinjection_dir on front side at the new pos */ - if(move) { - res = XD(select_reinjection_dir_and_check_validity)(scn, solid_front, - rwalk, dir0, dir2, delta_boundary_front, dir_front, &reinject_dst_front, - 0, NULL, &reinjection_is_valid, &hit0); - if(res != RES_OK) goto error; - if(!reinjection_is_valid) continue; - } - } while(!reinjection_is_valid && ++iattempt < MAX_ATTEMPTS); - - /* Could not find a valid reinjection */ - if(iattempt >= MAX_ATTEMPTS) { - *rwalk = rwalk_saved; - log_warn(scn->dev, - "%s: could not find a valid solid/solid reinjection at {%g, %g, %g}.\n", - FUNC_NAME, SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP_IRRECOVERABLE; - goto error; - } - - r = ssp_rng_canonical(rng); - if(tcr == 0) { /* No thermal contact resistance */ - /* Define the reinjection side. Note that the proba should be : Lf/Df' / - * (Lf/Df' + Lb/Db') - * - * with L<f|b> the lambda of the <front|back> side and D<f|b>' the adjusted - * delta of the <front|back> side, i.e. : D<f|b>' = - * reinject_dst_<front|back> / sqrt(DIM) - * - * Anyway, one can avoid to compute the adjusted delta by directly using the - * adjusted reinjection distance since the resulting proba is strictly the - * same; sqrt(DIM) can be simplified. */ - proba = (lambda_front/reinject_dst_front) - / (lambda_front/reinject_dst_front + lambda_back/reinject_dst_back); - } else { - const double df = reinject_dst_front/sqrt(DIM); - const double db = reinject_dst_back/sqrt(DIM); - const double tmp_front = lambda_front/df; - const double tmp_back = lambda_back/db; - const double tmp_r = tcr*tmp_front*tmp_back; - switch(rwalk->hit_side) { - case SDIS_BACK: - /* When coming from the BACK side, the probability to be reinjected on - * the FRONT side depends on the thermal contact resistance: it - * decreases when the TCR increases (and tends to 0 when TCR -> +inf) */ - proba = (tmp_front) / (tmp_front + tmp_back + tmp_r); - break; - case SDIS_FRONT: - /* Same thing when coming from the FRONT side: the probability of - * reinjection on the FRONT side depends on the thermal contact - * resistance: it increases when the TCR increases (and tends to 1 when - * the TCR -> +inf) */ - proba = (tmp_front + tmp_r) / (tmp_front + tmp_back + tmp_r); - break; - default: FATAL("Unreachable code.\n"); break; - } - } - - if(r < proba) { /* Reinject in front */ - dir = dir_front; - hit = &hit0; - mdm = solid_front; - reinject_dst = reinject_dst_front; - } else { /* Reinject in back */ - dir = dir_back; - hit = &hit1; - mdm = solid_back; - reinject_dst = reinject_dst_back; - } - - /* Handle the volumic power */ - power = solid_get_volumic_power(mdm, &rwalk->vtx); - if(power != SDIS_VOLUMIC_POWER_NONE) { - const double delta_in_meter = reinject_dst * scn->fp_to_meter; - const double lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); - tmp = delta_in_meter * delta_in_meter / (2.0 * DIM * lambda); - T->value += power * tmp; - - if(ctx->green_path) { - res = green_path_add_power_term(ctx->green_path, mdm, &rwalk->vtx, tmp); - if(res != RES_OK) goto error; - } - } - - /* Time rewind */ - res = XD(time_rewind)(mdm, rng, reinject_dst * scn->fp_to_meter, ctx, rwalk, T); - if(res != RES_OK) goto error; - if(T->done) goto exit; /* Limit condition was reached */ - - /* Perform reinjection. */ - XD(move_pos)(rwalk->vtx.P, dir, (float)reinject_dst); - if(hit->distance == reinject_dst) { - T->func = XD(boundary_path); - rwalk->mdm = NULL; - rwalk->hit = *hit; - rwalk->hit_side = fX(dot)(hit->normal, dir) < 0 ? SDIS_FRONT : SDIS_BACK; - } else { - T->func = XD(conductive_path); - rwalk->mdm = mdm; - rwalk->hit = SXD_HIT_NULL; - rwalk->hit_side = SDIS_SIDE_NULL__; - } - - /* Register the new vertex against the heat path */ - res = register_heat_vertex - (ctx->heat_path, &rwalk->vtx, T->value, SDIS_HEAT_VERTEX_CONDUCTION); - if(res != RES_OK) goto error; - -exit: - return res; -error: - goto exit; -} - -static res_T -XD(solid_fluid_boundary_path) - (const struct sdis_scene* scn, - const struct rwalk_context* ctx, - const struct sdis_interface_fragment* frag, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - struct sdis_interface* interf = NULL; - struct sdis_medium* mdm_front = NULL; - struct sdis_medium* mdm_back = NULL; - struct sdis_medium* solid = NULL; - struct sdis_medium* fluid = NULL; - struct XD(rwalk) rwalk_saved; - struct sXd(hit) hit = SXD_HIT_NULL; - struct sdis_interface_fragment frag_fluid; - double hc; - double hr; - double epsilon; /* Interface emissivity */ - double lambda; - double fluid_proba; - double radia_proba; - double delta; - double delta_boundary; - double r; - double tmp; - float dir0[DIM], dir1[DIM]; - float reinject_dst; - /* In 2D it is useless to try to resample a reinjection direction since there - * is only one possible direction */ - const int MAX_ATTEMPTS = DIM == 2 ? 1 : 10; - int iattempt; - int reinjection_is_valid = 0; - res_T res = RES_OK; - ASSERT(scn && rwalk && rng && T && ctx); - ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); - - /* Retrieve the solid and the fluid split by the boundary */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - mdm_front = interface_get_medium(interf, SDIS_FRONT); - mdm_back = interface_get_medium(interf, SDIS_BACK); - ASSERT(mdm_front->type != mdm_back->type); - - frag_fluid = *frag; - if(mdm_front->type == SDIS_SOLID) { - solid = mdm_front; - fluid = mdm_back; - frag_fluid.side = SDIS_BACK; - } else { - solid = mdm_back; - fluid = mdm_front; - frag_fluid.side = SDIS_FRONT; - } - - /* Fetch the solid properties */ - lambda = solid_get_thermal_conductivity(solid, &rwalk->vtx); - delta = solid_get_delta(solid, &rwalk->vtx); - - /* Note that the reinjection distance is *FIXED*. It MUST ensure that the - * orthogonal distance from the boundary to the point to chalenge is equal to - * delta. */ - delta_boundary = sqrt(DIM) * delta; - - rwalk_saved = *rwalk; - reinjection_is_valid = 0; - iattempt = 0; - do { - if(iattempt != 0) *rwalk = rwalk_saved; - - /* Sample a reinjection direction */ - XD(sample_reinjection_dir)(rwalk, rng, dir0); - - /* Reflect the sampled direction around the normal */ - XD(reflect)(dir1, dir0, rwalk->hit.normal); - - if(solid == mdm_back) { - fX(minus)(dir0, dir0); - fX(minus)(dir1, dir1); - } - - /* Select the solid reinjection direction and distance */ - res = XD(select_reinjection_dir_and_check_validity)(scn, solid, rwalk, - dir0, dir1, delta_boundary, dir0, &reinject_dst, 1, NULL, - &reinjection_is_valid, &hit); - if(res != RES_OK) goto error; - - } while(!reinjection_is_valid && ++iattempt < MAX_ATTEMPTS); - - /* Could not find a valid reinjecton */ - if(iattempt >= MAX_ATTEMPTS) { - *rwalk = rwalk_saved; - log_warn(scn->dev, - "%s: could not find a valid solid/fluid reinjection at {%g, %g %g}.\n", - FUNC_NAME, SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP_IRRECOVERABLE; - goto error; - } - - /* Define the orthogonal dst from the reinjection pos to the interface */ - delta = reinject_dst / sqrt(DIM); - - /* Fetch the boundary properties */ - epsilon = interface_side_get_emissivity(interf, &frag_fluid); - hc = interface_get_convection_coef(interf, frag); - - /* Compute the radiative coefficient */ - hr = 4.0 * BOLTZMANN_CONSTANT * ctx->Tref3 * epsilon; - - /* Compute the probas to switch in solid, fluid or radiative random walk */ - tmp = lambda / (delta * scn->fp_to_meter); - fluid_proba = hc / (tmp + hr + hc); - radia_proba = hr / (tmp + hr + hc); - /*solid_proba = tmp / (tmp + hr + hc);*/ - - r = ssp_rng_canonical(rng); - if(r < radia_proba) { /* Switch in radiative random walk */ - T->func = XD(radiative_path); - rwalk->mdm = fluid; - rwalk->hit_side = rwalk->mdm == mdm_front ? SDIS_FRONT : SDIS_BACK; - } else if(r < fluid_proba + radia_proba) { /* Switch to convective random walk */ - T->func = XD(convective_path); - rwalk->mdm = fluid; - rwalk->hit_side = rwalk->mdm == mdm_front ? SDIS_FRONT : SDIS_BACK; - } else { /* Solid random walk */ - /* Handle the volumic power */ - const double power = solid_get_volumic_power(solid, &rwalk->vtx); - if(power != SDIS_VOLUMIC_POWER_NONE) { - const double delta_in_meter = reinject_dst * scn->fp_to_meter; - tmp = delta_in_meter * delta_in_meter / (2.0 * DIM * lambda); - T->value += power * tmp; - - if(ctx->green_path) { - res = green_path_add_power_term(ctx->green_path, solid, &rwalk->vtx, tmp); - if(res != RES_OK) goto error; - } - } - - /* Time rewind */ - res = XD(time_rewind)(solid, rng, reinject_dst * scn->fp_to_meter, ctx, rwalk, T); - if(res != RES_OK) goto error; - if(T->done) goto exit; /* Limit condition was reached */ - - /* Perform solid reinjection */ - XD(move_pos)(rwalk->vtx.P, dir0, reinject_dst); - if(hit.distance == reinject_dst) { - T->func = XD(boundary_path); - rwalk->mdm = NULL; - rwalk->hit = hit; - rwalk->hit_side = fX(dot)(hit.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; - } else { - T->func = XD(conductive_path); - rwalk->mdm = solid; - rwalk->hit = SXD_HIT_NULL; - rwalk->hit_side = SDIS_SIDE_NULL__; - } - - /* Register the new vertex against the heat path */ - res = register_heat_vertex - (ctx->heat_path, &rwalk->vtx, T->value, SDIS_HEAT_VERTEX_CONDUCTION); - if(res != RES_OK) goto error; - } - -exit: - return res; -error: - goto exit; -} - -static res_T -XD(solid_boundary_with_flux_path) - (const struct sdis_scene* scn, - const struct rwalk_context* ctx, - const struct sdis_interface_fragment* frag, - const double phi, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - struct XD(rwalk) rwalk_saved; - struct sdis_interface* interf = NULL; - struct sdis_medium* mdm = NULL; - double lambda; - double delta; - double delta_boundary; - double delta_in_meter; - double power; - double tmp; - struct sXd(hit) hit; - float dir0[DIM]; - float dir1[DIM]; - float reinject_dst; - /* In 2D it is useless to try to resample a reinjection direction since there - * is only one possible direction */ - const int MAX_ATTEMPTS = DIM == 2 ? 1 : 10; - int iattempt = 0; - int reinjection_is_valid = 0; - res_T res = RES_OK; - ASSERT(frag && phi != SDIS_FLUX_NONE); - ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); - (void)ctx; - - /* Fetch current interface */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - ASSERT(phi == interface_side_get_flux(interf, frag)); - - /* Fetch incoming solid */ - mdm = interface_get_medium(interf, frag->side); - ASSERT(mdm->type == SDIS_SOLID); - - /* Fetch medium properties */ - lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); - delta = solid_get_delta(mdm, &rwalk->vtx); - - /* Compute the reinjection distance. It MUST ensure that the orthogonal - * distance from the boundary to the point to chalenge is equal to delta. */ - delta_boundary = delta * sqrt(DIM); - - rwalk_saved = *rwalk; - reinjection_is_valid = 0; - iattempt = 0; - do { - if(iattempt != 0) *rwalk = rwalk_saved; - /* Sample a reinjection direction */ - XD(sample_reinjection_dir)(rwalk, rng, dir0); - - /* Reflect the sampled direction around the normal */ - XD(reflect)(dir1, dir0, rwalk->hit.normal); - - if(frag->side == SDIS_BACK) { - fX(minus)(dir0, dir0); - fX(minus)(dir1, dir1); - } - - /* Select the reinjection direction and distance */ - res = XD(select_reinjection_dir_and_check_validity)(scn, mdm, rwalk, dir0, - dir1, delta_boundary, dir0, &reinject_dst, 1, NULL, - &reinjection_is_valid, &hit); - if(res != RES_OK) goto error; - - } while(!reinjection_is_valid && ++iattempt < MAX_ATTEMPTS); - - /* Could not find a valid reinjecton */ - if(iattempt >= MAX_ATTEMPTS) { - *rwalk = rwalk_saved; - log_warn(scn->dev, - "%s: could not find a valid solid/fluid with flux reinjection " - "at {%g, %g, %g}.\n", FUNC_NAME, SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP_IRRECOVERABLE; - goto error; - } - - /* Define the orthogonal dst from the reinjection pos to the interface */ - delta = reinject_dst / sqrt(DIM); - - /* Handle the flux */ - delta_in_meter = delta * scn->fp_to_meter; - tmp = delta_in_meter / lambda; - T->value += phi * tmp; - if(ctx->green_path) { - res = green_path_add_flux_term(ctx->green_path, interf, frag, tmp); - if(res != RES_OK) goto error; - } - - /* Handle the volumic power */ - power = solid_get_volumic_power(mdm, &rwalk->vtx); - if(power != SDIS_VOLUMIC_POWER_NONE) { - delta_in_meter = reinject_dst * scn->fp_to_meter; - tmp = delta_in_meter * delta_in_meter / (2.0 * DIM * lambda); - T->value += power * tmp; - if(ctx->green_path) { - res = green_path_add_power_term(ctx->green_path, mdm, &rwalk->vtx, tmp); - if(res != RES_OK) goto error; - } - } - - /* Time rewind */ - res = XD(time_rewind)(mdm, rng, reinject_dst * scn->fp_to_meter, ctx, rwalk, T); - if(res != RES_OK) goto error; - if(T->done) goto exit; /* Limit condition was reached */ - - /* Reinject. If the reinjection move the point too close of a boundary, - * assume that the zone is isotherm and move to the boundary. */ - XD(move_pos)(rwalk->vtx.P, dir0, reinject_dst); - if(hit.distance == reinject_dst) { - T->func = XD(boundary_path); - rwalk->mdm = NULL; - rwalk->hit = hit; - rwalk->hit_side = fX(dot)(hit.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; - } else { - T->func = XD(conductive_path); - rwalk->mdm = mdm; - rwalk->hit = SXD_HIT_NULL; - rwalk->hit_side = SDIS_SIDE_NULL__; - } - - /* Register the new vertex against the heat path */ - res = register_heat_vertex - (ctx->heat_path, &rwalk->vtx, T->value, SDIS_HEAT_VERTEX_CONDUCTION); - if(res != RES_OK) goto error; - -exit: - return res; -error: - goto exit; -} - /******************************************************************************* * Local functions ******************************************************************************/ res_T XD(boundary_path) (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct XD(rwalk)* rwalk, struct ssp_rng* rng, struct XD(temperature)* T) @@ -1030,8 +89,11 @@ XD(boundary_path) if(mdm_front->type == mdm_back->type) { res = XD(solid_solid_boundary_path)(scn, ctx, &frag, rwalk, rng, T); + } else if(ctx->nbranchings == ctx->max_branchings) { + res = XD(solid_fluid_boundary_picard1_path)(scn, ctx, &frag, rwalk, rng, T); } else { - res = XD(solid_fluid_boundary_path)(scn, ctx, &frag, rwalk, rng, T); + ASSERT(ctx->nbranchings < ctx->max_branchings); + res = XD(solid_fluid_boundary_picardN_path)(scn, ctx, &frag, rwalk, rng, T); } if(res != RES_OK) goto error; diff --git a/src/sdis_heat_path_boundary_Xd_c.h b/src/sdis_heat_path_boundary_Xd_c.h @@ -0,0 +1,875 @@ +/* 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_green.h" +#include "sdis_heat_path_boundary_c.h" +#include "sdis_interface_c.h" +#include "sdis_log.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +struct XD(find_reinjection_ray_args) { + const struct sdis_medium* solid; /* Medium into which the reinjection occurs */ + const struct XD(rwalk)* rwalk; /* Current random walk state */ + float dir0[DIM]; /* Challenged ray direction */ + float dir1[DIM]; /* Challenged ray direction */ + double distance; /* Maximum reinjection distance */ + + /* Define if the random walk position can be moved or not to find a valid + * reinjection direction */ + int can_move; +}; +static const struct XD(find_reinjection_ray_args) +XD(FIND_REINJECTION_RAY_ARGS_NULL) = { NULL, NULL, {0}, {0}, 0, 0 }; + +struct XD(reinjection_ray) { + double org[DIM]; /* Origin of the reinjection */ + float dir[DIM]; /* Direction of the reinjection */ + float dst; /* Reinjection distance along dir */ + struct sXd(hit) hit; /* Hit along the reinjection dir */ + + /* Define whether or not the random walk was moved to find this reinjection + * ray */ + int position_was_moved; +}; +static const struct XD(reinjection_ray) +XD(REINJECTION_RAY_NULL) = { {0}, {0}, 0, SXD_HIT_NULL__, 0 }; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE int +XD(check_find_reinjection_ray_args) + (const struct XD(find_reinjection_ray_args)* args) +{ + return args + && args->solid + && args->rwalk + && args->distance > 0 + && fX(is_normalized)(args->dir0) + && fX(is_normalized)(args->dir1); +} + +static INLINE int +XD(check_sample_reinjection_step_args) + (const struct XD(sample_reinjection_step_args)* args) +{ + return args + && args->rng + && args->solid + && args->solid->type == SDIS_SOLID + && args->rwalk + && args->distance > 0 + && (unsigned)args->side < SDIS_SIDE_NULL__; +} + +static INLINE int +XD(check_reinjection_step)(const struct XD(reinjection_step)* step) +{ + return step + && fX(is_normalized)(step->direction) + && step->distance > 0; +} + +static INLINE int +XD(check_solid_reinjection_args)(const struct XD(solid_reinjection_args)* args) +{ + return args + && XD(check_reinjection_step)(args->reinjection) + && args->rng + && args->rwalk + && args->rwalk_ctx + && args->T + && args->fp_to_meter > 0; +} + +/* Check that the interface fragment is consistent with the current state of + * the random walk */ +static INLINE int +XD(check_rwalk_fragment_consistency) + (const struct XD(rwalk)* rwalk, + const struct sdis_interface_fragment* frag) +{ + double N[DIM]; + double uv[2] = {0, 0}; + ASSERT(rwalk && frag); + dX(normalize)(N, dX_set_fX(N, rwalk->hit.normal)); + if( SXD_HIT_NONE(&rwalk->hit) + || !dX(eq_eps)(rwalk->vtx.P, frag->P, 1.e-6) + || !dX(eq_eps)(N, frag->Ng, 1.e-6) + || !( (IS_INF(rwalk->vtx.time) && IS_INF(frag->time)) + || eq_eps(rwalk->vtx.time, frag->time, 1.e-6))) { + return 0; + } +#if (SDIS_XD_DIMENSION == 2) + uv[0] = rwalk->hit.u; +#else + d2_set_f2(uv, rwalk->hit.uv); +#endif + return d2_eq_eps(uv, frag->uv, 1.e-6); +} + +static void +XD(sample_reinjection_dir) + (const struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + float dir[DIM]) +{ +#if DIM == 2 + /* The sampled directions is defined by rotating the normal around the Z axis + * of an angle of PI/4 or -PI/4. Let the rotation matrix defined as + * | cos(a) -sin(a) | + * | sin(a) cos(a) | + * with a = PI/4, dir = sqrt(2)/2 * | 1 -1 | . N + * | 1 1 | + * with a =-PI/4, dir = sqrt(2)/2 * | 1 1 | . N + * |-1 1 | + * Note that since the sampled direction is finally normalized, we can + * discard the sqrt(2)/2 constant. */ + const uint64_t r = ssp_rng_uniform_uint64(rng, 0, 1); + ASSERT(rwalk && rng && dir); + ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + ASSERT(!rwalk->mdm); + + if(r) { + dir[0] = rwalk->hit.normal[0] - rwalk->hit.normal[1]; + dir[1] = rwalk->hit.normal[0] + rwalk->hit.normal[1]; + } else { + dir[0] = rwalk->hit.normal[0] + rwalk->hit.normal[1]; + dir[1] =-rwalk->hit.normal[0] + rwalk->hit.normal[1]; + } + f2_normalize(dir, dir); +#else + /* Sample a random direction around the normal whose cosine is 1/sqrt(3). To + * do so we sample a position onto a cone whose height is 1/sqrt(2) and the + * radius of its base is 1. */ + float frame[9]; + ASSERT(rwalk && rng && dir); + ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + ASSERT(!rwalk->mdm); + ASSERT(fX(is_normalized)(rwalk->hit.normal)); + + ssp_ran_circle_uniform_float(rng, dir, NULL); + dir[2] = (float)(1.0/sqrt(2)); + + f33_basis(frame, rwalk->hit.normal); + f33_mulf3(dir, frame, dir); + f3_normalize(dir, dir); + ASSERT(eq_epsf(f3_dot(dir, rwalk->hit.normal), (float)(1.0/sqrt(3)), 1.e-4f)); +#endif +} + + +#if DIM == 2 +static void +XD(move_away_primitive_boundaries) + (const struct XD(rwalk)* rwalk, + const double delta, + double position[DIM]) /* Position to move */ +{ + struct sXd(attrib) attr; + float pos[DIM]; + float dir[DIM]; + float len; + const float st = 0.5f; + ASSERT(rwalk && !SXD_HIT_NONE(&rwalk->hit) && delta > 0); + + SXD(primitive_get_attrib(&rwalk->hit.prim, SXD_POSITION, st, &attr)); + + fX_set_dX(pos, position); + fX(sub)(dir, attr.value, pos); + len = fX(normalize)(dir, dir); + len = MMIN(len, (float)(delta*0.1)); + + XD(move_pos)(position, dir, len); +} +#else +/* Move the submitted position away from the primitive boundaries to avoid + * numerical issues leading to inconsistent random walks. */ +static void +XD(move_away_primitive_boundaries) + (const struct XD(rwalk)* rwalk, + const double delta, + double position[DIM]) +{ + struct s3d_attrib v0, v1, v2; /* Triangle vertices */ + float E[3][4]; /* 3D edge equations */ + float dst[3]; /* Distance from current position to edge equation */ + float N[3]; /* Triangle normal */ + float P[3]; /* Random walk position */ + float tmp[3]; + float min_dst, max_dst; + float cos_a1, cos_a2; + float len; + int imax = 0; + int imin = 0; + int imid = 0; + int i; + ASSERT(rwalk && delta > 0 && !S3D_HIT_NONE(&rwalk->hit)); + + fX_set_dX(P, position); + + /* Fetch triangle vertices */ + S3D(triangle_get_vertex_attrib(&rwalk->hit.prim, 0, S3D_POSITION, &v0)); + S3D(triangle_get_vertex_attrib(&rwalk->hit.prim, 1, S3D_POSITION, &v1)); + S3D(triangle_get_vertex_attrib(&rwalk->hit.prim, 2, S3D_POSITION, &v2)); + + /* Compute the edge vector */ + f3_sub(E[0], v1.value, v0.value); + f3_sub(E[1], v2.value, v1.value); + f3_sub(E[2], v0.value, v2.value); + + /* Compute the triangle normal */ + f3_cross(N, E[1], E[0]); + + /* Compute the 3D edge equation */ + f3_normalize(E[0], f3_cross(E[0], E[0], N)); + f3_normalize(E[1], f3_cross(E[1], E[1], N)); + f3_normalize(E[2], f3_cross(E[2], E[2], N)); + E[0][3] = -f3_dot(E[0], v0.value); + E[1][3] = -f3_dot(E[1], v1.value); + E[2][3] = -f3_dot(E[2], v2.value); + + /* Compute the distance from current position to the edges */ + dst[0] = f3_dot(E[0], P) + E[0][3]; + dst[1] = f3_dot(E[1], P) + E[1][3]; + dst[2] = f3_dot(E[2], P) + E[2][3]; + + /* Retrieve the min and max distance from random walk position to triangle + * edges */ + min_dst = MMIN(MMIN(dst[0], dst[1]), dst[2]); + max_dst = MMAX(MMAX(dst[0], dst[1]), dst[2]); + + /* Sort the edges with respect to their distance to the random walk position */ + FOR_EACH(i, 0, 3) { + if(dst[i] == min_dst) { + imin = i; + } else if(dst[i] == max_dst) { + imax = i; + } else { + imid = i; + } + } + (void)imax; + + /* TODO if the current position is near a vertex, one should move toward the + * farthest edge along its normal to avoid too small displacement */ + + /* Compute the distance `dst' from the current position to the edges to move + * to, along the normal of the edge from which the random walk is the nearest + * + * +. cos(a) = d / dst => dst = d / cos_a + * / `*. + * / | `*. + * / dst| a /`*. + * / | / `*. + * / | / d `*. + * / |/ `*. + * +---------o----------------+ */ + cos_a1 = f3_dot(E[imin], f3_minus(tmp, E[imid])); + cos_a2 = f3_dot(E[imin], f3_minus(tmp, E[imax])); + dst[imid] = cos_a1 > 0 ? dst[imid] / cos_a1 : FLT_MAX; + dst[imax] = cos_a2 > 0 ? dst[imax] / cos_a2 : FLT_MAX; + + /* Compute the maximum displacement distance into the triangle along the + * normal of the edge from which the random walk is the nearest */ + len = MMIN(dst[imid], dst[imax]); + ASSERT(len != FLT_MAX); + + /* Define the displacement distance as the minimum between 10 percent of + * delta and len / 2. */ + len = MMIN(len*0.5f, (float)(delta*0.1)); + XD(move_pos)(position, E[imin], len); +} +#endif + +static res_T +XD(find_reinjection_ray) + (const struct sdis_scene* scn, + const struct XD(find_reinjection_ray_args)* args, + struct XD(reinjection_ray)* ray) +{ + /* Emperical scale factor applied to the challenged reinjection distance. If + * the distance to reinject is less than this adjusted value, the solver will + * try to discard the reinjection distance if possible in order to avoid + * numerical issues. */ + const float REINJECT_DST_MIN_SCALE = 0.125f; + + /* # attempts to find a ray direction */ + int MAX_ATTEMPTS = 1; + + /* Physical properties */ + struct sdis_interface* interf; + struct sdis_medium* mdm0; + struct sdis_medium* mdm1; + + struct hit_filter_data filter_data; + struct sXd(hit) hit; + struct sXd(hit) hit0; + struct sXd(hit) hit1; + double tmp[DIM]; + double dst; + double dst0; + double dst1; + const float* dir; + float reinject_threshold; + double dst_adjusted; + float org[DIM]; + const float range[2] = {0, FLT_MAX}; + enum sdis_side side; + int iattempt = 0; + res_T res = RES_OK; + + ASSERT(scn && args && ray); + ASSERT(XD(check_find_reinjection_ray_args)(args)); + + *ray = XD(REINJECTION_RAY_NULL); + MAX_ATTEMPTS = args->can_move ? 2 : 1; + + dst_adjusted = args->distance * RAY_RANGE_MAX_SCALE; + reinject_threshold = (float)args->distance * REINJECT_DST_MIN_SCALE; + + dX(set)(ray->org, args->rwalk->vtx.P); + + do { + fX_set_dX(org, ray->org); + filter_data.XD(hit) = args->rwalk->hit; + filter_data.epsilon = args->distance * 0.01; + SXD(scene_view_trace_ray + (scn->sXd(view), org, args->dir0, range, &filter_data, &hit0)); + SXD(scene_view_trace_ray + (scn->sXd(view), org, args->dir1, range, &filter_data, &hit1)); + + /* Retrieve the medium at the reinjection pos along dir0 */ + if(SXD_HIT_NONE(&hit0)) { + XD(move_pos)(dX(set)(tmp, ray->org), args->dir0, (float)args->distance); + res = scene_get_medium_in_closed_boundaries(scn, tmp, &mdm0); + if(res == RES_BAD_OP) { mdm0 = NULL; res = RES_OK; } + if(res != RES_OK) goto error; + } else { + interf = scene_get_interface(scn, hit0.prim.prim_id); + side = fX(dot)(args->dir0, hit0.normal) < 0 ? SDIS_FRONT : SDIS_BACK; + mdm0 = interface_get_medium(interf, side); + } + + /* Retrieve the medium at the reinjection pos along dir1 */ + if(SXD_HIT_NONE(&hit1)) { + XD(move_pos)(dX(set)(tmp, ray->org), args->dir1, (float)args->distance); + res = scene_get_medium_in_closed_boundaries(scn, tmp, &mdm1); + if(res == RES_BAD_OP) { mdm1 = NULL; res = RES_OK; } + if(res != RES_OK) goto error; + } else { + interf = scene_get_interface(scn, hit1.prim.prim_id); + side = fX(dot)(args->dir1, hit1.normal) < 0 ? SDIS_FRONT : SDIS_BACK; + mdm1 = interface_get_medium(interf, side); + } + + dst0 = dst1 = -1; + if(mdm0 == args->solid) { /* Check reinjection consistency */ + if(hit0.distance <= dst_adjusted) { + dst0 = hit0.distance; + } else { + dst0 = args->distance; + hit0 = SXD_HIT_NULL; + } + } + if(mdm1 == args->solid) { /* Check reinjection consistency */ + if(hit1.distance <= dst_adjusted) { + dst1 = hit1.distance; + } else { + dst1 = args->distance; + hit1 = SXD_HIT_NULL; + } + } + + /* No valid reinjection. Maybe the random walk is near a sharp corner and + * thus the ray-tracing misses the enclosure geometry. Another possibility + * is that the random walk lies roughly on an edge. In this case, sampled + * reinjection dirs can intersect the primitive on the other side of the + * edge. Normally, this primitive should be filtered by the "hit_filter" + * function but this may be not the case due to a "threshold effect". In + * both situations, try to slightly move away from the primitive boundaries + * and retry to find a valid reinjection. */ + if(dst0 == -1 && dst1 == -1) { + XD(move_away_primitive_boundaries)(args->rwalk, args->distance, ray->org); + ray->position_was_moved = 1; + } + } while(dst0 == -1 && dst1 == -1 && ++iattempt < MAX_ATTEMPTS); + + if(dst0 == -1 && dst1 == -1) { /* No valid reinjection */ +#if DIM == 2 + log_warn(scn->dev, "%s: no valid reinjection direction at {%g, %g}.\n", + FUNC_NAME, SPLIT2(ray->org)); +#else + log_warn(scn->dev, "%s: no valid reinjection direction at {%g, %g, %g}.\n", + FUNC_NAME, SPLIT3(ray->org)); +#endif + res = RES_BAD_OP_IRRECOVERABLE; + goto error; + } + + if(dst0 == -1) { + /* Invalid dir0 -> move along dir1 */ + dir = args->dir1; + dst = dst1; + hit = hit1; + } else if(dst1 == -1) { + /* Invalid dir1 -> move along dir0 */ + dir = args->dir0; + dst = dst0; + hit = hit0; + } else if(dst0 < reinject_threshold && dst1 < reinject_threshold) { + /* The displacement along dir0 and dir1 are both below the reinjection + * threshold that defines a distance under which the temperature gradients + * are ignored. Move along the direction that allows the maximum + * displacement. */ + if(dst0 > dst1) { + dir = args->dir0; + dst = dst0; + hit = hit0; + } else { + dir = args->dir1; + dst = dst1; + hit = hit1; + } + } else if(dst0 < reinject_threshold) { + /* Ingore dir0 that is bellow the reinject threshold */ + dir = args->dir1; + dst = dst1; + hit = hit1; + } else if(dst1 < reinject_threshold) { + /* Ingore dir1 that is bellow the reinject threshold */ + dir = args->dir0; + dst = dst0; + hit = hit0; + } else { + /* All reinjection directions are valid. Choose the first 1 that was + * randomly selected by the sample_reinjection_dir procedure and adjust + * the displacement distance. */ + dir = args->dir0; + + /* Define the reinjection distance along dir0 and its corresponding hit */ + if(dst0 <= dst1) { + dst = dst0; + hit = hit0; + } else { + dst = dst1; + hit = SXD_HIT_NULL; + } + + /* If the displacement distance is too close of a boundary, move to the + * boundary in order to avoid numerical uncertainty. */ + if(!SXD_HIT_NONE(&hit0) + && dst0 != dst + && eq_eps(dst0, dst, dst0*0.1)) { + dst = dst0; + hit = hit0; + } + } + + /* Setup the ray */ + fX(set)(ray->dir, dir); + ray->dst = (float)dst; + ray->hit = hit; + +exit: + return res; +error: + goto exit; +} + +static res_T +XD(find_reinjection_ray_and_check_validity) + (const struct sdis_scene* scn, + const struct XD(find_reinjection_ray_args)* args, + struct XD(reinjection_ray)* ray) +{ + double pos[DIM]; + struct sdis_medium* reinject_mdm; + res_T res = RES_OK; + + ASSERT(scn && args && ray); + ASSERT(XD(check_find_reinjection_ray_args)(args)); + + /* Select a reinjection direction */ + res = XD(find_reinjection_ray)(scn, args, ray); + if(res != RES_OK) goto error; + + if(SXD_HIT_NONE(&ray->hit)) { + /* Check medium consistency at the reinjection position */ + XD(move_pos)(dX(set)(pos, ray->org), ray->dir, (float)ray->dst); + res = scene_get_medium_in_closed_boundaries(scn, pos, &reinject_mdm); + if(res != RES_OK) goto error; + + if(reinject_mdm != args->solid) { + res = RES_BAD_OP; + goto error; + } + } + +exit: + return res; +error: + goto exit; +} + +static res_T +XD(handle_volumic_power) + (struct sdis_medium* solid, + struct rwalk_context* rwalk_ctx, + struct XD(rwalk)* rwalk, + const double reinject_dst_m, + struct XD(temperature)* T) +{ + double power; + double lambda; + double power_term; + size_t picard_order; + res_T res = RES_OK; + + /* Check pre-conditions */ + ASSERT(solid && rwalk_ctx && rwalk && T && reinject_dst_m > 0); + + /* Fetch the volumic power */ + power = solid_get_volumic_power(solid, &rwalk->vtx); + if(power == SDIS_VOLUMIC_POWER_NONE) goto exit; /* Do nothing */ + + /* Currently, the power term can be correctly taken into account only when + * the radiative temperature is linearized, i.e. when the picard order is + * equal to 1 */ + picard_order = get_picard_order(rwalk_ctx); + if(picard_order > 1) { + log_err(solid->dev, + "%s: invalid not null volumic power '%g' kg/m^3. Could not manage a " + "volumic power when the picard order is not equal to 1; Picard order is " + "currently set to %lu.\n", + FUNC_NAME, power, (unsigned long)picard_order); + res = RES_BAD_ARG; + goto error; + } + + /* Fetch the conductivity */ + lambda = solid_get_thermal_conductivity(solid, &rwalk->vtx); + + /* Compute the power term and handle the volumic power */ + power_term = (reinject_dst_m * reinject_dst_m)/ (2.0 * DIM * lambda); + T->value += power * power_term; + + /* Update the green path with the power term */ + if(rwalk_ctx->green_path) { + res = green_path_add_power_term + (rwalk_ctx->green_path, solid, &rwalk->vtx, power_term); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +XD(sample_reinjection_step_solid_fluid) + (const struct sdis_scene* scn, + const struct XD(sample_reinjection_step_args)* args, + struct XD(reinjection_step)* step) +{ + /* Input/output data of the function finding a valid reinjection ray */ + struct XD(find_reinjection_ray_args) find_reinject_ray_args = + XD(FIND_REINJECTION_RAY_ARGS_NULL); + struct XD(reinjection_ray) ray = XD(REINJECTION_RAY_NULL); + + /* In 2D it is useless to try to resample a reinjection direction since there + * is only one possible direction */ + const int MAX_ATTEMPTS = DIM == 2 ? 1 : 10; + + /* Miscellaneous variables */ + float dir0[DIM]; /* Sampled direction */ + float dir1[DIM]; /* Sampled direction reflected */ + int iattempt = 0; /* #attempts to find a reinjection dir */ + res_T res = RES_OK; + + /* Pre-conditions */ + ASSERT(scn && args && step); + ASSERT(XD(check_sample_reinjection_step_args)(args)); + + iattempt = 0; + do { + /* Sample a reinjection direction */ + XD(sample_reinjection_dir)(args->rwalk, args->rng, dir0); + + /* Reflect the sampled direction around the normal */ + XD(reflect)(dir1, dir0, args->rwalk->hit.normal); + + /* Flip the sampled directions if one wants to reinject to back side */ + if(args->side == SDIS_BACK) { + fX(minus)(dir0, dir0); + fX(minus)(dir1, dir1); + } + + /* Find the reinjection step */ + find_reinject_ray_args.solid = args->solid; + find_reinject_ray_args.rwalk = args->rwalk; + find_reinject_ray_args.distance = args->distance; + find_reinject_ray_args.can_move = 1; + fX(set)(find_reinject_ray_args.dir0, dir0); + fX(set)(find_reinject_ray_args.dir1, dir1); + res = XD(find_reinjection_ray_and_check_validity) + (scn, &find_reinject_ray_args, &ray); + if(res == RES_BAD_OP) continue; /* Cannot find a valid reinjection ray. Retry */ + if(res != RES_OK) goto error; + + } while(res != RES_OK && ++iattempt < MAX_ATTEMPTS); + + /* Could not find a valid reinjecton step */ + if(iattempt >= MAX_ATTEMPTS) { + log_warn(scn->dev, + "%s: could not find a valid reinjection step at `%g %g %g'.\n", + FUNC_NAME, SPLIT3(args->rwalk->vtx.P)); + res = RES_BAD_OP_IRRECOVERABLE; + goto error; + } + + /* Setup the reinjection step */ + step->hit = ray.hit; + step->distance = ray.dst; + fX(set)(step->direction, ray.dir); + + /* Update the random walk position if necessary */ + if(ray.position_was_moved) { + dX(set)(args->rwalk->vtx.P, ray.org); + } + + /* Post-conditions */ + ASSERT(dX(eq)(args->rwalk->vtx.P, ray.org)); + ASSERT(XD(check_reinjection_step)(step)); + +exit: + return res; +error: + goto exit; +} + +res_T +XD(sample_reinjection_step_solid_solid) + (const struct sdis_scene* scn, + const struct XD(sample_reinjection_step_args)* args_frt, + const struct XD(sample_reinjection_step_args)* args_bck, + struct XD(reinjection_step)* step_frt, + struct XD(reinjection_step)* step_bck) +{ + /* Input/output data of the function finding a valid reinjection ray */ + struct XD(find_reinjection_ray_args) find_reinject_ray_frt_args = + XD(FIND_REINJECTION_RAY_ARGS_NULL); + struct XD(find_reinjection_ray_args) find_reinject_ray_bck_args = + XD(FIND_REINJECTION_RAY_ARGS_NULL); + struct XD(reinjection_ray) ray_frt = XD(REINJECTION_RAY_NULL); + struct XD(reinjection_ray) ray_bck = XD(REINJECTION_RAY_NULL); + + /* Initial random walk position used as a backup */ + double rwalk_pos_backup[DIM]; + + /* Variables shared by the two side */ + struct XD(rwalk)* rwalk = NULL; + struct ssp_rng* rng = NULL; + + /* In 2D it is useless to try to resample a reinjection direction since there + * is only one possible direction */ + const int MAX_ATTEMPTS = DIM == 2 ? 1 : 10; + + float dir_frt_samp[DIM]; /* Sampled direction */ + float dir_frt_refl[DIM]; /* Sampled direction reflected */ + float dir_bck_samp[DIM]; /* Negated sampled direction */ + float dir_bck_refl[DIM]; /* Negated sampled direction reflected */ + int iattempt = 0; /* #attempts to find a reinjection dir */ + res_T res = RES_OK; + + /* Pre-conditions */ + ASSERT(scn && args_frt && args_bck && step_frt && step_bck); + ASSERT(XD(check_sample_reinjection_step_args)(args_frt)); + ASSERT(XD(check_sample_reinjection_step_args)(args_bck)); + ASSERT(args_frt->side == SDIS_FRONT); + ASSERT(args_bck->side == SDIS_BACK); + + rng = args_frt->rng; + rwalk = args_frt->rwalk; + ASSERT(args_bck->rng == rng); + ASSERT(args_bck->rwalk == rwalk); + + dX(set)(rwalk_pos_backup, rwalk->vtx.P); + iattempt = 0; + do { + /* Restore random walk pos */ + if(iattempt != 0) dX(set)(rwalk->vtx.P, rwalk_pos_backup); + + /* Sample a reinjection direction and reflect it around the normal. Then + * reflect them on the back side of the interface. */ + XD(sample_reinjection_dir)(rwalk, rng, dir_frt_samp); + XD(reflect)(dir_frt_refl, dir_frt_samp, rwalk->hit.normal); + fX(minus)(dir_bck_samp, dir_frt_samp); + fX(minus)(dir_bck_refl, dir_frt_refl); + + /* Find the reinjection ray for the front side */ + find_reinject_ray_frt_args.solid = args_frt->solid; + find_reinject_ray_frt_args.rwalk = args_frt->rwalk; + find_reinject_ray_frt_args.distance = args_frt->distance; + find_reinject_ray_frt_args.can_move = 1; + fX(set)(find_reinject_ray_frt_args.dir0, dir_frt_samp); + fX(set)(find_reinject_ray_frt_args.dir1, dir_frt_refl); + res = XD(find_reinjection_ray_and_check_validity) + (scn, &find_reinject_ray_frt_args, &ray_frt); + if(res == RES_BAD_OP) continue; + if(res != RES_OK) goto error; + + /* Update the random walk position if necessary */ + if(ray_frt.position_was_moved) dX(set)(rwalk->vtx.P, ray_frt.org); + + /* Select the reinjection direction and distance for the back side */ + find_reinject_ray_bck_args.solid = args_bck->solid; + find_reinject_ray_bck_args.rwalk = args_bck->rwalk; + find_reinject_ray_bck_args.distance = args_bck->distance; + find_reinject_ray_bck_args.can_move = 1; + fX(set)(find_reinject_ray_bck_args.dir0, dir_bck_samp); + fX(set)(find_reinject_ray_bck_args.dir1, dir_bck_refl); + res = XD(find_reinjection_ray_and_check_validity) + (scn, &find_reinject_ray_bck_args, &ray_bck); + if(res == RES_BAD_OP) continue; + if(res != RES_OK) goto error; + + /* Update the random walk position if necessary */ + if(ray_bck.position_was_moved) dX(set)(rwalk->vtx.P, ray_bck.org); + + /* If random walk was moved to find a valid rinjection ray on back side, + * one has to find a valid reinjection ob front side from the new pos */ + if(ray_bck.position_was_moved) { + find_reinject_ray_frt_args.can_move = 0; + res = XD(find_reinjection_ray_and_check_validity) + (scn, &find_reinject_ray_frt_args, &ray_frt); + if(res == RES_BAD_OP) continue; + if(res != RES_OK) goto error; + + /* Update the random walk position if necessary */ + if(ray_frt.position_was_moved) dX(set)(rwalk->vtx.P, ray_frt.org); + } + } while(res != RES_OK && ++iattempt < MAX_ATTEMPTS); + + /* Could not find a valid reinjection */ + if(iattempt >= MAX_ATTEMPTS) { + dX(set)(rwalk->vtx.P, rwalk_pos_backup); /* Restore random walk pos */ + log_warn(scn->dev, + "%s: could not find a valid solid/solid reinjection at {%g, %g, %g}.\n", + FUNC_NAME, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP_IRRECOVERABLE; + goto error; + } + + /* Setup the front and back reinjection steps */ + step_frt->hit = ray_frt.hit; + step_bck->hit = ray_bck.hit; + step_frt->distance = ray_frt.dst; + step_bck->distance = ray_bck.dst; + fX(set)(step_frt->direction, ray_frt.dir); + fX(set)(step_bck->direction, ray_bck.dir); + + /* Post-conditions */ + ASSERT(XD(check_reinjection_step)(step_frt)); + ASSERT(XD(check_reinjection_step)(step_bck)); + +exit: + return res; +error: + goto exit; +} + +res_T +XD(solid_reinjection) + (struct sdis_medium* solid, + struct XD(solid_reinjection_args)* args) +{ + struct solid_props props = SOLID_PROPS_NULL; + double reinject_dst_m; /* Reinjection distance in meters */ + double mu; + res_T res = RES_OK; + ASSERT(solid && XD(check_solid_reinjection_args)(args)); + + reinject_dst_m = args->reinjection->distance * args->fp_to_meter; + + res = solid_get_properties(solid, &args->rwalk->vtx, &props); + if(res != RES_OK) goto error; + + /* Manage the volumic power */ + res = XD(handle_volumic_power) + (solid, args->rwalk_ctx, args->rwalk, reinject_dst_m, args->T); + if(res != RES_OK) goto error; + + /* Time rewind */ + args->rwalk->mdm = solid; /* Medium into which the time is rewind */ + mu = (2*DIM*props.lambda)/(props.rho*props.cp*reinject_dst_m*reinject_dst_m); + res = XD(time_rewind) + (mu, props.t0, args->rng, args->rwalk, args->rwalk_ctx, args->T); + if(res != RES_OK) goto error; + + /* Test if a limit condition was reached */ + if(args->T->done) goto exit; + + /* Move the random walk to the reinjection position */ + XD(move_pos) + (args->rwalk->vtx.P, + args->reinjection->direction, + args->reinjection->distance); + + /* The random walk is in the solid */ + if(args->reinjection->hit.distance != args->reinjection->distance) { + args->T->func = XD(conductive_path); + args->rwalk->mdm = solid; + args->rwalk->hit = SXD_HIT_NULL; + args->rwalk->hit_side = SDIS_SIDE_NULL__; + + /* The random walk is at a boundary */ + } else { + args->T->func = XD(boundary_path); + args->rwalk->mdm = NULL; + args->rwalk->hit = args->reinjection->hit; + if(fX(dot)(args->reinjection->hit.normal, args->reinjection->direction) < 0) { + args->rwalk->hit_side = SDIS_FRONT; + } else { + args->rwalk->hit_side = SDIS_BACK; + } + } + + /* Register the new vertex against the heat path */ + res = register_heat_vertex + (args->rwalk_ctx->heat_path, + &args->rwalk->vtx, + args->T->value, + SDIS_HEAT_VERTEX_CONDUCTION, + (int)args->rwalk_ctx->nbranchings); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_boundary_Xd_fixed_flux.h b/src/sdis_heat_path_boundary_Xd_fixed_flux.h @@ -0,0 +1,135 @@ +/* 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_green.h" +#include "sdis_heat_path_boundary_c.h" +#include "sdis_interface_c.h" +#include "sdis_log.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Boundary path between a solid and a fluid with a fixed flux + ******************************************************************************/ +res_T +XD(solid_boundary_with_flux_path) + (struct sdis_scene* scn, + struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + const double phi, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + /* Input/output arguments of the function used to sample a reinjection */ + struct XD(sample_reinjection_step_args) samp_reinject_step_args = + XD(SAMPLE_REINJECTION_STEP_ARGS_NULL); + struct XD(reinjection_step) reinject_step = XD(REINJECTION_STEP_NULL); + + /* Reinjection arguments */ + struct XD(solid_reinjection_args) solid_reinject_args = + XD(SOLID_REINJECTION_ARGS_NULL); + + /* Data attached to the boundary */ + struct sdis_interface* interf = NULL; + struct sdis_medium* solid = NULL; + + /* Miscellaneous variables */ + double lambda; /* Solid conductivity */ + double delta_boundary; /* Orthogonal reinjection dst at the boundary */ + double delta; /* Orthogonal fitted reinjection dst at the boundary */ + double delta_m; /* Delta in meters */ + double flux_term; + size_t picard_order; + enum sdis_side solid_side = SDIS_SIDE_NULL__; + res_T res = RES_OK; + + ASSERT(frag && phi != SDIS_FLUX_NONE); + ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); + (void)ctx; + + /* Currently, the flux can be correctly taken into account only when the + * radiative temperature is linearized, i.e. when the picard order is equal + * to 1 */ + picard_order = get_picard_order(ctx); + if(picard_order > 1 && phi != 0) { + log_err(scn->dev, + "%s: invalid flux '%g' W/m^2. Could not manage a flux != 0 when the " + "picard order is not equal to 1; Picard order is currently set to %lu.\n", + FUNC_NAME, phi, (unsigned long)picard_order); + res = RES_BAD_ARG; + goto error; + } + + /* Retrieve the solid split by the interface */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + solid = interface_get_medium(interf, frag->side); + solid_side = frag->side; + ASSERT(phi == interface_side_get_flux(interf, frag)); + ASSERT(solid->type == SDIS_SOLID); + + /* Fetch the solid properties */ + lambda = solid_get_thermal_conductivity(solid, &rwalk->vtx); + delta = solid_get_delta(solid, &rwalk->vtx); + + /* Note that the reinjection distance is *FIXED*. It MUST ensure that the + * orthogonal distance from the boundary to the reinjection point is at most + * equal to delta. */ + delta_boundary = delta * sqrt(DIM); + + /* Sample a reinjection step */ + samp_reinject_step_args.rng = rng; + samp_reinject_step_args.solid = solid; + samp_reinject_step_args.rwalk = rwalk; + samp_reinject_step_args.distance = delta_boundary; + samp_reinject_step_args.side = solid_side; + res = XD(sample_reinjection_step_solid_fluid) + (scn, &samp_reinject_step_args, &reinject_step); + if(res != RES_OK) goto error; + + /* Define the orthogonal dst from the boundary to the reinjection position */ + delta = reinject_step.distance / sqrt(DIM); + delta_m = delta * scn->fp_to_meter; + + /* Handle the flux */ + flux_term = delta_m / lambda; + T->value += phi * flux_term; + if(ctx->green_path) { + res = green_path_add_flux_term(ctx->green_path, interf, frag, flux_term); + if(res != RES_OK) goto error; + } + + /* Perform the reinjection into the solid */ + solid_reinject_args.reinjection = &reinject_step; + solid_reinject_args.rwalk_ctx = ctx; + solid_reinject_args.rwalk = rwalk; + solid_reinject_args.rng = rng; + solid_reinject_args.T = T; + solid_reinject_args.fp_to_meter = scn->fp_to_meter; + res = XD(solid_reinjection)(solid, &solid_reinject_args); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" 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 @@ -25,176 +25,238 @@ #include "sdis_Xd_begin.h" /******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE res_T +XD(check_Tref) + (const struct sdis_scene* scn, + const double pos[DIM], + const double Tref, + const char* func_name) +{ + ASSERT(scn && pos && func_name); + +#if DIM == 2 + #define STR_VECX "%g %g" + #define SPLITX SPLIT2 +#else + #define STR_VECX "%g %g %g" + #define SPLITX SPLIT3 +#endif + if(Tref < 0) { + log_err(scn->dev, + "%s: invalid reference temperature `%gK' at the position `"STR_VECX"'.\n", + func_name, Tref, SPLITX(pos)); + return RES_BAD_OP_IRRECOVERABLE; + } + if(Tref > scn->tmax) { + log_err(scn->dev, + "%s: invalid maximum temperature `%gK'. The reference temperature `%gK'" + "at the position `"STR_VECX"' is greater than this temperature.\n", + func_name, scn->tmax, Tref, SPLITX(pos)); + return RES_BAD_OP_IRRECOVERABLE; + } +#undef STR_VECX +#undef SPLITX + + return RES_OK; +} + +static INLINE res_T +XD(rwalk_get_Tref) + (const struct sdis_scene* scn, + const struct XD(rwalk)* rwalk, + const struct XD(temperature)* T, + double* out_Tref) +{ + double Tref = -1; + res_T res = RES_OK; + ASSERT(rwalk && T && out_Tref); + + if(T->done) { + /* The path reaches a limit condition, i.e. it goes to the infinity and + * fetches the ambient radiative temperature. We do not use the limit + * conditions as the reference temperature to make the sampled paths + * independant of them. */ + Tref = scn->trad.reference; + } else { + struct sdis_interface_fragment frag; + struct sdis_interface* interf = NULL; + ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + + /* Fetch the interface where the random walk ends */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + ASSERT(rwalk->hit_side!=SDIS_FRONT || interf->medium_front->type==SDIS_FLUID); + ASSERT(rwalk->hit_side!=SDIS_BACK || interf->medium_back->type==SDIS_FLUID); + + /* Fragment on the fluid side of the boundary onto which the rwalk ends */ + XD(setup_interface_fragment) + (&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); + + Tref = interface_side_get_reference_temperature(interf, &frag); + } + + res = XD(check_Tref)(scn, rwalk->vtx.P, Tref, FUNC_NAME); + if(res != RES_OK) goto error; + +exit: + *out_Tref = Tref; + return res; +error: + Tref = -1; + goto exit; +} + +/******************************************************************************* * Boundary path between a solid and a fluid ******************************************************************************/ res_T XD(solid_fluid_boundary_picard1_path) - (const struct sdis_scene* scn, - const struct rwalk_context* ctx, + (struct sdis_scene* scn, + struct rwalk_context* ctx, const struct sdis_interface_fragment* frag, struct XD(rwalk)* rwalk, struct ssp_rng* rng, struct XD(temperature)* T) { + /* Input/output arguments of the function used to sample a reinjection */ + struct XD(sample_reinjection_step_args) samp_reinject_step_args = + XD(SAMPLE_REINJECTION_STEP_ARGS_NULL); + struct XD(reinjection_step) reinject_step = + XD(REINJECTION_STEP_NULL); + + /* Temperature and random walk state of the sampled radiative path */ + struct XD(temperature) T_s; + struct XD(rwalk) rwalk_s; + + /* Fragment on the fluid side of the boundary */ + struct sdis_interface_fragment frag_fluid; + + /* Data attached to the boundary */ struct sdis_interface* interf = NULL; - struct sdis_medium* mdm_front = NULL; - struct sdis_medium* mdm_back = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* fluid = NULL; - struct XD(rwalk) rwalk_saved; - struct sXd(hit) hit = SXD_HIT_NULL; - struct sdis_interface_fragment frag_fluid; - double hc; - double hr; + + double h_cond; /* Conductive coefficient */ + double h_conv; /* Convective coefficient */ + double h_radi_hat; /* Radiative coefficient with That */ + double h_hat; /* Sum of h_<conv|cond|rad_hat> */ + double p_conv; /* Convective proba */ + double p_cond; /* Conductive proba */ + double epsilon; /* Interface emissivity */ - double lambda; - double fluid_proba; - double radia_proba; - double delta; - double delta_boundary; + double Tref; /* Reference temperature */ + double Tref_s; /* Reference temperature of the sampled radiative path */ + double lambda; /* Solid conductivity */ + double delta_boundary; /* Orthogonal reinjection dst at the boundary */ + double delta; /* Orthogonal fitted reinjection dst at the boundary */ + double r; - double tmp; - float dir0[DIM], dir1[DIM]; - float reinject_dst; - /* In 2D it is useless to try to resample a reinjection direction since there - * is only one possible direction */ - const int MAX_ATTEMPTS = DIM == 2 ? 1 : 10; - int iattempt; - int reinjection_is_valid = 0; + struct sdis_heat_vertex hvtx = SDIS_HEAT_VERTEX_NULL; + enum sdis_side solid_side = SDIS_SIDE_NULL__; + enum sdis_side fluid_side = SDIS_SIDE_NULL__; res_T res = RES_OK; + ASSERT(scn && rwalk && rng && T && ctx); ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); /* Retrieve the solid and the fluid split by the boundary */ interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - mdm_front = interface_get_medium(interf, SDIS_FRONT); - mdm_back = interface_get_medium(interf, SDIS_BACK); - ASSERT(mdm_front->type != mdm_back->type); - - /* Setup the fluid side fragment */ - frag_fluid = *frag; - if(mdm_front->type == SDIS_SOLID) { - solid = mdm_front; - fluid = mdm_back; - frag_fluid.side = SDIS_BACK; - } else { - solid = mdm_back; - fluid = mdm_front; - frag_fluid.side = SDIS_FRONT; + solid = interface_get_medium(interf, SDIS_FRONT); + fluid = interface_get_medium(interf, SDIS_BACK); + solid_side = SDIS_FRONT; + fluid_side = SDIS_BACK; + if(solid->type != SDIS_SOLID) { + SWAP(struct sdis_medium*, solid, fluid); + SWAP(enum sdis_side, solid_side, fluid_side); + ASSERT(fluid->type == SDIS_FLUID); } - /* Fetch the boundary properties */ - epsilon = interface_side_get_emissivity(interf, &frag_fluid); - Tref = interface_side_get_reference_temperature(interf, &frag_fluid); + /* Setup a fragment for the fluid side */ + frag_fluid = *frag; + frag_fluid.side = fluid_side; /* Fetch the solid properties */ lambda = solid_get_thermal_conductivity(solid, &rwalk->vtx); delta = solid_get_delta(solid, &rwalk->vtx); - /* Note that the reinjection distance is *FIXED*. It MUST ensure that the - * orthogonal distance from the boundary to the point to chalenge is equal to - * delta. */ - delta_boundary = sqrt(DIM) * delta; - - rwalk_saved = *rwalk; - reinjection_is_valid = 0; - iattempt = 0; - do { - if(iattempt != 0) *rwalk = rwalk_saved; - - /* Sample a reinjection direction */ - XD(sample_reinjection_dir)(rwalk, rng, dir0); - - /* Reflect the sampled direction around the normal */ - XD(reflect)(dir1, dir0, rwalk->hit.normal); - - if(solid == mdm_back) { - fX(minus)(dir0, dir0); - fX(minus)(dir1, dir1); - } + /* Fetch the boundary emissivity */ + epsilon = interface_side_get_emissivity(interf, &frag_fluid); - /* Select the solid reinjection direction and distance */ - res = XD(select_reinjection_dir_and_check_validity)(scn, solid, rwalk, - dir0, dir1, delta_boundary, dir0, &reinject_dst, 1, NULL, - &reinjection_is_valid, &hit); + if(epsilon <= 0) { + Tref = 0; + } else { + /* Check the Tref */ + Tref = interface_side_get_reference_temperature(interf, &frag_fluid); + res = XD(check_Tref)(scn, frag_fluid.P, Tref, FUNC_NAME); if(res != RES_OK) goto error; + } - } while(!reinjection_is_valid && ++iattempt < MAX_ATTEMPTS); + /* Note that the reinjection distance is *FIXED*. It MUST ensure that the + * orthogonal distance from the boundary to the reinjection point is at most + * equal to delta. */ + delta_boundary = sqrt(DIM) * delta; - /* Could not find a valid reinjecton */ - if(iattempt >= MAX_ATTEMPTS) { - *rwalk = rwalk_saved; - log_warn(scn->dev, - "%s: could not find a valid solid/fluid reinjection at {%g, %g %g}.\n", - FUNC_NAME, SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP_IRRECOVERABLE; - goto error; - } + /* Sample a reinjection step */ + samp_reinject_step_args.rng = rng; + samp_reinject_step_args.solid = solid; + samp_reinject_step_args.rwalk = rwalk; + samp_reinject_step_args.distance = delta_boundary; + samp_reinject_step_args.side = solid_side; + res = XD(sample_reinjection_step_solid_fluid) + (scn, &samp_reinject_step_args, &reinject_step); + if(res != RES_OK) goto error; /* Define the orthogonal dst from the reinjection pos to the interface */ - delta = reinject_dst / sqrt(DIM); + delta = reinject_step.distance / sqrt(DIM); /* Compute the convective, conductive and the upper bound radiative coef */ h_conv = interface_get_convection_coef(interf, frag); h_cond = lambda / (delta * scn->fp_to_meter); - h_rad_hat = 4.0 * BOLTZMANN_CONSTANT * ctx->That3 * epsilon; - + h_radi_hat = 4.0 * BOLTZMANN_CONSTANT * ctx->That3 * epsilon; + /* Compute a global upper bound coefficient */ - h_hat = h_conv + h_cond + h_rad_hat; + h_hat = h_conv + h_cond + h_radi_hat; /* Compute the probas to switch in solid, fluid or radiative random walk */ p_conv = h_conv / h_hat; p_cond = h_cond / h_hat; + /* Fetch the last registered heat path vertex */ + if(ctx->heat_path) hvtx = *heat_path_get_last_vertex(ctx->heat_path); + /* Null collision */ for(;;) { - r = ssp_rng_canonical(rng); + double h_radi; /* Radiative coefficient */ + double p_radi; /* Radiative proba */ + + /* Indices of the registered vertex of the sampled radiative path */ + size_t ihvtx_radi_begin = 0; + size_t ihvtx_radi_end = 0; + + r = ssp_rng_canonical(rng); /* Switch in convective path */ if(r < p_conv) { T->func = XD(convective_path); rwalk->mdm = fluid; - rwalk->hit_side = rwalk->mdm == mdm_front ? SDIS_FRONT : SDIS_BACK; + rwalk->hit_side = fluid_side; break; } /* Switch in conductive path */ if(r < p_conv + p_cond) { - /* Handle the volumic power */ - const double power = solid_get_volumic_power(solid, &rwalk->vtx); - if(power != SDIS_VOLUMIC_POWER_NONE) { - const double delta_in_meter = reinject_dst * scn->fp_to_meter; - tmp = delta_in_meter * delta_in_meter / (2.0 * DIM * lambda); - T->value += power * tmp; - - if(ctx->green_path) { - res = green_path_add_power_term(ctx->green_path, solid, &rwalk->vtx, tmp); - if(res != RES_OK) goto error; - } - } - - /* Time rewind */ - res = XD(time_rewind)(solid, rng, reinject_dst * scn->fp_to_meter, ctx, rwalk, T); - if(res != RES_OK) goto error; - if(T->done) goto exit; /* Limit condition was reached */ - - /* Perform solid reinjection */ - XD(move_pos)(rwalk->vtx.P, dir0, reinject_dst); - if(hit.distance == reinject_dst) { - T->func = XD(boundary_path); - rwalk->mdm = NULL; - rwalk->hit = hit; - rwalk->hit_side = fX(dot)(hit.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; - } else { - T->func = XD(conductive_path); - rwalk->mdm = solid; - rwalk->hit = SXD_HIT_NULL; - rwalk->hit_side = SDIS_SIDE_NULL__; - } - - /* Register the new vertex against the heat path */ - res = register_heat_vertex - (ctx->heat_path, &rwalk->vtx, T->value, SDIS_HEAT_VERTEX_CONDUCTION); + struct XD(solid_reinjection_args) solid_reinject_args = + XD(SOLID_REINJECTION_ARGS_NULL); + + /* Perform the reinjection into the solid */ + solid_reinject_args.reinjection = &reinject_step; + solid_reinject_args.rwalk_ctx = ctx; + solid_reinject_args.rwalk = rwalk; + solid_reinject_args.rng = rng; + solid_reinject_args.T = T; + solid_reinject_args.fp_to_meter = scn->fp_to_meter; + res = XD(solid_reinjection)(solid, &solid_reinject_args); if(res != RES_OK) goto error; break; } @@ -202,47 +264,56 @@ XD(solid_fluid_boundary_picard1_path) /* From there, we know the path is either a radiative path or a * null-collision */ - /* Trace a candidate radiative path and get the Tref at its end. - * TODO handle the registration of the path geometry */ - T_candidate = *T; - rwalk_candidate = *rwalk; - res = XD(radiative_path)(scn, ctx, &rwalk_candidate, rng, T_candidate); + if(ctx->heat_path) { + /* Fetch the index of the first vertex of the radiative path that is + * going to be traced i.e. the last registered vertex */ + ihvtx_radi_begin = heat_path_get_vertices_count(ctx->heat_path) - 1; + } + + /* Sample a radiative path and get the Tref at its end. */ + T_s = *T; + rwalk_s = *rwalk; + rwalk_s.mdm = fluid; + rwalk_s.hit_side = fluid_side; + res = XD(radiative_path)(scn, ctx, &rwalk_s, rng, &T_s); if(res != RES_OK) goto error; /* Get the Tref at the end of the candidate radiative path */ - if(T_candidate->done) { - Tref_candidate = T_candidate->value; - } else { - ASSERT(!SXD_HIT_NONE(rwalk_candidate->hit)); - XD(setup_interface_fragment) - (&frag_candidate, &rwalk_candidate->vtx, &rwalk_candidate->hit, - rwalk_candidate->hit_side); - interf_candidate = scene_get_interface - (scn, rwalk_candidate->hit.prim.prim_id); - - Tref_candidate = interface_side_get_reference_temperature(interf, f&rag); - } + res = XD(rwalk_get_Tref)(scn, &rwalk_s, &T_s, &Tref_s); + if(res != RES_OK) goto error; - if(Tref_candidate < 0) { - log_err(scn->dev, - "%s: invalid reference temperature `%gK' at the position `%g %g %g'.\n", - FUNC_NAME, Tref_candidate, SPLIT3(rwalk_candidate->vtx.P)); - res = RES_BAD_OP_IRRECOVERABLE; - goto error; - } + h_radi = BOLTZMANN_CONSTANT * epsilon * + ( Tref*Tref*Tref + + Tref*Tref * Tref_s + + Tref * Tref_s*Tref_s + + Tref_s*Tref_s*Tref_s); - h_rad = BOLTZMANN_CONSTANT - * epsilon - * ( Tref*Tref*Tref - + Tref*Tref * Tref_candidate - + Tref* Tref_candidate*Tref_candidate - + Tref_candidate*Tref_candidate*Tref_candidate); - - p_rad = h_rad / h_hat; - if(r < p_conv + p_cond + p_rad) { /* Radiative path */ - *rwalk = *rwalk_candidate; - *T = *T_candidate; + p_radi = h_radi / h_hat; + if(r < p_conv + p_cond + p_radi) { /* Radiative path */ + *rwalk = rwalk_s; + *T = T_s; break; + + /* Null collision: the sampled path is rejected. */ + } else { + + if(ctx->green_path) { + /* The limit condition of the green path could be set by the rejected + * sampled radiative path. Reset this limit condition. */ + green_path_reset_limit(ctx->green_path); + } + + if(ctx->heat_path) { + /* Set the sampled radiative path as a branch of the current path */ + ihvtx_radi_end = heat_path_get_vertices_count(ctx->heat_path); + heat_path_increment_sub_path_branch_id + (ctx->heat_path, ihvtx_radi_begin, ihvtx_radi_end); + + /* Add a break into the heat path geometry and restart it from the + * position of the input random walk. */ + res = heat_path_restart(ctx->heat_path, &hvtx); + if(res != RES_OK) goto error; + } } /* Null-collision, looping at the beginning */ diff --git a/src/sdis_heat_path_boundary_Xd_solid_fluid_picardN.h b/src/sdis_heat_path_boundary_Xd_solid_fluid_picardN.h @@ -0,0 +1,365 @@ +/* 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_green.h" +#include "sdis_heat_path_boundary_c.h" +#include "sdis_interface_c.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_realisation.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Generic helper functions + ******************************************************************************/ +static INLINE res_T +XD(sample_path) + (struct sdis_scene* scn, + const struct XD(rwalk)* rwalk_from, + struct rwalk_context* ctx, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + struct XD(rwalk) rwalk = XD(RWALK_NULL); + res_T res = RES_OK; + ASSERT(rwalk_from && rng && T); + + /* Clean-up the output variable */ + *T = XD(TEMPERATURE_NULL); + T->func = XD(boundary_path); + + /* Init the random walk */ + rwalk.vtx = rwalk_from->vtx; + rwalk.mdm = rwalk_from->mdm; + rwalk.hit = rwalk_from->hit; + rwalk.hit_side = rwalk_from->hit_side; + + /* Start the registration of a new heat path */ + if(ctx->heat_path) { + struct sdis_heat_vertex heat_vtx = SDIS_HEAT_VERTEX_NULL; + + heat_vtx.P[0] = rwalk.vtx.P[0]; + heat_vtx.P[1] = rwalk.vtx.P[1]; + heat_vtx.P[2] = rwalk.vtx.P[2]; + heat_vtx.time = rwalk.vtx.time; + heat_vtx.weight = 0; + heat_vtx.type = SDIS_HEAT_VERTEX_RADIATIVE; + heat_vtx.branch_id = (int)ctx->nbranchings + 1; + + res = heat_path_restart(ctx->heat_path, &heat_vtx); + if(res != RES_OK) goto error; + } + + /* Sample the path */ + res = XD(compute_temperature)(scn, ctx, &rwalk, rng, T); + if(res != RES_OK) goto error; + + /* Check the returned temperature */ + ASSERT(T->done); + if(T->value < scn->tmin || scn->tmax < T->value) { + log_err(scn->dev, "%s: invalid temperature range `[%g, %g]K` regarding the " + "retrieved temperature %gK.\n", FUNC_NAME, scn->tmin, scn->tmax, T->value); + res = RES_BAD_OP_IRRECOVERABLE; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Boundary path between a solid and a fluid + ******************************************************************************/ +res_T +XD(solid_fluid_boundary_picardN_path) + (struct sdis_scene* scn, + struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + /* Input/output arguments of the function used to sample a reinjection */ + struct XD(sample_reinjection_step_args) samp_reinject_step_args = + XD(SAMPLE_REINJECTION_STEP_ARGS_NULL); + struct XD(reinjection_step) reinject_step = + XD(REINJECTION_STEP_NULL); + + /* Fragment on the fluid side of the boundary */ + struct sdis_interface_fragment frag_fluid; + + /* Vertex of the heat path */ + struct sdis_heat_vertex hvtx = SDIS_HEAT_VERTEX_NULL; + struct sdis_heat_vertex hvtx_s = SDIS_HEAT_VERTEX_NULL; + + /* Data attached to the boundary */ + struct sdis_interface* interf = NULL; + struct sdis_medium* solid = NULL; + struct sdis_medium* fluid = NULL; + + double h_cond; /* Conductive coefficient */ + double h_conv; /* Convective coefficient */ + double h_radi_hat; /* Radiative coefficient with That */ + double h_hat; /* Sum of h_<conv|cond|rad_hat> */ + double p_conv; /* Convective proba */ + double p_cond; /* Conductive proba */ + + /* Min/Max Temperatures */ + double Tmin, Tmin2, Tmin3; + double That, That2, That3; + + double epsilon; /* Interface emissivity */ + double lambda; /* Solid conductivity */ + double delta_boundary; /* Orthogonal reinjection dst at the boundary */ + double delta; /* Orthogonal fitted reinjection dst at the boundary */ + + double r; + enum sdis_side solid_side = SDIS_SIDE_NULL__; + enum sdis_side fluid_side = SDIS_SIDE_NULL__; + res_T res = RES_OK; + + ASSERT(scn && rwalk && rng && T && ctx); + ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); + + /* Fetch the Min/max temperature */ + Tmin = ctx->Tmin; + Tmin2 = ctx->Tmin2; + Tmin3 = ctx->Tmin3; + That = ctx->That; + That2 = ctx->That2; + That3 = ctx->That3; + + /* Retrieve the solid and the fluid split by the boundary */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + solid = interface_get_medium(interf, SDIS_FRONT); + fluid = interface_get_medium(interf, SDIS_BACK); + solid_side = SDIS_FRONT; + fluid_side = SDIS_BACK; + if(solid->type != SDIS_SOLID) { + SWAP(struct sdis_medium*, solid, fluid); + SWAP(enum sdis_side, solid_side, fluid_side); + ASSERT(fluid->type == SDIS_FLUID); + } + + /* Setup a fragment for the fluid side */ + frag_fluid = *frag; + frag_fluid.side = fluid_side; + + /* Fetch the solid properties */ + lambda = solid_get_thermal_conductivity(solid, &rwalk->vtx); + delta = solid_get_delta(solid, &rwalk->vtx); + + /* Fetch the boundary emissivity */ + epsilon = interface_side_get_emissivity(interf, &frag_fluid); + + /* Note that the reinjection distance is *FIXED*. It MUST ensure that the + * orthogonal distance from the boundary to the reinjection point is at most + * equal to delta. */ + delta_boundary = sqrt(DIM) * delta; + + /* Sample a reinjection step */ + samp_reinject_step_args.rng = rng; + samp_reinject_step_args.solid = solid; + samp_reinject_step_args.rwalk = rwalk; + samp_reinject_step_args.distance = delta_boundary; + samp_reinject_step_args.side = solid_side; + res = XD(sample_reinjection_step_solid_fluid) + (scn, &samp_reinject_step_args, &reinject_step); + if(res != RES_OK) goto error; + + /* Define the orthogonal dst from the reinjection pos to the interface */ + delta = reinject_step.distance / sqrt(DIM); + + /* Compute the convective, conductive and the upper bound radiative coef */ + h_conv = interface_get_convection_coef(interf, frag); + h_cond = lambda / (delta * scn->fp_to_meter); + h_radi_hat = 4.0 * BOLTZMANN_CONSTANT * That3 * epsilon; + + /* Compute a global upper bound coefficient */ + h_hat = h_conv + h_cond + h_radi_hat; + + /* Compute the probas to switch in convection or conduction */ + p_conv = h_conv / h_hat; + p_cond = h_cond / h_hat; + + /* Fetch the last registered heat path vertex */ + if(ctx->heat_path) hvtx = *heat_path_get_last_vertex(ctx->heat_path); + + /* Null collision main loop */ + for(;;) { + /* Temperature and random walk state of the sampled radiative path */ + struct XD(temperature) T_s; + struct XD(rwalk) rwalk_s; + + double h_radi, h_radi_min, h_radi_max; /* Radiative coefficients */ + double p_radi, p_radi_min, p_radi_max; /* Radiative probas */ + double T0, T1, T2, T3, T4, T5; /* Computed temperatures */ + + /* Indices of the registered vertex of the sampled radiative path */ + size_t ihvtx_radi_begin = 0; + size_t ihvtx_radi_end = 0; + + r = ssp_rng_canonical(rng); + + /* Switch in convective path */ + if(r < p_conv) { + T->func = XD(convective_path); + rwalk->mdm = fluid; + rwalk->hit_side = fluid_side; + break; + } + + /* Switch in conductive path */ + if(r < p_conv + p_cond) { + struct XD(solid_reinjection_args) solid_reinject_args = + XD(SOLID_REINJECTION_ARGS_NULL); + + /* Perform the reinjection into the solid */ + solid_reinject_args.reinjection = &reinject_step; + solid_reinject_args.rwalk_ctx = ctx; + solid_reinject_args.rwalk = rwalk; + solid_reinject_args.rng = rng; + solid_reinject_args.T = T; + solid_reinject_args.fp_to_meter = scn->fp_to_meter; + res = XD(solid_reinjection)(solid, &solid_reinject_args); + if(res != RES_OK) goto error; + break; + } + + if(ctx->heat_path) { + /* Fetch the index of the first vertex of the radiative path that is + * going to be traced i.e. the last registered vertex */ + ihvtx_radi_begin = heat_path_get_vertices_count(ctx->heat_path) - 1; + } + + /* Sample a radiative path */ + T_s = *T; + rwalk_s = *rwalk; + rwalk_s.mdm = fluid; + rwalk_s.hit_side = fluid_side; + res = XD(radiative_path)(scn, ctx, &rwalk_s, rng, &T_s); + if(res != RES_OK) goto error; + + if(ctx->heat_path) { + /* Fetch the index after the last registered vertex of the sampled + * radiative path */ + ihvtx_radi_end = heat_path_get_vertices_count(ctx->heat_path); + } + + /* Fetch the last registered heat path vertex of the radiative path */ + if(ctx->heat_path) hvtx_s = *heat_path_get_last_vertex(ctx->heat_path); + + h_radi_min = 4.0 * BOLTZMANN_CONSTANT * Tmin3 * epsilon; + p_radi_min = h_radi_min / h_hat; + + /* Switch in radiative path */ + if(r < p_conv + p_cond + p_radi_min) { *rwalk = rwalk_s; *T = T_s; break; } + + /* Define some helper macros */ + #define SWITCH_IN_RADIATIVE { \ + *rwalk = rwalk_s; *T = T_s; \ + res = heat_path_restart(ctx->heat_path, &hvtx_s); \ + if(res != RES_OK) goto error; \ + } (void)0 + + #define NULL_COLLISION { \ + res = heat_path_restart(ctx->heat_path, &hvtx); \ + if(res != RES_OK) goto error; \ + if(ctx->heat_path) { \ + heat_path_increment_sub_path_branch_id \ + (ctx->heat_path, ihvtx_radi_begin, ihvtx_radi_end); \ + } \ + } (void)0 + + #define COMPUTE_TEMPERATURE(Result, RWalk, Temp) { \ + struct XD(temperature) T_p; \ + if((Temp)->done) { /* Ambient radiative temperature */ \ + ASSERT(SXD_HIT_NONE(&(RWalk)->hit)); \ + T_p = *(Temp); \ + } else { \ + res = XD(sample_path)(scn, RWalk, ctx, rng, &T_p); \ + if(res != RES_OK) goto error; \ + } \ + Result = T_p.value; \ + } (void)0 + + #define CHECK_PMIN_PMAX { \ + p_radi_min = h_radi_min*epsilon / h_hat; \ + p_radi_max = h_radi_max*epsilon / h_hat; \ + if(r < p_conv + p_cond + p_radi_min) { SWITCH_IN_RADIATIVE; break; } \ + if(r > p_conv + p_cond + p_radi_max) { NULL_COLLISION; continue; } \ + } (void)0 + + /* Sample a 1st heat path at the end of the radiative path */ + COMPUTE_TEMPERATURE(T0, &rwalk_s, &T_s); + h_radi_min = BOLTZMANN_CONSTANT*(Tmin3 + 3*Tmin2*T0); + h_radi_max = BOLTZMANN_CONSTANT*(That3 + 3*That2*T0); + CHECK_PMIN_PMAX; + + /* Sample a 2nd heat path at the end of the radiative path */ + COMPUTE_TEMPERATURE(T1, &rwalk_s, &T_s); + h_radi_min = BOLTZMANN_CONSTANT*(Tmin3 + Tmin2*T0 + 2*Tmin*T0*T1); + h_radi_max = BOLTZMANN_CONSTANT*(That3 + That2*T0 + 2*That*T0*T1); + CHECK_PMIN_PMAX; + + /* Sample a 3rd heat path at the end of the radiative path */ + COMPUTE_TEMPERATURE(T2, &rwalk_s, &T_s); + h_radi_min = BOLTZMANN_CONSTANT*(Tmin3 + Tmin2*T0 + Tmin*T0*T1 + T0*T1*T2); + h_radi_max = BOLTZMANN_CONSTANT*(That3 + That2*T0 + That*T0*T1 + T0*T1*T2); + CHECK_PMIN_PMAX; + + /* Sample a 1st heat path at the current position onto the interface */ + COMPUTE_TEMPERATURE(T3, rwalk, T); + h_radi_min = BOLTZMANN_CONSTANT*(Tmin2*T3 + Tmin*T0*T3 + T0*T1*T3 + T0*T1*T2); + h_radi_max = BOLTZMANN_CONSTANT*(That2*T3 + That*T0*T3 + T0*T1*T3 + T0*T1*T2); + CHECK_PMIN_PMAX; + + /* Sample a 2nd heat path at the current position onto the interface */ + COMPUTE_TEMPERATURE(T4, rwalk, T); + h_radi_min = BOLTZMANN_CONSTANT*(Tmin*T3*T4 + T0*T3*T4 + T0*T1*T3 + T0*T1*T2); + h_radi_max = BOLTZMANN_CONSTANT*(That*T3*T4 + T0*T3*T4 + T0*T1*T3 + T0*T1*T2); + CHECK_PMIN_PMAX; + + /* Sample a 3rd heat path at the current position onto the interface */ + COMPUTE_TEMPERATURE(T5, rwalk, T); + h_radi = BOLTZMANN_CONSTANT*(T3*T4*T5 + T0*T3*T4 + T0*T1*T3 + T0*T1*T2); + p_radi = h_radi * epsilon / h_hat; + + /* Switch in radiative path */ + if(r < p_cond + p_conv + p_radi) { SWITCH_IN_RADIATIVE; break; } + + /* Null-collision, looping at the beginning */ + NULL_COLLISION; + + #undef SWITCH_IN_RADIATIVE + #undef NULL_COLLISION + #undef COMPUTE_TEMPERATURE + #undef CHECK_PMIN_PMAX + } + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" + diff --git a/src/sdis_heat_path_boundary_Xd_solid_solid.h b/src/sdis_heat_path_boundary_Xd_solid_solid.h @@ -0,0 +1,175 @@ +/* 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_green.h" +#include "sdis_heat_path_boundary_c.h" +#include "sdis_interface_c.h" +#include "sdis_log.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Boundary path between a solid and a fluid + ******************************************************************************/ +res_T +XD(solid_solid_boundary_path) + (struct sdis_scene* scn, + struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + /* Input/output arguments of the function used to sample a reinjection */ + struct XD(sample_reinjection_step_args) samp_reinject_step_frt_args = + XD(SAMPLE_REINJECTION_STEP_ARGS_NULL); + struct XD(sample_reinjection_step_args) samp_reinject_step_bck_args = + XD(SAMPLE_REINJECTION_STEP_ARGS_NULL); + struct XD(reinjection_step) reinject_step_frt = XD(REINJECTION_STEP_NULL); + struct XD(reinjection_step) reinject_step_bck = XD(REINJECTION_STEP_NULL); + struct XD(reinjection_step)* reinject_step = NULL; + + /* Reinjection arguments */ + struct XD(solid_reinjection_args) solid_reinject_args = + XD(SOLID_REINJECTION_ARGS_NULL); + + /* Data attached to the boundary */ + struct sdis_interface* interf = NULL; + struct sdis_medium* solid_frt = NULL; + struct sdis_medium* solid_bck = NULL; + struct sdis_medium* solid = NULL; + + double lambda_frt; + double lambda_bck; + double delta_boundary_frt; + double delta_boundary_bck; + + double proba; + double r; + double tcr; + + res_T res = RES_OK; + ASSERT(scn && ctx && frag && rwalk && rng && T); + ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); + (void)frag, (void)ctx; + + /* Retrieve the two solids split by the boundary */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + solid_frt = interface_get_medium(interf, SDIS_FRONT); + solid_bck = interface_get_medium(interf, SDIS_BACK); + ASSERT(solid_frt->type == SDIS_SOLID); + ASSERT(solid_bck->type == SDIS_SOLID); + + /* Retrieve the thermal contact resistance */ + tcr = interface_get_thermal_contact_resistance(interf, frag); + + /* Fetch the properties of the media */ + lambda_frt = solid_get_thermal_conductivity(solid_frt, &rwalk->vtx); + lambda_bck = solid_get_thermal_conductivity(solid_bck, &rwalk->vtx); + + /* Note that the reinjection distance is *FIXED*. It MUST ensure that the + * orthogonal distance from the boundary to the reinjection point is at most + * equal to delta. */ + delta_boundary_frt = solid_get_delta(solid_frt, &rwalk->vtx) * sqrt(DIM); + delta_boundary_bck = solid_get_delta(solid_bck, &rwalk->vtx) * sqrt(DIM); + + /* Sample a front/back reinjection steps */ + samp_reinject_step_frt_args.rng = rng; + samp_reinject_step_bck_args.rng = rng; + samp_reinject_step_frt_args.solid = solid_frt; + samp_reinject_step_bck_args.solid = solid_bck; + samp_reinject_step_frt_args.rwalk = rwalk; + samp_reinject_step_bck_args.rwalk = rwalk; + samp_reinject_step_frt_args.distance = delta_boundary_frt; + samp_reinject_step_bck_args.distance = delta_boundary_bck; + samp_reinject_step_frt_args.side = SDIS_FRONT; + samp_reinject_step_bck_args.side = SDIS_BACK; + res = XD(sample_reinjection_step_solid_solid) + (scn, + &samp_reinject_step_frt_args, + &samp_reinject_step_bck_args, + &reinject_step_frt, + &reinject_step_bck); + if(res != RES_OK) goto error; + + r = ssp_rng_canonical(rng); + if(tcr == 0) { /* No thermal contact resistance */ + /* Define the reinjection side. Note that the proba should be : Lf/Df' / + * (Lf/Df' + Lb/Db') + * + * with L<f|b> the lambda of the <front|back> side and D<f|b>' the adjusted + * delta of the <front|back> side, i.e. : D<f|b>' = + * reinject_dst_<front|back> / sqrt(DIM) + * + * Anyway, one can avoid to compute the adjusted delta by directly using the + * adjusted reinjection distance since the resulting proba is strictly the + * same; sqrt(DIM) can be simplified. */ + const double tmp_frt = lambda_frt / reinject_step_frt.distance; + const double tmp_bck = lambda_bck / reinject_step_bck.distance; + proba = tmp_frt / (tmp_frt + tmp_bck); + } else { + const double delta_frt = reinject_step_frt.distance/sqrt(DIM); + const double delta_bck = reinject_step_bck.distance/sqrt(DIM); + const double tmp_frt = lambda_frt/delta_frt; + const double tmp_bck = lambda_bck/delta_bck; + const double tmp_tcr = tcr*tmp_frt*tmp_bck; + switch(rwalk->hit_side) { + case SDIS_BACK: + /* When coming from the BACK side, the probability to be reinjected on + * the FRONT side depends on the thermal contact resistance: it + * decreases when the TCR increases (and tends to 0 when TCR -> +inf) */ + proba = tmp_frt / (tmp_frt + tmp_bck + tmp_tcr); + break; + case SDIS_FRONT: + /* Same thing when coming from the FRONT side: the probability of + * reinjection on the FRONT side depends on the thermal contact + * resistance: it increases when the TCR increases (and tends to 1 when + * the TCR -> +inf) */ + proba = (tmp_frt + tmp_tcr) / (tmp_frt + tmp_bck + tmp_tcr); + break; + default: FATAL("Unreachable code.\n"); break; + } + } + + if(r < proba) { /* Reinject in front */ + reinject_step = &reinject_step_frt; + solid = solid_frt; + } else { /* Reinject in back */ + reinject_step = &reinject_step_bck; + solid = solid_bck; + } + + /* Perform the reinjection into the solid */ + solid_reinject_args.reinjection = reinject_step; + solid_reinject_args.rng = rng; + solid_reinject_args.rwalk = rwalk; + solid_reinject_args.rwalk_ctx = ctx; + solid_reinject_args.T = T; + solid_reinject_args.fp_to_meter = scn->fp_to_meter; + res = XD(solid_reinjection)(solid, &solid_reinject_args); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_boundary_c.h b/src/sdis_heat_path_boundary_c.h @@ -0,0 +1,219 @@ +/* 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_HEAT_PATH_BOUNDARY_C_H +#define SDIS_HEAT_PATH_BOUNDARY_C_H + +#include <star/s2d.h> +#include <star/s3d.h> +#include <rsys/rsys.h> + +/* Forward declarations */ +struct rwalk_2d; +struct rwalk_3d; +struct sdis_scene; +struct sdis_medium; + +/******************************************************************************* + * Sample a reinjection step + ******************************************************************************/ +struct sample_reinjection_step_args_2d { + struct ssp_rng* rng; /* Random number generator to use */ + const struct sdis_medium* solid; /* Solid in which to reinject */ + struct rwalk_2d* rwalk; /* Current state of the random walk */ + double distance; /* Maximum Reinjection distance */ + enum sdis_side side; /* Side of the boundary to re-inject */ +}; + +struct sample_reinjection_step_args_3d { + struct ssp_rng* rng; /* Random number generator to use */ + const struct sdis_medium* solid; /* Medium in which to reinject */ + struct rwalk_3d* rwalk; /* Current random walk state */ + double distance; /* Maximum Reinjection distance */ + enum sdis_side side; /* Side of the boundary to re-inject */ +}; + +struct reinjection_step_2d { + struct s2d_hit hit; /* Intersection along the reinjection direction */ + float direction[2]; /* Reinjection direction */ + float distance; /* Reinjection distance */ +}; + +struct reinjection_step_3d { + struct s3d_hit hit; /* Intersection along the reinjection direction */ + float direction[3]; /* Reinjection direction */ + float distance; /* Reinjection distance */ +}; + +#define SAMPLE_REINJECTION_STEP_ARGS_NULL___2d \ + {NULL, NULL, NULL, -1, SDIS_SIDE_NULL__} +#define SAMPLE_REINJECTION_STEP_ARGS_NULL___3d \ + {NULL, NULL, NULL, -1, SDIS_SIDE_NULL__} +static const struct sample_reinjection_step_args_2d +SAMPLE_REINJECTION_STEP_ARGS_NULL_2d = SAMPLE_REINJECTION_STEP_ARGS_NULL___2d; +static const struct sample_reinjection_step_args_3d +SAMPLE_REINJECTION_STEP_ARGS_NULL_3d = SAMPLE_REINJECTION_STEP_ARGS_NULL___3d; + +#define REINJECTION_STEP_NULL___2d {S2D_HIT_NULL__, {0,0}, 0} +#define REINJECTION_STEP_NULL___3d {S3D_HIT_NULL__, {0,0,0}, 0} +static const struct reinjection_step_2d +REINJECTION_STEP_NULL_2d = REINJECTION_STEP_NULL___2d; +static const struct reinjection_step_3d +REINJECTION_STEP_NULL_3d = REINJECTION_STEP_NULL___3d; + +extern LOCAL_SYM res_T +sample_reinjection_step_solid_fluid_2d + (const struct sdis_scene* scn, + const struct sample_reinjection_step_args_2d* args, + struct reinjection_step_2d* step); + +extern LOCAL_SYM res_T +sample_reinjection_step_solid_fluid_3d + (const struct sdis_scene* scn, + const struct sample_reinjection_step_args_3d* args, + struct reinjection_step_3d *step); + +extern LOCAL_SYM res_T +sample_reinjection_step_solid_solid_2d + (const struct sdis_scene* scn, + const struct sample_reinjection_step_args_2d* args_front, + const struct sample_reinjection_step_args_2d* args_back, + struct reinjection_step_2d* step_front, + struct reinjection_step_2d* step_back); + +extern LOCAL_SYM res_T +sample_reinjection_step_solid_solid_3d + (const struct sdis_scene* scn, + const struct sample_reinjection_step_args_3d* args_front, + const struct sample_reinjection_step_args_3d* args_back, + struct reinjection_step_3d* step_front, + struct reinjection_step_3d* step_back); + +/******************************************************************************* + * Reinject the random walk into a solid + ******************************************************************************/ +struct solid_reinjection_args_2d { + const struct reinjection_step_2d* reinjection; /* Reinjection to do */ + struct rwalk_context* rwalk_ctx; + struct rwalk_2d* rwalk; /* Current state of the random walk */ + struct ssp_rng* rng; /* Random number generator */ + struct temperature_2d* T; + double fp_to_meter; +}; + +struct solid_reinjection_args_3d { + const struct reinjection_step_3d* reinjection; /* Reinjection to do */ + struct rwalk_context* rwalk_ctx; + struct rwalk_3d* rwalk; /* Current state of the random walk */ + struct ssp_rng* rng; /* Random number generator */ + struct temperature_3d* T; + double fp_to_meter; +}; + +#define SOLID_REINJECTION_ARGS_NULL___2d {NULL,NULL,NULL,NULL,NULL,0} +#define SOLID_REINJECTION_ARGS_NULL___3d {NULL,NULL,NULL,NULL,NULL,0} +static const struct solid_reinjection_args_2d SOLID_REINJECTION_ARGS_NULL_2d = + SOLID_REINJECTION_ARGS_NULL___2d; +static const struct solid_reinjection_args_3d SOLID_REINJECTION_ARGS_NULL_3d = + SOLID_REINJECTION_ARGS_NULL___3d; + +extern LOCAL_SYM res_T +solid_reinjection_2d + (struct sdis_medium* solid, + struct solid_reinjection_args_2d* args); + +extern LOCAL_SYM res_T +solid_reinjection_3d + (struct sdis_medium* solid, + struct solid_reinjection_args_3d* args); + +/******************************************************************************* + * Boundary sub-paths + ******************************************************************************/ +extern LOCAL_SYM res_T +solid_boundary_with_flux_path_2d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + const double phi, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* T); + +extern LOCAL_SYM res_T +solid_boundary_with_flux_path_3d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + const double phi, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* T); + +extern LOCAL_SYM res_T +solid_fluid_boundary_picard1_path_2d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* T); + +extern LOCAL_SYM res_T +solid_fluid_boundary_picard1_path_3d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* T); + +extern LOCAL_SYM res_T +solid_fluid_boundary_picardN_path_2d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* T); + +extern LOCAL_SYM res_T +solid_fluid_boundary_picardN_path_3d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* T); + +extern LOCAL_SYM res_T +solid_solid_boundary_path_2d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* T); + +extern LOCAL_SYM res_T +solid_solid_boundary_path_3d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + const struct sdis_interface_fragment* frag, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* T); + +#endif /* SDIS_HEAT_PATH_BOUNDARY_C_H */ 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 @@ -25,6 +25,63 @@ #include "sdis_Xd_begin.h" /******************************************************************************* + * Non generic helper function + ******************************************************************************/ +#ifndef SDIS_HEAT_PATH_CONDUCTIVE_XD_H +#define SDIS_HEAT_PATH_CONDUCTIVE_XD_H + +static res_T +check_solid_constant_properties + (struct sdis_device* dev, + const int evaluate_green, + const struct solid_props* props_ref, + const struct solid_props* props) +{ + res_T res = RES_OK; + ASSERT(dev && props_ref && props); + + if(props_ref->lambda != props->lambda) { + log_err(dev, + "%s: invalid thermal conductivity. One assumes a constant conductivity " + "for the whole solid.\n", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + + if(props_ref->rho != props->rho) { + log_err(dev, + "%s: invalid volumic mass. One assumes a constant volumic mass for " + "the whole solid.\n", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + + if(props_ref->cp != props->cp) { + log_err(dev, + "%s: invalid calorific capacity. One assumes a constant calorific " + "capacity for the whole solid.\n", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + + if(evaluate_green && props_ref->power != props->power) { + log_err(dev, + "%s: invalid volumic power. When estimating the green function, a " + "constant volumic power is assumed for the whole solid.\n", + FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +#endif /* SDIS_HEAT_PATH_CONDUCTIVE_XD_H */ + +/******************************************************************************* * Helper functions ******************************************************************************/ /* Sample the next direction to walk toward and compute the distance to travel. @@ -193,20 +250,155 @@ error: goto exit; } + +/******************************************************************************* + * Handle the volumic power at a given diffusive step + ******************************************************************************/ +struct XD(handle_volumic_power_args) { + /* Forward/backward direction of the sampled diffusive step */ + const float* dir0; + const float* dir1; + + /* Forward/backward intersections along the sampled diffusive step */ + const struct sXd(hit)* hit0; + const struct sXd(hit)* hit1; + + /* Physical properties */ + double power; /* Volumic power */ + double lambda; /* Conductivity */ + + float delta_solid; /* Challenged length of a diffusive step */ + float delta; /* Current length of the current diffusive step */ + + size_t picard_order; +}; +static const struct XD(handle_volumic_power_args) +XD(HANDLE_VOLUMIC_POWER_ARGS_NULL) = { + NULL, NULL, NULL, NULL, -1, -1, -1, -1, 0 +}; + +static INLINE int +XD(check_handle_volumic_power_args) + (const struct XD(handle_volumic_power_args)* args) +{ + ASSERT(args); + return args + && args->dir0 + && args->dir1 + && args->hit0 + && args->hit1 + && args->lambda >= 0 + && args->delta_solid > 0 + && args->delta >= 0 + && args->delta_solid >= 0 + && args->picard_order > 0; +} + +static res_T +XD(handle_volumic_power) + (const struct sdis_scene* scn, + const struct XD(handle_volumic_power_args)* args, + double* out_power_term, + struct XD(temperature)* T) +{ + double power_term = 0; + res_T res = RES_OK; + ASSERT(scn && out_power_term && T && XD(check_handle_volumic_power_args)(args)); + + /* No volumic power. Do nothing */ + if(args->power == SDIS_VOLUMIC_POWER_NONE) goto exit; + + /* Check that picardN is not enabled when a volumic power is set since in + * this situation the upper bound of the Monte-Carlo weight required by + * picardN cannot be known */ + if(args->picard_order > 1) { + log_err(scn->dev, + "%s: invalid not null volumic power '%g' kg/m^3. Could not manage a " + "volumic power when the picard order is not equal to 1; Picard order is " + "currently set to %lu.\n", + FUNC_NAME, args->power, (unsigned long)args->picard_order); + res = RES_BAD_ARG; + goto error; + } + + /* No forward/backward intersection along the sampled direction */ + if(SXD_HIT_NONE(args->hit0) && SXD_HIT_NONE(args->hit1)) { + const double delta_in_meter = args->delta * scn->fp_to_meter; + power_term = delta_in_meter * delta_in_meter / (2.0 * DIM * args->lambda); + T->value += args->power * power_term; + + /* An intersection along this diffusive step is find. Use it to statically + * correct the power term currently registered */ + } else { + const double delta_s_adjusted = args->delta_solid * RAY_RANGE_MAX_SCALE; + const double delta_s_in_meter = args->delta_solid * scn->fp_to_meter; + double h; + double h_in_meter; + double cos_U_N; + float N[DIM]; + + if(args->delta == args->hit0->distance) { + fX(normalize)(N, args->hit0->normal); + cos_U_N = fX(dot)(args->dir0, N); + + } else { + ASSERT(args->delta == args->hit1->distance); + fX(normalize)(N, args->hit1->normal); + cos_U_N = fX(dot)(args->dir1, N); + } + + h = args->delta * fabs(cos_U_N); + h_in_meter = h * scn->fp_to_meter; + + /* The regular power term */ + power_term = h_in_meter * h_in_meter / (2.0*args->lambda); + + /* Add the power corrective term. Be careful to use the adjusted + * delta_solid to correctly handle the RAY_RANGE_MAX_SCALE factor in the + * computation of the limit angle. But keep going with the unmodified + * delta_solid in the corrective term since it was the one that was + * "wrongly" used in the previous step and that must be corrected. */ + if(h == delta_s_adjusted) { + power_term += + -(delta_s_in_meter * delta_s_in_meter) / (2.0*DIM*args->lambda); + + } else if(h < delta_s_adjusted) { + const double sin_a = h / delta_s_adjusted; +#if DIM==2 + /* tmp = sin(2a) / (PI - 2*a) */ + const double tmp = sin_a * sqrt(1 - sin_a*sin_a) / acos(sin_a); + power_term += + -(delta_s_in_meter * delta_s_in_meter) / (4.0*args->lambda) * tmp; +#else + const double tmp = (sin_a*sin_a*sin_a - sin_a) / (1-sin_a); + power_term += + (delta_s_in_meter * delta_s_in_meter) / (6.0*args->lambda) * tmp; +#endif + } + T->value += args->power * power_term; + } + +exit: + *out_power_term = power_term; + return res; +error: + goto exit; +} + /******************************************************************************* * Local function ******************************************************************************/ res_T XD(conductive_path) (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct XD(rwalk)* rwalk, struct ssp_rng* rng, struct XD(temperature)* T) { double position_start[DIM]; - double green_power_factor = 0; - double power_ref = SDIS_VOLUMIC_POWER_NONE; + struct solid_props props_ref = SOLID_PROPS_NULL; + double green_power_term = 0; struct sdis_medium* mdm; size_t istep = 0; /* Help for debug */ res_T res = RES_OK; @@ -217,7 +409,7 @@ XD(conductive_path) /* Check the random walk consistency */ res = scene_get_medium_in_closed_boundaries(scn, rwalk->vtx.P, &mdm); if(res != RES_OK || mdm != rwalk->mdm) { - log_warn(scn->dev, "%s: invalid solid random walk. " + log_err(scn->dev, "%s: invalid solid random walk. " "Unexpected medium at {%g, %g, %g}.\n", FUNC_NAME, SPLIT3(rwalk->vtx.P)); res = RES_BAD_OP_IRRECOVERABLE; goto error; @@ -225,27 +417,41 @@ XD(conductive_path) /* Save the submitted position */ dX(set)(position_start, rwalk->vtx.P); - if(ctx->green_path) { - /* Retrieve the power of the medium. Use it to check that it is effectively - * constant along the random walk */ - power_ref = solid_get_volumic_power(mdm, &rwalk->vtx); - } + /* Retrieve the solid properties at the current position. Use them to verify + * that those that are supposed to be constant by the conductive random walk + * remain the same. Note that we take care of the same constraints on the + * solid reinjection since once reinjected, the position of the random walk + * is that at the beginning of the conductive random walkh. Thus, after a + * reinjection, the next line retrieves the properties of the reinjection + * position. By comparing them to the properties along the random walk, we + * thus verify that the properties are constant throughout the random walk + * with respect to the properties of the reinjected position. */ + solid_get_properties(mdm, &rwalk->vtx, &props_ref); do { /* Solid random walk */ + struct XD(handle_volumic_power_args) handle_volpow_args = + XD(HANDLE_VOLUMIC_POWER_ARGS_NULL); struct sXd(hit) hit0, hit1; - double lambda; /* Thermal conductivity */ - double tmp; - double power_factor = 0; - double power; - float delta, delta_solid; /* Random walk numerical parameter */ + struct solid_props props = SOLID_PROPS_NULL; + double power_term = 0; + double mu; + float delta; /* Random walk numerical parameter */ + double delta_m; float dir0[DIM], dir1[DIM]; float org[DIM]; + /* Fetch solid properties */ + res = solid_get_properties(mdm, &rwalk->vtx, &props); + if(res != RES_OK) goto error; + + res = check_solid_constant_properties + (scn->dev, ctx->green_path != NULL, &props_ref, &props); + if(res != RES_OK) goto error; + /* Check the limit condition * REVIEW Rfo: This can be a bug if the random walk comes from a boundary */ - tmp = solid_get_temperature(mdm, &rwalk->vtx); - if(tmp >= 0) { - T->value += tmp; + if(props.temperature >= 0) { + T->value += props.temperature; T->done = 1; if(ctx->green_path) { @@ -261,88 +467,36 @@ XD(conductive_path) break; } - /* Fetch solid properties */ - delta_solid = (float)solid_get_delta(mdm, &rwalk->vtx); - lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); - power = solid_get_volumic_power(mdm, &rwalk->vtx); - - if(ctx->green_path && power_ref != power) { - log_err(scn->dev, - "%s: invalid non constant volumic power term. Expecting a constant " - "volumic power in time and space on green function estimation.\n", - FUNC_NAME); - res = RES_BAD_ARG; - goto error; - } - fX_set_dX(org, rwalk->vtx.P); /* Sample the direction to walk toward and compute the distance to travel */ - res = XD(sample_next_step_robust)(scn, mdm, rng, rwalk->vtx.P, delta_solid, - dir0, dir1, &hit0, &hit1, &delta); + res = XD(sample_next_step_robust)(scn, mdm, rng, rwalk->vtx.P, + (float)props.delta, dir0, dir1, &hit0, &hit1, &delta); if(res != RES_OK) goto error; /* Add the volumic power density to the measured temperature */ - if(power != SDIS_VOLUMIC_POWER_NONE) { - if((S3D_HIT_NONE(&hit0) && S3D_HIT_NONE(&hit1))) { /* Hit nothing */ - const double delta_in_meter = delta * scn->fp_to_meter; - power_factor = delta_in_meter * delta_in_meter / (2.0 * DIM * lambda); - T->value += power * power_factor; - } else { - const double delta_s_adjusted = delta_solid * RAY_RANGE_MAX_SCALE; - const double delta_s_in_meter = delta_solid * scn->fp_to_meter; - double h; - double h_in_meter; - double cos_U_N; - float N[DIM]; - - if(delta == hit0.distance) { - fX(normalize)(N, hit0.normal); - cos_U_N = fX(dot)(dir0, N); - } else { - ASSERT(delta == hit1.distance); - fX(normalize)(N, hit1.normal); - cos_U_N = fX(dot)(dir1, N); - } - - h = delta * fabs(cos_U_N); - h_in_meter = h * scn->fp_to_meter; - - /* The regular power term at wall */ - tmp = h_in_meter * h_in_meter / (2.0 * lambda); - - /* Add the power corrective term. Be careful to use the adjusted - * delta_solid to correctly handle the RAY_RANGE_MAX_SCALE factor in - * the computation of the limit angle. But keep going with the - * unmodified delta_solid in the corrective term since it was the one - * that was "wrongly" used in the previous step and that must be - * corrected. */ - if(h == delta_s_adjusted) { - tmp += -(delta_s_in_meter * delta_s_in_meter)/(2.0*DIM*lambda); - } else if(h < delta_s_adjusted) { - const double sin_a = h / delta_s_adjusted; -#if DIM==2 - /* tmp1 = sin(2a) / (PI - 2*a) */ - const double tmp1 = sin_a * sqrt(1 - sin_a*sin_a)/acos(sin_a); - tmp += -(delta_s_in_meter * delta_s_in_meter)/(4.0*lambda) * tmp1; -#else - const double tmp1 = (sin_a*sin_a*sin_a - sin_a)/ (1-sin_a); - tmp += (delta_s_in_meter * delta_s_in_meter)/(6.0*lambda) * tmp1; -#endif - } - power_factor = tmp; - T->value += power * power_factor; - } - } + handle_volpow_args.dir0 = dir0; + handle_volpow_args.dir1 = dir1; + handle_volpow_args.hit0 = &hit0; + handle_volpow_args.hit1 = &hit1; + handle_volpow_args.power = props.power; + handle_volpow_args.lambda = props.lambda; + handle_volpow_args.delta_solid = (float)props.delta; + handle_volpow_args.delta = delta; + handle_volpow_args.picard_order = get_picard_order(ctx); + res = XD(handle_volumic_power)(scn, &handle_volpow_args, &power_term, T); + if(res != RES_OK) goto error; /* Register the power term for the green function. Delay its registration * until the end of the conductive path, i.e. the path is valid */ - if(ctx->green_path && power != SDIS_VOLUMIC_POWER_NONE) { - green_power_factor += power_factor; + if(ctx->green_path && props.power != SDIS_VOLUMIC_POWER_NONE) { + green_power_term += power_term; } /* Rewind the time */ - res = XD(time_rewind)(rwalk->mdm, rng, delta * scn->fp_to_meter, ctx, rwalk, T); + delta_m = delta * scn->fp_to_meter; + mu = (2*DIM*props.lambda)/(props.rho*props.cp*delta_m*delta_m); + res = XD(time_rewind)(mu, props.t0, rng, rwalk, ctx, T); if(res != RES_OK) goto error; if(T->done) break; /* Limit condition was reached */ @@ -359,8 +513,8 @@ XD(conductive_path) XD(move_pos)(rwalk->vtx.P, dir0, delta); /* Register the new vertex against the heat path */ - res = register_heat_vertex - (ctx->heat_path, &rwalk->vtx, T->value, SDIS_HEAT_VERTEX_CONDUCTION); + res = register_heat_vertex(ctx->heat_path, &rwalk->vtx, T->value, + SDIS_HEAT_VERTEX_CONDUCTION, (int)ctx->nbranchings); if(res != RES_OK) goto error; ++istep; @@ -369,9 +523,9 @@ XD(conductive_path) } while(SXD_HIT_NONE(&rwalk->hit)); /* Register the power term for the green function */ - if(ctx->green_path && power_ref != SDIS_VOLUMIC_POWER_NONE) { + if(ctx->green_path && props_ref.power != SDIS_VOLUMIC_POWER_NONE) { res = green_path_add_power_term - (ctx->green_path, rwalk->mdm, &rwalk->vtx, green_power_factor); + (ctx->green_path, rwalk->mdm, &rwalk->vtx, green_power_term); if(res != RES_OK) goto error; } 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 @@ -24,12 +24,51 @@ #include "sdis_Xd_begin.h" /******************************************************************************* + * Non generic helper functions + ******************************************************************************/ +#ifndef SDIS_HEAT_PATH_CONVECTIVE_XD_H +#define SDIS_HEAT_PATH_CONVECTIVE_XD_H + +static res_T +check_fluid_constant_properties + (struct sdis_device* dev, + const struct fluid_props* props_ref, + const struct fluid_props* props) +{ + res_T res = RES_OK; + ASSERT(dev && props_ref && props); + + if(props_ref->rho != props->rho) { + log_err(dev, + "%s: invalid volumic mass. One assumes a constant volumic mass for " + "the whole fluid.\n", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + + if(props_ref->cp != props->cp) { + log_err(dev, + "%s: invalid calorific capacity. One assumes a constant calorific " + "capacity for the whole fluid.\n", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +#endif /* SDIS_HEAT_PATH_CONVECTIVE_XD_H */ + +/******************************************************************************* * Helper functions ******************************************************************************/ static res_T XD(register_heat_vertex_in_fluid) (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct XD(rwalk)* rwalk, const double weight) { @@ -60,88 +99,105 @@ XD(register_heat_vertex_in_fluid) fX(add)(pos, org, fX(mulf)(dir, dir, dst)); dX_set_fX(vtx.P, pos); - return register_heat_vertex - (ctx->heat_path, &vtx, weight, SDIS_HEAT_VERTEX_CONVECTION); + return register_heat_vertex(ctx->heat_path, &vtx, weight, + SDIS_HEAT_VERTEX_CONVECTION, (int)ctx->nbranchings); } -/******************************************************************************* - * Local functions - ******************************************************************************/ -res_T -XD(convective_path) +static res_T +XD(handle_known_fluid_temperature) (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct XD(rwalk)* rwalk, - struct ssp_rng* rng, struct XD(temperature)* T) { - struct sXd(attrib) attr_P, attr_N; - const struct sdis_interface* interf; - const struct enclosure* enc; - unsigned enc_ids[2]; - unsigned enc_id; - double rho; /* Volumic mass */ - double hc; /* Convection coef */ - double cp; /* Calorific capacity */ - double tmp; - double r; - int path_started_in_fluid; -#if SDIS_XD_DIMENSION == 2 - float st; -#else - float st[2]; -#endif + double temperature; + int known_temperature; res_T res = RES_OK; - (void)rng, (void)ctx; - ASSERT(scn && ctx && rwalk && rng && T); - ASSERT(rwalk->mdm->type == SDIS_FLUID); + ASSERT(scn && ctx && rwalk && T); + ASSERT(sdis_medium_get_type(rwalk->mdm) == SDIS_FLUID); - tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); - if(tmp >= 0) { /* T is known. */ - T->value += tmp; - T->done = 1; + temperature = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); - if(ctx->green_path) { - res = green_path_set_limit_vertex - (ctx->green_path, rwalk->mdm, &rwalk->vtx, rwalk->elapsed_time); - if(res != RES_OK) goto error; - } + /* Check if the temperature is known */ + known_temperature = temperature >= 0; + if(!known_temperature) goto exit; - res = XD(register_heat_vertex_in_fluid)(scn, ctx, rwalk, T->value); - if(res != RES_OK) goto error; + T->value += temperature; + T->done = 1; - goto exit; + if(ctx->green_path) { + res = green_path_set_limit_vertex + (ctx->green_path, rwalk->mdm, &rwalk->vtx, rwalk->elapsed_time); + if(res != RES_OK) goto error; } - path_started_in_fluid = SXD_HIT_NONE(&rwalk->hit); - if(path_started_in_fluid) { /* The path begins in the fluid */ - const float range[2] = {FLT_MIN, FLT_MAX}; - float dir[DIM] = {0}; - float org[DIM]; + res = XD(register_heat_vertex_in_fluid)(scn, ctx, rwalk, T->value); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} - dir[DIM-1] = 1; - fX_set_dX(org, rwalk->vtx.P); +static res_T +XD(handle_convective_path_startup) + (struct sdis_scene* scn, + struct XD(rwalk)* rwalk, + int* path_starts_in_fluid) +{ + const float range[2] = {FLT_MIN, FLT_MAX}; + float dir[DIM] = {0}; + float org[DIM] = {0}; + res_T res = RES_OK; + ASSERT(scn && rwalk && path_starts_in_fluid); + ASSERT(sdis_medium_get_type(rwalk->mdm) == SDIS_FLUID); - /* Init the path hit field required to define the current enclosure and - * fetch the interface data */ - SXD(scene_view_trace_ray(scn->sXd(view), org, dir, range, NULL, &rwalk->hit)); + *path_starts_in_fluid = SXD_HIT_NONE(&rwalk->hit); + if(*path_starts_in_fluid == 0) goto exit; /* Nothing to do */ - if(SXD_HIT_NONE(&rwalk->hit)) { - log_err(scn->dev, -"%s: the position %g %g %g lies in the surrounding fluid whose temperature must \n" -"be known.\n", FUNC_NAME, SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP; - goto error; - } + dir[DIM-1] = 1; + fX_set_dX(org, rwalk->vtx.P); - rwalk->hit_side = fX(dot)(rwalk->hit.normal, dir) < 0 ? SDIS_FRONT : SDIS_BACK; + /* Init the path hit field required to define the current enclosure and + * fetch the interface data */ + SXD(scene_view_trace_ray(scn->sXd(view), org, dir, range, NULL, &rwalk->hit)); + if(SXD_HIT_NONE(&rwalk->hit)) { + log_err(scn->dev, + "%s: the position %g %g %g lies in the surrounding fluid whose " + "temperature must be known.\n", FUNC_NAME, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP; + goto error; } + rwalk->hit_side = fX(dot)(rwalk->hit.normal, dir) < 0 ? SDIS_FRONT : SDIS_BACK; + +exit: + return res; +error: + goto exit; +} + +static res_T +XD(fetch_fluid_enclosure) + (struct sdis_scene* scn, + struct XD(rwalk)* rwalk, + const struct enclosure** out_enclosure) +{ + const struct sdis_interface* interf; + const struct enclosure* enc; + unsigned enc_ids[2]; + unsigned enc_id; + res_T res = RES_OK; + ASSERT(scn && rwalk && out_enclosure); + ASSERT(sdis_medium_get_type(rwalk->mdm) == SDIS_FLUID); + ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + /* Fetch the current interface and its associated enclosures */ interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); scene_get_enclosure_ids(scn, rwalk->hit.prim.prim_id, enc_ids); - /* Define the enclosure identifier of the current medium */ + /* Find the enclosure identifier of the current medium */ ASSERT(interf->medium_front != interf->medium_back); if(rwalk->mdm == interf->medium_front) { enc_id = enc_ids[0]; @@ -159,91 +215,101 @@ XD(convective_path) * the external enclosure. In this situation unknown temperature is * forbidden. */ log_err(scn->dev, -"%s: invalid enclosure. The surrounding fluid has an unset temperature.\n", + "%s: invalid enclosure. The surrounding fluid has an unset temperature.\n", FUNC_NAME); res = RES_BAD_ARG; goto error; } +exit: + *out_enclosure = enc; + return res; +error: + enc = NULL; + goto exit; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +XD(convective_path) + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + struct sXd(attrib) attr_P, attr_N; + struct fluid_props props_ref = FLUID_PROPS_NULL; + const struct sdis_interface* interf; + const struct enclosure* enc; + double r; +#if SDIS_XD_DIMENSION == 2 + float st; +#else + float st[2]; +#endif + int path_starts_in_fluid; + res_T res = RES_OK; + (void)rng, (void)ctx; + ASSERT(scn && ctx && rwalk && rng && T); + ASSERT(rwalk->mdm->type == SDIS_FLUID); + + res = XD(handle_known_fluid_temperature)(scn, ctx, rwalk, T); + if(res != RES_OK) goto error; + if(T->done) goto exit; /* The fluid temperature is known */ + + /* Setup the missing random walk member variables when the convective path + * starts from the fluid */ + res = XD(handle_convective_path_startup)(scn, rwalk, &path_starts_in_fluid); + if(res != RES_OK) goto error; + + res = XD(fetch_fluid_enclosure)(scn, rwalk, &enc); + if(res != RES_OK) goto error; + + /* Retrieve the fluid properties at the current position. Use them to verify + * that those that are supposed to be constant by the convective random walk + * remain the same. */ + res = fluid_get_properties(rwalk->mdm, &rwalk->vtx, &props_ref); + if(res != RES_OK) goto error; + /* The hc upper bound can be 0 if h is uniformly 0. In that case the result * is the initial condition. */ if(enc->hc_upper_bound == 0) { - /* Cannot be in the fluid without starting there. */ - ASSERT(path_started_in_fluid); - - rwalk->vtx.time = fluid_get_t0(rwalk->mdm); - tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - goto exit; + ASSERT(path_starts_in_fluid); /* Cannot be in the fluid without starting there. */ + rwalk->vtx.time = props_ref.t0; + res = XD(handle_known_fluid_temperature)(scn, ctx, rwalk, T); + if(res != RES_OK) goto error; + if(T->done) { + goto exit; /* Stop the random walk */ + } else { + log_err(scn->dev, "%s: undefined initial condition.", FUNC_NAME); + res = RES_BAD_OP; + goto error; } - - /* At t=t0, the initial condition should have been reached. */ - log_err(scn->dev, - "%s: undefined initial condition. " - "Time is %g but the temperature remains unknown.\n", - FUNC_NAME, rwalk->vtx.time); - res = RES_BAD_OP; - goto error; } /* Sample time until init condition is reached or a true convection occurs. */ for(;;) { struct sdis_interface_fragment frag; struct sXd(primitive) prim; - double mu, tau, t0; + struct fluid_props props = FLUID_PROPS_NULL; + double hc; + double mu; + + /* Fetch fluid properties */ + res = fluid_get_properties(rwalk->mdm, &rwalk->vtx, &props); + if(res != RES_OK) goto error; - /* Fetch other physical properties. */ - cp = fluid_get_calorific_capacity(rwalk->mdm, &rwalk->vtx); - rho = fluid_get_volumic_mass(rwalk->mdm, &rwalk->vtx); - t0 = fluid_get_t0(rwalk->mdm); /* Limit time */ + res = check_fluid_constant_properties(scn->dev, &props_ref, &props); + if(res != RES_OK) goto error; /* Sample the time using the upper bound. */ - mu = enc->hc_upper_bound / (rho * cp) * enc->S_over_V; - tau = ssp_ran_exp(rng, mu); - - /* Increment the elapsed time */ - ASSERT(rwalk->vtx.time > t0); - rwalk->elapsed_time += MMIN(tau, rwalk->vtx.time - t0); - - if(rwalk->vtx.time != INF) { - rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); /* Time rewind */ - - /* Register the new vertex against the heat path */ - res = XD(register_heat_vertex_in_fluid)(scn, ctx, rwalk, T->value); - if(res != RES_OK) goto error; - - if(rwalk->vtx.time == t0) { - /* Check the initial condition. */ - tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - if(ctx->heat_path) { - /* Update the registered vertex data */ - struct sdis_heat_vertex* vtx; - vtx = heat_path_get_last_vertex(ctx->heat_path); - vtx->time = rwalk->vtx.time; - vtx->weight = T->value; - } - - if(ctx->green_path) { - res = green_path_set_limit_vertex(ctx->green_path, rwalk->mdm, - &rwalk->vtx, rwalk->elapsed_time); - if(res != RES_OK) goto error; - } - goto exit; - } - /* The initial condition should have been reached. */ - log_err(scn->dev, - "%s: undefined initial condition. " - "Time is %g but the temperature remains unknown.\n", - FUNC_NAME, t0); - res = RES_BAD_OP; - goto error; - } - } + mu = enc->hc_upper_bound / (props.rho * props.cp) * enc->S_over_V; + res = XD(time_rewind)(mu, props.t0, rng, rwalk, ctx, T); + if(res != RES_OK) goto error; + if(T->done) break; /* Limit condition was reached */ /* Uniformly sample the enclosure. */ #if DIM == 2 @@ -283,8 +349,8 @@ XD(convective_path) } /* Register the new vertex against the heat path */ - res = register_heat_vertex - (ctx->heat_path, &rwalk->vtx, T->value, SDIS_HEAT_VERTEX_CONVECTION); + res = register_heat_vertex(ctx->heat_path, &rwalk->vtx, T->value, + SDIS_HEAT_VERTEX_CONVECTION, (int)ctx->nbranchings); if(res != RES_OK) goto error; /* Setup the fragment of the sampled position into the enclosure. */ 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 @@ -33,7 +33,7 @@ res_T XD(trace_radiative_path) (struct sdis_scene* scn, const float ray_dir[3], - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct XD(rwalk)* rwalk, struct ssp_rng* rng, struct XD(temperature)* T) @@ -42,12 +42,16 @@ XD(trace_radiative_path) * are assumed to be extruded to the infinity along the Z dimension. */ float N[3] = {0, 0, 0}; float dir[3] = {0, 0, 0}; + int branch_id; res_T res = RES_OK; ASSERT(scn && ray_dir && ctx && rwalk && rng && T); f3_set(dir, ray_dir); + /* (int)ctx->nbranchings < 0 <=> Beginning of the realisation */ + branch_id = MMAX((int)ctx->nbranchings, 0); + /* Launch the radiative random walk */ for(;;) { const struct sdis_interface* interf = NULL; @@ -74,8 +78,8 @@ XD(trace_radiative_path) #endif if(SXD_HIT_NONE(&rwalk->hit)) { /* Fetch the ambient radiative temperature */ rwalk->hit_side = SDIS_SIDE_NULL__; - if(ctx->Tarad >= 0) { - T->value += ctx->Tarad; + if(scn->trad.temperature >= 0) { + T->value += scn->trad.temperature; T->done = 1; if(ctx->green_path) { @@ -86,12 +90,14 @@ XD(trace_radiative_path) if(ctx->heat_path) { const float empirical_dst = 0.1f; struct sdis_rwalk_vertex vtx; + + vtx = rwalk->vtx; vtx.P[0] += dir[0] * empirical_dst; vtx.P[1] += dir[1] * empirical_dst; vtx.P[2] += dir[2] * empirical_dst; - res = register_heat_vertex - (ctx->heat_path, &vtx, T->value, SDIS_HEAT_VERTEX_RADIATIVE); + res = register_heat_vertex(ctx->heat_path, &vtx, T->value, + SDIS_HEAT_VERTEX_RADIATIVE, branch_id); if(res != RES_OK) goto error; } break; @@ -104,7 +110,7 @@ XD(trace_radiative_path) "such temperature, one has to setup a valid ambient radiative " "temperature, i.e. it must be greater or equal to 0.\n", FUNC_NAME, - ctx->Tarad, + scn->trad.temperature, SPLIT3(rwalk->vtx.P)); res = RES_BAD_OP; goto error; @@ -119,8 +125,8 @@ XD(trace_radiative_path) XD(move_pos)(rwalk->vtx.P, dir, rwalk->hit.distance); /* Register the random walk vertex against the heat path */ - res = register_heat_vertex - (ctx->heat_path, &rwalk->vtx, T->value, SDIS_HEAT_VERTEX_RADIATIVE); + res = register_heat_vertex(ctx->heat_path, &rwalk->vtx, T->value, + SDIS_HEAT_VERTEX_RADIATIVE, branch_id); if(res != RES_OK) goto error; /* Fetch the new interface and setup the hit fragment */ @@ -188,7 +194,7 @@ error: res_T XD(radiative_path) (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct XD(rwalk)* rwalk, struct ssp_rng* rng, struct XD(temperature)* T) 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,18 +67,20 @@ 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); } FOR_EACH(i, 0, 2) { switch(type[i]) { case SDIS_SOLID: - if(shaders[i]->emissivity || shaders[i]->specular_fraction) { + if(shaders[i]->emissivity + || shaders[i]->specular_fraction + || shaders[i]->reference_temperature) { log_warn(dev, - "%s: the interface side toward a solid can neither have the " - "emissivity nor the specular_fraction properties. The shader's " - " pointer functions for these attributes should be NULL.\n", + "%s: the interface side toward a solid cannot have an emissivity, " + "a specular_fraction or a reference temperature. The shader's " + "pointer functions for these attributes should be NULL.\n", caller_name); } break; 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 @@ -167,5 +167,21 @@ interface_side_get_specular_fraction ? shader->specular_fraction(frag, interf->data) : 0; } +static INLINE double +interface_side_get_reference_temperature + (const struct sdis_interface* interf, + const struct sdis_interface_fragment* frag) +{ + const struct sdis_interface_side_shader* shader; + ASSERT(interf && frag); + switch(frag->side) { + case SDIS_FRONT: shader = &interf->shader.front; break; + case SDIS_BACK: shader = &interf->shader.back; break; + default: FATAL("Unreachable code\n"); break; + } + return shader->reference_temperature + ? shader->reference_temperature(frag, interf->data) : -1; +} + #endif /* SDIS_INTERFACE_C_H */ 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 @@ -40,7 +40,7 @@ check_solid_shader(const struct sdis_solid_shader* shader) return shader->calorific_capacity && shader->thermal_conductivity && shader->volumic_mass - && shader->delta_solid + && shader->delta && shader->temperature && 0 <= shader->t0 && shader->t0 < INF; } 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 @@ -17,6 +17,7 @@ #define SDIS_MEDIUM_C_H #include "sdis.h" +#include "sdis_log.h" #include <rsys/free_list.h> #include <rsys/math.h> @@ -36,40 +37,93 @@ struct sdis_medium { struct sdis_device* dev; }; -static FINLINE unsigned -medium_get_id(const struct sdis_medium* mdm) -{ - ASSERT(mdm); - return mdm->id.index; -} +struct fluid_props { + double rho; /* Volumic mass */ + double cp; /* Calorific capacity */ + double temperature; + double t0; /* Initial time */ +}; +#define FLUID_PROPS_NULL__ {0,0,0,0} +static const struct fluid_props FLUID_PROPS_NULL = FLUID_PROPS_NULL__; + +struct solid_props { + double lambda; /* Conductivity */ + double rho; /* Volumic mass */ + double cp; /* Calorific capacity */ + double delta; /* Random walk step */ + double power; /* Volumic power */ + double temperature; + double t0; /* Initial time */ +}; +#define SOLID_PROPS_NULL__ {0,0,0,0,0,0,0} +static const struct solid_props SOLID_PROPS_NULL = SOLID_PROPS_NULL__; + +#define MDM_TYPE(Mdm) CONCAT(MDM_TYPE_, Mdm) +#define MDM_TYPE_solid SDIS_SOLID +#define MDM_TYPE_fluid SDIS_FLUID + +#define PROP_STR(Prop) CONCAT(PROP_STR_, Prop) +#define PROP_STR_calorific_capacity "calorific capacity" +#define PROP_STR_conductivity "conductivity" +#define PROP_STR_delta "delta" +#define PROP_STR_temperature "temperature" +#define PROP_STR_thermal_conductivity "thermal conductivity" +#define PROP_STR_volumic_mass "volumic mass" +#define PROP_STR_volumic_power "volumic power" + +#define DEFINE_MDM_GET_PROP_FUNC(Mdm, Prop) \ + static INLINE double \ + Mdm##_get_##Prop \ + (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) \ + { \ + ASSERT(mdm && mdm->type == MDM_TYPE(Mdm)); \ + return mdm->shader.Mdm.Prop(vtx, mdm->data); \ + } + +#define DEFINE_MDM_CHK_PROP_FUNC(Mdm, Prop, Low, Upp, LowIsInc, UppIsInc) \ + static INLINE res_T \ + Mdm##_check_##Prop \ + (struct sdis_device* dev, \ + const double val, /* Value of the property */ \ + const double pos[], /* Position at which the property was queried */ \ + const double time) /* Time at which the property was queried */ \ + { \ + const int low_test = LowIsInc ? Low <= val : Low < val; \ + const int upp_test = UppIsInc ? Upp >= val : Upp > val; \ + const char low_char = LowIsInc ? '[' : ']'; \ + const char upp_char = UppIsInc ? ']' : '['; \ + ASSERT(dev && pos); \ + \ + if(!low_test || !upp_test) { \ + log_err(dev, \ + "invalid "STR(Mdm)" "PROP_STR(Prop)" '%g': it must be in %c%g, %g%c " \ + "-- position=%g, %g, %g; time=%g\n", \ + val, low_char, (double)Low, (double)Upp, upp_char, SPLIT3(pos), time); \ + return RES_BAD_ARG; \ + } \ + return RES_OK; \ + } /******************************************************************************* * Fluid local functions ******************************************************************************/ -static INLINE double -fluid_get_calorific_capacity - (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) -{ - ASSERT(mdm && mdm->type == SDIS_FLUID); - return mdm->shader.fluid.calorific_capacity(vtx, mdm->data); -} +DEFINE_MDM_CHK_PROP_FUNC(fluid, calorific_capacity, 0, INF, 0, 1) +DEFINE_MDM_CHK_PROP_FUNC(fluid, volumic_mass, 0, INF, 0, 1) +DEFINE_MDM_CHK_PROP_FUNC(fluid, temperature, 0, INF, 1, 1) -static INLINE double -fluid_get_volumic_mass - (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) +static INLINE res_T +fluid_check_t0(struct sdis_device* dev, const double t0) { - ASSERT(mdm && mdm->type == SDIS_FLUID); - return mdm->shader.fluid.volumic_mass(vtx, mdm->data); + if(t0 < 0) { + log_err(dev, "invalid negative initial time '%g'.\n", t0); + return RES_BAD_ARG; + } + return RES_OK; } -static INLINE double -fluid_get_temperature - (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) -{ - ASSERT(mdm && mdm->type == SDIS_FLUID); - /*ASSERT(vtx->time >= mdm->shader.fluid.t0);*/ - return mdm->shader.fluid.temperature(vtx, mdm->data); -} +DEFINE_MDM_GET_PROP_FUNC(fluid, calorific_capacity) +DEFINE_MDM_GET_PROP_FUNC(fluid, volumic_mass) +DEFINE_MDM_GET_PROP_FUNC(fluid, temperature) static INLINE double fluid_get_t0(const struct sdis_medium* mdm) @@ -79,44 +133,76 @@ fluid_get_t0(const struct sdis_medium* mdm) return mdm->shader.fluid.t0; } -/******************************************************************************* - * Solid local functions - ******************************************************************************/ -static INLINE double -solid_get_calorific_capacity - (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) +static INLINE res_T +fluid_check_properties + (struct sdis_device* dev, + const struct fluid_props* props, + const double pos[], + const double time) { - ASSERT(mdm && mdm->type == SDIS_SOLID); - return mdm->shader.solid.calorific_capacity(vtx, mdm->data); -} + res_T res = RES_OK; + ASSERT(dev && props); -static INLINE double -solid_get_thermal_conductivity - (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) -{ - ASSERT(mdm && mdm->type == SDIS_SOLID); - return mdm->shader.solid.thermal_conductivity(vtx, mdm->data); + #define CHK_PROP(Prop, Val) { \ + res = fluid_check_##Prop(dev, Val, pos, time); \ + if(res != RES_OK) return res; \ + } (void)0 + CHK_PROP(volumic_mass, props->rho); + CHK_PROP(calorific_capacity, props->cp); + #undef CHK_PROP + + /* Do not check the temperature. An invalid temperature means that the + * temperature is unknown */ + + res = fluid_check_t0(dev, props->t0); + if(res != RES_OK) return res; + + return RES_OK; } -static INLINE double -solid_get_volumic_mass - (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) +static INLINE res_T +fluid_get_properties + (const struct sdis_medium* mdm, + const struct sdis_rwalk_vertex* vtx, + struct fluid_props* props) { - ASSERT(mdm && mdm->type == SDIS_SOLID); - return mdm->shader.solid.volumic_mass(vtx, mdm->data); + ASSERT(mdm && mdm->type == SDIS_FLUID); + props->rho = fluid_get_volumic_mass(mdm, vtx); + props->cp = fluid_get_calorific_capacity(mdm, vtx); + props->temperature = fluid_get_temperature(mdm, vtx); + return fluid_check_properties(mdm->dev, props, vtx->P, vtx->time); } -static INLINE double -solid_get_delta - (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) +/******************************************************************************* + * Solid local functions + ******************************************************************************/ +DEFINE_MDM_CHK_PROP_FUNC(solid, calorific_capacity,0, INF, 0, 1) +DEFINE_MDM_CHK_PROP_FUNC(solid, thermal_conductivity, 0, INF, 0, 1) +DEFINE_MDM_CHK_PROP_FUNC(solid, volumic_mass, 0, INF, 0, 1) +DEFINE_MDM_CHK_PROP_FUNC(solid, delta, 0, INF, 0, 1) +DEFINE_MDM_CHK_PROP_FUNC(solid, volumic_power, -INF, INF, 1, 1) +DEFINE_MDM_CHK_PROP_FUNC(solid, temperature, 0, INF, 1, 1) + +static INLINE res_T +solid_check_t0(struct sdis_device* dev, const double t0) { - ASSERT(mdm && mdm->type == SDIS_SOLID); - return mdm->shader.solid.delta_solid(vtx, mdm->data); + if(t0 < 0) { + log_err(dev, "invalid negative initial time '%g'.\n", t0); + return RES_BAD_ARG; + } + return RES_OK; } +DEFINE_MDM_GET_PROP_FUNC(solid, calorific_capacity) +DEFINE_MDM_GET_PROP_FUNC(solid, thermal_conductivity) +DEFINE_MDM_GET_PROP_FUNC(solid, volumic_mass) +DEFINE_MDM_GET_PROP_FUNC(solid, delta) +DEFINE_MDM_GET_PROP_FUNC(solid, temperature) + static INLINE double solid_get_volumic_power - (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) + (const struct sdis_medium* mdm, + const struct sdis_rwalk_vertex* vtx) { ASSERT(mdm && mdm->type == SDIS_SOLID); return mdm->shader.solid.volumic_power @@ -125,26 +211,64 @@ solid_get_volumic_power } static INLINE double -solid_get_temperature - (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) +solid_get_t0(const struct sdis_medium* mdm) { ASSERT(mdm && mdm->type == SDIS_SOLID); - /*ASSERT(vtx->time >= mdm->shader.solid.t0);*/ - return mdm->shader.solid.temperature(vtx, mdm->data); + ASSERT(0 <= mdm->shader.solid.t0 && mdm->shader.solid.t0 < INF); + return mdm->shader.solid.t0; } -static INLINE double -solid_get_t0(const struct sdis_medium* mdm) +static INLINE res_T +solid_check_properties + (struct sdis_device* dev, + const struct solid_props* props, + const double pos[], + const double time) +{ + res_T res = RES_OK; + ASSERT(dev && props); + + #define CHK_PROP(Prop, Val) { \ + res = solid_check_##Prop(dev, Val, pos, time); \ + if(res != RES_OK) return res; \ + } (void)0 + CHK_PROP(calorific_capacity, props->cp); + CHK_PROP(thermal_conductivity, props->lambda); + CHK_PROP(volumic_mass, props->rho); + CHK_PROP(delta, props->delta); + CHK_PROP(volumic_power, props->power); + #undef CHK_PROP + + /* Do not check the temperature. An invalid temperature means that the + * temperature is unknown */ + + res = solid_check_t0(dev, props->t0); + if(res != RES_OK) return res; + + return RES_OK; +} + +static INLINE res_T +solid_get_properties + (const struct sdis_medium* mdm, + const struct sdis_rwalk_vertex* vtx, + struct solid_props* props) { ASSERT(mdm && mdm->type == SDIS_SOLID); - ASSERT(0 <= mdm->shader.solid.t0 && mdm->shader.solid.t0 < INF); - return mdm->shader.solid.t0; + props->lambda = solid_get_thermal_conductivity(mdm, vtx); + props->rho = solid_get_volumic_mass(mdm, vtx); + props->cp = solid_get_calorific_capacity(mdm, vtx); + props->delta = solid_get_delta(mdm, vtx); + props->power = solid_get_volumic_power(mdm, vtx); + props->temperature = solid_get_temperature(mdm, vtx); + props->t0 = solid_get_t0(mdm); + return solid_check_properties(mdm->dev, props, vtx->P, vtx->time); } /******************************************************************************* * Generic functions ******************************************************************************/ -static FINLINE double +static INLINE double medium_get_temperature (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) { @@ -158,7 +282,7 @@ medium_get_temperature return temp; } -static FINLINE double +static INLINE double medium_get_t0(const struct sdis_medium* mdm) { double t0; @@ -171,5 +295,38 @@ medium_get_t0(const struct sdis_medium* mdm) return t0; } +static INLINE unsigned +medium_get_id(const struct sdis_medium* mdm) +{ + ASSERT(mdm); + return mdm->id.index; +} + +static INLINE const char* +medium_type_to_string(const enum sdis_medium_type type) +{ + const char* str = "none"; + switch(type) { + case SDIS_FLUID: str = "fluid"; break; + case SDIS_SOLID: str = "solid"; break; + default: FATAL("Unreachable code.\n"); break; + } + return str; +} + +#undef MDM_TYPE +#undef MDM_TYPE_solid +#undef MDM_TYPE_fluid +#undef PROP_STR +#undef PROP_STR_calorific_capacity +#undef PROP_STR_conductivity +#undef PROP_STR_delta +#undef PROP_STR_temperature +#undef PROP_STR_thermal_conductivity +#undef PROP_STR_volumic_mass +#undef PROP_STR_volumic_power +#undef DEFINE_MDM_CHK_PROP_FUNC +#undef DEFINE_MDM_GET_PROP_FUNC + #endif /* SDIS_MEDIUM_C_H */ 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> @@ -127,10 +129,11 @@ register_heat_vertex (struct sdis_heat_path* path, const struct sdis_rwalk_vertex* vtx, const double weight, - const enum sdis_heat_vertex_type type) + const enum sdis_heat_vertex_type type, + const int branch_id) { struct sdis_heat_vertex heat_vtx = SDIS_HEAT_VERTEX_NULL; - ASSERT(vtx); + ASSERT(vtx && branch_id >= 0); if(!path) return RES_OK; @@ -140,25 +143,41 @@ register_heat_vertex heat_vtx.time = vtx->time; heat_vtx.weight = weight; heat_vtx.type = type; + heat_vtx.branch_id = branch_id; return heat_path_add_vertex(path, &heat_vtx); } extern LOCAL_SYM res_T time_rewind_2d - (struct sdis_medium* mdm, /* Medium into which the time is rewinded */ + (const double mu, + const double t0, /* Initial time */ struct ssp_rng* rng, - const double dist_in_meter, - const struct rwalk_context* ctx, struct rwalk_2d* rwalk, + const struct rwalk_context* ctx, struct temperature_2d* T); extern LOCAL_SYM res_T time_rewind_3d - (struct sdis_medium* mdm, /* Medium into which the time is rewinded */ + (const double mu, + const double t0, /* Initial time */ struct ssp_rng* rng, - const double dist_in_meter, - const struct rwalk_context* ctx, struct rwalk_3d* rwalk, + const struct rwalk_context* ctx, 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 @@ -25,29 +25,19 @@ res_T XD(time_rewind) - (struct sdis_medium* mdm, + (const double mu, + const double t0, struct ssp_rng* rng, - const double dist_in_meter, - const struct rwalk_context* ctx, struct XD(rwalk)* rwalk, + const struct rwalk_context* ctx, struct XD(temperature)* T) { double temperature; - double lambda, rho, cp; - double tau, mu, t0; + double tau; res_T res = RES_OK; - ASSERT(mdm && rng && ctx && rwalk && dist_in_meter > 0); - ASSERT(sdis_medium_get_type(mdm) == SDIS_SOLID); - ASSERT(T->done == 0); - - /* Fetch physical properties */ - lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); - rho = solid_get_volumic_mass(mdm, &rwalk->vtx); - cp = solid_get_calorific_capacity(mdm, &rwalk->vtx); - t0 = solid_get_t0(mdm); /* Limit time */ + ASSERT(rwalk && rng && T); - /* Sample the time to reroll */ - mu = (2*DIM*lambda)/(rho*cp*dist_in_meter*dist_in_meter); + /* Sample the time using the upper bound. */ tau = ssp_ran_exp(rng, mu); /* Increment the elapsed time */ @@ -57,16 +47,18 @@ XD(time_rewind) if(IS_INF(rwalk->vtx.time)) goto exit; /* Steady computation */ /* Time rewind */ - rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); + rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); /* Time rewind */ /* The path does not reach the limit condition */ if(rwalk->vtx.time > t0) goto exit; - /* Fetch initial temperature */ - temperature = solid_get_temperature(mdm, &rwalk->vtx); + /* Fetch the initial temperature */ + temperature = medium_get_temperature(rwalk->mdm, &rwalk->vtx); if(temperature < 0) { - log_err(mdm->dev, "%s: the path reaches the limit condition but the " - "temperature remains unknown.\n", FUNC_NAME); + log_err(rwalk->mdm->dev, "the path reaches the limit condition but the " + "%s temperature remains unknown -- position=%g, %g, %g\n", + medium_type_to_string(sdis_medium_get_type(rwalk->mdm)), + SPLIT3(rwalk->vtx.P)); res = RES_BAD_ARG; goto error; } @@ -84,8 +76,8 @@ XD(time_rewind) } if(ctx->green_path) { - res = green_path_set_limit_vertex(ctx->green_path, mdm, &rwalk->vtx, - rwalk->elapsed_time); + res = green_path_set_limit_vertex(ctx->green_path, rwalk->mdm, + &rwalk->vtx, rwalk->elapsed_time); if(res != RES_OK) goto error; } 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 @@ -13,8 +13,26 @@ * 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_medium_c.h" #include "sdis_realisation.h" +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE int +check_ray_realisatio_args(const struct ray_realisation_args* args) +{ + return args + && args->rng + && args->medium + && args->medium->type == SDIS_FLUID + && args->time >= 0 + && args->picard_order > 0; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ /* Generate the generic realisations */ #define SDIS_XD_DIMENSION 2 #include "sdis_realisation_Xd.h" @@ -24,12 +42,7 @@ res_T ray_realisation_3d (struct sdis_scene* scn, - struct ssp_rng* rng, - struct sdis_medium* medium, - const double position[3], - const double direction[3], - const double time, - struct sdis_heat_path* heat_path, /* May be NULL */ + struct ray_realisation_args* args, double* weight) { struct rwalk_context ctx = RWALK_CONTEXT_NULL; @@ -37,31 +50,35 @@ ray_realisation_3d struct temperature_3d T = TEMPERATURE_NULL_3d; float dir[3]; res_T res = RES_OK; - ASSERT(scn && position && direction && time>=0 && weight); - ASSERT(medium && medium->type == SDIS_FLUID); + ASSERT(scn && weight && check_ray_realisatio_args(args)); - d3_set(rwalk.vtx.P, position); - rwalk.vtx.time = time; + d3_set(rwalk.vtx.P, args->position); + rwalk.vtx.time = args->time; rwalk.hit = S3D_HIT_NULL; rwalk.hit_side = SDIS_SIDE_NULL__; - rwalk.mdm = medium; - - ctx.Tarad = scn->ambient_radiative_temperature; - ctx.Tref3 = scn->reference_temperature * scn->reference_temperature - * scn->reference_temperature; - ctx.heat_path = heat_path; + rwalk.mdm = args->medium; - f3_set_d3(dir, direction); + ctx.heat_path = args->heat_path; + ctx.Tmin = scn->tmin; + ctx.Tmin2 = ctx.Tmin * ctx.Tmin; + ctx.Tmin3 = ctx.Tmin * ctx.Tmin2; + ctx.That = scn->tmax; + ctx.That2 = ctx.That * ctx.That; + ctx.That3 = ctx.That * ctx.That2; + ctx.max_branchings = args->picard_order - 1; + + f3_set_d3(dir, args->direction); /* Register the starting position against the heat path */ - res = register_heat_vertex(heat_path, &rwalk.vtx, 0, SDIS_HEAT_VERTEX_RADIATIVE); + res = register_heat_vertex + (args->heat_path, &rwalk.vtx, 0, SDIS_HEAT_VERTEX_RADIATIVE, 0); if(res != RES_OK) goto error; - res = trace_radiative_path_3d(scn, dir, &ctx, &rwalk, rng, &T); + res = trace_radiative_path_3d(scn, dir, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) goto error; if(!T.done) { - res = compute_temperature_3d(scn, &ctx, &rwalk, rng, &T); + res = compute_temperature_3d(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) goto error; } 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 @@ -35,93 +35,138 @@ enum flux_flag { }; /******************************************************************************* + * Helper function used to compute a temperature + ******************************************************************************/ +extern LOCAL_SYM res_T +compute_temperature_2d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* T); + +extern LOCAL_SYM res_T +compute_temperature_3d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* T); + +/******************************************************************************* * Realisation at a given position and time IN a medium ******************************************************************************/ +struct probe_realisation_args { + struct ssp_rng* rng; + struct sdis_medium* medium; /* Medium into which the realisation starts */ + double position[3]; /* Probe position */ + double time; /* Observation time */ + size_t picard_order; /* Picard order to estimate radiative temperature */ + struct green_path_handle* green_path; /* May be NULL */ + struct sdis_heat_path* heat_path; /* May be NULL */ + size_t irealisation; /* Id of the realisation (for debug) */ +}; +#define PROBE_REALISATION_ARGS_NULL__ { \ + NULL, NULL, {0,0,0}, -1, 0, NULL, NULL, 0 \ +} +static const struct probe_realisation_args PROBE_REALISATION_ARGS_NULL = + PROBE_REALISATION_ARGS_NULL__; + extern LOCAL_SYM res_T probe_realisation_2d - (const size_t irealisation, /* For debug */ - struct sdis_scene* scn, - struct ssp_rng* rng, - struct sdis_medium* medium, - const double position[2], - const double time, - struct green_path_handle* green_path, /* May be NULL */ - struct sdis_heat_path* heat_path, /* May be NULL */ + (struct sdis_scene* scn, + struct probe_realisation_args* args, double* weight); extern LOCAL_SYM res_T probe_realisation_3d - (const size_t irealisation, /* For debug */ - struct sdis_scene* scn, - struct ssp_rng* rng, - struct sdis_medium* medium, - const double position[3], - const double time, - struct green_path_handle* green_path, /* May be NULL */ - struct sdis_heat_path* heat_path, /* May be NULL */ + (struct sdis_scene* scn, + struct probe_realisation_args* args, double* weight); /******************************************************************************* * Realisation at a given position and time ON a given side of a boundary ******************************************************************************/ +struct boundary_realisation_args { + struct ssp_rng* rng; + size_t iprim; /* Index of the geometruc primitive */ + double uv[2]; /* Parametric coordinate into the geometric primitive */ + double time; /* Observation time */ + size_t picard_order; /* Picard order to estimate radiative temperature */ + enum sdis_side side; /* Side of the geometric primitive */ + struct green_path_handle* green_path; /* May be NULL */ + struct sdis_heat_path* heat_path; /* May be NULL */ +}; +#define BOUNDARY_REALISATION_ARGS_NULL__ { \ + NULL, SIZE_MAX, {0,0}, -1, 0, SDIS_SIDE_NULL__, NULL, NULL \ +} +static const struct boundary_realisation_args BOUNDARY_REALISATION_ARGS_NULL = + BOUNDARY_REALISATION_ARGS_NULL__; + extern LOCAL_SYM res_T boundary_realisation_2d (struct sdis_scene* scn, - struct ssp_rng* rng, - const size_t iprim, - const double u[1], - const double time, - const enum sdis_side side, - struct green_path_handle* green_path, /* May be NULL */ - struct sdis_heat_path* heat_path, /* May be NULL */ + struct boundary_realisation_args* args, double* weight); extern LOCAL_SYM res_T boundary_realisation_3d (struct sdis_scene* scn, - struct ssp_rng* rng, - const size_t iprim, - const double uv[2], - const double time, - const enum sdis_side side, - struct green_path_handle* green_path, /* May be NULL */ - struct sdis_heat_path* heat_path, /* May be NULL */ + struct boundary_realisation_args* args, double* weight); +/******************************************************************************* + * Realisation at a given position and time ON a given side of a boundary + ******************************************************************************/ +struct boundary_flux_realisation_args { + struct ssp_rng* rng; + size_t iprim; /* Index of the geometruc primitive */ + double uv[2]; /* Parametric coordinate into the geometric primitive */ + double time; /* Observation time */ + size_t picard_order; /* Picard order to estimate radiative temperature */ + enum sdis_side solid_side; /* Side of the geometric primitive */ + int flux_mask; /* Combination of enum flux_flag */ +}; +#define BOUNDARY_FLUX_REALISATION_ARGS_NULL__ { \ + NULL, SIZE_MAX, {0,0}, -1, 0, SDIS_SIDE_NULL__, 0 \ +} +static const struct boundary_flux_realisation_args +BOUNDARY_FLUX_REALISATION_ARGS_NULL = BOUNDARY_FLUX_REALISATION_ARGS_NULL__; + extern LOCAL_SYM res_T boundary_flux_realisation_2d (struct sdis_scene* scn, - struct ssp_rng* rng, - const size_t iprim, - const double uv[1], - const double time, - const enum sdis_side solid_side, - const int flux_mask, /* Combination of enum flux_flag */ + struct boundary_flux_realisation_args* args, struct bound_flux_result* result); extern LOCAL_SYM res_T boundary_flux_realisation_3d (struct sdis_scene* scn, - struct ssp_rng* rng, - const size_t iprim, - const double uv[2], - const double time, - const enum sdis_side solid_side, - const int flux_mask, /* Combination of enum flux_flag */ + struct boundary_flux_realisation_args* args, struct bound_flux_result* result); /******************************************************************************* * Realisation along a given ray at a given time. Available only in 3D. ******************************************************************************/ +struct ray_realisation_args { + struct ssp_rng* rng; + struct sdis_medium* medium; /* Medium into which the realisation starts */ + double position[3]; /* Ray position */ + double direction[3]; /* Ray direction */ + double time; /* Observation time */ + size_t picard_order; /* Picard order to estimate radiative temperature */ + struct sdis_heat_path* heat_path; /* May be NULL */ +}; +#define RAY_REALISATION_ARGS_NULL__ { \ + NULL, NULL, {0,0,0}, {0,0,0}, -1, 0, NULL \ +} +static const struct ray_realisation_args RAY_REALISATION_ARGS_NULL = + RAY_REALISATION_ARGS_NULL__; + extern LOCAL_SYM res_T ray_realisation_3d (struct sdis_scene* scn, - struct ssp_rng* rng, - struct sdis_medium* medium, - const double position[3], - const double direction[3], - const double time, - struct sdis_heat_path* heat_path, /* May be NULL */ + struct ray_realisation_args* args, double* weight); #endif /* SDIS_REALISATION_H */ 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 @@ -27,12 +27,58 @@ #include "sdis_Xd_begin.h" /******************************************************************************* - * Helper functions + * Non generic helper functions ******************************************************************************/ -static res_T +#ifndef SDIS_REALISATION_XD_H +#define SDIS_REALISATION_XD_H + +static INLINE int +check_probe_realisation_args(const struct probe_realisation_args* args) +{ + return args + && args->rng + && args->medium + && args->time >= 0 + && args->picard_order > 0; +} + +static INLINE int +check_boundary_realisation_args(const struct boundary_realisation_args* args) +{ + return args + && args->rng + && args->uv[0] >= 0 + && args->uv[0] <= 1 + && args->uv[1] >= 0 + && args->uv[1] <= 1 + && args->time >= 0 + && args->picard_order > 0 + && (args->side == SDIS_FRONT || args->side == SDIS_BACK); +} + +static INLINE int +check_boundary_flux_realisation_args + (const struct boundary_flux_realisation_args* args) +{ + return args + && args->rng + && args->uv[0] >= 0 + && args->uv[0] <= 1 + && args->uv[1] >= 0 + && args->uv[1] <= 1 + && args->time >= 0 + && args->picard_order > 0 + && (args->solid_side == SDIS_FRONT || args->solid_side == SDIS_BACK); +} +#endif /* SDIS_REALISATION_XD_H */ + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T XD(compute_temperature) (struct sdis_scene* scn, - const struct rwalk_context* ctx, + struct rwalk_context* ctx, struct XD(rwalk)* rwalk, struct ssp_rng* rng, struct XD(temperature)* T) @@ -51,11 +97,14 @@ XD(compute_temperature) res_T res = RES_OK; ASSERT(scn && ctx && rwalk && rng && T); + ctx->nbranchings += 1; + CHK(ctx->nbranchings <= ctx->max_branchings); + if(ctx->heat_path && T->func == XD(boundary_path)) { heat_vtx = heat_path_get_last_vertex(ctx->heat_path); } - do { + while(!T->done) { /* Save the current random walk state */ const struct XD(rwalk) rwalk_bkp = *rwalk; const struct XD(temperature) T_bkp = *T; @@ -94,32 +143,23 @@ XD(compute_temperature) } heat_vtx = NULL; /* Notify that the first vertex is finalized */ } + } - } while(!T->done); exit: #ifndef NDEBUG sa_release(stack); #endif + ctx->nbranchings -= 1; return res == RES_BAD_OP_IRRECOVERABLE ? RES_BAD_OP : res; error: goto exit; } - -/******************************************************************************* - * Local functions - ******************************************************************************/ res_T XD(probe_realisation) - (const size_t irealisation, /* For debug */ - struct sdis_scene* scn, - struct ssp_rng* rng, - struct sdis_medium* medium, - const double position[DIM], - const double time, - struct green_path_handle* green_path, /* May be NULL */ - struct sdis_heat_path* heat_path, /* May be NULL */ + (struct sdis_scene* scn, + struct probe_realisation_args* args, double* weight) { struct rwalk_context ctx = RWALK_CONTEXT_NULL; @@ -131,38 +171,37 @@ XD(probe_realisation) (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx); res_T res = RES_OK; - ASSERT(medium && position && weight && time >= 0); - (void)irealisation; + ASSERT(scn && weight && check_probe_realisation_args(args)); - switch(medium->type) { + switch(args->medium->type) { case SDIS_FLUID: T.func = XD(convective_path); get_initial_temperature = fluid_get_temperature; - t0 = fluid_get_t0(medium); + t0 = fluid_get_t0(args->medium); break; case SDIS_SOLID: T.func = XD(conductive_path); get_initial_temperature = solid_get_temperature; - t0 = solid_get_t0(medium); + t0 = solid_get_t0(args->medium); break; default: FATAL("Unreachable code\n"); break; } - dX(set)(rwalk.vtx.P, position); - rwalk.vtx.time = time; + dX(set)(rwalk.vtx.P, args->position); + rwalk.vtx.time = args->time; /* Register the starting position against the heat path */ - type = medium->type == SDIS_SOLID + type = args->medium->type == SDIS_SOLID ? SDIS_HEAT_VERTEX_CONDUCTION : SDIS_HEAT_VERTEX_CONVECTION; - res = register_heat_vertex(heat_path, &rwalk.vtx, 0, type); + res = register_heat_vertex(args->heat_path, &rwalk.vtx, 0, type, 0); if(res != RES_OK) goto error; if(t0 >= rwalk.vtx.time) { double tmp; /* Check the initial condition. */ rwalk.vtx.time = t0; - tmp = get_initial_temperature(medium, &rwalk.vtx); + tmp = get_initial_temperature(args->medium, &rwalk.vtx); if(tmp >= 0) { *weight = tmp; goto exit; @@ -177,17 +216,19 @@ XD(probe_realisation) } rwalk.hit = SXD_HIT_NULL; - rwalk.mdm = medium; - - ctx.green_path = green_path; - ctx.heat_path = heat_path; - ctx.Tarad = scn->ambient_radiative_temperature; - ctx.Tref3 = - scn->reference_temperature - * scn->reference_temperature - * scn->reference_temperature; - - res = XD(compute_temperature)(scn, &ctx, &rwalk, rng, &T); + rwalk.mdm = args->medium; + + ctx.green_path = args->green_path; + ctx.heat_path = args->heat_path; + ctx.Tmin = scn->tmin; + ctx.Tmin2 = ctx.Tmin * ctx.Tmin; + ctx.Tmin3 = ctx.Tmin * ctx.Tmin2; + ctx.That = scn->tmax; + ctx.That2 = ctx.That * ctx.That; + ctx.That3 = ctx.That * ctx.That2; + ctx.max_branchings = args->picard_order - 1; + + res = XD(compute_temperature)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) goto error; ASSERT(T.value >= 0); @@ -202,13 +243,7 @@ error: res_T XD(boundary_realisation) (struct sdis_scene* scn, - struct ssp_rng* rng, - const size_t iprim, - const double uv[DIM-1], - const double time, - const enum sdis_side side, - struct green_path_handle* green_path, /* May be NULL */ - struct sdis_heat_path* heat_path, /* May be NULL */ + struct boundary_realisation_args* args, double* weight) { struct rwalk_context ctx = RWALK_CONTEXT_NULL; @@ -221,23 +256,23 @@ XD(boundary_realisation) float st[2]; #endif res_T res = RES_OK; - ASSERT(uv && weight && time >= 0); + ASSERT(scn && weight && check_boundary_realisation_args(args)); T.func = XD(boundary_path); - rwalk.hit_side = side; + rwalk.hit_side = args->side; rwalk.hit.distance = 0; - rwalk.vtx.time = time; + rwalk.vtx.time = args->time; rwalk.mdm = NULL; /* The random walk is at an interface between 2 media */ #if SDIS_XD_DIMENSION == 2 - st = (float)uv[0]; + st = (float)args->uv[0]; #else - f2_set_d2(st, uv); + f2_set_d2(st, args->uv); #endif /* Fetch the primitive */ SXD(scene_view_get_primitive - (scn->sXd(view), (unsigned int)iprim, &rwalk.hit.prim)); + (scn->sXd(view), (unsigned int)args->iprim, &rwalk.hit.prim)); /* Retrieve the world space position of the probe onto the primitive */ SXD(primitive_get_attrib(&rwalk.hit.prim, SXD_POSITION, st, &attr)); @@ -253,17 +288,21 @@ XD(boundary_realisation) f2_set(rwalk.hit.uv, st); #endif - res = register_heat_vertex(heat_path, &rwalk.vtx, 0/*weight*/, - SDIS_HEAT_VERTEX_CONDUCTION); + res = register_heat_vertex(args->heat_path, &rwalk.vtx, 0/*weight*/, + SDIS_HEAT_VERTEX_CONDUCTION, 0/*Branch id*/); if(res != RES_OK) goto error; - ctx.green_path = green_path; - ctx.heat_path = heat_path; - ctx.Tarad = scn->ambient_radiative_temperature; - ctx.Tref3 = scn->reference_temperature * scn->reference_temperature - * scn->reference_temperature; - - res = XD(compute_temperature)(scn, &ctx, &rwalk, rng, &T); + ctx.green_path = args->green_path; + ctx.heat_path = args->heat_path; + ctx.Tmin = scn->tmin; + ctx.Tmin2 = ctx.Tmin * ctx.Tmin; + ctx.Tmin3 = ctx.Tmin * ctx.Tmin2; + ctx.That = scn->tmax; + ctx.That2 = ctx.That * ctx.That; + ctx.That3 = ctx.That * ctx.That2; + ctx.max_branchings = args->picard_order - 1; + + res = XD(compute_temperature)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) goto error; *weight = T.value; @@ -277,12 +316,7 @@ error: res_T XD(boundary_flux_realisation) (struct sdis_scene* scn, - struct ssp_rng* rng, - const size_t iprim, - const double uv[DIM-1], - const double time, - const enum sdis_side solid_side, - const int flux_mask, + struct boundary_flux_realisation_args* args, struct bound_flux_result* result) { struct rwalk_context ctx = RWALK_CONTEXT_NULL; @@ -300,25 +334,36 @@ XD(boundary_flux_realisation) #endif double P[SDIS_XD_DIMENSION]; float N[SDIS_XD_DIMENSION]; - const double Tr3 = scn->reference_temperature * scn->reference_temperature - * scn->reference_temperature; - const enum sdis_side fluid_side = - (solid_side == SDIS_FRONT) ? SDIS_BACK : SDIS_FRONT; + double Tmin, Tmin2, Tmin3; + double That, That2, That3; + enum sdis_side fluid_side; res_T res = RES_OK; - const char compute_radiative = (flux_mask & FLUX_FLAG_RADIATIVE) != 0; - const char compute_convective = (flux_mask & FLUX_FLAG_CONVECTIVE) != 0; - ASSERT(uv && result && time >= 0 ); + char compute_radiative; + char compute_convective; + ASSERT(scn && result && check_boundary_flux_realisation_args(args)); #if SDIS_XD_DIMENSION == 2 #define SET_PARAM(Dest, Src) (Dest).u = (Src); - st = (float)uv[0]; + st = (float)args->uv[0]; #else #define SET_PARAM(Dest, Src) f2_set((Dest).uv, (Src)); - f2_set_d2(st, uv); + f2_set_d2(st, args->uv); #endif + Tmin = scn->tmin; + Tmin2 = Tmin * Tmin; + Tmin3 = Tmin * Tmin2; + That = scn->tmax; + That2 = That * That; + That3 = That * That2; + + fluid_side = (args->solid_side/*solid*/==SDIS_FRONT) ? SDIS_BACK : SDIS_FRONT; + + compute_radiative = (args->flux_mask & FLUX_FLAG_RADIATIVE) != 0; + compute_convective = (args->flux_mask & FLUX_FLAG_CONVECTIVE) != 0; + /* Fetch the primitive */ - SXD(scene_view_get_primitive(scn->sXd(view), (unsigned int)iprim, &prim)); + SXD(scene_view_get_primitive(scn->sXd(view), (unsigned int)args->iprim, &prim)); /* Retrieve the world space position of the probe onto the primitive */ SXD(primitive_get_attrib(&prim, SXD_POSITION, st, &attr)); @@ -332,33 +377,37 @@ XD(boundary_flux_realisation) rwalk = XD(RWALK_NULL); \ rwalk.hit_side = (Side); \ rwalk.hit.distance = 0; \ - rwalk.vtx.time = time; \ + rwalk.vtx.time = args->time; \ rwalk.mdm = (Mdm); \ rwalk.hit.prim = prim; \ SET_PARAM(rwalk.hit, st); \ - ctx.Tarad = scn->ambient_radiative_temperature; \ - ctx.Tref3 = Tr3; \ + ctx.Tmin = Tmin; \ + ctx.Tmin3 = Tmin3; \ + ctx.That = That; \ + ctx.That2 = That2; \ + ctx.That3 = That3; \ + ctx.max_branchings = args->picard_order - 1; \ dX(set)(rwalk.vtx.P, P); \ fX(set)(rwalk.hit.normal, N); \ T = XD(TEMPERATURE_NULL); \ } (void)0 /* Compute boundary temperature */ - RESET_WALK(solid_side, NULL); + RESET_WALK(args->solid_side, NULL); T.func = XD(boundary_path); - res = XD(compute_temperature)(scn, &ctx, &rwalk, rng, &T); + res = XD(compute_temperature)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) return res; result->Tboundary = T.value; /* Fetch the fluid medium */ - interf = scene_get_interface(scn, (unsigned)iprim); + interf = scene_get_interface(scn, (unsigned)args->iprim); fluid_mdm = interface_get_medium(interf, fluid_side); /* Compute radiative temperature */ if(compute_radiative) { RESET_WALK(fluid_side, fluid_mdm); T.func = XD(radiative_path); - res = XD(compute_temperature)(scn, &ctx, &rwalk, rng, &T); + res = XD(compute_temperature)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) return res; ASSERT(T.value >= 0); result->Tradiative = T.value; @@ -368,7 +417,7 @@ XD(boundary_flux_realisation) if(compute_convective) { RESET_WALK(fluid_side, fluid_mdm); T.func = XD(convective_path); - res = XD(compute_temperature)(scn, &ctx, &rwalk, rng, &T); + res = XD(compute_temperature)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) return res; result->Tfluid = T.value; } 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 @@ -203,40 +203,42 @@ sdis_scene_set_fp_to_meter res_T sdis_scene_get_ambient_radiative_temperature (const struct sdis_scene* scn, - double* trad) + struct sdis_ambient_radiative_temperature* trad) { if(!scn || !trad) return RES_BAD_ARG; - *trad = scn->ambient_radiative_temperature; + *trad = scn->trad; return RES_OK; } res_T -sdis_scene_set_reference_temperature +sdis_scene_set_ambient_radiative_temperature (struct sdis_scene* scn, - const double tref) + const struct sdis_ambient_radiative_temperature* trad) { - if(!scn || tref < 0) return RES_BAD_ARG; - scn->reference_temperature = tref; + if(!scn) return RES_BAD_ARG; + scn->trad = *trad; return RES_OK; } res_T -sdis_scene_get_reference_temperature +sdis_scene_get_temperature_range (const struct sdis_scene* scn, - double* tref) + double t_range[2]) { - if(!scn || !tref) return RES_BAD_ARG; - *tref = scn->reference_temperature; + if(!scn || !t_range) return RES_BAD_ARG; + t_range[0] = scn->tmin; + t_range[1] = scn->tmax; return RES_OK; } res_T -sdis_scene_set_ambient_radiative_temperature +sdis_scene_set_temperature_range (struct sdis_scene* scn, - const double trad) + const double t_range[2]) { - if(!scn) return RES_BAD_ARG; - scn->ambient_radiative_temperature = trad; + if(!scn || !t_range) return RES_BAD_ARG; + scn->tmin = t_range[0]; + scn->tmax = t_range[1]; return RES_OK; } @@ -474,7 +476,8 @@ scene_compute_hash(const struct sdis_scene* scn, hash256_T hash) } else { S3D(scene_view_primitives_count(scn->s3d_view, &nprims)); } - WRITE(&scn->reference_temperature, 1); + WRITE(&scn->trad.reference, 1); + WRITE(&scn->tmax, 1); WRITE(&scn->fp_to_meter, 1); FOR_EACH(iprim, 0, nprims) { struct sdis_interface* interf = NULL; @@ -539,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 @@ -912,8 +912,9 @@ XD(scene_create) SDIS(device_ref_get(dev)); scn->dev = dev; scn->fp_to_meter = args->fp_to_meter; - scn->ambient_radiative_temperature = args->trad; - scn->reference_temperature = args->tref; + scn->trad = args->trad; + scn->tmin = args->t_range[0]; + scn->tmax = args->t_range[1]; scn->outer_enclosure_id = UINT_MAX; darray_interf_init(dev->allocator, &scn->interfaces); darray_medium_init(dev->allocator, &scn->media); 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 @@ -209,8 +209,9 @@ struct sdis_scene { unsigned outer_enclosure_id; double fp_to_meter; - double ambient_radiative_temperature; /* In Kelvin */ - double reference_temperature; + struct sdis_ambient_radiative_temperature trad; + double tmin; /* Minimum temperature of the system (In Kelvin) */ + double tmax; /* Maximum temperature of the system (In Kelvin) */ ref_T ref; struct sdis_device* dev; @@ -242,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 @@ -256,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, @@ -289,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,170 +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 */ - 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 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 */ - res_simul = ray_realisation_3d(scn, rng, mdm, ray_pos, ray_dir, - time, pheat_path, &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 */ - 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, estimator); - if(res != RES_OK) goto error; - } - -exit: - return res; -error: - goto exit; -} - -/******************************************************************************* * Exported functions ******************************************************************************/ res_T @@ -327,181 +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, 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,5 +1,5 @@ -/* 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 * the Free Software Foundation, either version 3 of the License, or @@ -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,89 @@ 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 + +static INLINE res_T +check_solve_boundary_args(const struct sdis_solve_boundary_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->sides || !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; + } + + /* Check RNG type */ + if(!args->rng_state && args->rng_type >= SSP_RNG_TYPES_COUNT__) { + return RES_BAD_ARG; + } + + 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; + } + + /* Check RNG type */ + if(!args->rng_state && args->rng_type >= SSP_RNG_TYPES_COUNT__) { + 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) @@ -80,63 +164,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 || !args || !args->nrealisations || args->nrealisations > INT64_MAX - || !args->primitives || !args->sides || !args->nprimitives) { - res = RES_BAD_ARG; - goto error; - } - if(!out_estimator && !out_green) { - res = RES_BAD_ARG; - goto error; - } - if(args->time_range[0] < 0 || args->time_range[1] < args->time_range[0]) { - res = RES_BAD_ARG; - goto error; - } - if(args->time_range[1] > DBL_MAX - && args->time_range[0] != args->time_range[1]) { - 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 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 submitted primitive indices */ + FOR_EACH(i, 0, args->nprimitives) { + res = scene_check_primitive_index(scn, args->primitives[i]); + if(res != RES_OK) goto error; + } - 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", @@ -146,6 +224,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); @@ -181,59 +272,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, args->rng_type, &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; @@ -257,7 +348,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; @@ -298,8 +390,18 @@ XD(solve_boundary) side = args->sides[prim.prim_id]; /* Invoke the boundary realisation */ - res_simul = XD(boundary_realisation)(scn, rng, iprim, uv, time, side, - pgreen_path, pheat_path, &w); + realis_args.rng = rng; + realis_args.iprim = iprim; + realis_args.time = time; + realis_args.picard_order = args->picard_order; + realis_args.side = side; + realis_args.green_path = pgreen_path; + realis_args.heat_path = pheat_path; + realis_args.uv[0] = uv[0]; +#if SDIS_XD_DIMENSION == 3 + realis_args.uv[1] = uv[1]; +#endif + res_simul = XD(boundary_realisation)(scn, &realis_args, &w); /* Fatal error */ if(res_simul != RES_OK && res_simul != RES_BAD_OP) { @@ -341,9 +443,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); @@ -351,58 +453,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])); - } - 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(scene) SXD(scene_ref_put(scene)); if(shape) SXD(shape_ref_put(shape)); if(view) SXD(scene_view_ref_put(view)); @@ -411,14 +537,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; } @@ -428,59 +548,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 */ @@ -518,63 +638,70 @@ 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, args->rng_type, &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); + + #define PROGRESS_MSG "Solving surface flux: " + print_progress(scn->dev, progress, PROGRESS_MSG); + + /* Begin time registration of the computation */ + time_current(&time0); - nrealisations = args->nrealisations; + /* 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 boundary_flux_realisation_args realis_args = + 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; const struct sdis_medium *fmd, *bmd; enum sdis_side solid_side, fluid_side; struct bound_flux_result result = BOUND_FLUX_RESULT_NULL__; - const double Tref = scn->reference_temperature; - double epsilon, hc, hr, imposed_flux, imposed_temp; + double epsilon, hc, hr, imposed_flux, imposed_temp, Tref; size_t iprim; double uv[DIM - 1]; float st[DIM - 1]; @@ -638,6 +765,7 @@ XD(solve_boundary_flux) /* Fetch interface parameters */ epsilon = interface_side_get_emissivity(interf, &frag); + Tref = interface_side_get_reference_temperature(interf, &frag); hc = interface_get_convection_coef(interf, &frag); hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; frag.side = solid_side; @@ -655,8 +783,19 @@ XD(solve_boundary_flux) flux_mask = 0; if(hr > 0) flux_mask |= FLUX_FLAG_RADIATIVE; if(hc > 0) flux_mask |= FLUX_FLAG_CONVECTIVE; - res_simul = XD(boundary_flux_realisation)(scn, rng, iprim, uv, time, - solid_side, flux_mask, &result); + + /* Invoke the boundary flux realisation */ + realis_args.rng = rng; + realis_args.iprim = iprim; + realis_args.time = time; + realis_args.picard_order = args->picard_order; + realis_args.solid_side = solid_side; + realis_args.flux_mask = flux_mask; + realis_args.uv[0] = uv[0]; +#if SDIS_XD_DIMENSION == 3 + realis_args.uv[1] = uv[1]; +#endif + res_simul = XD(boundary_flux_realisation)(scn, &realis_args, &result); /* Stop time registration */ time_sub(&t0, time_current(&t1), &t0); @@ -701,52 +840,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)); @@ -754,10 +918,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,704 @@ +/* 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; + } + + /* Check RNG type */ + if(!args->rng_state && args->rng_type >= SSP_RNG_TYPES_COUNT__) { + 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, args->rng_state, args->rng_type, &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 @@ -135,6 +140,71 @@ sample_medium_enclosure return enc_cumul_found->enc; } +static INLINE res_T +check_solve_medium_args(const struct sdis_solve_medium_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; + } + + /* Check picard order */ + if(args->picard_order < 1) { + return RES_BAD_ARG; + } + + /* Check the RNG type */ + if(!args->rng_state && args->rng_type >= SSP_RNG_TYPES_COUNT__) { + return RES_BAD_ARG; + } + + 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; + } + + /* Check the RNG type */ + if(!args->rng_state && args->rng_type >= SSP_RNG_TYPES_COUNT__) { + return RES_BAD_ARG; + } + + return RES_OK; +} + #endif /* !SDIS_SOLVE_MEDIUM_XD_H */ /******************************************************************************* @@ -204,74 +274,73 @@ 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 || !args || !args->medium || !args->nrealisations - || args->nrealisations > INT64_MAX) { - res = RES_BAD_ARG; - goto error; - } - if(!out_estimator && !out_green) { + 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; + 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 " + "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; } - if(out_estimator) { - if(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 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, args->rng_type, &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); @@ -280,31 +349,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; @@ -321,14 +398,11 @@ XD(solve_medium) if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ 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; } @@ -348,9 +422,15 @@ XD(solve_medium) } /* Run a probe realisation */ - res_simul = XD(probe_realisation)((size_t)irealisation, scn, rng, - args->medium, pos, time, pgreen_path, pheat_path, &weight); - + realis_args.rng = rng; + realis_args.medium = args->medium; + realis_args.time = time; + realis_args.picard_order = args->picard_order; + realis_args.green_path = pgreen_path; + realis_args.heat_path = pheat_path; + realis_args.irealisation = (size_t)irealisation; + dX(set)(realis_args.position, pos); + res_simul = XD(probe_realisation)(scn, &realis_args, &weight); if(res_simul != RES_OK && res_simul != RES_BAD_OP) { ATOMIC_SET(&res, res_simul); goto error_it; @@ -390,9 +470,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); @@ -400,71 +480,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])); - } - 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(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; } @@ -474,39 +572,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"); @@ -514,32 +613,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, args->rng_type, &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 */ @@ -552,20 +646,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; @@ -606,52 +713,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" @@ -28,6 +29,46 @@ #include "sdis_Xd_begin.h" /******************************************************************************* + * Helper function + ******************************************************************************/ +#ifndef SDIS_SOLVE_PROBE_XD_H +#define SDIS_SOLVE_PROBE_XD_H + +static INLINE res_T +check_solve_probe_args(const struct sdis_solve_probe_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; + } + + /* Check the RNG type */ + if(!args->rng_state && args->rng_type >= SSP_RNG_TYPES_COUNT__) { + return RES_BAD_ARG; + } + + return RES_OK; +} + +#endif /* SDIS_SOLVE_PROBE_XD_H */ + +/******************************************************************************* * Generic solve function ******************************************************************************/ static res_T @@ -37,72 +78,72 @@ 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 || !args || !args->nrealisations || args->nrealisations > INT64_MAX) { - res = RES_BAD_ARG; - goto error; - } - if(!out_estimator && !out_green) { - res = RES_BAD_ARG; - goto error; - } - if(args->time_range[0] < 0 || args->time_range[1] < args->time_range[0]) { - res = RES_BAD_ARG; - goto error; - } - if(args->time_range[1] > DBL_MAX - && args->time_range[0] != args->time_range[1]) { + 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; + 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 " + "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; } -#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, args->rng_type, &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); @@ -110,31 +151,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; @@ -148,26 +197,33 @@ 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; } - res_simul = XD(probe_realisation)((size_t)irealisation, scn, rng, medium, - args->position, time, pgreen_path, pheat_path, &w); + /* Invoke the probe realisation */ + realis_args.rng = rng; + realis_args.medium = medium; + realis_args.time = time; + realis_args.picard_order = args->picard_order; + realis_args.green_path = pgreen_path; + realis_args.heat_path = pheat_path; + realis_args.irealisation = (size_t)irealisation; + dX(set)(realis_args.position, args->position); + res_simul = XD(probe_realisation)(scn, &realis_args, &w); /* Handle fatal error */ if(res_simul != RES_OK && res_simul != RES_BAD_OP) { @@ -204,84 +260,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])); - } - 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; } 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" @@ -28,6 +31,85 @@ #include "sdis_Xd_begin.h" /******************************************************************************* + * Helper function + ******************************************************************************/ +#ifndef SDIS_SOLVE_PROBE_BOUNDARY_XD_H +#define SDIS_SOLVE_PROBE_BOUNDARY_XD_H + +static INLINE res_T +check_solve_probe_boundary_args + (const struct sdis_solve_probe_boundary_args* args) +{ + if(!args) return RES_BAD_ARG; + + /* Check #realisations */ + if(!args->nrealisations || args->nrealisations > INT64_MAX) { + return RES_BAD_ARG; + } + + /* Check side */ + if((unsigned)args->side >= SDIS_SIDE_NULL__) { + 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; + } + + /* Check the RNG type */ + if(!args->rng_state && args->rng_type >= SSP_RNG_TYPES_COUNT__) { + return RES_BAD_ARG; + } + + 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; + } + + /* Check the RNG type */ + if(!args->rng_state && args->rng_type >= SSP_RNG_TYPES_COUNT__) { + return RES_BAD_ARG; + } + + return RES_OK; +} + +#endif /* SDIS_SOLVE_PROBE_BOUNDARY_XD_H */ + +/******************************************************************************* * Local functions ******************************************************************************/ static res_T @@ -37,141 +119,111 @@ 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 || !args || !args->nrealisations || args->nrealisations > INT64_MAX - || ((unsigned)args->side >= SDIS_SIDE_NULL__)) { - res = RES_BAD_ARG; - goto error; - } - if(!out_estimator && !out_green) { - res = RES_BAD_ARG; - goto error; - } - if(args->time_range[0] < 0 || args->time_range[1] < args->time_range[0]) { - res = RES_BAD_ARG; - goto error; - } - if(args->time_range[1] > DBL_MAX - && args->time_range[0] != args->time_range[1]) { - 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 + 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; + 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 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); + 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 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; - } - } +#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(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, args->rng_type, &rng_proxy, &per_thread_rng); + 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; } + /* Allocate the per process progress status */ + res = alloc_process_progress(scn->dev, &progress); + if(res != RES_OK) 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; @@ -190,11 +242,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; } @@ -203,8 +253,19 @@ XD(solve_probe_boundary) pheat_path = &heat_path; } - res_simul = XD(boundary_realisation)(scn, rng, args->iprim, args->uv, time, - args->side, pgreen_path, pheat_path, &w); + /* Invoke the boundary realisation */ + realis_args.rng = rng; + realis_args.iprim = args->iprim; + realis_args.time = time; + realis_args.picard_order = args->picard_order; + realis_args.side = args->side; + realis_args.green_path = pgreen_path; + realis_args.heat_path = pheat_path; + realis_args.uv[0] = args->uv[0]; +#if SDIS_XD_DIMENSION == 3 + realis_args.uv[1] = args->uv[1]; +#endif + res_simul = XD(boundary_realisation)(scn, &realis_args, &w); /* Handle fatal error */ if(res_simul != RES_OK && res_simul != RES_BAD_OP) { @@ -246,9 +307,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: @@ -257,70 +318,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; } @@ -330,86 +409,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, @@ -421,37 +477,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, args->rng_type, &rng_proxy, &per_thread_rng); + if(res != RES_OK) goto error; - /* Create the per thread accumulator */ + /* Allocate the per process progress status */ + res = alloc_process_progress(scn->dev, &progress); + if(res != RES_OK) goto error; + + /* 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 */ @@ -459,28 +508,41 @@ 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) { + struct boundary_flux_realisation_args realis_args = + 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__; - const double Tref = scn->reference_temperature; + double Tref = -1; size_t n; int pcent; res_T res_simul = RES_OK; @@ -496,6 +558,7 @@ XD(solve_probe_boundary_flux) frag.time = time; frag.side = fluid_side; epsilon = interface_side_get_emissivity(interf, &frag); + Tref = interface_side_get_reference_temperature(interf, &frag); hc = interface_get_convection_coef(interf, &frag); hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; frag.side = solid_side; @@ -503,7 +566,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; @@ -513,8 +577,19 @@ XD(solve_probe_boundary_flux) flux_mask = 0; if(hr > 0) flux_mask |= FLUX_FLAG_RADIATIVE; if(hc > 0) flux_mask |= FLUX_FLAG_CONVECTIVE; - res_simul = XD(boundary_flux_realisation)(scn, rng, args->iprim, args->uv, - time, solid_side, flux_mask, &result); + + /* Invoke the boundary flux realisation */ + realis_args.rng = rng; + realis_args.iprim = args->iprim; + realis_args.time = time; + realis_args.picard_order = args->picard_order; + realis_args.solid_side = solid_side; + realis_args.flux_mask = flux_mask; + realis_args.uv[0] = args->uv[0]; +#if SDIS_XD_DIMENSION == 3 + realis_args.uv[1] = args->uv[1]; +#endif + res_simul = XD(boundary_flux_realisation)(scn, &realis_args, &result); /* Stop time registration */ time_sub(&t0, time_current(&t1), &t0); @@ -559,60 +634,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; @@ -202,7 +201,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.volumic_power = solid_get_volumic_power; /* Create the fluid */ @@ -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 @@ -69,7 +69,7 @@ static const double vertices[16/*#vertices*/*3/*#coords per vertex*/] = { -1.5, 1.0, 1.0, 1.5, 1.0, 1.0, }; -static const size_t nvertices = sizeof(vertices) / (3*sizeof(double)); +static const size_t nvertices = sizeof(vertices) / (sizeof(double)*3); static const size_t indices[32/*#triangles*/*3/*#indices per triangle*/] = { 0, 2, 1, 1, 2, 3, /* Solid back face */ @@ -91,7 +91,7 @@ static const size_t indices[32/*#triangles*/*3/*#indices per triangle*/] = { 3, 7, 11, 11, 7, 15, /* Right fluid top face */ 1, 9, 5, 5, 9, 13 /* Right fluid bottom face */ }; -static const size_t ntriangles = sizeof(indices) / (3*sizeof(size_t)); +static const size_t ntriangles = sizeof(indices) / (sizeof(size_t)*3); static void get_indices(const size_t itri, size_t ids[3], void* ctx) @@ -193,6 +193,7 @@ struct interfac { double convection_coef; double emissivity; double specular_fraction; + double Tref; }; static double @@ -227,6 +228,14 @@ interface_get_specular_fraction return ((const struct interfac*)sdis_data_cget(data))->specular_fraction; } +static double +interface_get_Tref + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interfac*)sdis_data_cget(data))->Tref; +} + /******************************************************************************* * Helper functions ******************************************************************************/ @@ -251,10 +260,12 @@ create_interface if(sdis_medium_get_type(front) == SDIS_FLUID) { shader.front.emissivity = interface_get_emissivity; shader.front.specular_fraction = interface_get_specular_fraction; + shader.front.reference_temperature = interface_get_Tref; } if(sdis_medium_get_type(back) == SDIS_FLUID) { shader.back.emissivity = interface_get_emissivity; shader.back.specular_fraction = interface_get_specular_fraction; + shader.back.reference_temperature = interface_get_Tref; } shader.convection_coef_upper_bound = MMAX(0, interf->convection_coef); @@ -267,6 +278,54 @@ create_interface } /******************************************************************************* + * Test that the evaluation of the green function failed with a picard order + * greater than 1, i.e. when one want to handle the non-linearties of the + * system. + ******************************************************************************/ +static void +test_invalidity_picardN_green + (struct sdis_scene* scn, + struct sdis_medium* solid) +{ + struct sdis_solve_probe_args probe = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_solve_boundary_args bound = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT; + struct sdis_solve_medium_args mdm = SDIS_SOLVE_MEDIUM_ARGS_DEFAULT; + struct sdis_solve_probe_boundary_args probe_bound = + SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT; + + struct sdis_green_function* green = NULL; + CHK(scn); + + CHK(probe.picard_order == 1); + CHK(probe_bound.picard_order == 1); + CHK(bound.picard_order == 1); + CHK(mdm.picard_order == 1); + + probe.position[0] = 0; + probe.position[1] = 0; + probe.position[2] = 0; + probe.picard_order = 2; + BA(sdis_solve_probe_green_function(scn, &probe, &green)); + + probe_bound.iprim = 2; /* Solid left */ + probe_bound.uv[0] = 0.3; + probe_bound.uv[1] = 0.3; + probe_bound.side = SDIS_FRONT; + probe_bound.picard_order = 2; + BA(sdis_solve_probe_boundary_green_function(scn, &probe_bound, &green)); + + bound.primitives = &probe_bound.iprim; + bound.sides = &probe_bound.side; + bound.nprimitives = 1; + bound.picard_order = 2; + BA(sdis_solve_boundary_green_function(scn, &bound, &green)); + + mdm.medium = solid; + mdm.picard_order = 2; + BA(sdis_solve_medium_green_function(scn, &mdm, &green)); +} + +/******************************************************************************* * Test ******************************************************************************/ int @@ -283,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; @@ -295,12 +355,14 @@ main(int argc, char** argv) const double T0 = 300; /* Fixed temperature on the left side of the system */ const double T1 = 310; /* Fixed temperature on the right side of the system */ const double thickness = 2.0; /* Thickness of the solid along X */ + double t_range[2]; double Ts0, Ts1, hr, tmp; struct interfac* p_intface; (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; @@ -315,7 +377,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; OK(sdis_solid_create(dev, &solid_shader, data, &solid)); OK(sdis_data_ref_put(data)); @@ -327,7 +389,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = temperature_unknown; OK(sdis_solid_create(dev, &solid_shader, data, &solid2)); OK(sdis_data_ref_put(data)); @@ -337,6 +399,7 @@ main(int argc, char** argv) interf.convection_coef = -1; interf.emissivity = -1; interf.specular_fraction = -1; + interf.Tref = Tref; create_interface(dev, solid, solid2, &interf, interfaces+0); /* Create the interface that emits radiative heat from the solid */ @@ -344,6 +407,7 @@ main(int argc, char** argv) interf.convection_coef = 0; interf.emissivity = emissivity; interf.specular_fraction = 1; + interf.Tref = Tref; create_interface(dev, solid, fluid, &interf, interfaces+1); /* Create the interface that forces the radiative heat to bounce */ @@ -351,6 +415,7 @@ main(int argc, char** argv) interf.convection_coef = 0; interf.emissivity = 0; interf.specular_fraction = 1; + interf.Tref = Tref; create_interface(dev, fluid, solid2, &interf, interfaces+2); /* Create the interface with a limit condition of T0 Kelvin */ @@ -358,6 +423,7 @@ main(int argc, char** argv) interf.convection_coef = 0; interf.emissivity = 1; interf.specular_fraction = 1; + interf.Tref = T0; create_interface(dev, fluid, solid2, &interf, interfaces+3); /* Create the interface with a limit condition of T1 Kelvin */ @@ -365,6 +431,7 @@ main(int argc, char** argv) interf.convection_coef = 0; interf.emissivity = 1; interf.specular_fraction = 1; + interf.Tref = T1; create_interface(dev, fluid, solid2, &interf, interfaces+4); /* Setup the per primitive interface of the solid medium */ @@ -398,7 +465,8 @@ main(int argc, char** argv) scn_args.get_position = get_position; scn_args.nprimitives = ntriangles; scn_args.nvertices = nvertices; - scn_args.tref = Tref; + scn_args.t_range[0] = MMIN(T0, T1); + scn_args.t_range[1] = MMAX(T0, T1); scn_args.context = &geom; OK(sdis_scene_create(dev, &scn_args, &scn)); @@ -476,6 +544,9 @@ main(int argc, char** argv) /* Check same green used at a different temperature */ p_intface->temperature = T1b = T1 + ((double)isimul + 1) * 10; + t_range[0] = MMIN(T0, T1b); + t_range[1] = MMAX(T0, T1b); + OK(sdis_scene_set_temperature_range(scn, t_range)); OK(sdis_solve_probe(scn, &solve_args, &estimator)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); @@ -522,6 +593,8 @@ main(int argc, char** argv) printf("\n\n"); } + test_invalidity_picardN_green(scn, solid); + /* Release memory */ OK(sdis_scene_ref_put(scn)); OK(sdis_interface_ref_put(interfaces[0])); 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 @@ -58,7 +58,7 @@ static const double vertices[8/*#vertices*/*2/*#coords par vertex*/] = { -1.5, 1.0, 1.5, 1.0 }; -static const size_t nvertices = sizeof(vertices) / (2*sizeof(double)); +static const size_t nvertices = sizeof(vertices) / (sizeof(double)*2); static const size_t indices[10/*#segments*/*2/*#indices per segment*/] = { 0, 1, /* Solid bottom segment */ @@ -74,7 +74,7 @@ static const size_t indices[10/*#segments*/*2/*#indices per segment*/] = { 3, 7, /* Right fluid top segment */ 7, 4 /* Right fluid right segment */ }; -static const size_t nsegments = sizeof(indices) / (2*sizeof(size_t)); +static const size_t nsegments = sizeof(indices) / (sizeof(size_t)*2); static void get_indices(const size_t iseg, size_t ids[2], void* ctx) @@ -158,11 +158,12 @@ struct interfac { double temperature; double emissivity; double specular_fraction; + double reference_temperature; } front, back; }; static const struct interfac INTERFACE_NULL = { - 0, {-1, -1, -1}, {-1, -1, -1} + 0, {-1, -1, -1, -1}, {-1, -1, -1, -1} }; static double @@ -223,6 +224,22 @@ interface_get_specular_fraction return f; } +static double +interface_get_reference_temperature + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + const struct interfac* interf; + double T = -1; + CHK(data != NULL && frag != NULL); + interf = sdis_data_cget(data); + switch(frag->side) { + case SDIS_FRONT: T = interf->front.reference_temperature; break; + case SDIS_BACK: T = interf->back.reference_temperature; break; + default: FATAL("Unreachable code.\n"); break; + } + return T; +} + /******************************************************************************* * Helper functions ******************************************************************************/ @@ -250,10 +267,12 @@ create_interface if(type_f == SDIS_FLUID) { shader.front.emissivity = interface_get_emissivity; shader.front.specular_fraction = interface_get_specular_fraction; + shader.front.reference_temperature = interface_get_reference_temperature; } if(type_b == SDIS_FLUID) { shader.back.emissivity = interface_get_emissivity; shader.back.specular_fraction = interface_get_specular_fraction; + shader.back.reference_temperature = interface_get_reference_temperature; } shader.convection_coef_upper_bound = MMAX(0, interf->convection_coef); @@ -266,6 +285,52 @@ create_interface } /******************************************************************************* + * Test that the evaluation of the green function failed with a picard order + * greater than 1, i.e. when one want to handle the non-linearties of the + * system. + ******************************************************************************/ +static void +test_invalidity_picardN_green + (struct sdis_scene* scn, + struct sdis_medium* solid) +{ + struct sdis_solve_probe_args probe = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_solve_boundary_args bound = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT; + struct sdis_solve_medium_args mdm = SDIS_SOLVE_MEDIUM_ARGS_DEFAULT; + struct sdis_solve_probe_boundary_args probe_bound = + SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT; + + struct sdis_green_function* green = NULL; + CHK(scn); + + CHK(probe.picard_order == 1); + CHK(probe_bound.picard_order == 1); + CHK(bound.picard_order == 1); + CHK(mdm.picard_order == 1); + + probe.position[0] = 0; + probe.position[1] = 0; + probe.picard_order = 2; + BA(sdis_solve_probe_green_function(scn, &probe, &green)); + + probe_bound.iprim = 1; /* Solid left */ + probe_bound.uv[0] = 0.5; + probe_bound.side = SDIS_FRONT; + probe_bound.picard_order = 2; + BA(sdis_solve_probe_boundary_green_function(scn, &probe_bound, &green)); + + bound.primitives = &probe_bound.iprim; + bound.sides = &probe_bound.side; + bound.nprimitives = 1; + bound.picard_order = 2; + BA(sdis_solve_boundary_green_function(scn, &bound, &green)); + + mdm.medium = solid; + mdm.picard_order = 2; + BA(sdis_solve_medium_green_function(scn, &mdm, &green)); +} + +/******************************************************************************* * Test ******************************************************************************/ int @@ -298,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; @@ -311,7 +376,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = temperature_unknown; OK(sdis_solid_create(dev, &solid_shader, data, &solid)); OK(sdis_data_ref_put(data)); @@ -323,7 +388,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_thermal_conductivity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; OK(sdis_solid_create(dev, &solid_shader, data, &solid2)); OK(sdis_data_ref_put(data)); @@ -336,6 +401,7 @@ main(int argc, char** argv) interf.back.temperature = UNKNOWN_TEMPERATURE; interf.back.emissivity = emissivity; interf.back.specular_fraction = -1; /* Should not be fetched */ + interf.back.reference_temperature = Tref; create_interface(dev, solid, fluid, &interf, interfaces+1); /* Create the interface that forces the radiative heat to bounce */ @@ -343,6 +409,7 @@ main(int argc, char** argv) interf.front.temperature = UNKNOWN_TEMPERATURE; interf.front.emissivity = 0; interf.front.specular_fraction = 1; + interf.front.reference_temperature = Tref; create_interface(dev, fluid, solid2, &interf, interfaces+2); /* Create the interface with a limit condition of T0 Kelvin */ @@ -350,6 +417,7 @@ main(int argc, char** argv) interf.front.temperature = T0; interf.front.emissivity = 1; interf.front.specular_fraction = 1; + interf.front.reference_temperature = T0; create_interface(dev, fluid, solid2, &interf, interfaces+3); /* Create the interface with a limit condition of T1 Kelvin */ @@ -357,6 +425,7 @@ main(int argc, char** argv) interf.front.temperature = T1; interf.front.emissivity = 1; interf.front.specular_fraction = 1; + interf.front.reference_temperature = T1; create_interface(dev, fluid, solid2, &interf, interfaces+4); /* Setup the per primitive interface of the solid medium */ @@ -384,8 +453,9 @@ main(int argc, char** argv) scn_args.get_position = get_position; scn_args.nprimitives = nsegments; scn_args.nvertices = nvertices; - scn_args.tref = Tref; scn_args.context = &geom; + scn_args.t_range[0] = MMIN(T0, T1); + scn_args.t_range[1] = MMAX(T0, T1); OK(sdis_scene_2d_create(dev, &scn_args, &scn)); hr = 4*BOLTZMANN_CONSTANT * Tref*Tref*Tref * emissivity; @@ -450,6 +520,8 @@ main(int argc, char** argv) printf("\n\n"); } + test_invalidity_picardN_green(scn, solid); + /* Release memory */ OK(sdis_scene_ref_put(scn)); OK(sdis_interface_ref_put(interfaces[0])); 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)); @@ -285,7 +283,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; /* Create the solid medium #1 */ @@ -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 @@ -37,8 +37,7 @@ /******************************************************************************* * Box geometry ******************************************************************************/ -static const double model3d_vertices[12/*#vertices*/ * 3/*#coords per vertex*/] -= { +static const double model3d_vertices[12/*#vertices*/*3/*#coords per vertex*/] = { 0, 0, 0, X0, 0, 0, L, 0, 0, @@ -52,7 +51,7 @@ static const double model3d_vertices[12/*#vertices*/ * 3/*#coords per vertex*/] X0, L, L, L, L, L }; -static const size_t model3d_nvertices = sizeof(model3d_vertices) / (3*sizeof(double)); +static const size_t model3d_nvertices = sizeof(model3d_vertices)/(sizeof(double)*3); /* The following array lists the indices toward the 3D vertices of each * triangle. @@ -64,8 +63,7 @@ static const size_t model3d_nvertices = sizeof(model3d_vertices) / (3*sizeof(dou * 6----7----8' 6----7'---8' 7 / * Front, right Back, left and Internal Z * and Top faces bottom faces face */ -static const size_t model3d_indices[22/*#triangles*/ * 3/*#indices per triangle*/] -= { +static const size_t model3d_indices[22/*#triangles*/*3/*#indices per triangle*/] = { 0, 3, 1, 1, 3, 4, 1, 4, 2, 2, 4, 5, /* -Z */ 0, 6, 3, 3, 6, 9, /* -X */ 6, 7, 9, 9, 7, 10, 7, 8, 10, 10, 8, 11, /* +Z */ @@ -74,7 +72,7 @@ static const size_t model3d_indices[22/*#triangles*/ * 3/*#indices per triangle* 0, 1, 7, 7, 6, 0, 1, 2, 8, 8, 7, 1, /* -Y */ 4, 10, 7, 7, 1, 4 /* Inside */ }; -static const size_t model3d_ntriangles = sizeof(model3d_indices) / (3*sizeof(size_t)); +static const size_t model3d_ntriangles = sizeof(model3d_indices)/(sizeof(size_t)*3); static INLINE void model3d_get_indices(const size_t itri, size_t ids[3], void* context) @@ -110,7 +108,7 @@ model3d_get_interface(const size_t itri, struct sdis_interface** bound, void* co /******************************************************************************* * Square geometry ******************************************************************************/ -static const double model2d_vertices[6/*#vertices*/ * 2/*#coords per vertex*/] = { +static const double model2d_vertices[6/*#vertices*/*2/*#coords per vertex*/] = { L, 0, X0, 0, 0, 0, @@ -118,7 +116,7 @@ static const double model2d_vertices[6/*#vertices*/ * 2/*#coords per vertex*/] = X0, L, L, L }; -static const size_t model2d_nvertices = sizeof(model2d_vertices) / (2*sizeof(double)); +static const size_t model2d_nvertices = sizeof(model2d_vertices)/(sizeof(double)*2); static const size_t model2d_indices[7/*#segments*/ * 2/*#indices per segment*/] = { 0, 1, 1, 2, /* Bottom */ @@ -127,8 +125,7 @@ static const size_t model2d_indices[7/*#segments*/ * 2/*#indices per segment*/] 5, 0, /* Right */ 4, 1 /* Inside */ }; -static const size_t model2d_nsegments = sizeof(model2d_indices) / (2*sizeof(size_t)); - +static const size_t model2d_nsegments = sizeof(model2d_indices) / (sizeof(size_t)*2); static INLINE void model2d_get_indices(const size_t iseg, size_t ids[2], void* context) 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 @@ -54,8 +54,8 @@ #define TL 100.0 #define LAMBDA2 0.2 -#define DELTA1 X0/25.0 -#define DELTA2 (L-X0)/25.0 +#define DELTA1 X0/30.0 +#define DELTA2 (L-X0)/30.0 /******************************************************************************* * Media @@ -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)); @@ -379,7 +377,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; /* Create the solid medium #1 */ @@ -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 @@ -153,6 +153,7 @@ solve ASSERT(scn && rng && interf); OK(sdis_scene_get_dimension(scn, &dim)); + FOR_EACH(isimul, 0, nsimuls) { int steady = (isimul % 2) == 0; @@ -335,6 +336,15 @@ solve printf("\n\n"); } + + /* Picard N is not supported with a flux != 0 */ + solve_args.position[0] = 0.1; + solve_args.position[1] = 0.1; + solve_args.position[2] = dim == SDIS_SCENE_2D ? 0 : 0.1; + solve_args.time_range[0] = INF; + solve_args.time_range[1] = INF; + solve_args.picard_order = 2; + BA(sdis_solve_probe(scn, &solve_args, &estimator)); } /******************************************************************************* @@ -343,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; @@ -363,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)); @@ -373,7 +381,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -449,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"); @@ -460,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)); @@ -85,7 +83,7 @@ main(int argc, char** argv) CHK(sdis_medium_get_type(solid) == SDIS_SOLID); CHK(sdis_medium_get_data(solid) == data); - OK(sdis_medium_ref_put(solid)); + OK(sdis_medium_ref_put(solid)); OK(sdis_data_ref_put(data)); solid_shader.calorific_capacity = NULL; @@ -100,9 +98,9 @@ main(int argc, char** argv) BA(sdis_solid_create(dev, &solid_shader, NULL, &solid)); solid_shader.volumic_mass = DUMMY_SOLID_SHADER.volumic_mass; - solid_shader.delta_solid = NULL; + solid_shader.delta = NULL; BA(sdis_solid_create(dev, &solid_shader, NULL, &solid)); - solid_shader.delta_solid = DUMMY_SOLID_SHADER.delta_solid; + solid_shader.delta = DUMMY_SOLID_SHADER.delta; solid_shader.temperature = NULL; BA(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -137,7 +135,7 @@ main(int argc, char** argv) CHK(solid_shader.calorific_capacity == solid_shader2.calorific_capacity); CHK(solid_shader.thermal_conductivity == solid_shader2.thermal_conductivity); CHK(solid_shader.volumic_mass == solid_shader2.volumic_mass); - CHK(solid_shader.delta_solid == solid_shader2.delta_solid); + CHK(solid_shader.delta == solid_shader2.delta); CHK(solid_shader.volumic_power == solid_shader2.volumic_power); CHK(solid_shader.temperature == solid_shader2.temperature); CHK(solid_shader.t0 == solid_shader2.t0); @@ -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 @@ -0,0 +1,776 @@ +/* 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" + +#include <string.h> + +#define UNKNOWN_TEMPERATURE -1 +#define N 10000 + +/* This test consists in solving the stationary temperature profile in a solid + * slab surrounded by two different radiative temperatures (left / right). The + * conductivity of the solid material is known, as well as its thickness and + * the source term (volumic power density). + * + * The purpose is to test the Picard radiative transfer algorithm, that can be + * compared with analytic results. This algorithm can use a possibly + * non-uniform reference temperature field. When the reference temperature + * field is uniform and the picard order set to 1, results should be identical + * to the classical Monte-Carlo algorithm (using a linearized radiative + * transfer scheme). + * + * Y + * | (0.1,1) + * o--- X +----------+------+ (1.1,1) + * |##########| | + * |##########| | + * 280K E=1|##########| E=1 | 350K + * |##########| | + * |##########| | + * (-1,-1) +----------+------+ + * (0,-1) + * + * + * + * Y (0.1, 1, 1) + * | +----------+------+ (1.1,1,1) + * o--- X /##########/' /| + * / +----------+------+ | + * Z |##########|*' | | 350K + * |##########|*' | | + * 280K E=1|##########|*'E=1 | | + * |##########|*+....|.+ + * |##########|/ |/ + * (-1,-1,-1) +----------+------+ + * (0,-1,-1) + * + * lambda = 1.15 W/(m.K) + * rho = 1000 kg.m^-3 + * cp = 800 J/(kg.K) + * emissivity = 1 + * + * Basic Tref = 300 K + * probe = 0.05 0 0 m + * (power = 1000 W.m^-3) */ + +enum interface_type { + ADIABATIC, + SOLID_FLUID_mX, + SOLID_FLUID_pX, + BOUNDARY_pX, + INTERFACES_COUNT__ +}; + +/******************************************************************************* + * Geometry + ******************************************************************************/ +struct geometry { + const double* positions; + const size_t* indices; + struct sdis_interface** interfaces; +}; + +static const double vertices_2d[6/*#vertices*/*2/*#coords par vertex*/] = { + 0.1, -1.0, + 0.0, -1.0, + 0.0, 1.0, + 0.1, 1.0, + 1.1, -1.0, + 1.1, 1.0 +}; +static const size_t nvertices_2d = sizeof(vertices_2d) / (sizeof(double)*2); + +static const size_t indices_2d[7/*#segments*/*2/*#indices per segment*/] = { + 0, 1, /* Solid -Y */ + 1, 2, /* Solid -X */ + 2, 3, /* Solid +Y */ + 3, 0, /* Solid +X */ + + 4, 0, /* Right fluid -Y */ + 3, 5, /* Right fluid +Y */ + 5, 4 /* Right fluid +X */ +}; +static const size_t nprimitives_2d = sizeof(indices_2d) / (sizeof(size_t)*2); + +static const double vertices_3d[12/*#vertices*/*3/*#coords per vertex*/] = { + 0.0,-1.0,-1.0, + 0.1,-1.0,-1.0, + 0.0, 1.0,-1.0, + 0.1, 1.0,-1.0, + 0.0,-1.0, 1.0, + 0.1,-1.0, 1.0, + 0.0, 1.0, 1.0, + 0.1, 1.0, 1.0, + 1.1,-1.0,-1.0, + 1.1, 1.0,-1.0, + 1.1,-1.0, 1.0, + 1.1, 1.0, 1.0 +}; +static const size_t nvertices_3d = sizeof(vertices_3d) / (sizeof(double)*3); + +static const size_t indices_3d[22/*#triangles*/*3/*#indices per triangle*/] = { + 0, 2, 1, 1, 2, 3, /* Solid -Z */ + 0, 4, 2, 2, 4, 6, /* Solid -X */ + 4, 5, 6, 6, 5, 7, /* Solid +Z */ + 3, 7, 1, 1, 7, 5, /* Solid +X */ + 2, 6, 3, 3, 6, 7, /* Solid +Y */ + 0, 1, 4, 4, 1, 5, /* Solid -Y */ + + 1, 3, 8, 8, 3, 9, /* Right fluid -Z */ + 5, 10, 7, 7, 10, 11, /* Right fluid +Z */ + 9, 11, 8, 8, 11, 10, /* Right fluid +X */ + 3, 7, 9, 9, 7, 11, /* Right fluid +Y */ + 1, 8, 5, 5, 8, 10 /* Right fluid -Y */ +}; +static const size_t nprimitives_3d = sizeof(indices_3d) / (sizeof(size_t)*3); + +static void +get_indices_2d(const size_t iseg, size_t ids[2], void* ctx) +{ + struct geometry* geom = ctx; + CHK(ctx != NULL); + ids[0] = geom->indices[iseg*2+0]; + ids[1] = geom->indices[iseg*2+1]; +} + +static void +get_indices_3d(const size_t itri, size_t ids[3], void* ctx) +{ + struct geometry* geom = ctx; + CHK(ctx != NULL); + ids[0] = geom->indices[itri*3+0]; + ids[1] = geom->indices[itri*3+1]; + ids[2] = geom->indices[itri*3+2]; +} + +static void +get_position_2d(const size_t ivert, double pos[2], void* ctx) +{ + struct geometry* geom = ctx; + CHK(ctx != NULL); + pos[0] = geom->positions[ivert*2+0]; + pos[1] = geom->positions[ivert*2+1]; +} + +static void +get_position_3d(const size_t ivert, double pos[3], void* ctx) +{ + struct geometry* geom = ctx; + CHK(ctx != NULL); + pos[0] = geom->positions[ivert*3+0]; + pos[1] = geom->positions[ivert*3+1]; + pos[2] = geom->positions[ivert*3+2]; +} + +static void +get_interface(const size_t iprim, struct sdis_interface** bound, void* ctx) +{ + struct geometry* geom = ctx; + CHK(ctx != NULL); + *bound = geom->interfaces[iprim]; +} + +/******************************************************************************* + * media + ******************************************************************************/ +struct solid { + double lambda; + double rho; + double cp; + double volumic_power; +}; + +static double +solid_get_calorific_capacity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + const struct solid* solid = sdis_data_cget(data); + CHK(vtx && solid); + return solid->cp; +} + +static double +solid_get_thermal_conductivity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + const struct solid* solid = sdis_data_cget(data); + CHK(vtx && solid); + return solid->lambda; +} + +static double +solid_get_volumic_mass + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + const struct solid* solid = sdis_data_cget(data); + CHK(vtx && solid); + return solid->rho; +} + +static double +solid_get_delta + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(vtx && data); + return 0.005; +} + +static double +solid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(vtx && data); + return UNKNOWN_TEMPERATURE; +} + +static double +solid_get_volumic_power + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + const struct solid* solid = sdis_data_cget(data); + CHK(vtx && solid); + return solid->volumic_power; +} + +static void +create_solid + (struct sdis_device* dev, + const struct solid* solid_props, + struct sdis_medium** solid) +{ + struct sdis_data* data = NULL; + struct sdis_solid_shader shader = SDIS_SOLID_SHADER_NULL; + CHK(dev && solid_props && solid); + + OK(sdis_data_create + (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); + memcpy(sdis_data_get(data), solid_props, sizeof(struct solid)); + shader.calorific_capacity = solid_get_calorific_capacity; + shader.thermal_conductivity = solid_get_thermal_conductivity; + shader.volumic_mass = solid_get_volumic_mass; + shader.delta = solid_get_delta; + shader.temperature = solid_get_temperature; + shader.volumic_power = solid_get_volumic_power; + OK(sdis_solid_create(dev, &shader, data, solid)); + OK(sdis_data_ref_put(data)); +} + +static void +create_fluid(struct sdis_device* dev, struct sdis_medium** fluid) +{ + struct sdis_fluid_shader shader = DUMMY_FLUID_SHADER; + OK(sdis_fluid_create(dev, &shader, NULL, fluid)); +} + +/******************************************************************************* + * Interface + ******************************************************************************/ +struct interf { + double temperature; + double h; + double emissivity; + double specular_fraction; + double Tref; +}; + +static double +interface_get_temperature + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + const struct interf* interf = sdis_data_cget(data); + CHK(frag && interf); + return interf->temperature; +} + +static double +interface_get_convection_coef + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + const struct interf* interf = sdis_data_cget(data); + CHK(frag && interf); + return interf->h; +} + +static double +interface_get_emissivity + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + const struct interf* interf = sdis_data_cget(data); + CHK(frag && interf); + return interf->emissivity; +} + +static double +interface_get_specular_fraction + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + const struct interf* interf = sdis_data_cget(data); + CHK(frag && interf); + return interf->specular_fraction; +} + +static double +interface_get_Tref + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + const struct interf* interf = sdis_data_cget(data); + CHK(frag && interf); + return interf->Tref; +} + +static void +create_interface + (struct sdis_device* dev, + struct sdis_medium* front, + struct sdis_medium* back, + const struct interf* interf, + struct sdis_interface** out_interf) +{ + struct sdis_interface_shader shader = SDIS_INTERFACE_SHADER_NULL; + struct sdis_data* data = NULL; + + CHK(interf != NULL); + + shader.front.temperature = interface_get_temperature; + shader.back.temperature = interface_get_temperature; + if(sdis_medium_get_type(front) != sdis_medium_get_type(back)) { + shader.convection_coef = interface_get_convection_coef; + } + if(sdis_medium_get_type(front) == SDIS_FLUID) { + shader.front.emissivity = interface_get_emissivity; + shader.front.specular_fraction = interface_get_specular_fraction; + shader.front.reference_temperature = interface_get_Tref; + } + if(sdis_medium_get_type(back) == SDIS_FLUID) { + shader.back.emissivity = interface_get_emissivity; + shader.back.specular_fraction = interface_get_specular_fraction; + shader.back.reference_temperature = interface_get_Tref; + } + shader.convection_coef_upper_bound = MMAX(0, interf->h); + + OK(sdis_data_create + (dev, sizeof(struct interf), ALIGNOF(struct interf), NULL, &data)); + memcpy(sdis_data_get(data), interf, sizeof(*interf)); + + OK(sdis_interface_create(dev, front, back, &shader, data, out_interf)); + OK(sdis_data_ref_put(data)); +} + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +struct reference_result { + double T; /* Temperature at the center of the solid [K] */ + double T1; /* Temperature on the left boundary of the solid [K] */ + double T2; /* Temperature on the right boundary of the solid [K] */ +}; +static const struct reference_result REFERENCE_RESULT_NULL = {0,0,0}; + +static void +test_picard + (struct sdis_scene* scn, + const size_t picard_order, + const struct reference_result* ref) +{ + struct sdis_solve_probe_args probe_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_solve_boundary_args bound_args = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT; + struct sdis_mc mc = SDIS_MC_NULL; + struct sdis_estimator* estimator = NULL; + enum sdis_scene_dimension dim; + size_t prims[2]; + enum sdis_side sides[2]; + CHK(scn && ref && picard_order >= 1); + + OK(sdis_scene_get_dimension(scn, &dim)); + switch(dim) { + case SDIS_SCENE_2D: printf(">>> 2D\n"); break; + case SDIS_SCENE_3D: printf(">>> 3D\n"); break; + default: FATAL("Unreachable code.\n"); break; + } + + probe_args.nrealisations = N; + probe_args.position[0] = 0.05; + probe_args.position[1] = 0; + probe_args.position[2] = 0; + probe_args.picard_order = picard_order; + OK(sdis_solve_probe(scn, &probe_args, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &mc)); + printf("Temperature at `%g %g %g' = %g ~ %g +/- %g\n", + SPLIT3(probe_args.position), ref->T, mc.E, mc.SE); + CHK(eq_eps(ref->T, mc.E, mc.SE*3)); + OK(sdis_estimator_ref_put(estimator)); + + switch(dim) { + case SDIS_SCENE_2D: + prims[0] = 1; sides[0] = SDIS_BACK; + bound_args.nprimitives = 1; + break; + case SDIS_SCENE_3D: + prims[0] = 2; sides[0] = SDIS_BACK; + prims[1] = 3; sides[1] = SDIS_BACK; + bound_args.nprimitives = 2; + break; + default: FATAL("Unreachable code.\n"); break; + } + bound_args.nrealisations = N; + bound_args.primitives = prims; + bound_args.sides = sides; + bound_args.picard_order = picard_order; + OK(sdis_solve_boundary(scn, &bound_args, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &mc)); + printf("T1 = %g ~ %g +/- %g\n", ref->T1, mc.E, mc.SE); + CHK(eq_eps(ref->T1, mc.E, mc.SE*3)); + OK(sdis_estimator_ref_put(estimator)); + + switch(dim) { + case SDIS_SCENE_2D: + prims[0] = 3; sides[0] = SDIS_BACK; + bound_args.nprimitives = 1; + break; + case SDIS_SCENE_3D: + prims[0] = 6; sides[0] = SDIS_BACK; + prims[1] = 7; sides[1] = SDIS_BACK; + bound_args.nprimitives = 2; + break; + default: FATAL("Unreachable code.\n"); break; + } + bound_args.nrealisations = N; + bound_args.primitives = prims; + bound_args.sides = sides; + bound_args.picard_order = picard_order; + OK(sdis_solve_boundary(scn, &bound_args, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &mc)); + printf("T2 = %g ~ %g +/- %g\n", ref->T2, mc.E, mc.SE); + CHK(eq_eps(ref->T2, mc.E, mc.SE*3)); + OK(sdis_estimator_ref_put(estimator)); +} + +static void +register_heat_paths(struct sdis_scene* scn, const size_t picard_order, FILE* stream) +{ + struct sdis_solve_probe_args probe_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_estimator* estimator = NULL; + CHK(scn && picard_order >= 1 && stream); + + + probe_args.nrealisations = 10; + probe_args.position[0] = 0.05; + probe_args.position[1] = 0; + probe_args.position[2] = 0; + probe_args.picard_order = picard_order; + probe_args.register_paths = SDIS_HEAT_PATH_ALL; + printf("Register %lu heat paths.\n", probe_args.nrealisations); + OK(sdis_solve_probe(scn, &probe_args, &estimator)); + dump_heat_paths(stream, estimator); + OK(sdis_estimator_ref_put(estimator)); +} + +static void +create_scene_3d + (struct sdis_device* dev, + struct sdis_interface* interfaces[INTERFACES_COUNT__], + struct sdis_scene** scn) +{ + struct geometry geom; + struct sdis_interface* prim_interfaces[32]; + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + + CHK(dev && interfaces && scn); + + /* Setup the per primitive interface of the solid medium */ + prim_interfaces[0] = prim_interfaces[1] = interfaces[ADIABATIC]; + prim_interfaces[2] = prim_interfaces[3] = interfaces[SOLID_FLUID_mX]; + prim_interfaces[4] = prim_interfaces[5] = interfaces[ADIABATIC]; + prim_interfaces[6] = prim_interfaces[7] = interfaces[SOLID_FLUID_pX]; + prim_interfaces[8] = prim_interfaces[9] = interfaces[ADIABATIC]; + prim_interfaces[10] = prim_interfaces[11] = interfaces[ADIABATIC]; + + /* Setup the per primitive interface for the right fluid */ + prim_interfaces[12] = prim_interfaces[13] = interfaces[BOUNDARY_pX]; + prim_interfaces[14] = prim_interfaces[15] = interfaces[BOUNDARY_pX]; + prim_interfaces[16] = prim_interfaces[17] = interfaces[BOUNDARY_pX]; + prim_interfaces[18] = prim_interfaces[19] = interfaces[BOUNDARY_pX]; + prim_interfaces[20] = prim_interfaces[21] = interfaces[BOUNDARY_pX]; + + /* Create the scene */ + geom.positions = vertices_3d; + geom.indices = indices_3d; + geom.interfaces = prim_interfaces; + scn_args.get_indices = get_indices_3d; + scn_args.get_interface = get_interface; + scn_args.get_position = get_position_3d; + scn_args.nprimitives = nprimitives_3d; + scn_args.nvertices = nvertices_3d; + scn_args.t_range[0] = 280; + scn_args.t_range[1] = 350; + scn_args.context = &geom; + OK(sdis_scene_create(dev, &scn_args, scn)); +} + +static void +create_scene_2d + (struct sdis_device* dev, + struct sdis_interface* interfaces[INTERFACES_COUNT__], + struct sdis_scene** scn) +{ + struct geometry geom; + struct sdis_interface* prim_interfaces[10/*#segment*/]; + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + + CHK(dev && interfaces && scn); + + /* Setup the per primitive interface of the solid medium */ + prim_interfaces[0] = interfaces[ADIABATIC]; + prim_interfaces[1] = interfaces[SOLID_FLUID_mX]; + prim_interfaces[2] = interfaces[ADIABATIC]; + prim_interfaces[3] = interfaces[SOLID_FLUID_pX]; + + /* Setup the per primitive interface of the fluid on the right of the medium */ + prim_interfaces[4] = interfaces[BOUNDARY_pX]; + prim_interfaces[5] = interfaces[BOUNDARY_pX]; + prim_interfaces[6] = interfaces[BOUNDARY_pX]; + + /* Create the scene */ + geom.positions = vertices_2d; + geom.indices = indices_2d; + geom.interfaces = prim_interfaces; + scn_args.get_indices = get_indices_2d; + scn_args.get_interface = get_interface; + scn_args.get_position = get_position_2d; + scn_args.nprimitives = nprimitives_2d; + scn_args.nvertices = nvertices_2d; + scn_args.t_range[0] = 280; + scn_args.t_range[1] = 350; + scn_args.context = &geom; + OK(sdis_scene_2d_create(dev, &scn_args, scn)); +} + +/******************************************************************************* + * Test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + FILE* stream = NULL; + + struct sdis_device* dev = NULL; + struct sdis_scene* scn_2d = NULL; + struct sdis_scene* scn_3d = NULL; + struct sdis_medium* solid = NULL; + struct sdis_medium* fluid = NULL; + struct sdis_medium* dummy = NULL; + struct sdis_interface* interfaces[INTERFACES_COUNT__]; + struct sdis_ambient_radiative_temperature amb_rad_temp; + + struct solid solid_props; + struct solid* psolid_props; + struct reference_result ref = REFERENCE_RESULT_NULL; + struct interf interf_props; + struct interf* pinterf_props[INTERFACES_COUNT__]; + + double t_range[2]; + + size_t i; + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); + + /* Solid medium */ + solid_props.lambda = 1.15; + solid_props.rho = 1000; + solid_props.cp = 800; + solid_props.volumic_power = SDIS_VOLUMIC_POWER_NONE; + create_solid(dev, &solid_props, &solid); + + /* Dummy solid medium */ + solid_props.lambda = 0; + solid_props.rho = 1000; + solid_props.cp = 800; + solid_props.volumic_power = SDIS_VOLUMIC_POWER_NONE; + create_solid(dev, &solid_props, &dummy); + + /* Fluid medium */ + create_fluid(dev, &fluid); + + /* Create the adiabatic interface for the solid */ + interf_props.temperature = UNKNOWN_TEMPERATURE; + interf_props.h = -1; + interf_props.emissivity = -1; + interf_props.specular_fraction = -1; + interf_props.Tref = UNKNOWN_TEMPERATURE; + create_interface(dev, solid, dummy, &interf_props, interfaces+ADIABATIC); + + /* Create the interface between the solid and the fluid */ + interf_props.temperature = UNKNOWN_TEMPERATURE; + interf_props.h = 0; + interf_props.emissivity = 1; + interf_props.specular_fraction = 0; + interf_props.Tref = 280; + create_interface(dev, solid, fluid, &interf_props, interfaces+SOLID_FLUID_mX); + interf_props.Tref = 350; + create_interface(dev, solid, fluid, &interf_props, interfaces+SOLID_FLUID_pX); + + /* Create the interface for the fluid on the right */ + interf_props.temperature = 350; + interf_props.h = -1; + interf_props.emissivity = 1; + interf_props.specular_fraction = -1; + interf_props.Tref = 350; + create_interface(dev, fluid, dummy, &interf_props, interfaces+BOUNDARY_pX); + + /* Fetch pointers toward the solid and the interfaces */ + psolid_props = sdis_data_get(sdis_medium_get_data(solid)); + FOR_EACH(i, 0, INTERFACES_COUNT__) { + pinterf_props[i] = sdis_data_get(sdis_interface_get_data(interfaces[i])); + } + + create_scene_2d(dev, interfaces, &scn_2d); + create_scene_3d(dev, interfaces, &scn_3d); + + CHK((stream = tmpfile()) != NULL); + + /* Test picard1 with a constant Tref <=> regular linearisation */ + printf("Test Picard1 with a constant Tref of 300 K\n"); + ref.T = 314.99999999999989; + ref.T1 = 307.64122364709766; + ref.T2 = 322.35877635290217; + pinterf_props[SOLID_FLUID_mX]->Tref = 300; + pinterf_props[SOLID_FLUID_pX]->Tref = 300; + pinterf_props[BOUNDARY_pX]->Tref = 300; + amb_rad_temp.temperature = 280; + amb_rad_temp.reference = 300; + OK(sdis_scene_set_ambient_radiative_temperature(scn_2d, &amb_rad_temp)); + OK(sdis_scene_set_ambient_radiative_temperature(scn_3d, &amb_rad_temp)); + test_picard(scn_2d, 1/*Picard order*/, &ref); + test_picard(scn_3d, 1/*Picard order*/, &ref); + printf("\n"); + + /* Test picard1 using T4 as a reference */ + printf("Test Picard1 using T4 as a reference\n"); + ref.T = 320.37126474482994; + ref.T1 = 312.12650299072266; + ref.T2 = 328.61602649893723; + pinterf_props[SOLID_FLUID_mX]->Tref = ref.T1; + pinterf_props[SOLID_FLUID_pX]->Tref = ref.T2; + pinterf_props[BOUNDARY_pX]->Tref = 350; + amb_rad_temp.temperature = 280; + amb_rad_temp.reference = 280; + OK(sdis_scene_set_ambient_radiative_temperature(scn_2d, &amb_rad_temp)); + OK(sdis_scene_set_ambient_radiative_temperature(scn_3d, &amb_rad_temp)); + test_picard(scn_2d, 1/*Picard order*/, &ref); + test_picard(scn_3d, 1/*Picard order*/, &ref); + printf("\n"); + + /* Test picard2 */ + printf("Test Picard2 with a constant Tref of 300K\n"); + ref.T = 320.37126474482994; + ref.T1 = 312.12650299072266; + ref.T2 = 328.61602649893723; + pinterf_props[SOLID_FLUID_mX]->Tref = 300; + pinterf_props[SOLID_FLUID_pX]->Tref = 300; + pinterf_props[BOUNDARY_pX]->Tref = 300; + amb_rad_temp.temperature = 280; + amb_rad_temp.reference = 300; + OK(sdis_scene_set_ambient_radiative_temperature(scn_2d, &amb_rad_temp)); + OK(sdis_scene_set_ambient_radiative_temperature(scn_3d, &amb_rad_temp)); + test_picard(scn_2d, 2/*Picard order*/, &ref); + test_picard(scn_3d, 2/*Picard order*/, &ref); + printf("\n"); + + t_range[0] = 200; + t_range[1] = 500; + + OK(sdis_scene_set_temperature_range(scn_2d, t_range)); + OK(sdis_scene_set_temperature_range(scn_3d, t_range)); + + /* Test picard3 */ + printf("Test Picard3 with a delta T of 300K\n"); + ref.T = 416.4023; + ref.T1 = 372.7557; + ref.T2 = 460.0489; + pinterf_props[BOUNDARY_pX]->temperature = t_range[1]; + pinterf_props[SOLID_FLUID_mX]->Tref = 350; + pinterf_props[SOLID_FLUID_pX]->Tref = 450; + pinterf_props[BOUNDARY_pX]->Tref = pinterf_props[BOUNDARY_pX]->temperature; + amb_rad_temp.temperature = t_range[0]; + amb_rad_temp.reference = t_range[0]; + OK(sdis_scene_set_ambient_radiative_temperature(scn_2d, &amb_rad_temp)); + OK(sdis_scene_set_ambient_radiative_temperature(scn_3d, &amb_rad_temp)); + test_picard(scn_2d, 3/*Picard order*/, &ref); + test_picard(scn_3d, 3/*Picard order*/, &ref); + register_heat_paths(scn_2d, 3/*Picard order*/, stream); + register_heat_paths(scn_3d, 3/*Picard order*/, stream); + printf("\n"); + + t_range[0] = 280; + t_range[1] = 350; + OK(sdis_scene_set_temperature_range(scn_2d, t_range)); + OK(sdis_scene_set_temperature_range(scn_3d, t_range)); + pinterf_props[BOUNDARY_pX]->temperature = t_range[1]; + + /* Add volumic power */ + psolid_props->volumic_power = 1000; + + /* Test picard1 with a volumic power and constant Tref */ + printf("Test Picard1 with a volumic power of 1000 W/m^3 and a constant Tref " + "of 300 K\n"); + ref.T = 324.25266420769509; + ref.T1 = 315.80693133305368; + ref.T2 = 330.52448403885825; + pinterf_props[SOLID_FLUID_mX]->Tref = 300; + pinterf_props[SOLID_FLUID_pX]->Tref = 300; + pinterf_props[BOUNDARY_pX]->Tref = 300; + amb_rad_temp.temperature = t_range[0]; + amb_rad_temp.reference = 300; + OK(sdis_scene_set_ambient_radiative_temperature(scn_2d, &amb_rad_temp)); + OK(sdis_scene_set_ambient_radiative_temperature(scn_3d, &amb_rad_temp)); + test_picard(scn_2d, 1/*Picard order*/, &ref); + test_picard(scn_3d, 1/*Picard order*/, &ref); + printf("\n"); + + /* Test picard1 with a volumic power and T4 a the reference */ + printf("Test Picard1 with a volumic power of 1000 W/m^3 and T4 as a reference\n"); + ref.T = 327.95981050850446; + ref.T1 = 318.75148773193359; + ref.T2 = 334.99422024159708; + pinterf_props[SOLID_FLUID_mX]->Tref = ref.T1; + pinterf_props[SOLID_FLUID_pX]->Tref = ref.T2; + pinterf_props[BOUNDARY_pX]->Tref = 350; + amb_rad_temp.temperature = 280; + amb_rad_temp.reference = 280; + OK(sdis_scene_set_ambient_radiative_temperature(scn_2d, &amb_rad_temp)); + OK(sdis_scene_set_ambient_radiative_temperature(scn_3d, &amb_rad_temp)); + test_picard(scn_2d, 1/*Picard order*/, &ref); + test_picard(scn_3d, 1/*Picard order*/, &ref); + printf("\n"); + + /* Release memory */ + OK(sdis_scene_ref_put(scn_2d)); + OK(sdis_scene_ref_put(scn_3d)); + OK(sdis_interface_ref_put(interfaces[ADIABATIC])); + OK(sdis_interface_ref_put(interfaces[SOLID_FLUID_mX])); + OK(sdis_interface_ref_put(interfaces[SOLID_FLUID_pX])); + OK(sdis_interface_ref_put(interfaces[BOUNDARY_pX])); + OK(sdis_medium_ref_put(fluid)); + OK(sdis_medium_ref_put(solid)); + OK(sdis_medium_ref_put(dummy)); + OK(sdis_device_ref_put(dev)); + CHK(fclose(stream) == 0); + + 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 @@ -258,9 +258,12 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) double duplicated_vertices[] = { 0, 0, 0, 0 }; struct sdis_scene* scn = NULL; struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + struct sdis_ambient_radiative_temperature trad = + SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL; double lower[2], upper[2]; + double t_range[2]; double u0, u1, u2, pos[2], pos1[2]; - double dst, fp, t; + double dst, fp; struct context ctx; struct senc2d_scene* scn2d; struct senc3d_scene* scn3d; @@ -353,29 +356,38 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) BA(sdis_scene_get_ambient_radiative_temperature(NULL, NULL)); BA(sdis_scene_get_ambient_radiative_temperature(scn, NULL)); - BA(sdis_scene_get_ambient_radiative_temperature(NULL, &t)); - OK(sdis_scene_get_ambient_radiative_temperature(scn, &t)); - CHK(t == SDIS_SCENE_CREATE_ARGS_DEFAULT.trad); - - t = 100; - BA(sdis_scene_set_ambient_radiative_temperature(NULL, t)); - OK(sdis_scene_set_ambient_radiative_temperature(scn, t)); - OK(sdis_scene_get_ambient_radiative_temperature(scn, &t)); - CHK(t == 100); - - BA(sdis_scene_get_reference_temperature(NULL, NULL)); - BA(sdis_scene_get_reference_temperature(scn, NULL)); - BA(sdis_scene_get_reference_temperature(NULL, &t)); - OK(sdis_scene_get_reference_temperature(scn, &t)); - CHK(t == SDIS_SCENE_CREATE_ARGS_DEFAULT.tref); - - t = -1; - BA(sdis_scene_set_reference_temperature(NULL, t)); - BA(sdis_scene_set_reference_temperature(scn, t)); - t = 100; - OK(sdis_scene_set_reference_temperature(scn, t)); - OK(sdis_scene_get_reference_temperature(scn, &t)); - CHK(t == 100); + BA(sdis_scene_get_ambient_radiative_temperature(NULL, &trad)); + OK(sdis_scene_get_ambient_radiative_temperature(scn, &trad)); + CHK(trad.temperature == SDIS_SCENE_CREATE_ARGS_DEFAULT.trad.temperature); + CHK(trad.reference == SDIS_SCENE_CREATE_ARGS_DEFAULT.trad.reference); + + trad.temperature = 100; + trad.reference = 110; + BA(sdis_scene_set_ambient_radiative_temperature(NULL, &trad)); + OK(sdis_scene_set_ambient_radiative_temperature(scn, &trad)); + trad = SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL; + OK(sdis_scene_get_ambient_radiative_temperature(scn, &trad)); + CHK(trad.temperature == 100); + CHK(trad.reference == 110); + + BA(sdis_scene_get_temperature_range(NULL, NULL)); + BA(sdis_scene_get_temperature_range(scn, NULL)); + BA(sdis_scene_get_temperature_range(NULL, t_range)); + OK(sdis_scene_get_temperature_range(scn, t_range)); + CHK(t_range[0] == SDIS_SCENE_CREATE_ARGS_DEFAULT.t_range[0]); + CHK(t_range[1] == SDIS_SCENE_CREATE_ARGS_DEFAULT.t_range[1]); + + t_range[0] = 1; + t_range[1] = 100; + + BA(sdis_scene_set_temperature_range(NULL, t_range)); + BA(sdis_scene_set_temperature_range(scn, NULL)); + OK(sdis_scene_set_temperature_range(scn, t_range)); + t_range[0] = -1; + t_range[1] = -1; + OK(sdis_scene_get_temperature_range(scn, t_range)); + CHK(t_range[0] == 1); + CHK(t_range[1] == 100); BA(sdis_scene_get_boundary_position(NULL, 1, &u0, pos)); BA(sdis_scene_get_boundary_position(scn, 4, &u0, pos)); @@ -456,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; @@ -468,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)); @@ -485,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; @@ -295,7 +293,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; solid_shader.volumic_power = solid_get_volumetric_power; @@ -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; @@ -195,16 +194,17 @@ main(int argc, char** argv) struct sdis_solve_probe_boundary_args probe_args = SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT; struct sdis_solve_boundary_args bound_args = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT; + struct ssp_rng* rng = NULL; struct interf* interf_props = NULL; struct fluid* fluid_param; double pos[3]; 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); @@ -222,7 +222,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -333,12 +333,53 @@ main(int argc, char** argv) probe_args.time_range[1] = 0; BA(SOLVE(box_scn, &probe_args, &estimator)); probe_args.time_range[0] = probe_args.time_range[1] = INF; + probe_args.picard_order = 0; + BA(SOLVE(box_scn, &probe_args, &estimator)); + probe_args.picard_order = 1; 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); + } + + /* Check RNG type */ + probe_args.rng_state = NULL; + probe_args.rng_type = SSP_RNG_TYPE_NULL; + BA(SOLVE(box_scn, &probe_args, &estimator2)); + probe_args.rng_type = + SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT.rng_type == SSP_RNG_THREEFRY + ? SSP_RNG_MT19937_64 : SSP_RNG_THREEFRY; + OK(SOLVE(box_scn, &probe_args, &estimator2)); + if(is_master_process) { + struct sdis_mc T, T2; + check_estimator(estimator2, N, ref); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_temperature(estimator2, &T2)); + CHK(T2.E != T.E); + OK(sdis_estimator_ref_put(estimator2)); + } + + /* Check RNG state */ + OK(ssp_rng_create(NULL, SSP_RNG_THREEFRY, &rng)); + OK(ssp_rng_discard(rng, 31415926535)); /* Move the RNG state */ + probe_args.rng_state = rng; + probe_args.rng_type = SSP_RNG_TYPE_NULL; + OK(SOLVE(box_scn, &probe_args, &estimator2)); + OK(ssp_rng_ref_put(rng)); + if(is_master_process) { + struct sdis_mc T, T2; + check_estimator(estimator2, N, ref); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_temperature(estimator2, &T2)); + CHK(T2.E != T.E); + OK(sdis_estimator_ref_put(estimator2)); + } + + probe_args.rng_state = SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT.rng_state; + probe_args.rng_type = SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT.rng_type; BA(GREEN(NULL, &probe_args, &green)); BA(GREEN(box_scn, NULL, &green)); @@ -354,21 +395,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; @@ -385,35 +433,39 @@ 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; BA(SOLVE(square_scn, &probe_args, &estimator)); fluid_param->temperature = Tf; - + /* Right-side temperature at initial time */ probe_args.time_range[0] = 0; probe_args.time_range[1] = 0; 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 @@ -464,11 +516,53 @@ main(int argc, char** argv) bound_args.time_range[1] = 0; BA(SOLVE(box_scn, &bound_args, &estimator)); bound_args.time_range[0] = bound_args.time_range[1] = INF; + bound_args.picard_order = 0; + BA(SOLVE(box_scn, &bound_args, &estimator)); + bound_args.picard_order = 1; /* 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); + } + + /* Check RNG type */ + bound_args.rng_state = NULL; + bound_args.rng_type = SSP_RNG_TYPE_NULL; + BA(SOLVE(box_scn, &bound_args, &estimator2)); + bound_args.rng_type = + SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT.rng_type == SSP_RNG_THREEFRY + ? SSP_RNG_MT19937_64 : SSP_RNG_THREEFRY; + OK(SOLVE(box_scn, &bound_args, &estimator2)); + if(is_master_process) { + struct sdis_mc T, T2; + check_estimator(estimator2, N, ref); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_temperature(estimator2, &T2)); + CHK(T2.E != T.E); + OK(sdis_estimator_ref_put(estimator2)); + } + + /* Check RNG state */ + OK(ssp_rng_create(NULL, SSP_RNG_THREEFRY, &rng)); + OK(ssp_rng_discard(rng, 31415926535)); /* Move the RNG state */ + bound_args.rng_state = rng; + bound_args.rng_type = SSP_RNG_TYPE_NULL; + OK(SOLVE(box_scn, &bound_args, &estimator2)); + OK(ssp_rng_ref_put(rng)); + if(is_master_process) { + struct sdis_mc T, T2; + check_estimator(estimator2, N, ref); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_temperature(estimator2, &T2)); + CHK(T2.E != T.E); + OK(sdis_estimator_ref_put(estimator2)); + } + + /* Restore args */ + bound_args.rng_state = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT.rng_state; + bound_args.rng_type = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT.rng_type; BA(GREEN(NULL, &bound_args, &green)); BA(GREEN(box_scn, NULL, &green)); @@ -493,14 +587,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; @@ -513,8 +612,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; @@ -526,25 +627,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; @@ -559,36 +666,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; @@ -598,28 +713,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 @@ -16,6 +16,7 @@ #include "sdis.h" #include "test_sdis_utils.h" +#include <star/ssp.h> #include <rsys/math.h> /* @@ -142,6 +143,7 @@ struct interf { double temperature; double emissivity; double hc; + double reference_temperature; }; static double @@ -171,6 +173,15 @@ interface_get_convection_coef return interf->hc; } +static double +interface_get_reference_temperature + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + const struct interf* interf = sdis_data_cget(data); + CHK(frag && data); + return interf->reference_temperature; +} + /******************************************************************************* * Helper function ******************************************************************************/ @@ -218,7 +229,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; @@ -229,6 +239,7 @@ main(int argc, char** argv) struct sdis_scene* box_scn = NULL; struct sdis_scene* square_scn = NULL; struct sdis_estimator* estimator = NULL; + struct sdis_estimator* estimator2 = NULL; 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; @@ -241,14 +252,15 @@ main(int argc, char** argv) SDIS_SOLVE_BOUNDARY_FLUX_ARGS_DEFAULT; struct interf* interf_props = NULL; struct fluid* fluid_param; + struct ssp_rng* rng = NULL; enum sdis_estimator_type type; 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 @@ -263,7 +275,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -289,7 +301,9 @@ main(int argc, char** argv) interf_props->hc = H; interf_props->temperature = Tb; interf_props->emissivity = EPSILON; + interf_props->reference_temperature = Tb; interf_shader.back.emissivity = interface_get_emissivity; + interf_shader.back.reference_temperature = interface_get_reference_temperature; OK(sdis_interface_create (dev, solid, fluid, &interf_shader, data, &interf_Tb)); interf_shader.back.emissivity = NULL; @@ -301,7 +315,9 @@ main(int argc, char** argv) interf_props->hc = H; interf_props->temperature = UNKNOWN_TEMPERATURE; interf_props->emissivity = EPSILON; + interf_props->reference_temperature = Tref; interf_shader.back.emissivity = interface_get_emissivity; + interf_shader.back.reference_temperature = interface_get_reference_temperature; OK(sdis_interface_create (dev, solid, fluid, &interf_shader, data, &interf_H)); interf_shader.back.emissivity = NULL; @@ -331,8 +347,10 @@ main(int argc, char** argv) scn_args.get_position = box_get_position; scn_args.nprimitives = box_ntriangles; scn_args.nvertices = box_nvertices; - scn_args.trad = Trad; - scn_args.tref = Tref; + scn_args.trad.temperature = Trad; + scn_args.trad.reference = Trad; + scn_args.t_range[0] = MMIN(MMIN(Tf, Trad), Tb); + scn_args.t_range[1] = MMAX(MMAX(Tf, Trad), Tb); scn_args.context = box_interfaces; OK(sdis_scene_create(dev, &scn_args, &box_scn)); @@ -342,8 +360,10 @@ main(int argc, char** argv) scn_args.get_position = square_get_position; scn_args.nprimitives = square_nsegments; scn_args.nvertices = square_nvertices; - scn_args.trad = Trad; - scn_args.tref = Tref; + scn_args.trad.temperature = Trad; + scn_args.trad.reference = Trad; + scn_args.t_range[0] = MMIN(MMIN(Tf, Trad), Tb); + scn_args.t_range[1] = MMAX(MMAX(Tf, Trad), Tb); scn_args.context = square_interfaces; OK(sdis_scene_2d_create(dev, &scn_args, &square_scn)); @@ -387,25 +407,69 @@ 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); + 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); + } + + /* Check the RNG type */ + probe_args.rng_state = NULL; + probe_args.rng_type = SSP_RNG_TYPE_NULL; + BA(SOLVE(box_scn, &probe_args, &estimator2)); + probe_args.rng_type = + SDIS_SOLVE_PROBE_BOUNDARY_FLUX_ARGS_DEFAULT.rng_type == SSP_RNG_THREEFRY + ? SSP_RNG_MT19937_64 : SSP_RNG_THREEFRY; + OK(SOLVE(box_scn, &probe_args, &estimator2)); + if(is_master_process) { + struct sdis_mc T, T2; + check_estimator(estimator2, N, analyticT, analyticCF, analyticRF, analyticTF); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_temperature(estimator2, &T2)); + CHK(T2.E != T.E); + OK(sdis_estimator_ref_put(estimator2)); + } - 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)); + /* Check RNG state */ + OK(ssp_rng_create(NULL, SSP_RNG_THREEFRY, &rng)); + OK(ssp_rng_discard(rng, 31415926535)); /* Move the RNG state */ + probe_args.rng_state = rng; + probe_args.rng_type = SSP_RNG_TYPE_NULL; + OK(SOLVE(box_scn, &probe_args, &estimator2)); + OK(ssp_rng_ref_put(rng)); + if(is_master_process) { + struct sdis_mc T, T2; + check_estimator(estimator2, N, analyticT, analyticCF, analyticRF, analyticTF); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_temperature(estimator2, &T2)); + CHK(T2.E != T.E); + OK(sdis_estimator_ref_put(estimator2)); + } + + if(estimator) OK(sdis_estimator_ref_put(estimator)); + + /* Restore arguments */ + probe_args.rng_state = SDIS_SOLVE_PROBE_BOUNDARY_FLUX_ARGS_DEFAULT.rng_state; + probe_args.rng_type = SDIS_SOLVE_PROBE_BOUNDARY_FLUX_ARGS_DEFAULT.rng_type; 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 @@ -442,10 +506,52 @@ 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); + } + + /* Check the RNG type */ + bound_args.rng_state = NULL; + bound_args.rng_type = SSP_RNG_TYPE_NULL; + BA(SOLVE(box_scn, &bound_args, &estimator2)); + bound_args.rng_type = + SDIS_SOLVE_PROBE_BOUNDARY_FLUX_ARGS_DEFAULT.rng_type == SSP_RNG_THREEFRY + ? SSP_RNG_MT19937_64 : SSP_RNG_THREEFRY; + OK(SOLVE(box_scn, &bound_args, &estimator2)); + if(is_master_process) { + struct sdis_mc T, T2; + check_estimator(estimator2, N, analyticT, analyticCF, analyticRF, analyticTF); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_temperature(estimator2, &T2)); + CHK(T2.E != T.E); + OK(sdis_estimator_ref_put(estimator2)); + } + + /* Check RNG state */ + OK(ssp_rng_create(NULL, SSP_RNG_THREEFRY, &rng)); + OK(ssp_rng_discard(rng, 31415926535)); /* Move the RNG state */ + bound_args.rng_state = rng; + bound_args.rng_type = SSP_RNG_TYPE_NULL; + OK(SOLVE(box_scn, &bound_args, &estimator2)); + OK(ssp_rng_ref_put(rng)); + if(is_master_process) { + struct sdis_mc T, T2; + check_estimator(estimator2, N, analyticT, analyticCF, analyticRF, analyticTF); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_temperature(estimator2, &T2)); + CHK(T2.E != T.E); + OK(sdis_estimator_ref_put(estimator2)); + } + + if(estimator) OK(sdis_estimator_ref_put(estimator)); + + /* Restore arguments */ + bound_args.rng_state = SDIS_SOLVE_BOUNDARY_FLUX_ARGS_DEFAULT.rng_state; + bound_args.rng_type = SDIS_SOLVE_BOUNDARY_FLUX_ARGS_DEFAULT.rng_type; /* Average temperature on the right side of the square */ prims[0] = 4; @@ -453,9 +559,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 @@ -471,10 +579,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 @@ -16,6 +16,7 @@ #include "sdis.h" #include "test_sdis_utils.h" +#include <star/ssp.h> #include <star/s3dut.h> #include <rsys/algorithm.h> @@ -237,8 +238,11 @@ struct interf { double epsilon; double specular_fraction; double temperature; + double reference_temperature; +}; +static const struct interf INTERF_NULL = { + 0, 0, 0, UNKOWN_TEMPERATURE, UNKOWN_TEMPERATURE }; -static const struct interf INTERF_NULL = {0, 0, 0, UNKOWN_TEMPERATURE}; static double interface_get_convection_coef @@ -272,6 +276,14 @@ interface_get_temperature return ((const struct interf*)sdis_data_cget(data))->temperature; } +static double +interface_get_reference_temperature + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->reference_temperature; +} + /******************************************************************************* * Helper functions ******************************************************************************/ @@ -299,7 +311,7 @@ create_solid solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; /* Create the solid medium */ @@ -372,10 +384,14 @@ create_interface if(sdis_medium_get_type(mdm_front) == SDIS_FLUID) { interface_shader.front.emissivity = interface_get_emissivity; interface_shader.front.specular_fraction = interface_get_specular_fraction; + interface_shader.front.reference_temperature = + interface_get_reference_temperature; } if(sdis_medium_get_type(mdm_back) == SDIS_FLUID) { interface_shader.back.emissivity = interface_get_emissivity; interface_shader.back.specular_fraction = interface_get_specular_fraction; + interface_shader.back.reference_temperature = + interface_get_reference_temperature; } /* Create the interface */ OK(sdis_interface_create @@ -525,15 +541,16 @@ 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; struct sdis_mc T = SDIS_MC_NULL; + struct sdis_mc T2 = SDIS_MC_NULL; struct sdis_mc time = SDIS_MC_NULL; struct sdis_camera* cam = NULL; struct sdis_device* dev = NULL; struct sdis_estimator_buffer* buf = NULL; + struct sdis_estimator_buffer* buf2 = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* fluid0 = NULL; struct sdis_medium* fluid1 = NULL; @@ -542,6 +559,9 @@ main(int argc, char** argv) struct sdis_scene* scn = NULL; struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; struct sdis_solve_camera_args solve_args = SDIS_SOLVE_CAMERA_ARGS_DEFAULT; + struct sdis_ambient_radiative_temperature trad = + SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL; + struct ssp_rng* rng = NULL; struct ssp_rng* rng_state = NULL; struct fluid fluid_param = FLUID_NULL; struct solid solid_param = SOLID_NULL; @@ -553,10 +573,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; @@ -590,17 +610,18 @@ main(int argc, char** argv) interface_param.epsilon = 1; interface_param.specular_fraction = 1; interface_param.temperature = UNKOWN_TEMPERATURE; + interface_param.reference_temperature = 300; 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); @@ -614,8 +635,10 @@ main(int argc, char** argv) scn_args.get_position = geometry_get_position; scn_args.nprimitives = ntris; scn_args.nvertices = npos; - scn_args.trad = 300; - scn_args.tref = 300; + scn_args.trad.temperature = 300; + scn_args.trad.reference = 300; + scn_args.t_range[0] = 300; + scn_args.t_range[1] = 350; scn_args.context = &geom; OK(sdis_scene_create(dev, &scn_args, &scn)); @@ -640,8 +663,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)); @@ -650,9 +673,12 @@ main(int argc, char** argv) solve_args.cam = NULL; BA(sdis_solve_camera(scn, &solve_args, &buf)); solve_args.cam = cam; - OK(sdis_scene_set_ambient_radiative_temperature(scn, -1)); + OK(sdis_scene_get_ambient_radiative_temperature(scn, &trad)); + trad.temperature = -1; + OK(sdis_scene_set_ambient_radiative_temperature(scn, &trad)); BA(sdis_solve_camera(scn, &solve_args, &buf)); - OK(sdis_scene_set_ambient_radiative_temperature(scn, 300)); + trad.temperature = 300; + OK(sdis_scene_set_ambient_radiative_temperature(scn, &trad)); solve_args.time_range[0] = solve_args.time_range[1] = -1; BA(sdis_solve_camera(scn, &solve_args, &buf)); solve_args.time_range[0] = 1; @@ -664,42 +690,81 @@ 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); + 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)); + } - 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)); + /* Check RNG type */ + solve_args.rng_state = NULL; + solve_args.rng_type = SSP_RNG_TYPE_NULL; + BA(sdis_solve_camera(scn, &solve_args, &buf2)); + solve_args.rng_type = + SDIS_SOLVE_CAMERA_ARGS_DEFAULT.rng_type == SSP_RNG_THREEFRY + ? SSP_RNG_MT19937_64 : SSP_RNG_THREEFRY; + OK(sdis_solve_camera(scn, &solve_args, &buf2)); + if(is_master_process) { + OK(sdis_estimator_buffer_get_temperature(buf2, &T2)); + CHK(T.E != T2.E); + CHK(T2.E + 3*T2.SE >= T.E - 3*T.SE + && T2.E - 3*T2.SE <= T.E + 3*T.SE); + OK(sdis_estimator_buffer_ref_put(buf2)); + } - 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); + /* Check the RNG state */ + OK(ssp_rng_create(NULL, SSP_RNG_THREEFRY, &rng)); + OK(ssp_rng_discard(rng, 31415926535)); /* Move the RNG state */ + solve_args.rng_state = rng; + solve_args.rng_type = SSP_RNG_TYPE_NULL; + OK(sdis_solve_camera(scn, &solve_args, &buf2)); + OK(ssp_rng_ref_put(rng)); + if(is_master_process) { + OK(sdis_estimator_buffer_get_temperature(buf2, &T2)); + CHK(T.E != T2.E); + CHK(T2.E + 3*T2.SE >= T.E - 3*T.SE + && T2.E - 3*T2.SE <= T.E + 3*T.SE); + OK(sdis_estimator_buffer_ref_put(buf2)); + } - /* Write the image */ - dump_image(buf); - OK(sdis_estimator_buffer_ref_put(buf)); + /* Restore args */ + solve_args.rng_state = SDIS_SOLVE_CAMERA_ARGS_DEFAULT.rng_state; + solve_args.rng_type = SDIS_SOLVE_CAMERA_ARGS_DEFAULT.rng_type; pfluid_param = sdis_data_get(sdis_medium_get_data(fluid1)); pfluid_param->temperature = UNKOWN_TEMPERATURE; @@ -717,11 +782,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 @@ -18,6 +18,7 @@ #include <rsys/math.h> #include <rsys/stretchy_array.h> +#include <star/ssp.h> #include <star/s3dut.h> #include <string.h> @@ -197,12 +198,12 @@ 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; struct s3dut_mesh* msh1 = NULL; struct sdis_mc T = SDIS_MC_NULL; + struct sdis_mc T2 = SDIS_MC_NULL; struct sdis_mc time = SDIS_MC_NULL; struct sdis_device* dev = NULL; struct sdis_medium* solid0 = NULL; @@ -225,6 +226,7 @@ main(int argc, char** argv) struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_interface_shader interface_shader = SDIS_INTERFACE_SHADER_NULL; struct sdis_solve_medium_args solve_args = SDIS_SOLVE_MEDIUM_ARGS_DEFAULT; + struct ssp_rng* rng = NULL; struct context ctx; double ref; double v, v0, v1; @@ -232,10 +234,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; @@ -259,7 +261,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; /* Create the solid0 medium */ @@ -309,13 +311,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 +383,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 +407,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 +441,52 @@ 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)); + } + + /* Check RNG type */ + solve_args.rng_state = NULL; + solve_args.rng_type = SSP_RNG_TYPE_NULL; + BA(sdis_solve_medium(scn, &solve_args, &estimator2)); + solve_args.rng_type = + SDIS_SOLVE_MEDIUM_ARGS_DEFAULT.rng_type == SSP_RNG_THREEFRY + ? SSP_RNG_MT19937_64 : SSP_RNG_THREEFRY; + OK(sdis_solve_medium(scn, &solve_args, &estimator2)); + if(is_master_process) { + OK(sdis_estimator_get_temperature(estimator2, &T2)); + CHK(eq_eps(T2.E, ref, 3*T2.SE)); + CHK(T2.E != T.E); + OK(sdis_estimator_ref_put(estimator2)); + } + + /* Check RNG state */ + OK(ssp_rng_create(NULL, SSP_RNG_THREEFRY, &rng)); + OK(ssp_rng_discard(rng, 31415926535)); /* Move the RNG state */ + solve_args.rng_state = rng; + solve_args.rng_type = SSP_RNG_TYPE_NULL; + OK(sdis_solve_medium(scn, &solve_args, &estimator2)); + OK(ssp_rng_ref_put(rng)); + if(is_master_process) { + OK(sdis_estimator_get_temperature(estimator2, &T2)); + CHK(eq_eps(T2.E, ref, 3*T2.SE)); + CHK(T2.E != T.E); + OK(sdis_estimator_ref_put(estimator2)); + } + + /* Restore args */ + solve_args.rng_state = SDIS_SOLVE_PROBE_ARGS_DEFAULT.rng_state; + solve_args.rng_type = SDIS_SOLVE_PROBE_ARGS_DEFAULT.rng_type; + + /* Solve green */ BA(sdis_solve_medium_green_function(NULL, &solve_args, &green)); @@ -453,22 +500,28 @@ main(int argc, char** argv) solve_args.medium = solid1; BA(sdis_solve_medium_green_function(scn, &solve_args, &green)); solve_args.medium = solid0; + solve_args.picard_order = 0; + BA(sdis_solve_medium_green_function(scn, &solve_args, &green)); + 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)); @@ -477,9 +530,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; @@ -246,7 +245,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; /* Create the solid0 medium */ @@ -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 @@ -141,6 +141,7 @@ struct interf { double hc; double epsilon; double specular_fraction; + double reference_temperature; }; static double @@ -167,6 +168,14 @@ interface_get_specular_fraction return ((const struct interf*)sdis_data_cget(data))->specular_fraction; } +static double +interface_get_reference_temperature + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->reference_temperature; +} + /******************************************************************************* * Helper functions ******************************************************************************/ @@ -199,10 +208,10 @@ process_heat_path(const struct sdis_heat_path* path, void* context) CHK(path && context); - BA(sdis_heat_path_get_vertices_count(NULL, &n)); - BA(sdis_heat_path_get_vertices_count(path, NULL)); - OK(sdis_heat_path_get_vertices_count(path, &n)); - CHK(n != 0); + BA(sdis_heat_path_get_line_strips_count(NULL, &n)); + BA(sdis_heat_path_get_line_strips_count(path, NULL)); + OK(sdis_heat_path_get_line_strips_count(path, &n)); + CHK(n == 1); BA(sdis_heat_path_get_status(NULL, &status)); BA(sdis_heat_path_get_status(path, NULL)); @@ -215,20 +224,28 @@ process_heat_path(const struct sdis_heat_path* path, void* context) default: FATAL("Unreachable code.\n"); break; } - BA(sdis_heat_path_get_vertex(NULL, 0, &vert)); - BA(sdis_heat_path_get_vertex(path, n, &vert)); - BA(sdis_heat_path_get_vertex(path, 0, NULL)); + BA(sdis_heat_path_line_strip_get_vertices_count(NULL, 0, &n)); + BA(sdis_heat_path_line_strip_get_vertices_count(path, 1, &n)); + BA(sdis_heat_path_line_strip_get_vertices_count(path, 0, NULL)); + OK(sdis_heat_path_line_strip_get_vertices_count(path, 0, &n)); + CHK(n != 0); + + BA(sdis_heat_path_line_strip_get_vertex(NULL, 0, 0, &vert)); + BA(sdis_heat_path_line_strip_get_vertex(path, 1, 1, &vert)); + BA(sdis_heat_path_line_strip_get_vertex(path, 0, n, &vert)); + BA(sdis_heat_path_line_strip_get_vertex(path, 0, 0, NULL)); FOR_EACH(i, 0, n) { - OK(sdis_heat_path_get_vertex(path, i, &vert)); + OK(sdis_heat_path_line_strip_get_vertex(path, 0, i, &vert)); CHK(vert.type == SDIS_HEAT_VERTEX_CONVECTION || vert.type == SDIS_HEAT_VERTEX_CONDUCTION || vert.type == SDIS_HEAT_VERTEX_RADIATIVE); } - BA(sdis_heat_path_for_each_vertex(NULL, dump_vertex_pos, context)); - BA(sdis_heat_path_for_each_vertex(path, NULL, context)); - OK(sdis_heat_path_for_each_vertex(path, dump_vertex_pos, context)); + BA(sdis_heat_path_line_strip_for_each_vertex(NULL, 0, dump_vertex_pos, context)); + BA(sdis_heat_path_line_strip_for_each_vertex(path, 1, dump_vertex_pos, context)); + BA(sdis_heat_path_line_strip_for_each_vertex(path, 0, NULL, context)); + OK(sdis_heat_path_line_strip_for_each_vertex(path, 0, dump_vertex_pos, context)); FOR_EACH(i, 0, n-1) { fprintf(ctx->stream, "l %lu %lu\n", @@ -262,11 +279,14 @@ 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; struct sdis_interface_shader interface_shader = SDIS_INTERFACE_SHADER_NULL; struct sdis_solve_probe_args solve_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_ambient_radiative_temperature trad = + SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL; struct dump_path_context dump_ctx = DUMP_PATH_CONTEXT_NULL; struct context ctx; struct fluid* fluid_param; @@ -275,6 +295,7 @@ main(int argc, char** argv) struct ssp_rng* rng_state = NULL; enum sdis_estimator_type type; FILE* stream = NULL; + double t_range[2]; double ref; const size_t N = 1000; const size_t N_dump = 10; @@ -284,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, 0, &dev)); + dev_args.allocator = &allocator; + OK(sdis_device_create(&dev_args, &dev)); /* Create the fluid medium */ OK(sdis_data_create @@ -307,7 +329,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; OK(sdis_solid_create(dev, &solid_shader, data, &solid)); OK(sdis_data_ref_put(data)); @@ -324,6 +346,7 @@ main(int argc, char** argv) interface_shader.back.temperature = NULL; interface_shader.back.emissivity = interface_get_emissivity; interface_shader.back.specular_fraction = interface_get_specular_fraction; + interface_shader.back.reference_temperature = interface_get_reference_temperature; OK(sdis_interface_create (dev, solid, fluid, &interface_shader, data, &interf)); OK(sdis_data_ref_put(data)); @@ -367,6 +390,9 @@ main(int argc, char** argv) solve_args.time_range[1] = 0; BA(sdis_solve_probe(scn, &solve_args, &estimator)); solve_args.time_range[0] = solve_args.time_range[1] = INF; + solve_args.picard_order = 0; + BA(sdis_solve_probe(scn, &solve_args, &estimator)); + solve_args.picard_order = 1; OK(sdis_solve_probe(scn, &solve_args, &estimator)); BA(sdis_estimator_get_type(estimator, NULL)); @@ -538,10 +564,14 @@ main(int argc, char** argv) /* Green and ambient radiative temperature */ solve_args.nrealisations = N; - OK(sdis_scene_set_ambient_radiative_temperature(scn, 300)); - OK(sdis_scene_set_reference_temperature(scn, 300)); + trad.temperature = trad.reference = 300; + t_range[0] = 300; + t_range[1] = 300; + OK(sdis_scene_set_ambient_radiative_temperature(scn, &trad)); + OK(sdis_scene_set_temperature_range(scn, t_range)); interface_param->epsilon = 1; + interface_param->reference_temperature = 300; OK(sdis_solve_probe(scn, &solve_args, &estimator)); OK(sdis_solve_probe_green_function(scn, &solve_args, &green)); @@ -554,7 +584,11 @@ main(int argc, char** argv) OK(sdis_estimator_ref_put(estimator2)); /* Check same green used at different ambient radiative temperature */ - OK(sdis_scene_set_ambient_radiative_temperature(scn, 600)); + trad.temperature = 600; + t_range[0] = 300; + t_range[1] = 600; + OK(sdis_scene_set_ambient_radiative_temperature(scn, &trad)); + OK(sdis_scene_set_temperature_range(scn, t_range)); OK(sdis_solve_probe(scn, &solve_args, &estimator)); OK(sdis_green_function_solve(green, &estimator2)); 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 @@ -16,8 +16,11 @@ #include "sdis.h" #include "test_sdis_utils.h" +#include <star/ssp.h> #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,8 +148,8 @@ interface_get_temperature int main(int argc, char** argv) { - struct mem_allocator allocator; struct sdis_mc T = SDIS_MC_NULL; + struct sdis_mc T2 = SDIS_MC_NULL; struct sdis_mc time = SDIS_MC_NULL; struct sdis_device* dev = NULL; struct sdis_data* data = NULL; @@ -165,16 +168,17 @@ main(int argc, char** argv) struct sdis_interface_shader interface_shader = DUMMY_INTERFACE_SHADER; struct sdis_interface* interfaces[12]; struct sdis_solve_probe_args solve_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct ssp_rng* rng = NULL; struct context ctx; struct interf* interface_param = NULL; double ref; 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; @@ -184,7 +188,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = temperature_unknown; OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -253,39 +257,80 @@ 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 */ + 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 RNG type */ + solve_args.rng_state = NULL; + solve_args.rng_type = SSP_RNG_TYPE_NULL; + BA(sdis_solve_probe(scn, &solve_args, &estimator2)); + solve_args.rng_type = + SDIS_SOLVE_PROBE_ARGS_DEFAULT.rng_type == SSP_RNG_THREEFRY + ? SSP_RNG_MT19937_64 : SSP_RNG_THREEFRY; + OK(sdis_solve_probe(scn, &solve_args, &estimator2)); + if(is_master_process) { + OK(sdis_estimator_get_temperature(estimator2, &T2)); + CHK(eq_eps(T2.E, ref, 3*T2.SE)); + CHK(T2.E != T.E); + OK(sdis_estimator_ref_put(estimator2)); + } + + /* Check the RNG state */ + OK(ssp_rng_create(NULL, SSP_RNG_THREEFRY, &rng)); + OK(ssp_rng_discard(rng, 31415926535)); /* Move the RNG state */ + solve_args.rng_state = rng; + solve_args.rng_type = SSP_RNG_TYPE_NULL; + OK(sdis_solve_probe(scn, &solve_args, &estimator2)); + OK(ssp_rng_ref_put(rng)); + if(is_master_process) { + OK(sdis_estimator_get_temperature(estimator2, &T2)); + CHK(eq_eps(T2.E, ref, 3*T2.SE)); + CHK(T2.E != T.E); + OK(sdis_estimator_ref_put(estimator2)); + } + + /* Restore args */ + solve_args.rng_state = SDIS_SOLVE_PROBE_ARGS_DEFAULT.rng_state; + solve_args.rng_type = SDIS_SOLVE_PROBE_ARGS_DEFAULT.rng_type; /* 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; @@ -180,7 +178,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = temperature_unknown; OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -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; @@ -210,7 +208,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = temperature_unknown; OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -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; @@ -205,7 +203,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = temperature_unknown; OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -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 @@ -174,7 +172,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -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 @@ -47,7 +47,7 @@ static const double vertices[12/*#vertices*/*3/*#coords per vertex*/] = { 1.0, 0.0, 1.0, 1.0, 1.0, 1.0 }; -static const size_t nvertices = sizeof(vertices) / (3*sizeof(double)); +static const size_t nvertices = sizeof(vertices) / (sizeof(double)*3); /* The following array lists the indices toward the 3D vertices of each * triangle. @@ -67,7 +67,7 @@ static const size_t indices[22/*#triangles*/*3/*#indices per triangle*/] = { 0, 2, 1, 1, 2, 3, 1, 3, 8, 8, 3, 9, /* Z min */ 4, 5, 6, 6, 5, 7, 5,10, 7, 7,10,11 /* Z max */ }; -static const size_t ntriangles = sizeof(indices) / (3*sizeof(size_t)); +static const size_t ntriangles = sizeof(indices) / (sizeof(size_t)*3); /******************************************************************************* * Box geometry functions @@ -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; @@ -520,7 +518,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; /* Create the solid medium */ @@ -530,7 +528,7 @@ main(int argc, char** argv) solid_param->rho = rho; solid_param->cp = cp; solid_param->lambda = lambda; - solid_param->delta = 1.0/20.0 * MMIN(MMIN(boxsz[0], boxsz[1]), boxsz[2]); + solid_param->delta = 1.0/30.0 * MMIN(MMIN(boxsz[0], boxsz[1]), boxsz[2]); solid_param->init_temperature = Tinit; OK(sdis_solid_create(dev, &solid_shader, data, &solid)); @@ -618,7 +616,7 @@ main(int argc, char** argv) (Tbounds, Tinit, boxsz, probe, time[0], rho, cp, lambda); /* Run simulation on regular scene */ - solid_param->delta = 1.0/20.0 * MMIN(MMIN(boxsz[0], boxsz[1]), boxsz[2]); + solid_param->delta = 1.0/30.0 * MMIN(MMIN(boxsz[0], boxsz[1]), boxsz[2]); solve_args.nrealisations = nrealisations; solve_args.position[0] = probe[0]; solve_args.position[1] = probe[1]; @@ -638,7 +636,7 @@ main(int argc, char** argv) OK(sdis_estimator_ref_put(estimator)); /* Run simulation on split scene */ - solid_param->delta = 1.0/20.0 * MMIN(MMIN(boxsz[0]/2.0, boxsz[1]), boxsz[2]); + solid_param->delta = 1.0/30.0 * MMIN(MMIN(boxsz[0]/2.0, boxsz[1]), boxsz[2]); OK(sdis_solve_probe(box2_scn, &solve_args, &estimator)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); OK(sdis_estimator_get_temperature(estimator, &temperature)); @@ -653,7 +651,7 @@ main(int argc, char** argv) /* Run simulation on matriochkas */ solid_param->delta = MMIN(MMIN(boxsz[0], boxsz[1]), boxsz[2]); solid_param->delta /= (double)nmatriochkas; - solid_param->delta *= 1.0/20.0; + solid_param->delta *= 1.0/30.0; OK(sdis_solve_probe(box_matriochka_scn, &solve_args, &estimator)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); OK(sdis_estimator_get_temperature(estimator, &temperature)); @@ -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 @@ -23,6 +23,20 @@ #include <star/ssp.h> /* + * The physical configuration is the following: a slab of fluid with known + * thermophysical properties but unknown temperature is located between a + * "ground" and a slab of solid, with also a unknown temperature profile. On + * the other side of the solid slab, is a "atmosphere" with known temperature, + * and known radiative temperature. + * + * Solving the system means: finding the temperature of the ground, of the + * fluid, of the boundaries, and also the temperature inside the solid, at + * various locations (the 1D slab is discretized in order to obtain the + * reference) + * + * The reference for this system comes from a numerical method and is not + * analytic. Thus the compliance test MC VS reference is not the usual |MC - + * ref| <= 3*sigma but is |MC -ref| <= (Tmax -Tmin) * 0.01. * * 3D 2D * @@ -59,6 +73,10 @@ #define HA 400 #define TR 260 +#define TMAX (MMAX(MMAX(MMAX(T0_FLUID, T0_SOLID), MMAX(TG, TA)), TR)) +#define TMIN (MMIN(MMIN(MMIN(T0_FLUID, T0_SOLID), MMIN(TG, TA)), TR)) +#define EPS ((TMAX-TMIN)*0.01) + /* hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon * Tref = (hr / (4 * 5.6696e-8 * epsilon)) ^ 1/3, hr = 6 */ #define TREF 297.974852286 @@ -71,13 +89,12 @@ #define X_PROBE (XH + 0.2 * XE) -#define DELTA (XE/30.0) +#define DELTA (XE/40.0) - /******************************************************************************* - * Box geometry - ******************************************************************************/ -static const double model3d_vertices[12/*#vertices*/ * 3/*#coords per vertex*/] -= { +/******************************************************************************* + * Box geometry + ******************************************************************************/ +static const double model3d_vertices[12/*#vertices*/*3/*#coords per vertex*/] = { 0, 0, 0, XH, 0, 0, XHpE, 0, 0, @@ -91,20 +108,19 @@ static const double model3d_vertices[12/*#vertices*/ * 3/*#coords per vertex*/] XH, XHpE, XHpE, XHpE, XHpE, XHpE }; -static const size_t model3d_nvertices = sizeof(model3d_vertices) / (3*sizeof(double)); +static const size_t model3d_nvertices = sizeof(model3d_vertices)/(sizeof(double)*3); /* The following array lists the indices toward the 3D vertices of each * triangle. * ,3---,4---,5 ,3----4----5 ,4 * ,' | ,' | ,'/| ,'/| \ | \ | ,'/| - * 9----10---11 / | 9' / | \ | \ | 10 / | Y - * |', |', | / ,2 | / ,0---,1---,2 | / ,1 | + * 9----10---11 / | 9' / | \ | \ | 10 / | Y + * |', |', | / ,2 | / ,0---,1---,2 | / ,1 | * | ',| ',|/,' |/,' | ,' | ,' |/,' o--X - * 6----7----8' 6----7'---8' 7 / - * Front, right Back, left and Internal Z + * 6----7----8' 6----7'---8' 7 / + * Front, right Back, left and Internal Z * and Top faces bottom faces face */ -static const size_t model3d_indices[22/*#triangles*/ * 3/*#indices per triangle*/] -= { +static const size_t model3d_indices[22/*#triangles*/*3/*#indices per triangle*/] = { 0, 3, 1, 1, 3, 4, 1, 4, 2, 2, 4, 5, /* -Z */ 0, 6, 3, 3, 6, 9, /* -X */ 6, 7, 9, 9, 7, 10, 7, 8, 10, 10, 8, 11, /* +Z */ @@ -113,7 +129,7 @@ static const size_t model3d_indices[22/*#triangles*/ * 3/*#indices per triangle* 0, 1, 7, 7, 6, 0, 1, 2, 8, 8, 7, 1, /* -Y */ 4, 10, 7, 7, 1, 4 /* Inside */ }; -static const size_t model3d_ntriangles = sizeof(model3d_indices) / (3*sizeof(size_t)); +static const size_t model3d_ntriangles = sizeof(model3d_indices)/(sizeof(size_t)*3); static INLINE void model3d_get_indices(const size_t itri, size_t ids[3], void* context) @@ -157,7 +173,7 @@ static const double model2d_vertices[6/*#vertices*/ * 2/*#coords per vertex*/] = XH, XHpE, XHpE, XHpE }; -static const size_t model2d_nvertices = sizeof(model2d_vertices) / (2*sizeof(double)); +static const size_t model2d_nvertices = sizeof(model2d_vertices)/(sizeof(double)*2); static const size_t model2d_indices[7/*#segments*/ * 2/*#indices per segment*/] = { 0, 1, 1, 2, /* Bottom */ @@ -166,8 +182,7 @@ static const size_t model2d_indices[7/*#segments*/ * 2/*#indices per segment*/] 5, 0, /* Right */ 4, 1 /* Inside */ }; -static const size_t model2d_nsegments = sizeof(model2d_indices) / (2*sizeof(size_t)); - +static const size_t model2d_nsegments = sizeof(model2d_indices)/(sizeof(size_t)*2); static INLINE void model2d_get_indices(const size_t iseg, size_t ids[2], void* context) @@ -309,6 +324,7 @@ struct interf { double temperature; double emissivity; double h; + double Tref; }; static double @@ -341,10 +357,56 @@ interface_get_emissivity return interf->emissivity; } +static double +interface_get_Tref + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + const struct interf* interf; + CHK(frag && data); + interf = sdis_data_cget(data); + return interf->Tref; +} + /******************************************************************************* * Helper functions ******************************************************************************/ static void +create_interface + (struct sdis_device* dev, + struct sdis_medium* front, + struct sdis_medium* back, + const struct interf* interf, + struct sdis_interface** out_interf) +{ + struct sdis_interface_shader shader = SDIS_INTERFACE_SHADER_NULL; + struct sdis_data* data = NULL; + + CHK(interf != NULL); + + shader.front.temperature = interface_get_temperature; + shader.back.temperature = interface_get_temperature; + if(sdis_medium_get_type(front) != sdis_medium_get_type(back)) { + shader.convection_coef = interface_get_convection_coef; + shader.convection_coef_upper_bound = interf->h; + } + if(sdis_medium_get_type(front) == SDIS_FLUID) { + shader.front.emissivity = interface_get_emissivity; + shader.front.reference_temperature = interface_get_Tref; + } + if(sdis_medium_get_type(back) == SDIS_FLUID) { + shader.back.emissivity = interface_get_emissivity; + shader.back.reference_temperature = interface_get_Tref; + } + + OK(sdis_data_create(dev, sizeof(struct interf), ALIGNOF(struct interf), + NULL, &data)); + *((struct interf*)sdis_data_get(data)) = *interf; + + OK(sdis_interface_create(dev, front, back, &shader, data, out_interf)); + OK(sdis_data_ref_put(data)); +} + +static void solve_tbound1 (struct sdis_scene* scn, struct ssp_rng* rng) @@ -359,10 +421,11 @@ solve_tbound1 size_t nreals; size_t nfails; enum sdis_scene_dimension dim; - double t[] = { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; - double ref[sizeof(t) / sizeof(*t)] - = { 290.046375, 289.903935, 289.840490, 289.802690, 289.777215, - 289.759034, 289.745710, 289.735826, 289.728448, 289.722921 }; + const double t[] = { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; + const double ref[sizeof(t) / sizeof(*t)] = { + 290.046375, 289.903935, 289.840490, 289.802690, 289.777215, 289.759034, + 289.745710, 289.735826, 289.728448, 289.722921 + }; const int nsimuls = sizeof(t) / sizeof(*t); int isimul; ASSERT(scn && rng); @@ -413,9 +476,8 @@ solve_tbound1 printf("Elapsed time = %s\n", dump); printf("Time per realisation (in usec) = %g +/- %g\n\n", time.E, time.SE); - CHK(nfails + nreals == N); - CHK(nfails <= N/1000); - CHK(t[isimul] == 0 || eq_eps(T.E, ref[isimul], T.SE * 4)); + CHK(eq_eps(T.E, ref[isimul], EPS)); + /*CHK(eq_eps(T.E, ref[isimul], T.SE*3));*/ OK(sdis_estimator_ref_put(estimator)); } @@ -436,10 +498,11 @@ solve_tbound2 size_t nreals; size_t nfails; enum sdis_scene_dimension dim; - double t[] = { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; - double ref[sizeof(t) / sizeof(*t)] - = { 309.08032, 309.34626, 309.46525, 309.53625, 309.58408, - 309.618121, 309.642928, 309.661167, 309.674614, 309.684524 }; + const double t[] = { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; + const double ref[sizeof(t) / sizeof(*t)] = { + 309.08032, 309.34626, 309.46525, 309.53625, 309.58408, 309.618121, + 309.642928, 309.661167, 309.674614, 309.684524 + }; const int nsimuls = sizeof(t) / sizeof(*t); int isimul; ASSERT(scn && rng); @@ -492,7 +555,8 @@ solve_tbound2 CHK(nfails + nreals == N); CHK(nfails <= N/1000); - CHK(eq_eps(T.E, ref[isimul], T.SE * 4)); + CHK(eq_eps(T.E, ref[isimul], EPS)); + /*CHK(eq_eps(T.E, ref[isimul], T.SE*3));*/ OK(sdis_estimator_ref_put(estimator)); } @@ -512,10 +576,11 @@ solve_tsolid size_t nreals; size_t nfails; enum sdis_scene_dimension dim; - double t[] = { 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; - double ref[sizeof(t) / sizeof(*t)] - = { 300, 300.87408, 302.25832, 303.22164, 303.89954, 304.39030, - 304.75041, 305.01595, 305.21193, 305.35641, 305.46271 }; + const double t[] = { 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; + const double ref[sizeof(t) / sizeof(*t)] = { + 300, 300.87408, 302.25832, 303.22164, 303.89954, 304.39030, 304.75041, + 305.01595, 305.21193, 305.35641, 305.46271 + }; const int nsimuls = sizeof(t) / sizeof(*t); int isimul; ASSERT(scn && rng); @@ -558,7 +623,8 @@ solve_tsolid CHK(nfails + nreals == N); CHK(nfails <= N / 1000); - CHK(eq_eps(T.E, ref[isimul], T.SE * 4)); + CHK(eq_eps(T.E, ref[isimul], EPS)); + /*CHK(eq_eps(T.E, ref[isimul], T.SE*3));*/ OK(sdis_estimator_ref_put(estimator)); } @@ -577,10 +643,12 @@ solve_tfluid size_t nreals; size_t nfails; enum sdis_scene_dimension dim; - double t[] = { 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; - double ref[sizeof(t) / sizeof(*t)] - = { 300, 309.53905, 309.67273, 309.73241, 309.76798, 309.79194, 309.80899, - 309.82141, 309.83055, 309.83728, 309.84224 }; + double eps; + const double t[] = { 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; + const double ref[sizeof(t) / sizeof(*t)] = { + 300, 309.53905, 309.67273, 309.73241, 309.76798, 309.79194, 309.80899, + 309.82141, 309.83055, 309.83728, 309.84224 + }; const int nsimuls = sizeof(t) / sizeof(*t); int isimul; ASSERT(scn); @@ -621,7 +689,9 @@ solve_tfluid CHK(nfails + nreals == N); CHK(nfails <= N / 1000); - CHK(eq_eps(T.E, ref[isimul], T.SE * 4)); + + eps = EPS; + CHK(eq_eps(T.E, ref[isimul], eps)); OK(sdis_estimator_ref_put(estimator)); } @@ -633,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; @@ -650,23 +719,21 @@ main(int argc, char** argv) 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; - struct sdis_interface_shader interf_shader = SDIS_INTERFACE_SHADER_NULL; struct sdis_interface* model3d_interfaces[22 /*#triangles*/]; struct sdis_interface* model2d_interfaces[7/*#segments*/]; - struct interf* interf_props = NULL; + struct interf interf_props; struct solid* solid_props = NULL; struct fluid* fluid_props = NULL; 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; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; /* Create the solid media */ @@ -718,66 +785,34 @@ main(int argc, char** argv) OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid_A)); OK(sdis_data_ref_put(data)); - /* Setup the interface shader */ - interf_shader.front.temperature = interface_get_temperature; - interf_shader.back.temperature = interface_get_temperature; - interf_shader.convection_coef = interface_get_convection_coef; - interf_shader.convection_coef_upper_bound = 0; - /* Create the adiabatic interfaces */ - OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); - interf_props = sdis_data_get(data); - interf_props->temperature = UNKNOWN_TEMPERATURE; - interf_props->h = 0; - OK(sdis_interface_create - (dev, fluid, dummy_solid, &interf_shader, data, &interf_adiabatic_1)); - OK(sdis_data_ref_put(data)); - - interf_shader.convection_coef = NULL; - OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); - interf_props = sdis_data_get(data); - interf_props->temperature = UNKNOWN_TEMPERATURE; - interf_props->h = 0; - OK(sdis_interface_create - (dev, solid, dummy_solid, &interf_shader, data, &interf_adiabatic_2)); - OK(sdis_data_ref_put(data)); + interf_props.temperature = UNKNOWN_TEMPERATURE; + interf_props.h = 0; + interf_props.emissivity = 0; + interf_props.Tref = TREF; + create_interface(dev, fluid, dummy_solid, &interf_props, &interf_adiabatic_1); + create_interface(dev, solid, dummy_solid, &interf_props, &interf_adiabatic_2); /* Create the P interface */ - interf_shader.convection_coef_upper_bound = HC; - interf_shader.convection_coef = interface_get_convection_coef; - interf_shader.front.emissivity = interface_get_emissivity; - OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); - interf_props = sdis_data_get(data); - interf_props->temperature = UNKNOWN_TEMPERATURE; - interf_props->h = HC; - interf_props->emissivity = 1; - OK(sdis_interface_create - (dev, fluid, solid, &interf_shader, data, &interf_P)); - OK(sdis_data_ref_put(data)); + interf_props.temperature = UNKNOWN_TEMPERATURE; + interf_props.h = HC; + interf_props.emissivity = 1; + interf_props.Tref = TREF; + create_interface(dev, fluid, solid, &interf_props, &interf_P); /* Create the TG interface */ - interf_shader.convection_coef_upper_bound = HG; - OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); - interf_props = sdis_data_get(data); - interf_props->temperature = TG; - interf_props->h = HG; - interf_props->emissivity = 1; - OK(sdis_interface_create - (dev, fluid, dummy_solid, &interf_shader, data, &interf_TG)); - OK(sdis_data_ref_put(data)); + interf_props.temperature = TG; + interf_props.h = HG; + interf_props.emissivity = 1; + interf_props.Tref = TG; + create_interface(dev, fluid, dummy_solid, &interf_props, &interf_TG); /* Create the TA interface */ - interf_shader.convection_coef_upper_bound = HA; - interf_shader.front.emissivity = NULL; - interf_shader.back.emissivity = interface_get_emissivity; - OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); - interf_props = sdis_data_get(data); - interf_props->temperature = UNKNOWN_TEMPERATURE; - interf_props->h = HA; - interf_props->emissivity = 1; - OK(sdis_interface_create - (dev, solid, fluid_A, &interf_shader, data, &interf_TA)); - OK(sdis_data_ref_put(data)); + interf_props.temperature = UNKNOWN_TEMPERATURE; + interf_props.h = HA; + interf_props.emissivity = 1; + interf_props.Tref = TREF; + create_interface(dev, solid, fluid_A, &interf_props, &interf_TA); /* Release the media */ OK(sdis_medium_ref_put(solid)); @@ -802,7 +837,7 @@ main(int argc, char** argv) model3d_interfaces[10] = interf_TA; model3d_interfaces[11] = interf_TA; /* Top */ - model3d_interfaces[12] = interf_adiabatic_1; + model3d_interfaces[12] = interf_adiabatic_1; model3d_interfaces[13] = interf_adiabatic_1; model3d_interfaces[14] = interf_adiabatic_2; model3d_interfaces[15] = interf_adiabatic_2; @@ -835,8 +870,10 @@ main(int argc, char** argv) scn_args.nprimitives = model3d_ntriangles; scn_args.nvertices = model3d_nvertices; scn_args.context = model3d_interfaces; - scn_args.trad = TR; - scn_args.tref = TREF; + scn_args.trad.temperature = TR; + scn_args.trad.reference = TR; + scn_args.t_range[0] = MMIN(MMIN(MMIN(MMIN(T0_FLUID, T0_SOLID), TA), TG), TR); + scn_args.t_range[1] = MMAX(MMAX(MMAX(MMAX(T0_FLUID, T0_SOLID), TA), TG), TR); OK(sdis_scene_create(dev, &scn_args, &box_scn)); /* Create the square scene */ @@ -846,8 +883,10 @@ main(int argc, char** argv) scn_args.nprimitives = model2d_nsegments; scn_args.nvertices = model2d_nvertices; scn_args.context = model2d_interfaces; - scn_args.trad = TR; - scn_args.tref = TREF; + scn_args.trad.temperature = TR; + scn_args.trad.reference = TR; + scn_args.t_range[0] = MMIN(MMIN(MMIN(MMIN(T0_FLUID, T0_SOLID), TA), TG), TR); + scn_args.t_range[1] = MMAX(MMAX(MMAX(MMAX(T0_FLUID, T0_SOLID), TA), TG), TR); OK(sdis_scene_2d_create(dev, &scn_args, &square_scn)); /* Release the interfaces */ @@ -858,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); @@ -875,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 @@ -17,6 +17,7 @@ #include <rsys/math.h> enum heat_vertex_attrib { + HEAT_VERTEX_BRANCH_ID, HEAT_VERTEX_WEIGHT, HEAT_VERTEX_TIME, HEAT_VERTEX_TYPE @@ -125,6 +126,7 @@ solve_green_path(struct sdis_green_path* path, void* ctx) BA(sdis_green_path_get_limit_point(NULL, &pt)); BA(sdis_green_path_get_limit_point(path, NULL)); if(end_type == SDIS_GREEN_PATH_END_RADIATIVE) { + struct sdis_ambient_radiative_temperature trad; struct sdis_green_function* green; struct sdis_scene* scn; BO(sdis_green_path_get_limit_point(path, &pt)); @@ -140,8 +142,9 @@ solve_green_path(struct sdis_green_path* path, void* ctx) BA(sdis_scene_get_ambient_radiative_temperature(NULL, NULL)); BA(sdis_scene_get_ambient_radiative_temperature(scn, NULL)); - BA(sdis_scene_get_ambient_radiative_temperature(NULL, &temp)); - OK(sdis_scene_get_ambient_radiative_temperature(scn, &temp)); + BA(sdis_scene_get_ambient_radiative_temperature(NULL, &trad)); + OK(sdis_scene_get_ambient_radiative_temperature(scn, &trad)); + temp = trad.temperature; } else { OK(sdis_green_path_get_limit_point(path, &pt)); switch(pt.type) { @@ -176,31 +179,54 @@ solve_green_path(struct sdis_green_path* path, void* ctx) return RES_OK; } +static size_t +heat_path_get_vertices_count(const struct sdis_heat_path* path) +{ + size_t istrip = 0; + size_t nstrips = 0; + size_t nvertices = 0; + CHK(path); + + OK(sdis_heat_path_get_line_strips_count(path, &nstrips)); + FOR_EACH(istrip, 0, nstrips) { + size_t n; + OK(sdis_heat_path_line_strip_get_vertices_count(path, istrip, &n)); + nvertices += n; + } + return nvertices; +} + static void -dump_heat_path_position(FILE* stream, const struct sdis_heat_path* path) +dump_heat_path_positions(FILE* stream, const struct sdis_heat_path* path) { - size_t nverts; - size_t ivert; + size_t istrip, nstrips; + size_t ivert, nverts; CHK(stream && path); - OK(sdis_heat_path_get_vertices_count(path, &nverts)); - FOR_EACH(ivert, 0, nverts) { - struct sdis_heat_vertex vtx; - OK(sdis_heat_path_get_vertex(path, ivert, &vtx)); - fprintf(stream, "%g %g %g\n", SPLIT3(vtx.P)); + OK(sdis_heat_path_get_line_strips_count(path, &nstrips)); + FOR_EACH(istrip, 0, nstrips) { + OK(sdis_heat_path_line_strip_get_vertices_count(path, istrip, &nverts)); + FOR_EACH(ivert, 0, nverts) { + struct sdis_heat_vertex vtx; + OK(sdis_heat_path_line_strip_get_vertex(path, istrip, ivert, &vtx)); + fprintf(stream, "%g %g %g\n", SPLIT3(vtx.P)); + } } } static void -dump_heat_path_segments - (FILE* stream, const struct sdis_heat_path* path, const size_t offset) +dump_heat_path_line_strip + (FILE* stream, + const struct sdis_heat_path* path, + const size_t istrip, + const size_t offset) { - size_t nverts, ivert; + size_t ivert, nverts; CHK(stream); - OK(sdis_heat_path_get_vertices_count(path, &nverts)); + OK(sdis_heat_path_line_strip_get_vertices_count(path, istrip, &nverts)); fprintf(stream, "%lu", (unsigned long)nverts); FOR_EACH(ivert, 0, nverts) { fprintf(stream, " %lu", (unsigned long)(ivert + offset)); @@ -214,29 +240,37 @@ dump_heat_path_vertex_attribs const struct sdis_heat_path* path, const enum heat_vertex_attrib attr) { - size_t nverts, ivert; + size_t ivert, nverts; + size_t istrip, nstrips; CHK(stream && path); - OK(sdis_heat_path_get_vertices_count(path, &nverts)); - FOR_EACH(ivert, 0, nverts) { - struct sdis_heat_vertex vtx; - OK(sdis_heat_path_get_vertex(path, ivert, &vtx)); - switch(attr) { - case HEAT_VERTEX_WEIGHT: - fprintf(stream, "%g\n", vtx.weight); - break; - case HEAT_VERTEX_TIME: - fprintf(stream, "%g\n", IS_INF(vtx.time) ? FLT_MAX : vtx.time); - break; - case HEAT_VERTEX_TYPE: - switch(vtx.type) { - case SDIS_HEAT_VERTEX_CONDUCTION: fprintf(stream, "0.0\n"); break; - case SDIS_HEAT_VERTEX_CONVECTION: fprintf(stream, "0.5\n"); break; - case SDIS_HEAT_VERTEX_RADIATIVE: fprintf(stream, "1.0\n"); break; - default: FATAL("Unreachable code.\n"); break; - } - break; - default: FATAL("Unreachable code.\n"); break; + OK(sdis_heat_path_get_line_strips_count(path, &nstrips)); + FOR_EACH(istrip, 0, nstrips) { + OK(sdis_heat_path_line_strip_get_vertices_count(path, istrip, &nverts)); + FOR_EACH(ivert, 0, nverts) { + struct sdis_heat_vertex vtx; + OK(sdis_heat_path_line_strip_get_vertex(path, istrip, ivert, &vtx)); + switch(attr) { + case HEAT_VERTEX_BRANCH_ID: + fprintf(stream, "%i\n", vtx.branch_id); + break; + case HEAT_VERTEX_WEIGHT: + fprintf(stream, "%g\n", vtx.weight); + break; + case HEAT_VERTEX_TIME: + fprintf(stream, "%g\n", + IS_INF(vtx.time) || vtx.time > FLT_MAX ? -1 : vtx.time); + break; + case HEAT_VERTEX_TYPE: + switch(vtx.type) { + case SDIS_HEAT_VERTEX_CONDUCTION: fprintf(stream, "0.0\n"); break; + case SDIS_HEAT_VERTEX_CONVECTION: fprintf(stream, "0.5\n"); break; + case SDIS_HEAT_VERTEX_RADIATIVE: fprintf(stream, "1.0\n"); break; + default: FATAL("Unreachable code.\n"); break; + } + break; + default: FATAL("Unreachable code.\n"); break; + } } } } @@ -304,9 +338,9 @@ dump_heat_paths(FILE* stream, const struct sdis_estimator* estimator) const struct sdis_heat_path* path; size_t ipath; size_t npaths; + size_t nstrips_overall; size_t nvertices; size_t offset; - size_t n; CHK(stream && estimator); OK(sdis_estimator_get_paths_count(estimator, &npaths)); @@ -318,30 +352,40 @@ dump_heat_paths(FILE* stream, const struct sdis_estimator* estimator) fprintf(stream, "ASCII\n"); fprintf(stream, "DATASET POLYDATA\n"); - /* Compute the overall number of vertices */ + /* Compute the overall number of vertices and the overall number of strips */ nvertices = 0; + nstrips_overall = 0; FOR_EACH(ipath, 0, npaths) { + size_t n; OK(sdis_estimator_get_path(estimator, ipath, &path)); - OK(sdis_heat_path_get_vertices_count(path, &n)); - nvertices += n; + nvertices += heat_path_get_vertices_count(path); + OK(sdis_heat_path_get_line_strips_count(path, &n)); + nstrips_overall += n; } /* Write path positions */ fprintf(stream, "POINTS %lu double\n", (unsigned long)nvertices); FOR_EACH(ipath, 0, npaths) { OK(sdis_estimator_get_path(estimator, ipath, &path)); - dump_heat_path_position(stream, path); + dump_heat_path_positions(stream, path); } /* Write the segment of the paths */ fprintf(stream, "LINES %lu %lu\n", - (unsigned long)npaths, (unsigned long)(npaths + nvertices)); + (unsigned long)(nstrips_overall), + (unsigned long)(nstrips_overall + nvertices)); offset = 0; FOR_EACH(ipath, 0, npaths) { + size_t path_istrip; + size_t path_nstrips; OK(sdis_estimator_get_path(estimator, ipath, &path)); - dump_heat_path_segments(stream, path, offset); - OK(sdis_heat_path_get_vertices_count(path, &n)); - offset += n; + OK(sdis_heat_path_get_line_strips_count(path, &path_nstrips)); + FOR_EACH(path_istrip, 0, path_nstrips) { + size_t n; + dump_heat_path_line_strip(stream, path, path_istrip, offset); + OK(sdis_heat_path_line_strip_get_vertices_count(path, path_istrip, &n)); + offset += n; + } } fprintf(stream, "POINT_DATA %lu\n", (unsigned long)nvertices); @@ -374,18 +418,31 @@ dump_heat_paths(FILE* stream, const struct sdis_estimator* estimator) dump_heat_path_vertex_attribs(stream, path, HEAT_VERTEX_TIME); } + /* Write the branch id of the random walk vertices */ + fprintf(stream, "SCALARS BranchID int 1\n"); + fprintf(stream, "LOOKUP_TABLE default\n"); + FOR_EACH(ipath, 0, npaths) { + OK(sdis_estimator_get_path(estimator, ipath, &path)); + dump_heat_path_vertex_attribs(stream, path, HEAT_VERTEX_BRANCH_ID); + } + /* Write path type */ - fprintf(stream, "CELL_DATA %lu\n", (unsigned long)npaths); + fprintf(stream, "CELL_DATA %lu\n", (unsigned long)nstrips_overall); fprintf(stream, "SCALARS Path_Type float 1\n"); fprintf(stream, "LOOKUP_TABLE path_type\n"); FOR_EACH(ipath, 0, npaths) { + size_t path_istrip; + size_t path_nstrips; enum sdis_heat_path_flag status = SDIS_HEAT_PATH_NONE; OK(sdis_estimator_get_path(estimator, ipath, &path)); OK(sdis_heat_path_get_status(path, &status)); - switch(status) { - case SDIS_HEAT_PATH_SUCCESS: fprintf(stream, "0.0\n"); break; - case SDIS_HEAT_PATH_FAILURE: fprintf(stream, "1.0\n"); break; - default: FATAL("Unreachable code.\n"); break; + OK(sdis_heat_path_get_line_strips_count(path, &path_nstrips)); + FOR_EACH(path_istrip, 0, path_nstrips) { + switch(status) { + case SDIS_HEAT_PATH_SUCCESS: fprintf(stream, "0.0\n"); break; + case SDIS_HEAT_PATH_FAILURE: fprintf(stream, "1.0\n"); break; + default: FATAL("Unreachable code.\n"); break; + } } } fprintf(stream, "LOOKUP_TABLE path_type 2\n"); @@ -423,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 */ @@ -41,7 +46,7 @@ static const double box_vertices[8/*#vertices*/*3/*#coords per vertex*/] = { 0.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; -static const size_t box_nvertices = sizeof(box_vertices) / (3*sizeof(double)); +static const size_t box_nvertices = sizeof(box_vertices) / (sizeof(double)*3); /* The following array lists the indices toward the 3D vertices of each * triangle. @@ -61,7 +66,7 @@ static const size_t box_indices[12/*#triangles*/*3/*#indices per triangle*/] = { 2, 6, 7, 7, 3, 2, /* +Y */ 0, 1, 5, 5, 4, 0 /* -Y */ }; -static const size_t box_ntriangles = sizeof(box_indices) / (3*sizeof(size_t)); +static const size_t box_ntriangles = sizeof(box_indices) / (sizeof(size_t)*3); static INLINE void box_get_indices(const size_t itri, size_t ids[3], void* context) @@ -103,7 +108,7 @@ static const double square_vertices[4/*#vertices*/*2/*#coords per vertex*/] = { 0.0, 1.0, 1.0, 1.0 }; -static const size_t square_nvertices = sizeof(square_vertices)/(2*sizeof(double)); +static const size_t square_nvertices = sizeof(square_vertices)/(sizeof(double)*2); static const size_t square_indices[4/*#segments*/*2/*#indices per segment*/]= { 0, 1, /* Bottom */ @@ -111,7 +116,7 @@ static const size_t square_indices[4/*#segments*/*2/*#indices per segment*/]= { 2, 3, /* Top */ 3, 0 /* Right */ }; -static const size_t square_nsegments = sizeof(square_indices)/(2*sizeof(size_t)); +static const size_t square_nsegments = sizeof(square_indices)/(sizeof(size_t)*2); static INLINE void square_get_indices(const size_t iseg, size_t ids[2], void* context) @@ -165,38 +170,99 @@ dummy_interface_getter } static const struct sdis_solid_shader DUMMY_SOLID_SHADER = { - dummy_medium_getter, - dummy_medium_getter, - dummy_medium_getter, - dummy_medium_getter, - dummy_medium_getter, - dummy_medium_getter, - 0 + dummy_medium_getter, /* Calorific capacity */ + dummy_medium_getter, /* Thermal conductivity */ + dummy_medium_getter, /* Volumic mass */ + dummy_medium_getter, /* Delta */ + dummy_medium_getter, /* Volumic power */ + dummy_medium_getter, /* Temperature */ + 0 /* Initial time */ }; static const struct sdis_fluid_shader DUMMY_FLUID_SHADER = { - dummy_medium_getter, - dummy_medium_getter, - dummy_medium_getter, - 0 + dummy_medium_getter, /* Calorific capacity */ + dummy_medium_getter, /* Volumic mass */ + dummy_medium_getter, /* Temperature */ + 0 /* Initial time */ }; #define DUMMY_INTERFACE_SIDE_SHADER__ { \ - dummy_interface_getter, \ - dummy_interface_getter, \ - dummy_interface_getter, \ - dummy_interface_getter \ + dummy_interface_getter, /* Temperature */ \ + dummy_interface_getter, /* Flux */ \ + dummy_interface_getter, /* Emissivity */ \ + dummy_interface_getter, /* Specular fraction */ \ + dummy_interface_getter /* Reference temperature */ \ } static const struct sdis_interface_shader DUMMY_INTERFACE_SHADER = { - dummy_interface_getter, - 0, - dummy_interface_getter, - DUMMY_INTERFACE_SIDE_SHADER__, - DUMMY_INTERFACE_SIDE_SHADER__ + dummy_interface_getter, /* Convection coef */ + 0, /* Upper bound of the convection coef */ + dummy_interface_getter, /* Thermal contact resistance */ + DUMMY_INTERFACE_SIDE_SHADER__, /* Front side */ + DUMMY_INTERFACE_SIDE_SHADER__ /* Back side */ }; /******************************************************************************* + * 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 @@ -51,7 +51,7 @@ #define T0 320 #define LAMBDA 0.1 #define P0 10 -#define DELTA 1.0/40.0 +#define DELTA 1.0/50.0 /******************************************************************************* * Media @@ -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)); @@ -409,7 +407,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; solid_shader.volumic_power = solid_get_volumic_power; @@ -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 @@ -66,7 +66,7 @@ static const double vertices[16/*#vertices*/*3/*#coords per vertex*/] = { 0.1, 0.6, 0.5, 0.1, 0.4, 0.5 }; -static const size_t nvertices = sizeof(vertices)/(3*sizeof(double)); +static const size_t nvertices = sizeof(vertices)/(sizeof(double)*3); static const size_t indices[36/*#triangles*/*3/*#indices per triangle*/]= { 0, 4, 5, 5, 1, 0, /* Cuboid left */ @@ -90,7 +90,7 @@ static const size_t indices[36/*#triangles*/*3/*#indices per triangle*/]= { 8, 9, 10, 10, 11, 8, /* Cube back */ 12, 15, 14, 14, 13, 12 /* Cube front */ }; -static const size_t ntriangles = sizeof(indices)/(3*sizeof(size_t)); +static const size_t ntriangles = sizeof(indices)/(sizeof(size_t)*3); /******************************************************************************* * Geometry @@ -262,7 +262,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; @@ -302,8 +301,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; @@ -330,7 +328,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; solid_shader.volumic_power = solid_get_volumic_power; @@ -467,8 +465,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_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 @@ -99,7 +99,7 @@ static const double vertices[8/*#vertices*/*2/*#coords per vertex*/] = { 0.1, 0.6, 0.1, 0.4 }; -static const size_t nvertices = sizeof(vertices)/(2*sizeof(double)); +static const size_t nvertices = sizeof(vertices)/(sizeof(double)*2); static const size_t indices[8/*#segments*/*2/*#indices per segment*/]= { 0, 1, /* Rectangle left */ @@ -111,7 +111,7 @@ static const size_t indices[8/*#segments*/*2/*#indices per segment*/]= { 6, 7, /* Square right */ 7, 4 /* Square bottom */ }; -static const size_t nsegments = sizeof(indices)/(2*sizeof(size_t)); +static const size_t nsegments = sizeof(indices)/(sizeof(size_t)*2); /******************************************************************************* * Geometry @@ -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; @@ -362,7 +360,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; solid_shader.volumic_power = solid_get_volumic_power; @@ -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 @@ -84,7 +84,7 @@ static const double vertices[8/*#vertices*/*2/*#coords per vertex*/] = { 100000.5, 1.4, /* 6 */ 100000.5, 0.0 /* 7 */ }; -static const size_t nvertices = sizeof(vertices)/(2*sizeof(double)); +static const size_t nvertices = sizeof(vertices)/(sizeof(double)*2); static const size_t indices[10/*#segments*/*2/*#indices per segment*/]= { 0, 1, @@ -98,7 +98,7 @@ static const size_t indices[10/*#segments*/*2/*#indices per segment*/]= { 6, 1, 2, 5 }; -static const size_t nsegments = sizeof(indices)/(2*sizeof(size_t)); +static const size_t nsegments = sizeof(indices)/(sizeof(size_t)*2); /******************************************************************************* * Geometry @@ -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; @@ -290,7 +288,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; solid_shader.volumic_power = solid_get_volumic_power; @@ -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; @@ -259,7 +257,7 @@ main(int argc, char** argv) solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta_solid = solid_get_delta; + solid_shader.delta = solid_get_delta; solid_shader.temperature = solid_get_temperature; solid_shader.volumic_power = solid_get_volumic_power; @@ -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; }