stardis-solver

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

commit db52f0b369426b18d4335b8a5c80c34ab7fb0704
parent 03a18686244416c338e1eb2aae8babcc7f133a9c
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 13 Feb 2018 11:17:30 +0100

Merge branch 'release_0.0'

Diffstat:
MREADME.md | 14+++++++++++++-
Mcmake/CMakeLists.txt | 13++++++++++---
Msrc/sdis.h | 55+++++++++++++++++++++++++++++++++++++------------------
Msrc/sdis_device.c | 10+++++++++-
Msrc/sdis_device_c.h | 1+
Msrc/sdis_interface.c | 88+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/sdis_interface_c.h | 27+++++++++++++++++----------
Msrc/sdis_scene.c | 496+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/sdis_scene_c.h | 36+++++++++++++-----------------------
Msrc/sdis_solve_probe.c | 544++++++++++---------------------------------------------------------------------
Asrc/sdis_solve_probe_Xd.h | 565+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_interface.c | 56++++++++++++++++++++++++++++----------------------------
Msrc/test_sdis_scene.c | 12++++++------
Msrc/test_sdis_solve_probe.c | 22+++++++++++-----------
Msrc/test_sdis_solve_probe2.c | 14+++++++-------
Asrc/test_sdis_solve_probe2_2d.c | 275+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_solve_probe3.c | 16++++++++--------
Asrc/test_sdis_solve_probe3_2d.c | 329+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_sdis_solve_probe_2d.c | 222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_sdis_utils.h | 42+++++++++++++++++++++++++++++++++++++++++-
20 files changed, 2082 insertions(+), 755 deletions(-)

