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