stardis-solver

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

commit 72bbe8659c3a7f6305495e5ce26937a142b71c66
parent e071677ae4fa254c9a238b5a7aa4105c294c3ecb
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Sun, 21 Apr 2024 19:23:23 +0200

Merge branch 'release_0.15'

Diffstat:
M.gitignore | 19++++++++++++-------
AMakefile | 516+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MREADME.md | 776++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Dcmake/CMakeLists.txt | 304-------------------------------------------------------------------------------
Aconfig.mk | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amake.sh | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asdis.pc.in | 18++++++++++++++++++
Msrc/sdis.c | 177++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/sdis.h | 489+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/sdis_Xd_begin.h | 78++++++++++++++++++++++--------------------------------------------------------
Msrc/sdis_Xd_end.h | 8+++++++-
Msrc/sdis_c.h | 35++++++++++++++++++++++++++++++++---
Msrc/sdis_camera.c | 2+-
Msrc/sdis_camera.h | 2+-
Msrc/sdis_data.c | 2+-
Msrc/sdis_device.c | 53++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/sdis_device_c.h | 7++++++-
Msrc/sdis_estimator.c | 2+-
Msrc/sdis_estimator_buffer.c | 7++++++-
Asrc/sdis_estimator_buffer_X_obs.h | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_estimator_buffer_c.h | 22+++++++++++++++++++++-
Msrc/sdis_estimator_c.h | 5++---
Msrc/sdis_green.c | 339+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/sdis_green.h | 14++++++++++----
Msrc/sdis_heat_path.c | 8+-------
Msrc/sdis_heat_path.h | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/sdis_heat_path_boundary.c | 10+++++-----
Msrc/sdis_heat_path_boundary_Xd.h | 4++--
Msrc/sdis_heat_path_boundary_Xd_c.h | 251++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Dsrc/sdis_heat_path_boundary_Xd_fixed_flux.h | 135-------------------------------------------------------------------------------
Asrc/sdis_heat_path_boundary_Xd_handle_external_net_flux.h | 581+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_heat_path_boundary_Xd_solid_fluid_picard1.h | 81+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/sdis_heat_path_boundary_Xd_solid_fluid_picardN.h | 17+++++++++++++----
Msrc/sdis_heat_path_boundary_Xd_solid_solid.h | 2+-
Msrc/sdis_heat_path_boundary_c.h | 79++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Asrc/sdis_heat_path_conductive.c | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/sdis_heat_path_conductive_Xd.h | 541-------------------------------------------------------------------------------
Asrc/sdis_heat_path_conductive_c.h | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_conductive_delta_sphere_Xd.h | 481+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_heat_path_conductive_wos_Xd.h | 669+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_heat_path_convective_Xd.h | 21+++++++++++----------
Msrc/sdis_heat_path_radiative_Xd.h | 83++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/sdis_interface.c | 2+-
Msrc/sdis_interface_c.h | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/sdis_log.c | 2+-
Msrc/sdis_log.h | 2+-
Msrc/sdis_medium.c | 52+++++++++++++++++++++++++---------------------------
Msrc/sdis_medium_c.h | 36+++---------------------------------
Msrc/sdis_misc.c | 2+-
Msrc/sdis_misc.h | 2+-
Msrc/sdis_misc_Xd.h | 4++--
Msrc/sdis_mpi.c | 2+-
Msrc/sdis_mpi.h | 2+-
Asrc/sdis_radiative_env.c | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_radiative_env_c.h | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_realisation.c | 13++++++++-----
Msrc/sdis_realisation.h | 58+++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/sdis_realisation_Xd.h | 43+++++++++++++++++++++++++++++--------------
Msrc/sdis_scene.c | 125++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/sdis_scene_Xd.h | 383+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/sdis_scene_c.h | 32+++++++++++++++++++++++++++-----
Msrc/sdis_solve.c | 30+++++++++++++++++++++++++++++-
Msrc/sdis_solve_boundary_Xd.h | 32++++++++++++++++++++++++--------
Msrc/sdis_solve_camera.c | 22++++++++++++++++------
Msrc/sdis_solve_medium_Xd.h | 8+++++++-
Msrc/sdis_solve_probe_Xd.h | 313++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/sdis_solve_probe_boundary_Xd.h | 350+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Asrc/sdis_source.c | 366+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/sdis_source_c.h | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sdis_tile.c | 2+-
Msrc/sdis_tile.h | 2+-
Asrc/sdis_version.h.in | 23+++++++++++++++++++++++
Msrc/test_sdis.c | 2+-
Msrc/test_sdis_accum_buffer.c | 2+-
Msrc/test_sdis_camera.c | 2+-
Msrc/test_sdis_compute_power.c | 3+--
Msrc/test_sdis_conducto_radiative.c | 26+++++++++++++++-----------
Msrc/test_sdis_conducto_radiative_2d.c | 26+++++++++++++++-----------
Msrc/test_sdis_contact_resistance.c | 11+++++------
Msrc/test_sdis_contact_resistance.h | 2+-
Msrc/test_sdis_contact_resistance_2.c | 11+++++------
Msrc/test_sdis_convection.c | 17+++++++++++------
Msrc/test_sdis_convection_non_uniform.c | 17+++++++++++------
Msrc/test_sdis_data.c | 2+-
Msrc/test_sdis_device.c | 10+++++++++-
Asrc/test_sdis_draw_external_flux.c | 429+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_enclosure_limit_conditions.c | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_external_flux.c | 580+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_external_flux_with_diffuse_radiance.c | 449+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_flux.c | 11+++++------
Msrc/test_sdis_flux2.c | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/test_sdis_flux_with_h.c | 4++--
Msrc/test_sdis_interface.c | 10+++++-----
Msrc/test_sdis_medium.c | 18+++++++++++-------
Asrc/test_sdis_mesh.h | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_picard.c | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Asrc/test_sdis_radiative_env.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_scene.c | 160++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/test_sdis_solid_random_walk_robustness.c | 7+++----
Msrc/test_sdis_solve_boundary.c | 15+++++++--------
Msrc/test_sdis_solve_boundary_flux.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/test_sdis_solve_camera.c | 172++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/test_sdis_solve_medium.c | 18++++++++++++------
Msrc/test_sdis_solve_medium_2d.c | 20+++++++++++++-------
Msrc/test_sdis_solve_probe.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/test_sdis_solve_probe2.c | 8++++----
Msrc/test_sdis_solve_probe2_2d.c | 5+++--
Msrc/test_sdis_solve_probe3.c | 4++--
Msrc/test_sdis_solve_probe3_2d.c | 4++--
Msrc/test_sdis_solve_probe_2d.c | 6+++---
Asrc/test_sdis_solve_probe_boundary_list.c | 493+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_solve_probe_list.c | 692+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_source.c | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_transcient.c | 6++----
Dsrc/test_sdis_unstationary_atm.c | 920-------------------------------------------------------------------------------
Asrc/test_sdis_unsteady.c | 275+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_unsteady_1d.c | 268+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_unsteady_analytic_profile.c | 380+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_unsteady_analytic_profile_2d.c | 410+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_unsteady_atm.c | 956+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_utils.c | 126++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/test_sdis_utils.h | 37++++++++++++++++++++++++++++++-------
Msrc/test_sdis_volumic_power.c | 168+++++++++++++++++++++++++++++++++----------------------------------------------
Msrc/test_sdis_volumic_power2.c | 19+++++++++----------
Msrc/test_sdis_volumic_power2_2d.c | 11+++++------
Msrc/test_sdis_volumic_power3_2d.c | 19+++++++++----------
Msrc/test_sdis_volumic_power4.c | 10+++++-----
127 files changed, 13043 insertions(+), 3501 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,12 +1,17 @@ -compile_commands.json .gitignore -CMakeCache.txt -CMakeFiles -Makefile -tmp [Bb]uild* *.sw[po] -*.[ao] +*.[aod] +*.so *~ +test* +!test*.[ch] +.config +.config_test +.test +rng_state tags - +*.pc +src/sdis_version.h +*.vtk +*.obj diff --git a/Makefile b/Makefile @@ -0,0 +1,516 @@ +# Copyright (C) 2016-2023 |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/>. + +.POSIX: +.SUFFIXES: # Clean up default inference rules + +include config.mk + +LIBNAME_STATIC = libsdis.a +LIBNAME_SHARED = libsdis.so +LIBNAME = $(LIBNAME_$(LIB_TYPE)) + +MPI_DEF = -DSDIS_ENABLE_MPI + +################################################################################ +# Library building +################################################################################ +MPI_SRC = src/sdis_mpi.c +SRC =\ + src/sdis.c\ + src/sdis_camera.c\ + src/sdis_data.c\ + src/sdis_device.c\ + src/sdis_estimator.c\ + src/sdis_estimator_buffer.c\ + src/sdis_green.c\ + src/sdis_heat_path.c\ + src/sdis_heat_path_boundary.c\ + src/sdis_heat_path_conductive.c\ + src/sdis_interface.c\ + src/sdis_log.c\ + src/sdis_medium.c\ + src/sdis_misc.c\ + src/sdis_radiative_env.c\ + src/sdis_realisation.c\ + src/sdis_scene.c\ + src/sdis_solve.c\ + src/sdis_solve_camera.c\ + src/sdis_source.c\ + src/sdis_tile.c\ + $($(DISTRIB_PARALLELISM)_SRC) +OBJ = $(SRC:.c=.o) +DEP = $(SRC:.c=.d) + +build_library: .config $(DEP) + @$(MAKE) -fMakefile $$(for i in $(DEP); do echo -f $${i}; done) \ + $$(if [ -n "$(LIBNAME)" ]; then \ + echo "$(LIBNAME)"; \ + else \ + echo "$(LIBNAME_SHARED)"; \ + fi) + +$(DEP) $(OBJ): config.mk + +$(LIBNAME_SHARED): $(OBJ) + $(CC) $(CFLAGS_SO) $(DPDC_CFLAGS) -o $@ $(OBJ) $(LDFLAGS_SO) $(DPDC_LIBS) + +$(LIBNAME_STATIC): libsdis.o + $(AR) -rc $@ $? + $(RANLIB) $@ + +libsdis.o: $(OBJ) + $(LD) -r $(OBJ) -o $@ + $(OBJCOPY) $(OCPFLAGS) $@ + +.config: config.mk + @if [ "$(DISTRIB_PARALLELISM)" = "MPI" ]; then \ + if ! $(PKG_CONFIG) --atleast-version $(MPI_VERSION) $(MPI_PC); then \ + echo "$(MPI_PC) $(MPI_VERSION) not found" >&2; exit 1; fi; fi + @if ! $(PKG_CONFIG) --atleast-version $(RSYS_VERSION) rsys; then \ + echo "rsys $(RSYS_VERSION) not found" >&2; exit 1; fi + @if ! $(PKG_CONFIG) --atleast-version $(S2D_VERSION) s2d; then \ + echo "s2d $(S2D_VERSION) not found" >&2; exit 1; fi + @if ! $(PKG_CONFIG) --atleast-version $(S3D_VERSION) s3d; then \ + echo "s3d $(S3D_VERSION) not found" >&2; exit 1; fi + @if ! $(PKG_CONFIG) --atleast-version $(SENC2D_VERSION) senc2d; then \ + echo "senc2d $(SENC2D_VERSION) not found" >&2; exit 1; fi + @if ! $(PKG_CONFIG) --atleast-version $(SENC3D_VERSION) senc3d; then \ + echo "senc3d $(SENC3D_VERSION) not found" >&2; exit 1; fi + @if ! $(PKG_CONFIG) --atleast-version $(SSP_VERSION) star-sp; then \ + echo "star-sp $(SSP_VERSION) not found" >&2; exit 1; fi + @if ! $(PKG_CONFIG) --atleast-version $(SWF_VERSION) swf; then \ + echo "swf $(SWF_VERSION) not found" >&2; exit 1; fi + @echo "config done" > $@ + +.SUFFIXES: .c .d .o +.c.d: + @$(CC) $(CFLAGS_SO) $(DPDC_CFLAGS) $($(DISTRIB_PARALLELISM)_DEF) -MM -MT "$(@:.d=.o) $@" $< -MF $@ + +.c.o: + $(CC) $(CFLAGS_SO) $(DPDC_CFLAGS) -DSDIS_SHARED_BUILD $($(DISTRIB_PARALLELISM)_DEF) -c $< -o $@ + +################################################################################ +# Installation +################################################################################ +PKG_MPI =, $(MPI_PC) >= $(MPI_VERSION) + +pkg: + sed -e 's#@PREFIX@#$(PREFIX)#g'\ + -e 's#@VERSION@#$(VERSION)#g'\ + -e 's#@RSYS_VERSION@#$(RSYS_VERSION)#g'\ + -e 's#@S2D_VERSION@#$(S2D_VERSION)#g'\ + -e 's#@S3D_VERSION@#$(S3D_VERSION)#g'\ + -e 's#@SENC2D_VERSION@#$(SENC2D_VERSION)#g'\ + -e 's#@SENC3D_VERSION@#$(SENC3D_VERSION)#g'\ + -e 's#@SSP_VERSION@#$(SSP_VERSION)#g'\ + -e 's#@MPI@#$(PKG_$(DISTRIB_PARALLELISM))#g'\ + sdis.pc.in > sdis.pc + +sdis-local.pc: sdis.pc.in config.mk + sed -e '1d'\ + -e 's#^includedir=.*#includedir=./src/#'\ + -e 's#^libdir=.*#libdir=./#'\ + -e 's#@RSYS_VERSION@#$(RSYS_VERSION)#g'\ + -e 's#@S2D_VERSION@#$(S2D_VERSION)#g'\ + -e 's#@S3D_VERSION@#$(S3D_VERSION)#g'\ + -e 's#@SENC2D_VERSION@#$(SENC2D_VERSION)#g'\ + -e 's#@SENC3D_VERSION@#$(SENC3D_VERSION)#g'\ + -e 's#@SSP_VERSION@#$(SSP_VERSION)#g'\ + -e 's#@MPI@#$(PKG_$(DISTRIB_PARALLELISM))#g'\ + sdis.pc.in > $@ + +src/sdis_version.h: src/sdis_version.h.in config.mk + sed -e 's#@VERSION_MAJOR@#$(VERSION_MAJOR)#g' \ + -e 's#@VERSION_MINOR@#$(VERSION_MINOR)#g' \ + -e 's#@VERSION_PATCH@#$(VERSION_PATCH)#g' \ + src/sdis_version.h.in > $@ + +install: build_library pkg src/sdis_version.h + @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/lib" $(LIBNAME) + @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/lib/pkgconfig" sdis.pc + @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/include/" src/sdis.h + @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/include/" src/sdis_version.h + @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/share/doc/stardis-solver" \ + COPYING README.md + +uninstall: + rm -f "$(DESTDIR)$(PREFIX)/lib/$(LIBNAME)" + rm -f "$(DESTDIR)$(PREFIX)/lib/pkgconfig/sdis.pc" + rm -f "$(DESTDIR)$(PREFIX)/share/doc/stardis-solver/COPYING" + rm -f "$(DESTDIR)$(PREFIX)/share/doc/stardis-solver/README.md" + rm -f "$(DESTDIR)$(PREFIX)/include/sdis.h" + rm -f "$(DESTDIR)$(PREFIX)/include/sdis_version.h" + +################################################################################ +# Miscellaneous targets +################################################################################ +all: build_library build_tests + +clean: clean_test + rm -f $(OBJ) $(TEST_OBJ) $(LIBNAME) + rm -f .config .config_test .test libsdis.o sdis.pc sdis-local.pc + +distclean: clean + rm -f $(DEP) $(TEST_DEP) + +lint: + shellcheck -o all make.sh + +################################################################################ +# Tests +################################################################################ +TEST_SRC =\ + src/test_sdis_camera.c\ + src/test_sdis_conducto_radiative.c\ + src/test_sdis_conducto_radiative_2d.c\ + src/test_sdis_contact_resistance.c\ + src/test_sdis_contact_resistance_2.c\ + src/test_sdis_convection.c\ + src/test_sdis_convection_non_uniform.c\ + src/test_sdis_data.c\ + src/test_sdis_draw_external_flux.c\ + src/test_sdis_enclosure_limit_conditions.c\ + src/test_sdis_external_flux_with_diffuse_radiance.c\ + src/test_sdis_flux.c\ + src/test_sdis_flux2.c\ + src/test_sdis_flux_with_h.c\ + src/test_sdis_interface.c\ + src/test_sdis_medium.c\ + src/test_sdis_picard.c\ + src/test_sdis_radiative_env.c\ + src/test_sdis_scene.c\ + src/test_sdis_solid_random_walk_robustness.c\ + src/test_sdis_solve_probe.c\ + src/test_sdis_solve_probe3.c\ + src/test_sdis_solve_probe_2d.c\ + src/test_sdis_solve_probe2_2d.c\ + src/test_sdis_solve_probe3_2d.c\ + src/test_sdis_source.c\ + src/test_sdis_transcient.c\ + src/test_sdis_unsteady.c\ + src/test_sdis_unsteady_1d.c\ + src/test_sdis_unsteady_analytic_profile.c\ + src/test_sdis_unsteady_analytic_profile_2d.c\ + src/test_sdis_unsteady_atm.c\ + src/test_sdis_volumic_power.c\ + src/test_sdis_volumic_power4.c +TEST_SRC_LONG =\ + src/test_sdis_volumic_power2.c\ + src/test_sdis_volumic_power2_2d.c\ + src/test_sdis_volumic_power3_2d.c +TEST_SRC_MPI =\ + src/test_sdis.c\ + src/test_sdis_compute_power.c\ + src/test_sdis_device.c\ + src/test_sdis_external_flux.c\ + src/test_sdis_solve_camera.c\ + src/test_sdis_solve_medium.c\ + src/test_sdis_solve_medium_2d.c\ + src/test_sdis_solve_boundary.c\ + src/test_sdis_solve_boundary_flux.c\ + src/test_sdis_solve_probe2.c\ + src/test_sdis_solve_probe_list.c\ + src/test_sdis_solve_probe_boundary_list.c +TEST_OBJ =\ + $(TEST_SRC:.c=.o)\ + $(TEST_SRC_MPI:.c=.o)\ + $(TEST_SRC_LONG:.c=.o)\ + src/test_sdis_utils.o +TEST_DEP =\ + $(TEST_SRC:.c=.d)\ + $(TEST_SRC_MPI:.c=.d)\ + $(TEST_SRC_LONG:.c=.d)\ + src/test_sdis_utils.d + +PKG_CONFIG_LOCAL = PKG_CONFIG_PATH="./:$${PKG_CONFIG_PATH}" $(PKG_CONFIG) +SDIS_CFLAGS = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --cflags sdis-local.pc) +SDIS_LIBS = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --libs sdis-local.pc) + +# Regular Compiler and linker flags +TEST_CFLAGS = $(CFLAGS_EXE) $(SDIS_CFLAGS) $(RSYS_CFLAGS) +TEST_LIBS = src/test_sdis_utils.o $(LDFLAGS_EXE) $(SDIS_LIBS) $(RSYS_LIBS) -lm + +# Compiler and linker flags for MPI tests +TEST_CFLAGS_MPI =\ + $(TEST_CFLAGS)\ + $($(DISTRIB_PARALLELISM)_CFLAGS)\ + $($(DISTRIB_PARALLELISM)_DEF) +TEST_LIBS_MPI =\ + $(TEST_LIBS)\ + $($(DISTRIB_PARALLELISM)_LIBS) + +build_tests: .config_test build_library $(TEST_DEP) src/test_sdis_utils.d .test + @$(MAKE) -fMakefile -f.test -fsrc/test_sdis_utils.d \ + $$(for i in $(TEST_DEP); do echo -f"$${i}"; done) \ + test_bin + +.config_test: config.mk + @if ! $(PKG_CONFIG) --atleast-version $(S3DUT_VERSION) s3dut; then \ + echo "s3dut $(S3DUT_VERSION) not found" >&2; exit 1; fi + @echo "config done" > $@ + +test: build_tests + @$(SHELL) make.sh run_test $(TEST_SRC) + @if [ "$(DISTRIB_PARALLELISM)" != "MPI" ]; then \ + $(SHELL) make.sh run_test $(TEST_SRC_MPI); \ + else \ + $(SHELL) make.sh run_test_mpi $(TEST_SRC_MPI); \ + fi + +test_all: test + @$(SHELL) make_sh run_test $(TEST_SRC_LONG) + +.test: Makefile + @$(SHELL) make.sh config_test $(TEST_SRC) $(TEST_SRC_MPI) $(TEST_SRC_LONG) > $@ + +clean_test: + @$(SHELL) make.sh clean_test $(TEST_SRC) $(TEST_SRC_MPI) $(TEST_SRC_LONG) + rm -f super_shape_2d.obj paths_wos_2d.vtk paths_delta_sphere_2d.vtk + rm -f super_shape_3d.obj paths_wos_3d.vtk paths_delta_sphere_3d.vtk + rm -f rng_state + +################################################################################ +# Regular tests +################################################################################ +src/test_sdis_camera.d \ +src/test_sdis_conducto_radiative.d \ +src/test_sdis_conducto_radiative_2d.d \ +src/test_sdis_contact_resistance.d \ +src/test_sdis_contact_resistance_2.d \ +src/test_sdis_convection.d \ +src/test_sdis_convection_non_uniform.d \ +src/test_sdis_data.d \ +src/test_sdis_enclosure_limit_conditions.d \ +src/test_sdis_flux.d \ +src/test_sdis_flux2.d \ +src/test_sdis_flux_with_h.d \ +src/test_sdis_interface.d \ +src/test_sdis_medium.d \ +src/test_sdis_picard.d \ +src/test_sdis_radiative_env.d \ +src/test_sdis_solve_probe.d \ +src/test_sdis_solve_probe_2d.d \ +src/test_sdis_solve_probe2_2d.d \ +src/test_sdis_solve_probe3_2d \ +src/test_sdis_source.d \ +src/test_sdis_transcient.d \ +src/test_sdis_unsteady.d \ +src/test_sdis_unsteady_1d.d \ +src/test_sdis_unsteady_analytic_profile_2d.d \ +src/test_sdis_unsteady_atm.d \ +src/test_sdis_utils.d \ +src/test_sdis_volumic_power.d \ +src/test_sdis_volumic_power2.d \ +src/test_sdis_volumic_power2_2d.d \ +src/test_sdis_volumic_power3_2d.d \ +src/test_sdis_volumic_power4.d \ +: config.mk sdis-local.pc + @$(CC) $(TEST_CFLAGS) -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ + +src/test_sdis_camera.o \ +src/test_sdis_conducto_radiative.o \ +src/test_sdis_conducto_radiative_2d.o \ +src/test_sdis_contact_resistance.o \ +src/test_sdis_contact_resistance_2.o \ +src/test_sdis_convection.o \ +src/test_sdis_convection_non_uniform.o \ +src/test_sdis_data.o \ +src/test_sdis_enclosure_limit_conditions.o \ +src/test_sdis_flux.o \ +src/test_sdis_flux2.o \ +src/test_sdis_flux_with_h.o \ +src/test_sdis_interface.o \ +src/test_sdis_medium.o \ +src/test_sdis_picard.o \ +src/test_sdis_radiative_env.o \ +src/test_sdis_solve_probe.o \ +src/test_sdis_solve_probe_2d.o \ +src/test_sdis_solve_probe2_2d.o \ +src/test_sdis_solve_probe3_2d.o \ +src/test_sdis_source.o \ +src/test_sdis_transcient.o \ +src/test_sdis_unsteady.o \ +src/test_sdis_unsteady_1d.o \ +src/test_sdis_unsteady_analytic_profile_2d.o \ +src/test_sdis_unsteady_atm.o \ +src/test_sdis_utils.o \ +src/test_sdis_volumic_power.o \ +src/test_sdis_volumic_power2.o \ +src/test_sdis_volumic_power2_2d.o \ +src/test_sdis_volumic_power3_2d.o \ +src/test_sdis_volumic_power4.o \ +: config.mk sdis-local.pc + $(CC) $(TEST_CFLAGS) -c $(@:.o=.c) -o $@ + +test_sdis_camera \ +test_sdis_conducto_radiative \ +test_sdis_conducto_radiative_2d \ +test_sdis_contact_resistance \ +test_sdis_contact_resistance_2 \ +test_sdis_convection \ +test_sdis_convection_non_uniform \ +test_sdis_data \ +test_sdis_enclosure_limit_conditions \ +test_sdis_flux \ +test_sdis_flux2 \ +test_sdis_flux_with_h \ +test_sdis_interface \ +test_sdis_medium \ +test_sdis_picard \ +test_sdis_radiative_env \ +test_sdis_solve_probe \ +test_sdis_solve_probe_2d \ +test_sdis_solve_probe2_2d \ +test_sdis_solve_probe3_2d \ +test_sdis_source \ +test_sdis_transcient \ +test_sdis_unsteady \ +test_sdis_unsteady_1d \ +test_sdis_unsteady_analytic_profile_2d \ +test_sdis_unsteady_atm \ +test_sdis_volumic_power \ +test_sdis_volumic_power2 \ +test_sdis_volumic_power2_2d \ +test_sdis_volumic_power3_2d \ +test_sdis_volumic_power4 \ +: config.mk sdis-local.pc $(LIBNAME) src/test_sdis_utils.o + $(CC) $(TEST_CFLAGS) -o $@ src/$@.o $(TEST_LIBS) + +################################################################################ +# Tests based on Star-3DUT +################################################################################ +src/test_sdis_draw_external_flux.d \ +src/test_sdis_external_flux_with_diffuse_radiance.d \ +src/test_sdis_solid_random_walk_robustness.d \ +src/test_sdis_solve_probe3.d \ +src/test_sdis_unsteady_analytic_profile.d \ +: config.mk sdis-local.pc + @$(CC) $(TEST_CFLAGS) $(S3DUT_CFLAGS) -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ + +src/test_sdis_draw_external_flux.o \ +src/test_sdis_external_flux_with_diffuse_radiance.o \ +src/test_sdis_solid_random_walk_robustness.o \ +src/test_sdis_solve_probe3.o \ +src/test_sdis_unsteady_analytic_profile.o \ +: config.mk sdis-local.pc + $(CC) $(TEST_CFLAGS) $(S3DUT_CFLAGS) -c $(@:.o=.c) -o $@ + +test_sdis_draw_external_flux \ +test_sdis_external_flux_with_diffuse_radiance \ +test_sdis_solid_random_walk_robustness \ +test_sdis_solve_probe3 \ +test_sdis_unsteady_analytic_profile \ +: config.mk sdis-local.pc $(LIBNAME) src/test_sdis_utils.o + $(CC) $(TEST_CFLAGS) $(S3DUT_CFLAGS) -o $@ src/$@.o $(TEST_LIBS) $(S3DUT_LIBS) + +################################################################################ +# Tests based on Star-Enclosures-<2|3>D +################################################################################ +src/test_sdis_scene.d \ +: config.mk sdis-local.pc + @$(CC) $(TEST_CFLAGS) $(SENC2D_CFLAGS) $(SENC3D_CFLAGS) -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ + +src/test_sdis_scene.o \ +: config.mk sdis-local.pc + $(CC) $(TEST_CFLAGS) $(SENC2D_CFLAGS) $(SENC3D_CFLAGS) -c $(@:.o=.c) -o $@ + +test_sdis_scene \ +: config.mk sdis-local.pc $(LIBNAME) src/test_sdis_utils.o + $(CC) $(TEST_CFLAGS) $(SENC2D_CFLAGS) $(SENC3D_CFLAGS) -o $@ src/$@.o $(TEST_LIBS) $(SENC2D_LIBS) $(SENC3D_LIBS) + +################################################################################ +# Tests with (optional) MPI support +################################################################################ +src/test_sdis.d \ +src/test_sdis_device.d \ +src/test_sdis_external_flux.d \ +src/test_sdis_solve_medium_2d.d \ +: config.mk sdis-local.pc + @$(CC) $(TEST_CFLAGS_MPI) -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ + +src/test_sdis.o \ +src/test_sdis_device.o \ +src/test_sdis_external_flux.o \ +src/test_sdis_solve_medium_2d.o \ +: config.mk sdis-local.pc + $(CC) $(TEST_CFLAGS_MPI) -c $(@:.o=.c) -o $@ + +test_sdis \ +test_sdis_device \ +test_sdis_external_flux \ +test_sdis_solve_medium_2d \ +: config.mk sdis-local.pc $(LIBNAME) src/test_sdis_utils.o + $(CC) $(TEST_CFLAGS_MPI) -o $@ src/$@.o $(TEST_LIBS_MPI) + +################################################################################ +# Tests based on Star-3DUT with (optional) MPI support +################################################################################ +src/test_sdis_compute_power.d \ +src/test_sdis_solve_camera.d \ +src/test_sdis_solve_medium.d \ +src/test_sdis_solve_probe_boundary_list.d \ +: config.mk sdis-local.pc + @$(CC) $(TEST_CFLAGS_MPI) $(S3DUT_CFLAGS) -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ + +src/test_sdis_compute_power.o \ +src/test_sdis_solve_camera.o \ +src/test_sdis_solve_medium.o \ +src/test_sdis_solve_probe_boundary_list.o \ +: config.mk sdis-local.pc + $(CC) $(TEST_CFLAGS_MPI) $(S3DUT_CFLAGS) -c $(@:.o=.c) -o $@ + +test_sdis_compute_power \ +test_sdis_solve_camera \ +test_sdis_solve_medium \ +test_sdis_solve_probe_boundary_list \ +: config.mk sdis-local.pc $(LIBNAME) src/test_sdis_utils.o + $(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 +################################################################################ +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 $@ + +src/test_sdis_solve_probe_list.o \ +: config.mk sdis-local.pc + $(CC) $(TEST_CFLAGS_MPI) $(S3D_CFLAGS) $(S3DUT_CFLAGS) -c $(@:.o=.c) -o $@ + +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) + +################################################################################ +# Tests based on Star-SP with (optional) MPI support +################################################################################ +src/test_sdis_solve_boundary.d \ +src/test_sdis_solve_boundary_flux.d \ +src/test_sdis_solve_probe2.d \ +: config.mk sdis-local.pc + @$(CC) $(TEST_CFLAGS_MPI) $(SSP_CFLAGS) -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ + +src/test_sdis_solve_boundary.o \ +src/test_sdis_solve_boundary_flux.o \ +src/test_sdis_solve_probe2.o \ +: config.mk sdis-local.pc + $(CC) $(TEST_CFLAGS_MPI) $(SSP_CFLAGS) -c $(@:.o=.c) -o $@ + +test_sdis_solve_boundary \ +test_sdis_solve_boundary_flux \ +test_sdis_solve_probe2 \ +: config.mk sdis-local.pc $(LIBNAME) src/test_sdis_utils.o + $(CC) $(TEST_CFLAGS_MPI) $(SSP_CFLAGS) -o $@ src/$@.o $(TEST_LIBS_MPI) $(SSP_LIBS) diff --git a/README.md b/README.md @@ -1,311 +1,475 @@ # Stardis-Solver -Stardis-Solver is *free software* that solves *coupled* convecto - conducto - -radiative *thermal problems* in *complex* 2D and 3D *environments*. This C89 -library internally relies on *Monte-Carlo* algorithms based on reformulations -of the main heat transfer phenomena as cross-recursive "thermal paths" that -explore space and time until a boundary condition or an initial condition is -found. The key concept here is that heat transfer phenomena are not considered -separately but naturally coupled via cross-recursive [Monte-Carlo -algorithms](https://hal.archives-ouvertes.fr/hal-02419604/). - -The hypothesis these algorithms are based upon are the following: - -- *conduction*: the discretization of thermal heat transfer in solids introduces - the notion of a conductive path length within the Monte-Carlo algorithm. - Solutions obtained using this algorithm are formally exact at the limit of a - null path length. In practice, this path length has to be adapted for a given - geometric configuration so that its value is small compared to the smallest - typical length of a solid. -- *convection*: fluid media are supposed to be isothermal, even if their - temperature may vary with time. This hypothesis relies on the assumption of - perfectly agitated fluids. -- *radiation*: local radiative transfer is solved by an [iterative numerical - method](https://hal.archives-ouvertes.fr/tel-03266863/) (Picard algorithm) - that requires the knowledge of a reference temperature field. At the basic - level (one level of recursion), and using a uniform reference temperature - field, this algorithm translates into the hypothesis of a linearized - radiative transfer. Using a higher order or recursion makes possible to - converge the result closer to the solution of a rigorous - spectrally-integrated radiative transfer (a difference of temperatures to the - power 4 when integrated over the whole spectrum). The higher the recursion - order, the better will be the convergence of the algorithm. - -In Stardis-Solver the system to simulate is represented by a *scene* whose -geometry defines the contour of the object only: in contrast to legacy thermal -solvers *no volumetric mesh* has to be provided. Each geometric primitive as an -associated *interface* that defines its physical properties (e.g. surface -emissivity) and reference the *media* defining the thermal properties on both -side of the primitive. The boundary and initial conditions are defined defined -on the interfaces (convection coefficient, fixed temperature/flux, etc.) and -the media (known temperature). Once fully and consistently described, -computations can be invoked on the resulting scene. - -The main features of the solver are currently: - -- *probe computation*: Stardis-Solver will compute the temperature at any given - position (spatial and temporal). The main idea is that thermal paths start - from this probe position, and scatter in space while going back in time, - until a (spatial) boundary condition or a (temporal) initial condition is - met. In addition to the value of temperature, using a Monte-Carlo method - makes possible to compute a numerical uncertainty (standard deviation of the - weight distribution) over each result. -- *flux computation*: Stardis-Solver can compute the flux over any surface (or - group of surfaces) at any time. Alternatively, it can also compute the total - energy output from a solid element where a internal source of power must be - taken into account. -- *green function*: the value of temperature computed at a probe position is - the average of the Monte-Carlo weight for every thermal path. In practice: - when no internal power sources have to be considered, the weight of any given - thermal path is the temperature of the boundary or initial condition that has - been reached; when internal power sources or imposed fluxes are taken into - account, additional contributions to the weight must be continuously - evaluated by the thermal conduction algorithm, but these contributions are - proportional to the local dissipated power/imposed flux. In any case, the - position and date at the end of each thermal path (and also accumulation - coefficients) can be stored during a first complete Monte-Carlo simulation. - This information, known as the Green function, can then be used in (very - fast) post-processing to compute all required results for different boundary - and initial conditions (and also different internal power sources/imposed - flux). Note that when using the Green function, only boundary and initial - conditions (as well as internal power sources) can be modified: in - particular, the geometry, thermal properties and exchange coefficients have - to remain identical. Furthermore, the green function is only valid under the - assumption of linearized radiative transfer. -- *path visualization*: Stardis-Solver can store the complete spatial and - temporal position along a set of thermal paths, for latter visualization. In - addition of their position and, each thermal path vertex register additional - data as the type of thermal phenomena it simulates, the accumulated - power/flux along the path, etc. +Stardis-Solver is *free software* that solves *coupled* convecto - +conducto - radiative *thermal problems* in *complex* 2D and 3D +*environments*. This C89 library internally relies on *Monte-Carlo* +algorithms based on reformulations of the main heat transfer phenomena +as cross-recursive "thermal paths" that explore space and time until a +boundary condition or an initial condition is found. The key concept +here is that heat transfer phenomena are not considered separately but +naturally coupled via cross-recursive [Monte-Carlo +algorithms](https://doi.org/10.1371/journal.pone.0283681) + +In Stardis-Solver the system to simulate is represented by a *scene* +whose geometry defines the contour of the object only: in contrast to +legacy thermal solvers *no volumetric mesh* has to be provided. Each +geometric primitive as an associated *interface* that defines its +physical properties (e.g. surface emissivity) and reference the *media* +defining the thermal properties on both side of the primitive. The +boundary conditions are defined on the interfaces (convection +coefficient, fixed temperature/flux, etc.), the media (known +temperature), the *radiative environment* (ambient radiative +temperature) or an *external source* (incident flux). Once fully and +consistently described, computations can be invoked on the resulting +scene. Stardis-Solver is currently used in two frameworks. The -[Stardis](https://gitlab.com/meso-star/stardis.git) CLI and its associated -tools is the reference workflow of Stardis-Solver. It proposes a complete -toolchain from fileformats describing the scene (geometry, thermal properties, -limit and boundary conditions) to computations and post-treatments of the -results ([Stardis-Green](https://gitlab.com/meso-star/stardis-green.git)). +[Stardis](https://gitlab.com/meso-star/stardis.git) CLI and its +associated tools is the reference workflow of Stardis-Solver. It +proposes a complete toolchain from fileformats describing the scene +(geometry, thermal properties, limit and boundary conditions) to +computations and post-treatments of the results +([Stardis-Green](https://gitlab.com/meso-star/stardis-green.git)). Stardis-Solver is also integrated into [SYRTHES](https://www.edf.fr/en/the-edf-group/world-s-largest-power-company/activities/research-and-development/scientific-communities/simulation-softwares?logiciel=10818), -the general thermal free software developed by Electricité De France (EDF). - -## How to build - -Stardis-Solver is compatible GNU/Linux as well as Microsoft Windows 7 and -later, both in 64-bits. It was successfully built with the [GNU Compiler -Collection](https://gcc.gnu.org) (versions 4.9.2 and later) as well as with -Microsoft Visual Studio 2015. - -It relies on the [CMake](http://www.cmake.org) and the -[RCMake](https://gitlab.com/vaplv/rcmake/) package to build. -It also depends on the -[RSys](https://gitlab.com/vaplv/rsys/), -[Star-2D](https://gitlab.com/meso-star/star-2d/), -[Star-3D](https://gitlab.com/meso-star/star-3d/), -[Star-Enclosures-3D](https://gitlab.com/meso-star/star-enclosures-3d/), -[Star-Enclosures-2D](https://gitlab.com/meso-star/star-enclosures-2d/) and -[Star-SP](https://gitlab.com/meso-star/star-sp/) libraries as well as on the -[OpenMP](http://www.openmp.org) 2.0 specification to parallelize its -computations. It may depend on [OpenMPI](https://www.open-mpi.org/) 2.0 if -distributed memory parallelism is enabled via the `ENABLE_MPI` variable of the -CMake file - -First ensure that CMake and a C compiler that implements the OpenMP 2.0 -specification are installed on your system. Then install the RCMake package as -well as all the aforementioned prerequisites. Finally generate the project from -the `cmake/CMakeLists.txt` file by appending to the `CMAKE_PREFIX_PATH` -variable the install directories of its dependencies. +the general thermal free software developed by Electricité De France +(EDF). + +The hypothesis these algorithms are based upon are the following: + +- *Conduction*: Stardis-Solver offers two ways of sampling unsteady + Brownian motion to solve for conductivity in a solid. The delta + sphere algorithm is based on the discrimination of thermal heat + transfer in solids, which introduces the notion of conductive path + length. Solutions obtained using this algorithm are formally exact + to the limit of zero path length. In practice, this path length + must be adapted to a given geometrical configuration so that its + value is small compared to the smallest typical space-time length + of a solid. + + As an alternative, the + [Walk on Sphere](https://www.jstor.org/stable/2237369) + algorithm samples an unbiased diffuse trajectory in a solid with + Dirichlet boundary conditions, unbiased with respect to what numerical + accuracy can account for. Its coupling with other boundary or + connection conditions behaves as with the delta sphere algorithm, i.e. + the solution is exact when the length of the trajectory used as a + first-order approximation tends towards 0. + +- *Convection*: fluid media are supposed to be isothermal, even if + their temperature may vary with time. This hypothesis relies on + the assumption of perfectly agitated fluids. + +- *Radiation*: local radiative transfer is solved by an [iterative + numerical method](https://hal.archives-ouvertes.fr/tel-03266863/) + (Picard algorithm) that requires the knowledge of a reference + temperature field. At the basic level (one level of recursion), and + using a uniform reference temperature field, this algorithm translates + into the hypothesis of a linearised radiative transfer. Using a higher + order or recursion makes possible to converge the result closer to the + solution of a rigorous spectrally-integrated radiative transfer (a + difference of temperatures to the power 4 when integrated over the + whole spectrum). The higher the recursion order, the better will be + the convergence of the algorithm. + +Despite its specific advantages, Stardis-Solver is not meant to fully +replace already well established and highly validated thermal simulation +tools. Instead, it can be seen as an additional tool that can be useful +for various purposes: + +- *Probe computation*: Stardis-Solver will *not* compute the full + temperature field of a system; instead, it can be used to focus on a + specific spatial/temporal zone of the system. The main idea is that + thermal paths start from this probe position, and scatter in space + while going back in time, until a (spatial) boundary condition or a + (temporal) initial condition is met. In addition to the value of + temperature, using a Monte-Carlo method makes possible to compute a + numerical uncertainty (standard deviation of the weight distribution) + over each result. + +- *Integrated calculation*: thanks to Monte-Carlo, Stardis-Solver can + calculate the temperature of an entire volume or surface, over a given + time range, without increasing calculation time or uncertainty + compared with a probe-based calculation at a specific point in time. + +- *Flux computation*: Stardis-Solver can compute the flux over any + surface (or group of surfaces) at any time or time range. + Alternatively, it can also compute the total energy output from a + solid element where a internal source of power must be taken into + account. + +- *Infrared rendering*: Stardis-Solver can be used to simulate the + radiative temperature reaching the sensor of a camera. + The [image rendering](https://dx.doi.org/10.1145/3592121) + will take into account coupled (unsteady) phenomena and the entire + geometry *without* ever having to calculate the temperature field. + +- *Green function*: the value of temperature computed at a probe + position is the average of the Monte-Carlo weight for every thermal + path. In practice: when no internal power sources have to be + considered, the weight of any given thermal path is the temperature of + the boundary or initial condition that has been reached; when internal + power sources, imposed fluxes or an external source are taken into + account, additional contributions to the weight must be continuously + evaluated by the thermal conduction algorithm, but, under linear + assumption, these contributions are proportional to the local + dissipated power, imposed flux or external source radiance. + + So the position and date at the end of each thermal path (and also + accumulation coefficients) can be stored during a first complete + Monte-Carlo simulation. This is actually an estimates of the Green + function, can then be used in (very fast) + [post-processing](https://doi.org/10.1016/j.cpc.2023.108911) to + compute all required results for different boundary and initial + conditions (and also different internal power density, imposed or + incident flux). Note that when using the Green function, only boundary + and initial conditions (as well as internal power sources) can be + modified: in particular, the geometry, thermal properties and exchange + coefficients have to remain identical. Furthermore, the green function + is only valid under the assumption of linearised radiative transfer. + +- *Path visualization*: Stardis-Solver can store the complete spatial + and temporal position along a set of thermal paths, for latter + visualization. In addition of their position and, each thermal path + vertex register additional data as the type of thermal phenomena it + simulates, the accumulated power/flux along the path, etc. + +## Requirements + +- C compiler with OpenMP support +- POSIX make +- pkg-config +- Message Passing Interface (optional) +- [RSys](https://gitlab.com/vaplv/rsys) +- [Star 2D](https://gitlab.com/meso-star/star-2d) +- [Star 3D](https://gitlab.com/meso-star/star-3d) +- [Star 3DUT](https://gitlab.com/meso-star/star-3dut) + (optional for tests) +- [Star Enclosures 2D](https://gitlab.com/meso-star/star-enclosures-2D) +- [Star Enclosures 3D](https://gitlab.com/meso-star/star-enclosures-3D) +- [Star SamPling](https://gitlab.com/meso-star/star-sp) +- [Star WoS Functions](https://gitlab.com/meso-star/star-wf) + +## Installation + +Edit config.mk as needed, then run: + + make clean install ## Release notes +### Version 0.15 + +#### New conduction algorithm + +Addition of a new algorithm for sampling a potentially unsteady +conductive path based on the Walk on Sphere (WoS) algorithm. Currently, +unlike the previous delta sphere algorithm, the initial condition must +be constant for the solid. Power density is also supported, but unlike +the delta sphere algorithm, it too cannot vary in time and space. In all +cases, the two algorithms coexist and can be selected via a new input +parameter to the resolution functions. By default, the delta sphere +algorithm is used. + +Note that the WoS algorithm is considered unbiased when sampling a +diffuse trajectory in a solid with Dirichlet boundary conditions. Even +if a numerical parameter is used to stop the algorithm when the +trajectory is close to the boundary, this distance can be set to the +machine's accuracy without significant impact on performance. Hence its +unbiased nature in relation to numerical precision. Its coupling with +other boundary or connection conditions behaves as with the delta sphere +algorithm, i.e. the solution tends towards the exact solution when the +delta tends towards 0. + +#### External spherical source + +An external spherical source can be added to the scene. Once defined, it +is considered as a new boundary condition whose contribution is +calculated at the solid/fluid interfaces in the form of an external net +flux. A new interface parameter controls on which solid/fluid interfaces +this external net flux is imposed. By default, when an external source +is defined, its contribution is calculated on all solid/fluid +interfaces. We point out that the radiative properties of the surface +can now vary according to the radiative source, which can now be +internal or external. + +The net external flux is calculated by sampling the radiative paths to +evaluate the direct and diffuse part of the incident flux due to the +external source. We emphasize that Stardis-Solver does not manage +semi-traparent media, but that the external source provides the optional +`diffuse_radiance` parameter which, once defined, corresponds to the +radiance emitted by the external source and diffused at least once into +the environment. In this way, the diffuse part of the flux manages not +only the radiation from the external source that reaches the interface +after one or more reflections, but also the external radiation scattered +in the environment, here simply represented by the `diffuse_radiance` +parameter. + +The external spherical source is defined by its position, radius, power +and diffuse radiance (see above). While the radius is constant, the +position and power are time-dependent, and the diffuse radiance also +depends on the direction along which the sampled trajectory reaches the +environment. + +The external spherical source is fully supported when estimating the +green function. Only the positions of the spherical source must remain +the same between green function estimation and use. + +Finally, as with net imposed fluxes and power densities, this external +source term cannot be used when solving non-linear radiative exchanges +using Picard iterations. + +#### Allow relative temperatures + +Allow to perform calculations relative to a given temperature T. In this +case, the temperatures managed by Stardis would be relative to T and +could therefore be negative, since they would express a deviation from +T. It should be noted that reference temperatures must always be +positive, i.e. expressed in the absolute domain. Finally, we emphasize +that relative calculations only make sense in linear situations, i.e. +negative temperatures are not valid for systems with non-linear +radiative exchanges. + +This is a major break in the API that callers *must* take into account. +Until now, negative temperatures were considered as unknown +temperatures, whereas they are now valid. For example, an interface with +a negative temperature could be considered adiabatic, whereas it is now +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 is known or not. + +#### Parallelize multiple probe resolutions + +Add `sdis_solve_probe_list` and `sdis_solve_probe_boundary_list` +functions. Unlike their single-probe counterpart, these functions +parallelize the list of probes, rather than parallelizing Monte Carlo +realizations. Calling these functions is therefore more advantageous in +terms of load distribution when the number of probes to be evaluated is +large compared to the cost of calculating a single probe. + +#### Miscellaneous + +- Updated the function profile used to define surface radiative + properties, i.e. surface emissivity and specular fraction. These can + now vary according to the source identifier (internal or external). +- Make the radiative environment programmable. From now on, its API is + the same as that of other resources such as interfaces or media. Its + temperature and reference temperature are retrieved by functions whose + input argument is the direction of the radiative path. +- Add an optional user filter function as input argument to + `sdis_scene_find_closest_point` function. The user can set their own + filter function to manage the candidate points to be closest. This + gives the caller a fine control during the inquiry to access the + geometry and traversal of the accelerating structure. + ### Version 0.14 - The net flux imposed can be combined with other boundary/connection conditions, i.e. a net flux can be set in addition to convective exchange and radiative transfer. -- Added support for plain text log messages. Until now, log messages were - intended to be read by a VT100-like terminal and could therefore contain - escape sequences that required post-processing to store them in plain text - log files. -- Added support for user-defined signature on the green function. It allows to - check that, when reloaded, a green function is the one expected by the user - according to its own constraints that the green function cannot check itself - such as, for example, that the same deltas are used in conductive random - walks. -- Changes the value of the constant `SDIS_VOLUMIC_POWER_NONE`. Its previous - value of zero caused problems during the evaluation of the propagator: a - media with a power density of zero was not registered in the list of media - with a volumic power. A volumic power that was not zero was therefore not - taken into account during the re-evaluation of the propagator. The constant - is now set to `DBL_MAX`, which means that the medium has no power density, - while a value of 0 is now treated as any valid power density term. +- Added support for plain text log messages. Until now, log messages + were intended to be read by a VT100-like terminal and could therefore + contain escape sequences that required post-processing to store them + in plain text log files. +- Added support for user-defined signature on the green function. It + allows to check that, when reloaded, a green function is the one + expected by the user according to its own constraints that the green + function cannot check itself such as, for example, that the same + deltas are used in conductive random walks. +- Changes the value of the constant `SDIS_VOLUMIC_POWER_NONE`. Its + previous value of zero caused problems during the evaluation of the + propagator: a media with a power density of zero was not registered in + the list of media with a volumic power. A volumic power that was not + zero was therefore not taken into account during the re-evaluation of + the propagator. The constant is now set to `DBL_MAX`, which means that + the medium has no power density, while a value of 0 is now treated as + any valid power density term. ### Version 0.13.1 -Fixed compilation errors and compilation warnings displayed on some versions of -GCC. +Fixed compilation errors and compilation warnings displayed on some +versions of GCC. ### Version 0.13 #### Non linear radiative transfer Uses a new [iterative numerical -method](https://hal.archives-ouvertes.fr/tel-03266863/) to estimate radiative -transfer. With a recursion level of 1, this is equivalent to a linearization of -the radiative transfer but with a reference temperature that can vary in time -and space. By using a higher-order recursion, one can converge towards a -rigorous estimate that takes into account the non-linearity of the radiative -transfer; the higher the recursion order, the better the convergence, but with -the counterpart of an increase in calculation time. +method](https://hal.archives-ouvertes.fr/tel-03266863/) to estimate +radiative transfer. With a recursion level of 1, this is equivalent to a +linearization of the radiative transfer but with a reference temperature +that can vary in time and space. By using a higher-order recursion, one +can converge towards a rigorous estimate that takes into account the +non-linearity of the radiative transfer; the higher the recursion order, +the better the convergence, but with the counterpart of an increase in +calculation time. #### Distributed memory parallelism Uses message passing interface to distribute computation across multiple -computers. Stardis-Solver now, uses a mixed parallelism: on one computer (i.e. -a node), it uses a shared memory parallelism and relies on the message passing -interface to parallelize calculations between several nodes. +computers. Stardis-Solver now, uses a mixed parallelism: on one computer +(i.e. a node), it uses a shared memory parallelism and relies on the +message passing interface to parallelize calculations between several +nodes. #### Type and state of the random number generator -Adds the member input variable `rng_type` to the solve functions. It defines -the type of random number generator to use when no generator is defined. Note -that the `sdis_solve_camera` function does not have a random number generator -as an input variable and has therefore been updated to support it. +Adds the member input variable `rng_type` to the solve functions. It +defines the type of random number generator to use when no generator is +defined. Note that the `sdis_solve_camera` function does not have a +random number generator as an input variable and has therefore been +updated to support it. #### Reading the source code -Refactoring and deep rewriting of the source code to simplify its reading. +Refactoring and deep rewriting of the source code to simplify its +reading. ### Version 0.12.3 -Fix green paths ending in a fluid (transcient computation): The path's end was -not correctly registred and the path was later treated as failed. +Fix green paths ending in a fluid (transcient computation): The path's +end was not correctly registred and the path was later treated as +failed. ### Version 0.12.2 -- Sets the required version of Star-SampPling to 0.12. This version fixes - compilation errors with gcc 11 but introduces API breaks. +- Sets the required version of Star-SampPling to 0.12. This version + fixes compilation errors with gcc 11 but introduces API breaks. - Fix warnings detected by gcc 11. ### Version 0.12.1 -Updates the way numerical issues are handled during a conductive random walk. -Previously, a zealous test would report a numerical error and stop the -calculations when that error could be handled. +Updates the way numerical issues are handled during a conductive random +walk. Previously, a zealous test would report a numerical error and +stop the calculations when that error could be handled. ### Version 0.12 -Add the support of thermal contact resistance between two solids: the new -`thermal_contact_resistance` functor on the data structure `struct -sdis_interface_shader` defines the thermal resistance contact in K.m^2.W^-1 at -a given time and at a specific position onto the interface. +Add the support of thermal contact resistance between two solids: the +new `thermal_contact_resistance` functor on the data structure `struct +sdis_interface_shader` defines the thermal resistance contact in +K.m^2.W^-1 at a given time and at a specific position onto the +interface. ### Version 0.11 -- Add support of unsteady green evaluation. The resulting green function can - then be used to quickly evaluate the system at the same time but with - different limit and initial conditions, volumetric powers and imposed fluxes. -- Add checks on green re-evaluation to ensure that the system remains unchanged - regarding its scale factor and its reference temperature. -- Remove the ambient radiative temperature, the reference temperature and the - geometry scale factor from the list of arguments submitted to the solve - functions. They become scene arguments defined on scene creation. +- Add support of unsteady green evaluation. The resulting green function + can then be used to quickly evaluate the system at the same time but + with different limit and initial conditions, volumetric powers and + imposed fluxes. +- Add checks on green re-evaluation to ensure that the system remains + unchanged regarding its scale factor and its reference temperature. +- Remove the ambient radiative temperature, the reference temperature + and the geometry scale factor from the list of arguments submitted to + the solve functions. They become scene arguments defined on scene + creation. - Update the `sdis_scene_[2d_]create` function profile: its data are now grouped into a variable of type `struct sdis_scene_create_args`. ### Version 0.10.1 -- In green function estimation, the time sent to the user callbacks is no more - the elapsed time from the beginning of the realisation: as in a regular - computation, it is now the observation time. +- In green function estimation, the time sent to the user callbacks is + no more the elapsed time from the beginning of the realisation: as in + a regular computation, it is now the observation time. - Fix the flux computation for boundaries with an imposed flux: it was previously ignored. The new `sdis_estimator_get_imposed_flux` function returns this estimated flux component. -- Return an error if the flux is computed at a boundary whose temperature is - known: this configuration is not currently supported. +- Return an error if the flux is computed at a boundary whose + temperature is known: this configuration is not currently supported. - Fix build with the CL compiler. ### Version 0.10 - Add support of green function [de]serialization. The - `sdis_green_function_write` function serializes the green function into a - stream while the `sdis_green_function_create_from_stream` function - deserialize it. Note that the scene used to deserialize the green function - must be the same of the one used to estimate it: the media and the interfaces - have to be created in the same order, the scene geometry must be the same, - etc. -- Add the `sdis_scene_find_closest_point` function: search the point onto the - scene geometry that is the closest of the submitted position. -- Add the `sdis_compute_power` function that evaluates the power of a medium. + `sdis_green_function_write` function serializes the green function + into a stream while the `sdis_green_function_create_from_stream` + function deserialize it. Note that the scene used to deserialize the + green function must be the same of the one used to estimate it: the + media and the interfaces have to be created in the same order, the + scene geometry must be the same, etc. +- Add the `sdis_scene_find_closest_point` function: search the point + onto the scene geometry that is the closest of the submitted position. +- Add the `sdis_compute_power` function that evaluates the power of a + medium. - Update the solver: the time of the sampled path is now rewind on solid reinjection. ### Version 0.9 -- Update the API of the solve functions: the parameters of the simulation are - now grouped into a unique data structure rather than separately submitted as - function arguments. Thank to this structure and its default value, updating - input parameters should now affect marginally the calling code. -- Improve the logger. Add a prefix to the printed text to indicate the type of - the message (info, error or warning). Add a progress message during - simulation. +- Update the API of the solve functions: the parameters of the + simulation are now grouped into a unique data structure rather than + separately submitted as function arguments. Thank to this structure + and its default value, updating input parameters should now affect + marginally the calling code. +- Improve the logger. Add a prefix to the printed text to indicate the + type of the message (info, error or warning). Add a progress message + during simulation. - Bump the version of the Star-Enclosures <2D|3D> dependencies to 0.5 ### Version 0.8.2 -- Fix an issue when the `sdis_solve_boundary_flux` function was invoked on a - boundary with radiative transfer: several sampled paths were rejected due to - data inconsistencies. +- Fix an issue when the `sdis_solve_boundary_flux` function was invoked + on a boundary with radiative transfer: several sampled paths were + rejected due to data inconsistencies. - Fix a memory leak when the scene creation failed. -- Enable parallelism on Star-Enclosure[2D] to improve the performances of the - enclosure extraction on the setup of the Stardis-Solver scene. +- Enable parallelism on Star-Enclosure[2D] to improve the performances + of the enclosure extraction on the setup of the Stardis-Solver scene. ### Version 0.8.1 - Fix a solver issue that led to reject valid sampled paths. -- Bump the version of the Star-Enclosure[2D] libraries to 0.4.2. These versions - fix a numerical issue that might led to an infinite loop at the scene creation. +- Bump the version of the Star-Enclosure[2D] libraries to 0.4.2. These + versions fix a numerical issue that might led to an infinite loop at + the scene creation. ### Version 0.8 -- Drastically improve the robustness of the solver~: far less realisations are - now rejected. +- Drastically improve the robustness of the solver~: far less + realisations are now rejected. - Add the estimation of the time spent per realisation estimate. Add the - `sdis_estimator_get_realisation_time` function that returns this estimate. -- Add the `sdis_estimator_buffer` API~: it manages a two dimensional array of - regular estimators and provides global estimations over the whole estimators - saved into the buffer. -- Update the signature of the `sdis_solve_camera` function~: it now returns a - `sdis_estimator_buffer`. It now also supports time integration as well as - heat paths registration. + `sdis_estimator_get_realisation_time` function that returns this + estimate. +- Add the `sdis_estimator_buffer` API~: it manages a two dimensional + array of regular estimators and provides global estimations over the + whole estimators saved into the buffer. +- Update the signature of the `sdis_solve_camera` function~: it now + returns a `sdis_estimator_buffer`. It now also supports time + integration as well as heat paths registration. ### Version 0.7 #### Add Green function support -Provide new solve functions that compute and save the Green function, i.e. the -propagator used in regular solvers. The resulting Green function can be then -evaluated to obtain an estimate of the temperature. - -The time spent to compute the Green function is comparable to the computation -time of regular solvers; actually, they rely on the same code. However, its -evaluation is instantaneous while it still handles the limit conditions, the -boundary fluxes and the power term of the media *at the moment* of the -evaluation. This means that one can estimate the Green function of a system -only one time and then evaluate it with different limit conditions, boundary -fluxes or power terms with negligible computation costs. - -Currently, Stardis-Solver assumes that during the Green function estimation, -the properties of the system do not depend on time. In addition, it assumes -that the boundary fluxes and the volumetric powers are constants in time and -space. Anyway, on Green function evaluation, the limit conditions of the -system can still vary in time and space; systems in steady state can be -simulated with Green functions. +Provide new solve functions that compute and save the Green function, +i.e. the propagator used in regular solvers. The resulting Green +function can be then evaluated to obtain an estimate of the temperature. + +The time spent to compute the Green function is comparable to the +computation time of regular solvers; actually, they rely on the same +code. However, its evaluation is instantaneous while it still handles +the limit conditions, the boundary fluxes and the power term of the +media *at the moment* of the evaluation. This means that one can +estimate the Green function of a system only one time and then evaluate +it with different limit conditions, boundary fluxes or power terms with +negligible computation costs. + +Currently, Stardis-Solver assumes that during the Green function +estimation, the properties of the system do not depend on time. In +addition, it assumes that the boundary fluxes and the volumetric powers +are constants in time and space. Anyway, on Green function evaluation, +the limit conditions of the system can still vary in time and space; +systems in steady state can be simulated with Green functions. #### Add heat path registration -Add the `int register_paths` mask to almost all solve functions to enable the -registration against the returned estimator of the failure and/or successful -random paths used by the solvers. For each path, the registered data are: +Add the `int register_paths` mask to almost all solve functions to +enable the registration against the returned estimator of the failure +and/or successful random paths used by the solvers. For each path, the +registered data are: - the vertices of the path; - the type of the path (failed or succeed); @@ -313,119 +477,127 @@ random paths used by the solvers. For each path, the registered data are: - the Monte-Carlo weight of each path vertex; - the current time of each path vertex. -Note that the amount of registered data can be huge if too more paths are -registered. Consequently, this functionality should be used with few -realisations to obtain a subset of representative paths, or to only register -the few paths that failed in order to diagnose what went wrong. +Note that the amount of registered data can be huge if too more paths +are registered. Consequently, this functionality should be used with +few realisations to obtain a subset of representative paths, or to only +register the few paths that failed in order to diagnose what went wrong. #### Miscellaneous -- Add the `sdis_solve_medium` function: it estimates the average temperature - of a medium. -- Fix the setup of the interfaces: the interface associated to a geometric - primitive could not be the right one. +- Add the `sdis_solve_medium` function: it estimates the average + temperature of a medium. +- Fix the setup of the interfaces: the interface associated to a + geometric primitive could not be the right one. ### Version 0.6.1 -- Bump version of the Star-Enclosures[2D] dependencies: the new versions fix - issues in the construction of fluid enclosures. -- Bump version of the Star-<2D|3D> dependencies: the new versions rely on - Embree3 rather than on Embree2 for their ray-tracing back-end. +- Bump version of the Star-Enclosures[2D] dependencies: the new versions + fix issues in the construction of fluid enclosures. +- Bump version of the Star-<2D|3D> dependencies: the new versions rely + on Embree3 rather than on Embree2 for their ray-tracing back-end. ### Version 0.6 -- Add the `sdis_solve_boundary` function: it computes the average temperature - on a subset of geometric primitives. +- Add the `sdis_solve_boundary` function: it computes the average + temperature on a subset of geometric primitives. - Add flux solvers: the new `sdis_solve_probe_boundary_flux` and - `sdis_solve_boundary_flux` functions estimate the convective and radiative - fluxes at a given surface position or for a sub-set of geometric primitives, - respectively. -- Add support of time integration: almost all solvers can estimate the average - temperature on a given time range. Only the `sdis_solve_camera` function does - not support time integration, yet. + `sdis_solve_boundary_flux` functions estimate the convective and + radiative fluxes at a given surface position or for a sub-set of + geometric primitives, respectively. +- Add support of time integration: almost all solvers can estimate the + average temperature on a given time range. Only the + `sdis_solve_camera` function does not support time integration, yet. - Add support of an explicit initial time `t0` for the fluid. -- Fix a bug in the estimation of unknown fluid temperatures: the associativity - between the internal Stardis-Solver data and the user defined data was wrong. +- Fix a bug in the estimation of unknown fluid temperatures: the + associativity between the internal Stardis-Solver data and the user + defined data was wrong. ### Version 0.5 Add support of fluid enclosure with unknown uniform temperature. - The convection coefficient of the surfaces surrounding a fluid whose - temperature is unknown can vary in time and space. Anyway, the caller has to - ensure that for each triangle of the fluid enclosure, the convection - coefficient returned by its `struct sdis_interface_shader` - at a given - position and time - is less than or equal to the `convection_coef_upper_bound` - parameter of the shader. + temperature is unknown can vary in time and space. Anyway, the caller + has to ensure that for each triangle of the fluid enclosure, the + convection coefficient returned by its + `struct sdis_interface_shader` - at a given position and time - is + less than or equal to the `convection_coef_upper_bound` parameter of + the shader. ### Version 0.4 Full rewrite of how the volumetric power is taken into account. - Change the scheme of the random walk "solid re-injection": use a 2D - re-injection scheme in order to handle 2D effects. On one hand, this scheme - drastically improves the accuracy of the temperature estimation in solid with - a volumetric power term. On the other hand it is more sensible to numerical - imprecisions. The previous 1D scheme is thus used in situations where the 2D - scheme exhibits too numerical issues, i.e. on sharp angles. + re-injection scheme in order to handle 2D effects. On one hand, this + scheme drastically improves the accuracy of the temperature estimation + in solid with a volumetric power term. On the other hand it is more + sensible to numerical imprecisions. The previous 1D scheme is thus + used in situations where the 2D scheme exhibits too numerical issues, + i.e. on sharp angles. - Add the missing volumetric power term on solid re-injection. -- Add a corrective term to fix the bias on the volumetric power introduced when - the random walk progresses at a distance of `delta` of a boundary. +- Add a corrective term to fix the bias on the volumetric power + introduced when the random walk progresses at a distance of `delta` of + a boundary. - Add several volumetric power tests. -- Remove the `delta_boundary` parameter of the `struct sdis_solid_shader` data - structure. +- Remove the `delta_boundary` parameter of the `struct + sdis_solid_shader` data structure. ### Version 0.3 -- Some interface properties become double sided: the temperature, emissivity - and specular fraction is defined for each side of the interface. Actually, - only the convection coefficient is shared by the 2 sides of the interface. - The per side interface properties are grouped into the new `struct - sdis_interface_side_shader` data structure. -- Add the support of fixed fluxes: the flux is a per side interface property. - Currently, the flux is handled only for the interface sides facing a solid - medium. +- Some interface properties become double sided: the temperature, + emissivity and specular fraction is defined for each side of the + interface. Actually, only the convection coefficient is shared by the + 2 sides of the interface. The per side interface properties are + grouped into the new `struct sdis_interface_side_shader` data + structure. +- Add the support of fixed fluxes: the flux is a per side interface + property. Currently, the flux is handled only for the interface sides + facing a solid medium. - Add the `sdis_scene_boundary_project_pos` function that computes the - parametric coordinates of a world space position projected onto a given - primitive with respect to its normal. If the projection lies outside the - primitive, its parametric coordinates are wrapped against its boundaries in - order to ensure that they are valid coordinates into the primitive. Actually, - this function was mainly added to help in the definition of the probe - position onto a boundary as expected by the + parametric coordinates of a world space position projected onto a + given primitive with respect to its normal. If the projection lies + outside the primitive, its parametric coordinates are wrapped against + its boundaries in order to ensure that they are valid coordinates into + the primitive. Actually, this function was mainly added to help in the + definition of the probe position onto a boundary as expected by the `sdis_solve_probe_boundary` function. -- Update the default comportment of the interface shader when a function is not - set. -- Rename the `SDIS_MEDIUM_<FLUID|SOLID>` constants in `SDIS_<FLUID|SOLID>`. -- Rename the `enum sdis_side_flag` enumerate in `enum sdis_side` and update its - values. +- Update the default comportment of the interface shader when a function + is not set. +- Rename the `SDIS_MEDIUM_<FLUID|SOLID>` constants in + `SDIS_<FLUID|SOLID>`. +- Rename the `enum sdis_side_flag` enumerate in `enum sdis_side` and + update its values. ### Version 0.2 -- Add the support of volumic power to solid media: add the `volumic_power` - functor to the `sdis_solid_shader` data structure that, once defined, should - return the volumic power of the solid at a specific position and time. On - solve invocation, the conductive random walks take into account this - spatio-temporal volumic power in the computation of the solid temperature. -- Add the `sdis_solve_probe_boundary` function: it computes the temperature at - a given position and time onto a geometric primitive. The probe position is - defined by the index of the primitive and a parametric coordinates onto it. -- Add the `sdis_scene_get_boundary_position` function: it computes a world - space position from the index of a geometric primitive and a parametric - coordinate onto it. -- Fix how the `sdis_solve_probe` was parallelised. The submitted `threads_hint` - parameter was not correctly handled. +- Add the support of volumic power to solid media: add the + `volumic_power` functor to the `sdis_solid_shader` data structure + that, once defined, should return the volumic power of the solid at a + specific position and time. On solve invocation, the conductive random + walks take into account this spatio-temporal volumic power in the + computation of the solid temperature. +- Add the `sdis_solve_probe_boundary` function: it computes the + temperature at a given position and time onto a geometric primitive. + The probe position is defined by the index of the primitive and a + parametric coordinates onto it. +- Add the `sdis_scene_get_boundary_position` function: it computes a + world space position from the index of a geometric primitive and a + parametric coordinate onto it. +- Fix how the `sdis_solve_probe` was parallelised. The submitted + `threads_hint` parameter was not correctly handled. ### Version 0.1 - Add the support of radiative temperature. - Add the `sdis_camera` API: it defines a pinhole camera into the scene. -- Add the `sdis_accum_buffer` API: it is a pool of MC accumulators, i.e. a sum - of MC weights and square weights. -- Add the `sdis_solve_camera` function: it relies on a `sdis_camera` and a - `sdis_accum_buffer` to compute the radiative temperature that reaches each - pixel of an image whose definition is defined by the caller. Note that - actually this function uses the same underlying MC algorithm behind the - `sdis_solve_probe` function. +- Add the `sdis_accum_buffer` API: it is a pool of MC accumulators, i.e. + a sum of MC weights and square weights. +- Add the `sdis_solve_camera` function: it relies on a `sdis_camera` and + a `sdis_accum_buffer` to compute the radiative temperature that + reaches each pixel of an image whose definition is defined by the + caller. Note that actually this function uses the same underlying MC + algorithm behind the `sdis_solve_probe` function. ### Version 0.0 @@ -433,13 +605,15 @@ First version and implementation of the Stardis-Solver API. - Support fluid/solid and solid/solid interfaces. - Only conduction is currently fully supported: convection and radiative - temperature are not computed yet. Fluid media can be added to the system but - currently, Stardis-Solver assumes that their temperature are known. + temperature are not computed yet. Fluid media can be added to the + system but currently, Stardis-Solver assumes that their temperature + are known. ## License -Copyright (C) 2016-2023 |Méso|Star> (<contact@meso-star.com>). Stardis-Solver -is free software released under the GPLv3+ license: GNU GPL version 3 or later. -You are welcome to redistribute it under certain conditions; refer to the -COPYING files for details. +Copyright (C) 2016-2024 |Méso|Star> (contact@meso-star.com) + +Stardis-Solver is free software released under the GPLv3+ license: GNU +GPL version 3 or later. You are welcome to redistribute it under +certain conditions; refer to the COPYING files for details. diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -1,304 +0,0 @@ -# Copyright (C) 2016-2023 |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/>. - -cmake_minimum_required(VERSION 3.1) -project(stardis C) -enable_testing() - -include(CMakeDependentOption) - -set(SDIS_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src) -option(NO_TEST "Do not build tests" OFF) -option(ENABLE_MPI - "Enable the support of distributed parallelism \ -using the Message Passing Interface specification." ON) - -CMAKE_DEPENDENT_OPTION(ALL_TESTS - "Perform basic and advanced tests" OFF "NOT NO_TEST" OFF) - -############################################################################### -# Check dependencies -############################################################################### -find_package(RCMake 0.4 REQUIRED) -find_package(Star2D 0.5 REQUIRED) -find_package(Star3D 0.8 REQUIRED) -find_package(StarSP 0.13 REQUIRED) -find_package(StarEnc2D 0.5 REQUIRED) -find_package(StarEnc3D 0.5 REQUIRED) -find_package(RSys 0.13 REQUIRED) -find_package(OpenMP 2.0 REQUIRED) - -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) -include(rcmake) -include(rcmake_runtime) - -include_directories( - ${Star2D_INCLUDE_DIR} - ${Star3D_INCLUDE_DIR} - ${StarSP_INCLUDE_DIR} - ${StarEnc2D_INCLUDE_DIR} - ${StarEnc3D_INCLUDE_DIR} - ${RSys_INCLUDE_DIR}) - -if(ENABLE_MPI) - find_package(MPI 2 REQUIRED) - set(CMAKE_C_COMPILER ${MPI_C_COMPILER}) - include_directories(${MPI_INCLUDE_PATH}) -endif() - -rcmake_append_runtime_dirs(_runtime_dirs - RSys Star2D Star3D StarSP StarEnc2D StarEnc3D) - -############################################################################### -# Configure and define targets -############################################################################### -set(VERSION_MAJOR 0) -set(VERSION_MINOR 14) -set(VERSION_PATCH 0) -set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) - -set(SDIS_FILES_SRC - sdis.c - sdis_camera.c - sdis_data.c - sdis_device.c - sdis_estimator.c - sdis_estimator_buffer.c - sdis_green.c - sdis_heat_path.c - sdis_heat_path_boundary.c - sdis_interface.c - sdis_log.c - sdis_medium.c - sdis_misc.c - sdis_realisation.c - sdis_scene.c - sdis_solve.c - sdis_solve_camera.c - sdis_tile.c) - -set(SDIS_FILES_INC_API - sdis.h) - -set(SDIS_FILES_INC - sdis_c.h - sdis_camera.h - sdis_device_c.h - sdis_estimator_c.h - sdis_green.h - sdis_heat_path.h - sdis_heat_path_boundary_c.h - sdis_heat_path_boundary_Xd.h - sdis_heat_path_boundary_Xd_fixed_flux.h - sdis_heat_path_boundary_Xd_solid_fluid_picard1.h - sdis_heat_path_boundary_Xd_solid_solid.h - sdis_heat_path_conductive_Xd.h - sdis_heat_path_convective_Xd.h - sdis_heat_path_radiative_Xd.h - sdis_interface_c.h - sdis_log.h - sdis_misc.h - sdis_misc_Xd.h - sdis_medium_c.h - sdis_realisation.h - sdis_realisation_Xd.h - sdis_scene_c.h - sdis_scene_Xd.h - sdis_solve_boundary_Xd.h - sdis_solve_medium_Xd.h - sdis_solve_probe_Xd.h - sdis_solve_probe_boundary_Xd.h - sdis_tile.h - sdis_Xd_begin.h - sdis_Xd_end.h) - -if(ENABLE_MPI) - set(SDIS_FILES_SRC ${SDIS_FILES_SRC} sdis_mpi.c) - set(SDIS_FILES_INC ${SDIS_FILES_INC} sdis_mpi.h) -endif() - -set(SDIS_FILES_DOC COPYING README.md) - -# Prepend each file by `SDIS_SOURCE_DIR' -rcmake_prepend_path(SDIS_FILES_SRC ${SDIS_SOURCE_DIR}) -rcmake_prepend_path(SDIS_FILES_INC ${SDIS_SOURCE_DIR}) -rcmake_prepend_path(SDIS_FILES_INC_API ${SDIS_SOURCE_DIR}) -rcmake_prepend_path(SDIS_FILES_DOC ${PROJECT_SOURCE_DIR}/../) - -if(CMAKE_COMPILER_IS_GNUCC) - set(MATH_LIB m) -endif() - -if(MSVC) - ### disable verbose warnings: - # warning C4127: conditional expression is constant - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4127") - # warning C4204: - # nonstandard extension used : non-constant aggregate initializer - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4204") - # warning C4938: Floating point reduction variable may cause inconsistent - # results under /fp:strict or #pragma fenv_access - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4938") -endif() - -add_library(sdis SHARED - ${SDIS_FILES_SRC} - ${SDIS_FILES_INC} - ${SDIS_FILES_INC_API}) -target_link_libraries(sdis - RSys Star2D Star3D StarSP StarEnc2D StarEnc3D ${MATH_LIB}) - -set_target_properties(sdis PROPERTIES - DEFINE_SYMBOL SDIS_SHARED_BUILD - COMPILE_FLAGS ${OpenMP_C_FLAGS} - VERSION ${VERSION} - SOVERSION ${VERSION_MAJOR}) -rcmake_copy_runtime_libraries(sdis) - -if(CMAKE_COMPILER_IS_GNUCC) - set_target_properties(sdis PROPERTIES LINK_FLAGS "${OpenMP_C_FLAGS}") -endif() - -if(ENABLE_MPI) - set_target_properties(sdis PROPERTIES COMPILE_DEFINITIONS "SDIS_ENABLE_MPI") -endif() - -rcmake_setup_devel(sdis Stardis ${VERSION} sdis_version.h) - -############################################################################### -# Add tests -############################################################################### -if(NOT NO_TEST) - find_package(Star3DUT REQUIRED 0.2.0) - - add_library(sdis-test-utils STATIC - ${SDIS_SOURCE_DIR}/test_sdis_utils.h - ${SDIS_SOURCE_DIR}/test_sdis_utils.c) - - function(build_test _name) - add_executable(${_name} ${SDIS_SOURCE_DIR}/${_name}.c) - target_link_libraries(${_name} sdis-test-utils RSys sdis) - endfunction() - - function(register_test _name) - add_test(${_name} ${ARGN}) - rcmake_set_test_runtime_dirs(${_name} _runtime_dirs) - endfunction() - - function(new_test _name) - build_test(${_name}) - register_test(${_name} ${_name}) - endfunction() - - new_test(test_sdis_camera) - new_test(test_sdis_conducto_radiative) - new_test(test_sdis_conducto_radiative_2d) - new_test(test_sdis_contact_resistance) - new_test(test_sdis_contact_resistance_2) - new_test(test_sdis_convection) - new_test(test_sdis_convection_non_uniform) - new_test(test_sdis_data) - new_test(test_sdis_device) - new_test(test_sdis_flux) - new_test(test_sdis_flux2) - new_test(test_sdis_flux_with_h) - new_test(test_sdis_interface) - new_test(test_sdis_medium) - new_test(test_sdis_picard) - new_test(test_sdis_scene) - new_test(test_sdis_solid_random_walk_robustness) - new_test(test_sdis_solve_probe) - new_test(test_sdis_solve_probe3) - new_test(test_sdis_solve_probe_2d) - new_test(test_sdis_solve_probe2_2d) - new_test(test_sdis_solve_probe3_2d) - new_test(test_sdis_transcient) - new_test(test_sdis_unstationary_atm) - new_test(test_sdis_volumic_power) - new_test(test_sdis_volumic_power4) - - build_test(test_sdis) - build_test(test_sdis_compute_power) - build_test(test_sdis_solve_camera) - build_test(test_sdis_solve_medium) - build_test(test_sdis_solve_medium_2d) - build_test(test_sdis_solve_boundary) - build_test(test_sdis_solve_boundary_flux) - build_test(test_sdis_solve_probe2) - - # Additionnal tests - build_test(test_sdis_volumic_power2) - build_test(test_sdis_volumic_power2_2d) - build_test(test_sdis_volumic_power3_2d) - - if(ALL_TESTS) - add_test(test_sdis_volumic_power2 test_sdis_volumic_power2) - add_test(test_sdis_volumic_power2_2d test_sdis_volumic_power2_2d) - add_test(test_sdis_volumic_power3_2d test_sdis_volumic_power3_2d) - endif() - - target_link_libraries(test_sdis_compute_power Star3DUT) - target_link_libraries(test_sdis_solid_random_walk_robustness Star3DUT) - target_link_libraries(test_sdis_solve_medium Star3DUT) - target_link_libraries(test_sdis_solve_probe3 Star3DUT) - target_link_libraries(test_sdis_solve_probe3_2d ${MATH_LIB}) - target_link_libraries(test_sdis_solve_camera Star3DUT) - - target_link_libraries(test_sdis_solve_boundary StarSP) - target_link_libraries(test_sdis_solve_boundary_flux StarSP) - target_link_libraries(test_sdis_solve_probe2 StarSP) - target_link_libraries(test_sdis_solve_medium StarSP) - - set(_mpi_tests - test_sdis - test_sdis_compute_power - test_sdis_solve_camera - test_sdis_solve_medium - test_sdis_solve_medium_2d - test_sdis_solve_boundary - test_sdis_solve_boundary_flux - test_sdis_solve_probe2) - - if(NOT ENABLE_MPI) - foreach(_test ${_mpi_tests}) - add_test(${_test} ${_test}) - endforeach() - else() - set_target_properties(test_sdis PROPERTIES - COMPILE_DEFINITIONS "SDIS_ENABLE_MPI") - set_target_properties(test_sdis_device PROPERTIES - COMPILE_DEFINITIONS "SDIS_ENABLE_MPI") - - foreach(_test ${_mpi_tests}) - set_target_properties(${_test} PROPERTIES - COMPILE_DEFINITIONS "SDIS_ENABLE_MPI") - add_test(${_test}_mpi_on mpirun -n 2 ${_test} mpi) - add_test(${_test}_no_mpi ${_test}) - endforeach() - endif() - - rcmake_copy_runtime_libraries(test_sdis_solid_random_walk_robustness) - -endif() - -############################################################################### -# Define output & install directories -############################################################################### -install(TARGETS sdis - ARCHIVE DESTINATION bin - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin) -install(FILES ${SDIS_FILES_INC_API} DESTINATION include/) -install(FILES ${SDIS_FILES_DOC} DESTINATION share/doc/stardis-solver) diff --git a/config.mk b/config.mk @@ -0,0 +1,143 @@ +VERSION_MAJOR = 0 +VERSION_MINOR = 15 +VERSION_PATCH = 0 +VERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH) +PREFIX = /usr/local + +LIB_TYPE = SHARED +#LIB_TYPE = STATIC + +BUILD_TYPE = RELEASE +#BUILD_TYPE = DEBUG + +# Defines whether distributed parallelism is supported. Any value other +# than MPI disables its supports. So, simply comment the macro to +# deactivate it. +DISTRIB_PARALLELISM = MPI + +# MPI pkg-config file +MPI_PC = ompi + +################################################################################ +# Tools +################################################################################ +AR = ar +CC = cc +LD = ld +OBJCOPY = objcopy +PKG_CONFIG = pkg-config +RANLIB = ranlib + +################################################################################ +# Dependencies +################################################################################ +PCFLAGS_SHARED = +PCFLAGS_STATIC = --static +PCFLAGS = $(PCFLAGS_$(LIB_TYPE)) + +MPI_VERSION = 2 +MPI_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags $(MPI_PC)) +MPI_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs $(MPI_PC)) + +RSYS_VERSION = 0.14 +RSYS_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags rsys) +RSYS_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs rsys) + +S2D_VERSION = 0.7 +S2D_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags s2d) +S2D_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs s2d) + +S3D_VERSION = 0.10 +S3D_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags s3d) +S3D_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs s3d) + +SENC2D_VERSION = 0.5 +SENC2D_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags senc2d) +SENC2D_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs senc2d) + +SENC3D_VERSION = 0.5 +SENC3D_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags senc3d) +SENC3D_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs senc3d) + +SSP_VERSION = 0.14 +SSP_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags star-sp) +SSP_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs star-sp) + +SWF_VERSION = 0.0 +SWF_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags swf) +SWF_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs swf) + +# For tests only +S3DUT_VERSION = 0.4 +S3DUT_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags s3dut) +S3DUT_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs s3dut) + +DPDC_CFLAGS =\ + $(RSYS_CFLAGS)\ + $(S2D_CFLAGS)\ + $(S3D_CFLAGS)\ + $(SENC2D_CFLAGS)\ + $(SENC3D_CFLAGS)\ + $(SSP_CFLAGS)\ + $(SWF_CFLAGS)\ + $($(DISTRIB_PARALLELISM)_CFLAGS)\ + -fopenmp +DPDC_LIBS =\ + $(RSYS_LIBS)\ + $(S2D_LIBS)\ + $(S3D_LIBS)\ + $(SENC2D_LIBS)\ + $(SENC3D_LIBS)\ + $(SSP_LIBS)\ + $(SWF_LIBS)\ + $($(DISTRIB_PARALLELISM)_LIBS)\ + -lm\ + -fopenmp + +################################################################################ +# Compilation options +################################################################################ +WFLAGS =\ + -Wall\ + -Wcast-align\ + -Wconversion\ + -Wextra\ + -Wmissing-declarations\ + -Wmissing-prototypes\ + -Wshadow + +CFLAGS_HARDENED =\ + -D_FORTIFY_SOURCES=2\ + -fcf-protection=full\ + -fstack-clash-protection\ + -fstack-protector-strong + +CFLAGS_COMMON =\ + -std=c89\ + -pedantic\ + -fvisibility=hidden\ + -fstrict-aliasing\ + $(CFLAGS_HARDENED)\ + $(WFLAGS) + +CFLAGS_RELEASE = -O3 -DNDEBUG $(CFLAGS_COMMON) +CFLAGS_DEBUG = -g $(CFLAGS_COMMON) +CFLAGS = $(CFLAGS_$(BUILD_TYPE)) + +CFLAGS_SO = $(CFLAGS) -fPIC +CFLAGS_EXE = $(CFLAGS) -fPIE + +################################################################################ +# Linker options +################################################################################ +LDFLAGS_HARDENED = -Wl,-z,relro,-z,now +LDFLAGS_DEBUG = $(LDFLAGS_HARDENED) +LDFLAGS_RELEASE = -s $(LDFLAGS_HARDENED) +LDFLAGS = $(LDFLAGS_$(BUILD_TYPE)) + +LDFLAGS_SO = $(LDFLAGS) -shared -Wl,--no-undefined +LDFLAGS_EXE = $(LDFLAGS) -pie + +OCPFLAGS_DEBUG = --localize-hidden +OCPFLAGS_RELEASE = --localize-hidden --strip-unneeded +OCPFLAGS = $(OCPFLAGS_$(BUILD_TYPE)) diff --git a/make.sh b/make.sh @@ -0,0 +1,87 @@ +#!/bin/sh + +# 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/>. + +set -e + +config_test() +{ + for i in "$@"; do + test=$(basename "${i}" ".c") + test_list="${test_list} ${test}" + printf "%s: %s\n" "${test}" "src/${test}.o" + done + printf "test_bin: %s\n" "${test_list}" +} + +check() +{ + name="$1" + prog="$2" + shift 2 + + printf "%s " "${name}" + if PATH=./:"${PATH}" "${prog}" "$@" > /dev/null 2>&1; then + printf "\033[1;32mOK\033[m\n" + else + printf "\033[1;31mError\033[m\n" + fi 2> /dev/null +} + +run_test() +{ + for i in "$@"; do + prog="$(basename "${i}" ".c")" + check "${prog}" "${prog}" + done +} + +run_test_mpi() +{ + for i in "$@"; do + test="$(basename "${i}" ".c")" + check "${test}_mpi_on" mpirun -n 2 "${test}" mpi + check "${test}_no_mpi" "${test}" + done +} + +clean_test() +{ + for i in "$@"; do + rm -f "$(basename "${i}" ".c")" + done +} + +install() +{ + prefix=$1 + shift 1 + + mkdir -p "${prefix}" + + for i in "$@"; do + dst="${prefix}/${i##*/}" + + if cmp -s "${i}" "${dst}"; then + printf "Up to date %s\n" "${dst}" + else + printf "Installing %s\n" "${dst}" + cp "${i}" "${prefix}" + fi + done +} + +"$@" diff --git a/sdis.pc.in b/sdis.pc.in @@ -0,0 +1,18 @@ +prefix=@PREFIX@ +includedir=${prefix}/include +libdir=${prefix}/lib + +Requires: \ + rsys >= @RSYS_VERSION@,\ + s2d >= @S2D_VERSION@,\ + s3d >= @S3D_VERSION@,\ + star-sp >= @SSP_VERSION@ +Requires.private:\ + senc2d >= @SENC3D_VERSION@,\ + senc3d >= @SENC3D_VERSION@ @MPI@ +Name: sdis +Description: Stardis Solver +Version: @VERSION@ +Libs: -L${libdir} -lsdis +Libs.private: -fopenmp -lm +CFlags: -I${includedir} diff --git a/src/sdis.c b/src/sdis.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -105,7 +105,7 @@ rewind_progress_printing(struct sdis_device* dev) || dev->mpi_nprocs == 1) return; - FOR_EACH(i, 0, dev->mpi_nprocs-1) { + FOR_EACH(i, 0, (size_t)(dev->mpi_nprocs-1)) { log_info(dev, "\033[1A\r"); /* Move up */ } } @@ -453,36 +453,54 @@ free_process_progress(struct sdis_device* dev, int32_t progress[]) } size_t -compute_process_realisations_count +compute_process_index_range (const struct sdis_device* dev, - const size_t nrealisations) + const size_t nindices, + size_t range[2]) { #ifndef SDIS_ENABLE_MPI - (void)dev, (void)nrealisations; - return nrealisations; + (void)dev; + range[0] = 0; + range[1] = nindices; /* Upper bound is _exclusive_ */ #else - size_t per_process_nrealisations = 0; - size_t remaining_nrealisations = 0; ASSERT(dev); - if(!dev->use_mpi) return nrealisations; - - /* Compute minimum the number of realisations on each process */ - per_process_nrealisations = nrealisations / (size_t)dev->mpi_nprocs; - - /* Define the remaining number of realisations that are not handle by one - * process */ - remaining_nrealisations = - nrealisations - - per_process_nrealisations * (size_t)dev->mpi_nprocs; - - /* Distribute the remaining realisations onto the processes */ - if((size_t)dev->mpi_rank >= remaining_nrealisations) { - return per_process_nrealisations; + if(!dev->use_mpi) { + range[0] = 0; + range[1] = nindices; } else { - return per_process_nrealisations + 1; + size_t per_process_indices = 0; + size_t remaining_indices = 0; + + /* Compute the minimum number of indices on each process */ + per_process_indices = nindices / (size_t)dev->mpi_nprocs; + + range[0] = per_process_indices * (size_t)dev->mpi_rank; + range[1] = range[0] + per_process_indices; /* Upper bound is _exclusive_ */ + ASSERT(range[0] <= range[1]); + + /* Set the remaining number of indices that are not managed by one process */ + remaining_indices = + nindices - per_process_indices * (size_t)dev->mpi_nprocs; + + /* Distribute the remaining indices among the processes. Each process whose + * rank is lower than the number of remaining indices takes an additional + * index. To ensure continuity of indices per process, subsequent processes + * shift their initial rank accordingly, i.e. process 1 shifts its indices + * by 1, process 2 shifts them by 2 and so on until there are no more + * indices to distribute. From then on, subsequent processes simply shift + * their index range by the number of remaining indices that have been + * distributed. */ + if((size_t)dev->mpi_rank < remaining_indices) { + range[0] += (size_t)dev->mpi_rank; + range[1] += (size_t)dev->mpi_rank + 1/* Take one more index */; + } else { + range[0] += remaining_indices; + range[1] += remaining_indices; + } } #endif + return range[1] - range[0]; } #ifndef SDIS_ENABLE_MPI @@ -568,6 +586,119 @@ error: #ifndef SDIS_ENABLE_MPI res_T +gather_accumulators_list + (struct sdis_device* dev, + const enum mpi_sdis_message msg, + const size_t nprobes, /* Total number of probes */ + const size_t process_probes[2], /* Ids of the probes managed by the process */ + struct accum* per_probe_acc) /* List of per probe accumulators */ +{ + (void)dev, (void)msg, (void) nprobes; + (void)process_probes, (void)per_probe_acc; + return RES_OK; +} +#else +res_T +gather_accumulators_list + (struct sdis_device* dev, + const enum mpi_sdis_message msg, + const size_t nprobes, /* Total number of probes */ + const size_t process_probes[2], /* Range of probes managed by the process */ + struct accum* per_probe_acc) /* List of per probe accumulators */ +{ + struct accum_list { + size_t size; + /* Simulate a C99 flexible array */ + ALIGN(16) struct accum accums[1/*Dummy element*/]; + }* accum_list = NULL; + size_t max_per_process_nprobes = 0; /* Maximum #probes per process */ + size_t process_nprobes = 0; /* Number of process probes */ + size_t msg_sz = 0; /* Size in bytes of the message to send */ + res_T res = RES_OK; + + /* Check pre-conditions */ + ASSERT(dev); + ASSERT(process_nprobes == 0 || (process_probes && per_probe_acc)); + + /* Without MPI, do nothing since per_probe_acc already has all the + * accumulators */ + if(!dev->use_mpi) goto exit; + + /* Defines the maximum number of probes managed by a process. In fact, it's + * the number of probes divided by the number of processes, plus one to manage + * the remainder of the entire division: the remaining probes are distributed + * between the processes */ + max_per_process_nprobes = nprobes/(size_t)dev->mpi_nprocs + 1; + + /* Number of probes */ + process_nprobes = process_probes[1] - process_probes[0]; + + /* Allocate the array into which the data to be collected is copied */ + msg_sz = + sizeof(struct accum_list) + + sizeof(struct accum)*max_per_process_nprobes + - 1/*Dummy element */; + if(msg_sz > INT_MAX) { + log_err(dev, "%s: invalid MPI message size %lu.\n", + FUNC_NAME, (unsigned long)msg_sz); + res = RES_BAD_ARG; + goto error; + } + + accum_list = MEM_CALLOC(dev->allocator, 1, msg_sz); + if(!accum_list) { + log_err(dev, + "%s: unable to allocate the temporary list of accumulators.\n", + FUNC_NAME); + res = RES_MEM_ERR; + goto error; + } + + /* Non master process */ + if(dev->mpi_rank != 0) { + + /* Setup the message to be sent */ + accum_list->size = process_nprobes; + memcpy(accum_list->accums, per_probe_acc, + sizeof(struct accum)*process_nprobes); + + mutex_lock(dev->mpi_mutex); + MPI(Send(accum_list, (int)msg_sz, MPI_CHAR, 0/*Dst*/, msg, MPI_COMM_WORLD)); + mutex_unlock(dev->mpi_mutex); + + /* Master process */ + } else { + size_t gathered_nprobes = process_nprobes; + int iproc; + + FOR_EACH(iproc, 1, dev->mpi_nprocs) { + MPI_Request req; + + /* Asynchronously receive the accumulator of `iproc' */ + mutex_lock(dev->mpi_mutex); + MPI(Irecv + (accum_list, (int)msg_sz, MPI_CHAR, iproc, msg, MPI_COMM_WORLD, &req)); + mutex_unlock(dev->mpi_mutex); + + mpi_waiting_for_request(dev, &req); + + memcpy(per_probe_acc+gathered_nprobes, accum_list->accums, + sizeof(struct accum)*accum_list->size); + + gathered_nprobes += accum_list->size; + } + } + +exit: + if(accum_list) MEM_RM(dev->allocator, accum_list); + return res; +error: + goto exit; +} +#endif + +#ifndef SDIS_ENABLE_MPI +res_T gather_green_functions (struct sdis_scene* scn, struct ssp_rng_proxy* rng_proxy, diff --git a/src/sdis.h b/src/sdis.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -16,11 +16,15 @@ #ifndef SDIS_H #define SDIS_H +#include <star/s2d.h> +#include <star/s3d.h> #include <star/ssp.h> #include <rsys/hash.h> #include <rsys/rsys.h> -#include <float.h> + +#include <float.h> /* DBL_MAX */ +#include <limits.h> /* UINT_MAX */ /* Library symbol management */ #if defined(SDIS_SHARED_BUILD) @@ -48,6 +52,14 @@ #define SDIS_FLUX_NONE DBL_MAX /* <=> No flux */ #define SDIS_PRIMITIVE_NONE SIZE_MAX /* Invalid primitive */ +/* Syntactic sugar used to define whether a temperature is known or not */ +#define SDIS_TEMPERATURE_NONE NaN /* Unknown temperature */ +#define SDIS_TEMPERATURE_IS_KNOWN(Temp) (!IS_NaN(Temp)) +#define SDIS_TEMPERATURE_IS_UNKNOWN(Temp) (IS_NaN(Temp)) + +/* Identifier of the internal source of radiation */ +#define SDIS_INTERN_SOURCE_ID UINT_MAX + /* Forward declaration of external opaque data types */ struct logger; struct mem_allocator; @@ -68,7 +80,9 @@ struct sdis_estimator_buffer; struct sdis_green_function; struct sdis_interface; struct sdis_medium; +struct sdis_radiative_env; /* Radiative environment */ struct sdis_scene; +struct sdis_source; /* Forward declaration of non ref counted types */ struct sdis_green_path; @@ -88,7 +102,14 @@ enum sdis_scene_dimension { SDIS_SCENE_3D }; -/* Random walk vertex, i.e. a spatiotemporal position at a given step of the +enum sdis_diffusion_algorithm { + SDIS_DIFFUSION_DELTA_SPHERE, + SDIS_DIFFUSION_WOS, /* Walk on Sphere */ + SDIS_DIFFUSION_ALGORITHMS_COUNT__, + SDIS_DIFFUSION_NONE = SDIS_DIFFUSION_ALGORITHMS_COUNT__ +}; + +/* Random walk vertex, i.e. a spatio-temporal position at a given step of the * random walk. */ struct sdis_rwalk_vertex { double P[3]; /* World space position */ @@ -98,7 +119,7 @@ struct sdis_rwalk_vertex { static const struct sdis_rwalk_vertex SDIS_RWALK_VERTEX_NULL = SDIS_RWALK_VERTEX_NULL__; -/* Spatiotemporal position onto an interface. As a random walk vertex, it +/* Spatio-temporal position onto an interface. As a random walk vertex, it * stores the position and time of the random walk, but since it lies onto an * interface, it has additionnal parameters as the normal of the interface and * the parametric coordinate of the position onto the interface */ @@ -113,24 +134,13 @@ struct sdis_interface_fragment { static const struct sdis_interface_fragment SDIS_INTERFACE_FRAGMENT_NULL = SDIS_INTERFACE_FRAGMENT_NULL__; -/******************************************************************************* - * Estimation data types - ******************************************************************************/ -enum sdis_estimator_type { - SDIS_ESTIMATOR_TEMPERATURE, /* In Kelvin */ - SDIS_ESTIMATOR_FLUX, /* In Watt/m^2 */ - SDIS_ESTIMATOR_POWER, /* In Watt */ - SDIS_ESTIMATOR_TYPES_COUNT__ +/* Ray traced in radiative environment */ +struct sdis_radiative_ray { + double dir[3]; /* Direction */ }; - -/* Monte-Carlo estimation */ -struct sdis_mc { - double E; /* Expected value */ - double V; /* Variance */ - double SE; /* Standard error */ -}; -#define SDIS_MC_NULL__ {0, 0, 0} -static const struct sdis_mc SDIS_MC_NULL = SDIS_MC_NULL__; +#define SDIS_RADIATIVE_RAY_NULL__ {{0,0,0}} +static const struct sdis_radiative_ray SDIS_RADIATIVE_RAY_NULL= + SDIS_RADIATIVE_RAY_NULL__; /* Input arguments of the sdis_device_create function */ struct sdis_device_create_args { @@ -158,6 +168,77 @@ struct sdis_info { #define SDIS_INFO_NULL__ {0} static const struct sdis_info SDIS_INFO_NULL = SDIS_INFO_NULL__; +/* Type of functor used to retrieve the source's position relative to time */ +typedef void +(*sdis_get_position_T) + (const double time, /* [s] */ + double pos[3], + struct sdis_data* data); + +/* Type of functor used to retrieve the source's power relative to time */ +typedef double +(*sdis_get_power_T) + (const double time, /* [s] */ + struct sdis_data* data); + +/* Type of functor used to retrieve the diffuse part of the external radiance */ +typedef double /* [W/perpendicular m^2/sr] */ +(*sdis_get_diffuse_radiance_T) + (const double time, /* [s] */ + const double dir[3], + struct sdis_data* data); + +/* Parameters of an external spherical source */ +struct sdis_spherical_source_shader { + sdis_get_position_T position; /* [m/fp_to_meter] */ + sdis_get_power_T power; /* Total power [W] */ + + /* Describes the diffuse part of the source's radiance, i.e. the radiance + * emitted by the source and scattered at least once in the environment. This + * parameter is actually used to approximate a semi-transparent medium. Its + * value can be NULL, meaning that the source has not been scattered by the + * environment, or, to put it another way, that the source is in a vacuum. */ + sdis_get_diffuse_radiance_T diffuse_radiance; /* [W/m^2/sr] */ + + struct sdis_data* data; /* Data sent to the position functor */ + double radius; /* [m] */ +}; +#define SDIS_SPHERICAL_SOURCE_SHADER_NULL__ {NULL, NULL, NULL, 0, 0} +static const struct sdis_spherical_source_shader +SDIS_SPHERICAL_SOURCE_SHADER_NULL = SDIS_SPHERICAL_SOURCE_SHADER_NULL__; + +struct sdis_scene_find_closest_point_args { + double position[3]; /* Query position */ + double radius; /* Maxium search distance around pos */ + + /* User defined filter function */ + s2d_hit_filter_function_T filter_2d; + s3d_hit_filter_function_T filter_3d; + void* filter_data; /* Filter function data */ +}; +#define SDIS_SCENE_FIND_CLOSEST_POINT_ARGS_NULL__ {{0,0,0}, 0, NULL, NULL, NULL} +static const struct sdis_scene_find_closest_point_args +SDIS_SCENE_FIND_CLOSEST_POINT_ARGS_NULL = SDIS_SCENE_FIND_CLOSEST_POINT_ARGS_NULL__; + +/******************************************************************************* + * Estimation data types + ******************************************************************************/ +enum sdis_estimator_type { + SDIS_ESTIMATOR_TEMPERATURE, /* In Kelvin */ + SDIS_ESTIMATOR_FLUX, /* In Watt/m^2 */ + SDIS_ESTIMATOR_POWER, /* In Watt */ + SDIS_ESTIMATOR_TYPES_COUNT__ +}; + +/* Monte-Carlo estimation */ +struct sdis_mc { + double E; /* Expected value */ + double V; /* Variance */ + double SE; /* Standard error */ +}; +#define SDIS_MC_NULL__ {0, 0, 0} +static const struct sdis_mc SDIS_MC_NULL = SDIS_MC_NULL__; + /******************************************************************************* * Data type used to describe physical properties ******************************************************************************/ @@ -171,14 +252,28 @@ enum sdis_medium_type { * medium. */ typedef double (*sdis_medium_getter_T) - (const struct sdis_rwalk_vertex* vert, - struct sdis_data* data); + (const struct sdis_rwalk_vertex* vert, /* Medium position */ + struct sdis_data* data); /* User data */ /* Functor type used to retrieve the spatio temporal physical properties of an * interface. */ typedef double (*sdis_interface_getter_T) - (const struct sdis_interface_fragment* frag, + (const struct sdis_interface_fragment* frag, /* Interface position */ + struct sdis_data* data); /* User data */ + +/* Type of functor for obtaining the spatio temporal physical properties of an + * interface, as a function of the radiation source */ +typedef double +(*sdis_radiative_interface_getter_T) + (const struct sdis_interface_fragment* frag, /* Interface position */ + const unsigned source_id, /* Identifier of the radiation source */ + struct sdis_data* data); /* User data */ + +/* Type of functor for obtaining radiative environment properties */ +typedef double +(*sdis_radiative_ray_getter_T) + (const struct sdis_radiative_ray* ray, struct sdis_data* data); /* Define the physical properties of a solid */ @@ -194,12 +289,14 @@ struct sdis_solid_shader { * submitted position and time */ sdis_medium_getter_T volumic_power; /* In W.m^-3 */ - /* Initial/limit condition. A temperature < 0 means that the temperature is - * unknown for the submitted random walk vertex. + /* Initial/limit condition. A temperature set to SDIS_TEMPERATURE_NONE + * means that the temperature is unknown for the submitted random walk vertex. * This getter is always called at time >= t0 (see below). */ sdis_medium_getter_T temperature; - /* The time until the initial condition is maintained for this solid; - * can neither be negative nor infinity, default is 0. */ + + /* 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} @@ -212,12 +309,14 @@ struct sdis_fluid_shader { sdis_medium_getter_T calorific_capacity; /* In J.K^-1.kg^-1 */ sdis_medium_getter_T volumic_mass; /* In kg.m^-3 */ - /* Initial/limit condition. A temperature < 0 means that the temperature is - * unknown for the submitted random walk vertex. + /* Initial/limit condition. A temperature set to SDIS_TEMPERATURE_NONE + * means that the temperature is unknown for the submitted random walk vertex. * This getter is always called at time >= t0 (see below). */ sdis_medium_getter_T temperature; - /* The time until the initial condition is maintained for this fluid; - * can neither be negative nor infinity, default is 0. */ + + /* The time until the initial condition is maintained for this fluid. + * 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_FLUID_SHADER_NULL__ {NULL, NULL, NULL, 0} @@ -228,18 +327,22 @@ static const struct sdis_fluid_shader SDIS_FLUID_SHADER_NULL = struct sdis_interface_side_shader { /* Fixed temperature/flux. May be NULL if the temperature/flux is unknown * onto the whole interface */ - sdis_interface_getter_T temperature; /* In Kelvin. < 0 <=> Unknown temp */ - sdis_interface_getter_T flux; /* In W.m^-2. SDIS_FLUX_NONE <=> no flux */ + sdis_interface_getter_T temperature; /* [K]. SDIS_TEMPERATURE_NONE = Unknown */ + sdis_interface_getter_T flux; /* [W.m^-2]. SDIS_FLUX_NONE = no flux */ /* Control the emissivity of the interface. May be NULL for solid/solid * interface or if the emissivity is 0 onto the whole interface. */ - sdis_interface_getter_T emissivity; /* Overall emissivity. */ - sdis_interface_getter_T specular_fraction; /* Specular part in [0,1] */ + sdis_radiative_interface_getter_T emissivity; /* Overall emissivity */ + sdis_radiative_interface_getter_T specular_fraction; /* Specular part in [0,1] */ /* Reference temperature used in Picard 1 */ sdis_interface_getter_T reference_temperature; + + /* Define whether external sources interact with the interface, i.e. whether + * external fluxes should be processed or not */ + int handle_external_flux; }; -#define SDIS_INTERFACE_SIDE_SHADER_NULL__ { NULL, NULL, NULL, NULL, NULL } +#define SDIS_INTERFACE_SIDE_SHADER_NULL__ { NULL, NULL, NULL, NULL, NULL, 1 } static const struct sdis_interface_side_shader SDIS_INTERFACE_SIDE_SHADER_NULL = SDIS_INTERFACE_SIDE_SHADER_NULL__; @@ -265,6 +368,15 @@ struct sdis_interface_shader { static const struct sdis_interface_shader SDIS_INTERFACE_SHADER_NULL = SDIS_INTERFACE_SHADER_NULL__; +/* Parameters of a radiative environment */ +struct sdis_radiative_env_shader { + sdis_radiative_ray_getter_T temperature; /* [K] */ + sdis_radiative_ray_getter_T reference_temperature; /* [K] */ +}; +#define SDIS_RADIATIVE_ENV_SHADER_NULL__ {NULL, NULL} +static const struct sdis_radiative_env_shader SDIS_RADIATIVE_ENV_SHADER_NULL = + SDIS_RADIATIVE_ENV_SHADER_NULL__; + /******************************************************************************* * Registered heat path data types ******************************************************************************/ @@ -310,57 +422,83 @@ typedef res_T ******************************************************************************/ enum sdis_green_path_end_type { SDIS_GREEN_PATH_END_AT_INTERFACE, + SDIS_GREEN_PATH_END_AT_RADIATIVE_ENV, SDIS_GREEN_PATH_END_IN_VOLUME, - SDIS_GREEN_PATH_END_RADIATIVE, SDIS_GREEN_PATH_END_TYPES_COUNT__, SDIS_GREEN_PATH_END_ERROR = SDIS_GREEN_PATH_END_TYPES_COUNT__ }; -enum sdis_point_type { - SDIS_FRAGMENT, - SDIS_VERTEX, - SDIS_POINT_TYPES_COUNT__, - SDIS_POINT_NONE = SDIS_POINT_TYPES_COUNT__ -}; - /* Spatio temporal point */ -struct sdis_point { +struct sdis_green_path_end { union { + /* Path end in volume */ struct { struct sdis_medium* medium; struct sdis_rwalk_vertex vertex; - } mdmvert; /* Medium and a vertex into it */ + } mdmvert; + /* Path end at interface */ struct { struct sdis_interface* intface; struct sdis_interface_fragment fragment; - } itfrag; /* Interface and a fragmetn onto it */ + } itfrag; + /* Path end in radiative environement */ + struct { + struct sdis_radiative_env* radenv; + struct sdis_radiative_ray ray; + } radenvray; } data; - enum sdis_point_type type; + enum sdis_green_path_end_type type; }; -#define SDIS_POINT_NULL__ { {{NULL, SDIS_RWALK_VERTEX_NULL__}}, SDIS_POINT_NONE} -static const struct sdis_point SDIS_POINT_NULL = SDIS_POINT_NULL__; +#define SDIS_GREEN_PATH_END_NULL__ { \ + {{NULL, SDIS_RWALK_VERTEX_NULL__}}, \ + SDIS_GREEN_PATH_END_ERROR \ +} +static const struct sdis_green_path_end SDIS_GREEN_PATH_END_NULL = + SDIS_GREEN_PATH_END_NULL__; + +struct sdis_green_external_flux_terms { + /* Term relative to source power [K/W] */ + double term_wrt_power; + + /* Term relative to diffuse source radiance [K/W/m^2/sr] */ + double term_wrt_diffuse_radiance; -/* Functor used to process the paths registered against the green function */ + double time; /* [s] */ + double dir[3]; /* Direction on which term_wrt_diffuse_radiance depends */ +}; +#define SDIS_GREEN_EXTERNAL_FLUX_TERMS_NULL__ {0,0,0,{0,0,0}} +static const struct sdis_green_external_flux_terms +SDIS_GREEN_EXTERNAL_FLUX_TERMS_NULL = SDIS_GREEN_EXTERNAL_FLUX_TERMS_NULL__; + +/* Function profile used to process the paths stored in the green function */ typedef res_T (*sdis_process_green_path_T) (struct sdis_green_path* path, void* context); -/* Functor used to process the power factor registered along a green path for a - * given medium */ +/* Function profile used to process power factors registered along a green path + * for a given medium */ typedef res_T (*sdis_process_medium_power_term_T) (struct sdis_medium* medium, - const double power_term, + const double power_term, /* [K/W] */ void* context); -/* Functor used to process the flux factor registered along a green path for a - * given interface side */ +/* Function profile used to process flux factors recorded along a green path for + * a given interface side */ typedef res_T (*sdis_process_interface_flux_term_T) (struct sdis_interface* interf, const enum sdis_side side, - const double flux_term, + const double flux_term, /* [K/W/m^2] */ + void* context); + +/* Function profile used to process external flux factors recorded along a green + * path */ +typedef res_T +(*sdis_process_external_flux_terms_T) + (struct sdis_source* source, + const struct sdis_green_external_flux_terms* terms, void* context); /******************************************************************************* @@ -389,15 +527,6 @@ typedef void double pos[], /* Output list of vertex coordinates */ void* ctx); -struct sdis_ambient_radiative_temperature { - double temperature; /* In Kelvin */ - double reference; /* Used to linearise the radiative transfer */ -}; -#define SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL__ {-1, -1} -static const struct sdis_ambient_radiative_temperature -SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL = - SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL__; - struct sdis_scene_create_args { /* Functors to retrieve the geometric description */ sdis_get_primitive_indices_T get_indices; @@ -410,10 +539,17 @@ struct sdis_scene_create_args { size_t nprimitives; /* #primitives, i.e. #segments or #triangles */ size_t nvertices; /* #vertices */ double fp_to_meter; /* Scale factor used to convert a float in meter */ - struct sdis_ambient_radiative_temperature trad; /* Ambient radiative temp */ /* Min/max temperature used to linearise the radiative temperature */ double t_range[2]; + + /* External source. Can be NULL <=> no external flux will be calculated on + * scene interfaces */ + struct sdis_source* source; + + /* Radiative environment. Can be NULL <=> sampled radiative trajectories + * cannot (in fact must not) reach the surrounding environment */ + struct sdis_radiative_env* radenv; }; #define SDIS_SCENE_CREATE_ARGS_DEFAULT__ { \ @@ -424,8 +560,9 @@ struct sdis_scene_create_args { 0, /* #primitives */ \ 0, /* #vertices */ \ 1.0, /* #Floating point to meter scale factor */ \ - SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL__,/* Ambient radiative temperature */\ - {0.0, -1.0} /* Temperature range */ \ + {SDIS_TEMPERATURE_NONE, SDIS_TEMPERATURE_NONE}, /* Temperature range */ \ + NULL, /* source */ \ + NULL /* Radiative environement */ \ } static const struct sdis_scene_create_args SDIS_SCENE_CREATE_ARGS_DEFAULT = SDIS_SCENE_CREATE_ARGS_DEFAULT__; @@ -447,6 +584,8 @@ struct sdis_solve_probe_args { struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ + enum sdis_diffusion_algorithm diff_algo; /* Diffusion algorithm to be used */ + /* Signature of the estimated green function. The signature is ignored in an * ordinary probe estimation. The signature of the green function can be * queried to verify that it is the expected one with respect to the caller's @@ -461,11 +600,30 @@ struct sdis_solve_probe_args { SDIS_HEAT_PATH_NONE, /* Register paths mask */ \ NULL, /* RNG state */ \ SSP_RNG_THREEFRY, /* RNG type */ \ + SDIS_DIFFUSION_DELTA_SPHERE, /* Diffusion algorithm */ \ {0} /* Signature */ \ } static const struct sdis_solve_probe_args SDIS_SOLVE_PROBE_ARGS_DEFAULT = SDIS_SOLVE_PROBE_ARGS_DEFAULT__; +struct sdis_solve_probe_list_args { + struct sdis_solve_probe_args* probes; /* List of probes to compute */ + size_t nprobes; /* Total number of probes */ + + /* State/type of the RNG to use for the list of probes to calculate. + * The state/type defines per probe is ignored */ + struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ + enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ +}; +#define SDIS_SOLVE_PROBE_LIST_ARGS_DEFAULT__ { \ + NULL, /* List of probes */ \ + 0, /* #probes */ \ + NULL, /* RNG state */ \ + SSP_RNG_THREEFRY /* RNG type */ \ +} +static const struct sdis_solve_probe_list_args +SDIS_SOLVE_PROBE_LIST_ARGS_DEFAULT = SDIS_SOLVE_PROBE_LIST_ARGS_DEFAULT__; + /* Arguments of a probe simulation */ struct sdis_solve_probe_boundary_args { size_t nrealisations; /* #realisations */ @@ -483,6 +641,8 @@ struct sdis_solve_probe_boundary_args { struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ + enum sdis_diffusion_algorithm diff_algo; /* Diffusion algorithm to be used */ + /* Signature of the estimated green function. The signature is ignored in an * ordinary probe estimation. The signature of the green function can be * queried to verify that it is the expected one with respect to the caller's @@ -499,12 +659,34 @@ struct sdis_solve_probe_boundary_args { SDIS_HEAT_PATH_NONE, \ NULL, /* RNG state */ \ SSP_RNG_THREEFRY, /* RNG type */ \ + SDIS_DIFFUSION_DELTA_SPHERE, /* Diffusion algorithm */ \ {0} /* Signature */ \ } static const struct sdis_solve_probe_boundary_args SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT = SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT__; +/* Input arguments of the solve function that distributes the calculations of + * several boundary probes rather than the realizations of a probe */ +struct sdis_solve_probe_boundary_list_args { + struct sdis_solve_probe_boundary_args* probes; /* List of probes to compute */ + size_t nprobes; /* Total number of probes */ + + /* State/type of the RNG to use for the list of probes to calculate. + * The state/type defines per probe is ignored */ + struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ + enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ +}; +#define SDIS_SOLVE_PROBE_BOUNDARY_LIST_ARGS_DEFAULT__ { \ + NULL, /* List of probes */ \ + 0, /* #probes */ \ + NULL, /* RNG state */ \ + SSP_RNG_THREEFRY /* RNG type */ \ +} +static const struct sdis_solve_probe_boundary_list_args +SDIS_SOLVE_PROBE_BOUNDARY_LIST_ARGS_DEFAULT = + SDIS_SOLVE_PROBE_BOUNDARY_LIST_ARGS_DEFAULT__; + struct sdis_solve_boundary_args { size_t nrealisations; /* #realisations */ const size_t* primitives; /* List of boundary primitives to handle */ @@ -521,6 +703,8 @@ struct sdis_solve_boundary_args { struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ + enum sdis_diffusion_algorithm diff_algo; /* Diffusion algorithm to be used */ + /* Signature of the estimated green function. The signature is ignored in an * ordinary probe estimation. The signature of the green function can be * queried to verify that it is the expected one with respect to the caller's @@ -537,6 +721,7 @@ struct sdis_solve_boundary_args { SDIS_HEAT_PATH_NONE, \ NULL, /* RNG state */ \ SSP_RNG_THREEFRY, /* RNG type */ \ + SDIS_DIFFUSION_DELTA_SPHERE, /* Diffusion algorithm */ \ {0} /* Signature */ \ } static const struct sdis_solve_boundary_args SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT = @@ -556,6 +741,8 @@ struct sdis_solve_medium_args { struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ + enum sdis_diffusion_algorithm diff_algo; /* Diffusion algorithm to be used */ + /* Signature of the estimated green function. The signature is ignored in an * ordinary probe estimation. The signature of the green function can be * queried to verify that it is the expected one with respect to the caller's @@ -570,6 +757,7 @@ struct sdis_solve_medium_args { SDIS_HEAT_PATH_NONE, \ NULL, /* RNG state */ \ SSP_RNG_THREEFRY, /* RNG type */ \ + SDIS_DIFFUSION_DELTA_SPHERE, /* Diffusion algorithm */ \ {0} /* Signature */ \ } static const struct sdis_solve_medium_args SDIS_SOLVE_MEDIUM_ARGS_DEFAULT = @@ -588,6 +776,8 @@ struct sdis_solve_probe_boundary_flux_args { struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ + + enum sdis_diffusion_algorithm diff_algo; /* Diffusion algorithm to be used */ }; #define SDIS_SOLVE_PROBE_BOUNDARY_FLUX_ARGS_DEFAULT__ { \ 10000, /* #realisations */ \ @@ -596,7 +786,8 @@ struct sdis_solve_probe_boundary_flux_args { {DBL_MAX,DBL_MAX}, /* Time range */ \ 1, /* Picard order */ \ NULL, /* RNG state */ \ - SSP_RNG_THREEFRY /* RNG type */ \ + SSP_RNG_THREEFRY, /* RNG type */ \ + SDIS_DIFFUSION_DELTA_SPHERE /* Diffusion algorithm */ \ } static const struct sdis_solve_probe_boundary_flux_args SDIS_SOLVE_PROBE_BOUNDARY_FLUX_ARGS_DEFAULT = @@ -615,6 +806,8 @@ struct sdis_solve_boundary_flux_args { struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ enum ssp_rng_type rng_type; /* RNG type to use if `rng_state' is NULL */ + + enum sdis_diffusion_algorithm diff_algo; /* Diffusion algorithm to be used */ }; #define SDIS_SOLVE_BOUNDARY_FLUX_ARGS_DEFAULT__ { \ 10000, /* #realisations */ \ @@ -623,7 +816,8 @@ struct sdis_solve_boundary_flux_args { {DBL_MAX,DBL_MAX}, /* Time range */ \ 1, /* Picard order */ \ NULL, /* RNG state */ \ - SSP_RNG_THREEFRY /* RNG type */ \ + SSP_RNG_THREEFRY, /* RNG type */ \ + SDIS_DIFFUSION_DELTA_SPHERE /* Diffusion algorithm */ \ } static const struct sdis_solve_boundary_flux_args SDIS_SOLVE_BOUNDARY_FLUX_ARGS_DEFAULT = @@ -644,6 +838,8 @@ struct sdis_solve_camera_args { struct ssp_rng* rng_state; /* Initial RNG state. May be NULL */ enum ssp_rng_type rng_type; /* RNG type to use */ + + enum sdis_diffusion_algorithm diff_algo; /* Diffusion algorithm to be used */ }; #define SDIS_SOLVE_CAMERA_ARGS_DEFAULT__ { \ NULL, /* Camera */ \ @@ -653,7 +849,8 @@ struct sdis_solve_camera_args { 256, /* #realisations per pixel */ \ SDIS_HEAT_PATH_NONE, \ NULL, /* RNG state */ \ - SSP_RNG_THREEFRY /* RNG type */ \ + SSP_RNG_THREEFRY, /* RNG type */ \ + SDIS_DIFFUSION_DELTA_SPHERE /* Diffusion algorithm */ \ } static const struct sdis_solve_camera_args SDIS_SOLVE_CAMERA_ARGS_DEFAULT = SDIS_SOLVE_CAMERA_ARGS_DEFAULT__; @@ -712,6 +909,11 @@ sdis_device_ref_put (struct sdis_device* dev); SDIS_API res_T +sdis_device_is_mpi_used + (struct sdis_device* dev, + int* is_mpi_used); + +SDIS_API res_T sdis_device_get_mpi_rank (struct sdis_device* dev, int* rank); @@ -908,6 +1110,66 @@ sdis_interface_get_id (const struct sdis_interface* interf); /******************************************************************************* + * API of the radiative environment. Describes the system when the sampled + * radiative paths reach infinity. + ******************************************************************************/ +SDIS_API res_T +sdis_radiative_env_create + (struct sdis_device* dev, + const struct sdis_radiative_env_shader* shader, + struct sdis_data* data, /* Data sent to the shader. May be NULL */ + struct sdis_radiative_env** radenv); + +SDIS_API res_T +sdis_radiative_env_ref_get + (struct sdis_radiative_env* radenv); + +SDIS_API res_T +sdis_radiative_env_ref_put + (struct sdis_radiative_env* radenv); + +SDIS_API res_T +sdis_radiative_env_get_shader + (struct sdis_radiative_env* radenv, + struct sdis_radiative_env_shader* shader); + +SDIS_API struct sdis_data* +sdis_radiative_env_get_data + (struct sdis_radiative_env* radenv); + +/******************************************************************************* + * External source API. When a scene has external sources, an external flux + * (in both its direct and diffuse parts) is imposed on the interfaces. + ******************************************************************************/ +SDIS_API res_T +sdis_spherical_source_create + (struct sdis_device* dev, + const struct sdis_spherical_source_shader* shader, + struct sdis_data* data, /* Data sent to the shader. May be NULL */ + struct sdis_source** source); + +SDIS_API res_T +sdis_spherical_source_get_shader + (const struct sdis_source* source, + struct sdis_spherical_source_shader* shader); + +SDIS_API res_T +sdis_source_ref_get + (struct sdis_source* source); + +SDIS_API res_T +sdis_source_ref_put + (struct sdis_source* source); + +SDIS_API struct sdis_data* +sdis_source_get_data + (struct sdis_source* source); + +SDIS_API unsigned +sdis_source_get_id + (const struct sdis_source* source); + +/******************************************************************************* * A scene is a collection of primitives. Each primitive is the geometric * support of the interface between 2 media. ******************************************************************************/ @@ -980,19 +1242,6 @@ sdis_scene_set_fp_to_meter (struct sdis_scene* scn, const double fp_to_meter); -/* Get scene's ambient radiative temperature */ -SDIS_API res_T -sdis_scene_get_ambient_radiative_temperature - (const struct sdis_scene* scn, - struct sdis_ambient_radiative_temperature* trad); - -/* Set scene's ambient radiative temperature. If set negative, any sample - * ending in ambient radiative temperature will fail */ -SDIS_API res_T -sdis_scene_set_ambient_radiative_temperature - (struct sdis_scene* scn, - const struct sdis_ambient_radiative_temperature* trad); - /* Get scene's minimum/maximum temperature */ SDIS_API res_T sdis_scene_get_temperature_range @@ -1015,8 +1264,7 @@ sdis_scene_set_temperature_range SDIS_API res_T sdis_scene_find_closest_point (const struct sdis_scene* scn, - const double pos[], /* Query position */ - const double radius, /* Maximum search distance around pos */ + const struct sdis_scene_find_closest_point_args* args, size_t* iprim, /* Primitive index onto which the closest point lies */ double uv[]); /* Parametric cordinate onto the primitive */ @@ -1089,6 +1337,22 @@ sdis_scene_get_medium_spread const struct sdis_medium* mdm, double* spread); +SDIS_API res_T +sdis_scene_get_device + (struct sdis_scene* scn, + struct sdis_device** device); + +SDIS_API res_T +sdis_scene_get_source + (struct sdis_scene* scn, + struct sdis_source** src); /* The returned pointer can be NULL <=> no source */ + +SDIS_API res_T +sdis_scene_get_radiative_env + (struct sdis_scene* scn, + /* The returned pointer can be NULL, i.e. there is no radiative environement*/ + struct sdis_radiative_env** radenv); + /******************************************************************************* * An estimator stores the state of a simulation ******************************************************************************/ @@ -1239,18 +1503,12 @@ sdis_green_path_get_elapsed_time (struct sdis_green_path* path_handle, double* elapsed); -/* Retrieve the path's end type. */ +/* Retrieve the spatio-temporal limit of a path used to estimate the green + * function */ SDIS_API res_T -sdis_green_path_get_end_type +sdis_green_path_get_end (struct sdis_green_path* path, - enum sdis_green_path_end_type* type); - -/* Retrieve the spatio-temporal end point of a path used to estimate the green - * function. Return RES_BAD_OP for paths ending radiative. */ -SDIS_API res_T -sdis_green_path_get_limit_point - (struct sdis_green_path* path, - struct sdis_point* pt); + struct sdis_green_path_end* end); /* Retrieve the green function the path belongs to */ SDIS_API res_T @@ -1270,6 +1528,11 @@ sdis_green_function_get_flux_terms_count (const struct sdis_green_path* path, size_t* nterms); +SDIS_API res_T +sdis_green_function_get_external_flux_terms_count + (const struct sdis_green_path* path, + size_t* nterms); + /* Iterate over all "power terms" associated to the path. Multiply each term * by the power of their associated medium, that is assumed to be constant in * time and space, gives the medium power registered along the path. */ @@ -1288,6 +1551,13 @@ sdis_green_path_for_each_flux_term sdis_process_interface_flux_term_T func, void* context); +/* Iterate over all external flux terms associated to the path */ +SDIS_API res_T +sdis_green_path_for_each_external_flux_terms + (struct sdis_green_path* path, + sdis_process_external_flux_terms_T func, + void* context); + /******************************************************************************* * Heat path API ******************************************************************************/ @@ -1380,6 +1650,27 @@ sdis_compute_power struct sdis_estimator** estimator); /******************************************************************************* + * Solvers of a list of probes + * + * Unlike their single-probe counterpart, this function parallelizes the list of + * probes, rather than calculating a single probe. Calling these functions is + * therefore more advantageous in terms of load distribution when the number of + * probes to be evaluated is large compared to the cost of calculating a single + * probe. + ******************************************************************************/ +SDIS_API res_T +sdis_solve_probe_list + (struct sdis_scene* scn, + const struct sdis_solve_probe_list_args* args, + struct sdis_estimator_buffer** buf); + +SDIS_API res_T +sdis_solve_probe_boundary_list + (struct sdis_scene* scn, + const struct sdis_solve_probe_boundary_list_args* args, + struct sdis_estimator_buffer** buf); + +/******************************************************************************* * Green solvers. * * Note that only the interfaces/media with flux/volumic power defined during @@ -1390,7 +1681,7 @@ sdis_compute_power * * Also note that the green solvers assume that the interface fluxes are * constant in time and space. The same applies to the volumic power of the - * solid media. + * solid media and the power of external sources. * * If these assumptions are not ensured by the caller, the behavior of the * estimated green function is undefined. diff --git a/src/sdis_Xd_begin.h b/src/sdis_Xd_begin.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -13,60 +13,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SDIS_XD_BEGIN_H -#define SDIS_XD_BEGIN_H - +#include "sdis.h" #include <rsys/rsys.h> -/* Forward declaration */ -struct green_path_handle; -struct sdis_heat_path; - -struct rwalk_context { - struct green_path_handle* green_path; - struct sdis_heat_path* heat_path; - - double Tmin; /* Lower bound temperature */ - double Tmin2; /* Tmin^2 */ - double Tmin3; /* Tmin^3 */ - - double That; /* Upper bound temperature */ - double That2; /* That^2 */ - double That3; /* That^3 */ - - /* Maximum branchings i.e. the maximum number of times - * XD(compute_temperature) can be called. It controls the number of - * ramifications of the heat path and currently is correlated to the Picard - * order used to estimate the radiative temperature. max_branchings == - * picard_order-1 */ - size_t max_branchings; - - /* Number of heat path branchings */ - size_t nbranchings; -}; -#define RWALK_CONTEXT_NULL__ { \ - NULL, /* Green path */ \ - NULL, /* Heat path */ \ - 0, /* Tmin */ \ - 0, /* Tmin^2 */ \ - 0, /* Tmin^3 */ \ - 0, /* That */ \ - 0, /* That^2 */ \ - 0, /* That^3 */ \ - 0, /* Max #branchings */ \ - SIZE_MAX, /* #branchings */ \ -} -static const struct rwalk_context RWALK_CONTEXT_NULL = RWALK_CONTEXT_NULL__; - -static INLINE size_t -get_picard_order(const struct rwalk_context* ctx) -{ - ASSERT(ctx); - return ctx->max_branchings + 1; -} - -#endif /* SDIS_XD_BEGIN_H */ - #ifdef SDIS_XD_BEGIN_H__ #error "This header is already included without its associated sdis_Xd_end.h file." #endif @@ -82,10 +31,18 @@ get_picard_order(const struct rwalk_context* ctx) #include <rsys/double2.h> #include <rsys/float2.h> #include <star/s2d.h> + + #define FORMAT_VECX "%g, %g" + #define SPLITX(V) SPLIT2(V) + #elif SDIS_XD_DIMENSION == 3 #include <rsys/double3.h> #include <rsys/float3.h> #include <star/s3d.h> + + #define FORMAT_VECX "%g, %g, %g" + #define SPLITX(V) SPLIT3(V) + #else #error "Invalid dimension." #endif @@ -105,15 +62,19 @@ get_picard_order(const struct rwalk_context* ctx) #define SXD_FLOAT2 CONCAT(CONCAT(S, DIM), D_FLOAT2) #define SXD_FLOAT3 CONCAT(CONCAT(S, DIM), D_FLOAT3) #define SXD_FLOATX CONCAT(CONCAT(CONCAT(S,DIM), D_FLOAT), DIM) +#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_EQ CONCAT(CONCAT(S, DIM), D_PRIMITIVE_EQ) /* Vector macros generic to SDIS_XD_DIMENSION */ #define dX(Func) CONCAT(CONCAT(CONCAT(d, DIM), _), Func) #define fX(Func) CONCAT(CONCAT(CONCAT(f, DIM), _), Func) #define fX_set_dX CONCAT(CONCAT(CONCAT(f, DIM), _set_d), DIM) +#define fXX_mulfX CONCAT(CONCAT(CONCAT(CONCAT(f, DIM), DIM), _mulf), DIM) #define dX_set_fX CONCAT(CONCAT(CONCAT(d, DIM), _set_f), DIM) -/* Macro making generic its submitted nae to SDIS_XD_DIMENSION */ +/* 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 */ @@ -125,16 +86,22 @@ get_picard_order(const struct rwalk_context* ctx) #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, SDIS_SIDE_NULL__ + SDIS_RWALK_VERTEX_NULL__, NULL, SXD_HIT_NULL__, {0,0,0}, 0, SDIS_SIDE_NULL__ }; struct XD(temperature) { @@ -150,4 +117,3 @@ struct XD(temperature) { 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 @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -31,11 +31,17 @@ #undef SXD_FLOAT2 #undef SXD_FLOAT3 #undef SXD_FLOATX +#undef SXD_GET_PRIMITIVE #undef SXD_SAMPLE +#undef SXD_TRACE #undef dX #undef fX #undef fX_set_dX +#undef fXX_mulfX #undef dX_set_fX +#undef FORMAT_VECX +#undef SPLITX + #undef SDIS_XD_BEGIN_H__ diff --git a/src/sdis_c.h b/src/sdis_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -85,11 +85,25 @@ free_process_progress (struct sdis_device* dev, int32_t progress[]); -/* Compute the number of realisations for the current process */ +/* Calculate the index range of the current process. It returns the size of the + * range. The overall_count is the number of calculations to parallelize between + * processes. For example, it may be the number of realisations of one + * calculation, or the total number of probe calculations. */ extern LOCAL_SYM size_t +compute_process_index_range + (const struct sdis_device* dev, + const size_t overall_count, + size_t range[2]); /* [lower, upper[ */ + +/* Return the number of realisations for the current process */ +static INLINE size_t compute_process_realisations_count (const struct sdis_device* dev, - const size_t overall_realisations_count); + const size_t overall_realisations_count) +{ + size_t range[2]; + return compute_process_index_range(dev, overall_realisations_count, range); +} /* Gather the accumulators and sum them in acc. With MPI, non master processes * store in acc the gathering of their per thread accumulators that are sent to @@ -102,6 +116,21 @@ gather_accumulators const struct accum* per_thread_acc, struct accum* acc); +/* Collect accumulators evaluated over multiple processes, with each accumulator + * storing a complete Monte Carlo calculation. Without MPI, nothing happens + * since the per_probe_acc variable already stores the entire list of + * accumulators. With MPI, non-master processes send their list of accumulators + * to the master process which saves them in the per_probe_acc, after its + * accumulators that it has managed, sorted against the identifiers of the + * probes listed in process_probes. */ +extern LOCAL_SYM res_T +gather_accumulators_list + (struct sdis_device* dev, + const enum mpi_sdis_message msg, + const size_t nprobes, /* Total number of probes */ + const size_t process_probes[2], /* Ids of the probes managed by the process */ + struct accum* per_probe_acc); /* List of per probe accumulators */ + /* Gather the green functions. With MPI, non master processes store in green * the gathering of their per thread green functions and sent the result to the * master process. The master process gathers both per thread green functions diff --git a/src/sdis_camera.c b/src/sdis_camera.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_camera.h b/src/sdis_camera.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_data.c b/src/sdis_data.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_device.c b/src/sdis_device.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -25,6 +25,7 @@ #include <star/s2d.h> #include <star/s3d.h> #include <star/ssp.h> +#include <star/swf.h> #include <omp.h> @@ -251,6 +252,37 @@ error: } static INLINE res_T +setup_starwf(struct sdis_device* dev) +{ + struct swf_H_tabulate_args H2d_args = SWF_H2D_TABULATE_ARGS_DEFAULT; + struct swf_H_tabulate_args H3d_args = SWF_H3D_TABULATE_ARGS_DEFAULT; + res_T res = RES_OK; + ASSERT(dev); + + H2d_args.allocator = dev->allocator; + H3d_args.allocator = dev->allocator; + + res = swf_H2d_tabulate(&H2d_args, &dev->H_2d); + if(res != RES_OK) { + log_err(dev, "Unable to tabulate H2d function -- %s.\n", + res_to_cstr(res)); + goto error; + } + + res = swf_H3d_tabulate(&H3d_args, &dev->H_3d); + if(res != RES_OK) { + log_err(dev, "Unable to tabulate H3d function -- %s.\n", + res_to_cstr(res)); + goto error; + } + +exit: + return res; +error: + goto exit; +} + +static INLINE res_T setup_mpi(struct sdis_device* dev, const struct sdis_device_create_args* args) { ASSERT(dev && args); @@ -279,11 +311,15 @@ device_release(ref_T* ref) dev = CONTAINER_OF(ref, struct sdis_device, ref); if(dev->s2d_dev) S2D(device_ref_put(dev->s2d_dev)); if(dev->s3d_dev) S3D(device_ref_put(dev->s3d_dev)); + if(dev->H_2d) SWF(tabulation_ref_put(dev->H_2d)); + if(dev->H_3d) SWF(tabulation_ref_put(dev->H_3d)); if(dev->logger == &dev->logger__) logger_release(&dev->logger__); ASSERT(flist_name_is_empty(&dev->interfaces_names)); ASSERT(flist_name_is_empty(&dev->media_names)); + ASSERT(flist_name_is_empty(&dev->source_names)); flist_name_release(&dev->interfaces_names); flist_name_release(&dev->media_names); + flist_name_release(&dev->source_names); #ifdef SDIS_ENABLE_MPI if(dev->mpi_mutex) mutex_destroy(dev->mpi_mutex); str_release(&dev->mpi_err_str); @@ -332,6 +368,7 @@ sdis_device_create ref_init(&dev->ref); flist_name_init(allocator, &dev->interfaces_names); flist_name_init(allocator, &dev->media_names); + flist_name_init(allocator, &dev->source_names); #ifdef SDIS_ENABLE_MPI str_init(allocator, &dev->mpi_err_str); #endif @@ -342,6 +379,8 @@ sdis_device_create if(res != RES_OK) goto error; res = setup_star3d(dev); if(res != RES_OK) goto error; + res = setup_starwf(dev); + if(res != RES_OK) goto error; res = setup_mpi(dev, args); if(res != RES_OK) goto error; @@ -373,6 +412,18 @@ sdis_device_ref_put(struct sdis_device* dev) } res_T +sdis_device_is_mpi_used(struct sdis_device* dev, int* is_mpi_used) +{ + if(!dev || !is_mpi_used) return RES_BAD_ARG; +#ifndef SDIS_ENABLE_MPI + *is_mpi_used = 0; +#else + *is_mpi_used = dev->use_mpi; +#endif + return RES_OK; +} + +res_T sdis_device_get_mpi_rank(struct sdis_device* dev, int* rank) { #ifndef SDIS_ENABLE_MPI diff --git a/src/sdis_device_c.h b/src/sdis_device_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -36,6 +36,7 @@ struct mutex; struct ssp_rng; struct ssp_rng_proxy; +struct swf_tabulation; struct name { FITEM; void* mem; }; #define FITEM_TYPE name @@ -60,10 +61,14 @@ struct sdis_device { struct flist_name interfaces_names; struct flist_name media_names; + struct flist_name source_names; struct s2d_device* s2d_dev; struct s3d_device* s3d_dev; + struct swf_tabulation* H_2d; + struct swf_tabulation* H_3d; + ref_T ref; }; diff --git a/src/sdis_estimator.c b/src/sdis_estimator.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_estimator_buffer.c b/src/sdis_estimator_buffer.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -273,3 +273,8 @@ estimator_buffer_save_rng_state return create_rng_from_rng_proxy(buf->dev, proxy, &buf->rng); } +/* Define the functions generic to the observable type */ +#define SDIS_X_OBS probe +#include "sdis_estimator_buffer_X_obs.h" +#define SDIS_X_OBS probe_boundary +#include "sdis_estimator_buffer_X_obs.h" diff --git a/src/sdis_estimator_buffer_X_obs.h b/src/sdis_estimator_buffer_X_obs.h @@ -0,0 +1,118 @@ +/* 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/>. */ + +/* Define functions on estimator buffer generic to the observable type. */ + +#ifndef SDIS_ESTIMATOR_BUFFER_X_OBS_H +#define SDIS_ESTIMATOR_BUFFER_X_OBS_H + +#include "sdis.h" +#include "sdis_estimator_c.h" +#include "sdis_estimator_buffer_c.h" +#include "sdis_misc.h" + +#endif /* SDIS_ESTIMATOR_BUFFER_X_OBS_H */ + +/* Check the generic observable parameter */ +#ifndef SDIS_X_OBS + #error "The SDIS_X_OBS macro must be defined." +#endif + +#define X_OBS(Name) CONCAT(CONCAT(Name, _), SDIS_X_OBS) +#define SDIS_SOLVE_X_OBS_ARGS CONCAT(CONCAT(sdis_solve_, SDIS_X_OBS),_args) + +res_T +X_OBS(estimator_buffer_create_from_observable_list) + (struct sdis_device* dev, + struct ssp_rng_proxy* rng_proxy, + const struct SDIS_SOLVE_X_OBS_ARGS obs_list_args[], + const struct accum* per_obs_acc_temp, + const struct accum* per_obs_acc_time, + const size_t nobs, /* #observables */ + struct sdis_estimator_buffer** out_estim_buffer) +{ + /* Accumulators throughout the buffer */ + struct accum acc_temp = ACCUM_NULL; + struct accum acc_time = ACCUM_NULL; + size_t nrealisations = 0; + + struct sdis_estimator_buffer* estim_buf = NULL; + size_t iobs = 0; + res_T res = RES_OK; + + ASSERT(dev && rng_proxy); + ASSERT(obs_list_args || !nobs); + ASSERT(per_obs_acc_time && per_obs_acc_time && out_estim_buffer); + + res = estimator_buffer_create(dev, nobs, 1, &estim_buf); + if(res != RES_OK) { + log_err(dev, "Unable to allocate the estimator buffer.\n"); + goto error; + } + + FOR_EACH(iobs, 0, nobs) { + const struct SDIS_SOLVE_X_OBS_ARGS* obs_args = NULL; + const struct accum* obs_acc_temp = NULL; + const struct accum* obs_acc_time = NULL; + struct sdis_estimator* estim = NULL; + + /* Get observable data */ + obs_args = obs_list_args + iobs; + obs_acc_temp = per_obs_acc_temp + iobs; + obs_acc_time = per_obs_acc_time + iobs; + ASSERT(obs_acc_temp->count == obs_acc_time->count); + + /* Setup the estimator of the current observable */ + estim = estimator_buffer_grab(estim_buf, iobs, 0); + estimator_setup_realisations_count + (estim, obs_args->nrealisations, obs_acc_temp->count); + estimator_setup_temperature + (estim, obs_acc_temp->sum, obs_acc_temp->sum2); + estimator_setup_realisation_time + (estim, obs_acc_time->sum, obs_acc_time->sum2); + + /* Update global accumulators */ + acc_temp.sum += obs_acc_temp->sum; + acc_temp.sum2 += obs_acc_temp->sum2; + acc_temp.count += obs_acc_temp->count; + acc_time.sum += obs_acc_time->sum; + acc_time.sum2 += obs_acc_time->sum2; + acc_time.count += obs_acc_time->count; + nrealisations += obs_args->nrealisations; + } + + ASSERT(acc_temp.count == acc_time.count); + + /* Setup global estimator */ + estimator_buffer_setup_realisations_count + (estim_buf, nrealisations, acc_temp.count); + estimator_buffer_setup_temperature + (estim_buf, acc_temp.sum, acc_temp.sum2); + estimator_buffer_setup_realisation_time + (estim_buf, acc_time.sum, acc_time.sum2); + + res = estimator_buffer_save_rng_state(estim_buf, rng_proxy); + if(res != RES_OK) goto error; + +exit: + *out_estim_buffer = estim_buf; + return res; +error: + goto exit; +} + +#undef X_OBS +#undef SDIS_SOLVE_X_OBS_ARGS +#undef SDIS_X_OBS diff --git a/src/sdis_estimator_buffer_c.h b/src/sdis_estimator_buffer_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -59,5 +59,25 @@ estimator_buffer_save_rng_state (struct sdis_estimator_buffer* buf, const struct ssp_rng_proxy* proxy); +extern LOCAL_SYM res_T +estimator_buffer_create_from_observable_list_probe + (struct sdis_device* dev, + struct ssp_rng_proxy* rng_proxy, + const struct sdis_solve_probe_args obs_list_args[], + const struct accum* per_obs_acc_temp, + const struct accum* per_obs_acc_time, + const size_t nobs, /* #observables */ + struct sdis_estimator_buffer** out_estim_buffer); + +extern LOCAL_SYM res_T +estimator_buffer_create_from_observable_list_probe_boundary + (struct sdis_device* dev, + struct ssp_rng_proxy* rng_proxy, + const struct sdis_solve_probe_boundary_args obs_list_args[], + const struct accum* per_obs_acc_temp, + const struct accum* per_obs_acc_time, + const size_t nobs, /* #observables */ + struct sdis_estimator_buffer** out_estim_buffer); + #endif /* SDIS_ESTIMATOR_BUFFER_C_H */ diff --git a/src/sdis_estimator_c.h b/src/sdis_estimator_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -26,7 +26,6 @@ struct ssp_rng; struct sdis_device; struct sdis_estimator; -enum sdis_estimator_type; enum flux_name { FLUX_CONVECTIVE, @@ -39,7 +38,7 @@ enum flux_name { struct sdis_estimator { struct accum temperature; struct accum realisation_time; - struct accum fluxes[FLUX_NAMES_COUNT__]; + struct accum fluxes[FLUX_NAMES_COUNT__]; struct { struct accum power; diff --git a/src/sdis_green.c b/src/sdis_green.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -20,10 +20,13 @@ #include "sdis_log.h" #include "sdis_medium_c.h" #include "sdis_misc.h" +#include "sdis_radiative_env_c.h" #include "sdis_scene_c.h" +#include "sdis_source_c.h" #include <star/ssp.h> +#include <rsys/cstr.h> #include <rsys/dynamic_array.h> #include <rsys/hash_table.h> #include <rsys/mem_allocator.h> @@ -81,13 +84,30 @@ flux_term_init(struct mem_allocator* allocator, struct flux_term* term) #define DARRAY_FUNCTOR_INIT flux_term_init #include <rsys/dynamic_array.h> +static INLINE void +extflux_terms_init + (struct mem_allocator* allocator, + struct sdis_green_external_flux_terms* terms) +{ + ASSERT(terms); (void)allocator; + *terms = SDIS_GREEN_EXTERNAL_FLUX_TERMS_NULL; +} + +/* Generate the dynamic array of the external flux terms */ +#define DARRAY_NAME extflux_terms +#define DARRAY_DATA struct sdis_green_external_flux_terms +#define DARRAY_FUNCTOR_INIT extflux_terms_init +#include <rsys/dynamic_array.h> + struct green_path { double elapsed_time; + struct darray_extflux_terms extflux_terms; /* List of external flux terms */ struct darray_flux_term flux_terms; /* List of flux terms */ struct darray_power_term power_terms; /* List of volumic power terms */ union { struct sdis_rwalk_vertex vertex; struct sdis_interface_fragment fragment; + struct sdis_radiative_ray ray; } limit; unsigned limit_id; /* Identifier of the limit medium/interface */ enum sdis_green_path_end_type end_type; @@ -102,11 +122,13 @@ static INLINE void green_path_init(struct mem_allocator* allocator, struct green_path* path) { ASSERT(path); + darray_extflux_terms_init(allocator, &path->extflux_terms); darray_flux_term_init(allocator, &path->flux_terms); darray_power_term_init(allocator, &path->power_terms); path->elapsed_time = -INF; path->limit.vertex = SDIS_RWALK_VERTEX_NULL; path->limit.fragment = SDIS_INTERFACE_FRAGMENT_NULL; + path->limit.ray = SDIS_RADIATIVE_RAY_NULL; path->limit_id = UINT_MAX; path->end_type = SDIS_GREEN_PATH_END_TYPES_COUNT__; path->ilast_medium = UINT16_MAX; @@ -119,6 +141,7 @@ green_path_release(struct green_path* path) ASSERT(path); darray_flux_term_release(&path->flux_terms); darray_power_term_release(&path->power_terms); + darray_extflux_terms_release(&path->extflux_terms); } static INLINE res_T @@ -132,6 +155,8 @@ green_path_copy(struct green_path* dst, const struct green_path* src) dst->end_type = src->end_type; dst->ilast_medium = src->ilast_medium; dst->ilast_interf = src->ilast_interf; + res = darray_extflux_terms_copy(&dst->extflux_terms, &src->extflux_terms); + if(res != RES_OK) return res; res = darray_flux_term_copy(&dst->flux_terms, &src->flux_terms); if(res != RES_OK) return res; res = darray_power_term_copy(&dst->power_terms, &src->power_terms); @@ -150,6 +175,9 @@ green_path_copy_and_clear(struct green_path* dst, struct green_path* src) dst->end_type = src->end_type; dst->ilast_medium = src->ilast_medium; dst->ilast_interf = src->ilast_interf; + res = darray_extflux_terms_copy_and_clear + (&dst->extflux_terms, &src->extflux_terms); + if(res != RES_OK) return res; res = darray_flux_term_copy_and_clear(&dst->flux_terms, &src->flux_terms); if(res != RES_OK) return res; res = darray_power_term_copy_and_clear(&dst->power_terms, &src->power_terms); @@ -169,6 +197,9 @@ green_path_copy_and_release(struct green_path* dst, struct green_path* src) dst->end_type = src->end_type; dst->ilast_medium = src->ilast_medium; dst->ilast_interf = src->ilast_interf; + res = darray_extflux_terms_copy_and_release + (&dst->extflux_terms, &src->extflux_terms); + if(res != RES_OK) return res; res = darray_flux_term_copy_and_release(&dst->flux_terms, &src->flux_terms); if(res != RES_OK) return res; res = darray_power_term_copy_and_release(&dst->power_terms, &src->power_terms); @@ -193,6 +224,11 @@ green_path_write(const struct green_path* path, FILE* stream) /* Write elapsed time */ WRITE(&path->elapsed_time, 1); + /* Write the list of external flux terms */ + sz = darray_extflux_terms_size_get(&path->extflux_terms); + WRITE(&sz, 1); + WRITE(darray_extflux_terms_cdata_get(&path->extflux_terms), sz); + /* Write the list of flux terms */ sz = darray_flux_term_size_get(&path->flux_terms); WRITE(&sz, 1); @@ -243,6 +279,12 @@ green_path_read(struct green_path* path, FILE* stream) /* Read elapsed time */ READ(&path->elapsed_time, 1); + /* Read the list of external flux terms */ + READ(&sz, 1); + res = darray_extflux_terms_resize(&path->extflux_terms, sz); + if(res != RES_OK) goto error; + READ(darray_extflux_terms_data_get(&path->extflux_terms), sz); + /* Read the list of flux terms */ READ(&sz, 1); res = darray_flux_term_resize(&path->flux_terms, sz); @@ -391,26 +433,116 @@ green_function_fetch_interf return *pinterf; } +static double /* [K] */ +green_path_power_contribution + (struct sdis_green_function* green, + const struct green_path* path) +{ + double temperature = 0; /* [K] */ + size_t i=0, n=0; + + ASSERT(green && path); + + n = darray_power_term_size_get(&path->power_terms); + FOR_EACH(i, 0, n) { + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + const struct power_term* power_term = NULL; + const struct sdis_medium* medium = NULL; + double power = 0; /* [W] */ + + power_term = darray_power_term_cdata_get(&path->power_terms) + i; + medium = green_function_fetch_medium(green, power_term->id); + + /* Dummy argument used only to satisfy the function profile used to recover + * power. Its position is unused, since power is assumed to be constant in + * space, and its time is set to infinity, since the green function is + * assumed to be evaluated at steady state */ + vtx.time = INF; + power = solid_get_volumic_power(medium, &vtx); + + temperature += power_term->term * power; /* [K] */ + } + return temperature; /* [K] */ +} + +static double /* [K] */ +green_path_flux_contribution + (struct sdis_green_function* green, + const struct green_path* path) +{ + double temperature = 0; + size_t i=0, n=0; + + ASSERT(green && path); + + n = darray_flux_term_size_get(&path->flux_terms); + FOR_EACH(i, 0, n) { + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + const struct flux_term* flux_term = NULL; + const struct sdis_interface* interf = NULL; + double flux = 0; /* [W/m^2] */ + + flux_term = darray_flux_term_cdata_get(&path->flux_terms) + i; + interf = green_function_fetch_interf(green, flux_term->id); + + /* Interface fragment. Its position is unused, since flux is assumed to be + * constant in space, and its time is set to infinity, since the green + * function is assumed to be evaluated at steady state */ + frag.time = INF; + frag.side = flux_term->side; + flux = interface_side_get_flux(interf, &frag); + + temperature += flux_term->term * flux; /* [K] */ + } + return temperature; /* [K] */ +} + +static double /* [K] */ +green_path_external_flux_contribution + (struct sdis_green_function* green, + const struct green_path* path) +{ + const struct sdis_source* extsrc = NULL; + double value = 0; + size_t i=0, n=0; + + ASSERT(green && path); + + if((extsrc = green->scn->source) == NULL) return 0; + + n = darray_extflux_terms_size_get(&path->extflux_terms); + FOR_EACH(i, 0, n) { + const struct sdis_green_external_flux_terms* extflux = NULL; + double power = 0; /* [W] */ + double diffrad = 0; /* [W/m^2/sr] */ + + extflux = darray_extflux_terms_cdata_get(&path->extflux_terms)+i; + power = source_get_power(extsrc, extflux->time); + diffrad = source_get_diffuse_radiance(extsrc, extflux->time, extflux->dir); + + value += extflux->term_wrt_power * power; /* [K] */ + value += extflux->term_wrt_diffuse_radiance * diffrad; /* [K] */ + } + return value; /* [K] */ +} + static res_T green_function_solve_path (struct sdis_green_function* green, const size_t ipath, double* weight) { - struct sdis_ambient_radiative_temperature trad = - SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL; - const struct power_term* power_terms = NULL; - const struct flux_term* flux_terms = NULL; const struct green_path* path = NULL; const struct sdis_medium* medium = NULL; const struct sdis_interface* interf = NULL; struct sdis_scene* scn = NULL; struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + struct sdis_radiative_ray ray = SDIS_RADIATIVE_RAY_NULL; double power; double flux; + double external_flux; double end_temperature; - size_t i, n; res_T res = RES_OK; ASSERT(green && ipath < darray_green_path_size_get(&green->paths) && weight); @@ -420,26 +552,9 @@ green_function_solve_path goto error; } - /* Compute medium power terms */ - power = 0; - n = darray_power_term_size_get(&path->power_terms); - power_terms = darray_power_term_cdata_get(&path->power_terms); - FOR_EACH(i, 0, n) { - vtx.time = INF; - medium = green_function_fetch_medium(green, power_terms[i].id); - power += power_terms[i].term * solid_get_volumic_power(medium, &vtx); - } - - /* Compute interface fluxes */ - flux = 0; - n = darray_flux_term_size_get(&path->flux_terms); - flux_terms = darray_flux_term_cdata_get(&path->flux_terms); - FOR_EACH(i, 0, n) { - frag.time = INF; - frag.side = flux_terms[i].side; - interf = green_function_fetch_interf(green, flux_terms[i].id); - flux += flux_terms[i].term * interface_side_get_flux(interf, &frag); - } + power = green_path_power_contribution(green, path); + flux = green_path_flux_contribution(green, path); + external_flux = green_path_external_flux_contribution(green, path); /* Compute path's end temperature */ switch(path->end_type) { @@ -448,25 +563,28 @@ green_function_solve_path frag = path->limit.fragment; end_temperature = interface_side_get_temperature(interf, &frag); break; + case SDIS_GREEN_PATH_END_AT_RADIATIVE_ENV: + SDIS(green_function_get_scene(green, &scn)); + ray = path->limit.ray; + end_temperature = radiative_env_get_temperature(green->scn->radenv, &ray); + break; case SDIS_GREEN_PATH_END_IN_VOLUME: medium = green_function_fetch_medium(green, path->limit_id); vtx = path->limit.vertex; end_temperature = medium_get_temperature(medium, &vtx); break; - case SDIS_GREEN_PATH_END_RADIATIVE: - SDIS(green_function_get_scene(green, &scn)); - SDIS(scene_get_ambient_radiative_temperature(scn, &trad)); - end_temperature = trad.temperature; - if(end_temperature < 0) { /* Cannot be negative if used */ - res = RES_BAD_ARG; - goto error; - } - break; default: FATAL("Unreachable code.\n"); break; } + if(SDIS_TEMPERATURE_IS_UNKNOWN(end_temperature)) { + log_err(green->scn->dev, + "%s: unknown boundary/initial condition.\n", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + /* Compute the path weight */ - *weight = power + flux + end_temperature; + *weight = power + flux + external_flux + end_temperature; exit: return res; @@ -1032,7 +1150,7 @@ error: res_T sdis_green_function_get_scene - (const struct sdis_green_function* green, + (const struct sdis_green_function* green, struct sdis_scene** scn) { if(!green || !scn) return RES_BAD_ARG; @@ -1131,39 +1249,15 @@ error: } res_T -sdis_green_path_get_end_type - (struct sdis_green_path* path_handle, enum sdis_green_path_end_type* type) -{ - const struct green_path* path = NULL; - struct sdis_green_function* green = NULL; - res_T res = RES_OK; - - if(!path_handle || !type) { - res = RES_BAD_ARG; - goto error; - } - - green = path_handle->green__; - ASSERT(path_handle->id__ < darray_green_path_size_get(&green->paths)); - - path = darray_green_path_cdata_get(&green->paths) + path_handle->id__; - *type = path->end_type; - -exit: - return res; -error: - goto exit; -} - -res_T -sdis_green_path_get_limit_point - (struct sdis_green_path* path_handle, struct sdis_point* pt) +sdis_green_path_get_end + (struct sdis_green_path* path_handle, + struct sdis_green_path_end* end) { const struct green_path* path = NULL; struct sdis_green_function* green = NULL; res_T res = RES_OK; - if(!path_handle || !pt) { + if(!path_handle || !end) { res = RES_BAD_ARG; goto error; } @@ -1172,19 +1266,21 @@ sdis_green_path_get_limit_point ASSERT(path_handle->id__ < darray_green_path_size_get(&green->paths)); path = darray_green_path_cdata_get(&green->paths) + path_handle->id__; + end->type = path->end_type; switch(path->end_type) { case SDIS_GREEN_PATH_END_AT_INTERFACE: - pt->data.itfrag.intface = green_function_fetch_interf(green, path->limit_id); - pt->data.itfrag.fragment = path->limit.fragment; - pt->type = SDIS_FRAGMENT; + end->data.itfrag.intface = green_function_fetch_interf(green, path->limit_id); + end->data.itfrag.fragment = path->limit.fragment; + break; + case SDIS_GREEN_PATH_END_AT_RADIATIVE_ENV: + end->data.radenvray.radenv = green->scn->radenv; + end->data.radenvray.ray = path->limit.ray; break; case SDIS_GREEN_PATH_END_IN_VOLUME: - pt->data.mdmvert.medium = green_function_fetch_medium(green, path->limit_id); - pt->data.mdmvert.vertex = path->limit.vertex; - pt->type = SDIS_VERTEX; + end->data.mdmvert.medium = green_function_fetch_medium(green, path->limit_id); + end->data.mdmvert.vertex = path->limit.vertex; break; - case SDIS_GREEN_PATH_END_RADIATIVE: case SDIS_GREEN_PATH_END_ERROR: res = RES_BAD_OP; goto error; @@ -1278,6 +1374,33 @@ error: } res_T +sdis_green_function_get_external_flux_terms_count + (const struct sdis_green_path* path_handle, + size_t* nterms) +{ + const struct green_path* path = NULL; + struct sdis_green_function* green = NULL; + res_T res = RES_OK; + + if(!path_handle || !nterms) { + res = RES_BAD_ARG; + goto error; + } + + green = path_handle->green__; (void)green; + ASSERT(path_handle->id__ < darray_green_path_size_get(&green->paths)); + + path = darray_green_path_cdata_get(&green->paths) + path_handle->id__; + + *nterms = darray_extflux_terms_size_get(&path->extflux_terms); + +exit: + return res; +error: + goto exit; +} + +res_T sdis_green_path_for_each_power_term (struct sdis_green_path* path_handle, sdis_process_medium_power_term_T func, @@ -1352,6 +1475,50 @@ error: goto exit; } +res_T +sdis_green_path_for_each_external_flux_terms + (struct sdis_green_path* path_handle, + sdis_process_external_flux_terms_T func, + void* context) +{ + const struct green_path* path = NULL; + struct sdis_green_function* green = NULL; + size_t i, n; + res_T res = RES_OK; + + if(!path_handle || !func) { + res = RES_BAD_ARG; + goto error; + } + + green = path_handle->green__; + ASSERT(path_handle->id__ < darray_green_path_size_get(&green->paths)); + + path = darray_green_path_cdata_get(&green->paths) + path_handle->id__; + + n = darray_extflux_terms_size_get(&path->extflux_terms); + if(n && !green->scn->source) { + /* In can't have external flux terms without an external source */ + log_err(green->scn->dev, "%s: the external source is missing\n", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + + FOR_EACH(i, 0, n) { + const struct sdis_green_external_flux_terms* terms = NULL; + terms = darray_extflux_terms_cdata_get(&path->extflux_terms) + i; + + res = func(green->scn->source, terms, context); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + + /******************************************************************************* * Local functions ******************************************************************************/ @@ -1574,14 +1741,16 @@ green_path_set_limit_vertex } res_T -green_path_set_limit_radiative +green_path_set_limit_radiative_ray (struct green_path_handle* handle, + const struct sdis_radiative_ray* ray, const double elapsed_time) { ASSERT(handle); ASSERT(handle->path->end_type == SDIS_GREEN_PATH_END_TYPES_COUNT__); handle->path->elapsed_time = elapsed_time; - handle->path->end_type = SDIS_GREEN_PATH_END_RADIATIVE; + handle->path->limit.ray = *ray; + handle->path->end_type = SDIS_GREEN_PATH_END_AT_RADIATIVE_ENV; return RES_OK; } @@ -1710,3 +1879,25 @@ exit: error: goto exit; } + +res_T +green_path_add_external_flux_terms + (struct green_path_handle* handle, + const struct sdis_green_external_flux_terms* terms) +{ + res_T res = RES_OK; + ASSERT(handle && terms); + + res = darray_extflux_terms_push_back(&handle->path->extflux_terms, terms); + if(res != RES_OK) { + log_err(handle->green->scn->dev, + "%s: cannot store external flux terms -- %s\n", + FUNC_NAME, res_to_cstr(res)); + goto error; + } + +exit: + return res; +error: + goto exit; +} diff --git a/src/sdis_green.h b/src/sdis_green.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -20,9 +20,9 @@ #include <rsys/rsys.h> /* Current version the green function data structure. One should increment it - * and perform a version management onto serialized data when the gren function + * and perform a version management onto serialized data when the green function * data structure is updated. */ -static const int SDIS_GREEN_FUNCTION_VERSION = 2; +static const int SDIS_GREEN_FUNCTION_VERSION = 3; /* Forward declaration */ struct accum; @@ -84,8 +84,9 @@ green_path_set_limit_vertex const double elapsed_time); extern LOCAL_SYM res_T -green_path_set_limit_radiative +green_path_set_limit_radiative_ray (struct green_path_handle* handle, + const struct sdis_radiative_ray* ray, const double elapsed_time); extern LOCAL_SYM res_T @@ -106,5 +107,10 @@ green_path_add_flux_term const struct sdis_interface_fragment* fragment, const double term); +extern LOCAL_SYM res_T +green_path_add_external_flux_terms + (struct green_path_handle* handle, + const struct sdis_green_external_flux_terms* terms); + #endif /* SDIS_GREEN_H */ diff --git a/src/sdis_heat_path.c b/src/sdis_heat_path.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -27,12 +27,6 @@ #define SDIS_XD_DIMENSION 3 #include "sdis_heat_path_convective_Xd.h" -/* Generate the conductive path routines */ -#define SDIS_XD_DIMENSION 2 -#include "sdis_heat_path_conductive_Xd.h" -#define SDIS_XD_DIMENSION 3 -#include "sdis_heat_path_conductive_Xd.h" - /******************************************************************************* * Local functions ******************************************************************************/ diff --git a/src/sdis_heat_path.h b/src/sdis_heat_path.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -23,22 +23,77 @@ #include <rsys/rsys.h> /* Forward declarations */ +struct green_path_handle; struct rwalk_2d; struct rwalk_3d; -struct rwalk_context; 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 + * solve parameters. + ******************************************************************************/ + struct rwalk_context { + struct green_path_handle* green_path; + struct sdis_heat_path* heat_path; + + double Tmin; /* Lower bound temperature */ + double Tmin2; /* Tmin^2 */ + double Tmin3; /* Tmin^3 */ + + double That; /* Upper bound temperature */ + double That2; /* That^2 */ + double That3; /* That^3 */ + + /* Maximum branchings i.e. the maximum number of times XD(sample_coupled_path) + * can be called. It controls the number of ramifications of the heat path and + * currently is correlated to the Picard order used to estimate the radiative + * temperature. max_branchings == picard_order-1 */ + size_t max_branchings; + + /* Number of heat path branchings */ + size_t nbranchings; + + /* Id of the realisation (for debug) */ + size_t irealisation; + + /* Algorithm used for the diffusive random walks, + * i.e. for sampling conductive paths */ + enum sdis_diffusion_algorithm diff_algo; +}; +#define RWALK_CONTEXT_NULL__ { \ + NULL, /* Green path */ \ + NULL, /* Heat path */ \ + 0, /* Tmin */ \ + 0, /* Tmin^2 */ \ + 0, /* Tmin^3 */ \ + 0, /* That */ \ + 0, /* That^2 */ \ + 0, /* That^3 */ \ + 0, /* Max #branchings */ \ + SIZE_MAX, /* #branchings */ \ + SIZE_MAX, /* realisation id */ \ + SDIS_DIFFUSION_NONE /* Diffusion algorithm */ \ +} +static const struct rwalk_context RWALK_CONTEXT_NULL = RWALK_CONTEXT_NULL__; + +static INLINE size_t +get_picard_order(const struct rwalk_context* ctx) +{ + ASSERT(ctx); + return ctx->max_branchings + 1; +} + +/******************************************************************************* + * Heat path data structure used to record the geometry of sampled paths + ******************************************************************************/ /* Generate the dynamic array of heat vertices */ #define DARRAY_NAME heat_vertex #define DARRAY_DATA struct sdis_heat_vertex #include <rsys/dynamic_array.h> -/******************************************************************************* - * Heat path data structure - ******************************************************************************/ struct sdis_heat_path { /* List of the path vertices */ struct darray_heat_vertex vertices; @@ -293,4 +348,3 @@ boundary_path_3d struct temperature_3d* temperature); #endif /* SDIS_HEAT_PATH_H */ - diff --git a/src/sdis_heat_path_boundary.c b/src/sdis_heat_path_boundary.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -24,10 +24,6 @@ /* Generate the boundary path sub-routines */ #define SDIS_XD_DIMENSION 2 -#include "sdis_heat_path_boundary_Xd_fixed_flux.h" -#define SDIS_XD_DIMENSION 3 -#include "sdis_heat_path_boundary_Xd_fixed_flux.h" -#define SDIS_XD_DIMENSION 2 #include "sdis_heat_path_boundary_Xd_solid_fluid_picard1.h" #define SDIS_XD_DIMENSION 3 #include "sdis_heat_path_boundary_Xd_solid_fluid_picard1.h" @@ -45,3 +41,7 @@ #include "sdis_heat_path_boundary_Xd.h" #define SDIS_XD_DIMENSION 3 #include "sdis_heat_path_boundary_Xd.h" +#define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_boundary_Xd_handle_external_net_flux.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_boundary_Xd_handle_external_net_flux.h" diff --git a/src/sdis_heat_path_boundary_Xd.h b/src/sdis_heat_path_boundary_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -54,7 +54,7 @@ XD(boundary_path) /* Check if the boundary temperature is known */ tmp = interface_side_get_temperature(interf, &frag); - if(tmp >= 0) { + if(SDIS_TEMPERATURE_IS_KNOWN(tmp)) { T->value += tmp; T->done = 1; diff --git a/src/sdis_heat_path_boundary_Xd_c.h b/src/sdis_heat_path_boundary_Xd_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -302,7 +302,7 @@ XD(move_away_primitive_boundaries) static res_T XD(find_reinjection_ray) - (const struct sdis_scene* scn, + (struct sdis_scene* scn, const struct XD(find_reinjection_ray_args)* args, struct XD(reinjection_ray)* ray) { @@ -320,7 +320,7 @@ XD(find_reinjection_ray) struct sdis_medium* mdm0; struct sdis_medium* mdm1; - struct hit_filter_data filter_data; + struct hit_filter_data filter_data = HIT_FILTER_DATA_NULL; struct sXd(hit) hit; struct sXd(hit) hit0; struct sXd(hit) hit1; @@ -407,7 +407,8 @@ XD(find_reinjection_ray) * function but this may be not the case due to a "threshold effect". In * both situations, try to slightly move away from the primitive boundaries * and retry to find a valid reinjection. */ - if(dst0 == -1 && dst1 == -1) { + if(dst0 == -1 && dst1 == -1 + && iattempt < MAX_ATTEMPTS - 1) { /* Is there still a trial to be done? */ XD(move_away_primitive_boundaries)(args->rwalk, args->distance, ray->org); ray->position_was_moved = 1; } @@ -415,10 +416,10 @@ XD(find_reinjection_ray) if(dst0 == -1 && dst1 == -1) { /* No valid reinjection */ #if DIM == 2 - log_warn(scn->dev, "%s: no valid reinjection direction at {%g, %g}.\n", + log_err(scn->dev, "%s: no valid reinjection direction at {%g, %g}.\n", FUNC_NAME, SPLIT2(ray->org)); #else - log_warn(scn->dev, "%s: no valid reinjection direction at {%g, %g, %g}.\n", + log_err(scn->dev, "%s: no valid reinjection direction at {%g, %g, %g}.\n", FUNC_NAME, SPLIT3(ray->org)); #endif res = RES_BAD_OP_IRRECOVERABLE; @@ -497,7 +498,7 @@ error: static res_T XD(find_reinjection_ray_and_check_validity) - (const struct sdis_scene* scn, + (struct sdis_scene* scn, const struct XD(find_reinjection_ray_args)* args, struct XD(reinjection_ray)* ray) { @@ -590,7 +591,7 @@ error: ******************************************************************************/ res_T XD(sample_reinjection_step_solid_fluid) - (const struct sdis_scene* scn, + (struct sdis_scene* scn, const struct XD(sample_reinjection_step_args)* args, struct XD(reinjection_step)* step) { @@ -603,6 +604,10 @@ XD(sample_reinjection_step_solid_fluid) * 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 */ @@ -613,6 +618,26 @@ XD(sample_reinjection_step_solid_fluid) ASSERT(scn && args && step); ASSERT(XD(check_sample_reinjection_step_args)(args)); + /* Initialise the reinjection step */ + *step = XD(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 + * 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); + if(args->side == SDIS_BACK) fX(minus)(step->direction, step->direction); + step->distance = (float)args->distance; + goto exit; /* That's all folks! */ + } + iattempt = 0; do { /* Sample a reinjection direction */ @@ -643,7 +668,7 @@ XD(sample_reinjection_step_solid_fluid) /* Could not find a valid reinjecton step */ if(iattempt >= MAX_ATTEMPTS) { - log_warn(scn->dev, + log_err(scn->dev, "%s: could not find a valid reinjection step at `%g %g %g'.\n", FUNC_NAME, SPLIT3(args->rwalk->vtx.P)); res = RES_BAD_OP_IRRECOVERABLE; @@ -672,7 +697,7 @@ error: res_T XD(sample_reinjection_step_solid_solid) - (const struct sdis_scene* scn, + (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, @@ -697,6 +722,13 @@ XD(sample_reinjection_step_solid_solid) * is only one possible direction */ 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]; + float dir_frt_samp[DIM]; /* Sampled direction */ float dir_frt_refl[DIM]; /* Sampled direction reflected */ float dir_bck_samp[DIM]; /* Negated sampled direction */ @@ -710,12 +742,44 @@ XD(sample_reinjection_step_solid_solid) ASSERT(XD(check_sample_reinjection_step_args)(args_bck)); 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)); rng = args_frt->rng; rwalk = args_frt->rwalk; ASSERT(args_bck->rng == rng); ASSERT(args_bck->rwalk == rwalk); + /* Initialise the reinjection steps */ + *step_frt = XD(REINJECTION_STEP_NULL); + *step_bck = XD(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 + * 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); + step_frt->distance = (float)args_frt->distance; + multiple_media_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); + step_bck->distance = (float)args_bck->distance; + multiple_media_bck = 1; + } + + if(multiple_media_frt && multiple_media_bck) + goto exit; /* That's all folks! */ + dX(set)(rwalk_pos_backup, rwalk->vtx.P); iattempt = 0; do { @@ -729,40 +793,17 @@ XD(sample_reinjection_step_solid_solid) fX(minus)(dir_bck_samp, dir_frt_samp); fX(minus)(dir_bck_refl, dir_frt_refl); - /* Find the reinjection ray for the front side */ - find_reinject_ray_frt_args.solid = args_frt->solid; - find_reinject_ray_frt_args.rwalk = args_frt->rwalk; - find_reinject_ray_frt_args.distance = args_frt->distance; - find_reinject_ray_frt_args.can_move = 1; - fX(set)(find_reinject_ray_frt_args.dir0, dir_frt_samp); - fX(set)(find_reinject_ray_frt_args.dir1, dir_frt_refl); - res = XD(find_reinjection_ray_and_check_validity) - (scn, &find_reinject_ray_frt_args, &ray_frt); - if(res == RES_BAD_OP) continue; - if(res != RES_OK) goto error; - - /* Update the random walk position if necessary */ - if(ray_frt.position_was_moved) dX(set)(rwalk->vtx.P, ray_frt.org); - - /* Select the reinjection direction and distance for the back side */ - find_reinject_ray_bck_args.solid = args_bck->solid; - find_reinject_ray_bck_args.rwalk = args_bck->rwalk; - find_reinject_ray_bck_args.distance = args_bck->distance; - find_reinject_ray_bck_args.can_move = 1; - fX(set)(find_reinject_ray_bck_args.dir0, dir_bck_samp); - fX(set)(find_reinject_ray_bck_args.dir1, dir_bck_refl); - res = XD(find_reinjection_ray_and_check_validity) - (scn, &find_reinject_ray_bck_args, &ray_bck); - if(res == RES_BAD_OP) continue; - if(res != RES_OK) goto error; - - /* Update the random walk position if necessary */ - if(ray_bck.position_was_moved) dX(set)(rwalk->vtx.P, ray_bck.org); - - /* If random walk was moved to find a valid rinjection ray on back side, - * one has to find a valid reinjection ob front side from the new pos */ - if(ray_bck.position_was_moved) { - find_reinject_ray_frt_args.can_move = 0; + /* 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) { + + /* Find the reinjection ray for the front side */ + find_reinject_ray_frt_args.solid = args_frt->solid; + find_reinject_ray_frt_args.rwalk = args_frt->rwalk; + find_reinject_ray_frt_args.distance = args_frt->distance; + find_reinject_ray_frt_args.can_move = 1; + fX(set)(find_reinject_ray_frt_args.dir0, dir_frt_samp); + fX(set)(find_reinject_ray_frt_args.dir1, dir_frt_refl); res = XD(find_reinjection_ray_and_check_validity) (scn, &find_reinject_ray_frt_args, &ray_frt); if(res == RES_BAD_OP) continue; @@ -771,6 +812,39 @@ XD(sample_reinjection_step_solid_solid) /* Update the random walk position if necessary */ if(ray_frt.position_was_moved) dX(set)(rwalk->vtx.P, ray_frt.org); } + + /* 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) { + + /* Select the reinjection direction and distance for the back side */ + find_reinject_ray_bck_args.solid = args_bck->solid; + find_reinject_ray_bck_args.rwalk = args_bck->rwalk; + find_reinject_ray_bck_args.distance = args_bck->distance; + find_reinject_ray_bck_args.can_move = 1; + fX(set)(find_reinject_ray_bck_args.dir0, dir_bck_samp); + fX(set)(find_reinject_ray_bck_args.dir1, dir_bck_refl); + res = XD(find_reinjection_ray_and_check_validity) + (scn, &find_reinject_ray_bck_args, &ray_bck); + if(res == RES_BAD_OP) continue; + if(res != RES_OK) goto error; + + /* Update the random walk position if necessary */ + if(ray_bck.position_was_moved) dX(set)(rwalk->vtx.P, ray_bck.org); + + /* If random walk was moved to find a valid rinjection ray on back side, + * one has to find a valid reinjection on front side from the new pos */ + if(ray_bck.position_was_moved && !multiple_media_frt) { + find_reinject_ray_frt_args.can_move = 0; + res = XD(find_reinjection_ray_and_check_validity) + (scn, &find_reinject_ray_frt_args, &ray_frt); + if(res == RES_BAD_OP) continue; + if(res != RES_OK) goto error; + + /* Update the random walk position if necessary */ + if(ray_frt.position_was_moved) dX(set)(rwalk->vtx.P, ray_frt.org); + } + } } while(res != RES_OK && ++iattempt < MAX_ATTEMPTS); /* Could not find a valid reinjection */ @@ -784,16 +858,18 @@ XD(sample_reinjection_step_solid_solid) } /* Setup the front and back reinjection steps */ - step_frt->hit = ray_frt.hit; - step_bck->hit = ray_bck.hit; - step_frt->distance = ray_frt.dst; - step_bck->distance = ray_bck.dst; - fX(set)(step_frt->direction, ray_frt.dir); - fX(set)(step_bck->direction, ray_bck.dir); - - /* Post-conditions */ - ASSERT(XD(check_reinjection_step)(step_frt)); - ASSERT(XD(check_reinjection_step)(step_bck)); + if(!multiple_media_frt) { + step_frt->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 */ + } + if(!multiple_media_bck) { + step_bck->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 */ + } exit: return res; @@ -916,4 +992,71 @@ error: goto exit; } +res_T +XD(check_Tref) + (const struct sdis_scene* scn, + const double pos[DIM], + const double Tref, + const char* func_name) +{ + ASSERT(scn && pos && func_name); + + #define CHECK_TBOUND(Bound, Name) { \ + if(SDIS_TEMPERATURE_IS_UNKNOWN(Bound)) { \ + log_err(scn->dev, \ + "%s: the "Name" temperature cannot be unknown " \ + "to sampling a radiative path.\n", \ + func_name); \ + return RES_BAD_OP_IRRECOVERABLE; \ + } \ + \ + if((Bound) < 0) { \ + log_err(scn->dev, \ + "%s: the "Name" temperature cannot be negative " \ + "to sample a radiative path (T"Name" = %g K).\n", \ + func_name, (Bound)); \ + return RES_BAD_OP_IRRECOVERABLE; \ + } \ + } (void) 0 + CHECK_TBOUND(scn->tmin, "min"); + CHECK_TBOUND(scn->tmax, "max"); + #undef CHECK_TBOUND + + if(scn->tmin > scn->tmax) { + log_err(scn->dev, + "%s: the temperature range cannot be degenerated to sample a radiative " + "path (Tmin = %g K; Tmax = %g K).\n", + func_name, scn->tmin, scn->tmax); + return RES_BAD_OP_IRRECOVERABLE; + } + + if(SDIS_TEMPERATURE_IS_UNKNOWN(Tref)) { + log_err(scn->dev, + "%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; + } + + if(Tref < 0) { + log_err(scn->dev, + "%s: the reference temperature is negative at `"FORMAT_VECX" (Tref = %g K). " + "Sampling a radiative path requires a known, positive reference " + "temperature field.\n", + func_name, SPLITX(pos), Tref); + return RES_BAD_OP_IRRECOVERABLE; + } + + if(Tref < scn->tmin || scn->tmax < Tref) { + log_err(scn->dev, + "%s: invalid reference temperature at `"FORMAT_VECX"' (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); + return RES_BAD_OP_IRRECOVERABLE; + } + + return RES_OK; +} + #include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_boundary_Xd_fixed_flux.h b/src/sdis_heat_path_boundary_Xd_fixed_flux.h @@ -1,135 +0,0 @@ -/* Copyright (C) 2016-2023 |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_green.h" -#include "sdis_heat_path_boundary_c.h" -#include "sdis_interface_c.h" -#include "sdis_log.h" -#include "sdis_medium_c.h" -#include "sdis_misc.h" -#include "sdis_scene_c.h" - -#include <star/ssp.h> - -#include "sdis_Xd_begin.h" - -/******************************************************************************* - * Boundary path between a solid and a fluid with a fixed flux - ******************************************************************************/ -res_T -XD(solid_boundary_with_flux_path) - (struct sdis_scene* scn, - struct rwalk_context* ctx, - const struct sdis_interface_fragment* frag, - const double phi, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - /* Input/output arguments of the function used to sample a reinjection */ - struct XD(sample_reinjection_step_args) samp_reinject_step_args = - XD(SAMPLE_REINJECTION_STEP_ARGS_NULL); - struct XD(reinjection_step) reinject_step = XD(REINJECTION_STEP_NULL); - - /* Reinjection arguments */ - struct XD(solid_reinjection_args) solid_reinject_args = - XD(SOLID_REINJECTION_ARGS_NULL); - - /* Data attached to the boundary */ - struct sdis_interface* interf = NULL; - struct sdis_medium* solid = NULL; - - /* Miscellaneous variables */ - double lambda; /* Solid conductivity */ - double delta_boundary; /* Orthogonal reinjection dst at the boundary */ - double delta; /* Orthogonal fitted reinjection dst at the boundary */ - double delta_m; /* Delta in meters */ - double flux_term; - size_t picard_order; - enum sdis_side solid_side = SDIS_SIDE_NULL__; - res_T res = RES_OK; - - ASSERT(frag && phi != SDIS_FLUX_NONE); - ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); - (void)ctx; - - /* Currently, the flux can be correctly taken into account only when the - * radiative temperature is linearized, i.e. when the picard order is equal - * to 1 */ - picard_order = get_picard_order(ctx); - if(picard_order > 1 && phi != 0) { - log_err(scn->dev, - "%s: invalid flux '%g' W/m^2. Could not manage a flux != 0 when the " - "picard order is not equal to 1; Picard order is currently set to %lu.\n", - FUNC_NAME, phi, (unsigned long)picard_order); - res = RES_BAD_ARG; - goto error; - } - - /* Retrieve the solid split by the interface */ - interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); - solid = interface_get_medium(interf, frag->side); - solid_side = frag->side; - ASSERT(phi == interface_side_get_flux(interf, frag)); - ASSERT(solid->type == SDIS_SOLID); - - /* Fetch the solid properties */ - lambda = solid_get_thermal_conductivity(solid, &rwalk->vtx); - delta = solid_get_delta(solid, &rwalk->vtx); - - /* Note that the reinjection distance is *FIXED*. It MUST ensure that the - * orthogonal distance from the boundary to the reinjection point is at most - * equal to delta. */ - delta_boundary = delta * sqrt(DIM); - - /* Sample a reinjection step */ - samp_reinject_step_args.rng = rng; - samp_reinject_step_args.solid = solid; - samp_reinject_step_args.rwalk = rwalk; - samp_reinject_step_args.distance = delta_boundary; - samp_reinject_step_args.side = solid_side; - res = XD(sample_reinjection_step_solid_fluid) - (scn, &samp_reinject_step_args, &reinject_step); - if(res != RES_OK) goto error; - - /* Define the orthogonal dst from the boundary to the reinjection position */ - delta = reinject_step.distance / sqrt(DIM); - delta_m = delta * scn->fp_to_meter; - - /* Handle the flux */ - flux_term = delta_m / lambda; - T->value += phi * flux_term; - if(ctx->green_path) { - res = green_path_add_flux_term(ctx->green_path, interf, frag, flux_term); - if(res != RES_OK) goto error; - } - - /* Perform the reinjection into the solid */ - solid_reinject_args.reinjection = &reinject_step; - solid_reinject_args.rwalk_ctx = ctx; - solid_reinject_args.rwalk = rwalk; - solid_reinject_args.rng = rng; - solid_reinject_args.T = T; - solid_reinject_args.fp_to_meter = scn->fp_to_meter; - res = XD(solid_reinjection)(solid, &solid_reinject_args); - if(res != RES_OK) goto error; - -exit: - return res; -error: - goto exit; -} - -#include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_boundary_Xd_handle_external_net_flux.h b/src/sdis_heat_path_boundary_Xd_handle_external_net_flux.h @@ -0,0 +1,581 @@ +/* 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_boundary_c.h" +#include "sdis_interface_c.h" +#include "sdis_log.h" +#include "sdis_scene_c.h" +#include "sdis_source_c.h" + +#include <rsys/cstr.h> /* res_to_cstr */ + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Non generic data types and function + ******************************************************************************/ +#ifndef SDIS_HEAT_PATH_BOUNDARY_XD_HANDLE_EXTERNAL_NET_FLUX_H +#define SDIS_HEAT_PATH_BOUNDARY_XD_HANDLE_EXTERNAL_NET_FLUX_H + +enum brdf_component { + BRDF_SPECULAR, + BRDF_DIFFUSE, + BRDF_NONE +}; + +struct brdf_sample { + double dir[3]; + double pdf; + enum brdf_component cpnt; +}; +#define BRDF_SAMPLE_NULL__ {{0}, 0, BRDF_NONE} +static const struct brdf_sample BRDF_SAMPLE_NULL = BRDF_SAMPLE_NULL__; + +struct brdf { + double emissivity; + double specular_fraction; +}; +#define BRDF_NULL__ {0, 0} +static const struct brdf BRDF_NULL = BRDF_NULL__; + +/* Incident diffuse flux is made up of two components. One corresponds to the + * diffuse flux due to the reflection of the source on surfaces. The other is + * the diffuse flux due to the source's radiation scattering at least once in + * the environment. */ +struct incident_diffuse_flux { + double reflected; /* [W/m^2] */ + double scattered; /* [W/m^2] */ + double dir[3]; /* Direction along wich the scattered part was retrieved */ +}; +#define INCIDENT_DIFFUSE_FLUX_NULL__ {0, 0, {0,0,0}} +static const struct incident_diffuse_flux INCIDENT_DIFFUSE_FLUX_NULL = + INCIDENT_DIFFUSE_FLUX_NULL__; + +/* Reflect the V wrt the normal N. By convention V points outward the surface. + * In fact, this function is a double-precision version of the reflect_3d + * function. TODO Clean this "repeat" */ +static FINLINE double* +reflect(double res[3], const double V[3], const double N[3]) +{ + double tmp[3]; + double cos_V_N; + ASSERT(res && V && N); + ASSERT(d3_is_normalized(V) && d3_is_normalized(N)); + cos_V_N = d3_dot(V, N); + d3_muld(tmp, N, 2*cos_V_N); + d3_sub(res, tmp, V); + return res; +} + +static void +sample_brdf + (const struct brdf* brdf, + struct ssp_rng* rng, + const double wi[3], /* Incident direction. Point away from the surface */ + const double N[3], /* Surface normal */ + struct brdf_sample* sample) +{ + double r = 0; /* Random number */ + + /* Preconditions */ + ASSERT(brdf && rng && wi && N && sample); + ASSERT(d3_is_normalized(wi) && d3_is_normalized(N)); + ASSERT(d3_dot(wi, N) > 0); + + r = ssp_rng_canonical(rng); + + /* Sample the specular part */ + if(r < brdf->specular_fraction) { + reflect(sample->dir, wi, N); + sample->pdf = 1; + sample->cpnt = BRDF_SPECULAR; + + /* Sample the diffuse part */ + } else { + ssp_ran_hemisphere_cos(rng, N, sample->dir, NULL); + sample->pdf = 1.0/PI; + sample->cpnt = BRDF_DIFFUSE; + } +} + +/* Check that the trajectory reaches a valid interface, i.e. that it is on a + * fluid/solid interface and has reached it from the fluid */ +static res_T +check_interface + (const struct sdis_interface* interf, + const struct sdis_interface_fragment* frag) +{ + enum sdis_medium_type mdm_frt_type = SDIS_MEDIUM_TYPES_COUNT__; + enum sdis_medium_type mdm_bck_type = SDIS_MEDIUM_TYPES_COUNT__; + enum sdis_side fluid_side = SDIS_SIDE_NULL__; + res_T res = RES_OK; + + mdm_frt_type = sdis_medium_get_type(interf->medium_front); + mdm_bck_type = sdis_medium_get_type(interf->medium_back); + + /* Semi-transparent materials are not supported. This means that a solid/solid + * interface must not be intersected when tracing radiative paths */ + if(mdm_frt_type == SDIS_SOLID && mdm_bck_type == SDIS_SOLID) { + log_err(interf->dev, + "Error when sampling the trajectory to calculate the incident diffuse " + "flux. The trajectory reaches a solid/solid interface, whereas this is " + "supposed to be impossible (path position: %g, %g, %g).\n", + SPLIT3(frag->P)); + res = RES_BAD_OP; + goto error; + } + + /* Find out which side of the interface the fluid is on */ + if(mdm_frt_type == SDIS_FLUID) { + fluid_side = SDIS_FRONT; + } else if(mdm_bck_type == SDIS_FLUID) { + fluid_side = SDIS_BACK; + } else { + FATAL("Unreachable code\n"); + } + + /* Check that the current position is on the correct side of the interface */ + if(frag->side != fluid_side) { + log_err(interf->dev, + "Inconsistent intersection when sampling the trajectory to calculate the " + "incident diffuse flux. The radiative path reaches an interface on " + "its solid side, whereas this is supposed to be impossible " + "(path position: %g, %g, %g).\n", + SPLIT3(frag->P)); + res = RES_BAD_OP; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +#endif /* SDIS_HEAT_PATH_BOUNDARY_XD_HANDLE_EXTERNAL_NET_FLUX_H */ + +/******************************************************************************* + * Generic helper functions + ******************************************************************************/ +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) +{ + int net_flux = 0; + res_T res = RES_OK; + + /* Handle bugs */ + ASSERT(scn && func_name && args); + ASSERT(args->interf && args->frag); + ASSERT(!SXD_HIT_NONE(args->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); + + net_flux = interface_side_is_external_flux_handled(args->interf, args->frag); + net_flux = net_flux && (scn->source != NULL); + + if(net_flux && args->picard_order != 1) { + log_err(scn->dev, + "%s: Impossible to process external fluxes when Picard order is not " + "equal to 1; Picard order is currently set to %lu.\n", + func_name, (unsigned long)args->picard_order); + res = RES_BAD_ARG; + return res; + } + + if(sdis_medium_get_type(args->interf->medium_back) + == sdis_medium_get_type(args->interf->medium_front)) { + log_err(scn->dev, + "%s: external fluxes can only be processed on fluid/solid interfaces.\n", + func_name); + res = RES_BAD_ARG; + return res; + } + + return RES_OK; +} + +static INLINE void +XD(trace_ray) + (const struct sdis_scene* scn, + const double pos[DIM], + const double dir[3], + const double distance, + const struct sXd(hit)* hit_from, + struct sXd(hit)* hit) +{ + struct hit_filter_data filter_data = HIT_FILTER_DATA_NULL; + float ray_org[DIM] = {0}; + float ray_dir[3] = {0}; + float ray_range[2] = {0}; + ASSERT(scn && pos && dir && distance >= 0 && hit_from && hit); + + fX_set_dX(ray_org, pos); + f3_set_d3(ray_dir, dir); + ray_range[0] = 0; + ray_range[1] = (float)distance; + filter_data.XD(hit) = *hit_from; + filter_data.epsilon = 1.e-4; +#if DIM == 2 + SXD(scene_view_trace_ray_3d + (scn->sXd(view), ray_org, ray_dir, ray_range, &filter_data, hit)); +#else + SXD(scene_view_trace_ray + (scn->sXd(view), ray_org, ray_dir, ray_range, &filter_data, hit)); +#endif +} + +static INLINE double /* [W/m^2/sr] */ +XD(direct_contribution) + (const struct sdis_scene* scn, + struct source_sample* sample, + const double pos[DIM], + const struct sXd(hit)* hit_from) +{ + struct sXd(hit) hit = SXD_HIT_NULL; + ASSERT(scn && sample && pos && hit_from); + + /* Is the source hidden */ + XD(trace_ray)(scn, pos, sample->dir, sample->dst, hit_from, &hit); + if(!SXD_HIT_NONE(&hit)) return 0; /* [W/m^2/sr] */ + + /* Note that the value returned is not the source's actual radiance, but the + * radiance relative to the source's power. Care must therefore be taken to + * multiply it by the power of the source to obtain its real contribution. + * This trick makes it possible to manage the external flux in the green + * function. */ + return sample->radiance_term; /* [W/m^2/sr] */ +} + +static INLINE void +XD(setup_fragment) + (struct sdis_interface_fragment* frag, + const double pos[DIM], + const double dir[DIM], /* Direction _toward_ the hit position */ + const double time, /* Current time */ + const double N[DIM],/* Surface normal */ + const struct sXd(hit)* hit) +{ + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + enum sdis_side side = SDIS_SIDE_NULL__; + ASSERT(frag && pos && dir && N); + ASSERT(dX(is_normalized)(N)); + + /* Setup the interface fragment at the intersection position */ + dX(set)(vtx.P, pos); + vtx.time = time; + side = dX(dot)(dir, N) < 0 ? SDIS_FRONT : SDIS_BACK; + XD(setup_interface_fragment)(frag, &vtx, hit, side); +} + +static INLINE res_T +XD(setup_brdf) + (struct sdis_device* dev, + const struct sdis_source* src, + struct brdf* brdf, + const struct sdis_interface* interf, + const struct sdis_interface_fragment* frag) +{ + double epsilon = 0; + double alpha = 0; + unsigned src_id = 0; + res_T res = RES_OK; + ASSERT(brdf && frag); + ASSERT((frag->side == SDIS_FRONT + && sdis_medium_get_type(interf->medium_front) == SDIS_FLUID) + || sdis_medium_get_type(interf->medium_back) == SDIS_FLUID); + + src_id = sdis_source_get_id(src); + + epsilon = interface_side_get_emissivity(interf, src_id, frag); + res = interface_side_check_emissivity(dev, epsilon, frag->P, frag->time); + if(res != RES_OK) goto error; + + alpha = interface_side_get_specular_fraction(interf, src_id, frag); + res = interface_side_check_specular_fraction(dev, alpha, frag->P, frag->time); + if(res != RES_OK) goto error; + + brdf->emissivity = epsilon; + brdf->specular_fraction = alpha; + +exit: + return res; +error: + *brdf = BRDF_NULL; + goto exit; +} + +static res_T +XD(compute_incident_diffuse_flux) + (const struct sdis_scene* scn, + struct ssp_rng* rng, + const double in_pos[DIM], /* position */ + const double in_N[DIM], /* Surface normal. (Away from the surface) */ + const double time, + const struct sXd(hit)* in_hit, /* Current intersection */ + struct incident_diffuse_flux* diffuse_flux) /* [W/m^2] */ +{ + struct sXd(hit) hit = SXD_HIT_NULL; + double pos[3] = {0}; /* In 3D for ray tracing ray to the source */ + double dir[3] = {0}; /* Incident direction (toward the surface). Always 3D.*/ + double N[3] = {0}; /* Surface normal. Always 3D */ + res_T res = RES_OK; + ASSERT(in_pos && in_N && in_hit && diffuse_flux); + + /* Local copy of input argument */ + dX(set)(pos, in_pos); + dX(set)(N, in_N); + hit = *in_hit; + + /* Sample a diffusive direction in 3D */ + ssp_ran_hemisphere_cos(rng, N, dir, NULL); + + *diffuse_flux = INCIDENT_DIFFUSE_FLUX_NULL; + + for(;;) { + /* External sources */ + struct source_sample src_sample = SOURCE_SAMPLE_NULL; + + /* Interface */ + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + struct sdis_interface* interf = NULL; + + /* BRDF */ + struct brdf brdf = BRDF_NULL; + struct brdf_sample brdf_sample = BRDF_SAMPLE_NULL; + + /* Miscellaneous */ + double L = 0; /* incident flux to bounce position */ + double wi[3] = {0}; /* Incident direction (outward the surface). Always 3D */ + double vec[DIM] = {0}; /* Temporary variable */ + + d3_minus(wi, dir); /* Always in 3D */ + + /* Find the following surface along the direction of propagation */ + XD(trace_ray)(scn, pos, dir, INF, &hit, &hit); + if(SXD_HIT_NONE(&hit)) { + /* No surface. Handle the radiance emitted by the source and scattered at + * least once in the environment. Note that the value returned is not the + * actual scattered component of the incident diffuse flux: it relates + * to the radiance of the source scattered along the input dir at the + * given instant. It must therefore be multiplied by this radiance to + * obtain its real contribution. This trick makes it possible to manage + * the external flux in the green function. */ + diffuse_flux->scattered = PI; + diffuse_flux->dir[0] = dir[0]; + diffuse_flux->dir[1] = dir[1]; + diffuse_flux->dir[2] = dir[2]; + break; + } + + /* Retrieve the current position and normal */ + dX(add)(pos, pos, dX(muld)(vec, dir, hit.distance)); + dX_set_fX(N, hit.normal); + dX(normalize(N, N)); + + /* Retrieve the current interface properties */ + interf = scene_get_interface(scn, hit.prim.prim_id); + XD(setup_fragment)(&frag, pos, dir, time, N, &hit); + + /* Check that the path reaches a valid interface */ + res = check_interface(interf, &frag); + if(res != RES_OK) goto error; + + XD(setup_brdf)(scn->dev, scn->source, &brdf, interf, &frag); + + /* Check if path is absorbed */ + if(ssp_rng_canonical(rng) < brdf.emissivity) break; + + /* Sample rebound direction */ + if(frag.side == SDIS_BACK) dX(minus)(N, N); /* Revert normal if necessary */ + sample_brdf(&brdf, rng, wi, N, &brdf_sample); + d3_set(dir, brdf_sample.dir); /* Always in 3D */ + + /* Calculate the direct contribution if the rebound is specular */ + if(brdf_sample.cpnt == BRDF_SPECULAR) { + res = source_trace_to(scn->source, pos, brdf_sample.dir, time, &src_sample); + if(res != RES_OK) goto error; + + if(!SOURCE_SAMPLE_NONE(&src_sample)) { + const double Ld = XD(direct_contribution)(scn, &src_sample, pos, &hit); + L = Ld; /* [W/m^2/sr] */ + } + + /* Calculate the direct contribution of the rebound is diffuse */ + } else { + double cos_theta = 0; + ASSERT(brdf_sample.cpnt == BRDF_DIFFUSE); + + /* Sample an external source to handle its direct contribution at the + * bounce position */ + res = source_sample(scn->source, rng, pos, time, &src_sample); + CHK(res == RES_OK); + cos_theta = d3_dot(src_sample.dir, N); + + /* The source is behind the surface */ + if(cos_theta <= 0) { + L = 0; /* [W/m^2/sr] */ + + /* The source is above the surface */ + } else { + const double Ld = XD(direct_contribution)(scn, &src_sample, pos, &hit); + L = Ld * cos_theta / (PI * src_sample.pdf); /* [W/m^2/sr] */ + } + } + diffuse_flux->reflected += L; /* [W/m^2/sr] */ + } + diffuse_flux->reflected *= PI; /* [W/m^2] */ + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +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) +{ + /* Terms to be registered in the green function */ + struct sdis_green_external_flux_terms green = + SDIS_GREEN_EXTERNAL_FLUX_TERMS_NULL; + + /* Sampling external sources */ + struct source_sample src_sample = SOURCE_SAMPLE_NULL; + + /* External flux */ + struct incident_diffuse_flux incident_flux_diffuse = INCIDENT_DIFFUSE_FLUX_NULL; + double incident_flux = 0; /* [W/m^2] */ + double incident_flux_direct = 0; /* [W/m^2] */ + double net_flux = 0; /* [W/m^2] */ + double net_flux_sc = 0; /* [W/m^2] */ + + /* Sampled path */ + double N[3] = {0}; /* Normal. Always in 3D */ + + /* On the fluid side */ + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + + /* Miscellaneous */ + double sum_h = 0; + double emissivity = 0; /* Emissivity */ + double Ld = 0; /* Incident radiance [W/m^2/sr] */ + double cos_theta = 0; + unsigned src_id = 0; + int handle_flux = 0; + res_T res = RES_OK; + ASSERT(scn && args && T); + + res = XD(check_handle_external_net_flux_args)(scn, FUNC_NAME, args); + if(res != RES_OK) goto error; + + /* Setup the interface fragment on flud side */ + frag = *args->frag; + if(sdis_medium_get_type(args->interf->medium_front) == SDIS_FLUID) { + frag.side = SDIS_FRONT; + } else { + ASSERT(sdis_medium_get_type(args->interf->medium_back) == SDIS_FLUID); + frag.side = SDIS_BACK; + } + + /* No external sources <=> no external fluxes. Nothing to do */ + handle_flux = interface_side_is_external_flux_handled(args->interf, &frag); + handle_flux = handle_flux && (scn->source != NULL); + if(!handle_flux) goto exit; + + /* Sample the external source */ + res = source_sample + (scn->source, rng, frag.P, frag.time, &src_sample); + if(res != RES_OK) goto error; + + /* Setup the normal to ensure that it points toward the fluid medium */ + dX(set)(N, frag.Ng); + if(frag.side == SDIS_BACK) dX(minus)(N, N); + + /* Calculate the incident direct flux if the external source is above the + * 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); + 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); + if(res != RES_OK) goto error; + + /* Calculate the incident flux without the part scattered by the environment. + * The latter depends on the source's diffuse radiance, not on its power. On + * the other hand, both the direct incident flux and the diffuse incident flux + * reflected by surfaces are linear with respect to the source power. This + * term can therefore be recorded in the green function in relation to this + * power, whereas the incident diffused flux coming from the scattered source + * radiance depends on the diffuse radiance of the source */ + incident_flux = /* [W/m^2] */ + incident_flux_direct + incident_flux_diffuse.reflected; + + /* Calculate the net flux [W/m^2] */ + src_id = sdis_source_get_id(scn->source); + emissivity = interface_side_get_emissivity(args->interf, src_id, &frag); + res = interface_side_check_emissivity(scn->dev, emissivity, frag.P, frag.time); + if(res != RES_OK) goto error; + net_flux = incident_flux * emissivity; /* [W/m^2] */ + + /* Calculate the net flux from the radiance source scattered at least once by + * the medium */ + net_flux_sc = incident_flux_diffuse.scattered * emissivity; /* [W/m^2] */ + + /* Until now, net flux has been calculated on the basis of source power and + * source diffuse radiance. What is actually calculated are the external flux + * terms of the green function. These must be multiplied by the source power + * and the source diffuse radiance, then added together to give the actual + * external flux */ + sum_h = (args->h_radi + args->h_conv + args->h_cond); + green.term_wrt_power = net_flux / sum_h; /* [K/W] */ + green.term_wrt_diffuse_radiance = net_flux_sc / sum_h; /* [K/W/m^2/sr] */ + green.time = frag.time; /* [s] */ + green.dir[0] = incident_flux_diffuse.dir[0]; + green.dir[1] = incident_flux_diffuse.dir[1]; + green.dir[2] = incident_flux_diffuse.dir[2]; + + T->value += green.term_wrt_power * source_get_power(scn->source, green.time); + if(green.term_wrt_diffuse_radiance) { + T->value += + green.term_wrt_diffuse_radiance + * source_get_diffuse_radiance(scn->source, green.time, green.dir); + } + + /* Register the external net flux terms */ + if(args->green_path) { + res = green_path_add_external_flux_terms(args->green_path, &green); + if(res != RES_OK) goto error; + } + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_boundary_Xd_solid_fluid_picard1.h b/src/sdis_heat_path_boundary_Xd_solid_fluid_picard1.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -18,6 +18,7 @@ #include "sdis_interface_c.h" #include "sdis_medium_c.h" #include "sdis_misc.h" +#include "sdis_radiative_env_c.h" #include "sdis_scene_c.h" #include <star/ssp.h> @@ -28,48 +29,13 @@ * Helper functions ******************************************************************************/ static INLINE res_T -XD(check_Tref) - (const struct sdis_scene* scn, - const double pos[DIM], - const double Tref, - const char* func_name) -{ - ASSERT(scn && pos && func_name); - -#if DIM == 2 - #define STR_VECX "%g %g" - #define SPLITX SPLIT2 -#else - #define STR_VECX "%g %g %g" - #define SPLITX SPLIT3 -#endif - if(Tref < 0) { - log_err(scn->dev, - "%s: invalid reference temperature `%gK' at the position `"STR_VECX"'.\n", - func_name, Tref, SPLITX(pos)); - return RES_BAD_OP_IRRECOVERABLE; - } - if(Tref > scn->tmax) { - log_err(scn->dev, - "%s: invalid maximum temperature `%gK'. The reference temperature `%gK' " - "at the position `"STR_VECX"' is greater than this temperature.\n", - func_name, scn->tmax, Tref, SPLITX(pos)); - return RES_BAD_OP_IRRECOVERABLE; - } -#undef STR_VECX -#undef SPLITX - - return RES_OK; -} - -static INLINE res_T XD(rwalk_get_Tref) (const struct sdis_scene* scn, const struct XD(rwalk)* rwalk, const struct XD(temperature)* T, double* out_Tref) { - double Tref = -1; + double Tref = SDIS_TEMPERATURE_NONE; res_T res = RES_OK; ASSERT(rwalk && T && out_Tref); @@ -78,7 +44,11 @@ XD(rwalk_get_Tref) * fetches the ambient radiative temperature. We do not use the limit * conditions as the reference temperature to make the sampled paths * independant of them. */ - Tref = scn->trad.reference; + struct sdis_radiative_ray ray = SDIS_RADIATIVE_RAY_NULL; + ray.dir[0] = rwalk->dir[0]; + ray.dir[1] = rwalk->dir[1]; + ray.dir[2] = rwalk->dir[2]; + Tref = radiative_env_get_reference_temperature(scn->radenv, &ray); } else { struct sdis_interface_fragment frag; struct sdis_interface* interf = NULL; @@ -122,6 +92,10 @@ XD(solid_fluid_boundary_picard1_path) /* 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); + /* 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); @@ -185,7 +159,8 @@ XD(solid_fluid_boundary_picard1_path) delta = solid_get_delta(solid, &rwalk->vtx); /* Fetch the boundary emissivity */ - epsilon = interface_side_get_emissivity(interf, &frag_fluid); + epsilon = interface_side_get_emissivity + (interf, SDIS_INTERN_SOURCE_ID, &frag_fluid); if(epsilon <= 0) { Tref = 0; @@ -218,7 +193,13 @@ XD(solid_fluid_boundary_picard1_path) /* Compute the convective, conductive and the upper bound radiative coef */ h_conv = interface_get_convection_coef(interf, frag); h_cond = lambda / delta_m; - h_radi_hat = 4.0 * BOLTZMANN_CONSTANT * ctx->That3 * epsilon; + if(epsilon <= 0) { + h_radi_hat = 0; /* No radiative transfert */ + } else { + res = scene_check_temperature_range(scn); + if(res != RES_OK) { res = RES_BAD_OP_IRRECOVERABLE; goto error; } + h_radi_hat = 4.0 * BOLTZMANN_CONSTANT * ctx->That3 * epsilon; + } /* Compute a global upper bound coefficient */ h_hat = h_conv + h_cond + h_radi_hat; @@ -227,7 +208,7 @@ XD(solid_fluid_boundary_picard1_path) p_conv = h_conv / h_hat; p_cond = h_cond / h_hat; - /* Handle the net flux if any */ + /* Handle the net flux if any */ handle_net_flux_args.interf = interf; handle_net_flux_args.frag = frag; handle_net_flux_args.green_path = ctx->green_path; @@ -238,6 +219,18 @@ XD(solid_fluid_boundary_picard1_path) res = XD(handle_net_flux)(scn, &handle_net_flux_args, T); if(res != RES_OK) goto error; + /* 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.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; + handle_external_net_flux_args.h_conv = h_conv; + handle_external_net_flux_args.h_radi = h_radi_hat; + res = XD(handle_external_net_flux)(scn, rng, &handle_external_net_flux_args, T); + if(res != RES_OK) goto error; + /* Fetch the last registered heat path vertex */ if(ctx->heat_path) hvtx = *heat_path_get_last_vertex(ctx->heat_path); @@ -298,6 +291,12 @@ XD(solid_fluid_boundary_picard1_path) res = XD(rwalk_get_Tref)(scn, &rwalk_s, &T_s, &Tref_s); if(res != RES_OK) goto error; + /* The reference temperatures must be known, as this is a radiative path. + * If this is not the case, an error should be reported before this point. + * Hence these assertions to detect unexpected behavior */ + ASSERT(SDIS_TEMPERATURE_IS_KNOWN(Tref)); + ASSERT(SDIS_TEMPERATURE_IS_KNOWN(Tref_s)); + h_radi = BOLTZMANN_CONSTANT * epsilon * ( Tref*Tref*Tref + Tref*Tref * Tref_s diff --git a/src/sdis_heat_path_boundary_Xd_solid_fluid_picardN.h b/src/sdis_heat_path_boundary_Xd_solid_fluid_picardN.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -102,7 +102,7 @@ XD(sample_path) } /* Sample the path */ - res = XD(compute_temperature)(scn, ctx, &rwalk, rng, T); + res = XD(sample_coupled_path)(scn, ctx, &rwalk, rng, T); if(res != RES_OK) goto error; /* Check the returned temperature */ @@ -209,7 +209,8 @@ XD(solid_fluid_boundary_picardN_path) delta = solid_get_delta(solid, &rwalk->vtx); /* Fetch the boundary emissivity */ - epsilon = interface_side_get_emissivity(interf, &frag_fluid); + epsilon = interface_side_get_emissivity + (interf, SDIS_INTERN_SOURCE_ID, &frag_fluid); /* Note that the reinjection distance is *FIXED*. It MUST ensure that the * orthogonal distance from the boundary to the reinjection point is at most @@ -232,7 +233,15 @@ XD(solid_fluid_boundary_picardN_path) /* Compute the convective, conductive and the upper bound radiative coef */ h_conv = interface_get_convection_coef(interf, frag); h_cond = lambda / (delta * scn->fp_to_meter); - h_radi_hat = 4.0 * BOLTZMANN_CONSTANT * That3 * epsilon; + h_radi_hat = epsilon > 0 ? 4.0 * BOLTZMANN_CONSTANT * That3 * epsilon : 0; + + if(epsilon <= 0) { + h_radi_hat = 0; /* No radiative transfert */ + } else { + res = scene_check_temperature_range(scn); + if(res != RES_OK) { res = RES_BAD_OP_IRRECOVERABLE; goto error; } + h_radi_hat = 4.0 * BOLTZMANN_CONSTANT * That3 * epsilon; + } /* Compute a global upper bound coefficient */ h_hat = h_conv + h_cond + h_radi_hat; diff --git a/src/sdis_heat_path_boundary_Xd_solid_solid.h b/src/sdis_heat_path_boundary_Xd_solid_solid.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_heat_path_boundary_c.h b/src/sdis_heat_path_boundary_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -75,19 +75,19 @@ REINJECTION_STEP_NULL_3d = REINJECTION_STEP_NULL___3d; extern LOCAL_SYM res_T sample_reinjection_step_solid_fluid_2d - (const struct sdis_scene* scn, + (struct sdis_scene* scn, const struct sample_reinjection_step_args_2d* args, struct reinjection_step_2d* step); extern LOCAL_SYM res_T sample_reinjection_step_solid_fluid_3d - (const struct sdis_scene* scn, + (struct sdis_scene* scn, const struct sample_reinjection_step_args_3d* args, struct reinjection_step_3d *step); extern LOCAL_SYM res_T sample_reinjection_step_solid_solid_2d - (const struct sdis_scene* scn, + (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, @@ -95,7 +95,7 @@ sample_reinjection_step_solid_solid_2d extern LOCAL_SYM res_T sample_reinjection_step_solid_solid_3d - (const struct sdis_scene* scn, + (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, @@ -169,6 +169,75 @@ handle_net_flux_3d struct temperature_3d* T); /******************************************************************************* + * Handle external flux + ******************************************************************************/ +struct handle_external_net_flux_args_2d { + struct sdis_interface* interf; + const struct sdis_interface_fragment* frag; + const struct s2d_hit* hit; + + struct green_path_handle* green_path; /* Store the propagator */ + struct sdis_heat_path* heat_path; /* Save paths */ + + size_t picard_order; + double h_cond; /* Convective coefficient, i.e. lambda/delta */ + double h_conv; /* Condutive coefficient */ + double h_radi; /* Radiative coefficient */ +}; + +struct handle_external_net_flux_args_3d { + struct sdis_interface* interf; + const struct sdis_interface_fragment* frag; + const struct s3d_hit* hit; + + struct green_path_handle* green_path; /* Store the propagator */ + struct sdis_heat_path* heat_path; /* Save paths */ + + size_t picard_order; + double h_cond; /* Convective coefficient, i.e. lambda/delta */ + double h_conv; /* Condutive coefficient */ + 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; + +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); + +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); + +/******************************************************************************* + * Miscellaneous functions + ******************************************************************************/ +extern LOCAL_SYM res_T +check_Tref_2d + (const struct sdis_scene* scn, + const double pos[2], + const double Tref, + const char* call_func_name); + +extern LOCAL_SYM res_T +check_Tref_3d + (const struct sdis_scene* scn, + const double pos[3], + const double Tref, + const char* call_func_name); + +/******************************************************************************* * Boundary sub-paths ******************************************************************************/ extern LOCAL_SYM res_T diff --git a/src/sdis_heat_path_conductive.c b/src/sdis_heat_path_conductive.c @@ -0,0 +1,128 @@ +/* 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_heat_path_conductive_c.h" +#include "sdis_log.h" +#include "sdis_medium_c.h" + +/******************************************************************************* + * Local function + ******************************************************************************/ +res_T +check_solid_constant_properties + (struct sdis_device* dev, + const int evaluate_green, + const int use_wos_diffusion, + const struct solid_props* props_ref, + const struct solid_props* props) +{ + res_T res = RES_OK; + ASSERT(dev && props_ref && props); + + if(props_ref->lambda != props->lambda) { + log_err(dev, + "%s: invalid thermal conductivity. One assumes a constant conductivity " + "for the whole solid.\n", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + + if(props_ref->rho != props->rho) { + log_err(dev, + "%s: invalid volumic mass. One assumes a constant volumic mass for " + "the whole solid.\n", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + + if(props_ref->cp != props->cp) { + log_err(dev, + "%s: invalid calorific capacity. One assumes a constant calorific " + "capacity for the whole solid.\n", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + + if((evaluate_green || use_wos_diffusion) && props_ref->power != props->power) { + log_err(dev, + "%s: invalid variable power density. Stardis expects a constant power " + "density per solid when using WoS diffusion and/or green function " + "evaluation.", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +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_delta_sphere_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_conductive_delta_sphere_Xd.h" +#define SDIS_XD_DIMENSION 2 +#include "sdis_heat_path_conductive_wos_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_heat_path_conductive_wos_Xd.h" diff --git a/src/sdis_heat_path_conductive_Xd.h b/src/sdis_heat_path_conductive_Xd.h @@ -1,541 +0,0 @@ -/* Copyright (C) 2016-2023 |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_device_c.h" -#include "sdis_green.h" -#include "sdis_heat_path.h" -#include "sdis_medium_c.h" -#include "sdis_misc.h" -#include "sdis_scene_c.h" - -#include <star/ssp.h> - -#include "sdis_Xd_begin.h" - -/******************************************************************************* - * Non generic helper function - ******************************************************************************/ -#ifndef SDIS_HEAT_PATH_CONDUCTIVE_XD_H -#define SDIS_HEAT_PATH_CONDUCTIVE_XD_H - -static res_T -check_solid_constant_properties - (struct sdis_device* dev, - const int evaluate_green, - const struct solid_props* props_ref, - const struct solid_props* props) -{ - res_T res = RES_OK; - ASSERT(dev && props_ref && props); - - if(props_ref->lambda != props->lambda) { - log_err(dev, - "%s: invalid thermal conductivity. One assumes a constant conductivity " - "for the whole solid.\n", FUNC_NAME); - res = RES_BAD_ARG; - goto error; - } - - if(props_ref->rho != props->rho) { - log_err(dev, - "%s: invalid volumic mass. One assumes a constant volumic mass for " - "the whole solid.\n", FUNC_NAME); - res = RES_BAD_ARG; - goto error; - } - - if(props_ref->cp != props->cp) { - log_err(dev, - "%s: invalid calorific capacity. One assumes a constant calorific " - "capacity for the whole solid.\n", FUNC_NAME); - res = RES_BAD_ARG; - goto error; - } - - if(evaluate_green && props_ref->power != props->power) { - log_err(dev, - "%s: invalid volumic power. When estimating the green function, a " - "constant volumic power is assumed for the whole solid.\n", - FUNC_NAME); - res = RES_BAD_ARG; - goto error; - } - -exit: - return res; -error: - goto exit; -} - -#endif /* SDIS_HEAT_PATH_CONDUCTIVE_XD_H */ - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -/* Sample the next direction to walk toward and compute the distance to travel. - * Return the sampled direction `dir0', the distance to travel along this - * direction, the hit `hit0' along `dir0' wrt to the returned distance, the - * direction `dir1' used to adjust the displacement distance, and the hit - * `hit1' along `dir1' used to adjust the displacement distance. */ -static float -XD(sample_next_step) - (struct sdis_scene* scn, - struct ssp_rng* rng, - const float pos[DIM], - const float delta_solid, - float dir0[DIM], /* Sampled direction */ - float dir1[DIM], /* Direction used to adjust delta */ - struct sXd(hit)* hit0, /* Hit along the sampled direction */ - struct sXd(hit)* hit1) /* Hit used to adjust delta */ -{ - struct sXd(hit) hits[2]; - float dirs[2][DIM]; - float range[2]; - float delta; - ASSERT(scn && rng && pos && delta_solid>0 && dir0 && dir1 && hit0 && hit1); - - *hit0 = SXD_HIT_NULL; - *hit1 = SXD_HIT_NULL; - -#if DIM == 2 - /* Sample a main direction around 2PI */ - ssp_ran_circle_uniform_float(rng, dirs[0], NULL); -#else - /* Sample a main direction around 4PI */ - ssp_ran_sphere_uniform_float(rng, dirs[0], NULL); -#endif - - /* Negate the sampled dir */ - fX(minus)(dirs[1], dirs[0]); - - /* Use the previously sampled direction to estimate the minimum distance from - * `pos' to the scene boundary */ - f2(range, FLT_MIN, delta_solid*RAY_RANGE_MAX_SCALE); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dirs[0], range, NULL, &hits[0])); - SXD(scene_view_trace_ray(scn->sXd(view), pos, dirs[1], range, NULL, &hits[1])); - if(SXD_HIT_NONE(&hits[0]) && SXD_HIT_NONE(&hits[1])) { - delta = delta_solid; - } else { - delta = MMIN(hits[0].distance, hits[1].distance); - } - - if(!SXD_HIT_NONE(&hits[0]) - && delta != hits[0].distance - && eq_eps(hits[0].distance, delta, delta_solid*0.1)) { - /* Set delta to the main hit distance if it is roughly equal to it in order - * to avoid numerical issues on moving along the main direction. */ - delta = hits[0].distance; - } - - /* Setup outputs */ - if(delta <= delta_solid*0.1 && hits[1].distance == delta) { - /* Snap the random walk to the boundary if delta is too small */ - fX(set)(dir0, dirs[1]); - *hit0 = hits[1]; - fX(splat)(dir1, (float)INF); - *hit1 = SXD_HIT_NULL; - } else { - fX(set)(dir0, dirs[0]); - *hit0 = hits[0]; - if(delta == hits[0].distance) { - fX(set)(dir1, dirs[0]); - *hit1 = hits[0]; - } else if(delta == hits[1].distance) { - fX(set)(dir1, dirs[1]); - *hit1 = hits[1]; - } else { - fX(splat)(dir1, 0); - *hit1 = SXD_HIT_NULL; - } - } - - return delta; -} - -/* Sample the next direction to walk toward and compute the distance to travel. - * If the targeted position does not lie inside the current medium, reject it - * and sample a new next step. */ -static res_T -XD(sample_next_step_robust) - (struct sdis_scene* scn, - struct sdis_medium* current_mdm, - struct ssp_rng* rng, - const double pos[DIM], - const float delta_solid, - float dir0[DIM], /* Sampled direction */ - float dir1[DIM], /* Direction used to adjust delta */ - struct sXd(hit)* hit0, /* Hit along the sampled direction */ - struct sXd(hit)* hit1, /* Hit used to adjust delta */ - float* out_delta) -{ - struct sdis_medium* mdm; - 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(dir0 && dir1 && hit0 && hit1 && out_delta); - - fX_set_dX(org, pos); - do { - double pos_next[DIM]; - - /* Compute the next step */ - delta = XD(sample_next_step) - (scn, rng, org, delta_solid, dir0, dir1, hit0, hit1); - - /* 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; } - 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); - } - - /* Check medium consistency */ - if(current_mdm != mdm) { -#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 -#endif - } - } while(current_mdm != mdm && ++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 - res = RES_BAD_OP; - goto error; - } - - *out_delta = delta; - -exit: - return res; -error: - goto exit; -} - - -/******************************************************************************* - * Handle the volumic power at a given diffusive step - ******************************************************************************/ -struct XD(handle_volumic_power_args) { - /* Forward/backward direction of the sampled diffusive step */ - const float* dir0; - const float* dir1; - - /* Forward/backward intersections along the sampled diffusive step */ - const struct sXd(hit)* hit0; - const struct sXd(hit)* hit1; - - /* Physical properties */ - double power; /* Volumic power */ - double lambda; /* Conductivity */ - - float delta_solid; /* Challenged length of a diffusive step */ - float delta; /* Current length of the current diffusive step */ - - size_t picard_order; -}; -static const struct XD(handle_volumic_power_args) -XD(HANDLE_VOLUMIC_POWER_ARGS_NULL) = { - NULL, NULL, NULL, NULL, -1, -1, -1, -1, 0 -}; - -static INLINE int -XD(check_handle_volumic_power_args) - (const struct XD(handle_volumic_power_args)* args) -{ - ASSERT(args); - return args - && args->dir0 - && args->dir1 - && args->hit0 - && args->hit1 - && args->lambda >= 0 - && args->delta_solid > 0 - && args->delta >= 0 - && args->delta_solid >= 0 - && args->picard_order > 0; -} - -static res_T -XD(handle_volumic_power) - (const struct sdis_scene* scn, - const struct XD(handle_volumic_power_args)* args, - double* out_power_term, - struct XD(temperature)* T) -{ - double power_term = 0; - res_T res = RES_OK; - ASSERT(scn && out_power_term && T && XD(check_handle_volumic_power_args)(args)); - - /* No volumic power. Do nothing */ - if(args->power == SDIS_VOLUMIC_POWER_NONE) goto exit; - - /* Check that picardN is not enabled when a volumic power is set since in - * this situation the upper bound of the Monte-Carlo weight required by - * picardN cannot be known */ - if(args->picard_order > 1) { - log_err(scn->dev, - "%s: invalid not null volumic power '%g' kg/m^3. Could not manage a " - "volumic power when the picard order is not equal to 1; Picard order is " - "currently set to %lu.\n", - FUNC_NAME, args->power, (unsigned long)args->picard_order); - res = RES_BAD_ARG; - goto error; - } - - /* No forward/backward intersection along the sampled direction */ - if(SXD_HIT_NONE(args->hit0) && SXD_HIT_NONE(args->hit1)) { - const double delta_in_meter = args->delta * scn->fp_to_meter; - power_term = delta_in_meter * delta_in_meter / (2.0 * DIM * args->lambda); - T->value += args->power * power_term; - - /* An intersection along this diffusive step is find. Use it to statically - * correct the power term currently registered */ - } else { - const double delta_s_adjusted = args->delta_solid * RAY_RANGE_MAX_SCALE; - const double delta_s_in_meter = args->delta_solid * scn->fp_to_meter; - double h; - double h_in_meter; - double cos_U_N; - float N[DIM] = {0}; - - if(args->delta == args->hit0->distance) { - fX(normalize)(N, args->hit0->normal); - cos_U_N = fX(dot)(args->dir0, N); - - } else { - ASSERT(args->delta == args->hit1->distance); - fX(normalize)(N, args->hit1->normal); - cos_U_N = fX(dot)(args->dir1, N); - } - - h = args->delta * fabs(cos_U_N); - h_in_meter = h * scn->fp_to_meter; - - /* The regular power term */ - power_term = h_in_meter * h_in_meter / (2.0*args->lambda); - - /* Add the power corrective term. Be careful to use the adjusted - * delta_solid to correctly handle the RAY_RANGE_MAX_SCALE factor in the - * computation of the limit angle. But keep going with the unmodified - * delta_solid in the corrective term since it was the one that was - * "wrongly" used in the previous step and that must be corrected. */ - if(h == delta_s_adjusted) { - power_term += - -(delta_s_in_meter * delta_s_in_meter) / (2.0*DIM*args->lambda); - - } else if(h < delta_s_adjusted) { - const double sin_a = h / delta_s_adjusted; -#if DIM==2 - /* tmp = sin(2a) / (PI - 2*a) */ - const double tmp = sin_a * sqrt(1 - sin_a*sin_a) / acos(sin_a); - power_term += - -(delta_s_in_meter * delta_s_in_meter) / (4.0*args->lambda) * tmp; -#else - const double tmp = (sin_a*sin_a*sin_a - sin_a) / (1-sin_a); - power_term += - (delta_s_in_meter * delta_s_in_meter) / (6.0*args->lambda) * tmp; -#endif - } - T->value += args->power * power_term; - } - -exit: - *out_power_term = power_term; - return res; -error: - goto exit; -} - -/******************************************************************************* - * Local function - ******************************************************************************/ -res_T -XD(conductive_path) - (struct sdis_scene* scn, - struct rwalk_context* ctx, - struct XD(rwalk)* rwalk, - struct ssp_rng* rng, - struct XD(temperature)* T) -{ - double position_start[DIM]; - struct solid_props props_ref = SOLID_PROPS_NULL; - double green_power_term = 0; - struct sdis_medium* mdm; - size_t istep = 0; /* Help for debug */ - res_T res = RES_OK; - ASSERT(scn && rwalk && rng && T); - ASSERT(rwalk->mdm->type == SDIS_SOLID); - (void)ctx, (void)istep; - - /* Check the random walk consistency */ - res = scene_get_medium_in_closed_boundaries(scn, rwalk->vtx.P, &mdm); - if(res != RES_OK || mdm != rwalk->mdm) { - log_err(scn->dev, "%s: invalid solid random walk. " - "Unexpected medium at {%g, %g, %g}.\n", FUNC_NAME, SPLIT3(rwalk->vtx.P)); - res = RES_BAD_OP_IRRECOVERABLE; - goto error; - } - /* Save the submitted position */ - dX(set)(position_start, rwalk->vtx.P); - - /* Retrieve the solid properties at the current position. Use them to verify - * that those that are supposed to be constant by the conductive random walk - * remain the same. Note that we take care of the same constraints on the - * solid reinjection since once reinjected, the position of the random walk - * is that at the beginning of the conductive random walk. Thus, after a - * reinjection, the next line retrieves the properties of the reinjection - * position. By comparing them to the properties along the random walk, we - * thus verify that the properties are constant throughout the random walk - * with respect to the properties of the reinjected position. */ - solid_get_properties(mdm, &rwalk->vtx, &props_ref); - - do { /* Solid random walk */ - struct XD(handle_volumic_power_args) handle_volpow_args = - XD(HANDLE_VOLUMIC_POWER_ARGS_NULL); - struct sXd(hit) hit0, hit1; - struct solid_props props = SOLID_PROPS_NULL; - double power_term = 0; - double mu; - float delta; /* Random walk numerical parameter */ - double delta_m; - float dir0[DIM], dir1[DIM]; - float org[DIM]; - - /* Fetch solid properties */ - res = solid_get_properties(mdm, &rwalk->vtx, &props); - if(res != RES_OK) goto error; - - res = check_solid_constant_properties - (scn->dev, ctx->green_path != NULL, &props_ref, &props); - if(res != RES_OK) goto error; - - /* Check the limit condition - * REVIEW Rfo: This can be a bug if the random walk comes from a boundary */ - if(props.temperature >= 0) { - T->value += props.temperature; - T->done = 1; - - 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; - } - - if(ctx->heat_path) { - heat_path_get_last_vertex(ctx->heat_path)->weight = T->value; - } - - break; - } - - 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, - (float)props.delta, dir0, dir1, &hit0, &hit1, &delta); - if(res != RES_OK) goto error; - - /* Add the volumic power density to the measured temperature */ - handle_volpow_args.dir0 = dir0; - handle_volpow_args.dir1 = dir1; - handle_volpow_args.hit0 = &hit0; - handle_volpow_args.hit1 = &hit1; - handle_volpow_args.power = props.power; - handle_volpow_args.lambda = props.lambda; - handle_volpow_args.delta_solid = (float)props.delta; - handle_volpow_args.delta = delta; - handle_volpow_args.picard_order = get_picard_order(ctx); - res = XD(handle_volumic_power)(scn, &handle_volpow_args, &power_term, T); - if(res != RES_OK) goto error; - - /* Register the power term for the green function. Delay its registration - * until the end of the conductive path, i.e. the path is valid */ - if(ctx->green_path && props.power != SDIS_VOLUMIC_POWER_NONE) { - green_power_term += power_term; - } - - /* 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); - 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->hit_side = SDIS_SIDE_NULL__; - } else { - rwalk->hit = hit0; - rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; - } - - /* Update the random walk position */ - XD(move_pos)(rwalk->vtx.P, dir0, delta); - - /* Register the new vertex against the heat path */ - res = register_heat_vertex(ctx->heat_path, &rwalk->vtx, T->value, - SDIS_HEAT_VERTEX_CONDUCTION, (int)ctx->nbranchings); - if(res != RES_OK) goto error; - - ++istep; - - /* Keep going while the solid random walk does not hit an interface */ - } while(SXD_HIT_NONE(&rwalk->hit)); - - /* Register the power term for the green function */ - if(ctx->green_path && props_ref.power != SDIS_VOLUMIC_POWER_NONE) { - res = green_path_add_power_term - (ctx->green_path, rwalk->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 */ - -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 @@ -0,0 +1,78 @@ +/* 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/>. */ + +#ifndef SDIS_HEAT_PATH_CONDUCTIVE_C_H +#define SDIS_HEAT_PATH_CONDUCTIVE_C_H + +#include <rsys/rsys.h> + +/* Forward declarations */ +struct rwalk_2d; +struct rwalk_3d; +struct rwalk_context; +struct sdis_device; +struct sdis_scene; +struct solid_props; +struct ssp_rng; +struct temperature_2d; +struct temperature_3d; + +extern LOCAL_SYM res_T +check_solid_constant_properties + (struct sdis_device* dev, + const int evaluate_green, + const int use_wos_diffusion, + const struct solid_props* props_ref, + const struct solid_props* props); + +/******************************************************************************* + * 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 ssp_rng* rng, + struct temperature_2d* T); + +extern LOCAL_SYM res_T +conductive_path_delta_sphere_3d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* T); + +/******************************************************************************* + * Conductive paths using the walk on sphere algorithm + ******************************************************************************/ +extern LOCAL_SYM res_T +conductive_path_wos_2d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct rwalk_2d* rwalk, + struct ssp_rng* rng, + struct temperature_2d* T); + +extern LOCAL_SYM res_T +conductive_path_wos_3d + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct rwalk_3d* rwalk, + struct ssp_rng* rng, + struct temperature_3d* T); + +#endif /* SDIS_HEAT_PATH_CONDUCTIVE_C_H */ diff --git a/src/sdis_heat_path_conductive_delta_sphere_Xd.h b/src/sdis_heat_path_conductive_delta_sphere_Xd.h @@ -0,0 +1,481 @@ +/* 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_log.h" +#include "sdis_green.h" +#include "sdis_interface_c.h" +#include "sdis_medium_c.h" +#include "sdis_misc.h" +#include "sdis_scene_c.h" + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +/* Sample the next direction to walk toward and compute the distance to travel. + * Return the sampled direction `dir0', the distance to travel along this + * direction, the hit `hit0' along `dir0' wrt to the returned distance, the + * direction `dir1' used to adjust the displacement distance, and the hit + * `hit1' along `dir1' used to adjust the displacement distance. */ +static float +XD(sample_next_step) + (struct sdis_scene* scn, + struct ssp_rng* rng, + const float pos[DIM], + const float delta_solid, + float dir0[DIM], /* Sampled direction */ + float dir1[DIM], /* Direction used to adjust delta */ + struct sXd(hit)* hit0, /* Hit along the sampled direction */ + struct sXd(hit)* hit1) /* Hit used to adjust delta */ +{ + struct sXd(hit) hits[2]; + float dirs[2][DIM]; + float range[2]; + float delta; + ASSERT(scn && rng && pos && delta_solid>0 && dir0 && dir1 && hit0 && hit1); + + *hit0 = SXD_HIT_NULL; + *hit1 = SXD_HIT_NULL; + +#if DIM == 2 + /* Sample a main direction around 2PI */ + ssp_ran_circle_uniform_float(rng, dirs[0], NULL); +#else + /* Sample a main direction around 4PI */ + ssp_ran_sphere_uniform_float(rng, dirs[0], NULL); +#endif + + /* Negate the sampled dir */ + fX(minus)(dirs[1], dirs[0]); + + /* Use the previously sampled direction to estimate the minimum distance from + * `pos' to the scene boundary */ + f2(range, FLT_MIN, delta_solid*RAY_RANGE_MAX_SCALE); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dirs[0], range, NULL, &hits[0])); + SXD(scene_view_trace_ray(scn->sXd(view), pos, dirs[1], range, NULL, &hits[1])); + if(SXD_HIT_NONE(&hits[0]) && SXD_HIT_NONE(&hits[1])) { + delta = delta_solid; + } else { + delta = MMIN(hits[0].distance, hits[1].distance); + } + + if(!SXD_HIT_NONE(&hits[0]) + && delta != hits[0].distance + && eq_eps(hits[0].distance, delta, delta_solid*0.1)) { + /* Set delta to the main hit distance if it is roughly equal to it in order + * to avoid numerical issues on moving along the main direction. */ + delta = hits[0].distance; + } + + /* Setup outputs */ + if(delta <= delta_solid*0.1 && hits[1].distance == delta) { + /* Snap the random walk to the boundary if delta is too small */ + fX(set)(dir0, dirs[1]); + *hit0 = hits[1]; + fX(splat)(dir1, (float)INF); + *hit1 = SXD_HIT_NULL; + } else { + fX(set)(dir0, dirs[0]); + *hit0 = hits[0]; + if(delta == hits[0].distance) { + fX(set)(dir1, dirs[0]); + *hit1 = hits[0]; + } else if(delta == hits[1].distance) { + fX(set)(dir1, dirs[1]); + *hit1 = hits[1]; + } else { + fX(splat)(dir1, 0); + *hit1 = SXD_HIT_NULL; + } + } + + return delta; +} + +/* Sample the next direction to walk toward and compute the distance to travel. + * If the targeted position does not lie inside the current medium, reject it + * and sample a new next step. */ +static res_T +XD(sample_next_step_robust) + (struct sdis_scene* scn, + struct sdis_medium* current_mdm, + struct ssp_rng* rng, + const double pos[DIM], + const float delta_solid, + float dir0[DIM], /* Sampled direction */ + float dir1[DIM], /* Direction used to adjust delta */ + struct sXd(hit)* hit0, /* Hit along the sampled direction */ + struct sXd(hit)* hit1, /* Hit used to adjust delta */ + float* out_delta) +{ + struct sdis_medium* mdm; + 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(dir0 && dir1 && hit0 && hit1 && out_delta); + + fX_set_dX(org, pos); + do { + double pos_next[DIM]; + + /* Compute the next step */ + delta = XD(sample_next_step) + (scn, rng, org, delta_solid, dir0, dir1, hit0, hit1); + + /* 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; } + 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); + } + + /* Check medium consistency */ + if(current_mdm != mdm) { +#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 +#endif + } + } while(current_mdm != mdm && ++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 + res = RES_BAD_OP; + goto error; + } + + *out_delta = delta; + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * Handle the volumic power at a given diffusive step + ******************************************************************************/ +struct XD(handle_volumic_power_args) { + /* Forward/backward direction of the sampled diffusive step */ + const float* dir0; + const float* dir1; + + /* Forward/backward intersections along the sampled diffusive step */ + const struct sXd(hit)* hit0; + const struct sXd(hit)* hit1; + + /* Physical properties */ + double power; /* Volumic power */ + double lambda; /* Conductivity */ + + float delta_solid; /* Challenged length of a diffusive step */ + float delta; /* Current length of the current diffusive step */ + + size_t picard_order; +}; +static const struct XD(handle_volumic_power_args) +XD(HANDLE_VOLUMIC_POWER_ARGS_NULL) = { + NULL, NULL, NULL, NULL, -1, -1, -1, -1, 0 +}; + +static INLINE int +XD(check_handle_volumic_power_args) + (const struct XD(handle_volumic_power_args)* args) +{ + ASSERT(args); + return args + && args->dir0 + && args->dir1 + && args->hit0 + && args->hit1 + && args->lambda >= 0 + && args->delta_solid > 0 + && args->delta >= 0 + && args->delta_solid >= 0 + && args->picard_order > 0; +} + +static res_T +XD(handle_volumic_power) + (const struct sdis_scene* scn, + const struct XD(handle_volumic_power_args)* args, + double* out_power_term, + struct XD(temperature)* T) +{ + double power_term = 0; + res_T res = RES_OK; + ASSERT(scn && out_power_term && T && XD(check_handle_volumic_power_args)(args)); + + /* No volumic power. Do nothing */ + if(args->power == SDIS_VOLUMIC_POWER_NONE) goto exit; + + /* Check that picardN is not enabled when a volumic power is set since in + * this situation the upper bound of the Monte-Carlo weight required by + * picardN cannot be known */ + if(args->picard_order > 1) { + log_err(scn->dev, + "%s: invalid not null volumic power '%g' kg/m^3. Could not manage a " + "volumic power when the picard order is not equal to 1; Picard order is " + "currently set to %lu.\n", + FUNC_NAME, args->power, (unsigned long)args->picard_order); + res = RES_BAD_ARG; + goto error; + } + + /* No forward/backward intersection along the sampled direction */ + if(SXD_HIT_NONE(args->hit0) && SXD_HIT_NONE(args->hit1)) { + const double delta_in_meter = args->delta * scn->fp_to_meter; + power_term = delta_in_meter * delta_in_meter / (2.0 * DIM * args->lambda); + T->value += args->power * power_term; + + /* An intersection along this diffusive step is find. Use it to statically + * correct the power term currently registered */ + } else { + const double delta_s_adjusted = args->delta_solid * RAY_RANGE_MAX_SCALE; + const double delta_s_in_meter = args->delta_solid * scn->fp_to_meter; + double h; + double h_in_meter; + double cos_U_N; + float N[DIM] = {0}; + + if(args->delta == args->hit0->distance) { + fX(normalize)(N, args->hit0->normal); + cos_U_N = fX(dot)(args->dir0, N); + + } else { + ASSERT(args->delta == args->hit1->distance); + fX(normalize)(N, args->hit1->normal); + cos_U_N = fX(dot)(args->dir1, N); + } + + h = args->delta * fabs(cos_U_N); + h_in_meter = h * scn->fp_to_meter; + + /* The regular power term */ + power_term = h_in_meter * h_in_meter / (2.0*args->lambda); + + /* Add the power corrective term. Be careful to use the adjusted + * delta_solid to correctly handle the RAY_RANGE_MAX_SCALE factor in the + * computation of the limit angle. But keep going with the unmodified + * delta_solid in the corrective term since it was the one that was + * "wrongly" used in the previous step and that must be corrected. */ + if(h == delta_s_adjusted) { + power_term += + -(delta_s_in_meter * delta_s_in_meter) / (2.0*DIM*args->lambda); + + } else if(h < delta_s_adjusted) { + const double sin_a = h / delta_s_adjusted; +#if DIM==2 + /* tmp = sin(2a) / (PI - 2*a) */ + const double tmp = sin_a * sqrt(1 - sin_a*sin_a) / acos(sin_a); + power_term += + -(delta_s_in_meter * delta_s_in_meter) / (4.0*args->lambda) * tmp; +#else + const double tmp = (sin_a*sin_a*sin_a - sin_a) / (1-sin_a); + power_term += + (delta_s_in_meter * delta_s_in_meter) / (6.0*args->lambda) * tmp; +#endif + } + T->value += args->power * power_term; + } + +exit: + *out_power_term = power_term; + return res; +error: + goto exit; +} + +/******************************************************************************* + * Local function + ******************************************************************************/ +res_T +XD(conductive_path_delta_sphere) + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + double position_start[DIM]; + struct solid_props props_ref = SOLID_PROPS_NULL; + double green_power_term = 0; + struct sdis_medium* mdm; + size_t istep = 0; /* Help for debug */ + res_T res = RES_OK; + ASSERT(scn && rwalk && rng && T); + ASSERT(rwalk->mdm->type == SDIS_SOLID); + (void)ctx, (void)istep; + + /* Check the random walk consistency */ + res = scene_get_medium_in_closed_boundaries(scn, rwalk->vtx.P, &mdm); + if(res != RES_OK || mdm != rwalk->mdm) { + log_err(scn->dev, "%s: invalid solid random walk. " + "Unexpected medium at {%g, %g, %g}.\n", FUNC_NAME, SPLIT3(rwalk->vtx.P)); + res = RES_BAD_OP_IRRECOVERABLE; + goto error; + } + /* Save the submitted position */ + dX(set)(position_start, rwalk->vtx.P); + + /* Retrieve the solid properties at the current position. Use them to verify + * that those that are supposed to be constant by the conductive random walk + * remain the same. Note that we take care of the same constraints on the + * solid reinjection since once reinjected, the position of the random walk + * is that at the beginning of the conductive random walk. Thus, after a + * reinjection, the next line retrieves the properties of the reinjection + * position. By comparing them to the properties along the random walk, we + * thus verify that the properties are constant throughout the random walk + * with respect to the properties of the reinjected position. */ + solid_get_properties(mdm, &rwalk->vtx, &props_ref); + + do { /* Solid random walk */ + struct XD(handle_volumic_power_args) handle_volpow_args = + XD(HANDLE_VOLUMIC_POWER_ARGS_NULL); + struct sXd(hit) hit0, hit1; + struct solid_props props = SOLID_PROPS_NULL; + double power_term = 0; + double mu; + float delta; /* Random walk numerical parameter */ + double delta_m; + float dir0[DIM], dir1[DIM]; + float org[DIM]; + + /* Fetch solid properties */ + res = solid_get_properties(mdm, &rwalk->vtx, &props); + if(res != RES_OK) goto error; + + res = check_solid_constant_properties + (scn->dev, ctx->green_path != NULL, 0/*use WoS?*/, &props_ref, &props); + if(res != RES_OK) goto error; + + /* Check the limit condition + * REVIEW Rfo: This can be a bug if the random walk comes from a boundary */ + if(SDIS_TEMPERATURE_IS_KNOWN(props.temperature)) { + T->value += props.temperature; + T->done = 1; + + 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; + } + + if(ctx->heat_path) { + heat_path_get_last_vertex(ctx->heat_path)->weight = T->value; + } + + break; + } + + 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, + (float)props.delta, dir0, dir1, &hit0, &hit1, &delta); + if(res != RES_OK) goto error; + + /* Add the volumic power density to the measured temperature */ + handle_volpow_args.dir0 = dir0; + handle_volpow_args.dir1 = dir1; + handle_volpow_args.hit0 = &hit0; + handle_volpow_args.hit1 = &hit1; + handle_volpow_args.power = props.power; + handle_volpow_args.lambda = props.lambda; + handle_volpow_args.delta_solid = (float)props.delta; + handle_volpow_args.delta = delta; + handle_volpow_args.picard_order = get_picard_order(ctx); + res = XD(handle_volumic_power)(scn, &handle_volpow_args, &power_term, T); + if(res != RES_OK) goto error; + + /* Register the power term for the green function. Delay its registration + * until the end of the conductive path, i.e. the path is valid */ + if(ctx->green_path && props.power != SDIS_VOLUMIC_POWER_NONE) { + green_power_term += power_term; + } + + /* 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); + 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->hit_side = SDIS_SIDE_NULL__; + } else { + rwalk->hit = hit0; + rwalk->hit_side = fX(dot)(hit0.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK; + } + + /* Update the random walk position */ + XD(move_pos)(rwalk->vtx.P, dir0, delta); + + /* Register the new vertex against the heat path */ + res = register_heat_vertex(ctx->heat_path, &rwalk->vtx, T->value, + SDIS_HEAT_VERTEX_CONDUCTION, (int)ctx->nbranchings); + if(res != RES_OK) goto error; + + ++istep; + + /* Keep going while the solid random walk does not hit an interface */ + } while(SXD_HIT_NONE(&rwalk->hit)); + + /* Register the power term for the green function */ + if(ctx->green_path && props_ref.power != SDIS_VOLUMIC_POWER_NONE) { + res = green_path_add_power_term + (ctx->green_path, rwalk->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 */ + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_conductive_wos_Xd.h b/src/sdis_heat_path_conductive_wos_Xd.h @@ -0,0 +1,669 @@ +/* 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_device_c.h" +#include "sdis_heat_path_conductive_c.h" +#include "sdis_medium_c.h" +#include "sdis_scene_c.h" + +#include <star/swf.h> + +#include "sdis_Xd_begin.h" + +/******************************************************************************* + * Helper function + ******************************************************************************/ +static res_T +XD(check_medium_consistency) + (struct sdis_scene* scn, + const struct XD(rwalk)* rwalk) +{ + struct sdis_medium* mdm = NULL; + res_T res = RES_OK; + ASSERT(rwalk); + + res = scene_get_medium_in_closed_boundaries(scn, rwalk->vtx.P, &mdm); + if(res != RES_OK) goto error; + + /* Check medium consistency */ + if(mdm != rwalk->mdm) { + log_err(scn->dev, + "%s:%s: invalid solid walk. Unexpected medium (position: "FORMAT_VECX").\n", + __FILE__, FUNC_NAME, SPLITX(rwalk->vtx.P)); + res = RES_BAD_OP_IRRECOVERABLE; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +static res_T +XD(time_travel) + (struct sdis_scene* scn, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + const double alpha, /* Diffusivity, i.e. lambda/(rho*cp) */ + const double t0, /* Initial time [s] */ + double* distance, /* Displacement [m/fp_to_meter] */ + struct XD(temperature)* T) +{ + double dir[DIM] = {0}; + double dst = 0; /* Distance [m] */ + double tau = 0; /* Time [s] */ + double x = 0; + double r = 0; + double temperature = 0; /* [k] */ + double time = 0; /* [s] */ + res_T res = RES_OK; + ASSERT(scn && rwalk && rng && alpha > 0 && distance && T); + + dst = *distance * scn->fp_to_meter; + ASSERT(dst >= 0); + + /* No displacement => no time travel */ + if(distance == 0) goto exit; + + /* Sample x = tau*alpha/distance^2 */ + r = ssp_rng_canonical(rng); + x = swf_tabulation_inverse(XD(scn->dev->H), SWF_QUADRATIC, r); + + /* Retrieve the time to travel */ + tau = x / alpha * dst * dst; + time = MMIN(tau, rwalk->vtx.time - t0); + + /* Increment the elapsed time */ + rwalk->elapsed_time += time; + + if(IS_INF(rwalk->vtx.time)) goto exit; /* Steady computation */ + + /* Let's take a trip back in time */ + rwalk->vtx.time = MMAX(t0, rwalk->vtx.time - tau); + + /* The path does not reach the initial condition */ + if(rwalk->vtx.time > t0) goto exit; + + /* The path reaches the initial condition. Sample a distance corresponding to + * the travel time to the initial condition. + * + * TODO while we use the H function to sample the distance, one should use the + * U function. For the moment, this function is not available, hence the use + * of H. This is not a problem, since we currently assume that the initial + * condition is uniform. Position is only used for path geometry */ + 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 */ + + /* Uniformly sample a direction and move along it of the distance that + * separate the current position ot 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); + + /* Fetch the initial temperature */ + temperature = medium_get_temperature(rwalk->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", + __FILE__, FUNC_NAME, + medium_type_to_string(sdis_medium_get_type(rwalk->mdm)), + SPLIT3(rwalk->vtx.P)); + res = RES_BAD_ARG; + goto error; + } + + /* Update the temperature */ + T->value += temperature; + T->done = 1; + +exit: + return res; +error: + goto exit; +} + +#if DIM == 2 +static INLINE enum sdis_side +compute_hit_side_2d + (const struct s2d_hit* hit, + const double pos[2]) /* Position from which intersection occurs */ +{ + struct s2d_attrib p0, p1; /* Segment positions */ + double v0[2] = {0}; /* Vector from segment vertex 0 to segment vertex 1 */ + double v1[2] = {0}; /* Vector from segment vertex 0 to input position */ + double z = 0; + + /* Check pre-conditions */ + ASSERT(hit && pos && !S2D_HIT_NONE(hit)); + + /* Retrieve the positions of the intersected segment */ + S2D(segment_get_vertex_attrib(&hit->prim, 0, S2D_POSITION, &p0)); + S2D(segment_get_vertex_attrib(&hit->prim, 1, S2D_POSITION, &p1)); + + v0[0] = p1.value[0] - p0.value[0]; + v0[1] = p1.value[1] - p0.value[1]; + v1[0] = pos[0] - p0.value[0]; + v1[1] = pos[1] - p0.value[1]; + + /* Z coordinate of the cross product between v0 and v1. Its sign indicates on + * which side of the segment the position lies. */ + z = d2_cross(v1, v0); + return z > 0 ? SDIS_FRONT : SDIS_BACK; +} +#endif + +#if DIM == 3 +static INLINE enum sdis_side +compute_hit_side_3d + (const struct s3d_hit* hit, + const double pos[3]) /* Position from which intersection occurs */ +{ + struct s3d_attrib v0; /* Position of the 1st triangle vertex */ + double p[3] = {0}; /* Position of the 1st triangle vertex in double */ + double N[3] = {0}; /* Normalized triangle normal */ + double D = 0; /* Last parameter of the plane triangle plane equation */ + double dst = 0; /* Distance of pos to the plane */ + + /* Check pre-conditions */ + ASSERT(hit && pos && !S3D_HIT_NONE(hit)); + + /* Retrieve the positions of the intersected triangle */ + S3D(triangle_get_vertex_attrib(&hit->prim, 0, S3D_POSITION, &v0)); + d3_set_f3(p, v0.value); + + /* Compute the plane equation of the triangle */ + d3_set_f3(N, hit->normal); + d3_normalize(N, N); + D = -d3_dot(N, p); + + /* Calculate the distance of the input position from the plane of the triangle + * and use the sign to define which side of the triangle the position is on */ + dst = d3_dot(N, pos) + D; + return dst > 0 ? SDIS_FRONT : SDIS_BACK; +} +#endif + +/* Verify that the submitted position is in the expected medium */ +static res_T +XD(check_diffusion_position) + (struct sdis_scene* scn, + const struct sdis_medium* expected_medium, + const double pos[DIM]) +{ + struct sdis_interface* interf = NULL; + struct sdis_medium* mdm = NULL; + enum sdis_side side; + + struct sXd(hit) hit = SXD_HIT_NULL; + float wos_pos[DIM] = {0}; + float wos_radius = 0; + 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); + 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); + side = XD(compute_hit_side)(&hit, pos); + mdm = side == SDIS_FRONT ? interf->medium_front : interf->medium_back; + if(mdm != expected_medium) { + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +static res_T +XD(setup_hit_wos) + (struct sdis_scene* scn, + const struct sXd(hit)* hit, + struct XD(rwalk)* rwalk) +{ + /* Geometry */ + struct sXd(primitive) prim; + struct sXd(attrib) attr; + + /* Properties */ + struct sdis_interface* interf = NULL; + struct sdis_medium* mdm = NULL; + enum sdis_side side = SDIS_SIDE_NULL__; + + /* Miscellaneous */ + double tgt[DIM] = {0}; /* Target point, i.e. hit position */ + res_T res = RES_OK; + + /* Check pre-conditions */ + ASSERT(rwalk && hit); + + /* Find intersected position */ + SXD(scene_view_get_primitive(scn->sXd(view), hit->prim.prim_id, &prim)); +#if DIM == 2 + SXD(primitive_get_attrib(&prim, SXD_POSITION, hit->u, &attr)); +#else + SXD(primitive_get_attrib(&prim, SXD_POSITION, hit->uv, &attr)); +#endif + + /* Calculate on which side the intersection occurs */ + 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) { + log_err(scn->dev, + "%s:%s: the conductive path has reached an invalid interface; " + "unexpected medium (position: "FORMAT_VECX"; side: %s).\n", + __FILE__, FUNC_NAME, SPLITX(tgt), side == SDIS_FRONT ? "front" : "back"); + res = RES_BAD_OP_IRRECOVERABLE; + goto error; + } + + /* Random walk update. Do not set the medium to NULL as the intersection is + * found regardless of time, so the initial condition could be reached before + * 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->hit_side = side; + +exit: + return res; +error: + goto exit; +} + +static res_T +XD(setup_hit_rt) + (struct sdis_scene* scn, + const double pos[DIM], + const double dir[DIM], + const struct sXd(hit)* hit, + struct XD(rwalk)* rwalk) +{ + /* Properties */ + struct sdis_interface* interf = NULL; + struct sdis_medium* mdm = NULL; + enum sdis_side side = SDIS_SIDE_NULL__; + + /* Miscellaneous */ + double tgt[DIM] = {0}; /* Target point, i.e. hit position */ + double N[DIM] = {0}; + res_T res = RES_OK; + + /* Check pre-conditions */ + ASSERT(pos && dir && rwalk && hit); + ASSERT(dX(is_normalized)(dir)); + + /* Calculate on which side the intersection occurs */ + dX(muld)(tgt, dir, hit->distance); + dX(add)(tgt, tgt, pos); + dX_set_fX(N, hit->normal); + dX(normalize)(N, N); + 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) { + log_err(scn->dev, + "%s:%s: the conductive path has reached an invalid interface; " + "unexpected medium (position: "FORMAT_VECX"; side: %s).\n", + __FILE__, FUNC_NAME, SPLITX(tgt), side == SDIS_FRONT ? "front" : "back"); + res = RES_BAD_OP_IRRECOVERABLE; + goto error; + } + + /* Random walk update. Do not set the medium to NULL as the intersection is + * found regardless of time, so the initial condition could be reached before + * 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->hit_side = side; + +exit: + return res; +error: + goto exit; +} + +static res_T +XD(sample_next_position) + (struct sdis_scene* scn, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + double* distance) /* Displacement distance */ +{ + /* Intersection */ + struct sXd(hit) hit = SXD_HIT_NULL; + + /* Walk on sphere */ + double wos_distance = 0; + double wos_epsilon = 0; + float wos_pos[DIM] = {0}; + float wos_radius = 0; + + /* Miscellaneous */ + res_T res = RES_OK; + ASSERT(rwalk && rng && distance); + + /* Find the closest distance from the current position to the geometry */ + wos_radius = (float)INF; + fX_set_dX(wos_pos, rwalk->vtx.P); + SXD(scene_view_closest_point(scn->sXd(view), wos_pos, wos_radius, NULL, &hit)); + CHK(!SXD_HIT_NONE(&hit)); + wos_distance = hit.distance; + + /* The current position is in the epsilon shell: + * move it to the nearest interface position */ + wos_epsilon = scn->fp_to_meter*1.e-6; + if(wos_distance <= wos_epsilon) { + res = XD(setup_hit_wos)(scn, &hit, rwalk); + if(res != RES_OK) goto error; + + /* Uniformly sample a new position on the surrounding sphere */ + } else { + double pos[DIM] = {0}; + double dir[DIM] = {0}; + +#if DIM == 2 + ssp_ran_circle_uniform(rng, dir, NULL); +#else + ssp_ran_sphere_uniform(rng, dir, NULL); +#endif + dX(muld)(pos, dir, (double)hit.distance); + dX(add)(pos, pos, rwalk->vtx.P); + + /* Check that the new position is in the intended medium. Please note that + * we do not use the scene_get_medium_in_closed_boundaries function. It uses + * the ray-tracing operator, which has its own numerical uncertainty that is + * not the same as that of the closest point operator used by this + * scattering algorithm. It can therefore return the expected medium, + * whereas the nearest point operator would return an inconsistent medium. + * 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); + + /* Diffusion position is valid => move the path to the new position */ + if(res == RES_OK) { + dX(set)(rwalk->vtx.P, pos); + + /* As a result, the new position is detected as being in the wrong medium. + * This means that there has been a numerical problem in moving the + * position, which has therefore jumped the solid boundary. To solve this + * problem, we can move the trajectory on the solid interface along the + * direction of displacement. Indeed, we can assume that the position we + * want to move to is actually inside the epsilon shell. In this case, the + * trajectory will be moved to this interface in the next step anyway. */ + } else { + float rt_pos[DIM] = {0}; + float rt_dir[DIM] = {0}; + float rt_range[2] = {0, 0}; + + fX_set_dX(rt_pos, rwalk->vtx.P); + fX_set_dX(rt_dir, dir); + rt_range[0] = 0; + rt_range[1] = (float)INF; + SXD(scene_view_trace_ray(scn->sXd(view), rt_pos, rt_dir, rt_range, NULL, &hit)); + + /* An intersection should be found. If not, we can do nothing and simply + * reject the path. + * + * TODO: we could take the treatment of numerical problems a step further + * by sampling other directions and trying to move in them again. But at + * present, and until we have proof to the contrary, we assume that the + * rejection of a path should not occur, or that it will be so rare that + * 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", + __FILE__, FUNC_NAME, SPLITX(pos), SPLITX(dir), wos_distance); + res = RES_BAD_OP_IRRECOVERABLE; + goto error; + } + + res = XD(setup_hit_rt)(scn, rwalk->vtx.P, dir, &hit, rwalk); + if(res != RES_OK) goto error; + } + } + +exit: + *distance = hit.distance; + return res; +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 + ******************************************************************************/ +res_T +XD(conductive_path_wos) + (struct sdis_scene* scn, + struct rwalk_context* ctx, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + /* Properties */ + struct sdis_medium* mdm = NULL; + struct solid_props props_ref = SOLID_PROPS_NULL; + struct solid_props props = SOLID_PROPS_NULL; + double alpha = 0; /* diffusivity, i.e. lambda/(rho*cp) */ + + /* Miscellaneous */ + size_t ndiffusion_steps = 0; /* For debug */ + double green_power_term = 0; + int green = 0; + const int wos = 1; + res_T res = RES_OK; + (void)ctx; /* Avoid the "unused variable" warning */ + + /* 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_medium_consistency)(scn, rwalk); + if(res != RES_OK) goto error; + + /* Retrieve the solid properties at the current position. Use them to verify + * that those that are supposed to be constant by the conductive random walk + * remain the same. Note that we take care of the same constraints on the + * solid reinjection since once reinjected, the position of the random walk + * is that at the beginning of the conductive random walk. Thus, after a + * reinjection, the next line retrieves the properties of the reinjection + * position. By comparing them to the properties along the random walk, we + * thus verify that the properties are constant throughout the random walk + * with respect to the properties of the reinjected position. */ + solid_get_properties(rwalk->mdm, &rwalk->vtx, &props_ref); + props = props_ref; + + /* The algorithm assumes that lambda, rho and cp are constants. The + * diffusivity of the material (alpha) can therefore be calculated once */ + alpha = props_ref.lambda / (props_ref.rho * props_ref.cp); + + /* Sample a diffusive path */ + for(;;) { + double power_term = 0; /* */ + double dst = 0; /* [m/fp_to_meter] */ + + /* Register the new vertex against the heat path */ + #define REGISTER_HEAT_VERTEX { \ + res = register_heat_vertex(ctx->heat_path, &rwalk->vtx, T->value, \ + SDIS_HEAT_VERTEX_CONDUCTION, (int)ctx->nbranchings); \ + if(res != RES_OK) goto error; \ + } (void)0 + + /* The temperature is known */ + if(SDIS_TEMPERATURE_IS_KNOWN(props.temperature)) { + REGISTER_HEAT_VERTEX; + T->value += props.temperature; + T->done = 1; + break; + } + + /* Find the next position of the conductive path */ + res = XD(sample_next_position)(scn, rwalk, rng, &dst); + if(res != RES_OK) goto error; + + /* Going back in time */ + res = XD(time_travel)(scn, rwalk, rng, alpha, props.t0, &dst, T); + if(res != RES_OK) goto error; + + /* Add the volumic power density */ + res = XD(handle_volumic_power_wos)(scn, &props, dst, &power_term, T); + if(res != RES_OK) goto error; + + REGISTER_HEAT_VERTEX; + + /* Accumulate the power term */ + if(green) green_power_term += power_term; + + /* The path reaches the initial condition */ + if(T->done) { + T->func = NULL; + break; + } + + /* The path reaches a boundary */ + if(!SXD_HIT_NONE(&rwalk->hit)) { + T->func = XD(boundary_path); + rwalk->mdm = NULL; + break; + } + + #undef REGISTER_VERTEX + + /* Retreive and check solid properties at the new position */ + res = solid_get_properties(rwalk->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; + + ++ndiffusion_steps; /* For debug */ + } + + /* Save green function data */ + res = XD(update_green_path) + (ctx->green_path, rwalk, mdm, &props_ref, green_power_term, T); + +exit: + return res; +error: + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_heat_path_convective_Xd.h b/src/sdis_heat_path_convective_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -73,7 +73,7 @@ XD(register_heat_vertex_in_fluid) const double weight) { struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; - struct hit_filter_data filter_data; + 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]; @@ -119,7 +119,7 @@ XD(handle_known_fluid_temperature) temperature = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); /* Check if the temperature is known */ - known_temperature = temperature >= 0; + known_temperature = SDIS_TEMPERATURE_IS_KNOWN(temperature); if(!known_temperature) goto exit; T->value += temperature; @@ -210,14 +210,15 @@ XD(fetch_fluid_enclosure) /* Fetch the enclosure data */ enc = scene_get_enclosure(scn, enc_id); - if(!enc) { - /* The possibility for a fluid enclosure to be unregistred is that it is - * the external enclosure. In this situation unknown temperature is - * forbidden. */ + ASSERT(enc != NULL); + if(enc->medium_id == ENCLOSURE_MULTI_MEDIA) { + /* The enclosures with multiple media are used to describe limit + * conditions and therefore they cannot be fetched */ log_err(scn->dev, - "%s: invalid enclosure. The surrounding fluid has an unset temperature.\n", - FUNC_NAME); - res = RES_BAD_ARG; + "%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); + res = RES_BAD_ARG; goto error; } diff --git a/src/sdis_heat_path_radiative_Xd.h b/src/sdis_heat_path_radiative_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -20,6 +20,7 @@ #include "sdis_log.h" #include "sdis_medium_c.h" #include "sdis_misc.h" +#include "sdis_radiative_env_c.h" #include "sdis_scene_c.h" #include <star/ssp.h> @@ -55,7 +56,7 @@ XD(trace_radiative_path) /* Launch the radiative random walk */ for(;;) { const struct sdis_interface* interf = NULL; - struct hit_filter_data filter_data; + 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; double alpha; @@ -77,44 +78,52 @@ XD(trace_radiative_path) (scn->sXd(view), pos, dir, range, &filter_data, &rwalk->hit)); #endif if(SXD_HIT_NONE(&rwalk->hit)) { /* Fetch the ambient radiative temperature */ + struct sdis_radiative_ray ray = SDIS_RADIATIVE_RAY_NULL; + double trad = 0; /* [K] */ + rwalk->hit_side = SDIS_SIDE_NULL__; - if(scn->trad.temperature >= 0) { - T->value += scn->trad.temperature; - T->done = 1; - - if(ctx->green_path) { - res = green_path_set_limit_radiative - (ctx->green_path, rwalk->elapsed_time); - if(res != RES_OK) goto error; - } - if(ctx->heat_path) { - const float empirical_dst = 0.1f; - struct sdis_rwalk_vertex vtx; - - - vtx = rwalk->vtx; - vtx.P[0] += dir[0] * empirical_dst; - vtx.P[1] += dir[1] * empirical_dst; - vtx.P[2] += dir[2] * empirical_dst; - res = register_heat_vertex(ctx->heat_path, &vtx, T->value, - SDIS_HEAT_VERTEX_RADIATIVE, branch_id); - if(res != RES_OK) goto error; - } - break; - } else { + d3_set_f3(rwalk->dir, dir); + d3_normalize(rwalk->dir, rwalk->dir); + d3_set(ray.dir, rwalk->dir); + + trad = radiative_env_get_temperature(scn->radenv, &ray); + if(SDIS_TEMPERATURE_IS_UNKNOWN(trad)) { log_err(scn->dev, - "%s: the random walk reaches an invalid ambient radiative temperature " - "of `%gK' at position `%g %g %g'. This may be due to numerical " - "inaccuracies or to inconsistency in the simulated system (eg: " - "unclosed geometry). For systems where the random walks can reach " - "such temperature, one has to setup a valid ambient radiative " - "temperature, i.e. it must be greater or equal to 0.\n", - FUNC_NAME, - scn->trad.temperature, - SPLIT3(rwalk->vtx.P)); + "%s: the random walk has reached an invalid radiative environment from " + "position `%g %g %g' along direction `%g %g %g': the temperature is " + "unknown. This may be due to numerical inaccuracies or inconsistencies " + "in the simulated system (e.g. non-closed geometry). For systems where " + "random walks can reach such a temperature, we need to define a valid " + "radiative temperature, i.e. one with a known temperature.\n", + FUNC_NAME, SPLIT3(rwalk->vtx.P), SPLIT3(rwalk->dir)); res = RES_BAD_OP; goto error; } + + T->value += trad; + T->done = 1; + + if(ctx->green_path) { + res = green_path_set_limit_radiative_ray + (ctx->green_path, &ray, rwalk->elapsed_time); + if(res != RES_OK) goto error; + } + + if(ctx->heat_path) { + const float empirical_dst = 0.1f * (float)scn->fp_to_meter; + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; + + vtx = rwalk->vtx; + vtx.P[0] += dir[0] * empirical_dst; + vtx.P[1] += dir[1] * empirical_dst; + vtx.P[2] += dir[2] * empirical_dst; + res = register_heat_vertex(ctx->heat_path, &vtx, T->value, + SDIS_HEAT_VERTEX_RADIATIVE, branch_id); + if(res != RES_OK) goto error; + } + + /* Stop the radiative path */ + break; } /* Define the hit side */ @@ -134,7 +143,7 @@ XD(trace_radiative_path) XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit, rwalk->hit_side); /* Fetch the interface emissivity */ - epsilon = interface_side_get_emissivity(interf, &frag); + epsilon = interface_side_get_emissivity(interf, SDIS_INTERN_SOURCE_ID, &frag); if(epsilon > 1 || epsilon < 0) { log_err(scn->dev, "%s: invalid overall emissivity `%g' at position `%g %g %g'.\n", @@ -176,7 +185,7 @@ XD(trace_radiative_path) goto error; } } - alpha = interface_side_get_specular_fraction(interf, &frag); + alpha = interface_side_get_specular_fraction(interf, SDIS_INTERN_SOURCE_ID, &frag); r = ssp_rng_canonical(rng); if(r < alpha) { /* Sample specular part */ reflect_3d(dir, f3_minus(dir, dir), N); diff --git a/src/sdis_interface.c b/src/sdis_interface.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_interface_c.h b/src/sdis_interface_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -17,6 +17,8 @@ #define SDIS_INTERFACE_C_H #include "sdis.h" +#include "sdis_log.h" + #include <rsys/free_list.h> #include <rsys/ref_count.h> #include <float.h> @@ -118,7 +120,9 @@ interface_side_get_temperature case SDIS_BACK: shader = &interf->shader.back; break; default: FATAL("Unreachable code.\n"); } - return shader->temperature ? shader->temperature(frag, interf->data) : -1; + return shader->temperature + ? shader->temperature(frag, interf->data) + : SDIS_TEMPERATURE_NONE; } static INLINE double @@ -139,6 +143,7 @@ interface_side_get_flux static INLINE double interface_side_get_emissivity (const struct sdis_interface* interf, + const unsigned source_id, const struct sdis_interface_fragment* frag) { const struct sdis_interface_side_shader* shader; @@ -148,12 +153,15 @@ interface_side_get_emissivity case SDIS_BACK: shader = &interf->shader.back; break; default: FATAL("Unreachable code\n"); break; } - return shader->emissivity ? shader->emissivity(frag, interf->data) : 0; + return shader->emissivity + ? shader->emissivity(frag, source_id, interf->data) + : 0; } static INLINE double interface_side_get_specular_fraction (const struct sdis_interface* interf, + const unsigned source_id, const struct sdis_interface_fragment* frag) { const struct sdis_interface_side_shader* shader; @@ -164,7 +172,8 @@ interface_side_get_specular_fraction default: FATAL("Unreachable code\n"); break; } return shader->specular_fraction - ? shader->specular_fraction(frag, interf->data) : 0; + ? shader->specular_fraction(frag, source_id, interf->data) + : 0; } static INLINE double @@ -180,8 +189,80 @@ interface_side_get_reference_temperature default: FATAL("Unreachable code\n"); break; } return shader->reference_temperature - ? shader->reference_temperature(frag, interf->data) : -1; + ? shader->reference_temperature(frag, interf->data) + : SDIS_TEMPERATURE_NONE; } -#endif /* SDIS_INTERFACE_C_H */ +static INLINE int +interface_side_is_external_flux_handled + (const struct sdis_interface* interf, + const struct sdis_interface_fragment* frag) +{ + const struct sdis_interface_side_shader* shader; + ASSERT(interf && frag); + switch(frag->side) { + case SDIS_FRONT: shader = &interf->shader.front; break; + case SDIS_BACK: shader = &interf->shader.back; break; + default: FATAL("Unreachable code\n"); break; + } + return shader->handle_external_flux; +} + +/******************************************************************************* + * Check interface properties + ******************************************************************************/ +#define DEFINE_INTERF_CHK_PROP_FUNC(Interf, Prop, Low, Upp, LowIsInc, UppIsInc)\ + static INLINE res_T \ + Interf##_check_##Prop \ + (struct sdis_device* dev, \ + const double val, /* Value of the property */ \ + const double pos[3], /* Position at which the property was queried */ \ + const double time) /* Time at which the property was queried */ \ + { \ + const int low_test = LowIsInc ? Low <= val : Low < val; \ + const int upp_test = UppIsInc ? Upp >= val : Upp > val; \ + const char low_char = LowIsInc ? '[' : ']'; \ + const char upp_char = UppIsInc ? ']' : '['; \ + ASSERT(dev && pos); \ + \ + if(!low_test || !upp_test) { \ + log_err(dev, \ + "invalid "STR(Interf)" "PROP_STR(Prop)" '%g': " \ + "it must be in %c%g, %g%c -- position=%g, %g, %g; time=%g\n", \ + val, low_char, (double)Low, (double)Upp, upp_char, SPLIT3(pos), time); \ + return RES_BAD_ARG; \ + } \ + return RES_OK; \ + } +#define PROP_STR(Prop) CONCAT(PROP_STR_, Prop) +#define PROP_STR_convection_coef "convection coefficient" +#define PROP_STR_thermal_contact_resistance "thermal contact resistance" +#define PROP_STR_convection_coef_upper_bound "convection coefficient upper bound" +#define PROP_STR_temperature "temperature" +#define PROP_STR_flux "net flux" +#define PROP_STR_emissivity "emissivity" +#define PROP_STR_specular_fraction "specular fraction" +#define PROP_STR_reference_temperature "reference temperature" + +DEFINE_INTERF_CHK_PROP_FUNC(interface, convection_coef, 0, INF, 1, 1) +DEFINE_INTERF_CHK_PROP_FUNC(interface, thermal_contact_resistance, 0, INF, 1, 1) +DEFINE_INTERF_CHK_PROP_FUNC(interface, convection_coef_upper_bound, 0, INF, 1, 1) +DEFINE_INTERF_CHK_PROP_FUNC(interface_side, temperature, 0, INF, 1, 1) +DEFINE_INTERF_CHK_PROP_FUNC(interface_side, flux, -INF, INF, 1, 1) +DEFINE_INTERF_CHK_PROP_FUNC(interface_side, emissivity, 0, 1, 1, 1) +DEFINE_INTERF_CHK_PROP_FUNC(interface_side, specular_fraction, 0, 1, 1, 1) +DEFINE_INTERF_CHK_PROP_FUNC(interface_side, reference_temperature, 0, INF, 1, 1) + +#undef DEFINE_INTERF_CHK_PROP_FUNC +#undef PROP_STR +#undef PROP_STR_convection_coef +#undef PROP_STR_thermal_contact_resistance +#undef PROP_STR_convection_coef_upper_bound +#undef PROP_STR_temperature +#undef PROP_STR_flux +#undef PROP_STR_emissivity +#undef PROP_STR_specular_fraction +#undef PROP_STR_reference_temperature + +#endif /* SDIS_INTERFACE_C_H */ diff --git a/src/sdis_log.c b/src/sdis_log.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_log.h b/src/sdis_log.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_medium.c b/src/sdis_medium.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -23,26 +23,30 @@ /******************************************************************************* * Helper functions ******************************************************************************/ -static int +static res_T check_fluid_shader(const struct sdis_fluid_shader* shader) { - ASSERT(shader); - return shader->calorific_capacity - && shader->volumic_mass - && shader->temperature - && 0 <= shader->t0 && shader->t0 < INF; + if(!shader + || !shader->calorific_capacity + || !shader->volumic_mass + || !shader->temperature) + return RES_BAD_ARG; + + return RES_OK; } -static int +static res_T check_solid_shader(const struct sdis_solid_shader* shader) { - ASSERT(shader); - return shader->calorific_capacity - && shader->thermal_conductivity - && shader->volumic_mass - && shader->delta - && shader->temperature - && 0 <= shader->t0 && shader->t0 < INF; + if(!shader + || !shader->calorific_capacity + || !shader->thermal_conductivity + || !shader->volumic_mass + || !shader->delta + || !shader->temperature) + return RES_BAD_ARG; + + return RES_OK; } static res_T @@ -108,14 +112,11 @@ sdis_fluid_create struct sdis_medium* medium = NULL; res_T res = RES_OK; - if(!dev || !shader || !out_medium) { - res = RES_BAD_ARG; - goto error; - } + if(!dev || !out_medium) { res = RES_BAD_ARG; goto error; } - if(!check_fluid_shader(shader)) { + res = check_fluid_shader(shader); + if(res != RES_OK) { log_err(dev, "%s: invalid fluid shader.\n", FUNC_NAME); - res = RES_BAD_ARG; goto error; } @@ -162,14 +163,11 @@ sdis_solid_create struct sdis_medium* medium = NULL; res_T res = RES_OK; - if(!dev || !shader || !out_medium) { - res = RES_BAD_ARG; - goto error; - } + if(!dev || !out_medium) { res = RES_BAD_ARG; goto error; } - if(!check_solid_shader(shader)) { + res = check_solid_shader(shader); + if(res != RES_OK) { log_err(dev, "%s: invalid solid shader.\n", FUNC_NAME); - res = RES_BAD_ARG; goto error; } diff --git a/src/sdis_medium_c.h b/src/sdis_medium_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -109,17 +109,7 @@ static const struct solid_props SOLID_PROPS_NULL = SOLID_PROPS_NULL__; ******************************************************************************/ DEFINE_MDM_CHK_PROP_FUNC(fluid, calorific_capacity, 0, INF, 0, 1) DEFINE_MDM_CHK_PROP_FUNC(fluid, volumic_mass, 0, INF, 0, 1) -DEFINE_MDM_CHK_PROP_FUNC(fluid, temperature, 0, INF, 1, 1) - -static INLINE res_T -fluid_check_t0(struct sdis_device* dev, const double t0) -{ - if(t0 < 0) { - log_err(dev, "invalid negative initial time '%g'.\n", t0); - return RES_BAD_ARG; - } - return RES_OK; -} +DEFINE_MDM_CHK_PROP_FUNC(fluid, temperature, -INF, INF, 1, 1) DEFINE_MDM_GET_PROP_FUNC(fluid, calorific_capacity) DEFINE_MDM_GET_PROP_FUNC(fluid, volumic_mass) @@ -151,12 +141,6 @@ fluid_check_properties CHK_PROP(calorific_capacity, props->cp); #undef CHK_PROP - /* Do not check the temperature. An invalid temperature means that the - * temperature is unknown */ - - res = fluid_check_t0(dev, props->t0); - if(res != RES_OK) return res; - return RES_OK; } @@ -181,17 +165,7 @@ DEFINE_MDM_CHK_PROP_FUNC(solid, thermal_conductivity, 0, INF, 0, 1) DEFINE_MDM_CHK_PROP_FUNC(solid, volumic_mass, 0, INF, 0, 1) DEFINE_MDM_CHK_PROP_FUNC(solid, delta, 0, INF, 0, 1) DEFINE_MDM_CHK_PROP_FUNC(solid, volumic_power, -INF, INF, 1, 1) -DEFINE_MDM_CHK_PROP_FUNC(solid, temperature, 0, INF, 1, 1) - -static INLINE res_T -solid_check_t0(struct sdis_device* dev, const double t0) -{ - if(t0 < 0) { - log_err(dev, "invalid negative initial time '%g'.\n", t0); - return RES_BAD_ARG; - } - return RES_OK; -} +DEFINE_MDM_CHK_PROP_FUNC(solid, temperature, -INF, INF, 1, 1) DEFINE_MDM_GET_PROP_FUNC(solid, calorific_capacity) DEFINE_MDM_GET_PROP_FUNC(solid, thermal_conductivity) @@ -214,7 +188,6 @@ static INLINE double solid_get_t0(const struct sdis_medium* mdm) { ASSERT(mdm && mdm->type == SDIS_SOLID); - ASSERT(0 <= mdm->shader.solid.t0 && mdm->shader.solid.t0 < INF); return mdm->shader.solid.t0; } @@ -242,9 +215,6 @@ solid_check_properties /* Do not check the temperature. An invalid temperature means that the * temperature is unknown */ - res = solid_check_t0(dev, props->t0); - if(res != RES_OK) return res; - return RES_OK; } diff --git a/src/sdis_misc.c b/src/sdis_misc.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_misc.h b/src/sdis_misc.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_misc_Xd.h b/src/sdis_misc_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -54,7 +54,7 @@ XD(time_rewind) /* Fetch the initial temperature */ temperature = medium_get_temperature(rwalk->mdm, &rwalk->vtx); - if(temperature < 0) { + 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)), diff --git a/src/sdis_mpi.c b/src/sdis_mpi.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_mpi.h b/src/sdis_mpi.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_radiative_env.c b/src/sdis_radiative_env.c @@ -0,0 +1,108 @@ +/* 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_device_c.h" +#include "sdis_log.h" +#include "sdis_radiative_env_c.h" + +#include <rsys/mem_allocator.h> + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +radiative_env_release(ref_T* ref) +{ + struct sdis_radiative_env* radenv = NULL; + struct sdis_device* dev = NULL; + ASSERT(ref); + radenv = CONTAINER_OF(ref, struct sdis_radiative_env, ref); + dev = radenv->dev; + if(radenv->data) SDIS(data_ref_put(radenv->data)); + MEM_RM(dev->allocator, radenv); + SDIS(device_ref_put(dev)); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +sdis_radiative_env_create + (struct sdis_device* dev, + const struct sdis_radiative_env_shader* shader, + struct sdis_data* data, /* Data sent to the shader. May be NULL */ + struct sdis_radiative_env** out_radenv) +{ + struct sdis_radiative_env* radenv = NULL; + res_T res = RES_OK; + + if(!dev || !shader || !out_radenv) { + res = RES_BAD_ARG; + goto error; + } + + radenv = MEM_CALLOC(dev->allocator, 1, sizeof(*radenv)); + if(!radenv) { + res = RES_MEM_ERR; + goto error; + } + ref_init(&radenv->ref); + SDIS(device_ref_get(dev)); + radenv->dev = dev; + radenv->shader = *shader; + if(data) { + SDIS(data_ref_get(data)); + radenv->data = data; + } + +exit: + if(out_radenv) *out_radenv = radenv; + return res; +error: + goto exit; +} + +res_T +sdis_radiative_env_ref_get(struct sdis_radiative_env* radenv) +{ + if(!radenv) return RES_BAD_ARG; + ref_get(&radenv->ref); + return RES_OK; +} + +res_T +sdis_radiative_env_ref_put(struct sdis_radiative_env* radenv) +{ + if(!radenv) return RES_BAD_ARG; + ref_put(&radenv->ref, radiative_env_release); + return RES_OK; +} + +res_T +sdis_radiative_env_get_shader + (struct sdis_radiative_env* radenv, + struct sdis_radiative_env_shader* shader) +{ + if(!radenv || !shader) return RES_BAD_ARG; + *shader = radenv->shader; + return RES_OK; +} + +struct sdis_data* +sdis_radiative_env_get_data(struct sdis_radiative_env* radenv) +{ + ASSERT(radenv); + return radenv->data; +} diff --git a/src/sdis_radiative_env_c.h b/src/sdis_radiative_env_c.h @@ -0,0 +1,52 @@ +/* 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/>. */ + +#ifndef SDIS_RADIATIVE_ENV_C_H +#define SDIS_RADIATIVE_ENV_C_H + +#include "sdis.h" +#include <rsys/ref_count.h> + +struct sdis_radiative_env { + struct sdis_radiative_env_shader shader; + struct sdis_data* data; + + ref_T ref; + struct sdis_device* dev; +}; + +static INLINE double +radiative_env_get_temperature + (const struct sdis_radiative_env* radenv, + const struct sdis_radiative_ray* ray) +{ + ASSERT(ray && d3_is_normalized(ray->dir)); + return radenv && radenv->shader.temperature + ? radenv->shader.temperature(ray, radenv->data) + : SDIS_TEMPERATURE_NONE; +} + +static INLINE double +radiative_env_get_reference_temperature + (const struct sdis_radiative_env* radenv, + const struct sdis_radiative_ray* ray) +{ + ASSERT(ray && d3_is_normalized(ray->dir)); + return radenv && radenv->shader.reference_temperature + ? radenv->shader.reference_temperature(ray, radenv->data) + : SDIS_TEMPERATURE_NONE; +} + +#endif /* SDIS_RADIATIVE_ENV_C_H */ diff --git a/src/sdis_realisation.c b/src/sdis_realisation.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -20,14 +20,15 @@ * Helper functions ******************************************************************************/ static INLINE int -check_ray_realisatio_args(const struct ray_realisation_args* args) +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; + && args->picard_order > 0 + && (unsigned)args->diff_algo < SDIS_DIFFUSION_ALGORITHMS_COUNT__; } /******************************************************************************* @@ -50,7 +51,7 @@ ray_realisation_3d struct temperature_3d T = TEMPERATURE_NULL_3d; float dir[3]; res_T res = RES_OK; - ASSERT(scn && weight && check_ray_realisatio_args(args)); + ASSERT(scn && weight && check_ray_realisation_args(args)); d3_set(rwalk.vtx.P, args->position); rwalk.vtx.time = args->time; @@ -66,6 +67,8 @@ ray_realisation_3d ctx.That2 = ctx.That * ctx.That; ctx.That3 = ctx.That * ctx.That2; ctx.max_branchings = args->picard_order - 1; + ctx.irealisation = args->irealisation; + ctx.diff_algo = args->diff_algo; f3_set_d3(dir, args->direction); @@ -78,7 +81,7 @@ ray_realisation_3d if(res != RES_OK) goto error; if(!T.done) { - res = compute_temperature_3d(scn, &ctx, &rwalk, args->rng, &T); + res = sample_coupled_path_3d(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) goto error; } diff --git a/src/sdis_realisation.h b/src/sdis_realisation.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -35,10 +35,10 @@ enum flux_flag { }; /******************************************************************************* - * Helper function used to compute a temperature + * Helper function used to sample a coupled path ******************************************************************************/ extern LOCAL_SYM res_T -compute_temperature_2d +sample_coupled_path_2d (struct sdis_scene* scn, struct rwalk_context* ctx, struct rwalk_2d* rwalk, @@ -46,7 +46,7 @@ compute_temperature_2d struct temperature_2d* T); extern LOCAL_SYM res_T -compute_temperature_3d +sample_coupled_path_3d (struct sdis_scene* scn, struct rwalk_context* ctx, struct rwalk_3d* rwalk, @@ -65,9 +65,18 @@ struct probe_realisation_args { struct green_path_handle* green_path; /* May be NULL */ struct sdis_heat_path* heat_path; /* May be NULL */ size_t irealisation; /* Id of the realisation (for debug) */ + enum sdis_diffusion_algorithm diff_algo; /* Diffusion algorithm to be used */ }; #define PROBE_REALISATION_ARGS_NULL__ { \ - NULL, NULL, {0,0,0}, -1, 0, NULL, NULL, 0 \ + NULL, /* RNG */ \ + NULL, /* Medium */ \ + {0,0,0}, /* Position */ \ + -1, /* Observation time */ \ + 0, /* Picard order */ \ + NULL, /* Green path */ \ + NULL, /* Heat path */ \ + SIZE_MAX, /* Realisation ID */ \ + SDIS_DIFFUSION_NONE /* Diffusion algorithm */ \ } static const struct probe_realisation_args PROBE_REALISATION_ARGS_NULL = PROBE_REALISATION_ARGS_NULL__; @@ -96,9 +105,20 @@ struct boundary_realisation_args { enum sdis_side side; /* Side of the geometric primitive */ struct green_path_handle* green_path; /* May be NULL */ struct sdis_heat_path* heat_path; /* May be NULL */ + size_t irealisation; /* Id of the realisation (for debug) */ + enum sdis_diffusion_algorithm diff_algo; /* Diffusion algorithm to be used */ }; #define BOUNDARY_REALISATION_ARGS_NULL__ { \ - NULL, SIZE_MAX, {0,0}, -1, 0, SDIS_SIDE_NULL__, NULL, NULL \ + NULL, /* RNG */ \ + SIZE_MAX, /* Primitive ID */ \ + {0,0}, /* Parametric coordinates */ \ + -1, /* Observation time */ \ + 0, /* Picard order */ \ + SDIS_SIDE_NULL__, /* Interface side */ \ + NULL, /* Green path */ \ + NULL, /* Heat path */ \ + SIZE_MAX, /* Realisation ID */ \ + SDIS_DIFFUSION_NONE /* Diffusion algorithm */ \ } static const struct boundary_realisation_args BOUNDARY_REALISATION_ARGS_NULL = BOUNDARY_REALISATION_ARGS_NULL__; @@ -126,9 +146,19 @@ struct boundary_flux_realisation_args { size_t picard_order; /* Picard order to estimate radiative temperature */ enum sdis_side solid_side; /* Side of the geometric primitive */ int flux_mask; /* Combination of enum flux_flag */ + size_t irealisation; /* Id of the realisation (for debug) */ + enum sdis_diffusion_algorithm diff_algo; /* Diffusion algorithm to be used */ }; #define BOUNDARY_FLUX_REALISATION_ARGS_NULL__ { \ - NULL, SIZE_MAX, {0,0}, -1, 0, SDIS_SIDE_NULL__, 0 \ + NULL, /* RNG */ \ + SIZE_MAX, /* Primitive ID */ \ + {0,0}, /* Parametric coordinates */ \ + -1, /* Observation time */ \ + 0, /* Picard order */ \ + SDIS_SIDE_NULL__, /* Interface side */ \ + 0, /* Flux mask */ \ + SIZE_MAX, /* Realisation ID */ \ + SDIS_DIFFUSION_NONE /* Diffusion algorithm */ \ } static const struct boundary_flux_realisation_args BOUNDARY_FLUX_REALISATION_ARGS_NULL = BOUNDARY_FLUX_REALISATION_ARGS_NULL__; @@ -146,7 +176,7 @@ boundary_flux_realisation_3d struct bound_flux_result* result); /******************************************************************************* - * Realisation along a given ray at a given time. Available only in 3D. + * Realisation along a given ray at a given time. Available only in 3D ******************************************************************************/ struct ray_realisation_args { struct ssp_rng* rng; @@ -156,9 +186,19 @@ struct ray_realisation_args { double time; /* Observation time */ size_t picard_order; /* Picard order to estimate radiative temperature */ struct sdis_heat_path* heat_path; /* May be NULL */ + size_t irealisation; /* Id of the realisation (for debug) */ + enum sdis_diffusion_algorithm diff_algo; /* Diffusion algorithm to be used */ }; #define RAY_REALISATION_ARGS_NULL__ { \ - NULL, NULL, {0,0,0}, {0,0,0}, -1, 0, NULL \ + NULL, /* RNG */ \ + NULL, /* Medium */ \ + {0,0,0}, /* Position */ \ + {0,0,0}, /* Direction */ \ + -1, /* Observation time */ \ + 0, /* Picard order */ \ + NULL, /* Heat path */ \ + SIZE_MAX, /* Realisation ID */ \ + SDIS_DIFFUSION_NONE /* Diffusion algorithm */ \ } static const struct ray_realisation_args RAY_REALISATION_ARGS_NULL = RAY_REALISATION_ARGS_NULL__; diff --git a/src/sdis_realisation_Xd.h b/src/sdis_realisation_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -39,7 +39,8 @@ check_probe_realisation_args(const struct probe_realisation_args* args) && args->rng && args->medium && args->time >= 0 - && args->picard_order > 0; + && args->picard_order > 0 + && (unsigned)args->diff_algo < SDIS_DIFFUSION_ALGORITHMS_COUNT__; } static INLINE int @@ -53,7 +54,8 @@ check_boundary_realisation_args(const struct boundary_realisation_args* args) && args->uv[1] <= 1 && args->time >= 0 && args->picard_order > 0 - && (args->side == SDIS_FRONT || args->side == SDIS_BACK); + && (args->side == SDIS_FRONT || args->side == SDIS_BACK) + && (unsigned)args->diff_algo < SDIS_DIFFUSION_ALGORITHMS_COUNT__; } static INLINE int @@ -68,7 +70,8 @@ check_boundary_flux_realisation_args && args->uv[1] <= 1 && args->time >= 0 && args->picard_order > 0 - && (args->solid_side == SDIS_FRONT || args->solid_side == SDIS_BACK); + && (args->solid_side == SDIS_FRONT || args->solid_side == SDIS_BACK) + && (unsigned)args->diff_algo < SDIS_DIFFUSION_ALGORITHMS_COUNT__; } #endif /* SDIS_REALISATION_XD_H */ @@ -76,7 +79,7 @@ check_boundary_flux_realisation_args * Local functions ******************************************************************************/ res_T -XD(compute_temperature) +XD(sample_coupled_path) (struct sdis_scene* scn, struct rwalk_context* ctx, struct XD(rwalk)* rwalk, @@ -123,7 +126,13 @@ XD(compute_temperature) res = T->func(scn, ctx, rwalk, rng, T); if(res == RES_BAD_OP) { *rwalk = rwalk_bkp; *T = T_bkp; } } while(res == RES_BAD_OP && ++nfails < MAX_FAILS); - if(res != RES_OK) goto error; + if(res != RES_OK) { + log_err(scn->dev, "%s: reject path (realisation: %lu; branch: %lu)\n", + FUNC_NAME, + (unsigned long)ctx->irealisation, + (unsigned long)ctx->nbranchings); + goto error; + } /* Update the type of the first vertex of the random walks that begin on a * boundary. Indeed, one knows the "right" type of the first vertex only @@ -202,7 +211,7 @@ XD(probe_realisation) /* Check the initial condition. */ rwalk.vtx.time = t0; tmp = get_initial_temperature(args->medium, &rwalk.vtx); - if(tmp >= 0) { + if(SDIS_TEMPERATURE_IS_KNOWN(tmp)) { *weight = tmp; goto exit; } @@ -227,11 +236,13 @@ XD(probe_realisation) ctx.That2 = ctx.That * ctx.That; ctx.That3 = ctx.That * ctx.That2; ctx.max_branchings = args->picard_order - 1; + ctx.irealisation = args->irealisation; + ctx.diff_algo = args->diff_algo; - res = XD(compute_temperature)(scn, &ctx, &rwalk, args->rng, &T); + res = XD(sample_coupled_path)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) goto error; - ASSERT(T.value >= 0); + ASSERT(SDIS_TEMPERATURE_IS_KNOWN(T.value)); *weight = T.value; exit: @@ -301,8 +312,10 @@ XD(boundary_realisation) ctx.That2 = ctx.That * ctx.That; ctx.That3 = ctx.That * ctx.That2; ctx.max_branchings = args->picard_order - 1; + ctx.irealisation = args->irealisation; + ctx.diff_algo = args->diff_algo; - res = XD(compute_temperature)(scn, &ctx, &rwalk, args->rng, &T); + res = XD(sample_coupled_path)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) goto error; *weight = T.value; @@ -387,6 +400,8 @@ XD(boundary_flux_realisation) ctx.That2 = That2; \ ctx.That3 = That3; \ ctx.max_branchings = args->picard_order - 1; \ + 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); \ @@ -395,7 +410,7 @@ XD(boundary_flux_realisation) /* Compute boundary temperature */ RESET_WALK(args->solid_side, NULL); T.func = XD(boundary_path); - res = XD(compute_temperature)(scn, &ctx, &rwalk, args->rng, &T); + res = XD(sample_coupled_path)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) return res; result->Tboundary = T.value; @@ -407,9 +422,9 @@ XD(boundary_flux_realisation) if(compute_radiative) { RESET_WALK(fluid_side, fluid_mdm); T.func = XD(radiative_path); - res = XD(compute_temperature)(scn, &ctx, &rwalk, args->rng, &T); + res = XD(sample_coupled_path)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) return res; - ASSERT(T.value >= 0); + ASSERT(SDIS_TEMPERATURE_IS_KNOWN(T.value)); result->Tradiative = T.value; } @@ -417,7 +432,7 @@ XD(boundary_flux_realisation) if(compute_convective) { RESET_WALK(fluid_side, fluid_mdm); T.func = XD(convective_path); - res = XD(compute_temperature)(scn, &ctx, &rwalk, args->rng, &T); + res = XD(sample_coupled_path)(scn, &ctx, &rwalk, args->rng, &T); if(res != RES_OK) return res; result->Tfluid = T.value; } diff --git a/src/sdis_scene.c b/src/sdis_scene.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -13,21 +13,20 @@ * 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_scene_Xd.h" - -/* Generate the Generic functions of the scene */ -#define SDIS_SCENE_DIMENSION 2 -#include "sdis_scene_Xd.h" -#define SDIS_SCENE_DIMENSION 3 -#include "sdis_scene_Xd.h" - #include "sdis.h" #include "sdis_interface_c.h" #include "sdis_scene_c.h" +#include "sdis_source_c.h" #include <float.h> #include <limits.h> +/* Generate the Generic functions of the scene */ +#define SDIS_XD_DIMENSION 2 +#include "sdis_scene_Xd.h" +#define SDIS_XD_DIMENSION 3 +#include "sdis_scene_Xd.h" + /******************************************************************************* * Helper function ******************************************************************************/ @@ -108,6 +107,8 @@ scene_release(ref_T * ref) if(scn->s3d_view) S3D(scene_view_ref_put(scn->s3d_view)); if(scn->senc2d_scn) SENC2D(scene_ref_put(scn->senc2d_scn)); if(scn->senc3d_scn) SENC3D(scene_ref_put(scn->senc3d_scn)); + if(scn->source) SDIS(source_ref_put(scn->source)); + if(scn->radenv) SDIS(radiative_env_ref_put(scn->radenv)); MEM_RM(dev->allocator, scn); SDIS(device_ref_put(dev)); } @@ -194,26 +195,6 @@ sdis_scene_set_fp_to_meter } res_T -sdis_scene_get_ambient_radiative_temperature - (const struct sdis_scene* scn, - struct sdis_ambient_radiative_temperature* trad) -{ - if(!scn || !trad) return RES_BAD_ARG; - *trad = scn->trad; - return RES_OK; -} - -res_T -sdis_scene_set_ambient_radiative_temperature - (struct sdis_scene* scn, - const struct sdis_ambient_radiative_temperature* trad) -{ - if(!scn) return RES_BAD_ARG; - scn->trad = *trad; - return RES_OK; -} - -res_T sdis_scene_get_temperature_range (const struct sdis_scene* scn, double t_range[2]) @@ -238,16 +219,15 @@ sdis_scene_set_temperature_range res_T sdis_scene_find_closest_point (const struct sdis_scene* scn, - const double pos[], - const double radius, + const struct sdis_scene_find_closest_point_args* args, size_t* iprim, double uv[]) { if(!scn) return RES_BAD_ARG; if(scene_is_2d(scn)) { - return scene_find_closest_point_2d(scn, pos, radius, iprim, uv); + return scene_find_closest_point_2d(scn, args, iprim, uv); } else { - return scene_find_closest_point_3d(scn, pos, radius, iprim, uv); + return scene_find_closest_point_3d(scn, args, iprim, uv); } } @@ -410,6 +390,32 @@ error: goto exit; } +res_T +sdis_scene_get_device(struct sdis_scene* scn, struct sdis_device** device) +{ + if(!scn || !device) return RES_BAD_ARG; + *device = scn->dev; + return RES_OK; +} + +res_T +sdis_scene_get_source(struct sdis_scene* scn, struct sdis_source** source) +{ + if(!scn || !source) return RES_BAD_ARG; + *source = scn->source; + return RES_OK; +} + +res_T +sdis_scene_get_radiative_env + (struct sdis_scene* scn, + struct sdis_radiative_env** radenv) +{ + if(!scn || !radenv) return RES_BAD_ARG; + *radenv = scn->radenv; + return RES_OK; +} + /******************************************************************************* * Local miscellaneous function ******************************************************************************/ @@ -422,7 +428,7 @@ scene_get_interface(const struct sdis_scene* scn, const unsigned iprim) res_T scene_get_medium - (const struct sdis_scene* scn, + (struct sdis_scene* scn, const double pos[], struct get_medium_info* info, struct sdis_medium** out_medium) @@ -434,7 +440,7 @@ scene_get_medium res_T scene_get_medium_in_closed_boundaries - (const struct sdis_scene* scn, + (struct sdis_scene* scn, const double pos[], struct sdis_medium** out_medium) { @@ -448,6 +454,7 @@ scene_compute_hash(const struct sdis_scene* scn, hash256_T hash) { struct sha256_ctx sha256_ctx; size_t iprim, nprims; + int has_radenv = 0; res_T res = RES_OK; ASSERT(scn && hash); @@ -461,9 +468,18 @@ scene_compute_hash(const struct sdis_scene* scn, hash256_T hash) #define SHA256_UPD(Var, Nb) \ sha256_ctx_update(&sha256_ctx, (const char*)(Var), sizeof(*Var)*(Nb)) - SHA256_UPD(&scn->trad.reference, 1); + has_radenv = scn->radenv != NULL; + + SHA256_UPD(&has_radenv, 1); SHA256_UPD(&scn->tmax, 1); SHA256_UPD(&scn->fp_to_meter, 1); + + if(scn->source) { + hash256_T src_hash; + source_compute_signature(scn->source, src_hash); + sha256_ctx_update(&sha256_ctx, src_hash, sizeof(hash256_T)); + } + FOR_EACH(iprim, 0, nprims) { struct sdis_interface* interf = NULL; size_t ivert; @@ -557,3 +573,40 @@ error: goto exit; } +res_T +scene_check_temperature_range(const struct sdis_scene* scn) +{ + res_T res = RES_OK; + ASSERT(scn); + + if(SDIS_TEMPERATURE_IS_UNKNOWN(scn->tmin)) { + log_err(scn->dev, + "%s the defined minimum temperature is unknown " + "when it is expected to be known.\n", + FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + + if(SDIS_TEMPERATURE_IS_UNKNOWN(scn->tmax)) { + log_err(scn->dev, + "%s the defined maximum temperature is unknown " + "when it is expected to be known.\n", + FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + + if(scn->tmin > scn->tmax) { + log_err(scn->dev, + "%s: defined temperature range degenerated -- [%g, %g] K\n", + FUNC_NAME, scn->tmin, scn->tmax); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + goto exit; +} diff --git a/src/sdis_scene_Xd.h b/src/sdis_scene_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -13,8 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef SDIS_SCENE_DIMENSION - #ifndef SDIS_SCENE_XD_H #define SDIS_SCENE_XD_H @@ -123,8 +121,20 @@ check_sdis_scene_create_args(const struct sdis_scene_create_args* args) && args->fp_to_meter > 0; } +static INLINE res_T +check_sdis_scene_find_closest_point_args + (const struct sdis_scene_find_closest_point_args* args) +{ + /* Undefined input arguments */ + if(!args) return RES_BAD_ARG; + + /* Invalid radius */ + if(args->radius <= 0) return RES_BAD_ARG; + + return RES_OK; +} + #endif /* SDIS_SCENE_XD_H */ -#else /* !SDIS_SCENE_DIMENSION */ #include "sdis_device_c.h" @@ -137,39 +147,18 @@ check_sdis_scene_create_args(const struct sdis_scene_create_args* args) #include <limits.h> /* Check the submitted dimension and include its specific headers */ -#define SENCXD_DIM SDIS_SCENE_DIMENSION -#if (SDIS_SCENE_DIMENSION == 2) +#define SENCXD_DIM SDIS_XD_DIMENSION +#if (SDIS_XD_DIMENSION == 2) #include <star/sencX2d.h> #include <star/s2d.h> -#elif (SDIS_SCENE_DIMENSION == 3) +#elif (SDIS_XD_DIMENSION == 3) #include <star/sencX3d.h> #include <star/s3d.h> #else - #error "Invalid SDIS_SCENE_DIMENSION value." + #error "Invalid SDIS_XD_DIMENSION value." #endif -/* Syntactic sugar */ -#define DIM SDIS_SCENE_DIMENSION - -/* Star-XD macros generic to SDIS_SCENE_DIMENSION */ -#define sXd(Name) CONCAT(CONCAT(CONCAT(s, DIM), d_), Name) -#define SXD CONCAT(CONCAT(S, DIM), D) -#define SXD_VERTEX_DATA_NULL CONCAT(CONCAT(S,DIM),D_VERTEX_DATA_NULL) -#define SXD_POSITION CONCAT(CONCAT(S, DIM), D_POSITION) -#define SXD_TRACE CONCAT(CONCAT(S,DIM), D_TRACE) -#define SXD_SAMPLE CONCAT(CONCAT(S,DIM), D_SAMPLE) -#define SXD_GET_PRIMITIVE CONCAT(CONCAT(S,DIM), D_GET_PRIMITIVE) -#define SXD_HIT_NONE CONCAT(CONCAT(S,DIM), D_HIT_NONE) -#define SXD_PRIMITIVE_EQ CONCAT(CONCAT(S,DIM), D_PRIMITIVE_EQ) -#define SXD_FLOATX CONCAT(CONCAT(CONCAT(S,DIM), D_FLOAT), DIM) - -/* Vector macros generic to SDIS_SCENE_DIMENSION */ -#define fX(Func) CONCAT(CONCAT(CONCAT(f, DIM), _), Func) -#define fX_set_dX CONCAT(CONCAT(CONCAT(f, DIM), _set_d), DIM) -#define fXX_mulfX CONCAT(CONCAT(CONCAT(CONCAT(f, DIM), DIM), _mulf), DIM) - -/* Macro making generic its subimitted name to SDIS_SCENE_DIMENSION */ -#define XD(Name) CONCAT(CONCAT(CONCAT(Name, _), DIM), d) +#include "sdis_Xd_begin.h" #if DIM == 2 #define HIT_ON_BOUNDARY hit_on_vertex @@ -339,21 +328,17 @@ static int hit_shared_edge (const struct s3d_primitive* tri0, const struct s3d_primitive* tri1, + const float uv0[2], /* Barycentric coordinates of tested position on tri0 */ + const float uv1[2], /* Barycentric coordinates of tested position on tri1 */ const float pos0[3], /* Tested position onto the triangle 0 */ const float pos1[3]) /* Tested Position onto the triangle 1 */ { struct s3d_attrib tri0_vertices[3]; /* Vertex positions of the triangle 0 */ struct s3d_attrib tri1_vertices[3]; /* Vertex positions of the triangle 1 */ - float E0[3], E1[3]; /* Temporary variables storing triangle edges */ - float N0[3], N1[3]; /* Temporary Normals */ - float tri0_2area, tri1_2area; /* 2*area of the submitted triangles */ - float tmp0_2area, tmp1_2area; - float cos_normals; int tri0_edge[2] = {-1, -1}; /* Shared edge vertex ids for the triangle 0 */ int tri1_edge[2] = {-1, -1}; /* Shared edge vertex ids for the triangle 1 */ int edge_ivertex = 0; /* Temporary variable */ int tri0_ivertex, tri1_ivertex; - int iv0, iv1, iv2; ASSERT(tri0 && tri1 && pos0 && pos1); /* Fetch the vertices of the triangle 0 */ @@ -385,55 +370,102 @@ hit_shared_edge }} /* The triangles do not have a common edge */ - if(edge_ivertex < 2) return 0; - - /* Ensure that the vertices of the shared edge are registered in the right - * order regarding the triangle vertices, i.e. (0,1), (1,2) or (2,0) */ - if((tri0_edge[0]+1)%3 != tri0_edge[1]) SWAP(int, tri0_edge[0], tri0_edge[1]); - if((tri1_edge[0]+1)%3 != tri1_edge[1]) SWAP(int, tri1_edge[0], tri1_edge[1]); - - /* Compute the shared edge normal lying in the triangle 0 plane */ - iv0 = tri0_edge[0]; - iv1 = tri0_edge[1]; - iv2 = (tri0_edge[1]+1) % 3; - f3_sub(E0, tri0_vertices[iv1].value, tri0_vertices[iv0].value); - f3_sub(E1, tri0_vertices[iv2].value, tri0_vertices[iv0].value); - f3_cross(N0, E0, E1); /* Triangle 0 normal */ - tri0_2area = f3_len(N0); - f3_cross(N0, N0, E0); - - /* Compute the shared edge normal lying in the triangle 1 plane */ - iv0 = tri1_edge[0]; - iv1 = tri1_edge[1]; - iv2 = (tri1_edge[1]+1) % 3; - f3_sub(E0, tri1_vertices[iv1].value, tri1_vertices[iv0].value); - f3_sub(E1, tri1_vertices[iv2].value, tri1_vertices[iv0].value); - f3_cross(N1, E0, E1); - tri1_2area = f3_len(N1); - f3_cross(N1, N1, E0); - - /* Compute the cosine between the 2 edge normals */ - f3_normalize(N0, N0); - f3_normalize(N1, N1); - cos_normals = f3_dot(N0, N1); - - /* The angle formed by the 2 triangles is sharp */ - if(cos_normals > SHARP_ANGLE_COS_THRESOLD) return 0; + if(edge_ivertex == 0) { + return 0; + + /* The triangles have a common vertex */ + } else if(edge_ivertex == 1) { + float bcoord0, bcoord1; + int hit_vertex; + + /* Retrieve the barycentric coordinate of the position on triangle 0 + * corresponding to the vertex shared between the 2 triangles. */ + switch(tri0_edge[0]) { + case 0: bcoord0 = uv0[0]; break; + case 1: bcoord0 = uv0[1]; break; + case 2: bcoord0 = CLAMP(1.f - uv0[0] - uv0[1], 0.f, 1.f); break; + default: FATAL("Unreachable code\n"); break; + } - /* Compute the 2 times the area of the (pos0, shared_edge.vertex0, - * shared_edge.vertex1) triangles */ - f3_sub(E0, tri0_vertices[tri0_edge[0]].value, pos0); - f3_sub(E1, tri0_vertices[tri0_edge[1]].value, pos0); - tmp0_2area = f3_len(f3_cross(N0, E0, E1)); + /* Retrieve the barycentric coordinate of the position on triangle 1 + * corresponding to the vertex shared between the 2 triangles. */ + switch(tri1_edge[0]) { + case 0: bcoord1 = uv1[0]; break; + case 1: bcoord1 = uv1[1]; break; + case 2: bcoord1 = CLAMP(1.f - uv0[0] - uv0[1], 0.f, 1.f); break; + default: FATAL("Unreachable code\n"); break; + } - /* Compute the 2 times the area of the (pos1, shared_edge.vertex0, - * shared_edge.vertex1) triangles */ - f3_sub(E0, tri1_vertices[tri1_edge[0]].value, pos1); - f3_sub(E1, tri1_vertices[tri1_edge[1]].value, pos1); - tmp1_2area = f3_len(f3_cross(N1, E0, E1)); + /* Check that the both positions lie on the shared vertex */ + hit_vertex = eq_epsf(1.f, bcoord0, ON_VERTEX_EPSILON) + && eq_epsf(1.f, bcoord1, ON_VERTEX_EPSILON); + return hit_vertex; - return (eq_epsf(tri0_2area, 0, 1.e-6f) || tmp0_2area/tri0_2area < ON_EDGE_EPSILON) - && (eq_epsf(tri1_2area, 0, 1.e-6f) || tmp1_2area/tri1_2area < ON_EDGE_EPSILON); + /* The triangles have a common edge */ + } else { + float E0[3], E1[3]; /* Temporary variables storing triangle edges */ + float N0[3], N1[3]; /* Temporary Normals */ + float tri0_2area, tri1_2area; /* 2*area of the submitted triangles */ + float tmp0_2area, tmp1_2area; + float cos_normals; + int iv0, iv1, iv2; + int hit_edge; + + /* Ensure that the vertices of the shared edge are registered in the right + * order regarding the triangle vertices, i.e. (0,1), (1,2) or (2,0) */ + if((tri0_edge[0]+1)%3 != tri0_edge[1]) SWAP(int, tri0_edge[0], tri0_edge[1]); + if((tri1_edge[0]+1)%3 != tri1_edge[1]) SWAP(int, tri1_edge[0], tri1_edge[1]); + + /* Compute the shared edge normal lying in the triangle 0 plane */ + iv0 = tri0_edge[0]; + iv1 = tri0_edge[1]; + iv2 = (tri0_edge[1]+1) % 3; + f3_sub(E0, tri0_vertices[iv1].value, tri0_vertices[iv0].value); + f3_sub(E1, tri0_vertices[iv2].value, tri0_vertices[iv0].value); + f3_cross(N0, E0, E1); /* Triangle 0 normal */ + tri0_2area = f3_len(N0); + f3_cross(N0, N0, E0); + + /* Compute the shared edge normal lying in the triangle 1 plane */ + iv0 = tri1_edge[0]; + iv1 = tri1_edge[1]; + iv2 = (tri1_edge[1]+1) % 3; + f3_sub(E0, tri1_vertices[iv1].value, tri1_vertices[iv0].value); + f3_sub(E1, tri1_vertices[iv2].value, tri1_vertices[iv0].value); + f3_cross(N1, E0, E1); + tri1_2area = f3_len(N1); + f3_cross(N1, N1, E0); + + /* Compute the cosine between the 2 edge normals */ + f3_normalize(N0, N0); + f3_normalize(N1, N1); + cos_normals = f3_dot(N0, N1); + + /* The angle formed by the 2 triangles is sharp */ + if(cos_normals > SHARP_ANGLE_COS_THRESOLD) return 0; + + /* Compute the 2 times the area of the (pos0, shared_edge.vertex0, + * shared_edge.vertex1) triangles */ + f3_sub(E0, tri0_vertices[tri0_edge[0]].value, pos0); + f3_sub(E1, tri0_vertices[tri0_edge[1]].value, pos0); + tmp0_2area = f3_len(f3_cross(N0, E0, E1)); + + /* Compute the 2 times the area of the (pos1, shared_edge.vertex0, + * shared_edge.vertex1) triangles */ + f3_sub(E0, tri1_vertices[tri1_edge[0]].value, pos1); + f3_sub(E1, tri1_vertices[tri1_edge[1]].value, pos1); + tmp1_2area = f3_len(f3_cross(N1, E0, E1)); + + hit_edge = + ( eq_epsf(tri0_2area, 0, 1.e-6f) + || eq_epsf(tmp0_2area, 0, 1.e-6f) + || tmp0_2area/tri0_2area < ON_EDGE_EPSILON); + hit_edge = hit_edge && + ( eq_epsf(tri1_2area, 0, 1.e-6f) + || eq_epsf(tmp1_2area, 0, 1.e-6f) + || tmp1_2area/tri1_2area < ON_EDGE_EPSILON); + return hit_edge; + } } #undef ON_EDGE_EPSILON #endif /* DIM == 2 */ @@ -446,15 +478,26 @@ XD(hit_filter_function) const float org[DIM], const float dir[DIM], const float range[2], - void* ray_data, + void* query_data, void* global_data) { - const struct hit_filter_data* filter_data = ray_data; - const struct sXd(hit)* hit_from = &filter_data->XD(hit); + const struct hit_filter_data* filter_data = query_data; + const struct sXd(hit)* hit_from = NULL; (void)org, (void)dir, (void)global_data, (void)range; /* No user defined data. Do not filter */ - if(!ray_data || SXD_HIT_NONE(hit_from)) return 0; + if(!filter_data) return 0; + + /* Call the custom filter function if it exists + * or perform regular filtering otherwise */ + if(filter_data->XD(custom_filter)) { + return filter_data->XD(custom_filter) + (hit, org, dir, range, filter_data->custom_filter_data, global_data); + } + + /* There is no intersection to discard */ + hit_from = &filter_data->XD(hit); + if(SXD_HIT_NONE(hit_from)) return 0; if(SXD_PRIMITIVE_EQ(&hit_from->prim, &hit->prim)) return 1; @@ -463,14 +506,17 @@ XD(hit_filter_function) if(eq_epsf(hit->distance, 0, (float)filter_data->epsilon)) { float pos[DIM]; + int reject_hit = 0; fX(add)(pos, org, fX(mulf)(pos, dir, hit->distance)); /* If the targeted point is near of the origin, check that it lies on an * edge/vertex shared by the 2 primitives */ #if DIM == 2 - return hit_shared_vertex(&hit_from->prim, &hit->prim, org, pos); + reject_hit = hit_shared_vertex(&hit_from->prim, &hit->prim, org, pos); #else - return hit_shared_edge(&hit_from->prim, &hit->prim, org, pos); + reject_hit = hit_shared_edge + (&hit_from->prim, &hit->prim, hit_from->uv, hit->uv, org, pos); #endif + return reject_hit; } return 0; } @@ -741,7 +787,7 @@ error: /* Build the Star-XD scene view of a specific enclosure and map their local * primitive id to their primitive id in the whole scene */ static res_T -XD(setup_enclosure_geometry)(struct sdis_scene* scn, struct sencXd(enclosure)* enc) +XD(register_enclosure)(struct sdis_scene* scn, struct sencXd(enclosure)* enc) { struct sXd(device)* sXd_dev = NULL; struct sXd(scene)* sXd_scn = NULL; @@ -775,7 +821,19 @@ XD(setup_enclosure_geometry)(struct sdis_scene* scn, struct sencXd(enclosure)* e enc_data = htable_enclosure_find(&scn->enclosures, &header.enclosure_id); ASSERT(enc_data != NULL); - /* Setup the vertex data */ + /* Setup the medium id of the enclosure */ + if(header.enclosed_media_count > 1) { + enc_data->medium_id = ENCLOSURE_MULTI_MEDIA; + } else { + SENCXD(enclosure_get_medium(enc, 0, &enc_data->medium_id)); + } + + /* Do not configure the enclosure geometry for enclosures that are infinite + * or composed of several media, i.e. that define boundary conditions */ + if(header.is_infinite || header.enclosed_media_count > 1) + goto exit; + + /* Setup the vertex data */ vdata.usage = SXD_POSITION; vdata.type = SXD_FLOATX; vdata.get = XD(enclosure_position); @@ -808,7 +866,7 @@ XD(setup_enclosure_geometry)(struct sdis_scene* scn, struct sencXd(enclosure)* e ASSERT(enc_data->S_over_V >= 0); #undef CALL - /* Set enclosure hc upper bound regardless of its media being a fluid */ + /* Set enclosure hc upper bound regardless of its media being a fluid */ p_ub = htable_d_find(&scn->tmp_hc_ub, &header.enclosure_id); ASSERT(p_ub); enc_data->hc_upper_bound = *p_ub; @@ -822,9 +880,6 @@ XD(setup_enclosure_geometry)(struct sdis_scene* scn, struct sencXd(enclosure)* e (enc, iprim, darray_uint_data_get(&enc_data->local2global)+iprim, &side)); } - /* Setup the medium id of the enclosure */ - SENCXD(enclosure_get_medium(enc, 0, &enc_data->medium_id)); - exit: enclosure_release(&enc_dummy); if(sXd_shape) SXD(shape_ref_put(sXd_shape)); @@ -861,11 +916,9 @@ XD(setup_enclosures)(struct sdis_scene* scn, struct sencXd(scene)* senc_scn) if(header.enclosed_media_count != 1 && !header.is_infinite) inner_multi++; - /* Silently discard infinite enclosures */ - if(!header.is_infinite) { - res = XD(setup_enclosure_geometry)(scn, enc); - if(res != RES_OK) goto error; - } + res = XD(register_enclosure)(scn, enc); + if(res != RES_OK) goto error; + SENCXD(enclosure_ref_put(enc)); enc = NULL; } @@ -912,7 +965,6 @@ XD(scene_create) SDIS(device_ref_get(dev)); scn->dev = dev; scn->fp_to_meter = args->fp_to_meter; - scn->trad = args->trad; scn->tmin = args->t_range[0]; scn->tmax = args->t_range[1]; scn->outer_enclosure_id = UINT_MAX; @@ -922,6 +974,16 @@ XD(scene_create) htable_enclosure_init(dev->allocator, &scn->enclosures); htable_d_init(dev->allocator, &scn->tmp_hc_ub); + if(args->source) { + SDIS(source_ref_get(args->source)); + scn->source = args->source; + } + + if(args->radenv) { + SDIS(radiative_env_ref_get(args->radenv)); + scn->radenv = args->radenv; + } + res = XD(run_analyze) (scn, args->nprimitives, @@ -968,8 +1030,7 @@ error: static res_T XD(scene_find_closest_point) (const struct sdis_scene* scn, - const double pos[3], - const double radius, + const struct sdis_scene_find_closest_point_args* args, size_t* iprim, double uv[2]) { @@ -978,30 +1039,37 @@ XD(scene_find_closest_point) float query_radius; res_T res = RES_OK; - if(!scn || !pos || radius <= 0 || !iprim || !uv - || scene_is_2d(scn) != (DIM == 2)) { + if(!scn || !iprim || !uv || scene_is_2d(scn) != (DIM == 2)) { res = RES_BAD_ARG; goto error; } + res = check_sdis_scene_find_closest_point_args(args); + if(res != RES_OK) goto error; /* Avoid a null query radius due to casting in single-precision */ - query_radius = MMAX((float)radius, FLT_MIN); + query_radius = MMAX((float)args->radius, FLT_MIN); + + fX_set_dX(query_pos, args->position); + + /* Do not filter anything */ + if(!args->XD(filter)) { + res = sXd(scene_view_closest_point) + (scn->sXd(view), query_pos, query_radius, NULL, &hit); + + /* Filter points according to user-defined filter function */ + } else { + struct hit_filter_data filter_data = HIT_FILTER_DATA_NULL; + filter_data.XD(custom_filter) = args->XD(filter); + filter_data.custom_filter_data = args->filter_data; + res = sXd(scene_view_closest_point) + (scn->sXd(view), query_pos, query_radius, &filter_data, &hit); + } - fX_set_dX(query_pos, pos); - res = sXd(scene_view_closest_point) - (scn->sXd(view), query_pos, query_radius, NULL, &hit); if(res != RES_OK) { -#if DIM == 2 log_err(scn->dev, - "%s: error querying the closest position at {%g, %g} " - "for a radius of %g -- %s.\n", - FUNC_NAME, SPLIT2(query_pos), query_radius, res_to_cstr(res)); -#else - log_err(scn->dev, - "%s: error querying the closest position at {%g, %g, %g} " + "%s: error querying the closest position at `"FORMAT_VECX"' " "for a radius of %g -- %s.\n", - FUNC_NAME, SPLIT3(query_pos), query_radius, res_to_cstr(res)); -#endif + FUNC_NAME, SPLITX(query_pos), query_radius, res_to_cstr(res)); goto error; } @@ -1028,7 +1096,7 @@ error: ******************************************************************************/ static INLINE res_T XD(scene_get_medium) - (const struct sdis_scene* scn, + (struct sdis_scene* scn, const double pos[DIM], struct get_medium_info* info, /* May be NULL */ struct sdis_medium** out_medium) @@ -1104,10 +1172,30 @@ 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); - medium = interface_get_medium - (interf, cos_N_dir < 0 ? SDIS_FRONT : SDIS_BACK); + scene_get_enclosure_ids(scn, hit.prim.prim_id, enc_ids); + + 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) { @@ -1121,49 +1209,29 @@ XD(scene_get_medium) } if(iprim >= nprims) { + log_warn(scn->dev, "%s: could not retrieve the medium at {%g, %g, %g}.\n", + FUNC_NAME, P[0], P[1], DIM == 3 ? P[2] : 0); res = RES_BAD_OP; goto error; } -#if DIM == 2 - 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}.\n", - FUNC_NAME, (unsigned long)iprim, SPLIT2(P)); - } -#else 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", - FUNC_NAME, (unsigned long)iprim, SPLIT3(P)); + FUNC_NAME, (unsigned long)iprim, P[0], P[1], DIM == 3 ? P[2] : 0); } -#endif exit: *out_medium = medium; return res; error: - { - /* RES_BAD_OP means that this is a recoverable issue. In such case, print a - * warning rather than an error. */ - void (*log_func)(const struct sdis_device*, const char*, ...) = - (res == RES_BAD_OP ? log_warn : log_err); -#if DIM == 2 - log_func(scn->dev, "%s: could not retrieve the medium at {%g, %g}.\n", - FUNC_NAME, SPLIT2(pos)); -#else - log_func(scn->dev, "%s: could not retrieve the medium at {%g, %g, %g}.\n", - FUNC_NAME, SPLIT3(pos)); -#endif - } goto exit; } static INLINE res_T XD(scene_get_medium_in_closed_boundaries) - (const struct sdis_scene* scn, + (struct sdis_scene* scn, const double pos[DIM], struct sdis_medium** out_medium) { @@ -1230,29 +1298,12 @@ error: goto exit; } -#if (SDIS_SCENE_DIMENSION == 2) -#include <star/sencX2d_undefs.h> -#else /* SDIS_SCENE_DIMENSION == 3 */ -#include <star/sencX3d_undefs.h> +#if (SDIS_XD_DIMENSION == 2) + #include <star/sencX2d_undefs.h> +#else /* SDIS_XD_DIMENSION == 3 */ + #include <star/sencX3d_undefs.h> #endif -#undef SDIS_SCENE_DIMENSION -#undef DIM -#undef sXd -#undef SXD -#undef SXD_VERTEX_DATA_NULL -#undef SXD_POSITION -#undef SXD_FLOAT3 -#undef SXD_TRACE -#undef SXD_TRACE -#undef SXD_GET_PRIMITIVE -#undef SXD_HIT_NONE -#undef SXD_PRIMITIVE_EQ -#undef SXD_FLOATX -#undef fX -#undef fX_set_dX -#undef fXX_mulfX -#undef XD #undef HIT_ON_BOUNDARY -#endif /* !SDIS_SCENE_DIMENSION */ +#include "sdis_Xd_end.h" diff --git a/src/sdis_scene_c.h b/src/sdis_scene_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -36,7 +36,17 @@ struct hit_filter_data { struct s2d_hit hit_2d; struct s3d_hit hit_3d; double epsilon; /* Threshold defining roughly equal intersections */ + + /* Bypass the regular filter function */ + s2d_hit_filter_function_T custom_filter_2d; + s3d_hit_filter_function_T custom_filter_3d; + + /* Custom filter query data. It is ignored if custom_filter is NULL */ + void* custom_filter_data; }; +#define HIT_FILTER_DATA_NULL__ {S2D_HIT_NULL__,S3D_HIT_NULL__,0,NULL,NULL,NULL} +static const struct hit_filter_data HIT_FILTER_DATA_NULL = + HIT_FILTER_DATA_NULL__; struct get_medium_info { /* Targeted position */ @@ -76,6 +86,8 @@ 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; @@ -100,7 +112,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 = UINT_MAX; + enc->medium_id = ENCLOSURE_MULTI_MEDIA; } static INLINE void @@ -209,10 +221,12 @@ struct sdis_scene { unsigned outer_enclosure_id; double fp_to_meter; - struct sdis_ambient_radiative_temperature trad; double tmin; /* Minimum temperature of the system (In Kelvin) */ double tmax; /* Maximum temperature of the system (In Kelvin) */ + struct sdis_source* source; /* External source. May be NULL */ + struct sdis_radiative_env* radenv; /* Radiative environment. May be NULL */ + ref_T ref; struct sdis_device* dev; }; @@ -231,7 +245,7 @@ scene_get_interface extern LOCAL_SYM res_T scene_get_medium - (const struct sdis_scene* scene, + (struct sdis_scene* scene, const double position[], struct get_medium_info* info, /* May be NULL */ struct sdis_medium** medium); @@ -248,7 +262,7 @@ scene_get_medium * are opened to infinity). */ extern LOCAL_SYM res_T scene_get_medium_in_closed_boundaries - (const struct sdis_scene* scn, + (struct sdis_scene* scn, const double position[], struct sdis_medium** medium); @@ -276,6 +290,14 @@ extern LOCAL_SYM res_T scene_check_dimensionality_3d (const struct sdis_scene* scn); +/* Check that the temperature range of the scene is well defined, i.e. that the + * minimum and maximum temperatures are known and that they define a valid + * range. If this is not the case, the function displays an error message and + * returns RES_BAD_ARG */ +extern LOCAL_SYM res_T +scene_check_temperature_range + (const struct sdis_scene* scn); + static INLINE void scene_get_enclosure_ids (const struct sdis_scene* scn, diff --git a/src/sdis_solve.c b/src/sdis_solve.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -57,6 +57,20 @@ sdis_solve_probe } res_T +sdis_solve_probe_list + (struct sdis_scene* scn, + const struct sdis_solve_probe_list_args* args, + struct sdis_estimator_buffer** out_buf) +{ + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + return solve_probe_list_2d(scn, args, out_buf); + } else { + return solve_probe_list_3d(scn, args, out_buf); + } +} + +res_T sdis_solve_probe_green_function (struct sdis_scene* scn, const struct sdis_solve_probe_args* args, @@ -85,6 +99,20 @@ sdis_solve_probe_boundary } res_T +sdis_solve_probe_boundary_list + (struct sdis_scene* scn, + const struct sdis_solve_probe_boundary_list_args* args, + struct sdis_estimator_buffer** out_buf) +{ + if(!scn) return RES_BAD_ARG; + if(scene_is_2d(scn)) { + return solve_probe_boundary_list_2d(scn, args, out_buf); + } else { + return solve_probe_boundary_list_3d(scn, args, out_buf); + } +} + +res_T sdis_solve_probe_boundary_green_function (struct sdis_scene* scn, const struct sdis_solve_probe_boundary_args* args, diff --git a/src/sdis_solve_boundary_Xd.h b/src/sdis_solve_boundary_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -23,6 +23,7 @@ #include "sdis_misc.h" #include "sdis_realisation.h" #include "sdis_scene_c.h" +#include "sdis_heat_path_boundary_c.h" /* check_Tref_<2d|3d> */ #include <rsys/clock_time.h> #include <star/ssp.h> @@ -78,6 +79,11 @@ check_solve_boundary_args(const struct sdis_solve_boundary_args* args) return RES_BAD_ARG; } + /* Check the diffusion algorithm */ + if((unsigned)args->diff_algo >= SDIS_DIFFUSION_ALGORITHMS_COUNT__) { + return RES_BAD_ARG; + } + return RES_OK; } @@ -404,6 +410,8 @@ XD(solve_boundary) realis_args.side = side; realis_args.green_path = pgreen_path; realis_args.heat_path = pheat_path; + realis_args.irealisation = (size_t)irealisation; + realis_args.diff_algo = args->diff_algo; realis_args.uv[0] = uv[0]; #if SDIS_XD_DIMENSION == 3 realis_args.uv[1] = uv[1]; @@ -776,14 +784,21 @@ XD(solve_boundary_flux) if(res_local!= RES_OK) { ATOMIC_SET(&res, res_local); continue; } /* Fetch interface parameters */ - epsilon = interface_side_get_emissivity(interf, &frag); - Tref = interface_side_get_reference_temperature(interf, &frag); + epsilon = interface_side_get_emissivity(interf, SDIS_INTERN_SOURCE_ID, &frag); hc = interface_get_convection_coef(interf, &frag); - hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; + Tref = interface_side_get_reference_temperature(interf, &frag); + if(epsilon <= 0) { + hr = 0; + } else { + res_local = XD(check_Tref)(scn, frag.P, Tref, FUNC_NAME); + if(res_local != RES_OK) { ATOMIC_SET(&res, &res_local); continue; } + hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; + } + frag.side = solid_side; imposed_flux = interface_side_get_flux(interf, &frag); imposed_temp = interface_side_get_temperature(interf, &frag); - if(imposed_temp >= 0) { + if(SDIS_TEMPERATURE_IS_KNOWN(imposed_temp)) { /* Flux computation on T boundaries is not supported yet */ log_err(scn->dev, "%s: Attempt to compute a flux at a Dirichlet boundary " "(not available yet).\n", FUNC_NAME); @@ -803,6 +818,8 @@ XD(solve_boundary_flux) realis_args.picard_order = args->picard_order; realis_args.solid_side = solid_side; realis_args.flux_mask = flux_mask; + realis_args.irealisation = (size_t)irealisation; + realis_args.diff_algo = args->diff_algo; realis_args.uv[0] = uv[0]; #if SDIS_XD_DIMENSION == 3 realis_args.uv[1] = uv[1]; @@ -818,10 +835,9 @@ XD(solve_boundary_flux) } else if(res_simul == RES_OK) { /* Update accumulators */ const double usec = (double)time_val(&t0, TIME_NSEC) * 0.001; /* Convective flux from fluid to solid */ - const double w_conv = hc * (result.Tfluid - result.Tboundary); + const double w_conv = hc > 0 ? hc * (result.Tfluid - result.Tboundary) : 0; /* Radiative flux from ambient to solid */ - const double w_rad = (result.Tradiative < 0) ? - 0 : hr * (result.Tradiative - result.Tboundary); + const double w_rad = hr > 0 ? hr * (result.Tradiative - result.Tboundary) : 0; /* Imposed flux that goes _into_ the solid */ const double w_imp = (imposed_flux != SDIS_FLUX_NONE) ? imposed_flux : 0; const double w_total = w_conv + w_rad + w_imp; diff --git a/src/sdis_solve_camera.c b/src/sdis_solve_camera.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -72,6 +72,11 @@ check_solve_camera_args(const struct sdis_solve_camera_args* args) return RES_BAD_ARG; } + /* Check the diffusion algorithm */ + if((unsigned)args->diff_algo >= SDIS_DIFFUSION_ALGORITHMS_COUNT__) { + return RES_BAD_ARG; + } + return RES_OK; } @@ -87,6 +92,7 @@ solve_pixel const int register_paths, /* Combination of enum sdis_heat_path_flag */ const double pix_sz[2], /* Pixel size in the normalized image plane */ const size_t picard_order, + const enum sdis_diffusion_algorithm diff_algo, struct sdis_estimator* estimator, struct pixel* pixel) { @@ -134,6 +140,8 @@ solve_pixel realis_args.time = time; realis_args.picard_order = picard_order; realis_args.heat_path = pheat_path; + realis_args.irealisation = (size_t)irealisation; + realis_args.diff_algo = diff_algo; d3_set(realis_args.position, ray_pos); d3_set(realis_args.direction, ray_dir); res_simul = ray_realisation_3d(scn, &realis_args, &w); @@ -196,6 +204,7 @@ solve_tile const int register_paths, /* Combination of enum sdis_heat_path_flag */ const double pix_sz[2], /* Pixel size in the normalized image plane */ const size_t picard_order, + const enum sdis_diffusion_algorithm diff_algo, struct sdis_estimator_buffer* buf, struct tile* tile) { @@ -233,7 +242,7 @@ solve_tile } res = solve_pixel (scn, rng, mdm, cam, time_range, ipix_image, spp, register_paths, pix_sz, - picard_order, estimator, pixel); + picard_order, diff_algo, estimator, pixel); if(res != RES_OK) goto error; } @@ -494,7 +503,7 @@ sdis_solve_camera char buffer[128]; /* Temporary buffer used to store formated time */ /* Stardis variables */ - struct sdis_estimator_buffer* buf= NULL; + struct sdis_estimator_buffer* buf = NULL; struct sdis_medium* medium = NULL; /* Random number generators */ @@ -602,7 +611,7 @@ sdis_solve_camera /* Here we go! Launch the Monte Carlo estimation */ omp_set_num_threads((int)scn->dev->nthreads); - register_paths = is_master_process + register_paths = is_master_process ? args->register_paths : SDIS_HEAT_PATH_NONE; #pragma omp parallel for schedule(static, 1/*chunk size*/) for(mcode = mcode_1st; mcode < (int64_t)ntiles_adjusted; mcode+=mcode_incr) { @@ -620,7 +629,7 @@ sdis_solve_camera tile_org[0] = morton2D_decode_u16((uint32_t)(mcode>>0)); if(tile_org[0] >= ntiles_x) continue; /* Discard tile */ tile_org[1] = morton2D_decode_u16((uint32_t)(mcode>>1)); - if(tile_org[1] >= ntiles_y) continue; /* Disaard tile */ + if(tile_org[1] >= ntiles_y) continue; /* Discard tile */ res_local = tile_create(scn->dev->allocator, &tile); if(tile == NULL) { @@ -650,7 +659,8 @@ sdis_solve_camera /* Draw the tile */ res_local = solve_tile (scn, rng, medium, args->cam, args->time_range, tile_org, tile_sz, - args->spp, register_paths, pix_sz, args->picard_order, buf, tile); + args->spp, register_paths, pix_sz, args->picard_order, args->diff_algo, + buf, tile); if(res_local != RES_OK) { ATOMIC_SET(&res, res_local); continue; diff --git a/src/sdis_solve_medium_Xd.h b/src/sdis_solve_medium_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -172,6 +172,11 @@ check_solve_medium_args(const struct sdis_solve_medium_args* args) return RES_BAD_ARG; } + /* Check the diffusion algorithm */ + if((unsigned)args->diff_algo >= SDIS_DIFFUSION_ALGORITHMS_COUNT__) { + return RES_BAD_ARG; + } + return RES_OK; } @@ -436,6 +441,7 @@ XD(solve_medium) realis_args.green_path = pgreen_path; realis_args.heat_path = pheat_path; realis_args.irealisation = (size_t)irealisation; + realis_args.diff_algo = args->diff_algo; dX(set)(realis_args.position, pos); res_simul = XD(probe_realisation)(scn, &realis_args, &weight); if(res_simul != RES_OK && res_simul != RES_BAD_OP) { diff --git a/src/sdis_solve_probe_Xd.h b/src/sdis_solve_probe_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -16,6 +16,7 @@ #include "sdis_c.h" #include "sdis_device_c.h" #include "sdis_estimator_c.h" +#include "sdis_estimator_buffer_c.h" #include "sdis_log.h" #include "sdis_green.h" #include "sdis_misc.h" @@ -63,11 +64,114 @@ check_solve_probe_args(const struct sdis_solve_probe_args* args) return RES_BAD_ARG; } + /* Check the diffusion algorithm */ + if((unsigned)args->diff_algo >= SDIS_DIFFUSION_ALGORITHMS_COUNT__) { + return RES_BAD_ARG; + } + + return RES_OK; +} + +static INLINE res_T +check_solve_probe_list_args + (struct sdis_device* dev, + const struct sdis_solve_probe_list_args* args) +{ + size_t iprobe = 0; + + if(!args) return RES_BAD_ARG; + + /* Check the list of probes */ + if(!args->probes || !args->nprobes) { + return RES_BAD_ARG; + } + + /* Check the RNG type */ + if(!args->rng_state && args->rng_type >= SSP_RNG_TYPES_COUNT__) { + return RES_BAD_ARG; + } + + FOR_EACH(iprobe, 0, args->nprobes) { + const res_T res = check_solve_probe_args(args->probes+iprobe); + if(res != RES_OK) return res; + + if(args->probes[iprobe].register_paths != SDIS_HEAT_PATH_NONE) { + log_warn(dev, + "Unable to save paths for probe %lu. " + "Saving path is not supported when solving multiple probes\n", + (unsigned long)iprobe); + } + } + return RES_OK; } #endif /* SDIS_SOLVE_PROBE_XD_H */ +static res_T +XD(solve_one_probe) + (struct sdis_scene* scn, + struct ssp_rng* rng, + const struct sdis_solve_probe_args* args, + struct accum* acc_temp, + struct accum* acc_time) +{ + struct sdis_medium* medium = NULL; /* Medium in which the probe lies */ + size_t irealisation = 0; + res_T res = RES_OK; + ASSERT(scn && rng && check_solve_probe_args(args) == RES_OK); + ASSERT(acc_temp && acc_time); + + *acc_temp = ACCUM_NULL; + *acc_time = ACCUM_NULL; + + /* Retrieve the medium in which the submitted position lies */ + res = scene_get_medium(scn, args->position, NULL, &medium); + if(res != RES_OK) goto error; + + FOR_EACH(irealisation, 0, args->nrealisations) { + struct probe_realisation_args realis_args = PROBE_REALISATION_ARGS_NULL; + double w = NaN; /* MC weight */ + double usec = 0; /* Time of a realisation */ + double time = 0; /* Sampled observation time */ + struct time t0, t1; /* Register the time spent solving a realisation */ + + /* Begin time registration of the realisation */ + time_current(&t0); + + /* Sample observation time */ + time = sample_time(rng, args->time_range); + + /* Run a realisation */ + realis_args.rng = rng; + realis_args.medium = medium; + 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; + } + +exit: + return res; +error: + goto exit; +} + /******************************************************************************* * Generic solve function ******************************************************************************/ @@ -229,6 +333,7 @@ XD(solve_probe) realis_args.green_path = pgreen_path; realis_args.heat_path = pheat_path; realis_args.irealisation = (size_t)irealisation; + realis_args.diff_algo = args->diff_algo; dX(set)(realis_args.position, args->position); res_simul = XD(probe_realisation)(scn, &realis_args, &w); @@ -316,7 +421,7 @@ XD(solve_probe) if(res != RES_OK) goto error; \ } (void)0 GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TEMP, acc_temp); - GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TEMP, acc_time); + GATHER_ACCUMS(MPI_SDIS_MSG_ACCUM_TIME, acc_time); #undef GATHER_ACCUMS time_sub(&time0, time_current(&time1), &time0); @@ -369,5 +474,207 @@ error: goto exit; } -#include "sdis_Xd_end.h" +static res_T +XD(solve_probe_list) + (struct sdis_scene* scn, + const struct sdis_solve_probe_list_args* args, + struct sdis_estimator_buffer** out_estim_buf) +{ + /* Time registration */ + struct time time0, time1; + char buf[128]; /* Temporary buffer used to store formated time */ + + /* Device variables */ + struct mem_allocator* allocator = NULL; + + /* Stardis variables */ + struct sdis_estimator_buffer* estim_buf = NULL; + + /* Random Number generator */ + struct ssp_rng_proxy* rng_proxy = NULL; + struct ssp_rng** per_thread_rng = NULL; + + /* Probe variables */ + size_t process_probes[2] = {0, 0}; /* Probes range managed by the process */ + size_t process_nprobes = 0; /* Number of probes managed by the process */ + size_t nprobes = 0; + struct accum* per_probe_acc_temp = NULL; + struct accum* per_probe_acc_time = NULL; + + /* Miscellaneous */ + int32_t* progress = NULL; /* Per process progress bar */ + int pcent_progress = 1; /* Percentage requiring progress update */ + int is_master_process = 1; + int64_t i = 0; + ATOMIC nsolved_probes = 0; + ATOMIC res = RES_OK; + + /* Check input arguments */ + if(!scn || !out_estim_buf) { res = RES_BAD_ARG; goto error; } + res = check_solve_probe_list_args(scn->dev, args); + if(res != RES_OK) goto error; + res = XD(scene_check_dimensionality)(scn); + if(res != RES_OK) goto error; + +#ifdef SDIS_ENABLE_MPI + is_master_process = !scn->dev->use_mpi || scn->dev->mpi_rank == 0; +#endif + allocator = scn->dev->allocator; + + /* Update the progress bar every percent if escape sequences are allowed in + * log messages or only every 10 percent when only plain text is allowed. + * This reduces the number of lines of plain text printed */ + pcent_progress = scn->dev->no_escape_sequence ? 10 : 1; + + /* Create the per thread RNGs */ + res = create_per_thread_rng + (scn->dev, args->rng_state, args->rng_type, &rng_proxy, &per_thread_rng); + if(res != RES_OK) goto error; + + /* Allocate the per process progress status */ + res = alloc_process_progress(scn->dev, &progress); + if(res != RES_OK) goto error; + + /* Synchronise the processes */ + process_barrier(scn->dev); + + /* Define the range of probes to manage in this process */ + process_nprobes = compute_process_index_range + (scn->dev, args->nprobes, process_probes); + + #define PROGRESS_MSG "Solving probes: " + print_progress(scn->dev, progress, PROGRESS_MSG); + + /* If there is no work to be done on this process (i.e. no probe to + * calculate), simply print its completion and go straight to the + * synchronization barrier.*/ + if(process_nprobes == 0) { + progress[0] = 100; + print_progress_update(scn->dev, progress, PROGRESS_MSG); + goto post_sync; + } + + /* Allocate the list of accumulators per probe. On the master process, + * allocate a complete list in which the accumulators of all processes will be + * stored. */ + nprobes = is_master_process ? args->nprobes : process_nprobes; + per_probe_acc_temp = MEM_CALLOC(allocator, nprobes, sizeof(*per_probe_acc_temp)); + per_probe_acc_time = MEM_CALLOC(allocator, nprobes, sizeof(*per_probe_acc_time)); + if(!per_probe_acc_temp || !per_probe_acc_time) { + log_err(scn->dev, "Unable to allocate the list of accumulators per probe.\n"); + res = RES_MEM_ERR; + goto error; + } + + /* Begin time registration of the computation */ + time_current(&time0); + + /* Here we go! 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 */ + const int ithread = omp_get_thread_num(); /* Thread ID */ + struct ssp_rng* rng = per_thread_rng[ithread]; /* RNG of the thread */ + + /* Probe */ + struct accum* probe_acc_temp = NULL; + struct accum* probe_acc_time = NULL; + const struct sdis_solve_probe_args* probe_args = NULL; /* Solve args */ + const size_t iprobe = process_probes[0] + (size_t)i; /* Probe ID */ + + /* Misc */ + size_t n = 0; /* Number of solved probes */ + int pcent = 0; /* Current progress */ + res_T res_local = RES_OK; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occured */ + + /* Retrieve the probe arguments */ + probe_args = &args->probes[iprobe]; + + /* Retrieve the probe accumulators */ + probe_acc_temp = &per_probe_acc_temp[i]; + probe_acc_time = &per_probe_acc_time[i]; + + res_local = XD(solve_one_probe) + (scn, rng, probe_args, probe_acc_temp, probe_acc_time); + if(res_local != RES_OK) { + ATOMIC_SET(&res, res_local); + continue; + } + + /* Update progress */ + n = (size_t)ATOMIC_INCR(&nsolved_probes); + pcent = (int)((double)n * 100.0 / (double)process_nprobes + 0.5/*round*/); + + #pragma omp critical + if(pcent/pcent_progress > progress[0]/pcent_progress) { + progress[0] = pcent; + print_progress_update(scn->dev, progress, PROGRESS_MSG); + } + } + +post_sync: + /* Synchronise processes */ + process_barrier(scn->dev); + + res = gather_res_T(scn->dev, (res_T)res); + if(res != RES_OK) goto error; + + print_progress_completion(scn->dev, progress, PROGRESS_MSG); + #undef PROGRESS_MSG + + /* Report computation time */ + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "%lu probes solved in %s.\n", + (unsigned long)args->nprobes, buf); + + /* Gather the RNG proxy sequence IDs and ensure that the RNG proxy state of + * the master process is greater than the RNG proxy state of all other + * processes */ + res = gather_rng_proxy_sequence_id(scn->dev, rng_proxy); + if(res != RES_OK) goto error; + + time_current(&time0); + + /* Gather the list of accumulators */ + #define GATHER_ACCUMS_LIST(Msg, Acc) { \ + res = gather_accumulators_list \ + (scn->dev, Msg, args->nprobes, process_probes, per_probe_acc_##Acc); \ + if(res != RES_OK) goto error; \ + } (void)0 + GATHER_ACCUMS_LIST(MPI_SDIS_MSG_ACCUM_TEMP, temp); + GATHER_ACCUMS_LIST(MPI_SDIS_MSG_ACCUM_TIME, time); + #undef GATHER_ACCUMS_LIST + + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Probes accumulator gathered in %s.\n", buf); + + if(is_master_process) { + res = estimator_buffer_create_from_observable_list_probe + (scn->dev, rng_proxy, args->probes, per_probe_acc_temp, + per_probe_acc_time, args->nprobes, &estim_buf); + if(res != RES_OK) goto error; + } + +exit: + if(per_thread_rng) release_per_thread_rng(scn->dev, per_thread_rng); + if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); + if(per_probe_acc_temp) MEM_RM(allocator, per_probe_acc_temp); + if(per_probe_acc_time) MEM_RM(allocator, per_probe_acc_time); + if(progress) free_process_progress(scn->dev, progress); + if(out_estim_buf) *out_estim_buf = estim_buf; + return (res_T)res; +error: + if(estim_buf) { + SDIS(estimator_buffer_ref_put(estim_buf)); + estim_buf = NULL; + } + goto exit; +} + +#include "sdis_Xd_end.h" diff --git a/src/sdis_solve_probe_boundary_Xd.h b/src/sdis_solve_probe_boundary_Xd.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -23,6 +23,7 @@ #include "sdis_misc.h" #include "sdis_realisation.h" #include "sdis_scene_c.h" +#include "sdis_heat_path_boundary_c.h" /* check_Tref_<2d|3d> */ #include <rsys/clock_time.h> #include <star/ssp.h> @@ -71,6 +72,45 @@ check_solve_probe_boundary_args return RES_BAD_ARG; } + /* Check the diffusion algorithm */ + if((unsigned)args->diff_algo >= SDIS_DIFFUSION_ALGORITHMS_COUNT__) { + return RES_BAD_ARG; + } + + return RES_OK; +} + +static INLINE res_T +check_solve_probe_boundary_list_args + (struct sdis_device* dev, + const struct sdis_solve_probe_boundary_list_args* args) +{ + size_t iprobe = 0; + + if(!args) return RES_BAD_ARG; + + /* Check the list of probes */ + if(!args->probes || !args->nprobes) { + return RES_BAD_ARG; + } + + /* Check the RNG type */ + if(!args->rng_state && args->rng_type >= SSP_RNG_TYPES_COUNT__) { + return RES_BAD_ARG; + } + + FOR_EACH(iprobe, 0, args->nprobes) { + const res_T res = check_solve_probe_boundary_args(args->probes+iprobe); + if(res != RES_OK) return res; + + if(args->probes[iprobe].register_paths != SDIS_HEAT_PATH_NONE) { + log_warn(dev, + "Unable to save paths for probe boundary %lu. " + "Saving path is not supported when solving multiple probes\n", + (unsigned long)iprobe); + } + } + return RES_OK; } @@ -104,11 +144,77 @@ check_solve_probe_boundary_flux_args return RES_BAD_ARG; } + /* Check the diffusion algorithm */ + if((unsigned)args->diff_algo >= SDIS_DIFFUSION_ALGORITHMS_COUNT__) { + return RES_BAD_ARG; + } + return RES_OK; } #endif /* SDIS_SOLVE_PROBE_BOUNDARY_XD_H */ +static res_T +XD(solve_one_probe_boundary) + (struct sdis_scene* scn, + struct ssp_rng* rng, + const struct sdis_solve_probe_boundary_args* args, + struct accum* acc_temp, + struct accum* acc_time) +{ + size_t irealisation = 0; + res_T res = RES_OK; + ASSERT(scn && rng && check_solve_probe_boundary_args(args) == RES_OK); + + *acc_temp = ACCUM_NULL; + *acc_time = ACCUM_NULL; + + FOR_EACH(irealisation, 0, args->nrealisations) { + struct boundary_realisation_args realis_args = BOUNDARY_REALISATION_ARGS_NULL; + double w = NaN; /* MC weight */ + double usec = 0; /* Time of a realisation */ + double time = 0; /* Sampled observation time */ + struct time t0, t1; /* Register the time spent solving a realisation */ + + /* Begin time registration of the realisation */ + time_current(&t0); + + /* Sample observation time */ + time = sample_time(rng, args->time_range); + + /* Run a realisation */ + realis_args.rng = rng; + realis_args.iprim = args->iprim; + realis_args.time = time; + realis_args.picard_order = args->picard_order; + realis_args.side = args->side; + realis_args.irealisation = irealisation; + realis_args.diff_algo = args->diff_algo; + realis_args.uv[0] = args->uv[0]; +#if SDIS_XD_DIMENSION == 3 + 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; + } +exit: + return res; +error: + goto exit; +} + /******************************************************************************* * Local functions ******************************************************************************/ @@ -268,6 +374,8 @@ XD(solve_probe_boundary) realis_args.side = args->side; realis_args.green_path = pgreen_path; realis_args.heat_path = pheat_path; + realis_args.irealisation = (size_t)irealisation; + realis_args.diff_algo = args->diff_algo; realis_args.uv[0] = args->uv[0]; #if SDIS_XD_DIMENSION == 3 realis_args.uv[1] = args->uv[1]; @@ -410,6 +518,207 @@ error: } static res_T +XD(solve_probe_boundary_list) + (struct sdis_scene* scn, + const struct sdis_solve_probe_boundary_list_args* args, + struct sdis_estimator_buffer** out_estim_buf) +{ + /* Time registration */ + struct time time0, time1; + char buf[128]; /* Temporary buffer used to store formated time */ + + /* Device variable */ + struct mem_allocator* allocator = NULL; + + /* Stardis variables */ + struct sdis_estimator_buffer* estim_buf = NULL; + + /* Random Number generator */ + struct ssp_rng_proxy* rng_proxy = NULL; + struct ssp_rng** per_thread_rng = NULL; + + /* Probe variables */ + size_t process_probes[2] = {0, 0}; /* Probes range managed by the process */ + size_t process_nprobes = 0; /* Number of probes managed by the process */ + size_t nprobes = 0; + struct accum* per_probe_acc_temp = NULL; + struct accum* per_probe_acc_time = NULL; + + /* Miscellaneous */ + int32_t* progress = NULL; /* Per process progress bar */ + int is_master_process = 1; + int pcent_progress = 1; /* Percentage requiring progress update */ + int64_t i = 0; + ATOMIC nsolved_probes = 0; + ATOMIC res = RES_OK; + + if(!scn || !out_estim_buf) { res = RES_BAD_ARG; goto error; } + res = check_solve_probe_boundary_list_args(scn->dev, args); + if(res != RES_OK) goto error; + res = XD(scene_check_dimensionality)(scn); + if(res != RES_OK) goto error; + +#ifdef SDIS_ENABLE_MPI + is_master_process = !scn->dev->use_mpi || scn->dev->mpi_rank == 0; +#endif + + allocator = scn->dev->allocator; + + /* Update the progress bar every percent if escape sequences are allowed in + * log messages or only every 10 percent when only plain text is allowed. + * This reduces the number of lines of plain text printed */ + pcent_progress = scn->dev->no_escape_sequence ? 10 : 1; + + /* Create the per threads RNGs */ + res = create_per_thread_rng + (scn->dev, args->rng_state, args->rng_type, &rng_proxy, &per_thread_rng); + if(res != RES_OK) goto error; + + /* Allocate the per process progress status */ + res = alloc_process_progress(scn->dev, &progress); + if(res != RES_OK) goto error; + + /* Synchronise the processes */ + process_barrier(scn->dev); + + /* Define the range of probes to manage in this process */ + process_nprobes = compute_process_index_range + (scn->dev, args->nprobes, process_probes); + + #define PROGRESS_MSG "Solving surface probes: " + print_progress(scn->dev, progress, PROGRESS_MSG); + + /* If there is no work to be done on this process (i.e. no probe to + * calculate), simply print its completion and go straight to the + * synchronization barrier.*/ + if(process_nprobes == 0) { + progress[0] = 100; + print_progress_update(scn->dev, progress, PROGRESS_MSG); + goto post_sync; + } + + /* Allocate the list of accumulators per probe. On the master process, + * allocate a complete list in which the accumulators of all processes will be + * stored. */ + nprobes = is_master_process ? args->nprobes : process_nprobes; + per_probe_acc_temp = MEM_CALLOC(allocator, nprobes, sizeof(*per_probe_acc_temp)); + per_probe_acc_time = MEM_CALLOC(allocator, nprobes, sizeof(*per_probe_acc_time)); + if(!per_probe_acc_temp || !per_probe_acc_time) { + log_err(scn->dev, "Unable to allocate the list of accumulators per probe.\n"); + res = RES_MEM_ERR; + goto error; + } + + /* Begin time registration of the computation */ + time_current(&time0); + + /* Calculation of probe list */ + #pragma omp parallel for schedule(static) + for(i = 0; i < (int64_t)process_nprobes; ++i) { + /* Thread */ + const int ithread = omp_get_thread_num(); /* Thread ID */ + struct ssp_rng* rng = per_thread_rng[ithread]; /* RNG of the thread */ + + /* Probe */ + struct accum* probe_acc_temp = NULL; + struct accum* probe_acc_time = NULL; + const struct sdis_solve_probe_boundary_args* probe_args = NULL; + const size_t iprobe = process_probes[0] + (size_t)i; /* Probe ID */ + + /* Miscellaneous */ + size_t n = 0; /* Number of solved probes */ + int pcent = 0; /* Current progress */ + res_T res_local = RES_OK; + + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ + + /* Retrieve the probe arguments */ + probe_args = &args->probes[iprobe]; + + /* Retrieve the probe accumulators */ + probe_acc_temp = &per_probe_acc_temp[i]; + probe_acc_time = &per_probe_acc_time[i]; + + res_local = XD(solve_one_probe_boundary) + (scn, rng, probe_args, probe_acc_temp, probe_acc_time); + if(res_local != RES_OK) { + ATOMIC_SET(&res, res_local); + continue; + } + + /* Update progress */ + n = (size_t)ATOMIC_INCR(&nsolved_probes); + pcent = (int)((double)n * 100.0 / (double)process_nprobes + 0.5/*round*/); + + #pragma omp critical + if(pcent/pcent_progress > progress[0]/pcent_progress) { + progress[0] = pcent; + print_progress_update(scn->dev, progress, PROGRESS_MSG); + } + } + +post_sync: + /* Synchronise processes */ + process_barrier(scn->dev); + + res = gather_res_T(scn->dev, (res_T)res); + if(res != RES_OK) goto error; + + print_progress_completion(scn->dev, progress, PROGRESS_MSG); + #undef PROGRESS_MSG + + /* Report computatio time */ + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "%lu surface probes solved in %s.\n", + (unsigned long)args->nprobes, buf); + + /* Gather the RNG proxy sequence IDs and ensure that the RNG proxy state of + * the master process is greater than the RNG proxy state of all other + * processes */ + res = gather_rng_proxy_sequence_id(scn->dev, rng_proxy); + if(res != RES_OK) goto error; + + time_current(&time0); + + /* Gather the list of accumulators */ + #define GATHER_ACCUMS_LIST(Msg, Acc) { \ + res = gather_accumulators_list \ + (scn->dev, Msg, args->nprobes, process_probes, per_probe_acc_##Acc); \ + if(res != RES_OK) goto error; \ + } (void)0 + GATHER_ACCUMS_LIST(MPI_SDIS_MSG_ACCUM_TEMP, temp); + GATHER_ACCUMS_LIST(MPI_SDIS_MSG_ACCUM_TIME, time); + #undef GATHER_ACCUMS_LIST + + time_sub(&time0, time_current(&time1), &time0); + time_dump(&time0, TIME_ALL, NULL, buf, sizeof(buf)); + log_info(scn->dev, "Probes accumulator gathered in %s.\n", buf); + + if(is_master_process) { + res = estimator_buffer_create_from_observable_list_probe_boundary + (scn->dev, rng_proxy, args->probes, per_probe_acc_temp, + per_probe_acc_time, args->nprobes, &estim_buf); + if(res != RES_OK) goto error; + } + +exit: + if(per_thread_rng) release_per_thread_rng(scn->dev, per_thread_rng); + if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); + if(per_probe_acc_temp) MEM_RM(allocator, per_probe_acc_temp); + if(per_probe_acc_time) MEM_RM(allocator, per_probe_acc_time); + if(progress) free_process_progress(scn->dev, progress); + if(out_estim_buf) *out_estim_buf = estim_buf; + return (res_T)res; +error: + if(estim_buf) { + SDIS(estimator_buffer_ref_put(estim_buf)); + estim_buf = NULL; + } + goto exit; +} + +static res_T XD(solve_probe_boundary_flux) (struct sdis_scene* scn, const struct sdis_solve_probe_boundary_flux_args* args, @@ -544,6 +853,7 @@ XD(solve_probe_boundary_flux) BOUNDARY_FLUX_REALISATION_ARGS_NULL; struct time t0, t1; const int ithread = omp_get_thread_num(); + struct sdis_interface_fragment frag_local = frag; struct ssp_rng* rng = per_thread_rng[ithread]; struct accum* acc_temp = &per_thread_acc_tp[ithread]; struct accum* acc_time = &per_thread_acc_ti[ithread]; @@ -554,9 +864,10 @@ XD(solve_probe_boundary_flux) double time, epsilon, hc, hr, imposed_flux, imposed_temp; int flux_mask = 0; struct bound_flux_result result = BOUND_FLUX_RESULT_NULL__; - double Tref = -1; + double Tref = SDIS_TEMPERATURE_NONE; size_t n; int pcent; + res_T res_local = RES_OK; res_T res_simul = RES_OK; if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occurred */ @@ -567,16 +878,24 @@ XD(solve_probe_boundary_flux) time = sample_time(rng, args->time_range); /* Compute hr and hc */ - frag.time = time; - frag.side = fluid_side; - epsilon = interface_side_get_emissivity(interf, &frag); - Tref = interface_side_get_reference_temperature(interf, &frag); - hc = interface_get_convection_coef(interf, &frag); - hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; - frag.side = solid_side; - imposed_flux = interface_side_get_flux(interf, &frag); - imposed_temp = interface_side_get_temperature(interf, &frag); - if(imposed_temp >= 0) { + frag_local.time = time; + frag_local.side = fluid_side; + epsilon = interface_side_get_emissivity + (interf, SDIS_INTERN_SOURCE_ID, &frag_local); + Tref = interface_side_get_reference_temperature(interf, &frag_local); + hc = interface_get_convection_coef(interf, &frag_local); + if(epsilon <= 0) { + hr = 0; + } else { + res_local = XD(check_Tref)(scn, frag_local.P, Tref, FUNC_NAME); + if(res_local != RES_OK) { ATOMIC_SET(&res, &res_local); continue; } + hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon; + } + + frag_local.side = solid_side; + imposed_flux = interface_side_get_flux(interf, &frag_local); + imposed_temp = interface_side_get_temperature(interf, &frag_local); + if(SDIS_TEMPERATURE_IS_KNOWN(imposed_temp)) { /* Flux computation on T boundaries is not supported yet */ log_err(scn->dev, "%s: Attempt to compute a flux at a Dirichlet boundary " @@ -597,6 +916,8 @@ XD(solve_probe_boundary_flux) realis_args.picard_order = args->picard_order; realis_args.solid_side = solid_side; realis_args.flux_mask = flux_mask; + realis_args.irealisation = (size_t)irealisation; + realis_args.diff_algo = args->diff_algo; realis_args.uv[0] = args->uv[0]; #if SDIS_XD_DIMENSION == 3 realis_args.uv[1] = args->uv[1]; @@ -612,10 +933,9 @@ XD(solve_probe_boundary_flux) } else if(res_simul == RES_OK) { /* Update accumulators */ const double usec = (double)time_val(&t0, TIME_NSEC) * 0.001; /* Convective flux from fluid to solid */ - const double w_conv = hc * (result.Tfluid - result.Tboundary); + const double w_conv = hc > 0 ? hc * (result.Tfluid - result.Tboundary) : 0; /* Radiative flux from ambient to solid */ - const double w_rad = (result.Tradiative < 0) ? - 0 : hr * (result.Tradiative - result.Tboundary); + const double w_rad = hr > 0 ? hr * (result.Tradiative - result.Tboundary) : 0; /* Imposed flux that goes _into_ the solid */ const double w_imp = (imposed_flux != SDIS_FLUX_NONE) ? imposed_flux : 0; /* Total flux */ diff --git a/src/sdis_source.c b/src/sdis_source.c @@ -0,0 +1,366 @@ +/* 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_device_c.h" +#include "sdis_log.h" +#include "sdis_source_c.h" + +#include <rsys/free_list.h> +#include <rsys/mem_allocator.h> +#include <rsys/ref_count.h> + +#include <star/ssp.h> + +struct sdis_source { + struct sdis_spherical_source_shader spherical; + struct sdis_data* data; + + struct fid id; /* Unique identifier of the source */ + struct sdis_device* dev; + ref_T ref; +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static res_T +check_spherical_source_shader + (struct sdis_device* dev, + const char* func_name, + const struct sdis_spherical_source_shader* shader) +{ + ASSERT(func_name); + if(!shader) return RES_BAD_ARG; + + if(!shader->position) { + log_err(dev, "%s: the position functor is missing.\n", func_name); + return RES_BAD_ARG; + } + + if(!shader->power) { + log_err(dev, "%s: the power functor is missing.\n", func_name); + return RES_BAD_ARG; + } + + if(shader->radius < 0) { + log_err(dev, "%s: invalid source radius '%g' m. It cannot be negative.\n", + func_name, shader->radius); + return RES_BAD_ARG; + } + + return RES_OK; +} + +static void +release_source(ref_T* ref) +{ + struct sdis_device* dev = NULL; + struct sdis_source* src = CONTAINER_OF(ref, struct sdis_source, ref); + ASSERT(ref); + dev = src->dev; + if(src->data) SDIS(data_ref_put(src->data)); + flist_name_del(&dev->source_names, src->id); + MEM_RM(dev->allocator, src); + SDIS(device_ref_put(dev)); +} + +/******************************************************************************* + * Exported symbols + ******************************************************************************/ +res_T +sdis_spherical_source_create + (struct sdis_device* dev, + const struct sdis_spherical_source_shader* shader, + struct sdis_data* data, + struct sdis_source** out_src) +{ + struct sdis_source* src = NULL; + res_T res = RES_OK; + + if(!dev || !out_src) { res = RES_BAD_ARG; goto error; } + res = check_spherical_source_shader(dev, FUNC_NAME, shader); + if(res != RES_OK) goto error; + + src = MEM_CALLOC(dev->allocator, 1, sizeof(*src)); + if(!src) { + log_err(dev, "%s: cannot allocate spherical source.\n", FUNC_NAME); + res = RES_OK; + goto error; + } + ref_init(&src->ref); + SDIS(device_ref_get(dev)); + if(data) SDIS(data_ref_get(data)); + src->spherical = *shader; + src->data = data; + src->dev = dev; + src->id = flist_name_add(&dev->source_names); + flist_name_get(&dev->source_names, src->id)->mem = src; + +exit: + if(out_src) *out_src = src; + return res; +error: + if(src) { SDIS(source_ref_put(src)); src = NULL; } + goto exit; +} + +res_T +sdis_spherical_source_get_shader + (const struct sdis_source* source, + struct sdis_spherical_source_shader* shader) +{ + if(!source || !shader) return RES_BAD_ARG; + *shader = source->spherical; + return RES_OK; +} + +res_T +sdis_source_ref_get(struct sdis_source* src) +{ + if(!src) return RES_BAD_ARG; + ref_get(&src->ref); + return RES_OK; +} + +res_T +sdis_source_ref_put(struct sdis_source* src) +{ + if(!src) return RES_BAD_ARG; + ref_put(&src->ref, release_source); + return RES_OK; +} + +struct sdis_data* +sdis_source_get_data(struct sdis_source* src) +{ + ASSERT(src); + return src->data; +} + +unsigned +sdis_source_get_id(const struct sdis_source* source) +{ + ASSERT(source); + return source->id.index; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +res_T +source_sample + (const struct sdis_source* src, + struct ssp_rng* rng, + const double pos[3], + const double time, + struct source_sample* sample) +{ + double src_pos[3]; /* [m] */ + double main_dir[3]; + double half_angle; /* [radians] */ + double cos_half_angle; /* [radians] */ + double dst; /* [m] */ + double radius; /* Source radius [m] */ + double power; /* Source power [W] */ + double area; /* Source area [m^2] */ + res_T res = RES_OK; + ASSERT(src && rng && pos && sample); + + /* Retrieve current source position, radius and power */ + src->spherical.position(time, src_pos, src->data); + power = src->spherical.power(time, src->data); + radius = src->spherical.radius; + + if(power < 0) { + log_err(src->dev, "%s: invalid source power '%g' W. It cannot be negative.\n", + FUNC_NAME, power); + res = RES_BAD_ARG; + goto error; + } + + area = 4*PI*radius*radius; /* [m^2] */ + + /* compute the direction of `pos' toward the center of the source */ + d3_sub(main_dir, src_pos, pos); + + /* Normalize the direction and keep the distance from `pos' to the center of + * the source */ + dst = d3_normalize(main_dir, main_dir); + if(dst <= radius) { + log_err(src->dev, + "%s: the position from which the external source is sampled " + "is included in the source:\n" + "\tsource position = %g, %g, %g\n" + "\tsource radius = %g\n" + "\tposition = %g, %g, %g\n" + "\ttime = %g\n" + "\tdistance from position to source = %g\n", + FUNC_NAME, SPLIT3(src_pos), radius, SPLIT3(pos), time, dst); + res = RES_BAD_ARG; + goto error; + } + + /* Point source */ + if(area == 0) { + d3_set(sample->dir, main_dir); + sample->pdf = 1; + sample->dst = dst; + sample->power = power; /* [W] */ + sample->radiance_term = 1.0 / (4*PI*dst*dst); /* [W/m^2/sr] */ + sample->radiance = sample->power * sample->radiance_term; /* [W/m^2/sr] */ + + /* Spherical source */ + } else { + /* Sample the source according to its solid angle, + * i.e. 2*PI*(1 - cos(half_angle)) */ + half_angle = asin(radius/dst); + cos_half_angle = cos(half_angle); + ssp_ran_sphere_cap_uniform /* pdf = 1/(2*PI*(1-cos(half_angle))) */ + (rng, main_dir, cos_half_angle, sample->dir, &sample->pdf); + + /* Set other sample variables */ + sample->dst = dst - radius; /* From pos to source boundaries [m] */ + sample->power = power; /* [W] */ + sample->radiance_term = 1.0 / (PI*area); /* [W/m^2/sr] */ + sample->radiance = sample->power * sample->radiance_term; /* [W/m^2/sr] */ + } + +exit: + return res; +error: + goto exit; +} + +res_T +source_trace_to + (const struct sdis_source* src, + const double pos[3], /* Ray origin */ + const double dir[3], /* Ray direction */ + const double time, /* Time at which ray is traced */ + struct source_sample* sample) +{ + double src_pos[3]; /* [m] */ + double main_dir[3]; + double radius; /* [m] */ + double power; /* [W] */ + double dst; /* Distance from pos to the source center [m] */ + double half_angle; /* [radian] */ + res_T res = RES_OK; + ASSERT(src && pos && dir && sample); + ASSERT(d3_is_normalized(dir)); + + radius = src->spherical.radius; + + /* Point sources cannot be targeted */ + if(radius == 0) { + *sample = SOURCE_SAMPLE_NULL; + goto exit; + } + + /* Retrieve current source position and power */ + src->spherical.position(time, src_pos, src->data); + power = src->spherical.power(time, src->data); + + if(power < 0) { + log_err(src->dev, "%s: invalid source power '%g' W. It cannot be negative.\n", + FUNC_NAME, power); + res = RES_BAD_ARG; + goto error; + } + + /* compute the direction of `pos' toward the center of the source */ + d3_sub(main_dir, src_pos, pos); + + /* Normalize the direction and keep the distance from `pos' to the center of + * the source */ + dst = d3_normalize(main_dir, main_dir); + if(dst <= radius) { + log_err(src->dev, + "%s: the position from which the external source is targeted " + "is included in the source:\n" + "\tsource position = %g, %g, %g\n" + "\tsource radius = %g\n" + "\tposition = %g, %g, %g\n" + "\ttime = %g\n" + "\tdistance from position to source = %g\n", + FUNC_NAME, SPLIT3(src_pos), radius, SPLIT3(pos), time, dst); + res = RES_BAD_ARG; + goto error; + } + + /* Compute the half angle of the source as seen from pos */ + half_angle = asin(radius/dst); + + /* The source is missed */ + if(d3_dot(dir, main_dir) < cos(half_angle)) { + *sample = SOURCE_SAMPLE_NULL; + + /* The source is intersected */ + } else { + const double area = 4*PI*radius*radius; /* [m^2] */ + + d3_set(sample->dir, dir); + sample->pdf = 1; + sample->dst = dst - radius; /* From pos to source boundaries [m] */ + sample->power = power; /* [W] */ + sample->radiance_term = 1.0 / (PI*area); /* [W/m^2/sr] */ + sample->radiance = sample->power * sample->radiance_term; /* [W/m^2/sr] */ + } + +exit: + return res; +error: + *sample = SOURCE_SAMPLE_NULL; + goto exit; +} + +double /* [W] */ +source_get_power(const struct sdis_source* src, const double time /* [s] */) +{ + ASSERT(src); + return src->spherical.power(time, src->data); +} + +double /* [W/perpendicular m^2/sr] */ +source_get_diffuse_radiance + (const struct sdis_source* src, + const double time /* [s] */, + const double dir[3]) +{ + ASSERT(src); + if(src->spherical.diffuse_radiance == NULL) { + return 0; + } else { + return src->spherical.diffuse_radiance(time, dir, src->data); + } +} + +void +source_compute_signature(const struct sdis_source* src, hash256_T hash) +{ + struct sha256_ctx ctx; + ASSERT(src && hash); + + /* Calculate the source signature. Currently, it is only the source radius. + * But the Source API is designed to be independent of source type. In the + * future, the source will not necessarily be spherical, so the data to be + * hashed will depend on the type of source. This function anticipate this by + * calculating a hash even if it is currently dispensable. */ + sha256_ctx_init(&ctx); + sha256_ctx_update + (&ctx, (const char*)&src->spherical.radius, sizeof(src->spherical.radius)); + sha256_ctx_finalize(&ctx, hash); +} diff --git a/src/sdis_source_c.h b/src/sdis_source_c.h @@ -0,0 +1,80 @@ +/* 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/>. */ + +#ifndef SDIS_SOURCE_C_H +#define SDIS_SOURCE_C_H + +#include <rsys/hash.h> +#include <rsys/rsys.h> + +struct sdis_source; +struct ssp_rng; + +struct source_sample { + double dir[3]; /* Direction _to_ the source */ + double pdf; /* pdf of sampled direction */ + double dst; /* Distance to the source [m] */ + double power; /* [W] */ + double radiance; /* [W/m^2/sr] */ + + /* Radiance relative to power, i.e. the source power is assumed to be equal to + * 1. It must be multiplied by the source power to obtain the actual radiance + * of the source. In other words, this variable defines the contribution of + * the source independently of its power, and can therefore be recorded in the + * green function */ + double radiance_term; /* [W/m^2/sr] */ +}; +#define SOURCE_SAMPLE_NULL__ {{0,0,0}, 0, 0, 0, 0, 0} +static const struct source_sample SOURCE_SAMPLE_NULL = SOURCE_SAMPLE_NULL__; + +/* Helper macro used to define whether a sample is valid or not */ +#define SOURCE_SAMPLE_NONE(Sample) ((Sample)->pdf == 0) + +extern LOCAL_SYM res_T +source_sample + (const struct sdis_source* source, + struct ssp_rng* rng, + const double pos[3], /* Position from which the source is sampled */ + const double time, /* Time at which the source is sampled */ + struct source_sample* sample); + +/* Trace a ray toward the source. The returned sample has a pdf of 1 or 0 + * whether the source is intersected by the ray or not, respectively. You can + * use the SOURCE_SAMPLE_NONE macro to check this */ +extern LOCAL_SYM res_T +source_trace_to + (const struct sdis_source* source, + const double pos[3], /* Ray origin */ + const double dir[3], /* Ray direction */ + const double time, /* Time at which ray is traced */ + struct source_sample* sample); /* pdf == 0 if no source is reached */ + +extern LOCAL_SYM double /* [W] */ +source_get_power + (const struct sdis_source* source, + const double time); /* [s] */ + +extern LOCAL_SYM double /* [W/m^2/sr] */ +source_get_diffuse_radiance + (const struct sdis_source* source, + const double time, /* [s] */ + const double dir[3]); + +extern LOCAL_SYM void +source_compute_signature + (const struct sdis_source* source, + hash256_T hash); + +#endif /* SDIS_SOURCE_C_H */ diff --git a/src/sdis_tile.c b/src/sdis_tile.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_tile.h b/src/sdis_tile.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/sdis_version.h.in b/src/sdis_version.h.in @@ -0,0 +1,23 @@ +/* Copyright (C) 2016-2023 |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/>. */ + +#ifndef SDIS_VERSION_H +#define SDIS_VERSION_H + +#define SDIS_VERSION_MAJOR @VERSION_MAJOR@ +#define SDIS_VERSION_MINOR @VERSION_MINOR@ +#define SDIS_VERSION_PATCH @VERSION_PATCH@ + +#endif /* SDIS_VERSION_H */ diff --git a/src/test_sdis.c b/src/test_sdis.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/test_sdis_accum_buffer.c b/src/test_sdis_accum_buffer.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/test_sdis_camera.c b/src/test_sdis_camera.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/test_sdis_compute_power.c b/src/test_sdis_compute_power.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -19,7 +19,6 @@ #include <rsys/stretchy_array.h> #include <star/s3dut.h> -#define UNKOWN_TEMPERATURE -1 #define N 100000ul /* #realisations */ #define POWER0 10 #define POWER1 5 diff --git a/src/test_sdis_conducto_radiative.c b/src/test_sdis_conducto_radiative.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -19,8 +19,6 @@ #include <rsys/math.h> #include <star/ssp.h> -#define UNKNOWN_TEMPERATURE -1 - /* The scene is composed of a solid cube whose temperature is unknown. The cube * faces on +/-X are in contact with a fluid and their convection coefficient * is null while their emissivity is 1. The left and right fluids are enclosed @@ -134,7 +132,7 @@ static double temperature_unknown(const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) { CHK(vtx != NULL); (void)data; - return -1; + return SDIS_TEMPERATURE_NONE; } static double @@ -179,7 +177,7 @@ solid_get_temperature CHK(data != NULL); t0 = ((const struct solid*)sdis_data_cget(data))->t0; if(vtx->time > t0) { - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } else { return ((const struct solid*)sdis_data_cget(data))->initial_temperature; } @@ -214,16 +212,22 @@ interface_get_convection_coef static double interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (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 interfac*)sdis_data_cget(data))->emissivity; } static double interface_get_specular_fraction - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (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 interfac*)sdis_data_cget(data))->specular_fraction; } @@ -395,7 +399,7 @@ main(int argc, char** argv) OK(sdis_data_ref_put(data)); /* Create the interface that forces to keep in conduction */ - interf.temperature = UNKNOWN_TEMPERATURE; + interf.temperature = SDIS_TEMPERATURE_NONE; interf.convection_coef = -1; interf.emissivity = -1; interf.specular_fraction = -1; @@ -403,7 +407,7 @@ main(int argc, char** argv) create_interface(dev, solid, solid2, &interf, interfaces+0); /* Create the interface that emits radiative heat from the solid */ - interf.temperature = UNKNOWN_TEMPERATURE; + interf.temperature = SDIS_TEMPERATURE_NONE; interf.convection_coef = 0; interf.emissivity = emissivity; interf.specular_fraction = 1; @@ -411,7 +415,7 @@ main(int argc, char** argv) create_interface(dev, solid, fluid, &interf, interfaces+1); /* Create the interface that forces the radiative heat to bounce */ - interf.temperature = UNKNOWN_TEMPERATURE; + interf.temperature = SDIS_TEMPERATURE_NONE; interf.convection_coef = 0; interf.emissivity = 0; interf.specular_fraction = 1; @@ -483,7 +487,7 @@ main(int argc, char** argv) struct sdis_estimator* estimator2; struct sdis_green_function* green; struct sdis_solve_probe_args solve_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; - double ref = -1; + double ref = SDIS_TEMPERATURE_NONE; size_t nreals = 0; size_t nfails = 0; const size_t N = 10000; diff --git a/src/test_sdis_conducto_radiative_2d.c b/src/test_sdis_conducto_radiative_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -18,8 +18,6 @@ #include <star/ssp.h> -#define UNKNOWN_TEMPERATURE -1 - /* The scene is composed of a solid square whose temperature is unknown. The * square segments on +/-X are in contact with a fluid and their convection * coefficient is null while their emissivity is 1. The left and right fluids @@ -113,7 +111,7 @@ static double temperature_unknown(const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) { CHK(vtx != NULL); (void)data; - return -1; + return SDIS_TEMPERATURE_NONE; } static double @@ -163,7 +161,7 @@ struct interfac { }; static const struct interfac INTERFACE_NULL = { - 0, {-1, -1, -1, -1}, {-1, -1, -1, -1} + 0, {SDIS_TEMPERATURE_NONE, -1, -1, -1}, {-1, -1, -1, -1} }; static double @@ -171,7 +169,7 @@ interface_get_temperature (const struct sdis_interface_fragment* frag, struct sdis_data* data) { const struct interfac* interf; - double T = -1; + double T = SDIS_TEMPERATURE_NONE; CHK(data != NULL && frag != NULL); interf = sdis_data_cget(data); switch(frag->side) { @@ -194,10 +192,13 @@ interface_get_convection_coef static double interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) { const struct interfac* interf; double e = -1; + (void)source_id; CHK(data != NULL && frag != NULL); interf = sdis_data_cget(data); switch(frag->side) { @@ -210,10 +211,13 @@ interface_get_emissivity static double interface_get_specular_fraction - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) { const struct interfac* interf; double f = -1; + (void)source_id; CHK(data != NULL && frag != NULL); interf = sdis_data_cget(data); switch(frag->side) { @@ -229,7 +233,7 @@ interface_get_reference_temperature (const struct sdis_interface_fragment* frag, struct sdis_data* data) { const struct interfac* interf; - double T = -1; + double T = SDIS_TEMPERATURE_NONE; CHK(data != NULL && frag != NULL); interf = sdis_data_cget(data); switch(frag->side) { @@ -398,7 +402,7 @@ main(int argc, char** argv) /* Create the interface that emits radiative heat from the solid */ interf = INTERFACE_NULL; - interf.back.temperature = UNKNOWN_TEMPERATURE; + interf.back.temperature = SDIS_TEMPERATURE_NONE; interf.back.emissivity = emissivity; interf.back.specular_fraction = -1; /* Should not be fetched */ interf.back.reference_temperature = Tref; @@ -406,7 +410,7 @@ main(int argc, char** argv) /* Create the interface that forces the radiative heat to bounce */ interf = INTERFACE_NULL; - interf.front.temperature = UNKNOWN_TEMPERATURE; + interf.front.temperature = SDIS_TEMPERATURE_NONE; interf.front.emissivity = 0; interf.front.specular_fraction = 1; interf.front.reference_temperature = Tref; diff --git a/src/test_sdis_contact_resistance.c b/src/test_sdis_contact_resistance.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -52,7 +52,6 @@ * (0,0,0)///x=X0/// */ -#define UNKNOWN_TEMPERATURE -1 #define N 10000 /* #realisations */ #define T0 0.0 @@ -80,7 +79,7 @@ fluid_get_temperature { (void)data; CHK(vtx); - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } static double @@ -120,7 +119,7 @@ solid_get_temperature (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) { CHK(vtx && data); - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } /******************************************************************************* @@ -314,7 +313,7 @@ main(int argc, char** argv) /* Create the adiabatic interfaces */ OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); interf_props = sdis_data_get(data); - interf_props->temperature = UNKNOWN_TEMPERATURE; + interf_props->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create (dev, solid1, fluid, &interf_shader, data, &interf_adiabatic1)); OK(sdis_interface_create @@ -342,7 +341,7 @@ main(int argc, char** argv) interf_shader.thermal_contact_resistance = interface_get_contact_resistance; OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); interf_props = sdis_data_get(data); - interf_props->temperature = UNKNOWN_TEMPERATURE; + interf_props->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create (dev, solid1, solid2, &interf_shader, data, &interf_R)); OK(sdis_data_ref_put(data)); diff --git a/src/test_sdis_contact_resistance.h b/src/test_sdis_contact_resistance.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/test_sdis_contact_resistance_2.c b/src/test_sdis_contact_resistance_2.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -45,7 +45,6 @@ * (0,0,0)///x=X0/// */ -#define UNKNOWN_TEMPERATURE -1 #define N 10000 /* #realisations */ #define T0 0.0 @@ -73,7 +72,7 @@ fluid_get_temperature { (void)data; CHK(vtx); - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } static double @@ -113,7 +112,7 @@ solid_get_temperature (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) { CHK(vtx && data); - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } /******************************************************************************* @@ -408,7 +407,7 @@ main(int argc, char** argv) /* Create the adiabatic interfaces */ OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); interf_props = sdis_data_get(data); - interf_props->temperature = UNKNOWN_TEMPERATURE; + interf_props->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create (dev, solid1, fluid, &interf_shader, data, &interf_adiabatic1)); OK(sdis_interface_create @@ -436,7 +435,7 @@ main(int argc, char** argv) interf_shader.thermal_contact_resistance = interface_get_contact_resistance; OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); interf_props = sdis_data_get(data); - interf_props->temperature = UNKNOWN_TEMPERATURE; + interf_props->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create (dev, solid1, solid2, &interf_shader, data, &interf_R)); OK(sdis_data_ref_put(data)); diff --git a/src/test_sdis_convection.c b/src/test_sdis_convection.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -48,7 +48,6 @@ * (0,0,0) */ -#define UNKNOWN_TEMPERATURE -1 #define N 100000 /* #realisations */ #define Tf_0 280.0 @@ -73,9 +72,9 @@ fluid_get_temperature { CHK(vtx != NULL); if(*((int*)sdis_data_cget(is_stationary))) { - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } else { - return vtx->time <= 0 ? Tf_0 : UNKNOWN_TEMPERATURE; + return vtx->time <= 0 ? Tf_0 : SDIS_TEMPERATURE_NONE; } } @@ -123,16 +122,22 @@ interface_get_convection_coef static double interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) { + (void)source_id; CHK(frag && data); return 0; } static double interface_get_specular_fraction - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) { + (void)source_id; CHK(frag && data); return 0; } diff --git a/src/test_sdis_convection_non_uniform.c b/src/test_sdis_convection_non_uniform.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -48,7 +48,6 @@ * (0,0,0) */ -#define UNKNOWN_TEMPERATURE -1 #define N 100000 /* #realisations */ #define Tf_0 280.0 @@ -80,9 +79,9 @@ fluid_get_temperature CHK(vtx != NULL); CHK(is_stationary != NULL); if(*((int*)sdis_data_cget(is_stationary))) { - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } else { - return vtx->time <= 0 ? Tf_0 : UNKNOWN_TEMPERATURE; + return vtx->time <= 0 ? Tf_0 : SDIS_TEMPERATURE_NONE; } } @@ -132,16 +131,22 @@ interface_get_convection_coef static double interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) { + (void)source_id; CHK(frag && data); return 0; } static double interface_get_specular_fraction - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) { + (void)source_id; CHK(frag && data); return 0; } diff --git a/src/test_sdis_data.c b/src/test_sdis_data.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 diff --git a/src/test_sdis_device.c b/src/test_sdis_device.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -37,6 +37,7 @@ main(int argc, char** argv) struct logger logger; struct mem_allocator allocator; struct sdis_device* dev; + int is_mpi_used; #ifdef SDIS_ENABLE_MPI int provided; #endif @@ -84,6 +85,9 @@ main(int argc, char** argv) args.nthreads_hint = SDIS_NTHREADS_DEFAULT; OK(sdis_device_create(&args, &dev)); + BA(sdis_device_is_mpi_used(NULL, &is_mpi_used)); + BA(sdis_device_is_mpi_used(dev, NULL)); + OK(sdis_device_ref_put(dev)); args.use_mpi = 1; @@ -91,10 +95,14 @@ main(int argc, char** argv) #ifndef SDIS_ENABLE_MPI OK(sdis_device_create(&args, &dev)); + OK(sdis_device_is_mpi_used(dev, &is_mpi_used)); + CHK(!is_mpi_used); OK(sdis_device_ref_put(dev)); #else CHK(MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided) == MPI_SUCCESS); OK(sdis_device_create(&args, &dev)); + OK(sdis_device_is_mpi_used(dev, &is_mpi_used)); + CHK(is_mpi_used); CHK(MPI_Finalize() == MPI_SUCCESS); OK(sdis_device_ref_put(dev)); #endif diff --git a/src/test_sdis_draw_external_flux.c b/src/test_sdis_draw_external_flux.c @@ -0,0 +1,429 @@ +/* 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/s3dut.h> + +#define IMG_WIDTH 320 +#define IMG_HEIGHT 240 +#define IMG_SPP 64 +#define SOURCE_POWER 30e6 /* W */ +#define EMISSIVITY 0.5 +#define T_RAD 300 /* [K] */ +#define T_REF 300 /* [K] */ + +/* + * The system consists of a floor (i.e. a parallelepiped), a super-form floating + * above it and a spherical external source. The test attempts to render the + * scene and thus verify the entire sampling of conductive-radiative paths + * coupled to an external flux from the external source. + */ + +/******************************************************************************* + * Geometries + ******************************************************************************/ +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 = 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, &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_ground(struct mesh* mesh) +{ + #define DEPTH 2.0 + const double translate[3] = {0, 0, -DEPTH-1}; + + struct s3dut_mesh* ground = NULL; + struct s3dut_mesh_data ground_data; + + OK(s3dut_create_cuboid(NULL, 20, 20, DEPTH, &ground)); + OK(s3dut_mesh_get_data(ground, &ground_data)); + mesh_append(mesh, ground_data.positions, ground_data.nvertices, + ground_data.indices, ground_data.nprimitives, translate); + OK(s3dut_mesh_ref_put(ground)); + #undef DEPTH +} + +/******************************************************************************* + * Media & interface + ******************************************************************************/ +#define MEDIUM_PROP(Type, Prop, Val) \ + static double \ + Type##_get_##Prop \ + (const struct sdis_rwalk_vertex* vtx, \ + struct sdis_data* data) \ + { \ + (void)vtx, (void)data; /* Avoid the "unused variable" warning */ \ + return Val; \ + } +MEDIUM_PROP(solid, calorific_capacity, 500.0) /* [J/K/Kg] */ +MEDIUM_PROP(solid, thermal_conductivity, 25.0) /* [W/m/K] */ +MEDIUM_PROP(solid, volumic_mass, 7500.0) /* [kg/m^3] */ +MEDIUM_PROP(solid, temperature, 310) /* [K] */ +MEDIUM_PROP(solid, delta, 1.0/20.0) /* [m] */ +MEDIUM_PROP(fluid, calorific_capacity, 2.0) /* [J/K/Kg] */ +MEDIUM_PROP(fluid, volumic_mass, 25.0) /* |kg/m^3] */ +MEDIUM_PROP(fluid, temperature, SDIS_TEMPERATURE_NONE) /* [K] */ +#undef MEDIUM_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; +} + +static struct sdis_medium* +create_fluid(struct sdis_device* sdis) +{ + struct sdis_fluid_shader shader = SDIS_FLUID_SHADER_NULL; + struct sdis_medium* fluid = NULL; + + shader.calorific_capacity = fluid_get_calorific_capacity; + shader.volumic_mass = fluid_get_volumic_mass; + shader.temperature = fluid_get_temperature; + OK(sdis_fluid_create(sdis, &shader, NULL, &fluid)); + return fluid; +} + +static double +interface_get_reference_temperature + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + (void)frag, (void)data; /* Avoid the "unused variable" warning */ + return T_REF; +} + +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 SDIS_TEMPERATURE_NONE; +} + +static double +interface_get_emissivity + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) +{ + /* Avoid the "unused variable" warning */ + (void)frag, (void)source_id, (void)data; + return EMISSIVITY; +} + +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.front.reference_temperature = interface_get_reference_temperature; + shader.front.emissivity = interface_get_emissivity; + shader.back.temperature = interface_get_temperature; + OK(sdis_interface_create(sdis, front, back, &shader, NULL, &interf)); + return interf; +} + +/******************************************************************************* + * External source + ******************************************************************************/ +static void +source_get_position + (const double time, + double pos[3], + struct sdis_data* data) +{ + (void)time, (void)data; /* Avoid the "unusued variable" warning */ + pos[0] = 5.0; + pos[1] = 5.0; + pos[2] = 5.0; +} + +static double +source_get_power(const double time, struct sdis_data* data) +{ + (void)time, (void)data; /* Avoid the "unusued variable" warning */ + return SOURCE_POWER; /* [W] */ +} + +static double +source_get_diffuse_radiance + (const double time, + const double dir[3], + struct sdis_data* data) +{ + (void)time, (void)data; /* Avoid the "unusued variable" warning */ + CHK(d3_is_normalized(dir)); + return 50; +} + +static struct sdis_source* +create_source(struct sdis_device* sdis) +{ + struct sdis_spherical_source_shader shader = SDIS_SPHERICAL_SOURCE_SHADER_NULL; + struct sdis_source* source = NULL; + + shader.position = source_get_position; + shader.power = source_get_power; + shader.diffuse_radiance = source_get_diffuse_radiance; + shader.radius = 3e-1; /* [m] */ + OK(sdis_spherical_source_create(sdis, &shader, NULL, &source)); + return source; +} + +/******************************************************************************* + * Radiative environment + ******************************************************************************/ +static double +radenv_get_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray, (void)data; + return T_RAD; +} + +static double +radenv_get_reference_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray, (void)data; + return T_REF; +} + +static struct sdis_radiative_env* +create_radenv(struct sdis_device* sdis) +{ + struct sdis_radiative_env_shader shader = SDIS_RADIATIVE_ENV_SHADER_NULL; + struct sdis_radiative_env* radenv = NULL; + + shader.temperature = radenv_get_temperature; + shader.reference_temperature = radenv_get_reference_temperature; + OK(sdis_radiative_env_create(sdis, &shader, NULL, &radenv)); + return radenv; +} + +/******************************************************************************* + * Scene, i.e. the system to simulate + ******************************************************************************/ +struct scene_context { + const struct mesh* mesh; + struct sdis_interface* interf; +}; +static const struct scene_context SCENE_CONTEXT_NULL = {NULL, 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)); + ids[0] = (unsigned)context->mesh->indices[itri*3+0]; + ids[1] = (unsigned)context->mesh->indices[itri*3+1]; + ids[2] = (unsigned)context->mesh->indices[itri*3+2]; +} + +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)); + *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 < 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, + const struct mesh* mesh, + struct sdis_interface* interf, + struct sdis_source* source, + struct sdis_radiative_env* radenv) +{ + 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.mesh = mesh; + 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 = mesh_ntriangles(mesh); + scn_args.nvertices = mesh_nvertices(mesh); + scn_args.t_range[0] = MMIN(T_RAD, T_REF); + scn_args.t_range[1] = MMAX(T_RAD, T_REF); + scn_args.source = source; + scn_args.radenv = radenv; + scn_args.context = &context; + OK(sdis_scene_create(sdis, &scn_args, &scn)); + return scn; +} + +/******************************************************************************* + * Rendering point of view + ******************************************************************************/ +static struct sdis_camera* +create_camera(struct sdis_device* sdis) +{ + const double pos[3] = {-3.81, 11.23, 5.29}; + const double tgt[3] = {-0.46, 0, -0.32}; + const double up[3] = {0, 0, 1}; + struct sdis_camera* cam = NULL; + + OK(sdis_camera_create(sdis, &cam)); + OK(sdis_camera_set_proj_ratio(cam, (double)IMG_WIDTH/(double)IMG_HEIGHT)); + OK(sdis_camera_set_fov(cam, MDEG2RAD(30))); + OK(sdis_camera_look_at(cam, pos, tgt, up)); + return cam; +} + +/******************************************************************************* + * Draw the submitted scene + ******************************************************************************/ +/* Write an image in htrdr-image(5) format */ +static void +write_image(FILE* stream, struct sdis_estimator_buffer* image) +{ + size_t x, y; + + /* Header */ + fprintf(stream, "%d %d\n", IMG_WIDTH, IMG_HEIGHT); + + /* Pixels ordered by row */ + FOR_EACH(y, 0, IMG_HEIGHT) { + FOR_EACH(x, 0, IMG_WIDTH) { + const struct sdis_estimator* pixel = NULL; + struct sdis_mc temperature = SDIS_MC_NULL; + + OK(sdis_estimator_buffer_at(image, x, y, &pixel)); + OK(sdis_estimator_get_temperature(pixel, &temperature)); + fprintf(stream, "%g %g 0 0 0 0 0 0\n", temperature.E, temperature.SE); + } + } +} + +static void +draw(struct sdis_scene* scn, struct sdis_camera* cam) +{ + struct sdis_solve_camera_args args = SDIS_SOLVE_CAMERA_ARGS_DEFAULT; + struct sdis_estimator_buffer* image = NULL; + + args.cam = cam; + args.image_definition[0] = IMG_WIDTH; + args.image_definition[1] = IMG_HEIGHT; + args.spp = IMG_SPP; + + OK(sdis_solve_camera(scn, &args, &image)); + write_image(stdout, image); + OK(sdis_estimator_buffer_ref_put(image)); +} + +/******************************************************************************* + * The test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + /* Stardis */ + struct sdis_camera* cam = NULL; + struct sdis_device* dev = NULL; + struct sdis_interface* interf = NULL; + struct sdis_medium* fluid = NULL; + struct sdis_medium* solid = NULL; + struct sdis_scene* scn = NULL; + struct sdis_source* source = NULL; + struct sdis_radiative_env* radenv = NULL; + + /* Miscellaneous */ + struct mesh mesh = MESH_NULL; + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); + + mesh_init(&mesh); + mesh_add_super_shape(&mesh); + mesh_add_ground(&mesh); + + solid = create_solid(dev); + fluid = create_fluid(dev); + interf = create_interface(dev, fluid, solid); + source = create_source(dev); + radenv = create_radenv(dev); + scn = create_scene(dev, &mesh, interf, source, radenv); + cam = create_camera(dev); + + draw(scn, cam); + + /* For debug of scene geometry */ + /*dump_mesh(stdout, + mesh.positions, mesh_nvertices(&mesh), + mesh.indices, mesh_ntriangles(&mesh));*/ + + mesh_release(&mesh); + OK(sdis_camera_ref_put(cam)); + OK(sdis_device_ref_put(dev)); + OK(sdis_interface_ref_put(interf)); + OK(sdis_medium_ref_put(fluid)); + OK(sdis_medium_ref_put(solid)); + OK(sdis_scene_ref_put(scn)); + OK(sdis_source_ref_put(source)); + OK(sdis_radiative_env_ref_put(radenv)); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_enclosure_limit_conditions.c b/src/test_sdis_enclosure_limit_conditions.c @@ -0,0 +1,264 @@ +/* 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 "test_sdis_utils.h" + +/* This test verifies multi-material enclosures, i.e. enclosures whose + * interfaces refer to several media, whereas an enclosure should only cover + * one. This configuration remains valid, however, if these enclosures are used + * to define boundary conditions, such as convective exchange with several + * fluids at known temperature. Although they are valid, it is forbidden to + * place a probe in these enclosures. + * + * +----------------------+ + * | | + * | +----------------+ | + * | | T2 | | + * | | | | T4 + * | | __\ | | __\ + * | | T1 / / T3 | | / / Text + * | | \__/ | | \__/ + * | | | | + * | | T0 | | + * | +----------------+ | + * Y | | + * | +----------------------+ + * o--X */ + +#define CONVECTION_COEF 10 +#define DELTA 0.00625 +#define NREALISATIONS 10000 +#define Text 360.0 + +/******************************************************************************* + * Media + ******************************************************************************/ +#define DEFINE_MEDIUM_GETTER(Func, Val) \ + static double \ + Func(const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) \ + { \ + (void)vtx, (void)data; \ + return (Val); \ + } +DEFINE_MEDIUM_GETTER(solid_get_calorific_capacity, 1) +DEFINE_MEDIUM_GETTER(solid_get_thermal_conductivity, 1) +DEFINE_MEDIUM_GETTER(solid_get_volumic_mass, 1) +DEFINE_MEDIUM_GETTER(solid_get_delta, DELTA) +DEFINE_MEDIUM_GETTER(solid_get_temperature, SDIS_TEMPERATURE_NONE) +DEFINE_MEDIUM_GETTER(fluid_get_temperature, *((double*)sdis_data_cget(data))) +#undef DEFINE_MEDIUM_GETTER + +static struct sdis_medium* +create_solid(struct sdis_device* dev) +{ + 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(dev, &shader, NULL, &solid)); + return solid; +} + +static struct sdis_medium* +create_fluid(struct sdis_device* dev, const double temperature) +{ + struct sdis_fluid_shader shader = DUMMY_FLUID_SHADER; + struct sdis_medium* fluid = NULL; + struct sdis_data* data = NULL; + + OK(sdis_data_create + (dev, sizeof(double), ALIGNOF(double), NULL, &data)); + shader.temperature = fluid_get_temperature; + *((double*)sdis_data_get(data)) = temperature; + OK(sdis_fluid_create(dev, &shader, data, &fluid)); + OK(sdis_data_ref_put(data)); + return fluid; +} + +/******************************************************************************* + * Interface + ******************************************************************************/ +static double +interface_get_convection_coef + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + (void)frag, (void)data; + return CONVECTION_COEF; +} + +static struct sdis_interface* +create_interface + (struct sdis_device* dev, + struct sdis_medium* front, + struct sdis_medium* back) +{ + struct sdis_interface_shader shader = SDIS_INTERFACE_SHADER_NULL; + struct sdis_interface* interf = NULL; + + shader.convection_coef = interface_get_convection_coef; + shader.convection_coef_upper_bound = CONVECTION_COEF; + OK(sdis_interface_create(dev, front, back, &shader, NULL, &interf)); + return interf; +} + +/******************************************************************************* + * Scene + ******************************************************************************/ +static void +get_position(const size_t ivert, double pos[2], void* context) +{ + (void)context; + if(ivert < square_nvertices) { + /* Hole */ + pos[0] = square_vertices[ivert*2 + 0]*0.5 + 0.25; + pos[1] = square_vertices[ivert*2 + 1]*0.5 + 0.25; + } else { + /* Border */ + pos[0] = square_vertices[(ivert-square_nvertices)*2 + 0]; + pos[1] = square_vertices[(ivert-square_nvertices)*2 + 1]; + } +} + +static void +get_indices(const size_t iseg, size_t ids[2], void* context) +{ + (void)context; + if(iseg < square_nsegments) { + /* Hole */ + ids[0] = square_indices[iseg*2 + 0]; + ids[1] = square_indices[iseg*2 + 1]; + } else { + /* Border */ + ids[0] = square_indices[(iseg-square_nsegments)*2 + 0] + square_nvertices; + ids[1] = square_indices[(iseg-square_nsegments)*2 + 1] + square_nvertices; + } +} + +static void +get_interface(const size_t iseg, struct sdis_interface** bound, void* context) +{ + struct sdis_interface** interfs = context; + (void)context; + if(iseg < square_nsegments) { + *bound = interfs[iseg]; /* Hole */ + } else { + *bound = interfs[4]; /* Border */ + } +} + +static struct sdis_scene* +create_scene_2d(struct sdis_device* dev) +{ + struct sdis_scene_create_args args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + struct sdis_interface* interfaces[5] = {NULL}; + struct sdis_medium* fluid[5] = {NULL}; + struct sdis_medium* solid = NULL; + struct sdis_scene* scn = NULL; + + fluid[0] = create_fluid(dev, 280); + fluid[1] = create_fluid(dev, 300); + fluid[2] = create_fluid(dev, 320); + fluid[3] = create_fluid(dev, 340); + fluid[4] = create_fluid(dev, Text); + solid = create_solid(dev); + + interfaces[0] = create_interface(dev, fluid[0], solid); + interfaces[1] = create_interface(dev, fluid[1], solid); + interfaces[2] = create_interface(dev, fluid[2], solid); + interfaces[3] = create_interface(dev, fluid[3], solid); + interfaces[4] = create_interface(dev, solid, fluid[4]); + + args.get_indices = get_indices; + args.get_interface = get_interface; + args.get_position = get_position; + args.nprimitives = square_nsegments*2/*border + hole*/; + args.nvertices = square_nvertices*2/*border + hole*/; + args.context = interfaces; + OK(sdis_scene_2d_create(dev, &args, &scn)); + + OK(sdis_interface_ref_put(interfaces[0])); + OK(sdis_interface_ref_put(interfaces[1])); + OK(sdis_interface_ref_put(interfaces[2])); + OK(sdis_interface_ref_put(interfaces[3])); + OK(sdis_interface_ref_put(interfaces[4])); + OK(sdis_medium_ref_put(fluid[0])); + OK(sdis_medium_ref_put(fluid[1])); + OK(sdis_medium_ref_put(fluid[2])); + OK(sdis_medium_ref_put(fluid[3])); + OK(sdis_medium_ref_put(fluid[4])); + OK(sdis_medium_ref_put(solid)); + return scn; +} + +/******************************************************************************* + * Check + ******************************************************************************/ +static void +solve_probe(struct sdis_scene* scn) +{ + struct sdis_solve_probe_args args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_estimator* estimator = NULL; + + args.position[0] = 0.5; + args.position[1] = 0.5; + args.position[2] = 0; + args.nrealisations = NREALISATIONS; + CHK(sdis_solve_probe(scn, &args, &estimator) == RES_BAD_OP); + + args.position[0] = 2; + args.position[1] = 0.5; + args.position[2] = 0; + args.nrealisations = NREALISATIONS; + OK(sdis_solve_probe(scn, &args, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_ref_put(estimator)); + CHK(Text == T.E); /* Zero variance */ + + args.position[0] = 0.1; + args.position[1] = 0.1; + args.position[2] = 0; + args.nrealisations = NREALISATIONS; + OK(sdis_solve_probe(scn, &args, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_ref_put(estimator)); + printf("T(%g, %g) ~ %g +/- %g\n", SPLIT2(args.position), T.E, T.SE); +} + +/******************************************************************************* + * Test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct sdis_device* dev = NULL; + struct sdis_scene* scn_2d = NULL; + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); + scn_2d = create_scene_2d(dev); + + solve_probe(scn_2d); + + OK(sdis_device_ref_put(dev)); + OK(sdis_scene_ref_put(scn_2d)); + + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_external_flux.c b/src/test_sdis_external_flux.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 "test_sdis_utils.h" +#include "sdis.h" + +#include <rsys/rsys.h> + +/* + * The system consists of 2 parallelepipeds: a vertical one called the wall, and + * a horizontal one representing the floor. The wall is a black body, while the + * floor is a perfectly reflective surface. The surrounding fluid has a fixed + * temperature and, finally, an external spherical source represents the sun. + * This test calculates the steady temperature at a position in the wall and + * compares it with the analytical solution given for a perfectly diffuse or + * specular ground. + * + * (-0.1,1500) + * +---+ External source + * | E=1 T_FLUID ## + * Probe x | _\ #### + * | | / / ## + * +---+ \__/ + * (0,500) + * + * (0,0) + * Y *--------E=0------------- - - - + * | | + * o--X *------------------------ - - - + * / (0,-1) + * Z + * + */ + +#define T_FLUID 300.0 /* [K] */ +#define T_REF 300.0 /* [K] */ + +/******************************************************************************* + * Geometries + ******************************************************************************/ +static const double positions_2d[] = { + /* Ground */ + 1.0e12, -1.0, + 0.0, -1.0, + 0.0, 0.0, + 1.0e12, 0.0, + + /* Wall */ + 0.0, 500.0, + -0.1, 500.0, + -0.1, 1500.0, + 0.0, 1500.0 +}; +static const size_t nvertices_2d = sizeof(positions_2d) / (sizeof(double)*2); + +static const double positions_3d[] = { + /* Ground */ + 0.0, -1.0, -1.0e6, + 1.0e12, -1.0, -1.0e6, + 0.0, 1.0, -1.0e6, + 1.0e12, 1.0, -1.0e6, + 0.0, -1.0, 1.0e6, + 1.0e12, -1.0, 1.0e6, + 0.0, 1.0, 1.0e6, + 1.0e12, 1.0, 1.0e6, + + /* Wall */ + -0.1, 500.0, -500.0, + 0.0, 500.0, -500.0, + -0.1, 1500.0, -500.0, + 0.0, 1500.0, -500.0, + -0.1, 500.0, 500.0, + 0.0, 500.0, 500.0, + -0.1, 1500.0, 500.0, + 0.0, 1500.0, 500.0 +}; +static const size_t nvertices_3d = sizeof(positions_3d) / (sizeof(double)*3); + +static const size_t indices_2d[] = { + /* Ground */ + 0, 1, /* -y */ + 1, 2, /* -x */ + 2, 3, /* +y */ + 3, 0, /* +x */ + + /* Wall */ + 4, 5, /* -y */ + 5, 6, /* -x */ + 6, 7, /* +y */ + 7, 4 /* +x */ +}; +static const size_t nsegments = sizeof(indices_2d) / (sizeof(size_t)*2); + +static const size_t indices_3d[] = { + /* Ground */ + 0, 2, 1, 1, 2, 3, /* -z */ + 0, 4, 2, 2, 4, 6, /* -x */ + 4, 5, 6, 6, 5, 7, /* +z */ + 3, 7, 5, 5, 1, 3, /* +x */ + 2, 6, 7, 7, 3, 2, /* +y */ + 0, 1, 5, 5, 4, 0, /* -y */ + + /* Wall */ + 8, 10, 9, 9, 10, 11, /* -z */ + 8, 12, 10, 10, 12, 14, /* -x */ + 12, 13, 14, 14, 13, 15, /* +z */ + 11, 15, 13, 13, 9, 11, /* +x */ + 10, 14, 15, 15, 11, 10, /* +y */ + 8, 9, 13, 13, 12, 8 /* -y */ +}; +static const size_t ntriangles = sizeof(indices_3d) / (sizeof(size_t)*3); + +/******************************************************************************* + * Media + ******************************************************************************/ +#define MEDIUM_PROP(Type, Prop, Val) \ + static double \ + Type##_get_##Prop \ + (const struct sdis_rwalk_vertex* vtx, \ + struct sdis_data* data) \ + { \ + (void)vtx, (void)data; /* Avoid the "unused variable" warning */ \ + return Val; \ + } +MEDIUM_PROP(medium, volumic_mass, 1700) /* [kj/m^3] */ +MEDIUM_PROP(medium, calorific_capacity, 800) /* [J/K/Kg] */ +MEDIUM_PROP(solid, thermal_conductivity, 1.15) /* [W/m/K] */ +MEDIUM_PROP(solid, delta, 0.1/20.0) /* [m] */ +MEDIUM_PROP(solid, temperature, SDIS_TEMPERATURE_NONE/*<=> unknown*/) /* [K] */ +MEDIUM_PROP(fluid, temperature, T_FLUID) /* [K] */ +#undef MEDIUM_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 = medium_get_calorific_capacity; + shader.thermal_conductivity = solid_get_thermal_conductivity; + shader.volumic_mass = medium_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_fluid(struct sdis_device* sdis) +{ + struct sdis_fluid_shader shader = SDIS_FLUID_SHADER_NULL; + struct sdis_medium* fluid = NULL; + + shader.calorific_capacity = medium_get_calorific_capacity; + shader.volumic_mass = medium_get_volumic_mass; + shader.temperature = fluid_get_temperature; + OK(sdis_fluid_create(sdis, &shader, NULL, &fluid)); + return fluid; +} + +/******************************************************************************* + * Interfaces + ******************************************************************************/ +struct interface { + double emissivity; + double specular_fraction; + double convection_coef; /* [W/m^2/K] */ +}; + +#define INTERF_PROP(Prop, Val) \ + static double \ + interface_get_##Prop \ + (const struct sdis_interface_fragment* frag, \ + struct sdis_data* data) \ + { \ + (void)frag, (void)data; /* Avoid the "unused variable" warning */ \ + return Val; \ + } +INTERF_PROP(temperature, SDIS_TEMPERATURE_NONE/*<=> unknown*/) /* [K] */ +INTERF_PROP(reference_temperature, T_REF) /* [K] */ +#undef INTERF_PROP + +#define INTERF_PROP(Prop) \ + static double \ + interface_get_##Prop \ + (const struct sdis_interface_fragment* frag, \ + const unsigned source_id, \ + struct sdis_data* data) \ + { \ + struct interface* interf_data = NULL; \ + (void)frag, (void)source_id; /* Avoid the "unused variable" warning */ \ + interf_data = sdis_data_get(data); \ + return source_id == SDIS_INTERN_SOURCE_ID ? 0 : interf_data->Prop; \ + } +INTERF_PROP(emissivity) +INTERF_PROP(specular_fraction) +#undef INTERF_PROP + +static double /* [W/m^2/K] */ +interface_get_convection_coef + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + struct interface* interf_data = NULL; + (void)frag; /* Avoid the "unused variable" warning */ + interf_data = sdis_data_get(data); + return interf_data->convection_coef; +} + +static struct sdis_interface* +create_interface + (struct sdis_device* sdis, + struct sdis_medium* front, + struct sdis_medium* back, + const double emissivity, + const double convection_coef, + struct interface** out_interf_data) /* May be NULL */ +{ + struct sdis_data* data = NULL; + struct sdis_interface* interf = NULL; + struct sdis_interface_shader shader = SDIS_INTERFACE_SHADER_NULL; + struct interface* interf_data = NULL; + + OK(sdis_data_create + (sdis, sizeof(struct interface), ALIGNOF(struct interface), NULL, &data)); + interf_data = sdis_data_get(data); + interf_data->emissivity = emissivity; + interf_data->convection_coef = convection_coef; /* [W/m^2/K] */ + interf_data->specular_fraction = 0; /* Diffuse */ + if(out_interf_data) *out_interf_data = interf_data; + + shader.front.temperature = interface_get_temperature; + shader.back.temperature = interface_get_temperature; + shader.back.reference_temperature = interface_get_reference_temperature; + shader.back.emissivity = interface_get_emissivity; + shader.back.specular_fraction = interface_get_specular_fraction; + shader.convection_coef = interface_get_convection_coef; + shader.convection_coef_upper_bound = convection_coef; + OK(sdis_interface_create(sdis, front, back, &shader, data, &interf)); + OK(sdis_data_ref_put(data)); + + return interf; +} + +/******************************************************************************* + * External source + ******************************************************************************/ +static void +source_get_position + (const double time, + double pos[3], + struct sdis_data* data) +{ + const double elevation = MDEG2RAD(30); /* [radian] */ + const double distance = 1.5e11; /* [m] */ + (void)time, (void)data; /* Avoid the "unusued variable" warning */ + + pos[0] = cos(elevation) * distance; + pos[1] = sin(elevation) * distance; + pos[2] = 0; +} + +static double +source_get_power(const double time, struct sdis_data* data) +{ + (void)time, (void)data; /* Avoid the "unusued variable" warning */ + return 3.845e26; /* [W] */ +} + +static struct sdis_source* +create_source(struct sdis_device* sdis) +{ + struct sdis_spherical_source_shader shader = SDIS_SPHERICAL_SOURCE_SHADER_NULL; + struct sdis_source* src = NULL; + + shader.position = source_get_position; + shader.power = source_get_power; + shader.radius = 6.5991756e8; /* [m] */ + OK(sdis_spherical_source_create(sdis, &shader, NULL, &src)); + return src; +} + +/******************************************************************************* + * Radiative environment + ******************************************************************************/ +static double +radenv_get_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray, (void)data; + return 0; /* [K] */ +} + +static double +radenv_get_reference_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray, (void)data; + return T_REF; /* [K] */ +} + +static struct sdis_radiative_env* +create_radenv(struct sdis_device* sdis) +{ + struct sdis_radiative_env_shader shader = SDIS_RADIATIVE_ENV_SHADER_NULL; + struct sdis_radiative_env* radenv = NULL; + + shader.temperature = radenv_get_temperature; + shader.reference_temperature = radenv_get_reference_temperature; + OK(sdis_radiative_env_create(sdis, &shader, NULL, &radenv)); + return radenv; +} + +/******************************************************************************* + * Scene + ******************************************************************************/ +struct scene_context { + struct sdis_interface* interf_ground; + struct sdis_interface* interf_wall; +}; +static const struct scene_context SCENE_CONTEXT_NULL = {NULL, NULL}; + +static void +scene_get_indices_2d(const size_t iseg, size_t ids[2], void* ctx) +{ + struct scene_context* context = ctx; + CHK(ids && context && iseg < nsegments); + ids[0] = (unsigned)indices_2d[iseg*2+0]; + ids[1] = (unsigned)indices_2d[iseg*2+1]; +} + +static void +scene_get_indices_3d(const size_t itri, size_t ids[3], void* ctx) +{ + struct scene_context* context = ctx; + CHK(ids && context && itri < ntriangles); + ids[0] = (unsigned)indices_3d[itri*3+0]; + ids[1] = (unsigned)indices_3d[itri*3+1]; + ids[2] = (unsigned)indices_3d[itri*3+2]; +} + +static void +scene_get_interface_2d(const size_t iseg, struct sdis_interface** interf, void* ctx) +{ + struct scene_context* context = ctx; + CHK(interf && context && iseg < nsegments); + *interf = iseg < 4 ? context->interf_ground : context->interf_wall; +} + +static void +scene_get_interface_3d(const size_t itri, struct sdis_interface** interf, void* ctx) +{ + struct scene_context* context = ctx; + CHK(interf && context && itri < ntriangles); + *interf = itri < 12 ? context->interf_ground : context->interf_wall; +} + +static void +scene_get_position_2d(const size_t ivert, double pos[2], void* ctx) +{ + struct scene_context* context = ctx; + CHK(pos && context && ivert < nvertices_2d); + pos[0] = positions_2d[ivert*2+0]; + pos[1] = positions_2d[ivert*2+1]; +} + +static void +scene_get_position_3d(const size_t ivert, double pos[3], void* ctx) +{ + struct scene_context* context = ctx; + CHK(pos && context && ivert < nvertices_3d); + pos[0] = positions_3d[ivert*3+0]; + pos[1] = positions_3d[ivert*3+1]; + pos[2] = positions_3d[ivert*3+2]; +} + +static struct sdis_scene* +create_scene_2d + (struct sdis_device* sdis, + struct sdis_interface* interf_ground, + struct sdis_interface* interf_wall, + struct sdis_source* source, + struct sdis_radiative_env* radenv) +{ + struct sdis_scene* scn = NULL; + struct sdis_source* src = NULL; + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + struct scene_context context = SCENE_CONTEXT_NULL; + + context.interf_ground = interf_ground; + context.interf_wall = interf_wall; + + scn_args.get_indices = scene_get_indices_2d; + scn_args.get_interface = scene_get_interface_2d; + scn_args.get_position = scene_get_position_2d; + scn_args.nprimitives = nsegments; + scn_args.nvertices = nvertices_2d; + scn_args.t_range[0] = T_REF; /* [K] */ + scn_args.t_range[1] = T_REF; /* [K] */ + scn_args.source = source; + scn_args.radenv = radenv; + scn_args.context = &context; + OK(sdis_scene_2d_create(sdis, &scn_args, &scn)); + + BA(sdis_scene_get_source(NULL, &src)); + BA(sdis_scene_get_source(scn, NULL)); + OK(sdis_scene_get_source(scn, &src)); + CHK(src == source); + + return scn; +} + +static struct sdis_scene* +create_scene_3d + (struct sdis_device* sdis, + struct sdis_interface* interf_ground, + struct sdis_interface* interf_wall, + struct sdis_source* source, + struct sdis_radiative_env* radenv) +{ + struct sdis_scene* scn = NULL; + struct sdis_source* src = NULL; + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + struct scene_context context = SCENE_CONTEXT_NULL; + + context.interf_ground = interf_ground; + context.interf_wall = interf_wall; + + scn_args.get_indices = scene_get_indices_3d; + scn_args.get_interface = scene_get_interface_3d; + scn_args.get_position = scene_get_position_3d; + scn_args.nprimitives = ntriangles; + scn_args.nvertices = nvertices_3d; + scn_args.t_range[0] = 0; /* [K] */ + scn_args.t_range[1] = 0; /* [K] */ + scn_args.source = source; + scn_args.radenv = radenv; + scn_args.context = &context; + OK(sdis_scene_create(sdis, &scn_args, &scn)); + + BA(sdis_scene_get_source(NULL, &src)); + BA(sdis_scene_get_source(scn, NULL)); + OK(sdis_scene_get_source(scn, &src)); + CHK(src == source); + + return scn; +} + +/******************************************************************************* + * Validations + ******************************************************************************/ +static void +check + (struct sdis_scene* scn, + const size_t nrealisations, + const double analytical_ref, + const int is_master_process) +{ + struct sdis_solve_probe_args probe_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_estimator* estimator = NULL; + + probe_args.position[0] = -0.05; + probe_args.position[1] = 1000; + probe_args.position[2] = 0; + probe_args.nrealisations = nrealisations; + OK(sdis_solve_probe(scn, &probe_args, &estimator)); + + if(!is_master_process) return; + + OK(sdis_estimator_get_temperature(estimator, &T)); + printf("T(%g, %g, %g) = %g ~ %g +/- %g\n", + SPLIT3(probe_args.position), analytical_ref, T.E, T.SE); + OK(sdis_estimator_ref_put(estimator)); + + CHK(eq_eps(analytical_ref, T.E, 3*T.SE)); +} + +static void +check_green + (struct sdis_scene* scn, + const size_t nrealisations, + const double analytical_ref, + const int is_master_process) +{ + struct sdis_solve_probe_args probe_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_green_function* green = NULL; + struct sdis_estimator* estimator = NULL; + + probe_args.position[0] = -0.05; + probe_args.position[1] = 1000; + probe_args.position[2] = 0; + probe_args.nrealisations = nrealisations; + OK(sdis_solve_probe_green_function(scn, &probe_args, &green)); + + if(!is_master_process) return; + + OK(sdis_green_function_solve(green, &estimator)); + check_green_function(green); + + OK(sdis_estimator_get_temperature(estimator, &T)); + + printf("T(%g, %g, %g) = %g ~ %g +/- %g\n", + SPLIT3(probe_args.position), analytical_ref, T.E, T.SE); + OK(sdis_green_function_ref_put(green)); + OK(sdis_estimator_ref_put(estimator)); + + CHK(eq_eps(analytical_ref, T.E, 3*T.SE)); +} + +/******************************************************************************* + * The test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct sdis_device* dev = NULL; + struct sdis_medium* fluid = NULL; + struct sdis_medium* solid = NULL; + struct sdis_interface* interf_ground = NULL; + struct sdis_interface* interf_wall = NULL; + struct sdis_radiative_env* radenv = NULL; + struct sdis_scene* scn_2d = NULL; + struct sdis_scene* scn_3d = NULL; + struct sdis_source* src = NULL; + + struct interface* ground_interf_data = NULL; + int is_master_process = 0; + (void) argc, (void)argv; + + create_default_device(&argc, &argv, &is_master_process, &dev); + + fluid = create_fluid(dev); + solid = create_solid(dev); + interf_ground = create_interface + (dev, solid, fluid, 0/*emissivity*/, 0/*h*/, &ground_interf_data); + interf_wall = create_interface + (dev, solid, fluid, 1/*emissivity*/, 10/*h*/, NULL); + src = create_source(dev); + radenv = create_radenv(dev); + scn_2d = create_scene_2d(dev, interf_ground, interf_wall, src, radenv); + scn_3d = create_scene_3d(dev, interf_ground, interf_wall, src, radenv); + + ground_interf_data->specular_fraction = 0; /* Lambertian */ + check(scn_2d, 10000, 375.88, is_master_process); + check_green(scn_3d, 10000, 375.88, is_master_process); + + ground_interf_data->specular_fraction = 1; /* Specular */ + check(scn_2d, 100000, 417.77, is_master_process); + check_green(scn_3d, 100000, 417.77, is_master_process); + + OK(sdis_medium_ref_put(fluid)); + OK(sdis_medium_ref_put(solid)); + OK(sdis_interface_ref_put(interf_ground)); + OK(sdis_interface_ref_put(interf_wall)); + OK(sdis_radiative_env_ref_put(radenv)); + OK(sdis_source_ref_put(src)); + OK(sdis_scene_ref_put(scn_2d)); + OK(sdis_scene_ref_put(scn_3d)); + + free_default_device(dev); + + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_external_flux_with_diffuse_radiance.c b/src/test_sdis_external_flux_with_diffuse_radiance.c @@ -0,0 +1,449 @@ +/* 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 "test_sdis_utils.h" +#include "sdis.h" + +#include <rsys/rsys.h> +#include <star/s3dut.h> + +/* + * The system is a sphere with an emissivity of 1. It is illuminated by an + * external spherical source that is sufficiently far away from the sphere + * radius to assume a constant flux density received per m^2 of surface + * perpendicular to the direction towards the source center. + * + * In such situation, the value of the surface temperature is equal to : + * + * Ts = [q/(4*sigma)+Tamb^4]^0.25 + * q = P/(4PI*d^2) + * + * with Tamb the temperature of the environment, sigma the Boltzmann constant, + * P the source power and d the distance of the source from the center of + * the sphere. + * + * If one adds a diffuse radiance Ldiff, the surface temperature becomes : + * + * Ts = [(q/4+Ldiff*PI)/sigma+Tamb^4]^0.25 + * + * The test checks that we retrieved these analatycal results by Monte Carlo + * + * + * R = 0.01 m External source + * __ d = 10 m ### + * T = ? -> / .\ ..............................##### r = 0.3 m + * \__/ ##### + * E=1 ### + * P = 3^7 W + */ + +/* The source */ +#define SOURCE_POWER 1.0e5 /* [W] */ +#define SOURCE_RADIUS 0.3 /* [m/fp_to_meter] */ +#define SOURCE_DISTANCE 10 /* [m/fp_to_meter] */ + +/* Miscellaneous */ +#define SPHERE_RADIUS 0.01 /* [m/fp_to_meter] */ +#define SPHERE_T_REF 320 /* [K] */ +#define T_RAD 300.0 /* [K] */ +#define T_REF 300.0 /* [K] */ +#define T_RAD4 (T_RAD*T_RAD*T_RAD*T_RAD) + +#define NREALISATIONS 10000 + +/******************************************************************************* + * Geometry + ******************************************************************************/ +static struct s3dut_mesh* +create_sphere(void) +{ + struct s3dut_mesh* mesh = NULL; + OK(s3dut_create_sphere(NULL, SPHERE_RADIUS, 128, 64, &mesh)); + return mesh; +} + +/******************************************************************************* + * Media + ******************************************************************************/ +#define MEDIUM_PROP(Type, Prop, Val) \ + static double \ + Type##_get_##Prop \ + (const struct sdis_rwalk_vertex* vtx, \ + struct sdis_data* data) \ + { \ + (void)vtx, (void)data; /* Avoid the "unused variable" warning */ \ + return Val; \ + } +MEDIUM_PROP(medium, volumic_mass, 1) /* [kj/m^3] */ +MEDIUM_PROP(medium, calorific_capacity, 1) /* [J/K/Kg] */ +MEDIUM_PROP(medium, temperature, SDIS_TEMPERATURE_NONE) /* [K] */ +MEDIUM_PROP(solid, thermal_conductivity, 1) /* [W/m/K] */ +MEDIUM_PROP(solid, delta, (2*SPHERE_RADIUS)/10.0) /* [m] */ +#undef MEDIUM_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 = medium_get_calorific_capacity; + shader.thermal_conductivity = solid_get_thermal_conductivity; + shader.volumic_mass = medium_get_volumic_mass; + shader.delta = solid_get_delta; + shader.temperature = medium_get_temperature; + OK(sdis_solid_create(sdis, &shader, NULL, &solid)); + return solid; +} + +static struct sdis_medium* +create_fluid(struct sdis_device* sdis) +{ + struct sdis_fluid_shader shader = SDIS_FLUID_SHADER_NULL; + struct sdis_medium* fluid = NULL; + + shader.calorific_capacity = medium_get_calorific_capacity; + shader.volumic_mass = medium_get_volumic_mass; + shader.temperature = medium_get_temperature; + OK(sdis_fluid_create(sdis, &shader, NULL, &fluid)); + return fluid; +} + +/******************************************************************************* + * Interface + ******************************************************************************/ +static double +interface_get_emissivity + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) +{ + (void)frag, (void)source_id, (void)data; + return 1; +} + +static double +interface_get_reference_temperature + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + (void)frag, (void)data; + return SPHERE_T_REF; /* [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.emissivity = interface_get_emissivity; + shader.front.reference_temperature = interface_get_reference_temperature; + OK(sdis_interface_create(sdis, front, back, &shader, NULL, &interf)); + return interf; +} + +/******************************************************************************* + * Radiative environment + ******************************************************************************/ +#define RADENV_PROP(Prop, Val) \ + static double \ + radenv_get_##Prop \ + (const struct sdis_radiative_ray* ray, \ + struct sdis_data* data) \ + { \ + (void)ray, (void)data; \ + return (Val); /* [K] */ \ + } +RADENV_PROP(temperature, T_RAD) +RADENV_PROP(reference_temperature, T_REF) +#undef RADENV_PROP + +static struct sdis_radiative_env* +create_radenv(struct sdis_device* sdis) +{ + struct sdis_radiative_env_shader shader = SDIS_RADIATIVE_ENV_SHADER_NULL; + struct sdis_radiative_env* radenv = NULL; + + shader.temperature = radenv_get_temperature; + shader.reference_temperature = radenv_get_reference_temperature; + OK(sdis_radiative_env_create(sdis, &shader, NULL, &radenv)); + return radenv; +} + +/******************************************************************************* + * External source + ******************************************************************************/ +static void +source_get_position + (const double time, + double pos[3], + struct sdis_data* data) +{ + (void)time, (void)data; /* Avoid the "unusued variable" warning */ + pos[0] = SOURCE_DISTANCE; + pos[1] = 0; + pos[2] = 0; +} + +static double +source_get_power(const double time, struct sdis_data* data) +{ + (void)time, (void)data; /* Avoid the "unused variable" warning */ + return SOURCE_POWER; /* [W] */ +} + +static double +source_get_diffuse_radiance + (const double time, + const double dir[3], + struct sdis_data* data) +{ + const double* Ldiff = sdis_data_cget(data);; + (void)time, (void)dir; /* Avoid the "unused variable" warning */ + return *Ldiff; /* [W/m^2/sr] */ +} + +static struct sdis_source* +create_source(struct sdis_device* sdis, double** diffuse_radiance) +{ + struct sdis_spherical_source_shader shader = SDIS_SPHERICAL_SOURCE_SHADER_NULL; + struct sdis_data* data = NULL; + struct sdis_source* src = NULL; + + OK(sdis_data_create(sdis, sizeof(double), ALIGNOF(double), NULL, &data)); + *diffuse_radiance = sdis_data_get(data); + **diffuse_radiance = 0; + + shader.position = source_get_position; + shader.power = source_get_power; + shader.diffuse_radiance = source_get_diffuse_radiance; + shader.radius = SOURCE_RADIUS; + OK(sdis_spherical_source_create(sdis, &shader, data, &src)); + OK(sdis_data_ref_put(data)); + return src; +} + +/******************************************************************************* + * The scene + ******************************************************************************/ +struct scene_context { + const struct s3dut_mesh_data* mesh; + struct sdis_interface* interf; +}; +static const struct scene_context SCENE_CONTEXT_NULL = {NULL, 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->nprimitives); + ids[0] = (unsigned)context->mesh->indices[itri*3+0]; + ids[1] = (unsigned)context->mesh->indices[itri*3+1]; + ids[2] = (unsigned)context->mesh->indices[itri*3+2]; +} + +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->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->nvertices); + 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, + const struct s3dut_mesh_data* mesh, + struct sdis_interface* interf, + struct sdis_source* source, + struct sdis_radiative_env* radenv) +{ + 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.mesh = mesh; + 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 = mesh->nprimitives; + scn_args.nvertices = mesh->nvertices; + scn_args.t_range[0] = MMIN(T_REF, SPHERE_T_REF); + scn_args.t_range[1] = MMAX(T_REF, SPHERE_T_REF); + scn_args.source = source; + scn_args.radenv = radenv; + scn_args.context = &context; + OK(sdis_scene_create(sdis, &scn_args, &scn)); + return scn; +} + +/******************************************************************************* + * Validations + ******************************************************************************/ +static double +analytic_temperature(const double Ldiff) +{ + const double q = SOURCE_POWER/(4*PI*SOURCE_DISTANCE*SOURCE_DISTANCE); + const double Ts = pow((q/4 + Ldiff*PI)/BOLTZMANN_CONSTANT + T_RAD4, 0.25); + return Ts; +} + +static void +check + (struct sdis_scene* scn, + const struct s3dut_mesh_data* mesh, + const double Ldiff) +{ + struct sdis_solve_boundary_args args = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_estimator* estimator = NULL; + enum sdis_side* sides = NULL; + double ref = 0; + size_t i = 0; + + sides = mem_alloc(mesh->nprimitives*sizeof(*sides)); + FOR_EACH(i, 0, mesh->nprimitives) sides[i] = SDIS_FRONT; + + args.primitives = mesh->indices; + args.sides = sides; + args.nprimitives = mesh->nprimitives; + args.nrealisations = NREALISATIONS; + OK(sdis_solve_boundary(scn, &args, &estimator)); + + OK(sdis_estimator_get_temperature(estimator, &T)); + ref = analytic_temperature(Ldiff); + printf("Ts = %g ~ %g +/- %g\n", ref, T.E, T.SE); + OK(sdis_estimator_ref_put(estimator)); + + CHK(eq_eps(T.E, ref, T.SE * 3)); + + mem_rm(sides); +} + +static void +solve_green(struct sdis_green_function* green, const double Ldiff) +{ + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_estimator* estimator = NULL; + double ref = 0; + + OK(sdis_green_function_solve(green, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + + ref = analytic_temperature(Ldiff); + printf("Ts = %g ~ %g +/- %g\n", ref, T.E, T.SE); + CHK(eq_eps(T.E, ref, T.SE * 3)); + + OK(sdis_estimator_ref_put(estimator)); +} + +static void +check_green + (struct sdis_scene* scn, + const struct s3dut_mesh_data* mesh, + double* Ldiff) +{ + struct sdis_solve_boundary_args args = SDIS_SOLVE_BOUNDARY_ARGS_DEFAULT; + struct sdis_green_function* green = NULL; + enum sdis_side* sides = NULL; + size_t i = 0; + + CHK(Ldiff); + + sides = mem_alloc(mesh->nprimitives*sizeof(*sides)); + FOR_EACH(i, 0, mesh->nprimitives) sides[i] = SDIS_FRONT; + + *Ldiff = 0; + + args.primitives = mesh->indices; + args.sides = sides; + args.nprimitives = mesh->nprimitives; + args.nrealisations = NREALISATIONS; + OK(sdis_solve_boundary_green_function(scn, &args, &green)); + + solve_green(green, (*Ldiff = 50)); + solve_green(green, (*Ldiff = 45)); + solve_green(green, (*Ldiff = 40)); + + OK(sdis_green_function_ref_put(green)); + + mem_rm(sides); +} + +/******************************************************************************* + * The test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + /* Stardis */ + struct sdis_device* dev = NULL; + struct sdis_interface* interf = NULL; + struct sdis_medium* fluid = NULL; + struct sdis_medium* solid = NULL; + struct sdis_radiative_env* radenv = NULL; + struct sdis_scene* scene = NULL; + struct sdis_source* source = NULL; + + /* Miscellaneous */ + struct s3dut_mesh_data mesh; + struct s3dut_mesh* sphere = NULL; + double* Ldiff = NULL; + + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); + + sphere = create_sphere(); + OK(s3dut_mesh_get_data(sphere, &mesh)); + + fluid = create_fluid(dev); + solid = create_solid(dev); + interf = create_interface(dev, fluid, solid); + radenv = create_radenv(dev); + source = create_source(dev, &Ldiff); + + scene = create_scene(dev, &mesh, interf, source, radenv); + + check(scene, &mesh, (*Ldiff = 50)); + check_green(scene, &mesh, Ldiff); + + OK(s3dut_mesh_ref_put(sphere)); + OK(sdis_device_ref_put(dev)); + OK(sdis_interface_ref_put(interf)); + OK(sdis_medium_ref_put(fluid)); + OK(sdis_medium_ref_put(solid)); + OK(sdis_radiative_env_ref_put(radenv)); + OK(sdis_scene_ref_put(scene)); + OK(sdis_source_ref_put(source)); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_flux.c b/src/test_sdis_flux.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -43,7 +43,6 @@ * (0,0,0) ///// */ -#define UNKNOWN_TEMPERATURE -1 #define N 10000 #define PHI 10.0 @@ -96,7 +95,7 @@ solid_get_temperature (void)data; CHK(vtx != NULL); if(vtx->time > 0) - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; else return T0; } @@ -146,7 +145,7 @@ solve struct sdis_solve_probe_args solve_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; size_t nreals; size_t nfails; - double ref = -1; + double ref = SDIS_TEMPERATURE_NONE; const int nsimuls = 4; int isimul; enum sdis_scene_dimension dim; @@ -392,7 +391,7 @@ main(int argc, char** argv) /* Create the adiabatic interface */ OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); interf_props = sdis_data_get(data); - interf_props->temperature = UNKNOWN_TEMPERATURE; + interf_props->temperature = SDIS_TEMPERATURE_NONE; interf_props->phi = 0; OK(sdis_interface_create (dev, solid, fluid, &interf_shader, data, &interf_adiabatic)); @@ -409,7 +408,7 @@ main(int argc, char** argv) /* Create the PHI interface */ OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); interf_props = sdis_data_get(data); - interf_props->temperature = UNKNOWN_TEMPERATURE; + interf_props->temperature = SDIS_TEMPERATURE_NONE; interf_props->phi = PHI; OK(sdis_interface_create (dev, solid, fluid, &interf_shader, data, &interf_phi)); diff --git a/src/test_sdis_flux2.c b/src/test_sdis_flux2.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -16,8 +16,6 @@ #include "sdis.h" #include "test_sdis_utils.h" -#define UNKNOWN_TEMPERATURE -1 - /* This test consists in solving the temperature profile in a solid slab * surrounded by two different convective and radiative temperatures. The * conductivity of the solid material is known, as well as its thickness. A net @@ -175,7 +173,7 @@ solid_get_temperature CHK(vtx && solid); if(vtx->time > 0) { - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } else { /* The initial temperature is a linear profile between T1 and T2, where T1 * and T2 are the temperature on the left and right slab boundary, @@ -283,17 +281,23 @@ interface_get_convection_coef static double interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) { const struct interf* interf = sdis_data_cget(data); + (void)source_id; CHK(frag && interf); return interf->emissivity; } static double interface_get_specular_fraction - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) { + (void)source_id; CHK(frag && data); return 0; /* Unused */ } @@ -349,19 +353,53 @@ create_interface } /******************************************************************************* + * Create the radiative environment + ******************************************************************************/ +static double +radenv_get_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray, (void)data; + return 320; /* [K] */ +} + +static double +radenv_get_reference_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray, (void)data; + return 300; /* [K] */ +} + +static struct sdis_radiative_env* +create_radenv(struct sdis_device* sdis) +{ + struct sdis_radiative_env_shader shader = SDIS_RADIATIVE_ENV_SHADER_NULL; + struct sdis_radiative_env* radenv = NULL; + + shader.temperature = radenv_get_temperature; + shader.reference_temperature = radenv_get_reference_temperature; + OK(sdis_radiative_env_create(sdis, &shader, NULL, &radenv)); + return radenv; +} + +/******************************************************************************* * Create scene ******************************************************************************/ static void create_scene_3d (struct sdis_device* dev, struct sdis_interface* interfaces[INTERFACES_COUNT__], + struct sdis_radiative_env* radenv, struct sdis_scene** scn) { struct geometry geom; struct sdis_interface* prim_interfaces[32]; struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; - CHK(dev && interfaces && scn); + CHK(dev && interfaces && radenv && scn); /* Setup the per primitive interface of the solid medium */ prim_interfaces[0] = prim_interfaces[1] = interfaces[ADIABATIC]; @@ -390,8 +428,7 @@ create_scene_3d scn_args.t_range[0] = 300; scn_args.t_range[1] = 300; scn_args.context = &geom; - scn_args.trad.temperature = 320; - scn_args.trad.reference = 300; + scn_args.radenv = radenv; OK(sdis_scene_create(dev, &scn_args, scn)); } @@ -432,6 +469,7 @@ int main(int argc, char** argv) { struct sdis_device* dev = NULL; + struct sdis_radiative_env* radenv = NULL; struct sdis_scene* scn_3d = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* dummy = NULL; @@ -467,6 +505,8 @@ main(int argc, char** argv) OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); + radenv = create_radenv(dev); + /* Solid medium */ solid_props.lambda = 1.15; solid_props.rho = 1700; @@ -489,8 +529,8 @@ main(int argc, char** argv) interf_props.h = 0; interf_props.emissivity = 0; interf_props.phi = SDIS_FLUX_NONE; - interf_props.temperature = UNKNOWN_TEMPERATURE; - interf_props.Tref = UNKNOWN_TEMPERATURE; + interf_props.temperature = SDIS_TEMPERATURE_NONE; + interf_props.Tref = SDIS_TEMPERATURE_NONE; create_interface(dev, solid, dummy, &interf_props, &interfaces[ADIABATIC]); /* Interfaces with a fixed temperature */ @@ -506,7 +546,7 @@ main(int argc, char** argv) interf_props.h = 2; interf_props.emissivity = 1; interf_props.phi = 10000; - interf_props.temperature = UNKNOWN_TEMPERATURE; + interf_props.temperature = SDIS_TEMPERATURE_NONE; interf_props.Tref = 300; create_interface (dev, solid, fluid1, &interf_props, &interfaces[SOLID_FLUID_WITH_FLUX]); @@ -514,18 +554,19 @@ main(int argc, char** argv) interf_props.h = 8; interf_props.emissivity = 1; interf_props.phi = SDIS_FLUX_NONE; - interf_props.temperature = UNKNOWN_TEMPERATURE; + interf_props.temperature = SDIS_TEMPERATURE_NONE; interf_props.Tref = 300; create_interface (dev, solid, fluid2, &interf_props, &interfaces[SOLID_FLUID]); - create_scene_3d(dev, interfaces, &scn_3d); + create_scene_3d(dev, interfaces, radenv, &scn_3d); FOR_EACH(iprobe, 0, nprobes) { check(scn_3d, &probes[iprobe]); } /* Release memory */ + OK(sdis_radiative_env_ref_put(radenv)); OK(sdis_scene_ref_put(scn_3d)); OK(sdis_medium_ref_put(solid)); OK(sdis_medium_ref_put(dummy)); diff --git a/src/test_sdis_flux_with_h.c b/src/test_sdis_flux_with_h.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -107,7 +107,7 @@ solid_get_temperature struct sdis_data* data) { (void)data, (void)vtx; - return -1; + return SDIS_TEMPERATURE_NONE; } static double diff --git a/src/test_sdis_interface.c b/src/test_sdis_interface.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -88,18 +88,18 @@ main(int argc, char** argv) OK(CREATE(dev, solid, solid, &shader, NULL, &interf)); OK(sdis_interface_ref_put(interf)); - shader.back.emissivity = dummy_interface_getter; + shader.back.emissivity = dummy_radiative_interface_getter; OK(CREATE(dev, solid, fluid, &shader, NULL, &interf)); OK(sdis_interface_ref_put(interf)); - shader.back.specular_fraction = dummy_interface_getter; + shader.back.specular_fraction = dummy_radiative_interface_getter; OK(CREATE(dev, solid, fluid, &shader, NULL, &interf)); OK(sdis_interface_ref_put(interf)); shader.back = SDIS_INTERFACE_SIDE_SHADER_NULL; - shader.front.emissivity = dummy_interface_getter; + shader.front.emissivity = dummy_radiative_interface_getter; OK(CREATE(dev, solid, fluid, &shader, NULL, &interf)); /* Warning */ OK(sdis_interface_ref_put(interf)); shader.front.emissivity = NULL; - shader.front.specular_fraction = dummy_interface_getter; + shader.front.specular_fraction = dummy_radiative_interface_getter; OK(CREATE(dev, solid, fluid, &shader, NULL, &interf)); /* Warning */ OK(sdis_interface_ref_put(interf)); shader.front.specular_fraction = NULL; diff --git a/src/test_sdis_medium.c b/src/test_sdis_medium.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -63,10 +63,12 @@ main(int argc, char** argv) BA(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); fluid_shader.temperature = DUMMY_FLUID_SHADER.temperature; - fluid_shader.t0 = -1; - BA(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); + fluid_shader.t0 = -INF; + OK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); + OK(sdis_medium_ref_put(fluid)); fluid_shader.t0 = INF; - BA(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); + OK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); + OK(sdis_medium_ref_put(fluid)); fluid_shader.t0 = DUMMY_FLUID_SHADER.t0; BA(sdis_fluid_create(dev, &SDIS_FLUID_SHADER_NULL, NULL, &fluid)); @@ -106,10 +108,12 @@ main(int argc, char** argv) BA(sdis_solid_create(dev, &solid_shader, NULL, &solid)); solid_shader.temperature = DUMMY_SOLID_SHADER.temperature; - solid_shader.t0 = -1; - BA(sdis_solid_create(dev, &solid_shader, NULL, &solid)); + solid_shader.t0 = -INF; + OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); + OK(sdis_medium_ref_put(solid)); solid_shader.t0 = INF; - BA(sdis_solid_create(dev, &solid_shader, NULL, &solid)); + OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); + OK(sdis_medium_ref_put(solid)); solid_shader.t0 = DUMMY_SOLID_SHADER.t0; OK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid)); diff --git a/src/test_sdis_mesh.h b/src/test_sdis_mesh.h @@ -0,0 +1,120 @@ +/* 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/>. */ + +#ifndef TEST_SDIS_MESH_H +#define TEST_SDIS_MESH_H + +#include <rsys/rsys.h> +#include <rsys/stretchy_array.h> + +struct mesh { + double* positions; /* List of double 3 */ + size_t* indices; /* List of size_t 3 */ +}; +#define MESH_NULL__ {NULL, NULL} +static const struct mesh MESH_NULL = MESH_NULL__; + +static INLINE void +mesh_init(struct mesh* mesh) +{ + CHK(mesh); + *mesh = MESH_NULL; +} + +static INLINE void +mesh_release(struct mesh* mesh) +{ + CHK(mesh); + sa_release(mesh->positions); + sa_release(mesh->indices); +} + +/* Number of vertices */ +static INLINE size_t +mesh_nvertices(const struct mesh* mesh) +{ + CHK(mesh); + return sa_size(mesh->positions) / 3/* #coords per vertex */; +} + +/* Number of triangles */ +static INLINE size_t +mesh_ntriangles(const struct mesh* mesh) +{ + CHK(mesh); + return sa_size(mesh->indices) / 3/* #indices per triangle */; +} + +static INLINE void +mesh_append + (struct mesh* mesh, + const double* in_positions, + const size_t in_nvertices, + const size_t* in_indices, + const size_t in_ntriangles, + const double in_translate[3]) /* May be NULL */ +{ + double translate[3] = {0, 0, 0}; + double* positions = NULL; + size_t* indices = NULL; + size_t ivert = 0; + size_t i = 0; + CHK(mesh != NULL); + + ivert = mesh_nvertices(mesh); + positions = sa_add(mesh->positions, in_nvertices*3); + indices = sa_add(mesh->indices, in_ntriangles*3); + + if(in_translate) { + translate[0] = in_translate[0]; + translate[1] = in_translate[1]; + translate[2] = in_translate[2]; + } + + FOR_EACH(i, 0, in_nvertices) { + positions[i*3 + 0] = in_positions[i*3 + 0] + translate[0]; + positions[i*3 + 1] = in_positions[i*3 + 1] + translate[1]; + positions[i*3 + 2] = in_positions[i*3 + 2] + translate[2]; + } + + FOR_EACH(i, 0, in_ntriangles) { + indices[i*3 + 0] = in_indices[i*3 + 0] + ivert; + indices[i*3 + 1] = in_indices[i*3 + 1] + ivert; + indices[i*3 + 2] = in_indices[i*3 + 2] + ivert; + } +} + +static INLINE void +mesh_dump(const struct mesh* mesh, FILE* stream) +{ + size_t i, n; + CHK(mesh != NULL); + + n = mesh_nvertices(mesh); + FOR_EACH(i, 0, n) { + fprintf(stream, "v %g %g %g\n", SPLIT3(mesh->positions+i*3)); + } + + n = mesh_ntriangles(mesh); + FOR_EACH(i, 0, n) { + fprintf(stream, "f %lu %lu %lu\n", + (unsigned long)(mesh->indices[i*3+0] + 1), + (unsigned long)(mesh->indices[i*3+1] + 1), + (unsigned long)(mesh->indices[i*3+2] + 1)); + } + fflush(stream); +} + +#endif /* TEST_SDIS_MESH_H */ diff --git a/src/test_sdis_picard.c b/src/test_sdis_picard.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -18,7 +18,6 @@ #include <string.h> -#define UNKNOWN_TEMPERATURE -1 #define N 10000 /* This test consists in solving the stationary temperature profile in a solid @@ -234,7 +233,7 @@ solid_get_temperature (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) { CHK(vtx && data); - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } static double @@ -307,18 +306,24 @@ interface_get_convection_coef static double interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) { const struct interf* interf = sdis_data_cget(data); + (void)source_id; CHK(frag && interf); return interf->emissivity; } static double interface_get_specular_fraction - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) { const struct interf* interf = sdis_data_cget(data); + (void)source_id; CHK(frag && interf); return interf->specular_fraction; } @@ -371,6 +376,52 @@ create_interface } /******************************************************************************* + * Radiative environment + ******************************************************************************/ +struct radenv { + double temperature; /* [K] */ + double reference; /* [K] */ +}; + +static double +radenv_get_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray; + return ((const struct radenv*)sdis_data_cget(data))->temperature; +} + +static double +radenv_get_reference_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray; + return ((const struct radenv*)sdis_data_cget(data))->reference; +} + +static struct sdis_radiative_env* +create_radenv(struct sdis_device* dev) +{ + struct sdis_radiative_env_shader shader = SDIS_RADIATIVE_ENV_SHADER_NULL; + struct sdis_radiative_env* radenv = NULL; + struct sdis_data* data = NULL; + struct radenv* env = NULL; + + OK(sdis_data_create(dev, sizeof(struct radenv), ALIGNOF(radenv), NULL, &data)); + env = sdis_data_get(data); + env->temperature = 300; + env->reference = 300; + + shader.temperature = radenv_get_temperature; + shader.reference_temperature = radenv_get_reference_temperature; + OK(sdis_radiative_env_create(dev, &shader, data, &radenv)); + OK(sdis_data_ref_put(data)); + return radenv; +} + +/******************************************************************************* * Helper functions ******************************************************************************/ struct reference_result { @@ -483,13 +534,14 @@ static void create_scene_3d (struct sdis_device* dev, struct sdis_interface* interfaces[INTERFACES_COUNT__], + struct sdis_radiative_env* radenv, struct sdis_scene** scn) { struct geometry geom; struct sdis_interface* prim_interfaces[32]; struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; - CHK(dev && interfaces && scn); + CHK(dev && interfaces && radenv && scn); /* Setup the per primitive interface of the solid medium */ prim_interfaces[0] = prim_interfaces[1] = interfaces[ADIABATIC]; @@ -517,6 +569,7 @@ create_scene_3d scn_args.nvertices = nvertices_3d; scn_args.t_range[0] = 280; scn_args.t_range[1] = 350; + scn_args.radenv = radenv; scn_args.context = &geom; OK(sdis_scene_create(dev, &scn_args, scn)); } @@ -525,13 +578,14 @@ static void create_scene_2d (struct sdis_device* dev, struct sdis_interface* interfaces[INTERFACES_COUNT__], + struct sdis_radiative_env* radenv, struct sdis_scene** scn) { struct geometry geom; struct sdis_interface* prim_interfaces[10/*#segment*/]; struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; - CHK(dev && interfaces && scn); + CHK(dev && interfaces && radenv && scn); /* Setup the per primitive interface of the solid medium */ prim_interfaces[0] = interfaces[ADIABATIC]; @@ -555,6 +609,7 @@ create_scene_2d scn_args.nvertices = nvertices_2d; scn_args.t_range[0] = 280; scn_args.t_range[1] = 350; + scn_args.radenv = radenv; scn_args.context = &geom; OK(sdis_scene_2d_create(dev, &scn_args, scn)); } @@ -568,14 +623,15 @@ main(int argc, char** argv) FILE* stream = NULL; struct sdis_device* dev = NULL; + struct sdis_radiative_env* radenv = NULL; struct sdis_scene* scn_2d = NULL; struct sdis_scene* scn_3d = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* fluid = NULL; struct sdis_medium* dummy = NULL; struct sdis_interface* interfaces[INTERFACES_COUNT__]; - struct sdis_ambient_radiative_temperature amb_rad_temp; + struct radenv* radenv_props = NULL; struct solid solid_props; struct solid* psolid_props; struct reference_result ref = REFERENCE_RESULT_NULL; @@ -589,6 +645,9 @@ main(int argc, char** argv) OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); + radenv = create_radenv(dev); + radenv_props = sdis_data_get(sdis_radiative_env_get_data(radenv)); + /* Solid medium */ solid_props.lambda = 1.15; solid_props.rho = 1000; @@ -607,15 +666,15 @@ main(int argc, char** argv) create_fluid(dev, &fluid); /* Create the adiabatic interface for the solid */ - interf_props.temperature = UNKNOWN_TEMPERATURE; + interf_props.temperature = SDIS_TEMPERATURE_NONE; interf_props.h = -1; interf_props.emissivity = -1; interf_props.specular_fraction = -1; - interf_props.Tref = UNKNOWN_TEMPERATURE; + interf_props.Tref = SDIS_TEMPERATURE_NONE; create_interface(dev, solid, dummy, &interf_props, interfaces+ADIABATIC); /* Create the interface between the solid and the fluid */ - interf_props.temperature = UNKNOWN_TEMPERATURE; + interf_props.temperature = SDIS_TEMPERATURE_NONE; interf_props.h = 0; interf_props.emissivity = 1; interf_props.specular_fraction = 0; @@ -638,8 +697,8 @@ main(int argc, char** argv) pinterf_props[i] = sdis_data_get(sdis_interface_get_data(interfaces[i])); } - create_scene_2d(dev, interfaces, &scn_2d); - create_scene_3d(dev, interfaces, &scn_3d); + create_scene_2d(dev, interfaces, radenv, &scn_2d); + create_scene_3d(dev, interfaces, radenv, &scn_3d); CHK((stream = tmpfile()) != NULL); @@ -651,10 +710,8 @@ main(int argc, char** argv) pinterf_props[SOLID_FLUID_mX]->Tref = 300; pinterf_props[SOLID_FLUID_pX]->Tref = 300; pinterf_props[BOUNDARY_pX]->Tref = 300; - amb_rad_temp.temperature = 280; - amb_rad_temp.reference = 300; - OK(sdis_scene_set_ambient_radiative_temperature(scn_2d, &amb_rad_temp)); - OK(sdis_scene_set_ambient_radiative_temperature(scn_3d, &amb_rad_temp)); + radenv_props->temperature = 280; + radenv_props->reference = 300; test_picard(scn_2d, 1/*Picard order*/, &ref); test_picard(scn_3d, 1/*Picard order*/, &ref); printf("\n"); @@ -667,10 +724,8 @@ main(int argc, char** argv) pinterf_props[SOLID_FLUID_mX]->Tref = ref.T1; pinterf_props[SOLID_FLUID_pX]->Tref = ref.T2; pinterf_props[BOUNDARY_pX]->Tref = 350; - amb_rad_temp.temperature = 280; - amb_rad_temp.reference = 280; - OK(sdis_scene_set_ambient_radiative_temperature(scn_2d, &amb_rad_temp)); - OK(sdis_scene_set_ambient_radiative_temperature(scn_3d, &amb_rad_temp)); + radenv_props->temperature = 280; + radenv_props->reference = 280; test_picard(scn_2d, 1/*Picard order*/, &ref); test_picard(scn_3d, 1/*Picard order*/, &ref); printf("\n"); @@ -683,10 +738,8 @@ main(int argc, char** argv) pinterf_props[SOLID_FLUID_mX]->Tref = 300; pinterf_props[SOLID_FLUID_pX]->Tref = 300; pinterf_props[BOUNDARY_pX]->Tref = 300; - amb_rad_temp.temperature = 280; - amb_rad_temp.reference = 300; - OK(sdis_scene_set_ambient_radiative_temperature(scn_2d, &amb_rad_temp)); - OK(sdis_scene_set_ambient_radiative_temperature(scn_3d, &amb_rad_temp)); + radenv_props->temperature = 280; + radenv_props->reference = 300; test_picard(scn_2d, 2/*Picard order*/, &ref); test_picard(scn_3d, 2/*Picard order*/, &ref); printf("\n"); @@ -706,10 +759,8 @@ main(int argc, char** argv) pinterf_props[SOLID_FLUID_mX]->Tref = 350; pinterf_props[SOLID_FLUID_pX]->Tref = 450; pinterf_props[BOUNDARY_pX]->Tref = pinterf_props[BOUNDARY_pX]->temperature; - amb_rad_temp.temperature = t_range[0]; - amb_rad_temp.reference = t_range[0]; - OK(sdis_scene_set_ambient_radiative_temperature(scn_2d, &amb_rad_temp)); - OK(sdis_scene_set_ambient_radiative_temperature(scn_3d, &amb_rad_temp)); + radenv_props->temperature = t_range[0]; + radenv_props->reference = t_range[0]; test_picard(scn_2d, 3/*Picard order*/, &ref); test_picard(scn_3d, 3/*Picard order*/, &ref); register_heat_paths(scn_2d, 3/*Picard order*/, stream); @@ -734,10 +785,8 @@ main(int argc, char** argv) pinterf_props[SOLID_FLUID_mX]->Tref = 300; pinterf_props[SOLID_FLUID_pX]->Tref = 300; pinterf_props[BOUNDARY_pX]->Tref = 300; - amb_rad_temp.temperature = t_range[0]; - amb_rad_temp.reference = 300; - OK(sdis_scene_set_ambient_radiative_temperature(scn_2d, &amb_rad_temp)); - OK(sdis_scene_set_ambient_radiative_temperature(scn_3d, &amb_rad_temp)); + radenv_props->temperature = t_range[0]; + radenv_props->reference = 300; test_picard(scn_2d, 1/*Picard order*/, &ref); test_picard(scn_3d, 1/*Picard order*/, &ref); printf("\n"); @@ -750,15 +799,14 @@ main(int argc, char** argv) pinterf_props[SOLID_FLUID_mX]->Tref = ref.T1; pinterf_props[SOLID_FLUID_pX]->Tref = ref.T2; pinterf_props[BOUNDARY_pX]->Tref = 350; - amb_rad_temp.temperature = 280; - amb_rad_temp.reference = 280; - OK(sdis_scene_set_ambient_radiative_temperature(scn_2d, &amb_rad_temp)); - OK(sdis_scene_set_ambient_radiative_temperature(scn_3d, &amb_rad_temp)); + radenv_props->temperature = 280; + radenv_props->reference = 280; test_picard(scn_2d, 1/*Picard order*/, &ref); test_picard(scn_3d, 1/*Picard order*/, &ref); printf("\n"); /* Release memory */ + OK(sdis_radiative_env_ref_put(radenv)); OK(sdis_scene_ref_put(scn_2d)); OK(sdis_scene_ref_put(scn_3d)); OK(sdis_interface_ref_put(interfaces[ADIABATIC])); diff --git a/src/test_sdis_radiative_env.c b/src/test_sdis_radiative_env.c @@ -0,0 +1,107 @@ +/* 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 <rsys/rsys.h> + +/******************************************************************************* + * Helper function + ******************************************************************************/ +static double +radenv_get_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray, (void)data; + return 300; +} + +static double +radenv_get_reference_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray, (void)data; + return 300; +} + +static void +check_api(struct sdis_device* sdis) +{ + struct sdis_radiative_env_shader shader = SDIS_RADIATIVE_ENV_SHADER_NULL; + struct sdis_radiative_env_shader shader2 = SDIS_RADIATIVE_ENV_SHADER_NULL; + struct sdis_radiative_env* radenv = NULL; + struct sdis_data* data = NULL; + + CHK(sdis != NULL); + BA(sdis_radiative_env_create(NULL, &shader, NULL, &radenv)); + BA(sdis_radiative_env_create(sdis, NULL, NULL, &radenv)); + OK(sdis_radiative_env_create(sdis, &shader, NULL, &radenv)); + + BA(sdis_radiative_env_get_shader(NULL, &shader2)); + BA(sdis_radiative_env_get_shader(radenv, NULL)); + OK(sdis_radiative_env_get_shader(radenv, &shader2)); + + CHK(shader2.temperature == shader.temperature); + CHK(shader2.reference_temperature == shader.reference_temperature); + + CHK(sdis_radiative_env_get_data(radenv) == NULL); + + BA(sdis_radiative_env_ref_get(NULL)); + OK(sdis_radiative_env_ref_get(radenv)); + BA(sdis_radiative_env_ref_put(NULL)); + OK(sdis_radiative_env_ref_put(radenv)); + OK(sdis_radiative_env_ref_put(radenv)); + + shader.temperature = radenv_get_temperature; + shader.reference_temperature = radenv_get_reference_temperature; + + OK(sdis_data_create(sdis, sizeof(uint32_t), ALIGNOF(uint32_t), NULL, &data)); + *((uint32_t*)sdis_data_get(data)) = 0xD3CAFBAD; + + OK(sdis_radiative_env_create(sdis, &shader, data, &radenv)); + OK(sdis_data_ref_put(data)); + + OK(sdis_radiative_env_get_shader(radenv, &shader2)); + + CHK(shader2.temperature == shader.temperature); + CHK(shader2.reference_temperature == shader.reference_temperature); + + CHK(sdis_radiative_env_get_data(radenv) == data); + data = sdis_radiative_env_get_data(radenv); + CHK(*((uint32_t*)sdis_data_get(data)) == 0xD3CAFBAD); + + OK(sdis_radiative_env_ref_put(radenv)); +} + +/******************************************************************************* + * The test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct sdis_device* sdis = NULL; + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &sdis)); + + check_api(sdis); + + OK(sdis_device_ref_put(sdis)); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_scene.c b/src/test_sdis_scene.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -19,8 +19,8 @@ #include <rsys/double2.h> #include <rsys/double3.h> #include <rsys/math.h> -#include<star/senc2d.h> -#include<star/senc3d.h> +#include <star/senc2d.h> +#include <star/senc3d.h> struct context { const double* positions; @@ -87,7 +87,10 @@ get_interface(const size_t itri, struct sdis_interface** bound, void* context) } static void -test_scene_3d(struct sdis_device* dev, struct sdis_interface* interf) +test_scene_3d + (struct sdis_device* dev, + struct sdis_interface* interf, + struct sdis_radiative_env* in_radenv) { size_t duplicated_indices[] = { 0, 1, 2, 0, 2, 1 }; size_t degenerated_indices[] = { 0, 1, 1 }; @@ -98,7 +101,11 @@ test_scene_3d(struct sdis_device* dev, struct sdis_interface* interf) struct senc2d_scene* scn2d; struct senc3d_scene* scn3d; 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; struct sdis_scene* scn = NULL; + struct sdis_device* dev2 = NULL; + struct sdis_radiative_env* radenv = NULL; size_t ntris, npos; size_t iprim; size_t i; @@ -163,6 +170,11 @@ test_scene_3d(struct sdis_device* dev, struct sdis_interface* interf) OK(sdis_scene_get_dimension(scn, &dim)); CHK(dim == SDIS_SCENE_3D); + BA(sdis_scene_get_device(NULL, &dev2)); + BA(sdis_scene_get_device(scn, NULL)); + OK(sdis_scene_get_device(scn, &dev2)); + CHK(dev == dev2); + BA(sdis_scene_get_aabb(NULL, lower, upper)); BA(sdis_scene_get_aabb(scn, NULL, upper)); BA(sdis_scene_get_aabb(scn, lower, NULL)); @@ -189,12 +201,15 @@ test_scene_3d(struct sdis_device* dev, struct sdis_interface* interf) BA(sdis_scene_boundary_project_position(scn, 6, pos, NULL)); OK(sdis_scene_boundary_project_position(scn, 6, pos, uv1)); - BA(sdis_scene_find_closest_point(NULL, pos, INF, &iprim, uv2)); - BA(sdis_scene_find_closest_point(scn, NULL, INF, &iprim, uv2)); - BA(sdis_scene_find_closest_point(scn, pos, 0, &iprim, uv2)); - BA(sdis_scene_find_closest_point(scn, pos, INF, NULL, uv2)); - BA(sdis_scene_find_closest_point(scn, pos, INF, &iprim, NULL)); - OK(sdis_scene_find_closest_point(scn, pos, INF, &iprim, uv2)); + closest_pt_args.position[0] = pos[0]; + closest_pt_args.position[1] = pos[1]; + closest_pt_args.position[2] = pos[2]; + closest_pt_args.radius = INF; + BA(sdis_scene_find_closest_point(NULL, &closest_pt_args, &iprim, uv2)); + BA(sdis_scene_find_closest_point(scn, NULL, &iprim, uv2)); + BA(sdis_scene_find_closest_point(scn, &closest_pt_args, NULL, uv2)); + BA(sdis_scene_find_closest_point(scn, &closest_pt_args, &iprim, NULL)); + OK(sdis_scene_find_closest_point(scn, &closest_pt_args, &iprim, uv2)); CHK(iprim == 6); CHK(d2_eq_eps(uv0, uv1, 1.e-6)); @@ -203,7 +218,10 @@ test_scene_3d(struct sdis_device* dev, struct sdis_interface* interf) pos[0] = 0.5; pos[1] = 0.1; pos[2] = 0.25; - OK(sdis_scene_find_closest_point(scn, pos, INF, &iprim, uv2)); + closest_pt_args.position[0] = pos[0]; + closest_pt_args.position[1] = pos[1]; + closest_pt_args.position[2] = pos[2]; + OK(sdis_scene_find_closest_point(scn, &closest_pt_args, &iprim, uv2)); CHK(iprim == 10); OK(sdis_scene_boundary_project_position(scn, 10, pos, uv0)); @@ -213,7 +231,11 @@ test_scene_3d(struct sdis_device* dev, struct sdis_interface* interf) dst = d3_len(d3_sub(pos1, pos, pos1)); CHK(eq_eps(dst, 0.1, 1.e-6)); - OK(sdis_scene_find_closest_point(scn, pos, 0.09, &iprim, uv2)); + closest_pt_args.position[0] = pos[0]; + closest_pt_args.position[1] = pos[1]; + closest_pt_args.position[2] = pos[2]; + closest_pt_args.radius = 0.09; + OK(sdis_scene_find_closest_point(scn, &closest_pt_args, &iprim, uv2)); CHK(iprim == SDIS_PRIMITIVE_NONE); FOR_EACH(i, 0, 64) { @@ -222,7 +244,12 @@ test_scene_3d(struct sdis_device* dev, struct sdis_interface* interf) OK(sdis_scene_get_boundary_position(scn, 4, uv0, pos)); OK(sdis_scene_boundary_project_position(scn, 4, pos, uv1)); - OK(sdis_scene_find_closest_point(scn, pos, INF, &iprim, uv2)); + + closest_pt_args.position[0] = pos[0]; + closest_pt_args.position[1] = pos[1]; + closest_pt_args.position[2] = pos[2]; + closest_pt_args.radius = INF; + OK(sdis_scene_find_closest_point(scn, &closest_pt_args, &iprim, uv2)); CHK(d2_eq_eps(uv0, uv1, 1.e-6)); CHK(d2_eq_eps(uv1, uv2, 1.e-6)); CHK(iprim == 4); @@ -243,23 +270,40 @@ test_scene_3d(struct sdis_device* dev, struct sdis_interface* interf) /* No 2D available */ BA(sdis_scene_get_senc2d_scene(scn, &scn2d)); + BA(sdis_scene_get_radiative_env(NULL, &radenv)); + BA(sdis_scene_get_radiative_env(scn, NULL)); + OK(sdis_scene_get_radiative_env(scn, &radenv)); + CHK(radenv == NULL); + BA(sdis_scene_ref_get(NULL)); OK(sdis_scene_ref_get(scn)); BA(sdis_scene_ref_put(NULL)); OK(sdis_scene_ref_put(scn)); OK(sdis_scene_ref_put(scn)); + + scn_args.radenv = in_radenv; + OK(sdis_scene_create(dev, &scn_args, &scn)); + OK(sdis_scene_get_radiative_env(scn, &radenv)); + CHK(radenv == in_radenv); + CHK(radenv != NULL); + + OK(sdis_scene_ref_put(scn)); } static void -test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) +test_scene_2d + (struct sdis_device* dev, + struct sdis_interface* interf, + struct sdis_radiative_env* in_radenv) { size_t duplicated_indices[] = { 0, 1, 1, 0 }; size_t degenerated_indices[] = { 0, 0 }; double duplicated_vertices[] = { 0, 0, 0, 0 }; struct sdis_scene* scn = NULL; struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; - struct sdis_ambient_radiative_temperature trad = - SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL; + struct sdis_scene_find_closest_point_args closest_pt_args = + SDIS_SCENE_FIND_CLOSEST_POINT_ARGS_NULL; + struct sdis_radiative_env* radenv = NULL; double lower[2], upper[2]; double t_range[2]; double u0, u1, u2, pos[2], pos1[2]; @@ -354,28 +398,20 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) OK(sdis_scene_get_fp_to_meter(scn, &fp)); CHK(fp == 2); - BA(sdis_scene_get_ambient_radiative_temperature(NULL, NULL)); - BA(sdis_scene_get_ambient_radiative_temperature(scn, NULL)); - BA(sdis_scene_get_ambient_radiative_temperature(NULL, &trad)); - OK(sdis_scene_get_ambient_radiative_temperature(scn, &trad)); - CHK(trad.temperature == SDIS_SCENE_CREATE_ARGS_DEFAULT.trad.temperature); - CHK(trad.reference == SDIS_SCENE_CREATE_ARGS_DEFAULT.trad.reference); - - trad.temperature = 100; - trad.reference = 110; - BA(sdis_scene_set_ambient_radiative_temperature(NULL, &trad)); - OK(sdis_scene_set_ambient_radiative_temperature(scn, &trad)); - trad = SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL; - OK(sdis_scene_get_ambient_radiative_temperature(scn, &trad)); - CHK(trad.temperature == 100); - CHK(trad.reference == 110); - BA(sdis_scene_get_temperature_range(NULL, NULL)); BA(sdis_scene_get_temperature_range(scn, NULL)); BA(sdis_scene_get_temperature_range(NULL, t_range)); OK(sdis_scene_get_temperature_range(scn, t_range)); - CHK(t_range[0] == SDIS_SCENE_CREATE_ARGS_DEFAULT.t_range[0]); - CHK(t_range[1] == SDIS_SCENE_CREATE_ARGS_DEFAULT.t_range[1]); + if(SDIS_TEMPERATURE_IS_KNOWN(t_range[0])) { + CHK(t_range[0] == SDIS_SCENE_CREATE_ARGS_DEFAULT.t_range[0]); + } else { + CHK(SDIS_TEMPERATURE_IS_UNKNOWN(SDIS_SCENE_CREATE_ARGS_DEFAULT.t_range[0])); + } + if(SDIS_TEMPERATURE_IS_KNOWN(t_range[1])) { + CHK(t_range[1] == SDIS_SCENE_CREATE_ARGS_DEFAULT.t_range[1]); + } else { + CHK(SDIS_TEMPERATURE_IS_UNKNOWN(SDIS_SCENE_CREATE_ARGS_DEFAULT.t_range[0])); + } t_range[0] = 1; t_range[1] = 100; @@ -383,8 +419,8 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) BA(sdis_scene_set_temperature_range(NULL, t_range)); BA(sdis_scene_set_temperature_range(scn, NULL)); OK(sdis_scene_set_temperature_range(scn, t_range)); - t_range[0] = -1; - t_range[1] = -1; + t_range[0] = SDIS_TEMPERATURE_NONE; + t_range[1] = SDIS_TEMPERATURE_NONE; OK(sdis_scene_get_temperature_range(scn, t_range)); CHK(t_range[0] == 1); CHK(t_range[1] == 100); @@ -401,12 +437,14 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) BA(sdis_scene_boundary_project_position(scn, 1, pos, NULL)); OK(sdis_scene_boundary_project_position(scn, 1, pos, &u1)); - BA(sdis_scene_find_closest_point(NULL, pos, INF, &iprim, &u2)); - BA(sdis_scene_find_closest_point(scn, NULL, INF, &iprim, &u2)); - BA(sdis_scene_find_closest_point(scn, pos, 0, &iprim, &u2)); - BA(sdis_scene_find_closest_point(scn, pos, INF, NULL, &u2)); - BA(sdis_scene_find_closest_point(scn, pos, INF, &iprim, NULL)); - OK(sdis_scene_find_closest_point(scn, pos, INF, &iprim, &u2)); + closest_pt_args.position[0] = pos[0]; + closest_pt_args.position[1] = pos[1]; + closest_pt_args.radius = INF; + BA(sdis_scene_find_closest_point(NULL, &closest_pt_args, &iprim, &u2)); + BA(sdis_scene_find_closest_point(scn, NULL, &iprim, &u2)); + BA(sdis_scene_find_closest_point(scn, &closest_pt_args, NULL, &u2)); + BA(sdis_scene_find_closest_point(scn, &closest_pt_args, &iprim, NULL)); + OK(sdis_scene_find_closest_point(scn, &closest_pt_args, &iprim, &u2)); CHK(eq_eps(u0, u1, 1.e-6)); CHK(eq_eps(u1, u2, 1.e-6)); @@ -414,7 +452,10 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) pos[0] = 0.5; pos[1] = 0.1; - OK(sdis_scene_find_closest_point(scn, pos, INF, &iprim, &u2)); + closest_pt_args.position[0] = pos[0]; + closest_pt_args.position[1] = pos[1]; + closest_pt_args.radius = INF; + OK(sdis_scene_find_closest_point(scn, &closest_pt_args, &iprim, &u2)); CHK(iprim == 0); OK(sdis_scene_boundary_project_position(scn, 0, pos, &u0)); @@ -424,7 +465,10 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) dst = d2_len(d2_sub(pos1, pos, pos1)); CHK(eq_eps(dst, 0.1, 1.e-6)); - OK(sdis_scene_find_closest_point(scn, pos, 0.09, &iprim, &u2)); + closest_pt_args.position[0] = pos[0]; + closest_pt_args.position[1] = pos[1]; + closest_pt_args.radius = 0.09; + OK(sdis_scene_find_closest_point(scn, &closest_pt_args, &iprim, &u2)); CHK(iprim == SDIS_PRIMITIVE_NONE); FOR_EACH(i, 0, 64) { @@ -432,7 +476,11 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) OK(sdis_scene_get_boundary_position(scn, 2, &u0, pos)); OK(sdis_scene_boundary_project_position(scn, 2, pos, &u1)); - OK(sdis_scene_find_closest_point(scn, pos, INF, &iprim, &u2)); + + closest_pt_args.position[0] = pos[0]; + closest_pt_args.position[1] = pos[1]; + closest_pt_args.radius = INF; + OK(sdis_scene_find_closest_point(scn, &closest_pt_args, &iprim, &u2)); CHK(eq_eps(u0, u1, 1.e-6)); CHK(eq_eps(u1, u2, 1.e-6)); CHK(iprim == 2); @@ -458,11 +506,25 @@ test_scene_2d(struct sdis_device* dev, struct sdis_interface* interf) /* No 3D available */ BA(sdis_scene_get_senc3d_scene(scn, &scn3d)); + BA(sdis_scene_get_radiative_env(NULL, NULL)); + BA(sdis_scene_get_radiative_env(scn, NULL)); + BA(sdis_scene_get_radiative_env(NULL, &radenv)); + OK(sdis_scene_get_radiative_env(scn, &radenv)); + CHK(radenv == NULL); + BA(sdis_scene_ref_get(NULL)); OK(sdis_scene_ref_get(scn)); BA(sdis_scene_ref_put(NULL)); OK(sdis_scene_ref_put(scn)); OK(sdis_scene_ref_put(scn)); + + scn_args.radenv = in_radenv; + OK(sdis_scene_2d_create(dev, &scn_args, &scn)); + OK(sdis_scene_get_radiative_env(scn, &radenv)); + CHK(radenv == in_radenv); + CHK(radenv != NULL); + + OK(sdis_scene_ref_put(scn)); } int @@ -472,9 +534,11 @@ main(int argc, char** argv) struct sdis_medium* solid = NULL; struct sdis_medium* fluid = NULL; struct sdis_interface* interf = NULL; + struct sdis_radiative_env* radenv = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_interface_shader interface_shader = SDIS_INTERFACE_SHADER_NULL; + struct sdis_radiative_env_shader ray_shader = DUMMY_RAY_SHADER; (void)argc, (void)argv; interface_shader.convection_coef = DUMMY_INTERFACE_SHADER.convection_coef; @@ -485,15 +549,17 @@ main(int argc, char** argv) OK(sdis_solid_create(dev, &solid_shader, NULL, &solid)); OK(sdis_interface_create (dev, solid, fluid, &interface_shader, NULL, &interf)); + OK(sdis_radiative_env_create(dev, &ray_shader, NULL, &radenv)); OK(sdis_medium_ref_put(solid)); OK(sdis_medium_ref_put(fluid)); - test_scene_3d(dev, interf); - test_scene_2d(dev, interf); + test_scene_3d(dev, interf, radenv); + test_scene_2d(dev, interf, radenv); OK(sdis_device_ref_put(dev)); OK(sdis_interface_ref_put(interf)); + OK(sdis_radiative_env_ref_put(radenv)); CHK(mem_allocated_size() == 0); return 0; diff --git a/src/test_sdis_solid_random_walk_robustness.c b/src/test_sdis_solid_random_walk_robustness.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -24,7 +24,6 @@ #define Hcoef 1.0 /* Convection coefficient */ #define Pw 10000.0 /* Volumetric power */ #define Nreals 10000 /* #realisations */ -#define UNKNOWN -1 /******************************************************************************* * Helper functions @@ -160,7 +159,7 @@ interface_get_temperature interf = sdis_data_cget(data); switch(interf->profile) { case PROFILE_UNKNOWN: - temperature = UNKNOWN; + temperature = SDIS_TEMPERATURE_NONE; break; case PROFILE_VOLUMETRIC_POWER: temperature = volumetric_temperature(frag->P, interf->upper); @@ -305,7 +304,7 @@ main(int argc, char** argv) solid_param->lambda = 10; solid_param->cp = 1.0; solid_param->rho = 1.0; - solid_param->temperature = -1; + solid_param->temperature = SDIS_TEMPERATURE_NONE; solid_param->power = SDIS_VOLUMIC_POWER_NONE; OK(sdis_solid_create(dev, &solid_shader, data, &solid)); OK(sdis_data_ref_put(data)); diff --git a/src/test_sdis_solve_boundary.c b/src/test_sdis_solve_boundary.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -42,7 +42,6 @@ * (0,0,0) ///// */ -#define UNKNOWN_TEMPERATURE -1 #define N 10000 /* #realisations */ #define N_dump 10 /* #dumped paths */ @@ -108,7 +107,7 @@ solid_get_temperature { (void)data; CHK(vtx != NULL); - if(vtx->time > 0) return UNKNOWN_TEMPERATURE; + if(vtx->time > 0) return SDIS_TEMPERATURE_NONE; return Tf; } @@ -237,7 +236,7 @@ main(int argc, char** argv) OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); interf_props = sdis_data_get(data); interf_props->hc = 0; - interf_props->temperature = UNKNOWN_TEMPERATURE; + interf_props->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create (dev, solid, fluid, &interf_shader, data, &interf_adiabatic)); OK(sdis_data_ref_put(data)); @@ -255,7 +254,7 @@ main(int argc, char** argv) OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); interf_props = sdis_data_get(data); interf_props->hc = H; - interf_props->temperature = UNKNOWN_TEMPERATURE; + interf_props->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create (dev, solid, fluid, &interf_shader, data, &interf_H)); OK(sdis_data_ref_put(data)); @@ -419,7 +418,7 @@ main(int argc, char** argv) } /* The external fluid cannot have an unknown temperature */ - fluid_param->temperature = UNKNOWN_TEMPERATURE; + fluid_param->temperature = SDIS_TEMPERATURE_NONE; BA(SOLVE(box_scn, &probe_args, &estimator)); fluid_param->temperature = Tf; @@ -445,7 +444,7 @@ main(int argc, char** argv) } /* The external fluid cannot have an unknown temperature */ - fluid_param->temperature = UNKNOWN_TEMPERATURE; + fluid_param->temperature = SDIS_TEMPERATURE_NONE; BA(SOLVE(square_scn, &probe_args, &estimator)); fluid_param->temperature = Tf; @@ -606,7 +605,7 @@ main(int argc, char** argv) bound_args.register_paths = SDIS_HEAT_PATH_ALL; /* Check simulation error handling when paths are registered */ - fluid_param->temperature = UNKNOWN_TEMPERATURE; + fluid_param->temperature = SDIS_TEMPERATURE_NONE; BA(SOLVE(box_scn, &bound_args, &estimator)); /* Dump path */ diff --git a/src/test_sdis_solve_boundary_flux.c b/src/test_sdis_solve_boundary_flux.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -62,7 +62,6 @@ * (0,0)/////// */ -#define UNKNOWN_TEMPERATURE -1 #define N 100000 /* #realisations */ #define Tf 300.0 @@ -133,7 +132,7 @@ solid_get_temperature { (void) data; CHK(vtx != NULL); - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } /******************************************************************************* @@ -157,9 +156,12 @@ interface_get_temperature static double interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) { const struct interf* interf = sdis_data_cget(data); + (void)source_id; CHK(frag && data); return interf->emissivity; } @@ -183,6 +185,39 @@ interface_get_reference_temperature } /******************************************************************************* + * Radiative environment + ******************************************************************************/ +static double +radenv_get_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray, (void)data; + return Trad; +} + +static double +radenv_get_reference_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray, (void)data; + return Trad; +} + +static struct sdis_radiative_env* +create_radenv(struct sdis_device* dev) +{ + struct sdis_radiative_env_shader shader = SDIS_RADIATIVE_ENV_SHADER_NULL; + struct sdis_radiative_env* radenv = NULL; + + shader.temperature = radenv_get_temperature; + shader.reference_temperature = radenv_get_reference_temperature; + OK(sdis_radiative_env_create(dev, &shader, NULL, &radenv)); + return radenv; +} + +/******************************************************************************* * Helper function ******************************************************************************/ static void @@ -236,6 +271,7 @@ main(int argc, char** argv) struct sdis_interface* interf_adiabatic = NULL; struct sdis_interface* interf_Tb = NULL; struct sdis_interface* interf_H = NULL; + struct sdis_radiative_env* radenv = NULL; struct sdis_scene* box_scn = NULL; struct sdis_scene* square_scn = NULL; struct sdis_estimator* estimator = NULL; @@ -251,7 +287,7 @@ main(int argc, char** argv) struct sdis_solve_boundary_flux_args bound_args = SDIS_SOLVE_BOUNDARY_FLUX_ARGS_DEFAULT; struct interf* interf_props = NULL; - struct fluid* fluid_param; + struct fluid* fluid_args = NULL; struct ssp_rng* rng = NULL; enum sdis_estimator_type type; double pos[3]; @@ -261,12 +297,13 @@ main(int argc, char** argv) (void)argc, (void)argv; create_default_device(&argc, &argv, &is_master_process, &dev); + radenv = create_radenv(dev); /* Create the fluid medium */ OK(sdis_data_create (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); - fluid_param = sdis_data_get(data); - fluid_param->temperature = Tf; + fluid_args = sdis_data_get(data); + fluid_args->temperature = Tf; fluid_shader.temperature = fluid_get_temperature; OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid)); OK(sdis_data_ref_put(data)); @@ -289,7 +326,7 @@ main(int argc, char** argv) OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); interf_props = sdis_data_get(data); interf_props->hc = 0; - interf_props->temperature = UNKNOWN_TEMPERATURE; + interf_props->temperature = SDIS_TEMPERATURE_NONE; interf_props->emissivity = 0; OK(sdis_interface_create (dev, solid, fluid, &interf_shader, data, &interf_adiabatic)); @@ -313,7 +350,7 @@ main(int argc, char** argv) OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); interf_props = sdis_data_get(data); interf_props->hc = H; - interf_props->temperature = UNKNOWN_TEMPERATURE; + interf_props->temperature = SDIS_TEMPERATURE_NONE; interf_props->emissivity = EPSILON; interf_props->reference_temperature = Tref; interf_shader.back.emissivity = interface_get_emissivity; @@ -347,10 +384,9 @@ main(int argc, char** argv) scn_args.get_position = box_get_position; scn_args.nprimitives = box_ntriangles; scn_args.nvertices = box_nvertices; - scn_args.trad.temperature = Trad; - scn_args.trad.reference = Trad; scn_args.t_range[0] = MMIN(MMIN(Tf, Trad), Tb); scn_args.t_range[1] = MMAX(MMAX(Tf, Trad), Tb); + scn_args.radenv = radenv; scn_args.context = box_interfaces; OK(sdis_scene_create(dev, &scn_args, &box_scn)); @@ -360,10 +396,9 @@ main(int argc, char** argv) scn_args.get_position = square_get_position; scn_args.nprimitives = square_nsegments; scn_args.nvertices = square_nvertices; - scn_args.trad.temperature = Trad; - scn_args.trad.reference = Trad; scn_args.t_range[0] = MMIN(MMIN(Tf, Trad), Tb); scn_args.t_range[1] = MMAX(MMAX(Tf, Trad), Tb); + scn_args.radenv = radenv; scn_args.context = square_interfaces; OK(sdis_scene_2d_create(dev, &scn_args, &square_scn)); @@ -577,6 +612,7 @@ main(int argc, char** argv) BA(SOLVE(square_scn, &bound_args, &estimator)); #undef SOLVE + OK(sdis_radiative_env_ref_put(radenv)); OK(sdis_scene_ref_put(box_scn)); OK(sdis_scene_ref_put(square_scn)); free_default_device(dev); @@ -584,4 +620,3 @@ main(int argc, char** argv) CHK(mem_allocated_size() == 0); return 0; } - diff --git a/src/test_sdis_solve_camera.c b/src/test_sdis_solve_camera.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -28,10 +28,9 @@ #include <string.h> -#define UNKOWN_TEMPERATURE -1 #define IMG_WIDTH 157 #define IMG_HEIGHT 53 -#define SPP 30 /* #Samples per pixel, i.e. #realisations per pixel */ +#define SPP 32 /* #Samples per pixel, i.e. #realisations per pixel */ /* * The scene is composed of a solid cube whose temperature is unknown. The @@ -152,7 +151,7 @@ struct fluid { double rho; double temperature; }; -static const struct fluid FLUID_NULL = {0, 0, UNKOWN_TEMPERATURE}; +static const struct fluid FLUID_NULL = {0, 0, SDIS_TEMPERATURE_NONE}; static double fluid_get_calorific_capacity @@ -188,7 +187,7 @@ struct solid { double delta; double temperature; }; -static const struct solid SOLID_NULL = {0, 0, 0, 0, UNKOWN_TEMPERATURE}; +static const struct solid SOLID_NULL = {0, 0, 0, 0, SDIS_TEMPERATURE_NONE}; static double solid_get_calorific_capacity @@ -241,7 +240,7 @@ struct interf { double reference_temperature; }; static const struct interf INTERF_NULL = { - 0, 0, 0, UNKOWN_TEMPERATURE, UNKOWN_TEMPERATURE + 0, 0, 0, SDIS_TEMPERATURE_NONE, SDIS_TEMPERATURE_NONE }; static double @@ -254,16 +253,22 @@ interface_get_convection_coef static double interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (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))->epsilon; } static double interface_get_specular_fraction - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (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; } @@ -285,6 +290,55 @@ interface_get_reference_temperature } /******************************************************************************* + * Radiative environment + ******************************************************************************/ +struct radenv { + double temperature; /* [K] */ + double reference; /* [K] */ +}; + +static double +radenv_get_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray; + return ((const struct radenv*)sdis_data_cget(data))->temperature; +} + +static double +radenv_get_reference_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray; + return ((const struct radenv*)sdis_data_cget(data))->reference; +} + +static struct sdis_radiative_env* +create_radenv + (struct sdis_device* dev, + const double temperature, + const double reference) +{ + struct sdis_radiative_env_shader shader = SDIS_RADIATIVE_ENV_SHADER_NULL; + struct sdis_radiative_env* radenv = NULL; + struct sdis_data* data = NULL; + struct radenv* env = NULL; + + OK(sdis_data_create(dev, sizeof(struct radenv), ALIGNOF(radenv), NULL, &data)); + env = sdis_data_get(data); + env->temperature = temperature; + env->reference = reference; + + shader.temperature = radenv_get_temperature; + shader.reference_temperature = radenv_get_reference_temperature; + OK(sdis_radiative_env_create(dev, &shader, data, &radenv)); + OK(sdis_data_ref_put(data)); + return radenv; +} + +/******************************************************************************* * Helper functions ******************************************************************************/ static void @@ -294,7 +348,7 @@ create_solid struct sdis_medium** solid) { struct sdis_data* data = NULL; - struct solid* solid_param = NULL; + struct solid* solid_args = NULL; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; CHK(param != NULL); @@ -303,8 +357,8 @@ create_solid /* Copy the solid parameters into the Stardis memory space */ OK(sdis_data_create (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); - solid_param = sdis_data_get(data); - memcpy(solid_param, param, sizeof(struct solid)); + solid_args = sdis_data_get(data); + memcpy(solid_args, param, sizeof(struct solid)); /* Setup the solid shader */ solid_shader.calorific_capacity = solid_get_calorific_capacity; @@ -329,7 +383,7 @@ create_fluid struct sdis_medium** fluid) { struct sdis_data* data = NULL; - struct fluid* fluid_param = NULL; + struct fluid* fluid_args = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; CHK(param != NULL); @@ -338,8 +392,8 @@ create_fluid /* Copy the fluid parameters into the Stardis memory space */ OK(sdis_data_create (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); - fluid_param = sdis_data_get(data); - memcpy(fluid_param, param, sizeof(struct fluid)); + 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; @@ -363,7 +417,7 @@ create_interface struct sdis_interface** interf) { struct sdis_data* data = NULL; - struct interf* interface_param = NULL; + struct interf* interface_args = NULL; struct sdis_interface_shader interface_shader = SDIS_INTERFACE_SHADER_NULL; CHK(mdm_front != NULL); @@ -374,8 +428,8 @@ create_interface /* Copy the interface parameters into the Stardis memory space */ OK(sdis_data_create (dev, sizeof(struct interf), ALIGNOF(struct interf), NULL, &data)); - interface_param = sdis_data_get(data); - memcpy(interface_param, param, sizeof(struct interf)); + 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; @@ -384,7 +438,7 @@ create_interface if(sdis_medium_get_type(mdm_front) == SDIS_FLUID) { interface_shader.front.emissivity = interface_get_emissivity; interface_shader.front.specular_fraction = interface_get_specular_fraction; - interface_shader.front.reference_temperature = + interface_shader.front.reference_temperature = interface_get_reference_temperature; } if(sdis_medium_get_type(mdm_back) == SDIS_FLUID) { @@ -556,17 +610,17 @@ main(int argc, char** argv) struct sdis_medium* fluid1 = NULL; struct sdis_interface* interf0 = NULL; struct sdis_interface* interf1 = 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 sdis_ambient_radiative_temperature trad = - SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL; struct ssp_rng* rng = NULL; struct ssp_rng* rng_state = NULL; - struct fluid fluid_param = FLUID_NULL; - struct solid solid_param = SOLID_NULL; - struct interf interface_param = INTERF_NULL; - struct fluid* pfluid_param = 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]; @@ -578,40 +632,43 @@ main(int argc, char** 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_param.temperature = 350; - fluid_param.rho = 0; - fluid_param.cp = 0; - create_fluid(dev, &fluid_param, &fluid0); + fluid_args.temperature = 350; + fluid_args.rho = 0; + fluid_args.cp = 0; + create_fluid(dev, &fluid_args, &fluid0); /* Create the fluid1 */ - fluid_param.temperature = 300; - fluid_param.rho = 0; - fluid_param.cp = 0; - create_fluid(dev, &fluid_param, &fluid1); + fluid_args.temperature = 300; + fluid_args.rho = 0; + fluid_args.cp = 0; + create_fluid(dev, &fluid_args, &fluid1); /* Create the solid medium */ - solid_param.cp = 1.0; - solid_param.lambda = 0.1; - solid_param.rho = 1.0; - solid_param.delta = 1.0/20.0; - solid_param.temperature = UNKOWN_TEMPERATURE; - create_solid(dev, &solid_param, &solid); + solid_args.cp = 1.0; + solid_args.lambda = 0.1; + solid_args.rho = 1.0; + solid_args.delta = 1.0/20.0; + solid_args.temperature = SDIS_TEMPERATURE_NONE; + create_solid(dev, &solid_args, &solid); /* Create the fluid0/solid interface */ - interface_param.hc = 1; - interface_param.epsilon = 0; - interface_param.specular_fraction = 0; - interface_param.temperature = UNKOWN_TEMPERATURE; - create_interface(dev, solid, fluid0, &interface_param, &interf0); + 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); /* Create the fluid1/solid interface */ - interface_param.hc = 0.1; - interface_param.epsilon = 1; - interface_param.specular_fraction = 1; - interface_param.temperature = UNKOWN_TEMPERATURE; - interface_param.reference_temperature = 300; - create_interface(dev, fluid1, solid, &interface_param, &interf1); + interface_args.hc = 0.1; + interface_args.epsilon = 1; + 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)); @@ -635,10 +692,9 @@ main(int argc, char** argv) scn_args.get_position = geometry_get_position; scn_args.nprimitives = ntris; scn_args.nvertices = npos; - scn_args.trad.temperature = 300; - scn_args.trad.reference = 300; 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)); @@ -673,12 +729,9 @@ main(int argc, char** argv) solve_args.cam = NULL; BA(sdis_solve_camera(scn, &solve_args, &buf)); solve_args.cam = cam; - OK(sdis_scene_get_ambient_radiative_temperature(scn, &trad)); - trad.temperature = -1; - OK(sdis_scene_set_ambient_radiative_temperature(scn, &trad)); + pradenv_args->temperature = SDIS_TEMPERATURE_NONE; BA(sdis_solve_camera(scn, &solve_args, &buf)); - trad.temperature = 300; - OK(sdis_scene_set_ambient_radiative_temperature(scn, &trad)); + 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; @@ -749,7 +802,7 @@ main(int argc, char** argv) /* Check the RNG state */ OK(ssp_rng_create(NULL, SSP_RNG_THREEFRY, &rng)); - OK(ssp_rng_discard(rng, 31415926535)); /* Move the RNG state */ + 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)); @@ -766,8 +819,8 @@ main(int argc, char** argv) solve_args.rng_state = SDIS_SOLVE_CAMERA_ARGS_DEFAULT.rng_state; solve_args.rng_type = SDIS_SOLVE_CAMERA_ARGS_DEFAULT.rng_type; - pfluid_param = sdis_data_get(sdis_medium_get_data(fluid1)); - pfluid_param->temperature = UNKOWN_TEMPERATURE; + 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)); @@ -778,6 +831,7 @@ main(int argc, char** argv) OK(sdis_medium_ref_put(solid)); OK(sdis_medium_ref_put(fluid0)); OK(sdis_medium_ref_put(fluid1)); + 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)); diff --git a/src/test_sdis_solve_medium.c b/src/test_sdis_solve_medium.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -178,16 +178,22 @@ interface_get_convection_coef static double interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (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))->epsilon; } static double interface_get_specular_fraction - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (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; } @@ -272,7 +278,7 @@ main(int argc, char** argv) solid_param->lambda = 0.1; solid_param->rho = 1.0; solid_param->delta = 1.0/20.0; - solid_param->temperature = -1; /* Unknown temperature */ + solid_param->temperature = SDIS_TEMPERATURE_NONE; /* Unknown temperature */ OK(sdis_solid_create(dev, &solid_shader, data, &solid0)); OK(sdis_data_ref_put(data)); @@ -284,7 +290,7 @@ main(int argc, char** argv) solid_param->lambda = 1.0; solid_param->rho = 1.0; solid_param->delta = 1.0/20.0; - solid_param->temperature = -1; /* Unknown temperature */ + solid_param->temperature = SDIS_TEMPERATURE_NONE; /* Unknown temperature */ OK(sdis_solid_create(dev, &solid_shader, data, &solid1)); OK(sdis_data_ref_put(data)); @@ -403,7 +409,7 @@ main(int argc, char** argv) /* Check simulation error handling when paths are registered */ solve_args.nrealisations = 10; solve_args.register_paths = SDIS_HEAT_PATH_ALL; - fluid_param->temperature = -1; + fluid_param->temperature = SDIS_TEMPERATURE_NONE; BA(sdis_solve_medium(scn, &solve_args, &estimator)); fluid_param->temperature = Tf1; OK(sdis_solve_medium(scn, &solve_args, &estimator)); diff --git a/src/test_sdis_solve_medium_2d.c b/src/test_sdis_solve_medium_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -166,16 +166,22 @@ interface_get_convection_coef static double interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (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))->epsilon; } static double interface_get_specular_fraction - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (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; } @@ -256,7 +262,7 @@ main(int argc, char** argv) solid_param->lambda = 0.1; solid_param->rho = 1.0; solid_param->delta = 1.0/20.0; - solid_param->temperature = -1; /* Unknown temperature */ + solid_param->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_solid_create(dev, &solid_shader, data, &solid0)); OK(sdis_data_ref_put(data)); @@ -268,7 +274,7 @@ main(int argc, char** argv) solid_param->lambda = 1.0; solid_param->rho = 1.0; solid_param->delta = 1.0/20.0; - solid_param->temperature = -1; /* Unknown temperature */ + solid_param->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_solid_create(dev, &solid_shader, data, &solid1)); OK(sdis_data_ref_put(data)); @@ -293,8 +299,8 @@ main(int argc, char** argv) OK(sdis_data_ref_put(data)); /* Setup the square geometry */ - sa_add(positions, square_nvertices*2); - sa_add(indices, square_nsegments*2); + (void)sa_add(positions, square_nvertices*2); + (void)sa_add(indices, square_nsegments*2); memcpy(positions, square_vertices, square_nvertices*sizeof(double[2])); memcpy(indices, square_indices, square_nsegments*sizeof(size_t[2])); diff --git a/src/test_sdis_solve_probe.c b/src/test_sdis_solve_probe.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -156,16 +156,22 @@ interface_get_convection_coef static double interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (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))->epsilon; } static double interface_get_specular_fraction - (const struct sdis_interface_fragment* frag, struct sdis_data* data) + (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; } @@ -179,6 +185,55 @@ interface_get_reference_temperature } /******************************************************************************* + * Radiative environment + ******************************************************************************/ +struct radenv { + double temperature; /* [K] */ + double reference; /* [K] */ +}; + +static double +radenv_get_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray; + return ((const struct radenv*)sdis_data_cget(data))->temperature; +} + +static double +radenv_get_reference_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray; + return ((const struct radenv*)sdis_data_cget(data))->reference; +} + +static struct sdis_radiative_env* +create_radenv + (struct sdis_device* dev, + const double temperature, /* [K] */ + const double reference) /* [K] */ +{ + struct sdis_radiative_env_shader shader = SDIS_RADIATIVE_ENV_SHADER_NULL; + struct sdis_radiative_env* radenv = NULL; + struct sdis_data* data = NULL; + struct radenv* radenv_args = NULL; + + OK(sdis_data_create(dev, sizeof(struct radenv), ALIGNOF(radenv), NULL, &data)); + radenv_args = sdis_data_get(data); + radenv_args->temperature = temperature; + radenv_args->reference = reference; + + shader.temperature = radenv_get_temperature; + shader.reference_temperature = radenv_get_reference_temperature; + OK(sdis_radiative_env_create(dev, &shader, data, &radenv)); + OK(sdis_data_ref_put(data)); + return radenv; +} + +/******************************************************************************* * Helper functions ******************************************************************************/ struct dump_path_context { @@ -274,6 +329,7 @@ main(int argc, char** argv) struct sdis_medium* solid = NULL; struct sdis_medium* fluid = NULL; struct sdis_interface* interf = NULL; + struct sdis_radiative_env* radenv = NULL; struct sdis_scene* scn = NULL; struct sdis_data* data = NULL; struct sdis_estimator* estimator = NULL; @@ -289,13 +345,12 @@ main(int argc, char** argv) struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_interface_shader interface_shader = SDIS_INTERFACE_SHADER_NULL; struct sdis_solve_probe_args solve_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; - struct sdis_ambient_radiative_temperature trad = - SDIS_AMBIENT_RADIATIVE_TEMPERATURE_NULL; struct dump_path_context dump_ctx = DUMP_PATH_CONTEXT_NULL; struct context ctx; - struct fluid* fluid_param; - struct solid* solid_param; - struct interf* interface_param; + struct radenv* radenv_args; + struct fluid* fluid_args; + struct solid* solid_args; + struct interf* interface_args; struct ssp_rng* rng_state = NULL; enum sdis_estimator_type type; FILE* stream = NULL; @@ -315,8 +370,8 @@ main(int argc, char** argv) /* Create the fluid medium */ OK(sdis_data_create (dev, sizeof(struct fluid), ALIGNOF(struct fluid), NULL, &data)); - fluid_param = sdis_data_get(data); - fluid_param->temperature = 300; + fluid_args = sdis_data_get(data); + fluid_args->temperature = 300; fluid_shader.temperature = fluid_get_temperature; OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid)); OK(sdis_data_ref_put(data)); @@ -324,12 +379,12 @@ main(int argc, char** argv) /* Create the solid medium */ OK(sdis_data_create (dev, sizeof(struct solid), ALIGNOF(struct solid), NULL, &data)); - solid_param = sdis_data_get(data); - solid_param->cp = 1.0; - solid_param->lambda = 0.1; - solid_param->rho = 1.0; - solid_param->delta = 1.0/20.0; - solid_param->temperature = -1; /* Unknown temperature */ + solid_args = sdis_data_get(data); + solid_args->cp = 1.0; + solid_args->lambda = 0.1; + solid_args->rho = 1.0; + solid_args->delta = 1.0/20.0; + solid_args->temperature = SDIS_TEMPERATURE_NONE; /* Unknown temperature */ solid_shader.calorific_capacity = solid_get_calorific_capacity; solid_shader.thermal_conductivity = solid_get_thermal_conductivity; solid_shader.volumic_mass = solid_get_volumic_mass; @@ -341,10 +396,10 @@ main(int argc, char** argv) /* Create the solid/fluid interface */ OK(sdis_data_create(dev, sizeof(struct interf), ALIGNOF(struct interf), NULL, &data)); - interface_param = sdis_data_get(data); - interface_param->hc = 0.5; - interface_param->epsilon = 0; - interface_param->specular_fraction = 0; + interface_args = sdis_data_get(data); + interface_args->hc = 0.5; + interface_args->epsilon = 0; + interface_args->specular_fraction = 0; interface_shader.convection_coef = interface_get_convection_coef; interface_shader.front = SDIS_INTERFACE_SIDE_SHADER_NULL; interface_shader.back.temperature = NULL; @@ -359,6 +414,10 @@ main(int argc, char** argv) OK(sdis_medium_ref_put(solid)); OK(sdis_medium_ref_put(fluid)); + /* Create the radiative environment */ + radenv = create_radenv(dev, SDIS_TEMPERATURE_NONE, SDIS_TEMPERATURE_NONE); + radenv_args = sdis_data_get(sdis_radiative_env_get_data(radenv)); + /* Create the scene */ ctx.positions = box_vertices; ctx.indices = box_indices; @@ -368,6 +427,7 @@ main(int argc, char** argv) scn_args.get_position = get_position; scn_args.nprimitives = box_ntriangles; scn_args.nvertices = box_nvertices; + scn_args.radenv = radenv; scn_args.context = &ctx; OK(sdis_scene_create(dev, &scn_args, &scn)); @@ -397,6 +457,9 @@ main(int argc, char** argv) solve_args.picard_order = 0; BA(sdis_solve_probe(scn, &solve_args, &estimator)); solve_args.picard_order = 1; + solve_args.diff_algo = SDIS_DIFFUSION_NONE; + BA(sdis_solve_probe(scn, &solve_args, &estimator)); + solve_args.diff_algo = SDIS_DIFFUSION_DELTA_SPHERE; OK(sdis_solve_probe(scn, &solve_args, &estimator)); BA(sdis_estimator_get_type(estimator, NULL)); @@ -439,7 +502,7 @@ main(int argc, char** argv) ref = 300; printf("Temperature at (%g, %g, %g) with Tfluid=%g = %g ~ %g +/- %g\n", - SPLIT3(solve_args.position), fluid_param->temperature, ref, T.E, T.SE); + SPLIT3(solve_args.position), fluid_args->temperature, ref, T.E, T.SE); printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); @@ -454,10 +517,10 @@ main(int argc, char** argv) OK(sdis_estimator_ref_put(estimator)); /* The external fluid cannot have an unknown temperature */ - fluid_param->temperature = -1; + fluid_args->temperature = SDIS_TEMPERATURE_NONE; BA(sdis_solve_probe(scn, &solve_args, &estimator)); - fluid_param->temperature = 300; + fluid_args->temperature = 300; OK(sdis_solve_probe(scn, &solve_args, &estimator)); BA(sdis_solve_probe_green_function(NULL, &solve_args, &green)); @@ -481,7 +544,7 @@ main(int argc, char** argv) printf("\n"); /* Check same green used at a different temperature */ - fluid_param->temperature = 500; + fluid_args->temperature = 500; OK(sdis_solve_probe(scn, &solve_args, &estimator)); OK(sdis_estimator_get_realisation_count(estimator, &nreals)); @@ -491,7 +554,7 @@ main(int argc, char** argv) ref = 500; printf("Temperature at (%g, %g, %g) with Tfluid=%g = %g ~ %g +/- %g\n", - SPLIT3(solve_args.position), fluid_param->temperature, ref, T.E, T.SE); + SPLIT3(solve_args.position), fluid_args->temperature, ref, T.E, T.SE); printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); @@ -568,10 +631,10 @@ main(int argc, char** argv) solve_args.register_paths = SDIS_HEAT_PATH_ALL; /* Check simulation error handling when paths are registered */ - fluid_param->temperature = -1; + fluid_args->temperature = SDIS_TEMPERATURE_NONE; BA(sdis_solve_probe(scn, &solve_args, &estimator)); - fluid_param->temperature = 300; + fluid_args->temperature = 300; OK(sdis_solve_probe(scn, &solve_args, &estimator)); OK(sdis_estimator_get_paths_count(estimator, &n)); CHK(n == N_dump); @@ -592,14 +655,14 @@ main(int argc, char** argv) /* Green and ambient radiative temperature */ solve_args.nrealisations = N; - trad.temperature = trad.reference = 300; + radenv_args->temperature = 300; + radenv_args->reference = 300; t_range[0] = 300; t_range[1] = 300; - OK(sdis_scene_set_ambient_radiative_temperature(scn, &trad)); OK(sdis_scene_set_temperature_range(scn, t_range)); - interface_param->epsilon = 1; - interface_param->reference_temperature = 300; + interface_args->epsilon = 1; + interface_args->reference_temperature = 300; OK(sdis_solve_probe(scn, &solve_args, &estimator)); OK(sdis_solve_probe_green_function(scn, &solve_args, &green)); @@ -612,10 +675,9 @@ main(int argc, char** argv) OK(sdis_estimator_ref_put(estimator2)); /* Check same green used at different ambient radiative temperature */ - trad.temperature = 600; + radenv_args->temperature = 300; t_range[0] = 300; t_range[1] = 600; - OK(sdis_scene_set_ambient_radiative_temperature(scn, &trad)); OK(sdis_scene_set_temperature_range(scn, t_range)); OK(sdis_solve_probe(scn, &solve_args, &estimator)); @@ -627,6 +689,7 @@ main(int argc, char** argv) OK(sdis_estimator_ref_put(estimator)); OK(sdis_estimator_ref_put(estimator2)); OK(sdis_green_function_ref_put(green)); + OK(sdis_radiative_env_ref_put(radenv)); OK(sdis_scene_ref_put(scn)); OK(sdis_device_ref_put(dev)); diff --git a/src/test_sdis_solve_probe2.c b/src/test_sdis_solve_probe2.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -78,7 +78,7 @@ temperature_unknown(const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) { (void)data; CHK(vtx != NULL && IS_INF(vtx->time)); - return -1; + return SDIS_TEMPERATURE_NONE; } static double @@ -255,7 +255,7 @@ main(int argc, char** argv) solve_args.position[2] = 0.5; solve_args.time_range[0] = INF; solve_args.time_range[1] = INF; - + solve_args.diff_algo = SDIS_DIFFUSION_WOS; OK(sdis_solve_probe(scn, &solve_args, &estimator)); ref = 350 * solve_args.position[2] + (1-solve_args.position[2]) * 300; @@ -296,7 +296,7 @@ main(int argc, char** argv) /* Check the RNG state */ OK(ssp_rng_create(NULL, SSP_RNG_THREEFRY, &rng)); - OK(ssp_rng_discard(rng, 31415926535)); /* Move the RNG state */ + 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_probe(scn, &solve_args, &estimator2)); diff --git a/src/test_sdis_solve_probe2_2d.c b/src/test_sdis_solve_probe2_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -72,7 +72,7 @@ temperature_unknown(const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) { (void)data; CHK(vtx != NULL); - return -1; + return SDIS_TEMPERATURE_NONE; } static double @@ -246,6 +246,7 @@ main(int argc, char** argv) solve_args.position[1] = 0.5; solve_args.time_range[0] = INF; solve_args.time_range[1] = INF; + solve_args.diff_algo = SDIS_DIFFUSION_WOS; OK(sdis_solve_probe(scn, &solve_args, &estimator)); diff --git a/src/test_sdis_solve_probe3.c b/src/test_sdis_solve_probe3.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -97,7 +97,7 @@ temperature_unknown(const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) { (void)data; CHK(vtx != NULL); - return -1; + return SDIS_TEMPERATURE_NONE; } static double diff --git a/src/test_sdis_solve_probe3_2d.c b/src/test_sdis_solve_probe3_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -94,7 +94,7 @@ temperature_unknown(const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) { (void)data; CHK(vtx != NULL); - return -1; + return SDIS_TEMPERATURE_NONE; } static double diff --git a/src/test_sdis_solve_probe_2d.c b/src/test_sdis_solve_probe_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -116,7 +116,7 @@ solid_get_temperature (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) { (void)vtx, (void)data; - return -1; + return SDIS_TEMPERATURE_NONE; } static double @@ -234,7 +234,7 @@ main(int argc, char** argv) OK(sdis_green_function_ref_put(green)); /* The external fluid cannot have an unknown temperature */ - fluid_param->temperature = -1; + fluid_param->temperature = SDIS_TEMPERATURE_NONE; BA(sdis_solve_probe(scn, &solve_args, &estimator)); diff --git a/src/test_sdis_solve_probe_boundary_list.c b/src/test_sdis_solve_probe_boundary_list.c @@ -0,0 +1,493 @@ +/* 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/s3dut.h> +#include <rsys/math.h> + +#include <math.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 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) \ __ / + * | T(y) \ / \ / + * |/ T=? *__/ \ + * o--- T(x) /_ __ _\ + * \/ \/ + */ + +/******************************************************************************* + * 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; +} + +static INLINE float +rand_canonic(void) +{ + return (float)rand() / (float)((unsigned)RAND_MAX+1); +} + +static void +sample_sphere(double pos[3]) +{ + const double phi = rand_canonic() * 2 * PI; + const double v = rand_canonic(); + const double cos_theta = 1 - 2 * v; + const double sin_theta = 2 * sqrt(v * (1 - v)); + pos[0] = cos(phi) * sin_theta; + pos[1] = sin(phi) * sin_theta; + pos[2] = cos_theta; +} + +static INLINE void +check_intersection + (const double val0, + const double eps0, + const double val1, + const double eps1) +{ + double interval0[2], interval1[2]; + double intersection[2]; + interval0[0] = val0 - eps0; + interval0[1] = val0 + eps0; + interval1[0] = val1 - eps1; + interval1[1] = val1 + eps1; + intersection[0] = MMAX(interval0[0], interval1[0]); + intersection[1] = MMIN(interval0[1], interval1[1]); + CHK(intersection[0] <= intersection[1]); +} + +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 = 1; + 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)); +} + +/******************************************************************************* + * Solid, i.e. medium of super shape and 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/20.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; +} + +/******************************************************************************* + * 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; +} + +/******************************************************************************* + * Interfaces + ******************************************************************************/ +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_boundary_list_api + (struct sdis_scene* scn, + const struct sdis_solve_probe_boundary_list_args* in_args) +{ + struct sdis_solve_probe_boundary_list_args args = *in_args; + struct sdis_estimator_buffer* estim_buf = NULL; + + /* Check API */ + BA(sdis_solve_probe_boundary_list(NULL, &args, &estim_buf)); + BA(sdis_solve_probe_boundary_list(scn, NULL, &estim_buf)); + BA(sdis_solve_probe_boundary_list(scn, &args, NULL)); + args.nprobes = 0; + BA(sdis_solve_probe_boundary_list(scn, &args, &estim_buf)); + args.nprobes = in_args->nprobes; + args.probes = NULL; + BA(sdis_solve_probe_boundary_list(scn, &args, &estim_buf)); +} + +/* Check the estimators against the analytical solution */ +static void +check_estimator_buffer + (const struct sdis_estimator_buffer* estim_buf, + struct sdis_scene* scn, + const struct sdis_solve_probe_boundary_list_args* args) +{ + struct sdis_mc T = SDIS_MC_NULL; + size_t iprobe = 0; + + /* Variables used to check the global estimation results */ + size_t total_nrealisations = 0; + size_t total_nrealisations_ref = 0; + size_t total_nfailures = 0; + size_t total_nfailures_ref = 0; + double sum = 0; /* Global sum of weights */ + double sum2 = 0; /* Global sum of squared weights */ + double N = 0; /* Number of (successful) realisations */ + double E = 0; /* Expected value */ + double V = 0; /* Variance */ + double SE = 0; /* Standard Error */ + + /* Check the results */ + FOR_EACH(iprobe, 0, args->nprobes) { + const struct sdis_estimator* estimator = NULL; + size_t probe_nrealisations = 0; + size_t probe_nfailures = 0; + double pos[3] = {0,0,0}; + double probe_sum = 0; + double probe_sum2 = 0; + double ref = 0; + + OK(sdis_scene_get_boundary_position(scn, args->probes[iprobe].iprim, + args->probes[iprobe].uv, pos)); + + /* Fetch result */ + OK(sdis_estimator_buffer_at(estim_buf, iprobe, 0, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_count(estimator, &probe_nrealisations)); + OK(sdis_estimator_get_failure_count(estimator, &probe_nfailures)); + + /* Check probe estimation */ + ref = trilinear_profile(pos); + printf("T(%g, %g, %g) = %g ~ %g +/- %g\n", SPLIT3(pos), ref, T.E, T.SE); + CHK(eq_eps(ref, T.E, 3*T.SE)); + + /* Check miscellaneous results */ + CHK(probe_nrealisations == args->probes[iprobe].nrealisations); + + /* Compute the sum of weights and sum of squared weights of the probe */ + probe_sum = T.E * (double)probe_nrealisations; + probe_sum2 = (T.V + T.E*T.E) * (double)probe_nrealisations; + + /* Update the global estimation */ + total_nrealisations_ref += probe_nrealisations; + total_nfailures_ref += probe_nfailures; + sum += probe_sum; + sum2 += probe_sum2; + } + + /* Check the overall estimate of the estimate buffer. This estimate is not + * really a result expected by the caller since the probes are all + * independent. But to be consistent with the estimate buffer API, we need to + * provide it. And so we check its validity */ + OK(sdis_estimator_buffer_get_temperature(estim_buf, &T)); + OK(sdis_estimator_buffer_get_realisation_count(estim_buf, &total_nrealisations)); + OK(sdis_estimator_buffer_get_failure_count(estim_buf, &total_nfailures)); + + CHK(total_nrealisations == total_nrealisations_ref); + CHK(total_nfailures == total_nfailures_ref); + + N = (double)total_nrealisations_ref; + E = sum / N; + V = sum2 / N - E*E; + SE = sqrt(V/N); + check_intersection(E, SE, T.E, T.SE); +} + +static void +check_probe_boundary_list(struct sdis_scene* scn, const int is_master_process) +{ + #define NPROBES 10 + + /* Estimations */ + struct sdis_estimator_buffer* estim_buf = NULL; + + /* Probe variables */ + struct sdis_solve_probe_boundary_args probes[NPROBES]; + struct sdis_solve_probe_boundary_list_args args = + SDIS_SOLVE_PROBE_BOUNDARY_LIST_ARGS_DEFAULT; + size_t iprobe; + + /* Miscellaneous */ + struct sdis_scene_find_closest_point_args closest_pt_args = + SDIS_SCENE_FIND_CLOSEST_POINT_ARGS_NULL; + + (void)is_master_process; + + /* Setup the list of probes to calculate */ + args.probes = probes; + args.nprobes = NPROBES; + FOR_EACH(iprobe, 0, NPROBES) { + sample_sphere(closest_pt_args.position); + closest_pt_args.radius = INF; + + probes[iprobe] = SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT; + probes[iprobe].nrealisations = 10000; + probes[iprobe].side = SDIS_FRONT; + + OK(sdis_scene_find_closest_point + (scn, &closest_pt_args, &probes[iprobe].iprim, probes[iprobe].uv)); + } + + check_probe_boundary_list_api(scn, &args); + + /* Solve the probes */ + OK(sdis_solve_probe_boundary_list(scn, &args, &estim_buf)); + if(!is_master_process) { + CHK(estim_buf == NULL); + } else { + check_estimator_buffer(estim_buf, scn, &args); + OK(sdis_estimator_buffer_ref_put(estim_buf)); + } + + #undef NPROBES +} + +/******************************************************************************* + * The test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + /* Stardis */ + struct sdis_device* dev = NULL; + struct sdis_interface* solid_fluid = NULL; + struct sdis_interface* solid_solid = NULL; + struct sdis_medium* fluid = NULL; + struct sdis_medium* solid = NULL; + struct sdis_scene* scn = 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); + + /* Setup the mesh */ + mesh_init(&mesh); + mesh_add_super_shape(&mesh); + sshape_end_id = mesh_ntriangles(&mesh); + mesh_add_sphere(&mesh); + + /* Setup physical properties */ + fluid = create_dummy(dev); + solid = create_solid(dev); + solid_fluid = create_interface(dev, solid, fluid); + solid_solid = create_interface(dev, solid, solid); + + /* Create the scene */ + ctx.mesh = &mesh; + ctx.sshape_end_id = sshape_end_id; + ctx.sshape = solid_fluid; + ctx.sphere = solid_solid; + scn = create_scene(dev, &ctx); + + check_probe_boundary_list(scn, is_master_process); + + mesh_release(&mesh); + OK(sdis_interface_ref_put(solid_fluid)); + OK(sdis_interface_ref_put(solid_solid)); + OK(sdis_medium_ref_put(fluid)); + 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_solve_probe_list.c b/src/test_sdis_solve_probe_list.c @@ -0,0 +1,692 @@ +/* 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 <rsys/float3.h> + +#include <star/s3d.h> +#include <star/s3dut.h> +#include <star/ssp.h> + +#ifdef SDIS_ENABLE_MPI + #include <mpi.h> +#endif + +/* + * The system is a trilinear profile of steady state temperature, i.e. at each + * point of the system we can analytically calculate the temperature. We immerse + * a super shape in this temperature field which represents a solid in which we + * want to evaluate by Monte Carlo the temperature at several positions. On the + * Monte Carlo side, the temperature of the super shape is unknown. Only the + * boundary temperature is set to the temperature of the aforementioned + * trilinear profile. Thus, we should find by Monte Carlo the temperature + * defined by the trilinear profile. + * + * T(z) /\ <-- T(x,y,z) + * | T(y) ___/ \___ + * |/ \ . T=? / + * o--- T(x) /_ __ _\ + * \/ \/ + * + */ + +/******************************************************************************* + * 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 = -3; + const double upper = +3; + + /* 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; +} + +static INLINE float +rand_canonic(void) +{ + return (float)rand() / (float)((unsigned)RAND_MAX+1); +} + +static INLINE void +check_intersection + (const double val0, + const double eps0, + const double val1, + const double eps1) +{ + double interval0[2], interval1[2]; + double intersection[2]; + interval0[0] = val0 - eps0; + interval0[1] = val0 + eps0; + interval1[0] = val1 - eps1; + interval1[1] = val1 + eps1; + intersection[0] = MMAX(interval0[0], interval1[0]); + intersection[1] = MMIN(interval0[1], interval1[1]); + CHK(intersection[0] <= intersection[1]); +} + +/******************************************************************************* + * 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; +} + +/******************************************************************************* + * View, i.e. acceleration structure used to query geometry. In this test it is + * used to calculate the delta parameter and to sample the probe positions in + * the supershape. + ******************************************************************************/ +static void +view_get_indices(const unsigned itri, unsigned ids[3], void* ctx) +{ + struct s3dut_mesh_data* mesh_data = ctx; + CHK(ids && mesh_data && itri < mesh_data->nprimitives); + /* Flip the indices to ensure that the normal points into the super shape */ + ids[0] = (unsigned)mesh_data->indices[itri*3+0]; + ids[1] = (unsigned)mesh_data->indices[itri*3+2]; + ids[2] = (unsigned)mesh_data->indices[itri*3+1]; +} + +static void +view_get_position(const unsigned ivert, float pos[3], void* ctx) +{ + struct s3dut_mesh_data* mesh_data = ctx; + CHK(pos && mesh_data && ivert < mesh_data->nvertices); + pos[0] = (float)mesh_data->positions[ivert*3+0]; + pos[1] = (float)mesh_data->positions[ivert*3+1]; + pos[2] = (float)mesh_data->positions[ivert*3+2]; +} + +static struct s3d_scene_view* +create_view(struct s3dut_mesh* mesh) +{ + struct s3d_vertex_data vdata = S3D_VERTEX_DATA_NULL; + struct s3d_device* s3d = NULL; + struct s3d_scene* scn = NULL; + struct s3d_shape* shape = NULL; + struct s3d_scene_view* view = NULL; + + struct s3dut_mesh_data mesh_data; + + OK(s3dut_mesh_get_data(mesh, &mesh_data)); + + vdata.usage = S3D_POSITION; + vdata.type = S3D_FLOAT3; + vdata.get = view_get_position; + OK(s3d_device_create(NULL, NULL, 0, &s3d)); + OK(s3d_shape_create_mesh(s3d, &shape)); + OK(s3d_mesh_setup_indexed_vertices(shape, (unsigned)mesh_data.nprimitives, + view_get_indices, (unsigned)mesh_data.nvertices, &vdata, 1, &mesh_data)); + OK(s3d_scene_create(s3d, &scn)); + OK(s3d_scene_attach_shape(scn, shape)); + OK(s3d_scene_view_create(scn, S3D_TRACE, &view)); + + OK(s3d_device_ref_put(s3d)); + OK(s3d_shape_ref_put(shape)); + OK(s3d_scene_ref_put(scn)); + + return view; +} + +static double +view_compute_delta(struct s3d_scene_view* view) +{ + float S = 0; /* Surface */ + float V = 0; /* Volume */ + + OK(s3d_scene_view_compute_area(view, &S)); + OK(s3d_scene_view_compute_volume(view, &V)); + CHK(S > 0 && V > 0); + + return (4.0*V/S)/30.0; +} + +static void +view_sample_position + (struct s3d_scene_view* view, + const double delta, + double pos[3]) +{ + /* Ray variables */ + const float dir[3] = {0, 1, 0}; + const float range[2] = {0, FLT_MAX}; + float org[3]; + + /* View variables */ + float low[3]; + float upp[3]; + + OK(s3d_scene_view_get_aabb(view, low, upp)); + + /* Sample a position in the supershape by uniformly sampling a position within + * its bounding box and rejecting it if it is not in the supershape or if it + * is too close to its boundaries. */ + for(;;) { + struct s3d_hit hit = S3D_HIT_NULL; + float N[3] = {0, 0, 0}; /* Normal */ + + pos[0] = low[0] + rand_canonic()*(upp[0] - low[0]); + pos[1] = low[1] + rand_canonic()*(upp[1] - low[1]); + pos[2] = low[2] + rand_canonic()*(upp[2] - low[2]); + + org[0] = (float)pos[0]; + org[1] = (float)pos[1]; + org[2] = (float)pos[2]; + OK(s3d_scene_view_trace_ray(view, org, dir, range, NULL, &hit)); + + if(S3D_HIT_NONE(&hit)) continue; + + f3_normalize(N, hit.normal); + if(f3_dot(N, dir) > 0) continue; + + OK(s3d_scene_view_closest_point(view, org, (float)INF, NULL, &hit)); + CHK(!S3D_HIT_NONE(&hit)); + + /* Sample position is in the super shape and is not too close to its + * boundaries */ + if(hit.distance > delta) break; + } +} + +/******************************************************************************* + * 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; +} + +/******************************************************************************* + * 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, 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) /* [K] */ +#undef SOLID_PROP + +static double +solid_get_delta + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + const double* delta = sdis_data_get(data); + (void)vtx; /* Avoid the "unused variable" warning */ + return *delta; +} + +static struct sdis_medium* +create_solid(struct sdis_device* sdis, struct s3d_scene_view* view) +{ + struct sdis_solid_shader shader = SDIS_SOLID_SHADER_NULL; + struct sdis_medium* solid = NULL; + struct sdis_data* data = NULL; + double* pdelta = NULL; + + OK(sdis_data_create(sdis, sizeof(double), ALIGNOF(double), NULL, &data)); + pdelta = sdis_data_get(data); + *pdelta = view_compute_delta(view); + + 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, 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; + + shader.front.temperature = interface_get_temperature; + shader.back.temperature = interface_get_temperature; + OK(sdis_interface_create(sdis, front, back, &shader, NULL, &interf)); + return interf; +} + +/******************************************************************************* + * Validations + ******************************************************************************/ +static void +check_probe_list_api + (struct sdis_scene* scn, + const struct sdis_solve_probe_list_args* in_args) +{ + struct sdis_solve_probe_list_args args = *in_args; + struct sdis_estimator_buffer* estim_buf = NULL; + + /* Check API */ + BA(sdis_solve_probe_list(NULL, &args, &estim_buf)); + BA(sdis_solve_probe_list(scn, NULL, &estim_buf)); + BA(sdis_solve_probe_list(scn, &args, NULL)); + args.nprobes = 0; + BA(sdis_solve_probe_list(scn, &args, &estim_buf)); + args.nprobes = in_args->nprobes; + args.probes = NULL; + BA(sdis_solve_probe_list(scn, &args, &estim_buf)); +} + +/* Check the estimators against the analytical solution */ +static void +check_estimator_buffer + (const struct sdis_estimator_buffer* estim_buf, + const struct sdis_solve_probe_list_args* args) +{ + struct sdis_mc T = SDIS_MC_NULL; + size_t iprobe = 0; + + /* Variables used to check the global estimation results */ + size_t total_nrealisations = 0; + size_t total_nrealisations_ref = 0; + size_t total_nfailures = 0; + size_t total_nfailures_ref = 0; + double sum = 0; /* Global sum of weights */ + double sum2 = 0; /* Global sum of squared weights */ + double N = 0; /* Number of (successful) realisations */ + double E = 0; /* Expected value */ + double V = 0; /* Variance */ + double SE = 0; /* Standard Error */ + + /* Check the results */ + FOR_EACH(iprobe, 0, args->nprobes) { + const struct sdis_estimator* estimator = NULL; + size_t probe_nrealisations = 0; + size_t probe_nfailures = 0; + double probe_sum = 0; + double probe_sum2 = 0; + double ref = 0; + + /* Fetch result */ + OK(sdis_estimator_buffer_at(estim_buf, iprobe, 0, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_count(estimator, &probe_nrealisations)); + OK(sdis_estimator_get_failure_count(estimator, &probe_nfailures)); + + /* Check probe estimation */ + ref = trilinear_profile(args->probes[iprobe].position); + printf("T(%g, %g, %g) = %g ~ %g +/- %g\n", + SPLIT3(args->probes[iprobe].position), ref, T.E, T.SE); + CHK(eq_eps(ref, T.E, 3*T.SE)); + + /* Check miscellaneous results */ + CHK(probe_nrealisations == args->probes[iprobe].nrealisations); + + /* Compute the sum of weights and sum of squared weights of the probe */ + probe_sum = T.E * (double)probe_nrealisations; + probe_sum2 = (T.V + T.E*T.E) * (double)probe_nrealisations; + + /* Update the global estimation */ + total_nrealisations_ref += probe_nrealisations; + total_nfailures_ref += probe_nfailures; + sum += probe_sum; + sum2 += probe_sum2; + } + + /* Check the overall estimate of the estimate buffer. This estimate is not + * really a result expected by the caller since the probes are all + * independent. But to be consistent with the estimate buffer API, we need to + * provide it. And so we check its validity */ + OK(sdis_estimator_buffer_get_temperature(estim_buf, &T)); + OK(sdis_estimator_buffer_get_realisation_count(estim_buf, &total_nrealisations)); + OK(sdis_estimator_buffer_get_failure_count(estim_buf, &total_nfailures)); + + CHK(total_nrealisations == total_nrealisations_ref); + CHK(total_nfailures == total_nfailures_ref); + + N = (double)total_nrealisations_ref; + E = sum / N; + V = sum2 / N - E*E; + SE = sqrt(V/N); + check_intersection(E, SE, T.E, T.SE); +} + +/* Check that the buffers store statistically independent estimates */ +static void +check_estimator_buffer_combination + (const struct sdis_estimator_buffer* estim_buf1, + const struct sdis_estimator_buffer* estim_buf2, + const struct sdis_solve_probe_list_args* args) +{ + size_t iprobe; + + /* Check that the 2 series of estimates are compatible but not identical */ + FOR_EACH(iprobe, 0, args->nprobes) { + const struct sdis_estimator* estimator1 = NULL; + const struct sdis_estimator* estimator2 = NULL; + size_t nrealisations1 = 0; + size_t nrealisations2 = 0; + struct sdis_mc T1 = SDIS_MC_NULL; + struct sdis_mc T2 = SDIS_MC_NULL; + double sum = 0; /* Sum of weights */ + double sum2 = 0; /* Sum of squared weights */ + double E = 0; /* Expected value */ + double V = 0; /* Variance */ + double SE = 0; /* Standard Error */ + double N = 0; /* Number of realisations */ + double ref = 0; /* Analytical solution */ + + /* Retrieve the estimation results */ + OK(sdis_estimator_buffer_at(estim_buf1, iprobe, 0, &estimator1)); + OK(sdis_estimator_buffer_at(estim_buf2, iprobe, 0, &estimator2)); + OK(sdis_estimator_get_temperature(estimator1, &T1)); + OK(sdis_estimator_get_temperature(estimator2, &T2)); + + /* Estimates are not identical... */ + CHK(T1.E != T2.E); + CHK(T1.SE != T2.SE); + + /* ... but are compatible ... */ + check_intersection(T1.E, 3*T1.SE, T2.E, 3*T2.SE); + + /* We can combine the 2 estimates since they must be numerically + * independent. We can therefore verify that their combination is valid with + * respect to the analytical solution */ + OK(sdis_estimator_get_realisation_count(estimator1, &nrealisations1)); + OK(sdis_estimator_get_realisation_count(estimator2, &nrealisations2)); + + sum = + T1.E*(double)nrealisations1 + + T2.E*(double)nrealisations2; + sum2 = + (T1.V + T1.E*T1.E)*(double)nrealisations1 + + (T2.V + T2.E*T2.E)*(double)nrealisations2; + N = (double)(nrealisations1 + nrealisations2); + E = sum / N; + V = sum2 / N - E*E; + SE = sqrt(V/N); + + ref = trilinear_profile(args->probes[iprobe].position); + printf("T(%g, %g, %g) = %g ~ %g +/- %g\n", + SPLIT3(args->probes[iprobe].position), ref, E, SE); + CHK(eq_eps(ref, E, 3*SE)); + } +} + +static void +check_probe_list + (struct sdis_scene* scn, + struct s3d_scene_view* view, + const int is_master_process) +{ + #define NPROBES 10 + + /* RNG state */ + const char* rng_state_filename = "rng_state"; + struct ssp_rng* rng = NULL; + enum ssp_rng_type rng_type = SSP_RNG_TYPE_NULL; + + /* Probe variables */ + struct sdis_solve_probe_args probes[NPROBES]; + struct sdis_solve_probe_list_args args = SDIS_SOLVE_PROBE_LIST_ARGS_DEFAULT; + size_t iprobe = 0; + + /* Estimations */ + struct sdis_estimator_buffer* estim_buf = NULL; + struct sdis_estimator_buffer* estim_buf2 = NULL; + + /* Misc */ + double delta = 0; + FILE* fp = NULL; + + delta = view_compute_delta(view); + + /* Setup the list of probes to calculate */ + args.probes = probes; + args.nprobes = NPROBES; + FOR_EACH(iprobe, 0, NPROBES) { + probes[iprobe] = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + probes[iprobe].nrealisations = 10000; + view_sample_position(view, delta, probes[iprobe].position); + } + + check_probe_list_api(scn, &args); + + /* Solve the probes */ + OK(sdis_solve_probe_list(scn, &args, &estim_buf)); + + if(!is_master_process) { + CHK(estim_buf == NULL); + } else { + check_estimator_buffer(estim_buf, &args); + + /* Check the use of a non-default rng_state. Run the calculations using the + * final RNG state from the previous estimate. Thus, the estimates are + * statically independent and can be combined. To do this, write the RNG + * state to a file so that it is read by all processes for the next test. */ + OK(sdis_estimator_buffer_get_rng_state(estim_buf, &rng)); + OK(ssp_rng_get_type(rng, &rng_type)); + CHK(fp = fopen(rng_state_filename, "w")); + CHK(fwrite(&rng_type, sizeof(rng_type), 1, fp) == 1); + OK(ssp_rng_write(rng, fp)); + CHK(fclose(fp) == 0); + } + +#ifdef SDIS_ENABLE_MPI + /* Synchronize processes. Wait for the master process to write RNG state to + * be used */ + { + struct sdis_device* sdis = NULL; + int is_mpi_used = 0; + OK(sdis_scene_get_device(scn, &sdis)); + OK(sdis_device_is_mpi_used(sdis, &is_mpi_used)); + if(is_mpi_used) { + CHK(MPI_Barrier(MPI_COMM_WORLD) == MPI_SUCCESS); + } + } +#endif + + /* Read the RNG state */ + CHK(fp = fopen(rng_state_filename, "r")); + CHK(fread(&rng_type, sizeof(rng_type), 1, fp) == 1); + OK(ssp_rng_create(NULL, rng_type, &rng)); + OK(ssp_rng_read(rng, fp)); + CHK(fclose(fp) == 0); + + /* Run a new calculation using the read RNG state */ + args.rng_state = rng; + OK(sdis_solve_probe_list(scn, &args, &estim_buf2)); + OK(ssp_rng_ref_put(rng)); + + if(!is_master_process) { + CHK(estim_buf2 == NULL); + } else { + check_estimator_buffer_combination(estim_buf, estim_buf2, &args); + OK(sdis_estimator_buffer_ref_put(estim_buf)); + OK(sdis_estimator_buffer_ref_put(estim_buf2)); + } + + #undef NPROBES +} + +/******************************************************************************* + * 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 s3d_scene_view* view = NULL; + struct s3dut_mesh* super_shape = NULL; + int is_master_process = 0; + + (void)argc, (void)argv; /* Avoid the "unused variable" warning */ + + create_default_device(&argc, &argv, &is_master_process, &sdis); + + super_shape = create_super_shape(); + view = create_view(super_shape); + + solid = create_solid(sdis, view); + dummy = create_dummy(sdis); + interf = create_interface(sdis, solid, dummy); + scn = create_scene(sdis, super_shape, interf); + + check_probe_list(scn, view, is_master_process); + + OK(s3dut_mesh_ref_put(super_shape)); + OK(s3d_scene_view_ref_put(view)); + OK(sdis_medium_ref_put(solid)); + OK(sdis_medium_ref_put(dummy)); + OK(sdis_interface_ref_put(interf)); + OK(sdis_scene_ref_put(scn)); + + free_default_device(sdis); + + CHK(mem_allocated_size() == 0); + + return 0; +} diff --git a/src/test_sdis_source.c b/src/test_sdis_source.c @@ -0,0 +1,130 @@ +/* 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" + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +spherical_source_get_position + (const double time, + double pos[3], + struct sdis_data* data) +{ + (void)time, (void)data; + pos[0] = pos[1] = pos[2] = 1.234; /* [m] */ +} + +static double +spherical_source_get_power + (const double time, + struct sdis_data* data) +{ + (void)time, (void)data; + return 10; /* [W] */ +} + +static double +spherical_source_get_diffuse_radiance + (const double time, + const double dir[3], + struct sdis_data* data) +{ + (void)time, (void)dir, (void)data; + return 50; /* [W/m^2/sr] */ +} + +static void +check_spherical_source(struct sdis_device* dev) +{ + struct sdis_spherical_source_shader shader = SDIS_SPHERICAL_SOURCE_SHADER_NULL; + struct sdis_spherical_source_shader shader2= SDIS_SPHERICAL_SOURCE_SHADER_NULL; + struct sdis_source* src = NULL; + struct sdis_source* src2 = NULL; + struct sdis_data* data = NULL; + const double dir[3] = {1,0,0}; + + /* Create a data to check its memory management */ + OK(sdis_data_create(dev, sizeof(double[3]), ALIGNOF(double[3]), NULL, &data)); + + shader.position = spherical_source_get_position; + shader.power = spherical_source_get_power; + shader.radius = 1; + BA(sdis_spherical_source_create(NULL, &shader, data, &src)); + BA(sdis_spherical_source_create(dev, NULL, data, &src)); + BA(sdis_spherical_source_create(dev, &shader, data, NULL)); + OK(sdis_spherical_source_create(dev, &shader, data, &src)); + + BA(sdis_spherical_source_get_shader(NULL, &shader2)); + BA(sdis_spherical_source_get_shader(src, NULL)); + OK(sdis_spherical_source_get_shader(src, &shader2)); + + CHK(sdis_source_get_data(src) == data); + + CHK(shader2.position == shader.position); + CHK(shader2.power == shader.power); + CHK(shader2.diffuse_radiance == shader.diffuse_radiance); + CHK(shader2.power(INF, data) == 10); + + BA(sdis_source_ref_get(NULL)); + OK(sdis_source_ref_get(src)); + BA(sdis_source_ref_put(NULL)); + OK(sdis_source_ref_put(src)); + OK(sdis_source_ref_put(src)); + + OK(sdis_data_ref_put(data)); + + OK(sdis_spherical_source_create(dev, &shader, NULL, &src)); + OK(sdis_spherical_source_create(dev, &shader, NULL, &src2)); + CHK(sdis_source_get_id(src) != sdis_source_get_id(src2)); + CHK(sdis_source_get_data(src) == NULL); + OK(sdis_source_ref_put(src)); + OK(sdis_source_ref_put(src2)); + + shader.position = NULL; + BA(sdis_spherical_source_create(dev, &shader, NULL, &src)); + shader.position = spherical_source_get_position; + shader.power = NULL; + BA(sdis_spherical_source_create(dev, &shader, NULL, &src)); + shader.power = spherical_source_get_power; + shader.diffuse_radiance = spherical_source_get_diffuse_radiance; + OK(sdis_spherical_source_create(dev, &shader, NULL, &src)); + + OK(sdis_spherical_source_get_shader(src, &shader2)); + CHK(shader2.diffuse_radiance = spherical_source_get_diffuse_radiance); + CHK(shader2.diffuse_radiance(INF, dir, NULL) == 50); + + OK(sdis_source_ref_put(src)); +} + +/******************************************************************************* + * The test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct sdis_device* dev = NULL; + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); + + check_spherical_source(dev); + + OK(sdis_device_ref_put(dev)); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_transcient.c b/src/test_sdis_transcient.c @@ -18,8 +18,6 @@ #include <string.h> -#define UNKNOWN_TEMPERATURE -1 - /* * The scene is composed of a solid cuboid whose temperature is fixed on its 6 * faces. The test consist in checking that the estimated temperature at a @@ -225,7 +223,7 @@ solid_get_temperature if(vtx->time <= 0) { return ((const struct solid*)sdis_data_cget(data))->init_temperature; } else { - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } } @@ -542,7 +540,7 @@ main(int argc, char** argv) interfs[3] = create_interface(dev, solid, fluid, &interf_shader, Tbounds[3]); interfs[4] = create_interface(dev, solid, fluid, &interf_shader, Tbounds[4]); interfs[5] = create_interface(dev, solid, fluid, &interf_shader, Tbounds[5]); - interfs[6] = create_interface(dev, solid, solid, &interf_shader, UNKNOWN_TEMPERATURE); + interfs[6] = create_interface(dev, solid, solid, &interf_shader, SDIS_TEMPERATURE_NONE); /* Setup the box scene context */ ctx.indices = box_indices; diff --git a/src/test_sdis_unstationary_atm.c b/src/test_sdis_unstationary_atm.c @@ -1,920 +0,0 @@ -/* Copyright (C) 2016-2023 |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 <rsys/clock_time.h> -#include <rsys/mem_allocator.h> -#include <rsys/double3.h> -#include <rsys/math.h> -#include <star/ssp.h> - -/* - * The physical configuration is the following: a slab of fluid with known - * thermophysical properties but unknown temperature is located between a - * "ground" and a slab of solid, with also a unknown temperature profile. On - * the other side of the solid slab, is a "atmosphere" with known temperature, - * and known radiative temperature. - * - * Solving the system means: finding the temperature of the ground, of the - * fluid, of the boundaries, and also the temperature inside the solid, at - * various locations (the 1D slab is discretized in order to obtain the - * reference) - * - * The reference for this system comes from a numerical method and is not - * analytic. Thus the compliance test MC VS reference is not the usual |MC - - * ref| <= 3*sigma but is |MC -ref| <= (Tmax -Tmin) * 0.01. - * - * 3D 2D - * - * /////////////// /////////////// - * +-----------+-+ +-----------+-+ - * /' / /| | | | - * +-----------+-+ | HA _\ <---- TR | _\ | | HA _\ <---- TR - * | | _\ | | | / / <---- TR TG| HG / / HC | | / / <---- TR - * | |HG / / HC| | | TA \__/ <---- TR | \__/ | | TA \__/ <---- TR - * TG| | \__/ | | | | | | - * | +.........|.|.+ +-----------+-+ - * |, |/|/ /////H///////E/ - * +-----------+-+ - * //////H//////E/ - */ - -#define XH 3 -#define XHpE 3.2 -#define XE (XHpE - XH) - -#define T0_SOLID 300 -#define T0_FLUID 300 - -#define UNKNOWN_TEMPERATURE -1 - -#define N 10000 /* #realisations */ - -#define TG 310 -#define HG 400 - -#define HC 400 - -#define TA 290 -#define HA 400 -#define TR 260 - -#define TMAX (MMAX(MMAX(MMAX(T0_FLUID, T0_SOLID), MMAX(TG, TA)), TR)) -#define TMIN (MMIN(MMIN(MMIN(T0_FLUID, T0_SOLID), MMIN(TG, TA)), TR)) -#define EPS ((TMAX-TMIN)*0.01) - -/* hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon - * Tref = (hr / (4 * 5.6696e-8 * epsilon)) ^ 1/3, hr = 6 */ -#define TREF 297.974852286 - -#define RHO_F 1.3 -#define CP_F 1005 -#define RHO_S 2400 -#define CP_S 800 -#define LAMBDA 0.6 - -#define X_PROBE (XH + 0.2 * XE) - -#define DELTA (XE/40.0) - -/******************************************************************************* - * Box geometry - ******************************************************************************/ -static const double model3d_vertices[12/*#vertices*/*3/*#coords per vertex*/] = { - 0, 0, 0, - XH, 0, 0, - XHpE, 0, 0, - 0, XHpE, 0, - XH, XHpE, 0, - XHpE, XHpE, 0, - 0, 0, XHpE, - XH, 0, XHpE, - XHpE, 0, XHpE, - 0, XHpE, XHpE, - XH, XHpE, XHpE, - XHpE, XHpE, XHpE -}; -static const size_t model3d_nvertices = sizeof(model3d_vertices)/(sizeof(double)*3); - -/* The following array lists the indices toward the 3D vertices of each - * triangle. - * ,3---,4---,5 ,3----4----5 ,4 - * ,' | ,' | ,'/| ,'/| \ | \ | ,'/| - * 9----10---11 / | 9' / | \ | \ | 10 / | Y - * |', |', | / ,2 | / ,0---,1---,2 | / ,1 | - * | ',| ',|/,' |/,' | ,' | ,' |/,' o--X - * 6----7----8' 6----7'---8' 7 / - * Front, right Back, left and Internal Z - * and Top faces bottom faces face */ -static const size_t model3d_indices[22/*#triangles*/*3/*#indices per triangle*/] = { - 0, 3, 1, 1, 3, 4, 1, 4, 2, 2, 4, 5, /* -Z */ - 0, 6, 3, 3, 6, 9, /* -X */ - 6, 7, 9, 9, 7, 10, 7, 8, 10, 10, 8, 11, /* +Z */ - 5, 11, 8, 8, 2, 5, /* +X */ - 3, 9, 10, 10, 4, 3, 4, 10, 11, 11, 5, 4, /* +Y */ - 0, 1, 7, 7, 6, 0, 1, 2, 8, 8, 7, 1, /* -Y */ - 4, 10, 7, 7, 1, 4 /* Inside */ -}; -static const size_t model3d_ntriangles = sizeof(model3d_indices)/(sizeof(size_t)*3); - -static INLINE void -model3d_get_indices(const size_t itri, size_t ids[3], void* context) -{ - (void)context; - CHK(ids); - CHK(itri < model3d_ntriangles); - ids[0] = model3d_indices[itri * 3 + 0]; - ids[1] = model3d_indices[itri * 3 + 1]; - ids[2] = model3d_indices[itri * 3 + 2]; -} - -static INLINE void -model3d_get_position(const size_t ivert, double pos[3], void* context) -{ - (void)context; - CHK(pos); - CHK(ivert < model3d_nvertices); - pos[0] = model3d_vertices[ivert * 3 + 0]; - pos[1] = model3d_vertices[ivert * 3 + 1]; - pos[2] = model3d_vertices[ivert * 3 + 2]; -} - -static INLINE void -model3d_get_interface(const size_t itri, struct sdis_interface** bound, void* context) -{ - struct sdis_interface** interfaces = context; - CHK(context && bound); - CHK(itri < model3d_ntriangles); - *bound = interfaces[itri]; -} - -/******************************************************************************* - * Square geometry - ******************************************************************************/ -static const double model2d_vertices[6/*#vertices*/ * 2/*#coords per vertex*/] = { - XHpE, 0, - XH, 0, - 0, 0, - 0, XHpE, - XH, XHpE, - XHpE, XHpE -}; -static const size_t model2d_nvertices = sizeof(model2d_vertices)/(sizeof(double)*2); - -static const size_t model2d_indices[7/*#segments*/ * 2/*#indices per segment*/] = { - 0, 1, 1, 2, /* Bottom */ - 2, 3, /* Left */ - 3, 4, 4, 5, /* Top */ - 5, 0, /* Right */ - 4, 1 /* Inside */ -}; -static const size_t model2d_nsegments = sizeof(model2d_indices)/(sizeof(size_t)*2); - -static INLINE void -model2d_get_indices(const size_t iseg, size_t ids[2], void* context) -{ - (void)context; - CHK(ids); - CHK(iseg < model2d_nsegments); - ids[0] = model2d_indices[iseg * 2 + 0]; - ids[1] = model2d_indices[iseg * 2 + 1]; -} - -static INLINE void -model2d_get_position(const size_t ivert, double pos[2], void* context) -{ - (void)context; - CHK(pos); - CHK(ivert < model2d_nvertices); - pos[0] = model2d_vertices[ivert * 2 + 0]; - pos[1] = model2d_vertices[ivert * 2 + 1]; -} - -static INLINE void -model2d_get_interface -(const size_t iseg, struct sdis_interface** bound, void* context) -{ - struct sdis_interface** interfaces = context; - CHK(context && bound); - CHK(iseg < model2d_nsegments); - *bound = interfaces[iseg]; -} - -/******************************************************************************* - * Media - ******************************************************************************/ -struct solid { - double lambda; - double rho; - double cp; - double delta; - double temperature; - double t0; -}; - -static double -solid_get_calorific_capacity - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - struct solid* solid; - CHK(vtx && data); - solid = ((struct solid*)sdis_data_cget(data)); - return solid->cp; -} - -static double -solid_get_thermal_conductivity - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - struct solid* solid; - CHK(vtx && data); - solid = ((struct solid*)sdis_data_cget(data)); - return solid->lambda; -} - -static double -solid_get_volumic_mass - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - struct solid* solid; - CHK(vtx && data); - solid = ((struct solid*)sdis_data_cget(data)); - return solid->rho; -} - -static double -solid_get_delta - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - struct solid* solid; - CHK(vtx && data); - solid = ((struct solid*)sdis_data_cget(data)); - return solid->delta; -} - -static double -solid_get_temperature - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - struct solid* solid; - CHK(vtx && data); - solid = ((struct solid*)sdis_data_cget(data)); - if(vtx->time <= solid->t0) - return solid->temperature; - return UNKNOWN_TEMPERATURE; -} - -struct fluid { - double rho; - double cp; - double t0; - double temperature; -}; - -static double -fluid_get_temperature - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - struct fluid* fluid; - CHK(vtx && data); - fluid = ((struct fluid*)sdis_data_cget(data)); - if(vtx->time <= fluid->t0) - return fluid->temperature; - return UNKNOWN_TEMPERATURE; -} - -static double -fluid_get_volumic_mass - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - struct fluid* fluid; - CHK(vtx && data); - fluid = ((struct fluid*)sdis_data_cget(data)); - return fluid->rho; -} - -static double -fluid_get_calorific_capacity - (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) -{ - struct fluid* fluid; - CHK(vtx && data); - fluid = ((struct fluid*)sdis_data_cget(data)); - return fluid->cp; -} - -/******************************************************************************* - * Interfaces - ******************************************************************************/ -struct interf { - double temperature; - double emissivity; - double h; - double Tref; -}; - -static double -interface_get_temperature - (const struct sdis_interface_fragment* frag, struct sdis_data* data) -{ - const struct interf* interf; - CHK(frag && data); - interf = sdis_data_cget(data); - return interf->temperature; -} - -static double -interface_get_convection_coef - (const struct sdis_interface_fragment* frag, struct sdis_data* data) -{ - const struct interf* interf; - CHK(frag && data); - interf = sdis_data_cget(data); - return interf->h; -} - -static double -interface_get_emissivity - (const struct sdis_interface_fragment* frag, struct sdis_data* data) -{ - const struct interf* interf; - CHK(frag && data); - interf = sdis_data_cget(data); - return interf->emissivity; -} - -static double -interface_get_Tref - (const struct sdis_interface_fragment* frag, struct sdis_data* data) -{ - const struct interf* interf; - CHK(frag && data); - interf = sdis_data_cget(data); - return interf->Tref; -} - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static void -create_interface - (struct sdis_device* dev, - struct sdis_medium* front, - struct sdis_medium* back, - const struct interf* interf, - struct sdis_interface** out_interf) -{ - struct sdis_interface_shader shader = SDIS_INTERFACE_SHADER_NULL; - struct sdis_data* data = NULL; - - CHK(interf != NULL); - - shader.front.temperature = interface_get_temperature; - shader.back.temperature = interface_get_temperature; - if(sdis_medium_get_type(front) != sdis_medium_get_type(back)) { - shader.convection_coef = interface_get_convection_coef; - shader.convection_coef_upper_bound = interf->h; - } - if(sdis_medium_get_type(front) == SDIS_FLUID) { - shader.front.emissivity = interface_get_emissivity; - shader.front.reference_temperature = interface_get_Tref; - } - if(sdis_medium_get_type(back) == SDIS_FLUID) { - shader.back.emissivity = interface_get_emissivity; - shader.back.reference_temperature = interface_get_Tref; - } - - OK(sdis_data_create(dev, sizeof(struct interf), ALIGNOF(struct interf), - NULL, &data)); - *((struct interf*)sdis_data_get(data)) = *interf; - - OK(sdis_interface_create(dev, front, back, &shader, data, out_interf)); - OK(sdis_data_ref_put(data)); -} - -static void -solve_tbound1 - (struct sdis_scene* scn, - struct ssp_rng* rng) -{ - char dump[128]; - struct time t0, t1; - struct sdis_estimator* estimator; - struct sdis_solve_probe_boundary_args solve_args - = SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT; - struct sdis_mc T = SDIS_MC_NULL; - struct sdis_mc time = SDIS_MC_NULL; - size_t nreals; - size_t nfails; - enum sdis_scene_dimension dim; - const double t[] = { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; - const double ref[sizeof(t) / sizeof(*t)] = { - 290.046375, 289.903935, 289.840490, 289.802690, 289.777215, 289.759034, - 289.745710, 289.735826, 289.728448, 289.722921 - }; - const int nsimuls = sizeof(t) / sizeof(*t); - int isimul; - ASSERT(scn && rng); - - OK(sdis_scene_get_dimension(scn, &dim)); - - solve_args.nrealisations = N; - solve_args.side = SDIS_FRONT; - FOR_EACH(isimul, 0, nsimuls) { - solve_args.time_range[0] = solve_args.time_range[1] = t[isimul]; - if(dim == SDIS_SCENE_2D) { - solve_args.iprim = model2d_nsegments - 1; - solve_args.uv[0] = ssp_rng_uniform_double(rng, 0, 1); - } else { - double u = ssp_rng_uniform_double(rng, 0, 1); - double v = ssp_rng_uniform_double(rng, 0, 1); - double w = ssp_rng_uniform_double(rng, 0, 1); - double x = 1 / (u + v + w); - solve_args.iprim = (isimul % 2) ? 10 : 11; /* +X face */ - solve_args.uv[0] = u * x; - solve_args.uv[1] = v * x; - } - - time_current(&t0); - OK(sdis_solve_probe_boundary(scn, &solve_args, &estimator)); - time_sub(&t0, time_current(&t1), &t0); - time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); - - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - - switch(dim) { - case SDIS_SCENE_2D: - printf("Unstationary temperature at (%lu/%g) at t=%g = %g ~ %g +/- %g\n", - (unsigned long)solve_args.iprim, solve_args.uv[0], t[isimul], - ref[isimul], T.E, T.SE); - break; - case SDIS_SCENE_3D: - printf("Unstationary temperature at (%lu/%g,%g) at t=%g = %g ~ %g +/- %g\n", - (unsigned long)solve_args.iprim, SPLIT2(solve_args.uv), t[isimul], - ref[isimul], T.E, T.SE); - break; - default: FATAL("Unreachable code.\n"); break; - } - printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); - printf("Elapsed time = %s\n", dump); - printf("Time per realisation (in usec) = %g +/- %g\n\n", time.E, time.SE); - - CHK(eq_eps(T.E, ref[isimul], EPS)); - /*CHK(eq_eps(T.E, ref[isimul], T.SE*3));*/ - - OK(sdis_estimator_ref_put(estimator)); - } -} - -static void -solve_tbound2 - (struct sdis_scene* scn, - struct ssp_rng* rng) -{ - char dump[128]; - struct time t0, t1; - struct sdis_estimator* estimator; - struct sdis_solve_probe_boundary_args solve_args - = SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT; - struct sdis_mc T = SDIS_MC_NULL; - struct sdis_mc time = SDIS_MC_NULL; - size_t nreals; - size_t nfails; - enum sdis_scene_dimension dim; - const double t[] = { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; - const double ref[sizeof(t) / sizeof(*t)] = { - 309.08032, 309.34626, 309.46525, 309.53625, 309.58408, 309.618121, - 309.642928, 309.661167, 309.674614, 309.684524 - }; - const int nsimuls = sizeof(t) / sizeof(*t); - int isimul; - ASSERT(scn && rng); - - OK(sdis_scene_get_dimension(scn, &dim)); - - solve_args.nrealisations = N; - solve_args.side = SDIS_FRONT; - FOR_EACH(isimul, 0, nsimuls) { - solve_args.time_range[0] = solve_args.time_range[1] = t[isimul]; - if(dim == SDIS_SCENE_2D) { - solve_args.iprim = model2d_nsegments - 1; - solve_args.uv[0] = ssp_rng_uniform_double(rng, 0, 1); - } else { - double u = ssp_rng_uniform_double(rng, 0, 1); - double v = ssp_rng_uniform_double(rng, 0, 1); - double w = ssp_rng_uniform_double(rng, 0, 1); - double x = 1 / (u + v + w); - solve_args.iprim = (isimul % 2) ? 20 : 21; /* Internal face */ - solve_args.uv[0] = u * x; - solve_args.uv[1] = v * x; - } - - time_current(&t0); - OK(sdis_solve_probe_boundary(scn, &solve_args, &estimator)); - time_sub(&t0, time_current(&t1), &t0); - time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); - - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - - switch(dim) { - case SDIS_SCENE_2D: - printf("Unstationary temperature at (%lu/%g) at t=%g = %g ~ %g +/- %g\n", - (unsigned long)solve_args.iprim, solve_args.uv[0], t[isimul], - ref[isimul], T.E, T.SE); - break; - case SDIS_SCENE_3D: - printf("Unstationary temperature at (%lu/%g,%g) at t=%g = %g ~ %g +/- %g\n", - (unsigned long)solve_args.iprim, SPLIT2(solve_args.uv), t[isimul], - ref[isimul], T.E, T.SE); - break; - default: FATAL("Unreachable code.\n"); break; - } - printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); - printf("Elapsed time = %s\n", dump); - printf("Time per realisation (in usec) = %g +/- %g\n\n", time.E, time.SE); - - CHK(nfails + nreals == N); - CHK(nfails <= N/1000); - CHK(eq_eps(T.E, ref[isimul], EPS)); - /*CHK(eq_eps(T.E, ref[isimul], T.SE*3));*/ - - OK(sdis_estimator_ref_put(estimator)); - } -} - -static void -solve_tsolid - (struct sdis_scene* scn, - struct ssp_rng* rng) -{ - char dump[128]; - struct time t0, t1; - struct sdis_estimator* estimator; - struct sdis_solve_probe_args solve_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; - struct sdis_mc T = SDIS_MC_NULL; - struct sdis_mc time = SDIS_MC_NULL; - size_t nreals; - size_t nfails; - enum sdis_scene_dimension dim; - const double t[] = { 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; - const double ref[sizeof(t) / sizeof(*t)] = { - 300, 300.87408, 302.25832, 303.22164, 303.89954, 304.39030, 304.75041, - 305.01595, 305.21193, 305.35641, 305.46271 - }; - const int nsimuls = sizeof(t) / sizeof(*t); - int isimul; - ASSERT(scn && rng); - - OK(sdis_scene_get_dimension(scn, &dim)); - - solve_args.nrealisations = N; - FOR_EACH(isimul, 0, nsimuls) { - solve_args.time_range[0] = solve_args.time_range[1] = t[isimul]; - solve_args.position[0] = X_PROBE; - solve_args.position[1] = ssp_rng_uniform_double(rng, 0.1*XHpE, 0.9*XHpE); - - if(dim == SDIS_SCENE_3D) - solve_args.position[2] = ssp_rng_uniform_double(rng, 0.1*XHpE, 0.9*XHpE); - - time_current(&t0); - OK(sdis_solve_probe(scn, &solve_args, &estimator)); - time_sub(&t0, time_current(&t1), &t0); - time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); - - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - - switch (dim) { - case SDIS_SCENE_2D: - printf("Unstationary temperature at (%g,%g) at t=%g = %g ~ %g +/- %g\n", - SPLIT2(solve_args.position), t[isimul], ref[isimul], T.E, T.SE); - break; - case SDIS_SCENE_3D: - printf("Unstationary temperature at (%g,%g,%g) at t=%g = %g ~ %g +/- %g\n", - SPLIT3(solve_args.position), t[isimul], ref[isimul], T.E, T.SE); - break; - default: FATAL("Unreachable code.\n"); break; - } - printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); - printf("Elapsed time = %s\n", dump); - printf("Time per realisation (in usec) = %g +/- %g\n\n", time.E, time.SE); - - CHK(nfails + nreals == N); - CHK(nfails <= N / 1000); - CHK(eq_eps(T.E, ref[isimul], EPS)); - /*CHK(eq_eps(T.E, ref[isimul], T.SE*3));*/ - - OK(sdis_estimator_ref_put(estimator)); - } -} - -static void -solve_tfluid - (struct sdis_scene* scn) -{ - char dump[128]; - struct time t0, t1; - struct sdis_estimator* estimator; - struct sdis_solve_probe_args solve_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; - struct sdis_mc T = SDIS_MC_NULL; - struct sdis_mc time = SDIS_MC_NULL; - size_t nreals; - size_t nfails; - enum sdis_scene_dimension dim; - double eps; - const double t[] = { 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; - const double ref[sizeof(t) / sizeof(*t)] = { - 300, 309.53905, 309.67273, 309.73241, 309.76798, 309.79194, 309.80899, - 309.82141, 309.83055, 309.83728, 309.84224 - }; - const int nsimuls = sizeof(t) / sizeof(*t); - int isimul; - ASSERT(scn); - - OK(sdis_scene_get_dimension(scn, &dim)); - - solve_args.nrealisations = N; - solve_args.position[0] = XH * 0.5; - solve_args.position[1] = XH * 0.5; - solve_args.position[2] = XH * 0.5; - FOR_EACH(isimul, 0, nsimuls) { - solve_args.time_range[0] = solve_args.time_range[1] = t[isimul]; - - time_current(&t0); - OK(sdis_solve_probe(scn, &solve_args, &estimator)); - time_sub(&t0, time_current(&t1), &t0); - time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); - - OK(sdis_estimator_get_realisation_count(estimator, &nreals)); - OK(sdis_estimator_get_failure_count(estimator, &nfails)); - OK(sdis_estimator_get_temperature(estimator, &T)); - OK(sdis_estimator_get_realisation_time(estimator, &time)); - - switch (dim) { - case SDIS_SCENE_2D: - printf("Unstationary fluid temperature at t=%g = %g ~ %g +/- %g\n", - t[isimul], ref[isimul], T.E, T.SE); - break; - case SDIS_SCENE_3D: - printf("Unstationary fluid temperature at t=%g = %g ~ %g +/- %g\n", - t[isimul], ref[isimul], T.E, T.SE); - break; - default: FATAL("Unreachable code.\n"); break; - } - printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); - printf("Elapsed time = %s\n", dump); - printf("Time per realisation (in usec) = %g +/- %g\n\n", time.E, time.SE); - - CHK(nfails + nreals == N); - CHK(nfails <= N / 1000); - - eps = EPS; - CHK(eq_eps(T.E, ref[isimul], eps)); - - OK(sdis_estimator_ref_put(estimator)); - } -} - -/******************************************************************************* - * Test - ******************************************************************************/ -int -main(int argc, char** argv) -{ - struct sdis_data* data = NULL; - struct sdis_device* dev = NULL; - struct sdis_medium* fluid = NULL; - struct sdis_medium* fluid_A = NULL; - struct sdis_medium* solid = NULL; - struct sdis_medium* dummy_solid = NULL; - struct sdis_interface* interf_adiabatic_1 = NULL; - struct sdis_interface* interf_adiabatic_2 = NULL; - struct sdis_interface* interf_TG = NULL; - struct sdis_interface* interf_P = NULL; - struct sdis_interface* interf_TA = NULL; - struct sdis_scene* box_scn = NULL; - struct sdis_scene* square_scn = NULL; - struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; - struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; - struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; - struct sdis_interface* model3d_interfaces[22 /*#triangles*/]; - struct sdis_interface* model2d_interfaces[7/*#segments*/]; - struct interf interf_props; - struct solid* solid_props = NULL; - struct fluid* fluid_props = NULL; - struct ssp_rng* rng = NULL; - (void)argc, (void)argv; - - OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); - - /* Setup the solid shader */ - solid_shader.calorific_capacity = solid_get_calorific_capacity; - solid_shader.thermal_conductivity = solid_get_thermal_conductivity; - solid_shader.volumic_mass = solid_get_volumic_mass; - solid_shader.delta = solid_get_delta; - solid_shader.temperature = solid_get_temperature; - - /* Create the solid media */ - OK(sdis_data_create(dev, sizeof(struct solid), 16, NULL, &data)); - solid_props = sdis_data_get(data); - solid_props->lambda = LAMBDA; - solid_props->cp = CP_S; - solid_props->rho = RHO_S; - solid_props->delta = DELTA; - solid_props->t0 = 0; - solid_props->temperature = T0_SOLID; - OK(sdis_solid_create(dev, &solid_shader, data, &solid)); - OK(sdis_data_ref_put(data)); - - /* Create a dummy solid media to be used outside the model */ - OK(sdis_data_create(dev, sizeof(struct solid), 16, NULL, &data)); - solid_props = sdis_data_get(data); - solid_props->lambda = 0; - solid_props->cp = 1; - solid_props->rho = 1; - solid_props->delta = 1; - solid_props->t0 = INF; - solid_props->temperature = UNKNOWN_TEMPERATURE; - OK(sdis_solid_create(dev, &solid_shader, data, &dummy_solid)); - OK(sdis_data_ref_put(data)); - - /* 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; - - /* Create the internal fluid media */ - OK(sdis_data_create(dev, sizeof(struct fluid), 16, NULL, &data)); - fluid_props = sdis_data_get(data); - fluid_props->cp = CP_F; - fluid_props->rho = RHO_F; - fluid_props->t0 = 0; - fluid_props->temperature = T0_FLUID; - OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid)); - OK(sdis_data_ref_put(data)); - - /* Create the 'A' fluid media */ - OK(sdis_data_create(dev, sizeof(struct fluid), 16, NULL, &data)); - fluid_props = sdis_data_get(data); - fluid_props->cp = 1; - fluid_props->rho = 1; - fluid_props->t0 = INF; - fluid_props->temperature = TA; - OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid_A)); - OK(sdis_data_ref_put(data)); - - /* Create the adiabatic interfaces */ - interf_props.temperature = UNKNOWN_TEMPERATURE; - interf_props.h = 0; - interf_props.emissivity = 0; - interf_props.Tref = TREF; - create_interface(dev, fluid, dummy_solid, &interf_props, &interf_adiabatic_1); - create_interface(dev, solid, dummy_solid, &interf_props, &interf_adiabatic_2); - - /* Create the P interface */ - interf_props.temperature = UNKNOWN_TEMPERATURE; - interf_props.h = HC; - interf_props.emissivity = 1; - interf_props.Tref = TREF; - create_interface(dev, fluid, solid, &interf_props, &interf_P); - - /* Create the TG interface */ - interf_props.temperature = TG; - interf_props.h = HG; - interf_props.emissivity = 1; - interf_props.Tref = TG; - create_interface(dev, fluid, dummy_solid, &interf_props, &interf_TG); - - /* Create the TA interface */ - interf_props.temperature = UNKNOWN_TEMPERATURE; - interf_props.h = HA; - interf_props.emissivity = 1; - interf_props.Tref = TREF; - create_interface(dev, solid, fluid_A, &interf_props, &interf_TA); - - /* Release the media */ - OK(sdis_medium_ref_put(solid)); - OK(sdis_medium_ref_put(dummy_solid)); - OK(sdis_medium_ref_put(fluid)); - OK(sdis_medium_ref_put(fluid_A)); - - /* Front */ - model3d_interfaces[0] = interf_adiabatic_1; - model3d_interfaces[1] = interf_adiabatic_1; - model3d_interfaces[2] = interf_adiabatic_2; - model3d_interfaces[3] = interf_adiabatic_2; - /* Left */ - model3d_interfaces[4] = interf_TG; - model3d_interfaces[5] = interf_TG; - /* Back */ - model3d_interfaces[6] = interf_adiabatic_1; - model3d_interfaces[7] = interf_adiabatic_1; - model3d_interfaces[8] = interf_adiabatic_2; - model3d_interfaces[9] = interf_adiabatic_2; - /* Right */ - model3d_interfaces[10] = interf_TA; - model3d_interfaces[11] = interf_TA; - /* Top */ - model3d_interfaces[12] = interf_adiabatic_1; - model3d_interfaces[13] = interf_adiabatic_1; - model3d_interfaces[14] = interf_adiabatic_2; - model3d_interfaces[15] = interf_adiabatic_2; - /* Bottom */ - model3d_interfaces[16] = interf_adiabatic_1; - model3d_interfaces[17] = interf_adiabatic_1; - model3d_interfaces[18] = interf_adiabatic_2; - model3d_interfaces[19] = interf_adiabatic_2; - /* Inside */ - model3d_interfaces[20] = interf_P; - model3d_interfaces[21] = interf_P; - - /* Bottom */ - model2d_interfaces[0] = interf_adiabatic_2; - model2d_interfaces[1] = interf_adiabatic_1; - /* Left */ - model2d_interfaces[2] = interf_TG; - /* Top */ - model2d_interfaces[3] = interf_adiabatic_1; - model2d_interfaces[4] = interf_adiabatic_2; - /* Right */ - model2d_interfaces[5] = interf_TA; - /* Contact */ - model2d_interfaces[6] = interf_P; - - /* Create the box scene */ - scn_args.get_indices = model3d_get_indices; - scn_args.get_interface = model3d_get_interface; - scn_args.get_position = model3d_get_position; - scn_args.nprimitives = model3d_ntriangles; - scn_args.nvertices = model3d_nvertices; - scn_args.context = model3d_interfaces; - scn_args.trad.temperature = TR; - scn_args.trad.reference = TR; - scn_args.t_range[0] = MMIN(MMIN(MMIN(MMIN(T0_FLUID, T0_SOLID), TA), TG), TR); - scn_args.t_range[1] = MMAX(MMAX(MMAX(MMAX(T0_FLUID, T0_SOLID), TA), TG), TR); - OK(sdis_scene_create(dev, &scn_args, &box_scn)); - - /* Create the square scene */ - scn_args.get_indices = model2d_get_indices; - scn_args.get_interface = model2d_get_interface; - scn_args.get_position = model2d_get_position; - scn_args.nprimitives = model2d_nsegments; - scn_args.nvertices = model2d_nvertices; - scn_args.context = model2d_interfaces; - scn_args.trad.temperature = TR; - scn_args.trad.reference = TR; - scn_args.t_range[0] = MMIN(MMIN(MMIN(MMIN(T0_FLUID, T0_SOLID), TA), TG), TR); - scn_args.t_range[1] = MMAX(MMAX(MMAX(MMAX(T0_FLUID, T0_SOLID), TA), TG), TR); - OK(sdis_scene_2d_create(dev, &scn_args, &square_scn)); - - /* Release the interfaces */ - OK(sdis_interface_ref_put(interf_adiabatic_1)); - OK(sdis_interface_ref_put(interf_adiabatic_2)); - OK(sdis_interface_ref_put(interf_TG)); - OK(sdis_interface_ref_put(interf_P)); - OK(sdis_interface_ref_put(interf_TA)); - - /* Solve */ - OK(ssp_rng_create(NULL, SSP_RNG_KISS, &rng)); - printf(">> Box scene\n"); - solve_tfluid(box_scn); - solve_tbound1(box_scn, rng); - solve_tbound2(box_scn, rng); - solve_tsolid(box_scn, rng); - printf("\n>> Square scene\n"); - solve_tfluid(square_scn); - solve_tbound1(box_scn, rng); - solve_tbound2(box_scn, rng); - solve_tsolid(square_scn, rng); - - OK(sdis_scene_ref_put(box_scn)); - OK(sdis_scene_ref_put(square_scn)); - OK(sdis_device_ref_put(dev)); - OK(ssp_rng_ref_put(rng)); - - CHK(mem_allocated_size() == 0); - return 0; -} - diff --git a/src/test_sdis_unsteady.c b/src/test_sdis_unsteady.c @@ -0,0 +1,275 @@ +/* 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" + +/* + * The scene is a solid cube whose temperature is fixed on its faces. The test + * calculates its temperature at a given position at different observation times + * and validates the result against the calculated values by analytically + * solving the green functions. + * + * T3 (0.1,0.1,0.1) + * +-------+ + * /' T4 /| + * +-------+ | T1 + * T0 | +.....|.+ + * |, T5 |/ + * +-------+ + * (0,0,0) T2 + */ + +#define T_INIT 280 /* [K] */ +#define T0 310 /* [K] */ +#define T1 320 /* [K] */ +#define T2 330 /* [K] */ +#define T3 310 /* [K] */ +#define T4 320 /* [K] */ +#define T5 300 /* [K] */ + +#define NREALISATIONS 10000 +#define FP_TO_METER 0.1 + +struct reference { + double pos[3]; /* [m/FP_TO_METER] */ + double time; /* [s] */ + double temp; /* [K] */ +}; + +static const struct reference references[] = { + {{0.3, 0.4, 0.6}, 1000.0000, 281.33455593977152}, + {{0.3, 0.4, 0.6}, 2000.0000, 286.90151817350699}, + {{0.3, 0.4, 0.6}, 3000.0000, 292.84330866161531}, + {{0.3, 0.4, 0.6}, 4000.0000, 297.81444160746452}, + {{0.3, 0.4, 0.6}, 5000.0000, 301.70787295764546}, + {{0.3, 0.4, 0.6}, 10000.000, 310.78920179442139}, + {{0.3, 0.4, 0.6}, 20000.000, 313.37629443163121}, + {{0.3, 0.4, 0.6}, 30000.000, 313.51064004438581}, + {{0.3, 0.4, 0.6}, 1000000.0, 313.51797642855502} +}; +static const size_t nreferences = sizeof(references)/sizeof(*references); + +/******************************************************************************* + * Solid, i.e. medium of the cube + ******************************************************************************/ +#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, 2000.0) /* [J/K/kg] */ +SOLID_PROP(thermal_conductivity, 0.5) /* [W/m/K] */ +SOLID_PROP(volumic_mass, 2500.0) /* [kg/m^3] */ +SOLID_PROP(delta, 1.0/60.0) +#undef SOLID_PROP + +static double /* [K] */ +solid_get_temperature + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + (void)data; + ASSERT(vtx); + if(vtx->time <= 0) return T_INIT; /* Initial temperature [K] */ + return SDIS_TEMPERATURE_NONE; +} + +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 cube. 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 known + ******************************************************************************/ +static double /* [K] */ +interface_get_temperature + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + (void)data; + ASSERT(frag); + + if(frag->Ng[0] == 1) return T0; + else if(frag->Ng[0] == -1) return T1; + else if(frag->Ng[1] == 1) return T2; + else if(frag->Ng[1] == -1) return T3; + else if(frag->Ng[2] == 1) return T4; + else if(frag->Ng[2] == -1) return T5; + else FATAL("Unreachable code\n"); +} + +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; +} + +/******************************************************************************* + * The scene + ******************************************************************************/ +static void +get_interface(const size_t itri, struct sdis_interface** interf, void* ctx) +{ + (void)itri; + ASSERT(interf && ctx); + *interf = ctx; +} + +static struct sdis_scene* +create_scene + (struct sdis_device* sdis, + struct sdis_interface* interf) +{ + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + struct sdis_scene* scn = NULL; + + scn_args.get_indices = box_get_indices; + scn_args.get_interface = get_interface; + scn_args.get_position = box_get_position; + scn_args.nprimitives = box_ntriangles; + scn_args.nvertices = box_nvertices; + scn_args.context = interf; + scn_args.fp_to_meter = FP_TO_METER; + OK(sdis_scene_create(sdis, &scn_args, &scn)); + + return scn; +} + +/******************************************************************************* + * Validations + ******************************************************************************/ +static void +check_probe + (struct sdis_scene* scn, + const struct reference* ref, + const enum sdis_diffusion_algorithm diff_algo) +{ + struct sdis_solve_probe_args args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_estimator* estimator = NULL; + CHK(ref); + + args.position[0] = ref->pos[0]; + args.position[1] = ref->pos[1]; + args.position[2] = ref->pos[2]; + args.time_range[0] = + args.time_range[1] = ref->time; /* [s] */ + args.diff_algo = diff_algo; + args.nrealisations = NREALISATIONS; + OK(sdis_solve_probe(scn, &args, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + + printf("T(%g, %g, %g) at %g s = %g ~ %g +/- %g\n", + ref->pos[0]*FP_TO_METER, ref->pos[1]*FP_TO_METER, ref->pos[2]*FP_TO_METER, + ref->time, ref->temp, T.E, T.SE); + CHK(eq_eps(ref->temp, T.E, 3*T.SE)); + + OK(sdis_estimator_ref_put(estimator)); +} + +static void +check + (struct sdis_scene* scn, + const enum sdis_diffusion_algorithm diff_algo) +{ + const char* str_algo = NULL; + size_t i = 0; + + switch(diff_algo) { + case SDIS_DIFFUSION_WOS: str_algo = "Walk on Sphere"; break; + case SDIS_DIFFUSION_DELTA_SPHERE: str_algo = "Delta sphere"; break; + default: FATAL("Unreachable code.\n"); break; + } + + printf("\n\x1b[1m\x1b[35m%s\x1b[0m\n", str_algo); + FOR_EACH(i, 0, nreferences) { + check_probe(scn, references + i, diff_algo); + } +} + +/******************************************************************************* + * The test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct sdis_device* sdis = NULL; + struct sdis_interface* interf = NULL; + struct sdis_medium* solid = NULL; + struct sdis_medium* dummy = NULL; + struct sdis_scene* scene = NULL; + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &sdis)); + + solid = create_solid(sdis); + dummy = create_dummy(sdis); + interf = create_interface(sdis, solid, dummy); + scene = create_scene(sdis, interf); + + check(scene, SDIS_DIFFUSION_DELTA_SPHERE); + check(scene, SDIS_DIFFUSION_WOS); + + OK(sdis_interface_ref_put(interf)); + OK(sdis_medium_ref_put(solid)); + OK(sdis_medium_ref_put(dummy)); + OK(sdis_scene_ref_put(scene)); + OK(sdis_device_ref_put(sdis)); + + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_unsteady_1d.c b/src/test_sdis_unsteady_1d.c @@ -0,0 +1,268 @@ +/* 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" + +/* + * The scene is a solid 1d slab simulated by square whose temperature is fixed + * on two of its faces, the two others are adiabatic. The test calculates its + * temperature at a given position at different observation times and validates + * the result against the calculated values by analytically solving the green + * functions. + * + * ///////(0.1,0.1) + * +-------+ + * | | T1 + * | | + * T0 | | + * +-------+ + * (0,0)/////// + */ + +#define T_INIT 280 /* [K] */ +#define T0 310 /* [K] */ +#define T1 320 /* [K] */ + +#define NREALISATIONS 10000 +#define FP_TO_METER 0.1 + +struct reference { + double pos[2]; /* [m/FP_TO_METER] */ + double time; /* [s] */ + double temp; /* [K] */ +}; + +static const struct reference references[] = { + {{0.5, 0.5}, 1000.0000, 280.02848664122115}, + {{0.5, 0.5}, 2000.0000, 280.86935314560424}, + {{0.5, 0.5}, 3000.0000, 282.88587826961236}, + {{0.5, 0.5}, 4000.0000, 285.39698306113996}, + {{0.5, 0.5}, 5000.0000, 287.96909375994932}, + {{0.5, 0.5}, 10000.000, 298.39293888670881}, + {{0.5, 0.5}, 20000.000, 308.80965010883347}, + {{0.5, 0.5}, 30000.000, 312.69280796373141}, + {{0.5, 0.5}, 1000000.0, 315.00000000000000} +}; +static const size_t nreferences = sizeof(references)/sizeof(*references); + +/******************************************************************************* + * Solid, i.e. medium of the cube + ******************************************************************************/ +#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, 2000.0) /* [J/K/kg] */ +SOLID_PROP(thermal_conductivity, 0.5) /* [W/m/K] */ +SOLID_PROP(volumic_mass, 2500.0) /* [kg/m^3] */ +SOLID_PROP(delta, 1.0/60.0) +#undef SOLID_PROP + +static double /* [K] */ +solid_get_temperature + (const struct sdis_rwalk_vertex* vtx, + struct sdis_data* data) +{ + (void)data; + ASSERT(vtx); + if(vtx->time <= 0) return T_INIT; /* Initial temperature [K] */ + return SDIS_TEMPERATURE_NONE; /* Unknown temperature */ +} + +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 cube. 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 of the system + ******************************************************************************/ +static double /* [K] */ +interface_get_temperature + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + (void)data; + ASSERT(frag); + + if(frag->Ng[0] == 1) return T0; + else if(frag->Ng[0] == -1) return T1; + else if(frag->Ng[1] == 1) return SDIS_TEMPERATURE_NONE; + else if(frag->Ng[1] == -1) return SDIS_TEMPERATURE_NONE; + else FATAL("Unreachable code\n"); +} + +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; +} + +/******************************************************************************* + * The scene + ******************************************************************************/ +static void +get_interface(const size_t itri, struct sdis_interface** interf, void* ctx) +{ + (void)itri; + ASSERT(interf && ctx); + *interf = ctx; +} + +static struct sdis_scene* +create_scene + (struct sdis_device* sdis, + struct sdis_interface* interf) +{ + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + struct sdis_scene* scn = NULL; + + scn_args.get_indices = square_get_indices; + scn_args.get_interface = get_interface; + scn_args.get_position = square_get_position; + scn_args.nprimitives = square_nsegments; + scn_args.nvertices = square_nvertices; + scn_args.context = interf; + scn_args.fp_to_meter = FP_TO_METER; + OK(sdis_scene_2d_create(sdis, &scn_args, &scn)); + + return scn; +} + +/******************************************************************************* + * Validations + ******************************************************************************/ +static void +check_probe + (struct sdis_scene* scn, + const struct reference* ref, + const enum sdis_diffusion_algorithm diff_algo) +{ + struct sdis_solve_probe_args args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_estimator* estimator = NULL; + CHK(ref); + + args.position[0] = ref->pos[0]; + args.position[1] = ref->pos[1]; + args.time_range[0] = + args.time_range[1] = ref->time; /* [s] */ + args.diff_algo = diff_algo; + args.nrealisations = NREALISATIONS; + OK(sdis_solve_probe(scn, &args, &estimator)); + OK(sdis_estimator_get_temperature(estimator, &T)); + + printf("T(%g, %g) at %g s = %g ~ %g +/- %g\n", + ref->pos[0]*FP_TO_METER, ref->pos[1]*FP_TO_METER, + ref->time, ref->temp, T.E, T.SE); + CHK(eq_eps(ref->temp, T.E, 3*T.SE)); + + OK(sdis_estimator_ref_put(estimator)); +} + +static void +check + (struct sdis_scene* scn, + const enum sdis_diffusion_algorithm diff_algo) +{ + const char* str_algo = NULL; + size_t i = 0; + + switch(diff_algo) { + case SDIS_DIFFUSION_WOS: str_algo = "Walk on Sphere"; break; + case SDIS_DIFFUSION_DELTA_SPHERE: str_algo = "Delta sphere"; break; + default: FATAL("Unreachable code.\n"); break; + } + + printf("\n\x1b[1m\x1b[35m%s\x1b[0m\n", str_algo); + FOR_EACH(i, 0, nreferences) { + check_probe(scn, references + i, diff_algo); + } +} + +/******************************************************************************* + * The test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct sdis_device* sdis = NULL; + struct sdis_interface* interf = NULL; + struct sdis_medium* solid = NULL; + struct sdis_medium* dummy = NULL; + struct sdis_scene* scene = NULL; + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &sdis)); + + solid = create_solid(sdis); + dummy = create_dummy(sdis); + interf = create_interface(sdis, solid, dummy); + scene = create_scene(sdis, interf); + + check(scene, SDIS_DIFFUSION_DELTA_SPHERE); + check(scene, SDIS_DIFFUSION_WOS); + + 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(scene)); + + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_unsteady_analytic_profile.c b/src/test_sdis_unsteady_analytic_profile.c @@ -0,0 +1,380 @@ +/* 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/mem_allocator.h> + +/* + * The system is an unsteady-state temperature profile, meaning that at any + * point, at any: time, we can analytically calculate the temperature. We + * immerse in this temperature field a supershape representing a solid in which + * we want to evaluate the temperature by Monte Carlo at a given position and + * observation time. On the Monte Carlo side, the temperature of the supershape + * is unknown. Only the boundary temperature is fixed at the temperature of the + * unsteady trilinear profile mentioned above. Monte Carlo would then have to + * find the temperature defined by the unsteady profile. + * + * T(z) /\ <-- T(x,y,z,t) + * | T(y) ___/ \___ + * |/ \ . T=? / + * o--- T(x) /_ __ _\ + * \/ \/ + */ + +#define LAMBDA 0.1 /* [W/(m.K)] */ +#define RHO 25.0 /* [kg/m^3] */ +#define CP 2.0 /* [J/K/kg)] */ +#define DELTA 1.0/20.0 /* [m/fp_to_meter] */ + +#define NREALISATIONS 100000 + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static double +temperature(const double pos[3], const double time) +{ + const double kx = PI/4.0; + const double ky = PI/4.0; + const double kz = PI/4.0; + const double alpha = LAMBDA / (RHO*CP); /* Diffusivity */ + + const double A = 0; /* No termal source */ + const double B1 = 10; + const double B2 = 1000; + + double x, y, z, t; + double a, b, c; + double temp; + + ASSERT(pos); + + x = pos[0]; + y = pos[1]; + z = pos[2]; + t = time; + + a = B1*(x*x*x*z-3*x*y*y*z); + b = B2*sin(kx*x)*sin(ky*y)*sin(kz*z)*exp(-alpha*(kx*kx + ky*ky + kz*kz)*t); + c = A * x*x*x*x * y*y*y * z*z; + + temp = (a + b - c) / LAMBDA; + return temp; +} + +static INLINE void +dump_s3dut_mesh(FILE* fp, const struct s3dut_mesh* mesh) +{ + struct s3dut_mesh_data mesh_data; + + OK(s3dut_mesh_get_data(mesh, &mesh_data)); + dump_mesh(fp, mesh_data .positions, mesh_data.nvertices, + mesh_data.indices, mesh_data.nprimitives); +} + +static void +dump_paths + (FILE* fp, + struct sdis_scene* scn, + const enum sdis_diffusion_algorithm diff_algo, + const double pos[3], + const double time, + const size_t npaths) +{ + struct sdis_solve_probe_args args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_estimator* estimator = NULL; + + args.nrealisations = npaths; + args.position[0] = pos[0]; + args.position[1] = pos[1]; + args.position[2] = pos[2]; + args.time_range[0] = time; + args.time_range[1] = time; + args.diff_algo = diff_algo; + args.register_paths = SDIS_HEAT_PATH_ALL; + OK(sdis_solve_probe(scn, &args, &estimator)); + + dump_heat_paths(fp, estimator); + + OK(sdis_estimator_ref_put(estimator)); +} + +/******************************************************************************* + * Geometry + ******************************************************************************/ +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; +} + +/******************************************************************************* + * 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; +} + +/******************************************************************************* + * 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, CP) /* [J/K/kg] */ +SOLID_PROP(thermal_conductivity, LAMBDA) /* [W/m/K] */ +SOLID_PROP(volumic_mass, RHO) /* [kg/m^3] */ +SOLID_PROP(delta, DELTA) /* [m/fp_to_meter] */ +SOLID_PROP(temperature, SDIS_TEMPERATURE_NONE/*<=> unknown*/) /* [K] */ +#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; + shader.t0 = -INF; + 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: its temperature is fixed to the unsteady-state profile + ******************************************************************************/ +static double +interface_get_temperature + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + (void)data; + return temperature(frag->P, frag->time); +} + +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; +} + +/******************************************************************************* + * Validations + ******************************************************************************/ +static void +check_probe + (struct sdis_scene* scn, + const enum sdis_diffusion_algorithm diff_algo, + const double pos[3], + const double time, /* [s] */ + const int green) +{ + 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.nrealisations = NREALISATIONS; + args.position[0] = pos[0]; + args.position[1] = pos[1]; + args.position[2] = pos[2]; + args.time_range[0] = time; + args.time_range[1] = time; + args.diff_algo = diff_algo; + + if(!green) { + OK(sdis_solve_probe(scn, &args, &estimator)); + } else { + struct sdis_green_function* greenfn = NULL; + + OK(sdis_solve_probe_green_function(scn, &args, &greenfn)); + OK(sdis_green_function_solve(greenfn, &estimator)); + OK(sdis_green_function_ref_put(greenfn)); + } + + OK(sdis_estimator_get_temperature(estimator, &T)); + + ref = temperature(pos, time); + printf("T(%g, %g, %g, %g) = %g ~ %g +/- %g\n", + SPLIT3(pos), time, ref, T.E, T.SE); + CHK(eq_eps(ref, T.E, 3*T.SE)); + + OK(sdis_estimator_ref_put(estimator)); +} + +/******************************************************************************* + * 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 */ + FILE* fp = NULL; + struct s3dut_mesh* super_shape = NULL; + const double pos[3] = {0.2,0.3,0.4}; /* [m/fp_to_meter] */ + const double time = 5; /* [s] */ + + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &sdis)); + + super_shape = create_super_shape(); + + /* Save the super shape geometry for debug and visualisation */ + CHK(fp = fopen("super_shape_3d.obj", "w")); + dump_s3dut_mesh(fp, super_shape); + CHK(fclose(fp) == 0); + + solid = create_solid(sdis); + dummy = create_dummy(sdis); + interf = create_interface(sdis, solid, dummy); + scn = create_scene(sdis, super_shape, interf); + + check_probe(scn, SDIS_DIFFUSION_DELTA_SPHERE, pos, time, 0/*green*/); + check_probe(scn, SDIS_DIFFUSION_WOS, pos, time, 0/*green*/); + check_probe(scn, SDIS_DIFFUSION_WOS, pos, time, 1/*green*/); + + /* Write 10 heat paths sampled by the delta sphere algorithm */ + CHK(fp = fopen("paths_delta_sphere_3d.vtk", "w")); + dump_paths(fp, scn, SDIS_DIFFUSION_DELTA_SPHERE, pos, time, 10); + CHK(fclose(fp) == 0); + + /* Write 10 heat paths sampled by the WoS algorithm */ + CHK(fp = fopen("paths_wos_3d.vtk", "w")); + dump_paths(fp, scn, SDIS_DIFFUSION_WOS, pos, time, 10); + CHK(fclose(fp) == 0); + + 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_unsteady_analytic_profile_2d.c b/src/test_sdis_unsteady_analytic_profile_2d.c @@ -0,0 +1,410 @@ +/* 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 <rsys/mem_allocator.h> +#include <rsys/stretchy_array.h> + +/* + * The system is an unsteady-state temperature profile, meaning that at any + * point, at any: time, we can analytically calculate the temperature. We + * immerse in this temperature field a supershape representing a solid in which + * we want to evaluate the temperature by Monte Carlo at a given position and + * observation time. On the Monte Carlo side, the temperature of the supershape + * is unknown. Only the boundary temperature is fixed at the temperature of the + * unsteady trilinear profile mentioned above. Monte Carlo would then have to + * find the temperature defined by the unsteady profile. + * + * T(y) /\ <-- T(x,y,t) + * | ___/ \___ + * | \ . T=? / + * o--- T(x) /_ __ _\ + * \/ \/ + * + */ + +#define LAMBDA 0.1 /* [W/(m.K)] */ +#define RHO 25.0 /* [kg/m^3] */ +#define CP 2.0 /* [J/K/kg)] */ +#define DELTA 1.0/20.0 /* [m/fp_to_meter] */ + +#define NREALISATIONS 100000 + +struct super_shape { + double* positions; + size_t* indices; +}; +#define SUPER_SHAPE_NULL__ {NULL, NULL} +static const struct super_shape SUPER_SHAPE_NULL = SUPER_SHAPE_NULL__; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +/* Unsteady-state temperature profile. Its corresponding power term is : + * P(x, y) = A*[12*x^2*y^3 + 5*x^4*y] */ +static double temperature(const double pos[2], const double time) +{ + const double kx = PI/4; + const double ky = PI/4; + const double alpha = LAMBDA / (RHO*CP); /* Diffusivity */ + + const double A = 0; /* No termal source */ + const double B1 = 0; + const double B2 = 1000; + + double x, y, t; + double a, b, c; + double temp; + ASSERT(pos); + + x = pos[0]; + y = pos[1]; + t = time; + + a = B1*(x*x*x - 3*x*y*y); + b = B2*sin(kx*x)*sin(ky*y)*exp(-alpha*(kx*kx + ky*ky)*t); + c = A * x*x*x*x + y*y*y; + + temp = (a + b - c) / LAMBDA; + return temp; +} + +static void +dump_paths + (FILE* fp, + struct sdis_scene* scn, + const enum sdis_diffusion_algorithm diff_algo, + const double pos[2], + const double time, + const size_t npaths) +{ + struct sdis_solve_probe_args args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_estimator* estimator = NULL; + + args.nrealisations = npaths; + args.position[0] = pos[0]; + args.position[1] = pos[1]; + args.time_range[0] = time; + args.time_range[1] = time; + args.diff_algo = diff_algo; + args.register_paths = SDIS_HEAT_PATH_ALL; + OK(sdis_solve_probe(scn, &args, &estimator)); + + dump_heat_paths(fp, estimator); + + OK(sdis_estimator_ref_put(estimator)); +} + +/******************************************************************************* + * 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, CP) /* [J/K/kg] */ +SOLID_PROP(thermal_conductivity, LAMBDA) /* [W/m/K] */ +SOLID_PROP(volumic_mass, RHO) /* [kg/m^3] */ +SOLID_PROP(delta, DELTA) /* [m/fp_to_meter] */ +SOLID_PROP(temperature, SDIS_TEMPERATURE_NONE/*<=> unknown*/) /* [K] */ +#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; + shader.t0 = -INF; + 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: its temperature is fixed to the unsteady-state profile + ******************************************************************************/ +static double +interface_get_temperature + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + (void)data; + return temperature(frag->P, frag->time); +} + +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; +} + +/******************************************************************************* + * Super shape + ******************************************************************************/ +static struct super_shape +create_super_shape(void) +{ + struct super_shape sshape = SUPER_SHAPE_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; +} + +static void +release_super_shape(struct super_shape* sshape) +{ + CHK(sshape); + sa_release(sshape->positions); + sa_release(sshape->indices); +} + +static INLINE size_t +super_shape_nsegments(const struct super_shape* sshape) +{ + CHK(sshape); + return sa_size(sshape->indices) / 2/*#indices per segment*/; +} + +static INLINE size_t +super_shape_nvertices(const struct super_shape* sshape) +{ + CHK(sshape); + return sa_size(sshape->positions) / 2/*#coords per vertex*/; +} + +/******************************************************************************* + * Scene, i.e. the system to simulate + ******************************************************************************/ +struct scene_context { + const struct super_shape* 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 super_shape* 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 = super_shape_nsegments(sshape); + scn_args.nvertices = super_shape_nvertices(sshape); + scn_args.context = &context; + OK(sdis_scene_2d_create(sdis, &scn_args, &scn)); + return scn; +} + +/******************************************************************************* + * Validations + ******************************************************************************/ +static void +check_probe + (struct sdis_scene* scn, + const enum sdis_diffusion_algorithm diff_algo, + const double pos[2], + const double time, /* [s] */ + const int green) +{ + 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.nrealisations = NREALISATIONS; + args.position[0] = pos[0]; + args.position[1] = pos[1]; + args.time_range[0] = time; + args.time_range[1] = time; + args.diff_algo = diff_algo; + + if(!green) { + OK(sdis_solve_probe(scn, &args, &estimator)); + } else { + struct sdis_green_function* greenfn = NULL; + + OK(sdis_solve_probe_green_function(scn, &args, &greenfn)); + OK(sdis_green_function_solve(greenfn, &estimator)); + OK(sdis_green_function_ref_put(greenfn)); + } + OK(sdis_estimator_get_temperature(estimator, &T)); + + ref = temperature(pos, time); + printf("T(%g, %g, %g) = %g ~ %g +/- %g\n", + SPLIT2(pos), time, ref, T.E, T.SE); + CHK(eq_eps(ref, T.E, 3*T.SE)); + + OK(sdis_estimator_ref_put(estimator)); +} + +/******************************************************************************* + * 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 */ + FILE* fp = NULL; + struct super_shape sshape = SUPER_SHAPE_NULL; + const double pos[3] = {0.2,0.3}; /* [m/fp_to_meter] */ + const double time = 5; /* [s] */ + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &sdis)); + + sshape = create_super_shape(); + + /* Save the super shape geometry for debug and visualisation */ + CHK(fp = fopen("super_shape_2d.obj", "w")); + dump_segments(fp, sshape.positions, super_shape_nvertices(&sshape), + sshape.indices, super_shape_nsegments(&sshape)); + CHK(fclose(fp) == 0); + + solid = create_solid(sdis); + dummy = create_dummy(sdis); + interf = create_interface(sdis, solid, dummy); + scn = create_scene(sdis, &sshape, interf); + + check_probe(scn, SDIS_DIFFUSION_DELTA_SPHERE, pos, time, 0/*green*/); + check_probe(scn, SDIS_DIFFUSION_WOS, pos, time, 0/*green*/); + check_probe(scn, SDIS_DIFFUSION_WOS, pos, time, 1/*green*/); + + /* Write 10 heat paths sampled by the delta sphere algorithm */ + CHK(fp = fopen("paths_delta_sphere_2d.vtk", "w")); + dump_paths(fp, scn, SDIS_DIFFUSION_DELTA_SPHERE, pos, time, 10); + CHK(fclose(fp) == 0); + + /* Write 10 heat paths sampled by the WoS algorithm */ + CHK(fp = fopen("paths_wos_2d.vtk", "w")); + dump_paths(fp, scn, SDIS_DIFFUSION_WOS, pos, time, 10); + CHK(fclose(fp) == 0); + + release_super_shape(&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_unsteady_atm.c b/src/test_sdis_unsteady_atm.c @@ -0,0 +1,956 @@ +/* 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 <rsys/clock_time.h> +#include <rsys/mem_allocator.h> +#include <rsys/double3.h> +#include <rsys/math.h> +#include <star/ssp.h> + +/* + * The physical configuration is the following: a slab of fluid with known + * thermophysical properties but unknown temperature is located between a + * "ground" and a slab of solid, with also a unknown temperature profile. On + * the other side of the solid slab, is an "atmosphere" with known temperature, + * and known radiative temperature. + * + * Solving the system means: finding the temperature of the ground, of the + * fluid, of the boundaries, and also the temperature inside the solid, at + * various locations (the 1D slab is discretized in order to obtain the + * reference) + * + * The reference for this system comes from a numerical method and is not + * analytic. Thus the compliance test MC VS reference is not the usual |MC - + * ref| <= 3*sigma but is |MC -ref| <= (Tmax -Tmin) * 0.01. + * + * 3D 2D + * + * /////////////// /////////////// + * +-----------+-+ +-----------+-+ + * /' / /| | | | + * +-----------+-+ | HA _\ <---- TR | _\ | | HA _\ <---- TR + * | | _\ | | | / / <---- TR TG| HG / / HC | | / / <---- TR + * | |HG / / HC| | | TA \__/ <---- TR | \__/ | | TA \__/ <---- TR + * TG| | \__/ | | | | | | + * | +.........|.|.+ +-----------+-+ + * |, |/|/ /////H///////E/ + * +-----------+-+ + * //////H//////E/ + */ + +#define XH 3 +#define XHpE 3.2 +#define XE (XHpE - XH) + +#define T0_SOLID 300 +#define T0_FLUID 300 + +#define N 10000 /* #realisations */ + +#define TG 310 +#define HG 400 + +#define HC 400 + +#define TA 290 +#define HA 400 +#define TR 260 + +#define TMAX (MMAX(MMAX(MMAX(T0_FLUID, T0_SOLID), MMAX(TG, TA)), TR)) +#define TMIN (MMIN(MMIN(MMIN(T0_FLUID, T0_SOLID), MMIN(TG, TA)), TR)) +#define EPS ((TMAX-TMIN)*0.01) + +/* hr = 4.0 * BOLTZMANN_CONSTANT * Tref * Tref * Tref * epsilon + * Tref = (hr / (4 * 5.6696e-8 * epsilon)) ^ 1/3, hr = 6 */ +#define TREF 297.974852286 + +#define RHO_F 1.3 +#define CP_F 1005 +#define RHO_S 2400 +#define CP_S 800 +#define LAMBDA 0.6 + +#define X_PROBE (XH + 0.2 * XE) + +#define DELTA (XE/40.0) + +/******************************************************************************* + * Box geometry + ******************************************************************************/ +static const double model3d_vertices[12/*#vertices*/*3/*#coords per vertex*/] = { + 0, 0, 0, + XH, 0, 0, + XHpE, 0, 0, + 0, XHpE, 0, + XH, XHpE, 0, + XHpE, XHpE, 0, + 0, 0, XHpE, + XH, 0, XHpE, + XHpE, 0, XHpE, + 0, XHpE, XHpE, + XH, XHpE, XHpE, + XHpE, XHpE, XHpE +}; +static const size_t model3d_nvertices = sizeof(model3d_vertices)/(sizeof(double)*3); + +/* The following array lists the indices toward the 3D vertices of each + * triangle. + * ,3---,4---,5 ,3----4----5 ,4 + * ,' | ,' | ,'/| ,'/| \ | \ | ,'/| + * 9----10---11 / | 9' / | \ | \ | 10 / | Y + * |', |', | / ,2 | / ,0---,1---,2 | / ,1 | + * | ',| ',|/,' |/,' | ,' | ,' |/,' o--X + * 6----7----8' 6----7'---8' 7 / + * Front, right Back, left and Internal Z + * and Top faces bottom faces face */ +static const size_t model3d_indices[22/*#triangles*/*3/*#indices per triangle*/] = { + 0, 3, 1, 1, 3, 4, 1, 4, 2, 2, 4, 5, /* -Z */ + 0, 6, 3, 3, 6, 9, /* -X */ + 6, 7, 9, 9, 7, 10, 7, 8, 10, 10, 8, 11, /* +Z */ + 5, 11, 8, 8, 2, 5, /* +X */ + 3, 9, 10, 10, 4, 3, 4, 10, 11, 11, 5, 4, /* +Y */ + 0, 1, 7, 7, 6, 0, 1, 2, 8, 8, 7, 1, /* -Y */ + 4, 10, 7, 7, 1, 4 /* Inside */ +}; +static const size_t model3d_ntriangles = sizeof(model3d_indices)/(sizeof(size_t)*3); + +static INLINE void +model3d_get_indices(const size_t itri, size_t ids[3], void* context) +{ + (void)context; + CHK(ids); + CHK(itri < model3d_ntriangles); + ids[0] = model3d_indices[itri * 3 + 0]; + ids[1] = model3d_indices[itri * 3 + 1]; + ids[2] = model3d_indices[itri * 3 + 2]; +} + +static INLINE void +model3d_get_position(const size_t ivert, double pos[3], void* context) +{ + (void)context; + CHK(pos); + CHK(ivert < model3d_nvertices); + pos[0] = model3d_vertices[ivert * 3 + 0]; + pos[1] = model3d_vertices[ivert * 3 + 1]; + pos[2] = model3d_vertices[ivert * 3 + 2]; +} + +static INLINE void +model3d_get_interface(const size_t itri, struct sdis_interface** bound, void* context) +{ + struct sdis_interface** interfaces = context; + CHK(context && bound); + CHK(itri < model3d_ntriangles); + *bound = interfaces[itri]; +} + +/******************************************************************************* + * Square geometry + ******************************************************************************/ +static const double model2d_vertices[6/*#vertices*/ * 2/*#coords per vertex*/] = { + XHpE, 0, + XH, 0, + 0, 0, + 0, XHpE, + XH, XHpE, + XHpE, XHpE +}; +static const size_t model2d_nvertices = sizeof(model2d_vertices)/(sizeof(double)*2); + +static const size_t model2d_indices[7/*#segments*/ * 2/*#indices per segment*/] = { + 0, 1, 1, 2, /* Bottom */ + 2, 3, /* Left */ + 3, 4, 4, 5, /* Top */ + 5, 0, /* Right */ + 4, 1 /* Inside */ +}; +static const size_t model2d_nsegments = sizeof(model2d_indices)/(sizeof(size_t)*2); + +static INLINE void +model2d_get_indices(const size_t iseg, size_t ids[2], void* context) +{ + (void)context; + CHK(ids); + CHK(iseg < model2d_nsegments); + ids[0] = model2d_indices[iseg * 2 + 0]; + ids[1] = model2d_indices[iseg * 2 + 1]; +} + +static INLINE void +model2d_get_position(const size_t ivert, double pos[2], void* context) +{ + (void)context; + CHK(pos); + CHK(ivert < model2d_nvertices); + pos[0] = model2d_vertices[ivert * 2 + 0]; + pos[1] = model2d_vertices[ivert * 2 + 1]; +} + +static INLINE void +model2d_get_interface +(const size_t iseg, struct sdis_interface** bound, void* context) +{ + struct sdis_interface** interfaces = context; + CHK(context && bound); + CHK(iseg < model2d_nsegments); + *bound = interfaces[iseg]; +} + +/******************************************************************************* + * Media + ******************************************************************************/ +struct solid { + double lambda; + double rho; + double cp; + double delta; + double temperature; + double t0; +}; + +static double +solid_get_calorific_capacity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + struct solid* solid; + CHK(vtx && data); + solid = ((struct solid*)sdis_data_cget(data)); + return solid->cp; +} + +static double +solid_get_thermal_conductivity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + struct solid* solid; + CHK(vtx && data); + solid = ((struct solid*)sdis_data_cget(data)); + return solid->lambda; +} + +static double +solid_get_volumic_mass + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + struct solid* solid; + CHK(vtx && data); + solid = ((struct solid*)sdis_data_cget(data)); + return solid->rho; +} + +static double +solid_get_delta + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + struct solid* solid; + CHK(vtx && data); + solid = ((struct solid*)sdis_data_cget(data)); + return solid->delta; +} + +static double +solid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + struct solid* solid; + CHK(vtx && data); + solid = ((struct solid*)sdis_data_cget(data)); + if(vtx->time <= solid->t0) + return solid->temperature; + return SDIS_TEMPERATURE_NONE; +} + +struct fluid { + double rho; + double cp; + double t0; + double temperature; +}; + +static double +fluid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + struct fluid* fluid; + CHK(vtx && data); + fluid = ((struct fluid*)sdis_data_cget(data)); + if(vtx->time <= fluid->t0) + return fluid->temperature; + return SDIS_TEMPERATURE_NONE; +} + +static double +fluid_get_volumic_mass + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + struct fluid* fluid; + CHK(vtx && data); + fluid = ((struct fluid*)sdis_data_cget(data)); + return fluid->rho; +} + +static double +fluid_get_calorific_capacity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + struct fluid* fluid; + CHK(vtx && data); + fluid = ((struct fluid*)sdis_data_cget(data)); + return fluid->cp; +} + +/******************************************************************************* + * Interfaces + ******************************************************************************/ +struct interf { + double temperature; + double emissivity; + double h; + double Tref; +}; + +static double +interface_get_temperature + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + const struct interf* interf; + CHK(frag && data); + interf = sdis_data_cget(data); + return interf->temperature; +} + +static double +interface_get_convection_coef + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + const struct interf* interf; + CHK(frag && data); + interf = sdis_data_cget(data); + return interf->h; +} + +static double +interface_get_emissivity + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) +{ + const struct interf* interf; + (void)source_id; + CHK(frag && data); + interf = sdis_data_cget(data); + return interf->emissivity; +} + +static double +interface_get_Tref + (const struct sdis_interface_fragment* frag, + struct sdis_data* data) +{ + const struct interf* interf; + CHK(frag && data); + interf = sdis_data_cget(data); + return interf->Tref; +} + +/******************************************************************************* + * Radiative environment + ******************************************************************************/ +static double +radenv_get_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray, (void)data; + return TR; /* [K] */ +} + +static double +radenv_get_reference_temperature + (const struct sdis_radiative_ray* ray, + struct sdis_data* data) +{ + (void)ray, (void)data; + return TR; /* [K] */ +} + +static struct sdis_radiative_env* +create_radenv(struct sdis_device* sdis) +{ + struct sdis_radiative_env_shader shader = SDIS_RADIATIVE_ENV_SHADER_NULL; + struct sdis_radiative_env* radenv = NULL; + + shader.temperature = radenv_get_temperature; + shader.reference_temperature = radenv_get_reference_temperature; + OK(sdis_radiative_env_create(sdis, &shader, NULL, &radenv)); + return radenv; +} + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +create_interface + (struct sdis_device* dev, + struct sdis_medium* front, + struct sdis_medium* back, + const struct interf* interf, + struct sdis_interface** out_interf) +{ + struct sdis_interface_shader shader = SDIS_INTERFACE_SHADER_NULL; + struct sdis_data* data = NULL; + + CHK(interf != NULL); + + shader.front.temperature = interface_get_temperature; + shader.back.temperature = interface_get_temperature; + if(sdis_medium_get_type(front) != sdis_medium_get_type(back)) { + shader.convection_coef = interface_get_convection_coef; + shader.convection_coef_upper_bound = interf->h; + } + if(sdis_medium_get_type(front) == SDIS_FLUID) { + shader.front.emissivity = interface_get_emissivity; + shader.front.reference_temperature = interface_get_Tref; + } + if(sdis_medium_get_type(back) == SDIS_FLUID) { + shader.back.emissivity = interface_get_emissivity; + shader.back.reference_temperature = interface_get_Tref; + } + + OK(sdis_data_create(dev, sizeof(struct interf), ALIGNOF(struct interf), + NULL, &data)); + *((struct interf*)sdis_data_get(data)) = *interf; + + OK(sdis_interface_create(dev, front, back, &shader, data, out_interf)); + OK(sdis_data_ref_put(data)); +} + +static void +solve_tbound1 + (struct sdis_scene* scn, + struct ssp_rng* rng) +{ + char dump[128]; + struct time t0, t1; + struct sdis_estimator* estimator; + struct sdis_solve_probe_boundary_args solve_args + = SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_mc time = SDIS_MC_NULL; + size_t nreals; + size_t nfails; + enum sdis_scene_dimension dim; + const double t[] = { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; + const double ref[sizeof(t) / sizeof(*t)] = { + 290.046375, 289.903935, 289.840490, 289.802690, 289.777215, 289.759034, + 289.745710, 289.735826, 289.728448, 289.722921 + }; + const int nsimuls = sizeof(t) / sizeof(*t); + int isimul; + ASSERT(scn && rng); + + OK(sdis_scene_get_dimension(scn, &dim)); + + solve_args.nrealisations = N; + solve_args.side = SDIS_FRONT; + FOR_EACH(isimul, 0, nsimuls) { + solve_args.time_range[0] = solve_args.time_range[1] = t[isimul]; + if(dim == SDIS_SCENE_2D) { + solve_args.iprim = model2d_nsegments - 1; + solve_args.uv[0] = ssp_rng_uniform_double(rng, 0, 1); + } else { + double u = ssp_rng_uniform_double(rng, 0, 1); + double v = ssp_rng_uniform_double(rng, 0, 1); + double w = ssp_rng_uniform_double(rng, 0, 1); + double x = 1 / (u + v + w); + solve_args.iprim = (isimul % 2) ? 10 : 11; /* +X face */ + solve_args.uv[0] = u * x; + solve_args.uv[1] = v * x; + } + + time_current(&t0); + OK(sdis_solve_probe_boundary(scn, &solve_args, &estimator)); + time_sub(&t0, time_current(&t1), &t0); + time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); + + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_time(estimator, &time)); + + switch(dim) { + case SDIS_SCENE_2D: + printf("Unstationary temperature at (%lu/%g) at t=%g = %g ~ %g +/- %g\n", + (unsigned long)solve_args.iprim, solve_args.uv[0], t[isimul], + ref[isimul], T.E, T.SE); + break; + case SDIS_SCENE_3D: + printf("Unstationary temperature at (%lu/%g,%g) at t=%g = %g ~ %g +/- %g\n", + (unsigned long)solve_args.iprim, SPLIT2(solve_args.uv), t[isimul], + ref[isimul], T.E, T.SE); + break; + default: FATAL("Unreachable code.\n"); break; + } + printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); + printf("Elapsed time = %s\n", dump); + printf("Time per realisation (in usec) = %g +/- %g\n\n", time.E, time.SE); + + CHK(eq_eps(T.E, ref[isimul], EPS)); + /*CHK(eq_eps(T.E, ref[isimul], T.SE*3));*/ + + OK(sdis_estimator_ref_put(estimator)); + } +} + +static void +solve_tbound2 + (struct sdis_scene* scn, + struct ssp_rng* rng) +{ + char dump[128]; + struct time t0, t1; + struct sdis_estimator* estimator; + struct sdis_solve_probe_boundary_args solve_args + = SDIS_SOLVE_PROBE_BOUNDARY_ARGS_DEFAULT; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_mc time = SDIS_MC_NULL; + size_t nreals; + size_t nfails; + enum sdis_scene_dimension dim; + const double t[] = { 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; + const double ref[sizeof(t) / sizeof(*t)] = { + 309.08032, 309.34626, 309.46525, 309.53625, 309.58408, 309.618121, + 309.642928, 309.661167, 309.674614, 309.684524 + }; + const int nsimuls = sizeof(t) / sizeof(*t); + int isimul; + ASSERT(scn && rng); + + OK(sdis_scene_get_dimension(scn, &dim)); + + solve_args.nrealisations = N; + solve_args.side = SDIS_FRONT; + FOR_EACH(isimul, 0, nsimuls) { + solve_args.time_range[0] = solve_args.time_range[1] = t[isimul]; + if(dim == SDIS_SCENE_2D) { + solve_args.iprim = model2d_nsegments - 1; + solve_args.uv[0] = ssp_rng_uniform_double(rng, 0, 1); + } else { + double u = ssp_rng_uniform_double(rng, 0, 1); + double v = ssp_rng_uniform_double(rng, 0, 1); + double w = ssp_rng_uniform_double(rng, 0, 1); + double x = 1 / (u + v + w); + solve_args.iprim = (isimul % 2) ? 20 : 21; /* Internal face */ + solve_args.uv[0] = u * x; + solve_args.uv[1] = v * x; + } + + time_current(&t0); + OK(sdis_solve_probe_boundary(scn, &solve_args, &estimator)); + time_sub(&t0, time_current(&t1), &t0); + time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); + + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_time(estimator, &time)); + + switch(dim) { + case SDIS_SCENE_2D: + printf("Unstationary temperature at (%lu/%g) at t=%g = %g ~ %g +/- %g\n", + (unsigned long)solve_args.iprim, solve_args.uv[0], t[isimul], + ref[isimul], T.E, T.SE); + break; + case SDIS_SCENE_3D: + printf("Unstationary temperature at (%lu/%g,%g) at t=%g = %g ~ %g +/- %g\n", + (unsigned long)solve_args.iprim, SPLIT2(solve_args.uv), t[isimul], + ref[isimul], T.E, T.SE); + break; + default: FATAL("Unreachable code.\n"); break; + } + printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); + printf("Elapsed time = %s\n", dump); + printf("Time per realisation (in usec) = %g +/- %g\n\n", time.E, time.SE); + + CHK(nfails + nreals == N); + CHK(nfails <= N/1000); + CHK(eq_eps(T.E, ref[isimul], EPS)); + /*CHK(eq_eps(T.E, ref[isimul], T.SE*3));*/ + + OK(sdis_estimator_ref_put(estimator)); + } +} + +static void +solve_tsolid + (struct sdis_scene* scn, + struct ssp_rng* rng) +{ + char dump[128]; + struct time t0, t1; + struct sdis_estimator* estimator; + struct sdis_solve_probe_args solve_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_mc time = SDIS_MC_NULL; + size_t nreals; + size_t nfails; + enum sdis_scene_dimension dim; + const double t[] = { 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; + const double ref[sizeof(t) / sizeof(*t)] = { + 300, 300.87408, 302.25832, 303.22164, 303.89954, 304.39030, 304.75041, + 305.01595, 305.21193, 305.35641, 305.46271 + }; + const int nsimuls = sizeof(t) / sizeof(*t); + int isimul; + ASSERT(scn && rng); + + OK(sdis_scene_get_dimension(scn, &dim)); + + solve_args.nrealisations = N; + FOR_EACH(isimul, 0, nsimuls) { + solve_args.time_range[0] = solve_args.time_range[1] = t[isimul]; + solve_args.position[0] = X_PROBE; + solve_args.position[1] = ssp_rng_uniform_double(rng, 0.1*XHpE, 0.9*XHpE); + + if(dim == SDIS_SCENE_3D) + solve_args.position[2] = ssp_rng_uniform_double(rng, 0.1*XHpE, 0.9*XHpE); + + time_current(&t0); + OK(sdis_solve_probe(scn, &solve_args, &estimator)); + time_sub(&t0, time_current(&t1), &t0); + time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); + + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_time(estimator, &time)); + + switch (dim) { + case SDIS_SCENE_2D: + printf("Unstationary temperature at (%g,%g) at t=%g = %g ~ %g +/- %g\n", + SPLIT2(solve_args.position), t[isimul], ref[isimul], T.E, T.SE); + break; + case SDIS_SCENE_3D: + printf("Unstationary temperature at (%g,%g,%g) at t=%g = %g ~ %g +/- %g\n", + SPLIT3(solve_args.position), t[isimul], ref[isimul], T.E, T.SE); + break; + default: FATAL("Unreachable code.\n"); break; + } + printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); + printf("Elapsed time = %s\n", dump); + printf("Time per realisation (in usec) = %g +/- %g\n\n", time.E, time.SE); + + CHK(nfails + nreals == N); + CHK(nfails <= N / 1000); + CHK(eq_eps(T.E, ref[isimul], EPS)); + /*CHK(eq_eps(T.E, ref[isimul], T.SE*3));*/ + + OK(sdis_estimator_ref_put(estimator)); + } +} + +static void +solve_tfluid + (struct sdis_scene* scn) +{ + char dump[128]; + struct time t0, t1; + struct sdis_estimator* estimator; + struct sdis_solve_probe_args solve_args = SDIS_SOLVE_PROBE_ARGS_DEFAULT; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_mc time = SDIS_MC_NULL; + size_t nreals; + size_t nfails; + enum sdis_scene_dimension dim; + double eps; + const double t[] = { 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000 }; + const double ref[sizeof(t) / sizeof(*t)] = { + 300, 309.53905, 309.67273, 309.73241, 309.76798, 309.79194, 309.80899, + 309.82141, 309.83055, 309.83728, 309.84224 + }; + const int nsimuls = sizeof(t) / sizeof(*t); + int isimul; + ASSERT(scn); + + OK(sdis_scene_get_dimension(scn, &dim)); + + solve_args.nrealisations = N; + solve_args.position[0] = XH * 0.5; + solve_args.position[1] = XH * 0.5; + solve_args.position[2] = XH * 0.5; + FOR_EACH(isimul, 0, nsimuls) { + solve_args.time_range[0] = solve_args.time_range[1] = t[isimul]; + + time_current(&t0); + OK(sdis_solve_probe(scn, &solve_args, &estimator)); + time_sub(&t0, time_current(&t1), &t0); + time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); + + OK(sdis_estimator_get_realisation_count(estimator, &nreals)); + OK(sdis_estimator_get_failure_count(estimator, &nfails)); + OK(sdis_estimator_get_temperature(estimator, &T)); + OK(sdis_estimator_get_realisation_time(estimator, &time)); + + switch (dim) { + case SDIS_SCENE_2D: + printf("Unstationary fluid temperature at t=%g = %g ~ %g +/- %g\n", + t[isimul], ref[isimul], T.E, T.SE); + break; + case SDIS_SCENE_3D: + printf("Unstationary fluid temperature at t=%g = %g ~ %g +/- %g\n", + t[isimul], ref[isimul], T.E, T.SE); + break; + default: FATAL("Unreachable code.\n"); break; + } + printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); + printf("Elapsed time = %s\n", dump); + printf("Time per realisation (in usec) = %g +/- %g\n\n", time.E, time.SE); + + CHK(nfails + nreals == N); + CHK(nfails <= N / 1000); + + eps = EPS; + CHK(eq_eps(T.E, ref[isimul], eps)); + + OK(sdis_estimator_ref_put(estimator)); + } +} + +/******************************************************************************* + * Test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct sdis_data* data = NULL; + struct sdis_device* dev = NULL; + struct sdis_medium* fluid = NULL; + struct sdis_medium* fluid_A = NULL; + struct sdis_medium* solid = NULL; + struct sdis_medium* dummy_solid = NULL; + struct sdis_interface* interf_adiabatic_1 = NULL; + struct sdis_interface* interf_adiabatic_2 = NULL; + struct sdis_interface* interf_TG = NULL; + struct sdis_interface* interf_P = NULL; + struct sdis_interface* interf_TA = NULL; + struct sdis_radiative_env* radenv = NULL; + struct sdis_scene* box_scn = NULL; + struct sdis_scene* square_scn = NULL; + struct sdis_scene_create_args scn_args = SDIS_SCENE_CREATE_ARGS_DEFAULT; + struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; + struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; + struct sdis_interface* model3d_interfaces[22 /*#triangles*/]; + struct sdis_interface* model2d_interfaces[7/*#segments*/]; + struct interf interf_props; + struct solid* solid_props = NULL; + struct fluid* fluid_props = NULL; + struct ssp_rng* rng = NULL; + (void)argc, (void)argv; + + OK(sdis_device_create(&SDIS_DEVICE_CREATE_ARGS_DEFAULT, &dev)); + + radenv = create_radenv(dev); + + /* Setup the solid shader */ + solid_shader.calorific_capacity = solid_get_calorific_capacity; + solid_shader.thermal_conductivity = solid_get_thermal_conductivity; + solid_shader.volumic_mass = solid_get_volumic_mass; + solid_shader.delta = solid_get_delta; + solid_shader.temperature = solid_get_temperature; + + /* Create the solid media */ + OK(sdis_data_create(dev, sizeof(struct solid), 16, NULL, &data)); + solid_props = sdis_data_get(data); + solid_props->lambda = LAMBDA; + solid_props->cp = CP_S; + solid_props->rho = RHO_S; + solid_props->delta = DELTA; + solid_props->t0 = 0; + solid_props->temperature = T0_SOLID; + OK(sdis_solid_create(dev, &solid_shader, data, &solid)); + OK(sdis_data_ref_put(data)); + + /* Create a dummy solid media to be used outside the model */ + OK(sdis_data_create(dev, sizeof(struct solid), 16, NULL, &data)); + solid_props = sdis_data_get(data); + solid_props->lambda = 0; + solid_props->cp = 1; + solid_props->rho = 1; + solid_props->delta = 1; + solid_props->t0 = INF; + solid_props->temperature = SDIS_TEMPERATURE_NONE; + OK(sdis_solid_create(dev, &solid_shader, data, &dummy_solid)); + OK(sdis_data_ref_put(data)); + + /* 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; + + /* Create the internal fluid media */ + OK(sdis_data_create(dev, sizeof(struct fluid), 16, NULL, &data)); + fluid_props = sdis_data_get(data); + fluid_props->cp = CP_F; + fluid_props->rho = RHO_F; + fluid_props->t0 = 0; + fluid_props->temperature = T0_FLUID; + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid)); + OK(sdis_data_ref_put(data)); + + /* Create the 'A' fluid media */ + OK(sdis_data_create(dev, sizeof(struct fluid), 16, NULL, &data)); + fluid_props = sdis_data_get(data); + fluid_props->cp = 1; + fluid_props->rho = 1; + fluid_props->t0 = INF; + fluid_props->temperature = TA; + OK(sdis_fluid_create(dev, &fluid_shader, data, &fluid_A)); + OK(sdis_data_ref_put(data)); + + /* Create the adiabatic interfaces */ + interf_props.temperature = SDIS_TEMPERATURE_NONE; + interf_props.h = 0; + interf_props.emissivity = 0; + interf_props.Tref = TREF; + create_interface(dev, fluid, dummy_solid, &interf_props, &interf_adiabatic_1); + create_interface(dev, solid, dummy_solid, &interf_props, &interf_adiabatic_2); + + /* Create the P interface */ + interf_props.temperature = SDIS_TEMPERATURE_NONE; + interf_props.h = HC; + interf_props.emissivity = 1; + interf_props.Tref = TREF; + create_interface(dev, fluid, solid, &interf_props, &interf_P); + + /* Create the TG interface */ + interf_props.temperature = TG; + interf_props.h = HG; + interf_props.emissivity = 1; + interf_props.Tref = TG; + create_interface(dev, fluid, dummy_solid, &interf_props, &interf_TG); + + /* Create the TA interface */ + interf_props.temperature = SDIS_TEMPERATURE_NONE; + interf_props.h = HA; + interf_props.emissivity = 1; + interf_props.Tref = TREF; + create_interface(dev, solid, fluid_A, &interf_props, &interf_TA); + + /* Release the media */ + OK(sdis_medium_ref_put(solid)); + OK(sdis_medium_ref_put(dummy_solid)); + OK(sdis_medium_ref_put(fluid)); + OK(sdis_medium_ref_put(fluid_A)); + + /* Front */ + model3d_interfaces[0] = interf_adiabatic_1; + model3d_interfaces[1] = interf_adiabatic_1; + model3d_interfaces[2] = interf_adiabatic_2; + model3d_interfaces[3] = interf_adiabatic_2; + /* Left */ + model3d_interfaces[4] = interf_TG; + model3d_interfaces[5] = interf_TG; + /* Back */ + model3d_interfaces[6] = interf_adiabatic_1; + model3d_interfaces[7] = interf_adiabatic_1; + model3d_interfaces[8] = interf_adiabatic_2; + model3d_interfaces[9] = interf_adiabatic_2; + /* Right */ + model3d_interfaces[10] = interf_TA; + model3d_interfaces[11] = interf_TA; + /* Top */ + model3d_interfaces[12] = interf_adiabatic_1; + model3d_interfaces[13] = interf_adiabatic_1; + model3d_interfaces[14] = interf_adiabatic_2; + model3d_interfaces[15] = interf_adiabatic_2; + /* Bottom */ + model3d_interfaces[16] = interf_adiabatic_1; + model3d_interfaces[17] = interf_adiabatic_1; + model3d_interfaces[18] = interf_adiabatic_2; + model3d_interfaces[19] = interf_adiabatic_2; + /* Inside */ + model3d_interfaces[20] = interf_P; + model3d_interfaces[21] = interf_P; + + /* Bottom */ + model2d_interfaces[0] = interf_adiabatic_2; + model2d_interfaces[1] = interf_adiabatic_1; + /* Left */ + model2d_interfaces[2] = interf_TG; + /* Top */ + model2d_interfaces[3] = interf_adiabatic_1; + model2d_interfaces[4] = interf_adiabatic_2; + /* Right */ + model2d_interfaces[5] = interf_TA; + /* Contact */ + model2d_interfaces[6] = interf_P; + + /* Create the box scene */ + scn_args.get_indices = model3d_get_indices; + scn_args.get_interface = model3d_get_interface; + scn_args.get_position = model3d_get_position; + scn_args.nprimitives = model3d_ntriangles; + scn_args.nvertices = model3d_nvertices; + scn_args.context = model3d_interfaces; + scn_args.radenv = radenv; + scn_args.t_range[0] = MMIN(MMIN(MMIN(MMIN(T0_FLUID, T0_SOLID), TA), TG), TR); + scn_args.t_range[1] = MMAX(MMAX(MMAX(MMAX(T0_FLUID, T0_SOLID), TA), TG), TR); + OK(sdis_scene_create(dev, &scn_args, &box_scn)); + + /* Create the square scene */ + scn_args.get_indices = model2d_get_indices; + scn_args.get_interface = model2d_get_interface; + scn_args.get_position = model2d_get_position; + scn_args.nprimitives = model2d_nsegments; + scn_args.nvertices = model2d_nvertices; + scn_args.context = model2d_interfaces; + scn_args.radenv = radenv; + scn_args.t_range[0] = MMIN(MMIN(MMIN(MMIN(T0_FLUID, T0_SOLID), TA), TG), TR); + scn_args.t_range[1] = MMAX(MMAX(MMAX(MMAX(T0_FLUID, T0_SOLID), TA), TG), TR); + OK(sdis_scene_2d_create(dev, &scn_args, &square_scn)); + + /* Release the interfaces */ + OK(sdis_interface_ref_put(interf_adiabatic_1)); + OK(sdis_interface_ref_put(interf_adiabatic_2)); + OK(sdis_interface_ref_put(interf_TG)); + OK(sdis_interface_ref_put(interf_P)); + OK(sdis_interface_ref_put(interf_TA)); + + /* Solve */ + OK(ssp_rng_create(NULL, SSP_RNG_KISS, &rng)); + printf(">> Box scene\n"); + solve_tfluid(box_scn); + solve_tbound1(box_scn, rng); + solve_tbound2(box_scn, rng); + solve_tsolid(box_scn, rng); + printf("\n>> Square scene\n"); + solve_tfluid(square_scn); + solve_tbound1(box_scn, rng); + solve_tbound2(box_scn, rng); + solve_tsolid(square_scn, rng); + + OK(sdis_radiative_env_ref_put(radenv)); + OK(sdis_scene_ref_put(box_scn)); + OK(sdis_scene_ref_put(square_scn)); + OK(sdis_device_ref_put(dev)); + OK(ssp_rng_ref_put(rng)); + + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_utils.c b/src/test_sdis_utils.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -42,7 +42,7 @@ accum_power_terms(struct sdis_medium* mdm, const double power_term, void* ctx) struct sdis_solid_shader shader = SDIS_SOLID_SHADER_NULL; struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; struct sdis_data* data = NULL; - double* power = ctx; + double* power = ctx; /* Power contribution [K] */ CHK(mdm && ctx); CHK(sdis_medium_get_type(mdm) == SDIS_SOLID); @@ -65,7 +65,7 @@ accum_flux_terms struct sdis_interface_shader shader = SDIS_INTERFACE_SHADER_NULL; struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; struct sdis_data* data = NULL; - double* flux = ctx; + double* flux = ctx; /* Flux contribution [K] */ double phi; CHK(interf && ctx); @@ -84,26 +84,69 @@ accum_flux_terms } static res_T +accum_extflux + (struct sdis_source* source, + const struct sdis_green_external_flux_terms* terms, + void* ctx) +{ + struct sdis_spherical_source_shader shader = SDIS_SPHERICAL_SOURCE_SHADER_NULL; + struct sdis_data* data = NULL; + double* extflux = ctx; /* External flux contribution [K] */ + double power = 0; /* [W] */ + double diffuse_radiance = 0; /* [W/m^2/sr] */ + + CHK(source && terms && ctx); + + data = sdis_source_get_data(source); + OK(sdis_spherical_source_get_shader(source, &shader)); + power = shader.power(terms->time, data); + if(shader.diffuse_radiance) { + diffuse_radiance = shader.diffuse_radiance(terms->time, terms->dir, data); + } + + *extflux += terms->term_wrt_power * power; + *extflux += terms->term_wrt_diffuse_radiance * diffuse_radiance; + return RES_OK; +} + +static res_T solve_green_path(struct sdis_green_path* path, void* ctx) { - struct sdis_point pt = SDIS_POINT_NULL; + struct sdis_green_path_end end = SDIS_GREEN_PATH_END_NULL; + struct sdis_rwalk_vertex vtx = SDIS_RWALK_VERTEX_NULL; struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + struct sdis_radiative_ray ray = SDIS_RADIATIVE_RAY_NULL; + struct sdis_solid_shader solid = SDIS_SOLID_SHADER_NULL; struct sdis_fluid_shader fluid = SDIS_FLUID_SHADER_NULL; struct sdis_interface_shader interf = SDIS_INTERFACE_SHADER_NULL; + struct sdis_radiative_env_shader radenv = SDIS_RADIATIVE_ENV_SHADER_NULL; + + struct sdis_green_function* green = NULL; + struct sdis_scene* scn = NULL; struct green_accum* acc = NULL; struct sdis_data* data = NULL; enum sdis_medium_type type; - enum sdis_green_path_end_type end_type; double power = 0; double flux = 0; + double extflux = 0; double time, temp = 0; double weight = 0; CHK(path && ctx); acc = ctx; + BA(sdis_green_path_get_green_function(NULL, NULL)); + BA(sdis_green_path_get_green_function(path, NULL)); + BA(sdis_green_path_get_green_function(NULL, &green)); + OK(sdis_green_path_get_green_function(path, &green)); + + BA(sdis_green_function_get_scene(NULL, NULL)); + BA(sdis_green_function_get_scene(NULL, &scn)); + BA(sdis_green_function_get_scene(green, NULL)); + OK(sdis_green_function_get_scene(green, &scn)); + BA(sdis_green_path_for_each_power_term(NULL, accum_power_terms, &power)); BA(sdis_green_path_for_each_power_term(path, NULL, &acc)); OK(sdis_green_path_for_each_power_term(path, accum_power_terms, &power)); @@ -112,67 +155,53 @@ solve_green_path(struct sdis_green_path* path, void* ctx) BA(sdis_green_path_for_each_flux_term(path, NULL, &acc)); OK(sdis_green_path_for_each_flux_term(path, accum_flux_terms, &flux)); + BA(sdis_green_path_for_each_external_flux_terms(NULL, &accum_extflux, &extflux)); + BA(sdis_green_path_for_each_external_flux_terms(path, NULL, &extflux)); + OK(sdis_green_path_for_each_external_flux_terms(path, &accum_extflux, &extflux)); + BA(sdis_green_path_get_elapsed_time(NULL, NULL)); BA(sdis_green_path_get_elapsed_time(path, NULL)); BA(sdis_green_path_get_elapsed_time(NULL, &time)); OK(sdis_green_path_get_elapsed_time(path, &time)); - BA(sdis_green_path_get_end_type(NULL, NULL)); - BA(sdis_green_path_get_end_type(path, NULL)); - BA(sdis_green_path_get_end_type(NULL, &end_type)); - OK(sdis_green_path_get_end_type(path, &end_type)); - - BA(sdis_green_path_get_limit_point(NULL, NULL)); - BA(sdis_green_path_get_limit_point(NULL, &pt)); - BA(sdis_green_path_get_limit_point(path, NULL)); - if(end_type == SDIS_GREEN_PATH_END_RADIATIVE) { - struct sdis_ambient_radiative_temperature trad; - struct sdis_green_function* green; - struct sdis_scene* scn; - BO(sdis_green_path_get_limit_point(path, &pt)); - BA(sdis_green_path_get_green_function(NULL, NULL)); - BA(sdis_green_path_get_green_function(path, NULL)); - BA(sdis_green_path_get_green_function(NULL, &green)); - OK(sdis_green_path_get_green_function(path, &green)); - - BA(sdis_green_function_get_scene(NULL, NULL)); - BA(sdis_green_function_get_scene(NULL, &scn)); - BA(sdis_green_function_get_scene(green, NULL)); - OK(sdis_green_function_get_scene(green, &scn)); - - BA(sdis_scene_get_ambient_radiative_temperature(NULL, NULL)); - BA(sdis_scene_get_ambient_radiative_temperature(scn, NULL)); - BA(sdis_scene_get_ambient_radiative_temperature(NULL, &trad)); - OK(sdis_scene_get_ambient_radiative_temperature(scn, &trad)); - temp = trad.temperature; - } else { - OK(sdis_green_path_get_limit_point(path, &pt)); - switch(pt.type) { - case SDIS_FRAGMENT: - frag = pt.data.itfrag.fragment; - OK(sdis_interface_get_shader(pt.data.itfrag.intface, &interf)); - data = sdis_interface_get_data(pt.data.itfrag.intface); + BA(sdis_green_path_get_end(NULL, NULL)); + BA(sdis_green_path_get_end(NULL, &end)); + BA(sdis_green_path_get_end(path, NULL)); + OK(sdis_green_path_get_end(path, &end)); + switch(end.type) { + case SDIS_GREEN_PATH_END_AT_INTERFACE: + frag = end.data.itfrag.fragment; + OK(sdis_interface_get_shader(end.data.itfrag.intface, &interf)); + data = sdis_interface_get_data(end.data.itfrag.intface); temp = frag.side == SDIS_FRONT ? interf.front.temperature(&frag, data) : interf.back.temperature(&frag, data); break; - case SDIS_VERTEX: - vtx = pt.data.mdmvert.vertex; - type = sdis_medium_get_type(pt.data.mdmvert.medium); - data = sdis_medium_get_data(pt.data.mdmvert.medium); + + case SDIS_GREEN_PATH_END_AT_RADIATIVE_ENV: + ray = end.data.radenvray.ray; + OK(sdis_radiative_env_get_shader(end.data.radenvray.radenv, &radenv)); + data = sdis_radiative_env_get_data(end.data.radenvray.radenv); + temp = radenv.temperature(&ray, data); + break; + + case SDIS_GREEN_PATH_END_IN_VOLUME: + vtx = end.data.mdmvert.vertex; + type = sdis_medium_get_type(end.data.mdmvert.medium); + data = sdis_medium_get_data(end.data.mdmvert.medium); if(type == SDIS_FLUID) { - OK(sdis_fluid_get_shader(pt.data.mdmvert.medium, &fluid)); + OK(sdis_fluid_get_shader(end.data.mdmvert.medium, &fluid)); temp = fluid.temperature(&vtx, data); } else { - OK(sdis_solid_get_shader(pt.data.mdmvert.medium, &solid)); + OK(sdis_solid_get_shader(end.data.mdmvert.medium, &solid)); temp = solid.temperature(&vtx, data); } break; + default: FATAL("Unreachable code.\n"); break; - } } - weight = temp + power + flux; + weight = temp + power + extflux + flux; acc->sum += weight; acc->sum2 += weight*weight; @@ -483,4 +512,3 @@ check_green_serialization OK(sdis_estimator_ref_put(e2)); OK(sdis_green_function_ref_put(green2)); } - diff --git a/src/test_sdis_utils.h b/src/test_sdis_utils.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -149,7 +149,7 @@ square_get_interface } /******************************************************************************* - * Medium & interface + * Medium, interface and ray ******************************************************************************/ static INLINE double dummy_medium_getter @@ -169,6 +169,25 @@ dummy_interface_getter return 0; } +static INLINE double +dummy_radiative_interface_getter + (const struct sdis_interface_fragment* frag, + const unsigned source_id, + struct sdis_data* data) +{ + (void)data, (void)source_id; + CHK(frag != NULL); + return 0; +} + +static INLINE double +dummy_ray_getter(const struct sdis_radiative_ray* ray, struct sdis_data* data) +{ + (void)data; + CHK(ray != NULL); + return 0; +} + static const struct sdis_solid_shader DUMMY_SOLID_SHADER = { dummy_medium_getter, /* Calorific capacity */ dummy_medium_getter, /* Thermal conductivity */ @@ -186,13 +205,13 @@ static const struct sdis_fluid_shader DUMMY_FLUID_SHADER = { 0 /* Initial time */ }; - #define DUMMY_INTERFACE_SIDE_SHADER__ { \ dummy_interface_getter, /* Temperature */ \ dummy_interface_getter, /* Flux */ \ - dummy_interface_getter, /* Emissivity */ \ - dummy_interface_getter, /* Specular fraction */ \ - dummy_interface_getter /* Reference temperature */ \ + dummy_radiative_interface_getter, /* Emissivity */ \ + dummy_radiative_interface_getter, /* Specular fraction */ \ + dummy_interface_getter, /* Reference temperature */ \ + 1 /* Handle external flux */ \ } static const struct sdis_interface_shader DUMMY_INTERFACE_SHADER = { dummy_interface_getter, /* Convection coef */ @@ -202,6 +221,11 @@ static const struct sdis_interface_shader DUMMY_INTERFACE_SHADER = { DUMMY_INTERFACE_SIDE_SHADER__ /* Back side */ }; +static const struct sdis_radiative_env_shader DUMMY_RAY_SHADER = { + dummy_ray_getter, + dummy_ray_getter +}; + /******************************************************************************* * Device creation ******************************************************************************/ @@ -427,4 +451,3 @@ check_green_serialization struct sdis_scene* scn); #endif /* TEST_SDIS_UTILS_H */ - diff --git a/src/test_sdis_volumic_power.c b/src/test_sdis_volumic_power.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -45,7 +45,6 @@ * (0,0,0) ///// */ -#define UNKNOWN_TEMPERATURE -1 #define N 10000 /* #realisations */ #define T0 320 @@ -54,6 +53,22 @@ #define DELTA 1.0/55.0 /******************************************************************************* + * Helper functions + ******************************************************************************/ +static const char* +algo_cstr(const enum sdis_diffusion_algorithm diff_algo) +{ + const char* cstr = "none"; + + switch(diff_algo) { + case SDIS_DIFFUSION_DELTA_SPHERE: cstr = "delta sphere"; break; + case SDIS_DIFFUSION_WOS: cstr = "WoS"; break; + default: FATAL("Unreachable code.\n"); break; + } + return cstr; +} + +/******************************************************************************* * Media ******************************************************************************/ struct solid { @@ -72,7 +87,7 @@ fluid_get_temperature { (void)data; CHK(vtx != NULL); - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } static double @@ -116,7 +131,7 @@ solid_get_temperature CHK(data != NULL); t0 = ((const struct solid*)sdis_data_cget(data))->t0; if(vtx->time > t0) { - return UNKNOWN_TEMPERATURE; + return SDIS_TEMPERATURE_NONE; } else { return ((const struct solid*)sdis_data_cget(data))->initial_temperature; } @@ -174,7 +189,6 @@ solve struct sdis_mc time = SDIS_MC_NULL; size_t nreals; size_t nfails; - double ref = -1; enum sdis_scene_dimension dim; const int nsimuls = 4; int isimul; @@ -182,11 +196,16 @@ solve OK(sdis_scene_get_dimension(scn, &dim)); FOR_EACH(isimul, 0, nsimuls) { - int steady = (isimul % 2) == 0; + const enum sdis_diffusion_algorithm algo = (isimul / 2) % 2 + ? SDIS_DIFFUSION_WOS + : SDIS_DIFFUSION_DELTA_SPHERE; + const int steady = (isimul % 2) == 0; + double power = P0 == SDIS_VOLUMIC_POWER_NONE ? 0 : P0; /* Restore power value */ solid->vpower = P0; + solve_args.diff_algo = algo; solve_args.position[0] = ssp_rng_uniform_double(rng, 0.1, 0.9); solve_args.position[1] = ssp_rng_uniform_double(rng, 0.1, 0.9); solve_args.position[2] = @@ -210,42 +229,28 @@ solve OK(sdis_estimator_get_temperature(estimator, &T)); OK(sdis_estimator_get_realisation_time(estimator, &time)); - switch(dim) { - case SDIS_SCENE_2D: - if(steady) { - double x = solve_args.position[0] - 0.5; - ref = solid->vpower / (2 * LAMBDA) * (1.0 / 4.0 - x * x) + T0; - printf("Steady temperature at (%g, %g) with Power=%g = %g ~ %g +/- %g\n", - SPLIT2(solve_args.position), solid->vpower, ref, T.E, T.SE); - } else { - printf("Mean temperature at (%g, %g) with t in [%g %g] and Power=%g" - " ~ %g +/- %g\n", - SPLIT2(solve_args.position), SPLIT2(solve_args.time_range), - solid->vpower, T.E, T.SE); - } - break; - case SDIS_SCENE_3D: - if(steady) { - double x = solve_args.position[0] - 0.5; - ref = solid->vpower / (2 * LAMBDA) * (1.0 / 4.0 - x * x) + T0; - printf("Steady temperature at (%g, %g, %g) with Power=%g = %g ~ %g +/- %g\n", - SPLIT3(solve_args.position), solid->vpower, ref, T.E, T.SE); - } else { - printf("Mean temperature at (%g, %g, %g) with t in [%g %g] and Power=%g" - " ~ %g +/- %g\n", - SPLIT3(solve_args.position), SPLIT2(solve_args.time_range), - solid->vpower, T.E, T.SE); - } - break; - default: FATAL("Unreachable code.\n"); break; + if(steady) { + const double x = solve_args.position[0] - 0.5; + const double ref = power / (2 * LAMBDA) * (1.0 / 4.0 - x * x) + T0; + printf + ("Steady temperature - pos: %g, %g, %g; Power: %g algo: %s " + "= %g ~ %g +/- %g\n", + SPLIT3(solve_args.position), power, algo_cstr(algo), ref, T.E, T.SE); + CHK(eq_eps(T.E, ref, T.SE * 3)); + } else { + printf( + "Mean temperature - pos: %g, %g, %g; t in [%g %g]; power: %g; algo: %s " + "~ %g +/- %g\n", + SPLIT3(solve_args.position), SPLIT2(solve_args.time_range), + power, algo_cstr(algo), T.E, T.SE); } + printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); printf("Elapsed time = %s\n", dump); printf("Time per realisation (in usec) = %g +/- %g\n\n", time.E, time.SE); CHK(nfails + nreals == N); CHK(nfails <= N/1000); - if(steady) CHK(eq_eps(T.E, ref, T.SE * 3)); /* Check green function */ time_current(&t0); @@ -258,37 +263,21 @@ solve OK(sdis_estimator_get_failure_count(estimator2, &nfails)); OK(sdis_estimator_get_temperature(estimator2, &T)); - switch(dim) { - case SDIS_SCENE_2D: - if(steady) { - double x = solve_args.position[0] - 0.5; - ref = solid->vpower / (2 * LAMBDA) * (1.0 / 4.0 - x * x) + T0; - printf("Green Steady temperature at (%g, %g) with Power=%g = %g" - " ~ %g +/- %g\n", - SPLIT2(solve_args.position), solid->vpower, ref, T.E, T.SE); - } else { - printf("Green Mean temperature at (%g, %g) with t in [%g %g] and" - " Power=%g ~ %g +/- %g\n", - SPLIT2(solve_args.position), SPLIT2(solve_args.time_range), - solid->vpower, T.E, T.SE); - } - break; - case SDIS_SCENE_3D: - if(steady) { - double x = solve_args.position[0] - 0.5; - ref = solid->vpower / (2 * LAMBDA) * (1.0 / 4.0 - x * x) + T0; - printf("Green Steady temperature at (%g, %g, %g) with Power=%g = %g" - " ~ %g +/- %g\n", - SPLIT3(solve_args.position), solid->vpower, ref, T.E, T.SE); - } else { - printf("Green Mean temperature at (%g, %g, %g) with t in [%g %g] and" - " Power=%g ~ %g +/- %g\n", - SPLIT3(solve_args.position), SPLIT2(solve_args.time_range), - solid->vpower, T.E, T.SE); - } - break; - default: FATAL("Unreachable code.\n"); break; + if(steady) { + const double x = solve_args.position[0] - 0.5; + const double ref = power / (2 * LAMBDA) * (1.0 / 4.0 - x * x) + T0; + printf + ("Green steady temperature - pos: %g, %g, %g; Power: %g algo: %s" + "= %g ~ %g +/- %g\n", + SPLIT3(solve_args.position), power, algo_cstr(algo), ref, T.E, T.SE); + } else { + printf( + "Green mean temperature - pos: %g, %g, %g; t: [%g %g]; power: %g; algo: %s " + "~ %g +/- %g\n", + SPLIT3(solve_args.position), SPLIT2(solve_args.time_range), + power, algo_cstr(algo), T.E, T.SE); } + printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); time_sub(&t0, &t1, &t0); time_dump(&t0, TIME_ALL, NULL, dump, sizeof(dump)); @@ -306,7 +295,7 @@ solve printf("\n"); /* Check same green used at a different power level */ - solid->vpower = 3 * P0; + solid->vpower = power = 3 * P0; time_current(&t0); OK(sdis_solve_probe(scn, &solve_args, &estimator)); @@ -318,43 +307,28 @@ solve OK(sdis_estimator_get_temperature(estimator, &T)); OK(sdis_estimator_get_realisation_time(estimator, &time)); - switch(dim) { - case SDIS_SCENE_2D: - if(steady) { - double x = solve_args.position[0] - 0.5; - ref = solid->vpower / (2 * LAMBDA) * (1.0 / 4.0 - x * x) + T0; - printf("Steady temperature at (%g, %g) with Power=%g = %g ~ %g +/- %g\n", - SPLIT2(solve_args.position), solid->vpower, ref, T.E, T.SE); - } else { - printf("Mean temperature at (%g, %g) with t in [%g %g] and Power=%g" - " ~ %g +/- %g\n", - SPLIT2(solve_args.position), SPLIT2(solve_args.time_range), - solid->vpower, T.E, T.SE); - } - break; - case SDIS_SCENE_3D: - if(steady) { - double x = solve_args.position[0] - 0.5; - ref = solid->vpower / (2 * LAMBDA) * (1.0 / 4.0 - x * x) + T0; - printf("Steady temperature at (%g, %g, %g) with Power=%g = %g" - " ~ %g +/- %g\n", - SPLIT3(solve_args.position), solid->vpower, ref, T.E, T.SE); - } else { - printf("Mean temperature at (%g, %g, %g) with t in [%g %g] and" - " Power=%g ~ %g +/- %g\n", - SPLIT3(solve_args.position), SPLIT2(solve_args.time_range), - solid->vpower, T.E, T.SE); - } - break; - default: FATAL("Unreachable code.\n"); break; + if(steady) { + const double x = solve_args.position[0] - 0.5; + const double ref = power / (2 * LAMBDA) * (1.0 / 4.0 - x * x) + T0; + printf + ("Steady temperature - pos: %g, %g, %g; Power: %g algo: %s " + "= %g ~ %g +/- %g\n", + SPLIT3(solve_args.position), power, algo_cstr(algo), ref, T.E, T.SE); + CHK(eq_eps(T.E, ref, T.SE * 3)); + } else { + printf( + "Mean temperature - pos: %g, %g, %g; t in [%g %g]; power: %g; algo: %s " + "~ %g +/- %g\n", + SPLIT3(solve_args.position), SPLIT2(solve_args.time_range), + power, algo_cstr(algo), T.E, T.SE); } + printf("#failures = %lu/%lu\n", (unsigned long)nfails, (unsigned long)N); printf("Elapsed time = %s\n", dump); printf("Time per realisation (in usec) = %g +/- %g\n", time.E, time.SE); CHK(nfails + nreals == N); CHK(nfails <= N/1000); - if(steady) CHK(eq_eps(T.E, ref, T.SE * 3)); time_current(&t0); OK(sdis_green_function_solve(green, &estimator2)); @@ -466,7 +440,7 @@ main(int argc, char** argv) /* Create the adiabatic interface */ OK(sdis_data_create(dev, sizeof(struct interf), 16, NULL, &data)); interf_props = sdis_data_get(data); - interf_props->temperature = UNKNOWN_TEMPERATURE; + interf_props->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create (dev, solid, fluid, &interf_shader, data, &interf_adiabatic)); OK(sdis_data_ref_put(data)); diff --git a/src/test_sdis_volumic_power2.c b/src/test_sdis_volumic_power2.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -19,7 +19,6 @@ #define N 10000 /* #realisations */ #define Pw 10000 /* Volumic power */ -#define NONE -1 #define DELTA 0.01 #define DELTA_PSQUARE 0.01 @@ -294,10 +293,10 @@ main(int argc, char** argv) {{0,-0.55, 0}, 90.250, 90.040} }; const struct reference refs2[] = { /* Lambda1=0.1, Lambda2=10, Pw=10000 */ - {{0, 0.85}, 678.170, -1}, - {{0, 0.65}, 1520.84, -1}, - {{0, 0.45}, 1794.57, -1}, - {{0, 0.25}, 1429.74, -1} + {{0, 0.85}, 678.170, 0}, + {{0, 0.65}, 1520.84, 0}, + {{0, 0.45}, 1794.57, 0}, + {{0, 0.25}, 1429.74, 0} }; (void)argc, (void)argv; @@ -341,7 +340,7 @@ main(int argc, char** argv) solid_param->lambda = 1; solid_param->delta = DELTA; solid_param->P = SDIS_VOLUMIC_POWER_NONE; - solid_param->T = -1; + solid_param->T = SDIS_TEMPERATURE_NONE; OK(sdis_solid_create(dev, &solid_shader, data, &solid1)); OK(sdis_data_ref_put(data)); @@ -354,7 +353,7 @@ main(int argc, char** argv) solid_param->lambda = 10; solid_param->delta = DELTA_PSQUARE; solid_param->P = Pw; - solid_param->T = -1; + solid_param->T = SDIS_TEMPERATURE_NONE; OK(sdis_solid_create(dev, &solid_shader, data, &solid2)); OK(sdis_data_ref_put(data)); @@ -387,7 +386,7 @@ main(int argc, char** argv) NULL, &data)); interf_param = sdis_data_get(data); interf_param->h = 5; - interf_param->temperature = NONE; + interf_param->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create(dev, solid1, fluid1, &interf_shader, data, &interf_solid1_fluid1)); OK(sdis_data_ref_put(data)); @@ -397,7 +396,7 @@ main(int argc, char** argv) NULL, &data)); interf_param = sdis_data_get(data); interf_param->h = 10; - interf_param->temperature = NONE; + interf_param->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create(dev, solid1, fluid2, &interf_shader, data, &interf_solid1_fluid2)); OK(sdis_data_ref_put(data)); diff --git a/src/test_sdis_volumic_power2_2d.c b/src/test_sdis_volumic_power2_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -19,11 +19,10 @@ #define N 10000 /* #realisations */ #define Pw 10000 /* Volumic power */ -#define NONE -1 /* H delta T */ -#define Tboundary1 NONE -#define Tboundary2 NONE +#define Tboundary1 SDIS_TEMPERATURE_NONE +#define Tboundary2 SDIS_TEMPERATURE_NONE #define DELTA 0.01 #define Tref 286.83 /* In Celsius. Computed with Syrthes at the position 0.5 */ @@ -373,7 +372,7 @@ main(int argc, char** argv) solid_param->lambda = 1; solid_param->delta = DELTA; solid_param->P = SDIS_VOLUMIC_POWER_NONE; - solid_param->T = -1; + solid_param->T = SDIS_TEMPERATURE_NONE; OK(sdis_solid_create(dev, &solid_shader, data, &solid1)); OK(sdis_data_ref_put(data)); @@ -386,7 +385,7 @@ main(int argc, char** argv) solid_param->lambda = 10; solid_param->delta = DELTA; solid_param->P = Pw; - solid_param->T = -1; + solid_param->T = SDIS_TEMPERATURE_NONE; OK(sdis_solid_create(dev, &solid_shader, data, &solid2)); OK(sdis_data_ref_put(data)); diff --git a/src/test_sdis_volumic_power3_2d.c b/src/test_sdis_volumic_power3_2d.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -41,11 +41,10 @@ #define Tb 1207.1122 /* Fixed temperatures */ -#define UNKNOWN_TEMPERATURE -1 -#define Tsolid1_fluid UNKNOWN_TEMPERATURE /*Tp1*/ -#define Tsolid2_fluid UNKNOWN_TEMPERATURE /*Tp2*/ -#define Tsolid_solid1 UNKNOWN_TEMPERATURE /*Ta*/ -#define Tsolid_solid2 UNKNOWN_TEMPERATURE /*Tb*/ +#define Tsolid1_fluid SDIS_TEMPERATURE_NONE /*Tp1*/ +#define Tsolid2_fluid SDIS_TEMPERATURE_NONE /*Tp2*/ +#define Tsolid_solid1 SDIS_TEMPERATURE_NONE /*Ta*/ +#define Tsolid_solid2 SDIS_TEMPERATURE_NONE /*Tb*/ #define PROBE_POS 1.8 @@ -304,7 +303,7 @@ main(int argc, char** argv) solid_param->lambda = LAMBDA1; solid_param->delta = DELTA1; solid_param->volumic_power = SDIS_VOLUMIC_POWER_NONE; - solid_param->temperature = -1; + solid_param->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_solid_create(dev, &solid_shader, data, &solid1)); OK(sdis_data_ref_put(data)); @@ -317,7 +316,7 @@ main(int argc, char** argv) solid_param->lambda = LAMBDA2; solid_param->delta = DELTA2; solid_param->volumic_power = SDIS_VOLUMIC_POWER_NONE; - solid_param->temperature = -1; + solid_param->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_solid_create(dev, &solid_shader, data, &solid2)); OK(sdis_data_ref_put(data)); @@ -330,7 +329,7 @@ main(int argc, char** argv) solid_param->lambda = LAMBDA; solid_param->delta = DELTA; solid_param->volumic_power = Pw; - solid_param->temperature = -1; + solid_param->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_solid_create(dev, &solid_shader, data, &solid)); OK(sdis_data_ref_put(data)); @@ -363,7 +362,7 @@ main(int argc, char** argv) NULL, &data)); interf_param = sdis_data_get(data); interf_param->h = 0; - interf_param->temperature = -1; + interf_param->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create(dev, solid, fluid, &interf_shader, data, &interf_solid_adiabatic)); OK(sdis_interface_create(dev, solid1, fluid, &interf_shader, data, diff --git a/src/test_sdis_volumic_power4.c b/src/test_sdis_volumic_power4.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2023 |Méso|Star> (contact@meso-star.com) +/* 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 @@ -270,7 +270,7 @@ main(int argc, char** argv) solid_param->lambda = LAMBDA; solid_param->delta = DELTA; solid_param->volumic_power = Power; - solid_param->temperature = -1; + solid_param->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_solid_create(dev, &solid_shader, data, &solid)); OK(sdis_data_ref_put(data)); @@ -283,7 +283,7 @@ main(int argc, char** argv) NULL, &data)); interf_param = sdis_data_get(data); interf_param->h = 0; - interf_param->temperature = -1; + interf_param->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create(dev, solid, fluid1, &interf_shader, data, &interf_adiabatic)); OK(sdis_data_ref_put(data)); @@ -293,7 +293,7 @@ main(int argc, char** argv) NULL, &data)); interf_param = sdis_data_get(data); interf_param->h = H; - interf_param->temperature = -1; + interf_param->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create(dev, solid, fluid1, &interf_shader, data, &interf_solid_fluid1)); OK(sdis_data_ref_put(data)); @@ -303,7 +303,7 @@ main(int argc, char** argv) NULL, &data)); interf_param = sdis_data_get(data); interf_param->h = H; - interf_param->temperature = -1; + interf_param->temperature = SDIS_TEMPERATURE_NONE; OK(sdis_interface_create(dev, solid, fluid2, &interf_shader, data, &interf_solid_fluid2)); OK(sdis_data_ref_put(data));