stardis-solver

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

commit 1701de82b410477d72319d78e1274f3589863847
parent 1db75a944fe9f179903ff7c51b3fde60367604fa
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 13 Mar 2019 14:11:17 +0100

Merge branch 'release_0.7'

Diffstat:
MREADME.md | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mcmake/CMakeLists.txt | 43+++++++++++++++++++++++++++++++++++++------
Msrc/sdis.h | 392++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Asrc/sdis_Xd_begin.h | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_Xd_end.h | 41+++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_estimator.c | 115++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/sdis_estimator_c.h | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Asrc/sdis_green.c | 998+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_green.h | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path.c | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path.h | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_boundary_Xd.h | 648+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_conductive_Xd.h | 308+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_convective_Xd.h | 308+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_radiative_Xd.h | 227+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_interface.c | 123+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/sdis_interface_c.h | 27+++++++++++++++++++++++----
Msrc/sdis_medium.c | 27+++++++++++++++++++++++++++
Msrc/sdis_medium_c.h | 40++++++++++++++++++++++++++++++++++------
Asrc/sdis_misc.h | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_realisation.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_realisation.h | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_realisation_Xd.h | 384+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_scene.c | 87++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/sdis_scene_Xd.h | 83+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/sdis_scene_c.h | 22+++++++++++++++++-----
Msrc/sdis_solve.c | 610+++++++++++++++++++++++--------------------------------------------------------
Dsrc/sdis_solve_Xd.h | 2265-------------------------------------------------------------------------------
Asrc/sdis_solve_boundary_Xd.h | 619+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_solve_medium_Xd.h | 412+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_solve_probe_Xd.h | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_solve_probe_boundary_Xd.h | 483+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_solve_radiative.c | 20++++++++++++++++++++
Msrc/test_sdis_conducto_radiative.c | 16+++++++++++++++-
Msrc/test_sdis_conducto_radiative_2d.c | 16+++++++++++++++-
Msrc/test_sdis_convection.c | 61++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/test_sdis_convection_non_uniform.c | 51+++++++++++++++++++++++++++++++++++++++++++--------
Msrc/test_sdis_flux.c | 129++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/test_sdis_interface.c | 31+++++++++++++++++++++++++++++++
Msrc/test_sdis_medium.c | 39++++++++++++++++++++++++++++++++++++---
Msrc/test_sdis_scene.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_solve_boundary.c | 160+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/test_sdis_solve_boundary_flux.c | 4++--
Asrc/test_sdis_solve_medium.c | 444+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_solve_medium_2d.c | 418+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_solve_probe.c | 151+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/test_sdis_solve_probe2.c | 12+++++++++++-
Msrc/test_sdis_solve_probe2_2d.c | 12+++++++++++-
Msrc/test_sdis_solve_probe3.c | 12+++++++++++-
Msrc/test_sdis_solve_probe3_2d.c | 12+++++++++++-
Msrc/test_sdis_solve_probe_2d.c | 13+++++++++++--
Asrc/test_sdis_utils.c | 361+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_utils.h | 46+++++++++++++++++++++++++++++++++++++++++++++-
Msrc/test_sdis_volumic_power.c | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/test_sdis_volumic_power2.c | 3+--
Msrc/test_sdis_volumic_power2_2d.c | 2+-
Msrc/test_sdis_volumic_power3_2d.c | 2+-
Msrc/test_sdis_volumic_power4_2d.c | 64++++++++++++++++++++++++++++------------------------------------
58 files changed, 8747 insertions(+), 3012 deletions(-)

diff --git a/README.md b/README.md @@ -25,12 +25,59 @@ variable the install directories of its dependencies. ## Release notes +### Version 0.7 + +#### Add Green function support + +Provide new solve functions that compute and save the Green function, i.e. the +propagator used in regular solvers. The resulting Green function can be then +evaluated to obtain an estimate of the temperature. + +The time spent to compute the Green function is comparable to the computation +time of regular solvers; actually, they rely on the same code. However, its +evaluation is instantaneous while it still handles the limit conditions, the +boundary fluxes and the power term of the media *at the moment* of the +evaluation. This means that one can estimate the Green function of a system +only one time and then evaluate it with different limit conditions, boundary +fluxes or power terms with negligible computation costs. + +Currently, Stardis assumes that during the Green function estimation, the +properties of the system do not depend on time. In addition, it assumes that +the boundary fluxes and the volumic powers are constants in time and space. +Anyway, on Green function evaluation, the limit conditions of the system can +still vary in time and space; systems in steady state can be simulated with +Green functions. + +#### Add heat path registration + +Add the `int register_paths` mask to almost all solve functions to enable the +registration against the returned estimator of the failure and/or successful +random paths used by the solvers. For each path, the registered data are: + +- the vertices of the path; +- the type of the path (failed or succeed); +- the type of the path vertices (conductive, convective or radiative); +- the Monte-Carlo weight of each path vertex; +- the current time of each path vertex. + +Note that the amount of registered data can be huge if too more paths are +registered. Consequently, this functionality should be used with few +realisations to obtain a subset of representative paths, or to only register +the few paths that failed in order to diagnose what went wrong. + +#### Miscellaneous + +- Add the `sdis_solve_medium` function: it estimates the average temperature + of a medium. +- Fix the setup of the interfaces: the interface associated to a geometric + primitive could not be the right one. + ### Version 0.6.1 - Bump version of the Star-Enclosures[2D] dependencies: the new versions fix issues in the construction of fluid enclosures. -- Bump version of the Star-<2D|3D> dependencies: the new versions rely on Embree3 - rather than on Embree2 for their ray-tracing back-end. +- Bump version of the Star-<2D|3D> dependencies: the new versions rely on + Embree3 rather than on Embree2 for their ray-tracing back-end. ### Version 0.6 @@ -138,8 +185,8 @@ First version and implementation of the Stardis solver API. ## License -Stardis is Copyright (C) 2016-2019 |Meso|Star> (<contact@meso-star.com>). It is -free software released under the GPLv3+ license. You are welcome to -redistribute it under certain conditions; refer to the COPYING files for -details. +Copyright (C) 2016-2019 |Meso|Star> (<contact@meso-star.com>). Stardis 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 @@ -16,6 +16,7 @@ cmake_minimum_required(VERSION 3.0) project(stardis C) enable_testing() +cmake_policy(SET CMP0054 OLD) # Disable OpenMP CMake warning include(CMakeDependentOption) @@ -41,14 +42,22 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) include(rcmake) include(rcmake_runtime) +include_directories( + ${Star2D_INCLUDE_DIR} + ${Star3D_INCLUDE_DIR} + ${StarSP_INCLUDE_DIR} + ${StarEnc_INCLUDE_DIR} + ${StarEnc2D_INCLUDE_DIR} + ${RSys_INCLUDE_DIR}) + rcmake_append_runtime_dirs(_runtime_dirs RSys Star3D StarSP StarEnc StarEnc2D) ################################################################################ # Configure and define targets ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 6) -set(VERSION_PATCH 1) +set(VERSION_MINOR 7) +set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SDIS_FILES_SRC @@ -57,8 +66,11 @@ set(SDIS_FILES_SRC sdis_data.c sdis_device.c sdis_estimator.c + sdis_green.c + sdis_heat_path.c sdis_interface.c sdis_medium.c + sdis_realisation.c sdis_scene.c sdis_solve.c) @@ -69,11 +81,24 @@ set(SDIS_FILES_INC sdis_camera.h sdis_device_c.h sdis_estimator_c.h + sdis_green.h + sdis_heat_path.h + sdis_heat_path_boundary_Xd.h + sdis_heat_path_conductive_Xd.h + sdis_heat_path_convective_Xd.h + sdis_heat_path_radiative_Xd.h sdis_interface_c.h sdis_medium_c.h + sdis_realisation.h + sdis_realisation_Xd.h sdis_scene_c.h sdis_scene_Xd.h - sdis_solve_Xd.h) + sdis_solve_boundary_Xd.h + sdis_solve_medium_Xd.h + sdis_solve_probe_Xd.h + sdis_solve_probe_boundary_Xd.h + sdis_Xd_begin.h + sdis_Xd_end.h) set(SDIS_FILES_DOC COPYING README.md) @@ -123,9 +148,13 @@ rcmake_setup_devel(sdis Stardis ${VERSION} sdis_version.h) if(NOT NO_TEST) find_package(Star3DUT REQUIRED 0.2.0) + add_library(sdis-test-utils STATIC + ${SDIS_SOURCE_DIR}/test_sdis_utils.h + ${SDIS_SOURCE_DIR}/test_sdis_utils.c) + function(build_test _name) add_executable(${_name} ${SDIS_SOURCE_DIR}/${_name}.c) - target_link_libraries(${_name} RSys sdis) + target_link_libraries(${_name} sdis-test-utils RSys sdis) endfunction() function(register_test _name) @@ -159,21 +188,23 @@ if(NOT NO_TEST) 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_volumic_power) + new_test(test_sdis_volumic_power4_2d) # Additionnal tests build_test(test_sdis_volumic_power2) build_test(test_sdis_volumic_power2_2d) build_test(test_sdis_volumic_power3_2d) - build_test(test_sdis_volumic_power4_2d) if(ALL_TESTS) add_test(test_sdis_volumic_power2 test_sdis_volumic_power2) add_test(test_sdis_volumic_power2_2d test_sdis_volumic_power2_2d) add_test(test_sdis_volumic_power3_2d test_sdis_volumic_power3_2d) - add_test(test_sdis_volumic_power4_2d test_sdis_volumic_power4_2d) endif() + target_link_libraries(test_sdis_solve_medium Star3DUT) 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) diff --git a/src/sdis.h b/src/sdis.h @@ -47,23 +47,28 @@ /* Forward declaration of external opaque data types */ struct logger; struct mem_allocator; +struct senc2d_descriptor; +struct senc_descriptor; /* Forward declaration of the Stardis opaque data types. These data types are - * ref counted. Once created with the appropriated `sdis_<TYPE>_create' - * function, the caller implicitly owns the created data, i.e. its reference - * counter is set to 1. The sdis_<TYPE>_ref_<get|put> functions get or release - * a reference on the data, i.e. they increment or decrement the reference - * counter, respectively. When this counter reaches 0, the object is silently - * destroyed and cannot be used anymore. */ + * ref counted. Once created the caller implicitly owns the created data, i.e. + * its reference counter is set to 1. The sdis_<TYPE>_ref_<get|put> functions + * get or release a reference on the data, i.e. they increment or decrement the + * reference counter, respectively. When this counter reaches 0, the object is + * silently destroyed and cannot be used anymore. */ struct sdis_accum_buffer; struct sdis_camera; struct sdis_data; struct sdis_device; struct sdis_estimator; +struct sdis_green_function; struct sdis_interface; struct sdis_medium; struct sdis_scene; +/* Forward declaration of non ref counted types */ +struct sdis_heat_path; + enum sdis_side { SDIS_FRONT, SDIS_BACK, @@ -77,9 +82,34 @@ enum sdis_medium_type { }; enum sdis_estimator_type { - SDIS_TEMPERATURE_ESTIMATOR, - SDIS_FLUX_ESTIMATOR, - SDIS_EST_TYPES_COUNT__ + SDIS_ESTIMATOR_TEMPERATURE, + SDIS_ESTIMATOR_FLUX, + SDIS_ESTIMATOR_TYPES_COUNT__ +}; + +enum sdis_scene_dimension { + SDIS_SCENE_2D, + SDIS_SCENE_3D +}; + +enum sdis_point_type { + SDIS_FRAGMENT, + SDIS_VERTEX, + SDIS_POINT_TYPES_COUNT__, + SDIS_POINT_NONE = SDIS_POINT_TYPES_COUNT__ +}; + +enum sdis_heat_vertex_type { + SDIS_HEAT_VERTEX_CONDUCTION, + SDIS_HEAT_VERTEX_CONVECTION, + SDIS_HEAT_VERTEX_RADIATIVE +}; + +enum sdis_heat_path_flag { + SDIS_HEAT_PATH_SUCCEED = BIT(0), + SDIS_HEAT_PATH_FAILED = BIT(1), + SDIS_HEAT_PATH_ALL = SDIS_HEAT_PATH_SUCCEED | SDIS_HEAT_PATH_FAILED, + SDIS_HEAT_PATH_NONE = 0 }; /* Random walk vertex, i.e. a spatiotemporal position at a given step of the @@ -230,6 +260,78 @@ typedef res_T const size_t naccums[2], /* #accumulations in X and Y */ const struct sdis_accum* accums); /* List of row ordered accumulations */ +/* Vertex of heat path v*/ +struct sdis_heat_vertex { + double P[3]; + double time; + double weight; + enum sdis_heat_vertex_type type; +}; +#define SDIS_HEAT_VERTEX_NULL__ {{0,0,0}, 0, 0, SDIS_HEAT_VERTEX_CONDUCTION} +static const struct sdis_heat_vertex SDIS_HEAT_VERTEX_NULL = + SDIS_HEAT_VERTEX_NULL__; + +/* Path used to estimate the green function */ +struct sdis_green_path { + /* Internal data. Should not be accessed */ + void* green__; + size_t id__; +}; +#define SDIS_GREEN_PATH_NULL__ {NULL, 0} +static const struct sdis_green_path SDIS_GREEN_PATH_NULL = + SDIS_GREEN_PATH_NULL__; + +struct sdis_point { + union { + struct { + struct sdis_medium* medium; + struct sdis_rwalk_vertex vertex; + } mdmvert; + struct { + struct sdis_interface* intface; + struct sdis_interface_fragment fragment; + } itfrag; + } data; + enum sdis_point_type type; +}; +#define SDIS_POINT_NULL__ { {{NULL, SDIS_RWALK_VERTEX_NULL__}}, SDIS_POINT_NONE} +static const struct sdis_point SDIS_POINT_NULL = SDIS_POINT_NULL__; + +/* Functor used to process the paths registered against the green function */ +typedef res_T +(*sdis_process_green_path_T) + (struct sdis_green_path* path, + void* context); + +/* Functor used to process the power factor registered along a green path for a + * given medium */ +typedef res_T +(*sdis_process_medium_power_term_T) + (struct sdis_medium* medium, + const double power_term, + void* context); + +/* Functor used to process the flux factor registered along a green path for a + * given interface side */ +typedef res_T +(*sdis_process_interface_flux_term_T) + (struct sdis_interface* interf, + const enum sdis_side side, + const double flux_term, + void* context); + +/* Functor used to process a heat path registered against the estimator */ +typedef res_T +(*sdis_process_heat_path_T) + (const struct sdis_heat_path* path, + void* context); + +/* Functor used to process the vertices of a heat path */ +typedef res_T +(*sdis_process_heat_vertex_T) + (const struct sdis_heat_vertex* vertex, + void* context); + BEGIN_DECLS /******************************************************************************* @@ -373,6 +475,11 @@ sdis_fluid_create struct sdis_medium** fluid); SDIS_API res_T +sdis_fluid_get_shader + (const struct sdis_medium* fluid, + struct sdis_fluid_shader* shader); + +SDIS_API res_T sdis_solid_create (struct sdis_device* dev, const struct sdis_solid_shader* shader, @@ -380,6 +487,11 @@ sdis_solid_create struct sdis_medium** solid); SDIS_API res_T +sdis_solid_get_shader + (const struct sdis_medium* solid, + struct sdis_solid_shader* shader); + +SDIS_API res_T sdis_medium_ref_get (struct sdis_medium* medium); @@ -395,6 +507,10 @@ SDIS_API struct sdis_data* sdis_medium_get_data (struct sdis_medium* medium); +SDIS_API unsigned +sdis_medium_get_id + (const struct sdis_medium* medium); + /******************************************************************************* * An interface is the boundary between 2 media. ******************************************************************************/ @@ -415,6 +531,19 @@ SDIS_API res_T sdis_interface_ref_put (struct sdis_interface* interf); +SDIS_API res_T +sdis_interface_get_shader + (const struct sdis_interface* interf, + struct sdis_interface_shader* shader); + +SDIS_API struct sdis_data* +sdis_interface_get_data + (struct sdis_interface* interf); + +SDIS_API unsigned +sdis_interface_get_id + (const struct sdis_interface* interf); + /******************************************************************************* * A scene is a collection of primitives. Each primitive is the geometric * support of the interface between 2 media. @@ -529,6 +658,38 @@ sdis_scene_boundary_project_position const double pos[3], double uv[]); +/* Get the descriptor of the 3D scene's enclosures */ +SDIS_API res_T +sdis_scene_get_analysis + (struct sdis_scene* scn, + struct senc_descriptor** descriptor); + +/* Get the descriptor of the 2D scene's enclosures */ +SDIS_API res_T +sdis_scene_2d_get_analysis + (struct sdis_scene* scn, + struct senc2d_descriptor** descriptor); + +/* Release the descriptor of the scene's enclosures; subsequent attempts to get + * it will fail. */ +SDIS_API res_T +sdis_scene_release_analysis + (struct sdis_scene* scn); + +SDIS_API res_T +sdis_scene_get_dimension + (const struct sdis_scene* scn, + enum sdis_scene_dimension* dim); + +/* Return the area/volume of occupied by a medium in a 2D/3D scene. Only + * enclosed media are handled, i.e. media whose border are explicitly defined + * by a geometry. */ +SDIS_API res_T +sdis_scene_get_medium_spread + (struct sdis_scene* scn, + const struct sdis_medium* mdm, + double* spread); + /******************************************************************************* * An estimator stores the state of a simulation ******************************************************************************/ @@ -548,7 +709,7 @@ sdis_estimator_get_type SDIS_API res_T sdis_estimator_get_realisation_count (const struct sdis_estimator* estimator, - size_t* nrealisations); /* Succesfull ones */ + size_t* nrealisations); /* Successful ones */ SDIS_API res_T sdis_estimator_get_failure_count @@ -570,14 +731,120 @@ sdis_estimator_get_radiative_flux (const struct sdis_estimator* estimator, struct sdis_mc* flux); - SDIS_API res_T sdis_estimator_get_total_flux (const struct sdis_estimator* estimator, struct sdis_mc* flux); +SDIS_API res_T +sdis_estimator_get_paths_count + (const struct sdis_estimator* estimator, + size_t* npaths); + +SDIS_API res_T +sdis_estimator_get_path + (const struct sdis_estimator* estimator, + const size_t ipath, + const struct sdis_heat_path** path); + +SDIS_API res_T +sdis_estimator_for_each_path + (const struct sdis_estimator* estimator, + sdis_process_heat_path_T func, + void* context); + /******************************************************************************* - * Miscellaneous functions + * The green function saves the estimation of the propagator + ******************************************************************************/ +SDIS_API res_T +sdis_green_function_ref_get + (struct sdis_green_function* green); + +SDIS_API res_T +sdis_green_function_ref_put + (struct sdis_green_function* green); + +SDIS_API res_T +sdis_green_function_solve + (struct sdis_green_function* green, + const double time_range[2], /* Observation time */ + struct sdis_estimator** estimator); + +/* Retrieve the number of valid paths used to estimate the green function. It + * is actually equal to the number of successful realisations. */ +SDIS_API res_T +sdis_green_function_get_paths_count + (const struct sdis_green_function* green, + size_t* npaths); + +/* Retrieve the number of rejected paths during the estimation of the green + * function due to numerical issues and data inconsistency */ +SDIS_API res_T +sdis_green_function_get_invalid_paths_count + (const struct sdis_green_function* green, + size_t* nfails); + +/* Iterate over all valid green function paths */ +SDIS_API res_T +sdis_green_function_for_each_path + (struct sdis_green_function* green, + sdis_process_green_path_T func, + void* context); + +/* Retrieve the spatio-temporal end point of a path used to estimate the green + * function. Note that this point went back in time from the relative + * observation time 0. Its time is thus negative; its absolute value + * represents the time spent by the path into the system. */ +SDIS_API res_T +sdis_green_path_get_limit_point + (struct sdis_green_path* path, + struct sdis_point* pt); + +/* Iterate over all "power terms" associated to the path. Multiply each term + * by the power of their associated medium, that is assumed to be constant in + * time and space, gives the medium power registered along the path. */ +SDIS_API res_T +sdis_green_path_for_each_power_term + (struct sdis_green_path* path, + sdis_process_medium_power_term_T func, + void* context); + +/* Iterate over all "flux terms" associated to the path. Multiply each term by + * the flux of their associated interface side, that is assumed to be constant + * in time and space, gives the interface side flux registered along the path. */ +SDIS_API res_T +sdis_green_path_for_each_flux_term + (struct sdis_green_path* path, + sdis_process_interface_flux_term_T func, + void* context); + +/******************************************************************************* + * Heat path API + ******************************************************************************/ +SDIS_API res_T +sdis_heat_path_get_vertices_count + (const struct sdis_heat_path* path, + size_t* nvertices); + +SDIS_API res_T +sdis_heat_path_get_status + (const struct sdis_heat_path* path, + enum sdis_heat_path_flag* status); + +SDIS_API res_T +sdis_heat_path_get_vertex + (const struct sdis_heat_path* path, + const size_t ivertex, + struct sdis_heat_vertex* vertex); + +SDIS_API res_T +sdis_heat_path_for_each_vertex + (const struct sdis_heat_path* path, + sdis_process_heat_vertex_T func, + void* context); + +/******************************************************************************* + * Solvers ******************************************************************************/ SDIS_API res_T sdis_solve_probe @@ -588,6 +855,7 @@ sdis_solve_probe const double fp_to_meter, /* Scale from floating point units to meters */ const double ambient_radiative_temperature, /* In Kelvin */ const double reference_temperature, /* In Kelvin */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ struct sdis_estimator** estimator); SDIS_API res_T @@ -601,6 +869,45 @@ sdis_solve_probe_boundary const double fp_to_meter, /* Scale from floating point units to meters */ const double ambient_radiative_temperature, /* In Kelvin */ const double reference_temperature, /* In Kelvin */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ + struct sdis_estimator** estimator); + +SDIS_API res_T +sdis_solve_boundary + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t primitives[], /* List of boundary primitives to handle */ + const enum sdis_side sides[], /* Per primitive side to consider */ + const size_t nprimitives, /* #primitives */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double ambient_radiative_temperature, /* In Kelvin */ + const double reference_temperature, /* In Kelvin */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ + struct sdis_estimator** estimator); + +SDIS_API res_T +sdis_solve_probe_boundary_flux + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t iprim, /* Identifier of the primitive on which the probe lies */ + const double uv[2], /* Parametric coordinates of the probe onto the primitve */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double ambient_radiative_temperature, /* In Kelvin */ + const double reference_temperature, /* In Kelvin */ + struct sdis_estimator** estimator); + +SDIS_API res_T +sdis_solve_boundary_flux + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t primitives[], /* List of boundary primitives to handle */ + const size_t nprimitives, /* #primitives */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double ambient_radiative_temperature, /* In Kelvin */ + const double reference_temperature, /* In Kelvin */ struct sdis_estimator** estimator); SDIS_API res_T @@ -618,42 +925,81 @@ sdis_solve_camera void* writer_data); SDIS_API res_T -sdis_solve_boundary +sdis_solve_medium (struct sdis_scene* scn, const size_t nrealisations, /* #realisations */ - const size_t primitives[], /* List of boundary primitives to handle */ - const enum sdis_side sides[], /* Per primitive side to consider */ - const size_t nprimitives, /* #primitives */ + struct sdis_medium* medium, /* Medium to solve */ const double time_range[2], /* Observation time */ const double fp_to_meter, /* Scale from floating point units to meters */ const double ambient_radiative_temperature, /* In Kelvin */ const double reference_temperature, /* In Kelvin */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ struct sdis_estimator** estimator); -/* Flux solver */ +/******************************************************************************* + * Green solvers. + * + * The caller should ensure that green solvers are invoked on scenes whose data + * do not depend on time. Indeed, on green estimation, the time parameter along + * the random walks registers the relative time spent in the system rather than + * an absolute time. As a consequence, the media/interfaces parameters cannot + * vary in time with respect to an absolute time value. + * + * In addition, the green solvers assumes that the interface fluxes are + * constants in time and space. In the same way the volumic power of the solid + * media must be constant in time and space too. Furthermore, note that only + * the interfaces/media that had a flux/volumic power during green estimation + * can update their flux/volumic power value for subsequent + * sdis_green_function_solve invocations: others interfaces/media are + * definitely registered against the green function as interfaces/media with no + * flux/volumic power. + * + * If the aforementioned assumptions are not ensured by the caller, the + * behavior of the estimated green function is undefined. + ******************************************************************************/ SDIS_API res_T -sdis_solve_probe_boundary_flux +sdis_solve_probe_green_function + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const double position[3], /* Probe position */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double ambient_radiative_temperature, /* In Kelvin */ + const double reference_temperature, /* In Kelvin */ + struct sdis_green_function** green); + +SDIS_API res_T +sdis_solve_probe_boundary_green_function (struct sdis_scene* scn, const size_t nrealisations, /* #realisations */ const size_t iprim, /* Identifier of the primitive on which the probe lies */ const double uv[2], /* Parametric coordinates of the probe onto the primitve */ - const double time_range[2], /* Observation time */ + const enum sdis_side side, /* Side of iprim on which the probe lies */ const double fp_to_meter, /* Scale from floating point units to meters */ const double ambient_radiative_temperature, /* In Kelvin */ const double reference_temperature, /* In Kelvin */ - struct sdis_estimator** estimator); + struct sdis_green_function** green); SDIS_API res_T -sdis_solve_boundary_flux +sdis_solve_boundary_green_function (struct sdis_scene* scn, const size_t nrealisations, /* #realisations */ const size_t primitives[], /* List of boundary primitives to handle */ + const enum sdis_side sides[], /* Per primitive side to consider */ const size_t nprimitives, /* #primitives */ - const double time_range[2], /* Observation time */ const double fp_to_meter, /* Scale from floating point units to meters */ const double ambient_radiative_temperature, /* In Kelvin */ const double reference_temperature, /* In Kelvin */ - struct sdis_estimator** estimator); + struct sdis_green_function** green); + +SDIS_API res_T +sdis_solve_medium_green_function + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + struct sdis_medium* medium, /* Medium to solve */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double ambient_radiative_temperature, /* In Kelvin */ + const double reference_temperature, /* In Kelvin */ + struct sdis_green_function** green); END_DECLS diff --git a/src/sdis_Xd_begin.h b/src/sdis_Xd_begin.h @@ -0,0 +1,144 @@ +/* Copyright (C) 2016-2019 |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_XD_BEGIN_H +#define SDIS_XD_BEGIN_H + +#include <rsys/rsys.h> + +/* Forward declaration */ +struct green_path_handle; +struct sdis_heat_path; + +struct accum { + double sum; /* Sum of MC weights */ + double sum2; /* Sum of square MC weights */ + size_t count; /* #accumulated MC weights */ +}; +#define ACCUM_NULL__ {0,0,0} +static const struct accum ACCUM_NULL = ACCUM_NULL__; + +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 */ +}; +#define RWALK_CONTEXT_NULL__ {NULL, NULL, 0, 0} +static const struct rwalk_context RWALK_CONTEXT_NULL = RWALK_CONTEXT_NULL__; + +static INLINE void +sum_accums + (const struct accum accums[], + const size_t naccums, + struct accum* accum) +{ + struct accum acc = ACCUM_NULL; + size_t i; + ASSERT(accums && naccums && accum); + + FOR_EACH(i, 0, naccums) { + acc.sum += accums[i].sum; + acc.sum2 += accums[i].sum2; + acc.count += accums[i].count; + } + *accum = acc; +} + +#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 */ +#ifndef SDIS_XD_DIMENSION + #error "The SDIS_XD_DIMENSION macro must be defined." +#endif + +#if SDIS_XD_DIMENSION == 2 + #include <rsys/double2.h> + #include <rsys/float2.h> + #include <star/s2d.h> +#elif SDIS_XD_DIMENSION == 3 + #include <rsys/double3.h> + #include <rsys/float3.h> + #include <star/s3d.h> +#else + #error "Invalid dimension." +#endif + +/* Syntactic sugar */ +#define DIM SDIS_XD_DIMENSION + +/* Star-XD macros generic to SDIS_XD_DIMENSION */ +#define sXd(Name) CONCAT(CONCAT(CONCAT(s, DIM), d_), Name) +#define sXd_dev CONCAT(CONCAT(s, DIM), d) +#define SXD_HIT_NONE CONCAT(CONCAT(S,DIM), D_HIT_NONE) +#define SXD_HIT_NULL CONCAT(CONCAT(S,DIM), D_HIT_NULL) +#define SXD_HIT_NULL__ CONCAT(CONCAT(S, DIM), D_HIT_NULL__) +#define SXD_POSITION CONCAT(CONCAT(S, DIM), D_POSITION) +#define SXD_GEOMETRY_NORMAL CONCAT(CONCAT(S, DIM), D_GEOMETRY_NORMAL) +#define SXD_VERTEX_DATA_NULL CONCAT(CONCAT(S, DIM), D_VERTEX_DATA_NULL) +#define SXD CONCAT(CONCAT(S, DIM), D) +#define SXD_FLOAT2 CONCAT(CONCAT(S, DIM), D_FLOAT2) +#define SXD_FLOAT3 CONCAT(CONCAT(S, DIM), D_FLOAT3) +#define SXD_SAMPLE CONCAT(CONCAT(S, DIM), D_SAMPLE) + +/* Vector macros generic to SDIS_XD_DIMENSION */ +#define dX(Func) CONCAT(CONCAT(CONCAT(d, DIM), _), Func) +#define fX(Func) CONCAT(CONCAT(CONCAT(f, DIM), _), Func) +#define fX_set_dX CONCAT(CONCAT(CONCAT(f, DIM), _set_d), DIM) +#define dX_set_fX CONCAT(CONCAT(CONCAT(d, DIM), _set_f), DIM) + +/* Macro making generic its submitted nae to SDIS_XD_DIMENSION */ +#define XD(Name) CONCAT(CONCAT(CONCAT(Name, _), DIM), d) + +/* Generate the generic data structures and constants */ +#if (SDIS_XD_DIMENSION == 2 && !defined(SDIS_2D_H)) \ +|| (SDIS_XD_DIMENSION == 3 && !defined(SDIS_3D_H)) + #if SDIS_XD_DIMENSION == 2 + #define SDIS_2D_H + #else + #define SDIS_3D_H + #endif + +/* Current state of the random walk */ +struct XD(rwalk) { + struct sdis_rwalk_vertex vtx; /* Position and time of the Random walk */ + struct sdis_medium* mdm; /* Medium in which the random walk lies */ + struct sXd(hit) hit; /* Hit of the random walk */ + enum sdis_side hit_side; +}; +static const struct XD(rwalk) XD(RWALK_NULL) = { + SDIS_RWALK_VERTEX_NULL__, NULL, SXD_HIT_NULL__, SDIS_SIDE_NULL__ +}; + +struct XD(temperature) { + res_T (*func)/* Next function to invoke in order to compute the temperature */ + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* temp); + double value; /* Current value of the temperature */ + int done; +}; +static const struct XD(temperature) XD(TEMPERATURE_NULL) = { NULL, 0, 0 }; + +#endif /* SDIX_<2|3>D_H */ + diff --git a/src/sdis_Xd_end.h b/src/sdis_Xd_end.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2016-2019 |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_XD_BEGIN_H__ + #error "The sdis_Xd_begin.h file must be included priorly to this file." +#endif + +#undef SDIS_XD_DIMENSION +#undef DIM + +#undef sXd +#undef sXd_dev +#undef SXD_HIT_NONE +#undef SXD_HIT_NULL +#undef SXD_HIT_NULL__ +#undef SXD_POSITION +#undef SXD_GEOMETRY_NORMAL +#undef SXD_VERTEX_DATA_NULL +#undef SXD +#undef SXD_FLOAT2 +#undef SXD_FLOAT3 +#undef SXD_SAMPLE + +#undef dX +#undef fX +#undef fX_set_dX +#undef dX_set_fX + +#undef SDIS_XD_BEGIN_H__ diff --git a/src/sdis_estimator.c b/src/sdis_estimator.c @@ -17,6 +17,8 @@ #include "sdis_device_c.h" #include "sdis_estimator_c.h" +#include <rsys/mutex.h> + /******************************************************************************* * Helper functions ******************************************************************************/ @@ -28,8 +30,8 @@ estimator_release(ref_T* ref) ASSERT(ref); estimator = CONTAINER_OF(ref, struct sdis_estimator, ref); dev = estimator->dev; - ASSERT((estimator->fluxes!=NULL) == (estimator->type==SDIS_FLUX_ESTIMATOR)); - MEM_RM(dev->allocator, estimator->fluxes); + darray_heat_path_release(&estimator->paths); + if(estimator->mutex) mutex_destroy(estimator->mutex); MEM_RM(dev->allocator, estimator); SDIS(device_ref_put(dev)); } @@ -93,10 +95,10 @@ res_T sdis_estimator_get_convective_flux (const struct sdis_estimator* estimator, struct sdis_mc* flux) { - if(!estimator || !flux ||estimator->type != SDIS_FLUX_ESTIMATOR) + if(!estimator || !flux ||estimator->type != SDIS_ESTIMATOR_FLUX) return RES_BAD_ARG; ASSERT(estimator->fluxes); - *flux = estimator->fluxes[FLUX_CONVECTIVE__]; + *flux = estimator->fluxes[FLUX_CONVECTIVE]; return RES_OK; } @@ -104,10 +106,10 @@ res_T sdis_estimator_get_radiative_flux (const struct sdis_estimator* estimator, struct sdis_mc* flux) { - if(!estimator || !flux || estimator->type != SDIS_FLUX_ESTIMATOR) + if(!estimator || !flux || estimator->type != SDIS_ESTIMATOR_FLUX) return RES_BAD_ARG; ASSERT(estimator->fluxes); - *flux = estimator->fluxes[FLUX_RADIATIVE__]; + *flux = estimator->fluxes[FLUX_RADIATIVE]; return RES_OK; } @@ -115,13 +117,63 @@ res_T sdis_estimator_get_total_flux (const struct sdis_estimator* estimator, struct sdis_mc* flux) { - if(!estimator || !flux || estimator->type != SDIS_FLUX_ESTIMATOR) + if(!estimator || !flux || estimator->type != SDIS_ESTIMATOR_FLUX) return RES_BAD_ARG; ASSERT(estimator->fluxes); - *flux = estimator->fluxes[FLUX_TOTAL__]; + *flux = estimator->fluxes[FLUX_TOTAL]; + return RES_OK; +} + +res_T +sdis_estimator_get_paths_count + (const struct sdis_estimator* estimator, size_t* npaths) +{ + if(!estimator || !npaths) return RES_BAD_ARG; + *npaths = darray_heat_path_size_get(&estimator->paths); return RES_OK; } +SDIS_API res_T +sdis_estimator_get_path + (const struct sdis_estimator* estimator, + const size_t ipath, + const struct sdis_heat_path** path) +{ + if(!estimator || !path + || ipath >= darray_heat_path_size_get(&estimator->paths)) + return RES_BAD_ARG; + *path = darray_heat_path_cdata_get(&estimator->paths) + ipath; + return RES_OK; +} + +res_T +sdis_estimator_for_each_path + (const struct sdis_estimator* estimator, + sdis_process_heat_path_T func, + void* context) +{ + const struct sdis_heat_path* paths = NULL; + size_t i, n; + res_T res = RES_OK; + + if(!estimator || !func) { + res = RES_BAD_ARG; + goto error; + } + + SDIS(estimator_get_paths_count(estimator, &n)); + paths = darray_heat_path_cdata_get(&estimator->paths); + FOR_EACH(i, 0, n) { + res = func(paths+i, context); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + /******************************************************************************* * Local functions ******************************************************************************/ @@ -134,9 +186,9 @@ estimator_create struct sdis_estimator* estimator = NULL; res_T res = RES_OK; - if(!dev || !out_estimator - || (type != SDIS_TEMPERATURE_ESTIMATOR && type != SDIS_FLUX_ESTIMATOR)) - { + if(!dev + || (unsigned)type >= SDIS_ESTIMATOR_TYPES_COUNT__ + || !out_estimator) { res = RES_BAD_ARG; goto error; } @@ -146,13 +198,19 @@ estimator_create res = RES_MEM_ERR; goto error; } - estimator->type = type; - estimator->fluxes = (type != SDIS_FLUX_ESTIMATOR) ? NULL - : MEM_CALLOC(dev->allocator, FLUX_NAMES_COUNT__, sizeof(struct sdis_mc)); ref_init(&estimator->ref); SDIS(device_ref_get(dev)); + estimator->nrealisations = 0; + estimator->nfailures = 0; estimator->dev = dev; - if(type == SDIS_FLUX_ESTIMATOR && !estimator->fluxes) goto error; + estimator->type = type; + darray_heat_path_init(dev->allocator, &estimator->paths); + + estimator->mutex = mutex_create(); + if(!estimator->mutex) { + res = RES_MEM_ERR; + goto error; + } exit: if(out_estimator) *out_estimator = estimator; @@ -165,3 +223,30 @@ error: goto exit; } +res_T +estimator_add_and_release_heat_path + (struct sdis_estimator* estimator, struct sdis_heat_path* path) +{ + struct sdis_heat_path* dst = NULL; + size_t i; + res_T res = RES_OK; + ASSERT(estimator && path); + + mutex_lock(estimator->mutex); + + i = darray_heat_path_size_get(&estimator->paths); + + res = darray_heat_path_resize(&estimator->paths, i+1); + if(res != RES_OK) goto error; + + dst = darray_heat_path_data_get(&estimator->paths) + i; + res = heat_path_copy_and_release(dst, path); + if(res != RES_OK) goto error; + +exit: + mutex_unlock(estimator->mutex); + return res; +error: + goto exit; +} + diff --git a/src/sdis_estimator_c.h b/src/sdis_estimator_c.h @@ -16,6 +16,9 @@ #ifndef SDIS_ESTIMATOR_C_H #define SDIS_ESTIMATOR_C_H +#include "sdis_heat_path.h" + +#include <rsys/math.h> #include <rsys/ref_count.h> /* Forward declarations */ @@ -23,26 +26,31 @@ struct sdis_device; struct sdis_estimator; enum sdis_estimator_type; -enum flux_names { - FLUX_CONVECTIVE__, - FLUX_RADIATIVE__, - FLUX_TOTAL__, +enum flux_name { + FLUX_CONVECTIVE, + FLUX_RADIATIVE, + FLUX_TOTAL, FLUX_NAMES_COUNT__ }; struct sdis_estimator { struct sdis_mc temperature; - struct sdis_mc* fluxes; + struct sdis_mc fluxes[FLUX_NAMES_COUNT__]; size_t nrealisations; size_t nfailures; + struct mutex* mutex; + struct darray_heat_path paths; /* Tracked paths */ + enum sdis_estimator_type type; ref_T ref; struct sdis_device* dev; }; +struct sdis_estimator_handle; + /******************************************************************************* - * Estmator data structure + * Estimator local API ******************************************************************************/ extern LOCAL_SYM res_T estimator_create @@ -50,5 +58,54 @@ estimator_create const enum sdis_estimator_type type, struct sdis_estimator** estimator); +/* Thread safe */ +extern LOCAL_SYM res_T +estimator_add_and_release_heat_path + (struct sdis_estimator* estimator, + struct sdis_heat_path* path); + +/* Must be invoked before any others "estimator_setup" functions */ +static INLINE void +estimator_setup_realisations_count + (struct sdis_estimator* estimator, + const size_t nrealisations, + const size_t nsuccesses) +{ + ASSERT(estimator && nrealisations && nsuccesses && nsuccesses<=nrealisations); + estimator->nrealisations = nsuccesses; + estimator->nfailures = nrealisations - nsuccesses; +} + +static INLINE void +estimator_setup_temperature + (struct sdis_estimator* estim, + const double sum, + const double sum2) +{ + double N; + ASSERT(estim && estim->nrealisations); + N = (double)estim->nrealisations; + estim->temperature.E = sum/N; + estim->temperature.V = sum2/N - estim->temperature.E*estim->temperature.E; + estim->temperature.V = MMAX(estim->temperature.V, 0); + estim->temperature.SE = sqrt(estim->temperature.V/N); +} + +static INLINE void +estimator_setup_flux + (struct sdis_estimator* estim, + const enum flux_name name, + const double sum, + const double sum2) +{ + double N; + ASSERT(estim && (unsigned)name < FLUX_NAMES_COUNT__ && estim->nrealisations); + N = (double)estim->nrealisations; + estim->fluxes[name].E = sum/N; + estim->fluxes[name].V = sum2/N - estim->fluxes[name].E*estim->fluxes[name].E; + estim->fluxes[name].V = MMAX(estim->fluxes[name].V, 0); + estim->fluxes[name].SE = sqrt(estim->fluxes[name].V/N); +} + #endif /* SDIS_PROBE_ESTIMATOR_C_H */ diff --git a/src/sdis_green.c b/src/sdis_green.c @@ -0,0 +1,998 @@ +/* Copyright (C) 2016-2019 |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_device_c.h" +#include "sdis_estimator_c.h" +#include "sdis_green.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_interface_c.h" + +#include <star/ssp.h> + +#include <rsys/dynamic_array.h> +#include <rsys/hash_table.h> +#include <rsys/mem_allocator.h> +#include <rsys/ref_count.h> + +#include <rsys/dynamic_array.h> +#include <rsys/ref_count.h> + +#include <limits.h> + +struct power_term { + double term; /* Power term computed during green estimation */ + unsigned id; /* Identifier of the medium of the term */ +}; + +#define POWER_TERM_NULL__ {DBL_MAX, UINT_MAX} +static const struct power_term POWER_TERM_NULL = POWER_TERM_NULL__; + +static INLINE void +power_term_init(struct mem_allocator* allocator, struct power_term* term) +{ + ASSERT(term); (void)allocator; + *term = POWER_TERM_NULL; +} + +/* Generate the dynamic array of power terms */ +#define DARRAY_NAME power_term +#define DARRAY_DATA struct power_term +#define DARRAY_FUNCTOR_INIT power_term_init +#include <rsys/dynamic_array.h> + +struct flux_term { + double term; + unsigned id; /* Id of the interface of the flux term */ + enum sdis_side side; +}; +#define FLUX_TERM_NULL__ {DBL_MAX, UINT_MAX, SDIS_SIDE_NULL__} +static const struct flux_term FLUX_TERM_NULL = FLUX_TERM_NULL__; + +static INLINE void +flux_term_init(struct mem_allocator* allocator, struct flux_term* term) +{ + ASSERT(term); (void)allocator; + *term = FLUX_TERM_NULL; +} + +/* Generate the dynamic array of flux terms */ +#define DARRAY_NAME flux_term +#define DARRAY_DATA struct flux_term +#define DARRAY_FUNCTOR_INIT flux_term_init +#include <rsys/dynamic_array.h> + +struct green_path { + struct darray_flux_term flux_terms; /* List of flux terms */ + struct darray_power_term power_terms; /* List of volumic power terms */ + union { + struct sdis_rwalk_vertex vertex; + struct sdis_interface_fragment fragment; + } limit; + unsigned limit_id; /* Identifier of the limit medium/interface */ + enum sdis_point_type limit_type; + + /* Indices of the last accessed medium/interface. Used to speed up the access + * to the medium/interface. */ + uint16_t ilast_medium; + uint16_t ilast_interf; +}; + +static INLINE void +green_path_init(struct mem_allocator* allocator, struct green_path* path) +{ + ASSERT(path); + darray_flux_term_init(allocator, &path->flux_terms); + darray_power_term_init(allocator, &path->power_terms); + path->limit.vertex = SDIS_RWALK_VERTEX_NULL; + path->limit_id = UINT_MAX; + path->limit_type = SDIS_POINT_NONE; + path->ilast_medium = UINT16_MAX; + path->ilast_interf = UINT16_MAX; +} + +static INLINE void +green_path_release(struct green_path* path) +{ + ASSERT(path); + darray_flux_term_release(&path->flux_terms); + darray_power_term_release(&path->power_terms); +} + +static INLINE res_T +green_path_copy(struct green_path* dst, const struct green_path* src) +{ + res_T res = RES_OK; + ASSERT(dst && src); + dst->limit = src->limit; + dst->limit_id = src->limit_id; + dst->limit_type = src->limit_type; + dst->ilast_medium = src->ilast_medium; + dst->ilast_interf = src->ilast_interf; + res = darray_flux_term_copy(&dst->flux_terms, &src->flux_terms); + if(res != RES_OK) return res; + res = darray_power_term_copy(&dst->power_terms, &src->power_terms); + if(res != RES_OK) return res; + return RES_OK; +} + +static INLINE res_T +green_path_copy_and_clear(struct green_path* dst, struct green_path* src) +{ + res_T res = RES_OK; + ASSERT(dst && src); + dst->limit = src->limit; + dst->limit_id = src->limit_id; + dst->limit_type = src->limit_type; + dst->ilast_medium = src->ilast_medium; + dst->ilast_interf = src->ilast_interf; + res = darray_flux_term_copy_and_clear(&dst->flux_terms, &src->flux_terms); + if(res != RES_OK) return res; + res = darray_power_term_copy_and_clear(&dst->power_terms, &src->power_terms); + if(res != RES_OK) return res; + return RES_OK; + +} + +static INLINE res_T +green_path_copy_and_release(struct green_path* dst, struct green_path* src) +{ + res_T res = RES_OK; + ASSERT(dst && src); + dst->limit = src->limit; + dst->limit_id = src->limit_id; + dst->limit_type = src->limit_type; + dst->ilast_medium = src->ilast_medium; + dst->ilast_interf = src->ilast_interf; + res = darray_flux_term_copy_and_release(&dst->flux_terms, &src->flux_terms); + if(res != RES_OK) return res; + res = darray_power_term_copy_and_release(&dst->power_terms, &src->power_terms); + if(res != RES_OK) return res; + return RES_OK; +} + +/* Generate the dynamic array of green paths */ +#define DARRAY_NAME green_path +#define DARRAY_DATA struct green_path +#define DARRAY_FUNCTOR_INIT green_path_init +#define DARRAY_FUNCTOR_RELEASE green_path_release +#define DARRAY_FUNCTOR_COPY green_path_copy +#define DARRAY_FUNCTOR_COPY_AND_RELEASE green_path_copy_and_release +#include <rsys/dynamic_array.h> + +/* Generate the hash table that maps and id to an interface */ +#define HTABLE_NAME interf +#define HTABLE_KEY unsigned +#define HTABLE_DATA struct sdis_interface* +#include <rsys/hash_table.h> + +/* Generate the hash table that maps and id to a medium */ +#define HTABLE_NAME medium +#define HTABLE_KEY unsigned +#define HTABLE_DATA struct sdis_medium* +#include <rsys/hash_table.h> + +struct sdis_green_function { + struct htable_medium media; + struct htable_interf interfaces; + struct darray_green_path paths; /* List of paths used to estimate the green */ + + size_t npaths_valid; + size_t npaths_invalid; + + struct ssp_rng_type rng_type; + FILE* rng_state; + + ref_T ref; + struct sdis_device* dev; +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static res_T +ensure_medium_registration + (struct sdis_green_function* green, + struct sdis_medium* mdm) +{ + unsigned id; + res_T res = RES_OK; + ASSERT(green && mdm); + + id = medium_get_id(mdm); + if(htable_medium_find(&green->media, &id)) goto exit; + + res = htable_medium_set(&green->media, &id, &mdm); + if(res != RES_OK) goto error; + + SDIS(medium_ref_get(mdm)); + +exit: + return res; +error: + goto exit; +} + +static res_T +ensure_interface_registration + (struct sdis_green_function* green, + struct sdis_interface* interf) +{ + unsigned id; + res_T res = RES_OK; + ASSERT(green && interf); + + id = interface_get_id(interf); + if(htable_interf_find(&green->interfaces, &id)) goto exit; + + res = htable_interf_set(&green->interfaces, &id, &interf); + if(res != RES_OK) goto error; + + SDIS(interface_ref_get(interf)); + +exit: + return res; +error: + goto exit; +} + +static FINLINE struct sdis_medium* +green_function_fetch_medium + (struct sdis_green_function* green, const unsigned medium_id) +{ + struct sdis_medium* const* pmdm = NULL; + ASSERT(green); + pmdm = htable_medium_find(&green->media, &medium_id); + ASSERT(pmdm); + return *pmdm; +} + +static FINLINE struct sdis_interface* +green_function_fetch_interf + (struct sdis_green_function* green, const unsigned interf_id) +{ + struct sdis_interface* const* pinterf = NULL; + ASSERT(green); + pinterf = htable_interf_find(&green->interfaces, &interf_id); + ASSERT(pinterf); + return *pinterf; +} + +static res_T +green_function_solve_path + (struct sdis_green_function* green, + const double time, /* Sampled time */ + const size_t ipath, + double* weight) +{ + const struct power_term* power_terms = NULL; + const struct flux_term* flux_terms = NULL; + const struct green_path* path = NULL; + const struct sdis_medium* medium = NULL; + const struct sdis_interface* interf = NULL; + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + double power; + double flux; + double temperature; + double time_curr; + size_t i, n; + res_T res = RES_OK; + ASSERT(green && ipath < darray_green_path_size_get(&green->paths) && weight); + ASSERT(time > 0); + + path = darray_green_path_cdata_get(&green->paths) + ipath; + if(path->limit_type == SDIS_POINT_NONE) { /* Rejected path */ + res = RES_BAD_OP; + goto error; + } + + /* Compute medium power terms */ + power = 0; + n = darray_power_term_size_get(&path->power_terms); + power_terms = darray_power_term_cdata_get(&path->power_terms); + FOR_EACH(i, 0, n) { + vtx.time = INF; + medium = green_function_fetch_medium(green, power_terms[i].id); + power += power_terms[i].term * solid_get_volumic_power(medium, &vtx); + } + + /* Compute interface fluxes */ + flux = 0; + n = darray_flux_term_size_get(&path->flux_terms); + flux_terms = darray_flux_term_cdata_get(&path->flux_terms); + FOR_EACH(i, 0, n) { + frag.time = INF; + frag.side = flux_terms[i].side; + interf = green_function_fetch_interf(green, flux_terms[i].id); + flux += flux_terms[i].term * interface_side_get_flux(interf, &frag); + } + + /* Setup time. */ + switch(path->limit_type) { + case SDIS_FRAGMENT: + time_curr = time + path->limit.fragment.time; + interf = green_function_fetch_interf(green, path->limit_id); + break; + case SDIS_VERTEX: + time_curr = time + path->limit.vertex.time; + medium = green_function_fetch_medium(green, path->limit_id); + break; + default: FATAL("Unreachable code.\n"); break; + } + + if(time_curr <= 0 + || (path->limit_type == SDIS_VERTEX && time_curr <= medium_get_t0(medium))) { + log_err(green->dev, + "%s: invalid observation time \"%g\": the initial condition is reached " + "while instationary system are not supported by the green function.\n", + FUNC_NAME, time); + res = RES_BAD_ARG; + goto error; + } + + /* Compute limit condition */ + switch(path->limit_type) { + case SDIS_FRAGMENT: + frag = path->limit.fragment; + frag.time = time_curr; + temperature = interface_side_get_temperature(interf, &frag); + break; + case SDIS_VERTEX: + vtx = path->limit.vertex; + vtx.time = time_curr; + temperature = medium_get_temperature(medium, &vtx); + break; + default: FATAL("Unreachable code.\n"); break; + } + + /* Compute the path weight */ + *weight = power + flux + temperature; + +exit: + return res; +error: + goto exit; +} + +static void +green_function_clear(struct sdis_green_function* green) +{ + struct htable_medium_iterator it_medium, end_medium; + struct htable_interf_iterator it_interf, end_interf; + ASSERT(green); + + /* Clean up medium hash table */ + htable_medium_begin(&green->media, &it_medium); + htable_medium_end(&green->media, &end_medium); + while(!htable_medium_iterator_eq(&it_medium, &end_medium)) { + struct sdis_medium* medium; + medium = *htable_medium_iterator_data_get(&it_medium); + SDIS(medium_ref_put(medium)); + htable_medium_iterator_next(&it_medium); + } + htable_medium_clear(&green->media); + + /* Clean up the interface hash table */ + htable_interf_begin(&green->interfaces, &it_interf); + htable_interf_end(&green->interfaces, &end_interf); + while(!htable_interf_iterator_eq(&it_interf, &end_interf)) { + struct sdis_interface* interf; + interf = *htable_interf_iterator_data_get(&it_interf); + SDIS(interface_ref_put(interf)); + htable_interf_iterator_next(&it_interf); + } + htable_interf_clear(&green->interfaces); + + /* Clean up the registered paths */ + darray_green_path_clear(&green->paths); +} + +static void +green_function_release(ref_T* ref) +{ + struct sdis_device* dev; + struct sdis_green_function* green; + ASSERT(ref); + green = CONTAINER_OF(ref, struct sdis_green_function, ref); + dev = green->dev; + green_function_clear(green); + htable_medium_release(&green->media); + htable_interf_release(&green->interfaces); + darray_green_path_release(&green->paths); + if(green->rng_state) fclose(green->rng_state); + MEM_RM(dev->allocator, green); + SDIS(device_ref_put(dev)); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +sdis_green_function_ref_get(struct sdis_green_function* green) +{ + if(!green) return RES_BAD_ARG; + ref_get(&green->ref); + return RES_OK; +} + +res_T +sdis_green_function_ref_put(struct sdis_green_function* green) +{ + if(!green) return RES_BAD_ARG; + ref_put(&green->ref, green_function_release); + return RES_OK; +} + +res_T +sdis_green_function_solve + (struct sdis_green_function* green, + const double time_range[2], + struct sdis_estimator** out_estimator) +{ + struct sdis_estimator* estimator = NULL; + struct ssp_rng* rng = NULL; + size_t npaths; + size_t ipath; + size_t N = 0; /* #realisations */ + double accum = 0; + double accum2 = 0; + res_T res = RES_OK; + + if(!green || !time_range || time_range[0] < 0 + || time_range[1] < time_range[0] || !out_estimator) { + res = RES_BAD_ARG; + goto error; + } + + res = ssp_rng_create(green->dev->allocator, &green->rng_type, &rng); + if(res != RES_OK) goto error; + + /* Avoid correlation by defining the RNG state from the final state of the + * RNG used to estimate the green function */ + rewind(green->rng_state); + res = ssp_rng_read(rng, green->rng_state); + if(res != RES_OK) goto error; + + npaths = darray_green_path_size_get(&green->paths); + + /* Create the estimator */ + res = estimator_create(green->dev, SDIS_ESTIMATOR_TEMPERATURE, &estimator); + if(res != RES_OK) goto error; + + /* Solve the green function */ + FOR_EACH(ipath, 0, npaths) { + const double time = sample_time(rng, time_range); + double w; + + res = green_function_solve_path(green, time, ipath, &w); + if(res == RES_BAD_OP) continue; + if(res != RES_OK) goto error; + + accum += w; + accum2 += w*w; + ++N; + } + + /* Setup the estimated temperature */ + estimator_setup_realisations_count(estimator, npaths, N); + estimator_setup_temperature(estimator, accum, accum2); + +exit: + if(rng) SSP(rng_ref_put(rng)); + if(out_estimator) *out_estimator = estimator; + return res; +error: + if(estimator) { + SDIS(estimator_ref_put(estimator)); + estimator = NULL; + } + goto exit; +} + +res_T +sdis_green_function_get_paths_count + (const struct sdis_green_function* green, size_t* npaths) +{ + if(!green || !npaths) return RES_BAD_ARG; + ASSERT(green->npaths_valid != SIZE_MAX); + *npaths = green->npaths_valid; + return RES_OK; +} + +res_T +sdis_green_function_get_invalid_paths_count + (const struct sdis_green_function* green, size_t* nfails) +{ + if(!green || !nfails) return RES_BAD_ARG; + ASSERT(green->npaths_invalid != SIZE_MAX); + *nfails = green->npaths_invalid; + return RES_OK; +} + +res_T +sdis_green_function_for_each_path + (struct sdis_green_function* green, + sdis_process_green_path_T func, + void* context) +{ + size_t npaths; + size_t ipath; + res_T res = RES_OK; + + if(!green || !func) { + res = RES_BAD_ARG; + goto error; + } + + npaths = darray_green_path_size_get(&green->paths); + FOR_EACH(ipath, 0, npaths) { + struct sdis_green_path path_handle; + const struct green_path* path = darray_green_path_cdata_get(&green->paths)+ipath; + + if(path->limit_type == SDIS_POINT_NONE) continue; + + path_handle.green__ = green; + path_handle.id__ = ipath; + + res = func(&path_handle, context); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + +res_T +sdis_green_path_get_limit_point + (struct sdis_green_path* path_handle, struct sdis_point* pt) +{ + const struct green_path* path = NULL; + struct sdis_green_function* green = NULL; + res_T res = RES_OK; + + if(!path_handle || !pt) { + res = RES_BAD_ARG; + goto error; + } + + green = path_handle->green__; + ASSERT(path_handle->id__ < darray_green_path_size_get(&green->paths)); + + path = darray_green_path_cdata_get(&green->paths) + path_handle->id__; + pt->type = path->limit_type; + + switch(path->limit_type) { + case SDIS_FRAGMENT: + pt->data.itfrag.intface = green_function_fetch_interf(green, path->limit_id); + pt->data.itfrag.fragment = path->limit.fragment; + break; + case SDIS_VERTEX: + pt->data.mdmvert.medium = green_function_fetch_medium(green, path->limit_id); + pt->data.mdmvert.vertex = path->limit.vertex; + break; + default: FATAL("Unreachable code.\n"); break; + } + +exit: + return res; +error: + goto exit; +} + +res_T +sdis_green_path_for_each_power_term + (struct sdis_green_path* path_handle, + sdis_process_medium_power_term_T func, + void* context) +{ + const struct green_path* path = NULL; + struct sdis_green_function* green = NULL; + const struct power_term* terms = NULL; + size_t i, n; + res_T res = RES_OK; + + if(!path_handle || !func) { + res = RES_BAD_ARG; + goto error; + } + + green = path_handle->green__; + ASSERT(path_handle->id__ < darray_green_path_size_get(&green->paths)); + + path = darray_green_path_cdata_get(&green->paths) + path_handle->id__; + + n = darray_power_term_size_get(&path->power_terms); + terms = darray_power_term_cdata_get(&path->power_terms); + FOR_EACH(i, 0, n) { + struct sdis_medium* mdm; + mdm = green_function_fetch_medium(green, terms[i].id); + res = func(mdm, terms[i].term, context); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + +res_T +sdis_green_path_for_each_flux_term + (struct sdis_green_path* path_handle, + sdis_process_interface_flux_term_T func, + void* context) +{ + const struct green_path* path = NULL; + struct sdis_green_function* green = NULL; + const struct flux_term* terms = NULL; + size_t i, n; + res_T res = RES_OK; + + if(!path_handle || !func) { + res = RES_BAD_ARG; + goto error; + } + + green = path_handle->green__; + ASSERT(path_handle->id__ < darray_green_path_size_get(&green->paths)); + + path = darray_green_path_cdata_get(&green->paths) + path_handle->id__; + + n = darray_flux_term_size_get(&path->flux_terms); + terms = darray_flux_term_cdata_get(&path->flux_terms); + FOR_EACH(i, 0, n) { + struct sdis_interface* interf; + interf = green_function_fetch_interf(green, terms[i].id); + + res = func(interf, terms[i].side, terms[i].term, context); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +green_function_create + (struct sdis_device* dev, struct sdis_green_function** out_green) +{ + struct sdis_green_function* green = NULL; + res_T res = RES_OK; + ASSERT(dev && out_green); + + green = MEM_CALLOC(dev->allocator, 1, sizeof(*green)); + if(!green) { + res = RES_MEM_ERR; + goto error; + } + ref_init(&green->ref); + SDIS(device_ref_get(dev)); + green->dev = dev; + htable_medium_init(dev->allocator, &green->media); + htable_interf_init(dev->allocator, &green->interfaces); + darray_green_path_init(dev->allocator, &green->paths); + green->npaths_valid = SIZE_MAX; + green->npaths_invalid = SIZE_MAX; + + green->rng_state = tmpfile(); + if(!green->rng_state) { + res = RES_IO_ERR; + goto error; + } + +exit: + *out_green = green; + return res; +error: + if(green) { + SDIS(green_function_ref_put(green)); + green = NULL; + } + goto exit; +} + +res_T +green_function_merge_and_clear + (struct sdis_green_function* dst, struct sdis_green_function* src) +{ + struct htable_medium_iterator it_medium, end_medium; + struct htable_interf_iterator it_interf, end_interf; + struct green_path* paths_src; + struct green_path* paths_dst; + size_t npaths_src; + size_t npaths_dst; + size_t npaths; + size_t i; + unsigned id; + res_T res = RES_OK; + ASSERT(dst && src); + + if(dst == src) goto exit; + + npaths_src = darray_green_path_size_get(&src->paths); + npaths_dst = darray_green_path_size_get(&dst->paths); + npaths = npaths_src + npaths_dst; + + res = darray_green_path_resize(&dst->paths, npaths); + if(res != RES_OK) goto error; + + paths_src = darray_green_path_data_get(&src->paths); + paths_dst = darray_green_path_data_get(&dst->paths) + npaths_dst; + + FOR_EACH(i, 0, darray_green_path_size_get(&src->paths)) { + res = green_path_copy_and_clear(&paths_dst[i], &paths_src[i]); + if(res != RES_OK) goto error; + } + + htable_medium_begin(&src->media, &it_medium); + htable_medium_end(&src->media, &end_medium); + while(!htable_medium_iterator_eq(&it_medium, &end_medium)) { + struct sdis_medium* medium; + medium = *htable_medium_iterator_data_get(&it_medium); + id = medium_get_id(medium); + res = htable_medium_set(&dst->media, &id, &medium); + if(res != RES_OK) goto error; + htable_medium_iterator_next(&it_medium); + } + + htable_interf_begin(&src->interfaces, &it_interf); + htable_interf_end(&src->interfaces, &end_interf); + while(!htable_interf_iterator_eq(&it_interf, &end_interf)) { + struct sdis_interface* interf; + interf = *htable_interf_iterator_data_get(&it_interf); + id = interface_get_id(interf); + res = htable_interf_set(&dst->interfaces, &id, &interf); + if(res != RES_OK) goto error; + htable_interf_iterator_next(&it_interf); + } + + green_function_clear(src); +exit: + return res; +error: + goto exit; +} + +res_T +green_function_redux_and_clear + (struct sdis_green_function* dst, + struct sdis_green_function* greens[], + const size_t ngreens) +{ + size_t i; + res_T res = RES_OK; + ASSERT(dst && greens && ngreens); + + FOR_EACH(i, 0, ngreens) { + res = green_function_merge_and_clear(dst, greens[i]); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + +res_T +green_function_finalize + (struct sdis_green_function* green, + struct ssp_rng_proxy* proxy) +{ + size_t i, n; + res_T res = RES_OK; + + if(!green || !proxy) { + res = RES_BAD_ARG; + goto error; + } + + /* Save the RNG state */ + SSP(rng_proxy_get_type(proxy, &green->rng_type)); + res = ssp_rng_proxy_write(proxy, green->rng_state); + if(res != RES_OK) goto error; + + /* Compute the number of valid/invalid green paths */ + green->npaths_valid = 0; + n = darray_green_path_size_get(&green->paths); + FOR_EACH(i, 0, n) { + const struct green_path* path = darray_green_path_cdata_get(&green->paths)+i; + green->npaths_valid += path->limit_type != SDIS_POINT_NONE; + } + green->npaths_invalid = n - green->npaths_valid; + +exit: + return res; +error: + goto exit; +} + +res_T +green_function_create_path + (struct sdis_green_function* green, + struct green_path_handle* handle) +{ + size_t n; + res_T res = RES_OK; + ASSERT(green && handle); + + n = darray_green_path_size_get(&green->paths); + res = darray_green_path_resize(&green->paths, n+1); + if(res != RES_OK) return res; + + handle->green = green; + handle->path = darray_green_path_data_get(&green->paths) + n; + return RES_OK; +} + +res_T +green_path_set_limit_interface_fragment + (struct green_path_handle* handle, + struct sdis_interface* interf, + const struct sdis_interface_fragment* frag) +{ + res_T res = RES_OK; + ASSERT(handle && interf && frag); + ASSERT(handle->path->limit_type == SDIS_POINT_NONE); + res = ensure_interface_registration(handle->green, interf); + if(res != RES_OK) return res; + handle->path->limit.fragment = *frag; + handle->path->limit_id = interface_get_id(interf); + handle->path->limit_type = SDIS_FRAGMENT; + return RES_OK; +} + +res_T +green_path_set_limit_vertex + (struct green_path_handle* handle, + struct sdis_medium* mdm, + const struct sdis_rwalk_vertex* vert) +{ + res_T res = RES_OK; + ASSERT(handle && mdm && vert); + ASSERT(handle->path->limit_type == SDIS_POINT_NONE); + res = ensure_medium_registration(handle->green, mdm); + if(res != RES_OK) return res; + handle->path->limit.vertex = *vert; + handle->path->limit_id = medium_get_id(mdm); + handle->path->limit_type = SDIS_VERTEX; + return RES_OK; +} + +res_T +green_path_add_power_term + (struct green_path_handle* handle, + struct sdis_medium* mdm, + const struct sdis_rwalk_vertex* vtx, + const double val) +{ + struct green_path* path; + struct power_term* terms; + size_t nterms; + size_t iterm; + unsigned id; + res_T res = RES_OK; + ASSERT(handle && mdm && vtx); + + /* Unused position and time: the current implementation of the green function + * assumes that the power is constant in space and time per medium. */ + (void)vtx; + + res = ensure_medium_registration(handle->green, mdm); + if(res != RES_OK) goto error; + + path = handle->path; + terms = darray_power_term_data_get(&path->power_terms); + nterms = darray_power_term_size_get(&path->power_terms); + id = medium_get_id(mdm); + iterm = SIZE_MAX; + + /* Early find */ + if(path->ilast_medium < nterms && terms[path->ilast_medium].id == id) { + iterm = path->ilast_medium; + } else { + /* Linear search of the submitted medium */ + FOR_EACH(iterm, 0, nterms) if(terms[iterm].id == id) break; + } + + /* Add the power term to the path wrt the submitted medium */ + if(iterm < nterms) { + terms[iterm].term += val; + } else { + struct power_term term = POWER_TERM_NULL__; + term.term = val; + term.id = id; + res = darray_power_term_push_back(&handle->path->power_terms, &term); + if(res != RES_OK) goto error; + } + + /* Register the slot into which the last accessed medium lies */ + CHK(iterm < UINT16_MAX); + path->ilast_medium = (uint16_t)iterm; + +exit: + return res; +error: + goto exit; +} + +res_T +green_path_add_flux_term + (struct green_path_handle* handle, + struct sdis_interface* interf, + const struct sdis_interface_fragment* frag, + const double val) +{ + struct green_path* path; + struct flux_term* terms; + size_t nterms; + size_t iterm; + unsigned id; + res_T res = RES_OK; + ASSERT(handle && interf && frag && val >= 0); + + res = ensure_interface_registration(handle->green, interf); + if(res != RES_OK) goto error; + + path = handle->path; + terms = darray_flux_term_data_get(&path->flux_terms); + nterms = darray_flux_term_size_get(&path->flux_terms); + id = interface_get_id(interf); + iterm = SIZE_MAX; + + /* Early find */ + if(path->ilast_interf < nterms + && terms[path->ilast_interf].id == id + && terms[path->ilast_interf].side == frag->side) { + iterm = path->ilast_interf; + } else { + /* Linear search of the submitted interface */ + FOR_EACH(iterm, 0, nterms) { + if(terms[iterm].id == id && terms[iterm].side == frag->side) { + break; + } + } + } + + /* Add the flux term to the path wrt the submitted interface */ + if(iterm < nterms) { + terms[iterm].term += val; + } else { + struct flux_term term = FLUX_TERM_NULL__; + term.term = val; + term.id = id; + term.side = frag->side; + res = darray_flux_term_push_back(&handle->path->flux_terms, &term); + if(res != RES_OK) goto error; + } + + /* Register the slot into which the last accessed interface lies */ + CHK(iterm < UINT16_MAX); + path->ilast_interf = (uint16_t)iterm; + +exit: + return res; +error: + goto exit; +} + diff --git a/src/sdis_green.h b/src/sdis_green.h @@ -0,0 +1,90 @@ +/* Copyright (C) 2016-2019 |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_GREEN_H +#define SDIS_GREEN_H + +#include <rsys/rsys.h> + +/* Forward declaration */ +struct sdis_green_function; +struct ssp_rng_proxy; +struct green_path; + +struct green_path_handle { + struct sdis_green_function* green; + struct green_path* path; +}; +#define GREEN_PATH_HANDLE_NULL__ {NULL, NULL} +static const struct green_path_handle GREEN_PATH_HANDLE_NULL = + GREEN_PATH_HANDLE_NULL__; + +extern LOCAL_SYM res_T +green_function_create + (struct sdis_device* dev, + struct sdis_green_function** green); + +/* Merge `src' into `dst' an clear `src' */ +extern LOCAL_SYM res_T +green_function_merge_and_clear + (struct sdis_green_function* dst, + struct sdis_green_function* src); + +extern LOCAL_SYM res_T +green_function_redux_and_clear + (struct sdis_green_function* dst, + struct sdis_green_function* greens[], + const size_t ngreens); + +/* Finalize the green function state (e.g.: computes the #paths & #failures, + * save the rng state, etc.) */ +extern LOCAL_SYM res_T +green_function_finalize + (struct sdis_green_function* green, + struct ssp_rng_proxy* rng_proxy); /* Proxy RNG used to estimate the function */ + +extern LOCAL_SYM res_T +green_function_create_path + (struct sdis_green_function* green, + struct green_path_handle* handle); + +extern LOCAL_SYM res_T +green_path_set_limit_interface_fragment + (struct green_path_handle* path, + struct sdis_interface* interf, + const struct sdis_interface_fragment* fragment); + +extern LOCAL_SYM res_T +green_path_set_limit_vertex + (struct green_path_handle* path, + struct sdis_medium* mdm, + const struct sdis_rwalk_vertex* vertex); + +extern LOCAL_SYM res_T +green_path_add_power_term + (struct green_path_handle* path, + struct sdis_medium* mdm, + const struct sdis_rwalk_vertex* vertex, + const double term); + +extern LOCAL_SYM res_T +green_path_add_flux_term + (struct green_path_handle* path, + struct sdis_interface* interf, + const struct sdis_interface_fragment* fragment, + const double term); + +#endif /* SDIS_GREEN_H */ + diff --git a/src/sdis_heat_path.c b/src/sdis_heat_path.c @@ -0,0 +1,105 @@ +/* Copyright (C) 2016-2019 |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" + +/* Generate the radiative path routines */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_radiative_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_radiative_Xd.h" + +/* Generate the convective path routines */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_convective_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_convective_Xd.h" + +/* Generate the conductive path routines */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_conductive_Xd.h" +#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 + ******************************************************************************/ +res_T +sdis_heat_path_get_vertices_count + (const struct sdis_heat_path* path, size_t* nvertices) +{ + if(!path || !nvertices) return RES_BAD_ARG; + *nvertices = darray_heat_vertex_size_get(&path->vertices); + return RES_OK; +} + +res_T +sdis_heat_path_get_status + (const struct sdis_heat_path* path, enum sdis_heat_path_flag* status) +{ + if(!path || !status) return RES_BAD_ARG; + *status = path->status; + return RES_OK; +} + +res_T +sdis_heat_path_get_vertex + (const struct sdis_heat_path* path, + const size_t ivertex, + struct sdis_heat_vertex* vertex) +{ + if(!path || !vertex + || ivertex >= darray_heat_vertex_size_get(&path->vertices)) { + return RES_BAD_ARG; + } + + *vertex = darray_heat_vertex_cdata_get(&path->vertices)[ivertex]; + return RES_OK; +} + +res_T +sdis_heat_path_for_each_vertex + (const struct sdis_heat_path* path, + sdis_process_heat_vertex_T func, + void* context) +{ + const struct sdis_heat_vertex* vertices; + size_t i, n; + res_T res = RES_OK; + + if(!path || !func) { + res = RES_BAD_ARG; + goto error; + } + + SDIS(heat_path_get_vertices_count(path, &n)); + vertices = darray_heat_vertex_cdata_get(&path->vertices); + FOR_EACH(i, 0, n) { + res = func(vertices+i, context); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + diff --git a/src/sdis_heat_path.h b/src/sdis_heat_path.h @@ -0,0 +1,216 @@ +/* Copyright (C) 2016-2019 |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_H +#define SDIS_HEAT_PATH_H + +#include "sdis.h" + +#include <rsys/dynamic_array.h> +#include <rsys/rsys.h> + +/* Forward declarations */ +struct rwalk_2d; +struct rwalk_3d; +struct rwalk_context; +struct sdis_scene; +struct ssp_rng; +struct temperature_2d; +struct temperature_3d; + +/* Generate the dynamic array of heat vertices */ +#define DARRAY_NAME heat_vertex +#define DARRAY_DATA struct sdis_heat_vertex +#include <rsys/dynamic_array.h> + +/******************************************************************************* + * Heat path data structure + ******************************************************************************/ +struct sdis_heat_path { + struct darray_heat_vertex vertices; + enum sdis_heat_path_flag status; +}; + +static INLINE void +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); +} + +static INLINE void +heat_path_release(struct sdis_heat_path* path) +{ + ASSERT(path); + darray_heat_vertex_release(&path->vertices); +} + +static INLINE res_T +heat_path_copy(struct sdis_heat_path* dst, const struct sdis_heat_path* src) +{ + ASSERT(dst && src); + dst->status = src->status; + return darray_heat_vertex_copy(&dst->vertices, &src->vertices); +} + +static INLINE res_T +heat_path_copy_and_release(struct sdis_heat_path* dst, struct sdis_heat_path* src) +{ + ASSERT(dst && src); + dst->status = src->status; + return darray_heat_vertex_copy_and_release(&dst->vertices, &src->vertices); +} + +static INLINE res_T +heat_path_copy_and_clear(struct sdis_heat_path* dst, struct sdis_heat_path* src) +{ + ASSERT(dst && src); + dst->status = src->status; + return darray_heat_vertex_copy_and_clear(&dst->vertices, &src->vertices); +} + +static INLINE res_T +heat_path_add_vertex(struct sdis_heat_path* path, const struct sdis_heat_vertex* vtx) +{ + ASSERT(path && vtx); + return darray_heat_vertex_push_back(&path->vertices, vtx); +} + +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); + ASSERT(sz); + return darray_heat_vertex_data_get(&path->vertices) + (sz-1); +} + +/* Generate the dynamic array of heat paths */ +#define DARRAY_NAME heat_path +#define DARRAY_DATA struct sdis_heat_path +#define DARRAY_FUNCTOR_INIT heat_path_init +#define DARRAY_FUNCTOR_RELEASE heat_path_release +#define DARRAY_FUNCTOR_COPY heat_path_copy +#define DARRAY_FUNCTOR_COPY_AND_RELEASE heat_path_copy_and_release +#include <rsys/dynamic_array.h> + +/******************************************************************************* + * Trace or pursue a radiative path + ******************************************************************************/ +extern LOCAL_SYM res_T +trace_radiative_path_2d + (struct sdis_scene* scn, + const float ray_dir[3], + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* temperature); + +extern LOCAL_SYM res_T +trace_radiative_path_3d + (struct sdis_scene* scn, + const float ray_dir[3], + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* temperature); + +extern LOCAL_SYM res_T +radiative_path_2d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* temperature); + +extern LOCAL_SYM res_T +radiative_path_3d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* temperature); + +/******************************************************************************* + * Convective path + ******************************************************************************/ +extern LOCAL_SYM res_T +convective_path_2d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* temperature); + +extern LOCAL_SYM res_T +convective_path_3d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* temperature); + +/******************************************************************************* + * Conductive path + ******************************************************************************/ +extern LOCAL_SYM res_T +conductive_path_2d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* temperature); + +extern LOCAL_SYM res_T +conductive_path_3d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* temperature); + +/******************************************************************************* + * Boundary sub-path + ******************************************************************************/ +extern LOCAL_SYM res_T +boundary_path_2d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* temperature); + +extern LOCAL_SYM res_T +boundary_path_3d + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* temperature); + +#endif /* SDIS_HEAT_PATH_H */ + diff --git a/src/sdis_heat_path_boundary_Xd.h b/src/sdis_heat_path_boundary_Xd.h @@ -0,0 +1,648 @@ +/* Copyright (C) 2016-2019 |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_device_c.h" +#include "sdis_green.h" +#include "sdis_heat_path.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 + * switches from 2D reinjection scheme to the 1D reinjection scheme in order to + * avoid numerical issues. */ +#define REINJECT_DST_MIN_SCALE 0.125f + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static FINLINE 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 +} + +/* 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 double fp_to_meter, + 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, hit2, hit3; + struct sXd(hit)* hit; + 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 delta_boundary; + double reinject_dst_front, reinject_dst_back; + double reinject_dst; + double proba; + double tmp; + double r; + double power; + float range0[2], range1[2]; + float dir0[DIM], dir1[DIM], dir2[DIM], dir3[DIM]; + float* dir; + float pos[DIM]; + int dim = DIM; + res_T res = RES_OK; + ASSERT(scn && fp_to_meter > 0 && 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); + + /* 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); + + /* 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); + + /* Trace the sampled directions on both sides of the interface to adjust the + * reinjection distance of the random walk . */ + fX_set_dX(pos, rwalk->vtx.P); + f2(range0, 0, (float)delta_boundary_front*RAY_RANGE_MAX_SCALE); + f2(range1, 0, (float)delta_boundary_back *RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range0, &rwalk->hit, &hit0)); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir1, range1, &rwalk->hit, &hit1)); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir2, range0, &rwalk->hit, &hit2)); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir3, range1, &rwalk->hit, &hit3)); + + /* Adjust the reinjection distance */ + reinject_dst_front = MMIN(MMIN(delta_boundary_front, hit0.distance), hit2.distance); + reinject_dst_back = MMIN(MMIN(delta_boundary_back, hit1.distance), hit3.distance); + + /* 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. */ + r = ssp_rng_canonical(rng); + proba = (lambda_front/reinject_dst_front) + / (lambda_front/reinject_dst_front + lambda_back/reinject_dst_back); + if(r < proba) { /* Reinject in front */ + dir = dir0; + hit = &hit0; + mdm = solid_front; + reinject_dst = reinject_dst_front; + delta_boundary = delta_boundary_front; + } else { /* Reinject in back */ + dir = dir1; + hit = &hit1; + mdm = solid_back; + reinject_dst = reinject_dst_back; + delta_boundary = delta_boundary_back; + } + + /* Switch in 1D reinjection scheme */ + if(reinject_dst < delta_boundary * REINJECT_DST_MIN_SCALE) { + if(dir == dir0) { + fX(set)(dir, rwalk->hit.normal); + } else { + fX(minus)(dir, rwalk->hit.normal); + } + + f2(range0, 0, (float)delta_boundary*RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir, range0, &rwalk->hit, hit)); + reinject_dst = MMIN(delta_boundary, hit->distance), + dim = 1; + + /* Hit something in 1D. Arbitrarily move the random walk to 0.5 of the hit + * distance */ + if(!SXD_HIT_NONE(hit)) { + reinject_dst *= 0.5; + *hit = SXD_HIT_NULL; + } + } + + /* 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 * 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; + } + } + + /* Reinject */ + XD(move_pos)(rwalk->vtx.P, dir, (float)reinject_dst); + if(eq_epsf(hit->distance, (float)reinject_dst, 1.e-4f)) { + 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 double fp_to_meter, + 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 sXd(hit) hit0 = SXD_HIT_NULL; + struct sXd(hit) hit1 = 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 pos[DIM]; + float dir0[DIM], dir1[DIM]; + float range[2]; + int dim = DIM; + res_T res = RES_OK; + ASSERT(scn && fp_to_meter > 0 && 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; + + /* 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); + } + + /* Trace dir0/dir1 to adjust the reinjection distance */ + fX_set_dX(pos, rwalk->vtx.P); + f2(range, 0, (float)delta_boundary*RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir1, range, &rwalk->hit, &hit1)); + + /* Adjust the delta boundary to the hit distance */ + tmp = MMIN(MMIN(delta_boundary, hit0.distance), hit1.distance); + + if(tmp >= delta_boundary * REINJECT_DST_MIN_SCALE) { + delta_boundary = tmp; + /* Define the orthogonal dst from the reinjection pos to the interface */ + delta = delta_boundary / sqrt(DIM); + } else { /* Switch in 1D reinjection scheme. */ + fX(set)(dir0, rwalk->hit.normal); + if(solid == mdm_back) fX(minus)(dir0, dir0); + f2(range, 0, (float)delta*RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); + delta_boundary = MMIN(hit0.distance, delta); + + /* Hit something in 1D. Arbitrarily move the random walk to 0.5 of the hit + * distance in order to avoid infinite bounces for parallel plane */ + if(!SXD_HIT_NONE(&hit0)) { + delta_boundary *= 0.5; + hit0 = SXD_HIT_NULL; + } + + delta = delta_boundary; + dim = 1; + } + + /* 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*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 = delta_boundary * 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; + } + } + + /* Reinject */ + XD(move_pos)(rwalk->vtx.P, dir0, (float)delta_boundary); + if(eq_epsf(hit0.distance, (float)delta_boundary, 1.e-4f)) { + T->func = XD(boundary_path); + rwalk->mdm = NULL; + rwalk->hit = hit0; + rwalk->hit_side = fX(dot)(hit0.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 double fp_to_meter, + 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 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) hit0; + struct sXd(hit) hit1; + float pos[DIM]; + float dir0[DIM]; + float dir1[DIM]; + float range[2]; + int dim = DIM; + 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); + + /* 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); + } + + /* Trace dir0/dir1 to adjust the reinjection distance wrt the geometry */ + fX_set_dX(pos, rwalk->vtx.P); + f2(range, 0, (float)delta_boundary*RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir1, range, &rwalk->hit, &hit1)); + + /* Adjust the delta boundary to the hit distance */ + tmp = MMIN(MMIN(delta_boundary, hit0.distance), hit1.distance); + + if(tmp >= delta_boundary * REINJECT_DST_MIN_SCALE) { + delta_boundary = tmp; + /* Define the orthogonal dst from the reinjection pos to the interface */ + delta = delta_boundary / sqrt(DIM); + } else { /* Switch in 1D reinjection scheme. */ + fX(set)(dir0, rwalk->hit.normal); + if(frag->side == SDIS_BACK) fX(minus)(dir0, dir0); + f2(range, 0, (float)delta*RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); + delta_boundary = MMIN(hit0.distance, delta_boundary); + + /* Hit something in 1D. Arbitrarily move the random walk to 0.5 of the hit + * distance in order to avoid infinite bounces for parallel plane */ + if(!SXD_HIT_NONE(&hit0)) { + delta_boundary *= 0.5; + hit0 = SXD_HIT_NULL; + } + + delta = delta_boundary; + dim = 1; + } + + /* Handle the flux */ + delta_in_meter = delta*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 = delta_boundary * 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; + } + } + + /* Reinject into the solid */ + XD(move_pos)(rwalk->vtx.P, dir0, (float)delta_boundary); + if(eq_epsf(hit0.distance, (float)delta_boundary, 1.e-4f)) { + T->func = XD(boundary_path); + rwalk->mdm = NULL; + rwalk->hit = hit0; + rwalk->hit_side = fX(dot)(hit0.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 double fp_to_meter, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + struct sdis_interface* interf = NULL; + struct sdis_medium* mdm_front = NULL; + struct sdis_medium* mdm_back = NULL; + struct sdis_medium* mdm = NULL; + double tmp; + res_T res = RES_OK; + ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); + ASSERT(rwalk->mdm == NULL); + ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + + XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); + + fX(normalize)(rwalk->hit.normal, rwalk->hit.normal); + + /* Retrieve the current interface */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + + /* Check if the boundary temperature is known */ + tmp = interface_side_get_temperature(interf, &frag); + if(tmp >= 0) { + T->value += tmp; + T->done = 1; + + if(ctx->green_path) { + res = green_path_set_limit_interface_fragment + (ctx->green_path, interf, &frag); + if(res != RES_OK) goto error; + } + if(ctx->heat_path) { + heat_path_get_last_vertex(ctx->heat_path)->weight = T->value; + } + goto exit; + } + + /* Check if the boundary flux is known. Note that currently, only solid media + * can have a flux as limit condition */ + mdm = interface_get_medium(interf, frag.side); + if(sdis_medium_get_type(mdm) == SDIS_SOLID ) { + const double phi = interface_side_get_flux(interf, &frag); + if(phi != SDIS_FLUX_NONE) { + res = XD(solid_boundary_with_flux_path) + (scn, fp_to_meter, ctx, &frag, phi, rwalk, rng, T); + if(res != RES_OK) goto error; + + goto exit; + } + } + + mdm_front = interface_get_medium(interf, SDIS_FRONT); + mdm_back = interface_get_medium(interf, SDIS_BACK); + + if(mdm_front->type == mdm_back->type) { + res = XD(solid_solid_boundary_path) + (scn, fp_to_meter, ctx, &frag, rwalk, rng, T); + } else { + res = XD(solid_fluid_boundary_path) + (scn, fp_to_meter, ctx, &frag, rwalk, rng, T); + } + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" + diff --git a/src/sdis_heat_path_conductive_Xd.h b/src/sdis_heat_path_conductive_Xd.h @@ -0,0 +1,308 @@ +/* Copyright (C) 2016-2019 |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_device_c.h" +#include "sdis_green.h" +#include "sdis_heat_path.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +res_T +XD(conductive_path) + (struct sdis_scene* scn, + const double fp_to_meter, + const 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 sdis_medium* mdm; + size_t istep = 0; /* Help for debug */ + res_T res = RES_OK; + ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T); + ASSERT(rwalk->mdm->type == SDIS_SOLID); + (void)ctx, (void)istep; + + /* Check the random walk consistency */ + CHK(scene_get_medium(scn, rwalk->vtx.P, NULL, &mdm) == RES_OK); + if(mdm != rwalk->mdm) { + 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; + } + /* 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); + } + + do { /* Solid random walk */ + struct get_medium_info info = GET_MEDIUM_INFO_NULL; + struct sXd(hit) hit0, hit1; + double lambda; /* Thermal conductivity */ + double rho; /* Volumic mass */ + double cp; /* Calorific capacity */ + double tmp; + double power_factor = 0; + double power; + float delta, delta_solid; /* Random walk numerical parameter */ + float range[2]; + float dir0[DIM], dir1[DIM]; + float org[DIM]; + + /* Check the limit condition */ + tmp = solid_get_temperature(mdm, &rwalk->vtx); + if(tmp >= 0) { + T->value += tmp; + T->done = 1; + + if(ctx->green_path) { + res = green_path_set_limit_vertex + (ctx->green_path, rwalk->mdm, &rwalk->vtx); + if(res != RES_OK) goto error; + } + + if(ctx->heat_path) { + heat_path_get_last_vertex(ctx->heat_path)->weight = T->value; + } + + break; + } + + /* Fetch solid properties */ + delta_solid = (float)solid_get_delta(mdm, &rwalk->vtx); + lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); + rho = solid_get_volumic_mass(mdm, &rwalk->vtx); + cp = solid_get_calorific_capacity(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; + } + +#if DIM == 2 + /* Sample a direction around 2PI */ + ssp_ran_circle_uniform_float(rng, dir0, NULL); +#else + /* Sample a direction around 4PI */ + ssp_ran_sphere_uniform_float(rng, dir0, NULL); +#endif + + /* Trace a ray along the sampled direction and its opposite to check if a + * surface is hit in [0, delta_solid]. */ + fX_set_dX(org, rwalk->vtx.P); + fX(minus)(dir1, dir0); + hit0 = hit1 = SXD_HIT_NULL; + range[0] = 0.f, range[1] = delta_solid*RAY_RANGE_MAX_SCALE; + SXD(scene_view_trace_ray(scn->sXd(view), org, dir0, range, NULL, &hit0)); + SXD(scene_view_trace_ray(scn->sXd(view), org, dir1, range, NULL, &hit1)); + + if(SXD_HIT_NONE(&hit0) && SXD_HIT_NONE(&hit1)) { + /* Hit nothing: move along dir0 of the original delta */ + delta = delta_solid; + + /* Add the volumic power density to the measured temperature */ + if(power != SDIS_VOLUMIC_POWER_NONE) { + const double delta_in_meter = delta * fp_to_meter; + power_factor = delta_in_meter * delta_in_meter / (2.0 * DIM * lambda); + T->value += power * power_factor; + } + } else { + /* Hit something: move along dir0 of the minimum hit distance */ + delta = MMIN(hit0.distance, hit1.distance); + + /* Add the volumic power density to the measured temperature */ + if(power != SDIS_VOLUMIC_POWER_NONE) { + const double delta_s_adjusted = delta_solid * RAY_RANGE_MAX_SCALE; + const double delta_s_in_meter = delta_solid * 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 * 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; + } + } + + /* 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; + } + + /* Sample the time */ + if(!IS_INF(rwalk->vtx.time)) { + double tau, mu, t0; + mu = (2*DIM*lambda) / (rho*cp*delta*fp_to_meter*delta*fp_to_meter); + tau = ssp_ran_exp(rng, mu); + t0 = ctx->green_path ? -INF : solid_get_t0(rwalk->mdm); + rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); + if(rwalk->vtx.time == t0) { + /* Check the initial condition */ + tmp = solid_get_temperature(mdm, &rwalk->vtx); + if(tmp >= 0) { + T->value += tmp; + T->done = 1; + + if(ctx->heat_path) { + struct sdis_heat_vertex* vtx; + vtx = heat_path_get_last_vertex(ctx->heat_path); + vtx->time = rwalk->vtx.time; + vtx->weight = T->value; + } + break; + } + /* The initial condition should have been reached */ + log_err(scn->dev, + "%s: undefined initial condition. " + "The time is %f but the temperature remains unknown.\n", + FUNC_NAME, t0); + res = RES_BAD_OP; + goto error; + } + } + + /* Define if the random walk hits something along dir0. Multiply delta by + * the empirical ray range scale factor to ensure that once moved, the + * random walk does not lie in the uncertainty zone near the geometry */ + if(hit0.distance > delta * RAY_RANGE_MAX_SCALE) { + rwalk->hit = SXD_HIT_NULL; + rwalk->hit_side = SDIS_SIDE_NULL__; + } else { + rwalk->hit = hit0; + rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; + } + + /* Update the random walk position */ + 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); + if(res != RES_OK) goto error; + + /* Fetch the current medium */ + if(SXD_HIT_NONE(&rwalk->hit)) { + CHK(scene_get_medium(scn, rwalk->vtx.P, &info, &mdm) == RES_OK); + } else { + const struct sdis_interface* interf; + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + mdm = interface_get_medium(interf, rwalk->hit_side); + } + + /* Check random walk consistency */ + if(mdm != rwalk->mdm) { + log_err(scn->dev, + "%s: inconsistent medium during the solid random walk.\n", FUNC_NAME); +#if DIM == 2 + #define VEC_STR "%g %g" + #define VEC_SPLIT SPLIT2 +#else + #define VEC_STR "%g %g %g" + #define VEC_SPLIT SPLIT3 +#endif + log_err(scn->dev, + " start position: " VEC_STR "; current position: " VEC_STR "\n", + VEC_SPLIT(position_start), VEC_SPLIT(rwalk->vtx.P)); + if(SXD_HIT_NONE(&rwalk->hit)) { + float hit_pos[DIM]; + fX(mulf)(hit_pos, info.ray_dir, info.XD(hit).distance); + fX(add)(hit_pos, info.ray_org, hit_pos); + log_err(scn->dev, " ray org: " VEC_STR "; ray dir: " VEC_STR "\n", + VEC_SPLIT(info.ray_org), VEC_SPLIT(info.ray_dir)); + log_err(scn->dev, " targeted point: " VEC_STR "\n", + VEC_SPLIT(info.pos_tgt)); + log_err(scn->dev, " hit pos: " VEC_STR "\n", VEC_SPLIT(hit_pos)); + } +#undef VEC_STR +#undef VEC_SPLIT + res = RES_BAD_OP; + goto error; + } + + ++istep; + + /* Keep going while the solid random walk does not hit an interface */ + } while(SXD_HIT_NONE(&rwalk->hit)); + + /* Register the power term for the green function */ + if(ctx->green_path && power_ref != SDIS_VOLUMIC_POWER_NONE) { + res = green_path_add_power_term + (ctx->green_path, rwalk->mdm, &rwalk->vtx, green_power_factor); + if(res != RES_OK) goto error; + } + + T->func = XD(boundary_path); + rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_convective_Xd.h b/src/sdis_heat_path_convective_Xd.h @@ -0,0 +1,308 @@ +/* Copyright (C) 2016-2019 |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_device_c.h" +#include "sdis_green.h" +#include "sdis_heat_path.h" +#include "sdis_medium_c.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static res_T +XD(register_heat_vertex_in_fluid) + (struct sdis_scene* scn, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + const double weight) +{ + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + const float empirical_dst = 0.1f; + const float range[2] = {0, FLT_MAX}; + float org[DIM]; + float dir[DIM]; + float pos[DIM]; + float dst; + struct sXd(hit) hit; + + if(!ctx->heat_path) return RES_OK; + + ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + + fX_set_dX(org, rwalk->vtx.P); + fX(set)(dir, rwalk->hit.normal); + if(rwalk->hit_side == SDIS_BACK) fX(minus)(dir, dir); + + SXD(scene_view_trace_ray(scn->sXd(view), org, dir, range, &rwalk->hit, &hit)); + dst = SXD_HIT_NONE(&hit) ? empirical_dst : hit.distance * 0.5f; + + vtx = rwalk->vtx; + 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); +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +XD(convective_path) + (struct sdis_scene* scn, + const double fp_to_meter, + const 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; +#if SDIS_XD_DIMENSION == 2 + float st; +#else + float st[2]; +#endif + res_T res = RES_OK; + (void)rng, (void)fp_to_meter, (void)ctx; + ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); + ASSERT(rwalk->mdm->type == SDIS_FLUID); + + tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); + if(tmp >= 0) { /* T is known. */ + T->value += tmp; + T->done = 1; + + if(ctx->green_path) { + res = green_path_set_limit_vertex(ctx->green_path, rwalk->mdm, &rwalk->vtx); + if(res != RES_OK) goto error; + } + + res = XD(register_heat_vertex_in_fluid)(scn, ctx, rwalk, T->value); + if(res != RES_OK) goto error; + + goto exit; + } + + if(SXD_HIT_NONE(&rwalk->hit)) { /* The path begins in the fluid */ + const float range[2] = {0, FLT_MAX}; + float dir[DIM] = {0}; + float org[DIM]; + + dir[DIM-1] = 1; + fX_set_dX(org, rwalk->vtx.P); + + /* 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)); + rwalk->hit_side = fX(dot)(rwalk->hit.normal, dir) < 0 ? SDIS_FRONT : SDIS_BACK; + + 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; + } + } + + /* 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 */ + ASSERT(interf->medium_front != interf->medium_back); + if(rwalk->mdm == interf->medium_front) { + enc_id = enc_ids[0]; + ASSERT(rwalk->hit_side == SDIS_FRONT); + } else { + ASSERT(rwalk->mdm == interf->medium_back); + enc_id = enc_ids[1]; + ASSERT(rwalk->hit_side == SDIS_BACK); + } + + /* Fetch the enclosure data */ + enc = scene_get_enclosure(scn, enc_id); + if(!enc) { + /* The possibility for a fluid enclosure to be unregistred is that it is + * 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", + FUNC_NAME); + res = RES_BAD_ARG; + 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(SXD_HIT_NONE(&rwalk->hit)); + + if(ctx->green_path) { + log_err(scn->dev, + "%s: the upper bound of the convection cannot of an enclosure cannot be " + "null when registering the green function; initial condition is not " + "supported.\n", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + + 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; + } + + /* At t=0, the initial condition should have been reached. */ + log_err(scn->dev, + "%s: undefined initial condition. " + "Time is 0 but the temperature remains unknown.\n", + FUNC_NAME); + 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; + + /* Fetch other physical properties. */ + cp = fluid_get_calorific_capacity(rwalk->mdm, &rwalk->vtx); + rho = fluid_get_volumic_mass(rwalk->mdm, &rwalk->vtx); + + /* Sample the time using the upper bound. */ + if(rwalk->vtx.time != INF) { + double mu, tau, t0; + mu = enc->hc_upper_bound / (rho * cp) * enc->S_over_V; + tau = ssp_ran_exp(rng, mu); + t0 = ctx->green_path ? -INF : fluid_get_t0(rwalk->mdm); + rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); + + /* 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 weight of the last heat vertex */ + heat_path_get_last_vertex(ctx->heat_path)->weight = T->value; + } + 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; + } + } + + /* Uniformly sample the enclosure. */ +#if DIM == 2 + SXD(scene_view_sample + (enc->sXd(view), + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, &rwalk->hit.u)); + st = rwalk->hit.u; +#else + SXD(scene_view_sample + (enc->sXd(view), + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, rwalk->hit.uv)); + f2_set(st, rwalk->hit.uv); +#endif + /* Map the sampled primitive id from the enclosure space to the scene + * space. Note that the overall scene has only one shape. As a consequence + * neither the geom_id nor the inst_id needs to be updated */ + rwalk->hit.prim.prim_id = enclosure_local2global_prim_id(enc, prim.prim_id); + + SXD(primitive_get_attrib(&rwalk->hit.prim, SXD_POSITION, st, &attr_P)); + SXD(primitive_get_attrib(&rwalk->hit.prim, SXD_GEOMETRY_NORMAL, st, &attr_N)); + dX_set_fX(rwalk->vtx.P, attr_P.value); + fX(set)(rwalk->hit.normal, attr_N.value); + + /* Fetch the interface of the sampled point. */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + if(rwalk->mdm == interf->medium_front) { + rwalk->hit_side = SDIS_FRONT; + } else if(rwalk->mdm == interf->medium_back) { + rwalk->hit_side = SDIS_BACK; + } else { + FATAL("Unexpected fluid interface.\n"); + } + + /* Register the new vertex against the heat path */ + res = register_heat_vertex + (ctx->heat_path, &rwalk->vtx, T->value, SDIS_HEAT_VERTEX_CONVECTION); + if(res != RES_OK) goto error; + + /* Setup the fragment of the sampled position into the enclosure. */ + XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); + + /* Fetch the convection coefficient of the sampled position */ + hc = interface_get_convection_coef(interf, &frag); + if(hc > enc->hc_upper_bound) { + log_err(scn->dev, + "%s: hc (%g) exceeds its provided upper bound (%g) at %g %g %g.\n", + FUNC_NAME, hc, enc->hc_upper_bound, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP; + goto error; + } + + r = ssp_rng_canonical_float(rng); + if(r < hc / enc->hc_upper_bound) { + /* True convection. Always true if hc == bound. */ + break; + } + } + + rwalk->hit.distance = 0; + T->func = XD(boundary_path); + rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_radiative_Xd.h b/src/sdis_heat_path_radiative_Xd.h @@ -0,0 +1,227 @@ +/* Copyright (C) 2016-2019 |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_device_c.h" +#include "sdis_green.h" +#include "sdis_heat_path.h" +#include "sdis_interface_c.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +XD(trace_radiative_path) + (struct sdis_scene* scn, + const float ray_dir[3], + const double fp_to_meter, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + /* The radiative random walk is always performed in 3D. In 2D, the geometry + * are assumed to be extruded to the infinity along the Z dimension. */ + float N[3] = {0, 0, 0}; + float dir[3] = {0, 0, 0}; + res_T res = RES_OK; + + ASSERT(scn && ray_dir && fp_to_meter > 0 && ctx && rwalk && rng && T); + (void)fp_to_meter; + + f3_set(dir, ray_dir); + + /* Launch the radiative random walk */ + for(;;) { + const struct sdis_interface* interf = NULL; + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + struct sdis_medium* chk_mdm = NULL; + double alpha; + double epsilon; + double r; + float pos[DIM]; + const float range[2] = { 0, FLT_MAX }; + + fX_set_dX(pos, rwalk->vtx.P); + + /* Trace the radiative ray */ +#if (SDIS_XD_DIMENSION == 2) + SXD(scene_view_trace_ray_3d + (scn->sXd(view), pos, dir, range, &rwalk->hit, &rwalk->hit)); +#else + SXD(scene_view_trace_ray + (scn->sXd(view), pos, dir, range, &rwalk->hit, &rwalk->hit)); +#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; + T->done = 1; + + if(ctx->green_path) { + struct sdis_rwalk_vertex vtx; + d3_splat(vtx.P, INF); + vtx.time = rwalk->vtx.time; + res = green_path_set_limit_vertex(ctx->green_path, rwalk->mdm, &vtx); + if(res != RES_OK) goto error; + } + 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); + if(res != RES_OK) goto error; + } + break; + } else { + log_err(scn->dev, + "%s: the random walk reaches an invalid ambient radiative temperature " + "of `%gK' at position `%g %g %g'. This may be due to numerical " + "inaccuracies or to inconsistency in the simulated system (eg: " + "unclosed geometry). For systems where the random walks can reach " + "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, + SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP; + goto error; + } + } + + /* Define the hit side */ + rwalk->hit_side = fX(dot)(dir, rwalk->hit.normal) < 0 + ? SDIS_FRONT : SDIS_BACK; + + /* Move the random walk to the hit position */ + 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); + if(res != RES_OK) goto error; + + /* Fetch the new interface and setup the hit fragment */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); + + /* Fetch the interface emissivity */ + epsilon = interface_side_get_emissivity(interf, &frag); + if(epsilon > 1 || epsilon < 0) { + log_err(scn->dev, + "%s: invalid overall emissivity `%g' at position `%g %g %g'.\n", + FUNC_NAME, epsilon, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP; + goto error; + } + + /* Switch in boundary temperature ? */ + r = ssp_rng_canonical(rng); + if(r < epsilon) { + T->func = XD(boundary_path); + rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ + break; + } + + /* Normalize the normal of the interface and ensure that it points toward the + * current medium */ + fX(normalize)(N, rwalk->hit.normal); + if(rwalk->hit_side == SDIS_BACK){ + chk_mdm = interf->medium_back; + fX(minus)(N, N); + } else { + chk_mdm = interf->medium_front; + } + + if(chk_mdm != rwalk->mdm) { + /* To ease the setting of models, the external enclosure is allowed to be + * incoherent regarding media. Here a radiative path is allowed to join + * 2 different fluids. */ + const int outside = scene_is_outside + (scn, rwalk->hit_side, rwalk->hit.prim.prim_id); + if(outside && chk_mdm->type == SDIS_FLUID) { + rwalk->mdm = chk_mdm; + } else { + log_err(scn->dev, "%s: inconsistent medium definition at `%g %g %g'.\n", + FUNC_NAME, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP; + goto error; + } + } + alpha = interface_side_get_specular_fraction(interf, &frag); + r = ssp_rng_canonical(rng); + if(r < alpha) { /* Sample specular part */ + reflect_3d(dir, f3_minus(dir, dir), N); + } else { /* Sample diffuse part */ + ssp_ran_hemisphere_cos_float(rng, N, dir, NULL); + } + } + +exit: + return res; +error: + goto exit; +} + +res_T +XD(radiative_path) + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + /* The radiative random walk is always performed in 3D. In 2D, the geometry + * are assumed to be extruded to the infinity along the Z dimension. */ + float N[3] = {0, 0, 0}; + float dir[3] = {0, 0, 0}; + res_T res = RES_OK; + + ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); + ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + (void)fp_to_meter; + + /* Normalize the normal of the interface and ensure that it points toward the + * current medium */ + fX(normalize(N, rwalk->hit.normal)); + if(rwalk->hit_side == SDIS_BACK) { + fX(minus(N, N)); + } + + /* Cosine weighted sampling of a direction around the surface normal */ + ssp_ran_hemisphere_cos_float(rng, N, dir, NULL); + + /* Launch the radiative random walk */ + res = XD(trace_radiative_path)(scn, dir, fp_to_meter, ctx, rwalk, rng, T); + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_interface.c b/src/sdis_interface.c @@ -16,6 +16,7 @@ #include "sdis.h" #include "sdis_device_c.h" #include "sdis_interface_c.h" +#include "sdis_scene_c.h" #include <rsys/double2.h> #include <rsys/double3.h> @@ -184,10 +185,34 @@ sdis_interface_ref_put(struct sdis_interface* interf) return RES_OK; } +SDIS_API res_T +sdis_interface_get_shader + (const struct sdis_interface* interf, + struct sdis_interface_shader* shader) +{ + if(!interf || !shader) return RES_BAD_ARG; + *shader = interf->shader; + return RES_OK; +} + +struct sdis_data* +sdis_interface_get_data(struct sdis_interface* interf) +{ + ASSERT(interf); + return interf->data; +} + +unsigned +sdis_interface_get_id(const struct sdis_interface* interf) +{ + ASSERT(interf); + return interf->id.index; +} + /******************************************************************************* * Local function ******************************************************************************/ -const struct sdis_medium* +struct sdis_medium* interface_get_medium (const struct sdis_interface* interf, const enum sdis_side side) { @@ -201,13 +226,6 @@ interface_get_medium return mdm; } -unsigned -interface_get_id(const struct sdis_interface* interf) -{ - ASSERT(interf); - return interf->id.index; -} - void setup_interface_fragment_2d (struct sdis_interface_fragment* frag, @@ -242,3 +260,92 @@ setup_interface_fragment_3d frag->side = side; } +res_T +build_interface_fragment_2d + (struct sdis_interface_fragment* frag, + const struct sdis_scene* scn, + const unsigned iprim, + const double* uv, + const enum sdis_side side) +{ + struct s2d_attrib attr_P, attr_N; + struct s2d_primitive prim = S2D_PRIMITIVE_NULL; + struct s2d_hit hit = S2D_HIT_NULL; + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + float st; + res_T res = RES_OK; + + ASSERT(frag && scn && uv && scene_is_2d(scn)); + ASSERT(side == SDIS_FRONT || side == SDIS_BACK); + + st = (float)uv[0]; + + #define CALL(Func) { res = Func; if(res != RES_OK) goto error; } (void)0 + CALL(s2d_scene_view_get_primitive(scn->s2d_view, iprim, &prim)); + CALL(s2d_primitive_get_attrib(&prim, S2D_POSITION, st, &attr_P)); + CALL(s2d_primitive_get_attrib(&prim, S2D_GEOMETRY_NORMAL, st, &attr_N)); + #undef CALL + + vtx.P[0] = attr_P.value[0]; + vtx.P[1] = attr_P.value[1]; + vtx.time = NaN; + hit.normal[0] = attr_N.value[0]; + hit.normal[1] = attr_N.value[1]; + hit.distance = 0; + hit.prim = prim; + + setup_interface_fragment_2d(frag, &vtx, &hit, side); + +exit: + return res; +error: + *frag = SDIS_INTERFACE_FRAGMENT_NULL; + goto exit; +} + +res_T +build_interface_fragment_3d + (struct sdis_interface_fragment* frag, + const struct sdis_scene* scn, + const unsigned iprim, + const double* uv, + const enum sdis_side side) +{ + struct s3d_attrib attr_P, attr_N; + struct s3d_primitive prim = S3D_PRIMITIVE_NULL; + struct s3d_hit hit = S3D_HIT_NULL; + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + float st[2]; + res_T res = RES_OK; + + ASSERT(frag && scn && uv && !scene_is_2d(scn)); + ASSERT(side == SDIS_FRONT || side == SDIS_BACK); + + st[0] = (float)uv[0]; + st[1] = (float)uv[1]; + + #define CALL(Func) { res = Func; if(res != RES_OK) goto error; } (void)0 + CALL(s3d_scene_view_get_primitive(scn->s3d_view, iprim, &prim)); + CALL(s3d_primitive_get_attrib(&prim, S3D_POSITION, st, &attr_P)); + CALL(s3d_primitive_get_attrib(&prim, S3D_GEOMETRY_NORMAL, st, &attr_N)); + #undef CALL + + vtx.P[0] = attr_P.value[0]; + vtx.P[1] = attr_P.value[1]; + vtx.P[2] = attr_P.value[2]; + vtx.time = NaN; + hit.normal[0] = attr_N.value[0]; + hit.normal[1] = attr_N.value[1]; + hit.normal[2] = attr_N.value[2]; + hit.distance = 0; + hit.prim = prim; + + setup_interface_fragment_3d(frag, &vtx, &hit, side); + +exit: + return res; +error: + *frag = SDIS_INTERFACE_FRAGMENT_NULL; + goto exit; +} + diff --git a/src/sdis_interface_c.h b/src/sdis_interface_c.h @@ -36,14 +36,17 @@ struct sdis_interface { struct sdis_device* dev; }; -extern LOCAL_SYM const struct sdis_medium* +extern LOCAL_SYM struct sdis_medium* interface_get_medium (const struct sdis_interface* interf, const enum sdis_side side); -extern LOCAL_SYM unsigned -interface_get_id - (const struct sdis_interface* interf); +static FINLINE unsigned +interface_get_id(const struct sdis_interface* interf) +{ + ASSERT(interf); + return interf->id.index; +} extern LOCAL_SYM void setup_interface_fragment_2d @@ -59,6 +62,22 @@ setup_interface_fragment_3d const struct s3d_hit* hit, const enum sdis_side side); +extern LOCAL_SYM res_T +build_interface_fragment_2d + (struct sdis_interface_fragment* frag, + const struct sdis_scene* scn, + const unsigned iprim, + const double uv[1], + const enum sdis_side side); + +extern LOCAL_SYM res_T +build_interface_fragment_3d + (struct sdis_interface_fragment* frag, + const struct sdis_scene* scn, + const unsigned iprim, + const double uv[2], + const enum sdis_side side); + static INLINE double interface_get_convection_coef (const struct sdis_interface* interf, diff --git a/src/sdis_medium.c b/src/sdis_medium.c @@ -142,6 +142,15 @@ error: } res_T +sdis_fluid_get_shader + (const struct sdis_medium* mdm, struct sdis_fluid_shader* shader) +{ + if(!mdm || mdm->type != SDIS_FLUID || !shader) return RES_BAD_ARG; + *shader = mdm->shader.fluid; + return RES_OK; +} + +res_T sdis_solid_create (struct sdis_device* dev, const struct sdis_solid_shader* shader, @@ -187,6 +196,15 @@ error: } res_T +sdis_solid_get_shader + (const struct sdis_medium* mdm, struct sdis_solid_shader* shader) +{ + if(!mdm || mdm->type != SDIS_SOLID || !shader) return RES_BAD_ARG; + *shader = mdm->shader.solid; + return RES_OK; +} + +res_T sdis_medium_ref_get(struct sdis_medium* medium) { if(!medium) return RES_BAD_ARG; @@ -208,9 +226,18 @@ sdis_medium_get_type(const struct sdis_medium* medium) ASSERT(medium != NULL); return medium->type; } + struct sdis_data* sdis_medium_get_data(struct sdis_medium* medium) { ASSERT(medium); return medium->data; } + +unsigned +sdis_medium_get_id(const struct sdis_medium* medium) +{ + ASSERT(medium); + return medium->id.index; +} + diff --git a/src/sdis_medium_c.h b/src/sdis_medium_c.h @@ -65,13 +65,12 @@ 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); + /*ASSERT(vtx->time >= mdm->shader.fluid.t0);*/ return mdm->shader.fluid.temperature(vtx, mdm->data); } static INLINE double - fluid_get_t0 -(const struct sdis_medium* mdm) +fluid_get_t0(const struct sdis_medium* mdm) { ASSERT(mdm && mdm->type == SDIS_FLUID); ASSERT(0 <= mdm->shader.fluid.t0 && mdm->shader.fluid.t0 < INF); @@ -128,18 +127,47 @@ solid_get_temperature (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) { ASSERT(mdm && mdm->type == SDIS_SOLID); - ASSERT(vtx->time >= mdm->shader.solid.t0); + /*ASSERT(vtx->time >= mdm->shader.solid.t0);*/ return mdm->shader.solid.temperature(vtx, mdm->data); } static INLINE double - solid_get_t0 -(const struct sdis_medium* mdm) +solid_get_t0(const struct sdis_medium* mdm) { ASSERT(mdm && mdm->type == SDIS_SOLID); ASSERT(0 <= mdm->shader.solid.t0 && mdm->shader.solid.t0 < INF); return mdm->shader.solid.t0; } +/******************************************************************************* + * Generic functions + ******************************************************************************/ +static FINLINE double +medium_get_temperature + (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx) +{ + double temp; + ASSERT(mdm); + switch(mdm->type) { + case SDIS_FLUID: temp = fluid_get_temperature(mdm, vtx); break; + case SDIS_SOLID: temp = solid_get_temperature(mdm, vtx); break; + default: FATAL("Unreachable code.\n"); break; + } + return temp; +} + +static FINLINE double +medium_get_t0(const struct sdis_medium* mdm) +{ + double t0; + ASSERT(mdm); + switch(mdm->type) { + case SDIS_FLUID: t0 = fluid_get_t0(mdm); break; + case SDIS_SOLID: t0 = solid_get_t0(mdm); break; + default: FATAL("Unreachable code.\n"); break; + } + return t0; +} + #endif /* SDIS_MEDIUM_C_H */ diff --git a/src/sdis_misc.h b/src/sdis_misc.h @@ -0,0 +1,111 @@ +/* Copyright (C) 2016-2019 |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_MISC_H +#define SDIS_MISC_H + +#include <rsys/float2.h> +#include <rsys/float3.h> +#include <star/ssp.h> + +/* Empirical scale factor to apply to the upper bound of the ray range in order + * to handle numerical imprecisions */ +#define RAY_RANGE_MAX_SCALE 1.001f + +/* Define a new result code from RES_BAD_OP saying that the bad operation is + * definitive, i.e. in the current state, the realisation will inevitably fail. + * It is thus unecessary to retry a specific section of the random walk */ +#define RES_BAD_OP_IRRECOVERABLE (-RES_BAD_OP) + +#define BOLTZMANN_CONSTANT 5.6696e-8 /* W/m^2/K^4 */ + +/* Reflect the V wrt the normal N. By convention V points outward the surface */ +static FINLINE float* +reflect_2d(float res[2], const float V[2], const float N[2]) +{ + float tmp[2]; + float cos_V_N; + ASSERT(res && V && N); + ASSERT(f2_is_normalized(V) && f2_is_normalized(N)); + cos_V_N = f2_dot(V, N); + f2_mulf(tmp, N, 2*cos_V_N); + f2_sub(res, tmp, V); + return res; +} + +/* Reflect the V wrt the normal N. By convention V points outward the surface */ +static FINLINE float* +reflect_3d(float res[3], const float V[3], const float N[3]) +{ + float tmp[3]; + float cos_V_N; + ASSERT(res && V && N); + ASSERT(f3_is_normalized(V) && f3_is_normalized(N)); + cos_V_N = f3_dot(V, N); + f3_mulf(tmp, N, 2*cos_V_N); + f3_sub(res, tmp, V); + return res; +} + +static FINLINE double* +move_pos_2d(double pos[2], const float dir[2], const float delta) +{ + ASSERT(pos && dir); + pos[0] += dir[0] * delta; + pos[1] += dir[1] * delta; + return pos; +} + +static FINLINE double* +move_pos_3d(double pos[3], const float dir[3], const float delta) +{ + ASSERT(pos && dir); + pos[0] += dir[0] * delta; + pos[1] += dir[1] * delta; + pos[2] += dir[2] * delta; + return pos; +} + +static INLINE double +sample_time(struct ssp_rng* rng, const double time_range[2]) +{ + ASSERT(time_range && time_range[0] >= 0 && time_range[1] >= time_range[0]); + ASSERT(rng); + if(time_range[0] == time_range[1]) return time_range[0]; + return ssp_rng_uniform_double(rng, time_range[0], time_range[1]); +} + +static INLINE res_T +register_heat_vertex + (struct sdis_heat_path* path, + const struct sdis_rwalk_vertex* vtx, + const double weight, + const enum sdis_heat_vertex_type type) +{ + struct sdis_heat_vertex heat_vtx = SDIS_HEAT_VERTEX_NULL; + ASSERT(vtx); + + if(!path) return RES_OK; + + heat_vtx.P[0] = vtx->P[0]; + heat_vtx.P[1] = vtx->P[1]; + heat_vtx.P[2] = vtx->P[2]; + heat_vtx.time = vtx->time; + heat_vtx.weight = weight; + heat_vtx.type = type; + return heat_path_add_vertex(path, &heat_vtx); +} + +#endif /* SDIS_MISC_H */ diff --git a/src/sdis_realisation.c b/src/sdis_realisation.c @@ -0,0 +1,71 @@ +/* Copyright (C) 2016-2019 |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_realisation.h" + +/* Generate the generic realisations */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_realisation_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_realisation_Xd.h" + +res_T +ray_realisation_3d + (struct sdis_scene* scn, + struct ssp_rng* rng, + struct sdis_medium* medium, + const double position[], + const double direction[], + const double time, + const double fp_to_meter, + const double Tarad, + const double Tref, + double* weight) +{ + struct rwalk_context ctx = RWALK_CONTEXT_NULL; + struct rwalk_3d rwalk = RWALK_NULL_3d; + struct temperature_3d T = TEMPERATURE_NULL_3d; + float dir[3]; + res_T res = RES_OK; + ASSERT(scn && position && direction && time>=0 && fp_to_meter>0 && weight); + ASSERT(Tref >= 0 && medium && medium->type == SDIS_FLUID); + + d3_set(rwalk.vtx.P, position); + rwalk.vtx.time = time; + rwalk.hit = S3D_HIT_NULL; + rwalk.hit_side = SDIS_SIDE_NULL__; + rwalk.mdm = medium; + + ctx.Tarad = Tarad; + ctx.Tref3 = Tref*Tref*Tref; + + f3_set_d3(dir, direction); + + res = trace_radiative_path_3d(scn, dir, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) goto error; + + if(!T.done) { + res = compute_temperature_3d(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) goto error; + } + + *weight = T.value; + +exit: + return res; +error: + goto exit; +} + diff --git a/src/sdis_realisation.h b/src/sdis_realisation.h @@ -0,0 +1,146 @@ +/* Copyright (C) 2016-2019 |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_REALISATION_H +#define SDIS_REALISATION_H + +#include "sdis.h" +#include "sdis_estimator_c.h" + +#include <rsys/rsys.h> + +/* Forward declarations */ +struct green_path_handle; +struct sdis_scene; +struct ssp_rng; + +enum flux_flag { + FLUX_FLAG_CONVECTIVE = BIT(FLUX_CONVECTIVE), + FLUX_FLAG_RADIATIVE = BIT(FLUX_RADIATIVE), + FLUX_FLAGS_ALL = FLUX_FLAG_CONVECTIVE | FLUX_FLAG_RADIATIVE +}; + +/******************************************************************************* + * Realisation at a given position and time IN a medium + ******************************************************************************/ +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, + const double fp_to_meter,/* Scale factor from floating point unit to meter */ + const double ambient_radiative_temperature, + const double reference_temperature, + struct green_path_handle* green_path, + struct sdis_heat_path* heat_path, + 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, + const double fp_to_meter,/* Scale factor from floating point unit to meter */ + const double ambient_radiative_temperature, + const double reference_temperature, + struct green_path_handle* green_path, + struct sdis_heat_path* heat_path, + double* weight); + +/******************************************************************************* + * Realisation at a given position and time ON a given side of a boundary + ******************************************************************************/ +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, + const double fp_to_meter, + const double ambient_radiative_temperature, + const double reference_temperature, + struct green_path_handle* green_path, + struct sdis_heat_path* heat_path, + 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, + const double fp_to_meter, + const double ambient_radiative_temperature, + const double reference_temperature, + struct green_path_handle* green_path, + struct sdis_heat_path* heat_path, + double* weight); + +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 double fp_to_meter, + const double ambient_radiative_temperature, + const double reference_temperature, + const int flux_mask, /* Combination of enum flux_flag */ + double weight[FLUX_NAMES_COUNT__]); + +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 double fp_to_meter, + const double ambient_radiative_temperature, + const double reference_temperature, + const int flux_mask, /* Combination of enum flux_flag */ + double weight[FLUX_NAMES_COUNT__]); + +/******************************************************************************* + * Realisation along a given ray at a given time. Available only in 3D. + ******************************************************************************/ +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, + const double fp_to_meter, + const double ambient_radiative_temperature, + const double reference_temperature, + double* weight); + +#endif /* SDIS_REALISATION_H */ + diff --git a/src/sdis_realisation_Xd.h b/src/sdis_realisation_Xd.h @@ -0,0 +1,384 @@ +/* Copyright (C) 2016-2019 |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_device_c.h" +#include "sdis_heat_path.h" +#include "sdis_interface_c.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_scene_c.h" + +#include <rsys/stretchy_array.h> +#include <star/ssp.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static res_T +XD(compute_temperature) + (struct sdis_scene* scn, + const double fp_to_meter, + const struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ +#ifndef NDEBUG + /* Stack that saves the state of each recursion steps. */ + struct entry { + struct XD(temperature) temperature; + struct XD(rwalk) rwalk; + }* stack = NULL; + size_t istack = 0; +#endif + /* Maximum accepted #failures before stopping the realisation */ + struct sdis_heat_vertex* heat_vtx = NULL; + const size_t MAX_FAILS = 10; + res_T res = RES_OK; + ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); + + if(ctx->heat_path && T->func == XD(boundary_path)) { + heat_vtx = heat_path_get_last_vertex(ctx->heat_path); + } + + do { + /* Save the current random walk state */ + const struct XD(rwalk) rwalk_bkp = *rwalk; + const struct XD(temperature) T_bkp = *T; + size_t nfails = 0; /* #failures */ + +#ifndef NDEBUG + struct entry e; + e.temperature = *T; + e.rwalk = *rwalk; + sa_push(stack, e); + ++istack; +#endif + + /* Reject the step if a BAD_OP occurs and retry up to MAX_FAILS times */ + do { + res = T->func(scn, fp_to_meter, ctx, rwalk, rng, T); + if(res == RES_BAD_OP) { *rwalk = rwalk_bkp; *T = T_bkp; } + } while(res == RES_BAD_OP && ++nfails < MAX_FAILS); + if(res != RES_OK) goto error; + + /* Update the type of the first vertex of the random walks that begin on a + * boundary. Indeed, one knows the "right" type of the first vertex only + * after the boundary_path execution that defines the sub path to resolve + * from the submitted boundary position. Note that if the boundary + * temperature is know, the type is let as it. */ + if(heat_vtx && !T->done) { + if(heat_path_get_last_vertex(ctx->heat_path) != heat_vtx) { + /* Path was reinjected into a solid */ + heat_vtx->type = SDIS_HEAT_VERTEX_CONDUCTION; + } else if(T->func == XD(convective_path)) { + heat_vtx->type = SDIS_HEAT_VERTEX_CONVECTION; + } else if(T->func == XD(radiative_path)) { + heat_vtx->type = SDIS_HEAT_VERTEX_RADIATIVE; + } else { + FATAL("Unreachable code.\n"); + } + heat_vtx = NULL; /* Notify that the first vertex is finalized */ + } + + } while(!T->done); + +exit: +#ifndef NDEBUG + sa_release(stack); +#endif + 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[], + const double time, + const double fp_to_meter,/* Scale factor from floating point unit to meter */ + const double ambient_radiative_temperature, + const double reference_temperature, + struct green_path_handle* green_path, + struct sdis_heat_path* heat_path, + double* weight) +{ + struct rwalk_context ctx = RWALK_CONTEXT_NULL; + struct XD(rwalk) rwalk = XD(RWALK_NULL); + struct XD(temperature) T = XD(TEMPERATURE_NULL); + enum sdis_heat_vertex_type type; + double t0; + double (*get_initial_temperature) + (const struct sdis_medium* mdm, + const struct sdis_rwalk_vertex* vtx); + res_T res = RES_OK; + ASSERT(medium && position && fp_to_meter > 0 && weight && time >= 0); + (void)irealisation; + + switch(medium->type) { + case SDIS_FLUID: + T.func = XD(convective_path); + get_initial_temperature = fluid_get_temperature; + t0 = fluid_get_t0(medium); + break; + case SDIS_SOLID: + T.func = XD(conductive_path); + get_initial_temperature = solid_get_temperature; + t0 = solid_get_t0(medium); + break; + default: FATAL("Unreachable code\n"); break; + } + + dX(set)(rwalk.vtx.P, position); + rwalk.vtx.time = time; + + /* Register the starting position against the heat path */ + type = medium->type == SDIS_SOLID + ? SDIS_HEAT_VERTEX_CONDUCTION + : SDIS_HEAT_VERTEX_CONVECTION; + res = register_heat_vertex(heat_path, &rwalk.vtx, 0, type); + if(res != RES_OK) goto error; + + /* No initial condition with green */ + if(!green_path && t0 >= rwalk.vtx.time) { + double tmp; + /* Check the initial condition. */ + rwalk.vtx.time = t0; + tmp = get_initial_temperature(medium, &rwalk.vtx); + if(tmp >= 0) { + *weight = tmp; + goto exit; + } + /* The initial condition should have been reached */ + log_err(scn->dev, + "%s: undefined initial condition. " + "The time is %f but the temperature remains unknown.\n", + FUNC_NAME, t0); + res = RES_BAD_OP; + goto error; + } + + rwalk.hit = SXD_HIT_NULL; + rwalk.mdm = medium; + + ctx.green_path = green_path; + ctx.heat_path = heat_path; + ctx.Tarad = ambient_radiative_temperature; + ctx.Tref3 = + reference_temperature + * reference_temperature + * reference_temperature; + + res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) goto error; + + *weight = T.value; + +exit: + return res; +error: + goto exit; +} + +res_T +XD(boundary_realisation) + (struct sdis_scene* scn, + struct ssp_rng* rng, + const size_t iprim, + const double uv[2], + const double time, + const enum sdis_side side, + const double fp_to_meter, + const double Tarad, + const double Tref, + struct green_path_handle* green_path, + struct sdis_heat_path* heat_path, + double* weight) +{ + struct rwalk_context ctx = RWALK_CONTEXT_NULL; + struct XD(rwalk) rwalk = XD(RWALK_NULL); + struct XD(temperature) T = XD(TEMPERATURE_NULL); + struct sXd(attrib) attr; +#if SDIS_XD_DIMENSION == 2 + float st; +#else + float st[2]; +#endif + res_T res = RES_OK; + ASSERT(uv && fp_to_meter > 0 && weight && Tref >= 0 && time >= 0); + + T.func = XD(boundary_path); + rwalk.hit_side = side; + rwalk.hit.distance = 0; + rwalk.vtx.time = time; + rwalk.mdm = NULL; /* The random walk is at an interface between 2 media */ + +#if SDIS_XD_DIMENSION == 2 + st = (float)uv[0]; +#else + f2_set_d2(st, uv); +#endif + + /* Fetch the primitive */ + SXD(scene_view_get_primitive + (scn->sXd(view), (unsigned int)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)); + dX_set_fX(rwalk.vtx.P, attr.value); + + /* Retrieve the primitive normal */ + SXD(primitive_get_attrib(&rwalk.hit.prim, SXD_GEOMETRY_NORMAL, st, &attr)); + fX(set)(rwalk.hit.normal, attr.value); + +#if SDIS_XD_DIMENSION==2 + rwalk.hit.u = st; +#else + f2_set(rwalk.hit.uv, st); +#endif + + res = register_heat_vertex(heat_path, &rwalk.vtx, 0/*weight*/, + SDIS_HEAT_VERTEX_CONDUCTION); + if(res != RES_OK) goto error; + + ctx.green_path = green_path; + ctx.heat_path = heat_path; + ctx.Tarad = Tarad; + ctx.Tref3 = Tref*Tref*Tref; + + res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) goto error; + + *weight = T.value; + +exit: + return res; +error: + goto exit; +} + +res_T +XD(boundary_flux_realisation) + (struct sdis_scene* scn, + struct ssp_rng* rng, + const size_t iprim, + const double uv[DIM], + const double time, + const enum sdis_side solid_side, + const double fp_to_meter, + const double Tarad, + const double Tref, + const int flux_mask, + double weight[3]) +{ + struct rwalk_context ctx = RWALK_CONTEXT_NULL; + struct XD(rwalk) rwalk; + struct XD(temperature) T; + struct sXd(attrib) attr; + struct sXd(primitive) prim; +#if SDIS_XD_DIMENSION == 2 + float st; +#else + float st[2]; +#endif + double P[SDIS_XD_DIMENSION]; + float N[SDIS_XD_DIMENSION]; + const double Tr3 = Tref * Tref * Tref; + const enum sdis_side fluid_side = + (solid_side == SDIS_FRONT) ? SDIS_BACK : SDIS_FRONT; + 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 && fp_to_meter > 0 && weight && time >= 0 && Tref >= 0); + +#if SDIS_XD_DIMENSION == 2 + #define SET_PARAM(Dest, Src) (Dest).u = (Src); + st = (float)uv[0]; +#else + #define SET_PARAM(Dest, Src) f2_set((Dest).uv, (Src)); + f2_set_d2(st, uv); +#endif + + /* Fetch the primitive */ + SXD(scene_view_get_primitive(scn->sXd(view), (unsigned int)iprim, &prim)); + + /* Retrieve the world space position of the probe onto the primitive */ + SXD(primitive_get_attrib(&prim, SXD_POSITION, st, &attr)); + dX_set_fX(P, attr.value); + + /* Retrieve the primitive normal */ + SXD(primitive_get_attrib(&prim, SXD_GEOMETRY_NORMAL, st, &attr)); + fX(set)(N, attr.value); + + #define RESET_WALK(Side, Mdm) { \ + rwalk = XD(RWALK_NULL); \ + rwalk.hit_side = (Side); \ + rwalk.hit.distance = 0; \ + rwalk.vtx.time = time; \ + rwalk.mdm = (Mdm); \ + rwalk.hit.prim = prim; \ + SET_PARAM(rwalk.hit, st); \ + ctx.Tarad = Tarad; \ + ctx.Tref3 = Tr3; \ + 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); + T.func = XD(boundary_path); + res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) return res; + weight[0] = T.value; + + /* Compute radiative temperature */ + if(compute_radiative) { + RESET_WALK(fluid_side, NULL); + T.func = XD(radiative_path); + res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) return res; + weight[1] = T.value; + } + + /* Compute fluid temperature */ + if(compute_convective) { + struct sdis_interface* interf = scene_get_interface(scn, (unsigned)iprim); + struct sdis_medium* mdm = interface_get_medium(interf, fluid_side); + + RESET_WALK(fluid_side, mdm); + T.func = XD(convective_path); + res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); + if(res != RES_OK) return res; + weight[2] = T.value; + } + + #undef SET_PARAM + #undef RESET_WALK + + return RES_OK; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_scene.c b/src/sdis_scene.c @@ -15,11 +15,9 @@ #include "sdis_scene_Xd.h" -/* Generate the 2D functions of the scene */ +/* Generate the Generic functions of the scene */ #define SDIS_SCENE_DIMENSION 2 #include "sdis_scene_Xd.h" - -/* Generate the 3D functions of the scene */ #define SDIS_SCENE_DIMENSION 3 #include "sdis_scene_Xd.h" @@ -106,6 +104,8 @@ scene_release(ref_T * ref) htable_d_release(&scn->tmp_hc_ub); if(scn->s2d_view) S2D(scene_view_ref_put(scn->s2d_view)); if(scn->s3d_view) S3D(scene_view_ref_put(scn->s3d_view)); + if(scn->senc_descriptor) SENC(descriptor_ref_put(scn->senc_descriptor)); + if(scn->senc2d_descriptor) SENC2D(descriptor_ref_put(scn->senc2d_descriptor)); MEM_RM(dev->allocator, scn); SDIS(device_ref_put(dev)); } @@ -275,10 +275,86 @@ sdis_scene_boundary_project_position return RES_OK; } +res_T +sdis_scene_2d_get_analysis + (struct sdis_scene* scn, + struct senc2d_descriptor** descriptor) +{ + if(!scn || !descriptor) return RES_BAD_ARG; + if(!scn->senc2d_descriptor) return RES_BAD_ARG; /* Scene is 3D */ + SENC2D(descriptor_ref_get(scn->senc2d_descriptor)); + *descriptor = scn->senc2d_descriptor; + return RES_OK; +} + +res_T +sdis_scene_get_analysis + (struct sdis_scene* scn, + struct senc_descriptor** descriptor) +{ + if(!scn || !descriptor) return RES_BAD_ARG; + if(!scn->senc_descriptor) return RES_BAD_ARG; /* Scene is 2D */ + SENC(descriptor_ref_get(scn->senc_descriptor)); + *descriptor = scn->senc_descriptor; + return RES_OK; +} + +res_T +sdis_scene_release_analysis(struct sdis_scene* scn) +{ + if(!scn) return RES_BAD_ARG; + if(scn->senc2d_descriptor) SENC2D(descriptor_ref_put(scn->senc2d_descriptor)); + if(scn->senc_descriptor) SENC(descriptor_ref_put(scn->senc_descriptor)); + scn->senc_descriptor = NULL; + scn->senc2d_descriptor = NULL; + return RES_OK; +} + +res_T +sdis_scene_get_dimension + (const struct sdis_scene* scn, enum sdis_scene_dimension* dim) +{ + if(!scn || !dim) return RES_BAD_ARG; + *dim = scene_is_2d(scn) ? SDIS_SCENE_2D : SDIS_SCENE_3D; + return RES_OK; +} + +res_T +sdis_scene_get_medium_spread + (struct sdis_scene* scn, + const struct sdis_medium* mdm, + double* out_spread) +{ + struct htable_enclosure_iterator it, end; + double spread = 0; + res_T res = RES_OK; + + if(!scn || !mdm || !out_spread) { + res = RES_BAD_ARG; + goto error; + } + + htable_enclosure_begin(&scn->enclosures, &it); + htable_enclosure_end(&scn->enclosures, &end); + while(!htable_enclosure_iterator_eq(&it, &end)) { + const struct enclosure* enc = htable_enclosure_iterator_data_get(&it); + htable_enclosure_iterator_next(&it); + if(sdis_medium_get_id(mdm) == enc->medium_id) { + spread += enc->V; + } + } + *out_spread = spread; + +exit: + return res; +error: + goto exit; +} + /******************************************************************************* * Local miscellaneous function ******************************************************************************/ -const struct sdis_interface* +struct sdis_interface* scene_get_interface(const struct sdis_scene* scn, const unsigned iprim) { ASSERT(scn && iprim < darray_prim_prop_size_get(&scn->prim_props)); @@ -290,9 +366,10 @@ scene_get_medium (const struct sdis_scene* scn, const double pos[], struct get_medium_info* info, - const struct sdis_medium** out_medium) + struct sdis_medium** out_medium) { return scene_is_2d(scn) ? scene_get_medium_2d(scn, pos, info, out_medium) : scene_get_medium_3d(scn, pos, info, out_medium); } + diff --git a/src/sdis_scene_Xd.h b/src/sdis_scene_Xd.h @@ -28,7 +28,6 @@ * Define the helper functions and the data types used by the scene * independently of its dimension, i.e. 2D or 3D. ******************************************************************************/ - /* Context used to wrap the user geometry and interfaces to Star-Enc */ struct geometry { void (*indices)(const size_t iprim, size_t ids[], void*); @@ -63,7 +62,7 @@ register_medium(struct sdis_scene* scn, struct sdis_medium* mdm) res_T res = RES_OK; ASSERT(scn && mdm); - /* Check that the front medium is already registered against the scene */ + /* Check that the medium is already registered against the scene */ id = medium_get_id(mdm); nmedia = darray_medium_size_get(&scn->media); if(id >= nmedia) { @@ -406,7 +405,7 @@ XD(setup_properties) #endif /* Fetch the interface of the primitive */ - interf(iprim, &itface, ctx); + interf(iprim_adjusted, &itface, ctx); /* Check that the interface is already registered against the scene */ id = interface_get_id(itface); @@ -559,7 +558,7 @@ XD(setup_enclosure_geometry)(struct sdis_scene* scn, struct sencXd(enclosure)* e enc_data = htable_enclosure_find(&scn->enclosures, &header.enclosure_id); ASSERT(enc_data != NULL); - /* Setup the vertex data */ + /* Setup the vertex data */ vdata.usage = SXD_POSITION; #if DIM == 2 vdata.type = S2D_FLOAT2; @@ -581,7 +580,7 @@ XD(setup_enclosure_geometry)(struct sdis_scene* scn, struct sencXd(enclosure)* e #endif CALL(sXd(scene_create)(sXd_dev, &sXd_scn)); CALL(sXd(scene_attach_shape)(sXd_scn, sXd_shape)); - CALL(sXd(scene_view_create)(sXd_scn, SXD_SAMPLE, &enc_data->sXd(view))); + CALL(sXd(scene_view_create)(sXd_scn, SXD_SAMPLE|SXD_TRACE, &enc_data->sXd(view))); /* Compute the S/V ratio */ #if DIM == 2 @@ -591,11 +590,12 @@ XD(setup_enclosure_geometry)(struct sdis_scene* scn, struct sencXd(enclosure)* e CALL(s3d_scene_view_compute_area(enc_data->s3d_view, &S)); CALL(s3d_scene_view_compute_volume(enc_data->s3d_view, &V)); #endif + enc_data->V = V; enc_data->S_over_V = S / V; ASSERT(enc_data->S_over_V >= 0); #undef CALL - /* Set enclosure hc upper bound regardless of its media being a fluid */ + /* Set enclosure hc upper bound regardless of its media being a fluid */ p_ub = htable_d_find(&scn->tmp_hc_ub, &header.enclosure_id); ASSERT(p_ub); enc_data->hc_upper_bound = *p_ub; @@ -613,6 +613,9 @@ XD(setup_enclosure_geometry)(struct sdis_scene* scn, struct sencXd(enclosure)* e #endif } + /* Setup the medium id of the enclosure */ + SENCXD(enclosure_get_medium(enc, 0, &enc_data->medium_id)); + exit: enclosure_release(&enc_dummy); if(sXd_shape) SXD(shape_ref_put(sXd_shape)); @@ -643,7 +646,6 @@ XD(setup_enclosures)(struct sdis_scene* scn, struct sencXd(descriptor)* desc) #else struct senc_enclosure_header header; #endif - const struct sdis_medium* mdm; SENCXD(descriptor_get_enclosure(desc, ienc, &enc)); SENCXD(enclosure_get_header(enc, &header)); @@ -659,51 +661,39 @@ XD(setup_enclosures)(struct sdis_scene* scn, struct sencXd(descriptor)* desc) if(header.enclosed_media_count != 1 && !header.is_infinite) { #ifndef NDEBUG /* Dump the problematic enclosure. */ + double tmp[DIM]; + unsigned indices[DIM]; unsigned i; -#if DIM == 2 -#define VEC_STR "%g %g" -#define VEC_SPLIT SPLIT2 -#define UVEC_STR "%u %u" -#define UVEC_SPLIT(A) 1 + (A)[0], 1 + (A)[1] -#define PRIM_COUNT segment_count -#define GET_PRIM senc2d_enclosure_get_segment -#else -#define VEC_STR "%g %g %g" -#define VEC_SPLIT SPLIT3 -#define UVEC_STR "%u %u %u" -#define UVEC_SPLIT(A) 1 + (A)[0], 1 + (A)[1], 1 + (A)[2] -#define PRIM_COUNT triangle_count -#define GET_PRIM senc_enclosure_get_triangle -#endif + #if DIM == 2 FOR_EACH(i, 0, header.vertices_count) { - double tmp[3]; SENCXD(enclosure_get_vertex(enc, i, tmp)); - log_warn(scn->dev, "v "VEC_STR"\n", VEC_SPLIT(tmp)); + log_warn(scn->dev, "v %g %g\n", SPLIT2(tmp)); } - FOR_EACH(i, 0, header.PRIM_COUNT) { - unsigned indices[3]; - ASSERT(GET_PRIM(enc, i, indices) == RES_OK); - log_warn(scn->dev, "f "UVEC_STR"\n", UVEC_SPLIT(indices)); + FOR_EACH(i, 0, header.segment_count) { + ASSERT(senc2d_enclosure_get_segment(enc, i, indices) == RES_OK); + log_warn(scn->dev, "f %u %u\n", indices[0]+1, indices[1]+1); } -#undef VEC_STR -#undef VEC_SPLIT -#undef UVEC_STR -#undef UVEC_SPLIT -#undef PRIM_COUNT -#undef GET_PRIM + #else + FOR_EACH(i, 0, header.vertices_count) { + SENCXD(enclosure_get_vertex(enc, i, tmp)); + log_warn(scn->dev, "v %g %g %g\n", SPLIT3(tmp)); + } + FOR_EACH(i, 0, header.triangle_count) { + ASSERT(senc_enclosure_get_triangle(enc, i, indices) == RES_OK); + log_warn(scn->dev, "f %u %u %u\n", + indices[0]+1, indices[1]+1, indices[2]+1); + } + #endif #endif res = RES_BAD_ARG; goto error; } SENCXD(enclosure_get_medium(enc, 0, &enclosed_medium)); - if(res != RES_OK) goto error; ASSERT(enclosed_medium < darray_medium_size_get(&scn->media)); - mdm = darray_medium_cdata_get(&scn->media)[enclosed_medium]; - ASSERT(mdm); - /* Silently discard the solid and infinite enclosures */ - if(mdm->type == SDIS_FLUID && !header.is_infinite) { + /* Silently discard infinite enclosures */ + if(!header.is_infinite) { res = XD(setup_enclosure_geometry)(scn, enc); if(res != RES_OK) goto error; } @@ -780,10 +770,14 @@ XD(scene_create) log_err(dev, "%s: could not setup the enclosures.\n", FUNC_NAME); goto error; } +#if DIM==2 + scn->senc2d_descriptor = desc; +#else + scn->senc_descriptor = desc; +#endif exit: if(out_scn) *out_scn = scn; - if(desc) SENCXD(descriptor_ref_put(desc)); return res; error: if(scn) { @@ -799,11 +793,11 @@ error: static INLINE res_T XD(scene_get_medium) (const struct sdis_scene* scn, - const double pos[2], + const double pos[DIM], struct get_medium_info* info, /* May be NULL */ - const struct sdis_medium** out_medium) + struct sdis_medium** out_medium) { - const struct sdis_medium* medium = NULL; + struct sdis_medium* medium = NULL; size_t iprim, nprims; size_t nfailures = 0; const size_t max_failures = 10; @@ -868,7 +862,8 @@ XD(scene_get_medium) fX(normalize)(N, hit.normal); cos_N_dir = fX(dot)(N, dir); - if(absf(cos_N_dir) > 1.e-1f) { /* Not roughly orthognonal */ + /* Not too close and not roughly orthognonal */ + if(hit.distance > 1.e-6 || absf(cos_N_dir) > 1.e-1f) { const struct sdis_interface* interf; interf = scene_get_interface(scn, hit.prim.prim_id); medium = interface_get_medium diff --git a/src/sdis_scene_c.h b/src/sdis_scene_c.h @@ -41,6 +41,10 @@ struct get_medium_info { struct s2d_hit hit_2d; struct s3d_hit hit_3d; }; +#define GET_MEDIUM_INFO_NULL__ \ + {{0,0,0}, {0,0,0}, {0,0,0}, S2D_HIT_NULL__, S3D_HIT_NULL__} +static const struct get_medium_info GET_MEDIUM_INFO_NULL = + GET_MEDIUM_INFO_NULL__; static INLINE void prim_prop_init(struct mem_allocator* allocator, struct prim_prop* prim) @@ -74,6 +78,9 @@ struct enclosure { double hc_upper_bound; double S_over_V; /* in 3D = surface/volume; in 2D = perimeter/area */ + double V; /* 3D = volume; 2D = area; */ + + unsigned medium_id; }; static INLINE void @@ -84,6 +91,7 @@ enclosure_init(struct mem_allocator* allocator, struct enclosure* enc) enc->s3d_view = NULL; darray_uint_init(allocator, &enc->local2global); enc->S_over_V = 0; + enc->V = 0; enc->hc_upper_bound = 0; } @@ -107,6 +115,7 @@ enclosure_copy(struct enclosure* dst, const struct enclosure* src) dst->s2d_view = src->s2d_view; } dst->S_over_V = src->S_over_V; + dst->V = src->V; dst->hc_upper_bound = src->hc_upper_bound; return darray_uint_copy(&dst->local2global, &src->local2global); } @@ -128,6 +137,7 @@ enclosure_copy_and_release(struct enclosure* dst, struct enclosure* src) src->s2d_view = NULL; } dst->S_over_V = src->S_over_V; + dst->V = src->V; dst->hc_upper_bound = src->hc_upper_bound; return RES_OK; } @@ -147,13 +157,13 @@ enclosure_local2global_prim_id #define DARRAY_FUNCTOR_INIT interface_init #include <rsys/dynamic_array.h> -/* Declare the array of medium */ +/* Declare the array of media */ #define DARRAY_NAME medium #define DARRAY_DATA struct sdis_medium* #define DARRAY_FUNCTOR_INIT medium_init #include <rsys/dynamic_array.h> -/* Declare the array of primitive */ +/* Declare the array of primitives */ #define DARRAY_NAME prim_prop #define DARRAY_DATA struct prim_prop #define DARRAY_FUNCTOR_INIT prim_prop_init @@ -169,7 +179,7 @@ enclosure_local2global_prim_id #define HTABLE_DATA_FUNCTOR_COPY_AND_RELEASE enclosure_copy_and_release #include <rsys/hash_table.h> -/* Declare the hash table that maps an enclosure id to its data */ +/* Declare the hash table that maps an enclosure id to hc upper bound */ #define HTABLE_NAME d #define HTABLE_KEY unsigned #define HTABLE_DATA double @@ -181,6 +191,8 @@ struct sdis_scene { struct darray_prim_prop prim_props; /* Per primitive properties */ struct s2d_scene_view* s2d_view; struct s3d_scene_view* s3d_view; + struct senc_descriptor* senc_descriptor; + struct senc2d_descriptor* senc2d_descriptor; struct htable_d tmp_hc_ub; /* Map an enclosure id to its hc upper bound */ struct htable_enclosure enclosures; /* Map an enclosure id to its data */ @@ -199,7 +211,7 @@ scene_get_primitives_count(const struct sdis_scene* scn) return darray_prim_prop_size_get(&scn->prim_props); } -extern LOCAL_SYM const struct sdis_interface* +extern LOCAL_SYM struct sdis_interface* scene_get_interface (const struct sdis_scene* scene, const unsigned iprim); @@ -209,7 +221,7 @@ scene_get_medium (const struct sdis_scene* scene, const double position[], struct get_medium_info* info, /* May be NULL */ - const struct sdis_medium** medium); + struct sdis_medium** medium); static INLINE void scene_get_enclosure_ids diff --git a/src/sdis_solve.c b/src/sdis_solve.c @@ -18,19 +18,34 @@ #include "sdis_device_c.h" #include "sdis_estimator_c.h" #include "sdis_interface_c.h" -#include "sdis_solve_Xd.h" - -/* Generate the 2D solver */ -#define SDIS_SOLVE_DIMENSION 2 -#include "sdis_solve_Xd.h" - -/* Generate the 3D solver */ -#define SDIS_SOLVE_DIMENSION 3 -#include "sdis_solve_Xd.h" #include <star/ssp.h> #include <omp.h> +/* Generate the probe solvers */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_solve_probe_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_solve_probe_Xd.h" + +/* Generate the probe boundary solvers */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_solve_probe_boundary_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_solve_probe_boundary_Xd.h" + +/* Generate the boundary solvers */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_solve_boundary_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_solve_boundary_Xd.h" + +/* Generate the medium solvers */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_solve_medium_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_solve_medium_Xd.h" + /******************************************************************************* * Helper functions ******************************************************************************/ @@ -49,7 +64,7 @@ static res_T solve_pixel (struct sdis_scene* scn, struct ssp_rng* rng, - const struct sdis_medium* mdm, + struct sdis_medium* mdm, const struct sdis_camera* cam, const double time, /* Observation time */ const double fp_to_meter, /* Scale from floating point units to meters */ @@ -111,7 +126,7 @@ static res_T solve_tile (struct sdis_scene* scn, struct ssp_rng* rng, - const struct sdis_medium* mdm, + struct sdis_medium* mdm, const struct sdis_camera* cam, const double time, const double fp_to_meter, @@ -170,102 +185,37 @@ sdis_solve_probe const double fp_to_meter,/* Scale factor from floating point unit to meter */ const double Tarad, /* Ambient radiative temperature */ const double Tref, /* Reference temperature */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ struct sdis_estimator** out_estimator) { - const struct sdis_medium* medium = NULL; - struct sdis_estimator* estimator = NULL; - struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - double weight = 0; - double sqr_weight = 0; - const int64_t rcount = (int64_t)nrealisations; - int64_t irealisation = 0; - size_t N = 0; /* #realisations that do not fail */ - size_t i; - ATOMIC res = RES_OK; - - if(!scn || !nrealisations || nrealisations > INT64_MAX || !position - || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] - || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) - || fp_to_meter <= 0 || Tref < 0 || !out_estimator) { - 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; - } - - /* Create the estimator */ - res = estimator_create(scn->dev, SDIS_TEMPERATURE_ESTIMATOR, &estimator); - if(res != RES_OK) goto error; - - /* Retrieve the medium in which the submitted position lies */ - res = scene_get_medium(scn, position, NULL, &medium); - if(res != RES_OK) goto error; - - /* Here we go! Launch the Monte Carlo estimation */ - omp_set_num_threads((int)scn->dev->nthreads); - #pragma omp parallel for schedule(static) reduction(+:weight,sqr_weight,N) - for(irealisation = 0; irealisation < rcount; ++irealisation) { - res_T res_local; - double w = NaN; - const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - - if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occured */ - - if(scene_is_2d(scn)) { - res_local = probe_realisation_2d - (scn, rng, medium, position, time_range, fp_to_meter, Tarad, Tref, &w); - } else { - res_local = probe_realisation_3d - (scn, rng, medium, position, time_range, fp_to_meter, Tarad, Tref, &w); - } - if(res_local != RES_OK) { - if(res_local != RES_BAD_OP) { - ATOMIC_SET(&res, res_local); - continue; - } - } else { - weight += w; - sqr_weight += w*w; - ++N; - } + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + return solve_probe_2d(scn, nrealisations, position, time_range, + fp_to_meter, Tarad, Tref, register_paths, NULL, out_estimator); + } else { + return solve_probe_3d(scn, nrealisations, position, time_range, + fp_to_meter, Tarad, Tref, register_paths, NULL, out_estimator); } - if(res != RES_OK) goto error; - - setup_estimator(estimator, nrealisations, N, weight, sqr_weight); +} -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_estimator) *out_estimator = estimator; - return (res_T)res; -error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; +res_T +sdis_solve_probe_green_function + (struct sdis_scene* scn, + const size_t nrealisations, + const double position[3], + const double fp_to_meter,/* Scale factor from floating point unit to meter */ + const double Tarad, /* Ambient radiative temperature */ + const double Tref, /* Reference temperature */ + struct sdis_green_function** out_green) +{ + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + return solve_probe_2d(scn, nrealisations, position, NULL, + fp_to_meter, Tarad, Tref, SDIS_HEAT_PATH_NONE, out_green, NULL); + } else { + return solve_probe_3d(scn, nrealisations, position, NULL, + fp_to_meter, Tarad, Tref, SDIS_HEAT_PATH_NONE, out_green, NULL); } - goto exit; } res_T @@ -279,133 +229,131 @@ sdis_solve_probe_boundary const double fp_to_meter, /* Scale from floating point units to meters */ const double Tarad, /* In Kelvin */ const double Tref, /* In Kelvin */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ struct sdis_estimator** out_estimator) { - struct sdis_estimator* estimator = NULL; - struct ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - double weight = 0; - double sqr_weight = 0; - const int64_t rcount = (int64_t)nrealisations; - int64_t irealisation = 0; - size_t N = 0; /* #realisations that do not fail */ - size_t i; - ATOMIC res = RES_OK; - - if(!scn || !nrealisations || nrealisations > INT64_MAX || !uv - || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] - || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) - || fp_to_meter <= 0 || Tref < 0 || (side != SDIS_FRONT && side != SDIS_BACK) - || !out_estimator) { - res = RES_BAD_ARG; - goto error; + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + return solve_probe_boundary_2d(scn, nrealisations, iprim, uv, time_range, + side, fp_to_meter, Tarad, Tref, register_paths, NULL, out_estimator); + } else { + return solve_probe_boundary_3d(scn, nrealisations, iprim, uv, time_range, + side, fp_to_meter, Tarad, Tref, register_paths, NULL, out_estimator); } +} - /* Check the primitive identifier */ - 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; +res_T +sdis_solve_probe_boundary_green_function + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t iprim, /* Identifier of the primitive on which the probe lies */ + const double uv[2], /* Parametric coordinates of the probe onto the primitve */ + const enum sdis_side side, /* Side of iprim on which the probe lies */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + struct sdis_green_function** green) +{ + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + return solve_probe_boundary_2d(scn, nrealisations, iprim, uv, NULL, + side, fp_to_meter, Tarad, Tref, SDIS_HEAT_PATH_NONE, green, NULL); + } else { + return solve_probe_boundary_3d(scn, nrealisations, iprim, uv, NULL, + side, fp_to_meter, Tarad, Tref, SDIS_HEAT_PATH_NONE, green, NULL); } +} - /* Check parametric coordinates */ +res_T +sdis_solve_boundary + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t primitives[], /* List of boundary primitives to handle */ + const enum sdis_side sides[], /* Per primitive side to consider */ + const size_t nprimitives, /* #primitives */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ + struct sdis_estimator** out_estimator) +{ + if(!scn) return RES_BAD_ARG; if(scene_is_2d(scn)) { - const double v = CLAMP(1.0 - uv[0], 0, 1); - if(uv[0] < 0 || uv[0] > 1 || !eq_eps(uv[0] + v, 1, 1.e-6)) { - log_err(scn->dev, -"%s: invalid parametric coordinates %g.\n" -"u + (1-u) must be equal to 1 with u [0, 1].\n", - FUNC_NAME, uv[0]); - res = RES_BAD_ARG; - goto error; - } + return solve_boundary_2d(scn, nrealisations, primitives, sides, nprimitives, + time_range, fp_to_meter, Tarad, Tref, register_paths, NULL, out_estimator); } else { - const double w = CLAMP(1 - uv[0] - uv[1], 0, 1); - if(uv[0] < 0 || uv[1] < 0 || uv[0] > 1 || uv[1] > 1 - || !eq_eps(w + uv[0] + uv[1], 1, 1.e-6)) { - log_err(scn->dev, -"%s: invalid parametric coordinates [%g, %g].\n" -"u + v + (1-u-v) must be equal to 1 with u and v in [0, 1].\n", - FUNC_NAME, uv[0], uv[1]); - res = RES_BAD_ARG; - goto error; - } + return solve_boundary_3d(scn, nrealisations, primitives, sides, nprimitives, + time_range, fp_to_meter, Tarad, Tref, register_paths, NULL, out_estimator); } +} - /* 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; +res_T +sdis_solve_boundary_green_function + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t primitives[], /* List of boundary primitives to handle */ + const enum sdis_side sides[], /* Per primitive side to consider */ + const size_t nprimitives, /* #primitives */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + struct sdis_green_function** green) +{ + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + return solve_boundary_2d(scn, nrealisations, primitives, sides, + nprimitives, NULL, fp_to_meter, Tarad, Tref, SDIS_HEAT_PATH_NONE, green, + NULL); + } else { + return solve_boundary_3d(scn, nrealisations, primitives, sides, + nprimitives, NULL, fp_to_meter, Tarad, Tref, SDIS_HEAT_PATH_NONE, green, + NULL); } +} - /* Create the estimator */ - res = estimator_create(scn->dev, SDIS_TEMPERATURE_ESTIMATOR, &estimator); - if(res != RES_OK) goto error; - - /* Here we go! Launch the Monte Carlo estimation */ - omp_set_num_threads((int)scn->dev->nthreads); - #pragma omp parallel for schedule(static) reduction(+:weight,sqr_weight,N) - for(irealisation = 0; irealisation < rcount; ++irealisation) { - res_T res_local; - double w = NaN; - const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - - if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ - - if(scene_is_2d(scn)) { - res_local = boundary_realisation_2d - (scn, rng, iprim, uv, time_range, side, fp_to_meter, Tarad, Tref, &w); - } else { - res_local = boundary_realisation_3d - (scn, rng, iprim, uv, time_range, side, fp_to_meter, Tarad, Tref, &w); - } - if(res_local != RES_OK) { - if(res_local != RES_BAD_OP) { - ATOMIC_SET(&res, res_local); - continue; - } - } else { - weight += w; - sqr_weight += w*w; - ++N; - } +res_T +sdis_solve_probe_boundary_flux + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t iprim, /* Identifier of the primitive on which the probe lies */ + const double uv[2], /* Parametric coordinates of the probe onto the primitve */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + struct sdis_estimator** out_estimator) +{ + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + return solve_probe_boundary_flux_2d(scn, nrealisations, iprim, uv, + time_range, fp_to_meter, Tarad, Tref, out_estimator); + } else { + return solve_probe_boundary_flux_3d(scn, nrealisations, iprim, uv, + time_range, fp_to_meter, Tarad, Tref, out_estimator); } - if(res != RES_OK) goto error; - - setup_estimator(estimator, nrealisations, N, weight, sqr_weight); +} -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_estimator) *out_estimator = estimator; - return (res_T)res; -error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; +res_T +sdis_solve_boundary_flux + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t primitives[], /* List of boundary primitives to handle */ + const size_t nprimitives, /* #primitives */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + struct sdis_estimator** out_estimator) +{ + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + return solve_boundary_flux_2d(scn, nrealisations, primitives, nprimitives, + time_range, fp_to_meter, Tarad, Tref, out_estimator); + } else { + return solve_boundary_flux_3d(scn, nrealisations, primitives, nprimitives, + time_range, fp_to_meter, Tarad, Tref, out_estimator); } - goto exit; } res_T @@ -425,7 +373,7 @@ sdis_solve_camera #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); - const struct sdis_medium* medium = NULL; + struct sdis_medium* medium = NULL; struct darray_accum* tiles = NULL; struct ssp_rng_proxy* rng_proxy = NULL; struct ssp_rng** rngs = NULL; @@ -547,244 +495,44 @@ error: } res_T -sdis_solve_boundary +sdis_solve_medium (struct sdis_scene* scn, const size_t nrealisations, /* #realisations */ - const size_t primitives[], /* List of boundary primitives to handle */ - const enum sdis_side sides[], /* Per primitive side to consider */ - const size_t nprimitives, /* #primitives */ + struct sdis_medium* medium, /* Medium to solve */ const double time_range[2], /* Observation time */ const double fp_to_meter, /* Scale from floating point units to meters */ const double Tarad, /* In Kelvin */ const double Tref, /* In Kelvin */ - struct sdis_estimator** out_estimator) + const int register_paths, /* Combination of enum sdis_heat_path_flag */ + struct sdis_estimator** estimator) { - res_T res = RES_OK; if(!scn) return RES_BAD_ARG; if(scene_is_2d(scn)) { - res = solve_boundary_2d(scn, nrealisations, primitives, sides, nprimitives, - time_range, fp_to_meter, Tarad, Tref, out_estimator); + return solve_medium_2d(scn, nrealisations, medium, time_range, fp_to_meter, + Tarad, Tref, register_paths, NULL, estimator); } else { - res = solve_boundary_3d(scn, nrealisations, primitives, sides, nprimitives, - time_range, fp_to_meter, Tarad, Tref, out_estimator); + return solve_medium_3d(scn, nrealisations, medium, time_range, fp_to_meter, + Tarad, Tref, register_paths, NULL, estimator); } - return res; } res_T -sdis_solve_probe_boundary_flux +sdis_solve_medium_green_function (struct sdis_scene* scn, const size_t nrealisations, /* #realisations */ - const size_t iprim, /* Identifier of the primitive on which the probe lies */ - const double uv[2], /* Parametric coordinates of the probe onto the primitve */ - const double time_range[2], /* Observation time */ + struct sdis_medium* medium, /* Medium to solve */ const double fp_to_meter, /* Scale from floating point units to meters */ const double Tarad, /* In Kelvin */ const double Tref, /* In Kelvin */ - struct sdis_estimator** out_estimator) + struct sdis_green_function** green) { - struct sdis_estimator* estimator = NULL; - 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; - double weight_t = 0, sqr_weight_t = 0; - double weight_fc = 0, sqr_weight_fc = 0; - double weight_fr = 0, sqr_weight_fr = 0; - double weight_f= 0, sqr_weight_f = 0; - const int64_t rcount = (int64_t)nrealisations; - int64_t irealisation = 0; - size_t N = 0; /* #realisations that do not fail */ - size_t i; - ATOMIC res = RES_OK; - - if(!scn || !nrealisations || nrealisations > INT64_MAX || !uv - || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] - || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) - || fp_to_meter <= 0 || Tref < 0 - || !out_estimator) { - res = RES_BAD_ARG; - goto error; - } - - /* Check the primitive identifier */ - 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; - } - - /* Check parametric coordinates */ - if(scene_is_2d(scn)) { - const double v = CLAMP(1.0 - uv[0], 0, 1); - if(uv[0] < 0 || uv[0] > 1 || !eq_eps(uv[0] + v, 1, 1.e-6)) { - log_err(scn->dev, - "%s: invalid parametric coordinates %g.\n" - "u + (1-u) must be equal to 1 with u [0, 1].\n", - FUNC_NAME, uv[0]); - res = RES_BAD_ARG; - goto error; - } - } else { - const double w = CLAMP(1 - uv[0] - uv[1], 0, 1); - if(uv[0] < 0 || uv[1] < 0 || uv[0] > 1 || uv[1] > 1 - || !eq_eps(w + uv[0] + uv[1], 1, 1.e-6)) { - log_err(scn->dev, - "%s: invalid parametric coordinates [%g, %g].\n" - "u + v + (1-u-v) must be equal to 1 with u and v in [0, 1].\n", - FUNC_NAME, uv[0], 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)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))) - { - res = RES_BAD_ARG; - goto error; - } - solid_side = (fmd->type == SDIS_SOLID) ? SDIS_FRONT : SDIS_BACK; - fluid_side = (fmd->type == SDIS_FLUID) ? SDIS_FRONT : SDIS_BACK; - - /* 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; - } - - /* Prebuild the interface fragment */ - if(scene_is_2d(scn)) { - res = interface_prebuild_fragment_2d(&frag, scn, (unsigned)iprim, - uv, fluid_side); - } else { - res = interface_prebuild_fragment_3d(&frag, scn, (unsigned)iprim, - uv, fluid_side); - } - - /* Create the estimator */ - res = estimator_create(scn->dev, SDIS_FLUX_ESTIMATOR, &estimator); - if(res != RES_OK) goto error; - - /* Here we go! Launch the Monte Carlo estimation */ - omp_set_num_threads((int)scn->dev->nthreads); - #pragma omp parallel for schedule(static) reduction(+:weight_t,sqr_weight_t,\ - weight_fc,sqr_weight_fc,weight_fr,sqr_weight_fr,weight_f,sqr_weight_f,N) - for(irealisation = 0; irealisation < rcount; ++irealisation) { - res_T res_local; - double T_brf[3] = { 0, 0, 0 }; - const int ithread = omp_get_thread_num(); - struct ssp_rng* rng = rngs[ithread]; - double time, epsilon, hc, hr; - - if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ - - /* Sample a time */ - time = sample_time(time_range, rng); - - /* Compute hr and hc */ - frag.time = time; - epsilon = interface_side_get_emissivity(interf, &frag); - hc = interface_get_convection_coef(interf, &frag); - hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; - - /* Fluid, Radiative and Solid temperatures */ - if(scene_is_2d(scn)) { - res_local = probe_flux_realisation_2d(scn, rng, iprim, uv, time, - solid_side, fp_to_meter, Tarad, Tref, hr>0, hc>0, T_brf); - } else { - res_local = probe_flux_realisation_3d(scn, rng, iprim, uv, time, - solid_side, fp_to_meter, Tarad, Tref, hr>0, hc>0, T_brf); - } - if(res_local != RES_OK) { - if(res_local != RES_BAD_OP) { - ATOMIC_SET(&res, res_local); - continue; - } - } else { - const double Tboundary = T_brf[0]; - const double Tradiative = T_brf[1]; - const double Tfluid = T_brf[2]; - const double w_conv = hc * (Tboundary - Tfluid); - const double w_rad = hr * (Tboundary - Tradiative); - const double w_total = w_conv + w_rad; - weight_t += Tboundary; - sqr_weight_t += Tboundary * Tboundary; - weight_fc += w_conv; - sqr_weight_fc += w_conv * w_conv; - weight_fr += w_rad; - sqr_weight_fr += w_rad * w_rad; - weight_f += w_total; - sqr_weight_f += w_total * w_total; - ++N; - } - } - if(res != RES_OK) goto error; - - setup_estimator(estimator, nrealisations, N, weight_t, sqr_weight_t); - setup_estimator_flux(estimator, FLUX_CONVECTIVE__, weight_fc, sqr_weight_fc); - setup_estimator_flux(estimator, FLUX_RADIATIVE__, weight_fr, sqr_weight_fr); - setup_estimator_flux(estimator, FLUX_TOTAL__, weight_f, sqr_weight_f); - -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_estimator) *out_estimator = estimator; - return (res_T)res; -error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } - goto exit; -} - -res_T -sdis_solve_boundary_flux - (struct sdis_scene* scn, - const size_t nrealisations, /* #realisations */ - const size_t primitives[], /* List of boundary primitives to handle */ - const size_t nprimitives, /* #primitives */ - const double time_range[2], /* Observation time */ - const double fp_to_meter, /* Scale from floating point units to meters */ - const double Tarad, /* In Kelvin */ - const double Tref, /* In Kelvin */ - struct sdis_estimator** out_estimator) -{ - res_T res = RES_OK; if(!scn) return RES_BAD_ARG; if(scene_is_2d(scn)) { - res = solve_boundary_flux_2d(scn, nrealisations, primitives, nprimitives, - time_range, fp_to_meter, Tarad, Tref, out_estimator); + return solve_medium_2d(scn, nrealisations, medium, NULL, fp_to_meter, Tarad, + Tref, SDIS_HEAT_PATH_NONE, green, NULL); } else { - res = solve_boundary_flux_3d(scn, nrealisations, primitives, nprimitives, - time_range, fp_to_meter, Tarad, Tref, out_estimator); + return solve_medium_3d(scn, nrealisations, medium, NULL, fp_to_meter, Tarad, + Tref, SDIS_HEAT_PATH_NONE, green, NULL); } - return res; } + diff --git a/src/sdis_solve_Xd.h b/src/sdis_solve_Xd.h @@ -1,2265 +0,0 @@ -/* Copyright (C) 2016-2019 |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_SOLVE_DIMENSION -#ifndef SDIS_SOLVE_XD_H -#define SDIS_SOLVE_XD_H - -#include "sdis_device_c.h" -#include "sdis_interface_c.h" -#include "sdis_medium_c.h" -#include "sdis_scene_c.h" - -#include <rsys/float2.h> -#include <rsys/float3.h> -#include <rsys/stretchy_array.h> - -#include <star/ssp.h> -#include <omp.h> - -/* Define a new result code from RES_BAD_OP saying that the bad operation is - * definitive, i.e. in the current state, the realisation will inevitably fail. - * It is thus unecessary to retry a specific section of the random walk */ -#define RES_BAD_OP_IRRECOVERABLE (-RES_BAD_OP) - -/* Empirical scale factor to apply to the upper bound of the ray range in order - * to handle numerical imprecisions */ -#define RAY_RANGE_MAX_SCALE 1.001f - -/* Emperical scale factor applied to the challenged reinjection distance. If - * the distance to reinject is less than this adjusted value, the solver - * switches from 2D reinjection scheme to the 1D reinjection scheme in order to - * avoid numerical issues. */ -#define REINJECT_DST_MIN_SCALE 0.125f - -#define BOLTZMANN_CONSTANT 5.6696e-8 /* W/m^2/K^4 */ - -struct rwalk_context { - double Tarad; /* Ambient radiative temperature */ - double Tref3; /* Reference temperature ^ 3 */ -}; - -/* Reflect the vector V wrt the normal N. By convention V points outward the - * surface. */ -static INLINE float* -reflect_2d(float res[2], const float V[2], const float N[2]) -{ - float tmp[2]; - float cos_V_N; - ASSERT(res && V && N); - ASSERT(f2_is_normalized(V) && f2_is_normalized(N)); - cos_V_N = f2_dot(V, N); - f2_mulf(tmp, N, 2*cos_V_N); - f2_sub(res, tmp, V); - return res; -} - -/* Reflect the vector V wrt the normal N. By convention V points outward the - * surface. */ -static INLINE float* -reflect_3d(float res[3], const float V[3], const float N[3]) -{ - float tmp[3]; - float cos_V_N; - ASSERT(res && V && N); - ASSERT(f3_is_normalized(V) && f3_is_normalized(N)); - cos_V_N = f3_dot(V, N); - f3_mulf(tmp, N, 2*cos_V_N); - f3_sub(res, tmp, V); - return res; -} - -static INLINE void -setup_estimator - (struct sdis_estimator* estimator, - const size_t nrealisations, - const size_t nsuccesses, - const double accum_weights, - const double accum_sqr_weights) -{ - ASSERT(estimator && nrealisations && nsuccesses); - estimator->nrealisations = nsuccesses; - estimator->nfailures = nrealisations - nsuccesses; - estimator->temperature.E = accum_weights / (double)nsuccesses; - estimator->temperature.V = - accum_sqr_weights / (double)nsuccesses - - estimator->temperature.E * estimator->temperature.E; - estimator->temperature.V = MMAX(estimator->temperature.V, 0); - estimator->temperature.SE = - sqrt(estimator->temperature.V / (double)nsuccesses); -} - -static INLINE void -setup_estimator_flux - (struct sdis_estimator* estimator, - const enum flux_names name, - const double accum_weights, - const double accum_sqr_weights) -{ - ASSERT(estimator && (unsigned)name < FLUX_NAMES_COUNT__ && estimator->fluxes); - ASSERT(estimator->nrealisations); - estimator->fluxes[name].E = accum_weights / (double)estimator->nrealisations; - estimator->fluxes[name].V = - accum_sqr_weights / (double)estimator->nrealisations - - estimator->fluxes[name].E * estimator->fluxes[name].E; - estimator->fluxes[name].V = MMAX(estimator->fluxes[name].V, 0); - estimator->fluxes[name].SE = - sqrt(estimator->fluxes[name].V / (double)estimator->nrealisations); -} - -static INLINE double -sample_time - (const double time_range[2], - struct ssp_rng* rng) -{ - ASSERT(time_range && time_range[0] >= 0 && time_range[1] >= time_range[0]); - ASSERT(rng); - if(time_range[0] == time_range[1]) return time_range[0]; - return ssp_rng_uniform_double(rng, time_range[0], time_range[1]); -} - -#endif /* SDIS_SOLVE_XD_H */ -#else - -#if (SDIS_SOLVE_DIMENSION == 2) - #include <rsys/double2.h> - #include <rsys/float2.h> - #include <star/s2d.h> -#elif (SDIS_SOLVE_DIMENSION == 3) - #include <rsys/double2.h> - #include <rsys/double3.h> - #include <rsys/float3.h> - #include <star/s3d.h> -#else - #error "Invalid SDIS_SOLVE_DIMENSION value." -#endif - -/* Syntactic sugar */ -#define DIM SDIS_SOLVE_DIMENSION - -/* Star-XD macros generic to SDIS_SOLVE_DIMENSION */ -#define sXd(Name) CONCAT(CONCAT(CONCAT(s, DIM), d_), Name) -#define SXD_HIT_NONE CONCAT(CONCAT(S,DIM), D_HIT_NONE) -#define SXD_HIT_NULL CONCAT(CONCAT(S,DIM), D_HIT_NULL) -#define SXD_HIT_NULL__ CONCAT(CONCAT(S, DIM), D_HIT_NULL__) -#define SXD_POSITION CONCAT(CONCAT(S, DIM), D_POSITION) -#define SXD_GEOMETRY_NORMAL CONCAT(CONCAT(S, DIM), D_GEOMETRY_NORMAL) -#define SXD_GEOMETRY_NORMAL CONCAT(CONCAT(S, DIM), D_GEOMETRY_NORMAL) -#define SXD_VERTEX_DATA_NULL CONCAT(CONCAT(S, DIM), D_VERTEX_DATA_NULL) -#define SXD CONCAT(CONCAT(S, DIM), D) -#define SXD_FLOAT2 CONCAT(CONCAT(S, DIM), D_FLOAT2) -#define SXD_FLOAT3 CONCAT(CONCAT(S, DIM), D_FLOAT3) -#define SXD_SAMPLE CONCAT(CONCAT(S, DIM), D_SAMPLE) -#define sXd_dev CONCAT(CONCAT(s, DIM), d) - -/* Vector macros generic to SDIS_SOLVE_DIMENSION */ -#define dX(Func) CONCAT(CONCAT(CONCAT(d, DIM), _), Func) -#define fX(Func) CONCAT(CONCAT(CONCAT(f, DIM), _), Func) -#define fX_set_dX CONCAT(CONCAT(CONCAT(f, DIM), _set_d), DIM) -#define dX_set_fX CONCAT(CONCAT(CONCAT(d, DIM), _set_f), DIM) - -/* Macro making generic its subimitted name to SDIS_SOLVE_DIMENSION */ -#define XD(Name) CONCAT(CONCAT(CONCAT(Name, _), DIM), d) - -/* Current state of the random walk */ -struct XD(rwalk) { - struct sdis_rwalk_vertex vtx; /* Position and time of the Random walk */ - const struct sdis_medium* mdm; /* Medium in which the random walk lies */ - struct sXd(hit) hit; /* Hit of the random walk */ - enum sdis_side hit_side; -}; -static const struct XD(rwalk) XD(RWALK_NULL) = { - SDIS_RWALK_VERTEX_NULL__, NULL, SXD_HIT_NULL__, SDIS_SIDE_NULL__ -}; - -struct XD(boundary_context) { - struct sXd(scene_view)* view; - const size_t* primitives; -}; -static const struct XD(boundary_context) XD(BOUNDARY_CONTEXT_NULL) = { - NULL, NULL -}; - -struct XD(temperature) { - res_T (*func)/* Next function to invoke in order to compute the temperature */ - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* temp); - double value; /* Current value of the temperature */ - int done; -}; -static const struct XD(temperature) XD(TEMPERATURE_NULL) = { NULL, 0, 0 }; - -static res_T -XD(boundary_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T); - -static res_T -XD(solid_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T); - -static res_T -XD(fluid_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T); - -static res_T -XD(radiative_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T); - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static INLINE void -XD(boundary_get_indices)(const unsigned iprim, unsigned ids[DIM], void* context) -{ - unsigned i; - (void)context; - ASSERT(ids); - FOR_EACH(i, 0, DIM) ids[i] = iprim*DIM + i; -} - -static INLINE void -XD(boundary_get_position)(const unsigned ivert, float pos[DIM], void* context) -{ - struct XD(boundary_context)* ctx = context; - struct sXd(primitive) prim; - struct sXd(attrib) attr; - const unsigned iprim_id = ivert / DIM; - const unsigned iprim_vert = ivert % DIM; - unsigned iprim; - ASSERT(pos && context); - - iprim = (unsigned)ctx->primitives[iprim_id]; - SXD(scene_view_get_primitive(ctx->view, iprim, &prim)); -#if DIM == 2 - s2d_segment_get_vertex_attrib(&prim, iprim_vert, S2D_POSITION, &attr); - ASSERT(attr.type == S2D_FLOAT2); -#else - s3d_triangle_get_vertex_attrib(&prim, iprim_vert, S3D_POSITION, &attr); - ASSERT(attr.type == S3D_FLOAT3); -#endif - fX(set)(pos, attr.value); -} - -static FINLINE void -XD(move_pos)(double pos[DIM], const float dir[DIM], const float delta) -{ - ASSERT(pos && dir); - pos[0] += dir[0] * delta; - pos[1] += dir[1] * delta; -#if(SDIS_SOLVE_DIMENSION == 3) - pos[2] += dir[2] * delta; -#endif -} - -static FINLINE 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 -} - -/* 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_SOLVE_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(trace_radiative_path) - (struct sdis_scene* scn, - const float ray_dir[3], - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - /* The radiative random walk is always perform in 3D. In 2D, the geometry are - * assumed to be extruded to the infinty along the Z dimension. */ - float N[3] = {0, 0, 0}; - float dir[3] = {0, 0, 0}; - res_T res = RES_OK; - - ASSERT(scn && ray_dir && fp_to_meter > 0 && ctx && rwalk && rng && T); - (void)fp_to_meter; - - f3_set(dir, ray_dir); - - /* Launch the radiative random walk */ - for(;;) { - const struct sdis_interface* interf = NULL; - struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; - const struct sdis_medium* chk_mdm = NULL; - double alpha; - double epsilon; - double r; - float pos[DIM]; - const float range[2] = { 0, FLT_MAX }; - - fX_set_dX(pos, rwalk->vtx.P); - - /* Trace the radiative ray */ -#if (SDIS_SOLVE_DIMENSION == 2) - SXD(scene_view_trace_ray_3d - (scn->sXd(view), pos, dir, range, &rwalk->hit, &rwalk->hit)); -#else - SXD(scene_view_trace_ray - (scn->sXd(view), pos, dir, range, &rwalk->hit, &rwalk->hit)); -#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; - T->done = 1; - break; - } else { - log_err(scn->dev, - "%s: the random walk reaches an invalid ambient radiative temperature " - "of `%gK' at position `%g %g %g'. This may be due to numerical " - "inaccuracies or to inconsistency in the simulated system (eg: " - "unclosed geometry). For systems where the random walks can reach " - "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, - SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP; - goto error; - } - } - - /* Define the hit side */ - rwalk->hit_side = fX(dot)(dir, rwalk->hit.normal) < 0 - ? SDIS_FRONT : SDIS_BACK; - - /* Move the random walk to the hit position */ - XD(move_pos)(rwalk->vtx.P, dir, rwalk->hit.distance); - - /* Fetch the new interface and setup the hit fragment */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); - - /* Fetch the interface emissivity */ - epsilon = interface_side_get_emissivity(interf, &frag); - if(epsilon > 1 || epsilon < 0) { - log_err(scn->dev, - "%s: invalid overall emissivity `%g' at position `%g %g %g'.\n", - FUNC_NAME, epsilon, SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP; - goto error; - } - - /* Switch in boundary temperature ? */ - r = ssp_rng_canonical(rng); - if(r < epsilon) { - T->func = XD(boundary_temperature); - rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ - break; - } - - /* Normalize the normal of the interface and ensure that it points toward the - * current medium */ - fX(normalize)(N, rwalk->hit.normal); - if(rwalk->hit_side == SDIS_BACK){ - chk_mdm = interf->medium_back; - fX(minus)(N, N); - } else { - chk_mdm = interf->medium_front; - } - - if(chk_mdm != rwalk->mdm) { - /* To ease the setting of models, the external enclosure is allowed to be - * incoherent regarding media. - * Here a radiative path is allowed to join 2 different fluids. */ - const int outside = scene_is_outside - (scn, rwalk->hit_side, rwalk->hit.prim.prim_id); - if(outside && chk_mdm->type == SDIS_FLUID) { - rwalk->mdm = chk_mdm; - } else { - log_err(scn->dev, "%s: inconsistent medium definition at `%g %g %g'.\n", - FUNC_NAME, SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP; - goto error; - } - } - alpha = interface_side_get_specular_fraction(interf, &frag); - r = ssp_rng_canonical(rng); - if(r < alpha) { /* Sample specular part */ - reflect_3d(dir, f3_minus(dir, dir), N); - } else { /* Sample diffuse part */ - ssp_ran_hemisphere_cos_float(rng, N, dir, NULL); - } - } - -exit: - return res; -error: - goto exit; -} - -res_T -XD(radiative_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - /* The radiative random walk is always perform in 3D. In 2D, the geometry are - * assumed to be extruded to the infinty along the Z dimension. */ - float N[3] = {0, 0, 0}; - float dir[3] = {0, 0, 0}; - res_T res = RES_OK; - - ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); - ASSERT(!SXD_HIT_NONE(&rwalk->hit)); - (void)fp_to_meter; - - /* Normalize the normal of the interface and ensure that it points toward the - * current medium */ - fX(normalize(N, rwalk->hit.normal)); - if(rwalk->hit_side == SDIS_BACK) { - fX(minus(N, N)); - } - - /* Cosine weighted sampling of a direction around the surface normal */ - ssp_ran_hemisphere_cos_float(rng, N, dir, NULL); - - /* Launch the radiative random walk */ - res = XD(trace_radiative_path)(scn, dir, fp_to_meter, ctx, rwalk, rng, T); - if(res != RES_OK) goto error; - -exit: - return res; -error: - goto exit; -} - -res_T -XD(fluid_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - struct sXd(attrib) attr_P, attr_N; - struct sdis_interface_fragment frag; - 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; -#if DIM == 2 - float st; -#else - float st[2]; -#endif - (void)rng, (void)fp_to_meter, (void)ctx; - ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); - ASSERT(rwalk->mdm->type == SDIS_FLUID); - - tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); - if(tmp >= 0) { /* T is known. */ - T->value += tmp; - T->done = 1; - return RES_OK; - } - - if(SXD_HIT_NONE(&rwalk->hit)) { /* The path begins in the fluid */ - const float range[2] = {0, FLT_MAX}; - float dir[DIM] = {0}; - float org[DIM]; - - dir[DIM-1] = 1; - fX_set_dX(org, rwalk->vtx.P); - - /* 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)); - rwalk->hit_side = fX(dot)(rwalk->hit.normal, dir) < 0 ? SDIS_FRONT : SDIS_BACK; - - 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)); - return RES_BAD_OP; - } - } - - /* 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 */ - ASSERT(interf->medium_front != interf->medium_back); - if(rwalk->mdm == interf->medium_front) { - enc_id = enc_ids[0]; - ASSERT(rwalk->hit_side == SDIS_FRONT); - } else { - ASSERT(rwalk->mdm == interf->medium_back); - enc_id = enc_ids[1]; - ASSERT(rwalk->hit_side == SDIS_BACK); - } - - /* Fetch the enclosure data */ - enc = scene_get_enclosure(scn, enc_id); - if(!enc) { - /* The possibility for a fluid enclosure to be unregistred is that it is - * 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", - FUNC_NAME); - return RES_BAD_ARG; - } - - /* 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(SXD_HIT_NONE(&rwalk->hit)); - 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; - return RES_OK; - } - - /* At t=0, the initial condition should have been reached. */ - log_err(scn->dev, - "%s: undefined initial condition. " - "Time is 0 but the temperature remains unknown.\n", - FUNC_NAME); - return RES_BAD_OP; - } - - /* A trick to force first r test result. */ - r = 1; - - /* Sample time until init condition is reached or a true convection occurs. */ - for(;;) { - struct sXd(primitive) prim; - /* Setup the fragment of the interface. */ - XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); - - /* Fetch hc. */ - hc = interface_get_convection_coef(interf, &frag); - if(hc > enc->hc_upper_bound) { - log_err(scn->dev, - "%s: hc (%g) exceeds its provided upper bound (%g) at %g %g %g.\n", - FUNC_NAME, hc, enc->hc_upper_bound, SPLIT3(rwalk->vtx.P)); - return RES_BAD_OP; - } - - if(r < hc / enc->hc_upper_bound) { - /* True convection. Always true if hc == bound. */ - break; - } - - /* Fetch other physical properties. */ - cp = fluid_get_calorific_capacity(rwalk->mdm, &rwalk->vtx); - rho = fluid_get_volumic_mass(rwalk->mdm, &rwalk->vtx); - - /* Sample the time using the upper bound. */ - if(rwalk->vtx.time != INF) { - double mu, tau, t0; - mu = enc->hc_upper_bound / (rho * cp) * enc->S_over_V; - tau = ssp_ran_exp(rng, mu); - t0 = fluid_get_t0(rwalk->mdm); - rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); - 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; - return RES_OK; - } - /* 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); - return RES_BAD_OP; - } - } - - /* Uniformly sample the enclosure. */ -#if DIM == 2 - SXD(scene_view_sample - (enc->sXd(view), - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - &prim, &rwalk->hit.u)); - st = rwalk->hit.u; -#else - SXD(scene_view_sample - (enc->sXd(view), - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - &prim, rwalk->hit.uv)); - f2_set(st, rwalk->hit.uv); -#endif - /* Map the sampled primitive id from the enclosure space to the scene - * space. Note that the overall scene has only one shape. As a consequence - * neither the geom_id nor the inst_id needs to be updated */ - rwalk->hit.prim.prim_id = enclosure_local2global_prim_id(enc, prim.prim_id); - - SXD(primitive_get_attrib(&rwalk->hit.prim, SXD_POSITION, st, &attr_P)); - SXD(primitive_get_attrib(&rwalk->hit.prim, SXD_GEOMETRY_NORMAL, st, &attr_N)); - dX_set_fX(rwalk->vtx.P, attr_P.value); - fX(set)(rwalk->hit.normal, attr_N.value); - - /* Fetch the interface of the sampled point. */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - if(rwalk->mdm == interf->medium_front) { - rwalk->hit_side = SDIS_FRONT; - } else if(rwalk->mdm == interf->medium_back) { - rwalk->hit_side = SDIS_BACK; - } else { - FATAL("Unexpected fluid interface.\n"); - } - - /* Renew r for next loop. */ - r = ssp_rng_canonical_float(rng); - } - - rwalk->hit.distance = 0; - T->func = XD(boundary_temperature); - rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ - return RES_OK; -} - -static void -XD(solid_solid_boundary_temperature) - (const struct sdis_scene* scn, - const double fp_to_meter, - 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, hit2, hit3; - struct sXd(hit)* hit; - const struct sdis_interface* interf = NULL; - const struct sdis_medium* solid_front = NULL; - const struct sdis_medium* solid_back = NULL; - const struct sdis_medium* mdm; - double lambda_front, lambda_back; - double delta_front, delta_back; - double delta_boundary_front, delta_boundary_back; - double delta_boundary; - double reinject_dst_front, reinject_dst_back; - double reinject_dst; - double proba; - double tmp; - double r; - double power; - float range0[2], range1[2]; - float dir0[DIM], dir1[DIM], dir2[DIM], dir3[DIM]; - float* dir; - float pos[DIM]; - int dim = DIM; - ASSERT(scn && fp_to_meter > 0 && 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); - - /* 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); - - /* 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); - - /* Trace the sampled directions on both sides of the interface to adjust the - * reinjection distance of the random walk . */ - fX_set_dX(pos, rwalk->vtx.P); - f2(range0, 0, (float)delta_boundary_front*RAY_RANGE_MAX_SCALE); - f2(range1, 0, (float)delta_boundary_back *RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range0, &rwalk->hit, &hit0)); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir1, range1, &rwalk->hit, &hit1)); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir2, range0, &rwalk->hit, &hit2)); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir3, range1, &rwalk->hit, &hit3)); - - /* Adjust the reinjection distance */ - reinject_dst_front = MMIN(MMIN(delta_boundary_front, hit0.distance), hit2.distance); - reinject_dst_back = MMIN(MMIN(delta_boundary_back, hit1.distance), hit3.distance); - - /* 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. */ - r = ssp_rng_canonical(rng); - proba = (lambda_front/reinject_dst_front) - / (lambda_front/reinject_dst_front + lambda_back/reinject_dst_back); - if(r < proba) { /* Reinject in front */ - dir = dir0; - hit = &hit0; - mdm = solid_front; - reinject_dst = reinject_dst_front; - delta_boundary = delta_boundary_front; - } else { /* Reinject in back */ - dir = dir1; - hit = &hit1; - mdm = solid_back; - reinject_dst = reinject_dst_back; - delta_boundary = delta_boundary_back; - } - - /* Switch in 1D reinjection scheme */ - if(reinject_dst < delta_boundary * REINJECT_DST_MIN_SCALE) { - if(dir == dir0) { - fX(set)(dir, rwalk->hit.normal); - } else { - fX(minus)(dir, rwalk->hit.normal); - } - - f2(range0, 0, (float)delta_boundary*RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir, range0, &rwalk->hit, hit)); - reinject_dst = MMIN(delta_boundary, hit->distance), - dim = 1; - - /* Hit something in 1D. Arbitrarily move the random walk to 0.5 of the hit - * distance */ - if(!SXD_HIT_NONE(hit)) { - reinject_dst *= 0.5; - *hit = SXD_HIT_NULL; - } - } - - /* 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 * fp_to_meter; - const double lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); - tmp = power * delta_in_meter * delta_in_meter / (2.0 * dim * lambda); - T->value += tmp; - } - - /* Reinject */ - XD(move_pos)(rwalk->vtx.P, dir, (float)reinject_dst); - if(eq_epsf(hit->distance, (float)reinject_dst, 1.e-4f)) { - T->func = XD(boundary_temperature); - rwalk->mdm = NULL; - rwalk->hit = *hit; - rwalk->hit_side = fX(dot)(hit->normal, dir) < 0 ? SDIS_FRONT : SDIS_BACK; - } else { - T->func = XD(solid_temperature); - rwalk->mdm = mdm; - rwalk->hit = SXD_HIT_NULL; - rwalk->hit_side = SDIS_SIDE_NULL__; - } -} - -static void -XD(solid_fluid_boundary_temperature) - (const struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - const struct sdis_interface_fragment* frag, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - const struct sdis_interface* interf = NULL; - const struct sdis_medium* mdm_front = NULL; - const struct sdis_medium* mdm_back = NULL; - const struct sdis_medium* solid = NULL; - const struct sdis_medium* fluid = NULL; - struct sXd(hit) hit0 = SXD_HIT_NULL; - struct sXd(hit) hit1 = 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 pos[DIM]; - float dir0[DIM], dir1[DIM]; - float range[2]; - int dim = DIM; - - ASSERT(scn && fp_to_meter > 0 && 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; - - /* 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); - } - - /* Trace dir0/dir1 to adjust the reinjection distance */ - fX_set_dX(pos, rwalk->vtx.P); - f2(range, 0, (float)delta_boundary*RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir1, range, &rwalk->hit, &hit1)); - - /* Adjust the delta boundary to the hit distance */ - tmp = MMIN(MMIN(delta_boundary, hit0.distance), hit1.distance); - - if(tmp >= delta_boundary * REINJECT_DST_MIN_SCALE) { - delta_boundary = tmp; - /* Define the orthogonal dst from the reinjection pos to the interface */ - delta = delta_boundary / sqrt(DIM); - } else { /* Switch in 1D reinjection scheme. */ - fX(set)(dir0, rwalk->hit.normal); - if(solid == mdm_back) fX(minus)(dir0, dir0); - f2(range, 0, (float)delta*RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); - delta_boundary = MMIN(hit0.distance, delta); - - /* Hit something in 1D. Arbitrarily move the random walk to 0.5 of the hit - * distance in order to avoid infinite bounces for parallel plane */ - if(!SXD_HIT_NONE(&hit0)) { - delta_boundary *= 0.5; - hit0 = SXD_HIT_NULL; - } - - delta = delta_boundary; - dim = 1; - } - - /* 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*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_temperature); - rwalk->mdm = fluid; - rwalk->hit_side = rwalk->mdm == mdm_front ? SDIS_FRONT : SDIS_BACK; - } else if(r < fluid_proba + radia_proba) { /* Switch to fluid random walk */ - T->func = XD(fluid_temperature); - 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 = delta_boundary * fp_to_meter; - tmp = power * delta_in_meter * delta_in_meter / (2.0 * dim * lambda); - T->value += tmp; - } - - /* Reinject */ - XD(move_pos)(rwalk->vtx.P, dir0, (float)delta_boundary); - if(eq_epsf(hit0.distance, (float)delta_boundary, 1.e-4f)) { - T->func = XD(boundary_temperature); - rwalk->mdm = NULL; - rwalk->hit = hit0; - rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; - } else { - T->func = XD(solid_temperature); - rwalk->mdm = solid; - rwalk->hit = SXD_HIT_NULL; - rwalk->hit_side = SDIS_SIDE_NULL__; - } - } -} - -static void -XD(solid_boundary_with_flux_temperature) - (const struct sdis_scene* scn, - const double fp_to_meter, - 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) -{ - const struct sdis_interface* interf = NULL; - const struct sdis_medium* mdm = NULL; - double lambda; - double delta; - double delta_boundary; - double delta_in_meter; - double power; - double tmp; - struct sXd(hit) hit0; - struct sXd(hit) hit1; - float pos[DIM]; - float dir0[DIM]; - float dir1[DIM]; - float range[2]; - int dim = DIM; - 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); - - /* 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); - } - - /* Trace dir0/dir1 to adjust the reinjection distance wrt the geometry */ - fX_set_dX(pos, rwalk->vtx.P); - f2(range, 0, (float)delta_boundary*RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir1, range, &rwalk->hit, &hit1)); - - /* Adjust the delta boundary to the hit distance */ - tmp = MMIN(MMIN(delta_boundary, hit0.distance), hit1.distance); - - if(tmp >= delta_boundary * REINJECT_DST_MIN_SCALE) { - delta_boundary = tmp; - /* Define the orthogonal dst from the reinjection pos to the interface */ - delta = delta_boundary / sqrt(DIM); - } else { /* Switch in 1D reinjection scheme. */ - fX(set)(dir0, rwalk->hit.normal); - if(frag->side == SDIS_BACK) fX(minus)(dir0, dir0); - f2(range, 0, (float)delta*RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dir0, range, &rwalk->hit, &hit0)); - delta_boundary = MMIN(hit0.distance, delta_boundary); - - /* Hit something in 1D. Arbitrarily move the random walk to 0.5 of the hit - * distance in order to avoid infinite bounces for parallel plane */ - if(!SXD_HIT_NONE(&hit0)) { - delta_boundary *= 0.5; - hit0 = SXD_HIT_NULL; - } - - delta = delta_boundary; - dim = 1; - } - - /* Handle the flux */ - delta_in_meter = delta*fp_to_meter; - T->value += phi * delta_in_meter / lambda; - - /* Handle the volumic power */ - power = solid_get_volumic_power(mdm, &rwalk->vtx); - if(power != SDIS_VOLUMIC_POWER_NONE) { - delta_in_meter = delta_boundary * fp_to_meter; - tmp = power * delta_in_meter * delta_in_meter / (2.0 * dim * lambda); - T->value += tmp; - } - - /* Reinject into the solid */ - XD(move_pos)(rwalk->vtx.P, dir0, (float)delta_boundary); - if(eq_epsf(hit0.distance, (float)delta_boundary, 1.e-4f)) { - T->func = XD(boundary_temperature); - rwalk->mdm = NULL; - rwalk->hit = hit0; - rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; - } else { - T->func = XD(solid_temperature); - rwalk->mdm = mdm; - rwalk->hit = SXD_HIT_NULL; - rwalk->hit_side = SDIS_SIDE_NULL__; - } -} - -res_T -XD(boundary_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; - const struct sdis_interface* interf = NULL; - const struct sdis_medium* mdm_front = NULL; - const struct sdis_medium* mdm_back = NULL; - const struct sdis_medium* mdm = NULL; - double tmp; - ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); - ASSERT(rwalk->mdm == NULL); - ASSERT(!SXD_HIT_NONE(&rwalk->hit)); - - XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); - - fX(normalize)(rwalk->hit.normal, rwalk->hit.normal); - - /* Retrieve the current interface */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - - /* Check if the boundary temperature is known */ - tmp = interface_side_get_temperature(interf, &frag); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - return RES_OK; - } - - /* Check if the boundary flux is known. Note that currently, only solid media - * can have a flux as limit condition */ - mdm = interface_get_medium(interf, frag.side); - if(sdis_medium_get_type(mdm) == SDIS_SOLID ) { - const double phi = interface_side_get_flux(interf, &frag); - if(phi != SDIS_FLUX_NONE) { - XD(solid_boundary_with_flux_temperature) - (scn, fp_to_meter, ctx, &frag, phi, rwalk, rng, T); - return RES_OK; - } - } - - mdm_front = interface_get_medium(interf, SDIS_FRONT); - mdm_back = interface_get_medium(interf, SDIS_BACK); - - if(mdm_front->type == mdm_back->type) { - XD(solid_solid_boundary_temperature) - (scn, fp_to_meter, ctx, &frag, rwalk, rng, T); - } else { - XD(solid_fluid_boundary_temperature) - (scn, fp_to_meter, ctx, &frag, rwalk, rng, T); - } - return RES_OK; -} - -#ifdef COMPILER_CL -#pragma warning(push) -#pragma warning(disable : 4701) -/* potentially uninitialized local variable 'info' used - * - * For warning numbers in the range 4700-4999, which are the ones associated - * with code generation, the state of the warning in effect when the compiler - * encounters the open curly brace of a function will be in effect for the rest - * of the function. Using the warning pragma in the function to change the - * state of a warning that has a number larger than 4699 will only take effect - * after the end of the function. The following example shows the correct - * placement of warning pragmas to disable a code-generation warning message, - * and then to restore it. */ -#endif -res_T -XD(solid_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - double position_start[DIM]; - const struct sdis_medium* mdm; - ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T); - ASSERT(rwalk->mdm->type == SDIS_SOLID); - (void)ctx; - - /* Check the random walk consistency */ - CHK(scene_get_medium(scn, rwalk->vtx.P, NULL, &mdm) == RES_OK); - if(mdm != rwalk->mdm) { - log_err(scn->dev, "%s: invalid solid random walk. " - "Unexpected medium at {%g, %g, %g}.\n", - FUNC_NAME, SPLIT3(rwalk->vtx.P)); - return RES_BAD_OP_IRRECOVERABLE; - } - /* Save the submitted position */ - dX(set)(position_start, rwalk->vtx.P); - - do { /* Solid random walk */ - struct get_medium_info info; - struct sXd(hit) hit0, hit1; - double lambda; /* Thermal conductivity */ - double rho; /* Volumic mass */ - double cp; /* Calorific capacity */ - double tmp; - double power; - float delta, delta_solid; /* Random walk numerical parameter */ - float range[2]; - float dir0[DIM], dir1[DIM]; - float org[DIM]; - - /* Check the limit condition */ - tmp = solid_get_temperature(mdm, &rwalk->vtx); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - return RES_OK; - } - - /* Fetch solid properties */ - delta_solid = (float)solid_get_delta(mdm, &rwalk->vtx); - lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); - rho = solid_get_volumic_mass(mdm, &rwalk->vtx); - cp = solid_get_calorific_capacity(mdm, &rwalk->vtx); - power = solid_get_volumic_power(mdm, &rwalk->vtx); - -#if (SDIS_SOLVE_DIMENSION == 2) - /* Sample a direction around 2PI */ - ssp_ran_circle_uniform_float(rng, dir0, NULL); -#else - /* Sample a direction around 4PI */ - ssp_ran_sphere_uniform_float(rng, dir0, NULL); -#endif - - /* Trace a ray along the sampled direction and its opposite to check if a - * surface is hit in [0, delta_solid]. */ - fX_set_dX(org, rwalk->vtx.P); - fX(minus)(dir1, dir0); - hit0 = hit1 = SXD_HIT_NULL; - range[0] = 0.f, range[1] = delta_solid*RAY_RANGE_MAX_SCALE; - SXD(scene_view_trace_ray(scn->sXd(view), org, dir0, range, NULL, &hit0)); - SXD(scene_view_trace_ray(scn->sXd(view), org, dir1, range, NULL, &hit1)); - - if(SXD_HIT_NONE(&hit0) && SXD_HIT_NONE(&hit1)) { - /* Hit nothing: move along dir0 of the original delta */ - delta = delta_solid; - - /* Add the volumic power density to the measured temperature */ - if(power != SDIS_VOLUMIC_POWER_NONE) { - const double delta_in_meter = delta * fp_to_meter; - tmp = power * delta_in_meter * delta_in_meter / (2.0 * DIM * lambda); - T->value += tmp; - } - } else { - /* Hit something: move along dir0 of the minimum hit distance */ - delta = MMIN(hit0.distance, hit1.distance); - - /* Add the volumic power density to the measured temperature */ - if(power != SDIS_VOLUMIC_POWER_NONE) { - const double delta_s_in_meter = delta_solid * 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 * fp_to_meter; - - /* The regular power term at wall */ - tmp = power * h_in_meter * h_in_meter / (2.0 * lambda); - - /* Add the power corrective term */ - if(h < delta_solid) { - const double sin_a = h / delta_solid; -#if DIM==2 - /* tmp1 = sin(2a) / (PI - 2*a) */ - const double tmp1 = sin_a * sqrt(1 - sin_a*sin_a)/acos(sin_a); - tmp += -(power*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 += (power*delta_s_in_meter*delta_s_in_meter)/(6*lambda) * tmp1; -#endif - - } else if(h == delta_solid) { - tmp += -(delta_s_in_meter*delta_s_in_meter*power)/(2.0*DIM*lambda); - } - T->value += tmp; - } - } - - /* Sample the time */ - if(rwalk->vtx.time != INF) { - double tau, mu, t0; - mu = (2*DIM*lambda) / (rho*cp*delta*fp_to_meter*delta*fp_to_meter); - tau = ssp_ran_exp(rng, mu); - t0 = solid_get_t0(rwalk->mdm); - rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); - if(rwalk->vtx.time == t0) { - /* Check the initial condition */ - tmp = solid_get_temperature(mdm, &rwalk->vtx); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - return RES_OK; - } - /* The initial condition should have been reached */ - log_err(scn->dev, - "%s: undefined initial condition. " - "The time is %f but the temperature remains unknown.\n", - FUNC_NAME, t0); - return RES_BAD_OP; - } - } - - /* Define if the random walk hits something along dir0 */ - if(hit0.distance > delta) { - rwalk->hit = SXD_HIT_NULL; - rwalk->hit_side = SDIS_SIDE_NULL__; - } else { - rwalk->hit = hit0; - rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; - } - - /* Update the random walk position */ - XD(move_pos)(rwalk->vtx.P, dir0, delta); - - /* Fetch the current medium */ - if(SXD_HIT_NONE(&rwalk->hit)) { - CHK(scene_get_medium(scn, rwalk->vtx.P, &info, &mdm) == RES_OK); - } else { - const struct sdis_interface* interf; - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - mdm = interface_get_medium(interf, rwalk->hit_side); - } - - /* Check random walk consistency */ - if(mdm != rwalk->mdm) { - log_err(scn->dev, - "%s: inconsistent medium during the solid random walk.\n", FUNC_NAME); -#if DIM == 2 - #define VEC_STR "%g %g" - #define VEC_SPLIT SPLIT2 -#else - #define VEC_STR "%g %g %g" - #define VEC_SPLIT SPLIT3 -#endif - log_err(scn->dev, - " start position: " VEC_STR "; current position: " VEC_STR "\n", - VEC_SPLIT(position_start), VEC_SPLIT(rwalk->vtx.P)); - if(SXD_HIT_NONE(&rwalk->hit)) { - float hit_pos[DIM]; - fX(mulf)(hit_pos, info.ray_dir, info.XD(hit).distance); - fX(add)(hit_pos, info.ray_org, hit_pos); - log_err(scn->dev, " ray org: " VEC_STR "; ray dir: " VEC_STR "\n", - VEC_SPLIT(info.ray_org), VEC_SPLIT(info.ray_dir)); - log_err(scn->dev, " targeted point: " VEC_STR "\n", - VEC_SPLIT(info.pos_tgt)); - log_err(scn->dev, " hit pos: " VEC_STR "\n", VEC_SPLIT(hit_pos)); - } -#undef VEC_STR -#undef VEC_SPLIT - return RES_BAD_OP; - } - - /* Keep going while the solid random walk does not hit an interface */ - } while(SXD_HIT_NONE(&rwalk->hit)); - - T->func = XD(boundary_temperature); - rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ - return RES_OK; -} -#ifdef COMPILER_CL -#pragma warning(pop) -#endif - -static res_T -XD(compute_temperature) - (struct sdis_scene* scn, - const double fp_to_meter, - const struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ -#ifndef NDEBUG - struct entry { - struct XD(temperature) temperature; - struct XD(rwalk) rwalk; - }* stack = NULL; - size_t istack = 0; -#endif - const size_t max_fails = 10; - res_T res = RES_OK; - ASSERT(scn && fp_to_meter > 0 && ctx && rwalk && rng && T); - - do { - /* Save the current random walk state */ - const struct XD(rwalk) rwalk_bkp = *rwalk; - const struct XD(temperature) T_bkp = *T; - - size_t nfails = 0; - -#ifndef NDEBUG - struct entry e; - e.temperature = *T; - e.rwalk = *rwalk; - sa_push(stack, e); - ++istack; -#endif - - /* Reject the current step if a BAD_OP occurs and retry up to "max_fails" - * times */ - do { - res = T->func(scn, fp_to_meter, ctx, rwalk, rng, T); - if(res == RES_BAD_OP) { *rwalk = rwalk_bkp; *T = T_bkp; } - } while(res == RES_BAD_OP && ++nfails < max_fails); - if(res != RES_OK) goto error; - - } while(!T->done); - -exit: -#ifndef NDEBUG - sa_release(stack); -#endif - return res == RES_BAD_OP_IRRECOVERABLE ? RES_BAD_OP : res; -error: - goto exit; -} - -static res_T -XD(probe_realisation) - (struct sdis_scene* scn, - struct ssp_rng* rng, - const struct sdis_medium* medium, - const double position[], - const double time_range[2], - const double fp_to_meter,/* Scale factor from floating point unit to meter */ - const double ambient_radiative_temperature, - const double reference_temperature, - double* weight) -{ - struct rwalk_context ctx; - struct XD(rwalk) rwalk = XD(RWALK_NULL); - struct XD(temperature) T = XD(TEMPERATURE_NULL); - double (*get_initial_temperature) - (const struct sdis_medium* mdm, const struct sdis_rwalk_vertex* vtx); - double t0; - res_T res = RES_OK; - ASSERT(medium && position && fp_to_meter > 0 && weight); - - switch(medium->type) { - case SDIS_FLUID: - T.func = XD(fluid_temperature); - get_initial_temperature = fluid_get_temperature; - t0 = fluid_get_t0(medium); - break; - case SDIS_SOLID: - T.func = XD(solid_temperature); - get_initial_temperature = solid_get_temperature; - t0 = solid_get_t0(medium); - break; - default: FATAL("Unreachable code\n"); break; - } - - dX(set)(rwalk.vtx.P, position); - /* Sample a time */ - rwalk.vtx.time = sample_time(time_range, rng); - if(t0 >= rwalk.vtx.time) { - double tmp; - /* Check the initial condition. */ - rwalk.vtx.time = t0; - tmp = get_initial_temperature(medium, &rwalk.vtx); - if(tmp >= 0) { - *weight = tmp; - return RES_OK; - } - /* The initial condition should have been reached */ - log_err(scn->dev, - "%s: undefined initial condition. " - "The time is %f but the temperature remains unknown.\n", - FUNC_NAME, t0); - return RES_BAD_OP; - } - - rwalk.hit = SXD_HIT_NULL; - rwalk.mdm = medium; - - ctx.Tarad = ambient_radiative_temperature; - ctx.Tref3 = - reference_temperature - * reference_temperature - * reference_temperature; - - res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); - if(res != RES_OK) return res; - - *weight = T.value; - return RES_OK; -} - -static res_T -XD(boundary_realisation) - (struct sdis_scene* scn, - struct ssp_rng* rng, - const size_t iprim, - const double uv[2], - const double time_range[2], - const enum sdis_side side, - const double fp_to_meter, - const double Tarad, - const double Tref, - double* weight) -{ - struct rwalk_context ctx; - struct XD(rwalk) rwalk = XD(RWALK_NULL); - struct XD(temperature) T = XD(TEMPERATURE_NULL); - struct sXd(attrib) attr; -#if SDIS_SOLVE_DIMENSION == 2 - float st; -#else - float st[2]; -#endif - res_T res = RES_OK; - ASSERT(uv && fp_to_meter > 0 && weight && Tref >= 0 - && time_range && time_range[0] >= 0 && time_range[1] >= time_range[0]); - - T.func = XD(boundary_temperature); - rwalk.hit_side = side; - rwalk.hit.distance = 0; - /* Sample a time */ - rwalk.vtx.time = sample_time(time_range, rng); - rwalk.mdm = NULL; /* The random walk is at an interface between 2 media */ - -#if SDIS_SOLVE_DIMENSION == 2 - st = (float)uv[0]; -#else - f2_set_d2(st, uv); -#endif - - /* Fetch the primitive */ - SXD(scene_view_get_primitive - (scn->sXd(view), (unsigned int)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)); - dX_set_fX(rwalk.vtx.P, attr.value); - - /* Retrieve the primitive normal */ - SXD(primitive_get_attrib(&rwalk.hit.prim, SXD_GEOMETRY_NORMAL, st, &attr)); - fX(set)(rwalk.hit.normal, attr.value); - -#if SDIS_SOLVE_DIMENSION==2 - rwalk.hit.u = st; -#else - f2_set(rwalk.hit.uv, st); -#endif - - ctx.Tarad = Tarad; - ctx.Tref3 = Tref*Tref*Tref; - - res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); - if(res != RES_OK) return res; - - *weight = T.value; - return RES_OK; -} - -static res_T -XD(probe_flux_realisation) - (struct sdis_scene* scn, - struct ssp_rng* rng, - const size_t iprim, - const double uv[DIM], - const double time, - const enum sdis_side solid_side, - const double fp_to_meter, - const double Tarad, - const double Tref, - const char compute_radiative, - const char compute_convective, - double weight[3]) -{ - struct rwalk_context ctx; - struct XD(rwalk) rwalk; - struct XD(temperature) T; - struct sXd(attrib) attr; - struct sXd(primitive) prim; -#if SDIS_SOLVE_DIMENSION == 2 - float st; -#else - float st[2]; -#endif - double P[SDIS_SOLVE_DIMENSION]; - float N[SDIS_SOLVE_DIMENSION]; - const double Tr3 = Tref * Tref * Tref; - const enum sdis_side fluid_side = - (solid_side == SDIS_FRONT) ? SDIS_BACK : SDIS_FRONT; - res_T res = RES_OK; - ASSERT(uv && fp_to_meter > 0 && weight && time >= 0 && Tref >= 0); - -#if SDIS_SOLVE_DIMENSION == 2 - #define SET_PARAM(Dest, Src) (Dest).u = (Src); - st = (float)uv[0]; -#else - #define SET_PARAM(Dest, Src) f2_set((Dest).uv, (Src)); - f2_set_d2(st, uv); -#endif - - /* Fetch the primitive */ - SXD(scene_view_get_primitive(scn->sXd(view), (unsigned int)iprim, &prim)); - - /* Retrieve the world space position of the probe onto the primitive */ - SXD(primitive_get_attrib(&prim, SXD_POSITION, st, &attr)); - dX_set_fX(P, attr.value); - - /* Retrieve the primitive normal */ - SXD(primitive_get_attrib(&prim, SXD_GEOMETRY_NORMAL, st, &attr)); - fX(set)(N, attr.value); - - #define RESET_WALK(Side, Mdm) \ - rwalk = XD(RWALK_NULL); \ - rwalk.hit_side = (Side); \ - rwalk.hit.distance = 0; \ - rwalk.vtx.time = time; \ - rwalk.mdm = (Mdm); \ - SET_PARAM(rwalk.hit, st); \ - ctx.Tarad = Tarad; \ - ctx.Tref3 = Tr3; \ - rwalk.hit.prim = prim; \ - dX(set)(rwalk.vtx.P, P); \ - fX(set)(rwalk.hit.normal, N); \ - T = XD(TEMPERATURE_NULL); - - /* Compute boundary temperature */ - RESET_WALK(solid_side, NULL); - T.func = XD(boundary_temperature); - res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); - if(res != RES_OK) return res; - weight[0] = T.value; - - /* Compute radiative temperature */ - if(compute_radiative) { - RESET_WALK(fluid_side, NULL); - T.func = XD(radiative_temperature); - res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); - if(res != RES_OK) return res; - weight[1] = T.value; - } - - /* Compute fluid temperature */ - if(compute_convective) { - const struct sdis_interface* interf = - scene_get_interface(scn, (unsigned)iprim); - const struct sdis_medium* mdm = interface_get_medium(interf, fluid_side); - - RESET_WALK(fluid_side, mdm); - T.func = XD(fluid_temperature); - res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); - if(res != RES_OK) return res; - weight[2] = T.value; - } - - #undef SET_PARAM - #undef RESET_WALK - - return RES_OK; -} - -static INLINE res_T -XD(interface_prebuild_fragment) - (struct sdis_interface_fragment* frag, - const struct sdis_scene* scn, - const unsigned iprim, - const double* uv, - const enum sdis_side fluid_side) -{ struct sXd(attrib) attr; - struct sXd(primitive) prim; - struct sXd(hit) hit; - struct sdis_rwalk_vertex vtx; -#if SDIS_SOLVE_DIMENSION == 2 - float st; -#else - float st[2]; -#endif - res_T res = RES_OK; - - ASSERT(frag && scn && uv); - ASSERT(fluid_side == SDIS_FRONT || fluid_side == SDIS_BACK); - - *frag = SDIS_INTERFACE_FRAGMENT_NULL; - -#if SDIS_SOLVE_DIMENSION == 2 -#define SET_PARAM(Dest, Src) (Dest).u = (Src); - st = (float)uv[0]; -#else -#define SET_PARAM(Dest, Src) f2_set((Dest).uv, (Src)); - f2_set_d2(st, uv); -#endif - res = sXd(scene_view_get_primitive(scn->sXd(view), iprim, &prim)); - if(res != RES_OK) return res; - res = sXd(primitive_get_attrib(&prim, SXD_POSITION, st, &attr)); - if(res != RES_OK) return res; - dX_set_fX(vtx.P, attr.value); - res = sXd(primitive_get_attrib(&prim, SXD_GEOMETRY_NORMAL, st, &attr)); - if(res != RES_OK) return res; - fX(set)(hit.normal, attr.value); - - hit.distance = 0; - hit.prim = prim; - vtx.time = NaN; - SET_PARAM(hit, st); - #undef SET_PARAM - XD(setup_interface_fragment)(frag, &vtx, &hit, fluid_side); - - return res; -} - -static res_T -XD(interface_get_hc_epsilon) - (double *hc, - double* epsilon, - const struct sdis_scene* scn, - const unsigned iprim, - const double* uv, - const double time, - const enum sdis_side fluid_side) -{ - struct sdis_interface_fragment frag; - const struct sdis_interface* interf; - res_T res = RES_OK; - - res = XD(interface_prebuild_fragment)(&frag, scn, iprim, uv, fluid_side); - frag.time = time; - interf = scene_get_interface(scn, iprim); - ASSERT(interf); - *epsilon = interface_side_get_emissivity(interf, &frag); - *hc = interface_get_convection_coef(interf, &frag); - - return res; -} - -#if SDIS_SOLVE_DIMENSION == 3 -static res_T -XD(ray_realisation) - (struct sdis_scene* scn, - struct ssp_rng* rng, - const struct sdis_medium* medium, - const double position[], - const double direction[], - const double time, - const double fp_to_meter, - const double Tarad, - const double Tref, - double* weight) -{ - struct rwalk_context ctx; - struct XD(rwalk) rwalk = XD(RWALK_NULL); - struct XD(temperature) T = XD(TEMPERATURE_NULL); - float dir[3]; - res_T res = RES_OK; - ASSERT(scn && position && direction && time>=0 && fp_to_meter>0 && weight - && Tref >= 0); - ASSERT(medium && medium->type == SDIS_FLUID); - - dX(set)(rwalk.vtx.P, position); - rwalk.vtx.time = time; - rwalk.hit = SXD_HIT_NULL; - rwalk.hit_side = SDIS_SIDE_NULL__; - rwalk.mdm = medium; - - ctx.Tarad = Tarad; - ctx.Tref3 = Tref*Tref*Tref; - - f3_set_d3(dir, direction); - - res = XD(trace_radiative_path)(scn, dir, fp_to_meter, &ctx, &rwalk, rng, &T); - if(res != RES_OK) goto error; - - if(!T.done) { - res = XD(compute_temperature)(scn, fp_to_meter, &ctx, &rwalk, rng, &T); - if(res != RES_OK) goto error; - } - - *weight = T.value; - -exit: - return res; -error: - goto exit; -} -#endif /* SDIS_SOLVE_DIMENSION == 3 */ - -static res_T -XD(solve_boundary) - (struct sdis_scene* scn, - const size_t nrealisations, /* #realisations */ - const size_t primitives[], /* List of boundary primitives to handle */ - const enum sdis_side sides[], /* Per primitive side to consider */ - const size_t nprimitives, /* #primitives */ - const double time_range[2], /* Observation time */ - const double fp_to_meter, /* Scale from floating point units to meters */ - const double Tarad, /* In Kelvin */ - const double Tref, /* In Kelvin */ - struct sdis_estimator** out_estimator) -{ - 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 ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - size_t i; - size_t N = 0; /* #realisations that do not fail */ - size_t view_nprims; - double weight=0, sqr_weight=0; - int64_t irealisation; - ATOMIC res = RES_OK; - - if(!scn || !nrealisations || nrealisations > INT64_MAX || !primitives - || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] - || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) - || !sides || !nprimitives || fp_to_meter < 0 || Tref < 0 - || !out_estimator) { - res = RES_BAD_ARG; - goto error; - } - - SXD(scene_view_primitives_count(scn->sXd(view), &view_nprims)); - FOR_EACH(i, 0, nprimitives) { - if(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)primitives[i], - (unsigned long)scene_get_primitives_count(scn)-1); - res = RES_BAD_ARG; - goto error; - } - } - - /* Create the Star-XD shape of the boundary */ -#if DIM == 2 - res = s2d_shape_create_line_segments(scn->dev->sXd_dev, &shape); -#else - res = s3d_shape_create_mesh(scn->dev->sXd_dev, &shape); -#endif - if(res != RES_OK) goto error; - - /* Initialise the boundary shape with the triangles/segments of the - * submitted primitives */ - ctx.primitives = primitives; - ctx.view = scn->sXd(view); - vdata.usage = SXD_POSITION; - vdata.get = XD(boundary_get_position); -#if DIM == 2 - vdata.type = S2D_FLOAT2; - res = s2d_line_segments_setup_indexed_vertices(shape, (unsigned)nprimitives, - boundary_get_indices_2d, (unsigned)(nprimitives*2), &vdata, 1, &ctx); -#else /* DIM == 3 */ - vdata.type = S3D_FLOAT3; - res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprimitives, - boundary_get_indices_3d, (unsigned)(nprimitives*3), &vdata, 1, &ctx); -#endif - if(res != RES_OK) goto error; - - /* Create and setup the boundary Star-XD scene */ - res = sXd(scene_create)(scn->dev->sXd_dev, &scene); - if(res != RES_OK) goto error; - res = sXd(scene_attach_shape)(scene, shape); - if(res != RES_OK) goto error; - res = sXd(scene_view_create)(scene, SXD_SAMPLE, &view); - if(res != RES_OK) 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(*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 estimator */ - res = estimator_create(scn->dev, SDIS_TEMPERATURE_ESTIMATOR, &estimator); - if(res != RES_OK) goto error; - - omp_set_num_threads((int)scn->dev->nthreads); - #pragma omp parallel for schedule(static) reduction(+:weight,sqr_weight,N) - for(irealisation=0; irealisation<(int64_t)nrealisations; ++irealisation) { - const int ithread = omp_get_thread_num(); - struct sXd(primitive) prim; - struct ssp_rng* rng = rngs[ithread]; - enum sdis_side side; - size_t iprim; - double w = NaN; - double uv[DIM-1]; - float st[DIM-1]; - res_T res_local = RES_OK; - - if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ - - /* Sample a position onto the boundary */ -#if DIM == 2 - res_local = s2d_scene_view_sample - (view, - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - &prim, st); - uv[0] = (double)st[0]; -#else - res_local = s3d_scene_view_sample - (view, - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - &prim, st); - d2_set_f2(uv, st); -#endif - if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } - - /* Map from boundary scene to sdis scene */ - ASSERT(prim.prim_id < nprimitives); - iprim = primitives[prim.prim_id]; - side = sides[prim.prim_id]; - - /* Invoke the boundary realisation */ - res_local = XD(boundary_realisation) - (scn, rng, iprim, uv, time_range, side, fp_to_meter, Tarad, Tref, &w); - - /* Update the MC accumulators */ - if(res_local == RES_OK) { - weight += w; - sqr_weight += w*w; - ++N; - } else if(res_local != RES_BAD_OP) { - ATOMIC_SET(&res, res_local); - continue; - } - } - - setup_estimator(estimator, nrealisations, N, weight, sqr_weight); - -exit: - if(scene) SXD(scene_ref_put(scene)); - if(shape) SXD(shape_ref_put(shape)); - if(view) SXD(scene_view_ref_put(view)); - if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); - if(out_estimator) *out_estimator = estimator; - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) {if(rngs[i]) SSP(rng_ref_put(rngs[i]));} - MEM_RM(scn->dev->allocator, rngs); - } - return (res_T)res; -error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } - goto exit; -} - -static res_T -XD(solve_boundary_flux) - (struct sdis_scene* scn, - const size_t nrealisations, /* #realisations */ - const size_t primitives[], /* List of boundary primitives to handle */ - const size_t nprimitives, /* #primitives */ - const double time_range[2], /* Observation time */ - const double fp_to_meter, /* Scale from floating point units to meters */ - const double Tarad, /* In Kelvin */ - const double Tref, /* In Kelvin */ - struct sdis_estimator** out_estimator) -{ - 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 ssp_rng_proxy* rng_proxy = NULL; - struct ssp_rng** rngs = NULL; - double weight_t = 0, sqr_weight_t = 0; - double weight_fc = 0, sqr_weight_fc = 0; - double weight_fr = 0, sqr_weight_fr = 0; - double weight_f = 0, sqr_weight_f = 0; - size_t i; - size_t N = 0; /* #realisations that do not fail */ - size_t view_nprims; - int64_t irealisation; - ATOMIC res = RES_OK; - - if(!scn || !nrealisations || nrealisations > INT64_MAX || !primitives - || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] - || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) - || !nprimitives || fp_to_meter < 0 || Tref < 0 - || !out_estimator) { - res = RES_BAD_ARG; - goto error; - } - - SXD(scene_view_primitives_count(scn->sXd(view), &view_nprims)); - FOR_EACH(i, 0, nprimitives) { - if(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)primitives[i], - (unsigned long)scene_get_primitives_count(scn)-1); - res = RES_BAD_ARG; - goto error; - } - } - - /* Create the Star-XD shape of the boundary */ -#if DIM == 2 - res = s2d_shape_create_line_segments(scn->dev->s2d, &shape); -#else - res = s3d_shape_create_mesh(scn->dev->s3d, &shape); -#endif - if(res != RES_OK) goto error; - - /* Initialise the boundary shape with the triangles/segments of the - * submitted primitives */ - ctx.primitives = primitives; - ctx.view = scn->sXd(view); - vdata.get = XD(boundary_get_position); -#if DIM == 2 - vdata.usage = S2D_POSITION; - vdata.type = S2D_FLOAT2; - res = s2d_line_segments_setup_indexed_vertices(shape, (unsigned)nprimitives, - boundary_get_indices_2d, (unsigned)(nprimitives*2), &vdata, 1, &ctx); -#else /* DIM == 3 */ - vdata.usage = S3D_POSITION; - vdata.type = S3D_FLOAT3; - res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprimitives, - boundary_get_indices_3d, (unsigned)(nprimitives*3), &vdata, 1, &ctx); -#endif - if(res != RES_OK) goto error; - - /* Create and setup the boundary Star-XD scene */ - res = sXd(scene_create)(scn->dev->sXd_dev, &scene); - if(res != RES_OK) goto error; - res = sXd(scene_attach_shape)(scene, shape); - if(res != RES_OK) goto error; - res = sXd(scene_view_create)(scene, SXD_SAMPLE, &view); - if(res != RES_OK) 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(*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 estimator */ - res = estimator_create(scn->dev, SDIS_FLUX_ESTIMATOR, &estimator); - if(res != RES_OK) goto error; - - omp_set_num_threads((int)scn->dev->nthreads); - #pragma omp parallel for schedule(static) reduction(+:weight_t,sqr_weight_t,\ - weight_fc,sqr_weight_fc,weight_fr,sqr_weight_fr,weight_f,sqr_weight_f,N) - for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { - const int ithread = omp_get_thread_num(); - struct sXd(primitive) prim; - struct ssp_rng* rng = rngs[ithread]; - const struct sdis_interface* interf; - const struct sdis_medium *fmd, *bmd; - enum sdis_side solid_side, fluid_side; - double T_brf[3] = { 0, 0, 0 }; - double epsilon, hc, hr; - size_t iprim; - double uv[DIM - 1]; - float st[DIM - 1]; - double time; - res_T res_local = RES_OK; - - if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ - - /* Sample a time */ - time = sample_time(time_range, rng); - - /* Sample a position onto the boundary */ -#if DIM == 2 - res_local = s2d_scene_view_sample - (view, - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - &prim, st); - uv[0] = (double)st[0]; -#else - res_local = s3d_scene_view_sample - (view, - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - ssp_rng_canonical_float(rng), - &prim, st); - d2_set_f2(uv, st); -#endif - if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } - - /* Map from boundary scene to sdis scene */ - ASSERT(prim.prim_id < nprimitives); - iprim = primitives[prim.prim_id]; - - interf = scene_get_interface(scn, (unsigned)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))) - { - ATOMIC_SET(&res, RES_BAD_ARG); - continue; - } - solid_side = (fmd->type == SDIS_SOLID) ? SDIS_FRONT : SDIS_BACK; - fluid_side = (fmd->type == SDIS_FLUID) ? SDIS_FRONT : SDIS_BACK; - - res_local = XD(interface_get_hc_epsilon)(&hc, &epsilon, scn, - (unsigned)iprim, uv, time, fluid_side); - if(res_local != RES_OK) { - ATOMIC_SET(&res, res_local); - continue; - } - hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; - - /* Fluid, Radiative and Solid temperatures */ - res_local = XD(probe_flux_realisation)(scn, rng, iprim, uv, time, - solid_side, fp_to_meter, Tarad, Tref, hr > 0, hc > 0, T_brf); - if(res_local != RES_OK) { - if(res_local != RES_BAD_OP) { - ATOMIC_SET(&res, res_local); - continue; - } - } else { - const double Tboundary = T_brf[0]; - const double Tradiative = T_brf[1]; - const double Tfluid = T_brf[2]; - const double w_conv = hc * (Tboundary - Tfluid); - const double w_rad = hr * (Tboundary - Tradiative); - const double w_total = w_conv + w_rad; - weight_t += Tboundary; - sqr_weight_t += Tboundary * Tboundary; - weight_fc += w_conv; - sqr_weight_fc += w_conv * w_conv; - weight_fr += w_rad; - sqr_weight_fr += w_rad * w_rad; - weight_f += w_total; - sqr_weight_f += w_total * w_total; - ++N; - } - } - if(res != RES_OK) goto error; - - setup_estimator(estimator, nrealisations, N, weight_t, sqr_weight_t); - setup_estimator_flux(estimator, FLUX_CONVECTIVE__, weight_fc, sqr_weight_fc); - setup_estimator_flux(estimator, FLUX_RADIATIVE__, weight_fr, sqr_weight_fr); - setup_estimator_flux(estimator, FLUX_TOTAL__, weight_f, sqr_weight_f); - -exit: - if(scene) SXD(scene_ref_put(scene)); - if(shape) SXD(shape_ref_put(shape)); - if(view) SXD(scene_view_ref_put(view)); - if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); - if(out_estimator) *out_estimator = estimator; - if(rngs) { - FOR_EACH(i, 0, scn->dev->nthreads) { if(rngs[i]) SSP(rng_ref_put(rngs[i])); } - MEM_RM(scn->dev->allocator, rngs); - } - return (res_T)res; -error: - if(estimator) { - SDIS(estimator_ref_put(estimator)); - estimator = NULL; - } - goto exit; -} - -#undef SDIS_SOLVE_DIMENSION -#undef DIM -#undef sXd -#undef sXd_dev -#undef SXD_HIT_NONE -#undef SXD_HIT_NULL -#undef SXD_HIT_NULL__ -#undef SXD_POSITION -#undef SXD_GEOMETRY_NORMAL -#undef SXD_VERTEX_DATA_NULL -#undef SXD_FLOAT2 -#undef SXD_FLOAT3 -#undef SXD_SAMPLE -#undef SXD -#undef dX -#undef fX -#undef fX_set_dX -#undef XD - -#endif /* !SDIS_SOLVE_DIMENSION */ - diff --git a/src/sdis_solve_boundary_Xd.h b/src/sdis_solve_boundary_Xd.h @@ -0,0 +1,619 @@ +/* Copyright (C) 2016-2019 |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_device_c.h" +#include "sdis_estimator_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 <omp.h> + +#include "sdis_Xd_begin.h" + +struct XD(boundary_context) { + struct sXd(scene_view)* view; + const size_t* primitives; +}; +static const struct XD(boundary_context) XD(BOUNDARY_CONTEXT_NULL) = { + NULL, NULL +}; + +/******************************************************************************* + * Help functions + ******************************************************************************/ +static INLINE void +XD(boundary_get_indices)(const unsigned iprim, unsigned ids[DIM], void* context) +{ + unsigned i; + (void)context; + ASSERT(ids); + FOR_EACH(i, 0, DIM) ids[i] = iprim*DIM + i; +} + +static INLINE void +XD(boundary_get_position)(const unsigned ivert, float pos[DIM], void* context) +{ + struct XD(boundary_context)* ctx = context; + struct sXd(primitive) prim; + struct sXd(attrib) attr; + const unsigned iprim_id = ivert / DIM; + const unsigned iprim_vert = ivert % DIM; + unsigned iprim; + ASSERT(pos && context); + + iprim = (unsigned)ctx->primitives[iprim_id]; + SXD(scene_view_get_primitive(ctx->view, iprim, &prim)); +#if DIM == 2 + s2d_segment_get_vertex_attrib(&prim, iprim_vert, S2D_POSITION, &attr); + ASSERT(attr.type == S2D_FLOAT2); +#else + s3d_triangle_get_vertex_attrib(&prim, iprim_vert, S3D_POSITION, &attr); + ASSERT(attr.type == S3D_FLOAT3); +#endif + fX(set)(pos, attr.value); +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +static res_T +XD(solve_boundary) + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t primitives[], /* List of boundary primitives to handle */ + const enum sdis_side sides[], /* Per primitive side to consider */ + const size_t nprimitives, /* #primitives */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ + struct sdis_green_function** out_green, + struct sdis_estimator** out_estimator) +{ + 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* accums = NULL; + size_t i; + size_t view_nprims; + int64_t irealisation; + ATOMIC res = RES_OK; + + if(!scn || !nrealisations || nrealisations > INT64_MAX || !primitives + || !sides || !nprimitives || fp_to_meter <= 0 || Tref < 0) { + res = RES_BAD_ARG; + goto error; + } + if(!out_estimator && !out_green) { + res = RES_BAD_ARG; + goto error; + } + if(out_estimator) { + if(!time_range || time_range[0] < 0 || time_range[1] < time_range[0] + || (time_range[1] > DBL_MAX && time_range[0] != 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 + + SXD(scene_view_primitives_count(scn->sXd(view), &view_nprims)); + FOR_EACH(i, 0, nprimitives) { + if(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)primitives[i], + (unsigned long)scene_get_primitives_count(scn)-1); + res = RES_BAD_ARG; + goto error; + } + } + + /* Create the Star-XD shape of the boundary */ +#if SDIS_XD_DIMENSION == 2 + res = s2d_shape_create_line_segments(scn->dev->sXd_dev, &shape); +#else + res = s3d_shape_create_mesh(scn->dev->sXd_dev, &shape); +#endif + if(res != RES_OK) goto error; + + /* Initialise the boundary shape with the triangles/segments of the + * submitted primitives */ + ctx.primitives = primitives; + ctx.view = scn->sXd(view); + vdata.usage = SXD_POSITION; + vdata.get = XD(boundary_get_position); +#if SDIS_XD_DIMENSION == 2 + vdata.type = S2D_FLOAT2; + res = s2d_line_segments_setup_indexed_vertices(shape, (unsigned)nprimitives, + boundary_get_indices_2d, (unsigned)(nprimitives*2), &vdata, 1, &ctx); +#else + vdata.type = S3D_FLOAT3; + res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprimitives, + boundary_get_indices_3d, (unsigned)(nprimitives*3), &vdata, 1, &ctx); +#endif + if(res != RES_OK) goto error; + + /* Create and setup the boundary Star-XD scene */ + res = sXd(scene_create)(scn->dev->sXd_dev, &scene); + if(res != RES_OK) goto error; + res = sXd(scene_attach_shape)(scene, shape); + if(res != RES_OK) goto error; + res = sXd(scene_view_create)(scene, SXD_SAMPLE, &view); + if(res != RES_OK) 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(*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 accumulator */ + accums = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*accums)); + if(!accums) { 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->dev, &greens[i]); + if(res != RES_OK) goto error; + } + } + + /* Create the estimator */ + if(out_estimator) { + res = estimator_create(scn->dev, SDIS_ESTIMATOR_TEMPERATURE, &estimator); + if(res != RES_OK) goto error; + } + + omp_set_num_threads((int)scn->dev->nthreads); + #pragma omp parallel for schedule(static) + for(irealisation=0; irealisation<(int64_t)nrealisations; ++irealisation) { + const int ithread = omp_get_thread_num(); + struct ssp_rng* rng = rngs[ithread]; + struct accum* accum = &accums[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; + struct sdis_heat_path heat_path; + struct sXd(primitive) prim; + enum sdis_side side; + size_t iprim; + double w = NaN; + double uv[DIM-1]; + float st[DIM-1]; + double time; + res_T res_local = RES_OK; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ + + if(!out_green) { + time = sample_time(rng, time_range); + if(register_paths) { + heat_path_init(scn->dev->allocator, &heat_path); + pheat_path = &heat_path; + } + } else { + /* Do not take care of the submitted time when registering the green + * function. Simply takes 0 as relative time */ + time = 0; + res_local = green_function_create_path(greens[ithread], &green_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + pgreen_path = &green_path; + } + + /* Sample a position onto the boundary */ +#if SDIS_XD_DIMENSION == 2 + res_local = s2d_scene_view_sample + (view, + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, st); + uv[0] = (double)st[0]; +#else + res_local = s3d_scene_view_sample + (view, + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, st); + d2_set_f2(uv, st); +#endif + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + + /* Map from boundary scene to sdis scene */ + ASSERT(prim.prim_id < nprimitives); + iprim = primitives[prim.prim_id]; + side = sides[prim.prim_id]; + + /* Invoke the boundary realisation */ + res_local = XD(boundary_realisation)(scn, rng, iprim, uv, time, side, + fp_to_meter, Tarad, Tref, pgreen_path, pheat_path, &w); + + /* Update the MC accumulators */ + if(res_local == RES_OK) { + accum->sum += w; + accum->sum2 += w*w; + ++accum->count; + } else if(res_local != RES_BAD_OP) { + ATOMIC_SET(&res, res_local); + continue; + } + + if(pheat_path) { + pheat_path->status = res_local == RES_OK + ? SDIS_HEAT_PATH_SUCCEED + : SDIS_HEAT_PATH_FAILED; + + /* Check if the path must be saved regarding the register_paths mask */ + if(!(register_paths & (int)pheat_path->status)) { + heat_path_release(pheat_path); + } else { /* Register the sampled path */ + res_local = estimator_add_and_release_heat_path(estimator, pheat_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + } + } + } + + /* Setup the estimated temperature */ + if(out_estimator) { + struct accum acc; + sum_accums(accums, scn->dev->nthreads, &acc); + estimator_setup_realisations_count(estimator, nrealisations, acc.count); + estimator_setup_temperature(estimator, acc.sum, acc.sum2); + } + + /* Setup the green function */ + if(out_green) { + /* 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); + if(res != RES_OK) goto error; + + /* Finalize the estimated green */ + res = green_function_finalize(green, 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(greens) { + FOR_EACH(i, 0, scn->dev->nthreads) { + if(greens[i]) SDIS(green_function_ref_put(greens[i])); + } + MEM_RM(scn->dev->allocator, greens); + } + if(accums) MEM_RM(scn->dev->allocator, accums); + if(scene) SXD(scene_ref_put(scene)); + if(shape) SXD(shape_ref_put(shape)); + if(view) SXD(scene_view_ref_put(view)); + 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(estimator) { + SDIS(estimator_ref_put(estimator)); + estimator = NULL; + } + if(green) { + SDIS(green_function_ref_put(green)); + green = NULL; + } + goto exit; +} + +static res_T +XD(solve_boundary_flux) + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t primitives[], /* List of boundary primitives to handle */ + const size_t nprimitives, /* #primitives */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + struct sdis_estimator** out_estimator) +{ + 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 ssp_rng_proxy* rng_proxy = NULL; + struct ssp_rng** rngs = NULL; + struct accum* acc_tp = NULL; /* Per thread temperature 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 */ + size_t i; + size_t view_nprims; + int64_t irealisation; + ATOMIC res = RES_OK; + + if(!scn || !nrealisations || nrealisations > INT64_MAX || !primitives + || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] + || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) + || !nprimitives || fp_to_meter < 0 || Tref < 0 + || !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 + + SXD(scene_view_primitives_count(scn->sXd(view), &view_nprims)); + FOR_EACH(i, 0, nprimitives) { + if(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)primitives[i], + (unsigned long)scene_get_primitives_count(scn)-1); + res = RES_BAD_ARG; + goto error; + } + } + + /* Create the Star-XD shape of the boundary */ +#if SDIS_XD_DIMENSION == 2 + res = s2d_shape_create_line_segments(scn->dev->s2d, &shape); +#else + res = s3d_shape_create_mesh(scn->dev->s3d, &shape); +#endif + if(res != RES_OK) goto error; + + /* Initialise the boundary shape with the triangles/segments of the + * submitted primitives */ + ctx.primitives = primitives; + ctx.view = scn->sXd(view); + vdata.get = XD(boundary_get_position); +#if SDIS_XD_DIMENSION == 2 + vdata.usage = S2D_POSITION; + vdata.type = S2D_FLOAT2; + res = s2d_line_segments_setup_indexed_vertices(shape, (unsigned)nprimitives, + XD(boundary_get_indices), (unsigned)(nprimitives*2), &vdata, 1, &ctx); +#else /* DIM == 3 */ + vdata.usage = S3D_POSITION; + vdata.type = S3D_FLOAT3; + res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)nprimitives, + XD(boundary_get_indices), (unsigned)(nprimitives*3), &vdata, 1, &ctx); +#endif + if(res != RES_OK) goto error; + + /* Create and setup the boundary Star-XD scene */ + res = sXd(scene_create)(scn->dev->sXd_dev, &scene); + if(res != RES_OK) goto error; + res = sXd(scene_attach_shape)(scene, shape); + if(res != RES_OK) goto error; + res = sXd(scene_view_create)(scene, SXD_SAMPLE, &view); + if(res != RES_OK) 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(*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 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_fc); + ALLOC_ACCUMS(acc_fl); + ALLOC_ACCUMS(acc_fr); + #undef ALLOC_ACCUMS + + /* Create the estimator */ + res = estimator_create(scn->dev, SDIS_ESTIMATOR_FLUX, &estimator); + if(res != RES_OK) goto error; + + omp_set_num_threads((int)scn->dev->nthreads); + #pragma omp parallel for schedule(static) + for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { + const int ithread = omp_get_thread_num(); + struct ssp_rng* rng = rngs[ithread]; + struct accum* acc_temp = &acc_tp[ithread]; + struct accum* acc_flux = &acc_fl[ithread]; + struct accum* acc_fcon = &acc_fc[ithread]; + struct accum* acc_frad = &acc_fr[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; + double T_brf[3] = { 0, 0, 0 }; + double epsilon, hc, hr; + size_t iprim; + double uv[DIM - 1]; + float st[DIM - 1]; + double time; + int flux_mask = 0; + res_T res_local = RES_OK; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ + + time = sample_time(rng, time_range); + + /* Sample a position onto the boundary */ +#if SDIS_XD_DIMENSION == 2 + res_local = s2d_scene_view_sample + (view, + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, st); + uv[0] = (double)st[0]; +#else + res_local = s3d_scene_view_sample + (view, + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + ssp_rng_canonical_float(rng), + &prim, st); + d2_set_f2(uv, st); +#endif + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + + /* Map from boundary scene to sdis scene */ + ASSERT(prim.prim_id < nprimitives); + iprim = primitives[prim.prim_id]; + + interf = scene_get_interface(scn, (unsigned)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))) + { + ATOMIC_SET(&res, RES_BAD_ARG); + continue; + } + solid_side = (fmd->type == SDIS_SOLID) ? SDIS_FRONT : SDIS_BACK; + fluid_side = (fmd->type == SDIS_FLUID) ? SDIS_FRONT : SDIS_BACK; + + /* Build interface fragment on the fluid side of the primitive */ + res_local = XD(build_interface_fragment) + (&frag, scn, (unsigned)iprim, uv, fluid_side); + if(res_local!= RES_OK) { ATOMIC_SET(&res, res_local); continue; } + + /* Fetch interface parameters */ + epsilon = interface_side_get_emissivity(interf, &frag); + hc = interface_get_convection_coef(interf, &frag); + + hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; + + /* Fluid, Radiative and Solid temperatures */ + flux_mask = 0; + if(hr > 0) flux_mask |= FLUX_FLAG_RADIATIVE; + if(hc > 0) flux_mask |= FLUX_FLAG_CONVECTIVE; + res_local = XD(boundary_flux_realisation)(scn, rng, iprim, uv, time, + solid_side, fp_to_meter, Tarad, Tref, flux_mask, T_brf); + if(res_local == RES_OK) { + const double Tboundary = T_brf[0]; + const double Tradiative = T_brf[1]; + const double Tfluid = T_brf[2]; + const double w_conv = hc * (Tboundary - Tfluid); + const double w_rad = hr * (Tboundary - Tradiative); + const double w_total = w_conv + w_rad; + /* Temperature */ + acc_temp->sum += Tboundary; + acc_temp->sum2 += Tboundary*Tboundary; + ++acc_temp->count; + /* Overwall flux */ + acc_flux->sum += w_total; + acc_flux->sum2 += w_total*w_total; + ++acc_flux->count; + /* Convective flux */ + acc_fcon->sum += w_conv; + acc_fcon->sum2 += w_conv*w_conv; + ++acc_fcon->count; + /* Radiative flux */ + acc_frad->sum += w_rad; + acc_frad->sum2 += w_rad*w_rad; + ++acc_frad->count; + } else if(res_local != RES_BAD_OP) { + ATOMIC_SET(&res, res_local); + continue; + } + } + if(res != RES_OK) goto error; + + /* Redux the per thread accumulators */ + sum_accums(acc_tp, scn->dev->nthreads, &acc_tp[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]); + ASSERT(acc_tp[0].count == acc_fl[0].count); + ASSERT(acc_tp[0].count == acc_fr[0].count); + ASSERT(acc_tp[0].count == acc_fc[0].count); + + /* 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_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_TOTAL, acc_fl[0].sum, acc_fl[0].sum2); + +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_tp) MEM_RM(scn->dev->allocator, acc_tp); + 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(scene) SXD(scene_ref_put(scene)); + if(shape) SXD(shape_ref_put(shape)); + if(view) SXD(scene_view_ref_put(view)); + 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; + } + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_solve_medium_Xd.h b/src/sdis_solve_medium_Xd.h @@ -0,0 +1,412 @@ +/* Copyright (C) 2016-2019 |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_device_c.h" +#include "sdis_estimator_c.h" +#include "sdis_green.h" +#include "sdis_realisation.h" +#include "sdis_scene_c.h" + +#include <rsys/algorithm.h> +#include <rsys/dynamic_array.h> + +#include "sdis_Xd_begin.h" + +#ifndef SDIS_SOLVE_MEDIUM_XD_H +#define SDIS_SOLVE_MEDIUM_XD_H + +/* + * Define the data structures and functions that are not generic to the + * SDIS_XD_DIMENSION parameter + */ + +struct enclosure_cumul { + const struct enclosure* enc; + double cumul; +}; + +/* Define the darray_enclosure_cumul dynamic array */ +#define DARRAY_NAME enclosure_cumul +#define DARRAY_DATA struct enclosure_cumul +#include <rsys/dynamic_array.h> + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE int +cmp_double_to_enc_cumuls(const void* a, const void* b) +{ + const double key = *(const double*)a; + const struct enclosure_cumul* enc_cumul = (const struct enclosure_cumul*)b; + if(key < enc_cumul->cumul) return -1; + if(key > enc_cumul->cumul) return +1; + return 0; +} + +static res_T +compute_medium_enclosure_cumulative + (struct sdis_scene* scn, + const struct sdis_medium* mdm, + struct darray_enclosure_cumul* cumul) +{ + struct htable_enclosure_iterator it, end; + double accum = 0; + res_T res = RES_OK; + ASSERT(scn && mdm && cumul); + + darray_enclosure_cumul_clear(cumul); + + htable_enclosure_begin(&scn->enclosures, &it); + htable_enclosure_end(&scn->enclosures, &end); + while(!htable_enclosure_iterator_eq(&it, &end)) { + struct enclosure_cumul enc_cumul; + const struct enclosure* enc = htable_enclosure_iterator_data_get(&it); + htable_enclosure_iterator_next(&it); + + if(sdis_medium_get_id(mdm) != enc->medium_id) continue; + + accum += enc->V; + enc_cumul.enc = enc; + enc_cumul.cumul = accum; + res = darray_enclosure_cumul_push_back(cumul, &enc_cumul); + if(res != RES_OK) goto error; + } + + if(darray_enclosure_cumul_size_get(cumul) == 0) { + log_err(scn->dev, + "%s: there is no enclosure that encompasses the submitted medium.\n", + FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + darray_enclosure_cumul_clear(cumul); + goto exit; +} + +static const struct enclosure* +sample_medium_enclosure + (const struct darray_enclosure_cumul* cumul, struct ssp_rng* rng) +{ + const struct enclosure_cumul* enc_cumuls = NULL; + const struct enclosure_cumul* enc_cumul_found = NULL; + double r; + size_t i; + size_t sz; + ASSERT(cumul && rng && darray_enclosure_cumul_size_get(cumul)); + + sz = darray_enclosure_cumul_size_get(cumul); + enc_cumuls = darray_enclosure_cumul_cdata_get(cumul); + if(sz == 1) { + enc_cumul_found = enc_cumuls; + } else { + /* Generate an uniform random number in [0, cumul[ */ + r = ssp_rng_canonical(rng); + r = r * enc_cumuls[sz-1].cumul; + + enc_cumul_found = search_lower_bound + (&r, enc_cumuls, sz, sizeof(*enc_cumuls), cmp_double_to_enc_cumuls); + ASSERT(enc_cumul_found); + + /* search_lower_bound returns the first entry that is not less than `r'. + * The following code discards entries that are also equal to `r'. */ + i = (size_t)(enc_cumul_found - enc_cumuls); + while(enc_cumuls[i].cumul == r && i < sz) ++i; + ASSERT(i < sz); + + enc_cumul_found = enc_cumuls + i; + } + return enc_cumul_found->enc; +} + +#endif /* !SDIS_SOLVE_MEDIUM_XD_H */ + +/******************************************************************************* + * Helper functions generic to the SDIS_XD_DIMENSION parameter + ******************************************************************************/ +static res_T +XD(sample_enclosure_position) + (const struct enclosure* enc, + struct ssp_rng* rng, + double pos[DIM]) +{ + const size_t MAX_NCHALLENGES = 1000; + float lower[DIM], upper[DIM]; + size_t ichallenge; + size_t i; + res_T res = RES_OK; + ASSERT(enc && rng && pos); + + SXD(scene_view_get_aabb(enc->sXd(view), lower, upper)); + + FOR_EACH(i, 0, DIM) { + if(lower[i] > upper[i] || eq_epsf(lower[i], upper[i], 1.e-6f)) { + res = RES_BAD_ARG; /* Degenerated enclosure */ + goto error; + } + } + + FOR_EACH(ichallenge, 0, MAX_NCHALLENGES) { + struct sXd(hit) hit = SXD_HIT_NULL; + const float dir[3] = {1,0,0}; + const float range[2] = {0, FLT_MAX}; + float org[DIM]; + + /* Generate an uniform position into the enclosure AABB */ + FOR_EACH(i, 0, DIM) { + org[i] = ssp_rng_uniform_float(rng, lower[i], upper[i]); + } + + /* Check that pos lies into the enclosure; trace a ray and check that it + * hits something and that the normal points towards the traced ray + * direction (enclosure normals point inword the enclosure) */ + SXD(scene_view_trace_ray(enc->sXd(view), org, dir, range, NULL, &hit)); + if(!SXD_HIT_NONE(&hit) && fX(dot)(dir, hit.normal) < 0) { + dX_set_fX(pos, org); + break; + } + } + + if(ichallenge >= MAX_NCHALLENGES) { + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local function + ******************************************************************************/ +static res_T +XD(solve_medium) + (struct sdis_scene* scn, + const size_t nrealisations, + struct sdis_medium* mdm, + const double time_range[2], + const double fp_to_meter,/* Scale factor from floating point unit to meter */ + const double Tarad, /* Ambient radiative temperature */ + const double Tref, /* Reference temperature */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ + 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; + 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 sdis_estimator* estimator = NULL; + struct accum* accums = NULL; + int64_t irealisation; + int cumul_is_init = 0; + size_t i; + ATOMIC res = RES_OK; + + if(!scn || !mdm || !nrealisations || nrealisations > INT64_MAX + || fp_to_meter <= 0 || Tref < 0) { + res = RES_BAD_ARG; + goto error; + } + if(!out_estimator && !out_green) { + res = RES_BAD_ARG; + goto error; + } + if(out_estimator) { + if(!time_range || time_range[0] < 0 || time_range[0] > time_range[1] + || (time_range[1] > DBL_MAX && time_range[0] != 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 + + /* 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(*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 accumulator */ + accums = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*accums)); + if(!accums) { res = RES_MEM_ERR; goto error; } + + /* Compute the enclosure cumulative */ + darray_enclosure_cumul_init(scn->dev->allocator, &cumul); + cumul_is_init = 1; + res = compute_medium_enclosure_cumulative(scn, mdm, &cumul); + 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->dev, &greens[i]); + if(res != RES_OK) goto error; + } + } + + /* Create the estimator */ + if(out_estimator) { + res = estimator_create(scn->dev, SDIS_ESTIMATOR_TEMPERATURE, &estimator); + if(res != RES_OK) goto error; + } + + omp_set_num_threads((int)scn->dev->nthreads); + #pragma omp parallel for schedule(static) + for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { + const int ithread = omp_get_thread_num(); + struct ssp_rng* rng = rngs[ithread]; + struct accum* accum = accums + ithread; + struct green_path_handle* pgreen_path = NULL; + struct green_path_handle green_path = GREEN_PATH_HANDLE_NULL; + const struct enclosure* enc = NULL; + struct sdis_heat_path* pheat_path = NULL; + struct sdis_heat_path heat_path; + double weight; + double time; + double pos[DIM]; + res_T res_local = RES_OK; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ + + if(!out_green) { + /* Sample the time */ + time = sample_time(rng, time_range); + + /* Prepare path registration if necessary */ + if(register_paths) { + heat_path_init(scn->dev->allocator, &heat_path); + pheat_path = &heat_path; + } + } else { + /* Do not take care of the submitted time when registering the green + * function. Simply takes 0 as relative time */ + time = 0; + res_local = green_function_create_path(greens[ithread], &green_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + + pgreen_path = &green_path; + } + + /* Uniformly Sample an enclosure that surround the submitted medium and + * uniformly sample a position into it */ + enc = sample_medium_enclosure(&cumul, rng); + res_local = XD(sample_enclosure_position)(enc, rng, pos); + if(res_local != RES_OK) { + log_err(scn->dev, "%s: could not sample a medium position.\n", FUNC_NAME); + ATOMIC_SET(&res, res_local); + continue; + } + + /* Run a probe realisation */ + res_local = XD(probe_realisation)((size_t)irealisation, scn, rng, mdm, pos, + time, fp_to_meter, Tarad, Tref, pgreen_path, pheat_path, &weight); + if(res_local != RES_OK) { + if(res_local != RES_BAD_OP) { ATOMIC_SET(&res, res_local); continue; } + } else { + accum->sum += weight; + accum->sum2 += weight*weight; + ++accum->count; + } + + /* Finalize the registered path */ + if(pheat_path) { + pheat_path->status = res_local == RES_OK + ? SDIS_HEAT_PATH_SUCCEED + : SDIS_HEAT_PATH_FAILED; + + /* Check if the path must be saved regarding the register_paths mask */ + if(!(register_paths & (int)pheat_path->status)) { + heat_path_release(pheat_path); + } else { /* Register the sampled path */ + res_local = estimator_add_and_release_heat_path(estimator, pheat_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + } + } + } + if(res != RES_OK) goto error; + + /* Setup the estimated temperature */ + if(out_estimator) { + struct accum acc; + sum_accums(accums, scn->dev->nthreads, &acc); + estimator_setup_realisations_count(estimator, nrealisations, acc.count); + estimator_setup_temperature(estimator, acc.sum, acc.sum2); + } + + if(out_green) { + /* 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); + if(res != RES_OK) goto error; + + /* Finalize the estimated green */ + res = green_function_finalize(green, 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(greens) { + FOR_EACH(i, 0, scn->dev->nthreads) { + if(greens[i]) SDIS(green_function_ref_put(greens[i])); + } + MEM_RM(scn->dev->allocator, greens); + } + if(accums) MEM_RM(scn->dev->allocator, accums); + 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; + } + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_solve_probe_Xd.h b/src/sdis_solve_probe_Xd.h @@ -0,0 +1,226 @@ +/* Copyright (C) 2016-2019 |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_device_c.h" +#include "sdis_estimator_c.h" +#include "sdis_green.h" +#include "sdis_misc.h" +#include "sdis_realisation.h" +#include "sdis_scene_c.h" + +#include <star/ssp.h> +#include <omp.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Generic solve function + ******************************************************************************/ +static res_T +XD(solve_probe) + (struct sdis_scene* scn, + const size_t nrealisations, + const double position[3], + const double time_range[2], + const double fp_to_meter,/* Scale factor from floating point unit to meter */ + const double Tarad, /* Ambient radiative temperature */ + const double Tref, /* Reference temperature */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ + struct sdis_green_function** out_green, /* May be NULL <=> No green func */ + struct sdis_estimator** out_estimator) /* May be NULL <=> No estimator */ +{ + struct sdis_medium* medium = 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* accums = NULL; + int64_t irealisation = 0; + size_t i; + ATOMIC res = RES_OK; + + if(!scn || !nrealisations || nrealisations > INT64_MAX || !position + || fp_to_meter <= 0 || Tref < 0) { + res = RES_BAD_ARG; + goto error; + } + if(!out_estimator && !out_green) { + res = RES_BAD_ARG; + goto error; + } + if(out_estimator) { + if(!time_range || time_range[0] < 0 || time_range[1] < time_range[0] + || (time_range[1] > DBL_MAX && time_range[0] != 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 + + /* 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(*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 accumulator */ + accums = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*accums)); + if(!accums) { res = RES_MEM_ERR; goto error; } + + /* Retrieve the medium in which the submitted position lies */ + res = scene_get_medium(scn, position, NULL, &medium); + if(res != RES_OK) 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->dev, &greens[i]); + if(res != RES_OK) goto error; + } + } + + /* Create the estimator */ + if(out_estimator) { + res = estimator_create(scn->dev, SDIS_ESTIMATOR_TEMPERATURE, &estimator); + if(res != RES_OK) goto error; + } + + /* Here we go! Launch the Monte Carlo estimation */ + omp_set_num_threads((int)scn->dev->nthreads); + #pragma omp parallel for schedule(static) + for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { + const int ithread = omp_get_thread_num(); + struct ssp_rng* rng = rngs[ithread]; + struct accum* accum = &accums[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; + struct sdis_heat_path heat_path; + double w = NaN; + double time; + res_T res_local; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ + + if(!out_green) { + time = sample_time(rng, time_range); + if(register_paths) { + heat_path_init(scn->dev->allocator, &heat_path); + pheat_path = &heat_path; + } + } else { + /* Do not take care of the submitted time when registering the green + * function. Simply takes 0 as relative time */ + time = 0; + res_local = green_function_create_path(greens[ithread], &green_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + + pgreen_path = &green_path; + } + + res_local = XD(probe_realisation)((size_t)irealisation, scn, rng, medium, + position, time, fp_to_meter, Tarad, Tref, pgreen_path, pheat_path, &w); + if(res_local == RES_OK) { + accum->sum += w; + accum->sum2 += w*w; + ++accum->count; + } else if(res_local != RES_BAD_OP) { + ATOMIC_SET(&res, res_local); + continue; + } + + if(pheat_path) { + pheat_path->status = res_local == RES_OK + ? SDIS_HEAT_PATH_SUCCEED + : SDIS_HEAT_PATH_FAILED; + + /* Check if the path must be saved regarding the register_paths mask */ + if(!(register_paths & (int)pheat_path->status)) { + heat_path_release(pheat_path); + } else { /* Register the sampled path */ + res_local = estimator_add_and_release_heat_path(estimator, pheat_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + } + } + } + if(res != RES_OK) goto error; + + /* Setup the estimated temperature */ + if(out_estimator) { + struct accum acc; + sum_accums(accums, scn->dev->nthreads, &acc); + estimator_setup_realisations_count(estimator, nrealisations, acc.count); + estimator_setup_temperature(estimator, acc.sum, acc.sum2); + } + + if(out_green) { + /* 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); + if(res != RES_OK) goto error; + + /* Finalize the estimated green */ + res = green_function_finalize(green, 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(greens) { + FOR_EACH(i, 0, scn->dev->nthreads) { + if(greens[i]) SDIS(green_function_ref_put(greens[i])); + } + MEM_RM(scn->dev->allocator, greens); + } + if(accums) MEM_RM(scn->dev->allocator, accums); + 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; + } + goto exit; +} + +#include "sdis_Xd_end.h" + diff --git a/src/sdis_solve_probe_boundary_Xd.h b/src/sdis_solve_probe_boundary_Xd.h @@ -0,0 +1,483 @@ +/* Copyright (C) 2016-2019 |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_device_c.h" +#include "sdis_estimator_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 <omp.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Local functions + ******************************************************************************/ +static res_T +XD(solve_probe_boundary) + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t iprim, /* Identifier of the primitive on which the probe lies */ + const double uv[2], /* Parametric coordinates of the probe onto the primitve */ + const double time_range[2], /* Observation time */ + const enum sdis_side side, /* Side of iprim on which the probe lies */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + const int register_paths, /* Combination of enum sdis_heat_path_flag */ + struct sdis_green_function** out_green, + struct sdis_estimator** out_estimator) +{ + 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* accums = NULL; + int64_t irealisation = 0; + size_t i; + ATOMIC res = RES_OK; + + if(!scn || !nrealisations || nrealisations > INT64_MAX || !uv + || fp_to_meter <= 0 || Tref < 0 || (side != SDIS_FRONT && side != SDIS_BACK)) { + res = RES_BAD_ARG; + goto error; + } + if(!out_estimator && !out_green) { + res = RES_BAD_ARG; + goto error; + } + if(out_estimator) { + if(!time_range || time_range[0] < 0 || time_range[1] < time_range[0] + || (time_range[1] > DBL_MAX && time_range[0] != 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 + + /* Check the primitive identifier */ + 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; + } + + /* Check parametric coordinates */ +#if SDIS_XD_DIMENSION == 2 + { + const double v = CLAMP(1.0 - uv[0], 0, 1); + if(uv[0] < 0 || uv[0] > 1 || !eq_eps(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, uv[0]); + res = RES_BAD_ARG; + goto error; + } + } +#else /* SDIS_XD_DIMENSION == 3 */ + { + const double w = CLAMP(1 - uv[0] - uv[1], 0, 1); + if(uv[0] < 0 || uv[1] < 0 || uv[0] > 1 || uv[1] > 1 + || !eq_eps(w + uv[0] + 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, uv[0], uv[1]); + res = RES_BAD_ARG; + goto error; + } + } +#endif + + /* 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; + } + + /* Create the per thread accumulator */ + accums = MEM_CALLOC(scn->dev->allocator, scn->dev->nthreads, sizeof(*accums)); + if(!accums) { 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->dev, &greens[i]); + if(res != RES_OK) goto error; + } + } + + /* Create the estimator */ + if(out_estimator) { + res = estimator_create(scn->dev, SDIS_ESTIMATOR_TEMPERATURE, &estimator); + if(res != RES_OK) goto error; + } + + /* Here we go! Launch the Monte Carlo estimation */ + omp_set_num_threads((int)scn->dev->nthreads); + #pragma omp parallel for schedule(static) + for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { + const int ithread = omp_get_thread_num(); + struct ssp_rng* rng = rngs[ithread]; + struct accum* accum = &accums[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; + struct sdis_heat_path heat_path; + double w = NaN; + double time; + res_T res_local; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ + + if(!out_green) { + time = sample_time(rng, time_range); + if(register_paths) { + heat_path_init(scn->dev->allocator, &heat_path); + pheat_path = &heat_path; + } + } else { + /* Do not take care of the submitted time when registering the green + * function. Simply takes 0 as relative time */ + time = 0; + res_local = green_function_create_path(greens[ithread], &green_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + pgreen_path = &green_path; + } + + res_local = XD(boundary_realisation)(scn, rng, iprim, uv, time, side, + fp_to_meter, Tarad, Tref, pgreen_path, pheat_path, &w); + if(res_local == RES_OK) { + accum->sum += w; + accum->sum2 += w*w; + ++accum->count; + } else if(res_local != RES_BAD_OP) { + ATOMIC_SET(&res, res_local); + continue; + } + + if(pheat_path) { + pheat_path->status = res_local == RES_OK + ? SDIS_HEAT_PATH_SUCCEED + : SDIS_HEAT_PATH_FAILED; + + /* Check if the path must be saved regarding the register_paths mask */ + if(!(register_paths & (int)pheat_path->status)) { + heat_path_release(pheat_path); + } else { /* Register the sampled path */ + res_local = estimator_add_and_release_heat_path(estimator, pheat_path); + if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; } + } + } + } + if(res != RES_OK) goto error; + + /* Setup the estimated temperature */ + if(out_estimator) { + struct accum acc; + sum_accums(accums, scn->dev->nthreads, &acc); + estimator_setup_realisations_count(estimator, nrealisations, acc.count); + estimator_setup_temperature(estimator, acc.sum, acc.sum2); + } + + if(out_green) { + /* 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); + if(res != RES_OK) goto error; + + /* Finalize the estimated green */ + res = green_function_finalize(green, 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(greens) { + FOR_EACH(i, 0, scn->dev->nthreads) { + if(greens[i]) SDIS(green_function_ref_put(greens[i])); + } + MEM_RM(scn->dev->allocator, greens); + } + if(accums) MEM_RM(scn->dev->allocator, accums); + 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; + } + goto exit; +} + +static res_T +XD(solve_probe_boundary_flux) + (struct sdis_scene* scn, + const size_t nrealisations, /* #realisations */ + const size_t iprim, /* Identifier of the primitive on which the probe lies */ + const double uv[2], /* Parametric coordinates of the probe onto the primitve */ + const double time_range[2], /* Observation time */ + const double fp_to_meter, /* Scale from floating point units to meters */ + const double Tarad, /* In Kelvin */ + const double Tref, /* In Kelvin */ + struct sdis_estimator** out_estimator) +{ + struct sdis_estimator* estimator = NULL; + 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_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 */ + int64_t irealisation = 0; + size_t i; + ATOMIC res = RES_OK; + + if(!scn || !nrealisations || nrealisations > INT64_MAX || !uv + || !time_range || time_range[0] < 0 || time_range[1] < time_range[0] + || (time_range[1] > DBL_MAX && time_range[0] != time_range[1]) + || fp_to_meter <= 0 || Tref < 0 + || !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(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; + } + + /* Check parametric coordinates */ + if(scene_is_2d(scn)) { + const double v = CLAMP(1.0 - uv[0], 0, 1); + if(uv[0] < 0 || uv[0] > 1 || !eq_eps(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, uv[0]); + res = RES_BAD_ARG; + goto error; + } + } else { + const double w = CLAMP(1 - uv[0] - uv[1], 0, 1); + if(uv[0] < 0 || uv[1] < 0 || uv[0] > 1 || uv[1] > 1 + || !eq_eps(w + uv[0] + 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, uv[0], 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)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))) { + res = RES_BAD_ARG; + goto error; + } + solid_side = (fmd->type == SDIS_SOLID) ? SDIS_FRONT : SDIS_BACK; + fluid_side = (fmd->type == SDIS_FLUID) ? SDIS_FRONT : SDIS_BACK; + + /* 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; + } + + /* 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_fc); + ALLOC_ACCUMS(acc_fl); + ALLOC_ACCUMS(acc_fr); + #undef ALLOC_ACCUMS + + /* Prebuild the interface fragment */ + res = XD(build_interface_fragment) + (&frag, scn, (unsigned)iprim, 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; + + /* Here we go! Launch the Monte Carlo estimation */ + omp_set_num_threads((int)scn->dev->nthreads); + #pragma omp parallel for schedule(static) + for(irealisation = 0; irealisation < (int64_t)nrealisations; ++irealisation) { + const int ithread = omp_get_thread_num(); + struct ssp_rng* rng = rngs[ithread]; + struct accum* acc_temp = &acc_tp[ithread]; + struct accum* acc_flux = &acc_fl[ithread]; + struct accum* acc_fcon = &acc_fc[ithread]; + struct accum* acc_frad = &acc_fr[ithread]; + double time, epsilon, hc, hr; + int flux_mask = 0; + double T_brf[3] = { 0, 0, 0 }; + res_T res_local; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ + + time = sample_time(rng, time_range); + + /* Compute hr and hc */ + frag.time = time; + epsilon = interface_side_get_emissivity(interf, &frag); + hc = interface_get_convection_coef(interf, &frag); + hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; + + /* Fluid, Radiative and Solid temperatures */ + flux_mask = 0; + if(hr > 0) flux_mask |= FLUX_FLAG_RADIATIVE; + if(hc > 0) flux_mask |= FLUX_FLAG_CONVECTIVE; + res_local = XD(boundary_flux_realisation)(scn, rng, iprim, uv, time, + solid_side, fp_to_meter, Tarad, Tref, flux_mask, T_brf); + if(res_local == RES_OK) { + const double Tboundary = T_brf[0]; + const double Tradiative = T_brf[1]; + const double Tfluid = T_brf[2]; + const double w_conv = hc * (Tboundary - Tfluid); + const double w_rad = hr * (Tboundary - Tradiative); + const double w_total = w_conv + w_rad; + /* Temperature */ + acc_temp->sum += Tboundary; + acc_temp->sum2 += Tboundary*Tboundary; + ++acc_temp->count; + /* Overwall flux */ + acc_flux->sum += w_total; + acc_flux->sum2 += w_total*w_total; + ++acc_flux->count; + /* Convective flux */ + acc_fcon->sum += w_conv; + acc_fcon->sum2 += w_conv*w_conv; + ++acc_fcon->count; + /* Radiative flux */ + acc_frad->sum += w_rad; + acc_frad->sum2 += w_rad*w_rad; + ++acc_frad->count; + } else if(res_local != RES_BAD_OP) { + ATOMIC_SET(&res, res_local); + continue; + } + } + if(res != RES_OK) goto error; + + /* Redux the per thread accumulators */ + sum_accums(acc_tp, scn->dev->nthreads, &acc_tp[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]); + ASSERT(acc_tp[0].count == acc_fl[0].count); + ASSERT(acc_tp[0].count == acc_fr[0].count); + ASSERT(acc_tp[0].count == acc_fc[0].count); + + /* 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_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_TOTAL, acc_fl[0].sum, acc_fl[0].sum2); + +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_tp) MEM_RM(scn->dev->allocator, acc_tp); + 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(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; + } + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_solve_radiative.c b/src/sdis_solve_radiative.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2016-2019 |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 SDIS_XD_DIMENSION 2 +#include "sdis_solve_Xd_radiative.h" + +#define SDIS_XD_DIMENSION 3 +#include "sdis_solve_Xd_radiative.h" diff --git a/src/test_sdis_conducto_radiative.c b/src/test_sdis_conducto_radiative.c @@ -385,6 +385,8 @@ main(int argc, char** argv) FOR_EACH(isimul, 0, nsimuls) { struct sdis_mc T = SDIS_MC_NULL; struct sdis_estimator* estimator; + struct sdis_estimator* estimator2; + struct sdis_green_function* green; double pos[3]; double time_range[2] = { INF, INF }; double ref, u; @@ -396,7 +398,7 @@ main(int argc, char** argv) pos[1] = ssp_rng_uniform_double(rng, -0.9, 0.9); pos[2] = ssp_rng_uniform_double(rng, -0.9, 0.9); - OK(sdis_solve_probe(scn, N, pos, time_range, 1, -1, Tref, &estimator)); + OK(sdis_solve_probe(scn, N, pos, time_range, 1, -1, Tref, 0, &estimator)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); OK(sdis_estimator_get_temperature(estimator, &T)); @@ -411,6 +413,18 @@ main(int argc, char** argv) CHK(nfails < N/1000); CHK(eq_eps(T.E, ref, 2*T.SE) == 1); + /* Check green function */ + OK(sdis_solve_probe_green_function(scn, N, pos, 1, -1, Tref, &green)); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + + OK(sdis_solve_probe(scn, 10, pos, time_range, 1, -1, Tref, + SDIS_HEAT_PATH_ALL, &estimator)); OK(sdis_estimator_ref_put(estimator)); } diff --git a/src/test_sdis_conducto_radiative_2d.c b/src/test_sdis_conducto_radiative_2d.c @@ -391,6 +391,8 @@ main(int argc, char** argv) FOR_EACH(isimul, 0, nsimuls) { struct sdis_mc T = SDIS_MC_NULL; struct sdis_estimator* estimator; + struct sdis_estimator* estimator2; + struct sdis_green_function* green; double pos[2]; double time_range[2] = { INF, INF }; double ref, u; @@ -401,7 +403,7 @@ main(int argc, char** argv) pos[0] = ssp_rng_uniform_double(rng, -0.9, 0.9); pos[1] = ssp_rng_uniform_double(rng, -0.9, 0.9); - OK(sdis_solve_probe(scn, 10000, pos, time_range, 1, -1, Tref, &estimator)); + OK(sdis_solve_probe(scn, 10000, pos, time_range, 1, -1, Tref, 0, &estimator)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); OK(sdis_estimator_get_temperature(estimator, &T)); @@ -416,6 +418,18 @@ main(int argc, char** argv) CHK(nfails < N/1000); CHK(eq_eps(T.E, ref, 3*T.SE) == 1); + /* Check green function */ + OK(sdis_solve_probe_green_function(scn, 10000, pos, 1, -1, Tref, &green)); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + + OK(sdis_solve_probe + (scn, 10, pos, time_range, 1, -1, Tref, SDIS_HEAT_PATH_ALL, &estimator)); OK(sdis_estimator_ref_put(estimator)); } diff --git a/src/test_sdis_convection.c b/src/test_sdis_convection.c @@ -69,27 +69,30 @@ ******************************************************************************/ static double fluid_get_temperature - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* is_stationary) { - (void)data; CHK(vtx != NULL); - return vtx->time <= 0 ? Tf_0 : UNKNOWN_TEMPERATURE; + if(*((int*)sdis_data_cget(is_stationary))) { + return UNKNOWN_TEMPERATURE; + } else { + return vtx->time <= 0 ? Tf_0 : UNKNOWN_TEMPERATURE; + } } static double fluid_get_volumic_mass - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* is_stationary) { - (void)data; + (void)is_stationary; CHK(vtx != NULL); return RHO; } static double fluid_get_calorific_capacity - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* is_stationary) { - (void)data; + (void)is_stationary; CHK(vtx != NULL); return CP; } @@ -167,6 +170,7 @@ main(int argc, char** argv) struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; struct sdis_medium* solid = NULL; + struct sdis_data* is_stationary = NULL; struct sdis_interface* interf_T0 = NULL; struct sdis_interface* interf_T1 = NULL; struct sdis_interface* interf_T2 = NULL; @@ -176,6 +180,8 @@ 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_green_function* green = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_interface_shader interf_shader = DUMMY_INTERFACE_SHADER; @@ -194,10 +200,12 @@ main(int argc, char** argv) OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev)); /* Create the fluid medium */ + OK(sdis_data_create(dev, sizeof(int), ALIGNOF(int), NULL, &is_stationary)); + *((int*)sdis_data_get(is_stationary)) = 0; fluid_shader.temperature = fluid_get_temperature; fluid_shader.calorific_capacity = fluid_get_calorific_capacity; fluid_shader.volumic_mass = fluid_get_volumic_mass; - OK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); + OK(sdis_fluid_create(dev, &fluid_shader, is_stationary, &fluid)); /* Create the solid_medium */ OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -265,17 +273,30 @@ main(int argc, char** argv) time_range[0] = time_range[1] = time; ref = Tf_0 * exp(-nu * time) + Tinf * (1 - exp(-nu * time)); + /* Setup stationary state */ + *((int*)sdis_data_get(is_stationary)) = IS_INF(time); + /* Solve in 3D */ - OK(sdis_solve_probe(box_scn, N, pos, time_range, 1.0, 0, 0, &estimator)); + OK(sdis_solve_probe(box_scn, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); CHK(nfails + nreals == N); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_ref_put(estimator)); printf(" t=%g : %g ~ %g +/- %g\n", time, ref, T.E, T.SE); if(nfails) printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); CHK(eq_eps(T.E, ref, T.SE * 3)); + + if(IS_INF(time)) { /* Check green function */ + OK(sdis_solve_probe_green_function(box_scn, N, pos, 1.0, 0, 0, &green)); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + } + + OK(sdis_estimator_ref_put(estimator)); } /* Test in 2D for various time values. */ @@ -288,22 +309,36 @@ main(int argc, char** argv) time_range[0] = time_range[1] = time; ref = Tf_0 * exp(-nu * time) + Tinf * (1 - exp(-nu * time)); + /* Setup stationnary state */ + *((int*)sdis_data_get(is_stationary)) = IS_INF(time); + /* Solve in 2D */ - OK(sdis_solve_probe(square_scn, N, pos, time_range, 1.0, 0, 0, &estimator)); + OK(sdis_solve_probe(square_scn, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); CHK(nfails + nreals == N); OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_ref_put(estimator)); printf(" t=%g : %g ~ %g +/- %g\n", time, ref, T.E, T.SE); if(nfails) printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); CHK(eq_eps(T.E, ref, T.SE * 3)); + + if(IS_INF(time)) { /* Check green function */ + OK(sdis_solve_probe_green_function(square_scn, N, pos, 1.0, 0, 0, &green)); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + } + + OK(sdis_estimator_ref_put(estimator)); } OK(sdis_scene_ref_put(box_scn)); OK(sdis_scene_ref_put(square_scn)); OK(sdis_device_ref_put(dev)); + OK(sdis_data_ref_put(is_stationary)); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); diff --git a/src/test_sdis_convection_non_uniform.c b/src/test_sdis_convection_non_uniform.c @@ -75,11 +75,15 @@ ******************************************************************************/ static double fluid_get_temperature - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* is_stationary) { - (void)data; CHK(vtx != NULL); - return vtx->time <= 0 ? Tf_0 : UNKNOWN_TEMPERATURE; + CHK(is_stationary != NULL); + if(*((int*)sdis_data_cget(is_stationary))) { + return UNKNOWN_TEMPERATURE; + } else { + return vtx->time <= 0 ? Tf_0 : UNKNOWN_TEMPERATURE; + } } static double @@ -176,6 +180,7 @@ main(int argc, char** argv) struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; struct sdis_medium* solid = NULL; + struct sdis_data* is_stationary = NULL; struct sdis_interface* interf_T0 = NULL; struct sdis_interface* interf_T1 = NULL; struct sdis_interface* interf_T2 = NULL; @@ -185,6 +190,8 @@ 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_green_function* green = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_interface_shader interf_shader = DUMMY_INTERFACE_SHADER; @@ -202,11 +209,14 @@ main(int argc, char** argv) OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev)); + OK(sdis_data_create(dev, sizeof(int), ALIGNOF(int), NULL, &is_stationary)); + *((int*)sdis_data_get(is_stationary)) = 0; + /* Create the fluid medium */ fluid_shader.temperature = fluid_get_temperature; fluid_shader.calorific_capacity = fluid_get_calorific_capacity; fluid_shader.volumic_mass = fluid_get_volumic_mass; - OK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); + OK(sdis_fluid_create(dev, &fluid_shader, is_stationary, &fluid)); /* Create the solid_medium */ OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); @@ -280,17 +290,29 @@ main(int argc, char** argv) time_range[0] = time_range[1] = time; ref = Tf_0 * exp(-nu * time) + Tinf * (1 - exp(-nu * time)); + *((int*)sdis_data_get(is_stationary)) = IS_INF(time); + /* Solve in 3D */ - OK(sdis_solve_probe(box_scn, N, pos, time_range, 1.0, 0, 0, &estimator)); + OK(sdis_solve_probe(box_scn, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); CHK(nfails + nreals == N); OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_ref_put(estimator)); printf(" t=%g : %g ~ %g +/- %g\n", time, ref, T.E, T.SE); if(nfails) printf("#failures = %lu/%lu\n", (unsigned long)nfails,(unsigned long)N); CHK(eq_eps(T.E, ref, T.SE * 3)); + + if(IS_INF(time)) { /* Check green function */ + OK(sdis_solve_probe_green_function(box_scn, N, pos, 1.0, 0, 0, &green)); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + } + + OK(sdis_estimator_ref_put(estimator)); } /* Test in 2D for various time values. */ @@ -303,21 +325,34 @@ main(int argc, char** argv) time_range[0] = time_range[1] = time; ref = Tf_0 * exp(-nu * time) + Tinf * (1 - exp(-nu * time)); - OK(sdis_solve_probe(square_scn, N, pos, time_range, 1.0, 0, 0, &estimator)); + *((int*)sdis_data_get(is_stationary)) = IS_INF(time); + + OK(sdis_solve_probe(square_scn, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); CHK(nfails + nreals == N); OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_ref_put(estimator)); printf(" t=%g : %g ~ %g +/- %g\n", time, ref, T.E, T.SE); if(nfails) printf("#failures = %lu/%lu\n", (unsigned long)nfails,(unsigned long)N); CHK(eq_eps(T.E, ref, T.SE * 3)); + + if(IS_INF(time)) { /* Check green function */ + OK(sdis_solve_probe_green_function(square_scn, N, pos, 1.0, 0, 0, &green)); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + } + + OK(sdis_estimator_ref_put(estimator)); } OK(sdis_scene_ref_put(box_scn)); OK(sdis_scene_ref_put(square_scn)); OK(sdis_device_ref_put(dev)); + OK(sdis_data_ref_put(is_stationary)); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); diff --git a/src/test_sdis_flux.c b/src/test_sdis_flux.c @@ -16,6 +16,7 @@ #include "sdis.h" #include "test_sdis_utils.h" +#include <rsys/clock_time.h> #include <rsys/double3.h> /* @@ -123,13 +124,101 @@ interface_get_flux } /******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +solve(struct sdis_scene* scn, const double pos[]) +{ + char dump[128]; + struct time t0, t1, t2; + struct sdis_estimator* estimator; + struct sdis_estimator* estimator2; + struct sdis_green_function* green; + struct sdis_mc T; + size_t nreals; + size_t nfails; + double ref; + const double time_range[2] = {INF, INF}; + enum sdis_scene_dimension dim; + ASSERT(scn && pos); + + ref = T0 + (1 - pos[0]) * PHI/LAMBDA; + + time_current(&t0); + OK(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); + time_sub(&t0, time_current(&t1), &t0); + time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); + + 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_scene_get_dimension(scn, &dim)); + + switch(dim) { + case SDIS_SCENE_2D: + printf("Temperature at (%g %g) = %g ~ %g +/- %g\n", + SPLIT2(pos), ref, T.E, T.SE); + break; + case SDIS_SCENE_3D: + printf("Temperature at (%g %g %g) = %g ~ %g +/- %g\n", + SPLIT3(pos), ref, T.E, T.SE); + break; + default: FATAL("Unreachable code.\n"); break; + } + printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); + printf("Elapsed time = %s\n\n", dump); + + CHK(nfails + nreals == N); + CHK(nfails < N/1000); + CHK(eq_eps(T.E, ref, T.SE*3)); + + time_current(&t0); + OK(sdis_solve_probe_green_function(scn, N, pos, 1.0, 0, 0, &green)); + time_current(&t1); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + time_current(&t2); + + OK(sdis_estimator_get_realisation_count(estimator2, &nreals)); + OK(sdis_estimator_get_failure_count(estimator2, &nfails)); + OK(sdis_estimator_get_temperature(estimator2, &T)); + + switch(dim) { + case SDIS_SCENE_2D: + printf("Green temperature at (%g %g) = %g ~ %g +/- %g\n", + SPLIT2(pos), ref, T.E, T.SE); + break; + case SDIS_SCENE_3D: + printf("Green temperature at (%g %g %g) = %g ~ %g +/- %g\n", + SPLIT3(pos), ref, T.E, T.SE); + break; + default: FATAL("Unreachable code.\n"); break; + } + printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); + time_sub(&t0, &t1, &t0); + time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); + printf("Green estimation time = %s\n", dump); + time_sub(&t1, &t2, &t1); + time_dump(&t1, TIME_ALL, NULL, dump, sizeof(dump)); + printf("Green solve time = %s\n", dump); + + check_green_function(green); + check_estimator_eq(estimator, estimator2); + + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + + printf("\n"); +} + +/******************************************************************************* * Test ******************************************************************************/ int main(int argc, char** argv) { struct mem_allocator allocator; - struct sdis_mc T = SDIS_MC_NULL; struct sdis_data* data = NULL; struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; @@ -139,7 +228,6 @@ main(int argc, char** argv) struct sdis_interface* interf_phi = NULL; struct sdis_scene* box_scn = NULL; struct sdis_scene* square_scn = NULL; - struct sdis_estimator* estimator = NULL; 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; @@ -147,10 +235,6 @@ main(int argc, char** argv) struct sdis_interface* square_interfaces[4/*#segments*/]; struct interf* interf_props = NULL; double pos[3]; - double time_range[2] = { INF, INF }; - double ref; - size_t nreals; - size_t nfails; (void)argc, (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); @@ -230,34 +314,12 @@ main(int argc, char** argv) OK(sdis_interface_ref_put(interf_T0)); OK(sdis_interface_ref_put(interf_phi)); + /* Solve */ d3_splat(pos, 0.25); - ref = T0 + (1 - pos[0]) * PHI/LAMBDA; - - /* Solve in 3D */ - OK(sdis_solve_probe(box_scn, N, pos, time_range, 1.0, 0, 0, &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_ref_put(estimator)); - printf("Temperature of the box at (%g %g %g) = %g ~ %g +/- %g\n", - SPLIT3(pos), ref, T.E, T.SE); - printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); - CHK(nfails + nreals == N); - CHK(nfails < N/1000); - CHK(eq_eps(T.E, ref, T.SE*3)); - - /* Solve in 2D */ - OK(sdis_solve_probe(square_scn, N, pos, time_range, 1.0, 0, 0, &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_ref_put(estimator)); - printf("Temperature of the square at (%g %g) = %g ~ %g +/- %g\n", - SPLIT2(pos), ref, T.E, T.SE); - printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); - CHK(nfails + nreals == N); - CHK(nfails < N/1000); - CHK(eq_eps(T.E, ref, T.SE*3)); + printf(">> Box scene\n"); + solve(box_scn, pos); + printf(">> Square Scene\n"); + solve(square_scn, pos); OK(sdis_scene_ref_put(box_scn)); OK(sdis_scene_ref_put(square_scn)); @@ -267,5 +329,4 @@ main(int argc, char** argv) 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 @@ -20,13 +20,16 @@ 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; struct sdis_medium* solid = NULL; struct sdis_interface* interf = NULL; + struct sdis_interface* interf2 = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_interface_shader shader = DUMMY_INTERFACE_SHADER; + struct sdis_interface_shader shader2 = SDIS_INTERFACE_SHADER_NULL; (void)argc, (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); @@ -107,6 +110,34 @@ main(int argc, char** argv) OK(sdis_interface_ref_put(interf)); BA(CREATE(dev, solid, fluid, &shader, NULL, &interf)); shader.convection_coef_upper_bound = 0; + + OK(sdis_data_create(dev, 4, 16, NULL, &data)); + OK(CREATE(dev, solid, fluid, &shader, data, &interf)); + OK(CREATE(dev, solid, fluid, &shader, NULL, &interf2)); + + CHK(sdis_interface_get_data(interf) == data); + CHK(sdis_interface_get_data(interf2) == NULL); + CHK(sdis_interface_get_id(interf) != sdis_interface_get_id(interf2)); + + BA(sdis_interface_get_shader(NULL, &shader2)); + BA(sdis_interface_get_shader(interf, NULL)); + OK(sdis_interface_get_shader(interf, &shader2)); + + CHK(shader.convection_coef == shader2.convection_coef); + CHK(shader.convection_coef_upper_bound == shader2.convection_coef_upper_bound); + CHK(shader.front.temperature == shader2.front.temperature); + CHK(shader.front.flux == shader2.front.flux); + CHK(shader.front.emissivity == shader2.front.emissivity); + CHK(shader.front.specular_fraction == shader2.front.specular_fraction); + CHK(shader.back.temperature == shader2.back.temperature); + CHK(shader.back.flux == shader2.back.flux); + CHK(shader.back.emissivity == shader2.back.emissivity); + CHK(shader.back.specular_fraction == shader2.back.specular_fraction); + + OK(sdis_interface_ref_put(interf)); + OK(sdis_interface_ref_put(interf2)); + OK(sdis_data_ref_put(data)); + #undef CREATE OK(sdis_device_ref_put(dev)); diff --git a/src/test_sdis_medium.c b/src/test_sdis_medium.c @@ -28,6 +28,8 @@ main(int argc, char** argv) struct sdis_medium* solid = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; + struct sdis_fluid_shader fluid_shader2 = SDIS_FLUID_SHADER_NULL; + struct sdis_solid_shader solid_shader2 = SDIS_SOLID_SHADER_NULL; (void)argc, (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); @@ -64,9 +66,9 @@ main(int argc, char** argv) fluid_shader.temperature = DUMMY_FLUID_SHADER.temperature; fluid_shader.t0 = -1; - BA(sdis_fluid_create(dev, &fluid_shader, NULL, &solid)); + BA(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); fluid_shader.t0 = INF; - BA(sdis_fluid_create(dev, &fluid_shader, NULL, &solid)); + BA(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); fluid_shader.t0 = DUMMY_FLUID_SHADER.t0; BA(sdis_fluid_create(dev, &SDIS_FLUID_SHADER_NULL, NULL, &fluid)); @@ -82,7 +84,8 @@ main(int argc, char** argv) OK(sdis_solid_create(dev, &solid_shader, data, &solid)); 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; @@ -111,6 +114,36 @@ main(int argc, char** argv) BA(sdis_solid_create(dev, &solid_shader, NULL, &solid)); solid_shader.t0 = DUMMY_SOLID_SHADER.t0; + OK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); + OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); + + CHK(sdis_medium_get_id(fluid) != sdis_medium_get_id(solid)); + + BA(sdis_fluid_get_shader(NULL, &fluid_shader2)); + BA(sdis_fluid_get_shader(fluid, NULL)); + BA(sdis_fluid_get_shader(solid, &fluid_shader2)); + OK(sdis_fluid_get_shader(fluid, &fluid_shader2)); + + CHK(fluid_shader.calorific_capacity == fluid_shader2.calorific_capacity); + CHK(fluid_shader.volumic_mass == fluid_shader2.volumic_mass); + CHK(fluid_shader.temperature == fluid_shader2.temperature); + CHK(fluid_shader.t0 == fluid_shader2.t0); + + BA(sdis_solid_get_shader(NULL, &solid_shader2)); + BA(sdis_solid_get_shader(solid, NULL)); + BA(sdis_solid_get_shader(fluid, &solid_shader2)); + OK(sdis_solid_get_shader(solid, &solid_shader2)); + + 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.volumic_power == solid_shader2.volumic_power); + CHK(solid_shader.temperature == solid_shader2.temperature); + CHK(solid_shader.t0 == solid_shader2.t0); + + OK(sdis_medium_ref_put(solid)); + OK(sdis_medium_ref_put(fluid)); OK(sdis_device_ref_put(dev)); check_memory_allocator(&allocator); diff --git a/src/test_sdis_scene.c b/src/test_sdis_scene.c @@ -19,6 +19,8 @@ #include <rsys/double2.h> #include <rsys/double3.h> #include <rsys/math.h> +#include<star/senc.h> +#include<star/senc2d.h> struct context { const double* positions; @@ -91,8 +93,11 @@ test_scene_3d(struct sdis_device* dev, struct sdis_interface* interf) double lower[3], upper[3]; double uv0[2], uv1[2], pos[3], pos1[3]; struct context ctx; + struct senc_descriptor* descriptor; + struct senc2d_descriptor* descriptor2d; size_t ntris, npos; size_t i; + enum sdis_scene_dimension dim; ctx.positions = box_vertices; ctx.indices = box_indices; @@ -118,6 +123,11 @@ test_scene_3d(struct sdis_device* dev, struct sdis_interface* interf) #undef POS #undef IFA + BA(sdis_scene_get_dimension(NULL, &dim)); + BA(sdis_scene_get_dimension(scn, NULL)); + OK(sdis_scene_get_dimension(scn, &dim)); + CHK(dim == SDIS_SCENE_3D); + BA(sdis_scene_get_aabb(NULL, lower, upper)); BA(sdis_scene_get_aabb(scn, NULL, upper)); BA(sdis_scene_get_aabb(scn, lower, NULL)); @@ -162,6 +172,20 @@ test_scene_3d(struct sdis_device* dev, struct sdis_interface* interf) OK(sdis_scene_get_boundary_position(scn, 6, uv1, pos1)); CHK(!d3_eq_eps(pos1, pos, 1.e-6)); + BA(sdis_scene_get_analysis(NULL, NULL)); + BA(sdis_scene_get_analysis(scn, NULL)); + BA(sdis_scene_get_analysis(NULL, &descriptor)); + OK(sdis_scene_get_analysis(scn, &descriptor)); + OK(senc_descriptor_ref_put(descriptor)); + /* No 2D available */ + BA(sdis_scene_2d_get_analysis(scn, &descriptor2d)); + BA(sdis_scene_release_analysis(NULL)); + OK(sdis_scene_release_analysis(scn)); + /* Already released */ + OK(sdis_scene_release_analysis(scn)); + /* Descriptor released: cannot get it anymore */ + BA(sdis_scene_get_analysis(scn, &descriptor)); + BA(sdis_scene_ref_get(NULL)); OK(sdis_scene_ref_get(scn)); BA(sdis_scene_ref_put(NULL)); @@ -176,8 +200,11 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) double lower[2], upper[2]; double u0, u1, pos[2]; struct context ctx; + struct senc2d_descriptor* descriptor; + struct senc_descriptor* descriptor3d; size_t nsegs, npos; size_t i; + enum sdis_scene_dimension dim; ctx.positions = square_vertices; ctx.indices = square_indices; @@ -203,6 +230,11 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) #undef POS #undef IFA + BA(sdis_scene_get_dimension(NULL, &dim)); + BA(sdis_scene_get_dimension(scn, NULL)); + OK(sdis_scene_get_dimension(scn, &dim)); + CHK(dim == SDIS_SCENE_2D); + BA(sdis_scene_get_aabb(NULL, lower, upper)); BA(sdis_scene_get_aabb(scn, NULL, upper)); BA(sdis_scene_get_aabb(scn, lower, NULL)); @@ -248,6 +280,20 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) OK(sdis_scene_boundary_project_position(scn, 3, pos, &u0)); CHK(eq_eps(u0, 1, 1.e-6)); + BA(sdis_scene_2d_get_analysis(NULL, NULL)); + BA(sdis_scene_2d_get_analysis(scn, NULL)); + BA(sdis_scene_2d_get_analysis(NULL, &descriptor)); + OK(sdis_scene_2d_get_analysis(scn, &descriptor)); + OK(senc2d_descriptor_ref_put(descriptor)); + /* No 3D available */ + BA(sdis_scene_get_analysis(scn, &descriptor3d)); + BA(sdis_scene_release_analysis(NULL)); + OK(sdis_scene_release_analysis(scn)); + /* Already released */ + OK(sdis_scene_release_analysis(scn)); + /* Descriptor released: cannot get it anymore */ + BA(sdis_scene_2d_get_analysis(scn, &descriptor)); + OK(sdis_scene_ref_put(scn)); } diff --git a/src/test_sdis_solve_boundary.c b/src/test_sdis_solve_boundary.c @@ -44,6 +44,7 @@ #define UNKNOWN_TEMPERATURE -1 #define N 10000 /* #realisations */ +#define N_dump 10 /* #dumped paths */ #define Tf 310.0 #define Tb 300.0 @@ -168,6 +169,7 @@ check_estimator int main(int argc, char** argv) { + FILE* fp = NULL; struct mem_allocator allocator; struct sdis_data* data = NULL; struct sdis_device* dev = NULL; @@ -179,6 +181,8 @@ 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_green_function* green = NULL; 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; @@ -197,7 +201,10 @@ 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(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev)); + + /* Temporary file used to dump heat paths */ + CHK((fp = tmpfile()) != NULL); /* Create the fluid medium */ OK(sdis_data_create @@ -286,51 +293,84 @@ main(int argc, char** argv) ref = (H*Tf + LAMBDA * Tb) / (H + LAMBDA); #define SOLVE sdis_solve_probe_boundary + #define GREEN sdis_solve_probe_boundary_green_function #define F SDIS_FRONT uv[0] = 0.3; uv[1] = 0.3; iprim = 6; - BA(SOLVE(NULL, N, iprim, uv, time_range, F, 1.0, 0, 0, &estimator)); - BA(SOLVE(box_scn, 0, iprim, uv, time_range, F, 1.0, 0, 0, &estimator)); - BA(SOLVE(box_scn, N, 12, uv, time_range, F, 1.0, 0, 0, &estimator)); - BA(SOLVE(box_scn, N, iprim, NULL, time_range, F, 1.0, 0, 0, &estimator)); - BA(SOLVE(box_scn, N, iprim, uv, NULL, F, 1.0, 0, 0, &estimator)); - BA(SOLVE(box_scn, N, iprim, uv, time_range, -1, 1.0, 0, 0, &estimator)); - BA(SOLVE(box_scn, N, iprim, uv, time_range, F, 1.0, 0, 0, NULL)); + BA(SOLVE(NULL, N, iprim, uv, time_range, F, 1.0, 0, 0, 0, &estimator)); + BA(SOLVE(box_scn, 0, iprim, uv, time_range, F, 1.0, 0, 0, 0, &estimator)); + BA(SOLVE(box_scn, N, 12, uv, time_range, F, 1.0, 0, 0, 0, &estimator)); + BA(SOLVE(box_scn, N, iprim, NULL, time_range, F, 1.0, 0, 0, 0, &estimator)); + BA(SOLVE(box_scn, N, iprim, uv, NULL, F, 1.0, 0, 0, 0, &estimator)); + BA(SOLVE(box_scn, N, iprim, uv, time_range, -1, 1.0, 0, 0, 0, &estimator)); + BA(SOLVE(box_scn, N, iprim, uv, time_range, F, 1.0, 0, 0, 0, NULL)); tr[0] = tr[1] = -1; - BA(SOLVE(box_scn, N, iprim, uv, tr, F, 1.0, 0, 0, NULL)); + BA(SOLVE(box_scn, N, iprim, uv, tr, F, 1.0, 0, 0, 0, NULL)); tr[0] = 1; - BA(SOLVE(box_scn, N, iprim, uv, tr, F, 1.0, 0, 0, NULL)); + BA(SOLVE(box_scn, N, iprim, uv, tr, F, 1.0, 0, 0, 0, NULL)); tr[1] = 0; - BA(SOLVE(box_scn, N, iprim, uv, tr, F, 1.0, 0, 0, NULL)); + BA(SOLVE(box_scn, N, iprim, uv, tr, F, 1.0, 0, 0, 0, NULL)); - OK(SOLVE(box_scn, N, iprim, uv, time_range, F, 1.0, 0, 0, &estimator)); + OK(SOLVE(box_scn, N, iprim, uv, time_range, F, 1.0, 0, 0, 0, &estimator)); OK(sdis_scene_get_boundary_position(box_scn, iprim, uv, pos)); printf("Boundary temperature of the box at (%g %g %g) = ", SPLIT3(pos)); check_estimator(estimator, N, ref); + + BA(GREEN(NULL, N, iprim, uv, F, 1, 0, 0, &green)); + BA(GREEN(box_scn, 0, iprim, uv, F, 1, 0, 0, &green)); + BA(GREEN(box_scn, N, 12, uv, F, 1, 0, 0, &green)); + BA(GREEN(box_scn, N, iprim, NULL, F, 1, 0, 0, &green)); + BA(GREEN(box_scn, N, iprim, uv, -1, 1, 0, 0, &green)); + BA(GREEN(box_scn, N, iprim, uv, F, 0, 0, 0, &green)); + BA(GREEN(box_scn, N, iprim, uv, F, 1, 0, 0, NULL)); + + OK(GREEN(box_scn, N, iprim, uv, F, 1, 0, 0, &green)); + check_green_function(green); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_estimator(estimator2, N, ref); + + OK(sdis_green_function_ref_put(green)); + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + + /* Dump paths */ + OK(SOLVE(box_scn, N_dump, iprim, uv, time_range, F, 1.0, 0, 0, + SDIS_HEAT_PATH_ALL, &estimator)); + dump_heat_paths(fp, estimator); OK(sdis_estimator_ref_put(estimator)); /* The external fluid cannot have an unknown temperature */ fluid_param->temperature = UNKNOWN_TEMPERATURE; - BA(SOLVE(box_scn, N, iprim, uv, time_range, F, 1.0, 0, 0, &estimator)); + BA(SOLVE(box_scn, N, iprim, uv, time_range, F, 1.0, 0, 0, 0, &estimator)); fluid_param->temperature = Tf; uv[0] = 0.5; iprim = 3; - BA(SOLVE(square_scn, N, 4, uv, time_range, F, 1.0, 0, 0, &estimator)); - OK(SOLVE(square_scn, N, iprim, uv, time_range, F, 1.0, 0, 0, &estimator)); + BA(SOLVE(square_scn, N, 4, uv, time_range, F, 1.0, 0, 0, 0, &estimator)); + OK(SOLVE(square_scn, N, iprim, uv, time_range, F, 1.0, 0, 0, 0, &estimator)); OK(sdis_scene_get_boundary_position(square_scn, iprim, uv, pos)); printf("Boundary temperature of the square at (%g %g) = ", SPLIT2(pos)); check_estimator(estimator, N, ref); + + OK(GREEN(square_scn, N, iprim, uv, F, 1, 0, 0, &green)); + check_green_function(green); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_estimator(estimator2, N, ref); + 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, N, iprim, uv, time_range, F, 1.0, 0, 0, &estimator)); + BA(SOLVE(square_scn, N, iprim, uv, time_range, F, 1.0, 0, 0, 0, &estimator)); fluid_param->temperature = Tf; + #undef F #undef SOLVE + #undef GREEN sides[0] = SDIS_FRONT; sides[1] = SDIS_FRONT; @@ -338,41 +378,78 @@ main(int argc, char** argv) sides[3] = SDIS_FRONT; #define SOLVE sdis_solve_boundary + #define GREEN sdis_solve_boundary_green_function prims[0] = 6; prims[1] = 7; - BA(SOLVE(NULL, N, prims, sides, 2, time_range, 1.0, 0, 0, &estimator)); - BA(SOLVE(box_scn, 0, prims, sides, 2, time_range, 1.0, 0, 0, &estimator)); - BA(SOLVE(box_scn, N, NULL, sides, 2, time_range, 1.0, 0, 0, &estimator)); - BA(SOLVE(box_scn, N, prims, NULL, 2, time_range, 1.0, 0, 0, &estimator)); - BA(SOLVE(box_scn, N, prims, sides, 0, time_range, 1.0, 0, 0, &estimator)); - BA(SOLVE(box_scn, N, prims, sides, 2, NULL, 1.0, 0, 0, &estimator)); - BA(SOLVE(box_scn, N, prims, sides, 2, time_range, 1.0, 0, 0, NULL)); + BA(SOLVE(NULL, N, prims, sides, 2, time_range, 1.0, 0, 0, 0, &estimator)); + BA(SOLVE(box_scn, 0, prims, sides, 2, time_range, 1.0, 0, 0, 0, &estimator)); + BA(SOLVE(box_scn, N, NULL, sides, 2, time_range, 1.0, 0, 0, 0, &estimator)); + BA(SOLVE(box_scn, N, prims, NULL, 2, time_range, 1.0, 0, 0, 0, &estimator)); + BA(SOLVE(box_scn, N, prims, sides, 0, time_range, 1.0, 0, 0, 0, &estimator)); + BA(SOLVE(box_scn, N, prims, sides, 2, NULL, 1.0, 0, 0, 0, &estimator)); + BA(SOLVE(box_scn, N, prims, sides, 2, time_range, 1.0, 0, 0, 0, NULL)); tr[0] = tr[1] = -1; - BA(SOLVE(box_scn, N, prims, sides, 2, tr, 1.0, 0, 0, NULL)); + BA(SOLVE(box_scn, N, prims, sides, 2, tr, 1.0, 0, 0, 0, NULL)); tr[0] = 1; - BA(SOLVE(box_scn, N, prims, sides, 2, tr, 1.0, 0, 0, NULL)); + BA(SOLVE(box_scn, N, prims, sides, 2, tr, 1.0, 0, 0, 0, NULL)); tr[1] = 0; - BA(SOLVE(box_scn, N, prims, sides, 2, tr, 1.0, 0, 0, NULL)); + BA(SOLVE(box_scn, N, prims, sides, 2, tr, 1.0, 0, 0, 0, NULL)); /* Average temperature on the right side of the box */ - OK(SOLVE(box_scn, N, prims, sides, 2, time_range, 1.0, 0, 0, &estimator)); + OK(SOLVE(box_scn, N, prims, sides, 2, time_range, 1.0, 0, 0, 0, &estimator)); printf("Average temperature of the right side of the box = "); check_estimator(estimator, N, ref); + + BA(GREEN(NULL, N, prims, sides, 2, 1.0, 0, 0, &green)); + BA(GREEN(box_scn, 0, prims, sides, 2, 1.0, 0, 0, &green)); + BA(GREEN(box_scn, N, NULL, sides, 2, 1.0, 0, 0, &green)); + BA(GREEN(box_scn, N, prims, NULL, 2, 1.0, 0, 0, &green)); + BA(GREEN(box_scn, N, prims, sides, 0, 1.0, 0, 0, &green)); + BA(GREEN(box_scn, N, prims, sides, 2, 0.0, 0, 0, &green)); + BA(GREEN(box_scn, N, prims, sides, 2, 1.0, 0, 0, NULL)); + + OK(GREEN(box_scn, N, prims, sides, 2, 1.0, 0, 0, &green)); + check_green_function(green); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_estimator(estimator2, N, ref); + + OK(sdis_green_function_ref_put(green)); + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + + /* Dump path */ + OK(SOLVE(box_scn, N_dump, prims, sides, 2, time_range, 1.0, 0, 0, + SDIS_HEAT_PATH_ALL, &estimator)); + dump_heat_paths(fp, estimator); OK(sdis_estimator_ref_put(estimator)); /* Average temperature on the right side of the square */ prims[0] = 3; sides[0] = SDIS_FRONT; - OK(SOLVE(square_scn, N, prims, sides, 1, time_range, 1.0, 0, 0, &estimator)); + OK(SOLVE(square_scn, N, prims, sides, 1, time_range, 1.0, 0, 0, 0, &estimator)); printf("Average temperature of the right side of the square = "); check_estimator(estimator, N, ref); + + OK(GREEN(square_scn, N, prims, sides, 1, 1.0, 0, 0, &green)); + check_green_function(green); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_estimator(estimator2, N, ref); + + OK(sdis_green_function_ref_put(green)); + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + + /* Dump path */ + OK(SOLVE(square_scn, N_dump, prims, sides, 1, time_range, 1.0, 0, 0, + SDIS_HEAT_PATH_ALL, &estimator)); + dump_heat_paths(fp, estimator); OK(sdis_estimator_ref_put(estimator)); /* Check out of bound prims */ prims[0] = 12; - BA(SOLVE(box_scn, N, prims, sides, 2, time_range, 1.0, 0, 0, &estimator)); + BA(SOLVE(box_scn, N, prims, sides, 2, time_range, 1.0, 0, 0, 0, &estimator)); prims[0] = 4; - BA(SOLVE(square_scn, N, prims, sides, 1, time_range, 1.0, 0, 0, &estimator)); + BA(SOLVE(square_scn, N, prims, sides, 1, time_range, 1.0, 0, 0, 0, &estimator)); /* Average temperature on the left+right sides of the box */ prims[0] = 2; @@ -382,24 +459,43 @@ main(int argc, char** argv) ref = (ref + Tb) / 2; - OK(SOLVE(box_scn, N, prims, sides, 4, time_range, 1.0, 0, 0, &estimator)); + OK(SOLVE(box_scn, N, prims, sides, 4, time_range, 1.0, 0, 0, 0, &estimator)); printf("Average temperature of the left+right sides of the box = "); check_estimator(estimator, N, ref); + + OK(GREEN(box_scn, N, prims, sides, 4, 1.0, 0, 0, &green)); + check_green_function(green); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_estimator(estimator2, N, ref); + + 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; - OK(SOLVE(square_scn, N, prims, sides, 2, time_range, 1.0, 0, 0, &estimator)); + OK(SOLVE(square_scn, N, prims, sides, 2, time_range, 1.0, 0, 0, 0, &estimator)); printf("Average temperature of the left+right sides of the square = "); check_estimator(estimator, N, ref); + + OK(GREEN(square_scn, N, prims, sides, 2, 1.0, 0, 0, &green)); + check_green_function(green); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_estimator(estimator2, N, ref); + + OK(sdis_green_function_ref_put(green)); OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); #undef SOLVE + #undef GREEN OK(sdis_scene_ref_put(box_scn)); OK(sdis_scene_ref_put(square_scn)); OK(sdis_device_ref_put(dev)); + CHK(fclose(fp) == 0); + check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); diff --git a/src/test_sdis_solve_boundary_flux.c b/src/test_sdis_solve_boundary_flux.c @@ -195,7 +195,7 @@ check_estimator printf("T = %g ~ %g +/- %g\n", T, V.E, V.SE); CHK(eq_eps(V.E, T, 3 * (V.SE ? V.SE : FLT_EPSILON))); OK(sdis_estimator_get_type(estimator, &type)); - if(type == SDIS_FLUX_ESTIMATOR) { + if(type == SDIS_ESTIMATOR_FLUX) { OK(sdis_estimator_get_convective_flux(estimator, &V)); printf("Convective flux = %g ~ %g +/- %g\n", CF, V.E, V.SE); CHK(eq_eps(V.E, CF, 3 * (V.SE ? V.SE : FLT_EPSILON))); @@ -366,7 +366,7 @@ main(int argc, char** argv) OK(SOLVE(box_scn, N, iprim, uv, time_range, 1.0, Trad, Tref, &estimator)); OK(sdis_estimator_get_type(estimator, &type)); - CHK(type == SDIS_FLUX_ESTIMATOR); + CHK(type == SDIS_ESTIMATOR_FLUX); OK(sdis_scene_get_boundary_position(box_scn, iprim, uv, pos)); printf("Boundary values of the box at (%g %g %g) = ", SPLIT3(pos)); diff --git a/src/test_sdis_solve_medium.c b/src/test_sdis_solve_medium.c @@ -0,0 +1,444 @@ +/* Copyright (C) 2016-2019 |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 <rsys/math.h> +#include <rsys/stretchy_array.h> +#include <star/s3dut.h> + +#include <string.h> + +#define Tf0 300.0 +#define Tf1 330.0 +#define N 1000ul /* #realisations */ +#define Np 10000ul /* #realisations precise */ + +/* + * The scene is composed of 2 super shapes whose temperature is unknown. The + * first super shape is surrounded by a fluid whose temperature is Tf0 while + * the second one is in fluid whose temperature is Tf1. The temperatures of the + * super shape 0 and 1 are thus uniform and equal to Tf0 and Tf1, respectively. + * + * This program performs 2 tests. In the first one, the super shapes 0 and 1 + * have different media; the medium solver thus estimates the + * temperature of one super shape. In the second test, the scene is updated to + * use the same medium for the 2 super shapes. In this case, when invoked on + * the right medium, the estimated temperature T is equal to : + * + * T = Tf0 * V0/(V0 + V1) + Tf1 * V1/(V0 + V1) + * + * with V0 and V1 the volume of the super shapes 0 and 1, respectively. + */ + +/******************************************************************************* + * Geometry + ******************************************************************************/ +struct context { + struct s3dut_mesh_data msh0; + struct s3dut_mesh_data msh1; + struct sdis_interface* interf0; + struct sdis_interface* interf1; +}; + +static void +get_indices(const size_t itri, size_t ids[3], void* context) +{ + const struct context* ctx = context; + /* Note that we swap the indices to ensure that the triangle normals point + * inward the super shape */ + if(itri < ctx->msh0.nprimitives) { + ids[0] = ctx->msh0.indices[itri*3+0]; + ids[2] = ctx->msh0.indices[itri*3+1]; + ids[1] = ctx->msh0.indices[itri*3+2]; + } else { + const size_t itri2 = itri - ctx->msh0.nprimitives; + ids[0] = ctx->msh1.indices[itri2*3+0] + ctx->msh0.nvertices; + ids[2] = ctx->msh1.indices[itri2*3+1] + ctx->msh0.nvertices; + ids[1] = ctx->msh1.indices[itri2*3+2] + ctx->msh0.nvertices; + } +} + +static void +get_position(const size_t ivert, double pos[3], void* context) +{ + const struct context* ctx = context; + if(ivert < ctx->msh0.nvertices) { + pos[0] = ctx->msh0.positions[ivert*3+0] - 2.0; + pos[1] = ctx->msh0.positions[ivert*3+1]; + pos[2] = ctx->msh0.positions[ivert*3+2]; + } else { + const size_t ivert2 = ivert - ctx->msh0.nvertices; + pos[0] = ctx->msh1.positions[ivert2*3+0] + 2.0; + pos[1] = ctx->msh1.positions[ivert2*3+1]; + pos[2] = ctx->msh1.positions[ivert2*3+2]; + } +} + +static void +get_interface(const size_t itri, struct sdis_interface** bound, void* context) +{ + const struct context* ctx = context; + *bound = itri < ctx->msh0.nprimitives ? ctx->interf0 : ctx->interf1; +} + +/******************************************************************************* + * Fluid medium + ******************************************************************************/ +struct fluid { + double temperature; +}; + +static double +fluid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct fluid*)sdis_data_cget(data))->temperature; +} + +/******************************************************************************* + * Solid medium + ******************************************************************************/ +struct solid { + double cp; + double lambda; + double rho; + double delta; + double temperature; +}; + +static double +solid_get_calorific_capacity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->cp; +} + +static double +solid_get_thermal_conductivity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->lambda; +} + +static double +solid_get_volumic_mass + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->rho; +} + +static double +solid_get_delta + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->delta; +} + +static double +solid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->temperature; +} + +struct interf { + double hc; + double epsilon; + double specular_fraction; +}; + +static double +interface_get_convection_coef + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->hc; +} + +static double +interface_get_emissivity + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->epsilon; +} + +static double +interface_get_specular_fraction + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->specular_fraction; +} + +/******************************************************************************* + * Test + ******************************************************************************/ +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_device* dev = NULL; + struct sdis_medium* solid0 = NULL; + struct sdis_medium* solid1 = NULL; + struct sdis_medium* fluid0 = NULL; + struct sdis_medium* fluid1 = NULL; + struct sdis_interface* solid0_fluid0 = NULL; + struct sdis_interface* solid0_fluid1 = NULL; + struct sdis_interface* solid1_fluid1 = NULL; + struct sdis_scene* scn = NULL; + struct sdis_data* data = NULL; + struct sdis_estimator* estimator = NULL; + struct sdis_estimator* estimator2 = NULL; + struct sdis_green_function* green = NULL; + struct fluid* fluid_param = NULL; + struct solid* solid_param = NULL; + struct interf* interface_param = NULL; + 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 context ctx; + const double trange[2] = {INF, INF}; + double ref; + double v, v0, v1; + size_t nreals; + size_t nfails; + size_t ntris; + size_t nverts; + (void)argc, (void)argv; + + OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + OK(sdis_device_create(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 1, &dev)); + + fluid_shader.temperature = fluid_get_temperature; + + /* Create the fluid0 medium */ + OK(sdis_data_create + (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); + fluid_param = sdis_data_get(data); + fluid_param->temperature = Tf0; + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid0)); + OK(sdis_data_ref_put(data)); + + /* Create the fluid1 medium */ + OK(sdis_data_create + (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); + fluid_param = sdis_data_get(data); + fluid_param->temperature = Tf1; + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid1)); + OK(sdis_data_ref_put(data)); + + /* 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.temperature = solid_get_temperature; + + /* Create the solid0 medium */ + OK(sdis_data_create + (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); + solid_param = sdis_data_get(data); + solid_param->cp = 1.0; + solid_param->lambda = 0.1; + solid_param->rho = 1.0; + solid_param->delta = 1.0/20.0; + solid_param->temperature = -1; /* Unknown temperature */ + OK(sdis_solid_create(dev, &solid_shader, data, &solid0)); + OK(sdis_data_ref_put(data)); + + /* Create the solid1 medium */ + OK(sdis_data_create + (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); + solid_param = sdis_data_get(data); + solid_param->cp = 1.0; + solid_param->lambda = 1.0; + solid_param->rho = 1.0; + solid_param->delta = 1.0/20.0; + solid_param->temperature = -1; /* Unknown temperature */ + OK(sdis_solid_create(dev, &solid_shader, data, &solid1)); + OK(sdis_data_ref_put(data)); + + /* Create the interfaces */ + OK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data)); + interface_param = sdis_data_get(data); + interface_param->hc = 0.5; + interface_param->epsilon = 0; + interface_param->specular_fraction = 0; + interface_shader.convection_coef = interface_get_convection_coef; + interface_shader.front = SDIS_INTERFACE_SIDE_SHADER_NULL; + interface_shader.back.temperature = NULL; + interface_shader.back.emissivity = interface_get_emissivity; + interface_shader.back.specular_fraction = interface_get_specular_fraction; + OK(sdis_interface_create + (dev, solid0, fluid0, &interface_shader, data, &solid0_fluid0)); + OK(sdis_interface_create + (dev, solid0, fluid1, &interface_shader, data, &solid0_fluid1)); + OK(sdis_interface_create + (dev, solid1, fluid1, &interface_shader, data, &solid1_fluid1)); + OK(sdis_data_ref_put(data)); + + /* 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_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_mesh_get_data(msh1, &ctx.msh1)); + + /* Create the scene */ + ctx.interf0 = solid0_fluid0; + ctx.interf1 = solid1_fluid1; + ntris = ctx.msh0.nprimitives + ctx.msh1.nprimitives; + nverts = ctx.msh0.nvertices + ctx.msh1.nvertices; +#if 0 + { + double* vertices = NULL; + size_t* indices = NULL; + size_t i; + CHK(vertices = MEM_CALLOC(&allocator, nverts*3, sizeof(*vertices))); + CHK(indices = MEM_CALLOC(&allocator, ntris*3, sizeof(*indices))); + FOR_EACH(i, 0, ntris) get_indices(i, indices + i*3, &ctx); + FOR_EACH(i, 0, nverts) get_position(i, vertices + i*3, &ctx); + dump_mesh(stdout, vertices, nverts, indices, ntris); + MEM_RM(&allocator, vertices); + MEM_RM(&allocator, indices); + } +#endif + + OK(sdis_scene_create(dev, ntris, get_indices, get_interface, nverts, + get_position, &ctx, &scn)); + + BA(sdis_scene_get_medium_spread(NULL, solid0, &v0)); + BA(sdis_scene_get_medium_spread(scn, NULL, &v0)); + BA(sdis_scene_get_medium_spread(scn, solid0, NULL)); + OK(sdis_scene_get_medium_spread(scn, solid0, &v0)); + CHK(v0 > 0); + OK(sdis_scene_get_medium_spread(scn, solid1, &v1)); + CHK(v1 > 0); + OK(sdis_scene_get_medium_spread(scn, fluid0, &v)); + CHK(v == 0); + OK(sdis_scene_get_medium_spread(scn, fluid1, &v)); + CHK(v == 0); + + BA(sdis_solve_medium(NULL, N, solid0, trange, 1.f, -1, 0, 0, &estimator)); + BA(sdis_solve_medium(scn, 0, solid0, trange, 1.f, -1, 0, 0, &estimator)); + BA(sdis_solve_medium(scn, N, NULL, trange, 1.f, -1, 0, 0, &estimator)); + BA(sdis_solve_medium(scn, N, solid0, NULL, 1.f, -1, 0, 0, &estimator)); + BA(sdis_solve_medium(scn, N, solid0, trange, 0.f, -1, 0, 0, &estimator)); + BA(sdis_solve_medium(scn, N, solid0, trange, 1.f, -1, 0, 0, NULL)); + OK(sdis_solve_medium(scn, N, solid0, trange, 1.f, -1, 0, 0, &estimator)); + + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + OK(sdis_estimator_get_temperature(estimator, &T)); + printf("Shape0 temperature = "STR(Tf0)" ~ %g +/- %g\n", T.E, T.SE); + printf("#failures = %lu/%lu\n", (unsigned long)nfails, N); + CHK(eq_eps(T.E, Tf0, T.SE)); + CHK(nreals + nfails == N); + OK(sdis_estimator_ref_put(estimator)); + + OK(sdis_solve_medium(scn, N, solid1, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + OK(sdis_estimator_get_temperature(estimator, &T)); + printf("Shape1 temperature = "STR(Tf1)" ~ %g +/- %g\n", T.E, T.SE); + printf("#failures = %lu/%lu\n", (unsigned long)nfails, N); + CHK(eq_eps(T.E, Tf1, T.SE)); + CHK(nreals + nfails == N); + OK(sdis_estimator_ref_put(estimator)); + +#if 0 + OK(sdis_solve_medium(scn, 1, solid1, trange, 1.f, -1, 0, + SDIS_HEAT_PATH_ALL, &estimator)); + dump_heat_paths(stderr, estimator); + OK(sdis_estimator_ref_put(estimator)); +#endif + + /* Create a new scene with the same medium in the 2 super shapes */ + OK(sdis_scene_ref_put(scn)); + ctx.interf0 = solid0_fluid0; + ctx.interf1 = solid0_fluid1; + OK(sdis_scene_create(dev, ntris, get_indices, get_interface, nverts, + get_position, &ctx, &scn)); + + OK(sdis_scene_get_medium_spread(scn, solid0, &v)); + CHK(eq_eps(v, v0+v1, 1.e-6)); + + BA(sdis_solve_medium(scn, N, solid1, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_solve_medium(scn, Np, solid0, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + 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("#failures = %lu/%lu\n", (unsigned long)nfails, Np); + CHK(eq_eps(T.E, ref, T.SE*3)); + + /* Solve green */ + BA(sdis_solve_medium_green_function(NULL, Np, solid0, 1.0, 0, 0, &green)); + BA(sdis_solve_medium_green_function(scn, 0, solid0, 1.0, 0, 0, &green)); + BA(sdis_solve_medium_green_function(scn, Np, NULL, 1.0, 0, 0, &green)); + BA(sdis_solve_medium_green_function(scn, Np, solid0, 0.0, 0, 0, &green)); + BA(sdis_solve_medium_green_function(scn, Np, solid0, 1.0, 0, -1, &green)); + BA(sdis_solve_medium_green_function(scn, Np, solid0, 1.0, 0, 0, NULL)); + BA(sdis_solve_medium_green_function(scn, Np, solid1, 1.0, 0, 0, &green)); + OK(sdis_solve_medium_green_function(scn, Np, solid0, 1.0, 0, 0, &green)); + + OK(sdis_green_function_solve(green, trange, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + + OK(sdis_green_function_ref_put(green)); + + 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)); + OK(sdis_medium_ref_put(solid1)); + OK(sdis_interface_ref_put(solid0_fluid0)); + OK(sdis_interface_ref_put(solid0_fluid1)); + OK(sdis_interface_ref_put(solid1_fluid1)); + 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_medium_2d.c b/src/test_sdis_solve_medium_2d.c @@ -0,0 +1,418 @@ +/* Copyright (C) 2016-2019 |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 <rsys/stretchy_array.h> +#include <rsys/math.h> + +#include <string.h> + +#define Tf0 300.0 +#define Tf1 330.0 +#define N 1000ul /* #realisations */ +#define Np 10000ul /* #realisations precise */ + +/* + * The scene is composed of a square and a disk whose temperature is unknown. + * The square is surrounded by a fluid whose temperature is Tf0 while the disk + * is in a fluid whose temperature is Tf1. The temperature of the square + * and the disk are thus uniform and equal to Tf0 and Tf1, respectively. + * + * # # Tf1 +---------+ + * # # _\ | | _\ Tf0 + * # # / / | | / / + * # # \__/ | | \__/ + * # # | | + * # # +---------+ + * + * This program performs 2 tests. In the first one, the square and the disk + * have different media; the medium solver estimates the temperature of + * the square or the one of the disk. In the second test, the scene is updated + * to use the same medium for the 2 shapes. When invoked on + * the right medium, the estimated temperature T is equal to : + * + * T = Tf0 * A0/(A0 + A1) + Tf1 * A1/(A0 + A1) + * + * with A0 and A1 the area of the shape and the area of the disk, respectively. + */ + +/******************************************************************************* + * Geometry + ******************************************************************************/ +struct context { + const double* positions; + const size_t* indices; + size_t nsegments_interf0; /* #segments of the interface 0 */ + struct sdis_interface* interf0; + struct sdis_interface* interf1; +}; + +static void +get_indices(const size_t iseg, size_t ids[2], void* context) +{ + const struct context* ctx = context; + ids[0] = ctx->indices[iseg*2+0]; + ids[1] = ctx->indices[iseg*2+1]; +} + +static void +get_position(const size_t ivert, double pos[2], void* context) +{ + const struct context* ctx = context; + pos[0] = ctx->positions[ivert*2+0]; + pos[1] = ctx->positions[ivert*2+1]; +} + +static void +get_interface(const size_t iseg, struct sdis_interface** bound, void* context) +{ + const struct context* ctx = context; + *bound = iseg < ctx->nsegments_interf0 ? ctx->interf0 : ctx->interf1; +} + +/******************************************************************************* + * Fluid medium + ******************************************************************************/ +struct fluid { + double temperature; +}; + +static double +fluid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct fluid*)sdis_data_cget(data))->temperature; +} + +/******************************************************************************* + * Solid medium + ******************************************************************************/ +struct solid { + double cp; + double lambda; + double rho; + double delta; + double temperature; +}; + +static double +solid_get_calorific_capacity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->cp; +} + +static double +solid_get_thermal_conductivity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->lambda; +} + +static double +solid_get_volumic_mass + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->rho; +} + +static double +solid_get_delta + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->delta; +} + +static double +solid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + CHK(data != NULL && vtx != NULL); + return ((const struct solid*)sdis_data_cget(data))->temperature; +} + +struct interf { + double hc; + double epsilon; + double specular_fraction; +}; + +static double +interface_get_convection_coef + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->hc; +} + +static double +interface_get_emissivity + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->epsilon; +} + +static double +interface_get_specular_fraction + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->specular_fraction; +} + +/******************************************************************************* + * Test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_device* dev = NULL; + struct sdis_medium* solid0 = NULL; + struct sdis_medium* solid1 = NULL; + struct sdis_medium* fluid0 = NULL; + struct sdis_medium* fluid1 = NULL; + struct sdis_interface* solid0_fluid0 = NULL; + struct sdis_interface* solid0_fluid1 = NULL; + struct sdis_interface* solid1_fluid1 = NULL; + struct sdis_scene* scn = NULL; + struct sdis_data* data = NULL; + struct sdis_estimator* estimator = NULL; + struct sdis_estimator* estimator2 = NULL; + struct sdis_green_function* green = NULL; + struct fluid* fluid_param = NULL; + struct solid* solid_param = NULL; + struct interf* interface_param = NULL; + 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 context ctx; + const double trange[2] = {INF, INF}; + double a, a0, a1; + double ref; + double* positions = NULL; + size_t* indices = NULL; + size_t nverts; + size_t nreals; + size_t nfails; + 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)); + + fluid_shader.temperature = fluid_get_temperature; + + /* Create the fluid0 medium */ + OK(sdis_data_create + (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); + fluid_param = sdis_data_get(data); + fluid_param->temperature = Tf0; + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid0)); + OK(sdis_data_ref_put(data)); + + /* Create the fluid1 medium */ + OK(sdis_data_create + (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); + fluid_param = sdis_data_get(data); + fluid_param->temperature = Tf1; + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid1)); + OK(sdis_data_ref_put(data)); + + /* 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.temperature = solid_get_temperature; + + /* Create the solid0 medium */ + OK(sdis_data_create + (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); + solid_param = sdis_data_get(data); + solid_param->cp = 1.0; + solid_param->lambda = 0.1; + solid_param->rho = 1.0; + solid_param->delta = 1.0/20.0; + solid_param->temperature = -1; /* Unknown temperature */ + OK(sdis_solid_create(dev, &solid_shader, data, &solid0)); + OK(sdis_data_ref_put(data)); + + /* Create the solid1 medium */ + OK(sdis_data_create + (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); + solid_param = sdis_data_get(data); + solid_param->cp = 1.0; + solid_param->lambda = 1.0; + solid_param->rho = 1.0; + solid_param->delta = 1.0/20.0; + solid_param->temperature = -1; /* Unknown temperature */ + OK(sdis_solid_create(dev, &solid_shader, data, &solid1)); + OK(sdis_data_ref_put(data)); + + /* Create the interfaces */ + OK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data)); + interface_param = sdis_data_get(data); + interface_param->hc = 0.5; + interface_param->epsilon = 0; + interface_param->specular_fraction = 0; + interface_shader.convection_coef = interface_get_convection_coef; + interface_shader.front = SDIS_INTERFACE_SIDE_SHADER_NULL; + interface_shader.back.temperature = NULL; + interface_shader.back.emissivity = interface_get_emissivity; + interface_shader.back.specular_fraction = interface_get_specular_fraction; + OK(sdis_interface_create + (dev, solid0, fluid0, &interface_shader, data, &solid0_fluid0)); + OK(sdis_interface_create + (dev, solid0, fluid1, &interface_shader, data, &solid0_fluid1)); + OK(sdis_interface_create + (dev, solid1, fluid1, &interface_shader, data, &solid1_fluid1)); + OK(sdis_data_ref_put(data)); + + /* Setup the square geometry */ + sa_add(positions, square_nvertices*2); + sa_add(indices, square_nsegments*2); + memcpy(positions, square_vertices, square_nvertices*sizeof(double[2])); + memcpy(indices, square_indices, square_nsegments*sizeof(size_t[2])); + + /* Transate the square in X */ + FOR_EACH(i, 0, square_nvertices) positions[i*2] += 2; + + /* Setup a disk */ + nverts = 64; + FOR_EACH(i, 0, nverts) { + const double theta = (double)i * (2*PI)/(double)nverts; + const double r = 1; /* Radius */ + const double x = cos(theta) * r - 2/* X translation */; + const double y = sin(theta) * r + 0.5/* Y translation */; + sa_push(positions, x); + sa_push(positions, y); + } + FOR_EACH(i, 0, nverts) { + const size_t i0 = i + square_nvertices; + const size_t i1 = (i+1) % nverts + square_nvertices; + /* Flip the ids to ensure that the normals point inward the disk */ + sa_push(indices, i1); + sa_push(indices, i0); + } + + /* Create the scene */ + ctx.positions = positions; + ctx.indices = indices; + ctx.nsegments_interf0 = square_nsegments; + ctx.interf0 = solid0_fluid0; + ctx.interf1 = solid1_fluid1; + OK(sdis_scene_2d_create(dev, sa_size(indices)/2, get_indices, get_interface, + sa_size(positions)/2, get_position, &ctx, &scn)); + + OK(sdis_scene_get_medium_spread(scn, solid0, &a0)); + CHK(eq_eps(a0, 1.0, 1.e-6)); + OK(sdis_scene_get_medium_spread(scn, solid1, &a1)); + /* Rough estimation since the disk is coarsely discretized */ + CHK(eq_eps(a1, PI, 1.e-1)); + + /* Estimate the temperature of the square */ + OK(sdis_solve_medium(scn, N, solid0, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + 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("#failures = %lu / %lu\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 */ + OK(sdis_solve_medium(scn, N, solid1, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + 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("#failures = %lu / %lu\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)); + ctx.interf0 = solid0_fluid0; + ctx.interf1 = solid0_fluid1; + OK(sdis_scene_2d_create(dev, sa_size(indices)/2, get_indices, get_interface, + sa_size(positions)/2, get_position, &ctx, &scn)); + + OK(sdis_scene_get_medium_spread(scn, solid0, &a)); + CHK(eq_eps(a, a0+a1, 1.e-6)); + + /* Estimate the temperature of the square and disk shapes */ + BA(sdis_solve_medium(scn, N, solid1, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_solve_medium(scn, Np, solid0, trange, 1.f, -1, 0, 0, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + 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("#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, Np, solid0, 1.0, 0, 0, &green)); + BA(sdis_solve_medium_green_function(scn, 0, solid0, 1.0, 0, 0, &green)); + BA(sdis_solve_medium_green_function(scn, Np, NULL, 1.0, 0, 0, &green)); + BA(sdis_solve_medium_green_function(scn, Np, solid0, 0.0, 0, 0, &green)); + BA(sdis_solve_medium_green_function(scn, Np, solid0, 1.0, 0, -1, &green)); + BA(sdis_solve_medium_green_function(scn, Np, solid0, 1.0, 0, 0, NULL)); + BA(sdis_solve_medium_green_function(scn, Np, solid1, 1.0, 0, 0, &green)); + OK(sdis_solve_medium_green_function(scn, Np, solid0, 1.0, 0, 0, &green)); + + OK(sdis_green_function_solve(green, trange, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + + OK(sdis_green_function_ref_put(green)); + + 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)); + OK(sdis_medium_ref_put(fluid1)); + OK(sdis_interface_ref_put(solid0_fluid0)); + OK(sdis_interface_ref_put(solid0_fluid1)); + OK(sdis_interface_ref_put(solid1_fluid1)); + OK(sdis_scene_ref_put(scn)); + + 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 @@ -167,6 +167,80 @@ interface_get_specular_fraction } /******************************************************************************* + * Helper functions + ******************************************************************************/ +struct dump_path_context { + FILE* stream; + size_t offset; + size_t nfailures; + size_t nsuccesses; +}; +static const struct dump_path_context DUMP_PATH_CONTEXT_NULL = {NULL, 0, 0, 0}; + +static res_T +dump_vertex_pos(const struct sdis_heat_vertex* vert, void* context) +{ + struct dump_path_context* ctx = context; + CHK(vert && context); + fprintf(ctx->stream, "v %g %g %g\n", SPLIT3(vert->P)); + return RES_OK; +} + +static res_T +process_heat_path(const struct sdis_heat_path* path, void* context) +{ + struct dump_path_context* ctx = context; + struct sdis_heat_vertex vert = SDIS_HEAT_VERTEX_NULL; + enum sdis_heat_path_flag status = SDIS_HEAT_PATH_NONE; + size_t i; + size_t n; + (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_status(NULL, &status)); + BA(sdis_heat_path_get_status(path, NULL)); + OK(sdis_heat_path_get_status(path, &status)); + CHK(status == SDIS_HEAT_PATH_SUCCEED || status == SDIS_HEAT_PATH_FAILED); + + switch(status) { + case SDIS_HEAT_PATH_FAILED: ++ctx->nfailures; break; + case SDIS_HEAT_PATH_SUCCEED: ++ctx->nsuccesses; break; + 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)); + + FOR_EACH(i, 0, n) { + OK(sdis_heat_path_get_vertex(path, 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)); + + FOR_EACH(i, 0, n-1) { + fprintf(ctx->stream, "l %lu %lu\n", + (unsigned long)(i+1 + ctx->offset), + (unsigned long)(i+2 + ctx->offset)); + } + + ctx->offset += n; + + return RES_OK; +} + +/******************************************************************************* * Test ******************************************************************************/ int @@ -182,9 +256,13 @@ main(int argc, char** argv) struct sdis_scene* scn = NULL; struct sdis_data* data = NULL; struct sdis_estimator* estimator = NULL; + struct sdis_estimator* estimator2 = NULL; + struct sdis_green_function* green = NULL; + const struct sdis_heat_path* path = NULL; 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 dump_path_context dump_ctx = DUMP_PATH_CONTEXT_NULL; struct context ctx; struct fluid* fluid_param; struct solid* solid_param; @@ -194,12 +272,14 @@ main(int argc, char** argv) double time_range[2]; double ref; const size_t N = 1000; + const size_t N_dump = 10; size_t nreals; size_t nfails; + size_t n; (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(NULL, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev)); /* Create the fluid medium */ OK(sdis_data_create @@ -261,18 +341,18 @@ main(int argc, char** argv) pos[1] = 0.5; pos[2] = 0.5; time_range[0] = time_range[1] = INF; - BA(sdis_solve_probe(NULL, N, pos, time_range, 1.0, 0, 0, &estimator)); - BA(sdis_solve_probe(scn, 0, pos, time_range, 1.0, 0, 0, &estimator)); - BA(sdis_solve_probe(scn, N, NULL, time_range, 1.0, 0, 0, &estimator)); - BA(sdis_solve_probe(scn, N, pos, time_range, 0, 0, 0, &estimator)); - BA(sdis_solve_probe(scn, N, pos, time_range, 0, 0, -1, &estimator)); - BA(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, NULL)); - OK(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, &estimator)); + BA(sdis_solve_probe(NULL, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); + BA(sdis_solve_probe(scn, 0, pos, time_range, 1.0, 0, 0, 0, &estimator)); + BA(sdis_solve_probe(scn, N, NULL, time_range, 1.0, 0, 0, 0, &estimator)); + BA(sdis_solve_probe(scn, N, pos, time_range, 0, 0, 0, 0, &estimator)); + BA(sdis_solve_probe(scn, N, pos, time_range, 0, 0, -1, 0, &estimator)); + BA(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, 0, NULL)); + OK(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); BA(sdis_estimator_get_type(estimator, NULL)); BA(sdis_estimator_get_type(NULL, &type)); OK(sdis_estimator_get_type(estimator, &type)); - CHK(type == SDIS_TEMPERATURE_ESTIMATOR); + CHK(type == SDIS_ESTIMATOR_TEMPERATURE); /* Fluxes aren't available after sdis_solve_probe */ BA(sdis_estimator_get_convective_flux(estimator, NULL)); @@ -316,8 +396,59 @@ main(int argc, char** argv) /* The external fluid cannot have an unknown temperature */ fluid_param->temperature = -1; - BA(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, &estimator)); + BA(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); + fluid_param->temperature = 300; + OK(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); + + BA(sdis_solve_probe_green_function(NULL, N, pos, 1.0, 0, 0, &green)); + BA(sdis_solve_probe_green_function(scn, 0, pos, 1.0, 0, 0, &green)); + BA(sdis_solve_probe_green_function(scn, N, NULL, 1.0, 0, 0, &green)); + BA(sdis_solve_probe_green_function(scn, N, pos, 0.0, 0, 0, &green)); + BA(sdis_solve_probe_green_function(scn, N, pos, 1.0, 0, -1, &green)); + BA(sdis_solve_probe_green_function(scn, N, pos, 1.0, 0, 0, NULL)); + OK(sdis_solve_probe_green_function(scn, N, pos, 1.0, 0, 0, &green)); + + BA(sdis_green_function_solve(NULL, time_range, &estimator2)); + BA(sdis_green_function_solve(green, NULL, &estimator2)); + BA(sdis_green_function_solve(green, time_range, NULL)); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + + check_green_function(green); + check_estimator_eq(estimator, estimator2); + + BA(sdis_green_function_ref_get(NULL)); + OK(sdis_green_function_ref_get(green)); + BA(sdis_green_function_ref_put(NULL)); + 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_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); + BA(sdis_estimator_get_paths_count(NULL, &n)); + BA(sdis_estimator_get_paths_count(estimator, NULL)); + OK(sdis_estimator_get_paths_count(estimator, &n)); + CHK(n == 0); + OK(sdis_estimator_ref_put(estimator)); + + OK(sdis_solve_probe(scn, N_dump, pos, time_range, 1.0, 0, 0, + SDIS_HEAT_PATH_ALL, &estimator)); + OK(sdis_estimator_get_paths_count(estimator, &n)); + CHK(n == N_dump); + + BA(sdis_estimator_get_path(NULL, 0, &path)); + BA(sdis_estimator_get_path(estimator, n, &path)); + BA(sdis_estimator_get_path(estimator, 0, NULL)); + OK(sdis_estimator_get_path(estimator, 0, &path)); + + dump_ctx.stream = stderr; + BA(sdis_estimator_for_each_path(NULL, process_heat_path, &dump_ctx)); + BA(sdis_estimator_for_each_path(estimator, NULL, &dump_ctx)); + OK(sdis_estimator_for_each_path(estimator, process_heat_path, &dump_ctx)); + + OK(sdis_estimator_ref_put(estimator)); OK(sdis_scene_ref_put(scn)); OK(sdis_device_ref_put(dev)); diff --git a/src/test_sdis_solve_probe2.c b/src/test_sdis_solve_probe2.c @@ -149,12 +149,14 @@ main(int argc, char** argv) struct sdis_device* dev = NULL; struct sdis_data* data = NULL; struct sdis_estimator* estimator = NULL; + struct sdis_estimator* estimator2 = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* fluid = NULL; struct sdis_interface* Tnone = NULL; struct sdis_interface* T300 = NULL; struct sdis_interface* T350 = NULL; struct sdis_scene* scn = NULL; + struct sdis_green_function* green = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_interface_shader interface_shader = DUMMY_INTERFACE_SHADER; @@ -240,7 +242,7 @@ main(int argc, char** argv) pos[1] = 0.5; pos[2] = 0.5; time_range[0] = time_range[1] = INF; - OK(sdis_solve_probe( scn, N, pos, time_range, 1.0, -1, 0, &estimator)); + OK(sdis_solve_probe(scn, N, pos, time_range, 1.0, -1, 0, 0, &estimator)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); OK(sdis_estimator_get_temperature(estimator, &T)); @@ -256,8 +258,16 @@ main(int argc, char** argv) CHK(nfails < N/1000); CHK(eq_eps(T.E, ref, 3*T.SE)); + /* Check green */ + OK(sdis_solve_probe_green_function(scn, N, pos, 1, -1, 0, &green)); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + /* Release data */ OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); OK(sdis_scene_ref_put(scn)); OK(sdis_device_ref_put(dev)); diff --git a/src/test_sdis_solve_probe2_2d.c b/src/test_sdis_solve_probe2_2d.c @@ -146,12 +146,14 @@ main(int argc, char** argv) struct sdis_device* dev = NULL; struct sdis_data* data = NULL; struct sdis_estimator* estimator = NULL; + struct sdis_estimator* estimator2 = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* fluid = NULL; struct sdis_interface* Tnone = NULL; struct sdis_interface* T300 = NULL; struct sdis_interface* T350 = NULL; struct sdis_scene* scn = NULL; + struct sdis_green_function* green = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_interface_shader interface_shader = DUMMY_INTERFACE_SHADER; @@ -238,7 +240,7 @@ main(int argc, char** argv) pos[0] = 0.5; pos[1] = 0.5; time_range[0] = time_range[1] = INF; - OK(sdis_solve_probe( scn, N, pos, time_range, 1.0, -1, 0, &estimator)); + OK(sdis_solve_probe(scn, N, pos, time_range, 1.0, -1, 0, 0, &estimator)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); OK(sdis_estimator_get_temperature(estimator, &T)); @@ -254,9 +256,17 @@ main(int argc, char** argv) CHK(nfails < N/1000); CHK(eq_eps(T.E, ref, T.SE*2)); + /* Check green function */ + OK(sdis_solve_probe_green_function(scn, N, pos, 1.0, -1, 0, &green)); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + /* Release data */ OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); OK(sdis_scene_ref_put(scn)); + OK(sdis_green_function_ref_put(green)); OK(sdis_device_ref_put(dev)); check_memory_allocator(&allocator); diff --git a/src/test_sdis_solve_probe3.c b/src/test_sdis_solve_probe3.c @@ -171,6 +171,7 @@ main(int argc, char** argv) struct sdis_device* dev = NULL; struct sdis_data* data = NULL; struct sdis_estimator* estimator = NULL; + struct sdis_estimator* estimator2 = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* fluid = NULL; struct sdis_interface* Tnone = NULL; @@ -178,6 +179,7 @@ main(int argc, char** argv) struct sdis_interface* T350 = NULL; struct sdis_interface* solid_solid = NULL; struct sdis_scene* scn = NULL; + struct sdis_green_function* green = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_interface_shader interface_shader = DUMMY_INTERFACE_SHADER; @@ -296,7 +298,7 @@ main(int argc, char** argv) pos[1] = 0.5; pos[2] = 0.5; time_range[0] = time_range[1] = INF; - OK(sdis_solve_probe( scn, N, pos, time_range, 1.0, -1, 0, &estimator)); + OK(sdis_solve_probe( scn, N, pos, time_range, 1.0, -1, 0, 0, &estimator)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); OK(sdis_estimator_get_temperature(estimator, &T)); @@ -312,8 +314,16 @@ main(int argc, char** argv) CHK(nfails < N/1000); CHK(eq_eps(T.E, ref, 2*T.SE)); + /* Check green function */ + OK(sdis_solve_probe_green_function(scn, N, pos, 1.0, -1, 0, &green)); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + /* Release data */ OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); OK(sdis_scene_ref_put(scn)); OK(sdis_device_ref_put(dev)); diff --git a/src/test_sdis_solve_probe3_2d.c b/src/test_sdis_solve_probe3_2d.c @@ -168,6 +168,7 @@ main(int argc, char** argv) struct sdis_device* dev = NULL; struct sdis_data* data = NULL; struct sdis_estimator* estimator = NULL; + struct sdis_estimator* estimator2 = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* fluid = NULL; struct sdis_interface* Tnone = NULL; @@ -175,6 +176,7 @@ main(int argc, char** argv) struct sdis_interface* T350 = NULL; struct sdis_interface* solid_solid = NULL; struct sdis_scene* scn = NULL; + struct sdis_green_function* green = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_interface_shader interface_shader = DUMMY_INTERFACE_SHADER; @@ -289,7 +291,7 @@ main(int argc, char** argv) pos[0] = 0.5; pos[1] = 0.5; time_range[0] = time_range[1] = INF; - OK(sdis_solve_probe( scn, N, pos, time_range, 1.0, -1, 0, &estimator)); + OK(sdis_solve_probe(scn, N, pos, time_range, 1.0, -1, 0, 0, &estimator)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); OK(sdis_estimator_get_temperature(estimator, &T)); @@ -305,8 +307,16 @@ main(int argc, char** argv) CHK(nfails < N/1000); CHK(eq_eps(T.E, ref, 3*T.SE)); + /* Check green function */ + OK(sdis_solve_probe_green_function(scn, N, pos, 1.0, -1, 0, &green)); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + /* Release data */ OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); OK(sdis_scene_ref_put(scn)); OK(sdis_device_ref_put(dev)); diff --git a/src/test_sdis_solve_probe_2d.c b/src/test_sdis_solve_probe_2d.c @@ -142,6 +142,8 @@ main(int argc, char** argv) struct sdis_scene* scn = NULL; struct sdis_data* data = NULL; struct sdis_estimator* estimator = NULL; + struct sdis_estimator* estimator2 = NULL; + struct sdis_green_function* green = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_interface_shader interface_shader = DUMMY_INTERFACE_SHADER; @@ -199,7 +201,7 @@ main(int argc, char** argv) pos[0] = 0.5; pos[1] = 0.5; time_range[0] = time_range[1] = INF; - OK(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, &estimator)); + OK(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); @@ -214,12 +216,19 @@ main(int argc, char** argv) CHK(nfails < N/1000); CHK(eq_eps(T.E, ref, T.SE)); + OK(sdis_solve_probe_green_function(scn, N, pos, 1.0, 0, 0, &green)); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + check_green_function(green); + check_estimator_eq(estimator, estimator2); + 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 = -1; - BA(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, &estimator)); + BA(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); OK(sdis_scene_ref_put(scn)); OK(sdis_device_ref_put(dev)); diff --git a/src/test_sdis_utils.c b/src/test_sdis_utils.c @@ -0,0 +1,361 @@ +/* Copyright (C) 2016-2019 |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 "test_sdis_utils.h" +#include <rsys/math.h> + +enum heat_vertex_attrib { + HEAT_VERTEX_WEIGHT, + HEAT_VERTEX_TIME, + HEAT_VERTEX_TYPE +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +struct green_accum { double sum, sum2; }; + +static res_T +count_green_paths(struct sdis_green_path* path, void* ctx) +{ + CHK(path && ctx); + *((size_t*)ctx) += 1; + return RES_OK; +} + +static res_T +accum_power_terms(struct sdis_medium* mdm, const double power_term, void* ctx) +{ + struct sdis_solid_shader shader = SDIS_SOLID_SHADER_NULL; + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + struct sdis_data* data = NULL; + double* power = ctx; + + CHK(mdm && ctx); + CHK(sdis_medium_get_type(mdm) == SDIS_SOLID); + + OK(sdis_solid_get_shader(mdm, &shader)); + data = sdis_medium_get_data(mdm); + vtx.time = INF; + + *power += power_term * shader.volumic_power(&vtx, data); + return RES_OK; +} + +static res_T +accum_flux_terms + (struct sdis_interface* interf, + const enum sdis_side side, + const double flux_term, + void* ctx) +{ + struct sdis_interface_shader shader = SDIS_INTERFACE_SHADER_NULL; + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + struct sdis_data* data = NULL; + double* flux = ctx; + double phi; + + CHK(interf && ctx); + + OK(sdis_interface_get_shader(interf, &shader)); + data = sdis_interface_get_data(interf); + frag.time = INF; + frag.side = side; + + phi = side == SDIS_FRONT + ? shader.front.flux(&frag, data) + : shader.back.flux(&frag, data); + + *flux += flux_term * phi; + return RES_OK; +} + +static res_T +solve_green_path(struct sdis_green_path* path, void* ctx) +{ + struct sdis_point pt = SDIS_POINT_NULL; + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + struct sdis_solid_shader solid = SDIS_SOLID_SHADER_NULL; + struct sdis_fluid_shader fluid = SDIS_FLUID_SHADER_NULL; + struct sdis_interface_shader interf = SDIS_INTERFACE_SHADER_NULL; + struct green_accum* acc = NULL; + struct sdis_data* data = NULL; + enum sdis_medium_type type; + double power = 0; + double flux = 0; + double temp = 0; + double weight = 0; + CHK(path && ctx); + + acc = ctx; + + BA(sdis_green_path_for_each_power_term(NULL, accum_power_terms, &power)); + BA(sdis_green_path_for_each_power_term(path, NULL, &acc)); + OK(sdis_green_path_for_each_power_term(path, accum_power_terms, &power)); + + BA(sdis_green_path_for_each_flux_term(NULL, accum_flux_terms, &flux)); + BA(sdis_green_path_for_each_flux_term(path, NULL, &acc)); + OK(sdis_green_path_for_each_flux_term(path, accum_flux_terms, &flux)); + + BA(sdis_green_path_get_limit_point(NULL, &pt)); + BA(sdis_green_path_get_limit_point(path, NULL)); + OK(sdis_green_path_get_limit_point(path, &pt)); + + switch(pt.type) { + case SDIS_FRAGMENT: + frag = pt.data.itfrag.fragment; + frag.time = INF; + OK(sdis_interface_get_shader(pt.data.itfrag.intface, &interf)); + data = sdis_interface_get_data(pt.data.itfrag.intface); + temp = frag.side == SDIS_FRONT + ? interf.front.temperature(&frag, data) + : interf.back.temperature(&frag, data); + break; + case SDIS_VERTEX: + vtx = pt.data.mdmvert.vertex; + vtx.time = INF; + type = sdis_medium_get_type(pt.data.mdmvert.medium); + data = sdis_medium_get_data(pt.data.mdmvert.medium); + if(type == SDIS_FLUID) { + OK(sdis_fluid_get_shader(pt.data.mdmvert.medium, &fluid)); + temp = fluid.temperature(&vtx, data); + } else { + OK(sdis_solid_get_shader(pt.data.mdmvert.medium, &solid)); + temp = solid.temperature(&vtx, data); + } + break; + default: FATAL("Unreachable code.\n"); break; + } + + weight = temp + power + flux; + acc->sum += weight; + acc->sum2 += weight*weight; + + return RES_OK; +} + +static void +dump_heat_path_position(FILE* stream, const struct sdis_heat_path* path) +{ + size_t nverts; + size_t ivert; + + 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)); + } +} + +static void +dump_heat_path_segments + (FILE* stream, const struct sdis_heat_path* path, const size_t offset) +{ + size_t nverts, ivert; + + CHK(stream); + + OK(sdis_heat_path_get_vertices_count(path, &nverts)); + fprintf(stream, "%lu", (unsigned long)nverts); + FOR_EACH(ivert, 0, nverts) { + fprintf(stream, " %lu", (unsigned long)(ivert + offset)); + } + fprintf(stream, "\n"); +} + +static void +dump_heat_path_vertex_attribs + (FILE* stream, + const struct sdis_heat_path* path, + const enum heat_vertex_attrib attr) +{ + size_t nverts, ivert; + 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; + } + } +} + +/******************************************************************************* + * Local function + ******************************************************************************/ +void +check_green_function(struct sdis_green_function* green) +{ + double time_range[2]; + struct sdis_estimator* estimator; + struct sdis_mc mc; + struct green_accum accum = {0, 0}; + double E, V, SE; + size_t nreals; + size_t nfails; + size_t n; + + time_range[0] = time_range[1] = INF; + + OK(sdis_green_function_solve(green, time_range, &estimator)); + + BA(sdis_green_function_get_paths_count(NULL, &n)); + BA(sdis_green_function_get_paths_count(green, NULL)); + OK(sdis_green_function_get_paths_count(green, &n)); + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + CHK(n == nreals); + + BA(sdis_green_function_get_invalid_paths_count(NULL, &n)); + BA(sdis_green_function_get_invalid_paths_count(green, NULL)); + OK(sdis_green_function_get_invalid_paths_count(green, &n)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + CHK(n == nfails); + + n = 0; + BA(sdis_green_function_for_each_path(NULL, count_green_paths, &n)); + BA(sdis_green_function_for_each_path(green, NULL, &n)); + OK(sdis_green_function_for_each_path(green, count_green_paths, &n)); + CHK(n == nreals); + + OK(sdis_green_function_for_each_path(green, solve_green_path, &accum)); + + E = accum.sum / (double)n; + V = MMAX(0, accum.sum2 / (double)n - E*E); + SE = sqrt(V/(double)n); + OK(sdis_estimator_get_temperature(estimator, &mc)); + + printf("Green: rebuild = %g +/- %g; solved = %g +/- %g\n", + E, SE, mc.E, mc.SE); + + CHK(E + SE >= mc.E - mc.SE); + CHK(E - SE <= mc.E + mc.SE); + + OK(sdis_estimator_ref_put(estimator)); +} + +void +dump_heat_paths(FILE* stream, struct sdis_estimator* estimator) +{ + const struct sdis_heat_path* path; + size_t ipath; + size_t npaths; + size_t nvertices; + size_t offset; + size_t n; + CHK(stream && estimator); + + OK(sdis_estimator_get_paths_count(estimator, &npaths)); + CHK(npaths); + + /* Header */ + fprintf(stream, "# vtk DataFile Version 2.0\n"); + fprintf(stream, "Heat paths\n"); + fprintf(stream, "ASCII\n"); + fprintf(stream, "DATASET POLYDATA\n"); + + /* Compute the overall number of vertices */ + nvertices = 0; + FOR_EACH(ipath, 0, npaths) { + OK(sdis_estimator_get_path(estimator, ipath, &path)); + OK(sdis_heat_path_get_vertices_count(path, &n)); + nvertices += 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); + } + + /* Write the segment of the paths */ + fprintf(stream, "LINES %lu %lu\n", + (unsigned long)npaths, (unsigned long)(npaths + nvertices)); + offset = 0; + FOR_EACH(ipath, 0, npaths) { + 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; + } + + fprintf(stream, "POINT_DATA %lu\n", (unsigned long)nvertices); + + /* Write the type of the random walk vertices */ + fprintf(stream, "SCALARS Vertex_Type float 1\n"); + fprintf(stream, "LOOKUP_TABLE vertex_type\n"); + FOR_EACH(ipath, 0, npaths) { + OK(sdis_estimator_get_path(estimator, ipath, &path)); + dump_heat_path_vertex_attribs(stream, path, HEAT_VERTEX_TYPE); + } + fprintf(stream, "LOOKUP_TABLE vertex_type 3\n"); + fprintf(stream, "0.0 1.0 1.0 1.0\n"); /* 0.0 = Magenta: conduction */ + fprintf(stream, "1.0 1.0 0.0 1.0\n"); /* 0.5 = Yellow: convection */ + fprintf(stream, "1.0 0.0 1.0 1.0\n"); /* 1.0 = Purple: radiative */ + + /* Write the weights of the random walk vertices */ + fprintf(stream, "SCALARS Weight double 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_WEIGHT); + } + + /* Write the time of the random walk vertices */ + fprintf(stream, "SCALARS Time double 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_TIME); + } + + /* Write path type */ + fprintf(stream, "CELL_DATA %lu\n", (unsigned long)npaths); + fprintf(stream, "SCALARS Path_Type float 1\n"); + fprintf(stream, "LOOKUP_TABLE path_type\n"); + FOR_EACH(ipath, 0, npaths) { + 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_SUCCEED: fprintf(stream, "0.0\n"); break; + case SDIS_HEAT_PATH_FAILED: fprintf(stream, "1.0\n"); break; + default: FATAL("Unreachable code.\n"); break; + } + } + fprintf(stream, "LOOKUP_TABLE path_type 2\n"); + fprintf(stream, "0.0 0.0 1.0 1.0\n"); /* 0.0 = Bleu: success */ + fprintf(stream, "1.0 0.0 0.0 1.0\n"); /* 1.0 = Red: failure */ +} diff --git a/src/test_sdis_utils.h b/src/test_sdis_utils.h @@ -240,15 +240,59 @@ dump_segments } static INLINE void +check_estimator_eq + (const struct sdis_estimator* e1, const struct sdis_estimator* e2) +{ + struct sdis_mc mc1, mc2; + enum sdis_estimator_type type1, type2; + ASSERT(e1 && e2); + + OK(sdis_estimator_get_type(e1, &type1)); + OK(sdis_estimator_get_type(e2, &type2)); + CHK(type1 == type2); + + OK(sdis_estimator_get_temperature(e1, &mc1)); + OK(sdis_estimator_get_temperature(e2, &mc2)); + CHK(mc1.E + 3*mc1.SE >= mc2.E - 3*mc2.SE); + CHK(mc1.E - 3*mc1.SE <= mc2.E + 3*mc2.SE); + + if(type1 == SDIS_ESTIMATOR_FLUX) { + OK(sdis_estimator_get_convective_flux(e1, &mc1)); + OK(sdis_estimator_get_convective_flux(e2, &mc2)); + CHK(mc1.E + 3*mc1.SE >= mc2.E - 3*mc2.SE); + CHK(mc1.E - 3*mc1.SE <= mc2.E + 3*mc2.SE); + + OK(sdis_estimator_get_radiative_flux(e1, &mc1)); + OK(sdis_estimator_get_radiative_flux(e2, &mc2)); + CHK(mc1.E + 3*mc1.SE >= mc2.E - 3*mc2.SE); + CHK(mc1.E - 3*mc1.SE <= mc2.E + 3*mc2.SE); + + OK(sdis_estimator_get_total_flux(e1, &mc1)); + OK(sdis_estimator_get_total_flux(e2, &mc2)); + CHK(mc1.E + 3*mc1.SE >= mc2.E - 3*mc2.SE); + CHK(mc1.E - 3*mc1.SE <= mc2.E + 3*mc2.SE); + } +} + +static INLINE void check_memory_allocator(struct mem_allocator* allocator) { if(MEM_ALLOCATED_SIZE(allocator)) { - char dump[128]; + char dump[1024]; MEM_DUMP(allocator, dump, sizeof(dump)); fprintf(stderr, "%s\n", dump); FATAL("Memory leaks.\n"); } } +extern LOCAL_SYM void +check_green_function + (struct sdis_green_function* green); + +extern LOCAL_SYM void +dump_heat_paths + (FILE* stream, + struct sdis_estimator* estimator); + #endif /* TEST_SDIS_UTILS_H */ diff --git a/src/test_sdis_volumic_power.c b/src/test_sdis_volumic_power.c @@ -16,6 +16,7 @@ #include "sdis.h" #include "test_sdis_utils.h" +#include <rsys/clock_time.h> #include <rsys/double3.h> #include <rsys/math.h> @@ -49,7 +50,7 @@ #define T0 320 #define LAMBDA 0.1 #define P0 10 -#define DELTA 1.0/20.0 +#define DELTA 1.0/40.0 /******************************************************************************* * Media @@ -145,13 +146,103 @@ interface_get_convection_coef } /******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +solve(struct sdis_scene* scn, const double pos[]) +{ + char dump[128]; + struct time t0, t1, t2; + struct sdis_estimator* estimator; + struct sdis_estimator* estimator2; + struct sdis_green_function* green; + struct sdis_mc T; + size_t nreals; + size_t nfails; + double x; + double ref; + const double time_range[2] = {INF, INF}; + enum sdis_scene_dimension dim; + ASSERT(scn && pos); + + x = pos[0] - 0.5; + ref = P0 / (2*LAMBDA) * (1.0/4.0 - x*x) + T0; + + time_current(&t0); + OK(sdis_solve_probe(scn, N, pos, time_range, 1.0, 0, 0, 0, &estimator)); + time_sub(&t0, time_current(&t1), &t0); + time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); + + 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_scene_get_dimension(scn, &dim)); + + switch(dim) { + case SDIS_SCENE_2D: + printf("Temperature at (%g %g) = %g ~ %g +/- %g [%g, %g]\n", + SPLIT2(pos), ref, T.E, T.SE, T.E-3*T.SE, T.E+3*T.SE); + break; + case SDIS_SCENE_3D: + printf("Temperature at (%g %g %g) = %g ~ %g +/- %g [%g, %g]\n", + SPLIT3(pos), ref, T.E, T.SE, T.E -3*T.SE, T.E + 3*T.SE); + break; + default: FATAL("Unreachable code.\n"); break; + } + printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); + printf("Elapsed time = %s\n\n", dump); + + CHK(nfails + nreals == N); + CHK(nfails < N/1000); + CHK(eq_eps(T.E, ref, T.SE*3)); + + time_current(&t0); + OK(sdis_solve_probe_green_function(scn, N, pos, 1.0, 0, 0, &green)); + time_current(&t1); + OK(sdis_green_function_solve(green, time_range, &estimator2)); + time_current(&t2); + + OK(sdis_estimator_get_realisation_count(estimator2, &nreals)); + OK(sdis_estimator_get_failure_count(estimator2, &nfails)); + OK(sdis_estimator_get_temperature(estimator2, &T)); + + switch(dim) { + case SDIS_SCENE_2D: + printf("Green temperature at (%g %g) = %g ~ %g +/- %g\n", + SPLIT2(pos), ref, T.E, T.SE); + break; + case SDIS_SCENE_3D: + printf("Green temperature at (%g %g %g) = %g ~ %g +/- %g\n", + SPLIT3(pos), ref, T.E, T.SE); + break; + default: FATAL("Unreachable code.\n"); break; + } + printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); + time_sub(&t0, &t1, &t0); + time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); + printf("Green estimation time = %s\n", dump); + time_sub(&t1, &t2, &t1); + time_dump(&t1, TIME_ALL, NULL, dump, sizeof(dump)); + printf("Green solve time = %s\n", dump); + + check_green_function(green); + check_estimator_eq(estimator, estimator2); + + OK(sdis_estimator_ref_put(estimator)); + OK(sdis_estimator_ref_put(estimator2)); + OK(sdis_green_function_ref_put(green)); + + printf("\n"); +} + +/******************************************************************************* * Test ******************************************************************************/ int main(int argc, char** argv) { struct mem_allocator allocator; - struct sdis_mc T = SDIS_MC_NULL; struct sdis_data* data = NULL; struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; @@ -161,7 +252,6 @@ main(int argc, char** argv) struct sdis_interface* interf_T0 = NULL; struct sdis_scene* box_scn = NULL; struct sdis_scene* square_scn = NULL; - struct sdis_estimator* estimator = NULL; 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; @@ -170,11 +260,6 @@ main(int argc, char** argv) struct interf* interf_props = NULL; struct solid* solid_props = NULL; double pos[3]; - double time_range[2] = { INF, INF }; - double x; - double ref; - size_t nreals; - size_t nfails; (void)argc, (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); @@ -263,36 +348,12 @@ main(int argc, char** argv) OK(sdis_interface_ref_put(interf_adiabatic)); OK(sdis_interface_ref_put(interf_T0)); + /* Solve */ d3_splat(pos, 0.25); - x = pos[0] - 0.5; - ref = P0 / (2*LAMBDA) * (1.0/4.0 - x*x) + T0; - - /* Solve in 3D */ - OK(sdis_solve_probe(box_scn, N, pos, time_range, 1.0, 0, 0, &estimator)); - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - CHK(nfails + nreals == N); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_ref_put(estimator)); - printf("Temperature of the box at (%g %g %g) = %g ~ %g +/- %g\n", - SPLIT3(pos), ref, T.E, T.SE); - printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); - CHK(nfails + nreals == N); - CHK(nfails < N/1000); - CHK(eq_eps(T.E, ref, 3*T.SE)); - - /* Solve in 2D */ - OK(sdis_solve_probe(square_scn, N, pos, time_range, 1.0, 0, 0, &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_ref_put(estimator)); - printf("Temperature of the square at (%g %g) = %g ~ %g +/- %g\n", - SPLIT2(pos), ref, T.E, T.SE); - printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); - CHK(nfails + nreals == N); - CHK(nfails < N/1000); - CHK(eq_eps(T.E, ref, 3*T.SE)); + printf(">> Box scene\n"); + solve(box_scn, pos); + printf(">> Square scene\n"); + solve(square_scn, pos); OK(sdis_scene_ref_put(box_scn)); OK(sdis_scene_ref_put(square_scn)); diff --git a/src/test_sdis_volumic_power2.c b/src/test_sdis_volumic_power2.c @@ -242,7 +242,7 @@ check(struct sdis_scene* scn, const struct reference refs[], const size_t nrefs) pos[1] = refs[i].pos[1]; pos[2] = refs[i].pos[2]; - OK(sdis_solve_probe(scn, N, pos, time_range, 1.f, -1, 0, &estimator)); + OK(sdis_solve_probe(scn, N, pos, time_range, 1.f, -1, 0, 0, &estimator)); OK(sdis_estimator_get_temperature(estimator, &T)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); @@ -256,7 +256,6 @@ check(struct sdis_scene* scn, const struct reference refs[], const size_t nrefs) } } - int main(int argc, char** argv) { diff --git a/src/test_sdis_volumic_power2_2d.c b/src/test_sdis_volumic_power2_2d.c @@ -261,7 +261,7 @@ check(struct sdis_scene* scn, const struct reference refs[], const size_t nrefs) pos[0] = refs[i].pos[0]; pos[1] = refs[i].pos[1]; - OK(sdis_solve_probe(scn, N, pos, time_range, 1.f, -1, 0, &estimator)); + OK(sdis_solve_probe(scn, N, pos, time_range, 1.f, -1, 0, 0, &estimator)); OK(sdis_estimator_get_temperature(estimator, &T)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); diff --git a/src/test_sdis_volumic_power3_2d.c b/src/test_sdis_volumic_power3_2d.c @@ -441,7 +441,7 @@ main(int argc, char** argv) FATAL("Unreachable code.\n"); } - OK(sdis_solve_probe(scn, N, pos, time_range, 1.f, -1, 0, &estimator)); + OK(sdis_solve_probe(scn, N, pos, time_range, 1.f, -1, 0, 0, &estimator)); OK(sdis_estimator_get_temperature(estimator, &T)); OK(sdis_estimator_get_failure_count(estimator, &nfails)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); diff --git a/src/test_sdis_volumic_power4_2d.c b/src/test_sdis_volumic_power4_2d.c @@ -15,24 +15,23 @@ #include "sdis.h" #include "test_sdis_utils.h" +#include <rsys/clock_time.h> #include <rsys/math.h> -#define Tf1 0 -#define Tf2 100 -#define Power 0 /*10000*/ -#define H1 50 -#define H2 50 +#define Tf 100.0 +#define Power 10000.0 +#define H 50.0 #define LAMBDA 100.0 -#define DELTA (1.0/20.0) +#define DELTA (1.0/2.0) #define N 10000 /* * The 2D scene is a solid slabs stretched along the X dimension to simulate a - * 1D case. The slab has a volumic power and has a convective exchange with the - * surrounding fluid whose temperature is fixed to Tfluid. + * 1D case. The slab has a volumic power and has a convective exchange with + * surrounding fluid whose temperature is fixed to Tf. * * - * _\ TFluid + * _\ Tf * / / * \__/ * @@ -42,17 +41,17 @@ * * ... -----Hboundary----- ... * - * _\ TFluid + * _\ Tf * / / * \__/ * */ static const double vertices[4/*#vertices*/*2/*#coords per vertex*/] = { - -10000.5,-0.5, - -10000.5, 0.5, - 10000.5, 0.5, - 10000.5,-0.5 + -1000000.5,-0.5, + -1000000.5, 0.5, + 1000000.5, 0.5, + 1000000.5,-0.5 }; static const size_t nvertices = sizeof(vertices)/sizeof(double[2]); @@ -200,6 +199,8 @@ interface_get_temperature int 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; @@ -223,8 +224,7 @@ main(int argc, char** argv) double pos[2]; double time_range[2] = { INF, INF }; double Tref; - double a, b, x; - double L; + double x; (void)argc, (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); @@ -238,14 +238,14 @@ main(int argc, char** argv) OK(sdis_data_create (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); fluid_param = sdis_data_get(data); - fluid_param->temperature = Tf1; + fluid_param->temperature = Tf; OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid1)); OK(sdis_data_ref_put(data)); OK(sdis_data_create (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); fluid_param = sdis_data_get(data); - fluid_param->temperature = Tf2; + fluid_param->temperature = Tf; OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid2)); OK(sdis_data_ref_put(data)); @@ -288,7 +288,7 @@ main(int argc, char** argv) OK(sdis_data_create (dev, sizeof(struct interf), ALIGNOF(struct interf), NULL, &data)); interf_param = sdis_data_get(data); - interf_param->h = H1; + interf_param->h = H; interf_param->temperature = -1; OK(sdis_interface_create(dev, solid, fluid1, &interf_shader, data, &interf_solid_fluid1)); @@ -298,7 +298,7 @@ main(int argc, char** argv) OK(sdis_data_create (dev, sizeof(struct interf), ALIGNOF(struct interf), NULL, &data)); interf_param = sdis_data_get(data); - interf_param->h = H2; + interf_param->h = H; interf_param->temperature = -1; OK(sdis_interface_create(dev, solid, fluid2, &interf_shader, data, &interf_solid_fluid2)); @@ -332,23 +332,15 @@ main(int argc, char** argv) pos[0] = 0; pos[1] = 0.25; - L = vertices[3] - vertices[1]; -#if 1 - x = pos[1] + vertices[3]; - a = (H2*Power*L + H1*H2*(Tf1 - Tf2) + H1*H2*Power*L*L/(2*LAMBDA)) - / (LAMBDA * (H1 + H2) + H1*H2*L); - b = Tf2 + a * LAMBDA / H2; - Tref = -Power / (2*LAMBDA) * x*x + a * x + b; -#else - tmp = LAMBDA / L; - T1 = H1 * (H2+tmp) / (tmp*(H1+H2) + H1*H2) * Tf1 - + H2 * tmp / (tmp*(H1+H2) + H1*H2) * Tf2; - T2 = H1 * tmp / (tmp*(H1+H2) + H1*H2) * Tf1 - + H2 * (H1+tmp) / (tmp*(H1+H2) + H1*H2) * Tf2; - Tref = T2 + (T1-T2)/L * (pos[1] + vertices[3]); -#endif + x = pos[1]; + Tref = -Power / (2*LAMBDA) * x*x + Tf + Power/(2*H) + Power/(8*LAMBDA); + + time_current(&t0); + OK(sdis_solve_probe(scn, N, pos, time_range, 1.f, -1, 0, 0, &estimator)); + time_sub(&t0, time_current(&t1), &t0); + time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); + printf("Elapsed time = %s\n", dump); - OK(sdis_solve_probe(scn, N, pos, time_range, 1.f, -1, 0, &estimator)); OK(sdis_estimator_get_temperature(estimator, &T)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); OK(sdis_estimator_get_failure_count(estimator, &nfails));