diff --git a/README.md b/README.md @@ -8,7 +8,8 @@ thermal problems. Stardis 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/) and +[RSys](https://gitlab.com/vaplv/rsys/), +[Star-2D](https://gitlab.com/meso-star/star-2d/), [Star-3D](https://gitlab.com/meso-star/star-3d/) and [Star-SP](https://gitlab.com/meso-star/star-sp/) libraries as well as on the [OpenMP](http://www.openmp.org) 1.2 specification to parallelize its @@ -20,6 +21,17 @@ 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. +## Release notes + +### Version 0.0 + +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 assumes that their temperature are known. + ## License Stardis is Copyright (C) |Meso|Star> 2016-2018 (<contact@meso-star.com>). It is diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -24,8 +24,9 @@ option(NO_TEST "Do not build tests" OFF) # Check dependencies ################################################################################ find_package(RCMake 0.3 REQUIRED) +find_package(Star2D 0.1 REQUIRED) find_package(Star3D 0.4 REQUIRED) -find_package(StarSP 0.5 REQUIRED) +find_package(StarSP 0.7 REQUIRED) find_package(RSys 0.6 REQUIRED) find_package(OpenMP 1.2 REQUIRED) @@ -38,7 +39,7 @@ rcmake_append_runtime_dirs(_runtime_dirs RSys Star3D StarSP) ################################################################################ # Configure and define targets ################################################################################ -set(VERSION_MAJOR 1) +set(VERSION_MAJOR 0) set(VERSION_MINOR 0) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) @@ -74,7 +75,7 @@ add_library(sdis SHARED ${SDIS_FILES_SRC} ${SDIS_FILES_INC} ${SDIS_FILES_INC_API}) -target_link_libraries(sdis RSys Star3D StarSP) +target_link_libraries(sdis RSys Star2D Star3D StarSP) set_target_properties(sdis PROPERTIES DEFINE_SYMBOL SDIS_SHARED_BUILD @@ -118,8 +119,14 @@ if(NOT NO_TEST) new_test(test_sdis_solve_probe) new_test(test_sdis_solve_probe2) 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) target_link_libraries(test_sdis_solve_probe3 Star3DUT) + if(CMAKE_COMPILER_IS_GNUCC) + target_link_libraries(test_sdis_solve_probe3_2d m) + endif() endif() ################################################################################ diff --git a/src/sdis.h b/src/sdis.h @@ -70,20 +70,21 @@ enum sdis_medium_type { SDIS_MEDIUM_TYPES_COUNT__ }; -/* Random walk vertex */ +/* Random walk vertex, i.e. a spatiotemporal position at a given step of the + * random walk. */ struct sdis_rwalk_vertex { double P[3]; /* World space position */ - double time; /* Time of the vertex */ + double time; /* "Time" of the vertex */ }; #define SDIS_RWALK_VERTEX_NULL__ {{0}, -1} static const struct sdis_rwalk_vertex SDIS_RWALK_VERTEX_NULL = SDIS_RWALK_VERTEX_NULL__; -/* Point onto an interface */ +/* Spatiotemporal position onto an interface */ struct sdis_interface_fragment { double P[3]; /* World space position */ - double Ng[3]; /* Normalized world space geometry normal */ - double uv[2]; /* Texture coordinates */ + double Ng[3]; /* Normalized world space geometry normal at the interface */ + double uv[2]; /* Parametric coordinates of the interface */ double time; /* Current time */ }; #define SDIS_INTERFACE_FRAGMENT_NULL__ {{0}, {0}, {0}, -1} @@ -105,7 +106,6 @@ typedef double (const struct sdis_rwalk_vertex* vert, struct sdis_data* data); - /* Functor type to retrieve the interface properties. */ typedef double (*sdis_interface_getter_T) @@ -160,7 +160,7 @@ sdis_device_create (struct logger* logger, /* May be NULL <=> use default logger */ struct mem_allocator* allocator, /* May be NULL <=> use default allocator */ const unsigned nthreads_hint, /* Hint on the number of threads to use */ - const int verbose, + const int verbose, /* Verbosity level */ struct sdis_device** dev); SDIS_API res_T @@ -172,12 +172,13 @@ sdis_device_ref_put (struct sdis_device* dev); /******************************************************************************* - * A data stores in the Stardis memory space a set of user defined data. + * A data stores in the Stardis memory space a set of user defined data. It can + * be seen as a ref counted memory space allocated by Stardis. ******************************************************************************/ SDIS_API res_T sdis_data_create (struct sdis_device* dev, - const size_t size, /* Size in bytes */ + const size_t size, /* Size in bytes of user defined data */ const size_t align, /* Data alignment. Must be a power of 2 */ void (*release)(void*),/* Invoked priorly to data destruction. May be NULL */ struct sdis_data** data); @@ -237,29 +238,46 @@ sdis_interface_create struct sdis_medium* back, const struct sdis_interface_shader* shader, struct sdis_data* data, /* Data sent to the shader. May be NULL */ - struct sdis_interface** interface); + struct sdis_interface** interf); SDIS_API res_T sdis_interface_ref_get - (struct sdis_interface* interface); + (struct sdis_interface* interf); SDIS_API res_T sdis_interface_ref_put - (struct sdis_interface* interface); + (struct sdis_interface* interf); /******************************************************************************* - * A scene is a collection of triangles. Each triangle is the support of the - * interface between 2 mediums. + * A scene is a collection of primitives. Each primitive is the geometric + * support of the interface between 2 mediums. ******************************************************************************/ SDIS_API res_T sdis_scene_create (struct sdis_device* dev, const size_t ntris, /* #triangles */ - void (*indices)(const size_t itri, size_t ids[3], void*), - void (*interface)(const size_t itri, struct sdis_interface** bound, void*), + void (*indices) /* Retrieve the indices toward the vertices of `itri' */ + (const size_t itri, size_t ids[3], void*), + void (*interf) /* Get the interface of the triangle `itri' */ + (const size_t itri, struct sdis_interface** bound, void*), + const size_t nverts, /* #vertices */ + void (*position) /* Retrieve the position of the vertex `ivert' */ + (const size_t ivert, double pos[3], void* ctx), + void* ctx, /* Client side data sent as input of the previous callbacks */ + struct sdis_scene** scn); + +SDIS_API res_T +sdis_scene_2d_create + (struct sdis_device* dev, + const size_t nsegs, /* #segments */ + void (*indices) /* Retrieve the indices toward the vertices of `iseg' */ + (const size_t iseg, size_t ids[2], void*), + void (*interf) /* Get the interface of the segment `iseg' */ + (const size_t iseg, struct sdis_interface** bound, void*), const size_t nverts, /* #vertices */ - void (*position)(const size_t ivert, double pos[3], void* ctx), - void* ctx, + void (*position) /* Retrieve the position of the vertex `ivert' */ + (const size_t ivert, double pos[2], void* ctx), + void* ctx, /* Client side data sent as input of the previous callbacks */ struct sdis_scene** scn); SDIS_API res_T @@ -270,6 +288,7 @@ SDIS_API res_T sdis_scene_ref_put (struct sdis_scene* scn); +/* Retrieve the Axis Aligned Bounding Box of the scene */ SDIS_API res_T sdis_scene_get_aabb (const struct sdis_scene* scn, diff --git a/src/sdis_device.c b/src/sdis_device.c @@ -19,6 +19,7 @@ #include <rsys/logger.h> #include <rsys/mem_allocator.h> +#include <star/s2d.h> #include <star/s3d.h> #include <omp.h> @@ -47,6 +48,7 @@ device_release(ref_T* ref) struct sdis_device* dev; ASSERT(ref); dev = CONTAINER_OF(ref, struct sdis_device, ref); + if(dev->s2d) S2D(device_ref_put(dev->s2d)); if(dev->s3d) S3D(device_ref_put(dev->s3d)); ASSERT(flist_name_is_empty(&dev->names)); flist_name_release(&dev->names); @@ -93,9 +95,15 @@ sdis_device_create ref_init(&dev->ref); flist_name_init(allocator, &dev->names); + res = s2d_device_create(log, allocator, 0, &dev->s2d); + if(res != RES_OK) { + log_err(dev, + "%s, could not create the Star-2D device on Stardis.\n", FUNC_NAME); + } + res = s3d_device_create(log, allocator, 0, &dev->s3d); if(res != RES_OK) { - log_err(dev, + log_err(dev, "%s: could not create the Star-3D device on Stardis.\n", FUNC_NAME); goto error; } diff --git a/src/sdis_device_c.h b/src/sdis_device_c.h @@ -31,6 +31,7 @@ struct sdis_device { struct flist_name names; + struct s2d_device* s2d; struct s3d_device* s3d; ref_T ref; diff --git a/src/sdis_interface.c b/src/sdis_interface.c @@ -21,6 +21,7 @@ #include <rsys/double3.h> #include <rsys/mem_allocator.h> +#include <star/s2d.h> #include <star/s3d.h> /******************************************************************************* @@ -57,16 +58,16 @@ check_interface_shader static void interface_release(ref_T* ref) { - struct sdis_interface* interface = NULL; + struct sdis_interface* interf = NULL; struct sdis_device* dev = NULL; ASSERT(ref); - interface = CONTAINER_OF(ref, struct sdis_interface, ref); - dev = interface->dev; - if(interface->medium_front) SDIS(medium_ref_put(interface->medium_front)); - if(interface->medium_back) SDIS(medium_ref_put(interface->medium_back)); - if(interface->data) SDIS(data_ref_put(interface->data)); - flist_name_del(&dev->names, interface->id); - MEM_RM(dev->allocator, interface); + interf = CONTAINER_OF(ref, struct sdis_interface, ref); + dev = interf->dev; + if(interf->medium_front) SDIS(medium_ref_put(interf->medium_front)); + if(interf->medium_back) SDIS(medium_ref_put(interf->medium_back)); + if(interf->data) SDIS(data_ref_put(interf->data)); + flist_name_del(&dev->names, interf->id); + MEM_RM(dev->allocator, interf); SDIS(device_ref_put(dev)); } @@ -82,7 +83,7 @@ sdis_interface_create struct sdis_data* data, struct sdis_interface** out_interface) { - struct sdis_interface* interface = NULL; + struct sdis_interface* interf = NULL; res_T res = RES_OK; if(!dev || !front || !back || !shader || !out_interface) { @@ -103,51 +104,51 @@ sdis_interface_create goto error; } - interface = MEM_CALLOC(dev->allocator, 1, sizeof(struct sdis_interface)); - if(!interface) { + interf = MEM_CALLOC(dev->allocator, 1, sizeof(struct sdis_interface)); + if(!interf) { log_err(dev, "%s: could not create the interface.\n", FUNC_NAME); res = RES_MEM_ERR; goto error; } - ref_init(&interface->ref); + ref_init(&interf->ref); SDIS(medium_ref_get(front)); SDIS(medium_ref_get(back)); SDIS(device_ref_get(dev)); - interface->medium_front = front; - interface->medium_back = back; - interface->dev = dev; - interface->shader = *shader; - interface->id = flist_name_add(&dev->names); + interf->medium_front = front; + interf->medium_back = back; + interf->dev = dev; + interf->shader = *shader; + interf->id = flist_name_add(&dev->names); if(data) { SDIS(data_ref_get(data)); - interface->data = data; + interf->data = data; } exit: - if(out_interface) *out_interface = interface; + if(out_interface) *out_interface = interf; return res; error: - if(interface) { - SDIS(interface_ref_put(interface)); - interface = NULL; + if(interf) { + SDIS(interface_ref_put(interf)); + interf = NULL; } goto exit; } res_T -sdis_interface_ref_get(struct sdis_interface* interface) +sdis_interface_ref_get(struct sdis_interface* interf) { - if(!interface) return RES_BAD_ARG; - ref_get(&interface->ref); + if(!interf) return RES_BAD_ARG; + ref_get(&interf->ref); return RES_OK; } res_T -sdis_interface_ref_put(struct sdis_interface* interface) +sdis_interface_ref_put(struct sdis_interface* interf) { - if(!interface) return RES_BAD_ARG; - ref_put(&interface->ref, interface_release); + if(!interf) return RES_BAD_ARG; + ref_put(&interf->ref, interface_release); return RES_OK; } @@ -156,27 +157,42 @@ sdis_interface_ref_put(struct sdis_interface* interface) ******************************************************************************/ const struct sdis_medium* interface_get_medium - (const struct sdis_interface* interface, const enum sdis_side_flag side) + (const struct sdis_interface* interf, const enum sdis_side_flag side) { struct sdis_medium* mdm = NULL; - ASSERT(interface); + ASSERT(interf); switch(side) { - case SDIS_FRONT: mdm = interface->medium_front; break; - case SDIS_BACK: mdm = interface->medium_back; break; + case SDIS_FRONT: mdm = interf->medium_front; break; + case SDIS_BACK: mdm = interf->medium_back; break; default: FATAL("Unreachable code.\n"); break; } return mdm; } unsigned -interface_get_id(const struct sdis_interface* interface) +interface_get_id(const struct sdis_interface* interf) { - ASSERT(interface); - return interface->id.index; + ASSERT(interf); + return interf->id.index; } void -setup_interface_fragment +setup_interface_fragment_2d + (struct sdis_interface_fragment* frag, + const struct sdis_rwalk_vertex* vertex, + const struct s2d_hit* hit) +{ + ASSERT(frag && vertex && hit && !S2D_HIT_NONE(hit)); + d2_set(frag->P, vertex->P); + frag->P[2] = 0; + d2_normalize(frag->Ng, d2_set_f2(frag->Ng, hit->normal)); + frag->Ng[2] = 0; + frag->uv[0] = hit->u; + frag->time = vertex->time; +} + +void +setup_interface_fragment_3d (struct sdis_interface_fragment* frag, const struct sdis_rwalk_vertex* vertex, const struct s3d_hit* hit) diff --git a/src/sdis_interface_c.h b/src/sdis_interface_c.h @@ -22,6 +22,7 @@ #include <float.h> /* Forward declaration of external type */ +struct s2d_hit; struct s3d_hit; struct sdis_interface { @@ -37,36 +38,42 @@ struct sdis_interface { extern LOCAL_SYM const struct sdis_medium* interface_get_medium - (const struct sdis_interface* interface, + (const struct sdis_interface* interf, const enum sdis_side_flag side); extern LOCAL_SYM unsigned interface_get_id - (const struct sdis_interface* interface); + (const struct sdis_interface* interf); extern LOCAL_SYM void -setup_interface_fragment +setup_interface_fragment_2d + (struct sdis_interface_fragment* frag, + const struct sdis_rwalk_vertex* vertex, + const struct s2d_hit* hit); + +extern LOCAL_SYM void +setup_interface_fragment_3d (struct sdis_interface_fragment* frag, const struct sdis_rwalk_vertex* vertex, const struct s3d_hit* hit); static INLINE double interface_get_temperature - (const struct sdis_interface* interface, + (const struct sdis_interface* interf, const struct sdis_interface_fragment* frag) { - ASSERT(interface && frag); - if(!interface->shader.temperature) return -DBL_MAX; - return interface->shader.temperature(frag, interface->data); + ASSERT(interf && frag); + if(!interf->shader.temperature) return -DBL_MAX; + return interf->shader.temperature(frag, interf->data); } static INLINE double interface_get_convection_coef - (const struct sdis_interface* interface, + (const struct sdis_interface* interf, const struct sdis_interface_fragment* frag) { - ASSERT(interface && frag); - return interface->shader.convection_coef(frag, interface->data); + ASSERT(interf && frag); + return interf->shader.convection_coef(frag, interf->data); } #endif /* SDIS_INTERFACE_C_H */ diff --git a/src/sdis_scene.c b/src/sdis_scene.c @@ -18,19 +18,122 @@ #include "sdis_interface_c.h" #include "sdis_scene_c.h" +#include <rsys/float2.h> #include <rsys/float3.h> +#include <rsys/double2.h> #include <rsys/double3.h> #include <rsys/mem_allocator.h> +#include <star/s2d.h> #include <star/s3d.h> #include <limits.h> +/* Context used to wrap the user geometry to Star-XD. */ +struct geometry_context { + void (*indices)(const size_t iprim, size_t ids[], void*); + void (*position)(const size_t ivert, double pos[], void*); + void* data; +}; + /******************************************************************************* * Helper function ******************************************************************************/ +/* Check that `hit' roughly lies on a vertex. For segments, a simple but + * approximative way is to test that its position have at least one barycentric + * coordinate roughly equal to 0 or 1. */ +static FINLINE int +hit_on_vertex(const struct s2d_hit* hit) +{ + const float on_vertex_eps = 1.e-4f; + float v; + ASSERT(hit && !S2D_HIT_NONE(hit)); + v = 1.f - hit->u; + return eq_epsf(hit->u, 0.f, on_vertex_eps) + || eq_epsf(hit->u, 1.f, on_vertex_eps) + || eq_epsf(v, 0.f, on_vertex_eps) + || eq_epsf(v, 1.f, on_vertex_eps); +} + +/* Check that `hit' roughly lies on an edge. For triangular primitives, a + * simple but approximative way is to test that its position have at least one + * barycentric coordinate roughly equal to 0 or 1. */ +static FINLINE int +hit_on_edge(const struct s3d_hit* hit) +{ + const float on_edge_eps = 1.e-4f; + float w; + ASSERT(hit && !S3D_HIT_NONE(hit)); + w = 1.f - hit->uv[0] - hit->uv[1]; + return eq_epsf(hit->uv[0], 0.f, on_edge_eps) + || eq_epsf(hit->uv[0], 1.f, on_edge_eps) + || eq_epsf(hit->uv[1], 0.f, on_edge_eps) + || eq_epsf(hit->uv[1], 1.f, on_edge_eps) + || eq_epsf(w, 0.f, on_edge_eps) + || eq_epsf(w, 1.f, on_edge_eps); +} + +static int +hit_filter_function_3d + (const struct s3d_hit* hit, + const float org[3], + const float dir[3], + void* ray_data, + void* filter_data) +{ + const struct s3d_hit* hit_from = ray_data; + (void)org, (void)dir, (void)filter_data; + + if(!hit_from || S3D_HIT_NONE(hit_from)) return 0; /* No filtering */ + + if(S3D_PRIMITIVE_EQ(&hit_from->prim, &hit->prim)) return 1; + + if(eq_epsf(hit->distance, 0, 1.e-6f)) { + /* If the targeted point is near of the origin, check that it lies on an + * edge shared by the 2 primitives. */ + return hit_on_edge(hit_from) && hit_on_edge(hit); + } + + return 0; +} + +static int +hit_filter_function_2d + (const struct s2d_hit* hit, + const float org[2], + const float dir[2], + void* ray_data, + void* filter_data) +{ + const struct s2d_hit* hit_from = ray_data; + (void)org, (void)dir, (void)filter_data; + + if(!hit_from || S2D_HIT_NONE(hit_from)) return 0; /* No filtering */ + + if(S2D_PRIMITIVE_EQ(&hit_from->prim, &hit->prim)) return 1; + + if(eq_epsf(hit->distance, 0, 1.e-6f)) { + /* If the targeted point is near of the origin, check that it lies on a + * vertex shared by the 2 segments. */ + return hit_on_vertex(hit_from) && hit_on_vertex(hit); + } + + return 0; +} + +static void +get_indices_2d(const unsigned iseg, unsigned out_ids[2], void* data) +{ + struct geometry_context* ctx = data; + size_t ids[2]; + ASSERT(ctx); + ctx->indices(iseg, ids, ctx->data); + out_ids[0] = (unsigned)ids[0]; + out_ids[1] = (unsigned)ids[1]; +} + static void -get_indices(const unsigned itri, unsigned out_ids[3], void* data) +get_indices_3d(const unsigned itri, unsigned out_ids[3], void* data) { struct geometry_context* ctx = data; size_t ids[3]; @@ -42,7 +145,18 @@ get_indices(const unsigned itri, unsigned out_ids[3], void* data) } static void -get_position(const unsigned ivert, float out_pos[3], void* data) +get_position_2d(const unsigned ivert, float out_pos[2], void* data) +{ + struct geometry_context* ctx = data; + double pos[2]; + ASSERT(ctx); + ctx->position(ivert, pos, ctx->data); + out_pos[0] = (float)pos[0]; + out_pos[1] = (float)pos[1]; +} + +static void +get_position_3d(const unsigned ivert, float out_pos[3], void* data) { struct geometry_context* ctx = data; double pos[3]; @@ -58,25 +172,25 @@ clear_interfaces(struct sdis_scene* scn) { size_t i; ASSERT(scn); - FOR_EACH(i, 0, darray_interface_size_get(&scn->interfaces)) { - if(darray_interface_cdata_get(&scn->interfaces)[i]) { - SDIS(interface_ref_put(darray_interface_data_get(&scn->interfaces)[i])); + FOR_EACH(i, 0, darray_interf_size_get(&scn->interfaces)) { + if(darray_interf_cdata_get(&scn->interfaces)[i]) { + SDIS(interface_ref_put(darray_interf_data_get(&scn->interfaces)[i])); } } - darray_interface_clear(&scn->interfaces); - darray_interface_clear(&scn->prim_interfaces); + darray_interf_clear(&scn->interfaces); + darray_interf_clear(&scn->prim_interfaces); } static res_T setup_interfaces (struct sdis_scene* scn, const size_t ntris, /* #triangles */ - void (*interface)(const size_t itri, struct sdis_interface**, void*), + void (*interf)(const size_t itri, struct sdis_interface**, void*), void* ctx) { size_t itri; res_T res = RES_OK; - ASSERT(ntris && interface); + ASSERT(ntris && interf); clear_interfaces(scn); @@ -86,24 +200,24 @@ setup_interfaces unsigned id; /* Retrieve the interface of the primitive */ - interface(itri, &itface, ctx); + interf(itri, &itface, ctx); id = interface_get_id(itface); /* Check that the interface is already registered against the scene */ - ninterfaces = darray_interface_size_get(&scn->interfaces); + ninterfaces = darray_interf_size_get(&scn->interfaces); if(id >= ninterfaces) { - res = darray_interface_resize(&scn->interfaces, id + 1); + res = darray_interf_resize(&scn->interfaces, id + 1); if(res != RES_OK) goto error; } - if(darray_interface_cdata_get(&scn->interfaces)[id]) { - ASSERT(darray_interface_cdata_get(&scn->interfaces)[id] == itface); + if(darray_interf_cdata_get(&scn->interfaces)[id]) { + ASSERT(darray_interf_cdata_get(&scn->interfaces)[id] == itface); } else { SDIS(interface_ref_get(itface)); - darray_interface_data_get(&scn->interfaces)[id] = itface; + darray_interf_data_get(&scn->interfaces)[id] = itface; } /* Register the primitive interface */ - res = darray_interface_push_back(&scn->prim_interfaces, &itface); + res = darray_interf_push_back(&scn->prim_interfaces, &itface); if(res != RES_OK) goto error; } @@ -115,7 +229,59 @@ error: } static res_T -setup_geometry +setup_geometry_2d + (struct sdis_scene* scn, + const size_t nsegs, /* #segments */ + void (*indices)(const size_t itri, size_t ids[2], void*), + const size_t nverts, /* #vertices */ + void (*position)(const size_t ivert, double pos[2], void* ctx), + void* ctx) +{ + struct geometry_context context; + struct s2d_shape* s2d_msh = NULL; + struct s2d_scene* s2d_scn = NULL; + struct s2d_vertex_data vdata = S2D_VERTEX_DATA_NULL; + res_T res = RES_OK; + ASSERT(scn && nsegs && indices && nverts && position); + + /* Setup the intermediary geometry context */ + context.indices = indices; + context.position = position; + context.data = ctx; + + /* Setup the vertex data */ + vdata.usage = S2D_POSITION; + vdata.type = S2D_FLOAT2; + vdata.get = get_position_2d; + + /* Create the Star-2D geometry */ + res = s2d_scene_create(scn->dev->s2d, &s2d_scn); + if(res != RES_OK) goto error; + res = s2d_shape_create_line_segments(scn->dev->s2d, &s2d_msh); + if(res != RES_OK) goto error; + res = s2d_line_segments_set_hit_filter_function + (s2d_msh, hit_filter_function_2d, NULL); + if(res != RES_OK) goto error; + res = s2d_scene_attach_shape(s2d_scn, s2d_msh); + if(res != RES_OK) goto error; + res = s2d_line_segments_setup_indexed_vertices(s2d_msh, (unsigned)nsegs, + get_indices_2d, (unsigned)nverts, &vdata, 1, &context); + if(res != RES_OK) goto error; + res = s2d_scene_view_create(s2d_scn, S2D_SAMPLE|S2D_TRACE|S2D_GET_PRIMITIVE, + &scn->s2d_view); + if(res != RES_OK) goto error; + +exit: + if(s2d_msh) S2D(shape_ref_put(s2d_msh)); + if(s2d_scn) S2D(scene_ref_put(s2d_scn)); + return res; +error: + if(scn->s2d_view) S2D(scene_view_ref_put(scn->s2d_view)); + goto exit; +} + +static res_T +setup_geometry_3d (struct sdis_scene* scn, const size_t ntris, /* #triangles */ void (*indices)(const size_t itri, size_t ids[3], void*), @@ -138,19 +304,19 @@ setup_geometry /* Setup the vertex data */ vdata.usage = S3D_POSITION; vdata.type = S3D_FLOAT3; - vdata.get = get_position; + vdata.get = get_position_3d; /* Create the Star-3D geometry */ res = s3d_scene_create(scn->dev->s3d, &s3d_scn); if(res != RES_OK) goto error; res = s3d_shape_create_mesh(scn->dev->s3d, &s3d_msh); if(res != RES_OK) goto error; - res = s3d_mesh_set_hit_filter_function(s3d_msh, hit_filter_function, NULL); + res = s3d_mesh_set_hit_filter_function(s3d_msh, hit_filter_function_3d, NULL); if(res != RES_OK) goto error; res = s3d_scene_attach_shape(s3d_scn, s3d_msh); if(res != RES_OK) goto error; - res = s3d_mesh_setup_indexed_vertices(s3d_msh, (unsigned)ntris, get_indices, - (unsigned)nverts, &vdata, 1, &context); + res = s3d_mesh_setup_indexed_vertices(s3d_msh, (unsigned)ntris, + get_indices_3d, (unsigned)nverts, &vdata, 1, &context); if(res != RES_OK) goto error; res = s3d_scene_view_create(s3d_scn, S3D_SAMPLE|S3D_TRACE|S3D_GET_PRIMITIVE, &scn->s3d_view); @@ -165,59 +331,23 @@ error: goto exit; } -/* Check that `hit' roughly lies on an edge. For triangular primitives, a - * simple but approximative way is to test that its position have at least one - * barycentric coordinate roughly equal to 0 or 1. */ -static FINLINE int -hit_on_edge(const struct s3d_hit* hit) -{ - const float on_edge_eps = 1.e-4f; - float w; - ASSERT(hit && !S3D_HIT_NONE(hit)); - w = 1.f - hit->uv[0] - hit->uv[1]; - return eq_epsf(hit->uv[0], 0.f, on_edge_eps) - || eq_epsf(hit->uv[0], 1.f, on_edge_eps) - || eq_epsf(hit->uv[1], 0.f, on_edge_eps) - || eq_epsf(hit->uv[1], 1.f, on_edge_eps) - || eq_epsf(w, 0.f, on_edge_eps) - || eq_epsf(w, 1.f, on_edge_eps); -} - -static void -scene_release(ref_T * ref) -{ - struct sdis_device* dev = NULL; - struct sdis_scene* scn = NULL; - ASSERT(ref); - scn = CONTAINER_OF(ref, struct sdis_scene, ref); - dev = scn->dev; - clear_interfaces(scn); - darray_interface_release(&scn->interfaces); - darray_interface_release(&scn->prim_interfaces); - if(scn->s3d_view) S3D(scene_view_ref_put(scn->s3d_view)); - MEM_RM(dev->allocator, scn); - SDIS(device_ref_put(dev)); -} - -/******************************************************************************* - * Exported functions - ******************************************************************************/ -res_T -sdis_scene_create +static res_T +scene_create (struct sdis_device* dev, - const size_t ntris, /* #triangles */ - void (*indices)(const size_t itri, size_t ids[3], void*), - void (*interface)(const size_t itri, struct sdis_interface** bound, void*), + const int is_2d, + const size_t nprims, /* #primitives */ + void (*indices)(const size_t iprim, size_t ids[], void*), + void (*interf)(const size_t iprim, struct sdis_interface** bound, void*), const size_t nverts, /* #vertices */ - void (*position)(const size_t ivert, double pos[3], void* ctx), + void (*position)(const size_t ivert, double pos[], void* ctx), void* ctx, struct sdis_scene** out_scn) { struct sdis_scene* scn = NULL; res_T res = RES_OK; - if(!dev || !out_scn || !ntris || !indices || !interface || !nverts - || !position || ntris > UINT_MAX || nverts > UINT_MAX) { + if(!dev || !out_scn || !nprims || !indices || !interf || !nverts + || !position || nprims > UINT_MAX || nverts > UINT_MAX) { res = RES_BAD_ARG; goto error; } @@ -231,16 +361,20 @@ sdis_scene_create ref_init(&scn->ref); SDIS(device_ref_get(dev)); scn->dev = dev; - darray_interface_init(dev->allocator, &scn->interfaces); - darray_interface_init(dev->allocator, &scn->prim_interfaces); + darray_interf_init(dev->allocator, &scn->interfaces); + darray_interf_init(dev->allocator, &scn->prim_interfaces); - res = setup_interfaces(scn, ntris, interface, ctx); + res = setup_interfaces(scn, nprims, interf, ctx); if(res != RES_OK) { log_err(dev, "%s: could not setup the scene interfaces.\n", FUNC_NAME); goto error; } - res = setup_geometry(scn, ntris, indices, nverts, position, ctx); + if(is_2d) { + res = setup_geometry_2d(scn, nprims, indices, nverts, position, ctx); + } else { + res = setup_geometry_3d(scn, nprims, indices, nverts, position, ctx); + } if(res != RES_OK) { log_err(dev, "%s: could not setup the scene geometry.\n", FUNC_NAME); goto error; @@ -257,48 +391,72 @@ error: goto exit; } -res_T -sdis_scene_ref_get(struct sdis_scene* scn) +static INLINE res_T +scene_get_medium_2d + (const struct sdis_scene* scn, + const double pos[2], + const struct sdis_medium** out_medium) { - if(!scn) return RES_BAD_ARG; - ref_get(&scn->ref); - return RES_OK; -} + const struct sdis_medium* medium = NULL; + size_t iprim, nprims; + size_t nfailures = 0; + const size_t max_failures = 10; + res_T res = RES_OK; + ASSERT(scn && pos); -res_T -sdis_scene_ref_put(struct sdis_scene* scn) -{ - if(!scn) return RES_BAD_ARG; - ref_put(&scn->ref, scene_release); - return RES_OK; -} + S2D(scene_view_primitives_count(scn->s2d_view, &nprims)); + FOR_EACH(iprim, 0, nprims) { + struct s2d_hit hit; + struct s2d_attrib attr; + struct s2d_primitive prim; + float s; + const float range[2] = {0.f, FLT_MAX}; + float N[2], P[2], dir[2], cos_N_dir; + s = 1.f / 3.f; -res_T -sdis_scene_get_aabb - (const struct sdis_scene* scn, double lower[3], double upper[3]) -{ - float low[3], upp[3]; - res_T res = RES_OK; - if(!scn || !lower || !upper) return RES_BAD_ARG; - res = s3d_scene_view_get_aabb(scn->s3d_view, low, upp); - if(res != RES_OK) return res; - d3_set_f3(lower, low); - d3_set_f3(upper, upp); - return RES_OK; -} + /* Retrieve a position onto the primitive */ + S2D(scene_view_get_primitive(scn->s2d_view, (unsigned)iprim, &prim)); + S2D(primitive_get_attrib(&prim, S2D_POSITION, s, &attr)); -/******************************************************************************* - * Local miscellaneous function - ******************************************************************************/ -const struct sdis_interface* -scene_get_interface(const struct sdis_scene* scn, const unsigned iprim) -{ - ASSERT(scn && iprim < darray_interface_size_get(&scn->prim_interfaces)); - return darray_interface_cdata_get(&scn->prim_interfaces)[iprim]; + /* Trace a ray from the randomw walk vertex toward the retrieved primitive + * position */ + f2_normalize(dir, f2_sub(dir, attr.value, f2_set_d2(P, pos))); + S2D(scene_view_trace_ray(scn->s2d_view, P, dir, range, NULL, &hit)); + + f2_normalize(N, hit.normal); + cos_N_dir = f2_dot(N, dir); + + /* Unforeseen error. One has to intersect a primitive ! */ + if(S2D_HIT_NONE(&hit)) { + ++nfailures; + if(nfailures < max_failures) { + continue; + } else { + res = RES_BAD_ARG; + goto error; + } + } + + if(absf(cos_N_dir) > 1.e-1f) { /* Not roughly orthognonal */ + const struct sdis_interface* interf; + interf = scene_get_interface(scn, hit.prim.prim_id); + medium = interface_get_medium + (interf, cos_N_dir < 0 ? SDIS_FRONT : SDIS_BACK); + break; + } + } + +exit: + *out_medium = medium; + return res; +error: + log_err(scn->dev, "%s: could not retrieve the medium at {%g, %g}.\n", + FUNC_NAME, SPLIT2(pos)); + goto exit; } -res_T -scene_get_medium +static INLINE res_T +scene_get_medium_3d (const struct sdis_scene* scn, const double pos[3], const struct sdis_medium** out_medium) @@ -315,9 +473,10 @@ scene_get_medium struct s3d_hit hit; struct s3d_attrib attr; struct s3d_primitive prim; - const float st[2] = { 1.f/3.f, 1.f/3.f }; + float st[2]; const float range[2] = {0.f, FLT_MAX}; float N[3], P[3], dir[3], cos_N_dir; + st[0] = st[1] = 1.f / 3.f; /* Or MSVC will issue a warning */ /* Retrieve a position onto the primitive */ S3D(scene_view_get_primitive(scn->s3d_view, (unsigned)iprim, &prim)); @@ -343,10 +502,10 @@ scene_get_medium } if(absf(cos_N_dir) > 1.e-1f) { /* Not roughly orthognonal */ - const struct sdis_interface* interface; - interface = scene_get_interface(scn, hit.prim.prim_id); + const struct sdis_interface* interf; + interf = scene_get_interface(scn, hit.prim.prim_id); medium = interface_get_medium - (interface, cos_N_dir < 0 ? SDIS_FRONT : SDIS_BACK); + (interf, cos_N_dir < 0 ? SDIS_FRONT : SDIS_BACK); break; } } @@ -360,27 +519,112 @@ error: goto exit; } -int -hit_filter_function - (const struct s3d_hit* hit, - const float org[3], - const float dir[3], - void* ray_data, - void* filter_data) +static void +scene_release(ref_T * ref) { - const struct s3d_hit* hit_from = ray_data; - (void)org, (void)dir, (void)filter_data; + struct sdis_device* dev = NULL; + struct sdis_scene* scn = NULL; + ASSERT(ref); + scn = CONTAINER_OF(ref, struct sdis_scene, ref); + dev = scn->dev; + clear_interfaces(scn); + darray_interf_release(&scn->interfaces); + darray_interf_release(&scn->prim_interfaces); + if(scn->s2d_view) S2D(scene_view_ref_put(scn->s2d_view)); + if(scn->s3d_view) S3D(scene_view_ref_put(scn->s3d_view)); + MEM_RM(dev->allocator, scn); + SDIS(device_ref_put(dev)); +} - if(!hit_from || S3D_HIT_NONE(hit_from)) return 0; /* No filtering */ +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +sdis_scene_create + (struct sdis_device* dev, + const size_t ntris, /* #triangles */ + void (*indices)(const size_t itri, size_t ids[3], void*), + void (*interf)(const size_t itri, struct sdis_interface** bound, void*), + const size_t nverts, /* #vertices */ + void (*position)(const size_t ivert, double pos[3], void* ctx), + void* ctx, + struct sdis_scene** out_scn) +{ + return scene_create + (dev, 0/*is_2D*/, ntris, indices, interf, nverts, position, ctx, out_scn); +} - if(S3D_PRIMITIVE_EQ(&hit_from->prim, &hit->prim)) return 1; +res_T +sdis_scene_2d_create + (struct sdis_device* dev, + const size_t nsegs, /* #segments */ + void (*indices)(const size_t itri, size_t ids[2], void*), + void (*interf)(const size_t itri, struct sdis_interface** bound, void*), + const size_t nverts, /* #vertices */ + void (*position)(const size_t ivert, double pos[2], void* ctx), + void* ctx, + struct sdis_scene** out_scn) +{ + return scene_create + (dev, 1/*is_2D*/, nsegs, indices, interf, nverts, position, ctx, out_scn); +} - if(eq_epsf(hit->distance, 0, 1.e-6f)) { - /* If the targeted point is near of the origin, check that it lies on an - * edge shared by the 2 primitives. */ - return hit_on_edge(hit_from) && hit_on_edge(hit); +res_T +sdis_scene_ref_get(struct sdis_scene* scn) +{ + if(!scn) return RES_BAD_ARG; + ref_get(&scn->ref); + return RES_OK; +} + +res_T +sdis_scene_ref_put(struct sdis_scene* scn) +{ + if(!scn) return RES_BAD_ARG; + ref_put(&scn->ref, scene_release); + return RES_OK; +} + +res_T +sdis_scene_get_aabb + (const struct sdis_scene* scn, double lower[], double upper[]) +{ + float low[3], upp[3]; + res_T res = RES_OK; + if(!scn || !lower || !upper) return RES_BAD_ARG; + + if(scene_is_2d(scn)) { + res = s2d_scene_view_get_aabb(scn->s2d_view, low, upp); + if(res != RES_OK) return res; + d2_set_f2(lower, low); + d2_set_f2(upper, upp); + } else { + res = s3d_scene_view_get_aabb(scn->s3d_view, low, upp); + if(res != RES_OK) return res; + d3_set_f3(lower, low); + d3_set_f3(upper, upp); } + return RES_OK; +} - return 0; +/******************************************************************************* + * Local miscellaneous function + ******************************************************************************/ +const struct sdis_interface* +scene_get_interface(const struct sdis_scene* scn, const unsigned iprim) +{ + ASSERT(scn && iprim < darray_interf_size_get(&scn->prim_interfaces)); + return darray_interf_cdata_get(&scn->prim_interfaces)[iprim]; +} + +res_T +scene_get_medium + (const struct sdis_scene* scn, + const double pos[], + const struct sdis_medium** out_medium) +{ + return scene_is_2d(scn) + ? scene_get_medium_2d(scn, pos, out_medium) + : scene_get_medium_3d(scn, pos, out_medium); } diff --git a/src/sdis_scene_c.h b/src/sdis_scene_c.h @@ -19,34 +19,25 @@ #include <rsys/dynamic_array.h> #include <rsys/ref_count.h> -/* Forward declaration of external types */ -struct s3d_hit; - -/* Context used to wrap the user geometry to Star-3D. */ -struct geometry_context { - void (*indices)(const size_t itri, size_t ids[3], void*); - void (*position)(const size_t ivert, double pos[3], void*); - void* data; -}; - static INLINE void interface_init (struct mem_allocator* allocator, - struct sdis_interface** interface) + struct sdis_interface** interf) { (void)allocator; - *interface = NULL; + *interf = NULL; } /* Declare the array of interfaces */ -#define DARRAY_NAME interface +#define DARRAY_NAME interf #define DARRAY_DATA struct sdis_interface* #define DARRAY_FUNCTOR_INIT interface_init #include <rsys/dynamic_array.h> struct sdis_scene { - struct darray_interface interfaces; /* List of interfaces own by the scene */ - struct darray_interface prim_interfaces; /* Per primitive interface */ + struct darray_interf interfaces; /* List of interfaces own by the scene */ + struct darray_interf prim_interfaces; /* Per primitive interface */ + struct s2d_scene_view* s2d_view; struct s3d_scene_view* s3d_view; ref_T ref; @@ -61,16 +52,15 @@ scene_get_interface extern LOCAL_SYM res_T scene_get_medium (const struct sdis_scene* scene, - const double position[3], + const double position[], const struct sdis_medium** medium); -extern LOCAL_SYM int -hit_filter_function - (const struct s3d_hit* hit, - const float ray_org[3], - const float ray_dir[3], - void* ray_data, /* struct s3d_hit* */ - void* filter_data); /* NULL */ +static FINLINE int +scene_is_2d(const struct sdis_scene* scn) +{ + ASSERT(scn && (scn->s2d_view || scn->s3d_view)); + return scn->s2d_view != NULL; +} #endif /* SDIS_SCENE_C_H */ diff --git a/src/sdis_solve_probe.c b/src/sdis_solve_probe.c @@ -16,454 +16,19 @@ #include "sdis.h" #include "sdis_device_c.h" #include "sdis_estimator_c.h" -#include "sdis_interface_c.h" -#include "sdis_medium_c.h" -#include "sdis_scene_c.h" +#include "sdis_solve_probe_Xd.h" -#include <rsys/double2.h> -#include <rsys/double3.h> -#include <rsys/float3.h> -#include <rsys/stretchy_array.h> +/* Generate the 2D solver */ +#define SDIS_SOLVE_PROBE_DIMENSION 2 +#include "sdis_solve_probe_Xd.h" -#include <star/s3d.h> -#include <star/ssp.h> - -/* Emperical scale factor to apply to the upper bound of the ray range in order - * to handle numerical imprecisions */ -#define RAY_RANGE_MAX_SCALE 1.0001f - -/* Current state of the random walk */ -struct rwalk { - struct sdis_rwalk_vertex vtx; /* Position and time of the Random walk */ - const struct sdis_medium* mdm; /* Medium in which the random walk lies */ - struct s3d_hit hit; /* Hit of the random walk */ -}; -static const struct rwalk RWALK_NULL = { - SDIS_RWALK_VERTEX_NULL__, NULL, S3D_HIT_NULL__ -}; - -struct temperature { - res_T (*func)/* Next function to invoke in order to compute the temperature */ - (const struct sdis_scene* scn, - const double fp_to_meter, - struct rwalk* rwalk, - struct ssp_rng* rng, - struct temperature* temp); - double value; /* Current value of the temperature */ - int done; -}; -static const struct temperature TEMPERATURE_NULL = { NULL, 0, 0 }; - -static res_T -boundary_temperature - (const struct sdis_scene* scn, - const double fp_to_meter, - struct rwalk* rwalk, - struct ssp_rng* rng, - struct temperature* T); - -static res_T -solid_temperature - (const struct sdis_scene* scn, - const double fp_to_meter, - struct rwalk* rwalk, - struct ssp_rng* rng, - struct temperature* T); - -static res_T -fluid_temperature - (const struct sdis_scene* scn, - const double fp_to_meter, - struct rwalk* rwalk, - struct ssp_rng* rng, - struct temperature* T); - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static FINLINE void -move_pos(double pos[3], const float dir[3], const float delta) -{ - ASSERT(pos && dir); - pos[0] += dir[0] * delta; - pos[1] += dir[1] * delta; - pos[2] += dir[2] * delta; -} - -/* Check that the interface fragment is consistent with the current state of - * the random walk */ -static INLINE int -check_rwalk_fragment_consistency - (const struct rwalk* rwalk, - const struct sdis_interface_fragment* frag) -{ - double N[3]; - double uv[2]; - ASSERT(rwalk && frag); - d3_normalize(N, d3_set_f3(N, rwalk->hit.normal)); - d2_set_f2(uv, rwalk->hit.uv); - return !S3D_HIT_NONE(&rwalk->hit) - && d3_eq_eps(rwalk->vtx.P, frag->P, 1.e-6) - && d3_eq_eps(N, frag->Ng, 1.e-6) - && d2_eq_eps(uv, frag->uv, 1.e-6) - && ( (IS_INF(rwalk->vtx.time) && IS_INF(frag->time)) - || eq_eps(rwalk->vtx.time, frag->time, 1.e-6)); -} - -res_T -fluid_temperature - (const struct sdis_scene* scn, - const double fp_to_meter, - struct rwalk* rwalk, - struct ssp_rng* rng, - struct temperature* T) -{ - double tmp; - (void)rng, (void)fp_to_meter; - ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T); - ASSERT(rwalk->mdm->type == SDIS_MEDIUM_FLUID); - - tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); - if(tmp < 0) { - log_err(scn->dev, "%s: unknown fluid temperature is not supported.\n", - FUNC_NAME); - return RES_BAD_OP; - } - T->value += tmp; - T->done = 1; - return RES_OK; -} - -static void -solid_solid_boundary_temperature - (const struct sdis_scene* scn, - const double fp_to_meter, - const struct sdis_interface_fragment* frag, - struct rwalk* rwalk, - struct ssp_rng* rng, - struct temperature* T) -{ - const struct sdis_interface* interface = NULL; - const struct sdis_medium* solid_front = NULL; - const struct sdis_medium* solid_back = NULL; - double lambda_front, lambda_back; - double delta_front_boundary, delta_back_boundary; - double delta_front_boundary_meter, delta_back_boundary_meter; - double delta_boundary; - double proba; - double tmp; - double r; - float pos[3], dir[3], range[2]; - ASSERT(scn && fp_to_meter > 0 && frag && rwalk && rng && T); - ASSERT(check_rwalk_fragment_consistency(rwalk, frag)); - (void)frag; - - /* Retrieve the current boundary media */ - interface = scene_get_interface(scn, rwalk->hit.prim.prim_id); - solid_front = interface_get_medium(interface, SDIS_FRONT); - solid_back = interface_get_medium(interface, SDIS_BACK); - ASSERT(solid_front->type == SDIS_MEDIUM_SOLID); - ASSERT(solid_back->type == SDIS_MEDIUM_SOLID); - - /* Fetch the properties of the media */ - lambda_front = solid_get_thermal_conductivity(solid_front, &rwalk->vtx); - lambda_back = solid_get_thermal_conductivity(solid_back, &rwalk->vtx); - delta_front_boundary = solid_get_delta_boundary(solid_front, &rwalk->vtx); - delta_back_boundary = solid_get_delta_boundary(solid_back, &rwalk->vtx); - - /* Convert the delta boundary from floating point units to meters */ - delta_front_boundary_meter = fp_to_meter * delta_front_boundary; - delta_back_boundary_meter = fp_to_meter * delta_back_boundary; - - /* Compute the proba to switch in solid0 or in solid1 */ - tmp = lambda_front / delta_front_boundary_meter; - proba = tmp / (tmp + lambda_back / delta_back_boundary_meter); - - r = ssp_rng_canonical(rng); - f3_normalize(dir, rwalk->hit.normal); - if(r < proba) { /* Reinject in front */ - delta_boundary = delta_front_boundary; - rwalk->mdm = solid_front; - } else { /* Reinject in back */ - delta_boundary = delta_back_boundary; - f3_minus(dir, dir); - rwalk->mdm = solid_back; - } - - /* "Reinject" the path into the solid along the surface normal. */ - f3_set_d3(pos, rwalk->vtx.P); - range[0] = 0, range[1] = (float)delta_boundary*RAY_RANGE_MAX_SCALE; - S3D(scene_view_trace_ray - (scn->s3d_view, pos, dir, range, &rwalk->hit, &rwalk->hit)); - if(!S3D_HIT_NONE(&rwalk->hit)) delta_boundary = rwalk->hit.distance * 0.5; - move_pos(rwalk->vtx.P, dir, (float)delta_boundary); - - /* Switch in solid random walk */ - T->func = solid_temperature; -} - -static void -solid_fluid_boundary_temperature - (const struct sdis_scene* scn, - const double fp_to_meter, - const struct sdis_interface_fragment* frag, - struct rwalk* rwalk, - struct ssp_rng* rng, - struct temperature* T) -{ - const struct sdis_interface* interface = NULL; - const struct sdis_medium* mdm_front = NULL; - const struct sdis_medium* mdm_back = NULL; - const struct sdis_medium* solid = NULL; - const struct sdis_medium* fluid = NULL; - double hc; - double lambda; - double fluid_proba; - double delta_boundary; - double r; - double tmp; - float dir[3], pos[3], range[2]; - - ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T); - ASSERT(check_rwalk_fragment_consistency(rwalk, frag)); - - /* Retrieve the solid and the fluid split by the boundary */ - interface = scene_get_interface(scn, rwalk->hit.prim.prim_id); - mdm_front = interface_get_medium(interface, SDIS_FRONT); - mdm_back = interface_get_medium(interface, SDIS_BACK); - ASSERT(mdm_front->type != mdm_back->type); - if(mdm_front->type == SDIS_MEDIUM_SOLID) { - solid = mdm_front; - fluid = mdm_back; - } else { - solid = mdm_back; - fluid = mdm_front; - } - - /* Fetch the solid properties */ - lambda = solid_get_thermal_conductivity(solid, &rwalk->vtx); - delta_boundary = solid_get_delta_boundary(solid, &rwalk->vtx); - hc = interface_get_convection_coef(interface, frag); - - /* Compute the probas to switch in solid or fluid random walk */ - tmp = lambda / (delta_boundary*fp_to_meter); - fluid_proba = hc / (tmp + hc); - - r = ssp_rng_canonical(rng); - if(r < fluid_proba) { /* Switch to fluid random walk */ - rwalk->mdm = fluid; - T->func = fluid_temperature; - } else { /* Solid random walk */ - rwalk->mdm = solid; - f3_normalize(dir, rwalk->hit.normal); - if(solid == mdm_back) f3_minus(dir, dir); - - /* "Reinject" the random walk into the solid */ - f3_set_d3(pos, rwalk->vtx.P); - range[0] = 0, range[1] = (float)delta_boundary*RAY_RANGE_MAX_SCALE; - S3D(scene_view_trace_ray - (scn->s3d_view, pos, dir, range, &rwalk->hit, &rwalk->hit)); - if(!S3D_HIT_NONE(&rwalk->hit)) delta_boundary = rwalk->hit.distance * 0.5; - move_pos(rwalk->vtx.P, dir, (float)delta_boundary); - - /* Switch in solid random walk */ - T->func = solid_temperature; - } -} - -res_T -boundary_temperature - (const struct sdis_scene* scn, - const double fp_to_meter, - struct rwalk* rwalk, - struct ssp_rng* rng, - struct temperature* T) -{ - struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; - const struct sdis_interface* interface = NULL; - const struct sdis_medium* mdm_front = NULL; - const struct sdis_medium* mdm_back = NULL; - double tmp; - ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T); - ASSERT(!S3D_HIT_NONE(&rwalk->hit)); - - setup_interface_fragment(&frag, &rwalk->vtx, &rwalk->hit); - - /* Retrieve the current interface */ - interface = scene_get_interface(scn, rwalk->hit.prim.prim_id); - - /* Check if the boundary condition is known */ - tmp = interface_get_temperature(interface, &frag); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - return RES_OK; - } - - mdm_front = interface_get_medium(interface, SDIS_FRONT); - mdm_back = interface_get_medium(interface, SDIS_BACK); - - if(mdm_front->type == mdm_back->type) { - solid_solid_boundary_temperature(scn, fp_to_meter, &frag, rwalk, rng, T); - } else { - solid_fluid_boundary_temperature(scn, fp_to_meter, &frag, rwalk, rng, T); - } - return RES_OK; -} +/* Generate the 3D solver */ +#define SDIS_SOLVE_PROBE_DIMENSION 3 +#include "sdis_solve_probe_Xd.h" -res_T -solid_temperature - (const struct sdis_scene* scn, - const double fp_to_meter, - struct rwalk* rwalk, - struct ssp_rng* rng, - struct temperature* T) -{ - double position_start[3]; - const struct sdis_medium* mdm; - ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T); - ASSERT(rwalk->mdm->type == SDIS_MEDIUM_SOLID); - - /* Check the random walk consistency */ - CHK(scene_get_medium(scn, rwalk->vtx.P, &mdm) == RES_OK); - if(mdm != rwalk->mdm) { - log_err(scn->dev, "%s: invalid solid random walk. " - "Unexpected medium at {%g, %g, %g}.\n", - FUNC_NAME, SPLIT3(rwalk->vtx.P)); - return RES_BAD_ARG; - } - /* Save the submitted position */ - d3_set(position_start, rwalk->vtx.P); - - do { /* Solid random walk */ - struct s3d_hit hit0, hit1; - double lambda; /* Thermal conductivity */ - double rho; /* Volumic mass */ - double cp; /* Calorific capacity */ - double tau, mu; - double tmp; - float delta, delta_solid; /* Random walk numerical parameter */ - float range[2]; - float dir0[3], dir1[3]; - float org[3]; - - /* Check the limit condition */ - tmp = solid_get_temperature(mdm, &rwalk->vtx); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - return RES_OK; - } - - /* Fetch solid properties */ - delta_solid = (float)solid_get_delta(mdm, &rwalk->vtx); - lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); - rho = solid_get_volumic_mass(mdm, &rwalk->vtx); - cp = solid_get_calorific_capacity(mdm, &rwalk->vtx); - - /* Sample a direction around 4PI */ - ssp_ran_sphere_uniform_float(rng, dir0, NULL); - - /* Trace a ray along the sampled direction and its opposite to check if a - * surface is hit in [0, delta_solid]. */ - f3_set_d3(org, rwalk->vtx.P); - f3_minus(dir1, dir0); - hit0 = hit1 = S3D_HIT_NULL; - range[0] = 0.f, range[1] = delta_solid*RAY_RANGE_MAX_SCALE; - S3D(scene_view_trace_ray(scn->s3d_view, org, dir0, range, NULL, &hit0)); - S3D(scene_view_trace_ray(scn->s3d_view, org, dir1, range, NULL, &hit1)); - - if(S3D_HIT_NONE(&hit0) && S3D_HIT_NONE(&hit1)) { - /* Hit nothing: move along dir0 of the original delta */ - delta = delta_solid; - } else { - /* Hit something: move along dir0 of the minimum hit distance */ - delta = MMIN(hit0.distance, hit1.distance); - } - - /* Sample the time */ - mu = (6 * lambda) / (rho * cp * delta * fp_to_meter * delta * fp_to_meter ); - tau = ssp_ran_exp(rng, mu); - rwalk->vtx.time -= tau; - - /* Check the initial condition */ - tmp = solid_get_temperature(mdm, &rwalk->vtx); - if(tmp >= 0) { - T->value += tmp; - T->done = 1; - return RES_OK; - } - - /* Define if the random walk hits something along dir0 */ - rwalk->hit = hit0.distance > delta ? S3D_HIT_NULL : hit0; - - /* Update the random walk position */ - move_pos(rwalk->vtx.P, dir0, delta); - - /* Fetch the current medium */ - if(S3D_HIT_NONE(&rwalk->hit)) { - CHK(scene_get_medium(scn, rwalk->vtx.P, &mdm) == RES_OK); - } else { - const struct sdis_interface* interface; - interface = scene_get_interface(scn, rwalk->hit.prim.prim_id); - mdm = interface_get_medium - (interface, - f3_dot(rwalk->hit.normal, dir0) < 0 ? SDIS_FRONT : SDIS_BACK); - } - - /* Check random walk consistency */ - if(mdm != rwalk->mdm) { - log_err(scn->dev, - "%s: inconsistent medium during the solid random walk.\n" - " start position: %g %g %g; current position: %g %g %g\n", - FUNC_NAME, SPLIT3(position_start), SPLIT3(rwalk->vtx.P)); - return RES_BAD_OP; - } - - /* Keep going while the solid random walk does not hit an interface */ - } while(S3D_HIT_NONE(&rwalk->hit)); - - T->func = boundary_temperature; - return RES_OK; -} - -static res_T -compute_temperature - (struct sdis_scene* scn, - const double fp_to_meter, - struct rwalk* rwalk, - struct ssp_rng* rng, - struct temperature* T) -{ -#ifndef NDEBUG - struct temperature* stack = NULL; - size_t istack; -#endif - res_T res = RES_OK; - ASSERT(scn && fp_to_meter && rwalk && rng && T); - - do { - res = T->func(scn, fp_to_meter, rwalk, rng, T); - if(res != RES_OK) goto error; - -#ifndef NDEBUG - sa_push(stack, *T); - ++istack; -#endif - } while(!T->done); - -exit: -#ifndef NDEBUG - sa_release(stack); -#endif - return res; -error: - goto exit; -} +#include <star/ssp.h> +#include <omp.h> -/******************************************************************************* - * Exported functions - ******************************************************************************/ res_T sdis_solve_probe (struct sdis_scene* scn, @@ -475,11 +40,14 @@ sdis_solve_probe { const struct sdis_medium* medium = NULL; struct sdis_estimator* estimator = NULL; - struct ssp_rng* rng = NULL; + struct ssp_rng_proxy* rng_proxy = NULL; + struct ssp_rng** rngs = NULL; double weight = 0; double sqr_weight = 0; size_t irealisation = 0; - res_T res = RES_OK; + size_t N = 0; /* #realisations that do not fail */ + size_t i; + ATOMIC res = RES_OK; if(!scn || !nrealisations || !position || time < 0 || fp_to_meter <= 0 || !out_estimator) { @@ -487,56 +55,79 @@ sdis_solve_probe goto error; } - res = scene_get_medium(scn, position, &medium); + /* Create the proxy RNG */ + res = ssp_rng_proxy_create(scn->dev->allocator, &ssp_rng_mt19937_64, + scn->dev->nthreads, &rng_proxy); if(res != RES_OK) goto error; - res = ssp_rng_create(scn->dev->allocator, &ssp_rng_mt19937_64, &rng); - if(res != RES_OK) goto error; + /* Create the per thread RNG */ + rngs = MEM_CALLOC + (scn->dev->allocator, scn->dev->nthreads, sizeof(struct ssp_rng*)); + if(!rngs) { + res = RES_MEM_ERR; + goto error; + } + FOR_EACH(i, 0, scn->dev->nthreads) { + res = ssp_rng_proxy_create_rng(rng_proxy, i, rngs+i); + if(res != RES_OK) goto error; + } + + /* Create the estimator */ res = estimator_create(scn->dev, &estimator); if(res != RES_OK) goto error; - FOR_EACH(irealisation, 0, nrealisations) { - struct rwalk rwalk = RWALK_NULL; - struct temperature T = TEMPERATURE_NULL; + /* Retrieve the medium in which the submitted position lies */ + res = scene_get_medium(scn, position, &medium); + if(res != RES_OK) goto error; - switch(medium->type) { - case SDIS_MEDIUM_FLUID: T.func = fluid_temperature; break; - case SDIS_MEDIUM_SOLID: T.func = solid_temperature; break; - default: FATAL("Unreachable code\n"); break; - } + /* Here we go! Launch the Monte Carlo estimation */ + #pragma omp parallel for schedule(static) reduction(+:weight,sqr_weight,N) + for(irealisation = 0; irealisation < nrealisations; ++irealisation) { + res_T res_local; + double w; + const int ithread = omp_get_thread_num(); + struct ssp_rng* rng = rngs[ithread]; - rwalk.vtx.P[0] = position[0]; - rwalk.vtx.P[1] = position[1]; - rwalk.vtx.P[2] = position[2]; - rwalk.vtx.time = time; - rwalk.hit = S3D_HIT_NULL; - rwalk.mdm = medium; + if(ATOMIC_GET(&res) != RES_OK) continue; /* An error occured */ - res = compute_temperature(scn, fp_to_meter, &rwalk, rng, &T); - if(res != RES_OK) { - if(res == RES_BAD_OP) { + if(scene_is_2d(scn)) { + res_local = probe_realisation_2d + (scn, rng, medium, position, time, fp_to_meter, &w); + } else { + res_local = probe_realisation_3d + (scn, rng, medium, position, time, fp_to_meter, &w); + } + if(res_local != RES_OK) { + if(res_local == RES_BAD_OP) { ++estimator->nfailures; } else { - goto error; + ATOMIC_SET(&res, res_local); + continue; } } else { - weight += T.value; - sqr_weight += T.value*T.value; - ++estimator->nrealisations; + weight += w; + sqr_weight += w*w; + ++N; } } - estimator->temperature.E = weight / (double)estimator->nrealisations; + estimator->nrealisations = N; + estimator->temperature.E = weight / (double)N; estimator->temperature.V = - sqr_weight / (double)estimator->nrealisations + sqr_weight / (double)N - estimator->temperature.E * estimator->temperature.E; - estimator->temperature.SE = - sqrt(estimator->temperature.V / (double)estimator->nrealisations); + estimator->temperature.SE = sqrt(estimator->temperature.V / (double)N); exit: - if(rng) SSP(rng_ref_put(rng)); + if(rngs) { + FOR_EACH(i, 0, scn->dev->nthreads) { + if(rngs[i]) SSP(rng_ref_put(rngs[i])); + } + MEM_RM(scn->dev->allocator, rngs); + } + if(rng_proxy) SSP(rng_proxy_ref_put(rng_proxy)); if(out_estimator) *out_estimator = estimator; - return res; + return (res_T)res; error: if(estimator) { SDIS(estimator_ref_put(estimator)); @@ -544,3 +135,4 @@ error: } goto exit; } + diff --git a/src/sdis_solve_probe_Xd.h b/src/sdis_solve_probe_Xd.h @@ -0,0 +1,565 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef SDIS_SOLVE_PROBE_DIMENSION +#ifndef SDIS_SOLVE_PROBE_XD_H +#define SDIS_SOLVE_PROBE_XD_H + +#include "sdis_device_c.h" +#include "sdis_interface_c.h" +#include "sdis_medium_c.h" +#include "sdis_scene_c.h" + +#include <rsys/stretchy_array.h> + +#include <star/ssp.h> + +/* Emperical scale factor to apply to the upper bound of the ray range in order + * to handle numerical imprecisions */ +#define RAY_RANGE_MAX_SCALE 1.0001f + +#endif /* SDIS_SOLVE_PROBE_XD_H */ +#else + +#if (SDIS_SOLVE_PROBE_DIMENSION == 2) + #include <rsys/double2.h> + #include <rsys/float2.h> + #include <star/s2d.h> +#elif (SDIS_SOLVE_PROBE_DIMENSION == 3) + #include <rsys/double2.h> + #include <rsys/double3.h> + #include <rsys/float3.h> + #include <star/s3d.h> +#else + #error "Invalid SDIS_SOLVE_PROBE_DIMENSION value." +#endif + +/* Syntactic sugar */ +#define DIM SDIS_SOLVE_PROBE_DIMENSION + +/* Star-XD macros generic to SDIS_SOLVE_PROBE_DIMENSION */ +#define sXd(Name) CONCAT(CONCAT(CONCAT(s, DIM), d_), Name) +#define SXD_HIT_NONE CONCAT(CONCAT(S,DIM), D_HIT_NONE) +#define SXD_HIT_NULL CONCAT(CONCAT(S,DIM), D_HIT_NULL) +#define SXD_HIT_NULL__ CONCAT(CONCAT(S, DIM), D_HIT_NULL__) +#define SXD CONCAT(CONCAT(S, DIM), D) + +/* Vector macros generic to SDIS_SOLVE_PROBE_DIMENSION */ +#define dX(Func) CONCAT(CONCAT(CONCAT(d, DIM), _), Func) +#define fX(Func) CONCAT(CONCAT(CONCAT(f, DIM), _), Func) +#define fX_set_dX CONCAT(CONCAT(CONCAT(f, DIM), _set_d), DIM) +#define dX_set_fX CONCAT(CONCAT(CONCAT(d, DIM), _set_f), DIM) + +/* Macro making generic its subimitted name to SDIS_SOLVE_PROBE_DIMENSION */ +#define XD(Name) CONCAT(CONCAT(CONCAT(Name, _), DIM), d) + +/* Current state of the random walk */ +struct XD(rwalk) { + struct sdis_rwalk_vertex vtx; /* Position and time of the Random walk */ + const struct sdis_medium* mdm; /* Medium in which the random walk lies */ + struct sXd(hit) hit; /* Hit of the random walk */ +}; +static const struct XD(rwalk) XD(RWALK_NULL) = { + SDIS_RWALK_VERTEX_NULL__, NULL, SXD_HIT_NULL__ +}; + +struct XD(temperature) { + res_T (*func)/* Next function to invoke in order to compute the temperature */ + (const struct sdis_scene* scn, + const double fp_to_meter, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* temp); + double value; /* Current value of the temperature */ + int done; +}; +static const struct XD(temperature) XD(TEMPERATURE_NULL) = { NULL, 0, 0 }; + +static res_T +XD(boundary_temperature) + (const struct sdis_scene* scn, + const double fp_to_meter, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T); + +static res_T +XD(solid_temperature) + (const struct sdis_scene* scn, + const double fp_to_meter, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T); + +static res_T +XD(fluid_temperature) + (const struct sdis_scene* scn, + const double fp_to_meter, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T); + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static FINLINE void +XD(move_pos)(double pos[DIM], const float dir[DIM], const float delta) +{ + ASSERT(pos && dir); + pos[0] += dir[0] * delta; + pos[1] += dir[1] * delta; +#if (SDIS_SOLVE_PROBE_DIMENSION == 3) + pos[2] += dir[2] * delta; +#endif + +} + +/* Check that the interface fragment is consistent with the current state of + * the random walk */ +static INLINE int +XD(check_rwalk_fragment_consistency) + (const struct XD(rwalk)* rwalk, + const struct sdis_interface_fragment* frag) +{ + double N[DIM]; + double uv[2] = {0, 0}; + ASSERT(rwalk && frag); + dX(normalize)(N, dX_set_fX(N, rwalk->hit.normal)); + if( SXD_HIT_NONE(&rwalk->hit) + || !dX(eq_eps)(rwalk->vtx.P, frag->P, 1.e-6) + || !dX(eq_eps)(N, frag->Ng, 1.e-6) + || !( (IS_INF(rwalk->vtx.time) && IS_INF(frag->time)) + || eq_eps(rwalk->vtx.time, frag->time, 1.e-6))) { + return 0; + } +#if (SDIS_SOLVE_PROBE_DIMENSION == 2) + uv[0] = rwalk->hit.u; +#else + d2_set_f2(uv, rwalk->hit.uv); +#endif + return d2_eq_eps(uv, frag->uv, 1.e-6); +} + +res_T +XD(fluid_temperature) + (const struct sdis_scene* scn, + const double fp_to_meter, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + double tmp; + (void)rng, (void)fp_to_meter; + ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T); + ASSERT(rwalk->mdm->type == SDIS_MEDIUM_FLUID); + + tmp = fluid_get_temperature(rwalk->mdm, &rwalk->vtx); + if(tmp < 0) { + log_err(scn->dev, "%s: unknown fluid temperature is not supported.\n", + FUNC_NAME); + return RES_BAD_OP; + } + T->value += tmp; + T->done = 1; + return RES_OK; +} + +static void +XD(solid_solid_boundary_temperature) + (const struct sdis_scene* scn, + const double fp_to_meter, + const struct sdis_interface_fragment* frag, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + const struct sdis_interface* interf = NULL; + const struct sdis_medium* solid_front = NULL; + const struct sdis_medium* solid_back = NULL; + double lambda_front, lambda_back; + double delta_front_boundary, delta_back_boundary; + double delta_front_boundary_meter, delta_back_boundary_meter; + double delta_boundary; + double proba; + double tmp; + double r; + float pos[DIM], dir[DIM], range[2]; + ASSERT(scn && fp_to_meter > 0 && frag && rwalk && rng && T); + ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); + (void)frag; + + /* Retrieve the current boundary media */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + solid_front = interface_get_medium(interf, SDIS_FRONT); + solid_back = interface_get_medium(interf, SDIS_BACK); + ASSERT(solid_front->type == SDIS_MEDIUM_SOLID); + ASSERT(solid_back->type == SDIS_MEDIUM_SOLID); + + /* Fetch the properties of the media */ + lambda_front = solid_get_thermal_conductivity(solid_front, &rwalk->vtx); + lambda_back = solid_get_thermal_conductivity(solid_back, &rwalk->vtx); + delta_front_boundary = solid_get_delta_boundary(solid_front, &rwalk->vtx); + delta_back_boundary = solid_get_delta_boundary(solid_back, &rwalk->vtx); + + /* Convert the delta boundary from floating point units to meters */ + delta_front_boundary_meter = fp_to_meter * delta_front_boundary; + delta_back_boundary_meter = fp_to_meter * delta_back_boundary; + + /* Compute the proba to switch in solid0 or in solid1 */ + tmp = lambda_front / delta_front_boundary_meter; + proba = tmp / (tmp + lambda_back / delta_back_boundary_meter); + + r = ssp_rng_canonical(rng); + fX(normalize)(dir, rwalk->hit.normal); + if(r < proba) { /* Reinject in front */ + delta_boundary = delta_front_boundary; + rwalk->mdm = solid_front; + } else { /* Reinject in back */ + delta_boundary = delta_back_boundary; + fX(minus)(dir, dir); + rwalk->mdm = solid_back; + } + + /* "Reinject" the path into the solid along the surface normal. */ + fX_set_dX(pos, rwalk->vtx.P); + range[0] = 0, range[1] = (float)delta_boundary*RAY_RANGE_MAX_SCALE; + SXD(scene_view_trace_ray + (scn->sXd(view), pos, dir, range, &rwalk->hit, &rwalk->hit)); + if(!SXD_HIT_NONE(&rwalk->hit)) delta_boundary = rwalk->hit.distance * 0.5; + XD(move_pos)(rwalk->vtx.P, dir, (float)delta_boundary); + + /* Switch in solid random walk */ + T->func = XD(solid_temperature); +} + +static void +XD(solid_fluid_boundary_temperature) + (const struct sdis_scene* scn, + const double fp_to_meter, + const struct sdis_interface_fragment* frag, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + const struct sdis_interface* interf = NULL; + const struct sdis_medium* mdm_front = NULL; + const struct sdis_medium* mdm_back = NULL; + const struct sdis_medium* solid = NULL; + const struct sdis_medium* fluid = NULL; + double hc; + double lambda; + double fluid_proba; + double delta_boundary; + double r; + double tmp; + float dir[DIM], pos[DIM], range[2]; + + ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T); + ASSERT(XD(check_rwalk_fragment_consistency)(rwalk, frag)); + + /* Retrieve the solid and the fluid split by the boundary */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + mdm_front = interface_get_medium(interf, SDIS_FRONT); + mdm_back = interface_get_medium(interf, SDIS_BACK); + ASSERT(mdm_front->type != mdm_back->type); + if(mdm_front->type == SDIS_MEDIUM_SOLID) { + solid = mdm_front; + fluid = mdm_back; + } else { + solid = mdm_back; + fluid = mdm_front; + } + + /* Fetch the solid properties */ + lambda = solid_get_thermal_conductivity(solid, &rwalk->vtx); + delta_boundary = solid_get_delta_boundary(solid, &rwalk->vtx); + hc = interface_get_convection_coef(interf, frag); + + /* Compute the probas to switch in solid or fluid random walk */ + tmp = lambda / (delta_boundary*fp_to_meter); + fluid_proba = hc / (tmp + hc); + + r = ssp_rng_canonical(rng); + if(r < fluid_proba) { /* Switch to fluid random walk */ + rwalk->mdm = fluid; + T->func = XD(fluid_temperature); + } else { /* Solid random walk */ + rwalk->mdm = solid; + fX(normalize)(dir, rwalk->hit.normal); + if(solid == mdm_back) fX(minus)(dir, dir); + + /* "Reinject" the random walk into the solid */ + fX_set_dX(pos, rwalk->vtx.P); + range[0] = 0, range[1] = (float)delta_boundary*RAY_RANGE_MAX_SCALE; + SXD(scene_view_trace_ray + (scn->sXd(view), pos, dir, range, &rwalk->hit, &rwalk->hit)); + if(!SXD_HIT_NONE(&rwalk->hit)) delta_boundary = rwalk->hit.distance * 0.5; + XD(move_pos)(rwalk->vtx.P, dir, (float)delta_boundary); + + /* Switch in solid random walk */ + T->func = XD(solid_temperature); + } +} + +res_T +XD(boundary_temperature) + (const struct sdis_scene* scn, + const double fp_to_meter, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + struct sdis_interface_fragment frag = SDIS_INTERFACE_FRAGMENT_NULL; + const struct sdis_interface* interf = NULL; + const struct sdis_medium* mdm_front = NULL; + const struct sdis_medium* mdm_back = NULL; + double tmp; + ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T); + ASSERT(!SXD_HIT_NONE(&rwalk->hit)); + + XD(setup_interface_fragment)(&frag, &rwalk->vtx, &rwalk->hit); + + /* Retrieve the current interface */ + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + + /* Check if the boundary condition is known */ + tmp = interface_get_temperature(interf, &frag); + if(tmp >= 0) { + T->value += tmp; + T->done = 1; + return RES_OK; + } + + mdm_front = interface_get_medium(interf, SDIS_FRONT); + mdm_back = interface_get_medium(interf, SDIS_BACK); + + if(mdm_front->type == mdm_back->type) { + XD(solid_solid_boundary_temperature)(scn, fp_to_meter, &frag, rwalk, rng, T); + } else { + XD(solid_fluid_boundary_temperature)(scn, fp_to_meter, &frag, rwalk, rng, T); + } + return RES_OK; +} + +res_T +XD(solid_temperature) + (const struct sdis_scene* scn, + const double fp_to_meter, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ + double position_start[DIM]; + const struct sdis_medium* mdm; + ASSERT(scn && fp_to_meter > 0 && rwalk && rng && T); + ASSERT(rwalk->mdm->type == SDIS_MEDIUM_SOLID); + + /* Check the random walk consistency */ + CHK(scene_get_medium(scn, rwalk->vtx.P, &mdm) == RES_OK); + if(mdm != rwalk->mdm) { + log_err(scn->dev, "%s: invalid solid random walk. " + "Unexpected medium at {%g, %g, %g}.\n", + FUNC_NAME, SPLIT3(rwalk->vtx.P)); + return RES_BAD_ARG; + } + /* Save the submitted position */ + dX(set)(position_start, rwalk->vtx.P); + + do { /* Solid random walk */ + struct sXd(hit) hit0, hit1; + double lambda; /* Thermal conductivity */ + double rho; /* Volumic mass */ + double cp; /* Calorific capacity */ + double tau, mu; + double tmp; + float delta, delta_solid; /* Random walk numerical parameter */ + float range[2]; + float dir0[DIM], dir1[DIM]; + float org[DIM]; + + /* Check the limit condition */ + tmp = solid_get_temperature(mdm, &rwalk->vtx); + if(tmp >= 0) { + T->value += tmp; + T->done = 1; + return RES_OK; + } + + /* Fetch solid properties */ + delta_solid = (float)solid_get_delta(mdm, &rwalk->vtx); + lambda = solid_get_thermal_conductivity(mdm, &rwalk->vtx); + rho = solid_get_volumic_mass(mdm, &rwalk->vtx); + cp = solid_get_calorific_capacity(mdm, &rwalk->vtx); + +#if (SDIS_SOLVE_PROBE_DIMENSION == 2) + /* Sample a direction around 2PI */ + ssp_ran_circle_uniform_float(rng, dir0, NULL); +#else + /* Sample a direction around 4PI */ + ssp_ran_sphere_uniform_float(rng, dir0, NULL); +#endif + + /* Trace a ray along the sampled direction and its opposite to check if a + * surface is hit in [0, delta_solid]. */ + fX_set_dX(org, rwalk->vtx.P); + fX(minus)(dir1, dir0); + hit0 = hit1 = SXD_HIT_NULL; + range[0] = 0.f, range[1] = delta_solid*RAY_RANGE_MAX_SCALE; + SXD(scene_view_trace_ray(scn->sXd(view), org, dir0, range, NULL, &hit0)); + SXD(scene_view_trace_ray(scn->sXd(view), org, dir1, range, NULL, &hit1)); + + if(SXD_HIT_NONE(&hit0) && SXD_HIT_NONE(&hit1)) { + /* Hit nothing: move along dir0 of the original delta */ + delta = delta_solid; + } else { + /* Hit something: move along dir0 of the minimum hit distance */ + delta = MMIN(hit0.distance, hit1.distance); + } + + /* Sample the time */ + mu = (2*DIM*lambda) / (rho*cp*delta*fp_to_meter*delta*fp_to_meter); + tau = ssp_ran_exp(rng, mu); + rwalk->vtx.time -= tau; + + /* Check the initial condition */ + tmp = solid_get_temperature(mdm, &rwalk->vtx); + if(tmp >= 0) { + T->value += tmp; + T->done = 1; + return RES_OK; + } + + /* Define if the random walk hits something along dir0 */ + rwalk->hit = hit0.distance > delta ? SXD_HIT_NULL : hit0; + + /* Update the random walk position */ + XD(move_pos)(rwalk->vtx.P, dir0, delta); + + /* Fetch the current medium */ + if(SXD_HIT_NONE(&rwalk->hit)) { + CHK(scene_get_medium(scn, rwalk->vtx.P, &mdm) == RES_OK); + } else { + const struct sdis_interface* interf; + interf = scene_get_interface(scn, rwalk->hit.prim.prim_id); + mdm = interface_get_medium + (interf, + fX(dot(rwalk->hit.normal, dir0)) < 0 ? SDIS_FRONT : SDIS_BACK); + } + + /* Check random walk consistency */ + if(mdm != rwalk->mdm) { + log_err(scn->dev, + "%s: inconsistent medium during the solid random walk.\n", FUNC_NAME); + if(DIM == 2) { + log_err(scn->dev, + " start position: %g %g; current position: %g %g\n", + SPLIT2(position_start), SPLIT2(rwalk->vtx.P)); + } else { + log_err(scn->dev, + " start position: %g %g %g; current position: %g %g %g\n", + SPLIT3(position_start), SPLIT3(rwalk->vtx.P)); + } + return RES_BAD_OP; + } + + /* Keep going while the solid random walk does not hit an interface */ + } while(SXD_HIT_NONE(&rwalk->hit)); + + T->func = XD(boundary_temperature); + return RES_OK; +} + +static res_T +XD(compute_temperature) + (struct sdis_scene* scn, + const double fp_to_meter, + struct XD(rwalk)* rwalk, + struct ssp_rng* rng, + struct XD(temperature)* T) +{ +#ifndef NDEBUG + struct XD(temperature)* stack = NULL; + size_t istack = 0; +#endif + res_T res = RES_OK; + ASSERT(scn && fp_to_meter && rwalk && rng && T); + + do { + res = T->func(scn, fp_to_meter, rwalk, rng, T); + if(res != RES_OK) goto error; + +#ifndef NDEBUG + sa_push(stack, *T); + ++istack; +#endif + } while(!T->done); + +exit: +#ifndef NDEBUG + sa_release(stack); +#endif + return res; +error: + goto exit; +} + +static res_T +XD(probe_realisation) + (struct sdis_scene* scn, + struct ssp_rng* rng, + const struct sdis_medium* medium, + const double position[], + const double time, + const double fp_to_meter,/* Scale factor from floating point unit to meter */ + double* weight) +{ + struct XD(rwalk) rwalk = XD(RWALK_NULL); + struct XD(temperature) T = XD(TEMPERATURE_NULL); + res_T res = RES_OK; + ASSERT(medium && position && fp_to_meter > 0 && weight && time >= 0); + + switch(medium->type) { + case SDIS_MEDIUM_FLUID: T.func = XD(fluid_temperature); break; + case SDIS_MEDIUM_SOLID: T.func = XD(solid_temperature); break; + default: FATAL("Unreachable code\n"); break; + } + + dX(set)(rwalk.vtx.P, position); + rwalk.vtx.time = time; + rwalk.hit = SXD_HIT_NULL; + rwalk.mdm = medium; + + res = XD(compute_temperature)(scn, fp_to_meter, &rwalk, rng, &T); + if(res != RES_OK) return res; + + *weight = T.value; + return RES_OK; +} + + +#undef SDIS_SOLVE_PROBE_DIMENSION +#undef DIM +#undef sXd +#undef SXD_HIT_NONE +#undef SXD_HIT_NULL +#undef SXD_HIT_NULL__ +#undef SXD +#undef dX +#undef fX +#undef fX_set_dX +#undef XD + +#endif /* !SDIS_SOLVE_PROBE_DIMENSION */ + diff --git a/src/test_sdis_interface.c b/src/test_sdis_interface.c @@ -23,7 +23,7 @@ main(int argc, char** argv) struct sdis_device* dev = NULL; struct sdis_medium* fluid = NULL; struct sdis_medium* solid = NULL; - struct sdis_interface* interface = NULL; + struct sdis_interface* interf = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; struct sdis_interface_shader shader = DUMMY_INTERFACE_SHADER; @@ -53,42 +53,42 @@ main(int argc, char** argv) CHK(CREATE(dev, NULL, fluid, &shader, NULL, NULL) == RES_BAD_ARG); CHK(CREATE(NULL, solid, fluid, &shader, NULL, NULL) == RES_BAD_ARG); CHK(CREATE(dev, solid, fluid, &shader, NULL, NULL) == RES_BAD_ARG); - CHK(CREATE(NULL, NULL, NULL, NULL, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(dev, NULL, NULL, NULL, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(NULL, solid, NULL, NULL, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(dev, solid, NULL, NULL, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(NULL, NULL, fluid, NULL, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(dev, NULL, fluid, NULL, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(NULL, solid, fluid, NULL, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(dev, solid, fluid, NULL, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(NULL, NULL, NULL, &shader, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(dev, NULL, NULL, &shader, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(NULL, solid, NULL, &shader, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(dev, solid, NULL, &shader, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(NULL, NULL, fluid, &shader, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(dev, NULL, fluid, &shader, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(NULL, solid, fluid, &shader, NULL, &interface) == RES_BAD_ARG); - CHK(CREATE(dev, solid, fluid, &shader, NULL, &interface) == RES_OK); + CHK(CREATE(NULL, NULL, NULL, NULL, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(dev, NULL, NULL, NULL, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(NULL, solid, NULL, NULL, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(dev, solid, NULL, NULL, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(NULL, NULL, fluid, NULL, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(dev, NULL, fluid, NULL, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(NULL, solid, fluid, NULL, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(dev, solid, fluid, NULL, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(NULL, NULL, NULL, &shader, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(dev, NULL, NULL, &shader, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(NULL, solid, NULL, &shader, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(dev, solid, NULL, &shader, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(NULL, NULL, fluid, &shader, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(dev, NULL, fluid, &shader, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(NULL, solid, fluid, &shader, NULL, &interf) == RES_BAD_ARG); + CHK(CREATE(dev, solid, fluid, &shader, NULL, &interf) == RES_OK); CHK(sdis_interface_ref_get(NULL) == RES_BAD_ARG); - CHK(sdis_interface_ref_get(interface) == RES_OK); + CHK(sdis_interface_ref_get(interf) == RES_OK); CHK(sdis_interface_ref_put(NULL) == RES_BAD_ARG); - CHK(sdis_interface_ref_put(interface) == RES_OK); - CHK(sdis_interface_ref_put(interface) == RES_OK); + CHK(sdis_interface_ref_put(interf) == RES_OK); + CHK(sdis_interface_ref_put(interf) == RES_OK); - CHK(CREATE(dev, solid, solid, &shader, NULL, &interface) == RES_BAD_ARG); + CHK(CREATE(dev, solid, solid, &shader, NULL, &interf) == RES_BAD_ARG); shader.convection_coef = NULL; - CHK(CREATE(dev, solid, solid, &shader, NULL, &interface) == RES_OK); - CHK(sdis_interface_ref_put(interface) == RES_OK); + CHK(CREATE(dev, solid, solid, &shader, NULL, &interf) == RES_OK); + CHK(sdis_interface_ref_put(interf) == RES_OK); shader.temperature = NULL; - CHK(CREATE(dev, solid, solid, &shader, NULL, &interface) == RES_OK); - CHK(sdis_interface_ref_put(interface) == RES_OK); + CHK(CREATE(dev, solid, solid, &shader, NULL, &interf) == RES_OK); + CHK(sdis_interface_ref_put(interf) == RES_OK); - CHK(CREATE(dev, solid, fluid, &shader, NULL, &interface) == RES_BAD_ARG); + CHK(CREATE(dev, solid, fluid, &shader, NULL, &interf) == RES_BAD_ARG); shader.convection_coef = DUMMY_INTERFACE_SHADER.convection_coef; - CHK(CREATE(dev, solid, fluid, &shader, NULL, &interface) == RES_OK); - CHK(sdis_interface_ref_put(interface) == RES_OK); + CHK(CREATE(dev, solid, fluid, &shader, NULL, &interf) == RES_OK); + CHK(sdis_interface_ref_put(interf) == RES_OK); #undef CREATE CHK(sdis_device_ref_put(dev) == RES_OK); diff --git a/src/test_sdis_scene.c b/src/test_sdis_scene.c @@ -20,7 +20,7 @@ struct context { const double* positions; const size_t* indices; - struct sdis_interface* interface; + struct sdis_interface* interf; }; static INLINE void @@ -52,7 +52,7 @@ get_interface(const size_t itri, struct sdis_interface** bound, void* context) CHK(ctx != NULL); CHK(itri < box_ntriangles); CHK(bound != NULL); - *bound = ctx->interface; + *bound = ctx->interf; } int @@ -62,7 +62,7 @@ main(int argc, char** argv) struct sdis_device* dev = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* fluid = NULL; - struct sdis_interface* interface = NULL; + struct sdis_interface* interf = NULL; struct sdis_scene* scn = NULL; struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; @@ -78,11 +78,11 @@ main(int argc, char** argv) CHK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid) == RES_OK); CHK(sdis_solid_create(dev, &solid_shader, NULL, &solid) == RES_OK); CHK(sdis_interface_create - (dev, solid, fluid, &interface_shader, NULL, &interface) == RES_OK); + (dev, solid, fluid, &interface_shader, NULL, &interf) == RES_OK); ctx.positions = box_vertices; ctx.indices = box_indices; - ctx.interface = interface; + ctx.interf = interf; ntris = box_ntriangles; npos = box_nvertices; @@ -122,7 +122,7 @@ main(int argc, char** argv) CHK(sdis_scene_ref_put(scn) == RES_OK); CHK(sdis_device_ref_put(dev) == RES_OK); - CHK(sdis_interface_ref_put(interface) == RES_OK); + CHK(sdis_interface_ref_put(interf) == RES_OK); CHK(sdis_medium_ref_put(solid) == RES_OK); CHK(sdis_medium_ref_put(fluid) == RES_OK); diff --git a/src/test_sdis_solve_probe.c b/src/test_sdis_solve_probe.c @@ -38,7 +38,7 @@ struct context { const double* positions; const size_t* indices; - struct sdis_interface* interface; + struct sdis_interface* interf; }; static void @@ -64,7 +64,7 @@ get_interface(const size_t itri, struct sdis_interface** bound, void* context) { struct context* ctx = context; (void)itri; - *bound = ctx->interface; + *bound = ctx->interf; } /******************************************************************************* @@ -144,7 +144,7 @@ solid_get_temperature /******************************************************************************* * Interface ******************************************************************************/ -struct interface { +struct interf { double hc; }; @@ -153,7 +153,7 @@ interface_get_convection_coef (const struct sdis_interface_fragment* frag, struct sdis_data* data) { CHK(data != NULL && frag != NULL); - return ((const struct interface*)sdis_data_cget(data))->hc; + return ((const struct interf*)sdis_data_cget(data))->hc; } /******************************************************************************* @@ -167,7 +167,7 @@ main(int argc, char** argv) struct sdis_device* dev = NULL; struct sdis_medium* solid = NULL; struct sdis_medium* fluid = NULL; - struct sdis_interface* interface = NULL; + struct sdis_interface* interf = NULL; struct sdis_scene* scn = NULL; struct sdis_data* data = NULL; struct sdis_estimator* estimator = NULL; @@ -177,7 +177,7 @@ main(int argc, char** argv) struct context ctx; struct fluid* fluid_param; struct solid* solid_param; - struct interface* interface_param; + struct interf* interface_param; double pos[3]; double time; double ref; @@ -218,14 +218,14 @@ main(int argc, char** argv) CHK(sdis_data_ref_put(data) == RES_OK); /* Create the solid/fluid interface */ - CHK(sdis_data_create(dev, sizeof(struct interface), - ALIGNOF(struct interface), NULL, &data) == RES_OK); + CHK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data) == RES_OK); interface_param = sdis_data_get(data); interface_param->hc = 0.5; interface_shader.convection_coef = interface_get_convection_coef; interface_shader.temperature = NULL; CHK(sdis_interface_create - (dev, solid, fluid, &interface_shader, data, &interface) == RES_OK); + (dev, solid, fluid, &interface_shader, data, &interf) == RES_OK); CHK(sdis_data_ref_put(data) == RES_OK); /* Release the media */ @@ -235,11 +235,11 @@ main(int argc, char** argv) /* Create the scene */ ctx.positions = box_vertices; ctx.indices = box_indices; - ctx.interface = interface; + ctx.interf = interf; CHK(sdis_scene_create(dev, box_ntriangles, get_indices, get_interface, box_nvertices, get_position, &ctx, &scn) == RES_OK); - CHK(sdis_interface_ref_put(interface) == RES_OK); + CHK(sdis_interface_ref_put(interf) == RES_OK); /* Test the solver */ pos[0] = 0.5; diff --git a/src/test_sdis_solve_probe2.c b/src/test_sdis_solve_probe2.c @@ -126,7 +126,7 @@ solid_get_delta_boundary /******************************************************************************* * Interface ******************************************************************************/ -struct interface { +struct interf { double temperature; }; @@ -144,7 +144,7 @@ interface_get_temperature (const struct sdis_interface_fragment* frag, struct sdis_data* data) { CHK(data != NULL && frag != NULL); - return ((const struct interface*)sdis_data_cget(data))->temperature; + return ((const struct interf*)sdis_data_cget(data))->temperature; } /******************************************************************************* @@ -169,7 +169,7 @@ main(int argc, char** argv) struct sdis_interface_shader interface_shader = DUMMY_INTERFACE_SHADER; struct sdis_interface* interfaces[12]; struct context ctx; - struct interface* interface_param = NULL; + struct interf* interface_param = NULL; double pos[3]; double time; double ref; @@ -202,8 +202,8 @@ main(int argc, char** argv) (dev, solid, fluid, &interface_shader, NULL, &Tnone) == RES_OK); /* Create the fluid/solid interface with a fixed temperature of 300K */ - CHK(sdis_data_create(dev, sizeof(struct interface), - ALIGNOF(struct interface), NULL, &data) == RES_OK); + CHK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data) == RES_OK); interface_param = sdis_data_get(data); interface_param->temperature = 300; interface_shader.convection_coef = null_convection_coef; @@ -213,8 +213,8 @@ main(int argc, char** argv) CHK(sdis_data_ref_put(data) == RES_OK); /* Create the fluid/solid interface with a fixed temperature of 350K */ - CHK(sdis_data_create(dev, sizeof(struct interface), - ALIGNOF(struct interface), NULL, &data) == RES_OK); + CHK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data) == RES_OK); interface_param = sdis_data_get(data); interface_param->temperature = 350; interface_shader.convection_coef = null_convection_coef; diff --git a/src/test_sdis_solve_probe2_2d.c b/src/test_sdis_solve_probe2_2d.c @@ -0,0 +1,275 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "sdis.h" +#include "test_sdis_utils.h" + +#include <rsys/math.h> + +/* + * The scene is composed of a solid square whose temperature is unknown. The + * convection coefficient with the surrounding fluid is null. The temperature + * is fixed at the left and right segment. + * + * (1,1) + * +-------+ + * | |350K + * | | + * 300K| | + * +-------+ + * (0,0) + */ + +/******************************************************************************* + * Geometry + ******************************************************************************/ +struct context { + const double* positions; + const size_t* indices; + struct sdis_interface** interfaces; /* Per primitive interfaces */ +}; + +static void +get_indices(const size_t iseg, size_t ids[2], void* context) +{ + struct context* ctx = context; + ids[0] = ctx->indices[iseg*2+0]; + ids[1] = ctx->indices[iseg*2+1]; +} + +static void +get_position(const size_t ivert, double pos[2], void* context) +{ + struct context* ctx = context; + pos[0] = ctx->positions[ivert*2+0]; + pos[1] = ctx->positions[ivert*2+1]; +} + +static void +get_interface(const size_t iseg, struct sdis_interface** bound, void* context) +{ + struct context* ctx = context; + *bound = ctx->interfaces[iseg]; +} + +/******************************************************************************* + * Medium data + ******************************************************************************/ +static double +temperature_unknown(const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)data; + CHK(vtx != NULL); + return -1; +} + +static double +solid_get_calorific_capacity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)data; + CHK(vtx != NULL && data == NULL); + return 2.0; +} + +static double +solid_get_thermal_conductivity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)data; + CHK(vtx != NULL); + return 50.0; +} + +static double +solid_get_volumic_mass + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)data; + CHK(vtx != NULL); + return 25.0; +} + +static double +solid_get_delta + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)data; + CHK(vtx != NULL); + return 1.0/20.0; +} + +static double +solid_get_delta_boundary + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)data; + CHK(vtx != NULL); + return 2.1/20.0; +} + +/******************************************************************************* + * Interface + ******************************************************************************/ +struct interf { + double temperature; +}; + +static double +null_convection_coef + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(frag != NULL); + (void)data; + return 0; +} + +static double +interface_get_temperature + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->temperature; +} + +/******************************************************************************* + * Test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_device* dev = NULL; + struct sdis_data* data = NULL; + struct sdis_estimator* estimator = NULL; + struct sdis_medium* solid = NULL; + struct sdis_medium* fluid = NULL; + struct sdis_interface* Tnone = NULL; + struct sdis_interface* T300 = NULL; + struct sdis_interface* T350 = NULL; + struct sdis_scene* scn = NULL; + struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; + struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; + struct sdis_interface_shader interface_shader = DUMMY_INTERFACE_SHADER; + struct sdis_interface* interfaces[4]; + struct context ctx; + struct interf* interface_param = NULL; + double pos[2]; + double time; + double ref; + const size_t N = 10000; + size_t nreals; + size_t nfails; + (void)argc, (void)argv; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + CHK(sdis_device_create + (NULL, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev) == RES_OK); + + /* Create the fluid medium */ + fluid_shader.temperature = temperature_unknown; + CHK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid) == RES_OK); + + /* Create the solid medium */ + solid_shader.calorific_capacity = solid_get_calorific_capacity; + solid_shader.thermal_conductivity = solid_get_thermal_conductivity; + solid_shader.volumic_mass = solid_get_volumic_mass; + solid_shader.delta_solid = solid_get_delta; + solid_shader.delta_boundary = solid_get_delta_boundary; + solid_shader.temperature = temperature_unknown; + CHK(sdis_solid_create(dev, &solid_shader, NULL, &solid) == RES_OK); + + /* Create the fluid/solid interface with no limit conidition */ + interface_shader.convection_coef = null_convection_coef; + interface_shader.temperature = NULL; + CHK(sdis_interface_create + (dev, solid, fluid, &interface_shader, NULL, &Tnone) == RES_OK); + + /* Create the fluid/solid interface with a fixed temperature of 300K */ + CHK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data) == RES_OK); + interface_param = sdis_data_get(data); + interface_param->temperature = 300; + interface_shader.convection_coef = null_convection_coef; + interface_shader.temperature = interface_get_temperature; + CHK(sdis_interface_create + (dev, solid, fluid, &interface_shader, data, &T300) == RES_OK); + CHK(sdis_data_ref_put(data) == RES_OK); + + /* Create the fluid/solid interface with a fixed temperature of 350K */ + CHK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data) == RES_OK); + interface_param = sdis_data_get(data); + interface_param->temperature = 350; + interface_shader.convection_coef = null_convection_coef; + interface_shader.temperature = interface_get_temperature; + CHK(sdis_interface_create + (dev, solid, fluid, &interface_shader, data, &T350) == RES_OK); + CHK(sdis_data_ref_put(data) == RES_OK); + + /* Release the media */ + CHK(sdis_medium_ref_put(solid) == RES_OK); + CHK(sdis_medium_ref_put(fluid) == RES_OK); + + /* Setup the per primitive scene interfaces */ + CHK(sizeof(interfaces)/sizeof(struct sdis_interface*) == square_nsegments); + interfaces[0] = Tnone; /* Bottom segment */ + interfaces[1] = T300; /* Left segment */ + interfaces[2] = Tnone; /* Top segment */ + interfaces[3] = T350; /* Right segment */ + + /* Create the scene */ + ctx.positions = square_vertices; + ctx.indices = square_indices; + ctx.interfaces = interfaces; + CHK(sdis_scene_2d_create(dev, square_nsegments, get_indices, get_interface, + square_nvertices, get_position, &ctx, &scn) == RES_OK); + + /* Release the interfaces */ + CHK(sdis_interface_ref_put(Tnone) == RES_OK); + CHK(sdis_interface_ref_put(T300) == RES_OK); + CHK(sdis_interface_ref_put(T350) == RES_OK); + + /* Launch the solver */ + pos[0] = 0.5; + pos[1] = 0.5; + time = INF; + CHK(sdis_solve_probe( scn, N, pos, time, 1.0, &estimator) == RES_OK); + CHK(sdis_estimator_get_realisation_count(estimator, &nreals) == RES_OK); + CHK(sdis_estimator_get_failure_count(estimator, &nfails) == RES_OK); + CHK(sdis_estimator_get_temperature(estimator, &T) == RES_OK); + + /* Print the estimation results */ + ref = 350 * pos[0] + (1-pos[0]) * 300; + printf("Temperature at (%g, %g) = %g ~ %g +/- %g\n", + SPLIT2(pos), ref, T.E, T.SE); + printf("#realisations: %lu; #failures: %lu\n", + (unsigned long)nreals, (unsigned long)nfails); + + /* Check the results */ + CHK(nfails + nreals == N); + CHK(eq_eps(T.E, ref, T.SE)); + + /* Release data */ + CHK(sdis_estimator_ref_put(estimator) == RES_OK); + CHK(sdis_scene_ref_put(scn) == RES_OK); + CHK(sdis_device_ref_put(dev) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_solve_probe3.c b/src/test_sdis_solve_probe3.c @@ -148,7 +148,7 @@ solid_get_delta_boundary /******************************************************************************* * Interface ******************************************************************************/ -struct interface { +struct interf { double temperature; }; @@ -166,7 +166,7 @@ interface_get_temperature (const struct sdis_interface_fragment* frag, struct sdis_data* data) { CHK(data != NULL && frag != NULL); - return ((const struct interface*)sdis_data_cget(data))->temperature; + return ((const struct interf*)sdis_data_cget(data))->temperature; } /******************************************************************************* @@ -193,7 +193,7 @@ main(int argc, char** argv) struct s3dut_mesh* msh = NULL; struct s3dut_mesh_data msh_data; struct context ctx = CONTEXT_NULL; - struct interface* interface_param = NULL; + struct interf* interface_param = NULL; double pos[3]; double time; double ref; @@ -229,8 +229,8 @@ main(int argc, char** argv) (dev, solid, fluid, &interface_shader, NULL, &Tnone) == RES_OK); /* Create the fluid/solid interface with a fixed temperature of 300K */ - CHK(sdis_data_create(dev, sizeof(struct interface), - ALIGNOF(struct interface), NULL, &data) == RES_OK); + CHK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data) == RES_OK); interface_param = sdis_data_get(data); interface_param->temperature = 300; interface_shader.convection_coef = null_convection_coef; @@ -240,8 +240,8 @@ main(int argc, char** argv) CHK(sdis_data_ref_put(data) == RES_OK); /* Create the fluid/solid interface with a fixed temperature of 350K */ - CHK(sdis_data_create(dev, sizeof(struct interface), - ALIGNOF(struct interface), NULL, &data) == RES_OK); + CHK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data) == RES_OK); interface_param = sdis_data_get(data); interface_param->temperature = 350; interface_shader.convection_coef = null_convection_coef; @@ -324,7 +324,7 @@ main(int argc, char** argv) /* Check the results */ CHK(nfails + nreals == N); - CHK(eq_eps(T.E, ref, T.SE)); + CHK(eq_eps(T.E, ref, 2*T.SE)); /* Release data */ CHK(sdis_estimator_ref_put(estimator) == RES_OK); diff --git a/src/test_sdis_solve_probe3_2d.c b/src/test_sdis_solve_probe3_2d.c @@ -0,0 +1,329 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * This 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/ssp.h> + +#include <rsys/stretchy_array.h> +#include <rsys/math.h> + +/* + * The scene is composed of a solid square whose temperature is unknown. The + * convection coefficient with the surrounding fluid is null. The temperature + * is fixed at the left and right face. At the center of the cube there is a + * solid disk whose physical properties are the same of the solid square; i.e. + * the disk influences the random walks but not the result. + * + * (1,1) + * +--------------+ + * | # # | + * | # # |350K + * | # # | + * | # # | + * 300K| # # | + * | # # | + * +--------------+ + * (0,0) + */ + +/******************************************************************************* + * Geometry + ******************************************************************************/ +struct context { + double* positions; + size_t* indices; + struct sdis_interface* solid_fluid_Tnone; + struct sdis_interface* solid_fluid_T300; + struct sdis_interface* solid_fluid_T350; + struct sdis_interface* solid_solid; +}; +static const struct context CONTEXT_NULL = { NULL }; + +static void +get_indices(const size_t iseg, size_t ids[2], void* context) +{ + struct context* ctx = context; + ids[0] = ctx->indices[iseg*2+0]; + ids[1] = ctx->indices[iseg*2+1]; +} + +static void +get_position(const size_t ivert, double pos[2], void* context) +{ + struct context* ctx = context; + pos[0] = ctx->positions[ivert*2+0]; + pos[1] = ctx->positions[ivert*2+1]; +} + +static void +get_interface(const size_t iseg, struct sdis_interface** bound, void* context) +{ + struct context* ctx = context; + CHK(bound != NULL && context != NULL); + + if(iseg == 1) { /* Square left segment */ + *bound = ctx->solid_fluid_T300; + } else if(iseg == 3 ) { /* Square right segment */ + *bound = ctx->solid_fluid_T350; + } else if(iseg < square_nsegments) { /* Square remaining segments */ + *bound = ctx->solid_fluid_Tnone; + } else { /* Faces of the internal geometry */ + *bound = ctx->solid_solid; + } +} + +/******************************************************************************* + * Medium data + ******************************************************************************/ +static double +temperature_unknown(const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)data; + CHK(vtx != NULL); + return -1; +} + +static double +solid_get_calorific_capacity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)data; + CHK(vtx != NULL && data == NULL); + return 2.0; +} + +static double +solid_get_thermal_conductivity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)data; + CHK(vtx != NULL); + return 50.0; +} + +static double +solid_get_volumic_mass + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)data; + CHK(vtx != NULL); + return 25.0; +} + +static double +solid_get_delta + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)data; + CHK(vtx != NULL); + return 1.0/20.0; +} + +static double +solid_get_delta_boundary + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)data; + CHK(vtx != NULL); + return 2.1/20.0; +} + +/******************************************************************************* + * Interface + ******************************************************************************/ +struct interf { + double temperature; +}; + +static double +null_convection_coef + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(frag != NULL); + (void)data; + return 0; +} + +static double +interface_get_temperature + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + CHK(data != NULL && frag != NULL); + return ((const struct interf*)sdis_data_cget(data))->temperature; +} + +/******************************************************************************* + * Test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_device* dev = NULL; + struct sdis_data* data = NULL; + struct sdis_estimator* estimator = NULL; + struct sdis_medium* solid = NULL; + struct sdis_medium* fluid = NULL; + struct sdis_interface* Tnone = NULL; + struct sdis_interface* T300 = NULL; + struct sdis_interface* T350 = NULL; + struct sdis_interface* solid_solid = NULL; + struct sdis_scene* scn = NULL; + struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; + struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; + struct sdis_interface_shader interface_shader = DUMMY_INTERFACE_SHADER; + struct context ctx = CONTEXT_NULL; + struct interf* interface_param = NULL; + double pos[2]; + double time; + double ref; + const size_t N = 10000; + size_t nsegs; + size_t nverts; + size_t nreals; + size_t nfails; + size_t i; + (void)argc, (void)argv; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + CHK(sdis_device_create + (NULL, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev) == RES_OK); + + /* Create the fluid medium */ + fluid_shader.temperature = temperature_unknown; + CHK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid) == RES_OK); + + /* Create the solid medium */ + solid_shader.calorific_capacity = solid_get_calorific_capacity; + solid_shader.thermal_conductivity = solid_get_thermal_conductivity; + solid_shader.volumic_mass = solid_get_volumic_mass; + solid_shader.delta_solid = solid_get_delta; + solid_shader.delta_boundary = solid_get_delta_boundary; + solid_shader.temperature = temperature_unknown; + CHK(sdis_solid_create(dev, &solid_shader, NULL, &solid) == RES_OK); + + /* Create the fluid/solid interface with no limit conidition */ + interface_shader.convection_coef = null_convection_coef; + interface_shader.temperature = NULL; + CHK(sdis_interface_create + (dev, solid, fluid, &interface_shader, NULL, &Tnone) == RES_OK); + + /* Create the fluid/solid interface with a fixed temperature of 300K */ + CHK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data) == RES_OK); + interface_param = sdis_data_get(data); + interface_param->temperature = 300; + interface_shader.convection_coef = null_convection_coef; + interface_shader.temperature = interface_get_temperature; + CHK(sdis_interface_create + (dev, solid, fluid, &interface_shader, data, &T300) == RES_OK); + CHK(sdis_data_ref_put(data) == RES_OK); + + /* Create the fluid/solid interface with a fixed temperature of 350K */ + CHK(sdis_data_create(dev, sizeof(struct interf), + ALIGNOF(struct interf), NULL, &data) == RES_OK); + interface_param = sdis_data_get(data); + interface_param->temperature = 350; + interface_shader.convection_coef = null_convection_coef; + interface_shader.temperature = interface_get_temperature; + CHK(sdis_interface_create + (dev, solid, fluid, &interface_shader, data, &T350) == RES_OK); + CHK(sdis_data_ref_put(data) == RES_OK); + + /* Create the solid/solid interface */ + interface_shader.convection_coef = NULL; + interface_shader.temperature = NULL; + CHK(sdis_interface_create + (dev, solid, solid, &interface_shader, NULL, &solid_solid) == RES_OK); + + /* Release the media */ + CHK(sdis_medium_ref_put(solid) == RES_OK); + CHK(sdis_medium_ref_put(fluid) == RES_OK); + + /* Register the square geometry */ + FOR_EACH(i, 0, square_nvertices) { + sa_push(ctx.positions, square_vertices[i*2+0]); + sa_push(ctx.positions, square_vertices[i*2+1]); + } + FOR_EACH(i, 0, square_nsegments) { + sa_push(ctx.indices, square_indices[i*2+0]); + sa_push(ctx.indices, square_indices[i*2+1]); + } + + /* Setup a disk at the center of the square */ + nverts = 64; + FOR_EACH(i, 0, nverts) { + const float theta = (float)i * (2*(float)PI)/(float)nverts; + const float r = 0.25f; /* radius */ + sa_push(ctx.positions, (float)cos(theta) * r + 0.5f); + sa_push(ctx.positions, (float)sin(theta) * r + 0.5f); + } + FOR_EACH(i, 0, nverts) { + sa_push(ctx.indices, i + square_nvertices); + sa_push(ctx.indices, (i+1)%nverts + square_nvertices); + } + + /* Create the scene */ + ctx.solid_fluid_Tnone = Tnone; + ctx.solid_fluid_T300 = T300; + ctx.solid_fluid_T350 = T350; + ctx.solid_solid = solid_solid; + nverts = sa_size(ctx.positions) / 2; + nsegs = sa_size(ctx.indices) / 2; + CHK(sdis_scene_2d_create(dev, nsegs, get_indices, get_interface, nverts, + get_position, &ctx, &scn) == RES_OK); + + /* Release the scene data */ + CHK(sdis_interface_ref_put(Tnone) == RES_OK); + CHK(sdis_interface_ref_put(T300) == RES_OK); + CHK(sdis_interface_ref_put(T350) == RES_OK); + CHK(sdis_interface_ref_put(solid_solid) == RES_OK); + sa_release(ctx.positions); + sa_release(ctx.indices); + + /* Launch the solver */ + pos[0] = 0.5; + pos[1] = 0.5; + time = INF; + CHK(sdis_solve_probe( scn, N, pos, time, 1.0, &estimator) == RES_OK); + CHK(sdis_estimator_get_realisation_count(estimator, &nreals) == RES_OK); + CHK(sdis_estimator_get_failure_count(estimator, &nfails) == RES_OK); + CHK(sdis_estimator_get_temperature(estimator, &T) == RES_OK); + + /* Print the estimation results */ + ref = 350 * pos[0] + (1-pos[0]) * 300; + printf("Temperature at (%g, %g) = %g ~ %g +/- %g\n", + SPLIT2(pos), ref, T.E, T.SE); + printf("#realisations: %lu; #failures: %lu\n", + (unsigned long)nreals, (unsigned long)nfails); + + /* Check the results */ + CHK(nfails + nreals == N); + CHK(eq_eps(T.E, ref, 3*T.SE)); + + /* Release data */ + CHK(sdis_estimator_ref_put(estimator) == RES_OK); + CHK(sdis_scene_ref_put(scn) == RES_OK); + CHK(sdis_device_ref_put(dev) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; + +} diff --git a/src/test_sdis_solve_probe_2d.c b/src/test_sdis_solve_probe_2d.c @@ -0,0 +1,222 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "sdis.h" +#include "test_sdis_utils.h" + +#include <rsys/math.h> + +/* + * The scene is composed of a solid square with unknown temperature. The + * surrounding fluid has a fixed constant temperature. + * + * (1,1) + * +-------+ _\ + * | | / / + * | | \__/ 300K + * | | + * +-------+ + * (0,0) + */ + +/******************************************************************************* + * Geometry + ******************************************************************************/ +struct context { + const double* positions; + const size_t* indices; + struct sdis_interface* interf; +}; + +static void +get_indices(const size_t iseg, size_t ids[2], void* context) +{ + struct context* ctx = context; + ids[0] = ctx->indices[iseg*2+0]; + ids[1] = ctx->indices[iseg*2+1]; +} + +static void +get_position(const size_t ivert, double pos[2], void* context) +{ + struct context* ctx = context; + pos[0] = ctx->positions[ivert*2+0]; + pos[1] = ctx->positions[ivert*2+1]; +} + +static void +get_interface(const size_t iseg, struct sdis_interface** bound, void* context) +{ + struct context* ctx = context; + (void)iseg; + *bound = ctx->interf; +} + +/******************************************************************************* + * Media & interface + ******************************************************************************/ +static double +fluid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)vtx, (void)data; + return 300.0; +} + +static double +solid_get_calorific_capacity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)vtx, (void)data; + return 1.0; +} + +static double +solid_get_thermal_conductivity + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)vtx, (void)data; + return 0.1; +} + +static double +solid_get_volumic_mass + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)vtx, (void)data; + return 1.0; +} + +static double +solid_get_delta + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)vtx, (void)data; + return 1.0/20.0; +} + +static double +solid_get_delta_boundary + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)vtx, (void)data; + return 2.1/20.0; +} + +static double +solid_get_temperature + (const struct sdis_rwalk_vertex* vtx, struct sdis_data* data) +{ + (void)vtx, (void)data; + return -1; +} + +static double +interface_get_convection_coef + (const struct sdis_interface_fragment* frag, struct sdis_data* data) +{ + (void)frag, (void)data; + return 0.5; +} + +/******************************************************************************* + * Main test + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct sdis_mc T = SDIS_MC_NULL; + struct sdis_device* dev = NULL; + struct sdis_medium* solid = NULL; + struct sdis_medium* fluid = NULL; + struct sdis_interface* interf = NULL; + struct sdis_scene* scn = NULL; + struct sdis_estimator* estimator = NULL; + struct sdis_fluid_shader fluid_shader = DUMMY_FLUID_SHADER; + struct sdis_solid_shader solid_shader = DUMMY_SOLID_SHADER; + struct sdis_interface_shader interface_shader = DUMMY_INTERFACE_SHADER; + struct context ctx; + double pos[2]; + double time; + double ref; + const size_t N = 1000; + size_t nreals; + size_t nfails; + (void)argc, (void)argv; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + CHK(sdis_device_create + (NULL, &allocator, SDIS_NTHREADS_DEFAULT, 0, &dev) == RES_OK); + + /* Create the fluid medium */ + fluid_shader.temperature = fluid_get_temperature; + CHK(sdis_fluid_create(dev, &fluid_shader, NULL, &fluid) == RES_OK); + + /* Create the solid medium */ + solid_shader.calorific_capacity = solid_get_calorific_capacity; + solid_shader.thermal_conductivity = solid_get_thermal_conductivity; + solid_shader.volumic_mass = solid_get_volumic_mass; + solid_shader.delta_solid = solid_get_delta; + solid_shader.delta_boundary = solid_get_delta_boundary; + solid_shader.temperature = solid_get_temperature; + CHK(sdis_solid_create(dev, &solid_shader, NULL, &solid) == RES_OK); + + /* Create the solid/fluid interface */ + interface_shader.convection_coef = interface_get_convection_coef; + interface_shader.temperature = NULL; + CHK(sdis_interface_create + (dev, solid, fluid, &interface_shader, NULL, &interf) == RES_OK); + + /* Release the media */ + CHK(sdis_medium_ref_put(solid) == RES_OK); + CHK(sdis_medium_ref_put(fluid) == RES_OK); + + /* Create the scene */ + ctx.positions = square_vertices; + ctx.indices = square_indices; + ctx.interf = interf; + CHK(sdis_scene_2d_create(dev, square_nsegments, get_indices, get_interface, + square_nvertices, get_position, &ctx, &scn) == RES_OK); + + CHK(sdis_interface_ref_put(interf) == RES_OK); + + /* Test the solver */ + pos[0] = 0.5; + pos[1] = 0.5; + time = INF; + CHK(sdis_solve_probe(scn, N, pos, time, 1.0, &estimator) == RES_OK); + CHK(sdis_estimator_get_realisation_count(estimator, &nreals) == RES_OK); + CHK(sdis_estimator_get_failure_count(estimator, &nfails) == RES_OK); + + CHK(nfails + nreals == N); + + CHK(sdis_estimator_get_temperature(estimator, &T) == RES_OK); + + ref = 300; + printf("Temperature at (%g, %g) = %g ~ %g +/- %g\n", + SPLIT2(pos), ref, T.E, T.SE); + CHK(eq_eps(T.E, ref, T.SE)); + + CHK(sdis_estimator_ref_put(estimator) == RES_OK); + + CHK(sdis_scene_ref_put(scn) == RES_OK); + CHK(sdis_device_ref_put(dev) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_sdis_utils.h b/src/test_sdis_utils.h @@ -20,7 +20,7 @@ #include <stdio.h> /******************************************************************************* - * Geometry + * Box geometry ******************************************************************************/ static const double box_vertices[8/*#vertices*/*3/*#coords per vertex*/] = { 0.0, 0.0, 0.0, @@ -55,6 +55,25 @@ static const size_t box_indices[12/*#triangles*/*3/*#indices per triangle*/] = { static const size_t box_ntriangles = sizeof(box_indices) / sizeof(size_t[3]); /******************************************************************************* + * Square geometry + ******************************************************************************/ +static const double square_vertices[4/*#vertices*/*2/*#coords per vertex*/] = { + 1.0, 0.0, + 0.0, 0.0, + 0.0, 1.0, + 1.0, 1.0 +}; +static const size_t square_nvertices = sizeof(square_vertices)/sizeof(double[2]); + +static const size_t square_indices[4/*#triangles*/*2/*#indices per segment*/]= { + 0, 1, /* Bottom */ + 1, 2, /* Left */ + 2, 3, /* Top */ + 3, 0 /* Right */ +}; +static const size_t square_nsegments = sizeof(square_indices)/sizeof(size_t[2]); + +/******************************************************************************* * Medium & interface ******************************************************************************/ static INLINE double @@ -121,6 +140,27 @@ dump_mesh } static INLINE void +dump_segments + (FILE* stream, + const double* pos, + const size_t npos, + const size_t* ids, + const size_t nids) +{ + size_t i; + CHK(pos != NULL && npos != 0); + CHK(ids != NULL && nids != 0); + FOR_EACH(i, 0, npos) { + fprintf(stream, "v %g %g 0\n", SPLIT2(pos+i*2)); + } + FOR_EACH(i, 0, nids) { + fprintf(stream, "l %lu %lu\n", + (unsigned long)(ids[i*2+0] + 1), + (unsigned long)(ids[i*2+1] + 1)); + } +} + +static INLINE void check_memory_allocator(struct mem_allocator* allocator) { if(MEM_ALLOCATED_SIZE(allocator)) {