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:
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));