stardis-solver

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

commit 51284033a44ed852765399fc26eda1efce1bd379
parent e59857a2ed0a5e7adebea69409704804411011c5
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 17 Jul 2024 11:19:19 +0200

Merge branch 'release_0.16'

Diffstat:
MMakefile | 51+++++++++++++++++++++++++++++++++++++++++++++++----
MREADME.md | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mconfig.mk | 6+++---
Msdis.pc.in | 4++--
Msrc/sdis.h | 106++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/sdis_Xd_begin.h | 42+-----------------------------------------
Msrc/sdis_Xd_end.h | 2++
Msrc/sdis_heat_path.h | 85++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/sdis_heat_path_boundary_Xd.h | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/sdis_heat_path_boundary_Xd_c.h | 427+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/sdis_heat_path_boundary_Xd_handle_external_net_flux.h | 12++++++------
Msrc/sdis_heat_path_boundary_Xd_solid_fluid_picard1.h | 53+++++++++++++++++++++++++++++------------------------
Msrc/sdis_heat_path_boundary_Xd_solid_fluid_picardN.h | 54+++++++++++++++++++++++++++++-------------------------
Msrc/sdis_heat_path_boundary_Xd_solid_solid.h | 44+++++++++++++++++++++++---------------------
Msrc/sdis_heat_path_boundary_c.h | 167++++++++++++++++++++++++++++++++-----------------------------------------------
Msrc/sdis_heat_path_conductive.c | 58++++++++++------------------------------------------------
Asrc/sdis_heat_path_conductive_Xd.h | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_heat_path_conductive_c.h | 44++++++++++++++++++++++++++++++++------------
Asrc/sdis_heat_path_conductive_custom_Xd.h | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_heat_path_conductive_delta_sphere_Xd.h | 94++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/sdis_heat_path_conductive_wos_Xd.h | 300+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/sdis_heat_path_convective_Xd.h | 193++++++++++++++++++++++++++++++-------------------------------------------------
Msrc/sdis_heat_path_radiative_Xd.h | 85+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/sdis_log.h | 2++
Msrc/sdis_misc.c | 83++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/sdis_misc.h | 19++++++-------------
Dsrc/sdis_misc_Xd.h | 90-------------------------------------------------------------------------------
Asrc/sdis_primkey.c | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_realisation.c | 42++++++++++++++++++++++++++++--------------
Msrc/sdis_realisation.h | 20+++++++++++---------
Msrc/sdis_realisation_Xd.h | 128+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/sdis_scene.c | 107++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/sdis_scene_Xd.h | 197+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/sdis_scene_c.h | 87++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/sdis_solve_camera.c | 30++++++++++++------------------
Msrc/sdis_solve_medium_Xd.h | 32+++++++++++++++++++-------------
Msrc/sdis_solve_probe_Xd.h | 66++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/sdis_solve_probe_boundary_Xd.h | 40+++++++++++++++++++++++++++-------------
Asrc/test_sdis_custom_solid_path_sampling.c | 580+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_custom_solid_path_sampling_2d.c | 600+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_mesh.h | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_primkey.c | 281+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_primkey_2d.c | 295+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_scene.c | 24++++++++++++++++++------
Msrc/test_sdis_solve_camera.c | 1022+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/test_sdis_solve_probe_boundary_list.c | 1+
Msrc/test_sdis_utils.h | 1+
47 files changed, 4495 insertions(+), 1704 deletions(-)

diff --git a/Makefile b/Makefile @@ -43,6 +43,7 @@ SRC =\ src/sdis_log.c\ src/sdis_medium.c\ src/sdis_misc.c\ + src/sdis_primkey.c\ src/sdis_radiative_env.c\ src/sdis_realisation.c\ src/sdis_scene.c\ @@ -192,6 +193,8 @@ TEST_SRC =\ src/test_sdis_interface.c\ src/test_sdis_medium.c\ src/test_sdis_picard.c\ + src/test_sdis_primkey.c\ + src/test_sdis_primkey_2d.c\ src/test_sdis_radiative_env.c\ src/test_sdis_scene.c\ src/test_sdis_solid_random_walk_robustness.c\ @@ -216,6 +219,8 @@ TEST_SRC_LONG =\ TEST_SRC_MPI =\ src/test_sdis.c\ src/test_sdis_compute_power.c\ + src/test_sdis_custom_solid_path_sampling.c\ + src/test_sdis_custom_solid_path_sampling_2d.c\ src/test_sdis_device.c\ src/test_sdis_external_flux.c\ src/test_sdis_solve_camera.c\ @@ -396,6 +401,7 @@ test_sdis_volumic_power4 \ ################################################################################ src/test_sdis_draw_external_flux.d \ src/test_sdis_external_flux_with_diffuse_radiance.d \ +src/test_sdis_primkey.d \ src/test_sdis_solid_random_walk_robustness.d \ src/test_sdis_solve_probe3.d \ src/test_sdis_unsteady_analytic_profile.d \ @@ -404,6 +410,7 @@ src/test_sdis_unsteady_analytic_profile.d \ src/test_sdis_draw_external_flux.o \ src/test_sdis_external_flux_with_diffuse_radiance.o \ +src/test_sdis_primkey.o \ src/test_sdis_solid_random_walk_robustness.o \ src/test_sdis_solve_probe3.o \ src/test_sdis_unsteady_analytic_profile.o \ @@ -412,6 +419,7 @@ src/test_sdis_unsteady_analytic_profile.o \ test_sdis_draw_external_flux \ test_sdis_external_flux_with_diffuse_radiance \ +test_sdis_primkey \ test_sdis_solid_random_walk_robustness \ test_sdis_solve_probe3 \ test_sdis_unsteady_analytic_profile \ @@ -419,6 +427,36 @@ test_sdis_unsteady_analytic_profile \ $(CC) $(TEST_CFLAGS) $(S3DUT_CFLAGS) -o $@ src/$@.o $(TEST_LIBS) $(S3DUT_LIBS) ################################################################################ +# Test based on Star-2D +################################################################################ +src/test_sdis_primkey_2d.d \ +: config.mk sdis-local.pc + @$(CC) $(TEST_CFLAGS) $(S2D_CFLAGS) -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ + +src/test_sdis_primkey_2d.o \ +: config.mk sdis-local.pc + $(CC) $(TEST_CFLAGS) $(S2D_CFLAGS) -c $(@:.o=.c) -o $@ + +test_sdis_primkey_2d \ +: config.mk sdis-local.pc $(LIBNAME) src/test_sdis_utils.o + $(CC) $(TEST_CFLAGS) $(S2D_CFLAGS) -o $@ src/$@.o $(TEST_LIBS) $(S2D_LIBS) + +################################################################################ +# Test based on Star-2D with (optional) MPI support +################################################################################ +src/test_sdis_custom_solid_path_sampling_2d.d \ +: config.mk sdis-local.pc + @$(CC) $(TEST_CFLAGS_MPI) $(S2D_CFLAGS) -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ + +src/test_sdis_custom_solid_path_sampling_2d.o \ +: config.mk sdis-local.pc + $(CC) $(TEST_CFLAGS_MPI) $(S2D_CFLAGS) -c $(@:.o=.c) -o $@ + +test_sdis_custom_solid_path_sampling_2d \ +: config.mk sdis-local.pc $(LIBNAME) src/test_sdis_utils.o + $(CC) $(TEST_CFLAGS_MPI) $(S2D_CFLAGS) -o $@ src/$@.o $(TEST_LIBS_MPI) $(S2D_LIBS) + +################################################################################ # Tests based on Star-Enclosures-<2|3>D ################################################################################ src/test_sdis_scene.d \ @@ -482,19 +520,24 @@ test_sdis_solve_probe_boundary_list \ $(CC) $(TEST_CFLAGS_MPI) $(S3DUT_CFLAGS) -o $@ src/$@.o $(TEST_LIBS_MPI) $(S3DUT_LIBS) ################################################################################ -# Tests based on Star-3D and Star-3DUT with (optional) MPI support +# Tests based on Star-3D, Star-3DUT and Star-SP with (optional) MPI support ################################################################################ +src/test_sdis_custom_solid_path_sampling.d \ src/test_sdis_solve_probe_list.d \ : config.mk sdis-local.pc - @$(CC) $(TEST_CFLAGS_MPI) $(S3D_CFLAGS) $(S3DUT_CFLAGS) -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ + @$(CC) $(TEST_CFLAGS_MPI) $(S3D_CFLAGS) $(S3DUT_CFLAGS) $(SSP_CFLAGS) \ + -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ +src/test_sdis_custom_solid_path_sampling.o \ src/test_sdis_solve_probe_list.o \ : config.mk sdis-local.pc - $(CC) $(TEST_CFLAGS_MPI) $(S3D_CFLAGS) $(S3DUT_CFLAGS) -c $(@:.o=.c) -o $@ + $(CC) $(TEST_CFLAGS_MPI) $(S3D_CFLAGS) $(S3DUT_CFLAGS) $(SSP_CFLAGS) -c $(@:.o=.c) -o $@ +test_sdis_custom_solid_path_sampling \ test_sdis_solve_probe_list \ : config.mk sdis-local.pc $(LIBNAME) src/test_sdis_utils.o - $(CC) $(TEST_CFLAGS_MPI) $(S3D_CFLAGS) $(S3DUT_CFLAGS) -o $@ src/$@.o $(TEST_LIBS_MPI) $(S3D_LIBS) $(S3DUT_LIBS) + $(CC) $(TEST_CFLAGS_MPI) $(S3D_CFLAGS) $(S3DUT_CFLAGS) $(SSP_CFLAGS) \ + -o $@ src/$@.o $(TEST_LIBS_MPI) $(S3D_LIBS) $(S3DUT_LIBS) $(SSP_CFLAGS) ################################################################################ # Tests based on Star-SP with (optional) MPI support diff --git a/README.md b/README.md @@ -158,6 +158,66 @@ Edit config.mk as needed, then run: ## Release notes +### Version 0.16 + +#### Add support for custom sampling of solid paths + +Add the `sample_path` functor as a member variable of the +`sdis_solid_shader` data structure which, once defined, is called to +sample a trajectory in this solid, instead of relying on the internal +sampling procedures for unsteady conductive paths (i.e. walk on +delta sphere or walk on sphere). + +Coupling takes place as usual, i.e. at the boundary. Only path sampling +is delegated to the caller. In other words, the connection physics +remain the same, and the user only has control over the physical model +of the custom solid. + +Note that to help the user map the sampled path to the solver limits +(i.e. the geometry reached by the path), we added the `sdis_primkey` API +which constructs a unique key from the vertices of a segment/triangle. +We can then use this key as input to the new functions +`sdis_scene_get_s<2|3>d_primitive` to find the solver primitive which +corresponds to this segment/triangle. It is therefore sufficient to know +the *exact* coordinates of the boundary mesh to geometrically couple the +paths sampled by the user with the boundaries of the geometries managed +by the solver. + +#### Correcting the sampling of radiative paths + +Allows sampling of radiative paths in enclosures with multiple media. +Multiple media are often used to define a set of Robin boundary +conditions. And although sampling a convective path in such an enclosure +is an error (since it is outside the space of convective paths), it is +still possible to sample radiative paths in this enclosure (it is +perfectly defined in the space of radiative paths). + +For example, it is now possible to render an infrared image of a system +with a set of Robin boundary conditions defined from a set of fluids +with fixed temperatures. + +#### Bug fixes + +- Fixes the `sdis_solve_probe_boundary_list` function. The calculation + did not use the expected number of threads, which could lead to an + invalid memory access. +- Updated error handling when resolving temperatures for several probes. + The calculation of a probe no longer stops as soon as a realisation is + rejected. As with the calculation of a single probe, if a result is + rejected, it is simply not taken into account in the estimate. +- Corrects numerical problems when sampling a conductive path with WoS. + The thresholds used to detect/manage numerical problems were + calculated from absolute distances in metres. This method was not + numerically robust when these distances were very small. They + are now calculated in relation to the delta of the solid, which must + be able to take account of spatio-temporal temperature gradients while + being large enough to allow numerical estimation. +- Corrects the position of the path when the initial condition is + reached during the sampling of a conductive path with WoS. The unit + used to update the position was wrong when the `fp_to_meter` + variable was not set to 1. In addition, the position at which the + initial condition was reached could be outside the solid. + ### Version 0.15.2 Correction of pkg-config file. A missing private dependency could lead @@ -249,8 +309,8 @@ a Dirichlet boundary condition. In other words, the same data could define totally different systems before or after this version. The macro `SDIS_TEMPERATURE_NONE` is added to define the unknown -temperature value. The two helper macros `SDIS_TEMPERATURE_IS_KNONW` and -`SDIS_TEMPERATURE_IS_UNKNONW` are also provided to test whether the +temperature value. The two helper macros `SDIS_TEMPERATURE_IS_KNOWN` and +`SDIS_TEMPERATURE_IS_UNKNOWN` are also provided to test whether the temperature is known or not. #### Parallelize multiple probe resolutions diff --git a/config.mk b/config.mk @@ -1,6 +1,6 @@ VERSION_MAJOR = 0 -VERSION_MINOR = 15 -VERSION_PATCH = 2 +VERSION_MINOR = 16 +VERSION_PATCH = 0 VERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH) PREFIX = /usr/local @@ -55,7 +55,7 @@ SENC2D_VERSION = 0.5 SENC2D_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags senc2d) SENC2D_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs senc2d) -SENC3D_VERSION = 0.5 +SENC3D_VERSION = 0.7.2 SENC3D_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags senc3d) SENC3D_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs senc3d) diff --git a/sdis.pc.in b/sdis.pc.in @@ -8,8 +8,8 @@ Requires: \ s3d >= @S3D_VERSION@,\ star-sp >= @SSP_VERSION@ Requires.private:\ - senc2d >= @SENC3D_VERSION@,\ - senc3d >= @SENC3D_VERSION@, \ + senc2d >= @SENC2D_VERSION@,\ + senc3d >= @SENC3D_VERSION@,\ swf >= @SWF_VERSION@ @MPI@ Name: sdis Description: Stardis Solver diff --git a/src/sdis.h b/src/sdis.h @@ -220,6 +220,51 @@ struct sdis_scene_find_closest_point_args { static const struct sdis_scene_find_closest_point_args SDIS_SCENE_FIND_CLOSEST_POINT_ARGS_NULL = SDIS_SCENE_FIND_CLOSEST_POINT_ARGS_NULL__; +/* A sampled path */ +struct sdis_path { + struct sdis_rwalk_vertex vtx; /* Current position and time */ + + /* Surface intersected by the path. When defined, the path is on a border */ + struct s2d_primitive prim_2d; + struct s3d_primitive prim_3d; + + double weight; /* Monte Carlo weight update along the path */ + + /* Define whether the path has reached a boundary condition in time/space */ + int at_limit; +}; +#define SDIS_PATH_NULL__ { \ + SDIS_RWALK_VERTEX_NULL__, \ + S2D_PRIMITIVE_NULL__, \ + S3D_PRIMITIVE_NULL__, \ + 0, /* MC weight */ \ + 0 /* At limit */ \ +} +static const struct sdis_path SDIS_PATH_NULL = SDIS_PATH_NULL__; + +/* Type of functor used by the user to write the way in which the path is + * sampled. So it's no longer Stardis that samples the path, but the user + * through his own function. */ +typedef res_T +(*sdis_sample_path_T) + (struct sdis_scene* scn, + struct ssp_rng* rng, + struct sdis_path* path, + struct sdis_data* data); + +/* Key to a geometric primitive, i.e its unique identifier. Its member variables + * must be treated as private variables, i.e. the caller must not access them + * directly but use the primkey API functions instead (see below) */ +struct sdis_primkey { + /* List of primitive nodes sorted in ascending order */ + double nodes[9]; + + /* Overall number of coordinates (4 in 2D, 9 in 3D) */ + unsigned ncoords; +}; +#define SDIS_PRIMKEY_NULL__ {{0,0,0,0,0,0,0,0,0},0} +static const struct sdis_primkey SDIS_PRIMKEY_NULL = SDIS_PRIMKEY_NULL__; + /******************************************************************************* * Estimation data types ******************************************************************************/ @@ -294,12 +339,16 @@ struct sdis_solid_shader { * This getter is always called at time >= t0 (see below). */ sdis_medium_getter_T temperature; + /* Function to be used to sample the path through the solid. If not defined, + * let stardis sample a conductive path */ + sdis_sample_path_T sample_path; + /* The time until the initial condition is maintained for this solid. * Can be negative or set to +/- infinity to simulate a system that is always * in the initial state or never reaches it, respectively. */ double t0; }; -#define SDIS_SOLID_SHADER_NULL__ {NULL, NULL, NULL, NULL, NULL, NULL, 0} +#define SDIS_SOLID_SHADER_NULL__ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0} static const struct sdis_solid_shader SDIS_SOLID_SHADER_NULL = SDIS_SOLID_SHADER_NULL__; @@ -1311,18 +1360,30 @@ sdis_scene_boundary_project_position const double pos[], double uv[]); -/* Get the 2D scene's enclosures. Only defined for a 2D scene. */ +/* Get Star-Enclosure-2D scene. Defined on 2D scene only */ SDIS_API res_T sdis_scene_get_senc2d_scene (struct sdis_scene* scn, struct senc2d_scene** senc2d_scn); -/* Get the 3D scene's enclosures. Only defined for a 3D scene. */ +/* Get Star-Enclosure-3D scene. Defined on 3D scene only */ SDIS_API res_T sdis_scene_get_senc3d_scene (struct sdis_scene* scn, struct senc3d_scene** senc3d_scn); +/* Get Star-2D scene view. Defined on 2D scene only */ +SDIS_API res_T +sdis_scene_get_s2d_scene_view + (struct sdis_scene* scn, + struct s2d_scene_view** s2d_view); + +/* Get Star-3D scene view. Defined on 3D scene only */ +SDIS_API res_T +sdis_scene_get_s3d_scene_view + (struct sdis_scene* scn, + struct s3d_scene_view** s3d_view); + SDIS_API res_T sdis_scene_get_dimension (const struct sdis_scene* scn, @@ -1353,6 +1414,20 @@ sdis_scene_get_radiative_env /* The returned pointer can be NULL, i.e. there is no radiative environement*/ struct sdis_radiative_env** radenv); +/* Get the internal Star-2D primitive corresponding to the primitive key */ +SDIS_API res_T +sdis_scene_get_s2d_primitive + (struct sdis_scene* scn, + const struct sdis_primkey* key, + struct s2d_primitive* primitive); + +/* Get the internal Star-3D primitive corresponding to the primitive key */ +SDIS_API res_T +sdis_scene_get_s3d_primitive + (struct sdis_scene* scn, + const struct sdis_primkey* key, + struct s3d_primitive* primitive); + /******************************************************************************* * An estimator stores the state of a simulation ******************************************************************************/ @@ -1717,6 +1792,31 @@ SDIS_API res_T sdis_get_info (struct sdis_info* info); +/******************************************************************************* + * Primitive identifier + ******************************************************************************/ +SDIS_API void +sdis_primkey_setup + (struct sdis_primkey* key, + const double node0[3], + const double node1[3], + const double node2[3]); + +SDIS_API void +sdis_primkey_2d_setup + (struct sdis_primkey* key, + const double node0[2], + const double node1[2]); + +SDIS_API size_t +sdis_primkey_hash + (const struct sdis_primkey* key); + +SDIS_API char +sdis_primkey_eq + (const struct sdis_primkey* key0, + const struct sdis_primkey* key1); + END_DECLS #endif /* SDIS_H */ diff --git a/src/sdis_Xd_begin.h b/src/sdis_Xd_begin.h @@ -65,6 +65,7 @@ #define SXD_GET_PRIMITIVE CONCAT(CONCAT(S, DIM), D_GET_PRIMITIVE) #define SXD_SAMPLE CONCAT(CONCAT(S, DIM), D_SAMPLE) #define SXD_TRACE CONCAT(CONCAT(S, DIM), D_TRACE) +#define SXD_PRIMITIVE_NULL CONCAT(CONCAT(S, DIM), D_PRIMITIVE_NULL) #define SXD_PRIMITIVE_EQ CONCAT(CONCAT(S, DIM), D_PRIMITIVE_EQ) /* Vector macros generic to SDIS_XD_DIMENSION */ @@ -76,44 +77,3 @@ /* Macro making generic its submitted name 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 - -struct rwalk_context; - -/* 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 */ - - /* Direction along which the random walk reached the radiative environment */ - double dir[3]; - - double elapsed_time; - enum sdis_side hit_side; -}; -static const struct XD(rwalk) XD(RWALK_NULL) = { - SDIS_RWALK_VERTEX_NULL__, NULL, SXD_HIT_NULL__, {0,0,0}, 0, SDIS_SIDE_NULL__ -}; - -struct XD(temperature) { - res_T (*func)/* Next function to invoke in order to compute the temperature */ - (struct sdis_scene* scn, - 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 @@ -34,6 +34,8 @@ #undef SXD_GET_PRIMITIVE #undef SXD_SAMPLE #undef SXD_TRACE +#undef SXD_PRIMITIVE_NULL +#undef SXD_PRIMITIVE_EQ #undef dX #undef fX diff --git a/src/sdis_heat_path.h b/src/sdis_heat_path.h @@ -17,6 +17,7 @@ #define SDIS_HEAT_PATH_H #include "sdis.h" +#include "sdis_scene_c.h" #include <rsys/dynamic_array.h> #include <rsys/dynamic_array_size_t.h> @@ -24,12 +25,8 @@ /* Forward declarations */ struct green_path_handle; -struct rwalk_2d; -struct rwalk_3d; struct sdis_scene; struct ssp_rng; -struct temperature_2d; -struct temperature_3d; /******************************************************************************* * Context of a random walk, i.e. its data concerning the current system and the @@ -87,6 +84,46 @@ get_picard_order(const struct rwalk_context* ctx) } /******************************************************************************* + * 2D/3D random walk and associated temperature, i.e. current state of the + * sampled path + ******************************************************************************/ +struct rwalk { + struct sdis_rwalk_vertex vtx; /* Position and time of the Random walk */ + unsigned enc_id; /* Id of the enclosure in which the random walk lies */ + struct s2d_hit hit_2d; + struct s3d_hit hit_3d; + + /* Direction along which the random walk reached the radiative environment */ + double dir[3]; + + double elapsed_time; + enum sdis_side hit_side; +}; +#define RWALK_NULL__ { \ + SDIS_RWALK_VERTEX_NULL__, \ + ENCLOSURE_ID_NULL, \ + S2D_HIT_NULL__, \ + S3D_HIT_NULL__, \ + {0,0,0}, \ + 0, \ + SDIS_SIDE_NULL__ \ +} +static const struct rwalk RWALK_NULL = RWALK_NULL__; + +struct temperature { + res_T (*func)/* Next function to invoke in order to compute the temperature */ + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct rwalk* rwalk, + struct ssp_rng* rng, + struct temperature* temp); + double value; /* Current value of the temperature */ + int done; +}; +#define TEMPERATURE_NULL__ {NULL,0,0} +static const struct temperature TEMPERATURE_NULL = TEMPERATURE_NULL__; + +/******************************************************************************* * Heat path data structure used to record the geometry of sampled paths ******************************************************************************/ /* Generate the dynamic array of heat vertices */ @@ -261,34 +298,34 @@ trace_radiative_path_2d (struct sdis_scene* scn, const float ray_dir[3], struct rwalk_context* ctx, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_2d* temperature); + struct temperature* temperature); extern LOCAL_SYM res_T trace_radiative_path_3d (struct sdis_scene* scn, const float ray_dir[3], struct rwalk_context* ctx, - struct rwalk_3d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_3d* temperature); + struct temperature* temperature); extern LOCAL_SYM res_T radiative_path_2d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_2d* temperature); + struct temperature* temperature); extern LOCAL_SYM res_T radiative_path_3d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_3d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_3d* temperature); + struct temperature* temperature); /******************************************************************************* * Convective path @@ -297,17 +334,17 @@ extern LOCAL_SYM res_T convective_path_2d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_2d* temperature); + struct temperature* temperature); extern LOCAL_SYM res_T convective_path_3d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_3d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_3d* temperature); + struct temperature* temperature); /******************************************************************************* * Conductive path @@ -316,17 +353,17 @@ extern LOCAL_SYM res_T conductive_path_2d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_2d* temperature); + struct temperature* temperature); extern LOCAL_SYM res_T conductive_path_3d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_3d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_3d* temperature); + struct temperature* temperature); /******************************************************************************* * Boundary sub-path @@ -335,16 +372,16 @@ extern LOCAL_SYM res_T boundary_path_2d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_2d* temperature); + struct temperature* temperature); extern LOCAL_SYM res_T boundary_path_3d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_3d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_3d* temperature); + struct temperature* temperature); #endif /* SDIS_HEAT_PATH_H */ diff --git a/src/sdis_heat_path_boundary_Xd.h b/src/sdis_heat_path_boundary_Xd.h @@ -25,15 +25,67 @@ #include "sdis_Xd_begin.h" /******************************************************************************* + * Helper functions + ******************************************************************************/ +/* This function checks whether the random walk is on a boundary and, if so, + * verifies that the temperature of the medium attached to the interface is + * known. This medium can be different from the medium of the enclosure. Indeed, + * the enclosure can contain several media used to set the temperatures of + * several boundary conditions. Hence this function, which queries the medium on + * the trajectory coming from a boundary */ +static res_T +XD(handle_known_medium_temperature) + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct rwalk* rwalk, + struct temperature* T) +{ + struct sdis_interface* interf = NULL; + struct sdis_medium* mdm = NULL; + double temperature = SDIS_TEMPERATURE_NONE; + res_T res = RES_OK; + ASSERT(scn && ctx && rwalk && T); + + /* Not at an interface */ + if(SXD_HIT_NONE(&rwalk->XD(hit))) return RES_OK; /* Nothing to do */ + + interf = scene_get_interface(scn, rwalk->XD(hit).prim.prim_id); + mdm = rwalk->hit_side==SDIS_FRONT ? interf->medium_front: interf->medium_back; + + temperature = medium_get_temperature(mdm, &rwalk->vtx); + + /* Check if the temperature is known */ + if(SDIS_TEMPERATURE_IS_UNKNOWN(temperature)) goto exit; + + T->value += temperature; + T->done = 1; + + if(ctx->green_path) { + res = green_path_set_limit_vertex + (ctx->green_path, mdm, &rwalk->vtx, rwalk->elapsed_time); + if(res != RES_OK) goto error; + } + + if(ctx->heat_path) { + heat_path_get_last_vertex(ctx->heat_path)->weight = T->value; + } + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* * Local functions ******************************************************************************/ res_T XD(boundary_path) (struct sdis_scene* scn, struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct XD(temperature)* T) + struct temperature* T) { struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; struct sdis_interface* interf = NULL; @@ -42,15 +94,16 @@ XD(boundary_path) double tmp; res_T res = RES_OK; ASSERT(scn && ctx && rwalk && rng && T); - ASSERT(rwalk->mdm == NULL); - ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + ASSERT(rwalk->enc_id == ENCLOSURE_ID_NULL); + ASSERT(!SXD_HIT_NONE(&rwalk->XD(hit))); - XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); + XD(setup_interface_fragment) + (&frag, &rwalk->vtx, &rwalk->XD(hit), rwalk->hit_side); - fX(normalize)(rwalk->hit.normal, rwalk->hit.normal); + fX(normalize)(rwalk->XD(hit).normal, rwalk->XD(hit).normal); /* Retrieve the current interface */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + interf = scene_get_interface(scn, rwalk->XD(hit).prim.prim_id); /* Check if the boundary temperature is known */ tmp = interface_side_get_temperature(interf, &frag); @@ -82,6 +135,32 @@ XD(boundary_path) } if(res != RES_OK) goto error; +#if 1 + if(T->done) goto exit; + + /* Handling limit boundary condition, i.e. the trajectory originates from a + * boundary and the medium temperature is known (e.g. Robin's condition). To + * simplify data description, we allow in such a situation, to define several + * medium on the same enclosure, each with a fixed temperature, i.e. different + * conditions are defined for the different interfaces that detour the + * enclosure. As a result, no path can be sampled in this enclosure, which is + * beyond the system boundary. The boundary medium must therefore be + * interrogated from the interface. + * + * Note that we check this boundary condition with convective paths to handle + * Robin's boundary conditions. But we also make this check when passing + * through conduction when there's no reason why a solid should have a fixed + * temperature and not its boundary: it should be a Dirichlet condition. + * Although it's not physical, such systems can still be defined + * computationally, and in fact it's also a handy way of testing + * border cases */ + if(T->func == XD(convective_path) || T->func == XD(conductive_path)) { + res = XD(handle_known_medium_temperature)(scn, ctx, rwalk, T); + if(res != RES_OK) goto error; + if(T->done) goto exit; /* That's all folks */ + } +#endif + exit: return res; error: @@ -89,4 +168,3 @@ error: } #include "sdis_Xd_end.h" - diff --git a/src/sdis_heat_path_boundary_Xd_c.h b/src/sdis_heat_path_boundary_Xd_c.h @@ -26,18 +26,18 @@ #include "sdis_Xd_begin.h" struct XD(find_reinjection_ray_args) { - const struct sdis_medium* solid; /* Medium into which the reinjection occurs */ - const struct XD(rwalk)* rwalk; /* Current random walk state */ + const struct rwalk* rwalk; /* Current random walk state */ float dir0[DIM]; /* Challenged ray direction */ float dir1[DIM]; /* Challenged ray direction */ double distance; /* Maximum reinjection distance */ + unsigned solid_enc_id; /* Enclosure id into which the reinjection occurs */ /* Define if the random walk position can be moved or not to find a valid * reinjection direction */ int can_move; }; static const struct XD(find_reinjection_ray_args) -XD(FIND_REINJECTION_RAY_ARGS_NULL) = { NULL, NULL, {0}, {0}, 0, 0 }; +XD(FIND_REINJECTION_RAY_ARGS_NULL) = { NULL, {0}, {0}, 0, ENCLOSURE_ID_NULL, 0 }; struct XD(reinjection_ray) { double org[DIM]; /* Origin of the reinjection */ @@ -55,80 +55,150 @@ XD(REINJECTION_RAY_NULL) = { {0}, {0}, 0, SXD_HIT_NULL__, 0 }; /******************************************************************************* * Helper functions ******************************************************************************/ -static INLINE int +static INLINE res_T XD(check_find_reinjection_ray_args) - (const struct XD(find_reinjection_ray_args)* args) + (struct sdis_scene* scn, + const struct XD(find_reinjection_ray_args)* args) { - return args - && args->solid - && args->rwalk - && args->distance > 0 - && fX(is_normalized)(args->dir0) - && fX(is_normalized)(args->dir1); + const struct enclosure* enc = NULL; + struct sdis_medium* mdm = NULL; + res_T res = RES_OK; + ASSERT(scn); + + /* Check pointers */ + if(!args || !args->rwalk) return RES_BAD_ARG; + + /* Check distance */ + if(args->distance <= 0) return RES_BAD_ARG; + + /* Check directions */ + if(!fX(is_normalized)(args->dir0) || !fX(is_normalized)(args->dir1)) { + return RES_BAD_ARG; + } + + /* Check enclosure id */ + if(args->solid_enc_id == ENCLOSURE_ID_NULL) { + return RES_BAD_ARG; + } + enc = scene_get_enclosure(scn, args->solid_enc_id); + + /* Check the enclosure */ + enc = scene_get_enclosure(scn, args->solid_enc_id); + if(enc->medium_id != MEDIUM_ID_MULTI) { + if((res = scene_get_enclosure_medium(scn, enc, &mdm)) != RES_OK) return res; + if(sdis_medium_get_type(mdm) != SDIS_SOLID) { + res = RES_BAD_ARG; + } + } + + return RES_OK; } -static INLINE int +static INLINE res_T XD(check_sample_reinjection_step_args) - (const struct XD(sample_reinjection_step_args)* args) + (struct sdis_scene* scn, + const struct sample_reinjection_step_args* args) { - return args - && args->rng - && args->solid - && args->solid->type == SDIS_SOLID - && args->rwalk - && args->distance > 0 - && (unsigned)args->side < SDIS_SIDE_NULL__; + const struct enclosure* enc = NULL; + struct sdis_medium* mdm = NULL; + res_T res = RES_OK; + ASSERT(scn); + + /* Check pointers */ + if(!args || !args->rng || !args->rwalk) return RES_BAD_ARG; + + /* Check distance */ + if(args->distance <= 0) return RES_BAD_ARG; + + /* Check side */ + if((unsigned)args->side >= SDIS_SIDE_NULL__) return RES_BAD_ARG; + + /* Check enclosure id */ + if(args->solid_enc_id == ENCLOSURE_ID_NULL) { + return RES_BAD_ARG; + } + + /* Check the enclosure */ + enc = scene_get_enclosure(scn, args->solid_enc_id); + if(enc->medium_id != MEDIUM_ID_MULTI) { + if((res = scene_get_enclosure_medium(scn, enc, &mdm)) != RES_OK) return res; + if(sdis_medium_get_type(mdm) != SDIS_SOLID) { + return RES_BAD_ARG; + } + } + + return RES_OK; } -static INLINE int -XD(check_reinjection_step)(const struct XD(reinjection_step)* step) +static INLINE res_T +XD(check_reinjection_step)(const struct reinjection_step* step) { - return step - && fX(is_normalized)(step->direction) - && step->distance > 0; + /* Check pointer */ + if(!step) return RES_BAD_ARG; + + /* Check direction */ + if(!fX(is_normalized)(step->direction)) return RES_BAD_ARG; + + /* Check distance */ + if(step->distance <= 0) return RES_BAD_ARG; + + return RES_OK; } -static INLINE int -XD(check_solid_reinjection_args)(const struct XD(solid_reinjection_args)* args) +static INLINE res_T +XD(check_solid_reinjection_args)(const struct solid_reinjection_args* args) { - return args - && XD(check_reinjection_step)(args->reinjection) - && args->rng - && args->rwalk - && args->rwalk_ctx - && args->T - && args->fp_to_meter > 0; + /* Check pointers */ + if(!args || !args->rng || !args->rwalk || !args->rwalk_ctx || !args->T) + return RES_BAD_ARG; + + /* Check unit */ + if(args->fp_to_meter <= 0) return RES_BAD_ARG; + + return XD(check_reinjection_step)(args->reinjection); } /* Check that the interface fragment is consistent with the current state of * the random walk */ -static INLINE int +static INLINE res_T XD(check_rwalk_fragment_consistency) - (const struct XD(rwalk)* rwalk, + (const struct 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; + + /* Check intersection */ + if(SXD_HIT_NONE(&rwalk->XD(hit))) return RES_BAD_ARG; + + /* Check positions */ + if(!dX(eq_eps)(rwalk->vtx.P, frag->P, 1.e-6)) return RES_BAD_ARG; + + /* Check normals */ + dX(normalize)(N, dX_set_fX(N, rwalk->XD(hit).normal)); + if(!dX(eq_eps)(N, frag->Ng, 1.e-6)) return RES_BAD_ARG; + + /* Check time */ + if(!eq_eps(rwalk->vtx.time, frag->time, 1.e-6) + && !(IS_INF(rwalk->vtx.time) && IS_INF(frag->time))) { + return RES_BAD_ARG; } + + /* Check parametric coordinates */ #if (SDIS_XD_DIMENSION == 2) - uv[0] = rwalk->hit.u; + uv[0] = rwalk->XD(hit).u; #else - d2_set_f2(uv, rwalk->hit.uv); + d2_set_f2(uv, rwalk->XD(hit).uv); #endif - return d2_eq_eps(uv, frag->uv, 1.e-6); + if(!d2_eq_eps(uv, frag->uv, 1.e-6)) return RES_BAD_ARG; + + return RES_OK; } static void XD(sample_reinjection_dir) - (const struct XD(rwalk)* rwalk, + (const struct rwalk* rwalk, struct ssp_rng* rng, float dir[DIM]) { @@ -145,15 +215,15 @@ XD(sample_reinjection_dir) * discard the sqrt(2)/2 constant. */ const uint64_t r = ssp_rng_uniform_uint64(rng, 0, 1); ASSERT(rwalk && rng && dir); - ASSERT(!SXD_HIT_NONE(&rwalk->hit)); - ASSERT(!rwalk->mdm); + ASSERT(!SXD_HIT_NONE(&rwalk->XD(hit))); + ASSERT(rwalk->enc_id == ENCLOSURE_ID_NULL); if(r) { - dir[0] = rwalk->hit.normal[0] - rwalk->hit.normal[1]; - dir[1] = rwalk->hit.normal[0] + rwalk->hit.normal[1]; + dir[0] = rwalk->XD(hit).normal[0] - rwalk->XD(hit).normal[1]; + dir[1] = rwalk->XD(hit).normal[0] + rwalk->XD(hit).normal[1]; } else { - dir[0] = rwalk->hit.normal[0] + rwalk->hit.normal[1]; - dir[1] =-rwalk->hit.normal[0] + rwalk->hit.normal[1]; + dir[0] = rwalk->XD(hit).normal[0] + rwalk->XD(hit).normal[1]; + dir[1] =-rwalk->XD(hit).normal[0] + rwalk->XD(hit).normal[1]; } f2_normalize(dir, dir); #else @@ -162,17 +232,17 @@ XD(sample_reinjection_dir) * radius of its base is 1. */ float frame[9]; ASSERT(rwalk && rng && dir); - ASSERT(!SXD_HIT_NONE(&rwalk->hit)); - ASSERT(!rwalk->mdm); - ASSERT(fX(is_normalized)(rwalk->hit.normal)); + ASSERT(!SXD_HIT_NONE(&rwalk->XD(hit))); + ASSERT(rwalk->enc_id == ENCLOSURE_ID_NULL); + ASSERT(fX(is_normalized)(rwalk->XD(hit).normal)); ssp_ran_circle_uniform_float(rng, dir, NULL); dir[2] = (float)(1.0/sqrt(2)); - f33_basis(frame, rwalk->hit.normal); + f33_basis(frame, rwalk->XD(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)); + ASSERT(eq_epsf(f3_dot(dir, rwalk->XD(hit).normal), (float)(1.0/sqrt(3)), 1.e-4f)); #endif } @@ -180,7 +250,7 @@ XD(sample_reinjection_dir) #if DIM == 2 static void XD(move_away_primitive_boundaries) - (const struct XD(rwalk)* rwalk, + (const struct rwalk* rwalk, const double delta, double position[DIM]) /* Position to move */ { @@ -189,9 +259,9 @@ XD(move_away_primitive_boundaries) float dir[DIM]; float len; const float st = 0.5f; - ASSERT(rwalk && !SXD_HIT_NONE(&rwalk->hit) && delta > 0); + ASSERT(rwalk && !SXD_HIT_NONE(&rwalk->XD(hit)) && delta > 0); - SXD(primitive_get_attrib(&rwalk->hit.prim, SXD_POSITION, st, &attr)); + SXD(primitive_get_attrib(&rwalk->XD(hit).prim, SXD_POSITION, st, &attr)); fX_set_dX(pos, position); fX(sub)(dir, attr.value, pos); @@ -205,7 +275,7 @@ XD(move_away_primitive_boundaries) * numerical issues leading to inconsistent random walks. */ static void XD(move_away_primitive_boundaries) - (const struct XD(rwalk)* rwalk, + (const struct rwalk* rwalk, const double delta, double position[DIM]) { @@ -222,14 +292,14 @@ XD(move_away_primitive_boundaries) int imin = 0; int imid = 0; int i; - ASSERT(rwalk && delta > 0 && !S3D_HIT_NONE(&rwalk->hit)); + ASSERT(rwalk && delta > 0 && !S3D_HIT_NONE(&rwalk->XD(hit))); fX_set_dX(P, position); /* Fetch triangle vertices */ - S3D(triangle_get_vertex_attrib(&rwalk->hit.prim, 0, S3D_POSITION, &v0)); - S3D(triangle_get_vertex_attrib(&rwalk->hit.prim, 1, S3D_POSITION, &v1)); - S3D(triangle_get_vertex_attrib(&rwalk->hit.prim, 2, S3D_POSITION, &v2)); + S3D(triangle_get_vertex_attrib(&rwalk->XD(hit).prim, 0, S3D_POSITION, &v0)); + S3D(triangle_get_vertex_attrib(&rwalk->XD(hit).prim, 1, S3D_POSITION, &v1)); + S3D(triangle_get_vertex_attrib(&rwalk->XD(hit).prim, 2, S3D_POSITION, &v2)); /* Compute the edge vector */ f3_sub(E[0], v1.value, v0.value); @@ -315,10 +385,10 @@ XD(find_reinjection_ray) /* # attempts to find a ray direction */ int MAX_ATTEMPTS = 1; - /* Physical properties */ - struct sdis_interface* interf; - struct sdis_medium* mdm0; - struct sdis_medium* mdm1; + /* Enclosures */ + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; + unsigned enc0_id = ENCLOSURE_ID_NULL; + unsigned enc1_id = ENCLOSURE_ID_NULL; struct hit_filter_data filter_data = HIT_FILTER_DATA_NULL; struct sXd(hit) hit; @@ -338,7 +408,7 @@ XD(find_reinjection_ray) res_T res = RES_OK; ASSERT(scn && args && ray); - ASSERT(XD(check_find_reinjection_ray_args)(args)); + ASSERT(XD(check_find_reinjection_ray_args)(scn, args) == RES_OK); *ray = XD(REINJECTION_RAY_NULL); MAX_ATTEMPTS = args->can_move ? 2 : 1; @@ -350,39 +420,39 @@ XD(find_reinjection_ray) do { fX_set_dX(org, ray->org); - filter_data.XD(hit) = args->rwalk->hit; + filter_data.XD(hit) = args->rwalk->XD(hit); filter_data.epsilon = args->distance * 0.01; SXD(scene_view_trace_ray (scn->sXd(view), org, args->dir0, range, &filter_data, &hit0)); SXD(scene_view_trace_ray (scn->sXd(view), org, args->dir1, range, &filter_data, &hit1)); - /* Retrieve the medium at the reinjection pos along dir0 */ - if(SXD_HIT_NONE(&hit0)) { + /* Retrieve the enclosure at the reinjection pos along dir0 */ + if(!SXD_HIT_NONE(&hit0)) { + scene_get_enclosure_ids(scn, hit0.prim.prim_id, enc_ids); + side = fX(dot)(args->dir0, hit0.normal) < 0 ? SDIS_FRONT : SDIS_BACK; + enc0_id = enc_ids[side]; + } else { XD(move_pos)(dX(set)(tmp, ray->org), args->dir0, (float)args->distance); - res = scene_get_medium_in_closed_boundaries(scn, tmp, &mdm0); - if(res == RES_BAD_OP) { mdm0 = NULL; res = RES_OK; } + res = scene_get_enclosure_id_in_closed_boundaries(scn, tmp, &enc0_id); + if(res == RES_BAD_OP) { enc0_id = ENCLOSURE_ID_NULL; res = RES_OK; } if(res != RES_OK) goto error; - } else { - interf = scene_get_interface(scn, hit0.prim.prim_id); - side = fX(dot)(args->dir0, hit0.normal) < 0 ? SDIS_FRONT : SDIS_BACK; - mdm0 = interface_get_medium(interf, side); } - /* Retrieve the medium at the reinjection pos along dir1 */ - if(SXD_HIT_NONE(&hit1)) { + /* Retrieve the enclosure at the reinjection pos along dir1 */ + if(!SXD_HIT_NONE(&hit1)) { + scene_get_enclosure_ids(scn, hit1.prim.prim_id, enc_ids); + side = fX(dot)(args->dir1, hit1.normal) < 0 ? SDIS_FRONT : SDIS_BACK; + enc1_id = enc_ids[side]; + } else { XD(move_pos)(dX(set)(tmp, ray->org), args->dir1, (float)args->distance); - res = scene_get_medium_in_closed_boundaries(scn, tmp, &mdm1); - if(res == RES_BAD_OP) { mdm1 = NULL; res = RES_OK; } + res = scene_get_enclosure_id_in_closed_boundaries(scn, tmp, &enc1_id); + if(res == RES_BAD_OP) { enc1_id = ENCLOSURE_ID_NULL; res = RES_OK; } if(res != RES_OK) goto error; - } else { - interf = scene_get_interface(scn, hit1.prim.prim_id); - side = fX(dot)(args->dir1, hit1.normal) < 0 ? SDIS_FRONT : SDIS_BACK; - mdm1 = interface_get_medium(interf, side); } dst0 = dst1 = -1; - if(mdm0 == args->solid) { /* Check reinjection consistency */ + if(enc0_id == args->solid_enc_id) { /* Check reinjection consistency */ if(hit0.distance <= dst_adjusted) { dst0 = hit0.distance; } else { @@ -390,7 +460,7 @@ XD(find_reinjection_ray) hit0 = SXD_HIT_NULL; } } - if(mdm1 == args->solid) { /* Check reinjection consistency */ + if(enc1_id == args->solid_enc_id) { /* Check reinjection consistency */ if(hit1.distance <= dst_adjusted) { dst1 = hit1.distance; } else { @@ -503,23 +573,25 @@ XD(find_reinjection_ray_and_check_validity) struct XD(reinjection_ray)* ray) { double pos[DIM]; - struct sdis_medium* reinject_mdm; res_T res = RES_OK; ASSERT(scn && args && ray); - ASSERT(XD(check_find_reinjection_ray_args)(args)); + ASSERT(XD(check_find_reinjection_ray_args)(scn, args) == RES_OK); /* Select a reinjection direction */ res = XD(find_reinjection_ray)(scn, args, ray); if(res != RES_OK) goto error; if(SXD_HIT_NONE(&ray->hit)) { - /* Check medium consistency at the reinjection position */ + unsigned enc_id = ENCLOSURE_ID_NULL; + + /* Obtain the enclosure in which the reinjection position lies */ XD(move_pos)(dX(set)(pos, ray->org), ray->dir, (float)ray->dst); - res = scene_get_medium_in_closed_boundaries(scn, pos, &reinject_mdm); + res = scene_get_enclosure_id_in_closed_boundaries(scn, pos, &enc_id); if(res != RES_OK) goto error; - if(reinject_mdm != args->solid) { + /* Check enclosure consistency at the reinjection position */ + if(enc_id != args->solid_enc_id) { res = RES_BAD_OP; goto error; } @@ -535,9 +607,9 @@ static res_T XD(handle_volumic_power) (struct sdis_medium* solid, struct rwalk_context* rwalk_ctx, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, const double reinject_dst_m, - struct XD(temperature)* T) + struct temperature* T) { double power; double lambda; @@ -592,22 +664,22 @@ error: res_T XD(sample_reinjection_step_solid_fluid) (struct sdis_scene* scn, - const struct XD(sample_reinjection_step_args)* args, - struct XD(reinjection_step)* step) + const struct sample_reinjection_step_args* args, + struct reinjection_step* step) { /* Input/output data of the function finding a valid reinjection ray */ struct XD(find_reinjection_ray_args) find_reinject_ray_args = XD(FIND_REINJECTION_RAY_ARGS_NULL); struct XD(reinjection_ray) ray = XD(REINJECTION_RAY_NULL); + /* Enclosures */ + const struct enclosure* solid_enc = NULL; + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; + /* In 2D it is useless to try to resample a reinjection direction since there * is only one possible direction */ const int MAX_ATTEMPTS = DIM == 2 ? 1 : 10; - /* Enclosure */ - const struct enclosure* solid_enclosure = NULL; - unsigned enc_ids[2]; - /* Miscellaneous variables */ float dir0[DIM]; /* Sampled direction */ float dir1[DIM]; /* Sampled direction reflected */ @@ -616,23 +688,23 @@ XD(sample_reinjection_step_solid_fluid) /* Pre-conditions */ ASSERT(scn && args && step); - ASSERT(XD(check_sample_reinjection_step_args)(args)); + ASSERT(XD(check_sample_reinjection_step_args)(scn, args) == RES_OK); /* Initialise the reinjection step */ - *step = XD(REINJECTION_STEP_NULL); + *step = REINJECTION_STEP_NULL; /* Check whether the solid enclosure is part of the system, or whether it is * only there to describe boundary conditions. In the latter case, the * enclosure has no geometrical existence and it is sufficient to return a * valid reinjection step which will be used to select the next step. Note * that if the trajectory passes through the solid enclosure, it will stop, - * i.e. the temperature of the solid should be fixed. If it doesn't, it's an + * i.e. the temperature of the solid should be fixed. If it doesn't, it's a * error */ - scene_get_enclosure_ids(scn, args->rwalk->hit.prim.prim_id, enc_ids); - solid_enclosure = scene_get_enclosure(scn, enc_ids[args->side]); - if(solid_enclosure->medium_id == ENCLOSURE_MULTI_MEDIA) { - step->hit = SXD_HIT_NULL; - fX(normalize)(step->direction, args->rwalk->hit.normal); + scene_get_enclosure_ids(scn, args->rwalk->XD(hit).prim.prim_id, enc_ids); + solid_enc = scene_get_enclosure(scn, enc_ids[args->side]); + if(solid_enc->medium_id == MEDIUM_ID_MULTI) { + step->XD(hit) = SXD_HIT_NULL; + fX(normalize)(step->direction, args->rwalk->XD(hit).normal); if(args->side == SDIS_BACK) fX(minus)(step->direction, step->direction); step->distance = (float)args->distance; goto exit; /* That's all folks! */ @@ -644,7 +716,7 @@ XD(sample_reinjection_step_solid_fluid) XD(sample_reinjection_dir)(args->rwalk, args->rng, dir0); /* Reflect the sampled direction around the normal */ - XD(reflect)(dir1, dir0, args->rwalk->hit.normal); + XD(reflect)(dir1, dir0, args->rwalk->XD(hit).normal); /* Flip the sampled directions if one wants to reinject to back side */ if(args->side == SDIS_BACK) { @@ -653,7 +725,7 @@ XD(sample_reinjection_step_solid_fluid) } /* Find the reinjection step */ - find_reinject_ray_args.solid = args->solid; + find_reinject_ray_args.solid_enc_id = args->solid_enc_id; find_reinject_ray_args.rwalk = args->rwalk; find_reinject_ray_args.distance = args->distance; find_reinject_ray_args.can_move = 1; @@ -676,7 +748,7 @@ XD(sample_reinjection_step_solid_fluid) } /* Setup the reinjection step */ - step->hit = ray.hit; + step->XD(hit) = ray.hit; step->distance = ray.dst; fX(set)(step->direction, ray.dir); @@ -687,7 +759,7 @@ XD(sample_reinjection_step_solid_fluid) /* Post-conditions */ ASSERT(dX(eq)(args->rwalk->vtx.P, ray.org)); - ASSERT(XD(check_reinjection_step)(step)); + ASSERT(XD(check_reinjection_step)(step) == RES_OK); exit: return res; @@ -698,10 +770,10 @@ error: res_T XD(sample_reinjection_step_solid_solid) (struct sdis_scene* scn, - const struct XD(sample_reinjection_step_args)* args_frt, - const struct XD(sample_reinjection_step_args)* args_bck, - struct XD(reinjection_step)* step_frt, - struct XD(reinjection_step)* step_bck) + const struct sample_reinjection_step_args* args_frt, + const struct sample_reinjection_step_args* args_bck, + struct reinjection_step* step_frt, + struct reinjection_step* step_bck) { /* Input/output data of the function finding a valid reinjection ray */ struct XD(find_reinjection_ray_args) find_reinject_ray_frt_args = @@ -715,7 +787,7 @@ XD(sample_reinjection_step_solid_solid) double rwalk_pos_backup[DIM]; /* Variables shared by the two side */ - struct XD(rwalk)* rwalk = NULL; + struct rwalk* rwalk = NULL; struct ssp_rng* rng = NULL; /* In 2D it is useless to try to resample a reinjection direction since there @@ -723,26 +795,26 @@ XD(sample_reinjection_step_solid_solid) const int MAX_ATTEMPTS = DIM == 2 ? 1 : 10; /* Enclosure */ - const struct enclosure* enclosure_bck = NULL; - const struct enclosure* enclosure_frt = NULL; - int multiple_media_bck = 0; - int multiple_media_frt = 0; unsigned enc_ids[2]; + const struct enclosure* enc_frt = NULL; + const struct enclosure* enc_bck = NULL; float dir_frt_samp[DIM]; /* Sampled direction */ float dir_frt_refl[DIM]; /* Sampled direction reflected */ float dir_bck_samp[DIM]; /* Negated sampled direction */ float dir_bck_refl[DIM]; /* Negated sampled direction reflected */ + int multi_frt = 0; + int multi_bck = 0; int iattempt = 0; /* #attempts to find a reinjection dir */ res_T res = RES_OK; /* Pre-conditions */ ASSERT(scn && args_frt && args_bck && step_frt && step_bck); - ASSERT(XD(check_sample_reinjection_step_args)(args_frt)); - ASSERT(XD(check_sample_reinjection_step_args)(args_bck)); + ASSERT(XD(check_sample_reinjection_step_args)(scn, args_frt) == RES_OK); + ASSERT(XD(check_sample_reinjection_step_args)(scn, args_bck) == RES_OK); ASSERT(args_frt->side == SDIS_FRONT); ASSERT(args_bck->side == SDIS_BACK); - ASSERT(SXD_PRIMITIVE_EQ(&args_frt->rwalk->hit.prim, &args_bck->rwalk->hit.prim)); + ASSERT(SXD_PRIMITIVE_EQ(&args_frt->rwalk->XD(hit).prim, &args_bck->rwalk->XD(hit).prim)); rng = args_frt->rng; rwalk = args_frt->rwalk; @@ -750,8 +822,8 @@ XD(sample_reinjection_step_solid_solid) ASSERT(args_bck->rwalk == rwalk); /* Initialise the reinjection steps */ - *step_frt = XD(REINJECTION_STEP_NULL); - *step_bck = XD(REINJECTION_STEP_NULL); + *step_frt = REINJECTION_STEP_NULL; + *step_bck = REINJECTION_STEP_NULL; /* Check whether the solid enclosure is part of the system, or whether it is * only there to describe boundary conditions. In the latter case, the @@ -760,25 +832,23 @@ XD(sample_reinjection_step_solid_solid) * that if the trajectory passes through the solid enclosure, it will stop, * i.e. the temperature of the solid should be fixed. If it doesn't, it's an * error */ - scene_get_enclosure_ids(scn, args_frt->rwalk->hit.prim.prim_id, enc_ids); - enclosure_frt = scene_get_enclosure(scn, enc_ids[SDIS_FRONT]); - enclosure_bck = scene_get_enclosure(scn, enc_ids[SDIS_BACK]); - if(enclosure_frt->medium_id == ENCLOSURE_MULTI_MEDIA) { - step_frt->hit = SXD_HIT_NULL; - fX(normalize)(step_frt->direction, args_frt->rwalk->hit.normal); + scene_get_enclosure_ids(scn, args_frt->rwalk->XD(hit).prim.prim_id, enc_ids); + enc_frt = scene_get_enclosure(scn, enc_ids[SDIS_FRONT]); + enc_bck = scene_get_enclosure(scn, enc_ids[SDIS_BACK]); + if(enc_frt->medium_id == MEDIUM_ID_MULTI) { + step_frt->XD(hit) = SXD_HIT_NULL; + fX(normalize)(step_frt->direction, args_frt->rwalk->XD(hit).normal); step_frt->distance = (float)args_frt->distance; - multiple_media_frt = 1; + multi_frt = 1; } - if(enclosure_bck->medium_id == ENCLOSURE_MULTI_MEDIA) { - step_bck->hit = SXD_HIT_NULL; - fX(normalize)(step_bck->direction, args_bck->rwalk->hit.normal); - fX(minus)(step_bck->direction, step_bck->direction); + if(enc_bck->medium_id == MEDIUM_ID_MULTI) { + step_bck->XD(hit) = SXD_HIT_NULL; + fX(normalize)(step_bck->direction, args_bck->rwalk->XD(hit).normal); step_bck->distance = (float)args_bck->distance; - multiple_media_bck = 1; + multi_bck = 1; } - if(multiple_media_frt && multiple_media_bck) - goto exit; /* That's all folks! */ + if(multi_frt && multi_bck) goto exit; /* That's all folks */ dX(set)(rwalk_pos_backup, rwalk->vtx.P); iattempt = 0; @@ -789,16 +859,15 @@ XD(sample_reinjection_step_solid_solid) /* Sample a reinjection direction and reflect it around the normal. Then * reflect them on the back side of the interface. */ XD(sample_reinjection_dir)(rwalk, rng, dir_frt_samp); - XD(reflect)(dir_frt_refl, dir_frt_samp, rwalk->hit.normal); + XD(reflect)(dir_frt_refl, dir_frt_samp, rwalk->XD(hit).normal); fX(minus)(dir_bck_samp, dir_frt_samp); fX(minus)(dir_bck_refl, dir_frt_refl); /* Reject the sampling of the re-injection step if it has already been * defined, i.e. if the enclosure is a limit condition */ - if(!multiple_media_frt) { - + if(!multi_frt) { /* Find the reinjection ray for the front side */ - find_reinject_ray_frt_args.solid = args_frt->solid; + find_reinject_ray_frt_args.solid_enc_id = args_frt->solid_enc_id; find_reinject_ray_frt_args.rwalk = args_frt->rwalk; find_reinject_ray_frt_args.distance = args_frt->distance; find_reinject_ray_frt_args.can_move = 1; @@ -815,10 +884,9 @@ XD(sample_reinjection_step_solid_solid) /* Reject the sampling of the re-injection step if it has already been * defined, i.e. if the enclosure is a limit condition */ - if(!multiple_media_bck) { - + if(!multi_bck) { /* Select the reinjection direction and distance for the back side */ - find_reinject_ray_bck_args.solid = args_bck->solid; + find_reinject_ray_bck_args.solid_enc_id = args_bck->solid_enc_id; find_reinject_ray_bck_args.rwalk = args_bck->rwalk; find_reinject_ray_bck_args.distance = args_bck->distance; find_reinject_ray_bck_args.can_move = 1; @@ -834,7 +902,7 @@ XD(sample_reinjection_step_solid_solid) /* If random walk was moved to find a valid rinjection ray on back side, * one has to find a valid reinjection on front side from the new pos */ - if(ray_bck.position_was_moved && !multiple_media_frt) { + if(ray_bck.position_was_moved) { find_reinject_ray_frt_args.can_move = 0; res = XD(find_reinjection_ray_and_check_validity) (scn, &find_reinject_ray_frt_args, &ray_frt); @@ -858,17 +926,17 @@ XD(sample_reinjection_step_solid_solid) } /* Setup the front and back reinjection steps */ - if(!multiple_media_frt) { - step_frt->hit = ray_frt.hit; + if(!multi_frt) { + step_frt->XD(hit) = ray_frt.hit; step_frt->distance = ray_frt.dst; fX(set)(step_frt->direction, ray_frt.dir); - ASSERT(XD(check_reinjection_step)(step_frt)); /* Post-condition */ + ASSERT(XD(check_reinjection_step)(step_frt) == RES_OK); /* Post-condition */ } - if(!multiple_media_bck) { - step_bck->hit = ray_bck.hit; + if(!multi_bck) { + step_bck->XD(hit) = ray_bck.hit; step_bck->distance = ray_bck.dst; fX(set)(step_bck->direction, ray_bck.dir); - ASSERT(XD(check_reinjection_step)(step_bck)); /* Post-condition */ + ASSERT(XD(check_reinjection_step)(step_bck) == RES_OK); /* Post-condition */ } exit: @@ -879,17 +947,28 @@ error: res_T XD(solid_reinjection) - (struct sdis_medium* solid, - struct XD(solid_reinjection_args)* args) + (struct sdis_scene* scn, + const unsigned solid_enc_id, + struct solid_reinjection_args* args) { + /* Properties */ struct solid_props props = SOLID_PROPS_NULL; + struct sdis_medium* solid = NULL; + const struct enclosure* enc = NULL; + double reinject_dst_m; /* Reinjection distance in meters */ double mu; res_T res = RES_OK; - ASSERT(solid && XD(check_solid_reinjection_args)(args)); + ASSERT(XD(check_solid_reinjection_args)(args) == RES_OK); + ASSERT(solid_enc_id != ENCLOSURE_ID_NULL); reinject_dst_m = args->reinjection->distance * args->fp_to_meter; + /* Get the enclosure medium properties */ + enc = scene_get_enclosure(scn, solid_enc_id); + res = scene_get_enclosure_medium(scn, enc, &solid); + if(res != RES_OK) goto error; + ASSERT(sdis_medium_get_type(solid) == SDIS_SOLID); res = solid_get_properties(solid, &args->rwalk->vtx, &props); if(res != RES_OK) goto error; @@ -899,10 +978,10 @@ XD(solid_reinjection) if(res != RES_OK) goto error; /* Time rewind */ - args->rwalk->mdm = solid; /* Medium into which the time is rewind */ + args->rwalk->enc_id = solid_enc_id; /* Enclosure into which the time is rewind */ mu = (2*DIM*props.lambda)/(props.rho*props.cp*reinject_dst_m*reinject_dst_m); - res = XD(time_rewind) - (mu, props.t0, args->rng, args->rwalk, args->rwalk_ctx, args->T); + res = time_rewind + (scn, mu, props.t0, args->rng, args->rwalk, args->rwalk_ctx, args->T); if(res != RES_OK) goto error; /* Test if a limit condition was reached */ @@ -915,18 +994,18 @@ XD(solid_reinjection) args->reinjection->distance); /* The random walk is in the solid */ - if(args->reinjection->hit.distance != args->reinjection->distance) { + if(args->reinjection->XD(hit).distance != args->reinjection->distance) { args->T->func = XD(conductive_path); - args->rwalk->mdm = solid; - args->rwalk->hit = SXD_HIT_NULL; + args->rwalk->enc_id = solid_enc_id; + args->rwalk->XD(hit) = SXD_HIT_NULL; args->rwalk->hit_side = SDIS_SIDE_NULL__; /* The random walk is at a boundary */ } else { args->T->func = XD(boundary_path); - args->rwalk->mdm = NULL; - args->rwalk->hit = args->reinjection->hit; - if(fX(dot)(args->reinjection->hit.normal, args->reinjection->direction) < 0) { + args->rwalk->enc_id = ENCLOSURE_ID_NULL; + args->rwalk->XD(hit) = args->reinjection->XD(hit); + if(fX(dot)(args->reinjection->XD(hit).normal, args->reinjection->direction) < 0) { args->rwalk->hit_side = SDIS_FRONT; } else { args->rwalk->hit_side = SDIS_BACK; @@ -952,7 +1031,7 @@ res_T XD(handle_net_flux) (const struct sdis_scene* scn, const struct handle_net_flux_args* args, - struct XD(temperature)* T) + struct temperature* T) { double flux_term; double phi; @@ -1013,7 +1092,7 @@ XD(check_Tref) if((Bound) < 0) { \ log_err(scn->dev, \ "%s: the "Name" temperature cannot be negative " \ - "to sample a radiative path (T"Name" = %g K).\n", \ + "to sample a radiative path -- T"Name" = %g K\n", \ func_name, (Bound)); \ return RES_BAD_OP_IRRECOVERABLE; \ } \ @@ -1032,7 +1111,7 @@ XD(check_Tref) if(SDIS_TEMPERATURE_IS_UNKNOWN(Tref)) { log_err(scn->dev, - "%s: the reference temperature is unknown at `"FORMAT_VECX". " + "%s: the reference temperature is unknown at ("FORMAT_VECX"). " "Sampling a radiative path requires a valid reference temperature field.\n", func_name, SPLITX(pos)); return RES_BAD_OP_IRRECOVERABLE; @@ -1040,7 +1119,7 @@ XD(check_Tref) if(Tref < 0) { log_err(scn->dev, - "%s: the reference temperature is negative at `"FORMAT_VECX" (Tref = %g K). " + "%s: the reference temperature is negative at ("FORMAT_VECX") and Tref = %g K. " "Sampling a radiative path requires a known, positive reference " "temperature field.\n", func_name, SPLITX(pos), Tref); @@ -1049,7 +1128,7 @@ XD(check_Tref) if(Tref < scn->tmin || scn->tmax < Tref) { log_err(scn->dev, - "%s: invalid reference temperature at `"FORMAT_VECX"' (Tref = %g K). " + "%s: invalid reference temperature at ("FORMAT_VECX") and Tref=%g K. " "It must be included in the provided temperature range " "(Tmin = %g K; Tmax = %g K)\n", func_name, SPLITX(pos), Tref, scn->tmin, scn->tmax); diff --git a/src/sdis_heat_path_boundary_Xd_handle_external_net_flux.h b/src/sdis_heat_path_boundary_Xd_handle_external_net_flux.h @@ -173,7 +173,7 @@ static INLINE res_T XD(check_handle_external_net_flux_args) (const struct sdis_scene* scn, const char* func_name, - const struct XD(handle_external_net_flux_args)* args) + const struct handle_external_net_flux_args* args) { int net_flux = 0; res_T res = RES_OK; @@ -181,7 +181,7 @@ XD(check_handle_external_net_flux_args) /* Handle bugs */ ASSERT(scn && func_name && args); ASSERT(args->interf && args->frag); - ASSERT(!SXD_HIT_NONE(args->hit)); + ASSERT(!SXD_HIT_NONE(args->XD(hit))); ASSERT(args->h_cond >= 0 && args->h_conv >= 0 && args->h_radi >= 0); ASSERT(args->h_cond + args->h_conv + args->h_radi > 0); @@ -453,8 +453,8 @@ res_T XD(handle_external_net_flux) (const struct sdis_scene* scn, struct ssp_rng* rng, - const struct XD(handle_external_net_flux_args)* args, - struct XD(temperature)* T) + const struct handle_external_net_flux_args* args, + struct temperature* T) { /* Terms to be registered in the green function */ struct sdis_green_external_flux_terms green = @@ -516,13 +516,13 @@ XD(handle_external_net_flux) * interface side */ cos_theta = d3_dot(N, src_sample.dir); if(cos_theta > 0) { - Ld = XD(direct_contribution)(scn, &src_sample, frag.P, args->hit); + Ld = XD(direct_contribution)(scn, &src_sample, frag.P, args->XD(hit)); incident_flux_direct = cos_theta * Ld / src_sample.pdf; /* [W/m^2] */ } /* Calculate the incident diffuse flux [W/m^2] */ res = XD(compute_incident_diffuse_flux) - (scn, rng, frag.P, N, frag.time, args->hit, &incident_flux_diffuse); + (scn, rng, frag.P, N, frag.time, args->XD(hit), &incident_flux_diffuse); if(res != RES_OK) goto error; /* Calculate the incident flux without the part scattered by the environment. diff --git a/src/sdis_heat_path_boundary_Xd_solid_fluid_picard1.h b/src/sdis_heat_path_boundary_Xd_solid_fluid_picard1.h @@ -31,8 +31,8 @@ static INLINE res_T XD(rwalk_get_Tref) (const struct sdis_scene* scn, - const struct XD(rwalk)* rwalk, - const struct XD(temperature)* T, + const struct rwalk* rwalk, + const struct temperature* T, double* out_Tref) { double Tref = SDIS_TEMPERATURE_NONE; @@ -52,16 +52,16 @@ XD(rwalk_get_Tref) } else { struct sdis_interface_fragment frag; struct sdis_interface* interf = NULL; - ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + ASSERT(!SXD_HIT_NONE(&rwalk->XD(hit))); /* Fetch the interface where the random walk ends */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + interf = scene_get_interface(scn, rwalk->XD(hit).prim.prim_id); ASSERT(rwalk->hit_side!=SDIS_FRONT || interf->medium_front->type==SDIS_FLUID); ASSERT(rwalk->hit_side!=SDIS_BACK || interf->medium_back->type==SDIS_FLUID); /* Fragment on the fluid side of the boundary onto which the rwalk ends */ XD(setup_interface_fragment) - (&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); + (&frag, &rwalk->vtx, &rwalk->XD(hit), rwalk->hit_side); Tref = interface_side_get_reference_temperature(interf, &frag); } @@ -85,30 +85,32 @@ XD(solid_fluid_boundary_picard1_path) (struct sdis_scene* scn, struct rwalk_context* ctx, const struct sdis_interface_fragment* frag, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct XD(temperature)* T) + struct temperature* T) { /* Input argument used to handle the net flux */ struct handle_net_flux_args handle_net_flux_args = HANDLE_NET_FLUX_ARGS_NULL; /* Input argument used to handle the external net flux */ - struct XD(handle_external_net_flux_args) handle_external_net_flux_args = - XD(HANDLE_EXTERNAL_NET_FLUX_ARGS_NULL); + struct handle_external_net_flux_args handle_external_net_flux_args = + HANDLE_EXTERNAL_NET_FLUX_ARGS_NULL; /* Input/output arguments of the function used to sample a reinjection */ - struct XD(sample_reinjection_step_args) samp_reinject_step_args = - XD(SAMPLE_REINJECTION_STEP_ARGS_NULL); - struct XD(reinjection_step) reinject_step = - XD(REINJECTION_STEP_NULL); + struct sample_reinjection_step_args samp_reinject_step_args = + SAMPLE_REINJECTION_STEP_ARGS_NULL; + struct reinjection_step reinject_step = REINJECTION_STEP_NULL; /* Temperature and random walk state of the sampled radiative path */ - struct XD(temperature) T_s; - struct XD(rwalk) rwalk_s; + struct temperature T_s; + struct rwalk rwalk_s; /* Fragment on the fluid side of the boundary */ struct sdis_interface_fragment frag_fluid; + /* The enclosures split by the boundary */ + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; + /* Data attached to the boundary */ struct sdis_interface* interf = NULL; struct sdis_medium* solid = NULL; @@ -136,10 +138,10 @@ XD(solid_fluid_boundary_picard1_path) res_T res = RES_OK; ASSERT(scn && rwalk && rng && T && ctx); - ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); + ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag) == RES_OK); /* Retrieve the solid and the fluid split by the boundary */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + interf = scene_get_interface(scn, rwalk->XD(hit).prim.prim_id); solid = interface_get_medium(interf, SDIS_FRONT); fluid = interface_get_medium(interf, SDIS_BACK); solid_side = SDIS_FRONT; @@ -150,6 +152,9 @@ XD(solid_fluid_boundary_picard1_path) ASSERT(fluid->type == SDIS_FLUID); } + /* Get the enclosures split by the boundary */ + scene_get_enclosure_ids(scn, rwalk->XD(hit).prim.prim_id, enc_ids); + /* Setup a fragment for the fluid side */ frag_fluid = *frag; frag_fluid.side = fluid_side; @@ -178,7 +183,7 @@ XD(solid_fluid_boundary_picard1_path) /* Sample a reinjection step */ samp_reinject_step_args.rng = rng; - samp_reinject_step_args.solid = solid; + samp_reinject_step_args.solid_enc_id = enc_ids[solid_side]; samp_reinject_step_args.rwalk = rwalk; samp_reinject_step_args.distance = delta_boundary; samp_reinject_step_args.side = solid_side; @@ -222,7 +227,7 @@ XD(solid_fluid_boundary_picard1_path) /* Handle the external net flux if any */ handle_external_net_flux_args.interf = interf; handle_external_net_flux_args.frag = frag; - handle_external_net_flux_args.hit = &rwalk->hit; + handle_external_net_flux_args.XD(hit) = &rwalk->XD(hit); handle_external_net_flux_args.green_path = ctx->green_path; handle_external_net_flux_args.picard_order = get_picard_order(ctx); handle_external_net_flux_args.h_cond = h_cond; @@ -248,15 +253,15 @@ XD(solid_fluid_boundary_picard1_path) /* Switch in convective path */ if(r < p_conv) { T->func = XD(convective_path); - rwalk->mdm = fluid; + rwalk->enc_id = enc_ids[fluid_side]; rwalk->hit_side = fluid_side; break; } /* Switch in conductive path */ if(r < p_conv + p_cond) { - struct XD(solid_reinjection_args) solid_reinject_args = - XD(SOLID_REINJECTION_ARGS_NULL); + struct solid_reinjection_args solid_reinject_args = + SOLID_REINJECTION_ARGS_NULL; /* Perform the reinjection into the solid */ solid_reinject_args.reinjection = &reinject_step; @@ -265,7 +270,7 @@ XD(solid_fluid_boundary_picard1_path) solid_reinject_args.rng = rng; solid_reinject_args.T = T; solid_reinject_args.fp_to_meter = scn->fp_to_meter; - res = XD(solid_reinjection)(solid, &solid_reinject_args); + res = XD(solid_reinjection)(scn, enc_ids[solid_side], &solid_reinject_args); if(res != RES_OK) goto error; break; } @@ -282,7 +287,7 @@ XD(solid_fluid_boundary_picard1_path) /* Sample a radiative path and get the Tref at its end. */ T_s = *T; rwalk_s = *rwalk; - rwalk_s.mdm = fluid; + rwalk_s.enc_id = enc_ids[fluid_side]; rwalk_s.hit_side = fluid_side; res = XD(radiative_path)(scn, ctx, &rwalk_s, rng, &T_s); if(res != RES_OK) goto error; diff --git a/src/sdis_heat_path_boundary_Xd_solid_fluid_picardN.h b/src/sdis_heat_path_boundary_Xd_solid_fluid_picardN.h @@ -66,23 +66,23 @@ error: static INLINE res_T XD(sample_path) (struct sdis_scene* scn, - const struct XD(rwalk)* rwalk_from, + const struct rwalk* rwalk_from, struct rwalk_context* ctx, struct ssp_rng* rng, - struct XD(temperature)* T) + struct temperature* T) { - struct XD(rwalk) rwalk = XD(RWALK_NULL); + struct rwalk rwalk = RWALK_NULL; res_T res = RES_OK; ASSERT(rwalk_from && rng && T); /* Clean-up the output variable */ - *T = XD(TEMPERATURE_NULL); + *T = TEMPERATURE_NULL; T->func = XD(boundary_path); /* Init the random walk */ rwalk.vtx = rwalk_from->vtx; - rwalk.mdm = rwalk_from->mdm; - rwalk.hit = rwalk_from->hit; + rwalk.enc_id = rwalk_from->enc_id; + rwalk.XD(hit) = rwalk_from->XD(hit); rwalk.hit_side = rwalk_from->hit_side; /* Start the registration of a new heat path */ @@ -128,15 +128,14 @@ XD(solid_fluid_boundary_picardN_path) (struct sdis_scene* scn, struct rwalk_context* ctx, const struct sdis_interface_fragment* frag, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct XD(temperature)* T) + struct temperature* T) { /* Input/output arguments of the function used to sample a reinjection */ - struct XD(sample_reinjection_step_args) samp_reinject_step_args = - XD(SAMPLE_REINJECTION_STEP_ARGS_NULL); - struct XD(reinjection_step) reinject_step = - XD(REINJECTION_STEP_NULL); + struct sample_reinjection_step_args samp_reinject_step_args = + SAMPLE_REINJECTION_STEP_ARGS_NULL; + struct reinjection_step reinject_step = REINJECTION_STEP_NULL; /* Fragment on the fluid side of the boundary */ struct sdis_interface_fragment frag_fluid; @@ -145,6 +144,9 @@ XD(solid_fluid_boundary_picardN_path) struct sdis_heat_vertex hvtx = SDIS_HEAT_VERTEX_NULL; struct sdis_heat_vertex hvtx_s = SDIS_HEAT_VERTEX_NULL; + /* The enclosures split by the boundary */ + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; + /* Data attached to the boundary */ struct sdis_interface* interf = NULL; struct sdis_medium* solid = NULL; @@ -172,8 +174,7 @@ XD(solid_fluid_boundary_picardN_path) res_T res = RES_OK; ASSERT(scn && rwalk && rng && T && ctx); - ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); - + ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag) == RES_OK); /* Fetch the Min/max temperature */ Tmin = ctx->Tmin; @@ -184,7 +185,7 @@ XD(solid_fluid_boundary_picardN_path) That3 = ctx->That3; /* Retrieve the solid and the fluid split by the boundary */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + interf = scene_get_interface(scn, rwalk->XD(hit).prim.prim_id); solid = interface_get_medium(interf, SDIS_FRONT); fluid = interface_get_medium(interf, SDIS_BACK); solid_side = SDIS_FRONT; @@ -195,6 +196,9 @@ XD(solid_fluid_boundary_picardN_path) ASSERT(fluid->type == SDIS_FLUID); } + /* Get the enclosures split by the boundary */ + scene_get_enclosure_ids(scn, rwalk->XD(hit).prim.prim_id, enc_ids); + /* Check that no net flux is set for this interface since the provided * picardN algorithm does not handle it */ res = check_net_flux(scn, interf, frag, get_picard_order(ctx)); @@ -219,7 +223,7 @@ XD(solid_fluid_boundary_picardN_path) /* Sample a reinjection step */ samp_reinject_step_args.rng = rng; - samp_reinject_step_args.solid = solid; + samp_reinject_step_args.solid_enc_id = enc_ids[solid_side]; samp_reinject_step_args.rwalk = rwalk; samp_reinject_step_args.distance = delta_boundary; samp_reinject_step_args.side = solid_side; @@ -256,8 +260,8 @@ XD(solid_fluid_boundary_picardN_path) /* Null collision main loop */ for(;;) { /* Temperature and random walk state of the sampled radiative path */ - struct XD(temperature) T_s; - struct XD(rwalk) rwalk_s; + struct temperature T_s; + struct rwalk rwalk_s; double h_radi, h_radi_min, h_radi_max; /* Radiative coefficients */ double p_radi, p_radi_min, p_radi_max; /* Radiative probas */ @@ -272,15 +276,15 @@ XD(solid_fluid_boundary_picardN_path) /* Switch in convective path */ if(r < p_conv) { T->func = XD(convective_path); - rwalk->mdm = fluid; + rwalk->enc_id = enc_ids[fluid_side]; rwalk->hit_side = fluid_side; break; } /* Switch in conductive path */ if(r < p_conv + p_cond) { - struct XD(solid_reinjection_args) solid_reinject_args = - XD(SOLID_REINJECTION_ARGS_NULL); + struct solid_reinjection_args solid_reinject_args = + SOLID_REINJECTION_ARGS_NULL; /* Perform the reinjection into the solid */ solid_reinject_args.reinjection = &reinject_step; @@ -289,7 +293,7 @@ XD(solid_fluid_boundary_picardN_path) solid_reinject_args.rng = rng; solid_reinject_args.T = T; solid_reinject_args.fp_to_meter = scn->fp_to_meter; - res = XD(solid_reinjection)(solid, &solid_reinject_args); + res = XD(solid_reinjection)(scn, enc_ids[solid_side], &solid_reinject_args); if(res != RES_OK) goto error; break; } @@ -303,7 +307,7 @@ XD(solid_fluid_boundary_picardN_path) /* Sample a radiative path */ T_s = *T; rwalk_s = *rwalk; - rwalk_s.mdm = fluid; + rwalk_s.enc_id = enc_ids[fluid_side]; rwalk_s.hit_side = fluid_side; res = XD(radiative_path)(scn, ctx, &rwalk_s, rng, &T_s); if(res != RES_OK) goto error; @@ -340,9 +344,9 @@ XD(solid_fluid_boundary_picardN_path) } (void)0 #define COMPUTE_TEMPERATURE(Result, RWalk, Temp) { \ - struct XD(temperature) T_p; \ + struct temperature T_p; \ if((Temp)->done) { /* Ambient radiative temperature */ \ - ASSERT(SXD_HIT_NONE(&(RWalk)->hit)); \ + ASSERT(SXD_HIT_NONE(&(RWalk)->XD(hit))); \ T_p = *(Temp); \ } else { \ res = XD(sample_path)(scn, RWalk, ctx, rng, &T_p); \ diff --git a/src/sdis_heat_path_boundary_Xd_solid_solid.h b/src/sdis_heat_path_boundary_Xd_solid_solid.h @@ -33,28 +33,29 @@ XD(solid_solid_boundary_path) (struct sdis_scene* scn, struct rwalk_context* ctx, const struct sdis_interface_fragment* frag, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct XD(temperature)* T) + struct temperature* T) { /* Input/output arguments of the function used to sample a reinjection */ - struct XD(sample_reinjection_step_args) samp_reinject_step_frt_args = - XD(SAMPLE_REINJECTION_STEP_ARGS_NULL); - struct XD(sample_reinjection_step_args) samp_reinject_step_bck_args = - XD(SAMPLE_REINJECTION_STEP_ARGS_NULL); - struct XD(reinjection_step) reinject_step_frt = XD(REINJECTION_STEP_NULL); - struct XD(reinjection_step) reinject_step_bck = XD(REINJECTION_STEP_NULL); - struct XD(reinjection_step)* reinject_step = NULL; + struct sample_reinjection_step_args samp_reinject_step_frt_args = + SAMPLE_REINJECTION_STEP_ARGS_NULL; + struct sample_reinjection_step_args samp_reinject_step_bck_args = + SAMPLE_REINJECTION_STEP_ARGS_NULL; + struct reinjection_step reinject_step_frt = REINJECTION_STEP_NULL; + struct reinjection_step reinject_step_bck = REINJECTION_STEP_NULL; + struct reinjection_step* reinject_step = NULL; /* Reinjection arguments */ - struct XD(solid_reinjection_args) solid_reinject_args = - XD(SOLID_REINJECTION_ARGS_NULL); + struct solid_reinjection_args solid_reinject_args = + SOLID_REINJECTION_ARGS_NULL; /* Data attached to the boundary */ + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; struct sdis_interface* interf = NULL; struct sdis_medium* solid_frt = NULL; struct sdis_medium* solid_bck = NULL; - struct sdis_medium* solid = NULL; + unsigned solid_enc_id = ENCLOSURE_ID_NULL; /* Solid to re-inject */ double lambda_frt; double lambda_bck; @@ -67,11 +68,12 @@ XD(solid_solid_boundary_path) res_T res = RES_OK; ASSERT(scn && ctx && frag && rwalk && rng && T); - ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); + ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag) == RES_OK); (void)frag, (void)ctx; - /* Retrieve the two solids split by the boundary */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + /* Retrieve the two enclosures and associated media split by the boundary */ + scene_get_enclosure_ids(scn, rwalk->XD(hit).prim.prim_id, enc_ids); + interf = scene_get_interface(scn, rwalk->XD(hit).prim.prim_id); solid_frt = interface_get_medium(interf, SDIS_FRONT); solid_bck = interface_get_medium(interf, SDIS_BACK); ASSERT(solid_frt->type == SDIS_SOLID); @@ -93,8 +95,8 @@ XD(solid_solid_boundary_path) /* Sample a front/back reinjection steps */ samp_reinject_step_frt_args.rng = rng; samp_reinject_step_bck_args.rng = rng; - samp_reinject_step_frt_args.solid = solid_frt; - samp_reinject_step_bck_args.solid = solid_bck; + samp_reinject_step_frt_args.solid_enc_id = enc_ids[SDIS_FRONT]; + samp_reinject_step_bck_args.solid_enc_id = enc_ids[SDIS_BACK]; samp_reinject_step_frt_args.rwalk = rwalk; samp_reinject_step_bck_args.rwalk = rwalk; samp_reinject_step_frt_args.distance = delta_boundary_frt; @@ -102,7 +104,7 @@ XD(solid_solid_boundary_path) samp_reinject_step_frt_args.side = SDIS_FRONT; samp_reinject_step_bck_args.side = SDIS_BACK; res = XD(sample_reinjection_step_solid_solid) - (scn, + (scn, &samp_reinject_step_frt_args, &samp_reinject_step_bck_args, &reinject_step_frt, @@ -150,10 +152,10 @@ XD(solid_solid_boundary_path) if(r < proba) { /* Reinject in front */ reinject_step = &reinject_step_frt; - solid = solid_frt; + solid_enc_id = enc_ids[SDIS_FRONT]; } else { /* Reinject in back */ reinject_step = &reinject_step_bck; - solid = solid_bck; + solid_enc_id = enc_ids[SDIS_BACK]; } /* Perform the reinjection into the solid */ @@ -163,7 +165,7 @@ XD(solid_solid_boundary_path) solid_reinject_args.rwalk_ctx = ctx; solid_reinject_args.T = T; solid_reinject_args.fp_to_meter = scn->fp_to_meter; - res = XD(solid_reinjection)(solid, &solid_reinject_args); + res = XD(solid_reinjection)(scn, solid_enc_id, &solid_reinject_args); if(res != RES_OK) goto error; exit: diff --git a/src/sdis_heat_path_boundary_c.h b/src/sdis_heat_path_boundary_c.h @@ -21,123 +21,92 @@ #include <rsys/rsys.h> /* Forward declarations */ -struct rwalk_2d; -struct rwalk_3d; +struct rwalk; struct sdis_scene; struct sdis_medium; /******************************************************************************* * Sample a reinjection step ******************************************************************************/ -struct sample_reinjection_step_args_2d { +struct sample_reinjection_step_args { struct ssp_rng* rng; /* Random number generator to use */ - const struct sdis_medium* solid; /* Solid in which to reinject */ - struct rwalk_2d* rwalk; /* Current state of the random walk */ + struct rwalk* rwalk; /* Current state of the random walk */ double distance; /* Maximum Reinjection distance */ + unsigned solid_enc_id; /* Enclosured Id of the solid in which to reinject */ enum sdis_side side; /* Side of the boundary to re-inject */ }; -struct sample_reinjection_step_args_3d { - struct ssp_rng* rng; /* Random number generator to use */ - const struct sdis_medium* solid; /* Medium in which to reinject */ - struct rwalk_3d* rwalk; /* Current random walk state */ - double distance; /* Maximum Reinjection distance */ - enum sdis_side side; /* Side of the boundary to re-inject */ -}; - -struct reinjection_step_2d { - struct s2d_hit hit; /* Intersection along the reinjection direction */ - float direction[2]; /* Reinjection direction */ - float distance; /* Reinjection distance */ -}; +#define SAMPLE_REINJECTION_STEP_ARGS_NULL__ \ + {NULL, NULL, -1, ENCLOSURE_ID_NULL, SDIS_SIDE_NULL__} +static const struct sample_reinjection_step_args +SAMPLE_REINJECTION_STEP_ARGS_NULL = SAMPLE_REINJECTION_STEP_ARGS_NULL__; -struct reinjection_step_3d { - struct s3d_hit hit; /* Intersection along the reinjection direction */ +struct reinjection_step { + struct s2d_hit hit_2d; /* 2D Intersection along the reinjection direction */ + struct s3d_hit hit_3d; /* 3D Intersection along the reinjection direction */ float direction[3]; /* Reinjection direction */ float distance; /* Reinjection distance */ }; -#define SAMPLE_REINJECTION_STEP_ARGS_NULL___2d \ - {NULL, NULL, NULL, -1, SDIS_SIDE_NULL__} -#define SAMPLE_REINJECTION_STEP_ARGS_NULL___3d \ - {NULL, NULL, NULL, -1, SDIS_SIDE_NULL__} -static const struct sample_reinjection_step_args_2d -SAMPLE_REINJECTION_STEP_ARGS_NULL_2d = SAMPLE_REINJECTION_STEP_ARGS_NULL___2d; -static const struct sample_reinjection_step_args_3d -SAMPLE_REINJECTION_STEP_ARGS_NULL_3d = SAMPLE_REINJECTION_STEP_ARGS_NULL___3d; - -#define REINJECTION_STEP_NULL___2d {S2D_HIT_NULL__, {0,0}, 0} -#define REINJECTION_STEP_NULL___3d {S3D_HIT_NULL__, {0,0,0}, 0} -static const struct reinjection_step_2d -REINJECTION_STEP_NULL_2d = REINJECTION_STEP_NULL___2d; -static const struct reinjection_step_3d -REINJECTION_STEP_NULL_3d = REINJECTION_STEP_NULL___3d; +#define REINJECTION_STEP_NULL__ {S2D_HIT_NULL__, S3D_HIT_NULL__, {0,0,0}, 0} +static const struct reinjection_step REINJECTION_STEP_NULL = + REINJECTION_STEP_NULL__; extern LOCAL_SYM res_T sample_reinjection_step_solid_fluid_2d (struct sdis_scene* scn, - const struct sample_reinjection_step_args_2d* args, - struct reinjection_step_2d* step); + const struct sample_reinjection_step_args* args, + struct reinjection_step* step); extern LOCAL_SYM res_T sample_reinjection_step_solid_fluid_3d (struct sdis_scene* scn, - const struct sample_reinjection_step_args_3d* args, - struct reinjection_step_3d *step); + const struct sample_reinjection_step_args* args, + struct reinjection_step *step); extern LOCAL_SYM res_T sample_reinjection_step_solid_solid_2d (struct sdis_scene* scn, - const struct sample_reinjection_step_args_2d* args_front, - const struct sample_reinjection_step_args_2d* args_back, - struct reinjection_step_2d* step_front, - struct reinjection_step_2d* step_back); + const struct sample_reinjection_step_args* args_front, + const struct sample_reinjection_step_args* args_back, + struct reinjection_step* step_front, + struct reinjection_step* step_back); extern LOCAL_SYM res_T sample_reinjection_step_solid_solid_3d (struct sdis_scene* scn, - const struct sample_reinjection_step_args_3d* args_front, - const struct sample_reinjection_step_args_3d* args_back, - struct reinjection_step_3d* step_front, - struct reinjection_step_3d* step_back); + const struct sample_reinjection_step_args* args_front, + const struct sample_reinjection_step_args* args_back, + struct reinjection_step* step_front, + struct reinjection_step* step_back); /******************************************************************************* * Reinject the random walk into a solid ******************************************************************************/ -struct solid_reinjection_args_2d { - const struct reinjection_step_2d* reinjection; /* Reinjection to do */ +struct solid_reinjection_args { + const struct reinjection_step* reinjection; /* Reinjection to do */ struct rwalk_context* rwalk_ctx; - struct rwalk_2d* rwalk; /* Current state of the random walk */ + struct rwalk* rwalk; /* Current state of the random walk */ struct ssp_rng* rng; /* Random number generator */ - struct temperature_2d* T; + struct temperature* T; double fp_to_meter; }; -struct solid_reinjection_args_3d { - const struct reinjection_step_3d* reinjection; /* Reinjection to do */ - struct rwalk_context* rwalk_ctx; - struct rwalk_3d* rwalk; /* Current state of the random walk */ - struct ssp_rng* rng; /* Random number generator */ - struct temperature_3d* T; - double fp_to_meter; -}; - -#define SOLID_REINJECTION_ARGS_NULL___2d {NULL,NULL,NULL,NULL,NULL,0} -#define SOLID_REINJECTION_ARGS_NULL___3d {NULL,NULL,NULL,NULL,NULL,0} -static const struct solid_reinjection_args_2d SOLID_REINJECTION_ARGS_NULL_2d = - SOLID_REINJECTION_ARGS_NULL___2d; -static const struct solid_reinjection_args_3d SOLID_REINJECTION_ARGS_NULL_3d = - SOLID_REINJECTION_ARGS_NULL___3d; +#define SOLID_REINJECTION_ARGS_NULL__ {NULL,NULL,NULL,NULL,NULL,0} +static const struct solid_reinjection_args SOLID_REINJECTION_ARGS_NULL = + SOLID_REINJECTION_ARGS_NULL__; extern LOCAL_SYM res_T solid_reinjection_2d - (struct sdis_medium* solid, - struct solid_reinjection_args_2d* args); + (struct sdis_scene* scn, + const unsigned solid_enc_id, + struct solid_reinjection_args* args); extern LOCAL_SYM res_T solid_reinjection_3d - (struct sdis_medium* solid, - struct solid_reinjection_args_3d* args); + (struct sdis_scene* scn, + const unsigned solid_enc_id, + struct solid_reinjection_args* args); /******************************************************************************* * Handle net flux @@ -160,21 +129,22 @@ extern LOCAL_SYM res_T handle_net_flux_2d (const struct sdis_scene* scn, const struct handle_net_flux_args* args, - struct temperature_2d* T); + struct temperature* T); extern LOCAL_SYM res_T handle_net_flux_3d (const struct sdis_scene* scn, const struct handle_net_flux_args* args, - struct temperature_3d* T); + struct temperature* T); /******************************************************************************* * Handle external flux ******************************************************************************/ -struct handle_external_net_flux_args_2d { +struct handle_external_net_flux_args { struct sdis_interface* interf; const struct sdis_interface_fragment* frag; - const struct s2d_hit* hit; + const struct s2d_hit* hit_2d; + const struct s3d_hit* hit_3d; struct green_path_handle* green_path; /* Store the propagator */ struct sdis_heat_path* heat_path; /* Save paths */ @@ -199,26 +169,23 @@ struct handle_external_net_flux_args_3d { double h_radi; /* Radiative coefficient */ }; -#define HANDLE_EXTERNAL_NET_FLUX_ARGS_NULL___2d {NULL,NULL,NULL,NULL,NULL,0,0,0,0} -#define HANDLE_EXTERNAL_NET_FLUX_ARGS_NULL___3d {NULL,NULL,NULL,NULL,NULL,0,0,0,0} -static const struct handle_external_net_flux_args_2d -HANDLE_EXTERNAL_NET_FLUX_ARGS_NULL_2d = HANDLE_EXTERNAL_NET_FLUX_ARGS_NULL___2d; -static const struct handle_external_net_flux_args_3d -HANDLE_EXTERNAL_NET_FLUX_ARGS_NULL_3d = HANDLE_EXTERNAL_NET_FLUX_ARGS_NULL___3d; +#define HANDLE_EXTERNAL_NET_FLUX_ARGS_NULL__ {NULL,NULL,NULL,NULL,NULL,NULL,0,0,0,0} +static const struct handle_external_net_flux_args +HANDLE_EXTERNAL_NET_FLUX_ARGS_NULL = HANDLE_EXTERNAL_NET_FLUX_ARGS_NULL__; extern LOCAL_SYM res_T handle_external_net_flux_2d (const struct sdis_scene* scn, struct ssp_rng* rng, - const struct handle_external_net_flux_args_2d* args, - struct temperature_2d* T); + const struct handle_external_net_flux_args* args, + struct temperature* T); extern LOCAL_SYM res_T handle_external_net_flux_3d (const struct sdis_scene* scn, struct ssp_rng* rng, - const struct handle_external_net_flux_args_3d* args, - struct temperature_3d* T); + const struct handle_external_net_flux_args* args, + struct temperature* T); /******************************************************************************* * Miscellaneous functions @@ -246,9 +213,9 @@ solid_boundary_with_flux_path_2d struct rwalk_context* ctx, const struct sdis_interface_fragment* frag, const double phi, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_2d* T); + struct temperature* T); extern LOCAL_SYM res_T solid_boundary_with_flux_path_3d @@ -256,62 +223,62 @@ solid_boundary_with_flux_path_3d struct rwalk_context* ctx, const struct sdis_interface_fragment* frag, const double phi, - struct rwalk_3d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_3d* T); + struct temperature* T); extern LOCAL_SYM res_T solid_fluid_boundary_picard1_path_2d (struct sdis_scene* scn, struct rwalk_context* ctx, const struct sdis_interface_fragment* frag, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_2d* T); + struct temperature* T); extern LOCAL_SYM res_T solid_fluid_boundary_picard1_path_3d (struct sdis_scene* scn, struct rwalk_context* ctx, const struct sdis_interface_fragment* frag, - struct rwalk_3d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_3d* T); + struct temperature* T); extern LOCAL_SYM res_T solid_fluid_boundary_picardN_path_2d (struct sdis_scene* scn, struct rwalk_context* ctx, const struct sdis_interface_fragment* frag, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_2d* T); + struct temperature* T); extern LOCAL_SYM res_T solid_fluid_boundary_picardN_path_3d (struct sdis_scene* scn, struct rwalk_context* ctx, const struct sdis_interface_fragment* frag, - struct rwalk_3d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_3d* T); + struct temperature* T); extern LOCAL_SYM res_T solid_solid_boundary_path_2d (struct sdis_scene* scn, struct rwalk_context* ctx, const struct sdis_interface_fragment* frag, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_2d* T); + struct temperature* T); extern LOCAL_SYM res_T solid_solid_boundary_path_3d (struct sdis_scene* scn, struct rwalk_context* ctx, const struct sdis_interface_fragment* frag, - struct rwalk_3d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_3d* T); + struct temperature* T); #endif /* SDIS_HEAT_PATH_BOUNDARY_C_H */ diff --git a/src/sdis_heat_path_conductive.c b/src/sdis_heat_path_conductive.c @@ -13,10 +13,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "sdis_heat_path.h" -#include "sdis_heat_path_conductive_c.h" #include "sdis_log.h" +#include "sdis_heat_path_conductive_c.h" #include "sdis_medium_c.h" +#include "sdis_scene_c.h" /******************************************************************************* * Local function @@ -71,54 +71,16 @@ error: goto exit; } -res_T -conductive_path_2d - (struct sdis_scene* scn, - struct rwalk_context* ctx, - struct rwalk_2d* rwalk, - struct ssp_rng* rng, - struct temperature_2d* T) -{ - res_T res = RES_OK; - ASSERT(ctx); - - switch(ctx->diff_algo) { - case SDIS_DIFFUSION_DELTA_SPHERE: - res = conductive_path_delta_sphere_2d(scn, ctx, rwalk, rng, T); - break; - case SDIS_DIFFUSION_WOS: - res = conductive_path_wos_2d(scn, ctx, rwalk, rng, T); - break; - default: FATAL("Unreachable code.\n"); break; - } - return res; -} - -res_T -conductive_path_3d - (struct sdis_scene* scn, - struct rwalk_context* ctx, - struct rwalk_3d* rwalk, - struct ssp_rng* rng, - struct temperature_3d* T) -{ - res_T res = RES_OK; - ASSERT(ctx); - - switch(ctx->diff_algo) { - case SDIS_DIFFUSION_DELTA_SPHERE: - res = conductive_path_delta_sphere_3d(scn, ctx, rwalk, rng, T); - break; - case SDIS_DIFFUSION_WOS: - res = conductive_path_wos_3d(scn, ctx, rwalk, rng, T); - break; - default: FATAL("Unreachable code.\n"); break; - } - return res; -} - /* Generate the conductive path functions */ #define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_conductive_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_conductive_Xd.h" +#define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_conductive_custom_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_conductive_custom_Xd.h" +#define SDIS_XD_DIMENSION 2 #include "sdis_heat_path_conductive_delta_sphere_Xd.h" #define SDIS_XD_DIMENSION 3 #include "sdis_heat_path_conductive_delta_sphere_Xd.h" diff --git a/src/sdis_heat_path_conductive_Xd.h b/src/sdis_heat_path_conductive_Xd.h @@ -0,0 +1,73 @@ +/* Copyright (C) 2016-2024 |Méso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "sdis.h" +#include "sdis_heat_path.h" +#include "sdis_heat_path_conductive_c.h" +#include "sdis_log.h" +#include "sdis_medium_c.h" +#include "sdis_scene_c.h" + +#include <rsys/cstr.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +XD(conductive_path) + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct rwalk* rwalk, + struct ssp_rng* rng, + struct temperature* T) +{ + struct sdis_medium* mdm = NULL; + unsigned enc_id = ENCLOSURE_ID_NULL; + res_T res = RES_OK; + ASSERT(ctx && rwalk); + + res = scene_get_enclosure_id_in_closed_boundaries(scn, rwalk->vtx.P, &enc_id); + if(res != RES_OK) goto error; + res = scene_get_enclosure_medium(scn, scene_get_enclosure(scn, enc_id), &mdm); + if(res != RES_OK) goto error; + ASSERT(sdis_medium_get_type(mdm) == SDIS_SOLID); + + /* The caller defined a custom function to sample the solid path */ + if(mdm->shader.solid.sample_path != NULL) { + res = XD(conductive_path_custom)(scn, enc_id, mdm, rwalk, rng, T); + if(res != RES_OK) goto error; + + } else { + switch(ctx->diff_algo) { + case SDIS_DIFFUSION_DELTA_SPHERE: + res = XD(conductive_path_delta_sphere)(scn, ctx, rwalk, rng, T); + break; + case SDIS_DIFFUSION_WOS: + res = XD(conductive_path_wos)(scn, ctx, rwalk, rng, T); + break; + default: FATAL("Unreachable code.\n"); break; + } + 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_c.h b/src/sdis_heat_path_conductive_c.h @@ -19,15 +19,14 @@ #include <rsys/rsys.h> /* Forward declarations */ -struct rwalk_2d; -struct rwalk_3d; +struct rwalk; struct rwalk_context; struct sdis_device; +struct sdis_medium; struct sdis_scene; struct solid_props; struct ssp_rng; -struct temperature_2d; -struct temperature_3d; +struct temperature; extern LOCAL_SYM res_T check_solid_constant_properties @@ -38,23 +37,44 @@ check_solid_constant_properties const struct solid_props* props); /******************************************************************************* + * Conductive paths using custom user algorithm + ******************************************************************************/ +extern LOCAL_SYM res_T +conductive_path_custom_2d + (struct sdis_scene* scn, + const unsigned enc_id, /* Enclosure in which path is sampled */ + const struct sdis_medium* mdm, /* Medium in which path is sampled */ + struct rwalk* rwalk, + struct ssp_rng* rng, + struct temperature* T); + +extern LOCAL_SYM res_T +conductive_path_custom_3d + (struct sdis_scene* scn, + const unsigned enc_id, /* Enclosure in which path is sampled */ + const struct sdis_medium* mdm, /* Medium in which path is sampled */ + struct rwalk* rwalk, + struct ssp_rng* rng, + struct temperature* T); + +/******************************************************************************* * Conductive paths using the delta sphere algorithm ******************************************************************************/ extern LOCAL_SYM res_T conductive_path_delta_sphere_2d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_2d* T); + struct temperature* T); extern LOCAL_SYM res_T conductive_path_delta_sphere_3d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_3d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_3d* T); + struct temperature* T); /******************************************************************************* * Conductive paths using the walk on sphere algorithm @@ -63,16 +83,16 @@ extern LOCAL_SYM res_T conductive_path_wos_2d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_2d* T); + struct temperature* T); extern LOCAL_SYM res_T conductive_path_wos_3d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_3d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_3d* T); + struct temperature* T); #endif /* SDIS_HEAT_PATH_CONDUCTIVE_C_H */ diff --git a/src/sdis_heat_path_conductive_custom_Xd.h b/src/sdis_heat_path_conductive_custom_Xd.h @@ -0,0 +1,211 @@ +/* Copyright (C) 2016-2024 |Méso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "sdis.h" +#include "sdis_log.h" +#include "sdis_medium_c.h" +#include "sdis_scene_c.h" + +#include <rsys/cstr.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Helper function + ******************************************************************************/ +static res_T +XD(check_sampled_path) + (struct sdis_scene* scn, + const struct rwalk* rwalk, + const struct sdis_path* path) +{ + int null_prim = 0; + res_T res = RES_OK; + ASSERT(scn && path); + + null_prim = SXD_PRIMITIVE_EQ(&path->XD(prim), &SXD_PRIMITIVE_NULL); + + /* Check end of path */ + if(!path->at_limit && null_prim) { + log_err(scn->dev, + "%s: the sampled path should have reached a limit condition or a boundary" + " -- pos=("FORMAT_VECX")\n", + FUNC_NAME, SPLITX(path->vtx.P)); + res = RES_BAD_ARG; + goto error; + } + + /* Check path time */ + if(path->vtx.time > rwalk->vtx.time) { + log_err(scn->dev, + "%s: the sampled trajectory cannot be in the future. " + "It can only go back in time -- starting time=%g s; current time=%g s\n", + FUNC_NAME, rwalk->vtx.time, path->vtx.time); + res = RES_BAD_ARG; + goto error; + } + + if(!null_prim) { + struct sXd(primitive) prim; + const unsigned iprim = path->XD(prim).prim_id; + + /* Check intersected primitive */ + res = sXd(scene_view_get_primitive)(scn->sXd(view), iprim, &prim); + if(res != RES_OK || !SXD_PRIMITIVE_EQ(&path->XD(prim), &prim)) { + log_err(scn->dev, + "%s: invalid intersected primitive on sampled path -- %s\n", + FUNC_NAME, res_to_cstr(res)); + goto error; + } + } + +exit: + return res; +error: + goto exit; +} + +static int +XD(keep_only_one_primitive) + (const struct sXd(hit)* hit, + const float org[DIM], + const float dir[DIM], + const float range[2], + void* query_data, + void* filter_data) +{ + const struct sXd(primitive)* prim = query_data; + (void)org, (void)dir, (void)range, (void)filter_data; + return !SXD_PRIMITIVE_EQ(prim, &hit->prim); +} + +static res_T +XD(get_path_hit) + (struct sdis_scene* scn, + struct sdis_path* path, + const struct sdis_medium* mdm, + struct sXd(hit)* hit) +{ + struct hit_filter_data filter_data = HIT_FILTER_DATA_NULL; + float query_radius = 0; + float query_pos[DIM] = {0}; + double delta = 0; + res_T res = RES_OK; + ASSERT(scn && path && hit); + + filter_data.XD(custom_filter) = XD(keep_only_one_primitive); + filter_data.custom_filter_data = &path->XD(prim); + + /* Search for the hit corresponding to the path position on a primitive. Search + * at a maximum distance from the delta of the medium, as this hit should be + * very close to the submitted position, since it should represent the same + * point. */ + delta = solid_get_delta(mdm, &path->vtx); + fX_set_dX(query_pos, path->vtx.P); + query_radius = (float)delta; + SXD(scene_view_closest_point(scn->sXd(view), query_pos, query_radius, + &filter_data, hit)); + ASSERT(SXD_PRIMITIVE_EQ(&hit->prim, &path->XD(prim))); + + if(SXD_HIT_NONE(hit)) { + log_warn(scn->dev, + "%s: the position returned by custom sampling of the conductive path " + "is too far from the primitive it should be on -- " + "query position=("FORMAT_VECX"); search distance=%g\n", + FUNC_NAME, SPLITX(path->vtx.P), delta); + res = RES_BAD_OP; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local function + ******************************************************************************/ +res_T +XD(conductive_path_custom) + (struct sdis_scene* scn, + const unsigned enc_id, + const struct sdis_medium* mdm, + struct rwalk* rwalk, + struct ssp_rng* rng, + struct temperature* T) +{ + struct sdis_path path = SDIS_PATH_NULL; + res_T res = RES_OK; + + /* Check pre-conditions */ + ASSERT(scn && rwalk && rng && T); + ASSERT(sdis_medium_get_type(mdm) == SDIS_SOLID); + ASSERT(mdm->shader.solid.sample_path); + + /* Sample a conductive path */ + path.vtx = rwalk->vtx; + res = mdm->shader.solid.sample_path(scn, rng, &path, mdm->data); + if(res != RES_OK) { + log_err(scn->dev, + "%s: error in customized sampling of a conductive path " + "from pos=("FORMAT_VECX") -- %s\n", + FUNC_NAME, SPLITX(rwalk->vtx.P), res_to_cstr(res)); + goto error; + } + + res = XD(check_sampled_path)(scn, rwalk, &path); + if(res!= RES_OK) goto error; + + /* Update random walk position and time from sampled path */ + rwalk->elapsed_time += rwalk->vtx.time - path.vtx.time; + rwalk->vtx = path.vtx; + + /* Calculate path intersection with geometry if it hasn't already reached a + * boundary condition */ + if(!path.at_limit) { + res = XD(get_path_hit)(scn, &path, mdm, &rwalk->XD(hit)); + if(res != RES_OK) goto error; + } + + + /* The path reached a boundary */ + if(!SXD_HIT_NONE(&rwalk->XD(hit))) { + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; + + rwalk->enc_id = ENCLOSURE_ID_NULL; /* Not in an enclosure */ + + /* Define which side of the interface the path is on */ + scene_get_enclosure_ids(scn, rwalk->XD(hit).prim.prim_id, enc_ids); + if(enc_id == enc_ids[SDIS_FRONT]) rwalk->hit_side = SDIS_FRONT; + else if(enc_id == enc_ids[SDIS_BACK]) rwalk->hit_side = SDIS_BACK; + else FATAL("Unreachable code.\n"); + } + + /* Update Monte Carlo weight */ + T->value += path.weight; + T->done = path.at_limit; + + /* The path either reaches a boundary condition and will be stopped, or is + * on a boundary and a boundary path must be sampled */ + T->func = XD(boundary_path); + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_conductive_delta_sphere_Xd.h b/src/sdis_heat_path_conductive_delta_sphere_Xd.h @@ -111,7 +111,7 @@ XD(sample_next_step) static res_T XD(sample_next_step_robust) (struct sdis_scene* scn, - struct sdis_medium* current_mdm, + const unsigned current_enc_id, struct ssp_rng* rng, const double pos[DIM], const float delta_solid, @@ -121,13 +121,14 @@ XD(sample_next_step_robust) struct sXd(hit)* hit1, /* Hit used to adjust delta */ float* out_delta) { - struct sdis_medium* mdm; + unsigned enc_id = ENCLOSURE_ID_NULL; float delta; float org[DIM]; const size_t MAX_ATTEMPTS = 100; size_t iattempt = 0; res_T res = RES_OK; - ASSERT(scn && current_mdm && rng && pos && delta_solid > 0); + ASSERT(scn && rng && pos && delta_solid > 0); + ASSERT(current_enc_id != ENCLOSURE_ID_NULL); ASSERT(dir0 && dir1 && hit0 && hit1 && out_delta); fX_set_dX(org, pos); @@ -141,44 +142,30 @@ XD(sample_next_step_robust) /* Retrieve the medium of the next step */ if(hit0->distance > delta) { XD(move_pos)(dX(set)(pos_next, pos), dir0, delta); - res = scene_get_medium_in_closed_boundaries(scn, pos_next, &mdm); - if(res == RES_BAD_OP) { mdm = NULL; res = RES_OK; } + res = scene_get_enclosure_id_in_closed_boundaries(scn, pos_next, &enc_id); + if(res == RES_BAD_OP) { enc_id = ENCLOSURE_ID_NULL; res = RES_OK; } if(res != RES_OK) goto error; } else { - struct sdis_interface* interf; - enum sdis_side side; - interf = scene_get_interface(scn, hit0->prim.prim_id); - side = fX(dot)(dir0, hit0->normal) < 0 ? SDIS_FRONT : SDIS_BACK; - mdm = interface_get_medium(interf, side); + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; + scene_get_enclosure_ids(scn, hit0->prim.prim_id, enc_ids); + enc_id = fX(dot)(dir0, hit0->normal) < 0 ? enc_ids[0] : enc_ids[1]; } /* Check medium consistency */ - if(current_mdm != mdm) { + if(current_enc_id != enc_id) { #if 0 -#if DIM == 2 - log_warn(scn->dev, - "%s: inconsistent medium during the solid random walk at {%g, %g}.\n", - FUNC_NAME, SPLIT2(pos)); -#else log_warn(scn->dev, - "%s: inconsistent medium during the solid random walk at {%g, %g, %g}.\n", - FUNC_NAME, SPLIT3(pos)); -#endif + "%s: inconsistent medium during the solid random walk -- " + "pos=("FORMAT_VECX")\n", FUNC_NAME, SPLITX(pos)); #endif } - } while(current_mdm != mdm && ++iattempt < MAX_ATTEMPTS); + } while(current_enc_id != enc_id && ++iattempt < MAX_ATTEMPTS); /* Handle error */ if(iattempt >= MAX_ATTEMPTS) { -#if DIM == 2 - log_warn(scn->dev, - "%s: could not find a next valid conductive step at {%g, %g}.\n", - FUNC_NAME, SPLIT2(pos)); -#else log_warn(scn->dev, - "%s: could not find a next valid conductive step at {%g, %g, %g}.\n", - FUNC_NAME, SPLIT3(pos)); -#endif + "%s: could not find a next valid conductive -- pos=("FORMAT_VECX")\n", + FUNC_NAME, SPLITX(pos)); res = RES_BAD_OP; goto error; } @@ -239,7 +226,7 @@ XD(handle_volumic_power) (const struct sdis_scene* scn, const struct XD(handle_volumic_power_args)* args, double* out_power_term, - struct XD(temperature)* T) + struct temperature* T) { double power_term = 0; res_T res = RES_OK; @@ -332,28 +319,43 @@ res_T XD(conductive_path_delta_sphere) (struct sdis_scene* scn, struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct XD(temperature)* T) + struct temperature* T) { - double position_start[DIM]; + /* Enclosure/medium in which the conductive path starts */ + struct sdis_medium* mdm = NULL; + unsigned enc_id = ENCLOSURE_ID_NULL; + + /* Physical properties */ struct solid_props props_ref = SOLID_PROPS_NULL; double green_power_term = 0; - struct sdis_medium* mdm; + + /* Miscellaneous */ + double position_start[DIM] = {0}; size_t istep = 0; /* Help for debug */ res_T res = RES_OK; + + /* Check pre-conditions */ ASSERT(scn && rwalk && rng && T); - ASSERT(rwalk->mdm->type == SDIS_SOLID); - (void)ctx, (void)istep; + + (void)ctx, (void)istep; /* Avoid "unsued variable" warnings */ + + res = scene_get_enclosure_id_in_closed_boundaries(scn, rwalk->vtx.P, &enc_id); + if(res != RES_OK) goto error; + res = scene_get_enclosure_medium(scn, scene_get_enclosure(scn, enc_id), &mdm); + if(res != RES_OK) goto error; + ASSERT(sdis_medium_get_type(mdm) == SDIS_SOLID); /* Check the random walk consistency */ - res = scene_get_medium_in_closed_boundaries(scn, rwalk->vtx.P, &mdm); - if(res != RES_OK || mdm != rwalk->mdm) { + if(enc_id != rwalk->enc_id) { log_err(scn->dev, "%s: invalid solid random walk. " - "Unexpected medium at {%g, %g, %g}.\n", FUNC_NAME, SPLIT3(rwalk->vtx.P)); + "Unexpected enclosure -- pos=("FORMAT_VECX")\n", + FUNC_NAME, SPLITX(rwalk->vtx.P)); res = RES_BAD_OP_IRRECOVERABLE; goto error; } + /* Save the submitted position */ dX(set)(position_start, rwalk->vtx.P); @@ -396,7 +398,7 @@ XD(conductive_path_delta_sphere) if(ctx->green_path) { res = green_path_set_limit_vertex - (ctx->green_path, rwalk->mdm, &rwalk->vtx, rwalk->elapsed_time); + (ctx->green_path, mdm, &rwalk->vtx, rwalk->elapsed_time); if(res != RES_OK) goto error; } @@ -410,7 +412,7 @@ XD(conductive_path_delta_sphere) fX_set_dX(org, rwalk->vtx.P); /* Sample the direction to walk toward and compute the distance to travel */ - res = XD(sample_next_step_robust)(scn, mdm, rng, rwalk->vtx.P, + res = XD(sample_next_step_robust)(scn, enc_id, rng, rwalk->vtx.P, (float)props.delta, dir0, dir1, &hit0, &hit1, &delta); if(res != RES_OK) goto error; @@ -436,16 +438,16 @@ XD(conductive_path_delta_sphere) /* Rewind the time */ delta_m = delta * scn->fp_to_meter; mu = (2*DIM*props.lambda)/(props.rho*props.cp*delta_m*delta_m); - res = XD(time_rewind)(mu, props.t0, rng, rwalk, ctx, T); + res = time_rewind(scn, mu, props.t0, rng, rwalk, ctx, T); if(res != RES_OK) goto error; if(T->done) break; /* Limit condition was reached */ /* Define if the random walk hits something along dir0 */ if(hit0.distance > delta) { - rwalk->hit = SXD_HIT_NULL; + rwalk->XD(hit) = SXD_HIT_NULL; rwalk->hit_side = SDIS_SIDE_NULL__; } else { - rwalk->hit = hit0; + rwalk->XD(hit) = hit0; rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; } @@ -460,17 +462,17 @@ XD(conductive_path_delta_sphere) ++istep; /* Keep going while the solid random walk does not hit an interface */ - } while(SXD_HIT_NONE(&rwalk->hit)); + } while(SXD_HIT_NONE(&rwalk->XD(hit))); /* Register the power term for the green function */ if(ctx->green_path && props_ref.power != SDIS_VOLUMIC_POWER_NONE) { res = green_path_add_power_term - (ctx->green_path, rwalk->mdm, &rwalk->vtx, green_power_term); + (ctx->green_path, mdm, &rwalk->vtx, green_power_term); if(res != RES_OK) goto error; } T->func = XD(boundary_path); - rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ + rwalk->enc_id = ENCLOSURE_ID_NULL; /* At the interface between 2 media */ exit: return res; diff --git a/src/sdis_heat_path_conductive_wos_Xd.h b/src/sdis_heat_path_conductive_wos_Xd.h @@ -23,24 +23,67 @@ #include "sdis_Xd_begin.h" /******************************************************************************* + * Non generic helper functions + ******************************************************************************/ +#ifndef SDIS_HEAT_PATH_CONDUCTIVE_WOS_XD_H +#define SDIS_HEAT_PATH_CONDUCTIVE_WOS_XD_H + +static res_T +update_green_path + (struct green_path_handle* green_path, + struct rwalk* rwalk, + struct sdis_medium* mdm, + const struct solid_props* props, + const double power_term, + const struct temperature* T) +{ + res_T res = RES_OK; + ASSERT(mdm && props && T); + + /* Is the green function estimated? */ + if(!green_path) goto exit; + + /* Save power term for green function if any */ + if(props->power != SDIS_VOLUMIC_POWER_NONE) { + res = green_path_add_power_term(green_path, mdm, &rwalk->vtx, power_term); + if(res != RES_OK) goto error; + } + + /* Set the green path limit to the current position if the initial condition + * has been reached */ + if(T->done) { + res = green_path_set_limit_vertex + (green_path, mdm, &rwalk->vtx, rwalk->elapsed_time); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + +#endif /* SDIS_HEAT_PATH_CONDUCTIVE_WOS_XD_H */ + +/******************************************************************************* * Helper function ******************************************************************************/ static res_T -XD(check_medium_consistency) +XD(check_enclosure_consistency) (struct sdis_scene* scn, - const struct XD(rwalk)* rwalk) + const struct rwalk* rwalk) { - struct sdis_medium* mdm = NULL; + unsigned enc_id = ENCLOSURE_ID_NULL; res_T res = RES_OK; ASSERT(rwalk); - res = scene_get_medium_in_closed_boundaries(scn, rwalk->vtx.P, &mdm); + res = scene_get_enclosure_id_in_closed_boundaries(scn, rwalk->vtx.P, &enc_id); if(res != RES_OK) goto error; - /* Check medium consistency */ - if(mdm != rwalk->mdm) { + /* Check enclosure consistency */ + if(enc_id != rwalk->enc_id) { log_err(scn->dev, - "%s:%s: invalid solid walk. Unexpected medium (position: "FORMAT_VECX").\n", + "%s:%s: invalid solid walk. Unexpected enclosure -- pos=("FORMAT_VECX")\n", __FILE__, FUNC_NAME, SPLITX(rwalk->vtx.P)); res = RES_BAD_OP_IRRECOVERABLE; goto error; @@ -55,12 +98,14 @@ error: static res_T XD(time_travel) (struct sdis_scene* scn, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, + struct sdis_medium* mdm, const double alpha, /* Diffusivity, i.e. lambda/(rho*cp) */ const double t0, /* Initial time [s] */ + const double pos[3], /* Position before the diffusive step */ double* distance, /* Displacement [m/fp_to_meter] */ - struct XD(temperature)* T) + struct temperature* T) { double dir[DIM] = {0}; double dst = 0; /* Distance [m] */ @@ -70,13 +115,13 @@ XD(time_travel) double temperature = 0; /* [k] */ double time = 0; /* [s] */ res_T res = RES_OK; - ASSERT(scn && rwalk && rng && alpha > 0 && distance && T); + ASSERT(scn && rwalk && rng && alpha > 0 && pos && distance && T); dst = *distance * scn->fp_to_meter; ASSERT(dst >= 0); /* No displacement => no time travel */ - if(distance == 0) goto exit; + if(dst == 0) goto exit; /* Sample x = tau*alpha/distance^2 */ r = ssp_rng_canonical(rng); @@ -107,27 +152,28 @@ XD(time_travel) r = ssp_rng_canonical(rng); x = swf_tabulation_inverse(XD(scn->dev->H), SWF_QUADRATIC, r); dst = sqrt(alpha * time / x); - *distance = dst; /* Update travel distance */ + *distance = dst / scn->fp_to_meter; /* Update travel distance */ /* Uniformly sample a direction and move along it of the distance that - * separate the current position ot its initial condition */ + * separate the path position before diffusion position to its initial + * condition */ #if DIM == 2 ssp_ran_circle_uniform(rng, dir, NULL); #else ssp_ran_sphere_uniform(rng, dir, NULL); #endif - dX(muld)(dir, dir, dst); - dX(add)(rwalk->vtx.P, rwalk->vtx.P, dir); + dX(muld)(dir, dir, *distance); + dX(add)(rwalk->vtx.P, pos, dir); /* Fetch the initial temperature */ - temperature = medium_get_temperature(rwalk->mdm, &rwalk->vtx); + temperature = medium_get_temperature(mdm, &rwalk->vtx); if(SDIS_TEMPERATURE_IS_UNKNOWN(temperature)) { log_err(scn->dev, "%s:%s: the path reaches the initial condition but the " - "%s temperature remains unknown -- position=%g, %g, %g\n", + "%s temperature remains unknown -- pos=("FORMAT_VECX")\n", __FILE__, FUNC_NAME, - medium_type_to_string(sdis_medium_get_type(rwalk->mdm)), - SPLIT3(rwalk->vtx.P)); + medium_type_to_string(sdis_medium_get_type(mdm)), + SPLITX(rwalk->vtx.P)); res = RES_BAD_ARG; goto error; } @@ -142,6 +188,32 @@ error: goto exit; } +static res_T +XD(handle_volumic_power_wos) + (struct sdis_scene* scn, + const struct solid_props* props, + const double distance, /* [m/fp_to_meter] */ + double* power_term, + struct temperature* T) +{ + double dst = distance * scn->fp_to_meter; /* [m] */ + double term = 0; + res_T res = RES_OK; + ASSERT(scn && props && distance >= 0 && power_term && T); + + if(props->power == SDIS_VOLUMIC_POWER_NONE) goto exit; + + /* No displacement => no power density */ + if(distance == 0) goto exit; + + term = dst*dst / (2*DIM*props->lambda); + T->value += props->power * term; + +exit: + *power_term = term; + return res; +} + #if DIM == 2 static INLINE enum sdis_side compute_hit_side_2d @@ -203,16 +275,16 @@ compute_hit_side_3d } #endif -/* Verify that the submitted position is in the expected medium */ +/* Verify that the submitted position is in the expected enclosure */ static res_T XD(check_diffusion_position) (struct sdis_scene* scn, - const struct sdis_medium* expected_medium, + const unsigned expected_enc_id, + const double delta, /* Used to adjust thresholds */ const double pos[DIM]) { - struct sdis_interface* interf = NULL; - struct sdis_medium* mdm = NULL; - enum sdis_side side; + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; + enum sdis_side side = SDIS_SIDE_NULL__; struct sXd(hit) hit = SXD_HIT_NULL; float wos_pos[DIM] = {0}; @@ -220,30 +292,30 @@ XD(check_diffusion_position) res_T res = RES_OK; /* Check pre-conditions */ - ASSERT(scn && expected_medium && pos); - - /* Look for the nearest surface within 1 mm of the position to be checked. By - * limiting the search radius we speed up the closest point query. If no - * surface is found, we assume that the position is in the intended medium. - * We rely on this assumption because this function is used to verify - * positions during diffusive random walks. Diffusion algorithms ensure that - * positions are in the current medium. This function is only concerned with - * numerical problems which, once the new position has been calculated, - * position the random walk beyond the medium. In other words, the path jumps - * a boundary that lies within the numerical imprecision of the calculation, - * i.e. very close to the position to be verified. So, if no surface is found - * close to this position, it means that there is no nearby boundary and, - * consequently, no numerical problem of this kind could have arisen. */ - wos_radius = (float)(scn->fp_to_meter * 1.0e-3); + ASSERT(scn && pos); + ASSERT(expected_enc_id != ENCLOSURE_ID_NULL); + + /* Look for the nearest surface of the position to be checked. By limiting the + * search radius to delta we speed up the closest point query. If no surface + * is found, we assume that the position is in the intended medium. We rely + * on this assumption because this function is used to verify positions during + * diffusive random walks. Diffusion algorithms ensure that positions are in + * the current medium. This function is only concerned with numerical problems + * which, once the new position has been calculated, position the random walk + * beyond the medium. In other words, the path jumps a boundary that lies + * within the numerical imprecision of the calculation, i.e. very close to the + * position to be verified. So, if no surface is found close to this position, + * it means that there is no nearby boundary and, consequently, no numerical + * problem of this kind could have arisen. */ + wos_radius = (float)delta; fX_set_dX(wos_pos, pos); SXD(scene_view_closest_point(scn->sXd(view), wos_pos, wos_radius, NULL, &hit)); if(SXD_HIT_NONE(&hit)) goto exit; - /* Fetch interface properties and check path consistency */ - interf = scene_get_interface(scn, hit.prim.prim_id); + /* Check path consistency */ + scene_get_enclosure_ids(scn, hit.prim.prim_id, enc_ids); side = XD(compute_hit_side)(&hit, pos); - mdm = side == SDIS_FRONT ? interf->medium_front : interf->medium_back; - if(mdm != expected_medium) { + if(enc_ids[side] != expected_enc_id) { res = RES_BAD_ARG; goto error; } @@ -258,15 +330,14 @@ static res_T XD(setup_hit_wos) (struct sdis_scene* scn, const struct sXd(hit)* hit, - struct XD(rwalk)* rwalk) + struct rwalk* rwalk) { /* Geometry */ struct sXd(primitive) prim; struct sXd(attrib) attr; /* Properties */ - struct sdis_interface* interf = NULL; - struct sdis_medium* mdm = NULL; + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; enum sdis_side side = SDIS_SIDE_NULL__; /* Miscellaneous */ @@ -288,13 +359,12 @@ XD(setup_hit_wos) dX_set_fX(tgt, attr.value); side = XD(compute_hit_side)(hit, rwalk->vtx.P); - /* Fetch interface properties and check path consistency */ - interf = scene_get_interface(scn, hit->prim.prim_id); - mdm = side == SDIS_FRONT ? interf->medium_front : interf->medium_back; - if(mdm != rwalk->mdm) { + /* Check path consistency */ + scene_get_enclosure_ids(scn, hit->prim.prim_id, enc_ids); + if(enc_ids[side] != rwalk->enc_id) { log_err(scn->dev, - "%s:%s: the conductive path has reached an invalid interface; " - "unexpected medium (position: "FORMAT_VECX"; side: %s).\n", + "%s:%s: the conductive path has reached an invalid interface. " + "Unexpected enclosure -- pos=("FORMAT_VECX"), side=%s\n", __FILE__, FUNC_NAME, SPLITX(tgt), side == SDIS_FRONT ? "front" : "back"); res = RES_BAD_OP_IRRECOVERABLE; goto error; @@ -305,7 +375,7 @@ XD(setup_hit_wos) * the interface. So we can't yet assume that the random walk has left the * current medium */ dX(set)(rwalk->vtx.P, tgt); - rwalk->hit = *hit; + rwalk->XD(hit) = *hit; rwalk->hit_side = side; exit: @@ -320,11 +390,10 @@ XD(setup_hit_rt) const double pos[DIM], const double dir[DIM], const struct sXd(hit)* hit, - struct XD(rwalk)* rwalk) + struct rwalk* rwalk) { /* Properties */ - struct sdis_interface* interf = NULL; - struct sdis_medium* mdm = NULL; + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; enum sdis_side side = SDIS_SIDE_NULL__; /* Miscellaneous */ @@ -344,12 +413,11 @@ XD(setup_hit_rt) side = dX(dot)(N, dir) > 0 ? SDIS_BACK : SDIS_FRONT; /* Fetch interface properties and check path consistency */ - interf = scene_get_interface(scn, hit->prim.prim_id); - mdm = side == SDIS_FRONT ? interf->medium_front : interf->medium_back; - if(mdm != rwalk->mdm) { + scene_get_enclosure_ids(scn, hit->prim.prim_id, enc_ids); + if(enc_ids[side] != rwalk->enc_id) { log_err(scn->dev, - "%s:%s: the conductive path has reached an invalid interface; " - "unexpected medium (position: "FORMAT_VECX"; side: %s).\n", + "%s:%s: the conductive path has reached an invalid interface. " + "Unexpected enclosure -- pos=("FORMAT_VECX"), side=%s\n", __FILE__, FUNC_NAME, SPLITX(tgt), side == SDIS_FRONT ? "front" : "back"); res = RES_BAD_OP_IRRECOVERABLE; goto error; @@ -360,7 +428,7 @@ XD(setup_hit_rt) * the interface. So we can't yet assume that the random walk has left the * current medium */ dX(set)(rwalk->vtx.P, tgt); - rwalk->hit = *hit; + rwalk->XD(hit) = *hit; rwalk->hit_side = side; exit: @@ -372,8 +440,9 @@ error: static res_T XD(sample_next_position) (struct sdis_scene* scn, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, + const double delta, /* Used to adjust thresholds */ double* distance) /* Displacement distance */ { /* Intersection */ @@ -396,9 +465,9 @@ XD(sample_next_position) CHK(!SXD_HIT_NONE(&hit)); wos_distance = hit.distance; - /* The current position is in the epsilon shell: + /* The current position is in the epsilon shell, i.e. 1% of delta: * move it to the nearest interface position */ - wos_epsilon = scn->fp_to_meter*1.e-6; + wos_epsilon = delta*1.e-2; if(wos_distance <= wos_epsilon) { res = XD(setup_hit_wos)(scn, &hit, rwalk); if(res != RES_OK) goto error; @@ -425,7 +494,7 @@ XD(sample_next_position) * The next diffusion step would then detect an error. This is why we use a * new function based on the same geometric operator used in the present * algorithm. */ - res = XD(check_diffusion_position)(scn, rwalk->mdm, pos); + res = XD(check_diffusion_position)(scn, rwalk->enc_id, delta, pos); /* Diffusion position is valid => move the path to the new position */ if(res == RES_OK) { @@ -459,8 +528,8 @@ XD(sample_next_position) * we don't care to save it. */ if(SXD_HIT_NONE(&hit)) { log_err(scn->dev, - "%s:%s: unable to find the next diffusion position " - "(position: "FORMAT_VECX"; direction: "FORMAT_VECX"; distance: %g\n", + "%s:%s: unable to find the next diffusion position -- " + "position=("FORMAT_VECX"), direction=("FORMAT_VECX"), distance=%g\n", __FILE__, FUNC_NAME, SPLITX(pos), SPLITX(dir), wos_distance); res = RES_BAD_OP_IRRECOVERABLE; goto error; @@ -478,67 +547,6 @@ error: goto exit; } -static res_T -XD(handle_volumic_power_wos) - (struct sdis_scene* scn, - const struct solid_props* props, - const double distance, /* [m/fp_to_meter] */ - double* power_term, - struct XD(temperature)* T) -{ - double dst = distance * scn->fp_to_meter; /* [m] */ - double term = 0; - res_T res = RES_OK; - ASSERT(scn && props && distance >= 0 && power_term && T); - - if(props->power == SDIS_VOLUMIC_POWER_NONE) goto exit; - - /* No displacement => no power density */ - if(distance == 0) goto exit; - - term = dst*dst / (2*DIM*props->lambda); - T->value += props->power * term; - -exit: - *power_term = term; - return res; -} - -static res_T -XD(update_green_path) - (struct green_path_handle* green_path, - struct XD(rwalk)* rwalk, - struct sdis_medium* mdm, - const struct solid_props* props, - const double power_term, - const struct XD(temperature)* T) -{ - res_T res = RES_OK; - ASSERT(mdm && props && T); - - /* Is the green function estimated? */ - if(!green_path) goto exit; - - /* Save power term for green function if any */ - if(props->power != SDIS_VOLUMIC_POWER_NONE) { - res = green_path_add_power_term(green_path, mdm, &rwalk->vtx, power_term); - if(res != RES_OK) goto error; - } - - /* Set the green path limit to the current position if the initial condition - * has been reached */ - if(T->done) { - res = green_path_set_limit_vertex - (green_path, mdm, &rwalk->vtx, rwalk->elapsed_time); - if(res != RES_OK) goto error; - } - -exit: - return res; -error: - goto exit; -} - /******************************************************************************* * Local function ******************************************************************************/ @@ -546,11 +554,12 @@ res_T XD(conductive_path_wos) (struct sdis_scene* scn, struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct XD(temperature)* T) + struct temperature* T) { /* Properties */ + const struct enclosure* enc = NULL; struct sdis_medium* mdm = NULL; struct solid_props props_ref = SOLID_PROPS_NULL; struct solid_props props = SOLID_PROPS_NULL; @@ -566,18 +575,18 @@ XD(conductive_path_wos) /* Check pre-conditions */ ASSERT(scn && ctx && rwalk && rng && T); - ASSERT(sdis_medium_get_type(rwalk->mdm) == SDIS_SOLID); /* Is green evaluated evaluated */ green = ctx->green_path != NULL; - /* Keep track of the solid. After conduction, a boundary may have been - * reached, so the random walk medium is NULL. However, this medium is still - * needed to update the green path. Hence this backup */ - mdm = rwalk->mdm; + res = XD(check_enclosure_consistency)(scn, rwalk); + if(res != RES_OK) goto error; - res = XD(check_medium_consistency)(scn, rwalk); + /* Get the enclosure medium */ + enc = scene_get_enclosure(scn, rwalk->enc_id); + res = scene_get_enclosure_medium(scn, enc, &mdm); if(res != RES_OK) goto error; + ASSERT(sdis_medium_get_type(mdm) == SDIS_SOLID); /* Retrieve the solid properties at the current position. Use them to verify * that those that are supposed to be constant by the conductive random walk @@ -588,7 +597,7 @@ XD(conductive_path_wos) * position. By comparing them to the properties along the random walk, we * thus verify that the properties are constant throughout the random walk * with respect to the properties of the reinjected position. */ - solid_get_properties(rwalk->mdm, &rwalk->vtx, &props_ref); + solid_get_properties(mdm, &rwalk->vtx, &props_ref); props = props_ref; /* The algorithm assumes that lambda, rho and cp are constants. The @@ -598,6 +607,7 @@ XD(conductive_path_wos) /* Sample a diffusive path */ for(;;) { double power_term = 0; /* */ + double pos[3] = {0,0,0}; /* Position before diffusive step */ double dst = 0; /* [m/fp_to_meter] */ /* Register the new vertex against the heat path */ @@ -615,12 +625,14 @@ XD(conductive_path_wos) break; } + d3_set(pos, rwalk->vtx.P); + /* Find the next position of the conductive path */ - res = XD(sample_next_position)(scn, rwalk, rng, &dst); + res = XD(sample_next_position)(scn, rwalk, rng, props.delta, &dst); if(res != RES_OK) goto error; /* Going back in time */ - res = XD(time_travel)(scn, rwalk, rng, alpha, props.t0, &dst, T); + res = XD(time_travel)(scn, rwalk, rng, mdm, alpha, props.t0, pos, &dst, T); if(res != RES_OK) goto error; /* Add the volumic power density */ @@ -639,16 +651,16 @@ XD(conductive_path_wos) } /* The path reaches a boundary */ - if(!SXD_HIT_NONE(&rwalk->hit)) { + if(!SXD_HIT_NONE(&rwalk->XD(hit))) { T->func = XD(boundary_path); - rwalk->mdm = NULL; + rwalk->enc_id = ENCLOSURE_ID_NULL; break; } #undef REGISTER_VERTEX /* Retreive and check solid properties at the new position */ - res = solid_get_properties(rwalk->mdm, &rwalk->vtx, &props); + res = solid_get_properties(mdm, &rwalk->vtx, &props); if(res != RES_OK) goto error; res = check_solid_constant_properties(scn->dev, green, wos, &props_ref, &props); if(res != RES_OK) goto error; @@ -657,7 +669,7 @@ XD(conductive_path_wos) } /* Save green function data */ - res = XD(update_green_path) + res = update_green_path (ctx->green_path, rwalk, mdm, &props_ref, green_power_term, T); exit: diff --git a/src/sdis_heat_path_convective_Xd.h b/src/sdis_heat_path_convective_Xd.h @@ -66,57 +66,19 @@ error: * Helper functions ******************************************************************************/ static res_T -XD(register_heat_vertex_in_fluid) - (struct sdis_scene* scn, - struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - const double weight) -{ - struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; - struct hit_filter_data filter_data = HIT_FILTER_DATA_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); - - filter_data.XD(hit) = rwalk->hit; - filter_data.epsilon = 1.e-6; - SXD(scene_view_trace_ray(scn->sXd(view), org, dir, range, &filter_data, &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, (int)ctx->nbranchings); -} - -static res_T XD(handle_known_fluid_temperature) - (struct sdis_scene* scn, - struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct XD(temperature)* T) + (struct rwalk_context* ctx, + struct rwalk* rwalk, + struct sdis_medium* mdm, + struct temperature* T) { double temperature; int known_temperature; res_T res = RES_OK; - ASSERT(scn && ctx && rwalk && T); - ASSERT(sdis_medium_get_type(rwalk->mdm) == SDIS_FLUID); + ASSERT(ctx && rwalk && T); + ASSERT(sdis_medium_get_type(mdm) == SDIS_FLUID); - temperature = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); + temperature = fluid_get_temperature(mdm, &rwalk->vtx); /* Check if the temperature is known */ known_temperature = SDIS_TEMPERATURE_IS_KNOWN(temperature); @@ -127,12 +89,13 @@ XD(handle_known_fluid_temperature) if(ctx->green_path) { res = green_path_set_limit_vertex - (ctx->green_path, rwalk->mdm, &rwalk->vtx, rwalk->elapsed_time); + (ctx->green_path, mdm, &rwalk->vtx, rwalk->elapsed_time); if(res != RES_OK) goto error; } - res = XD(register_heat_vertex_in_fluid)(scn, ctx, rwalk, T->value); - if(res != RES_OK) goto error; + if(ctx->heat_path) { + heat_path_get_last_vertex(ctx->heat_path)->weight = T->value; + } exit: return res; @@ -143,7 +106,7 @@ error: static res_T XD(handle_convective_path_startup) (struct sdis_scene* scn, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, int* path_starts_in_fluid) { const float range[2] = {FLT_MIN, FLT_MAX}; @@ -151,9 +114,8 @@ XD(handle_convective_path_startup) float org[DIM] = {0}; res_T res = RES_OK; ASSERT(scn && rwalk && path_starts_in_fluid); - ASSERT(sdis_medium_get_type(rwalk->mdm) == SDIS_FLUID); - *path_starts_in_fluid = SXD_HIT_NONE(&rwalk->hit); + *path_starts_in_fluid = SXD_HIT_NONE(&rwalk->XD(hit)); if(*path_starts_in_fluid == 0) goto exit; /* Nothing to do */ dir[DIM-1] = 1; @@ -161,8 +123,8 @@ XD(handle_convective_path_startup) /* Init the path hit field required to define the current enclosure and * fetch the interface data */ - SXD(scene_view_trace_ray(scn->sXd(view), org, dir, range, NULL, &rwalk->hit)); - if(SXD_HIT_NONE(&rwalk->hit)) { + SXD(scene_view_trace_ray(scn->sXd(view), org, dir, range, NULL, &rwalk->XD(hit))); + if(SXD_HIT_NONE(&rwalk->XD(hit))) { log_err(scn->dev, "%s: the position %g %g %g lies in the surrounding fluid whose " "temperature must be known.\n", FUNC_NAME, SPLIT3(rwalk->vtx.P)); @@ -170,7 +132,8 @@ XD(handle_convective_path_startup) goto error; } - rwalk->hit_side = fX(dot)(rwalk->hit.normal, dir) < 0 ? SDIS_FRONT : SDIS_BACK; + rwalk->hit_side = fX(dot)(rwalk->XD(hit).normal, dir) < 0 + ? SDIS_FRONT : SDIS_BACK; exit: return res; @@ -179,54 +142,28 @@ error: } static res_T -XD(fetch_fluid_enclosure) +XD(check_enclosure) (struct sdis_scene* scn, - struct XD(rwalk)* rwalk, - const struct enclosure** out_enclosure) + const struct rwalk* rwalk, + const struct enclosure* enc) { - const struct sdis_interface* interf; - const struct enclosure* enc; - unsigned enc_ids[2]; - unsigned enc_id; res_T res = RES_OK; - ASSERT(scn && rwalk && out_enclosure); - ASSERT(sdis_medium_get_type(rwalk->mdm) == SDIS_FLUID); - ASSERT(!SXD_HIT_NONE(&rwalk->hit)); - - /* Fetch the current interface and its associated enclosures */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - scene_get_enclosure_ids(scn, rwalk->hit.prim.prim_id, enc_ids); - - /* Find the enclosure identifier of the current medium */ - ASSERT(interf->medium_front != interf->medium_back); - if(rwalk->mdm == interf->medium_front) { - enc_id = enc_ids[0]; - ASSERT(rwalk->hit_side == SDIS_FRONT); - } else { - ASSERT(rwalk->mdm == interf->medium_back); - enc_id = enc_ids[1]; - ASSERT(rwalk->hit_side == SDIS_BACK); - } + ASSERT(scn && rwalk && enc); - /* Fetch the enclosure data */ - enc = scene_get_enclosure(scn, enc_id); - ASSERT(enc != NULL); - if(enc->medium_id == ENCLOSURE_MULTI_MEDIA) { + if(enc->medium_id == MEDIUM_ID_MULTI) { /* The enclosures with multiple media are used to describe limit * conditions and therefore they cannot be fetched */ log_err(scn->dev, - "%s: enclosure with multiple media at {%g, %g, %g}. " - "Path should be reached a limit condition before.\n", - FUNC_NAME, rwalk->vtx.P[0], rwalk->vtx.P[1], DIM==3 ? rwalk->vtx.P[2]:0); + "%s: enclosure with multiple media at ("FORMAT_VECX"). " + "The path should be reached a limit condition before.\n", + FUNC_NAME, SPLITX(rwalk->vtx.P)); res = RES_BAD_ARG; goto error; } exit: - *out_enclosure = enc; return res; error: - enc = NULL; goto exit; } @@ -237,14 +174,22 @@ res_T XD(convective_path) (struct sdis_scene* scn, struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct XD(temperature)* T) + struct temperature* T) { - struct sXd(attrib) attr_P, attr_N; + /* Properties */ struct fluid_props props_ref = FLUID_PROPS_NULL; - const struct sdis_interface* interf; - const struct enclosure* enc; + const struct sdis_interface* interf = NULL; + struct sdis_medium* mdm = NULL; + + /* Enclosure */ + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; + const struct enclosure* enc = NULL; + + /* Miscellaneous */ + struct sXd(attrib) attr_P, attr_N; + struct sXd(hit)* rwalk_hit = NULL; double r; #if SDIS_XD_DIMENSION == 2 float st; @@ -253,11 +198,19 @@ XD(convective_path) #endif int path_starts_in_fluid; res_T res = RES_OK; - (void)rng, (void)ctx; + ASSERT(scn && ctx && rwalk && rng && T); - ASSERT(rwalk->mdm->type == SDIS_FLUID); + (void)rng, (void)ctx; /* Avoid "unsued variable" warnings */ - res = XD(handle_known_fluid_temperature)(scn, ctx, rwalk, T); + rwalk_hit = &rwalk->XD(hit); + + /* Get the enclosure medium */ + enc = scene_get_enclosure(scn, rwalk->enc_id); + if((res = XD(check_enclosure)(scn, rwalk, enc)) != RES_OK) goto error; + if((res = scene_get_enclosure_medium(scn, enc, &mdm)) != RES_OK) goto error; + ASSERT(sdis_medium_get_type(mdm) == SDIS_FLUID); + + res = XD(handle_known_fluid_temperature)(ctx, rwalk, mdm, T); if(res != RES_OK) goto error; if(T->done) goto exit; /* The fluid temperature is known */ @@ -266,13 +219,10 @@ XD(convective_path) res = XD(handle_convective_path_startup)(scn, rwalk, &path_starts_in_fluid); if(res != RES_OK) goto error; - res = XD(fetch_fluid_enclosure)(scn, rwalk, &enc); - if(res != RES_OK) goto error; - /* Retrieve the fluid properties at the current position. Use them to verify * that those that are supposed to be constant by the convective random walk * remain the same. */ - res = fluid_get_properties(rwalk->mdm, &rwalk->vtx, &props_ref); + res = fluid_get_properties(mdm, &rwalk->vtx, &props_ref); if(res != RES_OK) goto error; /* The hc upper bound can be 0 if h is uniformly 0. In that case the result @@ -280,7 +230,7 @@ XD(convective_path) if(enc->hc_upper_bound == 0) { ASSERT(path_starts_in_fluid); /* Cannot be in the fluid without starting there. */ rwalk->vtx.time = props_ref.t0; - res = XD(handle_known_fluid_temperature)(scn, ctx, rwalk, T); + res = XD(handle_known_fluid_temperature)(ctx, rwalk, mdm, T); if(res != RES_OK) goto error; if(T->done) { goto exit; /* Stop the random walk */ @@ -300,7 +250,7 @@ XD(convective_path) double mu; /* Fetch fluid properties */ - res = fluid_get_properties(rwalk->mdm, &rwalk->vtx, &props); + res = fluid_get_properties(mdm, &rwalk->vtx, &props); if(res != RES_OK) goto error; res = check_fluid_constant_properties(scn->dev, &props_ref, &props); @@ -308,7 +258,7 @@ XD(convective_path) /* Sample the time using the upper bound. */ mu = enc->hc_upper_bound / (props.rho * props.cp) * enc->S_over_V; - res = XD(time_rewind)(mu, props.t0, rng, rwalk, ctx, T); + res = time_rewind(scn, mu, props.t0, rng, rwalk, ctx, T); if(res != RES_OK) goto error; if(T->done) break; /* Limit condition was reached */ @@ -318,44 +268,47 @@ XD(convective_path) (enc->sXd(view), ssp_rng_canonical_float(rng), ssp_rng_canonical_float(rng), - &prim, &rwalk->hit.u)); - st = rwalk->hit.u; + &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); + &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); + 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)); + 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); + 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) { + /* Define the interface side */ + scene_get_enclosure_ids(scn, rwalk_hit->prim.prim_id, enc_ids); + if(rwalk->enc_id == enc_ids[SDIS_BACK]) { rwalk->hit_side = SDIS_BACK; + } else if(rwalk->enc_id == enc_ids[SDIS_FRONT]) { + rwalk->hit_side = SDIS_FRONT; } else { - FATAL("Unexpected fluid interface.\n"); + FATAL("Unexpected fluid interface\n"); } + /* Get the interface properties */ + interf = scene_get_interface(scn, rwalk_hit->prim.prim_id); + /* Register the new vertex against the heat path */ res = register_heat_vertex(ctx->heat_path, &rwalk->vtx, T->value, SDIS_HEAT_VERTEX_CONVECTION, (int)ctx->nbranchings); if(res != RES_OK) goto error; /* Setup the fragment of the sampled position into the enclosure. */ - XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); + 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); @@ -374,9 +327,9 @@ XD(convective_path) } } - rwalk->hit.distance = 0; + rwalk_hit->distance = 0; T->func = XD(boundary_path); - rwalk->mdm = NULL; /* The random walk is at an interface between 2 media */ + rwalk->enc_id = ENCLOSURE_ID_NULL; /* Interface between 2 enclosures */ exit: return res; diff --git a/src/sdis_heat_path_radiative_Xd.h b/src/sdis_heat_path_radiative_Xd.h @@ -35,9 +35,9 @@ XD(trace_radiative_path) (struct sdis_scene* scn, const float ray_dir[3], struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct XD(temperature)* T) + struct 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. */ @@ -56,9 +56,11 @@ XD(trace_radiative_path) /* Launch the radiative random walk */ for(;;) { const struct sdis_interface* interf = NULL; + struct sdis_medium* chk_mdm = NULL; struct hit_filter_data filter_data = HIT_FILTER_DATA_NULL; struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; - struct sdis_medium* chk_mdm = NULL; + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; + unsigned chk_enc_id = ENCLOSURE_ID_NULL; double alpha; double epsilon; double r; @@ -68,16 +70,16 @@ XD(trace_radiative_path) fX_set_dX(pos, rwalk->vtx.P); /* Trace the radiative ray */ - filter_data.XD(hit) = rwalk->hit; + filter_data.XD(hit) = rwalk->XD(hit); filter_data.epsilon = 1.e-6; #if (SDIS_XD_DIMENSION == 2) SXD(scene_view_trace_ray_3d - (scn->sXd(view), pos, dir, range, &filter_data, &rwalk->hit)); + (scn->sXd(view), pos, dir, range, &filter_data, &rwalk->XD(hit))); #else SXD(scene_view_trace_ray - (scn->sXd(view), pos, dir, range, &filter_data, &rwalk->hit)); + (scn->sXd(view), pos, dir, range, &filter_data, &rwalk->XD(hit))); #endif - if(SXD_HIT_NONE(&rwalk->hit)) { /* Fetch the ambient radiative temperature */ + if(SXD_HIT_NONE(&rwalk->XD(hit))) { /* Fetch the ambient radiative temperature */ struct sdis_radiative_ray ray = SDIS_RADIATIVE_RAY_NULL; double trad = 0; /* [K] */ @@ -127,11 +129,11 @@ XD(trace_radiative_path) } /* Define the hit side */ - rwalk->hit_side = fX(dot)(dir, rwalk->hit.normal) < 0 + rwalk->hit_side = fX(dot)(dir, rwalk->XD(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); + XD(move_pos)(rwalk->vtx.P, dir, rwalk->XD(hit).distance); /* Register the random walk vertex against the heat path */ res = register_heat_vertex(ctx->heat_path, &rwalk->vtx, T->value, @@ -139,8 +141,8 @@ XD(trace_radiative_path) 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); + interf = scene_get_interface(scn, rwalk->XD(hit).prim.prim_id); + XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->XD(hit), rwalk->hit_side); /* Fetch the interface emissivity */ epsilon = interface_side_get_emissivity(interf, SDIS_INTERN_SOURCE_ID, &frag); @@ -156,35 +158,46 @@ XD(trace_radiative_path) 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 */ + rwalk->enc_id = ENCLOSURE_ID_NULL; /* Interface between 2 enclosures */ 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; + fX(normalize)(N, rwalk->XD(hit).normal); + if(rwalk->hit_side == SDIS_BACK) fX(minus)(N, N); + + /* Check that the radiative path still lies in the same enclosure */ + scene_get_enclosure_ids(scn, rwalk->XD(hit).prim.prim_id, enc_ids); + chk_enc_id = rwalk->hit_side == SDIS_FRONT ? enc_ids[0] : enc_ids[1]; + if(chk_enc_id != rwalk->enc_id) { + log_warn(scn->dev, + "%s: the radiative path has escaped from its cavity -- pos=(%g, %g, %g)\n", + FUNC_NAME, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP; + goto error; } - 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_warn(scn->dev, "%s: inconsistent medium definition at `%g %g %g'.\n", - FUNC_NAME, SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP; - goto error; - } + /* Verify that the intersection, although in the same enclosure, touches the + * interface of a fluid. We verify this by interface, since a radiative path + * can be traced in an enclosure containing several media used to describe a + * set of boundary conditions. + * + * If the enclosure is good but the media type is not, this means that the + * radiative path is sampled in the wrong media. This is not a numerical + * problem, but a user problem: trying to sample a radiative path in a solid + * when semi-transparent solids are not yet supported by Stardis. This error + * is therefore fatal for the calculation */ + chk_mdm = rwalk->hit_side == SDIS_FRONT + ? interf->medium_front : interf->medium_back; + if(sdis_medium_get_type(chk_mdm) == SDIS_SOLID) { + log_err(scn->dev, + "%s: a radiative path cannot evolve in a solid -- pos=(%g, %g, %g)\n", + FUNC_NAME, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP_IRRECOVERABLE; + goto error; } + alpha = interface_side_get_specular_fraction(interf, SDIS_INTERN_SOURCE_ID, &frag); r = ssp_rng_canonical(rng); if(r < alpha) { /* Sample specular part */ @@ -204,9 +217,9 @@ res_T XD(radiative_path) (struct sdis_scene* scn, struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct XD(temperature)* T) + struct 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. */ @@ -215,11 +228,11 @@ XD(radiative_path) res_T res = RES_OK; ASSERT(scn && ctx && rwalk && rng && T); - ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + ASSERT(!SXD_HIT_NONE(&rwalk->XD(hit))); /* Normalize the normal of the interface and ensure that it points toward the * current medium */ - fX(normalize(N, rwalk->hit.normal)); + fX(normalize(N, rwalk->XD(hit).normal)); if(rwalk->hit_side == SDIS_BACK) { fX(minus(N, N)); } diff --git a/src/sdis_log.h b/src/sdis_log.h @@ -28,6 +28,8 @@ #define MSG_ERROR_PREFIX_PLAIN_TEXT "stardis-solver (error): " #define MSG_WARNING_PREFIX_PLAIN_TEXT "stardis-solver (warning): " +struct sdis_device; + extern LOCAL_SYM res_T setup_log_default (struct sdis_device* dev); diff --git a/src/sdis_misc.c b/src/sdis_misc.c @@ -14,12 +14,85 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "sdis.h" +#include "sdis_heat_path.h" +#include "sdis_log.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_green.h" -/* Generate the generic functions */ -#define SDIS_XD_DIMENSION 2 -#include "sdis_misc_Xd.h" -#define SDIS_XD_DIMENSION 3 -#include "sdis_misc_Xd.h" +#include <star/ssp.h> + +res_T +time_rewind + (struct sdis_scene* scn, + const double mu, + const double t0, + struct ssp_rng* rng, + struct rwalk* rwalk, + const struct rwalk_context* ctx, + struct temperature* T) +{ + const struct enclosure* enc = NULL; + struct sdis_medium* mdm = NULL; + double temperature; + double tau; + res_T res = RES_OK; + ASSERT(scn && rwalk && ctx && rng && T); + + /* Get the current medium */ + enc = scene_get_enclosure(scn, rwalk->enc_id); + res = scene_get_enclosure_medium(scn, enc, &mdm); + if(res != RES_OK) goto error; + + /* Sample the time using the upper bound. */ + tau = ssp_ran_exp(rng, mu); + + /* Increment the elapsed time */ + ASSERT(rwalk->vtx.time >= t0); + rwalk->elapsed_time += MMIN(tau, rwalk->vtx.time - t0); + + if(IS_INF(rwalk->vtx.time)) goto exit; /* Steady computation */ + + /* Time rewind */ + rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); /* Time rewind */ + + /* The path does not reach the limit condition */ + if(rwalk->vtx.time > t0) goto exit; + + /* Fetch the initial temperature */ + temperature = medium_get_temperature(mdm, &rwalk->vtx); + if(SDIS_TEMPERATURE_IS_UNKNOWN(temperature)) { + log_err(mdm->dev, "the path reaches the limit condition but the " + "%s temperature remains unknown -- position=%g, %g, %g\n", + medium_type_to_string(sdis_medium_get_type(mdm)), + SPLIT3(rwalk->vtx.P)); + res = RES_BAD_ARG; + goto error; + } + + /* Update temperature */ + T->value += temperature; + T->done = 1; + + if(ctx->heat_path) { + /* Update the registered vertex data */ + struct sdis_heat_vertex* vtx; + vtx = heat_path_get_last_vertex(ctx->heat_path); + vtx->time = rwalk->vtx.time; + vtx->weight = T->value; + } + + if(ctx->green_path) { + res = green_path_set_limit_vertex + (ctx->green_path, mdm, &rwalk->vtx, rwalk->elapsed_time); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} res_T check_primitive_uv_2d(struct sdis_device* dev, const double param_coord[]) diff --git a/src/sdis_misc.h b/src/sdis_misc.h @@ -93,6 +93,7 @@ reflect_3d(float res[3], const float V[3], const float N[3]) cos_V_N = f3_dot(V, N); f3_mulf(tmp, N, 2*cos_V_N); f3_sub(res, tmp, V); + f3_normalize(res, res); /* Handle numerical issue */ return res; } @@ -148,22 +149,14 @@ register_heat_vertex } extern LOCAL_SYM res_T -time_rewind_2d - (const double mu, +time_rewind + (struct sdis_scene* scn, + const double mu, const double t0, /* Initial time */ struct ssp_rng* rng, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, const struct rwalk_context* ctx, - struct temperature_2d* T); - -extern LOCAL_SYM res_T -time_rewind_3d - (const double mu, - const double t0, /* Initial time */ - struct ssp_rng* rng, - struct rwalk_3d* rwalk, - const struct rwalk_context* ctx, - struct temperature_3d* T); + struct temperature* T); /* Check the validity of the parametric coordinate onto a 2D primitive. If it * is invalid, the function prints an error message and return RES_BAD_ARG. */ diff --git a/src/sdis_misc_Xd.h b/src/sdis_misc_Xd.h @@ -1,90 +0,0 @@ -/* Copyright (C) 2016-2024 |Méso|Star> (contact@meso-star.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#include "sdis_heat_path.h" -#include "sdis_log.h" -#include "sdis_medium_c.h" -#include "sdis_misc.h" -#include "sdis_green.h" - -#include <star/ssp.h> - -#include "sdis_Xd_begin.h" - -res_T -XD(time_rewind) - (const double mu, - const double t0, - struct ssp_rng* rng, - struct XD(rwalk)* rwalk, - const struct rwalk_context* ctx, - struct XD(temperature)* T) -{ - double temperature; - double tau; - res_T res = RES_OK; - ASSERT(rwalk && rng && T); - - /* Sample the time using the upper bound. */ - tau = ssp_ran_exp(rng, mu); - - /* Increment the elapsed time */ - ASSERT(rwalk->vtx.time >= t0); - rwalk->elapsed_time += MMIN(tau, rwalk->vtx.time - t0); - - if(IS_INF(rwalk->vtx.time)) goto exit; /* Steady computation */ - - /* Time rewind */ - rwalk->vtx.time = MMAX(rwalk->vtx.time - tau, t0); /* Time rewind */ - - /* The path does not reach the limit condition */ - if(rwalk->vtx.time > t0) goto exit; - - /* Fetch the initial temperature */ - temperature = medium_get_temperature(rwalk->mdm, &rwalk->vtx); - if(SDIS_TEMPERATURE_IS_UNKNOWN(temperature)) { - log_err(rwalk->mdm->dev, "the path reaches the limit condition but the " - "%s temperature remains unknown -- position=%g, %g, %g\n", - medium_type_to_string(sdis_medium_get_type(rwalk->mdm)), - SPLIT3(rwalk->vtx.P)); - res = RES_BAD_ARG; - goto error; - } - - /* Update temperature */ - T->value += temperature; - T->done = 1; - - if(ctx->heat_path) { - /* Update the registered vertex data */ - struct sdis_heat_vertex* vtx; - vtx = heat_path_get_last_vertex(ctx->heat_path); - vtx->time = rwalk->vtx.time; - vtx->weight = T->value; - } - - if(ctx->green_path) { - res = green_path_set_limit_vertex(ctx->green_path, rwalk->mdm, - &rwalk->vtx, rwalk->elapsed_time); - if(res != RES_OK) goto error; - } - -exit: - return res; -error: - goto exit; -} - -#include "sdis_Xd_end.h" diff --git a/src/sdis_primkey.c b/src/sdis_primkey.c @@ -0,0 +1,113 @@ +/* Copyright (C) 2016-2024 |Méso|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 <rsys/double2.h> +#include <rsys/double3.h> + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE int +cmp_dbl2(const double v0[2], const double v1[2]) +{ + if(v0[0] < v1[0]) return -1; + if(v0[0] > v1[0]) return +1; + if(v0[1] < v1[1]) return -1; + if(v0[1] > v1[1]) return +1; + return 0; +} + +static INLINE int +cmp_dbl3(const double v0[3], const double v1[3]) +{ + if(v0[0] < v1[0]) return -1; + if(v0[0] > v1[0]) return +1; + if(v0[1] < v1[1]) return -1; + if(v0[1] > v1[1]) return +1; + if(v0[2] < v1[2]) return -1; + if(v0[2] > v1[2]) return +1; + return 0; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +void +sdis_primkey_2d_setup + (struct sdis_primkey* key, + const double node0[2], + const double node1[2]) +{ + double *v0, *v1; + ASSERT(key && node0 && node1); + v0 = d2_set(key->nodes+0, node0); + v1 = d2_set(key->nodes+2, node1); + if(cmp_dbl2(v0, v1) > 0) { + SWAP(double, v0[0], v1[0]); + SWAP(double, v0[1], v1[1]); + } + key->ncoords = 4; +} + +void +sdis_primkey_setup + (struct sdis_primkey* key, + const double node0[3], + const double node1[3], + const double node2[3]) +{ + double *v0, *v1, *v2; + ASSERT(key && node0 && node1 && node2); + v0 = d3_set(key->nodes+0, node0); + v1 = d3_set(key->nodes+3, node1); + v2 = d3_set(key->nodes+6, node2); + + /* Bubble sort */ + #define SWAP_DBL3(V0, V1) { \ + SWAP(double, (V0)[0], (V1)[0]); \ + SWAP(double, (V0)[1], (V1)[1]); \ + SWAP(double, (V0)[2], (V1)[2]); \ + } (void)0 + if(cmp_dbl3(v0, v1) > 0) SWAP_DBL3(v0, v1); + if(cmp_dbl3(v1, v2) > 0) SWAP_DBL3(v1, v2); + if(cmp_dbl3(v0, v1) > 0) SWAP_DBL3(v0, v1); + #undef SWAP_DBL3 + key->ncoords = 9; +} + +size_t +sdis_primkey_hash(const struct sdis_primkey* key) +{ + return hash_fnv64(key->nodes, sizeof(double)*key->ncoords); +} + +char +sdis_primkey_eq + (const struct sdis_primkey* key0, + const struct sdis_primkey* key1) +{ + unsigned i = 0; + ASSERT(key0 && key1); + + if(key0->ncoords != key1->ncoords) return 0; + if(key0->ncoords != 4 && key0->ncoords != 9) return 0; + if(key1->ncoords != 4 && key1->ncoords != 9) return 0; + FOR_EACH(i, 0, key0->ncoords) { + if(key0->nodes[i] != key1->nodes[i]) return 0; + } + return 1; +} diff --git a/src/sdis_realisation.c b/src/sdis_realisation.c @@ -19,16 +19,30 @@ /******************************************************************************* * Helper functions ******************************************************************************/ -static INLINE int +static INLINE res_T check_ray_realisation_args(const struct ray_realisation_args* args) { - return args - && args->rng - && args->medium - && args->medium->type == SDIS_FLUID - && args->time >= 0 - && args->picard_order > 0 - && (unsigned)args->diff_algo < SDIS_DIFFUSION_ALGORITHMS_COUNT__; + /* Check pointers */ + if(!args || !args->rng) return RES_BAD_ARG; + + if(args->time < 0) return RES_BAD_ARG; + if(args->picard_order <= 0) return RES_BAD_ARG; + + if((unsigned)args->diff_algo >= SDIS_DIFFUSION_ALGORITHMS_COUNT__) { + return RES_BAD_ARG; + } + + /* Check the enclosure identifier. Only its validity is checked, not the fact + * that the enclosure is a fluid. Even though Stardis doesn't allow you to + * sample a radiative path in a solid, we don't query the medium of the + * enclosure since it may contain several: querying the medium will therefore + * return an error. The type of medium is checked later, when sampling the + * radiative path, when it reaches an interface whose medium must be a fluid*/ + if(args->enc_id == ENCLOSURE_ID_NULL) { + return RES_BAD_ARG; + } + + return RES_OK; } /******************************************************************************* @@ -47,17 +61,17 @@ ray_realisation_3d double* weight) { struct rwalk_context ctx = RWALK_CONTEXT_NULL; - struct rwalk_3d rwalk = RWALK_NULL_3d; - struct temperature_3d T = TEMPERATURE_NULL_3d; + struct rwalk rwalk = RWALK_NULL; + struct temperature T = TEMPERATURE_NULL; float dir[3]; res_T res = RES_OK; - ASSERT(scn && weight && check_ray_realisation_args(args)); + ASSERT(scn && weight && check_ray_realisation_args(args) == RES_OK); d3_set(rwalk.vtx.P, args->position); rwalk.vtx.time = args->time; - rwalk.hit = S3D_HIT_NULL; + rwalk.hit_3d = S3D_HIT_NULL; rwalk.hit_side = SDIS_SIDE_NULL__; - rwalk.mdm = args->medium; + rwalk.enc_id = args->enc_id; ctx.heat_path = args->heat_path; ctx.Tmin = scn->tmin; @@ -69,7 +83,7 @@ ray_realisation_3d ctx.max_branchings = args->picard_order - 1; ctx.irealisation = args->irealisation; ctx.diff_algo = args->diff_algo; - + f3_set_d3(dir, args->direction); /* Register the starting position against the heat path */ diff --git a/src/sdis_realisation.h b/src/sdis_realisation.h @@ -22,11 +22,13 @@ #include <rsys/rsys.h> /* Forward declarations */ +struct bound_flux_result; struct green_path_handle; +struct rwalk; struct sdis_heat_path; struct sdis_scene; struct ssp_rng; -struct bound_flux_result; +struct temperature; enum flux_flag { FLUX_FLAG_CONVECTIVE = BIT(FLUX_CONVECTIVE), @@ -41,24 +43,24 @@ extern LOCAL_SYM res_T sample_coupled_path_2d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_2d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_2d* T); + struct temperature* T); extern LOCAL_SYM res_T sample_coupled_path_3d (struct sdis_scene* scn, struct rwalk_context* ctx, - struct rwalk_3d* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct temperature_3d* T); + struct temperature* T); /******************************************************************************* * Realisation at a given position and time IN a medium ******************************************************************************/ struct probe_realisation_args { struct ssp_rng* rng; - struct sdis_medium* medium; /* Medium into which the realisation starts */ + unsigned enc_id; /* Enclosure into which the realisation starts */ double position[3]; /* Probe position */ double time; /* Observation time */ size_t picard_order; /* Picard order to estimate radiative temperature */ @@ -69,7 +71,7 @@ struct probe_realisation_args { }; #define PROBE_REALISATION_ARGS_NULL__ { \ NULL, /* RNG */ \ - NULL, /* Medium */ \ + ENCLOSURE_ID_NULL, /* Enclosure */ \ {0,0,0}, /* Position */ \ -1, /* Observation time */ \ 0, /* Picard order */ \ @@ -180,7 +182,7 @@ boundary_flux_realisation_3d ******************************************************************************/ struct ray_realisation_args { struct ssp_rng* rng; - struct sdis_medium* medium; /* Medium into which the realisation starts */ + unsigned enc_id; /* Enclosure into which the realisation starts */ double position[3]; /* Ray position */ double direction[3]; /* Ray direction */ double time; /* Observation time */ @@ -191,7 +193,7 @@ struct ray_realisation_args { }; #define RAY_REALISATION_ARGS_NULL__ { \ NULL, /* RNG */ \ - NULL, /* Medium */ \ + ENCLOSURE_ID_NULL, /* Enclosure */ \ {0,0,0}, /* Position */ \ {0,0,0}, /* Direction */ \ -1, /* Observation time */ \ diff --git a/src/sdis_realisation_Xd.h b/src/sdis_realisation_Xd.h @@ -32,18 +32,19 @@ #ifndef SDIS_REALISATION_XD_H #define SDIS_REALISATION_XD_H -static INLINE int +static INLINE res_T check_probe_realisation_args(const struct probe_realisation_args* args) { return args && args->rng - && args->medium + && args->enc_id != ENCLOSURE_ID_NULL && args->time >= 0 && args->picard_order > 0 - && (unsigned)args->diff_algo < SDIS_DIFFUSION_ALGORITHMS_COUNT__; + && (unsigned)args->diff_algo < SDIS_DIFFUSION_ALGORITHMS_COUNT__ + ? RES_OK : RES_BAD_ARG; } -static INLINE int +static INLINE res_T check_boundary_realisation_args(const struct boundary_realisation_args* args) { return args @@ -55,10 +56,11 @@ check_boundary_realisation_args(const struct boundary_realisation_args* args) && args->time >= 0 && args->picard_order > 0 && (args->side == SDIS_FRONT || args->side == SDIS_BACK) - && (unsigned)args->diff_algo < SDIS_DIFFUSION_ALGORITHMS_COUNT__; + && (unsigned)args->diff_algo < SDIS_DIFFUSION_ALGORITHMS_COUNT__ + ? RES_OK : RES_BAD_ARG; } -static INLINE int +static INLINE res_T check_boundary_flux_realisation_args (const struct boundary_flux_realisation_args* args) { @@ -71,7 +73,8 @@ check_boundary_flux_realisation_args && args->time >= 0 && args->picard_order > 0 && (args->solid_side == SDIS_FRONT || args->solid_side == SDIS_BACK) - && (unsigned)args->diff_algo < SDIS_DIFFUSION_ALGORITHMS_COUNT__; + && (unsigned)args->diff_algo < SDIS_DIFFUSION_ALGORITHMS_COUNT__ + ? RES_OK : RES_BAD_ARG; } #endif /* SDIS_REALISATION_XD_H */ @@ -82,15 +85,15 @@ res_T XD(sample_coupled_path) (struct sdis_scene* scn, struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, + struct rwalk* rwalk, struct ssp_rng* rng, - struct XD(temperature)* T) + struct temperature* T) { #ifndef NDEBUG /* Stack that saves the state of each recursion steps. */ struct entry { - struct XD(temperature) temperature; - struct XD(rwalk) rwalk; + struct temperature temperature; + struct rwalk rwalk; }* stack = NULL; size_t istack = 0; #endif @@ -109,8 +112,8 @@ XD(sample_coupled_path) while(!T->done) { /* Save the current random walk state */ - const struct XD(rwalk) rwalk_bkp = *rwalk; - const struct XD(temperature) T_bkp = *T; + const struct rwalk rwalk_bkp = *rwalk; + const struct temperature T_bkp = *T; size_t nfails = 0; /* #failures */ #ifndef NDEBUG @@ -171,27 +174,39 @@ XD(probe_realisation) struct probe_realisation_args* args, double* weight) { + /* Starting enclosure/medium */ + const struct enclosure* enc = NULL; + struct sdis_medium* mdm = NULL; + + /* Random walk */ struct rwalk_context ctx = RWALK_CONTEXT_NULL; - struct XD(rwalk) rwalk = XD(RWALK_NULL); - struct XD(temperature) T = XD(TEMPERATURE_NULL); + struct rwalk rwalk = RWALK_NULL; + struct temperature T = TEMPERATURE_NULL; + + /* Miscellaneous */ 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(scn && weight && check_probe_realisation_args(args)); + ASSERT(scn && weight && check_probe_realisation_args(args) == RES_OK); - switch(args->medium->type) { + /* Get the enclosure medium */ + enc = scene_get_enclosure(scn, args->enc_id); + res = scene_get_enclosure_medium(scn, enc, &mdm); + if(res != RES_OK) goto error; + + switch(sdis_medium_get_type(mdm)) { case SDIS_FLUID: T.func = XD(convective_path); get_initial_temperature = fluid_get_temperature; - t0 = fluid_get_t0(args->medium); + t0 = fluid_get_t0(mdm); break; case SDIS_SOLID: T.func = XD(conductive_path); get_initial_temperature = solid_get_temperature; - t0 = solid_get_t0(args->medium); + t0 = solid_get_t0(mdm); break; default: FATAL("Unreachable code\n"); break; } @@ -200,7 +215,7 @@ XD(probe_realisation) rwalk.vtx.time = args->time; /* Register the starting position against the heat path */ - type = args->medium->type == SDIS_SOLID + type = sdis_medium_get_type(mdm) == SDIS_SOLID ? SDIS_HEAT_VERTEX_CONDUCTION : SDIS_HEAT_VERTEX_CONVECTION; res = register_heat_vertex(args->heat_path, &rwalk.vtx, 0, type, 0); @@ -210,7 +225,7 @@ XD(probe_realisation) double tmp; /* Check the initial condition. */ rwalk.vtx.time = t0; - tmp = get_initial_temperature(args->medium, &rwalk.vtx); + tmp = get_initial_temperature(mdm, &rwalk.vtx); if(SDIS_TEMPERATURE_IS_KNOWN(tmp)) { *weight = tmp; goto exit; @@ -224,8 +239,8 @@ XD(probe_realisation) goto error; } - rwalk.hit = SXD_HIT_NULL; - rwalk.mdm = args->medium; + rwalk.XD(hit) = SXD_HIT_NULL; + rwalk.enc_id = args->enc_id; ctx.green_path = args->green_path; ctx.heat_path = args->heat_path; @@ -258,8 +273,8 @@ XD(boundary_realisation) double* weight) { struct rwalk_context ctx = RWALK_CONTEXT_NULL; - struct XD(rwalk) rwalk = XD(RWALK_NULL); - struct XD(temperature) T = XD(TEMPERATURE_NULL); + struct rwalk rwalk = RWALK_NULL; + struct temperature T = TEMPERATURE_NULL; struct sXd(attrib) attr; #if SDIS_XD_DIMENSION == 2 float st; @@ -267,13 +282,13 @@ XD(boundary_realisation) float st[2]; #endif res_T res = RES_OK; - ASSERT(scn && weight && check_boundary_realisation_args(args)); + ASSERT(scn && weight && check_boundary_realisation_args(args) == RES_OK); T.func = XD(boundary_path); rwalk.hit_side = args->side; - rwalk.hit.distance = 0; + rwalk.XD(hit).distance = 0; rwalk.vtx.time = args->time; - rwalk.mdm = NULL; /* The random walk is at an interface between 2 media */ + rwalk.enc_id = ENCLOSURE_ID_NULL; /* At an interface between 2 enclosures */ #if SDIS_XD_DIMENSION == 2 st = (float)args->uv[0]; @@ -283,20 +298,20 @@ XD(boundary_realisation) /* Fetch the primitive */ SXD(scene_view_get_primitive - (scn->sXd(view), (unsigned int)args->iprim, &rwalk.hit.prim)); + (scn->sXd(view), (unsigned int)args->iprim, &rwalk.XD(hit).prim)); /* Retrieve the world space position of the probe onto the primitive */ - SXD(primitive_get_attrib(&rwalk.hit.prim, SXD_POSITION, st, &attr)); + SXD(primitive_get_attrib(&rwalk.XD(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); + SXD(primitive_get_attrib(&rwalk.XD(hit).prim, SXD_GEOMETRY_NORMAL, st, &attr)); + fX(set)(rwalk.XD(hit).normal, attr.value); #if SDIS_XD_DIMENSION==2 - rwalk.hit.u = st; + rwalk.XD(hit).u = st; #else - f2_set(rwalk.hit.uv, st); + f2_set(rwalk.XD(hit).uv, st); #endif res = register_heat_vertex(args->heat_path, &rwalk.vtx, 0/*weight*/, @@ -332,14 +347,14 @@ XD(boundary_flux_realisation) struct boundary_flux_realisation_args* args, struct bound_flux_result* result) { + /* Random walk */ struct rwalk_context ctx = RWALK_CONTEXT_NULL; - struct XD(rwalk) rwalk; - struct XD(temperature) T; + struct rwalk rwalk = RWALK_NULL; + struct temperature T = TEMPERATURE_NULL; + + /* Boundary */ struct sXd(attrib) attr; struct sXd(primitive) prim; - struct sdis_interface* interf = NULL; - struct sdis_medium* fluid_mdm = NULL; - #if SDIS_XD_DIMENSION == 2 float st; #else @@ -347,13 +362,17 @@ XD(boundary_flux_realisation) #endif double P[SDIS_XD_DIMENSION]; float N[SDIS_XD_DIMENSION]; + unsigned enc_ids[2] = {ENCLOSURE_ID_NULL, ENCLOSURE_ID_NULL}; + enum sdis_side fluid_side; + + /* Miscellaneous */ double Tmin, Tmin2, Tmin3; double That, That2, That3; - enum sdis_side fluid_side; res_T res = RES_OK; char compute_radiative; char compute_convective; - ASSERT(scn && result && check_boundary_flux_realisation_args(args)); + + ASSERT(scn && result && check_boundary_flux_realisation_args(args) == RES_OK); #if SDIS_XD_DIMENSION == 2 #define SET_PARAM(Dest, Src) (Dest).u = (Src); @@ -386,14 +405,14 @@ XD(boundary_flux_realisation) SXD(primitive_get_attrib(&prim, SXD_GEOMETRY_NORMAL, st, &attr)); fX(set)(N, attr.value); - #define RESET_WALK(Side, Mdm) { \ - rwalk = XD(RWALK_NULL); \ + #define RESET_WALK(Side, EncId) { \ + rwalk = RWALK_NULL; \ rwalk.hit_side = (Side); \ - rwalk.hit.distance = 0; \ + rwalk.XD(hit).distance = 0; \ rwalk.vtx.time = args->time; \ - rwalk.mdm = (Mdm); \ - rwalk.hit.prim = prim; \ - SET_PARAM(rwalk.hit, st); \ + rwalk.enc_id = (EncId); \ + rwalk.XD(hit).prim = prim; \ + SET_PARAM(rwalk.XD(hit), st); \ ctx.Tmin = Tmin; \ ctx.Tmin3 = Tmin3; \ ctx.That = That; \ @@ -403,24 +422,23 @@ XD(boundary_flux_realisation) ctx.irealisation = args->irealisation; \ ctx.diff_algo = args->diff_algo; \ dX(set)(rwalk.vtx.P, P); \ - fX(set)(rwalk.hit.normal, N); \ - T = XD(TEMPERATURE_NULL); \ + fX(set)(rwalk.XD(hit).normal, N); \ + T = TEMPERATURE_NULL; \ } (void)0 /* Compute boundary temperature */ - RESET_WALK(args->solid_side, NULL); + RESET_WALK(args->solid_side, ENCLOSURE_ID_NULL); T.func = XD(boundary_path); res = XD(sample_coupled_path)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) return res; result->Tboundary = T.value; - /* Fetch the fluid medium */ - interf = scene_get_interface(scn, (unsigned)args->iprim); - fluid_mdm = interface_get_medium(interf, fluid_side); + /* Get the enclosures */ + scene_get_enclosure_ids(scn, (unsigned)args->iprim, enc_ids); /* Compute radiative temperature */ if(compute_radiative) { - RESET_WALK(fluid_side, fluid_mdm); + RESET_WALK(fluid_side, enc_ids[fluid_side]); T.func = XD(radiative_path); res = XD(sample_coupled_path)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) return res; @@ -430,7 +448,7 @@ XD(boundary_flux_realisation) /* Compute fluid temperature */ if(compute_convective) { - RESET_WALK(fluid_side, fluid_mdm); + RESET_WALK(fluid_side, enc_ids[fluid_side]); T.func = XD(convective_path); res = XD(sample_coupled_path)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) return res; diff --git a/src/sdis_scene.c b/src/sdis_scene.c @@ -103,6 +103,8 @@ scene_release(ref_T * ref) darray_prim_prop_release(&scn->prim_props); htable_enclosure_release(&scn->enclosures); htable_d_release(&scn->tmp_hc_ub); + htable_key2prim2d_release(&scn->key2prim2d); + htable_key2prim3d_release(&scn->key2prim3d); 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->senc2d_scn) SENC2D(scene_ref_put(scn->senc2d_scn)); @@ -332,7 +334,6 @@ sdis_scene_get_senc2d_scene { if(!scn || !senc2d_scn) return RES_BAD_ARG; if(!scn->senc2d_scn) return RES_BAD_ARG; /* Scene is 3D */ - SENC2D(scene_ref_get(scn->senc2d_scn)); *senc2d_scn = scn->senc2d_scn; return RES_OK; } @@ -344,12 +345,33 @@ sdis_scene_get_senc3d_scene { if(!scn || !senc3d_scn) return RES_BAD_ARG; if(!scn->senc3d_scn) return RES_BAD_ARG; /* Scene is 2D */ - SENC3D(scene_ref_get(scn->senc3d_scn)); *senc3d_scn = scn->senc3d_scn; return RES_OK; } res_T +sdis_scene_get_s2d_scene_view + (struct sdis_scene* scn, + struct s2d_scene_view** s2d_view) +{ + if(!scn || !s2d_view) return RES_BAD_ARG; + if(!scn->s2d_view) return RES_BAD_ARG; /* Scene is 3D */ + *s2d_view = scn->s2d_view; + return RES_OK; +} + +res_T +sdis_scene_get_s3d_scene_view + (struct sdis_scene* scn, + struct s3d_scene_view** s3d_view) +{ + if(!scn || !s3d_view) return RES_BAD_ARG; + if(!scn->s3d_view) return RES_BAD_ARG; /* Scene is 2D */ + *s3d_view = scn->s3d_view; + return RES_OK; +} + +res_T sdis_scene_get_dimension (const struct sdis_scene* scn, enum sdis_scene_dimension* dim) { @@ -416,6 +438,38 @@ sdis_scene_get_radiative_env return RES_OK; } +res_T +sdis_scene_get_s2d_primitive + (struct sdis_scene* scn, + const struct sdis_primkey* key, + struct s2d_primitive* out_prim) +{ + struct s2d_primitive* prim = NULL; + + if(!scn || !key || !out_prim || !scene_is_2d(scn)) return RES_BAD_ARG; + + if((prim = htable_key2prim2d_find(&scn->key2prim2d, key)) == NULL) + return RES_BAD_ARG; + *out_prim = *prim; + return RES_OK; +} + +res_T +sdis_scene_get_s3d_primitive + (struct sdis_scene* scn, + const struct sdis_primkey* key, + struct s3d_primitive* out_prim) +{ + struct s3d_primitive* prim = NULL; + + if(!scn || !key || !out_prim || scene_is_2d(scn)) return RES_BAD_ARG; + + if((prim = htable_key2prim3d_find(&scn->key2prim3d, key)) == NULL) + return RES_BAD_ARG; + *out_prim = *prim; + return RES_OK; +} + /******************************************************************************* * Local miscellaneous function ******************************************************************************/ @@ -427,26 +481,57 @@ scene_get_interface(const struct sdis_scene* scn, const unsigned iprim) } res_T -scene_get_medium +scene_get_enclosure_id (struct sdis_scene* scn, const double pos[], - struct get_medium_info* info, - struct sdis_medium** out_medium) + unsigned* enc_id) { return scene_is_2d(scn) - ? scene_get_medium_2d(scn, pos, info, out_medium) - : scene_get_medium_3d(scn, pos, info, out_medium); + ? scene_get_enclosure_id_2d(scn, pos, enc_id) + : scene_get_enclosure_id_3d(scn, pos, enc_id); } res_T -scene_get_medium_in_closed_boundaries +scene_get_enclosure_id_in_closed_boundaries (struct sdis_scene* scn, const double pos[], - struct sdis_medium** out_medium) + unsigned* enc_id) { return scene_is_2d(scn) - ? scene_get_medium_in_closed_boundaries_2d(scn, pos, out_medium) - : scene_get_medium_in_closed_boundaries_3d(scn, pos, out_medium); + ? scene_get_enclosure_id_in_closed_boundaries_2d(scn, pos, enc_id) + : scene_get_enclosure_id_in_closed_boundaries_3d(scn, pos, enc_id); +} + +res_T +scene_get_enclosure_medium + (struct sdis_scene* scn, + const struct enclosure* enc, + struct sdis_medium** out_mdm) +{ + struct sdis_medium* mdm = NULL; + res_T res = RES_OK; + + ASSERT(scn && enc && out_mdm); + + /* Check that the enclosure doesn't surround multiple media */ + if(enc->medium_id == MEDIUM_ID_MULTI) { + log_warn(scn->dev, + "%s: invalid medium request. The enclosure includes several media.\n", + FUNC_NAME); + res = RES_BAD_OP; + goto error; + } + + /* Obtain enclosure medium */ + ASSERT(enc->medium_id < darray_medium_size_get(&scn->media)); + mdm = darray_medium_data_get(&scn->media)[enc->medium_id]; + +error: + *out_mdm = mdm; + goto exit; +exit: + mdm = NULL; + return res; } res_T diff --git a/src/sdis_scene_Xd.h b/src/sdis_scene_Xd.h @@ -823,7 +823,7 @@ XD(register_enclosure)(struct sdis_scene* scn, struct sencXd(enclosure)* enc) /* Setup the medium id of the enclosure */ if(header.enclosed_media_count > 1) { - enc_data->medium_id = ENCLOSURE_MULTI_MEDIA; + enc_data->medium_id = MEDIUM_ID_MULTI; } else { SENCXD(enclosure_get_medium(enc, 0, &enc_data->medium_id)); } @@ -938,6 +938,89 @@ error: goto exit; } +#if DIM == 2 +static res_T +setup_primitive_keys_2d(struct sdis_scene* scn, struct senc2d_scene* senc_scn) +{ + unsigned iprim = 0; + unsigned nprims = 0; + res_T res = RES_OK; + ASSERT(scn && senc_scn); + + SENC2D(scene_get_primitives_count(senc_scn, &nprims)); + + FOR_EACH(iprim, 0, nprims) { + struct s2d_primitive prim = S2D_PRIMITIVE_NULL; + struct sdis_primkey key = SDIS_PRIMKEY_NULL; + unsigned ids[2] = {0,0}; + double v0[2] = {0,0}; + double v1[2] = {0,0}; + + /* Retrieve positions from Star-Enclosre, not Star-2D. Star-Enclosure keeps + * the positions submitted by the user as they are, without any + * transformation or conversion (Star-2D converts them to float). This + * ensures that the caller can construct the same key from his data */ + SENC2D(scene_get_primitive(senc_scn, iprim, ids)); + SENC2D(scene_get_vertex(senc_scn, ids[0], v0)); + SENC2D(scene_get_vertex(senc_scn, ids[1], v1)); + S2D(scene_view_get_primitive(scn->s2d_view, iprim, &prim)); + + sdis_primkey_2d_setup(&key, v0, v1); + + res = htable_key2prim2d_set(&scn->key2prim2d, &key, &prim); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + htable_key2prim2d_purge(&scn->key2prim2d); + goto exit; +} + +#elif DIM == 3 +static res_T +setup_primitive_keys_3d(struct sdis_scene* scn, struct senc3d_scene* senc_scn) +{ + unsigned iprim = 0; + unsigned nprims = 0; + res_T res = RES_OK; + ASSERT(scn && senc_scn); + + SENC3D(scene_get_primitives_count(senc_scn, &nprims)); + + FOR_EACH(iprim, 0, nprims) { + struct s3d_primitive prim = S3D_PRIMITIVE_NULL; + struct sdis_primkey key = SDIS_PRIMKEY_NULL; + unsigned ids[3] = {0}; + double v0[3] = {0}; + double v1[3] = {0}; + double v2[3] = {0}; + + /* Retrieve positions from Star-Enclosre, not Star-3D. Star-Enclosure keeps + * the positions submitted by the user as they are, without any + * transformation or conversion (Star-3D converts them to float). This + * ensures that the caller can construct the same key from his data */ + SENC3D(scene_get_primitive(senc_scn, iprim, ids)); + SENC3D(scene_get_vertex(senc_scn, ids[0], v0)); + SENC3D(scene_get_vertex(senc_scn, ids[1], v1)); + SENC3D(scene_get_vertex(senc_scn, ids[2], v2)); + S3D(scene_view_get_primitive(scn->s3d_view, iprim, &prim)); + + sdis_primkey_setup(&key, v0, v1, v2); + + res = htable_key2prim3d_set(&scn->key2prim3d, &key, &prim); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + htable_key2prim3d_purge(&scn->key2prim3d); + goto exit; +} +#endif + /* Create a Stardis scene */ static res_T XD(scene_create) @@ -956,8 +1039,9 @@ XD(scene_create) scn = MEM_CALLOC(dev->allocator, 1, sizeof(struct sdis_scene)); if(!scn) { - log_err(dev, "%s: could not allocate the Stardis scene.\n", FUNC_NAME); res = RES_MEM_ERR; + log_err(dev, "%s: unabale to allocate the scene -- %s\n", + FUNC_NAME, res_to_cstr(res)); goto error; } @@ -973,6 +1057,8 @@ XD(scene_create) darray_prim_prop_init(dev->allocator, &scn->prim_props); htable_enclosure_init(dev->allocator, &scn->enclosures); htable_d_init(dev->allocator, &scn->tmp_hc_ub); + htable_key2prim2d_init(dev->allocator, &scn->key2prim2d); + htable_key2prim3d_init(dev->allocator, &scn->key2prim3d); if(args->source) { SDIS(source_ref_get(args->source)); @@ -994,23 +1080,32 @@ XD(scene_create) args->context, &senc_scn); if(res != RES_OK) { - log_err(dev, "%s: error during the scene analysis.\n", FUNC_NAME); + log_err(dev, "%s: unable to analyze the scene -- %s\n", + FUNC_NAME, res_to_cstr(res)); goto error; } res = XD(setup_properties)(scn, senc_scn, args->get_interface, args->context); if(res != RES_OK) { - log_err(dev, "%s: could not setup the scene interfaces and their media.\n", - FUNC_NAME); + log_err(dev, "%s: unable to configure interfaces and media -- %s\n", + FUNC_NAME, res_to_cstr(res)); goto error; } res = XD(setup_scene_geometry)(scn, senc_scn); if(res != RES_OK) { - log_err(dev, "%s: could not setup the scene geometry.\n", FUNC_NAME); + log_err(dev, "%s: unable to configure scene geometry -- %s\n", + FUNC_NAME, res_to_cstr(res)); goto error; } res = XD(setup_enclosures)(scn, senc_scn); if(res != RES_OK) { - log_err(dev, "%s: could not setup the enclosures.\n", FUNC_NAME); + log_err(dev, "%s: unable to configure enclosures -- %s\n", + FUNC_NAME, res_to_cstr(res)); + goto error; + } + res = XD(setup_primitive_keys)(scn, senc_scn); + if(res != RES_OK) { + log_err(dev, "%s: unable to configure primitive keys -- %s\n", + FUNC_NAME, res_to_cstr(res)); goto error; } scn->sencXd(scn) = senc_scn; @@ -1094,14 +1189,12 @@ error: /******************************************************************************* * Local functions ******************************************************************************/ -static INLINE res_T -XD(scene_get_medium) +static res_T +XD(scene_get_enclosure_id) (struct sdis_scene* scn, const double pos[DIM], - struct get_medium_info* info, /* May be NULL */ - struct sdis_medium** out_medium) + unsigned* out_enc_id) { - struct sdis_medium* medium = NULL; size_t iprim, nprims; float P[DIM]; /* Range of the parametric coordinate into which positions are challenged */ @@ -1111,6 +1204,7 @@ XD(scene_get_medium) float st[3][2]; #endif size_t nsteps = 3; + unsigned enc_id = ENCLOSURE_ID_NULL; res_T res = RES_OK; ASSERT(scn && pos); @@ -1163,8 +1257,8 @@ XD(scene_get_medium) } while((SXD_HIT_NONE(&hit) || HIT_ON_BOUNDARY(&hit, P, dir)) && ++istep < nsteps); - /* No valid intersection is found on the current primitive. Challenge - * another. */ + /* No valid intersection is found on the current primitive. + * Challenge another. */ if(istep >= nsteps) continue; fX(normalize)(N, hit.normal); @@ -1172,44 +1266,17 @@ XD(scene_get_medium) /* Not too close and not roughly orthognonal */ if(hit.distance > 1.e-6 && absf(cos_N_dir) > 1.e-2f) { - const struct enclosure* enclosure = NULL; unsigned enc_ids[2]; - const struct sdis_interface* interf; - - interf = scene_get_interface(scn, hit.prim.prim_id); scene_get_enclosure_ids(scn, hit.prim.prim_id, enc_ids); + enc_id = cos_N_dir < 0 ? enc_ids[0] : enc_ids[1]; - if(cos_N_dir < 0) { - medium = interface_get_medium(interf, SDIS_FRONT); - enclosure = scene_get_enclosure(scn, enc_ids[0]); - } else { - medium = interface_get_medium(interf, SDIS_BACK); - enclosure = scene_get_enclosure(scn, enc_ids[1]); - } - - if(enclosure->medium_id == ENCLOSURE_MULTI_MEDIA) { - log_warn - (scn->dev, - "%s: invalid medium request at {%g, %g, %g}. " - "The position is located in an enclosure comprising several media.\n", - FUNC_NAME, P[0], P[1], DIM == 3 ? P[2] : 0); - res = RES_BAD_OP; - goto error; - } - - /* Register the get_medium_info */ - if(info) { - fX(set)(info->pos_tgt, attr.value); - fX(set)(info->ray_org, P); - fX(set)(info->ray_dir, dir); - info->XD(hit) = hit; - } - break; + break; /* That's all folks */ } } if(iprim >= nprims) { - log_warn(scn->dev, "%s: could not retrieve the medium at {%g, %g, %g}.\n", + log_warn(scn->dev, + "%s: cannot retrieve current enclosure at {%g, %g, %g}.\n", FUNC_NAME, P[0], P[1], DIM == 3 ? P[2] : 0); res = RES_BAD_OP; goto error; @@ -1217,31 +1284,32 @@ XD(scene_get_medium) if(iprim > 10 && iprim > (size_t)((double)nprims * 0.05)) { log_warn(scn->dev, - "%s: performance issue. Up to %lu primitives were tested to define the " - "current medium at {%g, %g, %g}.\n", + "%s: performance issue. Up to %lu primitives were tested to find " + "current enclosure at {%g, %g, %g}.\n", FUNC_NAME, (unsigned long)iprim, P[0], P[1], DIM == 3 ? P[2] : 0); } exit: - *out_medium = medium; + *out_enc_id = enc_id; return res; error: + enc_id = ENCLOSURE_ID_NULL; goto exit; } -static INLINE res_T -XD(scene_get_medium_in_closed_boundaries) +static res_T +XD(scene_get_enclosure_id_in_closed_boundaries) (struct sdis_scene* scn, const double pos[DIM], - struct sdis_medium** out_medium) + unsigned* out_enc_id) { - struct sdis_medium* medium = NULL; - float P[DIM]; - float frame[DIM*DIM]; float dirs[6][3] = {{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}}; + float frame[DIM*DIM]; + float P[DIM]; + unsigned enc_id = ENCLOSURE_ID_NULL; int idir; res_T res = RES_OK; - ASSERT(scn && pos); + ASSERT(scn && pos && out_enc_id); /* Build a frame that will be used to rotate the main axis by PI/4 around * each axis. This can avoid numerical issues when geometry is discretized @@ -1249,8 +1317,6 @@ XD(scene_get_medium_in_closed_boundaries) #if DIM == 2 f22_rotation(frame, (float)PI/4); #else -/* N[0] = N[1] = N[2] = (float)(1.0 / sqrt(3.0));*/ -/* f33_basis(frame, N);*/ f33_rotation(frame, (float)PI/4, (float)PI/4, (float)PI/4); #endif @@ -1279,22 +1345,23 @@ XD(scene_get_medium_in_closed_boundaries) /* Not too close and not roughly orthogonal */ if(hit.distance > 1.e-6 && absf(cos_N_dir) > 1.e-2f) { - const struct sdis_interface* interf; - interf = scene_get_interface(scn, hit.prim.prim_id); - medium = interface_get_medium - (interf, cos_N_dir < 0 ? SDIS_FRONT : SDIS_BACK); - break; + unsigned enc_ids[2]; + scene_get_enclosure_ids(scn, hit.prim.prim_id, enc_ids); + enc_id = cos_N_dir < 0 ? enc_ids[0] : enc_ids[1]; + + break; /* That's all folks */ } } - if(idir >= 2*DIM) { - res = XD(scene_get_medium)(scn, pos, NULL, &medium); + if(idir >= 2*DIM) { /* Fallback to scene_get_enclosure_id function */ + res = XD(scene_get_enclosure_id)(scn, pos, &enc_id); if(res != RES_OK) goto error; } exit: - *out_medium = medium; + *out_enc_id = enc_id; return res; error: + enc_id = ENCLOSURE_ID_NULL; goto exit; } diff --git a/src/sdis_scene_c.h b/src/sdis_scene_c.h @@ -26,6 +26,9 @@ #include <limits.h> +#define MEDIUM_ID_MULTI UINT_MAX +#define ENCLOSURE_ID_NULL UINT_MAX + struct prim_prop { struct sdis_interface* interf; unsigned front_enclosure; /* Id of the front facing enclosure */ @@ -48,21 +51,6 @@ struct hit_filter_data { static const struct hit_filter_data HIT_FILTER_DATA_NULL = HIT_FILTER_DATA_NULL__; -struct get_medium_info { - /* Targeted position */ - float pos_tgt[3]; - /* Ray trace to the targeted position in order to define the current medium */ - float ray_org[3]; - float ray_dir[3]; - /* Hit encouters along the ray and used to define the current medium */ - 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) { @@ -86,8 +74,6 @@ medium_init(struct mem_allocator* allocator, struct sdis_medium** medium) *medium = NULL; } -#define ENCLOSURE_MULTI_MEDIA UINT_MAX - struct enclosure { struct s2d_scene_view* s2d_view; struct s3d_scene_view* s3d_view; @@ -112,7 +98,7 @@ enclosure_init(struct mem_allocator* allocator, struct enclosure* enc) enc->S_over_V = 0; enc->V = 0; enc->hc_upper_bound = 0; - enc->medium_id = ENCLOSURE_MULTI_MEDIA; + enc->medium_id = MEDIUM_ID_MULTI; } static INLINE void @@ -173,6 +159,16 @@ enclosure_local2global_prim_id return darray_uint_cdata_get(&enc->local2global)[local_prim_id]; } +static INLINE void +primkey_init + (const struct mem_allocator* allocator, + struct sdis_primkey* key) +{ + ASSERT(allocator && key); + (void)allocator; + *key = SDIS_PRIMKEY_NULL; +} + /* Declare the array of interfaces */ #define DARRAY_NAME interf #define DARRAY_DATA struct sdis_interface* @@ -207,6 +203,24 @@ enclosure_local2global_prim_id #define HTABLE_DATA double #include <rsys/hash_table.h> +/* Declare the hash table that maps the primitive key to its 2D primitve */ +#define HTABLE_NAME key2prim2d +#define HTABLE_KEY struct sdis_primkey +#define HTABLE_KEY_FUNCTOR_INIT primkey_init +#define HTABLE_KEY_FUNCTOR_HASH sdis_primkey_hash +#define HTABLE_KEY_FUNCTOR_EQ sdis_primkey_eq +#define HTABLE_DATA struct s2d_primitive +#include <rsys/hash_table.h> + +/* Declare the hash table that maps the primitive key to its 3D primitive */ +#define HTABLE_NAME key2prim3d +#define HTABLE_KEY struct sdis_primkey +#define HTABLE_KEY_FUNCTOR_INIT primkey_init +#define HTABLE_KEY_FUNCTOR_HASH sdis_primkey_hash +#define HTABLE_KEY_FUNCTOR_EQ sdis_primkey_eq +#define HTABLE_DATA struct s3d_primitive +#include <rsys/hash_table.h> + struct sdis_scene { struct darray_interf interfaces; /* List of interfaces own by the scene */ struct darray_medium media; /* List of media own by the scene */ @@ -220,6 +234,10 @@ struct sdis_scene { struct htable_enclosure enclosures; /* Map an enclosure id to its data */ unsigned outer_enclosure_id; + /* Map a primivei key to its Star-2D/Star-3D primitive */ + struct htable_key2prim2d key2prim2d; + struct htable_key2prim3d key2prim3d; + double fp_to_meter; double tmin; /* Minimum temperature of the system (In Kelvin) */ double tmax; /* Maximum temperature of the system (In Kelvin) */ @@ -244,26 +262,31 @@ scene_get_interface const unsigned iprim); extern LOCAL_SYM res_T -scene_get_medium +scene_get_enclosure_id (struct sdis_scene* scene, const double position[], - struct get_medium_info* info, /* May be NULL */ - struct sdis_medium** medium); + unsigned* enclosure_id); -/* This function assumes that the tested position lies into finite enclosure. - * The medium into which it lies is thus retrieved by tracing a random ray - * around the current position. For possible infinite enclosure, one has to use - * the `scene_get_medium' function instead that, in counterpart, can be more - * time consuming. +/* This function assumes that the position under test lies within a finite + * enclosure. The enclosure in which it is located is therefore retrieved by + * tracing a random ray around the current position. For infinite enclosures, + * you need to use the `scene_get_enclosure_id' function, which in turn may take + * longer. * - * Note that actually, the function internally calls scene_get_medium if no - * valid medium is found with the regular procedure. This may be due to - * numerical issues or wrong assumptions on the current medium (its boundaries - * are opened to infinity). */ + * Note that the function actually calls scene_get_enclosure internally if no + * valid enclosure is found with the normal procedure. This may be due to + * numerical problems or incorrect assumptions about the current enclosure (its + * limits are open to infinity). */ extern LOCAL_SYM res_T -scene_get_medium_in_closed_boundaries - (struct sdis_scene* scn, +scene_get_enclosure_id_in_closed_boundaries + (struct sdis_scene* scene, const double position[], + unsigned* enclosure_id); + +extern LOCAL_SYM res_T +scene_get_enclosure_medium + (struct sdis_scene* scene, + const struct enclosure* enclosure, struct sdis_medium** medium); extern LOCAL_SYM res_T diff --git a/src/sdis_solve_camera.c b/src/sdis_solve_camera.c @@ -84,7 +84,7 @@ static res_T solve_pixel (struct sdis_scene* scn, struct ssp_rng* rng, - struct sdis_medium* mdm, + const unsigned enc_id, const struct sdis_camera* cam, const double time_range[2], /* Observation time */ const size_t ipix[2], /* Pixel coordinate in the image space */ @@ -99,7 +99,7 @@ solve_pixel struct sdis_heat_path* pheat_path = NULL; size_t irealisation; res_T res = RES_OK; - ASSERT(scn && mdm && rng && cam && ipix && nrealisations); + ASSERT(scn && rng && cam && ipix && nrealisations); ASSERT(pix_sz && pix_sz[0] > 0 && pix_sz[1] > 0); ASSERT(pixel && time_range); @@ -136,7 +136,7 @@ solve_pixel /* Launch the realisation */ realis_args.rng = rng; - realis_args.medium = mdm; + realis_args.enc_id = enc_id; realis_args.time = time; realis_args.picard_order = picard_order; realis_args.heat_path = pheat_path; @@ -195,7 +195,7 @@ static res_T solve_tile (struct sdis_scene* scn, struct ssp_rng* rng, - struct sdis_medium* mdm, + const unsigned enc_id, const struct sdis_camera* cam, const double time_range[2], const size_t tile_org[2], /* Origin of the tile in pixel space */ @@ -211,7 +211,7 @@ solve_tile size_t mcode; /* Morton code of the tile pixel */ size_t npixels; res_T res = RES_OK; - ASSERT(scn && rng && mdm && cam && spp); + ASSERT(scn && rng && cam && spp); ASSERT(tile_size && tile_size[0] && tile_size[1]); ASSERT(pix_sz && pix_sz[0] > 0 && pix_sz[1] > 0 && time_range); @@ -241,8 +241,8 @@ solve_tile estimator = estimator_buffer_grab(buf, ipix_image[0], ipix_image[1]); } res = solve_pixel - (scn, rng, mdm, cam, time_range, ipix_image, spp, register_paths, pix_sz, - picard_order, diff_algo, estimator, pixel); + (scn, rng, enc_id, cam, time_range, ipix_image, spp, register_paths, + pix_sz, picard_order, diff_algo, estimator, pixel); if(res != RES_OK) goto error; } @@ -504,12 +504,14 @@ sdis_solve_camera /* Stardis variables */ struct sdis_estimator_buffer* buf = NULL; - struct sdis_medium* medium = NULL; /* Random number generators */ struct ssp_rng_proxy* rng_proxy = NULL; struct ssp_rng** per_thread_rng = NULL; + /* Enclosure & medium in which the probe lies */ + unsigned enc_id = ENCLOSURE_ID_NULL; + /* Miscellaneous */ size_t ntiles_x, ntiles_y, ntiles, ntiles_adjusted; size_t ntiles_proc; /* #tiles for the current proc */ @@ -537,17 +539,9 @@ sdis_solve_camera } /* Retrieve the medium in which the submitted position lies */ - res = scene_get_medium(scn, args->cam->position, NULL, &medium); + res = scene_get_enclosure_id(scn, args->cam->position, &enc_id); if(res != RES_OK) goto error; - if(medium->type != SDIS_FLUID) { - log_err(scn->dev, - "%s: the camera position `%g %g %g' must be in a fluid medium.\n", - FUNC_NAME, SPLIT3(args->cam->position)); - res = RES_BAD_ARG; - goto error; - } - /* Create the per thread RNGs */ res = create_per_thread_rng (scn->dev, args->rng_state, args->rng_type, &rng_proxy, &per_thread_rng); @@ -658,7 +652,7 @@ sdis_solve_camera /* Draw the tile */ res_local = solve_tile - (scn, rng, medium, args->cam, args->time_range, tile_org, tile_sz, + (scn, rng, enc_id, args->cam, args->time_range, tile_org, tile_sz, args->spp, register_paths, pix_sz, args->picard_order, args->diff_algo, buf, tile); if(res_local != RES_OK) { diff --git a/src/sdis_solve_medium_Xd.h b/src/sdis_solve_medium_Xd.h @@ -39,7 +39,7 @@ */ struct enclosure_cumul { - const struct enclosure* enc; + unsigned enc_id; double cumul; }; @@ -79,12 +79,13 @@ compute_medium_enclosure_cumulative while(!htable_enclosure_iterator_eq(&it, &end)) { struct enclosure_cumul enc_cumul; const struct enclosure* enc = htable_enclosure_iterator_data_get(&it); + const unsigned* enc_id = htable_enclosure_iterator_key_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.enc_id = *enc_id; enc_cumul.cumul = accum; res = darray_enclosure_cumul_push_back(cumul, &enc_cumul); if(res != RES_OK) goto error; @@ -105,7 +106,7 @@ error: goto exit; } -static const struct enclosure* +static unsigned sample_medium_enclosure (const struct darray_enclosure_cumul* cumul, struct ssp_rng* rng) { @@ -137,7 +138,7 @@ sample_medium_enclosure enc_cumul_found = enc_cumuls + i; } - return enc_cumul_found->enc; + return enc_cumul_found->enc_id; } static INLINE res_T @@ -217,17 +218,22 @@ check_compute_power_args(const struct sdis_compute_power_args* args) ******************************************************************************/ static res_T XD(sample_enclosure_position) - (const struct enclosure* enc, + (struct sdis_scene* scn, + const unsigned enc_id, struct ssp_rng* rng, double pos[DIM]) { const size_t MAX_NCHALLENGES = 1000; + + const struct enclosure* enc = NULL; float lower[DIM], upper[DIM]; size_t ichallenge; size_t i; res_T res = RES_OK; - ASSERT(enc && rng && pos); + ASSERT(scn && rng && pos); + ASSERT(enc_id != ENCLOSURE_ID_NULL); + enc = scene_get_enclosure(scn, enc_id); SXD(scene_view_get_aabb(enc->sXd(view), lower, upper)); FOR_EACH(i, 0, DIM) { @@ -396,13 +402,13 @@ XD(solve_medium) struct accum* acc_time = &per_thread_acc_time[ithread]; struct green_path_handle* pgreen_path = NULL; struct green_path_handle green_path = GREEN_PATH_HANDLE_NULL; - const struct enclosure* enc = NULL; struct sdis_heat_path* pheat_path = NULL; struct sdis_heat_path heat_path; double weight; double time; double pos[DIM]; size_t n; + unsigned enc_id = ENCLOSURE_ID_NULL; int pcent; res_T res_local = RES_OK; res_T res_simul = RES_OK; @@ -425,8 +431,8 @@ XD(solve_medium) /* 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); + enc_id = sample_medium_enclosure(&cumul, rng); + res_local = XD(sample_enclosure_position)(scn, enc_id, 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); @@ -435,7 +441,7 @@ XD(solve_medium) /* Run a probe realisation */ realis_args.rng = rng; - realis_args.medium = args->medium; + realis_args.enc_id = enc_id; realis_args.time = time; realis_args.picard_order = args->picard_order; realis_args.green_path = pgreen_path; @@ -685,10 +691,10 @@ XD(compute_power) struct ssp_rng* rng = per_thread_rng[ithread]; struct accum* acc_mpow = &per_thread_acc_mpow[ithread]; struct accum* acc_time = &per_thread_acc_time[ithread]; - const struct enclosure* enc = NULL; double power = 0; double usec = 0; size_t n = 0; + unsigned enc_id = ENCLOSURE_ID_NULL; int pcent = 0; res_T res_local = RES_OK; @@ -702,8 +708,8 @@ XD(compute_power) /* 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, vtx.P); + enc_id = sample_medium_enclosure(&cumul, rng); + res_local = XD(sample_enclosure_position)(scn, enc_id, rng, vtx.P); if(res_local != RES_OK) { log_err(scn->dev, "%s: could not sample a medium position.\n", FUNC_NAME); ATOMIC_SET(&res, res_local); diff --git a/src/sdis_solve_probe_Xd.h b/src/sdis_solve_probe_Xd.h @@ -116,7 +116,9 @@ XD(solve_one_probe) struct accum* acc_temp, struct accum* acc_time) { - struct sdis_medium* medium = NULL; /* Medium in which the probe lies */ + /* Enclosure in which the probe lies */ + unsigned enc_id = ENCLOSURE_ID_NULL; + size_t irealisation = 0; res_T res = RES_OK; ASSERT(scn && rng && check_solve_probe_args(args) == RES_OK); @@ -126,7 +128,7 @@ XD(solve_one_probe) *acc_time = ACCUM_NULL; /* Retrieve the medium in which the submitted position lies */ - res = scene_get_medium(scn, args->position, NULL, &medium); + res = scene_get_enclosure_id(scn, args->position, &enc_id); if(res != RES_OK) goto error; FOR_EACH(irealisation, 0, args->nrealisations) { @@ -144,26 +146,38 @@ XD(solve_one_probe) /* Run a realisation */ realis_args.rng = rng; - realis_args.medium = medium; + realis_args.enc_id = enc_id; realis_args.time = time; realis_args.picard_order = args->picard_order; realis_args.irealisation = irealisation; realis_args.diff_algo = args->diff_algo; dX(set)(realis_args.position, args->position); res = XD(probe_realisation)(scn, &realis_args, &w); - if(res != RES_OK) goto error; - - /* Stop time registration */ - time_sub(&t0, time_current(&t1), &t0); - usec = (double)time_val(&t0, TIME_NSEC) * 0.001; - - /* Update MC weights */ - acc_temp->sum += w; - acc_temp->sum2 += w*w; - acc_temp->count += 1; - acc_time->sum += usec; - acc_time->sum2 += usec*usec; - acc_time->count += 1; + if(res != RES_OK && res != RES_BAD_OP) goto error; + + switch(res) { + /* Reject the realisation */ + case RES_BAD_OP: + res = RES_OK; + break; + + /* Update the accumulators */ + case RES_OK: + /* Stop time registration */ + time_sub(&t0, time_current(&t1), &t0); + usec = (double)time_val(&t0, TIME_NSEC) * 0.001; + + /* Update MC weights */ + acc_temp->sum += w; + acc_temp->sum2 += w*w; + acc_temp->count += 1; + acc_time->sum += usec; + acc_time->sum2 += usec*usec; + acc_time->count += 1; + break; + + default: FATAL("Unreachable code\n"); break; + } } exit: @@ -191,7 +205,6 @@ XD(solve_probe) size_t nthreads = 0; /* Stardis variables */ - struct sdis_medium* medium = NULL; struct sdis_estimator* estimator = NULL; struct sdis_green_function* green = NULL; struct sdis_green_function** per_thread_green = NULL; @@ -200,6 +213,10 @@ XD(solve_probe) struct ssp_rng_proxy* rng_proxy = NULL; struct ssp_rng** per_thread_rng = NULL; + /* Enclosure in which the probe lies */ + const struct enclosure* enc = NULL; + unsigned enc_id = ENCLOSURE_ID_NULL; + /* Miscellaneous */ struct accum* per_thread_acc_temp = NULL; struct accum* per_thread_acc_time = NULL; @@ -255,10 +272,19 @@ XD(solve_probe) if(!per_thread_acc_temp) { res = RES_MEM_ERR; goto error; } if(!per_thread_acc_time) { res = RES_MEM_ERR; goto error; } - /* Retrieve the medium in which the submitted position lies */ - res = scene_get_medium(scn, args->position, NULL, &medium); + /* Retrieve the enclosure in which the submitted position lies */ + res = scene_get_enclosure_id(scn, args->position, &enc_id); if(res != RES_OK) goto error; + /* Check that the enclosure does not contain multiple materials */ + enc = scene_get_enclosure(scn, enc_id); + if(enc->medium_id == MEDIUM_ID_MULTI) { + log_err(scn->dev, "%s: probe is in an enclosure with several media " + "-- pos=("FORMAT_VECX")\n", FUNC_NAME, SPLITX(args->position)); + res = RES_BAD_OP; + goto error; + } + /* Create the per thread green function */ if(out_green) { res = create_per_thread_green_function @@ -327,7 +353,7 @@ XD(solve_probe) /* Invoke the probe realisation */ realis_args.rng = rng; - realis_args.medium = medium; + realis_args.enc_id = enc_id; realis_args.time = time; realis_args.picard_order = args->picard_order; realis_args.green_path = pgreen_path; diff --git a/src/sdis_solve_probe_boundary_Xd.h b/src/sdis_solve_probe_boundary_Xd.h @@ -195,20 +195,33 @@ XD(solve_one_probe_boundary) realis_args.uv[1] = args->uv[1]; #endif res = XD(boundary_realisation)(scn, &realis_args, &w); - if(res != RES_OK) goto error; - - /* Stop time registration */ - time_sub(&t0, time_current(&t1), &t0); - usec = (double)time_val(&t0, TIME_NSEC) * 0.001; - - /* Update MC weights */ - acc_temp->sum += w; - acc_temp->sum2 += w*w; - acc_temp->count += 1; - acc_time->sum += usec; - acc_time->sum2 += usec*usec; - acc_time->count += 1; + if(res != RES_OK && res != RES_BAD_OP) goto error; + + switch(res) { + /* Reject the realisation */ + case RES_BAD_OP: + res = RES_OK; + break; + + /* Update the accumulators */ + case RES_OK: + /* Stop time registration */ + time_sub(&t0, time_current(&t1), &t0); + usec = (double)time_val(&t0, TIME_NSEC) * 0.001; + + /* Update MC weights */ + acc_temp->sum += w; + acc_temp->sum2 += w*w; + acc_temp->count += 1; + acc_time->sum += usec; + acc_time->sum2 += usec*usec; + acc_time->count += 1; + break; + + default: FATAL("Unreachable code\n"); break; + } } + exit: return res; error: @@ -613,6 +626,7 @@ XD(solve_probe_boundary_list) time_current(&time0); /* Calculation of probe list */ + omp_set_num_threads((int)scn->dev->nthreads); #pragma omp parallel for schedule(static) for(i = 0; i < (int64_t)process_nprobes; ++i) { /* Thread */ diff --git a/src/test_sdis_custom_solid_path_sampling.c b/src/test_sdis_custom_solid_path_sampling.c @@ -0,0 +1,580 @@ +/* Copyright (C) 2016-2024 |Méso|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_mesh.h" +#include "test_sdis_utils.h" + +#include <star/s3d.h> +#include <star/s3dut.h> +#include <star/ssp.h> + +#include <rsys/double3.h> + +/* + * The system is a trilinear profile of the temperature at steady state, i.e. at + * each point of the system we can calculate the temperature analytically. Two + * forms are immersed in this temperature field: a super shape and a sphere + * included in the super shape. On the Monte Carlo side, the temperature is + * unknown everywhere except on the surface of the super shape whose + * temperature is defined from the aformentionned trilinear profile. + * + * We will estimate the temperature at the position of a probe in solids by + * providing a user-side function to sample the conductive path in the sphere. + * We should find the temperature of the trilinear profile at the probe position + * by Monte Carlo, independently of this coupling with an external path sampling + * routine. + * + * + * /\ <-- T(x,y,z) + * ___/ \___ + * T(z) \ __ / + * | T(y) T=?/. \ / + * |/ / \__/ \ + * o--- T(x) /_ __ _\ + * \/ \/ + */ + +#define NREALISATIONS 10000 +#define SPHERE_RADIUS 1 + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static double +trilinear_profile(const double pos[3]) +{ + /* Range in X, Y and Z in which the trilinear profile is defined */ + const double lower = -4; + const double upper = +4; + + /* Upper temperature limit in X, Y and Z [K]. Lower temperature limit is + * implicitly 0 */ + const double a = 333; /* Upper temperature limit in X [K] */ + const double b = 432; /* Upper temperature limit in Y [K] */ + const double c = 579; /* Upper temperature limit in Z [K] */ + + double x, y, z; + + /* Check pre-conditions */ + CHK(pos); + CHK(lower <= pos[0] && pos[0] <= upper); + CHK(lower <= pos[1] && pos[1] <= upper); + CHK(lower <= pos[2] && pos[2] <= upper); + + x = (pos[0] - lower) / (upper - lower); + y = (pos[1] - lower) / (upper - lower); + z = (pos[2] - lower) / (upper - lower); + return a*x + b*y + c*z; +} + +/******************************************************************************* + * Scene view + ******************************************************************************/ +/* Parameter for get_inidices/get_vertices functions. Although its layout is the + * same as that of the mesh data structure, we have deliberately defined a new + * data type since mesh functions cannot be invoked. This is because the form's + * member variables are not necessarily extensible arrays, as is the case with + * mesh */ +struct shape { + double* pos; + size_t* ids; + size_t npos; + size_t ntri; +}; +#define SHAPE_NULL__ {NULL, NULL, 0, 0} +static const struct shape SHAPE_NULL = SHAPE_NULL__; + +static void +get_position(const unsigned ivert, float pos[3], void* ctx) +{ + const struct shape* shape = ctx; + CHK(shape && pos && ivert < shape->npos); + f3_set_d3(pos, shape->pos + ivert*3); +} + +static void +get_indices(const unsigned itri, unsigned ids[3], void* ctx) +{ + const struct shape* shape = ctx; + CHK(shape && ids && itri < shape->ntri); + ids[0] = (unsigned)shape->ids[itri*3+0]; + ids[1] = (unsigned)shape->ids[itri*3+1]; + ids[2] = (unsigned)shape->ids[itri*3+2]; +} + +static struct s3d_scene_view* +create_view(struct shape* shape_data) +{ + /* Star3D */ + struct s3d_vertex_data vdata = S3D_VERTEX_DATA_NULL; + struct s3d_device* dev = NULL; + struct s3d_scene* scn = NULL; + struct s3d_shape* shape = NULL; + struct s3d_scene_view* view = NULL; + + OK(s3d_device_create(NULL, NULL, 0, &dev)); + + /* Shape */ + vdata.usage = S3D_POSITION; + vdata.type = S3D_FLOAT3; + vdata.get = get_position; + OK(s3d_shape_create_mesh(dev, &shape)); + OK(s3d_mesh_setup_indexed_vertices(shape, (unsigned)shape_data->ntri, + get_indices, (unsigned)shape_data->npos, &vdata, 1, shape_data)); + + /* Scene view */ + OK(s3d_scene_create(dev, &scn)); + OK(s3d_scene_attach_shape(scn, shape)); + OK(s3d_scene_view_create(scn, S3D_TRACE|S3D_GET_PRIMITIVE, &view)); + + /* Clean up */ + OK(s3d_device_ref_put(dev)); + OK(s3d_scene_ref_put(scn)); + OK(s3d_shape_ref_put(shape)); + + return view; +} + +/******************************************************************************* + * Mesh, i.e. supershape and sphere + ******************************************************************************/ +static void +mesh_add_super_shape(struct mesh* mesh) +{ + struct s3dut_mesh* sshape = NULL; + struct s3dut_mesh_data sshape_data; + struct s3dut_super_formula f0 = S3DUT_SUPER_FORMULA_NULL; + struct s3dut_super_formula f1 = S3DUT_SUPER_FORMULA_NULL; + const double radius = 2; + const unsigned nslices = 256; + + f0.A = 1.5; f0.B = 1; f0.M = 11.0; f0.N0 = 1; f0.N1 = 1; f0.N2 = 2.0; + f1.A = 1.0; f1.B = 2; f1.M = 3.6; f1.N0 = 1; f1.N1 = 2; f1.N2 = 0.7; + OK(s3dut_create_super_shape(NULL, &f0, &f1, radius, nslices, nslices/2, &sshape)); + OK(s3dut_mesh_get_data(sshape, &sshape_data)); + mesh_append(mesh, sshape_data.positions, sshape_data.nvertices, + sshape_data.indices, sshape_data.nprimitives, NULL); + OK(s3dut_mesh_ref_put(sshape)); +} + +static void +mesh_add_sphere(struct mesh* mesh) +{ + struct s3dut_mesh* sphere = NULL; + struct s3dut_mesh_data sphere_data; + const double radius = SPHERE_RADIUS; + const unsigned nslices = 128; + + OK(s3dut_create_sphere(NULL, radius, nslices, nslices/2, &sphere)); + OK(s3dut_mesh_get_data(sphere, &sphere_data)); + mesh_append(mesh, sphere_data.positions, sphere_data.nvertices, + sphere_data.indices, sphere_data.nprimitives, NULL); + OK(s3dut_mesh_ref_put(sphere)); +} + +/******************************************************************************* + * Custom conductive path + ******************************************************************************/ +struct custom_solid { + struct s3d_scene_view* view; /* Star-3D view of the shape */ + const struct shape* shape; /* Raw data */ +}; + +static void +setup_solver_primitive + (struct sdis_scene* scn, + const struct shape* shape, + const struct s3d_hit* user_hit, + struct s3d_primitive* prim) +{ + struct sdis_primkey key = SDIS_PRIMKEY_NULL; + const double *v0, *v1, *v2; + float v0f[3], v1f[3], v2f[3]; + struct s3d_attrib attr0, attr1, attr2; + + v0 = shape->pos + shape->ids[user_hit->prim.prim_id*3+0]*3; + v1 = shape->pos + shape->ids[user_hit->prim.prim_id*3+1]*3; + v2 = shape->pos + shape->ids[user_hit->prim.prim_id*3+2]*3; + sdis_primkey_setup(&key, v0, v1, v2); + OK(sdis_scene_get_s3d_primitive(scn, &key, prim)); + + /* Check that the primitive on the solver side is the same as that on the + * user side. On the solver side, vertices are stored in simple precision in + * Star-3D view. We therefore need to take care of this conversion to check + * that the vertices are the same */ + OK(s3d_triangle_get_vertex_attrib(prim, 0, S3D_POSITION, &attr0)); + OK(s3d_triangle_get_vertex_attrib(prim, 1, S3D_POSITION, &attr1)); + OK(s3d_triangle_get_vertex_attrib(prim, 2, S3D_POSITION, &attr2)); + f3_set_d3(v0f, v0); + f3_set_d3(v1f, v1); + f3_set_d3(v2f, v2); + + /* The vertices have been inverted on the user's side to reverse the normal + * orientation. Below it is taken into account */ + CHK(f3_eq(v0f, attr0.value)); + CHK(f3_eq(v1f, attr2.value)); + CHK(f3_eq(v2f, attr1.value)); +} + +/* Implementation of a Walk on Sphere algorithm for an origin-centered spherical + * geometry of radius SPHERE_RADIUS */ +static res_T +sample_steady_diffusive_path + (struct sdis_scene* scn, + struct ssp_rng* rng, + struct sdis_path* path, + struct sdis_data* data) +{ + struct custom_solid* solid = NULL; + struct s3d_hit hit = S3D_HIT_NULL; + + double pos[3]; + const double epsilon = SPHERE_RADIUS * 1.e-6; /* Epsilon shell */ + + CHK(scn && rng && path && data); + + solid = sdis_data_get(data); + + d3_set(pos, path->vtx.P); + + do { + /* Distance from the geometry center to the current position */ + const double dst = d3_len(pos); + + double dir[3] = {0,0,0}; + double r = 0; /* Radius */ + + r = SPHERE_RADIUS - dst; + CHK(dst > 0); + + if(r > epsilon) { + /* Uniformly sample a new position on the surrounding sphere */ + ssp_ran_sphere_uniform(rng, dir, NULL); + + /* Move to the new position */ + d3_muld(dir, dir, r); + d3_add(pos, pos, dir); + + /* The current position is in the epsilon shell: + * move it to the nearest interface position */ + } else { + float posf[3]; + + d3_set(dir, pos); + d3_normalize(dir, dir); + d3_muld(pos, dir, SPHERE_RADIUS); + + /* Map the position to the sphere geometry */ + f3_set_d3(posf, pos); + OK(s3d_scene_view_closest_point(solid->view, posf, (float)INF, NULL, &hit)); + } + + /* The calculation is performed in steady state, so the path necessarily stops + * at a boundary */ + } while(S3D_HIT_NONE(&hit)); + + /* Setup the path state */ + d3_set(path->vtx.P, pos); + path->weight = 0; + path->at_limit = 0; + path->prim_2d = S2D_PRIMITIVE_NULL; + setup_solver_primitive(scn, solid->shape, &hit, &path->prim_3d); + + return RES_OK; +} + +/******************************************************************************* + * The solids, i.e. media of the super shape and the sphere + ******************************************************************************/ +#define SOLID_PROP(Prop, Val) \ + static double \ + solid_get_##Prop \ + (const struct sdis_rwalk_vertex* vtx, \ + struct sdis_data* data) \ + { \ + (void)vtx, (void)data; /* Avoid the "unused variable" warning */ \ + return Val; \ + } +SOLID_PROP(calorific_capacity, 500.0) /* [J/K/Kg] */ +SOLID_PROP(thermal_conductivity, 25.0) /* [W/m/K] */ +SOLID_PROP(volumic_mass, 7500.0) /* [kg/m^3] */ +SOLID_PROP(temperature, SDIS_TEMPERATURE_NONE/*<=> unknown*/) /* [K] */ +SOLID_PROP(delta, 1.0/40.0) /* [m] */ + +static struct sdis_medium* +create_solid(struct sdis_device* sdis) +{ + struct sdis_solid_shader shader = SDIS_SOLID_SHADER_NULL; + struct sdis_medium* solid = NULL; + + shader.calorific_capacity = solid_get_calorific_capacity; + shader.thermal_conductivity = solid_get_thermal_conductivity; + shader.volumic_mass = solid_get_volumic_mass; + shader.delta = solid_get_delta; + shader.temperature = solid_get_temperature; + + OK(sdis_solid_create(sdis, &shader, NULL, &solid)); + return solid; +} + +static struct sdis_medium* +create_custom + (struct sdis_device* sdis, + struct s3d_scene_view* view, + const struct shape* shape) +{ + /* Stardis variables */ + struct sdis_solid_shader shader = SDIS_SOLID_SHADER_NULL; + struct sdis_medium* solid = NULL; + struct sdis_data* data = NULL; + + /* Mesh variables */ + struct custom_solid* custom_solid = NULL; + const size_t sz = sizeof(struct custom_solid); + const size_t al = ALIGNOF(struct custom_solid); + + OK(sdis_data_create(sdis, sz, al, NULL, &data)); + custom_solid = sdis_data_get(data); + custom_solid->view = view; + custom_solid->shape = shape; + + shader.calorific_capacity = solid_get_calorific_capacity; + shader.thermal_conductivity = solid_get_thermal_conductivity; + shader.volumic_mass = solid_get_volumic_mass; + shader.delta = solid_get_delta; + shader.temperature = solid_get_temperature; + shader.sample_path = sample_steady_diffusive_path; + + OK(sdis_solid_create(sdis, &shader, data, &solid)); + OK(sdis_data_ref_put(data)); + + return solid; +} + +/******************************************************************************* + * Dummy environment, i.e. environment surrounding the super shape. It is + * defined only for Stardis compliance: in Stardis, an interface must divide 2 + * media. + ******************************************************************************/ +static struct sdis_medium* +create_dummy(struct sdis_device* sdis) +{ + struct sdis_fluid_shader shader = SDIS_FLUID_SHADER_NULL; + struct sdis_medium* dummy = NULL; + + shader.calorific_capacity = dummy_medium_getter; + shader.volumic_mass = dummy_medium_getter; + shader.temperature = dummy_medium_getter; + OK(sdis_fluid_create(sdis, &shader, NULL, &dummy)); + return dummy; +} + +/******************************************************************************* + * Interface: its temperature is fixed to the trilinear profile + ******************************************************************************/ +static double +interface_get_temperature + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + (void)data; /* Avoid the "unused variable" warning */ + return trilinear_profile(frag->P); +} + +static struct sdis_interface* +create_interface + (struct sdis_device* sdis, + struct sdis_medium* front, + struct sdis_medium* back) +{ + struct sdis_interface* interf = NULL; + struct sdis_interface_shader shader = SDIS_INTERFACE_SHADER_NULL; + + /* Fluid/solid interface: fix the temperature to the temperature profile */ + if(sdis_medium_get_type(front) != sdis_medium_get_type(back)) { + shader.front.temperature = interface_get_temperature; + shader.back.temperature = interface_get_temperature; + } + + OK(sdis_interface_create(sdis, front, back, &shader, NULL, &interf)); + return interf; +} + +/******************************************************************************* + * Scene, i.e. the system to simulate + ******************************************************************************/ +struct scene_context { + const struct mesh* mesh; + size_t sshape_end_id; /* Last triangle index of the super shape */ + struct sdis_interface* sshape; + struct sdis_interface* sphere; +}; +#define SCENE_CONTEXT_NULL__ {NULL, 0, 0, 0} +static const struct scene_context SCENE_CONTEXT_NULL = SCENE_CONTEXT_NULL__; + +static void +scene_get_indices(const size_t itri, size_t ids[3], void* ctx) +{ + struct scene_context* context = ctx; + CHK(ids && context && itri < mesh_ntriangles(context->mesh)); + /* Flip the indices to ensure that the normal points into the super shape */ + ids[0] = (unsigned)context->mesh->indices[itri*3+0]; + ids[1] = (unsigned)context->mesh->indices[itri*3+2]; + ids[2] = (unsigned)context->mesh->indices[itri*3+1]; +} + +static void +scene_get_interface(const size_t itri, struct sdis_interface** interf, void* ctx) +{ + struct scene_context* context = ctx; + CHK(interf && context && itri < mesh_ntriangles(context->mesh)); + if(itri < context->sshape_end_id) { + *interf = context->sshape; + } else { + *interf = context->sphere; + } +} + +static void +scene_get_position(const size_t ivert, double pos[3], void* ctx) +{ + struct scene_context* context = ctx; + CHK(pos && context && ivert < mesh_nvertices(context->mesh)); + pos[0] = context->mesh->positions[ivert*3+0]; + pos[1] = context->mesh->positions[ivert*3+1]; + pos[2] = context->mesh->positions[ivert*3+2]; +} + +static struct sdis_scene* +create_scene(struct sdis_device* sdis, struct scene_context* ctx) +{ + struct sdis_scene* scn = NULL; + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + + scn_args.get_indices = scene_get_indices; + scn_args.get_interface = scene_get_interface; + scn_args.get_position = scene_get_position; + scn_args.nprimitives = mesh_ntriangles(ctx->mesh); + scn_args.nvertices = mesh_nvertices(ctx->mesh); + scn_args.context = ctx; + OK(sdis_scene_create(sdis, &scn_args, &scn)); + return scn; +} + +/******************************************************************************* + * Validations + ******************************************************************************/ +static void +check_probe(struct sdis_scene* scn, const int is_master_process) +{ + struct sdis_solve_probe_args args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_estimator* estimator = NULL; + double ref = 0; + + args.position[0] = SPHERE_RADIUS*0.125; + args.position[1] = SPHERE_RADIUS*0.250; + args.position[2] = SPHERE_RADIUS*0.375; + args.nrealisations = NREALISATIONS; + + OK(sdis_solve_probe(scn, &args, &estimator)); + + if(!is_master_process) return; + + OK(sdis_estimator_get_temperature(estimator, &T)); + + ref = trilinear_profile(args.position); + + printf("T(%g, %g, %g) = %g ~ %g +/- %g\n", + SPLIT3(args.position), ref, T.E, T.SE); + + CHK(eq_eps(ref, T.E, T.SE*3)); + OK(sdis_estimator_ref_put(estimator)); +} + +/******************************************************************************* + * The test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + /* Stardis */ + struct sdis_device* dev = NULL; + struct sdis_interface* solid_dummy = NULL; + struct sdis_interface* custom_solid = NULL; + struct sdis_medium* solid = NULL; + struct sdis_medium* custom = NULL; + struct sdis_medium* dummy = NULL; /* Medium surrounding the solid */ + struct sdis_scene* scn = NULL; + + /* Star3D */ + struct shape shape = SHAPE_NULL; + struct s3d_scene_view* sphere_view = NULL; + + /* Miscellaneous */ + struct scene_context ctx = SCENE_CONTEXT_NULL; + struct mesh mesh = MESH_NULL; + size_t sshape_end_id = 0; /* Last index of the super shape */ + int is_master_process = 1; + (void)argc, (void)argv; + + create_default_device(&argc, &argv, &is_master_process, &dev); + + /* Mesh */ + mesh_init(&mesh); + mesh_add_super_shape(&mesh); + sshape_end_id = mesh_ntriangles(&mesh); + mesh_add_sphere(&mesh); + + /* Create a view of the sphere's geometry. This will be used to couple custom + * solid path sampling to the solver */ + shape.pos = mesh.positions; + shape.ids = mesh.indices + sshape_end_id*3; + shape.npos = mesh_nvertices(&mesh); + shape.ntri = mesh_ntriangles(&mesh) - sshape_end_id/* #sshape triangles*/; + sphere_view = create_view(&shape); + + /* Physical properties */ + dummy = create_dummy(dev); + solid = create_solid(dev); + custom = create_custom(dev, sphere_view, &shape); + solid_dummy = create_interface(dev, solid, dummy); + custom_solid = create_interface(dev, custom, solid); + + /* Scene */ + ctx.mesh = &mesh; + ctx.sshape_end_id = sshape_end_id; + ctx.sshape = solid_dummy; + ctx.sphere = custom_solid; + scn = create_scene(dev, &ctx); + + check_probe(scn, is_master_process); + + mesh_release(&mesh); + + OK(s3d_scene_view_ref_put(sphere_view)); + OK(sdis_interface_ref_put(solid_dummy)); + OK(sdis_interface_ref_put(custom_solid)); + OK(sdis_medium_ref_put(custom)); + OK(sdis_medium_ref_put(dummy)); + OK(sdis_medium_ref_put(solid)); + OK(sdis_scene_ref_put(scn)); + + free_default_device(dev); + + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_custom_solid_path_sampling_2d.c b/src/test_sdis_custom_solid_path_sampling_2d.c @@ -0,0 +1,600 @@ +/* Copyright (C) 2016-2024 |Méso|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_mesh.h" +#include "test_sdis_utils.h" + +#include <rsys/double2.h> +#include <rsys/float2.h> + +#include <star/s2d.h> + +/* + * The system is a bilinear profile of the temperature at steady state, i.e. at + * each point of the system we can calculate the temperature analytically. Two + * forms are immersed in this temperature field: a super shape and a circle + * included in the super shape. On the Monte Carlo side, the temperature is + * unknown everywhere except on the surface of the super shape whose + * temperature is defined from the aformentionned bilinear profile. + * + * We will estimate the temperature at the position of a probe in solids by + * providing a user-side function to sample the conductive path in the circle. + * We should find the temperature of the bilinear profile at the probe position + * by Monte Carlo, independently of this coupling with an external path sampling + * routine. + * + * + * /\ <-- T(x,y,z) + * ___/ \___ + * T(y) \ __ / + * | T(y) \ / \ / + * |/ T=? *__/ \ + * o--- T(x) /_ __ _\ + * \/ \/ + */ + +#define NREALISATIONS 10000 +#define CIRCLE_RADIUS 1 + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static double +bilinear_profile(const double pos[2]) +{ + /* Range in X, Y in which the trilinear profile is defined */ + const double lower = -4; + const double upper = +4; + + /* Upper temperature limit in X, Y and Z [K]. Lower temperature limit is + * implicitly 0 */ + const double a = 333; /* Upper temperature limit in X [K] */ + const double b = 432; /* Upper temperature limit in Y [K] */ + + double x, y; + + /* Check pre-conditions */ + CHK(pos); + CHK(lower <= pos[0] && pos[0] <= upper); + CHK(lower <= pos[1] && pos[1] <= upper); + + x = (pos[0] - lower) / (upper - lower); + y = (pos[1] - lower) / (upper - lower); + return a*x + b*y; +} + +/******************************************************************************* + * Scene view + ******************************************************************************/ +/* Parameter for get_inidices/get_vertices functions. Although its layout is the + * same as that of the mesh data structure, we have deliberately defined a new + * data type since mesh functions cannot be invoked. This is because the form's + * member variables are not necessarily extensible arrays, as is the case with + * mesh */ +struct shape { + double* pos; + size_t* ids; + size_t npos; + size_t nseg; +}; +#define SHAPE_NULL__ {NULL, NULL, 0, 0} +static const struct shape SHAPE_NULL = SHAPE_NULL__; + +static void +get_position(const unsigned ivert, float pos[2], void* ctx) +{ + const struct shape* shape = ctx; + CHK(shape && pos && ivert < shape->npos); + f2_set_d2(pos, shape->pos + ivert*2); +} + +static void +get_indices(const unsigned iseg, unsigned ids[2], void* ctx) +{ + const struct shape* shape = ctx; + CHK(shape && ids && iseg < shape->nseg); + ids[0] = (unsigned)shape->ids[iseg*2+0]; + ids[1] = (unsigned)shape->ids[iseg*2+1]; +} + +static struct s2d_scene_view* +create_view(struct shape* shape_data) +{ + /* Star2D */ + struct s2d_vertex_data vdata = S2D_VERTEX_DATA_NULL; + struct s2d_device* dev = NULL; + struct s2d_scene* scn = NULL; + struct s2d_shape* shape = NULL; + struct s2d_scene_view* view = NULL; + + OK(s2d_device_create(NULL, NULL, 0, &dev)); + + /* Shape */ + vdata.usage = S2D_POSITION; + vdata.type = S2D_FLOAT2; + vdata.get = get_position; + OK(s2d_shape_create_line_segments(dev, &shape)); + OK(s2d_line_segments_setup_indexed_vertices(shape, (unsigned)shape_data->nseg, + get_indices, (unsigned)shape_data->npos, &vdata, 1, shape_data)); + + /* Scene view */ + OK(s2d_scene_create(dev, &scn)); + OK(s2d_scene_attach_shape(scn, shape)); + OK(s2d_scene_view_create(scn, S2D_TRACE|S2D_GET_PRIMITIVE, &view)); + + /* Clean up */ + OK(s2d_device_ref_put(dev)); + OK(s2d_scene_ref_put(scn)); + OK(s2d_shape_ref_put(shape)); + + return view; +} + +/******************************************************************************* + * Mesh, i.e. supershape and sphere + ******************************************************************************/ +static void +mesh_add_super_shape(struct mesh* mesh) +{ + double* pos = NULL; + size_t* ids = NULL; + + const unsigned nslices = 128; + const double a = 1.0; + const double b = 1.0; + const double n1 = 1.0; + const double n2 = 1.0; + const double n3 = 1.0; + const double m = 6.0; + size_t i = 0; + + CHK(mesh); + + FOR_EACH(i, 0, nslices) { + const double theta = (double)i * (2.0*PI / (double)nslices); + const double tmp0 = pow(fabs(1.0/a * cos(m/4.0*theta)), n2); + const double tmp1 = pow(fabs(1.0/b * sin(m/4.0*theta)), n3); + const double tmp2 = pow(tmp0 + tmp1, 1.0/n1); + const double r = 1.0 / tmp2; + const double x = cos(theta) * r * CIRCLE_RADIUS*2; + const double y = sin(theta) * r * CIRCLE_RADIUS*2; + const size_t j = (i + 1) % nslices; + + sa_push(pos, x); + sa_push(pos, y); + sa_push(ids, i); + sa_push(ids, j); + } + + mesh_2d_append(mesh, pos, sa_size(pos)/2, ids, sa_size(ids)/2, NULL); + + sa_release(pos); + sa_release(ids); +} + +static void +mesh_add_circle(struct mesh* mesh) +{ + double* pos = NULL; + size_t* ids = NULL; + + const size_t nverts = 64; + size_t i = 0; + + CHK(mesh); + + FOR_EACH(i, 0, nverts) { + const double theta = (double)i * (2*PI)/(double)nverts; + const double x = cos(theta)*CIRCLE_RADIUS; + const double y = sin(theta)*CIRCLE_RADIUS; + const size_t j = (i+1)%nverts; + + sa_push(pos, x); + sa_push(pos, y); + sa_push(ids, i); + sa_push(ids, j); + } + + mesh_2d_append(mesh, pos, sa_size(pos)/2, ids, sa_size(ids)/2, NULL); + + sa_release(pos); + sa_release(ids); +} + +/******************************************************************************* + * Custom conductive path + ******************************************************************************/ +struct custom_solid { + struct s2d_scene_view* view; /* Star-2D view of the shape */ + const struct shape* shape; /* Raw data */ +}; + +static void +setup_solver_primitive + (struct sdis_scene* scn, + const struct shape* shape, + const struct s2d_hit* user_hit, + struct s2d_primitive* prim) +{ + struct sdis_primkey key = SDIS_PRIMKEY_NULL; + const double *v0, *v1; + float v0f[2], v1f[2]; + struct s2d_attrib attr0, attr1; + + v0 = shape->pos + shape->ids[user_hit->prim.prim_id*2+0]*2; + v1 = shape->pos + shape->ids[user_hit->prim.prim_id*2+1]*2; + sdis_primkey_2d_setup(&key, v0, v1); + OK(sdis_scene_get_s2d_primitive(scn, &key, prim)); + + /* Check that the primitive on the solver side is the same as that on the + * user side. On the solver side, vertices are stored in simple precision in + * Star-3D view. We therefore need to take care of this conversion to check + * that the vertices are the same */ + OK(s2d_segment_get_vertex_attrib(prim, 0, S2D_POSITION, &attr0)); + OK(s2d_segment_get_vertex_attrib(prim, 1, S2D_POSITION, &attr1)); + f2_set_d2(v0f, v0); + f2_set_d2(v1f, v1); + + /* The vertices have been inverted on the user's side to reverse the normal + * orientation. Below it is taken into account */ + CHK(f2_eq(v0f, attr0.value)); + CHK(f2_eq(v1f, attr1.value)); +} + +/* Implementation of a Walk on Sphere algorithm for an origin-centered spherical + * geometry of radius SPHERE_RADIUS */ +static res_T +sample_steady_diffusive_path + (struct sdis_scene* scn, + struct ssp_rng* rng, + struct sdis_path* path, + struct sdis_data* data) +{ + struct custom_solid* solid = NULL; + struct s2d_hit hit = S2D_HIT_NULL; + + double pos[2]; + const double epsilon = CIRCLE_RADIUS * 1.e-6; /* Epsilon shell */ + + CHK(scn && rng && path && data); + + solid = sdis_data_get(data); + + d2_set(pos, path->vtx.P); + + do { + /* Distance from the geometry center to the current position */ + const double dst = d2_len(pos); + + double dir[3] = {0,0,0}; + double r = 0; /* Radius */ + + r = CIRCLE_RADIUS - dst; + CHK(dst > 0); + + if(r > epsilon) { + /* Uniformly sample a new position on the surrounding sphere */ + ssp_ran_circle_uniform(rng, dir, NULL); + + /* Move to the new position */ + d2_muld(dir, dir, r); + d2_add(pos, pos, dir); + + /* The current position is in the epsilon shell: + * move it to the nearest interface position */ + } else { + float posf[2]; + + d2_set(dir, pos); + d2_normalize(dir, dir); + d2_muld(pos, dir, CIRCLE_RADIUS); + + /* Map the position to the sphere geometry */ + f2_set_d2(posf, pos); + OK(s2d_scene_view_closest_point(solid->view, posf, (float)INF, NULL, &hit)); + } + + /* The calculation is performed in steady state, so the path necessarily stops + * at a boundary */ + } while(S2D_HIT_NONE(&hit)); + + /* Setup the path state */ + d2_set(path->vtx.P, pos); + path->weight = 0; + path->at_limit = 0; + path->prim_3d = S3D_PRIMITIVE_NULL; + setup_solver_primitive(scn, solid->shape, &hit, &path->prim_2d); + + return RES_OK; +} +/******************************************************************************* + * The solids, i.e. media of the super shape and the sphere + ******************************************************************************/ +#define SOLID_PROP(Prop, Val) \ + static double \ + solid_get_##Prop \ + (const struct sdis_rwalk_vertex* vtx, \ + struct sdis_data* data) \ + { \ + (void)vtx, (void)data; /* Avoid the "unused variable" warning */ \ + return Val; \ + } +SOLID_PROP(calorific_capacity, 500.0) /* [J/K/Kg] */ +SOLID_PROP(thermal_conductivity, 25.0) /* [W/m/K] */ +SOLID_PROP(volumic_mass, 7500.0) /* [kg/m^3] */ +SOLID_PROP(temperature, SDIS_TEMPERATURE_NONE/*<=> unknown*/) /* [K] */ +SOLID_PROP(delta, 1.0/40.0) /* [m] */ + +static struct sdis_medium* +create_solid(struct sdis_device* sdis) +{ + struct sdis_solid_shader shader = SDIS_SOLID_SHADER_NULL; + struct sdis_medium* solid = NULL; + + shader.calorific_capacity = solid_get_calorific_capacity; + shader.thermal_conductivity = solid_get_thermal_conductivity; + shader.volumic_mass = solid_get_volumic_mass; + shader.delta = solid_get_delta; + shader.temperature = solid_get_temperature; + + OK(sdis_solid_create(sdis, &shader, NULL, &solid)); + return solid; +} + +static struct sdis_medium* +create_custom + (struct sdis_device* sdis, + struct s2d_scene_view* view, + const struct shape* shape) +{ + /* Stardis variables */ + struct sdis_solid_shader shader = SDIS_SOLID_SHADER_NULL; + struct sdis_medium* solid = NULL; + struct sdis_data* data = NULL; + + /* Mesh variables */ + struct custom_solid* custom_solid = NULL; + const size_t sz = sizeof(struct custom_solid); + const size_t al = ALIGNOF(struct custom_solid); + + OK(sdis_data_create(sdis, sz, al, NULL, &data)); + custom_solid = sdis_data_get(data); + custom_solid->view = view; + custom_solid->shape = shape; + + shader.calorific_capacity = solid_get_calorific_capacity; + shader.thermal_conductivity = solid_get_thermal_conductivity; + shader.volumic_mass = solid_get_volumic_mass; + shader.delta = solid_get_delta; + shader.temperature = solid_get_temperature; + shader.sample_path = sample_steady_diffusive_path; + + OK(sdis_solid_create(sdis, &shader, data, &solid)); + OK(sdis_data_ref_put(data)); + + return solid; +} + +/******************************************************************************* + * Dummy environment, i.e. environment surrounding the super shape. It is + * defined only for Stardis compliance: in Stardis, an interface must divide 2 + * media. + ******************************************************************************/ +static struct sdis_medium* +create_dummy(struct sdis_device* sdis) +{ + struct sdis_fluid_shader shader = SDIS_FLUID_SHADER_NULL; + struct sdis_medium* dummy = NULL; + + shader.calorific_capacity = dummy_medium_getter; + shader.volumic_mass = dummy_medium_getter; + shader.temperature = dummy_medium_getter; + OK(sdis_fluid_create(sdis, &shader, NULL, &dummy)); + return dummy; +} + +/******************************************************************************* + * Interface: its temperature is fixed to the trilinear profile + ******************************************************************************/ +static double +interface_get_temperature + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + (void)data; /* Avoid the "unused variable" warning */ + return bilinear_profile(frag->P); +} + +static struct sdis_interface* +create_interface + (struct sdis_device* sdis, + struct sdis_medium* front, + struct sdis_medium* back) +{ + struct sdis_interface* interf = NULL; + struct sdis_interface_shader shader = SDIS_INTERFACE_SHADER_NULL; + + /* Fluid/solid interface: fix the temperature to the temperature profile */ + if(sdis_medium_get_type(front) != sdis_medium_get_type(back)) { + shader.front.temperature = interface_get_temperature; + shader.back.temperature = interface_get_temperature; + } + + OK(sdis_interface_create(sdis, front, back, &shader, NULL, &interf)); + return interf; +} + +/******************************************************************************* + * Scene, i.e. the system to simulate + ******************************************************************************/ +struct scene_context { + const struct mesh* mesh; + size_t sshape_end_id; /* Last segment index of the super shape */ + struct sdis_interface* sshape; + struct sdis_interface* sphere; +}; +#define SCENE_CONTEXT_NULL__ {NULL, 0, 0, 0} +static const struct scene_context SCENE_CONTEXT_NULL = SCENE_CONTEXT_NULL__; + +static void +scene_get_indices(const size_t iseg, size_t ids[2], void* ctx) +{ + struct scene_context* context = ctx; + CHK(ids && context && iseg < mesh_2d_nsegments(context->mesh)); + ids[0] = (unsigned)context->mesh->indices[iseg*2+0]; + ids[1] = (unsigned)context->mesh->indices[iseg*2+1]; +} + +static void +scene_get_interface(const size_t iseg, struct sdis_interface** interf, void* ctx) +{ + struct scene_context* context = ctx; + CHK(interf && context && iseg < mesh_2d_nsegments(context->mesh)); + if(iseg < context->sshape_end_id) { + *interf = context->sshape; + } else { + *interf = context->sphere; + } +} + +static void +scene_get_position(const size_t ivert, double pos[2], void* ctx) +{ + struct scene_context* context = ctx; + CHK(pos && context && ivert < mesh_2d_nvertices(context->mesh)); + pos[0] = context->mesh->positions[ivert*2+0]; + pos[1] = context->mesh->positions[ivert*2+1]; +} + +static struct sdis_scene* +create_scene(struct sdis_device* sdis, struct scene_context* ctx) +{ + struct sdis_scene* scn = NULL; + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + + scn_args.get_indices = scene_get_indices; + scn_args.get_interface = scene_get_interface; + scn_args.get_position = scene_get_position; + scn_args.nprimitives = mesh_2d_nsegments(ctx->mesh); + scn_args.nvertices = mesh_2d_nvertices(ctx->mesh); + scn_args.context = ctx; + OK(sdis_scene_2d_create(sdis, &scn_args, &scn)); + return scn; +} + +/******************************************************************************* + * Validations + ******************************************************************************/ +static void +check_probe(struct sdis_scene* scn, const int is_master_process) +{ + struct sdis_solve_probe_args args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_estimator* estimator = NULL; + double ref = 0; + + args.position[0] = CIRCLE_RADIUS*0.125; + args.position[1] = CIRCLE_RADIUS*0.250; + args.nrealisations = NREALISATIONS; + + OK(sdis_solve_probe(scn, &args, &estimator)); + + if(!is_master_process) return; + + OK(sdis_estimator_get_temperature(estimator, &T)); + + ref = bilinear_profile(args.position); + + printf("T(%g, %g) = %g ~ %g +/- %g\n", + SPLIT2(args.position), ref, T.E, T.SE); + + CHK(eq_eps(ref, T.E, T.SE*3)); + OK(sdis_estimator_ref_put(estimator)); +} + +/******************************************************************************* + * The test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + /* Stardis */ + struct sdis_device* dev = NULL; + struct sdis_interface* solid_dummy = NULL; + struct sdis_interface* custom_solid = NULL; + struct sdis_medium* solid = NULL; + struct sdis_medium* custom = NULL; + struct sdis_medium* dummy = NULL; /* Medium surrounding the solid */ + struct sdis_scene* scn = NULL; + + /* Star 2D */ + struct shape shape = SHAPE_NULL; + struct s2d_scene_view* circle_view = NULL; + + /* Miscellaneous */ + struct scene_context ctx = SCENE_CONTEXT_NULL; + struct mesh mesh = MESH_NULL; + size_t sshape_end_id = 0; /* Last index of the super shape */ + int is_master_process = 1; + (void)argc, (void)argv; + + create_default_device(&argc, &argv, &is_master_process, &dev); + + /* Mesh */ + mesh_init(&mesh); + mesh_add_super_shape(&mesh); + sshape_end_id = mesh_2d_nsegments(&mesh); + mesh_add_circle(&mesh); + + /* Create a view of the circle's geometry. This will be used to couple custom + * solid path sampling to the solver */ + shape.pos = mesh.positions; + shape.ids = mesh.indices + sshape_end_id*2; + shape.npos = mesh_2d_nvertices(&mesh); + shape.nseg = mesh_2d_nsegments(&mesh) - sshape_end_id; + circle_view = create_view(&shape); + + /* Physical properties */ + dummy = create_dummy(dev); + solid = create_solid(dev); + custom = create_custom(dev, circle_view, &shape); + solid_dummy = create_interface(dev, dummy, solid); + custom_solid = create_interface(dev, solid, custom); + + /* Scene */ + ctx.mesh = &mesh; + ctx.sshape_end_id = sshape_end_id; + ctx.sshape = solid_dummy; + ctx.sphere = custom_solid; + scn = create_scene(dev, &ctx); + + check_probe(scn, is_master_process); + + mesh_release(&mesh); + + OK(s2d_scene_view_ref_put(circle_view)); + OK(sdis_interface_ref_put(solid_dummy)); + OK(sdis_interface_ref_put(custom_solid)); + OK(sdis_medium_ref_put(custom)); + OK(sdis_medium_ref_put(dummy)); + OK(sdis_medium_ref_put(solid)); + OK(sdis_scene_ref_put(scn)); + + free_default_device(dev); + + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_mesh.h b/src/test_sdis_mesh.h @@ -49,6 +49,13 @@ mesh_nvertices(const struct mesh* mesh) return sa_size(mesh->positions) / 3/* #coords per vertex */; } +static INLINE size_t +mesh_2d_nvertices(const struct mesh* mesh) +{ + CHK(mesh); + return sa_size(mesh->positions) / 2/* #coords per vertex */; +} + /* Number of triangles */ static INLINE size_t mesh_ntriangles(const struct mesh* mesh) @@ -57,6 +64,13 @@ mesh_ntriangles(const struct mesh* mesh) return sa_size(mesh->indices) / 3/* #indices per triangle */; } +static INLINE size_t +mesh_2d_nsegments(const struct mesh* mesh) +{ + CHK(mesh); + return sa_size(mesh->indices) / 2/* #indices per segment */; +} + static INLINE void mesh_append (struct mesh* mesh, @@ -97,6 +111,42 @@ mesh_append } static INLINE void +mesh_2d_append + (struct mesh* mesh, + const double* in_positions, + const size_t in_nvertices, + const size_t* in_indices, + const size_t in_nsegments, + const double in_translate[2]) /* May be NULL */ +{ + double translate[2] = {0, 0}; + double* positions = NULL; + size_t* indices = NULL; + size_t ivert = 0; + size_t i = 0; + CHK(mesh != NULL); + + ivert = mesh_2d_nvertices(mesh); + positions = sa_add(mesh->positions, in_nvertices*2); + indices = sa_add(mesh->indices, in_nsegments*2); + + if(in_translate) { + translate[0] = in_translate[0]; + translate[1] = in_translate[1]; + } + + FOR_EACH(i, 0, in_nvertices) { + positions[i*2 + 0] = in_positions[i*2 + 0] + translate[0]; + positions[i*2 + 1] = in_positions[i*2 + 1] + translate[1]; + } + + FOR_EACH(i, 0, in_nsegments) { + indices[i*2 + 0] = in_indices[i*2 + 0] + ivert; + indices[i*2 + 1] = in_indices[i*2 + 1] + ivert; + } +} + +static INLINE void mesh_dump(const struct mesh* mesh, FILE* stream) { size_t i, n; @@ -117,4 +167,24 @@ mesh_dump(const struct mesh* mesh, FILE* stream) fflush(stream); } +static INLINE void +mesh_2d_dump(const struct mesh* mesh, FILE* stream) +{ + size_t i, n; + CHK(mesh != NULL); + + n = mesh_2d_nvertices(mesh); + FOR_EACH(i, 0, n) { + fprintf(stream, "v %g %g\n", SPLIT2(mesh->positions+i*2)); + } + + n = mesh_2d_nsegments(mesh); + FOR_EACH(i, 0, n) { + fprintf(stream, "l %lu %lu\n", + (unsigned long)(mesh->indices[i*2+0] + 1), + (unsigned long)(mesh->indices[i*2+1] + 1)); + } + fflush(stream); +} + #endif /* TEST_SDIS_MESH_H */ diff --git a/src/test_sdis_primkey.c b/src/test_sdis_primkey.c @@ -0,0 +1,281 @@ +/* Copyright (C) 2016-2024 |Méso|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 <star/s3dut.h> +#include <rsys/float3.h> + +/* + * The system is a solid supershape whose boundary temperature is set to a + * constant. The temperature of the solid is therefore this same temperature. + * This simplistic test case is not used to verify a Monte Carlo estimate, but + * to ensure that the caller can recover the internal representation of the + * geometric primitives from his own data. + * + * /\ + * ___/ \___ + * \ / + * /_ __ _\ + * \/ \/ + * + */ + +/******************************************************************************* + * Super shape + ******************************************************************************/ +static struct s3dut_mesh* +create_super_shape(void) +{ + struct s3dut_mesh* mesh = NULL; + struct s3dut_super_formula f0 = S3DUT_SUPER_FORMULA_NULL; + struct s3dut_super_formula f1 = S3DUT_SUPER_FORMULA_NULL; + const double radius = 1; + const unsigned nslices = 256; + + f0.A = 1.5; f0.B = 1; f0.M = 11.0; f0.N0 = 1; f0.N1 = 1; f0.N2 = 2.0; + f1.A = 1.0; f1.B = 2; f1.M = 3.6; f1.N0 = 1; f1.N1 = 2; f1.N2 = 0.7; + OK(s3dut_create_super_shape(NULL, &f0, &f1, radius, nslices, nslices/2, &mesh)); + + return mesh; +} + +/******************************************************************************* + * Solid, i.e. medium of the super shape + ******************************************************************************/ +#define SOLID_PROP(Prop, Val) \ + static double \ + solid_get_##Prop \ + (const struct sdis_rwalk_vertex* vtx, \ + struct sdis_data* data) \ + { \ + (void)vtx, (void)data; /* Avoid the "unused variable" warning */ \ + return Val; \ + } +SOLID_PROP(calorific_capacity, 1) /* [J/K/Kg] */ +SOLID_PROP(thermal_conductivity, 1) /* [W/m/K] */ +SOLID_PROP(volumic_mass, 1) /* [kg/m^3] */ +SOLID_PROP(temperature, SDIS_TEMPERATURE_NONE) /* [K] */ +SOLID_PROP(delta, 1.0/20.0) /* [m/fp_to_meter] */ +#undef SOLID_PROP + +static struct sdis_medium* +create_solid(struct sdis_device* sdis) +{ + struct sdis_solid_shader shader = SDIS_SOLID_SHADER_NULL; + struct sdis_medium* solid = NULL; + + shader.calorific_capacity = solid_get_calorific_capacity; + shader.thermal_conductivity = solid_get_thermal_conductivity; + shader.volumic_mass = solid_get_volumic_mass; + shader.delta = solid_get_delta; + shader.temperature = solid_get_temperature; + OK(sdis_solid_create(sdis, &shader, NULL, &solid)); + return solid; +} + +/******************************************************************************* + * Dummy environment, i.e. environment surrounding the super shape. It is + * defined only for Stardis compliance: in Stardis, an interface must divide 2 + * media. + ******************************************************************************/ +static struct sdis_medium* +create_dummy(struct sdis_device* sdis) +{ + struct sdis_fluid_shader shader = SDIS_FLUID_SHADER_NULL; + struct sdis_medium* dummy = NULL; + + shader.calorific_capacity = dummy_medium_getter; + shader.volumic_mass = dummy_medium_getter; + shader.temperature = dummy_medium_getter; + OK(sdis_fluid_create(sdis, &shader, NULL, &dummy)); + return dummy; +} + +/******************************************************************************* + * Interface + ******************************************************************************/ +static double +interface_get_temperature + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + (void)frag, (void)data; /* Avoid the "unused variable" warning */ + return 300; /* [K] */ +} + +static struct sdis_interface* +create_interface + (struct sdis_device* sdis, + struct sdis_medium* front, + struct sdis_medium* back) +{ + struct sdis_interface* interf = NULL; + struct sdis_interface_shader shader = SDIS_INTERFACE_SHADER_NULL; + + shader.front.temperature = interface_get_temperature; + shader.back.temperature = interface_get_temperature; + OK(sdis_interface_create(sdis, front, back, &shader, NULL, &interf)); + return interf; +} + +/******************************************************************************* + * Scene, i.e. the system to simulate + ******************************************************************************/ +struct scene_context { + struct s3dut_mesh_data mesh_data; + struct sdis_interface* interf; +}; +static const struct scene_context SCENE_CONTEXT_NULL = {{0}, NULL}; + +static void +scene_get_indices(const size_t itri, size_t ids[3], void* ctx) +{ + struct scene_context* context = ctx; + CHK(ids && context && itri < context->mesh_data.nprimitives); + /* Flip the indices to ensure that the normal points into the super shape */ + ids[0] = (unsigned)context->mesh_data.indices[itri*3+0]; + ids[1] = (unsigned)context->mesh_data.indices[itri*3+2]; + ids[2] = (unsigned)context->mesh_data.indices[itri*3+1]; +} + +static void +scene_get_interface(const size_t itri, struct sdis_interface** interf, void* ctx) +{ + struct scene_context* context = ctx; + CHK(interf && context && itri < context->mesh_data.nprimitives); + *interf = context->interf; +} + +static void +scene_get_position(const size_t ivert, double pos[3], void* ctx) +{ + struct scene_context* context = ctx; + CHK(pos && context && ivert < context->mesh_data.nvertices); + pos[0] = context->mesh_data.positions[ivert*3+0]; + pos[1] = context->mesh_data.positions[ivert*3+1]; + pos[2] = context->mesh_data.positions[ivert*3+2]; +} + +static struct sdis_scene* +create_scene + (struct sdis_device* sdis, + const struct s3dut_mesh* mesh, + struct sdis_interface* interf) +{ + struct sdis_scene* scn = NULL; + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + struct scene_context context = SCENE_CONTEXT_NULL; + + OK(s3dut_mesh_get_data(mesh, &context.mesh_data)); + context.interf = interf; + + scn_args.get_indices = scene_get_indices; + scn_args.get_interface = scene_get_interface; + scn_args.get_position = scene_get_position; + scn_args.nprimitives = context.mesh_data.nprimitives; + scn_args.nvertices = context.mesh_data.nvertices; + scn_args.context = &context; + OK(sdis_scene_create(sdis, &scn_args, &scn)); + return scn; +} + +/******************************************************************************* + * Validation + ******************************************************************************/ +static void +check(struct sdis_scene* scn, const struct s3dut_mesh* mesh) +{ + struct s3d_primitive prim = S3D_PRIMITIVE_NULL; + struct s3dut_mesh_data mesh_data; + struct sdis_primkey key = SDIS_PRIMKEY_NULL; + size_t iprim = 0; + + OK(s3dut_mesh_get_data(mesh, &mesh_data)); + + BA(sdis_scene_get_s3d_primitive(NULL, &key, &prim)); + BA(sdis_scene_get_s3d_primitive(scn, NULL, &prim)); + BA(sdis_scene_get_s3d_primitive(scn, &key, NULL)); + BA(sdis_scene_get_s3d_primitive(scn, &key, &prim)); + + FOR_EACH(iprim, 0, mesh_data.nprimitives) { + const double *v0, *v1, *v2; + float v0f[3], v1f[3], v2f[3]; + struct s3d_attrib attr0, attr1, attr2; + + /* Check that a primitive can be obtained from the key constructed on the + * user side */ + v0 = mesh_data.positions + mesh_data.indices[iprim*3 + 0]*3; + v1 = mesh_data.positions + mesh_data.indices[iprim*3 + 1]*3; + v2 = mesh_data.positions + mesh_data.indices[iprim*3 + 2]*3; + sdis_primkey_setup(&key, v0, v1, v2); + OK(sdis_scene_get_s3d_primitive(scn, &key, &prim)); + + /* Check that the primitive on the solver side is the same as that on the + * user side. On the solver side, vertices are stored in simple precision in + * Star-3D view. We therefore need to take care of this conversion to check + * that the vertices are the same */ + OK(s3d_triangle_get_vertex_attrib(&prim, 0, S3D_POSITION, &attr0)); + OK(s3d_triangle_get_vertex_attrib(&prim, 1, S3D_POSITION, &attr1)); + OK(s3d_triangle_get_vertex_attrib(&prim, 2, S3D_POSITION, &attr2)); + f3_set_d3(v0f, v0); + f3_set_d3(v1f, v1); + f3_set_d3(v2f, v2); + + /* The vertices have been inverted on the user's side to reverse the normal + * orientation. Below it is taken into account */ + CHK(f3_eq(v0f, attr0.value)); + CHK(f3_eq(v1f, attr2.value)); + CHK(f3_eq(v2f, attr1.value)); + } +} + +/******************************************************************************* + * The test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + /* Stardis variables */ + struct sdis_device* sdis = NULL; + struct sdis_interface* interf = NULL; + struct sdis_medium* solid = NULL; + struct sdis_medium* dummy = NULL; + struct sdis_scene* scn = NULL; + + struct s3dut_mesh* super_shape = NULL; + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &sdis)); + + super_shape = create_super_shape(); + solid = create_solid(sdis); + dummy = create_dummy(sdis); + interf = create_interface(sdis, solid, dummy); + scn = create_scene(sdis, super_shape, interf); + + check(scn, super_shape); + + OK(s3dut_mesh_ref_put(super_shape)); + OK(sdis_device_ref_put(sdis)); + OK(sdis_interface_ref_put(interf)); + OK(sdis_medium_ref_put(solid)); + OK(sdis_medium_ref_put(dummy)); + OK(sdis_scene_ref_put(scn)); + + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_primkey_2d.c b/src/test_sdis_primkey_2d.c @@ -0,0 +1,295 @@ +/* Copyright (C) 2016-2024 |Méso|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 "test_sdis_mesh.h" + +#include <star/s2d.h> + +#include <rsys/float2.h> + +/* + * The system is a solid supershape whose boundary temperature is set to a + * constant. The temperature of the solid is therefore this same temperature. + * This simplistic test case is not used to verify a Monte Carlo estimate, but + * to ensure that the caller can recover the internal representation of the + * geometric primitives from his own data. + * + * /\ + * ___/ \___ + * \ / + * /_ __ _\ + * \/ \/ + * + */ + +/******************************************************************************* + * Super shape + ******************************************************************************/ +static struct mesh +create_super_shape(void) +{ + struct mesh sshape = MESH_NULL; + + const unsigned nslices = 128; + const double a = 1.0; + const double b = 1.0; + const double n1 = 1.0; + const double n2 = 1.0; + const double n3 = 1.0; + const double m = 6.0; + size_t i = 0; + + FOR_EACH(i, 0, nslices) { + const double theta = (double)i * (2.0*PI / (double)nslices); + const double tmp0 = pow(fabs(1.0/a * cos(m/4.0*theta)), n2); + const double tmp1 = pow(fabs(1.0/b * sin(m/4.0*theta)), n3); + const double tmp2 = pow(tmp0 + tmp1, 1.0/n1); + const double r = 1.0 / tmp2; + const double x = cos(theta) * r; + const double y = sin(theta) * r; + const size_t j = (i + 1) % nslices; + + sa_push(sshape.positions, x); + sa_push(sshape.positions, y); + sa_push(sshape.indices, i); + sa_push(sshape.indices, j); + } + + return sshape; +} + +/******************************************************************************* + * Solid, i.e. medium of the super shape + ******************************************************************************/ +#define SOLID_PROP(Prop, Val) \ + static double \ + solid_get_##Prop \ + (const struct sdis_rwalk_vertex* vtx, \ + struct sdis_data* data) \ + { \ + (void)vtx, (void)data; /* Avoid the "unused variable" warning */ \ + return Val; \ + } +SOLID_PROP(calorific_capacity, 1) /* [J/K/Kg] */ +SOLID_PROP(thermal_conductivity, 1) /* [W/m/K] */ +SOLID_PROP(volumic_mass, 1) /* [kg/m^3] */ +SOLID_PROP(temperature, SDIS_TEMPERATURE_NONE) /* [K] */ +SOLID_PROP(delta, 1.0/20.0) /* [m/fp_to_meter] */ +#undef SOLID_PROP + +static struct sdis_medium* +create_solid(struct sdis_device* sdis) +{ + struct sdis_solid_shader shader = SDIS_SOLID_SHADER_NULL; + struct sdis_medium* solid = NULL; + + shader.calorific_capacity = solid_get_calorific_capacity; + shader.thermal_conductivity = solid_get_thermal_conductivity; + shader.volumic_mass = solid_get_volumic_mass; + shader.delta = solid_get_delta; + shader.temperature = solid_get_temperature; + OK(sdis_solid_create(sdis, &shader, NULL, &solid)); + return solid; +} + +/******************************************************************************* + * Dummy environment, i.e. environment surrounding the super shape. It is + * defined only for Stardis compliance: in Stardis, an interface must divide 2 + * media. + ******************************************************************************/ +static struct sdis_medium* +create_dummy(struct sdis_device* sdis) +{ + struct sdis_fluid_shader shader = SDIS_FLUID_SHADER_NULL; + struct sdis_medium* dummy = NULL; + + shader.calorific_capacity = dummy_medium_getter; + shader.volumic_mass = dummy_medium_getter; + shader.temperature = dummy_medium_getter; + OK(sdis_fluid_create(sdis, &shader, NULL, &dummy)); + return dummy; +} + +/******************************************************************************* + * Interface + ******************************************************************************/ +static double +interface_get_temperature + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + (void)frag, (void)data; /* Avoid the "unused variable" warning */ + return 300; /* [K] */ +} + +static struct sdis_interface* +create_interface + (struct sdis_device* sdis, + struct sdis_medium* front, + struct sdis_medium* back) +{ + struct sdis_interface* interf = NULL; + struct sdis_interface_shader shader = SDIS_INTERFACE_SHADER_NULL; + + shader.front.temperature = interface_get_temperature; + shader.back.temperature = interface_get_temperature; + OK(sdis_interface_create(sdis, front, back, &shader, NULL, &interf)); + return interf; +} + +/******************************************************************************* + * Scene, i.e. the system to simulate + ******************************************************************************/ +struct scene_context { + const struct mesh* sshape; + struct sdis_interface* interf; +}; +static const struct scene_context SCENE_CONTEXT_NULL = {NULL, NULL}; + +static void +scene_get_indices(const size_t iseg, size_t ids[2], void* ctx) +{ + struct scene_context* context = ctx; + CHK(ids && context); + /* Flip the indices to ensure that the normal points into the super shape */ + ids[0] = (unsigned)context->sshape->indices[iseg*2+1]; + ids[1] = (unsigned)context->sshape->indices[iseg*2+0]; +} + +static void +scene_get_interface(const size_t iseg, struct sdis_interface** interf, void* ctx) +{ + struct scene_context* context = ctx; + (void)iseg; + CHK(interf && context); + *interf = context->interf; +} + +static void +scene_get_position(const size_t ivert, double pos[2], void* ctx) +{ + struct scene_context* context = ctx; + CHK(pos && context); + pos[0] = context->sshape->positions[ivert*2+0]; + pos[1] = context->sshape->positions[ivert*2+1]; +} + +static struct sdis_scene* +create_scene + (struct sdis_device* sdis, + const struct mesh* sshape, + struct sdis_interface* interf) +{ + struct sdis_scene* scn = NULL; + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + struct scene_context context = SCENE_CONTEXT_NULL; + + context.interf = interf; + context.sshape = sshape; + + scn_args.get_indices = scene_get_indices; + scn_args.get_interface = scene_get_interface; + scn_args.get_position = scene_get_position; + scn_args.nprimitives = mesh_2d_nsegments(sshape); + scn_args.nvertices = mesh_2d_nvertices(sshape); + scn_args.context = &context; + OK(sdis_scene_2d_create(sdis, &scn_args, &scn)); + return scn; +} + +/******************************************************************************* + * Validation + ******************************************************************************/ +static void +check(struct sdis_scene* scn, const struct mesh* mesh) +{ + struct s2d_primitive prim = S2D_PRIMITIVE_NULL; + struct sdis_primkey key = SDIS_PRIMKEY_NULL; + size_t iprim = 0; + size_t nprims = 0; + + BA(sdis_scene_get_s2d_primitive(NULL, &key, &prim)); + BA(sdis_scene_get_s2d_primitive(scn, NULL, &prim)); + BA(sdis_scene_get_s2d_primitive(scn, &key, NULL)); + BA(sdis_scene_get_s2d_primitive(scn, &key, &prim)); + + nprims = mesh_2d_nsegments(mesh); + FOR_EACH(iprim, 0, nprims) { + const double *v0, *v1; + float v0f[2], v1f[2]; + struct s2d_attrib attr0, attr1; + + /* Check that a primitive can be obtained from the key constructed on the + * user side */ + v0 = mesh->positions + mesh->indices[iprim*2 + 0]*2; + v1 = mesh->positions + mesh->indices[iprim*2 + 1]*2; + sdis_primkey_2d_setup(&key, v0, v1); + OK(sdis_scene_get_s2d_primitive(scn, &key, &prim)); + + /* Check that the primitive on the solver side is the same as that on the + * user side. On the solver side, vertices are stored in simple precision in + * Star-3D view. We therefore need to take care of this conversion to check + * that the vertices are the same */ + OK(s2d_segment_get_vertex_attrib(&prim, 0, S2D_POSITION, &attr0)); + OK(s2d_segment_get_vertex_attrib(&prim, 1, S2D_POSITION, &attr1)); + f2_set_d2(v0f, v0); + f2_set_d2(v1f, v1); + + /* The vertices have been inverted on the user's side to reverse the normal + * orientation. Below it is taken into account */ + CHK(f2_eq(v0f, attr1.value)); + CHK(f2_eq(v1f, attr0.value)); + } +} + +/******************************************************************************* + * The test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + /* Stardis */ + struct sdis_device* sdis = NULL; + struct sdis_interface* interf = NULL; + struct sdis_medium* solid = NULL; + struct sdis_medium* dummy = NULL; /* Medium surrounding the solid */ + struct sdis_scene* scn = NULL; + + /* Miscellaneous */ + struct mesh sshape = MESH_NULL; + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &sdis)); + + sshape = create_super_shape(); + solid = create_solid(sdis); + dummy = create_dummy(sdis); + interf = create_interface(sdis, solid, dummy); + scn = create_scene(sdis, &sshape, interf); + + check(scn, &sshape); + + mesh_release(&sshape); + OK(sdis_device_ref_put(sdis)); + OK(sdis_interface_ref_put(interf)); + OK(sdis_medium_ref_put(solid)); + OK(sdis_medium_ref_put(dummy)); + OK(sdis_scene_ref_put(scn)); + + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_scene.c b/src/test_sdis_scene.c @@ -100,6 +100,8 @@ test_scene_3d struct context ctx; struct senc2d_scene* scn2d; struct senc3d_scene* scn3d; + struct s2d_scene_view* view2d; + struct s3d_scene_view* view3d; struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; struct sdis_scene_find_closest_point_args closest_pt_args = SDIS_SCENE_FIND_CLOSEST_POINT_ARGS_NULL; @@ -266,9 +268,13 @@ test_scene_3d BA(sdis_scene_get_senc3d_scene(scn, NULL)); BA(sdis_scene_get_senc3d_scene(NULL, &scn3d)); OK(sdis_scene_get_senc3d_scene(scn, &scn3d)); - OK(senc3d_scene_ref_put(scn3d)); - /* No 2D available */ - BA(sdis_scene_get_senc2d_scene(scn, &scn2d)); + BA(sdis_scene_get_senc2d_scene(scn, &scn2d)); /* No 2D available */ + + BA(sdis_scene_get_s3d_scene_view(NULL, NULL)); + BA(sdis_scene_get_s3d_scene_view(NULL, &view3d)); + BA(sdis_scene_get_s3d_scene_view(scn, NULL)); + OK(sdis_scene_get_s3d_scene_view(scn, &view3d)); + BA(sdis_scene_get_s2d_scene_view(scn, &view2d)); /* No 2D available */ BA(sdis_scene_get_radiative_env(NULL, &radenv)); BA(sdis_scene_get_radiative_env(scn, NULL)); @@ -311,6 +317,8 @@ test_scene_2d struct context ctx; struct senc2d_scene* scn2d; struct senc3d_scene* scn3d; + struct s2d_scene_view* view2d; + struct s3d_scene_view* view3d; size_t nsegs, npos; size_t i; size_t iprim; @@ -502,9 +510,13 @@ test_scene_2d BA(sdis_scene_get_senc2d_scene(scn, NULL)); BA(sdis_scene_get_senc2d_scene(NULL, &scn2d)); OK(sdis_scene_get_senc2d_scene(scn, &scn2d)); - OK(senc2d_scene_ref_put(scn2d)); - /* No 3D available */ - BA(sdis_scene_get_senc3d_scene(scn, &scn3d)); + BA(sdis_scene_get_senc3d_scene(scn, &scn3d)); /* No 3D available */ + + BA(sdis_scene_get_s2d_scene_view(NULL, NULL)); + BA(sdis_scene_get_s2d_scene_view(NULL, &view2d)); + BA(sdis_scene_get_s2d_scene_view(scn, NULL)); + OK(sdis_scene_get_s2d_scene_view(scn, &view2d)); + BA(sdis_scene_get_s3d_scene_view(scn, &view3d)); /* No 3D available */ BA(sdis_scene_get_radiative_env(NULL, NULL)); BA(sdis_scene_get_radiative_env(scn, NULL)); diff --git a/src/test_sdis_solve_camera.c b/src/test_sdis_solve_camera.c @@ -33,27 +33,38 @@ #define SPP 32 /* #Samples per pixel, i.e. #realisations per pixel */ /* - * The scene is composed of a solid cube whose temperature is unknown. The - * emissivity of the cube is 1 and its convection coefficient with the - * surrounding fluid at 300K is 0.1. At the center of the cube there is a spherical - * fluid cavity whose temperature is 350K. The convection coefficient between - * the solid and the cavity is 1 and the emissivity of this interface is null. - * The ambient radiative temperature of the system is 300K. + * The scene consists of a solid cube whose temperature is unknown. The + * emissivity of the cube is 1 and the convection coefficient with the + * surrounding fluid at 300 K is 0.1. At the center of the cube is a spherical + * cavity of fluid with a temperature of 350 K. The convection coefficient + * between the solid and the cavity is 1, and the emissivity of this interface + * is zero. The ambient radiative temperature of the system is 300 K. * - * In this test, we compute the radiative temperature that reaches a camera - * that looks the cube and we `dump' a normalized image of the result. + * Finally, a parallelepiped below the cube symbolizes the ground. The + * temperature of its Robin condition is 280 K. This geometry verifies that a + * camera can draw a scene in an enclosure containing several media, such as + * those used to define several boundary conditions. * - * (1,1,1) - * +----------------+ - * /' # # /| - * +----*--------*--+ | - * | ' # # | | - * | ' # 350K # | | - * | ' # # | | - * | +.....#..#.....|.+ - * |/ |/ - * +----------------+ - * (-1,-1,-1) + * In this test, we calculate the radiative temperature that reaches a camera + * looking at the cube and produce an image of the result written to the + * standard output in htrdr-image(5) format. + * + * + * +----------------+ + * /' # # /| + * +----*--------*--+ | __\ 300 K + * | ' # # | | / / + * | ' # 350K # | | \__/ + * | ' # # | | + * | +.....#..#.....|.+ + * |/ |/ + * +----------------+ 280K + * +---------------------------------__\------+ + * / / / /| + * / \__/ / + + * +------------------------------------------+ / + * | |/ + * +------------------------------------------+ */ /******************************************************************************* @@ -82,6 +93,52 @@ struct geometry { static const struct geometry GEOMETRY_NULL = {NULL, NULL, NULL}; static void +geometry_add_shape + (struct geometry* geom, + const double* positions, + const size_t nverts, + const size_t* indices, + const size_t nprims, + const double transform[12], /* May be NULL <=> no transformation */ + struct sdis_interface* interf) +{ + struct map_interf* geom_interf = NULL; + size_t nverts_prev = 0; + size_t i; + + CHK(geom != NULL); + CHK(positions != NULL); + CHK(indices != NULL); + CHK(nverts != 0); + CHK(nprims != 0); + CHK(interf != NULL); + + /* Save the previous number of vertices/primitives of the geometry */ + nverts_prev = sa_size(geom->positions) / 3; + + /* Add the vertices */ + FOR_EACH(i, 0, nverts) { + double* pos = sa_add(geom->positions, 3); + d3_set(pos, positions + i*3); + if(transform) { + d33_muld3(pos, transform, pos); + d3_add(pos, transform+9, pos); + } + } + + /* Add the indices */ + FOR_EACH(i, 0, nprims) { + sa_push(geom->indices, indices[i*3+0] + nverts_prev); + sa_push(geom->indices, indices[i*3+1] + nverts_prev); + sa_push(geom->indices, indices[i*3+2] + nverts_prev); + } + + geom_interf = sa_add(geom->interfaces, 1); + geom_interf->key = sa_size(geom->indices) / 3 - 1; + geom_interf->interf = interf; +} + +static void geometry_release(struct geometry* geom) { CHK(geom != NULL); @@ -143,43 +200,59 @@ geometry_get_interface *bound = interf->interf; } -/******************************************************************************* - * Fluid medium - ******************************************************************************/ -struct fluid { - double cp; - double rho; - double temperature; -}; -static const struct fluid FLUID_NULL = {0, 0, SDIS_TEMPERATURE_NONE}; - -static double -fluid_get_calorific_capacity - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +static void +add_cube(struct geometry* geom, struct sdis_interface* interf) { - CHK(data != NULL && vtx != NULL); - return ((const struct fluid*)sdis_data_cget(data))->cp; + struct s3dut_mesh_data msh_data; + struct s3dut_mesh* msh = NULL; + CHK(geom); + + OK(s3dut_create_cuboid(NULL, 2, 2, 2, &msh)); + OK(s3dut_mesh_get_data(msh, &msh_data)); + geometry_add_shape(geom, msh_data.positions, msh_data.nvertices, + msh_data.indices, msh_data.nprimitives, NULL, interf); + OK(s3dut_mesh_ref_put(msh)); } -static double -fluid_get_volumic_mass - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +static void +add_sphere(struct geometry* geom, struct sdis_interface* interf) { - CHK(data != NULL && vtx != NULL); - return ((const struct fluid*)sdis_data_cget(data))->rho; + struct s3dut_mesh_data msh_data; + struct s3dut_mesh* msh = NULL; + CHK(geom); + + OK(s3dut_create_sphere(NULL, 0.5, 32, 16, &msh)); + OK(s3dut_mesh_get_data(msh, &msh_data)); + geometry_add_shape(geom, msh_data.positions, msh_data.nvertices, + msh_data.indices, msh_data.nprimitives, NULL, interf); + OK(s3dut_mesh_ref_put(msh)); } -static double -fluid_get_temperature - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +static void +add_ground(struct geometry* geom, struct sdis_interface* interf) { - CHK(data != NULL && vtx != NULL); - return ((const struct fluid*)sdis_data_cget(data))->temperature; + struct s3dut_mesh_data msh_data; + struct s3dut_mesh* msh = NULL; + const double transform[12] = {1,0,0, 0,1,0, 0,0,1, 0,0,-4}; + CHK(geom); + + OK(s3dut_create_cuboid(NULL, 10, 10, 2, &msh)); + OK(s3dut_mesh_get_data(msh, &msh_data)); + geometry_add_shape(geom, msh_data.positions, msh_data.nvertices, + msh_data.indices, msh_data.nprimitives, transform, interf); + OK(s3dut_mesh_ref_put(msh)); } /******************************************************************************* - * Solid medium + * Media ******************************************************************************/ +struct fluid { + double cp; + double rho; + double temperature; +}; +static const struct fluid FLUID_NULL = {0, 0, SDIS_TEMPERATURE_NONE}; + struct solid { double cp; double lambda; @@ -189,48 +262,87 @@ struct solid { }; static const struct solid SOLID_NULL = {0, 0, 0, 0, SDIS_TEMPERATURE_NONE}; -static double -solid_get_calorific_capacity - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +#define MEDIUM_GETTER(Type, Prop) \ + static double \ + Type##_get_##Prop \ + (const struct sdis_rwalk_vertex* vtx, \ + struct sdis_data* data) \ + { \ + CHK(data && vtx); \ + return ((const struct Type*)sdis_data_cget(data))->Prop; \ + } +/* Fluid getters */ +MEDIUM_GETTER(fluid, cp) +MEDIUM_GETTER(fluid, rho) +MEDIUM_GETTER(fluid, temperature) +/* Solid getters */ +MEDIUM_GETTER(solid, cp) +MEDIUM_GETTER(solid, lambda) +MEDIUM_GETTER(solid, rho) +MEDIUM_GETTER(solid, delta) +MEDIUM_GETTER(solid, temperature) +#undef MEDIUM_GETTER + +static struct sdis_medium* +create_fluid + (struct sdis_device* dev, + const struct fluid* param) { - CHK(data != NULL && vtx != NULL); - return ((const struct solid*)sdis_data_cget(data))->cp; -} + struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; + struct sdis_data* data = NULL; + struct sdis_medium* fluid = NULL; -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; -} + CHK(param != NULL); -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; -} + /* Copy the fluid parameters into the Stardis memory space */ + OK(sdis_data_create + (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); + memcpy(sdis_data_get(data), param, sizeof(struct fluid)); -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; + /* Setup the fluid shader */ + fluid_shader.calorific_capacity = fluid_get_cp; + fluid_shader.volumic_mass = fluid_get_rho; + fluid_shader.temperature = fluid_get_temperature; + + /* Create the fluid medium */ + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid)); + OK(sdis_data_ref_put(data)); + + return fluid; } -static double -solid_get_temperature - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +static struct sdis_medium* +create_solid + (struct sdis_device* dev, + const struct solid* param) { - CHK(data != NULL && vtx != NULL); - return ((const struct solid*)sdis_data_cget(data))->temperature; + struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; + struct sdis_data* data = NULL; + struct sdis_medium* solid = NULL; + + CHK(param != NULL); + + /* Copy the solid parameters into the Stardis memory space */ + OK(sdis_data_create + (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); + memcpy(sdis_data_get(data), param, sizeof(struct solid)); + + /* Setup the solid shader */ + solid_shader.calorific_capacity = solid_get_cp; + solid_shader.thermal_conductivity = solid_get_lambda; + solid_shader.volumic_mass = solid_get_rho; + solid_shader.delta = solid_get_delta; + solid_shader.temperature = solid_get_temperature; + + /* Create the solid medium */ + OK(sdis_solid_create(dev, &solid_shader, data, &solid)); + OK(sdis_data_ref_put(data)); + + return solid; } /******************************************************************************* - * Interface + * Interfaces ******************************************************************************/ struct interf { double hc; @@ -243,50 +355,76 @@ static const struct interf INTERF_NULL = { 0, 0, 0, SDIS_TEMPERATURE_NONE, SDIS_TEMPERATURE_NONE }; -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; -} +#define INTERFACE_GETTER(Prop) \ + static double \ + interface_get_##Prop \ + (const struct sdis_interface_fragment* frag, \ + struct sdis_data* data) \ + { \ + CHK(data && frag); \ + return ((const struct interf*)sdis_data_cget(data))->Prop; \ + } +INTERFACE_GETTER(hc) +INTERFACE_GETTER(temperature) +INTERFACE_GETTER(reference_temperature) +#undef INTERFACE_GETTER + +#define INTERFACE_GETTER(Prop) \ + static double \ + interface_get_##Prop \ + (const struct sdis_interface_fragment* frag, \ + const unsigned source_id, \ + struct sdis_data* data) \ + { \ + CHK(data && frag); \ + (void)source_id; \ + return ((const struct interf*)sdis_data_cget(data))->Prop; \ + } +INTERFACE_GETTER(epsilon) +INTERFACE_GETTER(specular_fraction) +#undef INTERFACE_GETTER -static double -interface_get_emissivity - (const struct sdis_interface_fragment* frag, - const unsigned source_id, - struct sdis_data* data) +static struct sdis_interface* +create_interface + (struct sdis_device* dev, + struct sdis_medium* mdm_front, + struct sdis_medium* mdm_back, + const struct interf* param) { - (void)source_id; - CHK(data != NULL && frag != NULL); - return ((const struct interf*)sdis_data_cget(data))->epsilon; -} + struct sdis_interface_shader interface_shader = SDIS_INTERFACE_SHADER_NULL; + struct sdis_data* data = NULL; + struct sdis_interface* interf = NULL; -static double -interface_get_specular_fraction - (const struct sdis_interface_fragment* frag, - const unsigned source_id, - struct sdis_data* data) -{ - (void)source_id; - CHK(data != NULL && frag != NULL); - return ((const struct interf*)sdis_data_cget(data))->specular_fraction; -} + CHK(mdm_front != NULL); + CHK(mdm_back != NULL); -static double -interface_get_temperature - (const struct sdis_interface_fragment* frag, struct sdis_data* data) -{ - CHK(data != NULL && frag != NULL); - return ((const struct interf*)sdis_data_cget(data))->temperature; -} + /* Copy the interface parameters into the Stardis memory space */ + OK(sdis_data_create + (dev, sizeof(struct interf), ALIGNOF(struct interf), NULL, &data)); + memcpy(sdis_data_get(data), param, sizeof(struct interf)); -static double -interface_get_reference_temperature - (const struct sdis_interface_fragment* frag, struct sdis_data* data) -{ - CHK(data != NULL && frag != NULL); - return ((const struct interf*)sdis_data_cget(data))->reference_temperature; + /* Setup the interface shader */ + interface_shader.convection_coef = interface_get_hc; + interface_shader.front.temperature = interface_get_temperature; + interface_shader.back.temperature = interface_get_temperature; + if(sdis_medium_get_type(mdm_front) == SDIS_FLUID) { + interface_shader.front.emissivity = interface_get_epsilon; + interface_shader.front.specular_fraction = interface_get_specular_fraction; + interface_shader.front.reference_temperature = + interface_get_reference_temperature; + } + if(sdis_medium_get_type(mdm_back) == SDIS_FLUID) { + interface_shader.back.emissivity = interface_get_epsilon; + interface_shader.back.specular_fraction = interface_get_specular_fraction; + interface_shader.back.reference_temperature = + interface_get_reference_temperature; + } + /* Create the interface */ + OK(sdis_interface_create + (dev, mdm_front, mdm_back, &interface_shader, data, &interf)); + OK(sdis_data_ref_put(data)); + + return interf; } /******************************************************************************* @@ -339,254 +477,289 @@ create_radenv } /******************************************************************************* - * Helper functions + * Scene ******************************************************************************/ -static void -create_solid +static struct sdis_scene* +create_scene (struct sdis_device* dev, - const struct solid* param, - struct sdis_medium** solid) + struct sdis_radiative_env* radenv, + struct geometry* geom) { - struct sdis_data* data = NULL; - struct solid* solid_args = NULL; - struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + struct sdis_scene* scn = NULL; + size_t ntris = 0; + size_t npos = 0; + CHK(geom); - CHK(param != NULL); - CHK(solid != NULL); + ntris = sa_size(geom->indices) / 3; /* #primitives */ + npos = sa_size(geom->positions) / 3; /* #positions */ - /* Copy the solid parameters into the Stardis memory space */ - OK(sdis_data_create - (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); - solid_args = sdis_data_get(data); - memcpy(solid_args, param, sizeof(struct solid)); + scn_args.get_indices = geometry_get_indices; + scn_args.get_interface = geometry_get_interface; + scn_args.get_position = geometry_get_position; + scn_args.nprimitives = ntris; + scn_args.nvertices = npos; + scn_args.t_range[0] = 300; + scn_args.t_range[1] = 350; + scn_args.radenv = radenv; + scn_args.context = geom; - /* Setup the solid shader */ - solid_shader.calorific_capacity = solid_get_calorific_capacity; - 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_get_delta; - solid_shader.temperature = solid_get_temperature; + OK(sdis_scene_create(dev, &scn_args, &scn)); + return scn; +} - /* Create the solid medium */ - OK(sdis_solid_create(dev, &solid_shader, data, solid)); +/******************************************************************************* + * Rendering point of view + ******************************************************************************/ +static struct sdis_camera* +create_camera(struct sdis_device* dev) +{ + const double pos[3] = {3, 3, 3}; + const double tgt[3] = {0, 0, 0}; + const double up[3] = {0, 0, 1}; + struct sdis_camera* cam = NULL; - /* Release the ownership onto the Stardis memory space storing the solid - * parameters */ - OK(sdis_data_ref_put(data)); + OK(sdis_camera_create(dev, &cam)); + OK(sdis_camera_set_proj_ratio(cam, (double)IMG_WIDTH/(double)IMG_HEIGHT)); + OK(sdis_camera_set_fov(cam, MDEG2RAD(70))); + OK(sdis_camera_look_at(cam, pos, tgt, up)); + return cam; } +/******************************************************************************* + * Draw the scene + ******************************************************************************/ static void -create_fluid - (struct sdis_device* dev, - const struct fluid* param, - struct sdis_medium** fluid) +check_pixel(const struct sdis_estimator* pixel) { - struct sdis_data* data = NULL; - struct fluid* fluid_args = NULL; - struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; - - CHK(param != NULL); - CHK(fluid != NULL); - - /* Copy the fluid parameters into the Stardis memory space */ - OK(sdis_data_create - (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); - fluid_args = sdis_data_get(data); - memcpy(fluid_args, param, sizeof(struct fluid)); - - /* Setup the fluid shader */ - fluid_shader.calorific_capacity = fluid_get_calorific_capacity; - fluid_shader.volumic_mass = fluid_get_volumic_mass; - fluid_shader.temperature = fluid_get_temperature; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_mc time = SDIS_MC_NULL; + size_t nfails = 0; + size_t nreals = 0; - /* Create the fluid medium */ - OK(sdis_fluid_create(dev, &fluid_shader, data, fluid)); + OK(sdis_estimator_get_realisation_count(pixel, &nreals)); + OK(sdis_estimator_get_failure_count(pixel, &nfails)); + OK(sdis_estimator_get_temperature(pixel, &T)); + OK(sdis_estimator_get_realisation_time(pixel, &time)); - /* Release the ownership onto the Stardis memory space storing the fluid - * parameters */ - OK(sdis_data_ref_put(data)); + CHK(nreals + nfails == SPP); + CHK(T.E > 0); + CHK(time.E > 0); } static void -create_interface - (struct sdis_device* dev, - struct sdis_medium* mdm_front, - struct sdis_medium* mdm_back, - const struct interf* param, - struct sdis_interface** interf) +check_image(const struct sdis_estimator_buffer* img) { - struct sdis_data* data = NULL; - struct interf* interface_args = NULL; - struct sdis_interface_shader interface_shader = SDIS_INTERFACE_SHADER_NULL; - - CHK(mdm_front != NULL); - CHK(mdm_back != NULL); - CHK(param != NULL); - CHK(interf != NULL); - - /* Copy the interface parameters into the Stardis memory space */ - OK(sdis_data_create - (dev, sizeof(struct interf), ALIGNOF(struct interf), NULL, &data)); - interface_args = sdis_data_get(data); - memcpy(interface_args, param, sizeof(struct interf)); - - /* Setup the interface shader */ - interface_shader.convection_coef = interface_get_convection_coef; - interface_shader.front.temperature = interface_get_temperature; - interface_shader.back.temperature = interface_get_temperature; - if(sdis_medium_get_type(mdm_front) == SDIS_FLUID) { - interface_shader.front.emissivity = interface_get_emissivity; - interface_shader.front.specular_fraction = interface_get_specular_fraction; - interface_shader.front.reference_temperature = - interface_get_reference_temperature; - } - if(sdis_medium_get_type(mdm_back) == SDIS_FLUID) { - interface_shader.back.emissivity = interface_get_emissivity; - interface_shader.back.specular_fraction = interface_get_specular_fraction; - interface_shader.back.reference_temperature = - interface_get_reference_temperature; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_mc time = SDIS_MC_NULL; + const struct sdis_estimator* pixel = NULL; + struct ssp_rng* rng_state = NULL; + size_t definition[2]; + size_t nreals, nfails; + size_t x, y; + + BA(sdis_estimator_buffer_get_realisation_count(NULL, &nreals)); + BA(sdis_estimator_buffer_get_realisation_count(img, NULL)); + OK(sdis_estimator_buffer_get_realisation_count(img, &nreals)); + + BA(sdis_estimator_buffer_get_failure_count(NULL, &nfails)); + BA(sdis_estimator_buffer_get_failure_count(img, NULL)); + OK(sdis_estimator_buffer_get_failure_count(img, &nfails)); + + BA(sdis_estimator_buffer_get_temperature(NULL, &T)); + BA(sdis_estimator_buffer_get_temperature(img, NULL)); + OK(sdis_estimator_buffer_get_temperature(img, &T)); + + BA(sdis_estimator_buffer_get_realisation_time(NULL, &time)); + BA(sdis_estimator_buffer_get_realisation_time(img, NULL)); + OK(sdis_estimator_buffer_get_realisation_time(img, &time)); + + BA(sdis_estimator_buffer_get_rng_state(NULL, &rng_state)); + BA(sdis_estimator_buffer_get_rng_state(img, NULL)); + OK(sdis_estimator_buffer_get_rng_state(img, &rng_state)); + + CHK(nreals + nfails == IMG_WIDTH*IMG_HEIGHT*SPP); + + fprintf(stderr, "Overall temperature ~ %g +/- %g\n", T.E, T.SE); + fprintf(stderr, "Time per realisation (in usec) ~ %g +/- %g\n", time.E, time.SE); + fprintf(stderr, "#failures = %lu/%lu\n", + (unsigned long)nfails, (unsigned long)(IMG_WIDTH*IMG_HEIGHT*SPP)); + + BA(sdis_estimator_buffer_get_definition(NULL, definition)); + BA(sdis_estimator_buffer_get_definition(img, NULL)); + OK(sdis_estimator_buffer_get_definition(img, definition)); + CHK(definition[0] == IMG_WIDTH); + CHK(definition[1] == IMG_HEIGHT); + + BA(sdis_estimator_buffer_at(NULL, 0, 0, &pixel)); + BA(sdis_estimator_buffer_at(img, IMG_WIDTH+1,0 , &pixel)); + BA(sdis_estimator_buffer_at(img, 0, IMG_HEIGHT+1, &pixel)); + BA(sdis_estimator_buffer_at(img, 0, 0, NULL)); + + /* Pixels ordered by row */ + FOR_EACH(y, 0, IMG_HEIGHT) { + FOR_EACH(x, 0, IMG_WIDTH) { + OK(sdis_estimator_buffer_at(img, x, y, &pixel)); + check_pixel(pixel); + } } - /* Create the interface */ - OK(sdis_interface_create - (dev, mdm_front, mdm_back, &interface_shader, data, interf)); +} - /* Release the ownership onto the Stardis memory space storing the interface - * parameters */ - OK(sdis_data_ref_put(data)); +/* Check that the images, although compatible from an estimation point of view, + * are not the same. This should be the case if different random sequences are + * used for each image */ +static void +check_image_difference + (const struct sdis_estimator_buffer* img0, + const struct sdis_estimator_buffer* img1) +{ + struct sdis_mc T0 = SDIS_MC_NULL; + struct sdis_mc T1 = SDIS_MC_NULL; + size_t definition0[2]; + size_t definition1[2]; + size_t nreals0, nfails0; + size_t nreals1, nfails1; + + OK(sdis_estimator_buffer_get_realisation_count(img0, &nreals0)); + OK(sdis_estimator_buffer_get_realisation_count(img1, &nreals1)); + + OK(sdis_estimator_buffer_get_failure_count(img0, &nfails0)); + OK(sdis_estimator_buffer_get_failure_count(img1, &nfails1)); + CHK(nreals0 + nfails0 == nreals1 + nfails1); + + OK(sdis_estimator_buffer_get_definition(img0, definition0)); + OK(sdis_estimator_buffer_get_definition(img1, definition1)); + CHK(definition0[0] == definition1[0]); + CHK(definition0[1] == definition1[1]); + + OK(sdis_estimator_buffer_get_temperature(img0, &T0)); + OK(sdis_estimator_buffer_get_temperature(img1, &T1)); + CHK(T0.E != T1.E || (T0.SE == 0 && T1.SE == 0)); + CHK(T0.E + 3*T0.SE >= T1.E - 3*T1.SE); + CHK(T0.E - 3*T0.SE <= T1.E + 3*T1.SE); } +/* Write an image in htrdr-image(5) format */ static void -geometry_add_shape - (struct geometry* geom, - const double* positions, - const size_t nverts, - const size_t* indices, - const size_t nprims, - const double transform[12], /* May be NULL <=> no transformation */ - struct sdis_interface* interf) +write_image(FILE* stream, struct sdis_estimator_buffer* img) { - struct map_interf* geom_interf = NULL; - size_t nverts_prev = 0; - size_t i; + size_t x, y; - CHK(geom != NULL); - CHK(positions != NULL); - CHK(indices != NULL); - CHK(nverts != 0); - CHK(nprims != 0); - CHK(interf != NULL); + /* Header */ + fprintf(stream, "%d %d\n", IMG_WIDTH, IMG_HEIGHT); - /* Save the previous number of vertices/primitives of the geometry */ - nverts_prev = sa_size(geom->positions) / 3; + /* Pixels ordered by row */ + FOR_EACH(y, 0, IMG_HEIGHT) { + FOR_EACH(x, 0, IMG_WIDTH) { + struct sdis_mc T = SDIS_MC_NULL; + const struct sdis_estimator* pixel = NULL; - /* Add the vertices */ - FOR_EACH(i, 0, nverts) { - double* pos = sa_add(geom->positions, 3); - d3_set(pos, positions + i*3); - if(transform) { - d33_muld3(pos, transform, pos); - d3_add(pos, transform+9, pos); + OK(sdis_estimator_buffer_at(img, x, y, &pixel)); + OK(sdis_estimator_get_temperature(pixel, &T)); + fprintf(stream, "%g %g 0 0 0 0 0 0\n", T.E, T.SE); } } +} - /* Add the indices */ - FOR_EACH(i, 0, nprims) { - sa_push(geom->indices, indices[i*3+0] + nverts_prev); - sa_push(geom->indices, indices[i*3+1] + nverts_prev); - sa_push(geom->indices, indices[i*3+2] + nverts_prev); - } - - geom_interf = sa_add(geom->interfaces, 1); - geom_interf->key = sa_size(geom->indices) / 3 - 1; - geom_interf->interf = interf; +static void +invalid_draw(struct sdis_scene* scn, struct sdis_camera* cam) +{ + struct sdis_solve_camera_args args = SDIS_SOLVE_CAMERA_ARGS_DEFAULT; + struct sdis_estimator_buffer* img = NULL; + + CHK(cam && scn); + + args.cam = cam; + args.time_range[0] = INF; + args.time_range[1] = INF; + args.image_definition[0] = IMG_WIDTH; + args.image_definition[1] = IMG_HEIGHT; + args.spp = SPP; + + BA(sdis_solve_camera(NULL, &args, &img)); + BA(sdis_solve_camera(scn, NULL, &img)); + BA(sdis_solve_camera(scn, &args, NULL)); + + /* Invalid camera */ + args.cam = NULL; + BA(sdis_solve_camera(scn, &args, &img)); + args.cam = cam; + + /* Invald time range */ + args.time_range[0] = args.time_range[1] = -1; + BA(sdis_solve_camera(scn, &args, &img)); + args.time_range[0] = 1; + BA(sdis_solve_camera(scn, &args, &img)); + args.time_range[1] = 0; + BA(sdis_solve_camera(scn, &args, &img)); + args.time_range[0] = args.time_range[1] = INF; + + /* Invalid image definition */ + args.image_definition[0] = 0; + BA(sdis_solve_camera(scn, &args, &img)); + args.image_definition[0] = IMG_WIDTH; + args.image_definition[1] = 0; + BA(sdis_solve_camera(scn, &args, &img)); + args.image_definition[1] = IMG_HEIGHT; + + /* Invalid number of samples per pixel */ + args.spp = 0; + BA(sdis_solve_camera(scn, &args, &img)); + args.spp = SPP; } static void -dump_image(const struct sdis_estimator_buffer* buf) +draw + (struct sdis_scene* scn, + struct sdis_camera* cam, + const int is_master_process) { - struct image img; - double* temps = NULL; - double Tmax = -DBL_MAX; - double Tmin = DBL_MAX; - double norm; - size_t definition[2]; - size_t ix, iy; - - CHK(buf != NULL); - OK(sdis_estimator_buffer_get_definition(buf, definition)); - - temps = mem_alloc(definition[0]*definition[1]*sizeof(double)); - CHK(temps != NULL); - - /* Check the results validity */ - FOR_EACH(iy, 0, definition[1]) { - FOR_EACH(ix, 0, definition[0]) { - const struct sdis_estimator* estimator; - struct sdis_mc T; - struct sdis_mc time; - size_t nreals; - size_t nfails; - - BA(sdis_estimator_buffer_at(NULL, ix, iy, &estimator)); - BA(sdis_estimator_buffer_at(buf, definition[0]+1, iy, &estimator)); - BA(sdis_estimator_buffer_at(buf, ix, definition[1]+1, &estimator)); - BA(sdis_estimator_buffer_at(buf, ix, iy, NULL)); - OK(sdis_estimator_buffer_at(buf, ix, iy, &estimator)); - - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - - CHK(nreals + nfails == SPP); - CHK(T.E > 0); - CHK(time.E > 0); - } - } + struct sdis_solve_camera_args args = SDIS_SOLVE_CAMERA_ARGS_DEFAULT; + struct sdis_estimator_buffer* img0 = NULL; + struct sdis_estimator_buffer* img1 = NULL; + struct ssp_rng* rng = NULL; - /* Compute the per pixel temperature */ - FOR_EACH(iy, 0, definition[1]) { - double* row = temps + iy * definition[0]; - FOR_EACH(ix, 0, definition[0]) { - const struct sdis_estimator* estimator; - struct sdis_mc T; + args.cam = cam; + args.time_range[0] = INF; + args.time_range[1] = INF; + args.image_definition[0] = IMG_WIDTH; + args.image_definition[1] = IMG_HEIGHT; + args.spp = SPP; - OK(sdis_estimator_buffer_at(buf, ix, iy, &estimator)); - OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_solve_camera(scn, &args, &img0)); - row[ix] = T.E; - Tmax = MMAX(row[ix], Tmax); - Tmin = MMIN(row[ix], Tmin); - } - } - if(Tmax != Tmin) { - norm = Tmax - Tmin; + if(!is_master_process) { + CHK(img0 == NULL); } else { - Tmin = 0; - norm = 1; + check_image(img0); + write_image(stdout, img0); } - /* Allocate the image memory space */ - OK(image_init(NULL, &img)); - OK(image_setup(&img, IMG_WIDTH, IMG_HEIGHT, IMG_WIDTH*3, IMAGE_RGB8, NULL)); - - FOR_EACH(iy, 0, definition[1]) { - const double* src_row = temps + iy*definition[0]; - char* dst_row = img.pixels + iy*img.pitch; - FOR_EACH(ix, 0, definition[0]) { - unsigned char* pixels = (unsigned char*) - (dst_row + ix * sizeof_image_format(img.format)); - const unsigned char T = (unsigned char) - ((src_row[ix] - Tmin)/ norm * 255.0); - pixels[0] = T; - pixels[1] = T; - pixels[2] = T; - } + /* Provide a RNG state and check that the results, although compatible, are + * not the same */ + OK(ssp_rng_create(NULL, SSP_RNG_THREEFRY, &rng)); + OK(ssp_rng_discard(rng, 3141592653589)); /* Move the RNG state */ + args.rng_state = rng; + args.rng_type = SSP_RNG_TYPE_NULL; + OK(sdis_solve_camera(scn, &args, &img1)); + if(is_master_process) { + check_image_difference(img0, img1); + OK(sdis_estimator_buffer_ref_put(img1)); + } + OK(ssp_rng_ref_put(rng)); + + /* Change the RNG type and check that the results, although compatible, are + * not the same */ + args.rng_state = NULL; + args.rng_type = SDIS_SOLVE_CAMERA_ARGS_DEFAULT.rng_type == SSP_RNG_THREEFRY + ? SSP_RNG_MT19937_64 : SSP_RNG_THREEFRY; + OK(sdis_solve_camera(scn, &args, &img1)); + if(is_master_process) { + check_image_difference(img0, img1); + OK(sdis_estimator_buffer_ref_put(img1)); } - OK(image_write_ppm_stream(&img, 0/*binary?*/, stdout)); - image_release(&img); - mem_rm(temps); + + if(is_master_process) OK(sdis_estimator_buffer_ref_put(img0)); } /******************************************************************************* @@ -596,56 +769,44 @@ int main(int argc, char** argv) { struct geometry geom = GEOMETRY_NULL; - struct s3dut_mesh* msh = NULL; - struct s3dut_mesh_data msh_data; - struct sdis_mc T = SDIS_MC_NULL; - struct sdis_mc T2 = SDIS_MC_NULL; - struct sdis_mc time = SDIS_MC_NULL; struct sdis_camera* cam = NULL; struct sdis_device* dev = NULL; - struct sdis_estimator_buffer* buf = NULL; - struct sdis_estimator_buffer* buf2 = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* fluid0 = NULL; struct sdis_medium* fluid1 = NULL; + struct sdis_medium* fluid2 = NULL; struct sdis_interface* interf0 = NULL; struct sdis_interface* interf1 = NULL; + struct sdis_interface* interf2 = NULL; struct sdis_radiative_env* radenv = NULL; struct sdis_scene* scn = NULL; - struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; - struct sdis_solve_camera_args solve_args = SDIS_SOLVE_CAMERA_ARGS_DEFAULT; - struct ssp_rng* rng = NULL; - struct ssp_rng* rng_state = NULL; struct fluid fluid_args = FLUID_NULL; struct solid solid_args = SOLID_NULL; struct interf interface_args = INTERF_NULL; - struct fluid* pfluid_args = NULL; - struct radenv* pradenv_args = NULL; - size_t ntris, npos; - size_t nreals, nfails; - size_t definition[2]; - double pos[3]; - double tgt[3]; - double up[3]; int is_master_process; (void)argc, (void)argv; create_default_device(&argc, &argv, &is_master_process, &dev); radenv = create_radenv(dev, 300, 300); - pradenv_args = sdis_data_get(sdis_radiative_env_get_data(radenv)); /* Create the fluid0 */ fluid_args.temperature = 350; fluid_args.rho = 0; fluid_args.cp = 0; - create_fluid(dev, &fluid_args, &fluid0); + fluid0 = create_fluid(dev, &fluid_args); /* Create the fluid1 */ fluid_args.temperature = 300; fluid_args.rho = 0; fluid_args.cp = 0; - create_fluid(dev, &fluid_args, &fluid1); + fluid1 = create_fluid(dev, &fluid_args); + + /* Create the fluid2 */ + fluid_args.temperature = 280; + fluid_args.rho = 0; + fluid_args.cp = 0; + fluid2 = create_fluid(dev, &fluid_args); /* Create the solid medium */ solid_args.cp = 1.0; @@ -653,14 +814,14 @@ main(int argc, char** argv) solid_args.rho = 1.0; solid_args.delta = 1.0/20.0; solid_args.temperature = SDIS_TEMPERATURE_NONE; - create_solid(dev, &solid_args, &solid); + solid = create_solid(dev, &solid_args); /* Create the fluid0/solid interface */ interface_args.hc = 1; interface_args.epsilon = 0; interface_args.specular_fraction = 0; interface_args.temperature = SDIS_TEMPERATURE_NONE; - create_interface(dev, solid, fluid0, &interface_args, &interf0); + interf0 = create_interface(dev, solid, fluid0, &interface_args); /* Create the fluid1/solid interface */ interface_args.hc = 0.1; @@ -668,178 +829,45 @@ main(int argc, char** argv) interface_args.specular_fraction = 1; interface_args.temperature = SDIS_TEMPERATURE_NONE; interface_args.reference_temperature = 300; - create_interface(dev, fluid1, solid, &interface_args, &interf1); - - /* Setup the cube geometry */ - OK(s3dut_create_cuboid(NULL, 2, 2, 2, &msh)); - OK(s3dut_mesh_get_data(msh, &msh_data)); - geometry_add_shape(&geom, msh_data.positions, msh_data.nvertices, - msh_data.indices, msh_data.nprimitives, NULL, interf1); - OK(s3dut_mesh_ref_put(msh)); + interf1 = create_interface(dev, fluid1, solid, &interface_args); - /* Setup the sphere geometry */ - OK(s3dut_create_sphere(NULL, 0.5, 32, 16, &msh)); - OK(s3dut_mesh_get_data(msh, &msh_data)); - geometry_add_shape(&geom, msh_data.positions, msh_data.nvertices, - msh_data.indices, msh_data.nprimitives, NULL, interf0); - OK(s3dut_mesh_ref_put(msh)); + /* Create the fluid2/ground interface */ + interface_args.hc = 0.2; + interface_args.epsilon = 1; + interface_args.specular_fraction = 1; + interface_args.temperature = SDIS_TEMPERATURE_NONE; + interface_args.reference_temperature = 300; + interf2 = create_interface(dev, fluid2, solid, &interface_args); - /* Setup the scene */ - ntris = sa_size(geom.indices) / 3; /* #primitives */ - npos = sa_size(geom.positions) / 3; /* #positions */ - scn_args.get_indices = geometry_get_indices; - scn_args.get_interface = geometry_get_interface; - scn_args.get_position = geometry_get_position; - scn_args.nprimitives = ntris; - scn_args.nvertices = npos; - scn_args.t_range[0] = 300; - scn_args.t_range[1] = 350; - scn_args.radenv = radenv; - scn_args.context = &geom; - OK(sdis_scene_create(dev, &scn_args, &scn)); + add_cube(&geom, interf1); + add_sphere(&geom, interf0); + add_ground(&geom, interf2); #if 0 dump_mesh(stdout, geom.positions, sa_size(geom.positions)/3, geom.indices, sa_size(geom.indices)/3); #endif - /* Setup the camera */ - d3(pos, 3, 3, 3); - d3(tgt, 0, 0, 0); - d3(up, 0, 0, 1); - OK(sdis_camera_create(dev, &cam)); - OK(sdis_camera_set_proj_ratio(cam, (double)IMG_WIDTH/(double)IMG_HEIGHT)); - OK(sdis_camera_set_fov(cam, MDEG2RAD(70))); - OK(sdis_camera_look_at(cam, pos, tgt, up)); - -#if 0 - dump_mesh(stdout, geom.positions, npos, geom.indices, ntris); - exit(0); -#endif - solve_args.cam = cam; - solve_args.time_range[0] = INF; - solve_args.time_range[0] = INF; - solve_args.image_definition[0] = IMG_WIDTH; - solve_args.image_definition[1] = IMG_HEIGHT; - solve_args.spp = SPP; - - BA(sdis_solve_camera(NULL, &solve_args, &buf)); - BA(sdis_solve_camera(scn, NULL, &buf)); - BA(sdis_solve_camera(scn, &solve_args, NULL)); - solve_args.cam = NULL; - BA(sdis_solve_camera(scn, &solve_args, &buf)); - solve_args.cam = cam; - pradenv_args->temperature = SDIS_TEMPERATURE_NONE; - BA(sdis_solve_camera(scn, &solve_args, &buf)); - pradenv_args->temperature = 300; - solve_args.time_range[0] = solve_args.time_range[1] = -1; - BA(sdis_solve_camera(scn, &solve_args, &buf)); - solve_args.time_range[0] = 1; - BA(sdis_solve_camera(scn, &solve_args, &buf)); - solve_args.time_range[1] = 0; - BA(sdis_solve_camera(scn, &solve_args, &buf)); - solve_args.time_range[0] = solve_args.time_range[1] = INF; - - /* Launch the simulation */ - OK(sdis_solve_camera(scn, &solve_args, &buf)); + scn = create_scene(dev, radenv, &geom); + cam = create_camera(dev); - if(!is_master_process) { - CHK(buf == NULL); - } else { - BA(sdis_estimator_buffer_get_realisation_count(NULL, &nreals)); - BA(sdis_estimator_buffer_get_realisation_count(buf, NULL)); - OK(sdis_estimator_buffer_get_realisation_count(buf, &nreals)); - - BA(sdis_estimator_buffer_get_failure_count(NULL, &nfails)); - BA(sdis_estimator_buffer_get_failure_count(buf, NULL)); - OK(sdis_estimator_buffer_get_failure_count(buf, &nfails)); - - BA(sdis_estimator_buffer_get_temperature(NULL, &T)); - BA(sdis_estimator_buffer_get_temperature(buf, NULL)); - OK(sdis_estimator_buffer_get_temperature(buf, &T)); - - BA(sdis_estimator_buffer_get_realisation_time(NULL, &time)); - BA(sdis_estimator_buffer_get_realisation_time(buf, NULL)); - OK(sdis_estimator_buffer_get_realisation_time(buf, &time)); - - BA(sdis_estimator_buffer_get_rng_state(NULL, &rng_state)); - BA(sdis_estimator_buffer_get_rng_state(buf, NULL)); - OK(sdis_estimator_buffer_get_rng_state(buf, &rng_state)); - - CHK(nreals + nfails == IMG_WIDTH*IMG_HEIGHT*SPP); - - fprintf(stderr, "Overall temperature ~ %g +/- %g\n", T.E, T.SE); - fprintf(stderr, "Time per realisation (in usec) ~ %g +/- %g\n", time.E, time.SE); - fprintf(stderr, "#failures = %lu/%lu\n", - (unsigned long)nfails, (unsigned long)(IMG_WIDTH*IMG_HEIGHT*SPP)); - - BA(sdis_estimator_buffer_get_definition(NULL, definition)); - BA(sdis_estimator_buffer_get_definition(buf, NULL)); - OK(sdis_estimator_buffer_get_definition(buf, definition)); - CHK(definition[0] == IMG_WIDTH); - CHK(definition[1] == IMG_HEIGHT); - - /* Write the image */ - dump_image(buf); - OK(sdis_estimator_buffer_ref_put(buf)); - } - - /* Check RNG type */ - solve_args.rng_state = NULL; - solve_args.rng_type = SSP_RNG_TYPE_NULL; - BA(sdis_solve_camera(scn, &solve_args, &buf2)); - solve_args.rng_type = - SDIS_SOLVE_CAMERA_ARGS_DEFAULT.rng_type == SSP_RNG_THREEFRY - ? SSP_RNG_MT19937_64 : SSP_RNG_THREEFRY; - OK(sdis_solve_camera(scn, &solve_args, &buf2)); - if(is_master_process) { - OK(sdis_estimator_buffer_get_temperature(buf2, &T2)); - CHK(T.E != T2.E || (T2.SE == 0 && T.SE ==0)); - CHK(T2.E + 3*T2.SE >= T.E - 3*T.SE - && T2.E - 3*T2.SE <= T.E + 3*T.SE); - OK(sdis_estimator_buffer_ref_put(buf2)); - } - - /* Check the RNG state */ - OK(ssp_rng_create(NULL, SSP_RNG_THREEFRY, &rng)); - OK(ssp_rng_discard(rng, 3141592653589)); /* Move the RNG state */ - solve_args.rng_state = rng; - solve_args.rng_type = SSP_RNG_TYPE_NULL; - OK(sdis_solve_camera(scn, &solve_args, &buf2)); - OK(ssp_rng_ref_put(rng)); - if(is_master_process) { - OK(sdis_estimator_buffer_get_temperature(buf2, &T2)); - CHK(T.E != T2.E || (T2.SE == 0 && T.SE == 0)); - CHK(T2.E + 3*T2.SE >= T.E - 3*T.SE - && T2.E - 3*T2.SE <= T.E + 3*T.SE); - OK(sdis_estimator_buffer_ref_put(buf2)); - } - - /* Restore args */ - solve_args.rng_state = SDIS_SOLVE_CAMERA_ARGS_DEFAULT.rng_state; - solve_args.rng_type = SDIS_SOLVE_CAMERA_ARGS_DEFAULT.rng_type; - - pfluid_args = sdis_data_get(sdis_medium_get_data(fluid1)); - pfluid_args->temperature = SDIS_TEMPERATURE_NONE; - - /* Check simulation error handling */ - BA(sdis_solve_camera(scn, &solve_args, &buf)); - solve_args.register_paths = SDIS_HEAT_PATH_ALL; - BA(sdis_solve_camera(scn, &solve_args, &buf)); + invalid_draw(scn, cam); + draw(scn, cam, is_master_process); /* Release memory */ OK(sdis_medium_ref_put(solid)); OK(sdis_medium_ref_put(fluid0)); OK(sdis_medium_ref_put(fluid1)); + OK(sdis_medium_ref_put(fluid2)); OK(sdis_radiative_env_ref_put(radenv)); OK(sdis_scene_ref_put(scn)); OK(sdis_camera_ref_put(cam)); OK(sdis_interface_ref_put(interf0)); OK(sdis_interface_ref_put(interf1)); + OK(sdis_interface_ref_put(interf2)); free_default_device(dev); geometry_release(&geom); CHK(mem_allocated_size() == 0); return 0; } - diff --git a/src/test_sdis_solve_probe_boundary_list.c b/src/test_sdis_solve_probe_boundary_list.c @@ -33,6 +33,7 @@ * estimate the temperature at the sphere boundary at several probe points. We * should find by Monte Carlo the temperature of the trilinear profile at the * position of the probe. It's the test. + * * /\ <-- T(x,y,z) * ___/ \___ * T(z) \ __ / diff --git a/src/test_sdis_utils.h b/src/test_sdis_utils.h @@ -195,6 +195,7 @@ static const struct sdis_solid_shader DUMMY_SOLID_SHADER = { dummy_medium_getter, /* Delta */ dummy_medium_getter, /* Volumic power */ dummy_medium_getter, /* Temperature */ + NULL, /* sample path */ 0 /* Initial time */ };